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

LLVM后端:替换x86后端的间接JMP,x86,llvm,code-generation,codegen,X86,Llvm,Code Generation,Codegen,我想将代码中的间接jmp*(eax)指令替换为mov*(eax),ebx;用于x86可执行文件的jmp*ebx 在实现这一点之前,我想让LLVM编译器在每次检测到jmp*(eax)指令时通过添加一些print语句来记录输出 然后我想继续替换间接序列 从我在谷歌搜索和文章中看到的情况来看,我可能可以通过修改llvm后端中的x86asmprinter来实现这一点。但我不知道该怎么做。 任何帮助或阅读都将不胜感激 注意:我的实际需求涉及间接跳转和弹出,但我想先从这一点开始,以便在深入了解更多内容之前对

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

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

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

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


注意:我的实际需求涉及间接跳转和弹出,但我想先从这一点开始,以便在深入了解更多内容之前对后端有更多的了解。

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

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

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

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

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

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

LLVM文档始终存在,但我发现Eli Bendersky的两篇文章比其他任何资源都更有帮助。和。这些文章讨论了非常复杂的TableGen描述和指令匹配过程,如果您感兴趣的话,这很酷

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