[Linux] Swap read/write in x64 page fault handler + exception code cleanup

This commit is contained in:
Triang3l 2022-07-04 23:51:26 +03:00
parent a9cbd9cc5f
commit 40aa73f7d7
1 changed files with 35 additions and 34 deletions

View File

@ -11,6 +11,7 @@
#include <signal.h> #include <signal.h>
#include <ucontext.h> #include <ucontext.h>
#include <cstdint>
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
@ -29,61 +30,62 @@ constexpr size_t kMaxHandlerCount = 8;
// Executed in order. // Executed in order.
std::pair<ExceptionHandler::Handler, void*> handlers_[kMaxHandlerCount]; std::pair<ExceptionHandler::Handler, void*> handlers_[kMaxHandlerCount];
void ExceptionHandlerCallback(int signum, siginfo_t* siginfo, void* sigctx) { static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
ucontext_t* ctx = static_cast<ucontext_t*>(sigctx); void* signal_context) {
mcontext_t& mcontext =
reinterpret_cast<ucontext_t*>(signal_context)->uc_mcontext;
X64Context thread_context; X64Context thread_context;
thread_context.rip = ctx->uc_mcontext.gregs[REG_RIP]; thread_context.rip = uint64_t(mcontext.gregs[REG_RIP]);
thread_context.eflags = ctx->uc_mcontext.gregs[REG_EFL]; thread_context.eflags = uint32_t(mcontext.gregs[REG_EFL]);
thread_context.rax = ctx->uc_mcontext.gregs[REG_RAX]; thread_context.rax = uint64_t(mcontext.gregs[REG_RAX]);
thread_context.rcx = ctx->uc_mcontext.gregs[REG_RCX]; thread_context.rcx = uint64_t(mcontext.gregs[REG_RCX]);
thread_context.rdx = ctx->uc_mcontext.gregs[REG_RDX]; thread_context.rdx = uint64_t(mcontext.gregs[REG_RDX]);
thread_context.rbx = ctx->uc_mcontext.gregs[REG_RBX]; thread_context.rbx = uint64_t(mcontext.gregs[REG_RBX]);
thread_context.rsp = ctx->uc_mcontext.gregs[REG_RSP]; thread_context.rsp = uint64_t(mcontext.gregs[REG_RSP]);
thread_context.rbp = ctx->uc_mcontext.gregs[REG_RBP]; thread_context.rbp = uint64_t(mcontext.gregs[REG_RBP]);
thread_context.rsi = ctx->uc_mcontext.gregs[REG_RSI]; thread_context.rsi = uint64_t(mcontext.gregs[REG_RSI]);
thread_context.rdi = ctx->uc_mcontext.gregs[REG_RDI]; thread_context.rdi = uint64_t(mcontext.gregs[REG_RDI]);
thread_context.r8 = ctx->uc_mcontext.gregs[REG_R8]; thread_context.r8 = uint64_t(mcontext.gregs[REG_R8]);
thread_context.r9 = ctx->uc_mcontext.gregs[REG_R9]; thread_context.r9 = uint64_t(mcontext.gregs[REG_R9]);
thread_context.r10 = ctx->uc_mcontext.gregs[REG_R10]; thread_context.r10 = uint64_t(mcontext.gregs[REG_R10]);
thread_context.r11 = ctx->uc_mcontext.gregs[REG_R11]; thread_context.r11 = uint64_t(mcontext.gregs[REG_R11]);
thread_context.r12 = ctx->uc_mcontext.gregs[REG_R12]; thread_context.r12 = uint64_t(mcontext.gregs[REG_R12]);
thread_context.r13 = ctx->uc_mcontext.gregs[REG_R13]; thread_context.r13 = uint64_t(mcontext.gregs[REG_R13]);
thread_context.r14 = ctx->uc_mcontext.gregs[REG_R14]; thread_context.r14 = uint64_t(mcontext.gregs[REG_R14]);
thread_context.r15 = ctx->uc_mcontext.gregs[REG_R15]; thread_context.r15 = uint64_t(mcontext.gregs[REG_R15]);
std::memcpy(thread_context.xmm_registers, ctx->uc_mcontext.fpregs->_xmm, std::memcpy(thread_context.xmm_registers, mcontext.fpregs->_xmm,
sizeof(thread_context.xmm_registers)); sizeof(thread_context.xmm_registers));
Exception ex; Exception ex;
switch (signum) { switch (signal_number) {
case SIGILL: case SIGILL:
ex.InitializeIllegalInstruction(&thread_context); ex.InitializeIllegalInstruction(&thread_context);
break; break;
case SIGSEGV: { case SIGSEGV: {
// x86_pf_error_code::X86_PF_WRITE
constexpr uint64_t kX86PageFaultErrorCodeWrite = UINT64_C(1) << 1;
Exception::AccessViolationOperation access_violation_operation = Exception::AccessViolationOperation access_violation_operation =
((ucontext_t*)sigctx)->uc_mcontext.gregs[REG_ERR] & 0x2 (uint64_t(mcontext.gregs[REG_ERR]) & kX86PageFaultErrorCodeWrite)
? Exception::AccessViolationOperation::kRead ? Exception::AccessViolationOperation::kWrite
: Exception::AccessViolationOperation::kWrite; : Exception::AccessViolationOperation::kRead;
ex.InitializeAccessViolation(&thread_context, ex.InitializeAccessViolation(
reinterpret_cast<uint64_t>(siginfo->si_addr), &thread_context, reinterpret_cast<uint64_t>(signal_info->si_addr),
access_violation_operation); access_violation_operation);
} break; } break;
default: default:
XELOGE("Unhandled signum: 0x{:08X}", signum); assert_unhandled_case(signal_number);
assert_always("Unhandled signum");
} }
for (size_t i = 0; i < xe::countof(handlers_) && handlers_[i].first; ++i) { for (size_t i = 0; i < xe::countof(handlers_) && handlers_[i].first; ++i) {
if (handlers_[i].first(&ex, handlers_[i].second)) { if (handlers_[i].first(&ex, handlers_[i].second)) {
// Exception handled. // Exception handled.
// TODO(benvanik): Update all thread state? Dirty flags? // TODO(benvanik): Update all thread state? Dirty flags?
ctx->uc_mcontext.gregs[REG_RIP] = thread_context.rip; mcontext.gregs[REG_RIP] = thread_context.rip;
return; return;
} }
} }
assert_always("Unhandled exception");
} }
void ExceptionHandler::Install(Handler fn, void* data) { void ExceptionHandler::Install(Handler fn, void* data) {
@ -110,7 +112,6 @@ void ExceptionHandler::Install(Handler fn, void* data) {
return; return;
} }
} }
XELOGW("Too many exception handlers installed");
assert_always("Too many exception handlers installed"); assert_always("Too many exception handlers installed");
} }