__CTOR_LIST__和__DTOR_LIST__解释

本文译至:

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。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]: C语言中的主函数固定名为:main(),每个系统中必须有一个,且只能有一个。它是第一个运行的函数。一般情况下,main()写在文件的最后面,其他被调用的函数放在前面,这样定义函数之前可以不加声明,如果放在主函数之后,则必须先声明,再定义,再被调用。\[1\] 引用\[2\]: Main()函数在C#里非常特殊,它是编译器规定的所有可执行程序的入口点。由于其特殊性,对Main()函数我们有以下几条准则:Main()函数必须封装在类或结构里来提供可执行程序的入口点。Main()函数必须为静态函数(static)。这允许C#不必创建实例对象即可运行程序。Main()函数保护级别没有特殊要求,public,protected,private等都可,但一般我们都指定其为public。Main()函数名的第一个字母要大写,否则将不具有入口点的语义。Main ()函数的参数只有两种参数形式:无参数和string 数组表示的命令行参数,即static void Main()或static void Main(string\[\]args),后者接受命令行参数。一个C#程序中只能有一个Main()函数入口点。其他形式的参数不具有入口点语义,C#不推荐通过其他参数形式重载Main()函数,这会引起编译警告。Main()函数返回值只能为void(无类型)或int(整数类型)。其他形式的返回值不具有入口点语义。\[2\] 引用\[3\]: 我们粗略的感受是它很类似于早先的汇编语言,但它具有了对象定义和操作的功能。我们可以看到它定义并实现了一个继承自System.Object的HelloWorld类及两个函数:Main()和.ctor()。其中.ctor()是HelloWorld类的构造函数,可在"HelloWorld.cs"源代码中我们并没有定义构造函数呀--是的,我们没有定义构造函数,但C#的编译器为我们添加了它。你还可以看到C#编译器也强制HelloWorld类继承System.Object类,虽然这个我们也没有指定。\[3\] 问题: __main()函数是什么? 回答: 在C语言中,主函数的名字是固定的,为main()。它是每个系统中必须有且只能有一个的函数,也是程序的入口点。\[1\]而在C#中,主函数的名字也是固定的,为Main()。它是编译器规定的所有可执行程序的入口点,必须封装在类或结构里来提供可执行程序的入口点。\[2\]至于__main()函数,根据提供的引用内容,我没有找到与之相关的信息。请确认是否有误。 #### 引用[.reference_title] - *1* [快速了解main()函数](https://blog.csdn.net/lengxinxuexic/article/details/81324632)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Main函数详解](https://blog.csdn.net/imbiz/article/details/5631843)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值