[Base] Linux Arm64 exception handler

This commit is contained in:
Triang3l 2022-07-05 20:46:49 +03:00
parent 2d5602447e
commit d51fafd07c
16 changed files with 477 additions and 119 deletions

View File

@ -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

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -18,7 +18,7 @@
namespace xe {
class Exception;
class X64Context;
class HostThreadContext;
} // namespace xe
namespace xe {

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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 {

View File

@ -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();

View File

@ -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"