From 579909104445183ad7cfcdf2de7eb602c13ce3bc Mon Sep 17 00:00:00 2001 From: Paris Oplopoios Date: Mon, 23 Sep 2024 19:19:52 +0300 Subject: [PATCH] Patch `extrq` (#943) * Use a singleton for instruction decoding * Use singleton class * Patch `EXTRQ` * Fixup signal context functions * Update CMakeLists.txt --------- Co-authored-by: georgemoralis --- CMakeLists.txt | 6 +- src/common/{disassembler.cpp => decoder.cpp} | 17 +- src/common/{disassembler.h => decoder.h} | 11 +- src/common/signal_context.cpp | 92 ++++++++ src/common/signal_context.h | 18 ++ src/core/cpu_patches.cpp | 226 ++++++++++++++++++- src/core/signals.cpp | 56 ++--- src/core/signals.h | 8 +- src/video_core/page_manager.cpp | 4 +- 9 files changed, 371 insertions(+), 67 deletions(-) rename src/common/{disassembler.cpp => decoder.cpp} (65%) rename src/common/{disassembler.h => decoder.h} (60%) create mode 100644 src/common/signal_context.cpp create mode 100644 src/common/signal_context.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d198d77..e0408ae1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -358,8 +358,8 @@ set(COMMON src/common/logging/backend.cpp src/common/config.h src/common/cstring.h src/common/debug.h - src/common/disassembler.cpp - src/common/disassembler.h + src/common/decoder.cpp + src/common/decoder.h src/common/elf_info.h src/common/endian.h src/common/enum.h @@ -378,6 +378,8 @@ set(COMMON src/common/logging/backend.cpp src/common/polyfill_thread.h src/common/rdtsc.cpp src/common/rdtsc.h + src/common/signal_context.h + src/common/signal_context.cpp src/common/singleton.h src/common/slot_vector.h src/common/string_util.cpp diff --git a/src/common/disassembler.cpp b/src/common/decoder.cpp similarity index 65% rename from src/common/disassembler.cpp rename to src/common/decoder.cpp index 2d1264a4..24990741 100644 --- a/src/common/disassembler.cpp +++ b/src/common/decoder.cpp @@ -2,18 +2,18 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include "common/disassembler.h" +#include "common/decoder.h" namespace Common { -Disassembler::Disassembler() { +DecoderImpl::DecoderImpl() { ZydisDecoderInit(&m_decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64); ZydisFormatterInit(&m_formatter, ZYDIS_FORMATTER_STYLE_INTEL); } -Disassembler::~Disassembler() = default; +DecoderImpl::~DecoderImpl() = default; -void Disassembler::printInstruction(void* code, u64 address) { +void DecoderImpl::printInstruction(void* code, u64 address) { ZydisDecodedInstruction instruction; ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT_VISIBLE]; ZyanStatus status = @@ -25,8 +25,8 @@ void Disassembler::printInstruction(void* code, u64 address) { } } -void Disassembler::printInst(ZydisDecodedInstruction& inst, ZydisDecodedOperand* operands, - u64 address) { +void DecoderImpl::printInst(ZydisDecodedInstruction& inst, ZydisDecodedOperand* operands, + u64 address) { const int bufLen = 256; char szBuffer[bufLen]; ZydisFormatterFormatInstruction(&m_formatter, &inst, operands, inst.operand_count_visible, @@ -34,4 +34,9 @@ void Disassembler::printInst(ZydisDecodedInstruction& inst, ZydisDecodedOperand* fmt::print("instruction: {}\n", szBuffer); } +ZyanStatus DecoderImpl::decodeInstruction(ZydisDecodedInstruction& inst, + ZydisDecodedOperand* operands, void* data, u64 size) { + return ZydisDecoderDecodeFull(&m_decoder, data, size, &inst, operands); +} + } // namespace Common diff --git a/src/common/disassembler.h b/src/common/decoder.h similarity index 60% rename from src/common/disassembler.h rename to src/common/decoder.h index b81f9e31..1f221959 100644 --- a/src/common/disassembler.h +++ b/src/common/decoder.h @@ -4,21 +4,26 @@ #pragma once #include +#include "common/singleton.h" #include "common/types.h" namespace Common { -class Disassembler { +class DecoderImpl { public: - Disassembler(); - ~Disassembler(); + DecoderImpl(); + ~DecoderImpl(); void printInst(ZydisDecodedInstruction& inst, ZydisDecodedOperand* operands, u64 address); void printInstruction(void* code, u64 address); + ZyanStatus decodeInstruction(ZydisDecodedInstruction& inst, ZydisDecodedOperand* operands, + void* data, u64 size = 15); private: ZydisDecoder m_decoder; ZydisFormatter m_formatter; }; +using Decoder = Common::Singleton; + } // namespace Common diff --git a/src/common/signal_context.cpp b/src/common/signal_context.cpp new file mode 100644 index 00000000..112160bc --- /dev/null +++ b/src/common/signal_context.cpp @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/arch.h" +#include "common/assert.h" +#include "common/signal_context.h" + +#ifdef _WIN32 +#include +#else +#include +#endif + +namespace Common { + +void* GetXmmPointer(void* ctx, u8 index) { +#if defined(_WIN32) +#define CASE(index) \ + case index: \ + return (void*)(&((EXCEPTION_POINTERS*)ctx)->ContextRecord->Xmm##index.Low) +#elif defined(__APPLE__) +#define CASE(index) \ + case index: \ + return (void*)(&((ucontext_t*)ctx)->uc_mcontext->__fs.__fpu_xmm##index); +#else +#define CASE(index) \ + case index: \ + return (void*)(&((ucontext_t*)ctx)->uc_mcontext.fpregs->_xmm[index].element[0]) +#endif + switch (index) { + CASE(0); + CASE(1); + CASE(2); + CASE(3); + CASE(4); + CASE(5); + CASE(6); + CASE(7); + CASE(8); + CASE(9); + CASE(10); + CASE(11); + CASE(12); + CASE(13); + CASE(14); + CASE(15); + default: { + UNREACHABLE_MSG("Invalid XMM register index: {}", index); + return nullptr; + } + } +#undef CASE +} + +void* GetRip(void* ctx) { +#if defined(_WIN32) + return (void*)((EXCEPTION_POINTERS*)ctx)->ContextRecord->Rip; +#elif defined(__APPLE__) + return (void*)((ucontext_t*)ctx)->uc_mcontext->__ss.__rip; +#else + return (void*)((ucontext_t*)ctx)->uc_mcontext.gregs[REG_RIP]; +#endif +} + +void IncrementRip(void* ctx, u64 length) { +#if defined(_WIN32) + ((EXCEPTION_POINTERS*)ctx)->ContextRecord->Rip += length; +#elif defined(__APPLE__) + ((ucontext_t*)ctx)->uc_mcontext->__ss.__rip += length; +#else + ((ucontext_t*)ctx)->uc_mcontext.gregs[REG_RIP] += length; +#endif +} + +bool IsWriteError(void* ctx) { +#if defined(_WIN32) + return ((EXCEPTION_POINTERS*)ctx)->ExceptionRecord->ExceptionInformation[0] == 1; +#elif defined(__APPLE__) +#if defined(ARCH_X86_64) + return ((ucontext_t*)ctx)->uc_mcontext->__es.__err & 0x2; +#elif defined(ARCH_ARM64) + return ((ucontext_t*)ctx)->uc_mcontext->__es.__esr & 0x40; +#endif +#else +#if defined(ARCH_X86_64) + return ((ucontext_t*)ctx)->uc_mcontext.gregs[REG_ERR] & 0x2; +#else +#error "Unsupported architecture" +#endif +#endif +} +} // namespace Common \ No newline at end of file diff --git a/src/common/signal_context.h b/src/common/signal_context.h new file mode 100644 index 00000000..b09da64f --- /dev/null +++ b/src/common/signal_context.h @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Common { + +void* GetXmmPointer(void* ctx, u8 index); + +void* GetRip(void* ctx); + +void IncrementRip(void* ctx, u64 length); + +bool IsWriteError(void* ctx); + +} // namespace Common \ No newline at end of file diff --git a/src/core/cpu_patches.cpp b/src/core/cpu_patches.cpp index 1b159d32..24438b6b 100644 --- a/src/core/cpu_patches.cpp +++ b/src/core/cpu_patches.cpp @@ -7,8 +7,12 @@ #include #include #include +#include #include "common/alignment.h" +#include "common/arch.h" #include "common/assert.h" +#include "common/decoder.h" +#include "common/signal_context.h" #include "common/types.h" #include "core/signals.h" #include "core/tls.h" @@ -26,6 +30,16 @@ using namespace Xbyak::util; +#define MAYBE_AVX(OPCODE, ...) \ + [&] { \ + Cpu cpu; \ + if (cpu.has(Cpu::tAVX)) { \ + c.v##OPCODE(__VA_ARGS__); \ + } else { \ + c.OPCODE(__VA_ARGS__); \ + } \ + }() + namespace Core { static Xbyak::Reg ZydisToXbyakRegister(const ZydisRegister reg) { @@ -586,6 +600,114 @@ static void GenerateTcbAccess(const ZydisDecodedOperand* operands, Xbyak::CodeGe #endif // __APPLE__ +static bool FilterNoSSE4a(const ZydisDecodedOperand*) { + Cpu cpu; + return !cpu.has(Cpu::tSSE4a); +} + +static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { + bool immediateForm = operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && + operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; + + ASSERT_MSG(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER, "operand 0 must be a register"); + + const auto dst = ZydisToXbyakRegisterOperand(operands[0]); + + ASSERT_MSG(dst.isXMM(), "operand 0 must be an XMM register"); + + Xbyak::Xmm xmm_dst = *reinterpret_cast(&dst); + + if (immediateForm) { + u8 length = operands[1].imm.value.u & 0x3F; + u8 index = operands[2].imm.value.u & 0x3F; + if (length == 0) { + length = 64; + } + + LOG_DEBUG(Core, "Patching immediate form EXTRQ, length: {}, index: {}", length, index); + + const Xbyak::Reg64 scratch1 = rax; + const Xbyak::Reg64 scratch2 = rcx; + + // Set rsp to before red zone and save scratch registers + c.lea(rsp, ptr[rsp - 128]); + c.pushfq(); + c.push(scratch1); + c.push(scratch2); + + u64 mask = (1ULL << length) - 1; + + // Get lower qword from xmm register + MAYBE_AVX(movq, scratch1, xmm_dst); + + if (index != 0) { + c.shr(scratch1, index); + } + + // We need to move mask to a register because we can't use all the possible + // immediate values with `and reg, imm32` + c.mov(scratch2, mask); + c.and_(scratch1, scratch2); + + // Writeback to xmm register, extrq instruction says top 64-bits are undefined so we don't + // care to preserve them + MAYBE_AVX(movq, xmm_dst, scratch1); + + c.pop(scratch2); + c.pop(scratch1); + c.popfq(); + c.lea(rsp, ptr[rsp + 128]); + } else { + ASSERT_MSG(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER && + operands[0].reg.value >= ZYDIS_REGISTER_XMM0 && + operands[0].reg.value <= ZYDIS_REGISTER_XMM15 && + operands[1].reg.value >= ZYDIS_REGISTER_XMM0 && + operands[1].reg.value <= ZYDIS_REGISTER_XMM15, + "Unexpected operand types for EXTRQ instruction"); + + const auto src = ZydisToXbyakRegisterOperand(operands[1]); + + ASSERT_MSG(src.isXMM(), "operand 1 must be an XMM register"); + + Xbyak::Xmm xmm_src = *reinterpret_cast(&src); + + const Xbyak::Reg64 scratch1 = rax; + const Xbyak::Reg64 scratch2 = rcx; + const Xbyak::Reg64 mask = rdx; + + c.lea(rsp, ptr[rsp - 128]); + c.pushfq(); + c.push(scratch1); + c.push(scratch2); + c.push(mask); + + // Construct the mask out of the length that resides in bottom 6 bits of source xmm + MAYBE_AVX(movq, scratch1, xmm_src); + c.mov(scratch2, scratch1); + c.and_(scratch2, 0x3F); + c.mov(mask, 1); + c.shl(mask, cl); + c.dec(mask); + + // Get the shift amount and store it in scratch2 + c.shr(scratch1, 8); + c.and_(scratch1, 0x3F); + c.mov(scratch2, scratch1); // cl now contains the shift amount + + MAYBE_AVX(movq, scratch1, xmm_dst); + c.shr(scratch1, cl); + c.and_(scratch1, mask); + MAYBE_AVX(movq, xmm_dst, scratch1); + + c.pop(mask); + c.pop(scratch2); + c.pop(scratch1); + c.popfq(); + c.lea(rsp, ptr[rsp + 128]); + } +} + using PatchFilter = bool (*)(const ZydisDecodedOperand*); using InstructionGenerator = void (*)(const ZydisDecodedOperand*, Xbyak::CodeGenerator&); struct PatchInfo { @@ -607,6 +729,8 @@ static const std::unordered_map Patches = { {ZYDIS_MNEMONIC_MOV, {FilterTcbAccess, GenerateTcbAccess, false}}, #endif + {ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}}, + #ifdef __APPLE__ // Patches for instruction sets not supported by Rosetta 2. // BMI1 @@ -622,7 +746,6 @@ static const std::unordered_map Patches = { }; static std::once_flag init_flag; -static ZydisDecoder instr_decoder; struct PatchModule { /// Mutex controlling access to module code regions. @@ -663,22 +786,31 @@ static PatchModule* GetModule(const void* ptr) { static std::pair TryPatch(u8* code, PatchModule* module) { ZydisDecodedInstruction instruction; ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT]; - const auto status = - ZydisDecoderDecodeFull(&instr_decoder, code, module->end - code, &instruction, operands); + const auto status = Common::Decoder::Instance()->decodeInstruction(instruction, operands, code, + module->end - code); if (!ZYAN_SUCCESS(status)) { return std::make_pair(false, 1); } if (Patches.contains(instruction.mnemonic)) { const auto& patch_info = Patches.at(instruction.mnemonic); + bool needs_trampoline = patch_info.trampoline; if (patch_info.filter(operands)) { auto& patch_gen = module->patch_gen; + if (needs_trampoline && instruction.length < 5) { + // Trampoline is needed but instruction is too short to patch. + // Return false and length to fall back to the illegal instruction handler, + // or to signal to AOT compilation that this instruction should be skipped and + // handled at runtime. + return std::make_pair(false, instruction.length); + } + // Reset state and move to current code position. patch_gen.reset(); patch_gen.setSize(code - patch_gen.getCode()); - if (patch_info.trampoline) { + if (needs_trampoline) { auto& trampoline_gen = module->trampoline_gen; const auto trampoline_ptr = trampoline_gen.getCurr(); @@ -714,6 +846,78 @@ static std::pair TryPatch(u8* code, PatchModule* module) { return std::make_pair(false, instruction.length); } +#if defined(ARCH_X86_64) + +static bool TryExecuteIllegalInstruction(void* ctx, void* code_address) { + ZydisDecodedInstruction instruction; + ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT]; + const auto status = + Common::Decoder::Instance()->decodeInstruction(instruction, operands, code_address); + + switch (instruction.mnemonic) { + case ZYDIS_MNEMONIC_EXTRQ: { + bool immediateForm = operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && + operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; + if (immediateForm) { + LOG_ERROR(Core, "EXTRQ immediate form should have been patched at code address: {}", + fmt::ptr(code_address)); + return false; + } else { + ASSERT_MSG(operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER && + operands[0].reg.value >= ZYDIS_REGISTER_XMM0 && + operands[0].reg.value <= ZYDIS_REGISTER_XMM15 && + operands[1].reg.value >= ZYDIS_REGISTER_XMM0 && + operands[1].reg.value <= ZYDIS_REGISTER_XMM15, + "Unexpected operand types for EXTRQ instruction"); + + const auto dstIndex = operands[0].reg.value - ZYDIS_REGISTER_XMM0; + const auto srcIndex = operands[1].reg.value - ZYDIS_REGISTER_XMM0; + + const auto dst = Common::GetXmmPointer(ctx, dstIndex); + const auto src = Common::GetXmmPointer(ctx, srcIndex); + + u64 lowQWordSrc; + memcpy(&lowQWordSrc, src, sizeof(lowQWordSrc)); + + u64 lowQWordDst; + memcpy(&lowQWordDst, dst, sizeof(lowQWordDst)); + + u64 mask = lowQWordSrc & 0x3F; + mask = (1ULL << mask) - 1; + + u64 shift = (lowQWordSrc >> 8) & 0x3F; + + lowQWordDst >>= shift; + lowQWordDst &= mask; + + memcpy(dst, &lowQWordDst, sizeof(lowQWordDst)); + + Common::IncrementRip(ctx, instruction.length); + + return true; + } + break; + } + default: { + LOG_ERROR(Core, "Unhandled illegal instruction at code address {}: {}", + fmt::ptr(code_address), ZydisMnemonicGetString(instruction.mnemonic)); + return false; + } + } + + UNREACHABLE(); +} +#elif defined(ARCH_ARM64) +// These functions shouldn't be needed for ARM as it will use a JIT so there's no need to patch +// instructions. +static bool TryExecuteIllegalInstruction(void*, void*) { + return false; +} +#else +#error "Unsupported architecture" +#endif + static bool TryPatchJit(void* code_address) { auto* code = static_cast(code_address); auto* module = GetModule(code); @@ -746,17 +950,19 @@ static void TryPatchAot(void* code_address, u64 code_size) { } } -static bool PatchesAccessViolationHandler(void* code_address, void* fault_address, bool is_write) { - return TryPatchJit(code_address); +static bool PatchesAccessViolationHandler(void* context, void* /* fault_address */) { + return TryPatchJit(Common::GetRip(context)); } -static bool PatchesIllegalInstructionHandler(void* code_address) { - return TryPatchJit(code_address); +static bool PatchesIllegalInstructionHandler(void* context) { + void* code_address = Common::GetRip(context); + if (!TryPatchJit(code_address)) { + return TryExecuteIllegalInstruction(context, code_address); + } + return true; } static void PatchesInit() { - ZydisDecoderInit(&instr_decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64); - if (!Patches.empty()) { auto* signals = Signals::Instance(); // Should be called last. diff --git a/src/core/signals.cpp b/src/core/signals.cpp index a16c150e..87f56c85 100644 --- a/src/core/signals.cpp +++ b/src/core/signals.cpp @@ -3,6 +3,8 @@ #include "common/arch.h" #include "common/assert.h" +#include "common/decoder.h" +#include "common/signal_context.h" #include "core/signals.h" #ifdef _WIN32 @@ -10,7 +12,6 @@ #else #include #ifdef ARCH_X86_64 -#include #include #endif #endif @@ -22,17 +23,14 @@ namespace Core { static LONG WINAPI SignalHandler(EXCEPTION_POINTERS* pExp) noexcept { const auto* signals = Signals::Instance(); - auto* code_address = reinterpret_cast(pExp->ContextRecord->Rip); - bool handled = false; switch (pExp->ExceptionRecord->ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: handled = signals->DispatchAccessViolation( - code_address, reinterpret_cast(pExp->ExceptionRecord->ExceptionInformation[1]), - pExp->ExceptionRecord->ExceptionInformation[0] == 1); + pExp, reinterpret_cast(pExp->ExceptionRecord->ExceptionInformation[1])); break; case EXCEPTION_ILLEGAL_INSTRUCTION: - handled = signals->DispatchIllegalInstruction(code_address); + handled = signals->DispatchIllegalInstruction(pExp); break; default: break; @@ -43,37 +41,14 @@ static LONG WINAPI SignalHandler(EXCEPTION_POINTERS* pExp) noexcept { #else -#ifdef __APPLE__ -#if defined(ARCH_X86_64) -#define CODE_ADDRESS(ctx) reinterpret_cast((ctx)->uc_mcontext->__ss.__rip) -#define IS_WRITE_ERROR(ctx) ((ctx)->uc_mcontext->__es.__err & 0x2) -#elif defined(ARCH_ARM64) -#define CODE_ADDRESS(ctx) reinterpret_cast((ctx)->uc_mcontext->__ss.__pc) -#define IS_WRITE_ERROR(ctx) ((ctx)->uc_mcontext->__es.__esr & 0x40) -#endif -#else -#if defined(ARCH_X86_64) -#define CODE_ADDRESS(ctx) reinterpret_cast((ctx)->uc_mcontext.gregs[REG_RIP]) -#define IS_WRITE_ERROR(ctx) ((ctx)->uc_mcontext.gregs[REG_ERR] & 0x2) -#endif -#endif - -#ifndef IS_WRITE_ERROR -#error "Missing IS_WRITE_ERROR() implementation for target OS and CPU architecture." -#endif - static std::string DisassembleInstruction(void* code_address) { char buffer[256] = ""; #ifdef ARCH_X86_64 - ZydisDecoder decoder; - ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64); - ZydisDecodedInstruction instruction; ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT]; - static constexpr u64 max_length = 0x20; const auto status = - ZydisDecoderDecodeFull(&decoder, code_address, max_length, &instruction, operands); + Common::Decoder::Instance()->decodeInstruction(instruction, operands, code_address); if (ZYAN_SUCCESS(status)) { ZydisFormatter formatter; ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL); @@ -87,23 +62,23 @@ static std::string DisassembleInstruction(void* code_address) { } static void SignalHandler(int sig, siginfo_t* info, void* raw_context) { - const auto* ctx = static_cast(raw_context); const auto* signals = Signals::Instance(); - auto* code_address = CODE_ADDRESS(ctx); + auto* code_address = Common::GetRip(raw_context); switch (sig) { case SIGSEGV: - case SIGBUS: - if (const bool is_write = IS_WRITE_ERROR(ctx); - !signals->DispatchAccessViolation(code_address, info->si_addr, is_write)) { + case SIGBUS: { + const bool is_write = Common::IsWriteError(raw_context); + if (!signals->DispatchAccessViolation(raw_context, info->si_addr)) { UNREACHABLE_MSG("Unhandled access violation at code address {}: {} address {}", fmt::ptr(code_address), is_write ? "Write to" : "Read from", fmt::ptr(info->si_addr)); } break; + } case SIGILL: - if (!signals->DispatchIllegalInstruction(code_address)) { + if (!signals->DispatchIllegalInstruction(raw_context)) { UNREACHABLE_MSG("Unhandled illegal instruction at code address {}: {}", fmt::ptr(code_address), DisassembleInstruction(code_address)); } @@ -150,19 +125,18 @@ SignalDispatch::~SignalDispatch() { #endif } -bool SignalDispatch::DispatchAccessViolation(void* code_address, void* fault_address, - bool is_write) const { +bool SignalDispatch::DispatchAccessViolation(void* context, void* fault_address) const { for (const auto& [handler, _] : access_violation_handlers) { - if (handler(code_address, fault_address, is_write)) { + if (handler(context, fault_address)) { return true; } } return false; } -bool SignalDispatch::DispatchIllegalInstruction(void* code_address) const { +bool SignalDispatch::DispatchIllegalInstruction(void* context) const { for (const auto& [handler, _] : illegal_instruction_handlers) { - if (handler(code_address)) { + if (handler(context)) { return true; } } diff --git a/src/core/signals.h b/src/core/signals.h index bb018a93..6ee525e1 100644 --- a/src/core/signals.h +++ b/src/core/signals.h @@ -8,8 +8,8 @@ namespace Core { -using AccessViolationHandler = bool (*)(void* code_address, void* fault_address, bool is_write); -using IllegalInstructionHandler = bool (*)(void* code_address); +using AccessViolationHandler = bool (*)(void* context, void* fault_address); +using IllegalInstructionHandler = bool (*)(void* context); /// Receives OS signals and dispatches to the appropriate handlers. class SignalDispatch { @@ -28,10 +28,10 @@ public: } /// Dispatches an access violation signal, returning whether it was successfully handled. - bool DispatchAccessViolation(void* code_address, void* fault_address, bool is_write) const; + bool DispatchAccessViolation(void* context, void* fault_address) const; /// Dispatches an illegal instruction signal, returning whether it was successfully handled. - bool DispatchIllegalInstruction(void* code_address) const; + bool DispatchIllegalInstruction(void* context) const; private: template diff --git a/src/video_core/page_manager.cpp b/src/video_core/page_manager.cpp index 23905e83..fb09e70f 100644 --- a/src/video_core/page_manager.cpp +++ b/src/video_core/page_manager.cpp @@ -6,6 +6,7 @@ #include "common/alignment.h" #include "common/assert.h" #include "common/error.h" +#include "common/signal_context.h" #include "core/signals.h" #include "video_core/page_manager.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" @@ -152,8 +153,9 @@ struct PageManager::Impl { #endif } - static bool GuestFaultSignalHandler(void* code_address, void* fault_address, bool is_write) { + static bool GuestFaultSignalHandler(void* context, void* fault_address) { const auto addr = reinterpret_cast(fault_address); + const bool is_write = Common::IsWriteError(context); if (is_write && owned_ranges.find(addr) != owned_ranges.end()) { const VAddr addr_aligned = Common::AlignDown(addr, PAGESIZE); rasterizer->InvalidateMemory(addr_aligned, PAGESIZE);