From af27ef8d6ca53b7f9cbeb9757ee22284377525f5 Mon Sep 17 00:00:00 2001 From: Tillmann Karras Date: Fri, 5 Aug 2016 01:50:31 +0100 Subject: [PATCH] Optionally disassemble x86_64 code using LLVM --- CMakeLists.txt | 9 +++++ src/backend_x64/emit_x64.cpp | 7 ++-- src/backend_x64/emit_x64.h | 12 ++++--- src/backend_x64/interface_x64.cpp | 58 ++++++++++++++++++++++++++++--- src/interface/interface.h | 3 ++ tests/CMakeLists.txt | 2 +- tests/arm/fuzz_arm.cpp | 20 +++++++---- 7 files changed, 91 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 34dbcbb3..8d435469 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ project(dynarmic) # Dynarmic project options option(DYNARMIC_USE_SYSTEM_BOOST "Use the system boost libraries" ON) +option(DYNARMIC_USE_LLVM "Support disassembly of jitted x86_64 code using LLVM" OFF) # Compiler flags if (NOT MSVC) @@ -81,6 +82,14 @@ include_directories(${Boost_INCLUDE_DIRS}) include_directories(externals/catch) enable_testing(true) # Enables unit-testing. +# Include LLVM +if (DYNARMIC_USE_LLVM) + find_package(LLVM REQUIRED CONFIG) + include_directories(${LLVM_INCLUDE_DIRS}) + add_definitions(-DDYNARMIC_USE_LLVM ${LLVM_DEFINITIONS}) + llvm_map_components_to_libnames(llvm_libs x86desc x86disassembler) +endif() + # Dynarmic project files add_subdirectory(src) add_subdirectory(tests) diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index 610250bc..68e2e62a 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -48,13 +48,13 @@ static void EraseInstruction(IR::Block& block, IR::Inst* inst) { block.instructions.erase(block.instructions.iterator_to(*inst)); } -CodePtr EmitX64::Emit(const Arm::LocationDescriptor descriptor, Dynarmic::IR::Block& block) { +EmitX64::BlockDescriptor* EmitX64::Emit(const Arm::LocationDescriptor descriptor, Dynarmic::IR::Block& block) { inhibit_emission.clear(); reg_alloc.Reset(); code->INT3(); CodePtr code_ptr = code->GetCodePtr(); - basic_blocks[descriptor] = code_ptr; + basic_blocks[descriptor].code_ptr = code_ptr; EmitCondPrelude(block.cond, block.cond_failed, block.location); @@ -84,7 +84,8 @@ CodePtr EmitX64::Emit(const Arm::LocationDescriptor descriptor, Dynarmic::IR::Bl reg_alloc.AssertNoMoreUses(); - return code_ptr; + basic_blocks[descriptor].size = code->GetCodePtr() - code_ptr; + return &basic_blocks[descriptor]; } void EmitX64::EmitIdentity(IR::Block& block, IR::Inst* inst) { diff --git a/src/backend_x64/emit_x64.h b/src/backend_x64/emit_x64.h index 809464ff..51ac3ff2 100644 --- a/src/backend_x64/emit_x64.h +++ b/src/backend_x64/emit_x64.h @@ -23,11 +23,15 @@ public: EmitX64(Gen::XEmitter* code, Routines* routines, UserCallbacks cb, Jit* jit_interface) : reg_alloc(code), code(code), routines(routines), cb(cb), jit_interface(jit_interface) {} - CodePtr Emit(const Arm::LocationDescriptor descriptor, IR::Block& ir); + struct BlockDescriptor { + CodePtr code_ptr; + size_t size; + }; + BlockDescriptor* Emit(const Arm::LocationDescriptor descriptor, IR::Block& ir); - CodePtr GetBasicBlock(Arm::LocationDescriptor descriptor) { + BlockDescriptor* GetBasicBlock(Arm::LocationDescriptor descriptor) { auto iter = basic_blocks.find(descriptor); - return iter != basic_blocks.end() ? iter->second : nullptr; + return iter != basic_blocks.end() ? &iter->second : nullptr; } void ClearCache(); @@ -62,7 +66,7 @@ private: Routines* routines; UserCallbacks cb; Jit* jit_interface; - std::unordered_map basic_blocks; + std::unordered_map basic_blocks; }; } // namespace BackendX64 diff --git a/src/backend_x64/interface_x64.cpp b/src/backend_x64/interface_x64.cpp index 18679cb4..0e3ba207 100644 --- a/src/backend_x64/interface_x64.cpp +++ b/src/backend_x64/interface_x64.cpp @@ -6,6 +6,11 @@ #include +#ifdef DYNARMIC_USE_LLVM +#include +#include +#endif + #include "backend_x64/emit_x64.h" #include "backend_x64/jitstate.h" #include "backend_x64/routines.h" @@ -13,6 +18,7 @@ #include "common/bit_util.h" #include "common/common_types.h" #include "common/scope_exit.h" +#include "common/string_util.h" #include "frontend/arm_types.h" #include "frontend/translate/translate.h" #include "interface/interface.h" @@ -44,14 +50,52 @@ struct Jit::Impl { Arm::LocationDescriptor descriptor{pc, TFlag, EFlag, jit_state.Fpscr}; - CodePtr code_ptr = GetBasicBlock(descriptor); + CodePtr code_ptr = GetBasicBlock(descriptor)->code_ptr; return routines.RunCode(&jit_state, code_ptr, cycle_count); } + + std::string Disassemble(Arm::LocationDescriptor descriptor) { + auto block = GetBasicBlock(descriptor); + std::string result = Common::StringFromFormat("address: %p\nsize: %zu bytes\n", block->code_ptr, block->size); + +#ifdef DYNARMIC_USE_LLVM + CodePtr end = block->code_ptr + block->size; + size_t remaining = block->size; + + LLVMInitializeX86TargetInfo(); + LLVMInitializeX86TargetMC(); + LLVMInitializeX86Disassembler(); + LLVMDisasmContextRef llvm_ctx = LLVMCreateDisasm("x86_64", nullptr, 0, nullptr, nullptr); + LLVMSetDisasmOptions(llvm_ctx, LLVMDisassembler_Option_AsmPrinterVariant); + + for (CodePtr pos = block->code_ptr; pos < end;) { + char buffer[80]; + size_t inst_size = LLVMDisasmInstruction(llvm_ctx, const_cast(pos), remaining, (u64)pos, buffer, sizeof(buffer)); + assert(inst_size); + for (CodePtr i = pos; i < pos + inst_size; i++) + result.append(Common::StringFromFormat("%02x ", *i)); + for (size_t i = inst_size; i < 10; i++) + result.append(" "); + result.append(buffer); + result.append("\n"); + + pos += inst_size; + remaining -= inst_size; + } + + LLVMDisasmDispose(llvm_ctx); +#else + result.append("(recompile with DYNARMIC_USE_LLVM=ON to disassemble the generated x86_64 code)\n"); +#endif + + return result; + } + private: - CodePtr GetBasicBlock(Arm::LocationDescriptor descriptor) { - CodePtr code_ptr = emitter.GetBasicBlock(descriptor); - if (code_ptr) - return code_ptr; + EmitX64::BlockDescriptor* GetBasicBlock(Arm::LocationDescriptor descriptor) { + auto block = emitter.GetBasicBlock(descriptor); + if (block) + return block; IR::Block ir_block = Arm::Translate(descriptor, callbacks.MemoryRead32); Optimization::GetSetElimination(ir_block); @@ -113,4 +157,8 @@ u32 Jit::Cpsr() const { return impl->jit_state.Cpsr; } +std::string Jit::Disassemble(Arm::LocationDescriptor descriptor) { + return impl->Disassemble(descriptor); +} + } // namespace Dynarmic diff --git a/src/interface/interface.h b/src/interface/interface.h index 74733b16..b178b0aa 100644 --- a/src/interface/interface.h +++ b/src/interface/interface.h @@ -8,6 +8,7 @@ #include +#include "frontend/arm_types.h" #include "common/common_types.h" namespace Dynarmic { @@ -75,6 +76,8 @@ public: return is_executing; } + std::string Disassemble(Arm::LocationDescriptor descriptor); + private: bool halt_requested = false; bool is_executing = false; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a6d1cc3b..ff5a7f4d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -33,5 +33,5 @@ set(HEADERS create_directory_groups(${SRCS} ${HEADERS}) add_executable(dynarmic_tests ${SRCS}) -target_link_libraries(dynarmic_tests dynarmic) +target_link_libraries(dynarmic_tests dynarmic ${llvm_libs}) set_target_properties(dynarmic_tests PROPERTIES LINKER_LANGUAGE CXX) diff --git a/tests/arm/fuzz_arm.cpp b/tests/arm/fuzz_arm.cpp index d5aa9248..a5ec8052 100644 --- a/tests/arm/fuzz_arm.cpp +++ b/tests/arm/fuzz_arm.cpp @@ -7,17 +7,18 @@ #include #include #include -#include +#include #include -#include -#include -#include #include "common/bit_util.h" #include "common/common_types.h" +#include "frontend/arm_types.h" #include "frontend/disassembler/disassembler.h" +#include "frontend/ir/ir.h" +#include "frontend/translate/translate.h" #include "interface/interface.h" +#include "ir_opt/passes.h" #include "rand_int.h" #include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" #include "skyeye_interpreter/skyeye_common/armstate.h" @@ -219,22 +220,27 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe printf("\nInitial Register Listing: \n"); for (int i = 0; i <= 15; i++) { - printf("%4i: %08x\n", i, initial_regs[i]); + auto reg = Dynarmic::Arm::RegToString(static_cast(i)); + printf("%4s: %08x\n", reg, initial_regs[i]); } printf("\nFinal Register Listing: \n"); printf(" interp jit\n"); for (int i = 0; i <= 15; i++) { - printf("%4i: %08x %08x %s\n", i, interp.Reg[i], jit.Regs()[i], interp.Reg[i] != jit.Regs()[i] ? "*" : ""); + auto reg = Dynarmic::Arm::RegToString(static_cast(i)); + printf("%4s: %08x %08x %s\n", reg, interp.Reg[i], jit.Regs()[i], interp.Reg[i] != jit.Regs()[i] ? "*" : ""); } printf("CPSR: %08x %08x %s\n", interp.Cpsr, jit.Cpsr(), interp.Cpsr != jit.Cpsr() ? "*" : ""); - Dynarmic::IR::Block ir_block = Dynarmic::Arm::Translate({0, false, false, 0}, &MemoryRead32); + Dynarmic::Arm::LocationDescriptor descriptor = {0, false, false, 0}; + Dynarmic::IR::Block ir_block = Dynarmic::Arm::Translate(descriptor, &MemoryRead32); Dynarmic::Optimization::GetSetElimination(ir_block); Dynarmic::Optimization::DeadCodeElimination(ir_block); Dynarmic::Optimization::VerificationPass(ir_block); printf("\n\nIR:\n%s", Dynarmic::IR::DumpBlock(ir_block).c_str()); + printf("\n\nx86_64:\n%s", jit.Disassemble(descriptor).c_str()); + #ifdef _MSC_VER __debugbreak(); #else