2015-12-19 9 views
4

У меня есть программа, которую я написал, которая использует LLVM 3.5 как JIT-компилятор, который я пытаюсь обновить, чтобы использовать MCJIT в LLVM 3.7. У меня это в основном работает, но я изо всех сил пытаюсь воспроизвести одну функцию отладки, реализованную с помощью LLVM 3.5.Как разобрать результат компиляции LLVM MCJIT?

Хотелось бы видеть код хост-машины (например, x86, x64 или ARM, а не LLVM IR), сгенерированный JIT-процессом; в отладочных сборках я регистрирую это, когда моя программа запущена. С LLVM 3.5 я смог сделать это, вызвав ExecutionEngine :: runJITOnFunction(), чтобы заполнить объект llvm :: MachineCodeInfo, который дал мне начальный адрес и размер сгенерированного кода. Тогда я смогу разобрать этот код.

Я не могу найти эквивалент в MCJIT. Я могу получить начальный адрес функции (например, через getPointerToFunction()), но не размер.

Я видел Disassemble Memory, но кроме того, что в ответах не так много деталей, похоже, это больше о том, как разобрать последовательность байтов. Я знаю, как это сделать, мой вопрос: как я могу получить последовательность байтов в первую очередь?

Если это поможет сделать это более конкретным, пожалуйста, переформулируйте этот вопрос следующим образом: «Как я могу расширить пример Kaleidoscope JIT, чтобы показать машинный код (x86, ARM и т. Д.), Который он производит, а не только LLVM IR? "

Спасибо.

ответ

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(); 
    

    Теперь с помощью этих обратных вызовов у вас есть контроль над памятью, где код получает излучаемую. (И размер передается непосредственно вашему методу). Просто запомните адрес буфера, который вы выделили для секции кода, и остановите программу в gdb и разобщите память (или выгрузите ее где-нибудь или даже используйте дизассемблер LLVM).

  2. Просто используйте llc на вашем LLVM IR с соответствующими параметрами (уровень оптимизации и т. Д.). Как я вижу, MCJIT называется так по какой-то причине, и причина в том, что он повторно использует существующие модули генерации кода (такие же, как и llc).
0

Включите следующий заголовок llvm/Object/SymbolSize.h и используйте функцию llvm::object::computeSymbolSizes(ObjectFile&). Вам нужно будет как-то получить экземпляр ObjectFile.

Чтобы получить этот экземпляр, вот что вы можете сделать:

  1. Объявите класс, который вызывается для преобразования Module к ObjectFile, что-то вроде: class ModuleToObjectFileCompiler { ... // Compile a Module to an ObjectFile. llvm::object::OwningBinary<llvm::object::ObjectFile> operator() (llvm::Module&); };
  2. Для реализации operator() из ModuleToObjectFileCompiler, посмотрите на llvm/ExecutionEngine/Orc/CompileUtils.h, где определен класс SimpleCompiler.

  3. Обеспечить экземпляр ModuleToObjectFileCompiler к экземпляру llvm::orc::IRCompileLayer, например: new llvm::orc::IRCompileLayer <llvm::orc::ObjectLinkingLayer <llvm::orc::DoNothingOnNotifyLoaded> > (_object_layer, _module_to_object_file);

  4. operator() из ModuleToObjectFileCompiler получает экземпляр ObjectFile, который вы можете предоставить в computeSymbolSizes(). Затем проверьте возвращенный std::vector, чтобы узнать размеры в байтах всех символов, определенных в этом Module. Сохраните информацию для интересующих вас символов. И все.