【问题标题】:How can I disassemble the result of LLVM MCJIT compilation?如何反汇编 LLVM MCJIT 编译的结果?
【发布时间】:2016-03-25 18:36:07
【问题描述】:

我编写了一个程序,它使用 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,但除了答案中没有那么多细节之外,它似乎更多的是关于如何反汇编字节序列。我知道该怎么做,我的问题是:我怎样才能首先掌握字节序列?

如果有助于更具体化,请将此问题重新解释为:“如何扩展示例 Kaleidoscope JIT 以显示它生成的机器代码(x86、ARM 等),而不仅仅是 LLVM IR?”

谢谢。

【问题讨论】:

    标签: llvm disassembly mcjit


    【解决方案1】:

    您在这里至少有两个选择。

    1. 提供您自己的内存管理器。这必须有很好的文档记录,并且在许多使用 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 的反汇编程序)。

    2. 只需在 LLVM IR 上使用 llc 并使用适当的选项(优化级别等)。正如我所看到的,MCJIT 被称为是有原因的,原因是它重用了现有的代码生成模块(与 llc 相同)。

    【讨论】:

      【解决方案2】:

      包含以下标头llvm/Object/SymbolSize.h,并使用函数llvm::object::computeSymbolSizes(ObjectFile&amp;)。您将需要以某种方式获取ObjectFile 的实例。

      要获得该实例,您可以执行以下操作:

      1. 声明一个类,该类被调用以将Module 转换为ObjectFile,类似于: class ModuleToObjectFileCompiler { ... // Compile a Module to an ObjectFile. llvm::object::OwningBinary<llvm::object::ObjectFile> operator() (llvm::Module&); };
      2. 要实现ModuleToObjectFileCompileroperator(),请查看定义SimpleCompiler 类的llvm/ExecutionEngine/Orc/CompileUtils.h

      3. ModuleToObjectFileCompiler 的实例提供给llvm::orc::IRCompileLayer 的实例,例如: new llvm::orc::IRCompileLayer <llvm::orc::ObjectLinkingLayer <llvm::orc::DoNothingOnNotifyLoaded> > (_object_layer, _module_to_object_file);

      4. ModuleToObjectFileCompileroperator() 接收ObjectFile 的实例,您可以将其提供给computeSymbolSizes()。然后检查返回的std::vector 以找出在该Module 中定义的所有符号的字节大小。保存您感兴趣的符号的信息。仅此而已。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-07-25
        • 2015-12-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-29
        • 2016-05-12
        相关资源
        最近更新 更多