From 60870165d5bb518d93374f02039987ed1e52adca Mon Sep 17 00:00:00 2001 From: SimoneN64 Date: Mon, 23 Sep 2024 22:56:44 +0200 Subject: [PATCH] Lay down basic disassembler --- src/backend/CMakeLists.txt | 5 ++- src/backend/core/BaseCPU.hpp | 2 ++ src/backend/core/Disassembler.cpp | 57 +++++++++++++++++++++++++++++++ src/backend/core/Disassembler.hpp | 47 +++++++++++++++++++++++++ src/backend/core/Interpreter.cpp | 4 +++ src/backend/core/Interpreter.hpp | 1 + src/backend/core/JIT.hpp | 2 ++ src/frontend/CMakeLists.txt | 3 +- src/frontend/Debugger.cpp | 4 +++ src/utils/MemoryHelpers.hpp | 10 ++++++ 10 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 src/backend/core/Disassembler.cpp create mode 100644 src/backend/core/Disassembler.hpp diff --git a/src/backend/CMakeLists.txt b/src/backend/CMakeLists.txt index d656ead3..88715054 100644 --- a/src/backend/CMakeLists.txt +++ b/src/backend/CMakeLists.txt @@ -2,6 +2,9 @@ file(GLOB SOURCES *.cpp) file(GLOB HEADERS *.hpp) add_subdirectory(core) +option(CAPSTONE_ARCHITECTURE_DEFAULT OFF) +option(CAPSTONE_MIPS_SUPPORT ON) +add_subdirectory(../../external/capstone capstone) add_library(backend ${SOURCES} ${HEADERS}) -target_link_libraries(backend PRIVATE core) \ No newline at end of file +target_link_libraries(backend PRIVATE core capstone) \ No newline at end of file diff --git a/src/backend/core/BaseCPU.hpp b/src/backend/core/BaseCPU.hpp index 7ec4239d..9184006d 100644 --- a/src/backend/core/BaseCPU.hpp +++ b/src/backend/core/BaseCPU.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include namespace n64 { struct BaseCPU { @@ -13,5 +14,6 @@ struct BaseCPU { virtual void Deserialize(const std::vector &) = 0; virtual Mem &GetMem() = 0; virtual Registers &GetRegs() = 0; + virtual Disassembler::DisassemblyResult Disassemble(u32, u32) const = 0; }; } // namespace n64 diff --git a/src/backend/core/Disassembler.cpp b/src/backend/core/Disassembler.cpp new file mode 100644 index 00000000..de23b618 --- /dev/null +++ b/src/backend/core/Disassembler.cpp @@ -0,0 +1,57 @@ +#include + +Disassembler::DisassemblyResult Disassembler::DisassembleSimple(u32 address, u32 instruction) { + cs_insn *insn; + auto bytes = Util::IntegralToBuffer(instruction); + auto count = cs_disasm(handle, bytes.data(), bytes.size(), address, 0, &insn); + + if (count <= 0) + return {}; + + DisassemblyResult result{true, fmt::format("0x{:016X}:\t{}\t{}", insn[0].address, insn[0].mnemonic, insn[0].op_str)}; + + cs_free(insn, count); + + return result; +} + +Disassembler::DisassemblyResult Disassembler::DisassembleDetailed(u32 address, u32 instruction) { + cs_insn *insn; + auto bytes = Util::IntegralToBuffer(instruction); + auto count = cs_disasm(handle, bytes.data(), bytes.size(), address, 0, &insn); + + if (count <= 0) + return {}; + + DisassemblyResult result{true}; + result.address = insn[0].address; + result.mnemonic = insn[0].mnemonic; + + result.full += result.address + ":\t"; + result.full += result.mnemonic + "\t"; + + cs_detail *details = insn[0].detail; + auto formatOperand = [&](const cs_mips_op &operand) { + switch (operand.type) { + case MIPS_OP_IMM: + return fmt::format("#{:X}", operand.is_unsigned ? operand.uimm : operand.imm); + case MIPS_OP_MEM: + return fmt::format("{}(0x{:X})", cs_reg_name(handle, operand.mem.base), operand.mem.disp); + case MIPS_OP_REG: + return fmt::format("{}", cs_reg_name(handle, operand.reg)); + } + }; + + for (u8 i = 0; i < details->mips.op_count && i < 3; i++) { + result.ops[i] = formatOperand(details->mips.operands[i]); + result.full += result.ops[i] + "\t"; + } + + result.full += "\t// "; + + // TODO: generate a comment + + cs_free(insn, count); + + return result; +} diff --git a/src/backend/core/Disassembler.hpp b/src/backend/core/Disassembler.hpp new file mode 100644 index 00000000..1f217872 --- /dev/null +++ b/src/backend/core/Disassembler.hpp @@ -0,0 +1,47 @@ +#pragma once +#include +#include +#include +#include +#include + +struct Disassembler { + struct DisassemblyResult { + bool success = false; + std::string full; + u64 address; + std::string mnemonic; + std::array ops{}; + }; + + static Disassembler &instance(bool rsp = false) { + static Disassembler ret(rsp); + return ret; + } + + DisassemblyResult Disassemble(u32 address, u32 instruction) { + return details ? DisassembleDetailed(address, instruction) : DisassembleSimple(address, instruction); + } + + ~Disassembler() { cs_close(&handle); } + +private: + DisassemblyResult DisassembleDetailed(u32 address, u32 instruction); + DisassemblyResult DisassembleSimple(u32 address, u32 instruction); + + Disassembler(bool rsp) : rsp(rsp) { + if (cs_open(CS_ARCH_MIPS, static_cast((rsp ? CS_MODE_32 : CS_MODE_64) | CS_MODE_BIG_ENDIAN), &handle) != + CS_ERR_OK) { + Util::panic("Could not initialize {} disassembler!", rsp ? "RSP" : "CPU"); + } + + if (cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON) != CS_ERR_OK) { + Util::warn("Could not enable disassembler's details!"); + details = false; + } + } + + bool rsp = false; + bool details = true; + csh handle; +}; diff --git a/src/backend/core/Interpreter.cpp b/src/backend/core/Interpreter.cpp index b89ebd95..4f0be20c 100644 --- a/src/backend/core/Interpreter.cpp +++ b/src/backend/core/Interpreter.cpp @@ -21,6 +21,10 @@ void Interpreter::CheckCompareInterrupt() { } } +Disassembler::DisassemblyResult Interpreter::Disassemble(u32 address, u32 instruction) const { + return Disassembler::instance().Disassemble(address, instruction); +} + int Interpreter::Step() { CheckCompareInterrupt(); diff --git a/src/backend/core/Interpreter.hpp b/src/backend/core/Interpreter.hpp index c121f23b..3742a2ea 100644 --- a/src/backend/core/Interpreter.hpp +++ b/src/backend/core/Interpreter.hpp @@ -20,6 +20,7 @@ struct Interpreter : BaseCPU { Mem &GetMem() override { return mem; } Registers &GetRegs() override { return regs; } + Disassembler::DisassemblyResult Disassemble(u32, u32) const override; private: Registers regs; diff --git a/src/backend/core/JIT.hpp b/src/backend/core/JIT.hpp index 2d2e60bf..597adade 100644 --- a/src/backend/core/JIT.hpp +++ b/src/backend/core/JIT.hpp @@ -20,6 +20,8 @@ struct JIT : BaseCPU { Registers &GetRegs() override { return regs; } + Disassembler::DisassemblyResult Disassemble(u32, u32) const override { return {}; } + private: Registers regs; Mem mem; diff --git a/src/frontend/CMakeLists.txt b/src/frontend/CMakeLists.txt index 220a9ea9..708801b2 100644 --- a/src/frontend/CMakeLists.txt +++ b/src/frontend/CMakeLists.txt @@ -36,8 +36,7 @@ include_directories( ../../external/parallel-rdp/parallel-rdp-standalone/util ../../external/unarr ../../external/SDL/include - ../../external/imgui - ../../external/imgui/backends + ../../external/capstone/include ) option(RAPIDJSON_BUILD_DOC "Build rapidjson documentation." OFF) diff --git a/src/frontend/Debugger.cpp b/src/frontend/Debugger.cpp index 6447037c..ef33578e 100644 --- a/src/frontend/Debugger.cpp +++ b/src/frontend/Debugger.cpp @@ -22,4 +22,8 @@ Debugger::Debugger() : QWidget(nullptr) { verLayout->addLayout(horLayout); setLayout(verLayout); + + connect(codeView, &QTreeView::activated, this, [&](QModelIndex index) { + + }); } diff --git a/src/utils/MemoryHelpers.hpp b/src/utils/MemoryHelpers.hpp index 833296b6..ba510475 100644 --- a/src/utils/MemoryHelpers.hpp +++ b/src/utils/MemoryHelpers.hpp @@ -6,6 +6,16 @@ #include namespace Util { +template +static FORCE_INLINE const std::vector &IntegralToBuffer(const T &val) { + std::vector ret{}; + ret.resize(sizeof(T)); + + memcpy(ret.data(), &val, sizeof(T)); + + return ret; +} + template static FORCE_INLINE T ReadAccess(const u8 *data, u32 index) { if constexpr (sizeof(T) == 8) {