123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- // Copyright 2008 Dolphin Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #include "Common/HostDisassembler.h"
- #include <span>
- #include <fmt/format.h>
- #include <fmt/ostream.h>
- #include <fmt/ranges.h>
- #if defined(HAVE_LLVM)
- #include <llvm-c/Disassembler.h>
- #include <llvm-c/Target.h>
- #elif defined(_M_X86_64)
- #include <disasm.h> // 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<u8*>(begin), static_cast<u64>(end - begin),
- reinterpret_cast<u64>(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<bx_address>(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> HostDisassembler::Factory(Platform arch)
- {
- switch (arch)
- {
- #if defined(HAVE_LLVM)
- case Platform::x86_64:
- return std::make_unique<HostDisassemblerLLVM>("x86_64-none-unknown");
- case Platform::aarch64:
- return std::make_unique<HostDisassemblerLLVM>("aarch64-none-unknown", "cortex-a57", 4);
- #elif defined(_M_X86_64)
- case Platform::x86_64:
- return std::make_unique<HostDisassemblerBochs>();
- #else
- case Platform{}: // warning C4065: "switch statement contains 'default' but no 'case' labels"
- #endif
- default:
- return std::make_unique<HostDisassembler>();
- }
- }
- 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;
- }
|