mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-25 04:02:42 +01:00
Exception handling fix
This commit is contained in:
parent
d68c748976
commit
56ba5a765b
@ -44,6 +44,7 @@ Open the *.SLN* file, and press *Build* > *Clean Solution*, then *Build Solution
|
|||||||
If you want to build with LLVM, then LLVM 3.6.2 is required.
|
If you want to build with LLVM, then LLVM 3.6.2 is required.
|
||||||
`cd rpcs3 && cmake CMakeLists.txt && make && cd ../` then run with `cd bin && ./rpcs3`.
|
`cd rpcs3 && cmake CMakeLists.txt && make && cd ../` then run with `cd bin && ./rpcs3`.
|
||||||
If you are on OSX and want to build with llvm don't forget to add `-DLLVM_DIR=/usr/local/opt/llvm36/lib/llvm-3.6/share/llvm/cmake` (or wherever llvm brew was installed) to cmake invocation.
|
If you are on OSX and want to build with llvm don't forget to add `-DLLVM_DIR=/usr/local/opt/llvm36/lib/llvm-3.6/share/llvm/cmake` (or wherever llvm brew was installed) to cmake invocation.
|
||||||
|
When using GDB, configure it to ignore SIGSEGV signal (`handle SIGSEGV nostop noprint`).
|
||||||
|
|
||||||
### Support
|
### Support
|
||||||
* [Donate by PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=MPJ3S9XQXCE3G)
|
* [Donate by PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=MPJ3S9XQXCE3G)
|
||||||
|
@ -517,6 +517,9 @@ typedef CONTEXT x64_context;
|
|||||||
#define XMMREG(context, reg) (reinterpret_cast<v128*>(&(&(context)->Xmm0)[reg]))
|
#define XMMREG(context, reg) (reinterpret_cast<v128*>(&(&(context)->Xmm0)[reg]))
|
||||||
#define EFLAGS(context) ((context)->EFlags)
|
#define EFLAGS(context) ((context)->EFlags)
|
||||||
|
|
||||||
|
#define ARG1(context) RCX(context)
|
||||||
|
#define ARG2(context) RDX(context)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
typedef ucontext_t x64_context;
|
typedef ucontext_t x64_context;
|
||||||
@ -569,11 +572,15 @@ static const decltype(REG_RAX) reg_table[] =
|
|||||||
|
|
||||||
#endif // __APPLE__
|
#endif // __APPLE__
|
||||||
|
|
||||||
|
#define ARG1(context) RDI(context)
|
||||||
|
#define ARG2(context) RSI(context)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define RAX(c) (*X64REG((c), 0))
|
#define RAX(c) (*X64REG((c), 0))
|
||||||
#define RCX(c) (*X64REG((c), 1))
|
#define RCX(c) (*X64REG((c), 1))
|
||||||
#define RDX(c) (*X64REG((c), 2))
|
#define RDX(c) (*X64REG((c), 2))
|
||||||
|
#define RSP(c) (*X64REG((c), 4))
|
||||||
#define RSI(c) (*X64REG((c), 6))
|
#define RSI(c) (*X64REG((c), 6))
|
||||||
#define RDI(c) (*X64REG((c), 7))
|
#define RDI(c) (*X64REG((c), 7))
|
||||||
#define RIP(c) (*X64REG((c), 16))
|
#define RIP(c) (*X64REG((c), 16))
|
||||||
@ -1114,31 +1121,32 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
|
|||||||
// TODO: allow recovering from a page fault as a feature of PS3 virtual memory
|
// TODO: allow recovering from a page fault as a feature of PS3 virtual memory
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
// Throw virtual memory access violation exception
|
||||||
|
[[noreturn]] void throw_access_violation(const char* cause, u32 address) // Don't change function definition
|
||||||
void _se_translator(unsigned int u, EXCEPTION_POINTERS* pExp)
|
|
||||||
{
|
{
|
||||||
const u64 addr64 = (u64)pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::base(0);
|
throw EXCEPTION("Access violation %s location 0x%08x", cause, address);
|
||||||
const bool is_writing = pExp->ExceptionRecord->ExceptionInformation[0] != 0;
|
|
||||||
|
|
||||||
if (u == EXCEPTION_ACCESS_VIOLATION)
|
|
||||||
{
|
|
||||||
if ((u32)addr64 == addr64)
|
|
||||||
{
|
|
||||||
throw EXCEPTION("Access violation %s location 0x%llx", is_writing ? "writing" : "reading", addr64);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::printf("Access violation %s location %p at %p\n", is_writing ? "writing" : "reading", (void*)pExp->ExceptionRecord->ExceptionInformation[1], pExp->ExceptionRecord->ExceptionAddress);
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const PVOID exception_handler = (atexit([]{ RemoveVectoredExceptionHandler(exception_handler); }), AddVectoredExceptionHandler(1, [](PEXCEPTION_POINTERS pExp) -> LONG
|
// Modify context in order to convert hardware exception to C++ exception
|
||||||
|
void prepare_throw_access_violation(x64_context* context, const char* cause, u32 address)
|
||||||
{
|
{
|
||||||
const u64 addr64 = (u64)pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::base(0);
|
// Set throw_access_violation() call args (old register values are lost)
|
||||||
|
ARG1(context) = (u64)cause;
|
||||||
|
ARG2(context) = address;
|
||||||
|
|
||||||
|
// Push the exception address as a "return" address (throw_access_violation() shall not return)
|
||||||
|
*--(u64*&)(RSP(context)) = RIP(context);
|
||||||
|
RIP(context) = (u64)std::addressof(throw_access_violation);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
const auto g_exception_handler = AddVectoredExceptionHandler(1, [](PEXCEPTION_POINTERS pExp) -> LONG
|
||||||
|
{
|
||||||
|
const u64 addr64 = pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::base(0);
|
||||||
const bool is_writing = pExp->ExceptionRecord->ExceptionInformation[0] != 0;
|
const bool is_writing = pExp->ExceptionRecord->ExceptionInformation[0] != 0;
|
||||||
|
|
||||||
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && (u32)addr64 == addr64 && thread_ctrl::get_current() && handle_access_violation((u32)addr64, is_writing, pExp->ContextRecord))
|
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && addr64 < 0x100000000ull && thread_ctrl::get_current() && handle_access_violation((u32)addr64, is_writing, pExp->ContextRecord))
|
||||||
{
|
{
|
||||||
return EXCEPTION_CONTINUE_EXECUTION;
|
return EXCEPTION_CONTINUE_EXECUTION;
|
||||||
}
|
}
|
||||||
@ -1146,12 +1154,41 @@ const PVOID exception_handler = (atexit([]{ RemoveVectoredExceptionHandler(excep
|
|||||||
{
|
{
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
}));
|
});
|
||||||
|
|
||||||
const auto exception_filter = SetUnhandledExceptionFilter([](PEXCEPTION_POINTERS pExp) -> LONG
|
const auto g_exception_filter = SetUnhandledExceptionFilter([](PEXCEPTION_POINTERS pExp) -> LONG
|
||||||
{
|
{
|
||||||
_se_translator(pExp->ExceptionRecord->ExceptionCode, pExp);
|
std::string msg;
|
||||||
|
|
||||||
|
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
|
||||||
|
{
|
||||||
|
const u64 addr64 = pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::base(0);
|
||||||
|
const auto cause = pExp->ExceptionRecord->ExceptionInformation[0] != 0 ? "writing" : "reading";
|
||||||
|
|
||||||
|
if (addr64 < 0x100000000ull)
|
||||||
|
{
|
||||||
|
// Setup throw_access_violation() call on the context
|
||||||
|
prepare_throw_access_violation(pExp->ContextRecord, cause, (u32)addr64);
|
||||||
|
return EXCEPTION_CONTINUE_EXECUTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg += fmt::format("Access violation %s location %p at %p.\n", cause, pExp->ExceptionRecord->ExceptionInformation[1], pExp->ExceptionRecord->ExceptionAddress);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
msg += fmt::format("Exception address: %p.\n", pExp->ExceptionRecord->ExceptionAddress);
|
||||||
|
|
||||||
|
for (DWORD i = 0; i < pExp->ExceptionRecord->NumberParameters; i++)
|
||||||
|
{
|
||||||
|
msg += fmt::format("ExceptionInformation[0x%x]: %p.\n", i, pExp->ExceptionRecord->ExceptionInformation[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg += fmt::format("Image base: %p.\n\n", GetModuleHandle(NULL));
|
||||||
|
msg += "Report this error to the developers. Press (Ctrl+C) to copy this message.";
|
||||||
|
|
||||||
|
// Report fatal error
|
||||||
|
MessageBoxA(0, msg.c_str(), fmt::format("Win32 Exception 0x%08X", pExp->ExceptionRecord->ExceptionCode).c_str(), MB_ICONERROR);
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1159,31 +1196,35 @@ const auto exception_filter = SetUnhandledExceptionFilter([](PEXCEPTION_POINTERS
|
|||||||
|
|
||||||
void signal_handler(int sig, siginfo_t* info, void* uct)
|
void signal_handler(int sig, siginfo_t* info, void* uct)
|
||||||
{
|
{
|
||||||
const u64 addr64 = (u64)info->si_addr - (u64)vm::base(0);
|
x64_context* context = (ucontext_t*)uct;
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
const bool is_writing = ((ucontext_t*)uct)->uc_mcontext->__es.__err & 0x2;
|
const bool is_writing = context->uc_mcontext->__es.__err & 0x2;
|
||||||
#else
|
#else
|
||||||
const bool is_writing = ((ucontext_t*)uct)->uc_mcontext.gregs[REG_ERR] & 0x2;
|
const bool is_writing = context->uc_mcontext.gregs[REG_ERR] & 0x2;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ((u32)addr64 == addr64 && thread_ctrl::get_current())
|
const u64 addr64 = (u64)info->si_addr - (u64)vm::base(0);
|
||||||
|
const auto cause = is_writing ? "writing" : "reading";
|
||||||
|
|
||||||
|
if (addr64 < 0x100000000ull && thread_ctrl::get_current())
|
||||||
{
|
{
|
||||||
if (handle_access_violation((u32)addr64, is_writing, (ucontext_t*)uct))
|
// Try to process access violation
|
||||||
|
if (!handle_access_violation((u32)addr64, is_writing, context))
|
||||||
{
|
{
|
||||||
return; // proceed execution
|
// Setup throw_access_violation() call on the context
|
||||||
|
prepare_throw_access_violation(context, cause, (u32)addr64);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this may be wrong
|
|
||||||
throw EXCEPTION("Access violation %s location 0x%llx", is_writing ? "writing" : "reading", addr64);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// else some fatal error
|
{
|
||||||
std::printf("Access violation %s location %p at %p\n", is_writing ? "writing" : "reading", info->si_addr, RIP((ucontext_t*)uct));
|
// Report fatal error (TODO)
|
||||||
std::abort();
|
std::printf("Access violation %s location %p at %p.\n", cause, info->si_addr, RIP(context));
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const int sigaction_result = []() -> int
|
int setup_signal_handler()
|
||||||
{
|
{
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
|
|
||||||
@ -1191,7 +1232,9 @@ const int sigaction_result = []() -> int
|
|||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
sa.sa_sigaction = signal_handler;
|
sa.sa_sigaction = signal_handler;
|
||||||
return sigaction(SIGSEGV, &sa, NULL);
|
return sigaction(SIGSEGV, &sa, NULL);
|
||||||
}();
|
}
|
||||||
|
|
||||||
|
const int g_sigaction_result = setup_signal_handler();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1205,9 +1248,9 @@ void thread_ctrl::initialize()
|
|||||||
SetCurrentThreadDebugName(g_tls_this_thread->m_name().c_str());
|
SetCurrentThreadDebugName(g_tls_this_thread->m_name().c_str());
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (!exception_handler || !exception_filter)
|
if (!g_exception_handler || !g_exception_filter)
|
||||||
#else
|
#else
|
||||||
if (sigaction_result == -1)
|
if (g_sigaction_result == -1)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
std::printf("Exceptions handlers are not set correctly.\n");
|
std::printf("Exceptions handlers are not set correctly.\n");
|
||||||
|
Loading…
Reference in New Issue
Block a user