You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

glibc如何查找由全局构造函数生成的_GLOBAL__sub_I_*类函数?

关于glibc如何查找_GLOBAL__sub_I_*类全局构造函数的解释

首先明确:_GLOBAL__sub_I_*是GCC为每个包含全局构造函数的编译单元(.o文件)自动生成的包装函数——它的作用是把当前编译单元里所有的全局构造逻辑(比如C++全局对象构造、C中用__attribute__((constructor))标记的函数)打包成一个统一入口,方便运行时批量调用。

glibc找到并调用这些函数的核心逻辑,依赖链接器的段组织和运行时启动流程,和Linux辅助向量无关(辅助向量是内核传递给用户空间的加载信息,和全局构造的调用逻辑不沾边):

1. 链接阶段:把_GLOBAL__sub_I_*地址存入.init_array段

当你用GCC编译带全局构造的代码时,编译输出的汇编里会包含:

  • _GLOBAL__sub_I_*的函数定义,内部会调用当前编译单元的所有全局构造函数;
  • 将_GLOBAL__sub_I_*的地址写入.init_array段的指令(比如.section .init_array,"aw" + .quad _GLOBAL__sub_I_main)。

链接器(ld)会把所有编译单元的.init_array段合并成一个全局的函数指针数组,这个数组里就包含了所有_GLOBAL__sub_I_*的地址。

2. glibc启动阶段:遍历.init_array调用所有构造函数

程序被内核加载后,入口是glibc的_start函数,它会调用__libc_start_main——这个函数是glibc初始化运行时环境的核心,其中就包含了调用全局构造的逻辑:

  • __libc_start_main会调用__run_init_array函数;
  • __run_init_array会读取.init_array段的起始和结束地址,遍历数组里的每个函数指针并逐个调用——这就触发了所有_GLOBAL__sub_I_*函数的执行,进而完成所有全局对象的构造。

关于musl的补充

musl的逻辑和glibc本质一致:它同样依赖链接器生成的.init_array段,启动代码会直接遍历这个数组调用所有构造函数。musl文档没提_GLOBAL__sub_I_*是因为这个名字是GCC的实现细节,不属于libc的规范范畴——不管是glibc还是musl,只需要处理.init_array里的函数指针即可,不用关心这些指针指向的函数具体叫什么。

研究入手方向

  • 用gcc -S编译一个带全局构造的代码(比如C++全局对象、C的__attribute__((constructor))函数),查看生成的汇编代码,直接看_GLOBAL__sub_I_*的定义和它如何被关联到.init_array;
  • 用ld --verbose查看默认链接脚本,找到对.init_array段的处理规则;
  • 阅读glibc的__run_init_array函数实现(位于csu/libc-start.c),看它如何遍历.init_array;
  • 查看musl的启动代码(src/start/start.S),它的逻辑更简洁,能快速理解.init_array的调用流程。

内容的提问来源于stack exchange,提问作者user21677582

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 07:52:52