Next: Macros for Initialization, Previous: Label Output, Up: Assembler Format
一些语言被编译后的代码中会包含构造者(也称为初始化例程)—当程序启动时,
用来初始化程序中的数据的函数。这些函数需要在程序开始前被调用—也就是说,
在调用main
之前。
编译一些语言还会生成析构者(也成为终止例程),将在程序终止时被调用。
为了让初始化和终止函数工作,编译器必须在汇编代码中输出一些东西, 使得那些函数在适当的时候被调用。当你将编译器移植到一个新的系统时, 你需要指定如何做。
GCC目前支持两种主要的方式,来执行初始化和终止函数。 每种方式都有两个变体。大多数结构体对于这四个变体都是通用的。
连接器必须构建两个这些函数的链表—一个是初始化函数链表,
叫做__CTOR_LIST__
,一个是终止函数,叫做__DTOR_LIST__
。
每个列表总是起始于一个被忽略的函数指针(其可能为0,-1, 或者之后的函数指针的个数,这取决于具体环境)。 随后是一系列指向构造者(或析构者)的零个或多个函数指针, 然后是一个包含0的函数指针。
取决于操作系统和它的可执行文件格式,或者crtstuff.c, 或者libgcc2.c会在起始时间和退出时间遍历这些列表。 构造者按照列表相反的顺序被调用;析构者按照向前的顺序。
处理静态构造者的最好的方式, 只有在目标文件格式提供任意命名section的时候才可以工作。 这会在构造者列表旁边设置一个section,析构者列表旁边设置另一个, 通常称作‘.ctors’和‘.dtors’。 每个定义了初始化函数的目标文件,还在构造section中放入一个字, 以指向那个函数。 连接器将所有这些字累积放入到一个邻近的‘.ctors’ section中。 终止函数按照类似的方式处理。
如果定义了TARGET_ASM_NAMED_SECTION,
则该方法将被target-def.h作为缺省方式选择。
不支持任意section,但是支持特定的构造者和析构者section的target,
可以定义CTORS_SECTION_ASM_OP
和DTORS_SECTION_ASM_OP
来达到相同的效果。
当支持任意section时,有两个变体, 取决于如何调用crtstuff.c中的代码。 在支持.init section(其在程序起始时被执行)的系统上, crtstuff.c的部分代码被编译到那个section中。 程序按照类似于下面的方式被gcc驱动连接:
ld -o output_file crti.o crtbegin.o ... -lgcc crtend.o crtn.o
函数(__init
)的序言出现在crti.o的.init
section中;
尾声出现在crtn.o中。同样的,函数__fini
在.fini section中。
通常这些文件由操作系统或者GNU C库提供,但GCC也为一些target提供。
目标文件crtbegin.o和crtend.o是从crtstuff.c
中编译出来的(对于大多target)。它们包含了,在.init
和.fini
section中的代码片断,用于跳转到.text
section中的例程中。
连接器会把section的所有部分放在一起,形成完整的__init
函数,
其可以在起始处调用我们需要的例程。
macro properly.
要使用这个变体,你必须适当的定义INIT_SECTION_ASM_OP
宏。
如果init section不可用,GCC在编译任何叫做main
(或者更加确切的说,
任何被语言前端expand_main_function
指定为程序入口点的函数)的函数时,
其会插入一个调用__main
的程序,以作为在函数序言之后首先执行的代码。
__main
函数在libgcc2.c中定义,并运行全局的构造者。
对于文件格式不支持任意section的,也有两个变体。对于最简单的变体,
必须使用GNU连接器(GNU ld
)和'a.out'格式。在这种情况下,
TARGET_ASM_CONSTRUCTOR
被定义,
用来生成一个‘N_SETT’类型的.stabs
条目,
来引用名字__CTOR_LIST__
,
并且其值为一个包含了初始化代码的void函数地址。
GNU连接器将其识别为一个要设定的值;该值会被累积,
并最终作为一个向量放在可执行程序中,
一个前导(被忽略的)数目和一个尾部的0元素。
TARGET_ASM_DESTRUCTOR
按照类似的情况被处理。
由于init section不可用,所以使得编译main
来调用__main
,
以开始初始化。
最后一个变体既不使用任意section,也不使用GNU连接器。这适合于,
你想进行动态连接并使用GNU连接器不支持的文件格式的时候,例如 'ECOFF'。
在这种情况下,TARGET_HAVE_CTORS_DTORS
为假,
初始化和终止函数简单的通过它们的名字来识别。这要求在连接过程中的额外程序,
叫做collect2。该程序作为GCC使用的连接器;
它通过运行普通的连接器来完成工作,但是还负责包含初始化和终止函数的向量。
这些函数通过__main
调用。要使用这种方式,
必须在config.gcc中定义use_collect2
。