HostDisassembler.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. // Copyright 2008 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "Common/HostDisassembler.h"
  4. #include <span>
  5. #include <fmt/format.h>
  6. #include <fmt/ostream.h>
  7. #include <fmt/ranges.h>
  8. #if defined(HAVE_LLVM)
  9. #include <llvm-c/Disassembler.h>
  10. #include <llvm-c/Target.h>
  11. #elif defined(_M_X86_64)
  12. #include <disasm.h> // Bochs
  13. #endif
  14. #if defined(HAVE_LLVM)
  15. class HostDisassemblerLLVM final : public HostDisassembler
  16. {
  17. public:
  18. explicit HostDisassemblerLLVM(const char* host_disasm, const char* cpu = "",
  19. std::size_t inst_size = 0);
  20. ~HostDisassemblerLLVM();
  21. private:
  22. LLVMDisasmContextRef m_llvm_context;
  23. std::size_t m_instruction_size;
  24. std::size_t Disassemble(const u8* begin, const u8* end, std::ostream& stream) override;
  25. };
  26. HostDisassemblerLLVM::HostDisassemblerLLVM(const char* host_disasm, const char* cpu,
  27. std::size_t inst_size)
  28. : m_instruction_size(inst_size)
  29. {
  30. LLVMInitializeAllTargetInfos();
  31. LLVMInitializeAllTargetMCs();
  32. LLVMInitializeAllDisassemblers();
  33. m_llvm_context = LLVMCreateDisasmCPU(host_disasm, cpu, nullptr, 0, nullptr, nullptr);
  34. // Couldn't create llvm context
  35. if (!m_llvm_context)
  36. return;
  37. LLVMSetDisasmOptions(m_llvm_context, LLVMDisassembler_Option_AsmPrinterVariant |
  38. LLVMDisassembler_Option_PrintLatency);
  39. }
  40. HostDisassemblerLLVM::~HostDisassemblerLLVM()
  41. {
  42. if (m_llvm_context)
  43. LLVMDisasmDispose(m_llvm_context);
  44. }
  45. std::size_t HostDisassemblerLLVM::Disassemble(const u8* begin, const u8* end, std::ostream& stream)
  46. {
  47. std::size_t instruction_count = 0;
  48. if (!m_llvm_context)
  49. return instruction_count;
  50. while (begin < end)
  51. {
  52. char inst_disasm[256];
  53. const auto inst_size =
  54. LLVMDisasmInstruction(m_llvm_context, const_cast<u8*>(begin), static_cast<u64>(end - begin),
  55. reinterpret_cast<u64>(begin), inst_disasm, sizeof(inst_disasm));
  56. if (inst_size == 0)
  57. {
  58. if (m_instruction_size != 0)
  59. {
  60. // If we are on an architecture that has a fixed instruction
  61. // size, we can continue onward past this bad instruction.
  62. fmt::println(stream, "{}\tInvalid inst: {:02x}", fmt::ptr(begin),
  63. fmt::join(std::span{begin, m_instruction_size}, ""));
  64. begin += m_instruction_size;
  65. }
  66. else
  67. {
  68. // We can't continue if we are on an architecture that has flexible
  69. // instruction sizes. Dump the rest of the block instead.
  70. fmt::println(stream, "{}\tInvalid inst: {:02x}", fmt::ptr(begin),
  71. fmt::join(std::span{begin, end}, ""));
  72. break;
  73. }
  74. }
  75. else
  76. {
  77. fmt::println(stream, "{}{}", fmt::ptr(begin), inst_disasm);
  78. begin += inst_size;
  79. }
  80. ++instruction_count;
  81. }
  82. return instruction_count;
  83. }
  84. #elif defined(_M_X86_64)
  85. class HostDisassemblerBochs final : public HostDisassembler
  86. {
  87. public:
  88. explicit HostDisassemblerBochs();
  89. ~HostDisassemblerBochs() = default;
  90. private:
  91. disassembler m_disasm;
  92. std::size_t Disassemble(const u8* begin, const u8* end, std::ostream& stream) override;
  93. };
  94. HostDisassemblerBochs::HostDisassemblerBochs()
  95. {
  96. m_disasm.set_syntax_intel();
  97. }
  98. std::size_t HostDisassemblerBochs::Disassemble(const u8* begin, const u8* end, std::ostream& stream)
  99. {
  100. std::size_t instruction_count = 0;
  101. while (begin < end)
  102. {
  103. char inst_disasm[256];
  104. const auto inst_size =
  105. m_disasm.disasm64(0, reinterpret_cast<bx_address>(begin), begin, inst_disasm);
  106. fmt::println(stream, "{}\t{}", fmt::ptr(begin), inst_disasm);
  107. begin += inst_size;
  108. ++instruction_count;
  109. }
  110. return instruction_count;
  111. }
  112. #endif
  113. std::unique_ptr<HostDisassembler> HostDisassembler::Factory(Platform arch)
  114. {
  115. switch (arch)
  116. {
  117. #if defined(HAVE_LLVM)
  118. case Platform::x86_64:
  119. return std::make_unique<HostDisassemblerLLVM>("x86_64-none-unknown");
  120. case Platform::aarch64:
  121. return std::make_unique<HostDisassemblerLLVM>("aarch64-none-unknown", "cortex-a57", 4);
  122. #elif defined(_M_X86_64)
  123. case Platform::x86_64:
  124. return std::make_unique<HostDisassemblerBochs>();
  125. #else
  126. case Platform{}: // warning C4065: "switch statement contains 'default' but no 'case' labels"
  127. #endif
  128. default:
  129. return std::make_unique<HostDisassembler>();
  130. }
  131. }
  132. std::size_t HostDisassembler::Disassemble(const u8* begin, const u8* end, std::ostream& stream)
  133. {
  134. fmt::println(stream, "{}\t{:02x}", fmt::ptr(begin), fmt::join(std::span{begin, end}, ""));
  135. return 0;
  136. }