无需内联汇编:如何用LLVM的BuildMI生成指向0x40010的CALL指令?
问题描述
我尝试调用地址0x40010,但LLVM似乎在通过自身计算修正该地址。请问能否在不使用内联汇编的情况下禁用这一行为?
我当前用于实现该需求的代码如下,但其无法生成预期的call 0x40010指令:
uintptr_t imm = 0x40010; const GlobalValue* GV = M->getNamedValue(MF->getName()); const MachineInstrBuilder& MIB = BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(X86::CALL64pcrel32)); MIB.addGlobalAddress(GV, imm, MCSymbolRefExpr::VK_None);
生成的MIR结果为:
CALL64pcrel32 @test + 262160, implicit $esp, implicit $ssp
当我使用以下代码时:
const MachineInstrBuilder& MIB = BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(X86::CALL64pcrel32)); MIB.addImm(imm);
生成的结果为:
CALL64pcrel32 262160, implicit $esp, implicit $ssp
但编译时会报错:Assertion failed: Target.getSymA() && "Relocation must reference a symbol!"
解决方案
问题根源
X86架构的CALL64pcrel32指令依赖PC相对重定位机制,LLVM对该指令的操作数有严格要求:必须关联一个符号(而非直接传入立即数),这就是直接使用addImm触发断言失败的原因。而你第一次尝试的addGlobalAddress用法,LLVM会将传入的偏移量视为目标全局符号的相对偏移,而非绝对地址,因此生成了@test + 262160的结果。
实现方式
要生成指向绝对地址0x40010的调用,你需要创建一个绑定到该地址的符号,再让LLVM生成指向该符号的调用指令,具体代码如下:
- 创建并绑定绝对地址的临时符号:
// 获取当前模块的MC上下文 MCContext &Ctx = M->getContext(); // 创建临时符号用于标记目标地址 MCSymbol *TargetAddrSym = Ctx.createTempSymbol(); // 通知汇编器将该符号绑定到绝对地址0x40010 M->getContext().getStreamer()->emitAbsoluteSymbol(TargetAddrSym, 0x40010);
- 构建CALL指令并引用该符号:
BuildMI(MBB, &MI, MI.getDebugLoc(), TII->get(X86::CALL64pcrel32)) .addSym(TargetAddrSym);
这样LLVM会生成指向该绝对符号的PC相对调用,最终汇编器会将其解析为预期的call 0x40010指令。
内容的提问来源于stack exchange,提问作者Kashikoi Clev-san
相关产品推荐
相关产品推荐

