提问者:小点点

LLVM后端:替换x86后端的间接jmp


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

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

然后我想继续取代间接序列。

从我在谷歌搜索和文章中看到的,我可能可以通过修改llvm编译器编译器后端的x86asmprint来实现这一点。但是我不确定如何去做。任何帮助或阅读都将不胜感激。

注意:我的实际需求涉及间接跳转和pop,但我想从这个开始,以便在深入了解更多内容之前更多地了解后端。


共1个答案

匿名用户

我已经完成了我的项目。为了别人的利益发布我的方法。

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

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

我们可以通过将打印语句添加到指令发射/打印阶段来实现这一点。在完成大部分来自IR的主转换后,有一个AsmPrinter通过,它通过每个函数的基本块中的每个机器指令。这个主循环位于lib/CodeGen/AsmPrinter/AsmPrinter. cpp:AsmPrinter::EmitFunctionBody()。还有其他相关函数,如EmitFunctionEpilot ogue,EmitFunctionPrologue。这些函数最终为特定架构调用EmitIn的指令,例如: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

DAG将有jmp指令指向移动指令(可能还有它之前的其他节点),因为rax的值取决于mov指令。您可以将此处的Node替换为所需的Nodes。如果操作正确,它最终应该会更改最终指令。SelectionDAG代码位于lib/CodeGen/SelectionDAG/SelectionDAGISel. cpp。最好先四处逛逛,找出理想的更改位置。每个IR语句在DAG进行拓扑排序之前都会经历多次更改,以便指令处于线性序列中。可以使用llc--help-隐藏中的-view-dag*选项查看图表。在我的例子中,我只是在EmitInstruc中添加了一个特定的检查,并添加了代码来发送我想要的两个指令。

LLVM留档总是在那里,但是我发现伊莱·本德尔斯基的两篇文章比任何其他资源都更有帮助。LLVM指令的生活和更深层次LLVM代码生成。这些文章讨论了非常复杂的TableGen描述和指令匹配过程,如果你感兴趣的话,这有点酷。