【发布时间】:2020-06-22 23:06:43
【问题描述】:
为了了解 POSIX libc 实现的来龙去脉,在过去一年左右的时间里,我一直在通过蚕食我认为在我的技能范围内的单一 Unix 规范实现功能的边缘来制作自己的试图制定一个前进的计划,我已经到了下一步是创建一个运行时库的地步。
现在我试图了解程序如何链接到 c 运行时库背后的逻辑。
阅读 gcc 手册中关于初始化以及 elf 格式文本和嵌入式系统上的一些文本的内容,我基本上了解到初始化代码进入它自己的 .init 和 .fini 部分,即 crti.o和 crtn.o 通常包含函数存根,这些存根是 crtbegin 和 crtend,通过调用链接描述文件应该定义的函数指针列表中指定的一系列函数来充实。
假设我上面的总结是正确的,这引出了我的问题,以这种方式设计 init 和 fini 函数背后的逻辑是什么,具体如下所述:
函数的序言(__init)出现在crti.o的.init部分;结语出现在 crtn.o 中。对于 .fini 部分中的函数 __fini 也是如此。通常这些文件由操作系统或 GNU C 库提供,但由 GCC 为少数目标提供。对象 crtbegin.o 和 crtend.o(对于大多数目标)是从 crtstuff.c 编译的。除其他外,它们还包含 .init 和 .fini 部分中的代码片段,这些代码片段分支到 .text 部分中的例程。链接器会将一个节的所有部分拉到一起,从而生成一个完整的 __init 函数,该函数会在启动时调用我们需要的例程。
为什么库 init 和 fini 函数不直接调用库初始化和销毁函数,而不是在链接描述文件中列出它们并将部分的位分散到多个目标文件中?
链接器列表中列出的函数也是从哪里来的。从下面引用的引用中,我假设这些函数特定于 crt,以将 std[in,out,err] 文件描述符与文件流相关联或将 locale_t 变量初始化为 C 语言环境等,如果是这样的话存储的函数。
链接器必须构建这些函数的两个列表——一个初始化函数列表,称为CTOR_LIST,和一个终止函数列表,称为DTOR_LIST。
【问题讨论】:
-
C 标准以
main()开始,以exit()结束(或任何其他等价物)。在main()之前和exit()之后需要发生的所有事情都是标准没有指定或关心的实现细节。这些细节取决于运行代码的系统,GCC 支持许多不同的系统。将您的实现移植到 16 个不同的系统后,您将开始了解 CTOR_LIST 和 DTOR_LIST 旨在解决的问题,并且您可能同意也可能不同意这些列表是一个好的解决方案. -
我并不是说我在判断事情是如何实现的,我只是不明白其中的原因