You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.03 15:12:32