shadPS4/src/core/signals.cpp
Paris Oplopoios 5799091044
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 <giorgosmrls@gmail.com>
2024-09-23 19:19:52 +03:00

146 lines
4.5 KiB
C++

// 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/decoder.h"
#include "common/signal_context.h"
#include "core/signals.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <csignal>
#ifdef ARCH_X86_64
#include <Zydis/Formatter.h>
#endif
#endif
namespace Core {
#if defined(_WIN32)
static LONG WINAPI SignalHandler(EXCEPTION_POINTERS* pExp) noexcept {
const auto* signals = Signals::Instance();
bool handled = false;
switch (pExp->ExceptionRecord->ExceptionCode) {
case EXCEPTION_ACCESS_VIOLATION:
handled = signals->DispatchAccessViolation(
pExp, reinterpret_cast<void*>(pExp->ExceptionRecord->ExceptionInformation[1]));
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
handled = signals->DispatchIllegalInstruction(pExp);
break;
default:
break;
}
return handled ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
}
#else
static std::string DisassembleInstruction(void* code_address) {
char buffer[256] = "<unable to decode>";
#ifdef ARCH_X86_64
ZydisDecodedInstruction instruction;
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
const auto status =
Common::Decoder::Instance()->decodeInstruction(instruction, operands, code_address);
if (ZYAN_SUCCESS(status)) {
ZydisFormatter formatter;
ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL);
ZydisFormatterFormatInstruction(&formatter, &instruction, operands,
instruction.operand_count_visible, buffer, sizeof(buffer),
reinterpret_cast<u64>(code_address), ZYAN_NULL);
}
#endif
return buffer;
}
static void SignalHandler(int sig, siginfo_t* info, void* raw_context) {
const auto* signals = Signals::Instance();
auto* code_address = Common::GetRip(raw_context);
switch (sig) {
case SIGSEGV:
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(raw_context)) {
UNREACHABLE_MSG("Unhandled illegal instruction at code address {}: {}",
fmt::ptr(code_address), DisassembleInstruction(code_address));
}
break;
default:
break;
}
}
#endif
SignalDispatch::SignalDispatch() {
#if defined(_WIN32)
ASSERT_MSG(handle = AddVectoredExceptionHandler(0, SignalHandler),
"Failed to register exception handler.");
#else
struct sigaction action {};
action.sa_sigaction = SignalHandler;
action.sa_flags = SA_SIGINFO | SA_ONSTACK;
sigemptyset(&action.sa_mask);
ASSERT_MSG(sigaction(SIGSEGV, &action, nullptr) == 0 &&
sigaction(SIGBUS, &action, nullptr) == 0,
"Failed to register access violation signal handler.");
ASSERT_MSG(sigaction(SIGILL, &action, nullptr) == 0,
"Failed to register illegal instruction signal handler.");
#endif
}
SignalDispatch::~SignalDispatch() {
#if defined(_WIN32)
ASSERT_MSG(RemoveVectoredExceptionHandler(handle), "Failed to remove exception handler.");
#else
struct sigaction action {};
action.sa_handler = SIG_DFL;
action.sa_flags = 0;
sigemptyset(&action.sa_mask);
ASSERT_MSG(sigaction(SIGSEGV, &action, nullptr) == 0 &&
sigaction(SIGBUS, &action, nullptr) == 0,
"Failed to remove access violation signal handler.");
ASSERT_MSG(sigaction(SIGILL, &action, nullptr) == 0,
"Failed to remove illegal instruction signal handler.");
#endif
}
bool SignalDispatch::DispatchAccessViolation(void* context, void* fault_address) const {
for (const auto& [handler, _] : access_violation_handlers) {
if (handler(context, fault_address)) {
return true;
}
}
return false;
}
bool SignalDispatch::DispatchIllegalInstruction(void* context) const {
for (const auto& [handler, _] : illegal_instruction_handlers) {
if (handler(context)) {
return true;
}
}
return false;
}
} // namespace Core