Hi, I’m George Mileka and I work on the Visual C++ libraries team. My team owns the CRT, ATL, MFC and STL…
One subject I have always been interested in is the startup code and initialization. The subject got even more challenging as we started supporting mixing native and managed code in our binaries.
Today I will talk about how the CRT initializes global states in the native world…
By default, the linker includes the CRT library which provides its own startup code. The CRT startup code initializes the CRT library, calls global initializers, and finally calls user-provided “main()” function (for console applications).
Now, consider the following code:
int gi = func();
According to the C/C++ standard, func() must be called before main() is executed. But who calls it?
An easy way to know, is to set a breakpoint in func(), start debugging the application and look at the stack! Luckily, the CRT code ships with VS!
Browsing the functions on the stack, you will find that the CRT is simply looping through a list of function pointers, calling each one as it goes through them. Those functions are either ones like func() or constructors for class instances…
So, where does the CRT get that list from?
There is a contract between the CRT and VC++ Compiler:
- When the VC++ compiler sees a global initializer, it generates a dynamic initializer in the .CRT$XCU section (note that the CRT is the section name and XCU is the group name). You can look at those dynamic initializers by running “dumpbin /all main.obj”, and then looking at the .CRT$XCU section (when main.cpp is compiled as a cpp file, not a c file). It will have something like this:
SECTION HEADER #C
Offset Type Applied To Index Name
——– —————- —————– ——– ——
00000000 DIR32 00000000 26 ??__Egi@@YAXXZ (void __cdecl `dynamic initializer for ‘gi”(void))
- CRT defines two pointers;
o __xc_a in .CRT$XCA
o __xc_z in .CRT$XCZ
Both groups do not have any other symbols defined except __xc_a and __xc_z.
Now – when the linker reads various .CRT groups, it combines them in one section and orders them alphabetically. This means that the user-defined global initializers (which the VC++ compiler puts in .CRT$XCU) will always come after .CRT$XCA and before .CRT$XCZ. So, it should look something like:
Pointer to Global Initializer 1
Pointer to Global Initializer 2
So, the CRT library can use both __xc_a and __xc_z to determine the start and end of the global initializers list (array) because of how they are laid out in memory once the image is loaded.
Please, send any questions, comments or feedback our way! We’ll be happy to address them!
Visual C++ Libraries Team