[Base] Linux Arm64 exception handler
This commit is contained in:
parent
2d5602447e
commit
d51fafd07c
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/base/exception_handler.h"
|
||||
|
||||
namespace xe {
|
||||
|
||||
// Based on VIXL Instruction::IsLoad and IsStore.
|
||||
// https://github.com/Linaro/vixl/blob/d48909dd0ac62197edb75d26ed50927e4384a199/src/aarch64/instructions-aarch64.cc#L484
|
||||
//
|
||||
// Copyright 2015, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
bool IsArm64LoadPrefetchStore(uint32_t instruction, bool& is_store_out) {
|
||||
if ((instruction & kArm64LoadStoreAnyMask) != kArm64LoadStoreAnyValue) {
|
||||
return false;
|
||||
}
|
||||
if ((instruction & kArm64LoadStorePairAnyMask) ==
|
||||
kArm64LoadStorePairAnyValue) {
|
||||
is_store_out = !(instruction & kArm64LoadStorePairLoadBit);
|
||||
return true;
|
||||
}
|
||||
switch (Arm64LoadStoreOp(instruction & kArm64LoadStoreMask)) {
|
||||
case Arm64LoadStoreOp::kLDRB_w:
|
||||
case Arm64LoadStoreOp::kLDRH_w:
|
||||
case Arm64LoadStoreOp::kLDR_w:
|
||||
case Arm64LoadStoreOp::kLDR_x:
|
||||
case Arm64LoadStoreOp::kLDRSB_x:
|
||||
case Arm64LoadStoreOp::kLDRSH_x:
|
||||
case Arm64LoadStoreOp::kLDRSW_x:
|
||||
case Arm64LoadStoreOp::kLDRSB_w:
|
||||
case Arm64LoadStoreOp::kLDRSH_w:
|
||||
case Arm64LoadStoreOp::kLDR_b:
|
||||
case Arm64LoadStoreOp::kLDR_h:
|
||||
case Arm64LoadStoreOp::kLDR_s:
|
||||
case Arm64LoadStoreOp::kLDR_d:
|
||||
case Arm64LoadStoreOp::kLDR_q:
|
||||
case Arm64LoadStoreOp::kPRFM:
|
||||
is_store_out = false;
|
||||
return true;
|
||||
case Arm64LoadStoreOp::kSTRB_w:
|
||||
case Arm64LoadStoreOp::kSTRH_w:
|
||||
case Arm64LoadStoreOp::kSTR_w:
|
||||
case Arm64LoadStoreOp::kSTR_x:
|
||||
case Arm64LoadStoreOp::kSTR_b:
|
||||
case Arm64LoadStoreOp::kSTR_h:
|
||||
case Arm64LoadStoreOp::kSTR_s:
|
||||
case Arm64LoadStoreOp::kSTR_d:
|
||||
case Arm64LoadStoreOp::kSTR_q:
|
||||
is_store_out = true;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace xe
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -10,14 +10,80 @@
|
|||
#ifndef XENIA_BASE_EXCEPTION_HANDLER_H_
|
||||
#define XENIA_BASE_EXCEPTION_HANDLER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/x64_context.h"
|
||||
#include "xenia/base/host_thread_context.h"
|
||||
|
||||
namespace xe {
|
||||
|
||||
// AArch64 load and store decoding based on VIXL.
|
||||
// https://github.com/Linaro/vixl/blob/ae5957cd66517b3f31dbf37e9bf39db6594abfe3/src/aarch64/constants-aarch64.h
|
||||
//
|
||||
// Copyright 2015, VIXL authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
constexpr uint32_t kArm64LoadStoreAnyMask = UINT32_C(0x0A000000);
|
||||
constexpr uint32_t kArm64LoadStoreAnyValue = UINT32_C(0x08000000);
|
||||
constexpr uint32_t kArm64LoadStorePairAnyMask = UINT32_C(0x3A000000);
|
||||
constexpr uint32_t kArm64LoadStorePairAnyValue = UINT32_C(0x28000000);
|
||||
constexpr uint32_t kArm64LoadStorePairLoadBit = UINT32_C(1) << 22;
|
||||
constexpr uint32_t kArm64LoadStoreMask = UINT32_C(0xC4C00000);
|
||||
|
||||
enum class Arm64LoadStoreOp : uint32_t {
|
||||
kSTRB_w = UINT32_C(0x00000000),
|
||||
kSTRH_w = UINT32_C(0x40000000),
|
||||
kSTR_w = UINT32_C(0x80000000),
|
||||
kSTR_x = UINT32_C(0xC0000000),
|
||||
kLDRB_w = UINT32_C(0x00400000),
|
||||
kLDRH_w = UINT32_C(0x40400000),
|
||||
kLDR_w = UINT32_C(0x80400000),
|
||||
kLDR_x = UINT32_C(0xC0400000),
|
||||
kLDRSB_x = UINT32_C(0x00800000),
|
||||
kLDRSH_x = UINT32_C(0x40800000),
|
||||
kLDRSW_x = UINT32_C(0x80800000),
|
||||
kLDRSB_w = UINT32_C(0x00C00000),
|
||||
kLDRSH_w = UINT32_C(0x40C00000),
|
||||
kSTR_b = UINT32_C(0x04000000),
|
||||
kSTR_h = UINT32_C(0x44000000),
|
||||
kSTR_s = UINT32_C(0x84000000),
|
||||
kSTR_d = UINT32_C(0xC4000000),
|
||||
kSTR_q = UINT32_C(0x04800000),
|
||||
kLDR_b = UINT32_C(0x04400000),
|
||||
kLDR_h = UINT32_C(0x44400000),
|
||||
kLDR_s = UINT32_C(0x84400000),
|
||||
kLDR_d = UINT32_C(0xC4400000),
|
||||
kLDR_q = UINT32_C(0x04C00000),
|
||||
kPRFM = UINT32_C(0xC0800000),
|
||||
};
|
||||
|
||||
bool IsArm64LoadPrefetchStore(uint32_t instruction, bool& is_store_out);
|
||||
|
||||
class Exception {
|
||||
public:
|
||||
enum class Code {
|
||||
|
@ -32,7 +98,7 @@ class Exception {
|
|||
kWrite,
|
||||
};
|
||||
|
||||
void InitializeAccessViolation(X64Context* thread_context,
|
||||
void InitializeAccessViolation(HostThreadContext* thread_context,
|
||||
uint64_t fault_address,
|
||||
AccessViolationOperation operation) {
|
||||
code_ = Code::kAccessViolation;
|
||||
|
@ -40,7 +106,7 @@ class Exception {
|
|||
fault_address_ = fault_address;
|
||||
access_violation_operation_ = operation;
|
||||
}
|
||||
void InitializeIllegalInstruction(X64Context* thread_context) {
|
||||
void InitializeIllegalInstruction(HostThreadContext* thread_context) {
|
||||
code_ = Code::kIllegalInstruction;
|
||||
thread_context_ = thread_context;
|
||||
}
|
||||
|
@ -48,24 +114,30 @@ class Exception {
|
|||
Code code() const { return code_; }
|
||||
|
||||
// Returns the platform-specific thread context info.
|
||||
X64Context* thread_context() const { return thread_context_; }
|
||||
HostThreadContext* thread_context() const { return thread_context_; }
|
||||
|
||||
#if XE_ARCH_AMD64
|
||||
// Returns the program counter where the exception occurred.
|
||||
// RIP on x64.
|
||||
uint64_t pc() const { return thread_context_->rip; }
|
||||
// Sets the program counter where execution will resume.
|
||||
void set_resume_pc(uint64_t pc) { thread_context_->rip = pc; }
|
||||
#else
|
||||
// Returns the program counter where the exception occurred.
|
||||
// RIP on x64.
|
||||
uint64_t pc() const {
|
||||
#if XE_ARCH_AMD64
|
||||
return thread_context_->rip;
|
||||
#elif XE_ARCH_ARM64
|
||||
return thread_context_->pc;
|
||||
#else
|
||||
assert_always();
|
||||
return 0;
|
||||
#endif // XE_ARCH
|
||||
}
|
||||
|
||||
// Sets the program counter where execution will resume.
|
||||
void set_resume_pc(uint64_t pc) { assert_always(); }
|
||||
#endif
|
||||
void set_resume_pc(uint64_t pc) {
|
||||
#if XE_ARCH_AMD64
|
||||
thread_context_->rip = pc;
|
||||
#elif XE_ARCH_ARM64
|
||||
thread_context_->pc = pc;
|
||||
#else
|
||||
assert_always();
|
||||
#endif // XE_ARCH
|
||||
}
|
||||
|
||||
// In case of AV, address that was read from/written to.
|
||||
uint64_t fault_address() const { return fault_address_; }
|
||||
|
@ -77,7 +149,7 @@ class Exception {
|
|||
|
||||
private:
|
||||
Code code_ = Code::kInvalidException;
|
||||
X64Context* thread_context_ = nullptr;
|
||||
HostThreadContext* thread_context_ = nullptr;
|
||||
uint64_t fault_address_ = 0;
|
||||
AccessViolationOperation access_violation_operation_ =
|
||||
AccessViolationOperation::kUnknown;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2017 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -13,7 +13,10 @@
|
|||
#include <ucontext.h>
|
||||
#include <cstdint>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/host_thread_context.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
namespace xe {
|
||||
|
||||
|
@ -35,8 +38,9 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
|||
mcontext_t& mcontext =
|
||||
reinterpret_cast<ucontext_t*>(signal_context)->uc_mcontext;
|
||||
|
||||
X64Context thread_context;
|
||||
HostThreadContext thread_context;
|
||||
|
||||
#if XE_ARCH_AMD64
|
||||
thread_context.rip = uint64_t(mcontext.gregs[REG_RIP]);
|
||||
thread_context.eflags = uint32_t(mcontext.gregs[REG_EFL]);
|
||||
thread_context.rax = uint64_t(mcontext.gregs[REG_RAX]);
|
||||
|
@ -57,6 +61,40 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
|||
thread_context.r15 = uint64_t(mcontext.gregs[REG_R15]);
|
||||
std::memcpy(thread_context.xmm_registers, mcontext.fpregs->_xmm,
|
||||
sizeof(thread_context.xmm_registers));
|
||||
#elif XE_ARCH_ARM64
|
||||
std::memcpy(thread_context.x, mcontext.regs, sizeof(thread_context.x));
|
||||
thread_context.sp = mcontext.sp;
|
||||
thread_context.pc = mcontext.pc;
|
||||
thread_context.pstate = mcontext.pstate;
|
||||
struct fpsimd_context* mcontext_fpsimd = nullptr;
|
||||
struct esr_context* mcontext_esr = nullptr;
|
||||
for (struct _aarch64_ctx* mcontext_extension =
|
||||
reinterpret_cast<struct _aarch64_ctx*>(mcontext.__reserved);
|
||||
mcontext_extension->magic;
|
||||
mcontext_extension = reinterpret_cast<struct _aarch64_ctx*>(
|
||||
reinterpret_cast<uint8_t*>(mcontext_extension) +
|
||||
mcontext_extension->size)) {
|
||||
switch (mcontext_extension->magic) {
|
||||
case FPSIMD_MAGIC:
|
||||
mcontext_fpsimd =
|
||||
reinterpret_cast<struct fpsimd_context*>(mcontext_extension);
|
||||
break;
|
||||
case ESR_MAGIC:
|
||||
mcontext_esr =
|
||||
reinterpret_cast<struct esr_context*>(mcontext_extension);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert_not_null(mcontext_fpsimd);
|
||||
if (mcontext_fpsimd) {
|
||||
thread_context.fpsr = mcontext_fpsimd->fpsr;
|
||||
thread_context.fpcr = mcontext_fpsimd->fpcr;
|
||||
std::memcpy(thread_context.v, mcontext_fpsimd->vregs,
|
||||
sizeof(thread_context.v));
|
||||
}
|
||||
#endif // XE_ARCH
|
||||
|
||||
Exception ex;
|
||||
switch (signal_number) {
|
||||
|
@ -64,12 +102,53 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
|||
ex.InitializeIllegalInstruction(&thread_context);
|
||||
break;
|
||||
case SIGSEGV: {
|
||||
Exception::AccessViolationOperation access_violation_operation;
|
||||
#if XE_ARCH_AMD64
|
||||
// x86_pf_error_code::X86_PF_WRITE
|
||||
constexpr uint64_t kX86PageFaultErrorCodeWrite = UINT64_C(1) << 1;
|
||||
Exception::AccessViolationOperation access_violation_operation =
|
||||
access_violation_operation =
|
||||
(uint64_t(mcontext.gregs[REG_ERR]) & kX86PageFaultErrorCodeWrite)
|
||||
? Exception::AccessViolationOperation::kWrite
|
||||
: Exception::AccessViolationOperation::kRead;
|
||||
#elif XE_ARCH_ARM64
|
||||
// For a Data Abort (EC - ESR_EL1 bits 31:26 - 0b100100 from a lower
|
||||
// Exception Level, 0b100101 without a change in the Exception Level),
|
||||
// bit 6 is 0 for reading from a memory location, 1 for writing to a
|
||||
// memory location.
|
||||
if (mcontext_esr && ((mcontext_esr->esr >> 26) & 0b111110) == 0b100100) {
|
||||
access_violation_operation =
|
||||
(mcontext_esr->esr & (UINT64_C(1) << 6))
|
||||
? Exception::AccessViolationOperation::kWrite
|
||||
: Exception::AccessViolationOperation::kRead;
|
||||
} else {
|
||||
// Determine the memory access direction based on which instruction has
|
||||
// requested it.
|
||||
// esr_context may be unavailable on certain hosts (for instance, on
|
||||
// Android, it was added only in NDK r16 - which is the first NDK
|
||||
// version to support the Android API level 27, while NDK r15 doesn't
|
||||
// have esr_context in its API 26 sigcontext.h).
|
||||
// On AArch64 (unlike on AArch32), the program counter is the address of
|
||||
// the currently executing instruction.
|
||||
bool instruction_is_store;
|
||||
if (IsArm64LoadPrefetchStore(
|
||||
*reinterpret_cast<const uint32_t*>(mcontext.pc),
|
||||
instruction_is_store)) {
|
||||
access_violation_operation =
|
||||
instruction_is_store ? Exception::AccessViolationOperation::kWrite
|
||||
: Exception::AccessViolationOperation::kRead;
|
||||
} else {
|
||||
assert_always(
|
||||
"No ESR in the exception thread context, or it's not a Data "
|
||||
"Abort, and the faulting instruction is not a known load, "
|
||||
"prefetch or store instruction");
|
||||
access_violation_operation =
|
||||
Exception::AccessViolationOperation::kUnknown;
|
||||
}
|
||||
}
|
||||
#else
|
||||
access_violation_operation =
|
||||
Exception::AccessViolationOperation::kUnknown;
|
||||
#endif // XE_ARCH
|
||||
ex.InitializeAccessViolation(
|
||||
&thread_context, reinterpret_cast<uint64_t>(signal_info->si_addr),
|
||||
access_violation_operation);
|
||||
|
@ -82,7 +161,11 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
|||
if (handlers_[i].first(&ex, handlers_[i].second)) {
|
||||
// Exception handled.
|
||||
// TODO(benvanik): Update all thread state? Dirty flags?
|
||||
#if XE_ARCH_AMD64
|
||||
mcontext.gregs[REG_RIP] = thread_context.rip;
|
||||
#elif XE_ARCH_ARM64
|
||||
mcontext.pc = thread_context.pc;
|
||||
#endif // XE_ARCH
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,8 +35,7 @@ LONG CALLBACK ExceptionHandlerCallback(PEXCEPTION_POINTERS ex_info) {
|
|||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
// TODO(benvanik): avoid this by mapping X64Context virtual?
|
||||
X64Context thread_context;
|
||||
HostThreadContext thread_context;
|
||||
thread_context.rip = ex_info->ContextRecord->Rip;
|
||||
thread_context.eflags = ex_info->ContextRecord->EFlags;
|
||||
std::memcpy(thread_context.int_registers, &ex_info->ContextRecord->Rax,
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/base/host_thread_context.h"
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/platform.h"
|
||||
#include "xenia/base/string_util.h"
|
||||
|
||||
namespace xe {
|
||||
|
||||
// NOTE: this order matches 1:1 with the HostRegister enums.
|
||||
static const char* kRegisterNames[] = {
|
||||
#if XE_ARCH_AMD64
|
||||
"rip", "eflags", "rax", "rcx", "rdx", "rbx", "rsp",
|
||||
"rbp", "rsi", "rdi", "r8", "r9", "r10", "r11",
|
||||
"r12", "r13", "r14", "r15", "xmm0", "xmm1", "xmm2",
|
||||
"xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9",
|
||||
"xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15",
|
||||
#elif XE_ARCH_ARM64
|
||||
"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9",
|
||||
"x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19",
|
||||
"x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29",
|
||||
"x30", "sp", "pc", "pstate", "fpsr", "fpcr", "q0", "q1", "q2", "q3",
|
||||
"q4", "q5", "q6", "q7", "q8", "q9", "q10", "q11", "q12", "q13",
|
||||
"q14", "q15", "q16", "q17", "q18", "q19", "q20", "q21", "q22", "q23",
|
||||
"q24", "q25", "q26", "q27", "q28", "q29", "q30", "q31",
|
||||
#endif // XE_ARCH
|
||||
};
|
||||
|
||||
const char* HostThreadContext::GetRegisterName(HostRegister reg) {
|
||||
return kRegisterNames[int(reg)];
|
||||
}
|
||||
|
||||
std::string HostThreadContext::GetStringFromValue(HostRegister reg,
|
||||
bool hex) const {
|
||||
#if XE_ARCH_AMD64
|
||||
switch (reg) {
|
||||
case X64Register::kRip:
|
||||
return hex ? string_util::to_hex_string(rip) : std::to_string(rip);
|
||||
case X64Register::kEflags:
|
||||
return hex ? string_util::to_hex_string(eflags) : std::to_string(eflags);
|
||||
default:
|
||||
if (int(reg) >= int(X64Register::kRax) &&
|
||||
int(reg) <= int(X64Register::kR15)) {
|
||||
auto value = int_registers[int(reg) - int(X64Register::kRax)];
|
||||
return hex ? string_util::to_hex_string(value) : std::to_string(value);
|
||||
} else if (int(reg) >= int(X64Register::kXmm0) &&
|
||||
int(reg) <= int(X64Register::kXmm15)) {
|
||||
auto value = xmm_registers[int(reg) - int(X64Register::kXmm0)];
|
||||
return hex ? string_util::to_hex_string(value) : xe::to_string(value);
|
||||
} else {
|
||||
assert_unhandled_case(reg);
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
#elif XE_ARCH_ARM64
|
||||
switch (reg) {
|
||||
case Arm64Register::kSp:
|
||||
return hex ? string_util::to_hex_string(sp) : std::to_string(sp);
|
||||
case Arm64Register::kPc:
|
||||
return hex ? string_util::to_hex_string(pc) : std::to_string(pc);
|
||||
case Arm64Register::kPstate:
|
||||
return hex ? string_util::to_hex_string(pstate) : std::to_string(pstate);
|
||||
case Arm64Register::kFpsr:
|
||||
return hex ? string_util::to_hex_string(fpsr) : std::to_string(fpsr);
|
||||
case Arm64Register::kFpcr:
|
||||
return hex ? string_util::to_hex_string(fpcr) : std::to_string(fpcr);
|
||||
default:
|
||||
if (int(reg) >= int(Arm64Register::kX0) &&
|
||||
int(reg) <= int(Arm64Register::kX30)) {
|
||||
auto value = x[int(reg) - int(Arm64Register::kX0)];
|
||||
return hex ? string_util::to_hex_string(value) : std::to_string(value);
|
||||
} else if (int(reg) >= int(Arm64Register::kV0) &&
|
||||
int(reg) <= int(Arm64Register::kV31)) {
|
||||
auto value = v[int(reg) - int(Arm64Register::kV0)];
|
||||
return hex ? string_util::to_hex_string(value) : xe::to_string(value);
|
||||
} else {
|
||||
assert_unhandled_case(reg);
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
#else
|
||||
assert_always(
|
||||
"HostThreadContext::GetStringFromValue not implemented for the target "
|
||||
"CPU architecture");
|
||||
return std::string();
|
||||
#endif // XE_ARCH
|
||||
}
|
||||
|
||||
} // namespace xe
|
|
@ -2,13 +2,13 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_BASE_X64_CONTEXT_H_
|
||||
#define XENIA_BASE_X64_CONTEXT_H_
|
||||
#ifndef XENIA_BASE_HOST_THREAD_CONTEXT_H_
|
||||
#define XENIA_BASE_HOST_THREAD_CONTEXT_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
@ -22,12 +22,10 @@
|
|||
|
||||
namespace xe {
|
||||
|
||||
class X64Context;
|
||||
// NOTE: The order of the registers in the enumerations must match the order in
|
||||
// the string table in host_thread_context.cc.
|
||||
|
||||
#if XE_ARCH_AMD64
|
||||
enum class X64Register {
|
||||
// NOTE: this order matches 1:1 with the order in the X64Context.
|
||||
// NOTE: this order matches 1:1 with a string table in the x64_context.cc.
|
||||
kRip,
|
||||
kEflags,
|
||||
kRax,
|
||||
|
@ -64,8 +62,92 @@ enum class X64Register {
|
|||
kXmm15,
|
||||
};
|
||||
|
||||
class X64Context {
|
||||
enum class Arm64Register {
|
||||
kX0,
|
||||
kX1,
|
||||
kX2,
|
||||
kX3,
|
||||
kX4,
|
||||
kX5,
|
||||
kX6,
|
||||
kX7,
|
||||
kX8,
|
||||
kX9,
|
||||
kX10,
|
||||
kX11,
|
||||
kX12,
|
||||
kX13,
|
||||
kX14,
|
||||
kX15,
|
||||
kX16,
|
||||
kX17,
|
||||
kX18,
|
||||
kX19,
|
||||
kX20,
|
||||
kX21,
|
||||
kX22,
|
||||
kX23,
|
||||
kX24,
|
||||
kX25,
|
||||
kX26,
|
||||
kX27,
|
||||
kX28,
|
||||
// FP (frame pointer).
|
||||
kX29,
|
||||
// LR (link register).
|
||||
kX30,
|
||||
kSp,
|
||||
kPc,
|
||||
kPstate,
|
||||
kFpsr,
|
||||
kFpcr,
|
||||
// In assembly, the whole 128 bits of the Neon vector registers are accessible
|
||||
// as Q# (quadword registers). VFP also uses these registers.
|
||||
kV0,
|
||||
kV1,
|
||||
kV2,
|
||||
kV3,
|
||||
kV4,
|
||||
kV5,
|
||||
kV6,
|
||||
kV7,
|
||||
kV8,
|
||||
kV9,
|
||||
kV10,
|
||||
kV11,
|
||||
kV12,
|
||||
kV13,
|
||||
kV14,
|
||||
kV15,
|
||||
kV16,
|
||||
kV17,
|
||||
kV18,
|
||||
kV19,
|
||||
kV20,
|
||||
kV21,
|
||||
kV22,
|
||||
kV23,
|
||||
kV24,
|
||||
kV25,
|
||||
kV26,
|
||||
kV27,
|
||||
kV28,
|
||||
kV29,
|
||||
kV30,
|
||||
kV31,
|
||||
};
|
||||
|
||||
#if XE_ARCH_AMD64
|
||||
using HostRegister = X64Register;
|
||||
#elif XE_ARCH_ARM64
|
||||
using HostRegister = Arm64Register;
|
||||
#else
|
||||
enum class HostRegister {};
|
||||
#endif // XE_ARCH
|
||||
|
||||
class HostThreadContext {
|
||||
public:
|
||||
#if XE_ARCH_AMD64
|
||||
uint64_t rip;
|
||||
uint32_t eflags;
|
||||
union {
|
||||
|
@ -89,7 +171,6 @@ class X64Context {
|
|||
};
|
||||
uint64_t int_registers[16];
|
||||
};
|
||||
|
||||
union {
|
||||
struct {
|
||||
vec128_t xmm0;
|
||||
|
@ -111,12 +192,19 @@ class X64Context {
|
|||
};
|
||||
vec128_t xmm_registers[16];
|
||||
};
|
||||
#elif XE_ARCH_ARM64
|
||||
uint64_t x[31];
|
||||
uint64_t sp;
|
||||
uint64_t pc;
|
||||
uint64_t pstate;
|
||||
uint32_t fpsr;
|
||||
uint32_t fpcr;
|
||||
vec128_t v[32];
|
||||
#endif // XE_ARCH
|
||||
|
||||
static const char* GetRegisterName(X64Register reg);
|
||||
std::string GetStringFromValue(X64Register reg, bool hex) const;
|
||||
void SetValueFromString(X64Register reg, std::string value, bool hex);
|
||||
static const char* GetRegisterName(HostRegister reg);
|
||||
std::string GetStringFromValue(HostRegister reg, bool hex) const;
|
||||
};
|
||||
#endif // XE_ARCH_AMD64
|
||||
|
||||
} // namespace xe
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/base/x64_context.h"
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/platform.h"
|
||||
#include "xenia/base/string_util.h"
|
||||
|
||||
namespace xe {
|
||||
|
||||
#if XE_ARCH_AMD64
|
||||
|
||||
// NOTE: this order matches 1:1 with the X64Register enum.
|
||||
static const char* kRegisterNames[] = {
|
||||
"rip", "eflags", "rax", "rcx", "rdx", "rbx", "rsp",
|
||||
"rbp", "rsi", "rdi", "r8", "r9", "r10", "r11",
|
||||
"r12", "r13", "r14", "r15", "xmm0", "xmm1", "xmm2",
|
||||
"xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9",
|
||||
"xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15",
|
||||
};
|
||||
|
||||
const char* X64Context::GetRegisterName(X64Register reg) {
|
||||
return kRegisterNames[static_cast<int>(reg)];
|
||||
}
|
||||
|
||||
std::string X64Context::GetStringFromValue(X64Register reg, bool hex) const {
|
||||
switch (reg) {
|
||||
case X64Register::kRip:
|
||||
return hex ? string_util::to_hex_string(rip) : std::to_string(rip);
|
||||
case X64Register::kEflags:
|
||||
return hex ? string_util::to_hex_string(eflags) : std::to_string(eflags);
|
||||
default:
|
||||
if (static_cast<int>(reg) >= static_cast<int>(X64Register::kRax) &&
|
||||
static_cast<int>(reg) <= static_cast<int>(X64Register::kR15)) {
|
||||
auto value = int_registers[static_cast<int>(reg) -
|
||||
static_cast<int>(X64Register::kRax)];
|
||||
return hex ? string_util::to_hex_string(value) : std::to_string(value);
|
||||
} else if (static_cast<int>(reg) >=
|
||||
static_cast<int>(X64Register::kXmm0) &&
|
||||
static_cast<int>(reg) <=
|
||||
static_cast<int>(X64Register::kXmm15)) {
|
||||
auto value = xmm_registers[static_cast<int>(reg) -
|
||||
static_cast<int>(X64Register::kXmm0)];
|
||||
return hex ? string_util::to_hex_string(value) : xe::to_string(value);
|
||||
} else {
|
||||
assert_unhandled_case(reg);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void X64Context::SetValueFromString(X64Register reg, std::string value,
|
||||
bool hex) {
|
||||
// TODO(benvanik): set value from string.
|
||||
assert_always(false);
|
||||
}
|
||||
|
||||
#endif // XE_ARCH_AMD64
|
||||
|
||||
} // namespace xe
|
|
@ -163,7 +163,7 @@ std::unique_ptr<GuestFunction> X64Backend::CreateGuestFunction(
|
|||
return std::make_unique<X64Function>(module, address);
|
||||
}
|
||||
|
||||
uint64_t ReadCapstoneReg(X64Context* context, x86_reg reg) {
|
||||
uint64_t ReadCapstoneReg(HostThreadContext* context, x86_reg reg) {
|
||||
switch (reg) {
|
||||
case X86_REG_RAX:
|
||||
return context->rax;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
namespace xe {
|
||||
class Exception;
|
||||
class X64Context;
|
||||
class HostThreadContext;
|
||||
} // namespace xe
|
||||
|
||||
namespace xe {
|
||||
|
|
|
@ -805,8 +805,8 @@ bool Processor::ResumeAllThreads() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void Processor::UpdateThreadExecutionStates(uint32_t override_thread_id,
|
||||
X64Context* override_context) {
|
||||
void Processor::UpdateThreadExecutionStates(
|
||||
uint32_t override_thread_id, HostThreadContext* override_context) {
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
uint64_t frame_host_pcs[64];
|
||||
xe::cpu::StackFrame cpu_frames[64];
|
||||
|
@ -828,7 +828,7 @@ void Processor::UpdateThreadExecutionStates(uint32_t override_thread_id,
|
|||
|
||||
// Grab stack trace and X64 context then resolve all symbols.
|
||||
uint64_t hash;
|
||||
X64Context* in_host_context = nullptr;
|
||||
HostThreadContext* in_host_context = nullptr;
|
||||
if (override_thread_id == thread_info->thread_id) {
|
||||
// If we were passed an override context we use that. Otherwise, ask the
|
||||
// stack walker for a new context.
|
||||
|
|
|
@ -213,8 +213,9 @@ class Processor {
|
|||
// Updates all cached thread execution info (state, call stacks, etc).
|
||||
// The given override thread handle and context will be used in place of
|
||||
// sampled values for that thread.
|
||||
void UpdateThreadExecutionStates(uint32_t override_handle = 0,
|
||||
X64Context* override_context = nullptr);
|
||||
void UpdateThreadExecutionStates(
|
||||
uint32_t override_handle = 0,
|
||||
HostThreadContext* override_context = nullptr);
|
||||
|
||||
// Suspends all breakpoints, uninstalling them as required.
|
||||
// No breakpoints will be triggered until they are resumed.
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "xenia/base/x64_context.h"
|
||||
#include "xenia/base/host_thread_context.h"
|
||||
#include "xenia/cpu/function.h"
|
||||
|
||||
namespace xe {
|
||||
|
@ -83,8 +83,8 @@ class StackWalker {
|
|||
virtual size_t CaptureStackTrace(void* thread_handle,
|
||||
uint64_t* frame_host_pcs,
|
||||
size_t frame_offset, size_t frame_count,
|
||||
const X64Context* in_host_context,
|
||||
X64Context* out_host_context,
|
||||
const HostThreadContext* in_host_context,
|
||||
HostThreadContext* out_host_context,
|
||||
uint64_t* out_stack_hash = nullptr) = 0;
|
||||
|
||||
// Resolves symbol information for the given stack frames.
|
||||
|
|
|
@ -153,8 +153,8 @@ class Win32StackWalker : public StackWalker {
|
|||
|
||||
size_t CaptureStackTrace(void* thread_handle, uint64_t* frame_host_pcs,
|
||||
size_t frame_offset, size_t frame_count,
|
||||
const X64Context* in_host_context,
|
||||
X64Context* out_host_context,
|
||||
const HostThreadContext* in_host_context,
|
||||
HostThreadContext* out_host_context,
|
||||
uint64_t* out_stack_hash) override {
|
||||
// TODO(benvanik): use xstate?
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/hh134240(v=vs.85).aspx
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/x64_context.h"
|
||||
#include "xenia/base/host_thread_context.h"
|
||||
#include "xenia/cpu/thread.h"
|
||||
#include "xenia/cpu/thread_state.h"
|
||||
|
||||
|
@ -70,10 +70,10 @@ struct ThreadDebugInfo {
|
|||
// Last-sampled PPC context.
|
||||
// This is updated whenever the debugger stops.
|
||||
ppc::PPCContext guest_context;
|
||||
// Last-sampled host x64 context.
|
||||
// Last-sampled host context.
|
||||
// This is updated whenever the debugger stops and must be used instead of any
|
||||
// value taken from the StackWalker as it properly respects exception stacks.
|
||||
X64Context host_context;
|
||||
HostThreadContext host_context;
|
||||
|
||||
// A single frame in a call stack.
|
||||
struct Frame {
|
||||
|
|
|
@ -960,7 +960,7 @@ void DebugWindow::DrawRegistersPane() {
|
|||
auto reg = static_cast<X64Register>(i);
|
||||
ImGui::BeginGroup();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("%3s", X64Context::GetRegisterName(reg));
|
||||
ImGui::Text("%3s", HostThreadContext::GetRegisterName(reg));
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(4, 0));
|
||||
ImGui::SameLine();
|
||||
|
@ -985,7 +985,7 @@ void DebugWindow::DrawRegistersPane() {
|
|||
static_cast<X64Register>(static_cast<int>(X64Register::kXmm0) + i);
|
||||
ImGui::BeginGroup();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("%5s", X64Context::GetRegisterName(reg));
|
||||
ImGui::Text("%5s", HostThreadContext::GetRegisterName(reg));
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(4, 0));
|
||||
ImGui::SameLine();
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/x64_context.h"
|
||||
#include "xenia/base/host_thread_context.h"
|
||||
#include "xenia/cpu/breakpoint.h"
|
||||
#include "xenia/cpu/debug_listener.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
|
|
Loading…
Reference in New Issue