summaryrefslogtreecommitdiff
path: root/Source/Core/Common/HostDisassembler.cpp
diff options
context:
space:
mode:
authormitaclaw <140017135+mitaclaw@users.noreply.github.com>2024-07-28 19:12:21 -0700
committermitaclaw <140017135+mitaclaw@users.noreply.github.com>2024-10-19 00:14:54 -0700
commitca9222a16b477869616b3938c374ed7a497802b4 (patch)
treecf2401e7505fb71441f37e63bafca9405e67206c /Source/Core/Common/HostDisassembler.cpp
parenta0987829e5c3440a43b6db768e6f94c930d367e5 (diff)
Move UICommon/Disassembler to Common/HostDisassembler
A preliminary commit for a cleaner diff and an easier review
Diffstat (limited to 'Source/Core/Common/HostDisassembler.cpp')
-rw-r--r--Source/Core/Common/HostDisassembler.cpp208
1 files changed, 208 insertions, 0 deletions
diff --git a/Source/Core/Common/HostDisassembler.cpp b/Source/Core/Common/HostDisassembler.cpp
new file mode 100644
index 0000000000..d21536ecc4
--- /dev/null
+++ b/Source/Core/Common/HostDisassembler.cpp
@@ -0,0 +1,208 @@
+// Copyright 2008 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "Common/HostDisassembler.h"
+
+#include <sstream>
+
+#if defined(HAVE_LLVM)
+#include <fmt/format.h>
+#include <llvm-c/Disassembler.h>
+#include <llvm-c/Target.h>
+#elif defined(_M_X86_64)
+#include <disasm.h> // Bochs
+#endif
+
+#include "Common/Assert.h"
+#include "Common/VariantUtil.h"
+#include "Core/PowerPC/JitInterface.h"
+#include "Core/System.h"
+
+#if defined(HAVE_LLVM)
+class HostDisassemblerLLVM : public HostDisassembler
+{
+public:
+ HostDisassemblerLLVM(const std::string& host_disasm, int inst_size = -1,
+ const std::string& cpu = "");
+ ~HostDisassemblerLLVM()
+ {
+ if (m_can_disasm)
+ LLVMDisasmDispose(m_llvm_context);
+ }
+
+private:
+ bool m_can_disasm;
+ LLVMDisasmContextRef m_llvm_context;
+ int m_instruction_size;
+
+ std::string DisassembleHostBlock(const u8* code_start, const u32 code_size,
+ u32* host_instructions_count, u64 starting_pc) override;
+};
+
+HostDisassemblerLLVM::HostDisassemblerLLVM(const std::string& host_disasm, int inst_size,
+ const std::string& cpu)
+ : m_can_disasm(false), m_instruction_size(inst_size)
+{
+ LLVMInitializeAllTargetInfos();
+ LLVMInitializeAllTargetMCs();
+ LLVMInitializeAllDisassemblers();
+
+ m_llvm_context =
+ LLVMCreateDisasmCPU(host_disasm.c_str(), cpu.c_str(), nullptr, 0, nullptr, nullptr);
+
+ // Couldn't create llvm context
+ if (!m_llvm_context)
+ return;
+
+ LLVMSetDisasmOptions(m_llvm_context, LLVMDisassembler_Option_AsmPrinterVariant |
+ LLVMDisassembler_Option_PrintLatency);
+
+ m_can_disasm = true;
+}
+
+std::string HostDisassemblerLLVM::DisassembleHostBlock(const u8* code_start, const u32 code_size,
+ u32* host_instructions_count,
+ u64 starting_pc)
+{
+ if (!m_can_disasm)
+ return "(No LLVM context)";
+
+ u8* disasmPtr = (u8*)code_start;
+ const u8* end = code_start + code_size;
+
+ std::ostringstream x86_disasm;
+ while ((u8*)disasmPtr < end)
+ {
+ char inst_disasm[256];
+ size_t inst_size = LLVMDisasmInstruction(m_llvm_context, disasmPtr, (u64)(end - disasmPtr),
+ starting_pc, inst_disasm, 256);
+
+ x86_disasm << "0x" << std::hex << starting_pc << "\t";
+ if (!inst_size)
+ {
+ x86_disasm << "Invalid inst:";
+
+ if (m_instruction_size != -1)
+ {
+ // If we are on an architecture that has a fixed instruction size
+ // We can continue onward past this bad instruction.
+ std::string inst_str;
+ for (int i = 0; i < m_instruction_size; ++i)
+ inst_str += fmt::format("{:02x}", disasmPtr[i]);
+
+ x86_disasm << inst_str << std::endl;
+ disasmPtr += m_instruction_size;
+ }
+ else
+ {
+ // We can't continue if we are on an architecture that has flexible instruction sizes
+ // Dump the rest of the block instead
+ std::string code_block;
+ for (int i = 0; (disasmPtr + i) < end; ++i)
+ code_block += fmt::format("{:02x}", disasmPtr[i]);
+
+ x86_disasm << code_block << std::endl;
+ break;
+ }
+ }
+ else
+ {
+ x86_disasm << inst_disasm << std::endl;
+ disasmPtr += inst_size;
+ starting_pc += inst_size;
+ }
+
+ (*host_instructions_count)++;
+ }
+
+ return x86_disasm.str();
+}
+#elif defined(_M_X86_64)
+class HostDisassemblerX86 : public HostDisassembler
+{
+public:
+ HostDisassemblerX86();
+
+private:
+ disassembler m_disasm;
+
+ std::string DisassembleHostBlock(const u8* code_start, const u32 code_size,
+ u32* host_instructions_count, u64 starting_pc) override;
+};
+
+HostDisassemblerX86::HostDisassemblerX86()
+{
+ m_disasm.set_syntax_intel();
+}
+
+std::string HostDisassemblerX86::DisassembleHostBlock(const u8* code_start, const u32 code_size,
+ u32* host_instructions_count, u64 starting_pc)
+{
+ u64 disasmPtr = (u64)code_start;
+ const u8* end = code_start + code_size;
+
+ std::ostringstream x86_disasm;
+ while ((u8*)disasmPtr < end)
+ {
+ char inst_disasm[256];
+ disasmPtr += m_disasm.disasm64(disasmPtr, disasmPtr, (u8*)disasmPtr, inst_disasm);
+ x86_disasm << inst_disasm << std::endl;
+ (*host_instructions_count)++;
+ }
+
+ return x86_disasm.str();
+}
+#endif
+
+std::unique_ptr<HostDisassembler> GetNewDisassembler(const std::string& arch)
+{
+#if defined(HAVE_LLVM)
+ if (arch == "x86")
+ return std::make_unique<HostDisassemblerLLVM>("x86_64-none-unknown");
+ if (arch == "aarch64")
+ return std::make_unique<HostDisassemblerLLVM>("aarch64-none-unknown", 4, "cortex-a57");
+ if (arch == "armv7")
+ return std::make_unique<HostDisassemblerLLVM>("armv7-none-unknown", 4, "cortex-a15");
+#elif defined(_M_X86_64)
+ if (arch == "x86")
+ return std::make_unique<HostDisassemblerX86>();
+#endif
+ return std::make_unique<HostDisassembler>();
+}
+
+DisassembleResult DisassembleBlock(HostDisassembler* disasm, u32 address)
+{
+ auto res = Core::System::GetInstance().GetJitInterface().GetHostCode(address);
+
+ return std::visit(overloaded{[&](JitInterface::GetHostCodeError error) {
+ DisassembleResult result;
+ switch (error)
+ {
+ case JitInterface::GetHostCodeError::NoJitActive:
+ result.text = "(No JIT active)";
+ break;
+ case JitInterface::GetHostCodeError::NoTranslation:
+ result.text = "(No translation)";
+ break;
+ default:
+ ASSERT(false);
+ break;
+ }
+ result.entry_address = address;
+ result.instruction_count = 0;
+ result.code_size = 0;
+ return result;
+ },
+ [&](JitInterface::GetHostCodeResult host_result) {
+ DisassembleResult new_result;
+ u32 instruction_count = 0;
+ new_result.text = disasm->DisassembleHostBlock(
+ host_result.code, host_result.code_size, &instruction_count,
+ (u64)host_result.code);
+ new_result.entry_address = host_result.entry_address;
+ new_result.code_size = host_result.code_size;
+ new_result.instruction_count = instruction_count;
+ return new_result;
+ }},
+ res);
+}