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

链接器如何在链接时解析共享库中数据对象的引用?实例疑问

链接器相关疑问解答

问题背景

现有以下代码文件及编译流程:

main.c

#include "other.h"
extern int i;
int main() {
  ++i;
  inci();
  return 0;
}

other.c

int i = 0;
void inci() {
  ++i;
}

编译命令

gcc -c main.c
gcc -shared -fpic other.c -o libother.so
gcc -o main main.o ./libother.so

反汇编内容

  • main.o的部分反汇编:
f:   8b 05 00 00 00 00       mov    0x0(%rip),%eax        # 15 <main+0x15>
  15:   83 c0 01                add    $0x1,%eax
  18:   89 05 00 00 00 00       mov    %eax,0x0(%rip)        # 1e <main+0x1e>
  1e:   b8 00 00 00 00          mov    $0x0,%eax
  23:   e8 00 00 00 00          call   28 <main+0x28>
  • 可执行文件main的部分反汇编:
1148:   8b 05 ca 2e 00 00       mov    0x2eca(%rip),%eax        # 4018 <i@@Base>
    114e:   83 c0 01                add    $0x1,%eax
    1151:   89 05 c1 2e 00 00       mov    %eax,0x2ec1(%rip)        # 4018 <i@@Base>
    1157:   b8 00 00 00 00          mov    $0x0,%eax
    115c:   e8 cf fe ff ff          call   1030 <inci@plt>

对应C语句++i;的汇编显示,链接器直接用PC相对地址引用变量i,而非通过GOT(全局偏移表)。但共享库是运行时加载的,可执行文件链接时应该不知道i的地址,核心疑问:

  1. 链接器如何确定i位于0x4018?
  2. 注释中的i@@Base是什么意思?

解答

1. 链接器确定i地址的原因

这是GCC链接器的默认优化行为:将共享库中的全局变量i复制到了可执行文件的全局数据段,也就是俗称的COMDAT折叠或共享库变量预绑定。

具体逻辑是:

  • 变量i在other.c中是带初始化值的强符号(int i=0),链接器在处理可执行文件与共享库的符号关联时,会识别这类变量。
  • 为了减少GOT间接访问的开销,链接器会把这个变量从共享库中提取出来,放到可执行文件自身的.data段里,分配一个固定地址(也就是反汇编里的0x4018)。
  • 运行时,共享库libother.so中的i会被重定向到可执行文件里的这个副本,确保主程序和共享库访问的是同一个变量。

如果要强制让变量通过GOT访问,可以编译共享库时加-fno-semantic-interposition选项,或者链接时用-Bsymbolic参数。

2. i@@Base的含义

i@@Base里的Base指的是可执行文件自身的符号表(Base Symbol Table),也就是主程序的全局符号空间。这个注释明确表示:当前访问的i是属于可执行文件自己的符号,而非共享库中的原始符号。

对比反汇编里的inci@plt,@plt表示通过PLT(过程链接表)间接访问共享库中的函数;而i@@Base直接指向主程序数据段的固定地址,说明这个变量已经被链接器绑定到了主程序的地址空间中,不需要运行时再通过GOT解析。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 16:20:23