// Copyright 2008 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "UICommon/Disassembler.h" #include #if defined(HAVE_LLVM) #include #include #include #elif defined(_M_X86_64) #include // 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 GetNewDisassembler(const std::string& arch) { #if defined(HAVE_LLVM) if (arch == "x86") return std::make_unique("x86_64-none-unknown"); if (arch == "aarch64") return std::make_unique("aarch64-none-unknown", 4, "cortex-a57"); if (arch == "armv7") return std::make_unique("armv7-none-unknown", 4, "cortex-a15"); #elif defined(_M_X86_64) if (arch == "x86") return std::make_unique(); #endif return std::make_unique(); } 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); }