[a64] Implement multi-arch capstone support

This commit is contained in:
Wunkolo 2024-05-09 07:24:35 -07:00
parent 6e83e2a42d
commit fbc306f702
5 changed files with 154 additions and 17 deletions

View File

@ -11,8 +11,8 @@
#include <climits>
#include "third_party/capstone/include/capstone/arm64.h"
#include "third_party/capstone/include/capstone/capstone.h"
#include "third_party/capstone/include/capstone/x86.h"
#include "xenia/base/profiling.h"
#include "xenia/base/reset_scope.h"
#include "xenia/base/string.h"
@ -33,11 +33,22 @@ namespace a64 {
using xe::cpu::hir::HIRBuilder;
A64Assembler::A64Assembler(A64Backend* backend)
: Assembler(backend), a64_backend_(backend) {}
: Assembler(backend), a64_backend_(backend), capstone_handle_(0) {
if (cs_open(CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN, &capstone_handle_) !=
CS_ERR_OK) {
assert_always("Failed to initialize capstone");
}
cs_option(capstone_handle_, CS_OPT_SYNTAX, CS_OPT_SYNTAX_INTEL);
cs_option(capstone_handle_, CS_OPT_DETAIL, CS_OPT_OFF);
}
A64Assembler::~A64Assembler() {
// Emitter must be freed before the allocator.
emitter_.reset();
if (capstone_handle_) {
cs_close(&capstone_handle_);
}
}
bool A64Assembler::Initialize() {
@ -106,7 +117,9 @@ void A64Assembler::DumpMachineCode(
size_t remaining_code_size = code_size;
uint64_t address = uint64_t(machine_code);
cs_insn insn = {0};
while (remaining_code_size) {
while (remaining_code_size &&
cs_disasm_iter(capstone_handle_, &code_ptr, &remaining_code_size,
&address, &insn)) {
// Look up source offset.
auto code_offset =
uint32_t(code_ptr - reinterpret_cast<uint8_t*>(machine_code));

View File

@ -46,6 +46,7 @@ class A64Assembler : public Assembler {
private:
A64Backend* a64_backend_;
std::unique_ptr<A64Emitter> emitter_;
uintptr_t capstone_handle_;
StringBuffer string_buffer_;
};

View File

@ -11,6 +11,9 @@
#include <cstddef>
#include "third_party/capstone/include/capstone/arm64.h"
#include "third_party/capstone/include/capstone/capstone.h"
#include "xenia/base/exception_handler.h"
#include "xenia/base/logging.h"
#include "xenia/cpu/backend/a64/a64_assembler.h"
@ -64,9 +67,21 @@ class A64ThunkEmitter : public A64Emitter {
void EmitLoadNonvolatileRegs();
};
A64Backend::A64Backend() : Backend() {}
A64Backend::A64Backend() : Backend(), code_cache_(nullptr) {
if (cs_open(CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN, &capstone_handle_) !=
CS_ERR_OK) {
assert_always("Failed to initialize capstone");
}
cs_option(capstone_handle_, CS_OPT_SYNTAX, CS_OPT_SYNTAX_INTEL);
cs_option(capstone_handle_, CS_OPT_DETAIL, CS_OPT_ON);
cs_option(capstone_handle_, CS_OPT_SKIPDATA, CS_OPT_OFF);
}
A64Backend::~A64Backend() {
if (capstone_handle_) {
cs_close(&capstone_handle_);
}
A64Emitter::FreeConstData(emitter_data_);
ExceptionHandler::Uninstall(&ExceptionCallbackThunk, this);
}
@ -132,10 +147,99 @@ std::unique_ptr<GuestFunction> A64Backend::CreateGuestFunction(
Module* module, uint32_t address) {
return std::make_unique<A64Function>(module, address);
}
uint64_t ReadCapstoneReg(HostThreadContext* context, arm64_reg reg) {
switch (reg) {
case ARM64_REG_X0:
return context->x[0];
case ARM64_REG_X1:
return context->x[1];
case ARM64_REG_X2:
return context->x[2];
case ARM64_REG_X3:
return context->x[3];
case ARM64_REG_X4:
return context->x[4];
case ARM64_REG_X5:
return context->x[5];
case ARM64_REG_X6:
return context->x[6];
case ARM64_REG_X7:
return context->x[7];
case ARM64_REG_X8:
return context->x[8];
case ARM64_REG_X9:
return context->x[9];
case ARM64_REG_X10:
return context->x[10];
case ARM64_REG_X11:
return context->x[11];
case ARM64_REG_X12:
return context->x[12];
case ARM64_REG_X13:
return context->x[13];
case ARM64_REG_X14:
return context->x[14];
case ARM64_REG_X15:
return context->x[15];
case ARM64_REG_X16:
return context->x[16];
case ARM64_REG_X17:
return context->x[17];
case ARM64_REG_X18:
return context->x[18];
case ARM64_REG_X19:
return context->x[19];
case ARM64_REG_X20:
return context->x[20];
case ARM64_REG_X21:
return context->x[21];
case ARM64_REG_X22:
return context->x[22];
case ARM64_REG_X23:
return context->x[23];
case ARM64_REG_X24:
return context->x[24];
case ARM64_REG_X25:
return context->x[25];
case ARM64_REG_X26:
return context->x[26];
case ARM64_REG_X27:
return context->x[27];
case ARM64_REG_X28:
return context->x[28];
case ARM64_REG_X29:
return context->x[29];
case ARM64_REG_X30:
return context->x[30];
default:
assert_unhandled_case(reg);
return 0;
}
}
uint64_t A64Backend::CalculateNextHostInstruction(ThreadDebugInfo* thread_info,
uint64_t current_pc) {
// TODO(wunkolo): Capstone hookup
return current_pc += 4;
auto machine_code_ptr = reinterpret_cast<const uint8_t*>(current_pc);
size_t remaining_machine_code_size = 64;
uint64_t host_address = current_pc;
cs_insn insn = {0};
cs_detail all_detail = {0};
insn.detail = &all_detail;
cs_disasm_iter(capstone_handle_, &machine_code_ptr,
&remaining_machine_code_size, &host_address, &insn);
auto& detail = all_detail.x86;
switch (insn.id) {
case ARM64_INS_B:
case ARM64_INS_BL:
case ARM64_INS_BLR:
case ARM64_INS_BR:
case ARM64_INS_RET:
// todo(wunkolo): determine next instruction
default:
// Not a branching instruction - just move over it.
return current_pc + insn.size;
}
}
void A64Backend::InstallBreakpoint(Breakpoint* breakpoint) {

View File

@ -70,7 +70,7 @@ class A64Backend : public Backend {
static bool ExceptionCallbackThunk(Exception* ex, void* data);
bool ExceptionCallback(Exception* ex);
// uintptr_t capstone_handle_ = 0;
uintptr_t capstone_handle_ = 0;
std::unique_ptr<A64CodeCache> code_cache_;
uintptr_t emitter_data_ = 0;

View File

@ -4,13 +4,37 @@ project("capstone")
kind("StaticLib")
language("C")
defines({
"CAPSTONE_X86_ATT_DISABLE",
"CAPSTONE_DIET_NO",
"CAPSTONE_X86_REDUCE_NO",
"CAPSTONE_HAS_X86",
"CAPSTONE_USE_SYS_DYN_MEM",
"_LIB",
})
filter("architecture:x86_64")
defines({
"CAPSTONE_HAS_X86",
"CAPSTONE_X86_ATT_DISABLE",
"CAPSTONE_X86_REDUCE_NO",
})
files({
"capstone/arch/X86/*.c",
"capstone/arch/X86/*.h",
"capstone/arch/X86/*.inc",
})
force_compile_as_c({
"capstone/arch/X86/**.c",
})
filter("architecture:ARM64")
defines({
"CAPSTONE_HAS_ARM64",
})
files({
"capstone/arch/AArch64/*.c",
"capstone/arch/AArch64/*.h",
"capstone/arch/AArch64/*.inc",
})
force_compile_as_c({
"capstone/arch/AArch64/**.c",
})
filter({})
includedirs({
"capstone",
"capstone/include",
@ -32,12 +56,7 @@ project("capstone")
"capstone/SStream.h",
"capstone/utils.c",
"capstone/utils.h",
"capstone/arch/X86/*.c",
"capstone/arch/X86/*.h",
"capstone/arch/X86/*.inc",
})
force_compile_as_c({
"capstone/**.c",
"capstone/arch/X86/**.c",
})
"capstone/**.c",
})