三种.so文件符号重定位机制差异分析及猜想验证
三种.so文件中符号重定位的工作机制与差异解析
先把三个示例代码清晰列出来,方便对照分析:
示例代码
- 全局非static变量:
int myglob = 42; int fun(int a, int b) { return myglob + a + b; }
- 静态全局变量:
static int myglob = 42; int fun(int a, int b) { return myglob + a + b; }
- extern声明变量:
extern int myglob; int fun(int a, int b) { return myglob + a + b; }
各场景的重定位细节与差异
场景2:static全局变量
你的猜想完全命中!static把myglob限定为文件内部链接的符号,它的定义和使用都在同一个目标文件里,链接器在处理时能直接确定它在.data段的偏移地址。
编译阶段,编译器会生成基于指令指针(IP/RIP)的相对偏移访问——简单说就是用rip + 固定偏移的方式直接读取myglob,全程不需要GOT(全局偏移表)介入。这种方式效率最高,属于直接寻址,没有额外的间接加载开销。
场景1:全局非static变量(默认外部链接)
这里你的猜想有个小细节需要补充:它的行为不是完全等同于场景3,而是分两种情况:
- 如果这个
.so是独立编译,且没有其他模块需要访问myglob:虽然myglob的符号属性是外部链接,但因为没有外部引用,编译器会自动优化成类似场景2的IP相对偏移访问,不需要GOT。 - 如果这个
.so被其他程序/模块引用了myglob:此时myglob是全局可见的符号,链接器会把它放入GOT表。因为动态链接时,myglob的地址可能被其他模块重定位,当前.so需要通过GOT来确保访问到最新的全局值。不过现代编译器默认优化下,只要myglob只在当前.so内部使用,即使是外部链接也会被优化为直接寻址,除非你手动禁用优化。
场景3:extern声明的变量
这部分你猜的完全正确!extern明确告诉编译器:myglob的定义在其他模块(可能是另一个.so或者主程序)。编译时编译器不知道它的具体地址,所以会生成指向GOT表项的间接访问。
动态链接阶段,加载器会找到myglob的实际内存地址,写入GOT对应的表项。当fun执行时,会先从GOT加载myglob的地址,再读取它的值——这是动态链接下跨模块访问全局变量的标准方式。
你的猜想验证总结
- 场景2和场景3的猜想:完全正确,和实际的重定位机制完全匹配。
- 场景1的猜想:部分正确,但存在细节差异——默认外部链接的全局变量并非一定和场景3一致,只有在被外部模块引用时才会使用GOT,否则会被优化为直接寻址。
内容的提问来源于stack exchange,提问作者ray an
相关产品推荐
相关产品推荐

