"Chris Torek" <no****@torek.n etwrote in message
news:eb******** @news2.newsguy. com...
In article <11************ **********@b28g 2000cwb.googleg roups.com>
<Su********@gma il.comwrote:
>>hmmm.... 'the system calls main' is precisely the answer I am
finding
in documentation everywhere..... and I am finding it sort of vague...
Probably because it is (deliberately) vague. :-)
As Eric Sosman noted elsethread, the method is up to the
implementation. It might be illustrative (if borderline off-topic,
and
probably on the "off" side of the border-line) to look at two real
implementations , however.
The first implementation to consider is BSD/OS on the i386. Here,
when you compile a program with:
shlicc -o foo foo.c -ljpeg -lm
you get an executable named "foo" that contains:
- your own code,
- some startup code, and
- references to the shared libraries used (libjpeg, libm, and
libc).
The startup code:
- saves argc and argv (actually just one register that points to
a system-provided data structure) and __environ for getenv() etc
- ensures that file descriptors 0, 1, and 2 are open
- locates the shared libraries, opening them and mapping them in
at the desired locations (text and initialized data, with
"uninitiali zed data" mapped to zero-fill space)
- calls any initialization routines in those shared libraries
(this is really for C++; pure C code never has any)
- calls your main(), passing the saved argc and argv (and a
third parameter which you may safely ignore)
- takes whatever value your main() returned and calls exit()
Initialization of static-duration data for your program -- whether
ordinary initialized variables like:
int globalvar = 3;
void f(void) { static int localstatic = 42; ... }
or uninitialized ("bss", Block Started by Symbol) zero-fill data
-- is handled automatically by the system. However, static-duration
data for shared libraries is handled by the startup code (with a
lot of help from the system; it winds up being just three __mmap()
calls).
An example of "who" accomplishes these things (info from Linux, but
any POSIX/ELF system should be similar):
1. The kernel loads the program image into memory when you do an
exec()
2. If there's dynamic objects linked, ld.so is called to load them and
link them into the image
3. The kernel examines the ELF header to find the entry point (usually
called _start())
4. The kernel jumps to that address
5. _start() handles the initialization tasks Chris lists above
6. _start() calls your main()
7. When main() returns or exit() is called, _start() does all the
cleanup tasks listed above
8. Last, _start() executes a syscall to the kernel to let it know the
process is finished
(I might have step 2 in the wrong place; _start() may call ld.so
instead of the kernel)
Another example is DOS COM files. DOS simply loads the file into
memory and jumps to a particular offset in that memory image. That
location is where the linker puts the equivalent of _start(), and then
execution proceeds roughly as above from step 5.
Every implementation is going to be different, but it needs to do
roughly the same things, so they'll all look roughly the same.
S
--
Stephen Sprunk "God does not play dice." --Albert Einstein
CCIE #3723 "God is an inveterate gambler, and He throws the
K5SSS dice at every possible opportunity." --Stephen Hawking
--
Posted via a free Usenet account from
http://www.teranews.com