CRT 初始化CRT Initialization

本主题介绍 CRT 如何在本机代码中初始化全局状态。This topic describes how the CRT initializes global states in native code.

默认情况下,链接器包括 CRT 库,此库提供其自己的启动代码。By default, the linker includes the CRT library, which provides its own startup code. 此启动代码初始化 CRT 库,调用全局初始值设定项,然后调用用于控制台应用程序的用户提供的 main 函数。This startup code initializes the CRT library, calls global initializers, and then calls the user-provided main function for console applications.

初始化全局对象Initializing a Global Object

考虑下列代码:Consider the following code:

int func(void)  
{  
    return 3;  
}  

int gi = func();  

int main()  
{  
    return gi;  
}  

根据 C/C++ 标准,在执行 func() 前必须调用 main()According to the C/C++ standard, func() must be called before main() is executed. 但哪个项来调用它呢?But who calls it?

确定这一点的一个方法是在 func() 中设置断点,调试应用程序并检查堆栈。One way to determine this is to set a breakpoint in func(), debug the application, and examine the stack. 由于 CRT 源代码是 Visual Studio 附带的,因此可以这样做。This is possible because the CRT source code is included with Visual Studio.

在堆栈上浏览函数时,您将发现 CRT 正在循环访问函数指针的列表并在遇到每个指针时对其进行调用。When you browse the functions on the stack, you will find that the CRT is looping through a list of function pointers and calling each one as it encounters them. 这些函数类似于 func() 或类实例的构造函数。These functions are either similar to func() or constructors for class instances.

CRT 从 Visual C++ 编译器中获取函数指针的列表。The CRT obtains the list of function pointers from the Visual C++ compiler. 当编译器发现全局初始值时,它将在 .CRT$XCU 部分(其中,CRT 是部分名称,XCU 是组名称)中生成一个动态初始值设定项。When the compiler sees a global initializer, it generates a dynamic initializer in the .CRT$XCU section (where CRT is the section name and XCU is the group name). 若要获取这些动态初始值设定项的列表,请运行命令 dumpbin /all main.obj,然后搜索 .CRT$XCU 部分(在将 main.cpp 作为 C++ 文件而不是 C 文件编译时)。To obtain a list of those dynamic initializers run the command dumpbin /all main.obj, and then search the .CRT$XCU section (when main.cpp is compiled as a C++ file, not a C file). 它将类似于以下内容:It will be similar to the following:

SECTION HEADER #6  
.CRT$XCU name  
       0 physical address  
       0 virtual address  
       4 size of raw data  
     1F2 file pointer to raw data (000001F2 to 000001F5)  
     1F6 file pointer to relocation table  
       0 file pointer to line numbers  
       1 number of relocations  
       0 number of line numbers  
40300040 flags  
         Initialized Data  
         4 byte align  
         Read Only  

RAW DATA #6  
  00000000: 00 00 00 00                                      ....  

RELOCATIONS #6  
                                                Symbol    Symbol  
 Offset    Type              Applied To         Index     Name  
 --------  ----------------  -----------------  --------  ------  
 00000000  DIR32                      00000000         C  ??__Egi@@YAXXZ (void __cdecl `dynamic initializer for 'gi''(void))  

CRT 定义两个指针:The CRT defines two pointers:

  • __xc_a中的.CRT$XCA__xc_a in .CRT$XCA

  • __xc_z中的.CRT$XCZ__xc_z in .CRT$XCZ

    除了 __xc_a__xc_z 之外,两个组未定义任何其他符号。Both groups do not have any other symbols defined except __xc_a and __xc_z.

    现在,当链接器读取各种 .CRT 组时,它会将这些组合并在一个部分中并按字母顺序对其进行排序。Now, when the linker reads various .CRT groups, it combines them in one section and orders them alphabetically. 这表示用户定义的全局初始值设定项(Visual C++ 编译器将其置于 .CRT$XCU 中)将始终出现在 .CRT$XCA 之后和 .CRT$XCZ 之前。This means that the user-defined global initializers (which the Visual C++ compiler puts in .CRT$XCU) will always come after .CRT$XCA and before .CRT$XCZ.

    此部分类似于以下内容:The section will resemble the following:

.CRT$XCA  
            __xc_a  
.CRT$XCU  
            Pointer to Global Initializer 1  
            Pointer to Global Initializer 2  
.CRT$XCZ  
            __xc_z  

CRT 库会使用 __xc_a__xc_z 来确定全局初始值设定项列表的开头和结尾,原因在于这些设定项在图像加载后在内存中布局的方式。So, the CRT library uses both __xc_a and __xc_z to determine the start and end of the global initializers list because of the way in which they are laid out in memory after the image is loaded.

另请参阅See Also

CRT 库功能CRT Library Features