gcc会向Linux ELF添加哪些功能?

36

当使用gcc将类似hello-world的C(或ASM)程序进行链接时,它会将一些内容添加到最终的可执行目标文件中。我只知道有运行时动态链接器和_start入口点,但这些添加的函数是什么类型呢?

00000000004003f0 t deregister_tm_clones
0000000000400430 t register_tm_clones
0000000000400470 t __do_global_dtors_aux
0000000000400490 t frame_dummy
00000000004004e0 T __libc_csu_init
0000000000400550 T __libc_csu_fini
0000000000400554 T _fini
0000000000600668 t __frame_dummy_init_array_entry
0000000000600668 t __init_array_start
0000000000600670 t __do_global_dtors_aux_fini_array_entry
0000000000600670 t __init_array_end

它们是什么?有什么作用?是否有相关描述?谷歌搜索无果。

7
请参考 Patrick Horgan 的《Linux x86 程序启动》(http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html)。注意,没有使用 libc 的纯汇编代码不会添加这些内容,因为它们来自于 libc。 - Jester
@Jester看起来非常不错,谢谢!如果它包含我要求的任何内容,您可以用这些信息回答(当然还需要一些信息)。 - Victor Polevoy
1个回答

42
大部分是在“main”程序之前或之后执行代码的方法,其中大部分都存在于文件中(https://github.com/gcc-mirror/gcc/blob/master/libgcc/crtstuff.c)。它们存在的目的是为了支持各种类C编程语言的特性,但也可以在C中使用。这可能看起来过于复杂,因为其中一些代表着遗留负担,而另一些则是为了支持GCC运行的各种不同架构所需的变化。
从您的列表中,逐个介绍(或两个一组):
00000000004003f0 t deregister_tm_clones
0000000000400430 t register_tm_clones

Transactional memory旨在让使用线程编程更加简单。它是一种替代基于锁的同步的方法。这些例程分别拆除和设置库(libitm)使用的表格,支持这些函数。更多有关TM的信息请参见https://gcc.gnu.org/wiki/TransactionalMemoryhttp://pmarlier.free.fr/gcc-tm-tut.html

 

0000000000400470 t __do_global_dtors_aux

在不支持.fini_array的系统中,程序退出时会运行所有全局析构函数。
0000000000400490 t frame_dummy

这个函数位于.init部分。它被定义为void frame_dummy ( void ),其存在的意义在于调用__register_frame_info_bases,该函数有参数。显然,从.init部分调用带参数的函数可能不可靠,因此需要使用这个函数,以便__register_frame_info_bases不会直接从.init section调用。 .eh_frame信息基础用于异常处理和类似功能(例如使用__attribute__((cleanup(..)))声明的函数)。
00000000004004e0 T __libc_csu_init
0000000000400550 T __libc_csu_fini

这些代码运行程序级别的初始化和终结器(类似于整个程序的构造函数/析构函数)。 如果您定义了以下函数:
void __attribute__ ((constructor)) mefirst () {
    /* ... do something here ... */
}

void __attribute__ ((destructor)) melast () {
    /* ... do something here ... */
}

这些程序将会在main()函数之前和之后被调用。

另请参阅https://gcc.gnu.org/onlinedocs/gccint/Initialization.html

0000000000400554 T _fini

这是一种已经过时的运行程序级(实际上是对象文件级)析构函数的方式(关于此的一些信息可以在man dlclose中找到)。

相应的构造函数的过时函数是__init

0000000000600668 t __frame_dummy_init_array_entry
0000000000600668 t __init_array_start

这些标记表示.init_array段的结束和开始,该段包含指向所有程序级别初始化器的指针(参见上文中的__libc_csu_init)。

 

0000000000600670 t __do_global_dtors_aux_fini_array_entry
0000000000600670 t __init_array_end

这些标记标志着.fini_array段的结束和开始,其中包含指向所有程序级终结器(请参见上面的__libc_csu_fini)的指针。 [编辑] 一些附加说明:
  • 链接http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html (wayback machine)来自Jester的问题评论,其中包含一个漂亮的图表和一个小样例程序,展示了这些内容的整体顺序以及如何从C语言中访问这些特性。

  • 'ctors'和'dtors'这些术语是'constructors'和'destructors'的缩写。

  • 全局构造函数/析构函数和目标文件构造函数/析构函数之间的区别最明显的时候是当您的程序由多个目标文件构建而成时。

  • 标记为“T”(__libc_csu_init,__libc_csu_fini,_fini)的符号是“全局”的(外部可见),其余符号(标记为“t”)则不是。


请问您能否添加一些内容来澄清“全局析构函数”、“程序级别初始化器和终结器”是什么,这样会让我的回答更加完整。 - Victor Polevoy
1
每个共享对象文件,而不是每个对象文件。 - o11c

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接