From fbc306f702f3f896b5e434051a6f60bd89db7a89 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Thu, 9 May 2024 07:24:35 -0700 Subject: [PATCH] [a64] Implement multi-arch capstone support --- src/xenia/cpu/backend/a64/a64_assembler.cc | 19 +++- src/xenia/cpu/backend/a64/a64_assembler.h | 1 + src/xenia/cpu/backend/a64/a64_backend.cc | 110 ++++++++++++++++++++- src/xenia/cpu/backend/a64/a64_backend.h | 2 +- third_party/capstone.lua | 39 ++++++-- 5 files changed, 154 insertions(+), 17 deletions(-) diff --git a/src/xenia/cpu/backend/a64/a64_assembler.cc b/src/xenia/cpu/backend/a64/a64_assembler.cc index 4647a4c0a..280b82468 100644 --- a/src/xenia/cpu/backend/a64/a64_assembler.cc +++ b/src/xenia/cpu/backend/a64/a64_assembler.cc @@ -11,8 +11,8 @@ #include +#include "third_party/capstone/include/capstone/arm64.h" #include "third_party/capstone/include/capstone/capstone.h" -#include "third_party/capstone/include/capstone/x86.h" #include "xenia/base/profiling.h" #include "xenia/base/reset_scope.h" #include "xenia/base/string.h" @@ -33,11 +33,22 @@ namespace a64 { using xe::cpu::hir::HIRBuilder; A64Assembler::A64Assembler(A64Backend* backend) - : Assembler(backend), a64_backend_(backend) {} + : Assembler(backend), a64_backend_(backend), capstone_handle_(0) { + if (cs_open(CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN, &capstone_handle_) != + CS_ERR_OK) { + assert_always("Failed to initialize capstone"); + } + cs_option(capstone_handle_, CS_OPT_SYNTAX, CS_OPT_SYNTAX_INTEL); + cs_option(capstone_handle_, CS_OPT_DETAIL, CS_OPT_OFF); +} A64Assembler::~A64Assembler() { // Emitter must be freed before the allocator. emitter_.reset(); + + if (capstone_handle_) { + cs_close(&capstone_handle_); + } } bool A64Assembler::Initialize() { @@ -106,7 +117,9 @@ void A64Assembler::DumpMachineCode( size_t remaining_code_size = code_size; uint64_t address = uint64_t(machine_code); cs_insn insn = {0}; - while (remaining_code_size) { + while (remaining_code_size && + cs_disasm_iter(capstone_handle_, &code_ptr, &remaining_code_size, + &address, &insn)) { // Look up source offset. auto code_offset = uint32_t(code_ptr - reinterpret_cast(machine_code)); diff --git a/src/xenia/cpu/backend/a64/a64_assembler.h b/src/xenia/cpu/backend/a64/a64_assembler.h index f85d12339..95e0a6f1e 100644 --- a/src/xenia/cpu/backend/a64/a64_assembler.h +++ b/src/xenia/cpu/backend/a64/a64_assembler.h @@ -46,6 +46,7 @@ class A64Assembler : public Assembler { private: A64Backend* a64_backend_; std::unique_ptr emitter_; + uintptr_t capstone_handle_; StringBuffer string_buffer_; }; diff --git a/src/xenia/cpu/backend/a64/a64_backend.cc b/src/xenia/cpu/backend/a64/a64_backend.cc index 60e6c6236..f95ae43d5 100644 --- a/src/xenia/cpu/backend/a64/a64_backend.cc +++ b/src/xenia/cpu/backend/a64/a64_backend.cc @@ -11,6 +11,9 @@ #include +#include "third_party/capstone/include/capstone/arm64.h" +#include "third_party/capstone/include/capstone/capstone.h" + #include "xenia/base/exception_handler.h" #include "xenia/base/logging.h" #include "xenia/cpu/backend/a64/a64_assembler.h" @@ -64,9 +67,21 @@ class A64ThunkEmitter : public A64Emitter { void EmitLoadNonvolatileRegs(); }; -A64Backend::A64Backend() : Backend() {} +A64Backend::A64Backend() : Backend(), code_cache_(nullptr) { + if (cs_open(CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN, &capstone_handle_) != + CS_ERR_OK) { + assert_always("Failed to initialize capstone"); + } + cs_option(capstone_handle_, CS_OPT_SYNTAX, CS_OPT_SYNTAX_INTEL); + cs_option(capstone_handle_, CS_OPT_DETAIL, CS_OPT_ON); + cs_option(capstone_handle_, CS_OPT_SKIPDATA, CS_OPT_OFF); +} A64Backend::~A64Backend() { + if (capstone_handle_) { + cs_close(&capstone_handle_); + } + A64Emitter::FreeConstData(emitter_data_); ExceptionHandler::Uninstall(&ExceptionCallbackThunk, this); } @@ -132,10 +147,99 @@ std::unique_ptr A64Backend::CreateGuestFunction( Module* module, uint32_t address) { return std::make_unique(module, address); } + +uint64_t ReadCapstoneReg(HostThreadContext* context, arm64_reg reg) { + switch (reg) { + case ARM64_REG_X0: + return context->x[0]; + case ARM64_REG_X1: + return context->x[1]; + case ARM64_REG_X2: + return context->x[2]; + case ARM64_REG_X3: + return context->x[3]; + case ARM64_REG_X4: + return context->x[4]; + case ARM64_REG_X5: + return context->x[5]; + case ARM64_REG_X6: + return context->x[6]; + case ARM64_REG_X7: + return context->x[7]; + case ARM64_REG_X8: + return context->x[8]; + case ARM64_REG_X9: + return context->x[9]; + case ARM64_REG_X10: + return context->x[10]; + case ARM64_REG_X11: + return context->x[11]; + case ARM64_REG_X12: + return context->x[12]; + case ARM64_REG_X13: + return context->x[13]; + case ARM64_REG_X14: + return context->x[14]; + case ARM64_REG_X15: + return context->x[15]; + case ARM64_REG_X16: + return context->x[16]; + case ARM64_REG_X17: + return context->x[17]; + case ARM64_REG_X18: + return context->x[18]; + case ARM64_REG_X19: + return context->x[19]; + case ARM64_REG_X20: + return context->x[20]; + case ARM64_REG_X21: + return context->x[21]; + case ARM64_REG_X22: + return context->x[22]; + case ARM64_REG_X23: + return context->x[23]; + case ARM64_REG_X24: + return context->x[24]; + case ARM64_REG_X25: + return context->x[25]; + case ARM64_REG_X26: + return context->x[26]; + case ARM64_REG_X27: + return context->x[27]; + case ARM64_REG_X28: + return context->x[28]; + case ARM64_REG_X29: + return context->x[29]; + case ARM64_REG_X30: + return context->x[30]; + default: + assert_unhandled_case(reg); + return 0; + } +} + uint64_t A64Backend::CalculateNextHostInstruction(ThreadDebugInfo* thread_info, uint64_t current_pc) { - // TODO(wunkolo): Capstone hookup - return current_pc += 4; + auto machine_code_ptr = reinterpret_cast(current_pc); + size_t remaining_machine_code_size = 64; + uint64_t host_address = current_pc; + cs_insn insn = {0}; + cs_detail all_detail = {0}; + insn.detail = &all_detail; + cs_disasm_iter(capstone_handle_, &machine_code_ptr, + &remaining_machine_code_size, &host_address, &insn); + auto& detail = all_detail.x86; + switch (insn.id) { + case ARM64_INS_B: + case ARM64_INS_BL: + case ARM64_INS_BLR: + case ARM64_INS_BR: + case ARM64_INS_RET: + // todo(wunkolo): determine next instruction + default: + // Not a branching instruction - just move over it. + return current_pc + insn.size; + } } void A64Backend::InstallBreakpoint(Breakpoint* breakpoint) { diff --git a/src/xenia/cpu/backend/a64/a64_backend.h b/src/xenia/cpu/backend/a64/a64_backend.h index 85801c592..57557414c 100644 --- a/src/xenia/cpu/backend/a64/a64_backend.h +++ b/src/xenia/cpu/backend/a64/a64_backend.h @@ -70,7 +70,7 @@ class A64Backend : public Backend { static bool ExceptionCallbackThunk(Exception* ex, void* data); bool ExceptionCallback(Exception* ex); - // uintptr_t capstone_handle_ = 0; + uintptr_t capstone_handle_ = 0; std::unique_ptr code_cache_; uintptr_t emitter_data_ = 0; diff --git a/third_party/capstone.lua b/third_party/capstone.lua index 6dc415974..8dfb328f7 100644 --- a/third_party/capstone.lua +++ b/third_party/capstone.lua @@ -4,13 +4,37 @@ project("capstone") kind("StaticLib") language("C") defines({ - "CAPSTONE_X86_ATT_DISABLE", "CAPSTONE_DIET_NO", - "CAPSTONE_X86_REDUCE_NO", - "CAPSTONE_HAS_X86", "CAPSTONE_USE_SYS_DYN_MEM", "_LIB", }) + filter("architecture:x86_64") + defines({ + "CAPSTONE_HAS_X86", + "CAPSTONE_X86_ATT_DISABLE", + "CAPSTONE_X86_REDUCE_NO", + }) + files({ + "capstone/arch/X86/*.c", + "capstone/arch/X86/*.h", + "capstone/arch/X86/*.inc", + }) + force_compile_as_c({ + "capstone/arch/X86/**.c", + }) + filter("architecture:ARM64") + defines({ + "CAPSTONE_HAS_ARM64", + }) + files({ + "capstone/arch/AArch64/*.c", + "capstone/arch/AArch64/*.h", + "capstone/arch/AArch64/*.inc", + }) + force_compile_as_c({ + "capstone/arch/AArch64/**.c", + }) + filter({}) includedirs({ "capstone", "capstone/include", @@ -32,12 +56,7 @@ project("capstone") "capstone/SStream.h", "capstone/utils.c", "capstone/utils.h", - - "capstone/arch/X86/*.c", - "capstone/arch/X86/*.h", - "capstone/arch/X86/*.inc", }) force_compile_as_c({ - "capstone/**.c", - "capstone/arch/X86/**.c", - }) + "capstone/**.c", + }) \ No newline at end of file