GCC中初始化函数是如何被处理的?

本文译至:

http://gcc.gnu.org/onlinedocs/gccint/Initialization.html

如我们所知,在GCC通过给代码追加__attribute__((constructor))和__attribute__((destructor))的方式可以追加初始函数和终止函数,

这篇文章介绍了GCC内部是如何实现上述处理的。

简单的说,就是在最经常的情况下,初始函数会被追加到.ctor section中,.init会调用对应的函数处理这些初始函数。终止情况类似。

----------------------------------------------------------

初始化函数是如何被处理的?

某些语言被编译后的代码包括构造体(也被称为初始化例程)-- 该函数被用于程序启动时初始化程序数据。这些函数需要在程序“开始”前被调用 - 就是说,在main函数前被调用。

同时,编译一些语言时会生成析构体(也被称为终止例程),它应在程序结束时被调用。

为了支持初始函数和终止函数,编译器必须在汇编代码中生成一些东西来使这些函数在合适的时间点被调用。当你把编译器移植到一个新的系统时,你需要去指定怎么去做。

目前GCC主要有两种方式支持初始函数和终止函数的执行,每种方式都有两个变体。对这四种变体而言,大部分结构是共通的。

链接器必须为这些函数建立两个列表 --  一个初始化函数的列表,称为 __CTOR_LIST__,和一个终止函数列表,称为 __DTOR_LIST__。

每个列表总是从一个被忽略的函数指针开始(该函数指针在不同环境下,可能是0, -1 或是其后的函数指针的个数)。该函数指针后面跟随着一系列的0或是更多的构造体(析构体)函数指针,最后以一个包含0的函数指针结束。

依据不同OS和它的可执行文件格式,crtstuff.c 或 libgcc2.c 会在启动时和退出时遍历这些列表。构造体按列表的逆序被调用,析构体按顺序被调用。

处理静态构造体的最佳方式只支持提供任意命名Section的目标文件格式。一个Section被用于构造体列表,另一个用于析构体列表。它们习惯上被叫做‘.ctors’ 和 ‘.dtors’。每个定义一个初始函数的目标文件在构造体的section里放置一个word来指向初始函数。链接器累积所有的word到一个连续的‘.ctors’ section中。终止函数也按类似的方式处理。

如果 TARGET_ASM_NAMED_SECTION 被定义,这种方法会被 target-def.h 设为默认方式。如果一个目标板不支持任意命名的section,但是又支持特殊的可指定的构造体和析构体也可以通过定义 CTORS_SECTION_ASM_OP 和 DTORS_SECTION_ASM_OP 来达到同样的效果。

当支持任意命名的section时,根据crtstuff.c代码被调用的区别有两种变体。在支持.init section(在程序启动时执行)的系统上,crtstuff.c的部分内容会被编译到这个section里。程序像这样被链接:

ld -o output_file crti.o crtbegin.o ... -lgcc crtend.o crtn.o

一个函数的prologue (__init) 出现在crti.o的 .init section 中 ;epilogue 出现在crtn.o中. 函数 __fini 在 .fini section的处理也一样. 正常情况下,这些文件由OS或GNU C库来提供,但是一些目标板是由GCC提供。

目标文件crtbegin.o 和crtend.o (大部分的情况下)是由crtstuff.c编译得到. 它们包含,除了别的以外,.init 和 .fini sections中的代码片段,用于跳转到 .text section中函数。链接器会将一个section的所有部分放在一起,来生成一个完整的__init函数来调用我们需要在启动阶段调用的函数。

为了使用这个变体,你必须正确的定义 INIT_SECTION_ASM_OP 宏。

如果init section不能使用,当GCC编译任何名为main的函数时(更精确点,任何被expand_main_function指定为程序入口点的函数),它在插入一个函数调用__main做为函数prologue后的第一段执行代码. __main 函数在 libgcc2.c 里被定义并执行全局的构造体。

不支持任意section的文件格式,同样也有两种变体。在最简单的变体里必须用到GNU 链接器(GNU ld)和‘a.out‘ 格式。这种情况下,TARGET_ASM_CONSTRUCTOR 被定义来生成一个类型为‘N_SETT‘的.stabs入口,参照__CTOR_LIST__, .stabs入口把指向初始化函数代码的void函数地址做为它的值。GNU链接器认为这是一个把值加到集合的请求;这些值会累积,最终放在可执行文件里做为一个向量,格式如前所述,有一个前导(被忽略)的count和一个末尾的0元素。TARGET_ASM_DESTRUCTOR 处理也类似。既然没有init
section可用,缺省的 INIT_SECTION_ASM_OP 使 main 的编译过程会去调用上述的__main函数,开始初始化处理。

最后的变体既不使用任意section也不用GNU 链接器。这在你想要动态链接且文件格式不被 GNU 链接器支持(如 ECOFF )的情况下推荐使用。在这种情况下,TARGET_HAVE_CTORS_DTORS 是错误的,初始和终止函数简单地通过它们的名称被识别。这个要求在链接阶段的使用一个叫 collect2 的额外程序。这个程序会假扮为链接器被 GCC 使用;它的工作是运行正常的链接器,也管理追加初始函数和终止函数的向量,这些函数通过上述的 __main
被调用。为了使用这个方法,必须在 config.gcc 里的 target 定义use_collect2。

GCC中初始化函数是如何被处理的?,布布扣,bubuko.com

时间: 2024-08-25 03:53:37

GCC中初始化函数是如何被处理的?的相关文章

GCC中初始化函数是怎样被处理的?

本文译至: http://gcc.gnu.org/onlinedocs/gccint/Initialization.html 如我们所知,在GCC通过给代码追加__attribute__((constructor))和__attribute__((destructor))的方式能够追加初始函数和终止函数, 这篇文章介绍了GCC内部是怎样实现上述处理的. 简单的说,就是在最常常的情况下,初始函数会被追加到.ctor section中,.init会调用相应的函数处理这些初始函数.终止情况类似. --

gcc attribute 初始化函数列表

gcc的__attribute__编译属性有很多子项,用于改变作用对象的特性.这里讨论section子项的作用. __attribute__的section子项使用方式为: ? 1 __attribute__((section("section_name"))) 其作用是将作用的函数或数据放入指定名为"section_name"的段. 看以下程序片段: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 2

ZeroMQ接口函数之 :zmq_msg_init_data - 从一个指定的存储空间中初始化一个ZMQ消息对象的数据

ZeroMQ 官方地址 :http://api.zeromq.org/4-1:zmq_msg_init_data zmq_msg_init_data(3) ØMQ Manual - ØMQ/3.2.5 Name zmq_msg_init_data - 从一个指定的存储空间中初始化一个ZMQ消息对象的数据 Synopsis typedef void (zmq_free_fn) (void *data, void *hint); int zmq_msg_init_data (zmq_msg_t *m

OC语言中的便利初始化函数和便利构造器

便利遍历初始化函数与便利构造器(以Student类为例): main函数 Student.h(声明) ................... Student.m(实现) .................

VC和gcc在保证函数static变量线程安全性上的区别

VC和gcc不同,不能保证静态变量的线程安全性.这就给我们的程序带来了很大的安全隐患和诸多不便.这一点应该引起我们的重视!尤其是在构造函数耗时比较长的时候,很可能给程序带来意想不到的结果.本文从测试代码开始,逐步分析原理,最后给出解决方案. 多线程状态下,VC不能保证在使用函数的静态变量的时候,它的构造函数已经被执行完毕,下面是一段测试代码: class TestStatic { public: TestStatic() { Sleep(1000*10); m_num = 999; } publ

[php-src]理解Php内核中的函数

内容均以php-5.6.14为例. 一. 函数结构 内核中定义一个php函数使用 PHP_FUNCTION 宏 包装,扩展也不例外,该宏在 main/php.h 第343行定义: 有着一系列类似以 PHP 命名的 Zend 宏包装器,它们是: /* PHP-named Zend macro wrappers */ /* 以PHP命名的Zend宏包装器 */ #define PHP_FN ZEND_FN #define PHP_MN ZEND_MN #define PHP_NAMED_FUNCTI

第3阶段——内核启动分析之start_kernel初始化函数(5)

内核启动分析之start_kernel初始化函数(init/main.c) stext函数启动内核后,就开始进入start_kernel初始化各个函数, 下面只是浅尝辄止的描述一下函数的功能,很多函数真正理解需要对linux相关体系有很深的了解后才能明白 代码如下: asmlinkage void __init start_kernel(void) { char * command_line; extern struct kernel_param __start___param[], __sto

结构体中定义函数指针

转自:http://blog.csdn.net/unix21/article/details/9293877 结构体指针变量的定义,定义结构体变量的一般形式如下: 形式1:先定义结构体类型,再定义变量 struct结构体标识符 { 成员变量列表;… }; struct 结构体标识符 *指针变量名; 变量初始化一:struct结构体标识符 变量名={初始化值1,初始化值2,…, 初始化值n }; 形式2:在定义类型的同时定义变量 struct结构体标识符 { 成员变量列表;… } *指针变量名;

GCC中的弱符号与强符号

我们经常在编程中碰到一种情况叫符号重复定义.多个目标文件中含有相同名字全局符号的定义,那么这些目标文件链接的时候将会出现符号重复定义的错误.比如我们在目标文件A和目标文件B都定义了一个全局整形变量global,并将它们都初始化,那么链接器将A和B进行链接时会报错: [html] view plain copy 1 b.o:(.data+0x0): multiple definition of `global' 2 a.o:(.data+0x0): first defined here 这种符号的