如何反汇编 LLVM MCJIT 编译的结果?
How can I disassemble the result of LLVM MCJIT compilation?
我有一个我编写的程序,它使用 LLVM 3.5 作为 JIT 编译器,我正在尝试更新它以在 LLVM 3.7 中使用 MCJIT。我大部分时间都在使用它,但我正在努力重现我用 LLVM 3.5 实现的一项仅调试功能。
我希望能够看到由 JIT 进程生成的主机机器代码(例如 x86、x64 或 ARM,而不是 LLVM IR);在调试版本中,我将其注销,因为我的程序是 运行。使用 LLVM 3.5,我能够通过调用 ExecutionEngine::runJITOnFunction() 来填充 llvm::MachineCodeInfo 对象来完成此操作,这为我提供了生成的起始地址 和大小 代码。然后我可以反汇编该代码。
我似乎无法在 MCJIT 中找到任何等效项。我可以获得函数的起始地址(例如通过 getPointerToFunction())但不是大小。
我看过Disassemble Memory但是除了答案中没有那么详细之外,似乎更多的是关于如何反汇编字节序列。我知道该怎么做,我的问题是:我怎样才能首先掌握字节序列?
如果有助于使这个问题更具体,请将此问题重新解释为:"How can I extend the example Kaleidoscope JIT to show the machine code (x86, ARM, etc) it produces, not just the LLVM IR?"
谢谢。
这里至少有两个选项。
提供您自己的内存管理器。这必须被很好地记录下来,并且在许多使用 MCJIT 的项目中都是这样做的。但为了完整起见,这里是代码:
class MCJITMemoryManager : public llvm::RTDyldMemoryManager {
public:
static std::unique_ptr<MCJITMemoryManager> Create();
MCJITMemoryManager();
virtual ~MCJITMemoryManager();
// Allocate a memory block of (at least) the given size suitable for
// executable code. The section_id is a unique identifier assigned by the
// MCJIT engine, and optionally recorded by the memory manager to access a
// loaded section.
byte* allocateCodeSection(uintptr_t size, unsigned alignment,
unsigned section_id,
llvm::StringRef section_name) override;
// Allocate a memory block of (at least) the given size suitable for data.
// The SectionID is a unique identifier assigned by the JIT engine, and
// optionally recorded by the memory manager to access a loaded section.
byte* allocateDataSection(uintptr_t size, unsigned alignment,
unsigned section_id, llvm::StringRef section_name,
bool is_readonly) override;
...
}
将内存管理器实例传递给 EngineBuilder:
std::unique_ptr<MCJITMemoryManager> manager = MCJITMemoryManager::Create();
llvm::ExecutionEngine* raw = lvm::EngineBuilder(std::move(module))
.setMCJITMemoryManager(std::move(manager))
...
.create();
现在,通过这些回调,您可以控制发出代码的内存。 (并且 size 直接传递给您的方法)。只需记住您为代码段分配的缓冲区地址,然后在 gdb 中停止程序并反汇编内存(或将其转储到某个地方,甚至使用 LLVM 的反汇编程序)。
- 只需在您的 LLVM IR 上使用
llc
并设置适当的选项(优化级别等)。正如我所见,MCJIT 之所以这样称呼是有原因的,原因是它重用了现有的代码生成模块(与 llc 相同)。
包括以下headerllvm/Object/SymbolSize.h
,并使用函数llvm::object::computeSymbolSizes(ObjectFile&)
。您将需要以某种方式获取 ObjectFile
的实例。
要获取该实例,您可以执行以下操作:
- 声明一个 class ,它被调用以将
Module
转换为 ObjectFile
,类似于:
class ModuleToObjectFileCompiler {
...
// Compile a Module to an ObjectFile.
llvm::object::OwningBinary<llvm::object::ObjectFile>
operator() (llvm::Module&);
};
要实现 ModuleToObjectFileCompiler
的 operator()
,请查看 llvm/ExecutionEngine/Orc/CompileUtils.h
,其中定义了 class SimpleCompiler
。
将ModuleToObjectFileCompiler
的实例提供给llvm::orc::IRCompileLayer
的实例,例如:
new llvm::orc::IRCompileLayer
<llvm::orc::ObjectLinkingLayer
<llvm::orc::DoNothingOnNotifyLoaded> >
(_object_layer, _module_to_object_file);
ModuleToObjectFileCompiler
的 operator()
接收 ObjectFile
的实例,您可以将其提供给 computeSymbolSizes()
。然后检查返回的 std::vector
以找出 Module
中定义的所有符号的字节大小。保存您感兴趣的符号的信息。仅此而已。
我有一个我编写的程序,它使用 LLVM 3.5 作为 JIT 编译器,我正在尝试更新它以在 LLVM 3.7 中使用 MCJIT。我大部分时间都在使用它,但我正在努力重现我用 LLVM 3.5 实现的一项仅调试功能。
我希望能够看到由 JIT 进程生成的主机机器代码(例如 x86、x64 或 ARM,而不是 LLVM IR);在调试版本中,我将其注销,因为我的程序是 运行。使用 LLVM 3.5,我能够通过调用 ExecutionEngine::runJITOnFunction() 来填充 llvm::MachineCodeInfo 对象来完成此操作,这为我提供了生成的起始地址 和大小 代码。然后我可以反汇编该代码。
我似乎无法在 MCJIT 中找到任何等效项。我可以获得函数的起始地址(例如通过 getPointerToFunction())但不是大小。
我看过Disassemble Memory但是除了答案中没有那么详细之外,似乎更多的是关于如何反汇编字节序列。我知道该怎么做,我的问题是:我怎样才能首先掌握字节序列?
如果有助于使这个问题更具体,请将此问题重新解释为:"How can I extend the example Kaleidoscope JIT to show the machine code (x86, ARM, etc) it produces, not just the LLVM IR?"
谢谢。
这里至少有两个选项。
提供您自己的内存管理器。这必须被很好地记录下来,并且在许多使用 MCJIT 的项目中都是这样做的。但为了完整起见,这里是代码:
class MCJITMemoryManager : public llvm::RTDyldMemoryManager { public: static std::unique_ptr<MCJITMemoryManager> Create(); MCJITMemoryManager(); virtual ~MCJITMemoryManager(); // Allocate a memory block of (at least) the given size suitable for // executable code. The section_id is a unique identifier assigned by the // MCJIT engine, and optionally recorded by the memory manager to access a // loaded section. byte* allocateCodeSection(uintptr_t size, unsigned alignment, unsigned section_id, llvm::StringRef section_name) override; // Allocate a memory block of (at least) the given size suitable for data. // The SectionID is a unique identifier assigned by the JIT engine, and // optionally recorded by the memory manager to access a loaded section. byte* allocateDataSection(uintptr_t size, unsigned alignment, unsigned section_id, llvm::StringRef section_name, bool is_readonly) override; ... }
将内存管理器实例传递给 EngineBuilder:
std::unique_ptr<MCJITMemoryManager> manager = MCJITMemoryManager::Create(); llvm::ExecutionEngine* raw = lvm::EngineBuilder(std::move(module)) .setMCJITMemoryManager(std::move(manager)) ... .create();
现在,通过这些回调,您可以控制发出代码的内存。 (并且 size 直接传递给您的方法)。只需记住您为代码段分配的缓冲区地址,然后在 gdb 中停止程序并反汇编内存(或将其转储到某个地方,甚至使用 LLVM 的反汇编程序)。
- 只需在您的 LLVM IR 上使用
llc
并设置适当的选项(优化级别等)。正如我所见,MCJIT 之所以这样称呼是有原因的,原因是它重用了现有的代码生成模块(与 llc 相同)。
包括以下headerllvm/Object/SymbolSize.h
,并使用函数llvm::object::computeSymbolSizes(ObjectFile&)
。您将需要以某种方式获取 ObjectFile
的实例。
要获取该实例,您可以执行以下操作:
- 声明一个 class ,它被调用以将
Module
转换为ObjectFile
,类似于:class ModuleToObjectFileCompiler { ... // Compile a Module to an ObjectFile. llvm::object::OwningBinary<llvm::object::ObjectFile> operator() (llvm::Module&); };
要实现
ModuleToObjectFileCompiler
的operator()
,请查看llvm/ExecutionEngine/Orc/CompileUtils.h
,其中定义了 classSimpleCompiler
。将
ModuleToObjectFileCompiler
的实例提供给llvm::orc::IRCompileLayer
的实例,例如:new llvm::orc::IRCompileLayer <llvm::orc::ObjectLinkingLayer <llvm::orc::DoNothingOnNotifyLoaded> > (_object_layer, _module_to_object_file);
ModuleToObjectFileCompiler
的operator()
接收ObjectFile
的实例,您可以将其提供给computeSymbolSizes()
。然后检查返回的std::vector
以找出Module
中定义的所有符号的字节大小。保存您感兴趣的符号的信息。仅此而已。