// Copyright 2008 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "Common/HostDisassembler.h" #include #include #include #include #if defined(HAVE_LLVM) #include #include #elif defined(_M_X86_64) #include // Bochs #endif #if defined(HAVE_LLVM) class HostDisassemblerLLVM final : public HostDisassembler { public: explicit HostDisassemblerLLVM(const char* host_disasm, const char* cpu = "", std::size_t inst_size = 0); ~HostDisassemblerLLVM(); private: LLVMDisasmContextRef m_llvm_context; std::size_t m_instruction_size; std::size_t Disassemble(const u8* begin, const u8* end, std::ostream& stream) override; }; HostDisassemblerLLVM::HostDisassemblerLLVM(const char* host_disasm, const char* cpu, std::size_t inst_size) : m_instruction_size(inst_size) { LLVMInitializeAllTargetInfos(); LLVMInitializeAllTargetMCs(); LLVMInitializeAllDisassemblers(); m_llvm_context = LLVMCreateDisasmCPU(host_disasm, cpu, nullptr, 0, nullptr, nullptr); // Couldn't create llvm context if (!m_llvm_context) return; LLVMSetDisasmOptions(m_llvm_context, LLVMDisassembler_Option_AsmPrinterVariant | LLVMDisassembler_Option_PrintLatency); } HostDisassemblerLLVM::~HostDisassemblerLLVM() { if (m_llvm_context) LLVMDisasmDispose(m_llvm_context); } std::size_t HostDisassemblerLLVM::Disassemble(const u8* begin, const u8* end, std::ostream& stream) { std::size_t instruction_count = 0; if (!m_llvm_context) return instruction_count; while (begin < end) { char inst_disasm[256]; const auto inst_size = LLVMDisasmInstruction(m_llvm_context, const_cast(begin), static_cast(end - begin), reinterpret_cast(begin), inst_disasm, sizeof(inst_disasm)); if (inst_size == 0) { if (m_instruction_size != 0) { // If we are on an architecture that has a fixed instruction // size, we can continue onward past this bad instruction. fmt::println(stream, "{}\tInvalid inst: {:02x}", fmt::ptr(begin), fmt::join(std::span{begin, m_instruction_size}, "")); begin += 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. fmt::println(stream, "{}\tInvalid inst: {:02x}", fmt::ptr(begin), fmt::join(std::span{begin, end}, "")); break; } } else { fmt::println(stream, "{}{}", fmt::ptr(begin), inst_disasm); begin += inst_size; } ++instruction_count; } return instruction_count; } #elif defined(_M_X86_64) class HostDisassemblerBochs final : public HostDisassembler { public: explicit HostDisassemblerBochs(); ~HostDisassemblerBochs() = default; private: disassembler m_disasm; std::size_t Disassemble(const u8* begin, const u8* end, std::ostream& stream) override; }; HostDisassemblerBochs::HostDisassemblerBochs() { m_disasm.set_syntax_intel(); } std::size_t HostDisassemblerBochs::Disassemble(const u8* begin, const u8* end, std::ostream& stream) { std::size_t instruction_count = 0; while (begin < end) { char inst_disasm[256]; const auto inst_size = m_disasm.disasm64(0, reinterpret_cast(begin), begin, inst_disasm); fmt::println(stream, "{}\t{}", fmt::ptr(begin), inst_disasm); begin += inst_size; ++instruction_count; } return instruction_count; } #endif std::unique_ptr HostDisassembler::Factory(Platform arch) { switch (arch) { #if defined(HAVE_LLVM) case Platform::x86_64: return std::make_unique("x86_64-none-unknown"); case Platform::aarch64: return std::make_unique("aarch64-none-unknown", "cortex-a57", 4); #elif defined(_M_X86_64) case Platform::x86_64: return std::make_unique(); #else case Platform{}: // warning C4065: "switch statement contains 'default' but no 'case' labels" #endif default: return std::make_unique(); } } std::size_t HostDisassembler::Disassemble(const u8* begin, const u8* end, std::ostream& stream) { fmt::println(stream, "{}\t{:02x}", fmt::ptr(begin), fmt::join(std::span{begin, end}, "")); return 0; }