mirror of https://github.com/PCSX2/pcsx2.git
Common: Tidy up signal handlers
Move MacOS into its own file. Fix assertion failure crash dumping.
This commit is contained in:
parent
f2e4a5e780
commit
d48f527d6d
|
@ -69,7 +69,7 @@ else()
|
||||||
find_package(Wayland REQUIRED Egl)
|
find_package(Wayland REQUIRED Egl)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Libbacktrace)
|
find_package(Libbacktrace REQUIRED)
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(DBUS REQUIRED dbus-1)
|
pkg_check_modules(DBUS REQUIRED dbus-1)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -44,15 +44,6 @@ target_sources(common PRIVATE
|
||||||
emitter/movs.cpp
|
emitter/movs.cpp
|
||||||
emitter/simd.cpp
|
emitter/simd.cpp
|
||||||
emitter/x86emitter.cpp
|
emitter/x86emitter.cpp
|
||||||
Darwin/DarwinThreads.cpp
|
|
||||||
Darwin/DarwinMisc.cpp
|
|
||||||
Darwin/DarwinSemaphore.cpp
|
|
||||||
Linux/LnxHostSys.cpp
|
|
||||||
Linux/LnxThreads.cpp
|
|
||||||
Linux/LnxMisc.cpp
|
|
||||||
Windows/WinThreads.cpp
|
|
||||||
Windows/WinHostSys.cpp
|
|
||||||
Windows/WinMisc.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# x86emitter headers
|
# x86emitter headers
|
||||||
|
@ -124,34 +115,32 @@ target_sources(common PRIVATE
|
||||||
emitter/legacy_types.h
|
emitter/legacy_types.h
|
||||||
emitter/x86emitter.h
|
emitter/x86emitter.h
|
||||||
emitter/x86types.h
|
emitter/x86types.h
|
||||||
Darwin/DarwinMisc.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set_source_files_properties(PrecompiledHeader.cpp PROPERTIES HEADER_FILE_ONLY TRUE)
|
|
||||||
|
|
||||||
if(USE_VTUNE)
|
|
||||||
target_link_libraries(common PUBLIC Vtune::Vtune)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
enable_language(ASM_MASM)
|
enable_language(ASM_MASM)
|
||||||
target_sources(common PRIVATE FastJmp.asm)
|
|
||||||
target_link_libraries(common PUBLIC WIL::WIL winmm pathcch)
|
|
||||||
target_sources(common PRIVATE
|
target_sources(common PRIVATE
|
||||||
CrashHandler.cpp
|
|
||||||
CrashHandler.h
|
|
||||||
FastJmp.asm
|
FastJmp.asm
|
||||||
HTTPDownloaderWinHTTP.cpp
|
HTTPDownloaderWinHTTP.cpp
|
||||||
HTTPDownloaderWinHTTP.h
|
HTTPDownloaderWinHTTP.h
|
||||||
StackWalker.cpp
|
StackWalker.cpp
|
||||||
StackWalker.h
|
StackWalker.h
|
||||||
|
Windows/WinThreads.cpp
|
||||||
|
Windows/WinHostSys.cpp
|
||||||
|
Windows/WinMisc.cpp
|
||||||
)
|
)
|
||||||
endif()
|
target_link_libraries(common PUBLIC
|
||||||
|
WIL::WIL
|
||||||
if(APPLE)
|
winmm
|
||||||
|
pathcch
|
||||||
|
)
|
||||||
|
elseif(APPLE)
|
||||||
target_sources(common PRIVATE
|
target_sources(common PRIVATE
|
||||||
CocoaTools.mm
|
CocoaTools.mm
|
||||||
CocoaTools.h
|
CocoaTools.h
|
||||||
|
Darwin/DarwinThreads.cpp
|
||||||
|
Darwin/DarwinMisc.cpp
|
||||||
|
Darwin/DarwinMisc.h
|
||||||
)
|
)
|
||||||
target_compile_options(common PRIVATE -fobjc-arc)
|
target_compile_options(common PRIVATE -fobjc-arc)
|
||||||
target_link_options(common PRIVATE -fobjc-link-runtime)
|
target_link_options(common PRIVATE -fobjc-link-runtime)
|
||||||
|
@ -159,15 +148,27 @@ if(APPLE)
|
||||||
"-framework Foundation"
|
"-framework Foundation"
|
||||||
"-framework IOKit"
|
"-framework IOKit"
|
||||||
)
|
)
|
||||||
|
else()
|
||||||
|
target_sources(common PRIVATE
|
||||||
|
Linux/LnxHostSys.cpp
|
||||||
|
Linux/LnxThreads.cpp
|
||||||
|
Linux/LnxMisc.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(common PRIVATE
|
||||||
|
${DBUS_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
target_link_libraries(common PRIVATE
|
||||||
|
${DBUS_LINK_LIBRARIES}
|
||||||
|
libbacktrace::libbacktrace
|
||||||
|
X11::X11
|
||||||
|
X11::Xrandr
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(UNIX AND NOT APPLE)
|
set_source_files_properties(PrecompiledHeader.cpp PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
target_include_directories(common PRIVATE ${DBUS_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(common PRIVATE ${DBUS_LINK_LIBRARIES} X11::X11 X11::Xrandr)
|
if(USE_VTUNE)
|
||||||
if(TARGET libbacktrace::libbacktrace)
|
target_link_libraries(common PUBLIC Vtune::Vtune)
|
||||||
target_compile_definitions(common PRIVATE "HAS_LIBBACKTRACE=1")
|
|
||||||
target_link_libraries(common PRIVATE libbacktrace::libbacktrace)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (USE_GCC AND CMAKE_INTERPROCEDURAL_OPTIMIZATION)
|
if (USE_GCC AND CMAKE_INTERPROCEDURAL_OPTIMIZATION)
|
||||||
|
@ -181,7 +182,9 @@ if(NOT WIN32)
|
||||||
HTTPDownloaderCurl.cpp
|
HTTPDownloaderCurl.cpp
|
||||||
HTTPDownloaderCurl.h
|
HTTPDownloaderCurl.h
|
||||||
)
|
)
|
||||||
target_link_libraries(common PRIVATE CURL::libcurl)
|
target_link_libraries(common PRIVATE
|
||||||
|
CURL::libcurl
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(common PRIVATE
|
target_link_libraries(common PRIVATE
|
||||||
|
|
|
@ -56,23 +56,31 @@ static bool WriteMinidump(HMODULE hDbgHelp, HANDLE hFile, HANDLE hProcess, DWORD
|
||||||
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
||||||
PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
|
PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
|
||||||
|
|
||||||
PFNMINIDUMPWRITEDUMP minidump_write_dump = hDbgHelp ?
|
PFNMINIDUMPWRITEDUMP minidump_write_dump =
|
||||||
reinterpret_cast<PFNMINIDUMPWRITEDUMP>(GetProcAddress(hDbgHelp, "MiniDumpWriteDump")) :
|
hDbgHelp ? reinterpret_cast<PFNMINIDUMPWRITEDUMP>(GetProcAddress(hDbgHelp, "MiniDumpWriteDump")) : nullptr;
|
||||||
nullptr;
|
|
||||||
if (!minidump_write_dump)
|
if (!minidump_write_dump)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
MINIDUMP_EXCEPTION_INFORMATION mei;
|
MINIDUMP_EXCEPTION_INFORMATION mei = {};
|
||||||
PMINIDUMP_EXCEPTION_INFORMATION mei_ptr = nullptr;
|
|
||||||
if (exception)
|
if (exception)
|
||||||
{
|
{
|
||||||
mei.ThreadId = thread_id;
|
mei.ThreadId = thread_id;
|
||||||
mei.ExceptionPointers = exception;
|
mei.ExceptionPointers = exception;
|
||||||
mei.ClientPointers = FALSE;
|
mei.ClientPointers = FALSE;
|
||||||
mei_ptr = &mei;
|
return minidump_write_dump(hProcess, process_id, hFile, type, &mei, nullptr, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return minidump_write_dump(hProcess, process_id, hFile, type, mei_ptr, nullptr, nullptr);
|
__try
|
||||||
|
{
|
||||||
|
RaiseException(EXCEPTION_INVALID_HANDLE, 0, 0, nullptr);
|
||||||
|
}
|
||||||
|
__except (WriteMinidump(hDbgHelp, hFile, GetCurrentProcess(), GetCurrentProcessId(), GetCurrentThreadId(),
|
||||||
|
GetExceptionInformation(), type),
|
||||||
|
EXCEPTION_EXECUTE_HANDLER)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::wstring s_write_directory;
|
static std::wstring s_write_directory;
|
||||||
|
@ -168,7 +176,7 @@ void CrashHandler::WriteDumpForCaller()
|
||||||
WriteMinidumpAndCallstack(nullptr);
|
WriteMinidumpAndCallstack(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(HAS_LIBBACKTRACE)
|
#elif !defined(__APPLE__)
|
||||||
|
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
|
||||||
|
@ -194,16 +202,13 @@ namespace CrashHandler
|
||||||
static void FreeBuffer(BacktraceBuffer* buf);
|
static void FreeBuffer(BacktraceBuffer* buf);
|
||||||
static void AppendToBuffer(BacktraceBuffer* buf, const char* format, ...);
|
static void AppendToBuffer(BacktraceBuffer* buf, const char* format, ...);
|
||||||
static int BacktraceFullCallback(void* data, uintptr_t pc, const char* filename, int lineno, const char* function);
|
static int BacktraceFullCallback(void* data, uintptr_t pc, const char* filename, int lineno, const char* function);
|
||||||
static void CallExistingSignalHandler(int signal, siginfo_t* siginfo, void* ctx);
|
static void LogCallstack(int signal, const void* exception_pc);
|
||||||
static void CrashSignalHandler(int signal, siginfo_t* siginfo, void* ctx);
|
|
||||||
|
|
||||||
static std::recursive_mutex s_crash_mutex;
|
static std::recursive_mutex s_crash_mutex;
|
||||||
static bool s_in_signal_handler = false;
|
static bool s_in_signal_handler = false;
|
||||||
|
|
||||||
static backtrace_state* s_backtrace_state = nullptr;
|
static backtrace_state* s_backtrace_state = nullptr;
|
||||||
static struct sigaction s_old_sigbus_action;
|
} // namespace CrashHandler
|
||||||
static struct sigaction s_old_sigsegv_action;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* CrashHandler::GetSignalName(int signal_no)
|
const char* CrashHandler::GetSignalName(int signal_no)
|
||||||
{
|
{
|
||||||
|
@ -214,7 +219,7 @@ const char* CrashHandler::GetSignalName(int signal_no)
|
||||||
case SIGSEGV: return "SIGSEGV";
|
case SIGSEGV: return "SIGSEGV";
|
||||||
case SIGBUS: return "SIGBUS";
|
case SIGBUS: return "SIGBUS";
|
||||||
default: return "UNKNOWN";
|
default: return "UNKNOWN";
|
||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,7 +258,8 @@ void CrashHandler::AppendToBuffer(BacktraceBuffer* buf, const char* format, ...)
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CrashHandler::BacktraceFullCallback(void* data, uintptr_t pc, const char* filename, int lineno, const char* function)
|
int CrashHandler::BacktraceFullCallback(void* data, uintptr_t pc, const char* filename, int lineno,
|
||||||
|
const char* function)
|
||||||
{
|
{
|
||||||
BacktraceBuffer* buf = static_cast<BacktraceBuffer*>(data);
|
BacktraceBuffer* buf = static_cast<BacktraceBuffer*>(data);
|
||||||
AppendToBuffer(buf, " %016p", pc);
|
AppendToBuffer(buf, " %016p", pc);
|
||||||
|
@ -261,34 +267,36 @@ int CrashHandler::BacktraceFullCallback(void* data, uintptr_t pc, const char* fi
|
||||||
AppendToBuffer(buf, " %s", function);
|
AppendToBuffer(buf, " %s", function);
|
||||||
if (filename)
|
if (filename)
|
||||||
AppendToBuffer(buf, " [%s:%d]", filename, lineno);
|
AppendToBuffer(buf, " [%s:%d]", filename, lineno);
|
||||||
|
|
||||||
AppendToBuffer(buf, "\n");
|
AppendToBuffer(buf, "\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandler::CallExistingSignalHandler(int signal, siginfo_t* siginfo, void* ctx)
|
void CrashHandler::LogCallstack(int signal, const void* exception_pc)
|
||||||
{
|
{
|
||||||
const struct sigaction& sa = (signal == SIGBUS) ? s_old_sigbus_action : s_old_sigsegv_action;
|
BacktraceBuffer buf;
|
||||||
if (sa.sa_flags & SA_SIGINFO)
|
AllocateBuffer(&buf);
|
||||||
{
|
if (signal != 0 || exception_pc)
|
||||||
sa.sa_sigaction(signal, siginfo, ctx);
|
AppendToBuffer(&buf, "*************** Unhandled %s at %p ***************\n", GetSignalName(signal), exception_pc);
|
||||||
}
|
else
|
||||||
else if (sa.sa_handler == SIG_DFL)
|
AppendToBuffer(&buf, "*******************************************************************\n");
|
||||||
{
|
|
||||||
// Re-raising the signal would just queue it, and since we'd restore the handler back to us,
|
const int rc = backtrace_full(s_backtrace_state, 0, BacktraceFullCallback, nullptr, &buf);
|
||||||
// we'd end up right back here again. So just abort, because that's probably what it'd do anyway.
|
if (rc != 0)
|
||||||
abort();
|
AppendToBuffer(&buf, " backtrace_full() failed: %d\n");
|
||||||
}
|
|
||||||
else if (sa.sa_handler != SIG_IGN)
|
AppendToBuffer(&buf, "*******************************************************************\n");
|
||||||
{
|
|
||||||
sa.sa_handler(signal);
|
if (buf.used > 0)
|
||||||
}
|
write(STDERR_FILENO, buf.buffer, buf.used);
|
||||||
|
|
||||||
|
FreeBuffer(&buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashHandler::CrashSignalHandler(int signal, siginfo_t* siginfo, void* ctx)
|
void CrashHandler::CrashSignalHandler(int signal, siginfo_t* siginfo, void* ctx)
|
||||||
{
|
{
|
||||||
std::unique_lock lock(s_crash_mutex);
|
std::unique_lock lock(s_crash_mutex);
|
||||||
|
|
||||||
// If we crash somewhere in libbacktrace, don't bother trying again.
|
// If we crash somewhere in libbacktrace, don't bother trying again.
|
||||||
if (!s_in_signal_handler)
|
if (!s_in_signal_handler)
|
||||||
{
|
{
|
||||||
|
@ -304,27 +312,17 @@ void CrashHandler::CrashSignalHandler(int signal, siginfo_t* siginfo, void* ctx)
|
||||||
void* const exception_pc = nullptr;
|
void* const exception_pc = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
BacktraceBuffer buf;
|
LogCallstack(signal, exception_pc);
|
||||||
AllocateBuffer(&buf);
|
|
||||||
AppendToBuffer(&buf, "*************** Unhandled %s at %p ***************\n", GetSignalName(signal), exception_pc);
|
|
||||||
|
|
||||||
const int rc = backtrace_full(s_backtrace_state, 0, BacktraceFullCallback, nullptr, &buf);
|
|
||||||
if (rc != 0)
|
|
||||||
AppendToBuffer(&buf, " backtrace_full() failed: %d\n");
|
|
||||||
|
|
||||||
AppendToBuffer(&buf, "*******************************************************************\n");
|
|
||||||
|
|
||||||
if (buf.used > 0)
|
|
||||||
write(STDERR_FILENO, buf.buffer, buf.used);
|
|
||||||
|
|
||||||
FreeBuffer(&buf);
|
|
||||||
|
|
||||||
s_in_signal_handler = false;
|
s_in_signal_handler = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chances are we're not going to have anything else to call, but just in case.
|
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
CallExistingSignalHandler(signal, siginfo, ctx);
|
|
||||||
|
// We can't continue from here. Just bail out and dump core.
|
||||||
|
std::fputs("Aborting application.\n", stderr);
|
||||||
|
std::fflush(stderr);
|
||||||
|
std::abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CrashHandler::Install()
|
bool CrashHandler::Install()
|
||||||
|
@ -333,15 +331,15 @@ bool CrashHandler::Install()
|
||||||
s_backtrace_state = backtrace_create_state(progpath.empty() ? nullptr : progpath.c_str(), 0, nullptr, nullptr);
|
s_backtrace_state = backtrace_create_state(progpath.empty() ? nullptr : progpath.c_str(), 0, nullptr, nullptr);
|
||||||
if (!s_backtrace_state)
|
if (!s_backtrace_state)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
|
|
||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
sa.sa_flags = SA_SIGINFO | SA_NODEFER;
|
sa.sa_flags = SA_SIGINFO | SA_NODEFER;
|
||||||
sa.sa_sigaction = CrashSignalHandler;
|
sa.sa_sigaction = CrashSignalHandler;
|
||||||
if (sigaction(SIGBUS, &sa, &s_old_sigbus_action) != 0)
|
if (sigaction(SIGBUS, &sa, nullptr) != 0)
|
||||||
return false;
|
return false;
|
||||||
if (sigaction(SIGSEGV, &sa, &s_old_sigsegv_action) != 0)
|
if (sigaction(SIGSEGV, &sa, nullptr) != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -353,6 +351,7 @@ void CrashHandler::SetWriteDirectory(std::string_view dump_directory)
|
||||||
|
|
||||||
void CrashHandler::WriteDumpForCaller()
|
void CrashHandler::WriteDumpForCaller()
|
||||||
{
|
{
|
||||||
|
LogCallstack(0, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
@ -370,4 +369,12 @@ void CrashHandler::WriteDumpForCaller()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CrashHandler::CrashSignalHandler(int signal, siginfo_t* siginfo, void* ctx)
|
||||||
|
{
|
||||||
|
// We can't continue from here. Just bail out and dump core.
|
||||||
|
std::fputs("Aborting application.\n", stderr);
|
||||||
|
std::fflush(stderr);
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -3,9 +3,18 @@
|
||||||
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <csignal>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace CrashHandler
|
namespace CrashHandler
|
||||||
{
|
{
|
||||||
bool Install();
|
bool Install();
|
||||||
void SetWriteDirectory(std::string_view dump_directory);
|
void SetWriteDirectory(std::string_view dump_directory);
|
||||||
void WriteDumpForCaller();
|
void WriteDumpForCaller();
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
// Allow crash handler to be invoked from a signal.
|
||||||
|
void CrashSignalHandler(int signal, siginfo_t* siginfo, void* ctx);
|
||||||
|
#endif
|
||||||
} // namespace CrashHandler
|
} // namespace CrashHandler
|
||||||
|
|
|
@ -1,11 +1,18 @@
|
||||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||||
// SPDX-License-Identifier: LGPL-3.0+
|
// SPDX-License-Identifier: LGPL-3.0+
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
#include "common/Assertions.h"
|
||||||
|
#include "common/BitUtils.h"
|
||||||
|
#include "common/Console.h"
|
||||||
|
#include "common/CrashHandler.h"
|
||||||
#include "common/Darwin/DarwinMisc.h"
|
#include "common/Darwin/DarwinMisc.h"
|
||||||
|
#include "common/Error.h"
|
||||||
|
#include "common/Pcsx2Types.h"
|
||||||
|
#include "common/Threading.h"
|
||||||
|
#include "common/WindowInfo.h"
|
||||||
#include "common/HostSys.h"
|
#include "common/HostSys.h"
|
||||||
|
|
||||||
|
#include <csignal>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
@ -17,17 +24,11 @@
|
||||||
#include <mach/mach_port.h>
|
#include <mach/mach_port.h>
|
||||||
#include <mach/mach_time.h>
|
#include <mach/mach_time.h>
|
||||||
#include <mach/mach_vm.h>
|
#include <mach/mach_vm.h>
|
||||||
|
#include <mach/task.h>
|
||||||
#include <mach/vm_map.h>
|
#include <mach/vm_map.h>
|
||||||
|
#include <mutex>
|
||||||
#include <IOKit/pwr_mgt/IOPMLib.h>
|
#include <IOKit/pwr_mgt/IOPMLib.h>
|
||||||
|
|
||||||
#include "common/Assertions.h"
|
|
||||||
#include "common/BitUtils.h"
|
|
||||||
#include "common/Console.h"
|
|
||||||
#include "common/Pcsx2Types.h"
|
|
||||||
#include "common/HostSys.h"
|
|
||||||
#include "common/Threading.h"
|
|
||||||
#include "common/WindowInfo.h"
|
|
||||||
|
|
||||||
// Darwin (OSX) is a bit different from Linux when requesting properties of
|
// Darwin (OSX) is a bit different from Linux when requesting properties of
|
||||||
// the OS because of its BSD/Mach heritage. Helpfully, most of this code
|
// the OS because of its BSD/Mach heritage. Helpfully, most of this code
|
||||||
// should translate pretty well to other *BSD systems. (e.g.: the sysctl(3)
|
// should translate pretty well to other *BSD systems. (e.g.: the sysctl(3)
|
||||||
|
@ -399,6 +400,116 @@ void HostSys::EndCodeWrite()
|
||||||
pthread_jit_write_protect_np(1);
|
pthread_jit_write_protect_np(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[maybe_unused]] static bool IsStoreInstruction(const void* ptr)
|
||||||
|
{
|
||||||
|
u32 bits;
|
||||||
|
std::memcpy(&bits, ptr, sizeof(bits));
|
||||||
|
|
||||||
|
// Based on vixl's disassembler Instruction::IsStore().
|
||||||
|
// if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed)
|
||||||
|
if ((bits & 0x0a000000) != 0x08000000)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed)
|
||||||
|
if ((bits & 0x3a000000) == 0x28000000)
|
||||||
|
{
|
||||||
|
// return Mask(LoadStorePairLBit) == 0
|
||||||
|
return (bits & (1 << 22)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (bits & 0xC4C00000)
|
||||||
|
{
|
||||||
|
case 0x00000000: // STRB_w
|
||||||
|
case 0x40000000: // STRH_w
|
||||||
|
case 0x80000000: // STR_w
|
||||||
|
case 0xC0000000: // STR_x
|
||||||
|
case 0x04000000: // STR_b
|
||||||
|
case 0x44000000: // STR_h
|
||||||
|
case 0x84000000: // STR_s
|
||||||
|
case 0xC4000000: // STR_d
|
||||||
|
case 0x04800000: // STR_q
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _M_ARM64
|
||||||
|
|
||||||
|
namespace PageFaultHandler
|
||||||
|
{
|
||||||
|
static void SignalHandler(int sig, siginfo_t* info, void* ctx);
|
||||||
|
|
||||||
|
static std::recursive_mutex s_exception_handler_mutex;
|
||||||
|
static bool s_in_exception_handler = false;
|
||||||
|
static bool s_installed = false;
|
||||||
|
} // namespace PageFaultHandler
|
||||||
|
|
||||||
|
void PageFaultHandler::SignalHandler(int sig, siginfo_t* info, void* ctx)
|
||||||
|
{
|
||||||
|
#if defined(_M_X86)
|
||||||
|
void* const exception_address =
|
||||||
|
reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext->__es.__faultvaddr);
|
||||||
|
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext->__ss.__rip);
|
||||||
|
const bool is_write = (static_cast<ucontext_t*>(ctx)->uc_mcontext->__es.__err & 2) != 0;
|
||||||
|
#elif defined(_M_ARM64)
|
||||||
|
void* const exception_address = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext->__es.__far);
|
||||||
|
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext->__ss.__pc);
|
||||||
|
const bool is_write = IsStoreInstruction(exception_pc);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Executing the handler concurrently from multiple threads wouldn't go down well.
|
||||||
|
s_exception_handler_mutex.lock();
|
||||||
|
|
||||||
|
// Prevent recursive exception filtering.
|
||||||
|
HandlerResult result = HandlerResult::ExecuteNextHandler;
|
||||||
|
if (!s_in_exception_handler)
|
||||||
|
{
|
||||||
|
s_in_exception_handler = true;
|
||||||
|
result = HandlePageFault(exception_pc, exception_address, is_write);
|
||||||
|
s_in_exception_handler = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_exception_handler_mutex.unlock();
|
||||||
|
|
||||||
|
// Resumes execution right where we left off (re-executes instruction that caused the SIGSEGV).
|
||||||
|
if (result == HandlerResult::ContinueExecution)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// We couldn't handle it. Pass it off to the crash dumper.
|
||||||
|
CrashHandler::CrashSignalHandler(sig, info, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PageFaultHandler::Install(Error* error)
|
||||||
|
{
|
||||||
|
std::unique_lock lock(s_exception_handler_mutex);
|
||||||
|
pxAssertRel(!s_installed, "Page fault handler has already been installed.");
|
||||||
|
|
||||||
|
struct sigaction sa;
|
||||||
|
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_flags = SA_SIGINFO;
|
||||||
|
sa.sa_sigaction = SignalHandler;
|
||||||
|
|
||||||
|
// MacOS uses SIGBUS for memory permission violations, as well as SIGSEGV on ARM64.
|
||||||
|
if (sigaction(SIGBUS, &sa, nullptr) != 0)
|
||||||
|
{
|
||||||
|
Error::SetErrno(error, "sigaction() for SIGBUS failed: ", errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _M_ARM64
|
||||||
|
if (sigaction(SIGSEGV, &sa, nullptr) != 0)
|
||||||
|
{
|
||||||
|
Error::SetErrno(error, "sigaction() for SIGSEGV failed: ", errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Allow us to ignore faults when running under lldb.
|
||||||
|
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, MACH_PORT_NULL, EXCEPTION_DEFAULT, 0);
|
||||||
|
|
||||||
|
s_installed = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
|
||||||
// SPDX-License-Identifier: LGPL-3.0+
|
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cassert> // assert
|
|
||||||
#include <pthread.h> // pthread_setcancelstate()
|
|
||||||
#include <sys/time.h> // gettimeofday()
|
|
||||||
#include <mach/mach.h>
|
|
||||||
#include <mach/task.h> // semaphore_create() and semaphore_destroy()
|
|
||||||
#include <mach/semaphore.h> // semaphore_*()
|
|
||||||
#include <mach/mach_error.h> // mach_error_string()
|
|
||||||
#include <mach/mach_time.h> // mach_absolute_time()
|
|
||||||
|
|
||||||
#include "common/Threading.h"
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
|
||||||
// Semaphore Implementation for Darwin/OSX
|
|
||||||
//
|
|
||||||
// Sadly, Darwin/OSX needs its own implementation of Semaphores instead of
|
|
||||||
// relying on phtreads, because OSX unnamed semaphore (the best kind)
|
|
||||||
// support is very poor.
|
|
||||||
//
|
|
||||||
// This implementation makes use of Mach primitives instead. These are also
|
|
||||||
// what Grand Central Dispatch (GCD) is based on, as far as I understand:
|
|
||||||
// http://newosxbook.com/articles/GCD.html.
|
|
||||||
//
|
|
||||||
// --------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
static void MACH_CHECK(kern_return_t mach_retval)
|
|
||||||
{
|
|
||||||
if (mach_retval != KERN_SUCCESS)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "mach error: %s", mach_error_string(mach_retval));
|
|
||||||
assert(mach_retval == KERN_SUCCESS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Threading::KernelSemaphore::KernelSemaphore()
|
|
||||||
{
|
|
||||||
MACH_CHECK(semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
Threading::KernelSemaphore::~KernelSemaphore()
|
|
||||||
{
|
|
||||||
MACH_CHECK(semaphore_destroy(mach_task_self(), m_sema));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Threading::KernelSemaphore::Post()
|
|
||||||
{
|
|
||||||
MACH_CHECK(semaphore_signal(m_sema));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Threading::KernelSemaphore::Wait()
|
|
||||||
{
|
|
||||||
MACH_CHECK(semaphore_wait(m_sema));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Threading::KernelSemaphore::TryWait()
|
|
||||||
{
|
|
||||||
mach_timespec_t time = {};
|
|
||||||
kern_return_t res = semaphore_timedwait(m_sema, time);
|
|
||||||
if (res == KERN_OPERATION_TIMED_OUT)
|
|
||||||
return false;
|
|
||||||
MACH_CHECK(res);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,19 +1,24 @@
|
||||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||||
// SPDX-License-Identifier: LGPL-3.0+
|
// SPDX-License-Identifier: LGPL-3.0+
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
|
||||||
|
|
||||||
#include <sched.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <mach/mach_init.h>
|
|
||||||
#include <mach/thread_act.h>
|
|
||||||
#include <mach/mach_port.h>
|
|
||||||
|
|
||||||
#include "common/PrecompiledHeader.h"
|
|
||||||
#include "common/Threading.h"
|
#include "common/Threading.h"
|
||||||
#include "common/Assertions.h"
|
#include "common/Assertions.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cassert> // assert
|
||||||
|
#include <sched.h>
|
||||||
|
#include <sys/time.h> // gettimeofday()
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <mach/mach.h>
|
||||||
|
#include <mach/mach_error.h> // mach_error_string()
|
||||||
|
#include <mach/mach_init.h>
|
||||||
|
#include <mach/mach_port.h>
|
||||||
|
#include <mach/mach_time.h> // mach_absolute_time()
|
||||||
|
#include <mach/semaphore.h> // semaphore_*()
|
||||||
|
#include <mach/task.h> // semaphore_create() and semaphore_destroy()
|
||||||
|
#include <mach/thread_act.h>
|
||||||
|
|
||||||
// Note: assuming multicore is safer because it forces the interlocked routines to use
|
// Note: assuming multicore is safer because it forces the interlocked routines to use
|
||||||
// the LOCK prefix. The prefix works on single core CPUs fine (but is slow), but not
|
// the LOCK prefix. The prefix works on single core CPUs fine (but is slow), but not
|
||||||
// having the LOCK prefix is very bad indeed.
|
// having the LOCK prefix is very bad indeed.
|
||||||
|
@ -89,6 +94,58 @@ u64 Threading::GetThreadCpuTime()
|
||||||
return us;
|
return us;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
// Semaphore Implementation for Darwin/OSX
|
||||||
|
//
|
||||||
|
// Sadly, Darwin/OSX needs its own implementation of Semaphores instead of
|
||||||
|
// relying on phtreads, because OSX unnamed semaphore (the best kind)
|
||||||
|
// support is very poor.
|
||||||
|
//
|
||||||
|
// This implementation makes use of Mach primitives instead. These are also
|
||||||
|
// what Grand Central Dispatch (GCD) is based on, as far as I understand:
|
||||||
|
// http://newosxbook.com/articles/GCD.html.
|
||||||
|
//
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void MACH_CHECK(kern_return_t mach_retval)
|
||||||
|
{
|
||||||
|
if (mach_retval != KERN_SUCCESS)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "mach error: %s", mach_error_string(mach_retval));
|
||||||
|
assert(mach_retval == KERN_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Threading::KernelSemaphore::KernelSemaphore()
|
||||||
|
{
|
||||||
|
MACH_CHECK(semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
Threading::KernelSemaphore::~KernelSemaphore()
|
||||||
|
{
|
||||||
|
MACH_CHECK(semaphore_destroy(mach_task_self(), m_sema));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Threading::KernelSemaphore::Post()
|
||||||
|
{
|
||||||
|
MACH_CHECK(semaphore_signal(m_sema));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Threading::KernelSemaphore::Wait()
|
||||||
|
{
|
||||||
|
MACH_CHECK(semaphore_wait(m_sema));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Threading::KernelSemaphore::TryWait()
|
||||||
|
{
|
||||||
|
mach_timespec_t time = {};
|
||||||
|
kern_return_t res = semaphore_timedwait(m_sema, time);
|
||||||
|
if (res == KERN_OPERATION_TIMED_OUT)
|
||||||
|
return false;
|
||||||
|
MACH_CHECK(res);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Threading::ThreadHandle::ThreadHandle() = default;
|
Threading::ThreadHandle::ThreadHandle() = default;
|
||||||
|
|
||||||
Threading::ThreadHandle::ThreadHandle(const ThreadHandle& handle)
|
Threading::ThreadHandle::ThreadHandle(const ThreadHandle& handle)
|
||||||
|
@ -220,5 +277,3 @@ void Threading::SetNameOfCurrentThread(const char* name)
|
||||||
{
|
{
|
||||||
pthread_setname_np(name);
|
pthread_setname_np(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -123,12 +123,18 @@ namespace HostSys
|
||||||
#else
|
#else
|
||||||
void FlushInstructionCache(void* address, u32 size);
|
void FlushInstructionCache(void* address, u32 size);
|
||||||
#endif
|
#endif
|
||||||
}
|
} // namespace HostSys
|
||||||
|
|
||||||
namespace PageFaultHandler
|
namespace PageFaultHandler
|
||||||
{
|
{
|
||||||
bool HandlePageFault(uptr pc, uptr addr, bool is_write);
|
enum class HandlerResult
|
||||||
bool Install(Error* error);
|
{
|
||||||
|
ContinueExecution,
|
||||||
|
ExecuteNextHandler,
|
||||||
|
};
|
||||||
|
|
||||||
|
HandlerResult HandlePageFault(void* exception_pc, void* fault_address, bool is_write);
|
||||||
|
bool Install(Error* error = nullptr);
|
||||||
} // namespace PageFaultHandler
|
} // namespace PageFaultHandler
|
||||||
|
|
||||||
class SharedMemoryMappingArea
|
class SharedMemoryMappingArea
|
||||||
|
|
|
@ -1,229 +1,30 @@
|
||||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||||
// SPDX-License-Identifier: LGPL-3.0+
|
// SPDX-License-Identifier: LGPL-3.0+
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
|
||||||
#define _XOPEN_SOURCE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(_WIN32)
|
|
||||||
#include <cstdio>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
#include "fmt/core.h"
|
|
||||||
|
|
||||||
#include "common/Assertions.h"
|
#include "common/Assertions.h"
|
||||||
#include "common/BitUtils.h"
|
#include "common/BitUtils.h"
|
||||||
#include "common/Console.h"
|
#include "common/Console.h"
|
||||||
|
#include "common/CrashHandler.h"
|
||||||
#include "common/Error.h"
|
#include "common/Error.h"
|
||||||
#include "common/HostSys.h"
|
#include "common/HostSys.h"
|
||||||
|
|
||||||
// Apple uses the MAP_ANON define instead of MAP_ANONYMOUS, but they mean
|
#include <cstdio>
|
||||||
// the same thing.
|
#include <csignal>
|
||||||
#if defined(__APPLE__) && !defined(MAP_ANONYMOUS)
|
#include <cerrno>
|
||||||
#define MAP_ANONYMOUS MAP_ANON
|
#include <fcntl.h>
|
||||||
#endif
|
#include <mutex>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <ucontext.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "fmt/core.h"
|
||||||
|
|
||||||
// FreeBSD does not have MAP_FIXED_NOREPLACE, but does have MAP_EXCL.
|
// FreeBSD does not have MAP_FIXED_NOREPLACE, but does have MAP_EXCL.
|
||||||
// MAP_FIXED combined with MAP_EXCL behaves like MAP_FIXED_NOREPLACE.
|
// MAP_FIXED combined with MAP_EXCL behaves like MAP_FIXED_NOREPLACE.
|
||||||
#if defined(__FreeBSD__) && !defined(MAP_FIXED_NOREPLACE)
|
#if defined(__FreeBSD__) && !defined(MAP_FIXED_NOREPLACE)
|
||||||
#define MAP_FIXED_NOREPLACE MAP_FIXED | MAP_EXCL
|
#define MAP_FIXED_NOREPLACE (MAP_FIXED | MAP_EXCL)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cerrno>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#ifndef __APPLE__
|
|
||||||
#include <ucontext.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static std::recursive_mutex s_exception_handler_mutex;
|
|
||||||
static bool s_in_exception_handler = false;
|
|
||||||
static bool s_exception_handler_installed = false;
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#include <mach/task.h>
|
|
||||||
#include <mach/mach_init.h>
|
|
||||||
#include <mach/mach_port.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__APPLE__) || defined(__aarch64__)
|
|
||||||
static struct sigaction s_old_sigbus_action;
|
|
||||||
#endif
|
|
||||||
#if !defined(__APPLE__) || defined(__aarch64__)
|
|
||||||
static struct sigaction s_old_sigsegv_action;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __aarch64__
|
|
||||||
[[maybe_unused]] static bool IsStoreInstruction(uptr ptr)
|
|
||||||
{
|
|
||||||
u32 bits;
|
|
||||||
std::memcpy(&bits, reinterpret_cast<const void*>(ptr), sizeof(bits));
|
|
||||||
|
|
||||||
// Based on vixl's disassembler Instruction::IsStore().
|
|
||||||
// if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed)
|
|
||||||
if ((bits & 0x0a000000) != 0x08000000)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed)
|
|
||||||
if ((bits & 0x3a000000) == 0x28000000)
|
|
||||||
{
|
|
||||||
// return Mask(LoadStorePairLBit) == 0
|
|
||||||
return (bits & (1 << 22)) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (bits & 0xC4C00000)
|
|
||||||
{
|
|
||||||
case 0x00000000: // STRB_w
|
|
||||||
case 0x40000000: // STRH_w
|
|
||||||
case 0x80000000: // STR_w
|
|
||||||
case 0xC0000000: // STR_x
|
|
||||||
case 0x04000000: // STR_b
|
|
||||||
case 0x44000000: // STR_h
|
|
||||||
case 0x84000000: // STR_s
|
|
||||||
case 0xC4000000: // STR_d
|
|
||||||
case 0x04800000: // STR_q
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void CallExistingSignalHandler(int signal, siginfo_t* siginfo, void* ctx)
|
|
||||||
{
|
|
||||||
#if defined(__aarch64__)
|
|
||||||
const struct sigaction& sa = (signal == SIGBUS) ? s_old_sigbus_action : s_old_sigsegv_action;
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
const struct sigaction& sa = s_old_sigbus_action;
|
|
||||||
#else
|
|
||||||
const struct sigaction& sa = s_old_sigsegv_action;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (sa.sa_flags & SA_SIGINFO)
|
|
||||||
{
|
|
||||||
sa.sa_sigaction(signal, siginfo, ctx);
|
|
||||||
}
|
|
||||||
else if (sa.sa_handler == SIG_DFL)
|
|
||||||
{
|
|
||||||
// Re-raising the signal would just queue it, and since we'd restore the handler back to us,
|
|
||||||
// we'd end up right back here again. So just abort, because that's probably what it'd do anyway.
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
else if (sa.sa_handler != SIG_IGN)
|
|
||||||
{
|
|
||||||
sa.sa_handler(signal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Linux implementation of SIGSEGV handler. Bind it using sigaction().
|
|
||||||
static void SysPageFaultSignalFilter(int signal, siginfo_t* info, void* ctx)
|
|
||||||
{
|
|
||||||
// Executing the handler concurrently from multiple threads wouldn't go down well.
|
|
||||||
std::unique_lock lock(s_exception_handler_mutex);
|
|
||||||
|
|
||||||
// Prevent recursive exception filtering.
|
|
||||||
if (s_in_exception_handler)
|
|
||||||
{
|
|
||||||
lock.unlock();
|
|
||||||
CallExistingSignalHandler(signal, info, ctx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__linux__)
|
|
||||||
const uptr exception_address = reinterpret_cast<uptr>(info->si_addr);
|
|
||||||
#if defined(__x86_64__)
|
|
||||||
const uptr exception_pc = static_cast<uptr>(static_cast<ucontext_t*>(ctx)->uc_mcontext.gregs[REG_RIP]);
|
|
||||||
const bool is_write = (static_cast<ucontext_t*>(ctx)->uc_mcontext.gregs[REG_ERR] & 2) != 0;
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
const uptr exception_pc = static_cast<uptr>(static_cast<ucontext_t*>(ctx)->uc_mcontext.pc);
|
|
||||||
const bool is_write = IsStoreInstruction(exception_pc);
|
|
||||||
#endif
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
#if defined(__x86_64__)
|
|
||||||
const uptr exception_pc = static_cast<uptr>(static_cast<ucontext_t*>(ctx)->uc_mcontext->__ss.__rip);
|
|
||||||
const uptr exception_address = static_cast<uptr>(static_cast<ucontext_t*>(ctx)->uc_mcontext->__es.__faultvaddr);
|
|
||||||
const bool is_write = (static_cast<ucontext_t*>(ctx)->uc_mcontext->__es.__err & 2) != 0;
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
const uptr exception_address = static_cast<uptr>(static_cast<ucontext_t*>(ctx)->uc_mcontext->__es.__far);
|
|
||||||
const uptr exception_pc = static_cast<uptr>(static_cast<ucontext_t*>(ctx)->uc_mcontext->__ss.__pc);
|
|
||||||
const bool is_write = IsStoreInstruction(exception_pc);
|
|
||||||
#endif
|
|
||||||
#elif defined(__FreeBSD__)
|
|
||||||
#if defined(__x86_64__)
|
|
||||||
const uptr exception_address = static_cast<uptr>(static_cast<ucontext_t*>(ctx)->uc_mcontext.mc_addr);
|
|
||||||
const uptr exception_pc = static_cast<uptr>(static_cast<ucontext_t*>(ctx)->uc_mcontext.mc_rip);
|
|
||||||
const bool is_write = (static_cast<ucontext_t*>(ctx)->uc_mcontext.mc_err & 2) != 0;
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
const uptr exception_address = static_cast<uptr>(static_cast<ucontext_t*>(ctx)->uc_mcontext->__es.__far);
|
|
||||||
const uptr exception_pc = static_cast<uptr>(static_cast<ucontext_t*>(ctx)->uc_mcontext->__ss.__pc);
|
|
||||||
const bool is_write = IsStoreInstruction(exception_pc);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
s_in_exception_handler = true;
|
|
||||||
|
|
||||||
const bool handled = PageFaultHandler::HandlePageFault(exception_pc, exception_address, is_write);
|
|
||||||
|
|
||||||
s_in_exception_handler = false;
|
|
||||||
|
|
||||||
// Resumes execution right where we left off (re-executes instruction that caused the SIGSEGV).
|
|
||||||
if (handled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Call old signal handler, which will likely dump core.
|
|
||||||
lock.unlock();
|
|
||||||
CallExistingSignalHandler(signal, info, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PageFaultHandler::Install(Error* error)
|
|
||||||
{
|
|
||||||
std::unique_lock lock(s_exception_handler_mutex);
|
|
||||||
pxAssertRel(!s_exception_handler_installed, "Page fault handler has already been installed.");
|
|
||||||
|
|
||||||
struct sigaction sa;
|
|
||||||
|
|
||||||
sigemptyset(&sa.sa_mask);
|
|
||||||
sa.sa_flags = SA_SIGINFO;
|
|
||||||
sa.sa_sigaction = SysPageFaultSignalFilter;
|
|
||||||
#ifdef __linux__
|
|
||||||
// Don't block the signal from executing recursively, we want to fire the original handler.
|
|
||||||
sa.sa_flags |= SA_NODEFER;
|
|
||||||
#endif
|
|
||||||
#if defined(__APPLE__) || defined(__aarch64__)
|
|
||||||
// MacOS uses SIGBUS for memory permission violations, as well as SIGSEGV on ARM64.
|
|
||||||
if (sigaction(SIGBUS, &sa, &s_old_sigbus_action) != 0)
|
|
||||||
{
|
|
||||||
Error::SetErrno(error, "sigaction() for SIGSEGV failed: ", errno);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if !defined(__APPLE__) || defined(__aarch64__)
|
|
||||||
if (sigaction(SIGSEGV, &sa, &s_old_sigsegv_action) != 0)
|
|
||||||
{
|
|
||||||
Error::SetErrno(error, "sigaction() for SIGBUS failed: ", errno);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if defined(__APPLE__) && defined(__aarch64__)
|
|
||||||
// Stops LLDB getting in a EXC_BAD_ACCESS loop when passing page faults to PCSX2.
|
|
||||||
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, MACH_PORT_NULL, EXCEPTION_DEFAULT, 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
s_exception_handler_installed = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef __APPLE__
|
|
||||||
|
|
||||||
static __ri uint LinuxProt(const PageProtectionMode& mode)
|
static __ri uint LinuxProt(const PageProtectionMode& mode)
|
||||||
{
|
{
|
||||||
u32 lnxmode = 0;
|
u32 lnxmode = 0;
|
||||||
|
@ -251,11 +52,6 @@ void* HostSys::Mmap(void* base, size_t size, const PageProtectionMode& mode)
|
||||||
if (base)
|
if (base)
|
||||||
flags |= MAP_FIXED_NOREPLACE;
|
flags |= MAP_FIXED_NOREPLACE;
|
||||||
|
|
||||||
#if defined(__APPLE__) && defined(_M_ARM64)
|
|
||||||
if (mode.CanExecute())
|
|
||||||
flags |= MAP_JIT;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void* res = mmap(base, size, prot, flags, -1, 0);
|
void* res = mmap(base, size, prot, flags, -1, 0);
|
||||||
if (res == MAP_FAILED)
|
if (res == MAP_FAILED)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -338,15 +134,6 @@ void HostSys::UnmapSharedMemory(void* baseaddr, size_t size)
|
||||||
pxFailRel("Failed to unmap shared memory");
|
pxFailRel("Failed to unmap shared memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _M_ARM64
|
|
||||||
|
|
||||||
void HostSys::FlushInstructionCache(void* address, u32 size)
|
|
||||||
{
|
|
||||||
__builtin___clear_cache(reinterpret_cast<char*>(address), reinterpret_cast<char*>(address) + size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SharedMemoryMappingArea::SharedMemoryMappingArea(u8* base_ptr, size_t size, size_t num_pages)
|
SharedMemoryMappingArea::SharedMemoryMappingArea(u8* base_ptr, size_t size, size_t num_pages)
|
||||||
: m_base_ptr(base_ptr)
|
: m_base_ptr(base_ptr)
|
||||||
, m_size(size)
|
, m_size(size)
|
||||||
|
@ -400,6 +187,137 @@ bool SharedMemoryMappingArea::Unmap(void* map_base, size_t map_size)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __APPLE__
|
namespace PageFaultHandler
|
||||||
|
{
|
||||||
|
static std::recursive_mutex s_exception_handler_mutex;
|
||||||
|
static bool s_in_exception_handler = false;
|
||||||
|
static bool s_installed = false;
|
||||||
|
} // namespace PageFaultHandler
|
||||||
|
|
||||||
|
#ifdef _M_ARM64
|
||||||
|
|
||||||
|
void HostSys::FlushInstructionCache(void* address, u32 size)
|
||||||
|
{
|
||||||
|
__builtin___clear_cache(reinterpret_cast<char*>(address), reinterpret_cast<char*>(address) + size);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[maybe_unused]] static bool IsStoreInstruction(const void* ptr)
|
||||||
|
{
|
||||||
|
u32 bits;
|
||||||
|
std::memcpy(&bits, ptr, sizeof(bits));
|
||||||
|
|
||||||
|
// Based on vixl's disassembler Instruction::IsStore().
|
||||||
|
// if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed)
|
||||||
|
if ((bits & 0x0a000000) != 0x08000000)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed)
|
||||||
|
if ((bits & 0x3a000000) == 0x28000000)
|
||||||
|
{
|
||||||
|
// return Mask(LoadStorePairLBit) == 0
|
||||||
|
return (bits & (1 << 22)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (bits & 0xC4C00000)
|
||||||
|
{
|
||||||
|
case 0x00000000: // STRB_w
|
||||||
|
case 0x40000000: // STRH_w
|
||||||
|
case 0x80000000: // STR_w
|
||||||
|
case 0xC0000000: // STR_x
|
||||||
|
case 0x04000000: // STR_b
|
||||||
|
case 0x44000000: // STR_h
|
||||||
|
case 0x84000000: // STR_s
|
||||||
|
case 0xC4000000: // STR_d
|
||||||
|
case 0x04800000: // STR_q
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _M_ARM64
|
||||||
|
|
||||||
|
namespace PageFaultHandler
|
||||||
|
{
|
||||||
|
static void SignalHandler(int sig, siginfo_t* info, void* ctx);
|
||||||
|
} // namespace PageFaultHandler
|
||||||
|
|
||||||
|
void PageFaultHandler::SignalHandler(int sig, siginfo_t* info, void* ctx)
|
||||||
|
{
|
||||||
|
#if defined(__linux__)
|
||||||
|
void* const exception_address = reinterpret_cast<void*>(info->si_addr);
|
||||||
|
|
||||||
|
#if defined(_M_X86)
|
||||||
|
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext.gregs[REG_RIP]);
|
||||||
|
const bool is_write = (static_cast<ucontext_t*>(ctx)->uc_mcontext.gregs[REG_ERR] & 2) != 0;
|
||||||
|
#elif defined(_M_ARM64)
|
||||||
|
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext.pc);
|
||||||
|
const bool is_write = IsStoreInstruction(exception_pc);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
|
||||||
|
#if defined(_M_X86)
|
||||||
|
void* const exception_address = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext.mc_addr);
|
||||||
|
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext.mc_rip);
|
||||||
|
const bool is_write = (static_cast<ucontext_t*>(ctx)->uc_mcontext.mc_err & 2) != 0;
|
||||||
|
#elif defined(_M_ARM64)
|
||||||
|
void* const exception_address = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext->__es.__far);
|
||||||
|
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext->__ss.__pc);
|
||||||
|
const bool is_write = IsStoreInstruction(exception_pc);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Executing the handler concurrently from multiple threads wouldn't go down well.
|
||||||
|
s_exception_handler_mutex.lock();
|
||||||
|
|
||||||
|
// Prevent recursive exception filtering.
|
||||||
|
HandlerResult result = HandlerResult::ExecuteNextHandler;
|
||||||
|
if (!s_in_exception_handler)
|
||||||
|
{
|
||||||
|
s_in_exception_handler = true;
|
||||||
|
result = HandlePageFault(exception_pc, exception_address, is_write);
|
||||||
|
s_in_exception_handler = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_exception_handler_mutex.unlock();
|
||||||
|
|
||||||
|
// Resumes execution right where we left off (re-executes instruction that caused the SIGSEGV).
|
||||||
|
if (result == HandlerResult::ContinueExecution)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// We couldn't handle it. Pass it off to the crash dumper.
|
||||||
|
CrashHandler::CrashSignalHandler(sig, info, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PageFaultHandler::Install(Error* error)
|
||||||
|
{
|
||||||
|
std::unique_lock lock(s_exception_handler_mutex);
|
||||||
|
pxAssertRel(!s_installed, "Page fault handler has already been installed.");
|
||||||
|
|
||||||
|
struct sigaction sa;
|
||||||
|
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_flags = SA_SIGINFO | SA_NODEFER;
|
||||||
|
sa.sa_sigaction = SignalHandler;
|
||||||
|
|
||||||
|
if (sigaction(SIGSEGV, &sa, nullptr) != 0)
|
||||||
|
{
|
||||||
|
Error::SetErrno(error, "sigaction() for SIGSEGV failed: ", errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _M_ARM64
|
||||||
|
// We can get SIGBUS on ARM64.
|
||||||
|
if (sigaction(SIGBUS, &sa, nullptr) != 0)
|
||||||
|
{
|
||||||
|
Error::SetErrno(error, "sigaction() for SIGBUS failed: ", errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
s_installed = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||||
// SPDX-License-Identifier: LGPL-3.0+
|
// SPDX-License-Identifier: LGPL-3.0+
|
||||||
|
|
||||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
|
||||||
|
|
||||||
#include "common/Pcsx2Types.h"
|
#include "common/Pcsx2Types.h"
|
||||||
#include "common/Console.h"
|
#include "common/Console.h"
|
||||||
#include "common/HostSys.h"
|
#include "common/HostSys.h"
|
||||||
|
@ -215,5 +213,3 @@ void Threading::SleepUntil(u64 ticks)
|
||||||
ts.tv_nsec = static_cast<long>(ticks % 1000000000ULL);
|
ts.tv_nsec = static_cast<long>(ticks % 1000000000ULL);
|
||||||
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, nullptr);
|
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||||
// SPDX-License-Identifier: LGPL-3.0+
|
// SPDX-License-Identifier: LGPL-3.0+
|
||||||
|
|
||||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
|
||||||
#ifndef _GNU_SOURCE
|
#ifndef _GNU_SOURCE
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "common/Threading.h"
|
||||||
|
#include "common/Assertions.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
@ -20,20 +22,10 @@
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#define gettid() syscall(SYS_gettid)
|
#define gettid() syscall(SYS_gettid)
|
||||||
#endif
|
#endif
|
||||||
|
#else
|
||||||
#elif defined(__unix__)
|
|
||||||
#include <pthread_np.h>
|
#include <pthread_np.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "common/Threading.h"
|
|
||||||
#include "common/Assertions.h"
|
|
||||||
|
|
||||||
#if !defined(__unix__)
|
|
||||||
|
|
||||||
#pragma message("LnxThreads.cpp should only be compiled by projects or makefiles targeted at Linux/BSD distros.")
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
// Note: assuming multicore is safer because it forces the interlocked routines to use
|
// Note: assuming multicore is safer because it forces the interlocked routines to use
|
||||||
// the LOCK prefix. The prefix works on single core CPUs fine (but is slow), but not
|
// the LOCK prefix. The prefix works on single core CPUs fine (but is slow), but not
|
||||||
// having the LOCK prefix is very bad indeed.
|
// having the LOCK prefix is very bad indeed.
|
||||||
|
@ -354,6 +346,3 @@ void Threading::SetNameOfCurrentThread(const char* name)
|
||||||
pthread_set_name_np(pthread_self(), name);
|
pthread_set_name_np(pthread_self(), name);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -137,7 +137,7 @@ void Threading::WorkSema::Reset()
|
||||||
m_state = STATE_RUNNING_0;
|
m_state = STATE_RUNNING_0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(__APPLE__) // macOS implementations are in DarwinSemaphore
|
#if !defined(__APPLE__) // macOS implementations are in DarwinThreads
|
||||||
|
|
||||||
Threading::KernelSemaphore::KernelSemaphore()
|
Threading::KernelSemaphore::KernelSemaphore()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||||
// SPDX-License-Identifier: LGPL-3.0+
|
// SPDX-License-Identifier: LGPL-3.0+
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
|
|
||||||
#include "common/HostSys.h"
|
#include "common/HostSys.h"
|
||||||
#include "common/AlignedMalloc.h"
|
#include "common/AlignedMalloc.h"
|
||||||
#include "common/Assertions.h"
|
#include "common/Assertions.h"
|
||||||
|
@ -17,57 +15,6 @@
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
static std::recursive_mutex s_exception_handler_mutex;
|
|
||||||
static bool s_in_exception_handler = false;
|
|
||||||
static bool s_exception_handler_installed = false;
|
|
||||||
|
|
||||||
long __stdcall SysPageFaultExceptionFilter(EXCEPTION_POINTERS* eps)
|
|
||||||
{
|
|
||||||
// Executing the handler concurrently from multiple threads wouldn't go down well.
|
|
||||||
std::unique_lock lock(s_exception_handler_mutex);
|
|
||||||
|
|
||||||
// Prevent recursive exception filtering.
|
|
||||||
if (s_in_exception_handler)
|
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
|
||||||
|
|
||||||
// Only interested in page faults.
|
|
||||||
if (eps->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
|
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
|
||||||
|
|
||||||
#if defined(_M_AMD64)
|
|
||||||
const uptr exception_pc = static_cast<uptr>(eps->ContextRecord->Rip);
|
|
||||||
#elif defined(_M_ARM64)
|
|
||||||
const uptr exception_pc = static_cast<uptr>(eps->ContextRecord->Pc);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const uptr exception_addr = static_cast<uptr>(eps->ExceptionRecord->ExceptionInformation[1]);
|
|
||||||
const bool is_write = (eps->ExceptionRecord->ExceptionInformation[0] == 1);
|
|
||||||
|
|
||||||
s_in_exception_handler = true;
|
|
||||||
|
|
||||||
const bool handled = PageFaultHandler::HandlePageFault(exception_pc, exception_addr, is_write);
|
|
||||||
|
|
||||||
s_in_exception_handler = false;
|
|
||||||
|
|
||||||
return handled ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PageFaultHandler::Install(Error* error)
|
|
||||||
{
|
|
||||||
std::unique_lock lock(s_exception_handler_mutex);
|
|
||||||
pxAssertRel(!s_exception_handler_installed, "Page fault handler has already been installed.");
|
|
||||||
|
|
||||||
PVOID handle = AddVectoredExceptionHandler(1, SysPageFaultExceptionFilter);
|
|
||||||
if (!handle)
|
|
||||||
{
|
|
||||||
Error::SetWin32(error, "AddVectoredExceptionHandler() failed: ", GetLastError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
s_exception_handler_installed = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DWORD ConvertToWinApi(const PageProtectionMode& mode)
|
static DWORD ConvertToWinApi(const PageProtectionMode& mode)
|
||||||
{
|
{
|
||||||
DWORD winmode = PAGE_NOACCESS;
|
DWORD winmode = PAGE_NOACCESS;
|
||||||
|
@ -309,7 +256,7 @@ bool SharedMemoryMappingArea::Unmap(void* map_base, size_t map_size)
|
||||||
|
|
||||||
// combine placeholders before and the range we're unmapping, i.e. to the left
|
// combine placeholders before and the range we're unmapping, i.e. to the left
|
||||||
if (!VirtualFreeEx(GetCurrentProcess(), OffsetPointer(left_it->first),
|
if (!VirtualFreeEx(GetCurrentProcess(), OffsetPointer(left_it->first),
|
||||||
left_it->second - left_it->first, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS))
|
left_it->second - left_it->first, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS))
|
||||||
{
|
{
|
||||||
pxFail("Failed to coalesce placeholders left for unmap");
|
pxFail("Failed to coalesce placeholders left for unmap");
|
||||||
}
|
}
|
||||||
|
@ -341,4 +288,60 @@ bool SharedMemoryMappingArea::Unmap(void* map_base, size_t map_size)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace PageFaultHandler
|
||||||
|
{
|
||||||
|
static LONG ExceptionHandler(PEXCEPTION_POINTERS exi);
|
||||||
|
|
||||||
|
static std::recursive_mutex s_exception_handler_mutex;
|
||||||
|
static bool s_in_exception_handler = false;
|
||||||
|
static bool s_installed = false;
|
||||||
|
} // namespace PageFaultHandler
|
||||||
|
|
||||||
|
LONG PageFaultHandler::ExceptionHandler(PEXCEPTION_POINTERS exi)
|
||||||
|
{
|
||||||
|
// Executing the handler concurrently from multiple threads wouldn't go down well.
|
||||||
|
std::unique_lock lock(s_exception_handler_mutex);
|
||||||
|
|
||||||
|
// Prevent recursive exception filtering.
|
||||||
|
if (s_in_exception_handler)
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
|
||||||
|
// Only interested in page faults.
|
||||||
|
if (exi->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
|
||||||
|
#if defined(_M_X86)
|
||||||
|
void* const exception_pc = reinterpret_cast<void*>(exi->ContextRecord->Rip);
|
||||||
|
#elif defined(_M_ARM64)
|
||||||
|
void* const exception_pc = reinterpret_cast<void*>(exi->ContextRecord->Pc);
|
||||||
|
#else
|
||||||
|
void* const exception_pc = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void* const exception_address = reinterpret_cast<void*>(exi->ExceptionRecord->ExceptionInformation[1]);
|
||||||
|
const bool is_write = exi->ExceptionRecord->ExceptionInformation[0] == 1;
|
||||||
|
|
||||||
|
s_in_exception_handler = true;
|
||||||
|
|
||||||
|
const HandlerResult handled = HandlePageFault(exception_pc, exception_address, is_write);
|
||||||
|
|
||||||
|
s_in_exception_handler = false;
|
||||||
|
|
||||||
|
return (handled == HandlerResult::ContinueExecution) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PageFaultHandler::Install(Error* error)
|
||||||
|
{
|
||||||
|
std::unique_lock lock(s_exception_handler_mutex);
|
||||||
|
pxAssertRel(!s_installed, "Page fault handler has already been installed.");
|
||||||
|
|
||||||
|
PVOID handle = AddVectoredExceptionHandler(1, ExceptionHandler);
|
||||||
|
if (!handle)
|
||||||
|
{
|
||||||
|
Error::SetWin32(error, "AddVectoredExceptionHandler() failed: ", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_installed = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||||
// SPDX-License-Identifier: LGPL-3.0+
|
// SPDX-License-Identifier: LGPL-3.0+
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
|
|
||||||
#include "common/FileSystem.h"
|
#include "common/FileSystem.h"
|
||||||
#include "common/HostSys.h"
|
#include "common/HostSys.h"
|
||||||
#include "common/RedtapeWindows.h"
|
#include "common/RedtapeWindows.h"
|
||||||
|
@ -126,4 +124,3 @@ void Threading::SleepUntil(u64 ticks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||||
// SPDX-License-Identifier: LGPL-3.0+
|
// SPDX-License-Identifier: LGPL-3.0+
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
|
|
||||||
#include "common/Threading.h"
|
#include "common/Threading.h"
|
||||||
#include "common/Assertions.h"
|
#include "common/Assertions.h"
|
||||||
#include "common/RedtapeWindows.h"
|
#include "common/RedtapeWindows.h"
|
||||||
|
@ -265,5 +263,3 @@ void Threading::SetNameOfCurrentThread(const char* name)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -74,9 +74,6 @@
|
||||||
<ClCompile Include="PrecompiledHeader.cpp">
|
<ClCompile Include="PrecompiledHeader.cpp">
|
||||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="Linux\LnxHostSys.cpp" />
|
|
||||||
<ClCompile Include="Linux\LnxMisc.cpp" />
|
|
||||||
<ClCompile Include="Linux\LnxThreads.cpp" />
|
|
||||||
<ClCompile Include="Windows\WinHostSys.cpp" />
|
<ClCompile Include="Windows\WinHostSys.cpp" />
|
||||||
<ClCompile Include="Windows\WinMisc.cpp" />
|
<ClCompile Include="Windows\WinMisc.cpp" />
|
||||||
<ClCompile Include="Windows\WinThreads.cpp" />
|
<ClCompile Include="Windows\WinThreads.cpp" />
|
||||||
|
|
|
@ -28,15 +28,6 @@
|
||||||
<ClCompile Include="emitter\avx.cpp">
|
<ClCompile Include="emitter\avx.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="Linux\LnxHostSys.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="Linux\LnxMisc.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="Linux\LnxThreads.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="emitter\movs.cpp">
|
<ClCompile Include="emitter\movs.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|
|
@ -1454,12 +1454,12 @@ static __fi void mmap_ClearCpuBlock(uint offset)
|
||||||
Cpu->Clear(m_PageProtectInfo[rampage].ReverseRamMap, __pagesize);
|
Cpu->Clear(m_PageProtectInfo[rampage].ReverseRamMap, __pagesize);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PageFaultHandler::HandlePageFault(uptr pc, uptr addr, bool is_write)
|
PageFaultHandler::HandlerResult PageFaultHandler::HandlePageFault(void* exception_pc, void* fault_address, bool is_write)
|
||||||
{
|
{
|
||||||
pxAssert(eeMem);
|
pxAssert(eeMem);
|
||||||
|
|
||||||
u32 vaddr;
|
u32 vaddr;
|
||||||
if (CHECK_FASTMEM && vtlb_GetGuestAddress(addr, &vaddr))
|
if (CHECK_FASTMEM && vtlb_GetGuestAddress(reinterpret_cast<uptr>(fault_address), &vaddr))
|
||||||
{
|
{
|
||||||
// this was inside the fastmem area. check if it's a code page
|
// this was inside the fastmem area. check if it's a code page
|
||||||
// fprintf(stderr, "Fault on fastmem %p vaddr %08X\n", info.addr, vaddr);
|
// fprintf(stderr, "Fault on fastmem %p vaddr %08X\n", info.addr, vaddr);
|
||||||
|
@ -1470,23 +1470,26 @@ bool PageFaultHandler::HandlePageFault(uptr pc, uptr addr, bool is_write)
|
||||||
{
|
{
|
||||||
// fprintf(stderr, "Not backpatching code write at %08X\n", vaddr);
|
// fprintf(stderr, "Not backpatching code write at %08X\n", vaddr);
|
||||||
mmap_ClearCpuBlock(offset);
|
mmap_ClearCpuBlock(offset);
|
||||||
return true;
|
return HandlerResult::ContinueExecution;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// fprintf(stderr, "Trying backpatching vaddr %08X\n", vaddr);
|
// fprintf(stderr, "Trying backpatching vaddr %08X\n", vaddr);
|
||||||
return vtlb_BackpatchLoadStore(pc, addr);
|
return vtlb_BackpatchLoadStore(reinterpret_cast<uptr>(exception_pc),
|
||||||
|
reinterpret_cast<uptr>(fault_address)) ?
|
||||||
|
HandlerResult::ContinueExecution :
|
||||||
|
HandlerResult::ExecuteNextHandler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// get bad virtual address
|
// get bad virtual address
|
||||||
uptr offset = addr - (uptr)eeMem->Main;
|
uptr offset = reinterpret_cast<uptr>(fault_address) - reinterpret_cast<uptr>(eeMem->Main);
|
||||||
if (offset >= Ps2MemSize::ExposedRam)
|
if (offset >= Ps2MemSize::ExposedRam)
|
||||||
return false;
|
return HandlerResult::ExecuteNextHandler;
|
||||||
|
|
||||||
mmap_ClearCpuBlock(offset);
|
mmap_ClearCpuBlock(offset);
|
||||||
return true;
|
return HandlerResult::ContinueExecution;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue