In article <11**********************@b28g2000cwb.googlegroups .com>
<Su********@gmail.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
"uninitialized 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).
The system has already provided a stack for automatic varibles at
the time the startup code is entered, so nothing need be done there.
The three standards streams (stdin, stdout, stderr) use initialized
data, so only the three "file descriptors" (0,1,2) need to be
handled in the startup code.
The second implementation to consider is a typical ROM-based system
(slightly modified to make it a "hosted" environment). Here, when
you compile your program, you get a "ROMable image", which you load
into some kind of programmable read-only memory (typically an EEPROM
or, today, flash memory). When you first power-up the device --
whether it is a cell phone or a digital video recorder or a
defibrillator or whatever -- it executes some startup code.
This startup code:
- depending on hardware involved, sets up devices like the CPU
clock, memory bank switchers, and so on (this code is often
very sensitive and tricky since a lot of it cannot refer to
memory, as the memory controlling hardware is not yet set up)
- tests and/or clears RAM (initializing ECC memory if needed)
- copies the ROM-image initialized data to the data area
(remember, the runtime code cannot refer directly to the ROM
data because the ROM is read-only -- "++globalvar" would
leave the variable unchanged!)
- zeros out other data if needed (the ECC setup may have zeroed
all of RAM already)
- creates a stack (this is actually usually done a bit earlier but
it interacts with memory-initialization so I moved it here)
- calls the C library's "set up stdin, stdout, stderr" routines
- makes up a fake argv, if desired
- calls your main(), passing 0 for argc
- if your main() returns, attempts to display a "rebooting" message
and then repeats as much of the above as needed
As you should now see, while there are similarities, the actual
code for these two implementations is quite different.
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it
http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.