【问题标题】:LLVM Backend : Replacing indirect jmps for x86 backendLLVM 后端:替换 x86 后端的间接 jmps
【发布时间】:2015-06-26 10:59:51
【问题描述】:

对于 x86 可执行文件,我想将代码中的间接 jmp *(eax) 指令替换为 mov *(eax),ebx; jmp *ebx

在实现这个之前,我想制作 LLVM 编译器,通过添加一些打印语句,在每次检测到 jmp *(eax) 指令时记录一个输出。

然后我想继续替换间接序列。

从我从谷歌搜索和文章中看到的,我可能可以通过修改 llvm 后端中的 x86asmprinter 来实现这一点。但我不知道该怎么做。 任何帮助或阅读将不胜感激。

注意:我的实际需求涉及间接跳转和弹出,但我想从这个开始以了解更多后端,然后再深入研究。

【问题讨论】:

    标签: x86 llvm code-generation codegen


    【解决方案1】:

    我完成了我的项目。发布我的方法以造福他人。

    LLVM 后端的主要功能是转换中间表示 最终可执行文件取决于目标架构和其他 规格。 LLVM 后端本身由几个阶段组成 目标特定优化、指令选择、调度和指令 发射。这些阶段是必需的,因为 IR 是一种非常通用的表示形式,并且 需要大量修改才能最终将它们转换为目标特定的可执行文件。

    1)每次编译器生成jmp *(eax)时记录

    我们可以通过在指令发出/打印阶段添加打印语句来实现这一点。在完成大部分来自 IR 的主要转换后,有一个 AsmPrinter 通道,它通过每个函数的基本块中的每个机器指令。这个主循环位于lib/CodeGen/AsmPrinter/AsmPrinter.cpp:AsmPrinter::EmitFunctionBody()。还有其他相关函数,如 EmitFunctionEpilogue、EmitFunctionPrologue。这些函数最终为特定架构调用 EmitInstruction,例如:lib/Target/X86/X86AsmPrinter.cpp。如果您稍微修改一下,您可以调用 MI.getOpcode() 并将其与为架构定义的枚举进行比较以打印日志。

    例如对于在 X86 中使用寄存器的跳转,它是 X86::JMP64r。您可以使用 MI.getOperand(0) 等获取关联的寄存器。

    if(MI->getOpcode() == X86::JMP64r)
    dbgs() << "Found jmp *x instruction\n";
    

    2)替换指令 所需的更改因您需要的更换类型而异。如果您需要有关寄存器或先前指令的更多上下文,我们将需要在 Pass 链中实现更高的更改。有一种指令表示称为选择 DAG(有向无环图),它存储每条指令与前一条指令的依赖关系。例如,在序列中

    mov myvalue,%rax
    jmp *rax
    

    由于 rax 的值取决于 mov 指令,因此 DAG 的 jmp 指令将指向 move 指令(可能还有其他节点)。您可以将此处的节点替换为所需的节点。如果做得正确,它最终应该会改变最终的指令。 SelectionDAG 代码位于lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp。总是最好先四处寻找,找出改变的理想场所。在对 DAG 进行拓扑排序之前,每个 IR 语句都会经历多次更改,从而使指令处于线性序列中。图表可以查看 使用在llc --help-hidden 中看到的 -view-dag* 选项。 就我而言,我只是在 EmitInstruction 中添加了一个特定的检查,并添加了代码来 Emit 我想要的两条指令。

    LLVM 文档总是在那里,但我发现 Eli Bendersky 的两篇文章比任何其他资源都更有帮助。 Life of LLVM InstructionDeeper look into LLVM Code Generation。这些文章讨论了非常复杂的 TableGen 描述和指令匹配过程,如果您有兴趣,这很酷。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多