Intel XED解码同一条指令时得到不同函数调用地址的问题
问题场景
测试用C代码如下:
#include <stdio.h> void greet(void){ printf("Hello!\n"); } int main (){ greet(); return 0; }
使用gcc将上述代码编译为名为hello的64位ELF可执行文件,开展两组XED解码实验:
- 第一组解码整个.text段,执行命令:
其中对./xed -i hellogreet函数的调用指令解码结果为:
结果符合预期,XDIS 1168: CALL BASE E8DCFFFFFF call 0x1149 <greet>0x1149确实是greet函数的入口地址。 - 第二组仅单独解码上述call指令的十六进制编码
E8DCFFFFFF,执行命令:
得到输出:./xed -d E8DCFFFFFFICLASS: CALL_NEAR CATEGORY: CALL EXTENSION: BASE IFORM: CALL_NEAR_RELBRz ISA_SET: I86 ATTRIBUTES: FIXED_BASE0 MPX_PREFIX_ABLE SCALABLE STACKPUSH0 SHORT: call 0xffffffe1
预期解码结果为call 0x1149,实际输出为call 0xffffffe1。
原因说明
- x86_64架构下,操作码
0xE8对应相对近调用指令,属于位置相关指令:它的操作数不是目标地址的绝对值,而是以「当前call指令的下一条指令地址」为基准的32位有符号相对偏移,跳转目标的计算规则为:目标绝对地址 = call指令起始地址 + 指令长度(固定5字节) + 32位有符号相对偏移 - 整文件反汇编时,XED可以从ELF的段信息中拿到.text段的加载基址,明确知道这条call指令的起始地址是
0x1168,因此能算出正确目标:- 指令编码
E8DCFFFFFF中,首字节E8是操作码,后续4字节DC FF FF FF是小端序存储的32位有符号偏移,按补码解析为十进制值-36 - call指令的下一条指令地址为
0x1168 + 5 = 0x116D - 代入公式计算目标地址:
0x116D + (-36) = 0x1149,和greet函数入口地址完全匹配。
- 指令编码
- 单独解码十六进制串时,XED没有任何文件格式提供的地址上下文,不知道这条指令实际会被加载到哪个内存地址运行,只能默认将指令起始地址设为
0来计算:- 默认下一条指令地址为
0 + 5 = 5 - 代入公式计算目标地址:
5 + (-36) = -31,将这个有符号值转为32位无符号表示就是0xffffffe1,和单独解码的输出完全一致。
- 默认下一条指令地址为
本质是单独解码指令字节流时,缺失了指令的实际加载地址这一必要上下文,自然无法算出正确的绝对跳转目标,只能给出基于0基址的计算结果。
内容的提问来源于stack exchange,提问作者Farzam
相关产品推荐
相关产品推荐

