我写了一个程序,它使用LLVM 3.5作为JIT编译器,我正在尝试更新它,以便在LLVM 3.7中使用MCJIT。我大部分时间都在工作,但我很难重现我在LLVM3.5中实现的一个仅限调试的功能。
我希望能够看到JIT进程生成的主机代码(例如x86、x64或ARM,而不是LLVM IR);在调试构建中,我会在程序运行时将其注销。使用LLVM 3.5,我可以通过调用ExecutionEngine::runJITOnFunction()来填充LLVM::MachineCodeInfo对象来实现这一点,该对象为我提供了生成代码的起始地址和大小。然后我可以反汇编那个代码。
我似乎在MCJIT中找不到任何类似的东西。我可以得到函数的起始地址(例如通过getPointerToFunction()),但不能得到大小。
我看过《分解内存》,但除了答案中没有那么多细节外,它似乎更多的是关于如何分解字节序列。我知道如何做到这一点,我的问题是:我如何首先掌握字节序列?
如果这有助于使其更加具体,请将此问题重新解释为:"我如何扩展示例Kaleidoscope JIT以显示它生成的机器代码(x86、ARM等),而不仅仅是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();
现在,通过这些回调,您可以控制代码发出的内存。(大小直接传递给您的方法)。只需记住为代码段分配的缓冲区地址,然后停止gdb中的程序并反汇编内存(或将其转储到某个地方,甚至使用LLVM的反汇编程序)。
- 只需在LLVM IR上使用
llc
,并提供适当的选项(优化级别等)。在我看来,MCJIT之所以被调用是有原因的,原因是它重用了现有的代码生成模块(与llc相同)
包含以下标头llvm/Object/SymbolSize.h
,并使用函数llvm::object::computeSymbolSizes(ObjectFile&)
。您将需要以某种方式获得ObjectFile
的实例。
要获得该实例,以下是您可以做的:
- 声明一个被调用以将
Module
转换为ObjectFile
的类,类似于:class ModuleToObjectFileCompiler { ... // Compile a Module to an ObjectFile. llvm::object::OwningBinary<llvm::object::ObjectFile> operator() (llvm::Module&); };
-
要实现
ModuleToObjectFileCompiler
的operator()
,请查看定义了类SimpleCompiler
的llvm/ExecutionEngine/Orc/CompileUtils.h
。 -
将
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
中定义的所有符号的字节大小。保存你感兴趣的符号的信息。仅此而已。