【问题标题】:Why does gcc by default link crtbegin.o and crtend.o to every compiled executable为什么 gcc 默认将 crtbegin.o 和 crtend.o 链接到每个编译的可执行文件
【发布时间】: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 旨在解决的问题,并且您可能同意也可能不同意这些列表是一个好的解决方案.
  • 我并不是说我在判断事情是如何实现的,我只是不明白其中的原因

标签: c gcc ld libc


【解决方案1】:

为什么库 init 和 fini 函数不直接调用库初始化和销毁​​函数,而不是在链接描述文件中列出它们并将部分的位分散到多个目标文件中?

他们不能直接调用函数,因为他们不知道要调用的函数。为了知道这一点,它们必须在链接时生成,本质上就是它们:由链接器连接同名部分生成。

但是,所有这些都是,或者至少应该是无关紧要的遗留废话。几十年来,带有__{init,fini}_array_{start,end} 符号或指向其中的DT_{INIT,FINI}_ARRAY _DYNAMIC 标记的.{init,fini}_array 部分,由libc/ldso 中提供的代码迭代,一直是执行ctors/dtors 的正确方法。但是 GCC 启动文件 (crt{begin,end}*.o) 仍然存在,在初始化它们(已删除)的 Java 运行时、事务内存库、展开表等时会做一些乱七八糟的事情。

【讨论】:

  • 等待是由 libc 内部的 __init / __fini 部分调用以初始化 c 库的 CTOR 和 DTOR 函数,或者这些函数是链接器的所有库的开始和结束例程的链接时间列表正在链接吗?
  • _init_fini 不再用于ctors/dtors。相反,.init_array.fini_array 部分由代码迭代。它们每个都包含一个函数指针数组,供 ctors/dtors 执行。
  • __init 和 __fini 是做什么用的,或者它们是退化的功能?
  • 他们仍然从 GCC 的启动文件 (crt{begin,end}*.o) 中获取一些片段,用于调用 Java 运行时初始化、事务内存仿真初始化、展开初始化等,以及调用 *legacy __do_global_[cd]tors_aux 函数,但是这些函数会迭代旧的 .ctors/.dtors 数组,这些数组不与现代工具一起使用。 .init_array/.fini_array 的迭代在别处。
  • 您能推荐一些关于这个主题的进一步阅读吗?我有兴趣学习,但出于法律原因不愿深入研究 glibc / gcc 源代码,我不想意外抄袭 gpl 代码,因为我计划最终以更简单的许可证发布产品。
猜你喜欢
  • 2019-06-21
  • 2019-04-29
  • 1970-01-01
  • 2010-11-16
  • 1970-01-01
  • 2012-04-15
  • 1970-01-01
  • 1970-01-01
  • 2019-01-30
相关资源
最近更新 更多