为什么链接类型会影响同节内相对跳转是否需要重定位?
问题核心原因:外部链接符号的不确定性
两个函数的链接属性差异直接决定了重定位需求:
compute2被static修饰,属于内部链接,作用域被限制在当前编译单元内。编译器在编译阶段可以100%确定这个函数不会被其他编译单元的同名符号覆盖、也不会被导出到外部,因此可以直接计算出main到compute2的相对偏移,直接写入call指令的操作数字段,不需要额外重定位。compute是默认的外部链接全局符号,即便当前定义和main在同一个编译单元的同一节,编译阶段依然无法确定它最终的运行时地址,必须留重定位项交给链接器处理,具体原因有两点:- 全局符号抢占规则:ELF链接规范中,全局符号默认允许被抢占:如果最终链接时存在其他目标文件、动态库也定义了同名的
compute全局符号,链接器会选择优先级更高的定义,而不是当前编译单元内的这个compute。这种情况下main和compute就不再是同一节内的相邻函数,原来的相对偏移自然失效。 - 链接优化的兼容性:即便最终确实用了当前编译单元的
compute定义,链接阶段的多种优化也可能改变它的相对位置,比如开启--gc-sections垃圾回收未使用节、函数级重排优化、LTO链接时优化等操作都可能调整符号的最终位置,编译阶段无法提前获知最终偏移。
- 全局符号抢占规则:ELF链接规范中,全局符号默认允许被抢占:如果最终链接时存在其他目标文件、动态库也定义了同名的
你提到的「同一节内相对地址固定」的结论只有在确定最终使用当前节内的符号定义时才成立,而外部链接符号的定义本身是不确定的,因此必须通过重定位让链接器在确定最终符号地址后再修正调用地址。
你可以做两个简单验证:
- 给
compute添加static修饰,它就会变成内部链接,编译后就不会再生成compute相关的重定位项 - 给
compute添加__attribute__((visibility("hidden")))属性,告诉链接器这个全局符号不会被外部库抢占,编译器也会直接计算相对偏移,不需要重定位
内容的提问来源于stack exchange,提问作者knatten
相关产品推荐
相关产品推荐

