链接前汇编call指令add_two_num+0x9含义及链接阶段作用
未链接目标文件call指令偏移问题解答
问题背景
研究链接前汇编语言的函数调用机制时,基于GCC编译C语言代码开展验证,测试用C函数的逻辑为调用外部函数实现两数相加,将结果写入入参指针指向的变量,源码如下:
void add_two_num(int x, int y, int * dest) { int val; val = dummy(x, y); *dest = val; }
将源码编译为未执行链接操作的elf64-x86-64格式目标文件后,使用objdump -d对目标代码反汇编,得到如下汇编结果:
file format elf64-x86-64 0000000000000004 <add_two_num>: 4: 53 push %rbx 5: 48 89 d3 mov %rdx,%rbx 8: e8 00 00 00 00 call d <add_two_num+0x9> d: 89 03 mov %eax,(%rbx) f: 5b pop %rbx 10: c3 ret
待解答的两个核心问题:
- 反汇编行
call d <add_two_num+0x9>中的+0x9代表什么含义? - 后续链接阶段该行会被修正为实际函数调用地址,这个偏移值在链接阶段是否具备实际作用?
问题解答
1. +0x9的实际含义
这个值是objdump反汇编时计算生成的临时假目标标记,不存在任何实际代码语义:
- x86-64架构下,
e8开头的近过程调用指令固定长度为5字节:1字节操作码+4字节PC相对跳转偏移。这条call指令的起始地址是0x8,CPU执行完这条指令时,程序计数器(即PC相对跳转的计算基准地址)正好是0x8 + 5 = 0xd,也就是call指令后紧邻的mov指令的地址。 - 未链接的目标文件中,call指令携带的4字节偏移全为0,是留给链接器填充的占位符。objdump反汇编时按照"基准地址 + 指令内存储的偏移值"规则计算跳转目标,得到的结果就是
0xd + 0 = 0xd。这个地址相对于add_two_num函数起始地址0x4的偏移量为9,因此objdump给这个计算出来的假跳转目标标记为<add_two_num+0x9>,并非代码真的要跳转到函数内偏移0x9的位置。
2. 该偏移值在链接阶段的作用
这个显示出来的+0x9标记在链接阶段没有任何实际作用:
- 首先这个标记只是反汇编工具生成的提示文本,根本不存在于目标文件的二进制内容中,链接器完全感知不到这个值的存在。
- 链接器处理这条call指令时,只会读取目标文件重定位表中存储的正式信息:待跳转的符号名
dummy、重定位类型(x86-64下对应PC相对调用的重定位类型)、待修正的指令位置、跳转偏移计算的基准地址(即call后下一条指令的地址),基于这些信息计算出真实的跳转偏移,替换掉指令中全0的占位符即可。 - 链接完成后再反汇编,call指令就会显示正确的跳转目标,这个临时的
+0x9标记也会随之消失。
内容的提问来源于stack exchange,提问作者arka
相关产品推荐
相关产品推荐

