Merge branch 'master' of https://github.com/xenia-project/xenia into canary_experimental
This commit is contained in:
88
src/xenia/base/exception_handler.cc
Normal file
88
src/xenia/base/exception_handler.cc
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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 & kArm64LoadLiteralFMask) == kArm64LoadLiteralFixed) {
|
||||
return true;
|
||||
}
|
||||
if ((instruction & kArm64LoadStoreAnyFMask) != kArm64LoadStoreAnyFixed) {
|
||||
return false;
|
||||
}
|
||||
if ((instruction & kArm64LoadStorePairAnyFMask) ==
|
||||
kArm64LoadStorePairAnyFixed) {
|
||||
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,97 @@
|
||||
#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.
|
||||
|
||||
// `Instruction address + literal offset` loads.
|
||||
// This includes PRFM_lit.
|
||||
constexpr uint32_t kArm64LoadLiteralFMask = UINT32_C(0x3B000000);
|
||||
constexpr uint32_t kArm64LoadLiteralFixed = UINT32_C(0x18000000);
|
||||
|
||||
constexpr uint32_t kArm64LoadStoreAnyFMask = UINT32_C(0x0A000000);
|
||||
constexpr uint32_t kArm64LoadStoreAnyFixed = UINT32_C(0x08000000);
|
||||
|
||||
constexpr uint32_t kArm64LoadStorePairAnyFMask = UINT32_C(0x3A000000);
|
||||
constexpr uint32_t kArm64LoadStorePairAnyFixed = 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),
|
||||
};
|
||||
|
||||
constexpr uint32_t kArm64LoadStoreOffsetFMask = UINT32_C(0x3B200C00);
|
||||
enum class Arm64LoadStoreOffsetFixed : uint32_t {
|
||||
kUnscaledOffset = UINT32_C(0x38000000),
|
||||
kPostIndex = UINT32_C(0x38000400),
|
||||
kPreIndex = UINT32_C(0x38000C00),
|
||||
kRegisterOffset = UINT32_C(0x38200800),
|
||||
};
|
||||
|
||||
constexpr uint32_t kArm64LoadStoreUnsignedOffsetFMask = UINT32_C(0x3B000000);
|
||||
constexpr uint32_t kArm64LoadStoreUnsignedOffsetFixed = UINT32_C(0x39000000);
|
||||
|
||||
bool IsArm64LoadPrefetchStore(uint32_t instruction, bool& is_store_out);
|
||||
|
||||
class Exception {
|
||||
public:
|
||||
enum class Code {
|
||||
@@ -32,7 +115,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 +123,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 +131,67 @@ class Exception {
|
||||
Code code() const { return code_; }
|
||||
|
||||
// Returns the platform-specific thread context info.
|
||||
X64Context* thread_context() const { return thread_context_; }
|
||||
// Note that certain registers must be modified through Modify* proxy
|
||||
// functions rather than directly:
|
||||
// x86-64:
|
||||
// - General-purpose registers (r##, r8-r15).
|
||||
// - XMM registers.
|
||||
// AArch64:
|
||||
// - General-purpose registers (Xn), including FP and LR.
|
||||
// - SIMD and floating-point registers (Vn).
|
||||
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
|
||||
}
|
||||
|
||||
#if XE_ARCH_AMD64
|
||||
// The index is relative to X64Register::kIntRegisterFirst.
|
||||
uint64_t& ModifyIntRegister(uint32_t index) {
|
||||
assert_true(index <= 15);
|
||||
modified_int_registers_ |= UINT16_C(1) << index;
|
||||
return thread_context_->int_registers[index];
|
||||
}
|
||||
uint16_t modified_int_registers() const { return modified_int_registers_; }
|
||||
vec128_t& ModifyXmmRegister(uint32_t index) {
|
||||
assert_true(index <= 15);
|
||||
modified_xmm_registers_ |= UINT16_C(1) << index;
|
||||
return thread_context_->xmm_registers[index];
|
||||
}
|
||||
uint16_t modified_xmm_registers() const { return modified_xmm_registers_; }
|
||||
#elif XE_ARCH_ARM64
|
||||
uint64_t& ModifyXRegister(uint32_t index) {
|
||||
assert_true(index <= 30);
|
||||
modified_x_registers_ |= UINT32_C(1) << index;
|
||||
return thread_context_->x[index];
|
||||
}
|
||||
uint32_t modified_x_registers() const { return modified_x_registers_; }
|
||||
vec128_t& ModifyVRegister(uint32_t index) {
|
||||
assert_true(index <= 31);
|
||||
modified_v_registers_ |= UINT32_C(1) << index;
|
||||
return thread_context_->v[index];
|
||||
}
|
||||
uint32_t modified_v_registers() const { return modified_v_registers_; }
|
||||
#endif // XE_ARCH
|
||||
|
||||
// In case of AV, address that was read from/written to.
|
||||
uint64_t fault_address() const { return fault_address_; }
|
||||
@@ -77,7 +203,14 @@ class Exception {
|
||||
|
||||
private:
|
||||
Code code_ = Code::kInvalidException;
|
||||
X64Context* thread_context_ = nullptr;
|
||||
HostThreadContext* thread_context_ = nullptr;
|
||||
#if XE_ARCH_AMD64
|
||||
uint16_t modified_int_registers_ = 0;
|
||||
uint16_t modified_xmm_registers_ = 0;
|
||||
#elif XE_ARCH_ARM64
|
||||
uint32_t modified_x_registers_ = 0;
|
||||
uint32_t modified_v_registers_ = 0;
|
||||
#endif // XE_ARCH
|
||||
uint64_t fault_address_ = 0;
|
||||
AccessViolationOperation access_violation_operation_ =
|
||||
AccessViolationOperation::kUnknown;
|
||||
|
||||
@@ -1,35 +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/exception_handler.h"
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/platform_linux.h"
|
||||
|
||||
namespace xe {
|
||||
|
||||
// This can be as large as needed, but isn't often needed.
|
||||
// As we will be sometimes firing many exceptions we want to avoid having to
|
||||
// scan the table too much or invoke many custom handlers.
|
||||
constexpr size_t kMaxHandlerCount = 8;
|
||||
|
||||
// All custom handlers, left-aligned and null terminated.
|
||||
// Executed in order.
|
||||
std::pair<ExceptionHandler::Handler, void*> handlers_[kMaxHandlerCount];
|
||||
|
||||
void ExceptionHandler::Install(Handler fn, void* data) {
|
||||
// TODO(dougvj) stub
|
||||
}
|
||||
|
||||
void ExceptionHandler::Uninstall(Handler fn, void* data) {
|
||||
// TODO(dougvj) stub
|
||||
}
|
||||
|
||||
} // namespace xe
|
||||
@@ -2,17 +2,285 @@
|
||||
******************************************************************************
|
||||
* 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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/base/exception_handler.h"
|
||||
|
||||
#include <signal.h>
|
||||
#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/math.h"
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
namespace xe {
|
||||
|
||||
// TODO(DrChat): Exception handling on linux.
|
||||
void ExceptionHandler::Install(Handler fn, void* data) {}
|
||||
void ExceptionHandler::Uninstall(Handler fn, void* data) {}
|
||||
bool signal_handlers_installed_ = false;
|
||||
struct sigaction original_sigill_handler_;
|
||||
struct sigaction original_sigsegv_handler_;
|
||||
|
||||
} // namespace xe
|
||||
// This can be as large as needed, but isn't often needed.
|
||||
// As we will be sometimes firing many exceptions we want to avoid having to
|
||||
// scan the table too much or invoke many custom handlers.
|
||||
constexpr size_t kMaxHandlerCount = 8;
|
||||
|
||||
// All custom handlers, left-aligned and null terminated.
|
||||
// Executed in order.
|
||||
std::pair<ExceptionHandler::Handler, void*> handlers_[kMaxHandlerCount];
|
||||
|
||||
static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
||||
void* signal_context) {
|
||||
mcontext_t& mcontext =
|
||||
reinterpret_cast<ucontext_t*>(signal_context)->uc_mcontext;
|
||||
|
||||
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]);
|
||||
// The REG_ order may be different than the register indices in the
|
||||
// instruction encoding.
|
||||
thread_context.rax = uint64_t(mcontext.gregs[REG_RAX]);
|
||||
thread_context.rcx = uint64_t(mcontext.gregs[REG_RCX]);
|
||||
thread_context.rdx = uint64_t(mcontext.gregs[REG_RDX]);
|
||||
thread_context.rbx = uint64_t(mcontext.gregs[REG_RBX]);
|
||||
thread_context.rsp = uint64_t(mcontext.gregs[REG_RSP]);
|
||||
thread_context.rbp = uint64_t(mcontext.gregs[REG_RBP]);
|
||||
thread_context.rsi = uint64_t(mcontext.gregs[REG_RSI]);
|
||||
thread_context.rdi = uint64_t(mcontext.gregs[REG_RDI]);
|
||||
thread_context.r8 = uint64_t(mcontext.gregs[REG_R8]);
|
||||
thread_context.r9 = uint64_t(mcontext.gregs[REG_R9]);
|
||||
thread_context.r10 = uint64_t(mcontext.gregs[REG_R10]);
|
||||
thread_context.r11 = uint64_t(mcontext.gregs[REG_R11]);
|
||||
thread_context.r12 = uint64_t(mcontext.gregs[REG_R12]);
|
||||
thread_context.r13 = uint64_t(mcontext.gregs[REG_R13]);
|
||||
thread_context.r14 = uint64_t(mcontext.gregs[REG_R14]);
|
||||
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) {
|
||||
case SIGILL:
|
||||
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;
|
||||
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);
|
||||
} break;
|
||||
default:
|
||||
assert_unhandled_case(signal_number);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < xe::countof(handlers_) && handlers_[i].first; ++i) {
|
||||
if (handlers_[i].first(&ex, handlers_[i].second)) {
|
||||
// Exception handled.
|
||||
#if XE_ARCH_AMD64
|
||||
mcontext.gregs[REG_RIP] = greg_t(thread_context.rip);
|
||||
mcontext.gregs[REG_EFL] = greg_t(thread_context.eflags);
|
||||
uint32_t modified_register_index;
|
||||
// The order must match the order in X64Register.
|
||||
static const size_t kIntRegisterMap[] = {
|
||||
REG_RAX, REG_RCX, REG_RDX, REG_RBX, REG_RSP, REG_RBP,
|
||||
REG_RSI, REG_RDI, REG_R8, REG_R9, REG_R10, REG_R11,
|
||||
REG_R12, REG_R13, REG_R14, REG_R15,
|
||||
};
|
||||
uint16_t modified_int_registers_remaining = ex.modified_int_registers();
|
||||
while (xe::bit_scan_forward(modified_int_registers_remaining,
|
||||
&modified_register_index)) {
|
||||
modified_int_registers_remaining &=
|
||||
~(UINT16_C(1) << modified_register_index);
|
||||
mcontext.gregs[kIntRegisterMap[modified_register_index]] =
|
||||
thread_context.int_registers[modified_register_index];
|
||||
}
|
||||
uint16_t modified_xmm_registers_remaining = ex.modified_xmm_registers();
|
||||
while (xe::bit_scan_forward(modified_xmm_registers_remaining,
|
||||
&modified_register_index)) {
|
||||
modified_xmm_registers_remaining &=
|
||||
~(UINT16_C(1) << modified_register_index);
|
||||
std::memcpy(&mcontext.fpregs->_xmm[modified_register_index],
|
||||
&thread_context.xmm_registers[modified_register_index],
|
||||
sizeof(vec128_t));
|
||||
}
|
||||
#elif XE_ARCH_ARM64
|
||||
uint32_t modified_register_index;
|
||||
uint32_t modified_x_registers_remaining = ex.modified_x_registers();
|
||||
while (xe::bit_scan_forward(modified_x_registers_remaining,
|
||||
&modified_register_index)) {
|
||||
modified_x_registers_remaining &=
|
||||
~(UINT32_C(1) << modified_register_index);
|
||||
mcontext.regs[modified_register_index] =
|
||||
thread_context.x[modified_register_index];
|
||||
}
|
||||
mcontext.sp = thread_context.sp;
|
||||
mcontext.pc = thread_context.pc;
|
||||
mcontext.pstate = thread_context.pstate;
|
||||
if (mcontext_fpsimd) {
|
||||
mcontext_fpsimd->fpsr = thread_context.fpsr;
|
||||
mcontext_fpsimd->fpcr = thread_context.fpcr;
|
||||
uint32_t modified_v_registers_remaining = ex.modified_v_registers();
|
||||
while (xe::bit_scan_forward(modified_v_registers_remaining,
|
||||
&modified_register_index)) {
|
||||
modified_v_registers_remaining &=
|
||||
~(UINT32_C(1) << modified_register_index);
|
||||
std::memcpy(&mcontext_fpsimd->vregs[modified_register_index],
|
||||
&thread_context.v[modified_register_index],
|
||||
sizeof(vec128_t));
|
||||
mcontext.regs[modified_register_index] =
|
||||
thread_context.x[modified_register_index];
|
||||
}
|
||||
}
|
||||
#endif // XE_ARCH
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExceptionHandler::Install(Handler fn, void* data) {
|
||||
if (!signal_handlers_installed_) {
|
||||
struct sigaction signal_handler;
|
||||
|
||||
std::memset(&signal_handler, 0, sizeof(signal_handler));
|
||||
signal_handler.sa_sigaction = ExceptionHandlerCallback;
|
||||
signal_handler.sa_flags = SA_SIGINFO;
|
||||
|
||||
if (sigaction(SIGILL, &signal_handler, &original_sigill_handler_) != 0) {
|
||||
assert_always("Failed to install new SIGILL handler");
|
||||
}
|
||||
if (sigaction(SIGSEGV, &signal_handler, &original_sigsegv_handler_) != 0) {
|
||||
assert_always("Failed to install new SIGSEGV handler");
|
||||
}
|
||||
signal_handlers_installed_ = true;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < xe::countof(handlers_); ++i) {
|
||||
if (!handlers_[i].first) {
|
||||
handlers_[i].first = fn;
|
||||
handlers_[i].second = data;
|
||||
return;
|
||||
}
|
||||
}
|
||||
assert_always("Too many exception handlers installed");
|
||||
}
|
||||
|
||||
void ExceptionHandler::Uninstall(Handler fn, void* data) {
|
||||
for (size_t i = 0; i < xe::countof(handlers_); ++i) {
|
||||
if (handlers_[i].first == fn && handlers_[i].second == data) {
|
||||
for (; i < xe::countof(handlers_) - 1; ++i) {
|
||||
handlers_[i] = handlers_[i + 1];
|
||||
}
|
||||
handlers_[i].first = nullptr;
|
||||
handlers_[i].second = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool has_any = false;
|
||||
for (size_t i = 0; i < xe::countof(handlers_); ++i) {
|
||||
if (handlers_[i].first) {
|
||||
has_any = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_any) {
|
||||
if (signal_handlers_installed_) {
|
||||
if (sigaction(SIGILL, &original_sigill_handler_, NULL) != 0) {
|
||||
assert_always("Failed to restore original SIGILL handler");
|
||||
}
|
||||
if (sigaction(SIGSEGV, &original_sigsegv_handler_, NULL) != 0) {
|
||||
assert_always("Failed to restore original SIGSEGV handler");
|
||||
}
|
||||
signal_handlers_installed_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace xe
|
||||
|
||||
@@ -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,
|
||||
@@ -79,8 +78,26 @@ LONG CALLBACK ExceptionHandlerCallback(PEXCEPTION_POINTERS ex_info) {
|
||||
for (size_t i = 0; i < xe::countof(handlers_) && handlers_[i].first; ++i) {
|
||||
if (handlers_[i].first(&ex, handlers_[i].second)) {
|
||||
// Exception handled.
|
||||
// TODO(benvanik): update all thread state? Dirty flags?
|
||||
ex_info->ContextRecord->Rip = thread_context.rip;
|
||||
ex_info->ContextRecord->EFlags = thread_context.eflags;
|
||||
uint32_t modified_register_index;
|
||||
uint16_t modified_int_registers_remaining = ex.modified_int_registers();
|
||||
while (xe::bit_scan_forward(modified_int_registers_remaining,
|
||||
&modified_register_index)) {
|
||||
modified_int_registers_remaining &=
|
||||
~(UINT16_C(1) << modified_register_index);
|
||||
(&ex_info->ContextRecord->Rax)[modified_register_index] =
|
||||
thread_context.int_registers[modified_register_index];
|
||||
}
|
||||
uint16_t modified_xmm_registers_remaining = ex.modified_xmm_registers();
|
||||
while (xe::bit_scan_forward(modified_xmm_registers_remaining,
|
||||
&modified_register_index)) {
|
||||
modified_xmm_registers_remaining &=
|
||||
~(UINT16_C(1) << modified_register_index);
|
||||
std::memcpy(&ex_info->ContextRecord->Xmm0 + modified_register_index,
|
||||
&thread_context.xmm_registers[modified_register_index],
|
||||
sizeof(vec128_t));
|
||||
}
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
}
|
||||
|
||||
95
src/xenia/base/host_thread_context.cc
Normal file
95
src/xenia/base/host_thread_context.cc
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* 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", "v0", "v1", "v2", "v3",
|
||||
"v4", "v5", "v6", "v7", "v8", "v9", "v10", "v11", "v12", "v13",
|
||||
"v14", "v15", "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23",
|
||||
"v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31",
|
||||
#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 (reg >= X64Register::kIntRegisterFirst &&
|
||||
reg <= X64Register::kIntRegisterLast) {
|
||||
auto value =
|
||||
int_registers[int(reg) - int(X64Register::kIntRegisterFirst)];
|
||||
return hex ? string_util::to_hex_string(value) : std::to_string(value);
|
||||
} else if (reg >= X64Register::kXmm0 && reg <= 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 (reg >= Arm64Register::kX0 && reg <= Arm64Register::kX30) {
|
||||
auto value = x[int(reg) - int(Arm64Register::kX0)];
|
||||
return hex ? string_util::to_hex_string(value) : std::to_string(value);
|
||||
} else if (reg >= Arm64Register::kV0 && reg <= 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,15 +22,18 @@
|
||||
|
||||
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, as well as remapping tables in
|
||||
// exception handler implementations.
|
||||
|
||||
#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,
|
||||
|
||||
kIntRegisterFirst,
|
||||
// The order matches the indices in the instruction encoding, as well as the
|
||||
// Windows CONTEXT structure.
|
||||
kRax = kIntRegisterFirst,
|
||||
kRcx,
|
||||
kRdx,
|
||||
kRbx,
|
||||
@@ -46,6 +49,8 @@ enum class X64Register {
|
||||
kR13,
|
||||
kR14,
|
||||
kR15,
|
||||
kIntRegisterLast = kR15,
|
||||
|
||||
kXmm0,
|
||||
kXmm1,
|
||||
kXmm2,
|
||||
@@ -64,8 +69,91 @@ 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,
|
||||
// The whole 128 bits of a Vn register are also known as Qn (quadword).
|
||||
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 +177,6 @@ class X64Context {
|
||||
};
|
||||
uint64_t int_registers[16];
|
||||
};
|
||||
|
||||
union {
|
||||
struct {
|
||||
vec128_t xmm0;
|
||||
@@ -111,12 +198,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
|
||||
Reference in New Issue
Block a user