diff --git a/CMakeLists.txt b/CMakeLists.txt index ee980147402d..9d866fe92c01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -340,9 +340,6 @@ macro(setup_target_project TargetName ProjectDir) endforeach() endmacro() -# Commented-out files are files that don't compile -# and were disabled in the original MSVC project anyway - set(CommonX86 Common/ABI.cpp Common/ABI.h @@ -428,6 +425,8 @@ add_library(Common STATIC Common/Crypto/sha1.h Common/Crypto/sha256.cpp Common/Crypto/sha256.h + Common/ExceptionHandlerSetup.cpp + Common/ExceptionHandlerSetup.h Common/FileUtil.cpp Common/FileUtil.h Common/KeyMap.cpp diff --git a/Common/CodeBlock.h b/Common/CodeBlock.h index f60c4b9305da..777fb164a4d2 100644 --- a/Common/CodeBlock.h +++ b/Common/CodeBlock.h @@ -19,7 +19,7 @@ class CodeBlockCommon { CodeBlockCommon() {} virtual ~CodeBlockCommon() {} - bool IsInSpace(const u8 *ptr) { + bool IsInSpace(const u8 *ptr) const { return (ptr >= region) && (ptr < (region + region_size)); } diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 80278606dff2..6486c530e777 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -368,6 +368,7 @@ + @@ -397,6 +398,7 @@ + @@ -449,6 +451,7 @@ + true @@ -521,4 +524,4 @@ - \ No newline at end of file + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index ebe7f5068812..c49d67d6af61 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -74,6 +74,8 @@ Vulkan + + @@ -137,6 +139,7 @@ Vulkan + @@ -152,4 +155,4 @@ {c14d66ef-5f7c-4565-975a-72774e7ccfb9} - \ No newline at end of file + diff --git a/Common/ExceptionHandlerSetup.cpp b/Common/ExceptionHandlerSetup.cpp new file mode 100644 index 000000000000..6cdbefede4f0 --- /dev/null +++ b/Common/ExceptionHandlerSetup.cpp @@ -0,0 +1,347 @@ +// Copyright 2008 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +// The corresponding file is called MemTools in the Dolphin project. + +#include "ppsspp_config.h" + +#include "Common/ExceptionHandlerSetup.h" +#include +#include +#include +#include +#include + +#include "Common/CommonFuncs.h" +#include "Common/CommonTypes.h" +#include "Common/MsgHandler.h" +#include "Common/Log.h" +#include "ext/native/thread/threadutil.h" + +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) +#include "Common/MachineContext.h" +#endif + +#if PPSSPP_PLATFORM(IOS) +#define USE_SIGACTION_ON_APPLE +#endif + +#ifdef __FreeBSD__ +#include +#endif +#ifndef _WIN32 +#include // Needed for _POSIX_VERSION +#endif + +static BadAccessHandler g_badAccessHandler; + +// We cannot handle exceptions in UWP builds. Bleh. +#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP) + +static PVOID g_vectoredExceptionHandle; + +static LONG NTAPI GlobalExceptionHandler(PEXCEPTION_POINTERS pPtrs) { + switch (pPtrs->ExceptionRecord->ExceptionCode) { + case EXCEPTION_ACCESS_VIOLATION: + { + int accessType = (int)pPtrs->ExceptionRecord->ExceptionInformation[0]; + if (accessType == 8) { // Rule out DEP + return (DWORD)EXCEPTION_CONTINUE_SEARCH; + } + + // virtual address of the inaccessible data + uintptr_t badAddress = (uintptr_t)pPtrs->ExceptionRecord->ExceptionInformation[1]; + CONTEXT* ctx = pPtrs->ContextRecord; + + if (g_badAccessHandler(badAddress, ctx)) { + return (DWORD)EXCEPTION_CONTINUE_EXECUTION; + } else { + // Let's not prevent debugging. + return (DWORD)EXCEPTION_CONTINUE_SEARCH; + } + } + + case EXCEPTION_STACK_OVERFLOW: + // Dolphin has some handling of this for the RET optimization emulation. + return EXCEPTION_CONTINUE_SEARCH; + + case EXCEPTION_ILLEGAL_INSTRUCTION: + // No SSE support? Or simply bad codegen? + return EXCEPTION_CONTINUE_SEARCH; + + case EXCEPTION_PRIV_INSTRUCTION: + // okay, dynarec codegen is obviously broken. + return EXCEPTION_CONTINUE_SEARCH; + + case EXCEPTION_IN_PAGE_ERROR: + // okay, something went seriously wrong, out of memory? + return EXCEPTION_CONTINUE_SEARCH; + + case EXCEPTION_BREAKPOINT: + // might want to do something fun with this one day? + return EXCEPTION_CONTINUE_SEARCH; + + default: + return EXCEPTION_CONTINUE_SEARCH; + } +} + +void InstallExceptionHandler(BadAccessHandler badAccessHandler) { + // Make sure this is only called once per process execution + // Instead, could make a Uninstall function, but whatever.. + if (g_badAccessHandler) { + g_badAccessHandler = badAccessHandler; + return; + } + + INFO_LOG(SYSTEM, "Installing exception handler"); + g_badAccessHandler = badAccessHandler; + g_vectoredExceptionHandle = AddVectoredExceptionHandler(TRUE, GlobalExceptionHandler); +} + +void UninstallExceptionHandler() { + RemoveVectoredExceptionHandler(g_vectoredExceptionHandle); + g_badAccessHandler = nullptr; + INFO_LOG(SYSTEM, "Removed exception handler"); +} + +#elif defined(__APPLE__) && !defined(USE_SIGACTION_ON_APPLE) + +static void CheckKR(const char* name, kern_return_t kr) { + if (kr) { + PanicAlert("%s failed: kr=%x", name, kr); + } +} + +static void ExceptionThread(mach_port_t port) { + setCurrentThreadName("Mach exception thread"); +#pragma pack(4) + struct { + mach_msg_header_t Head; + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + int64_t code[2]; + int flavor; + mach_msg_type_number_t old_stateCnt; + natural_t old_state[x86_THREAD_STATE64_COUNT]; + mach_msg_trailer_t trailer; + } msg_in; + + struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + int flavor; + mach_msg_type_number_t new_stateCnt; + natural_t new_state[x86_THREAD_STATE64_COUNT]; + } msg_out; +#pragma pack() + memset(&msg_in, 0xee, sizeof(msg_in)); + memset(&msg_out, 0xee, sizeof(msg_out)); + mach_msg_header_t* send_msg = nullptr; + mach_msg_size_t send_size = 0; + mach_msg_option_t option = MACH_RCV_MSG; + while (true) { + // If this isn't the first run, send the reply message. Then, receive + // a message: either a mach_exception_raise_state RPC due to + // thread_set_exception_ports, or MACH_NOTIFY_NO_SENDERS due to + // mach_port_request_notification. + CheckKR("mach_msg_overwrite", + mach_msg_overwrite(send_msg, option, send_size, sizeof(msg_in), port, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL, &msg_in.Head, 0)); + + if (msg_in.Head.msgh_id == MACH_NOTIFY_NO_SENDERS) { + // the other thread exited + mach_port_destroy(mach_task_self(), port); + return; + } + + if (msg_in.Head.msgh_id != 2406) { + PanicAlert("unknown message received"); + return; + } + + if (msg_in.flavor != x86_THREAD_STATE64) { + PanicAlert("unknown flavor %d (expected %d)", msg_in.flavor, x86_THREAD_STATE64); + return; + } + + x86_thread_state64_t* state = (x86_thread_state64_t*)msg_in.old_state; + + bool ok = g_badAccessHandler((uintptr_t)msg_in.code[1], state); + + // Set up the reply. + msg_out.Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(msg_in.Head.msgh_bits), 0); + msg_out.Head.msgh_remote_port = msg_in.Head.msgh_remote_port; + msg_out.Head.msgh_local_port = MACH_PORT_NULL; + msg_out.Head.msgh_id = msg_in.Head.msgh_id + 100; + msg_out.NDR = msg_in.NDR; + if (ok) { + msg_out.RetCode = KERN_SUCCESS; + msg_out.flavor = x86_THREAD_STATE64; + msg_out.new_stateCnt = x86_THREAD_STATE64_COUNT; + memcpy(msg_out.new_state, msg_in.old_state, x86_THREAD_STATE64_COUNT * sizeof(natural_t)); + } else { + // Pass the exception to the next handler (debugger or crash). + msg_out.RetCode = KERN_FAILURE; + msg_out.flavor = 0; + msg_out.new_stateCnt = 0; + } + msg_out.Head.msgh_size = + offsetof(__typeof__(msg_out), new_state) + msg_out.new_stateCnt * sizeof(natural_t); + + send_msg = &msg_out.Head; + send_size = msg_out.Head.msgh_size; + option |= MACH_SEND_MSG; + } +} + +void InstallExceptionHandler(BadAccessHandler badAccessHandler) { + if (g_badAccessHandler) { + // The rest of the setup we don't need to do again. + g_badAccessHandler = badAccessHandler; + return; + } + g_badAccessHandler = badAccessHandler; + + INFO_LOG(SYSTEM, "Installing exception handler"); + mach_port_t port; + CheckKR("mach_port_allocate", + mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port)); + std::thread exc_thread(ExceptionThread, port); + exc_thread.detach(); + // Obtain a send right for thread_set_exception_ports to copy... + CheckKR("mach_port_insert_right", + mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND)); + // Mach tries the following exception ports in order: thread, task, host. + // Debuggers set the task port, so we grab the thread port. + CheckKR("thread_set_exception_ports", + thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, port, + EXCEPTION_STATE | MACH_EXCEPTION_CODES, x86_THREAD_STATE64)); + // ...and get rid of our copy so that MACH_NOTIFY_NO_SENDERS works. + CheckKR("mach_port_mod_refs", + mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1)); + mach_port_t previous; + CheckKR("mach_port_request_notification", + mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_NO_SENDERS, 0, port, + MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous)); +} + +void UninstallExceptionHandler() { +} + +#elif defined(_POSIX_VERSION) + +static struct sigaction old_sa_segv; +static struct sigaction old_sa_bus; + +static void sigsegv_handler(int sig, siginfo_t* info, void* raw_context) { + if (sig != SIGSEGV && sig != SIGBUS) { + // We are not interested in other signals - handle it as usual. + return; + } + ucontext_t* context = (ucontext_t*)raw_context; + int sicode = info->si_code; + if (sicode != SEGV_MAPERR && sicode != SEGV_ACCERR) { + // Huh? Return. + return; + } + uintptr_t bad_address = (uintptr_t)info->si_addr; + + // Get all the information we can out of the context. +#ifdef __OpenBSD__ + ucontext_t* ctx = context; +#else + mcontext_t* ctx = &context->uc_mcontext; +#endif + // assume it's not a write + if (!g_badAccessHandler(bad_address, +#ifdef __APPLE__ + *ctx +#else + ctx +#endif + )) { + // retry and crash + // According to the sigaction man page, if sa_flags "SA_SIGINFO" is set to the sigaction + // function pointer, otherwise sa_handler contains one of: + // SIG_DEF: The 'default' action is performed + // SIG_IGN: The signal is ignored + // Any other value is a function pointer to a signal handler + + struct sigaction* old_sa; + if (sig == SIGSEGV) { + old_sa = &old_sa_segv; + } else { + old_sa = &old_sa_bus; + } + + if (old_sa->sa_flags & SA_SIGINFO) { + old_sa->sa_sigaction(sig, info, raw_context); + return; + } + if (old_sa->sa_handler == SIG_DFL) { + signal(sig, SIG_DFL); + return; + } + if (old_sa->sa_handler == SIG_IGN) { + // Ignore signal + return; + } + old_sa->sa_handler(sig); + } +} + +void InstallExceptionHandler(BadAccessHandler badAccessHandler) { + if (g_badAccessHandler) { + g_badAccessHandler = badAccessHandler; + return; + } + NOTICE_LOG(SYSTEM, "Installed exception handler"); + g_badAccessHandler = badAccessHandler; + + stack_t signal_stack; +#ifdef __FreeBSD__ + signal_stack.ss_sp = (char*)malloc(SIGSTKSZ); +#else + signal_stack.ss_sp = malloc(SIGSTKSZ); +#endif + signal_stack.ss_size = SIGSTKSZ; + signal_stack.ss_flags = 0; + if (sigaltstack(&signal_stack, nullptr)) + PanicAlert("sigaltstack failed"); + struct sigaction sa; + sa.sa_handler = nullptr; + sa.sa_sigaction = &sigsegv_handler; + sa.sa_flags = SA_SIGINFO; + sigemptyset(&sa.sa_mask); + sigaction(SIGSEGV, &sa, &old_sa_segv); +#ifdef __APPLE__ + sigaction(SIGBUS, &sa, &old_sa_bus); +#endif +} + +void UninstallExceptionHandler() { + stack_t signal_stack, old_stack; + signal_stack.ss_flags = SS_DISABLE; + if (!sigaltstack(&signal_stack, &old_stack) && !(old_stack.ss_flags & SS_DISABLE)) { + free(old_stack.ss_sp); + } + sigaction(SIGSEGV, &old_sa_segv, nullptr); +#ifdef __APPLE__ + sigaction(SIGBUS, &old_sa_bus, nullptr); +#endif + NOTICE_LOG(SYSTEM, "Uninstalled exception handler"); + g_badAccessHandler = nullptr; +} + +#else // Unsupported platform. Could also #error + +void InstallExceptionHandler(BadAccessHandler badAccessHandler) { + ERROR_LOG(SYSTEM, "Exception handler not implemented on this platform, can't install"); +} +void UninstallExceptionHandler() { } + +#endif diff --git a/Common/ExceptionHandlerSetup.h b/Common/ExceptionHandlerSetup.h new file mode 100644 index 000000000000..8f88176e0e4b --- /dev/null +++ b/Common/ExceptionHandlerSetup.h @@ -0,0 +1,17 @@ +// Copyright 2008 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +// On Windows, context is a CONTEXT object. +// On Apple, context is a x86_thread_state64_t. +// On Unix/Linux, context is a mcontext_t. +// On OpenBSD, context is a ucontext_t. +// Ugh, might need to abstract this better. +typedef bool (*BadAccessHandler)(uintptr_t address, void *context); + +void InstallExceptionHandler(BadAccessHandler accessHandler); +void UninstallExceptionHandler(); diff --git a/Common/MachineContext.h b/Common/MachineContext.h new file mode 100644 index 000000000000..34e5a280a04d --- /dev/null +++ b/Common/MachineContext.h @@ -0,0 +1,257 @@ +// Copyright 2008 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "ppsspp_config.h" + +#if PPSSPP_PLATFORM(WINDOWS) +#include +typedef CONTEXT SContext; +#if PPSSPP_ARCH(AMD64) +#define CTX_RAX Rax +#define CTX_RBX Rbx +#define CTX_RCX Rcx +#define CTX_RDX Rdx +#define CTX_RDI Rdi +#define CTX_RSI Rsi +#define CTX_RBP Rbp +#define CTX_RSP Rsp +#define CTX_R8 R8 +#define CTX_R9 R9 +#define CTX_R10 R10 +#define CTX_R11 R11 +#define CTX_R12 R12 +#define CTX_R13 R13 +#define CTX_R14 R14 +#define CTX_R15 R15 +#define CTX_RIP Rip +#else +#define CTX_RAX Eax +#define CTX_RBX Ebx +#define CTX_RCX Ecx +#define CTX_RDX Edx +#define CTX_RDI Edi +#define CTX_RSI Esi +#define CTX_RBP Ebp +#define CTX_RSP Esp +#define CTX_RIP Eip + +#endif +#elif defined(__APPLE__) && !defined(USE_SIGACTION_ON_APPLE) +// for modules: +#define _XOPEN_SOURCE +#include + +#include +#include +#if PPSSPP_ARCH(AMD64) +typedef x86_thread_state64_t SContext; +#define CTX_RAX __rax +#define CTX_RBX __rbx +#define CTX_RCX __rcx +#define CTX_RDX __rdx +#define CTX_RDI __rdi +#define CTX_RSI __rsi +#define CTX_RBP __rbp +#define CTX_RSP __rsp +#define CTX_R8 __r8 +#define CTX_R9 __r9 +#define CTX_R10 __r10 +#define CTX_R11 __r11 +#define CTX_R12 __r12 +#define CTX_R13 __r13 +#define CTX_R14 __r14 +#define CTX_R15 __r15 +#define CTX_RIP __rip +#else +#error No context definition for architecture +#endif +#elif defined(__APPLE__) +#include +typedef _STRUCT_MCONTEXT64 SContext; +#define CTX_RAX __ss.__rax +#define CTX_RBX __ss.__rbx +#define CTX_RCX __ss.__rcx +#define CTX_RDX __ss.__rdx +#define CTX_RDI __ss.__rdi +#define CTX_RSI __ss.__rsi +#define CTX_RBP __ss.__rbp +#define CTX_RSP __ss.__rsp +#define CTX_R8 __ss.__r8 +#define CTX_R9 __ss.__r9 +#define CTX_R10 __ss.__r10 +#define CTX_R11 __ss.__r11 +#define CTX_R12 __ss.__r12 +#define CTX_R13 __ss.__r13 +#define CTX_R14 __ss.__r14 +#define CTX_R15 __ss.__r15 +#define CTX_RIP __ss.__rip +#elif defined(__linux__) +#include + +#include +typedef mcontext_t SContext; + +#if PPSSPP_ARCH(AMD64) +#define CTX_RAX gregs[REG_RAX] +#define CTX_RBX gregs[REG_RBX] +#define CTX_RCX gregs[REG_RCX] +#define CTX_RDX gregs[REG_RDX] +#define CTX_RDI gregs[REG_RDI] +#define CTX_RSI gregs[REG_RSI] +#define CTX_RBP gregs[REG_RBP] +#define CTX_RSP gregs[REG_RSP] +#define CTX_R8 gregs[REG_R8] +#define CTX_R9 gregs[REG_R9] +#define CTX_R10 gregs[REG_R10] +#define CTX_R11 gregs[REG_R11] +#define CTX_R12 gregs[REG_R12] +#define CTX_R13 gregs[REG_R13] +#define CTX_R14 gregs[REG_R14] +#define CTX_R15 gregs[REG_R15] +#define CTX_RIP gregs[REG_RIP] +#elif PPSSPP_ARCH(X86) +#define CTX_RAX gregs[REG_EAX] +#define CTX_RBX gregs[REG_EBX] +#define CTX_RCX gregs[REG_ECX] +#define CTX_RDX gregs[REG_EDX] +#define CTX_RDI gregs[REG_EDI] +#define CTX_RSI gregs[REG_ESI] +#define CTX_RBP gregs[REG_EBP] +#define CTX_RSP gregs[REG_ESP] +#define CTX_RIP gregs[REG_EIP] +#else +#error No context definition for architecture +#endif +#elif defined(__OpenBSD__) +#include +typedef ucontext_t SContext; +#if PPSSPP_ARCH(AMD64) +#define CTX_RAX sc_rax +#define CTX_RBX sc_rbx +#define CTX_RCX sc_rcx +#define CTX_RDX sc_rdx +#define CTX_RDI sc_rdi +#define CTX_RSI sc_rsi +#define CTX_RBP sc_rbp +#define CTX_RSP sc_rsp +#define CTX_R8 sc_r8 +#define CTX_R9 sc_r9 +#define CTX_R10 sc_r10 +#define CTX_R11 sc_r11 +#define CTX_R12 sc_r12 +#define CTX_R13 sc_r13 +#define CTX_R14 sc_r14 +#define CTX_R15 sc_r15 +#define CTX_RIP sc_rip +#else +#error No context definition for architecture +#endif +#elif defined(__NetBSD__) +#include +typedef mcontext_t SContext; +#if PPSSPP_ARCH(AMD64) +#define CTX_RAX __gregs[_REG_RAX] +#define CTX_RBX __gregs[_REG_RBX] +#define CTX_RCX __gregs[_REG_RCX] +#define CTX_RDX __gregs[_REG_RDX] +#define CTX_RDI __gregs[_REG_RDI] +#define CTX_RSI __gregs[_REG_RSI] +#define CTX_RBP __gregs[_REG_RBP] +#define CTX_RSP __gregs[_REG_RSP] +#define CTX_R8 __gregs[_REG_R8] +#define CTX_R9 __gregs[_REG_R9] +#define CTX_R10 __gregs[_REG_R10] +#define CTX_R11 __gregs[_REG_R11] +#define CTX_R12 __gregs[_REG_R12] +#define CTX_R13 __gregs[_REG_R13] +#define CTX_R14 __gregs[_REG_R14] +#define CTX_R15 __gregs[_REG_R15] +#define CTX_RIP __gregs[_REG_RIP] +#else +#error No context definition for architecture +#endif +#elif defined(__FreeBSD__) +#include +typedef mcontext_t SContext; +#if PPSSPP_ARCH(AMD64) +#define CTX_RAX mc_rax +#define CTX_RBX mc_rbx +#define CTX_RCX mc_rcx +#define CTX_RDX mc_rdx +#define CTX_RDI mc_rdi +#define CTX_RSI mc_rsi +#define CTX_RBP mc_rbp +#define CTX_RSP mc_rsp +#define CTX_R8 mc_r8 +#define CTX_R9 mc_r9 +#define CTX_R10 mc_r10 +#define CTX_R11 mc_r11 +#define CTX_R12 mc_r12 +#define CTX_R13 mc_r13 +#define CTX_R14 mc_r14 +#define CTX_R15 mc_r15 +#define CTX_RIP mc_rip +#else +#error No context definition for architecture +#endif +#elif defined(__HAIKU__) +#include +typedef mcontext_t SContext; +#if PPSSPP_ARCH(AMD64) +#define CTX_RAX rax +#define CTX_RBX rbx +#define CTX_RCX rcx +#define CTX_RDX rdx +#define CTX_RDI rdi +#define CTX_RSI rsi +#define CTX_RBP rbp +#define CTX_RSP rsp +#define CTX_R8 r8 +#define CTX_R9 r9 +#define CTX_R10 r10 +#define CTX_R11 r11 +#define CTX_R12 r12 +#define CTX_R13 r13 +#define CTX_R14 r14 +#define CTX_R15 r15 +#define CTX_RIP rip +#else +#error No context definition for machine +#endif +#else +#error No context definition for OS +#endif + +#if PPSSPP_ARCH(AMD64) + +#include +#define CTX_PC CTX_RIP +static inline u64* ContextRN(SContext* ctx, int n) +{ + static const u8 offsets[] = { + offsetof(SContext, CTX_RAX), offsetof(SContext, CTX_RCX), offsetof(SContext, CTX_RDX), + offsetof(SContext, CTX_RBX), offsetof(SContext, CTX_RSP), offsetof(SContext, CTX_RBP), + offsetof(SContext, CTX_RSI), offsetof(SContext, CTX_RDI), offsetof(SContext, CTX_R8), + offsetof(SContext, CTX_R9), offsetof(SContext, CTX_R10), offsetof(SContext, CTX_R11), + offsetof(SContext, CTX_R12), offsetof(SContext, CTX_R13), offsetof(SContext, CTX_R14), + offsetof(SContext, CTX_R15)}; + return (u64*)((char*)ctx + offsets[n]); +} + +#elif PPSSPP_ARCH(X86) + +#include +#define CTX_PC CTX_RIP + +static inline u32* ContextRN(SContext* ctx, int n) +{ + static const u8 offsets[] = { + offsetof(SContext, CTX_RAX), offsetof(SContext, CTX_RCX), offsetof(SContext, CTX_RDX), + offsetof(SContext, CTX_RBX), offsetof(SContext, CTX_RSP), offsetof(SContext, CTX_RBP), + offsetof(SContext, CTX_RSI), offsetof(SContext, CTX_RDI)}; + return (u32*)((char*)ctx + offsets[n]); +} +#endif diff --git a/Common/x64Analyzer.cpp b/Common/x64Analyzer.cpp index 1940416d6a9c..0d04346c0457 100644 --- a/Common/x64Analyzer.cpp +++ b/Common/x64Analyzer.cpp @@ -17,8 +17,10 @@ #include "x64Analyzer.h" -bool DisassembleMov(const unsigned char *codePtr, InstructionInfo &info, int accessType) +bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info) { + int accessType = 0; + unsigned const char *startCodePtr = codePtr; u8 rex = 0; u8 codeByte = 0; @@ -80,6 +82,12 @@ bool DisassembleMov(const unsigned char *codePtr, InstructionInfo &info, int acc modRMbyte = *codePtr++; hasModRM = true; } + + // TODO: Add more cases. + if ((codeByte & 0xF0) == 0x80) + accessType = 1; + if ((codeByte & 0xF0) == 0xC0) + accessType = 1; } else { @@ -135,7 +143,6 @@ bool DisassembleMov(const unsigned char *codePtr, InstructionInfo &info, int acc else info.displacement = *((s32 *)codePtr); codePtr += displacementSize; - if (accessType == 1) { diff --git a/Common/x64Analyzer.h b/Common/x64Analyzer.h index ad2fed886d50..8ba78005e50a 100644 --- a/Common/x64Analyzer.h +++ b/Common/x64Analyzer.h @@ -19,7 +19,7 @@ #include "Common.h" -struct InstructionInfo +struct LSInstructionInfo { int operandSize; //8, 16, 32, 64 int instructionSize; @@ -60,4 +60,4 @@ enum AccessType { OP_ACCESS_WRITE = 1 }; -bool DisassembleMov(const unsigned char *codePtr, InstructionInfo &info, int accessType); +bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info); diff --git a/Core/Core.cpp b/Core/Core.cpp index ecc3d08259c4..dfb28a6fc890 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -395,6 +395,7 @@ const char *ExceptionTypeAsString(ExceptionType type) { const char *MemoryExceptionTypeAsString(MemoryExceptionType type) { switch (type) { + case MemoryExceptionType::UNKNOWN: return "Unknown"; case MemoryExceptionType::READ_WORD: return "Read Word"; case MemoryExceptionType::WRITE_WORD: return "Write Word"; case MemoryExceptionType::READ_BLOCK: return "Read Block"; diff --git a/Core/Core.h b/Core/Core.h index 6cb8004cf4ce..314a2823a15b 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -80,6 +80,8 @@ void Core_SetPowerSaving(bool mode); bool Core_GetPowerSaving(); enum class MemoryExceptionType { + NONE, + UNKNOWN, READ_WORD, WRITE_WORD, READ_BLOCK, diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 2f0016d511e3..251a2c0f9e8c 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -97,6 +97,11 @@ void FireMhzChange() { } void SetClockFrequencyHz(int cpuHz) { + if (cpuHz <= 0) { + // Paranoid check, protecting against division by zero and similar nonsense. + return; + } + // When the mhz changes, we keep track of what "time" it was before hand. // This way, time always moves forward, even if mhz is changed. lastGlobalTimeUs = GetGlobalTimeUs(); diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index 3a5bcba1a53b..44176dc293f8 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -237,6 +237,7 @@ void ArmJit::GenerateFixedCode() { CMP(R0, 0); B_CC(CC_EQ, outerLoop); + const uint8_t *quitLoop = GetCodePtr(); SetJumpTarget(badCoreState); SaveDowncount(); @@ -251,6 +252,12 @@ void ArmJit::GenerateFixedCode() { POP(9, R4, R5, R6, R7, R8, R9, R10, R11, R_PC); // Returns + crashHandler = GetCodePtr(); + MOVP2R(R0, &coreState); + MOVI2R(R1, CORE_RUNTIME_ERROR); + STR(R1, R0, 0); + B(quitLoop); + // Uncomment if you want to see the output... if (disasm) { INFO_LOG(JIT, "THE DISASM ========================"); diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h index 80a3bc6ae962..50322a65bc65 100644 --- a/Core/MIPS/ARM/ArmJit.h +++ b/Core/MIPS/ARM/ArmJit.h @@ -51,6 +51,8 @@ class ArmJit : public ArmGen::ARMXCodeBlock, public JitInterface, public MIPSFro void Compile(u32 em_address) override; // Compiles a block at current MIPS PC + const u8 *GetCrashHandler() const override { return crashHandler; } + bool CodeInRange(const u8 *ptr) const override { return IsInSpace(ptr); } bool DescribeCodePtr(const u8 *ptr, std::string &name) override; MIPSOpcode GetOriginalOp(MIPSOpcode op) override; @@ -313,6 +315,8 @@ class ArmJit : public ArmGen::ARMXCodeBlock, public JitInterface, public MIPSFro const u8 *restoreRoundingMode; const u8 *applyRoundingMode; + + const u8 *crashHandler; }; } // namespace MIPSComp diff --git a/Core/MIPS/ARM64/Arm64Asm.cpp b/Core/MIPS/ARM64/Arm64Asm.cpp index 755e35f57c5d..81f67fa3a351 100644 --- a/Core/MIPS/ARM64/Arm64Asm.cpp +++ b/Core/MIPS/ARM64/Arm64Asm.cpp @@ -277,6 +277,7 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) { CMP(SCRATCH1, 0); B(CC_EQ, outerLoop); + const uint8_t *quitLoop = GetCodePtr(); SetJumpTarget(badCoreState); SaveStaticRegisters(); @@ -286,6 +287,12 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) { RET(); + crashHandler = GetCodePtr(); + MOVP2R(SCRATCH1_64, &coreState); + MOVI2R(SCRATCH2, CORE_RUNTIME_ERROR); + STR(INDEX_UNSIGNED, SCRATCH2, SCRATCH1_64, 0); + B(quitLoop); + // Generate some integer conversion funcs. // MIPS order! static const RoundingMode roundModes[8] = { ROUND_N, ROUND_Z, ROUND_P, ROUND_M, ROUND_N, ROUND_Z, ROUND_P, ROUND_M }; diff --git a/Core/MIPS/ARM64/Arm64Jit.h b/Core/MIPS/ARM64/Arm64Jit.h index 6ad5921d53ef..17734ade594a 100644 --- a/Core/MIPS/ARM64/Arm64Jit.h +++ b/Core/MIPS/ARM64/Arm64Jit.h @@ -52,6 +52,8 @@ class Arm64Jit : public Arm64Gen::ARM64CodeBlock, public JitInterface, public MI void Compile(u32 em_address) override; // Compiles a block at current MIPS PC const u8 *DoJit(u32 em_address, JitBlock *b); + const u8 *GetCrashHandler() const override { return crashHandler; } + bool CodeInRange(const u8 *ptr) const override { return IsInSpace(ptr); } bool DescribeCodePtr(const u8 *ptr, std::string &name) override; MIPSOpcode GetOriginalOp(MIPSOpcode op) override; @@ -281,6 +283,8 @@ class Arm64Jit : public Arm64Gen::ARM64CodeBlock, public JitInterface, public MI const u8 *applyRoundingMode; const u8 *updateRoundingMode; + const u8 *crashHandler; + int jitStartOffset; // Indexed by FPCR FZ:RN bits for convenience. Uses SCRATCH2. diff --git a/Core/MIPS/IR/IRJit.h b/Core/MIPS/IR/IRJit.h index d378dcfc87a5..9beabe585ac4 100644 --- a/Core/MIPS/IR/IRJit.h +++ b/Core/MIPS/IR/IRJit.h @@ -160,7 +160,12 @@ class IRJit : public JitInterface { void InvalidateCacheAt(u32 em_address, int length = 4) override; void UpdateFCR31() override; + bool CodeInRange(const u8 *ptr) const override { + return false; + } + const u8 *GetDispatcher() const override { return nullptr; } + const u8 *GetCrashHandler() const override { return nullptr; } void LinkBlock(u8 *exitPoint, const u8 *checkedEntry) override; void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) override; diff --git a/Core/MIPS/JitCommon/JitCommon.h b/Core/MIPS/JitCommon/JitCommon.h index bf3023e21d67..08063f866e49 100644 --- a/Core/MIPS/JitCommon/JitCommon.h +++ b/Core/MIPS/JitCommon/JitCommon.h @@ -121,8 +121,10 @@ namespace MIPSComp { public: virtual ~JitInterface() {} + virtual bool CodeInRange(const u8 *ptr) const = 0; virtual bool DescribeCodePtr(const u8 *ptr, std::string &name) = 0; virtual const u8 *GetDispatcher() const = 0; + virtual const u8 *GetCrashHandler() const = 0; virtual JitBlockCache *GetBlockCache() = 0; virtual JitBlockCacheDebugInterface *GetBlockCacheDebugInterface() = 0; virtual void InvalidateCacheAt(u32 em_address, int length = 4) = 0; diff --git a/Core/MIPS/MIPS.h b/Core/MIPS/MIPS.h index 7d2c40e8797d..bcc991035895 100644 --- a/Core/MIPS/MIPS.h +++ b/Core/MIPS/MIPS.h @@ -17,6 +17,8 @@ #pragma once +#include "ppsspp_config.h" + #include #include "util/random/rng.h" @@ -148,7 +150,7 @@ extern u8 fromvoffset[128]; enum class CPUCore; -#if defined(PPSSPP_ARCH_X86) || defined(PPSSPP_ARCH_AMD64) +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) // Note that CTXREG is offset to point at the first floating point register, intentionally. This is so that a byte offset // can reach both GPR and FPR regs. @@ -246,7 +248,7 @@ class MIPSState static const u32 FCR0_VALUE = 0x00003351; -#if defined(PPSSPP_ARCH_X86) || defined(PPSSPP_ARCH_AMD64) +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) // FPU TEMP0, etc. are swapped in here if necessary (e.g. on x86.) float tempValues[NUM_X86_FPU_TEMPS]; #endif diff --git a/Core/MIPS/x86/Asm.cpp b/Core/MIPS/x86/Asm.cpp index fd1815132d1b..d927ba8c6d87 100644 --- a/Core/MIPS/x86/Asm.cpp +++ b/Core/MIPS/x86/Asm.cpp @@ -206,11 +206,21 @@ void Jit::GenerateFixedCode(JitOptions &jo) { } J_CC(CC_Z, outerLoop, true); + const uint8_t *quitLoop = GetCodePtr(); SetJumpTarget(badCoreState); RestoreRoundingMode(true); ABI_PopAllCalleeSavedRegsAndAdjustStack(); RET(); + crashHandler = GetCodePtr(); + if (RipAccessible((const void *)&coreState)) { + MOV(32, M(&coreState), Imm32(CORE_RUNTIME_ERROR)); + } else { + MOV(PTRBITS, R(RAX), ImmPtr((const void *)&coreState)); + MOV(32, MatR(RAX), Imm32(CORE_RUNTIME_ERROR)); + } + JMP(quitLoop, true); + // Let's spare the pre-generated code from unprotect-reprotect. endOfPregeneratedCode = AlignCodePage(); EndWrite(); diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index 2716f7233a49..0dbdf34d7b2a 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -442,6 +442,8 @@ bool Jit::DescribeCodePtr(const u8 *ptr, std::string &name) { name = "enterDispatcher"; else if (ptr == restoreRoundingMode) name = "restoreRoundingMode"; + else if (ptr == crashHandler) + name = "crashHandler"; else { u32 jitAddr = blocks.GetAddressFromBlockPtr(ptr); diff --git a/Core/MIPS/x86/Jit.h b/Core/MIPS/x86/Jit.h index 0eefe89c40bd..b065d9052216 100644 --- a/Core/MIPS/x86/Jit.h +++ b/Core/MIPS/x86/Jit.h @@ -63,6 +63,8 @@ class Jit : public Gen::XCodeBlock, public JitInterface, public MIPSFrontendInte void Compile(u32 em_address) override; // Compiles a block at current MIPS PC const u8 *DoJit(u32 em_address, JitBlock *b); + const u8 *GetCrashHandler() const override { return crashHandler; } + bool CodeInRange(const u8 *ptr) const override { return IsInSpace(ptr); } bool DescribeCodePtr(const u8 *ptr, std::string &name) override; void Comp_RunBlock(MIPSOpcode op) override; @@ -325,6 +327,8 @@ class Jit : public Gen::XCodeBlock, public JitInterface, public MIPSFrontendInte const u8 *endOfPregeneratedCode; + const u8 *crashHandler; + friend class JitSafeMem; friend class JitSafeMemFuncs; }; diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index 9c7a2cce19d1..4c36b7c6087c 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -29,6 +29,26 @@ #include "Common/MemArena.h" #include "Common/ChunkFile.h" +#ifdef __FreeBSD__ +#include +#endif +#ifndef _WIN32 +#include // Needed for _POSIX_VERSION +#endif + +#if PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(X86) +#include "Common/MachineContext.h" +#include "Common/x64Analyzer.h" +#elif PPSSPP_ARCH(ARM64) && !PPSSPP_PLATFORM(IOS) +#include "Core/Util/DisArm64.h" +typedef sigcontext SContext; +#define CTX_PC pc +#elif PPSSPP_ARCH(ARM) && !PPSSPP_PLATFORM(IOS) +#include "ext/disarm.h" +typedef sigcontext SContext; +#define CTX_PC arm_pc +#endif + #include "Core/MemMap.h" #include "Core/HDRemaster.h" #include "Core/MIPS/MIPS.h" @@ -41,6 +61,8 @@ #include "Core/ConfigValues.h" #include "Core/HLE/ReplaceTables.h" #include "Core/MIPS/JitCommon/JitBlockCache.h" +#include "Core/MIPS/JitCommon/JitCommon.h" +#include "UI/OnScreenDisplay.h" namespace Memory { @@ -85,6 +107,8 @@ u32 g_PSPModel; std::recursive_mutex g_shutdownLock; +static int64_t g_numReportedBadAccesses = 0; + // We don't declare the IO region in here since its handled by other means. static MemoryView views[] = { @@ -221,16 +245,22 @@ bool MemoryMap_Setup(u32 flags) { #if !PPSSPP_PLATFORM(ANDROID) if (g_arena.NeedsProbing()) { int base_attempts = 0; -#if defined(_WIN32) && PPSSPP_ARCH(32BIT) +#if PPSSPP_PLATFORM(WINDOWS) && PPSSPP_ARCH(32BIT) // Try a whole range of possible bases. Return once we got a valid one. uintptr_t max_base_addr = 0x7FFF0000 - 0x10000000; uintptr_t min_base_addr = 0x01000000; uintptr_t stride = 0x400000; -#else +#elif PPSSPP_ARCH(ARM64) && PPSSPP_PLATFORM(IOS) // iOS uintptr_t max_base_addr = 0x1FFFF0000ULL - 0x80000000ULL; uintptr_t min_base_addr = 0x100000000ULL; uintptr_t stride = 0x800000; +#else + uintptr_t max_base_addr = 0; + uintptr_t min_base_addr = 0; + uintptr_t stride = 0; + ERROR_LOG(MEMMAP, "MemoryMap_Setup: Hit a wrong path, should not be needed on this platform."); + return false; #endif for (uintptr_t base_addr = min_base_addr; base_addr < max_base_addr; base_addr += stride) { base_attempts++; @@ -290,6 +320,8 @@ void Init() { INFO_LOG(MEMMAP, "Memory system initialized. Base at %p (RAM at @ %p, uncached @ %p)", base, m_pPhysicalRAM, m_pUncachedRAM); + + g_numReportedBadAccesses = 0; } void Reinit() { @@ -457,4 +489,103 @@ void Memset(const u32 _Address, const u8 _iValue, const u32 _iLength) { CBreakPoints::ExecMemCheck(_Address, true, _iLength, currentMIPS->pc); } +// We do not support crash catching on UWP and iOS. +// On iOS, the sigcontext struct seems to be missing?? +#if !PPSSPP_PLATFORM(IOS) && !PPSSPP_PLATFORM(UWP) + +bool HandleFault(uintptr_t hostAddress, void *ctx) { + SContext *context = (SContext *)ctx; + const uint8_t *codePtr = (uint8_t *)(context->CTX_PC); + + // TODO: Check that codePtr is within the current JIT space. + bool inJitSpace = MIPSComp::jit && MIPSComp::jit->CodeInRange(codePtr); + if (!inJitSpace) { + // This is a crash in non-jitted code. Not something we want to handle here, ignore. + return false; + } + + uintptr_t baseAddress = (uintptr_t)base; +#ifdef MASKED_PSP_MEMORY + const uintptr_t addressSpaceSize = 0x40000000ULL; +#else + const uintptr_t addressSpaceSize = 0x100000000ULL; +#endif + + // Check whether hostAddress is within the PSP memory space, which (likely) means it was a guest executable that did the bad access. + if (hostAddress < baseAddress || hostAddress >= baseAddress + addressSpaceSize) { + // Host address outside - this was a different kind of crash. + return false; + } + + // OK, a guest executable did a bad access. Take care of it. + + uint32_t guestAddress = hostAddress - baseAddress; + + // TODO: Share the struct between the various analyzers, that will allow us to share most of + // the implementations here. + bool success = false; + + MemoryExceptionType type = MemoryExceptionType::NONE; + +#if PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(X86) + // X86, X86-64. Variable instruction size so need to analyze the mov instruction in detail. + + // To ignore the access, we need to disassemble the instruction and modify context->CTX_PC + LSInstructionInfo info; + success = X86AnalyzeMOV(codePtr, info); +#elif PPSSPP_ARCH(ARM64) + uint32_t word; + memcpy(&word, codePtr, 4); + // To ignore the access, we need to disassemble the instruction and modify context->CTX_PC + Arm64LSInstructionInfo info; + success = Arm64AnalyzeLoadStore((uint64_t)codePtr, word, &info); +#elif PPSSPP_ARCH(ARM) + uint32_t word; + memcpy(&word, codePtr, 4); + // To ignore the access, we need to disassemble the instruction and modify context->CTX_PC + ArmLSInstructionInfo info; + success = ArmAnalyzeLoadStore((uint32_t)codePtr, word, &info); +#endif + if (success) { + if (info.isMemoryWrite) { + type = MemoryExceptionType::WRITE_WORD; + } else { + type = MemoryExceptionType::READ_WORD; + } + } else { + type = MemoryExceptionType::UNKNOWN; + } + + if (success && g_Config.bIgnoreBadMemAccess) { + if (!info.isMemoryWrite) { + // It was a read. Fill the destination register with 0. + // TODO + } + // Move on to the next instruction. Note that handling bad accesses like this is pretty slow. + context->CTX_PC += info.instructionSize; + g_numReportedBadAccesses++; + if (g_numReportedBadAccesses < 100) { + ERROR_LOG(MEMMAP, "Bad memory access detected and ignored: %08x (%p)", guestAddress, (void *)hostAddress); + } + } else { + // Either bIgnoreBadMemAccess is off, or we failed recovery analysis. + uint32_t approximatePC = currentMIPS->pc; + Core_MemoryException(guestAddress, currentMIPS->pc, type); + + // Redirect execution to a crash handler that will exit the game. + context->CTX_PC = (uintptr_t)MIPSComp::jit->GetCrashHandler(); + ERROR_LOG(MEMMAP, "Bad memory access detected! %08x (%p) Stopping emulation.", guestAddress, (void *)hostAddress); + } + return true; +} + +#else + +bool HandleFault(uintptr_t hostAddress, void *ctx) { + ERROR_LOG(MEMMAP, "Exception handling not supported"); + return false; +} + +#endif + } // namespace diff --git a/Core/MemMap.h b/Core/MemMap.h index 4f598961f3a6..dcbbad5441f5 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -20,6 +20,7 @@ #include "ppsspp_config.h" #include +#include #ifndef offsetof #include #endif @@ -147,6 +148,10 @@ Opcode Read_Opcode_JIT(const u32 _Address); // used by JIT. Reads in the "Locked cache" mode void Write_Opcode_JIT(const u32 _Address, const Opcode& _Value); +// Called by exception handlers. We simply filter out accesses to PSP RAM and otherwise +// just leave it as-is. +bool HandleFault(uintptr_t hostAddress, void *context); + // Should be used by analyzers, disassemblers etc. Does resolve replacements. Opcode Read_Instruction(const u32 _Address, bool resolveReplacements = false); Opcode ReadUnchecked_Instruction(const u32 _Address, bool resolveReplacements = false); diff --git a/Core/System.cpp b/Core/System.cpp index c8a8b4c769d0..c679bb60b908 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -62,6 +62,7 @@ #include "Core/ELF/ParamSFO.h" #include "Core/SaveState.h" #include "Common/LogManager.h" +#include "Common/ExceptionHandlerSetup.h" #include "Core/HLE/sceAudiocodec.h" #include "GPU/GPUState.h" @@ -266,10 +267,11 @@ void CPU_Init() { return; } - if (coreParameter.updateRecent) { g_Config.AddRecent(filename); } + + InstallExceptionHandler(&Memory::HandleFault); } PSP_LoadingLock::PSP_LoadingLock() { @@ -281,6 +283,8 @@ PSP_LoadingLock::~PSP_LoadingLock() { } void CPU_Shutdown() { + UninstallExceptionHandler(); + // Since we load on a background thread, wait for startup to complete. PSP_LoadingLock lock; PSPLoaders_Shutdown(); diff --git a/Core/Util/DisArm64.cpp b/Core/Util/DisArm64.cpp index fd17c5441239..e6c983be554e 100644 --- a/Core/Util/DisArm64.cpp +++ b/Core/Util/DisArm64.cpp @@ -265,6 +265,40 @@ static void BranchExceptionAndSystem(uint32_t w, uint64_t addr, Instruction *ins } } +bool Arm64AnalyzeLoadStore(uint64_t addr, uint32_t w, Arm64LSInstructionInfo *info) { + *info = {}; + info->instructionSize = 4; + int id = (w >> 25) & 0xF; + switch (id) { + case 4: case 6: case 0xC: case 0xE: + break; + default: + return false; // not the expected instruction + } + + info->size = w >> 30; + info->Rt = (w & 0x1F); + info->Rn = ((w >> 5) & 0x1F); + info->Rm = ((w >> 16) & 0x1F); + int opc = (w >> 22) & 0x3; + if (opc == 0 || opc == 2) { + info->isMemoryWrite = true; + } + + if (((w >> 27) & 7) == 7) { + int V = (w >> 26) & 1; + if (V == 0) { + info->isIntegerLoadStore = true; + } else { + info->isFPLoadStore = true; + } + } else { + info->isPairLoadStore = true; + // TODO + } + return true; +} + static void LoadStore(uint32_t w, uint64_t addr, Instruction *instr) { int size = w >> 30; int imm9 = SignExtend9((w >> 12) & 0x1FF); diff --git a/Core/Util/DisArm64.h b/Core/Util/DisArm64.h index fc55c6c34b8a..ced77f2cc269 100644 --- a/Core/Util/DisArm64.h +++ b/Core/Util/DisArm64.h @@ -18,9 +18,28 @@ // Basic ARM64 disassembler. // No promises of accuracy, mostly just made to debug JIT code. -#include +#include typedef bool (*SymbolCallback)(char *buffer, int bufsize, uint8_t *address); void Arm64Dis(uint64_t addr, uint32_t w, char *output, int bufsize, bool includeWord, SymbolCallback symbolCallback = nullptr); +// Information about a load/store instruction. +struct Arm64LSInstructionInfo { + int instructionSize; + + bool isIntegerLoadStore; + bool isFPLoadStore; + bool isPairLoadStore; + + int size; // 0 = 8-bit, 1 = 16-bit, 2 = 32-bit, 3 = 64-bit + bool isMemoryWrite; + + int Rt; + int Rn; + int Rm; + + // TODO: more. +}; + +bool Arm64AnalyzeLoadStore(uint64_t addr, uint32_t op, Arm64LSInstructionInfo *info); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 80170e844bc6..7992aeab3155 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1158,7 +1158,7 @@ void EmuScreen::update() { PSP_CoreParameter().pixelHeight = pixel_yres * bounds.h / dp_yres; #endif - if (!invalid_) { + if (!invalid_ && coreState != CORE_RUNTIME_ERROR) { UpdateUIState(UISTATE_INGAME); } @@ -1271,7 +1271,8 @@ static void DrawCrashDump(DrawBuffer *draw2d) { FontID ubuntu24("UBUNTU24"); char statbuf[4096]; char versionString[256]; - sprintf(versionString, "%s", PPSSPP_GIT_VERSION); + snprintf(versionString, sizeof(versionString), "%s", PPSSPP_GIT_VERSION); + // TODO: Draw a lot more information. Full register set, and so on. #ifdef _DEBUG @@ -1539,8 +1540,10 @@ bool EmuScreen::hasVisibleUI() { return true; // Exception information. - if (coreState == CORE_RUNTIME_ERROR || coreState == CORE_STEPPING) + if (coreState == CORE_RUNTIME_ERROR || coreState == CORE_STEPPING) { return true; + } + return false; } diff --git a/UWP/CommonUWP/CommonUWP.vcxproj b/UWP/CommonUWP/CommonUWP.vcxproj index a5fe599d1012..88559e8c82a8 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj +++ b/UWP/CommonUWP/CommonUWP.vcxproj @@ -398,6 +398,7 @@ + @@ -435,6 +436,7 @@ + diff --git a/UWP/CommonUWP/CommonUWP.vcxproj.filters b/UWP/CommonUWP/CommonUWP.vcxproj.filters index 566049b279eb..100c103e66d1 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj.filters +++ b/UWP/CommonUWP/CommonUWP.vcxproj.filters @@ -16,6 +16,7 @@ + @@ -65,6 +66,7 @@ + diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 5bdf22bd1893..44ab4a73329a 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -15,6 +15,7 @@ ifeq ($(TARGET_ARCH_ABI),x86) ARCH_FILES := \ $(SRC)/Common/ABI.cpp \ $(SRC)/Common/x64Emitter.cpp \ + $(SRC)/Common/x64Analyzer.cpp \ $(SRC)/Common/CPUDetect.cpp \ $(SRC)/Common/Thunk.cpp \ $(SRC)/Core/MIPS/x86/CompALU.cpp \ @@ -36,6 +37,7 @@ ifeq ($(TARGET_ARCH_ABI),x86_64) ARCH_FILES := \ $(SRC)/Common/ABI.cpp \ $(SRC)/Common/x64Emitter.cpp \ + $(SRC)/Common/x64Analyzer.cpp \ $(SRC)/Common/CPUDetect.cpp \ $(SRC)/Common/Thunk.cpp \ $(SRC)/Core/MIPS/x86/CompALU.cpp \ @@ -205,6 +207,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Common/Crypto/sha256.cpp \ $(SRC)/Common/ChunkFile.cpp \ $(SRC)/Common/ColorConv.cpp \ + $(SRC)/Common/ExceptionHandlerSetup.cpp \ $(SRC)/Common/KeyMap.cpp \ $(SRC)/Common/LogManager.cpp \ $(SRC)/Common/MemArenaAndroid.cpp \ diff --git a/ext/disarm.cpp b/ext/disarm.cpp index 90c89c16b5d3..ece10410f4fb 100644 --- a/ext/disarm.cpp +++ b/ext/disarm.cpp @@ -72,6 +72,7 @@ #include "base/basictypes.h" #include "Common/ArmEmitter.h" +#include "ext/disarm.h" static const char *CCFlagsStr[] = { "EQ", // Equal @@ -759,30 +760,14 @@ static bool DisasmNeon(uint32_t op, char *text) { return false; } +bool ArmAnalyzeLoadStore(uint32_t addr, uint32_t op, ArmLSInstructionInfo *info) { + *info = {}; + info->instructionSize = 4; + // TODO - - - - - - - - - - - - - - - - - - - - - - + return false; +} typedef unsigned int word; diff --git a/ext/disarm.h b/ext/disarm.h index 9bae68f21567..901e74cd4fc2 100644 --- a/ext/disarm.h +++ b/ext/disarm.h @@ -17,18 +17,34 @@ #pragma once +#include -// This stuff only disassembles very old ARM but should be sufficient -// for the basics except for MOVW/MOVT. - - +// This stuff used to only disassemble very old ARM but has now +// been extended to support most (but not all) modern instructions, including NEON. // Disarm itself has the license you can see in the cpp file. // I'm not entirely sure it's 100% gpl compatible but it's nearly // public domain so meh. -// The only changes I've done is C++ compat and replaced the main -// program with this function. - const char *ArmRegName(int r); void ArmDis(unsigned int addr, unsigned int w, char *output, int bufsize, bool includeWord); + +// Information about a load/store instruction. +struct ArmLSInstructionInfo { + int instructionSize; + + bool isIntegerLoadStore; + bool isFPLoadStore; + bool isMultiLoadStore; + + int size; // 0 = 8-bit, 1 = 16-bit, 2 = 32-bit, 3 = 64-bit + bool isMemoryWrite; + + int Rt; + int Rn; + int Rm; + + // TODO: more. +}; + +bool ArmAnalyzeLoadStore(uint32_t addr, uint32_t op, ArmLSInstructionInfo *info); diff --git a/libretro/Makefile.common b/libretro/Makefile.common index ab7d30ebdfd9..ed78f44cc444 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -143,6 +143,7 @@ SOURCES_CXX += \ SOURCES_CXX += \ $(COMMONDIR)/ChunkFile.cpp \ $(COMMONDIR)/ConsoleListener.cpp \ + $(COMMONDIR)/ExceptionHandlerSetup.cpp \ $(COMMONDIR)/FileUtil.cpp \ $(COMMONDIR)/KeyMap.cpp \ $(COMMONDIR)/LogManager.cpp \