i386与x86-64平台多ELF重定位行为差异原因探究
同一地址ELF重定位行为差异的原因分析
先看测试代码和实际现象:
汇编代码
#ifdef __x86_64__ #define R(N) R_X86_64_##N #else #define R(N) R_386_##N #endif .globl foo foo: .int 0 .reloc foo, R(32), bar .reloc foo, R(32), baz
C代码
#include <stdio.h> __asm__( ".globl bar\n\t" "bar = 0x42\n\t" ".globl baz\n\t" "baz = 0x57" ); extern unsigned foo; int main(void) { printf("%X\n", foo); return 0; }
测试结果:
- 32位x86环境下用GCC编译链接,输出
99(0x42+0x57),两个重定位累加生效; - x86-64环境下编译链接,输出
57,仅最后一个重定位生效。
原因解析
这本质是不同架构ELF重定位类型的语义设计差异:
- 32位x86的
R_386_32重定位类型,链接器实现严格遵循System V gABI的“连续重定位组合生效”规则,每个重定位都会把符号值累加到目标地址的当前值上。foo初始为0,先加bar的0x42,再加baz的0x57,最终得到0x99。 - x86-64的
R_X86_64_32重定位类型,其设计语义是生成单个32位绝对地址绑定,链接器默认认为同一地址的多个该类型重定位是冗余输入,因此采用覆盖式处理——只保留最后一次重定位的结果,直接用baz的0x57覆盖foo的初始值,所以输出57。
虽然AMD64 psABI没有明确提及这个行为,但GCC、Clang搭配ld、gold等主流链接器都统一遵循这个实现逻辑,属于架构特定重定位类型的隐含处理规则。
内容的提问来源于stack exchange,提问作者Alex Shpilkin
相关产品推荐
相关产品推荐

