如何在LLVM后端向二进制文件插入全局汇编蹦床?
LLVM插入无序言/尾声的蹦床函数问题解决
你的当前方向存在核心问题,导致蹦床没有被保留到最终二进制中:
- 手动创建IR函数
__trampoline的同时,又通过内联ASM定义同名符号,两者冲突,LLVM会忽略其中一个 appendToUsed无法保证符号不被链接器丢弃,尤其是IR中没有实际调用该函数时
以下是两种可行的正确实现方式:
方法一:纯内联ASM + 强制保留符号
无需手动创建IR函数,直接通过内联ASM定义蹦床,同时确保链接器不会丢弃符号:
static constexpr StringLiteral TrampolineAsm = R"ASM( .section .text.__trampoline,"axG",@progbits,__trampoline .globl __trampoline .type __trampoline,@function .align 2 __trampoline: # 替换为你的蹦床指令,示例为x86_64间接跳转逻辑 jmp *%rdi .size __trampoline, .-__trampoline # 强制LLVM和链接器保留该符号 .pushsection .llvm.used,"aw",@progbits .quad __trampoline .popsection )ASM"; struct TrampolineModule : ModulePass { static char ID; TrampolineModule() : ModulePass(ID) {} bool runOnModule(Module& M) override { if (M.getNamedValue("__trampoline")) { return false; } M.appendModuleInlineAsm(TrampolineAsm); return true; } }; char TrampolineModule::ID = 0; static RegisterPass<TrampolineModule> X("trampoline-pass", "Insert trampoline function");
- 将
.weak改为.globl,确保符号全局可见 - 添加
.llvm.used段,替代appendToUsed的作用,强制保留符号 - 移除手动创建的IR函数,避免符号冲突
方法二:MachineFunctionPass直接生成机器码(更灵活)
如果需要根据目标架构动态调整蹦床指令,可直接在MachineFunctionPass中生成机器码,完全跳过LLVM的函数序言/尾声生成逻辑:
struct TrampolineMachine : MachineFunctionPass { static char ID; TrampolineMachine() : MachineFunctionPass(ID) {} bool runOnMachineFunction(MachineFunction& MF) override { static bool created = false; if (created) return false; created = true; // 创建对应蹦床的MachineFunction auto& ctx = MF.getFunction().getContext(); auto* func = Function::Create( FunctionType::get(Type::getVoidTy(ctx), {Type::getInt64Ty(ctx)}), GlobalValue::ExternalLinkage, "__trampoline", MF.getFunction().getModule() ); MachineFunction* trampolineMF = MF.getFunction().getContext().getModule()->getOrCreateMachineFunction(func); // 配置蹦床的代码段和对齐 trampolineMF->setSection(".text.__trampoline"); trampolineMF->setAlignment(Align(2)); // 添加蹦床机器指令(以x86_64为例) auto& TII = *trampolineMF->getSubtarget().getInstrInfo(); MachineBasicBlock* MBB = trampolineMF->CreateMachineBasicBlock(); trampolineMF->push_back(MBB); BuildMI(MBB, DebugLoc(), TII.get(X86::JMP64r)).addReg(X86::RDI); // 禁止生成序言/尾声及其他优化 func->addFnAttr(Attribute::NoProfiler); func->addFnAttr(Attribute::NoInline); func->addFnAttr(Attribute::NoUnwind); // 强制保留符号 appendToUsed(*func->getModule(), {func}); return true; } }; char TrampolineMachine::ID = 0; static RegisterPass<TrampolineMachine> X("trampoline-machine-pass", "Insert trampoline via machine code");
- 直接操作机器码,完全控制蹦床的指令内容
- 适合需要跨架构适配的场景
关键注意事项
- 禁止同时用内联ASM和IR函数定义同一个符号,避免冲突
- 必须通过
.llvm.used或appendToUsed强制保留符号,防止链接器丢弃 - 若使用IR函数,需添加
noprofiler、noinline等属性,确保LLVM不生成额外的序言/尾声代码
内容的提问来源于stack exchange,提问作者ebeneEinHalb
相关产品推荐
相关产品推荐

