It can't, of course, unless you were very careful not to use
any direct memory references (what is know as Position Independent Code). A flat binary object file (such as a DOS .com executable) can only be run beginning at the location of it's origin relative to it's CS base. So if (to use a real-mode example), CS is set to 0x1000, and your origin is at 0x100, then your code must be loaded into at absolute address 0x10100, and the loader will have to jump to that address (not 0x10000, the beginning of the segment) for it to run correctly. This is in fact exactly what DOS does when it runs a .com file (the first 256 bytes of the segment are used to hold the Program Segment Prefix, a special data area used by DOS for certain bookkeeping details - it's something of a holdover from CP/M, like the .com executable format itself).
To give a p-mode case, assuming that the CS selector is 0x0000 (which is how it is in most systems), and the origin is 0x300000, then the code is always loaded at 0x300000, and again, the loader needs to jump to there in order to start the program.
However, you have to keep in mind that the ORG directive only applies to flat-binary files, which generally are only used for certain very specific systems programming needs (primarily in the early stages of booting); the .com file format is something of an anomaly as these things go.
More typically, an executable file (whether a system program or an application) is in a relocatable object format such as OMF (more often called .exe), PE, COFF, a.out, or ELF. When the object file is generated by the compiler, two of the steps in creating an actual executable image are delayed: adding in any referenced external code, and assigning the actual code origin and label address values. Instead, it creates a symbol table which holds the offsets for all the labels in the program, relative to a nominal base of zero. This table is a part of the object file, often preceeding the code itself.
The first of these two binding stages is done by the linker (when the executable files is created for static libraries, or at runtime for dynamically shared libraries); it resolves all the external references, makes sure that all of them are correct, and adds the new label offsets to the symbol table. The result is what is usually called the executable file, but it is still not the final form of the program.
The second step is done at runtime by the loader, preferably delaying the binding to the last possible stage. The loader determines where the code should be loaded at (based on where the process's allocated memory is), and generates the final program image by reading the relative offsets from the symbol table and patching the appropriate addresses into the code.
For example, if this assembly language program
Code: Select all
;; test.asm
[extern baz]
main: mov eax, DWORD bar
call baz
bar dd "quux"
is assembled into FOO format, then the object file might look like
Code: Select all
SYMTAB
00000000 "main" 00 00 00 00
00000001 "baz" ** ** ** **
00000002 "bar" 0A 00 00 00
OBJ
B8 *00000002
E8 *00000001
71757578
(All values in hex; keep in mind that x86 is a little endian format, so the address-offset for [tt]bar[/tt] is actually 0x0000000A). Then, if the linker puts [tt]baz[/tt] directly after the end of the data (normally it wouldn't, but we'll assume it for demonstration purposes), it would then change the line in the symbol table to
Finally, when the loader runs, it gets the appropriate location to load the program at from the process manager - let's say, 0x300000 - and then proceeds to replace all the symbol references with the actual addresses to be used, and emits a binary image like this:
It is this final image that is loaded to 0x300000 (as a binary, of course, not as hex). The loader then jumps to the entry point, as it would with a flat-binary executable (which is essentially what the program now is).
For more details on object formats, linking and loading, see the home page for the book
Linkers and Loaders by John Levine.
HTH. Comments and corrections welcome.