diff --git a/src/common/crash_handler.cpp b/src/common/crash_handler.cpp index 772219155..79caafdf6 100644 --- a/src/common/crash_handler.cpp +++ b/src/common/crash_handler.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "crash_handler.h" +#include "dynamic_library.h" #include "file_system.h" #include "string_util.h" #include @@ -74,7 +75,7 @@ static bool WriteMinidump(HMODULE hDbgHelp, HANDLE hFile, HANDLE hProcess, DWORD } static std::wstring s_write_directory; -static HMODULE s_dbghelp_module = nullptr; +static DynamicLibrary s_dbghelp_module; static PVOID s_veh_handle = nullptr; static bool s_in_crash_handler = false; @@ -115,8 +116,8 @@ static void WriteMinidumpAndCallstack(PEXCEPTION_POINTERS exi) MiniDumpWithThreadInfo | MiniDumpWithIndirectlyReferencedMemory); const HANDLE hMinidumpFile = CreateFileW(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr); if (hMinidumpFile == INVALID_HANDLE_VALUE || - !WriteMinidump(s_dbghelp_module, hMinidumpFile, GetCurrentProcess(), GetCurrentProcessId(), GetCurrentThreadId(), - exi, minidump_type)) + !WriteMinidump(static_cast(s_dbghelp_module.GetHandle()), hMinidumpFile, GetCurrentProcess(), + GetCurrentProcessId(), GetCurrentThreadId(), exi, minidump_type)) { static const char error_message[] = "Failed to write minidump file.\n"; if (hFile != INVALID_HANDLE_VALUE) @@ -170,7 +171,9 @@ bool CrashHandler::Install() { // load dbghelp at install/startup, that way we're not LoadLibrary()'ing after a crash // .. because that probably wouldn't go down well. - s_dbghelp_module = StackWalker::LoadDbgHelpLibrary(); + HMODULE mod = StackWalker::LoadDbgHelpLibrary(); + if (mod) + s_dbghelp_module.Adopt(mod); s_veh_handle = AddVectoredExceptionHandler(0, ExceptionHandler); return (s_veh_handle != nullptr); @@ -189,21 +192,6 @@ void CrashHandler::WriteDumpForCaller() WriteMinidumpAndCallstack(nullptr); } -void CrashHandler::Uninstall() -{ - if (s_veh_handle) - { - RemoveVectoredExceptionHandler(s_veh_handle); - s_veh_handle = nullptr; - } - - if (s_dbghelp_module) - { - FreeLibrary(s_dbghelp_module); - s_dbghelp_module = nullptr; - } -} - #elif defined(ENABLE_LIBBACKTRACE) #include @@ -244,8 +232,8 @@ const char* CrashHandler::GetSignalName(int signal_no) { switch (signal_no) { - // Don't need to list all of them, there's only a couple we register. - // clang-format off + // Don't need to list all of them, there's only a couple we register. + // clang-format off case SIGSEGV: return "SIGSEGV"; case SIGBUS: return "SIGBUS"; default: return "UNKNOWN"; @@ -392,11 +380,6 @@ void CrashHandler::WriteDumpForCaller() { } -void CrashHandler::Uninstall() -{ - // We can't really unchain the signal handlers... so, YOLO. -} - #else bool CrashHandler::Install() @@ -412,8 +395,4 @@ void CrashHandler::WriteDumpForCaller() { } -void CrashHandler::Uninstall() -{ -} - #endif diff --git a/src/common/crash_handler.h b/src/common/crash_handler.h index ac72beef9..cc1b89dd0 100644 --- a/src/common/crash_handler.h +++ b/src/common/crash_handler.h @@ -8,5 +8,4 @@ namespace CrashHandler { bool Install(); void SetWriteDirectory(std::string_view dump_directory); void WriteDumpForCaller(); -void Uninstall(); } // namespace CrashHandler diff --git a/src/common/dynamic_library.cpp b/src/common/dynamic_library.cpp index 493faa30c..9f66c5104 100644 --- a/src/common/dynamic_library.cpp +++ b/src/common/dynamic_library.cpp @@ -101,6 +101,15 @@ bool DynamicLibrary::Open(const char* filename, Error* error) #endif } +void DynamicLibrary::Adopt(void* handle) +{ + AssertMsg(handle, "Handle is valid"); + + Close(); + + m_handle = handle; +} + void DynamicLibrary::Close() { if (!IsOpen()) diff --git a/src/common/dynamic_library.h b/src/common/dynamic_library.h index a9d8fb764..d34942c67 100644 --- a/src/common/dynamic_library.h +++ b/src/common/dynamic_library.h @@ -45,6 +45,9 @@ public: /// Returns true if the library was loaded and can be used. bool Open(const char* filename, Error* error); + /// Adopts, or takes ownership of an existing opened library. + void Adopt(void* handle); + /// Unloads the library, any function pointers from this library are no longer valid. void Close(); @@ -61,6 +64,9 @@ public: return *ptr != nullptr; } + /// Returns the opaque OS-specific handle. + void* GetHandle() const { return m_handle; } + /// Move assignment, transfer ownership. DynamicLibrary& operator=(DynamicLibrary&& move); diff --git a/src/common/error.cpp b/src/common/error.cpp index 32c8e62d3..53da9b3d1 100644 --- a/src/common/error.cpp +++ b/src/common/error.cpp @@ -138,8 +138,8 @@ void Error::SetHResult(std::string_view prefix, long err) static_cast(std::size(buf)), nullptr); if (r > 0) { - m_description = - fmt::format("{}HRESULT {:08X}: {}", prefix, err, StringUtil::WideStringToUTF8String(std::wstring_view(buf, r))); + m_description = fmt::format("{}HRESULT {:08X}: {}", prefix, static_cast(err), + StringUtil::WideStringToUTF8String(std::wstring_view(buf, r))); } else { diff --git a/src/core/bus.cpp b/src/core/bus.cpp index 92051a102..a0495f6d4 100644 --- a/src/core/bus.cpp +++ b/src/core/bus.cpp @@ -174,21 +174,18 @@ static constexpr size_t TOTAL_SIZE = LUT_OFFSET + LUT_SIZE; #define FIXUP_WORD_WRITE_VALUE(size, offset, value) \ ((size == MemoryAccessSize::Word) ? (value) : ((value) << (((offset) & 3u) * 8))) -bool Bus::AllocateMemory() +bool Bus::AllocateMemory(Error* error) { - Error error; s_shmem_handle = - MemMap::CreateSharedMemory(MemMap::GetFileMappingName("duckstation").c_str(), MemoryMap::TOTAL_SIZE, &error); + MemMap::CreateSharedMemory(MemMap::GetFileMappingName("duckstation").c_str(), MemoryMap::TOTAL_SIZE, error); if (!s_shmem_handle) { #ifndef __linux__ - error.AddSuffix("\nYou may need to close some programs to free up additional memory."); + Error::AddSuffix(error, "\nYou may need to close some programs to free up additional memory."); #else - error.AddSuffix( - "\nYou may need to close some programs to free up additional memory, or increase the size of /dev/shm."); + Error::AddSuffix( + error, "\nYou may need to close some programs to free up additional memory, or increase the size of /dev/shm."); #endif - - Host::ReportFatalError("Memory Allocation Failed", error.GetDescription()); return false; } @@ -198,7 +195,7 @@ bool Bus::AllocateMemory() MemoryMap::RAM_SIZE, PageProtect::ReadWrite)); if (!g_ram || !g_unprotected_ram) { - Host::ReportFatalError("Memory Allocation Failed", "Failed to map memory for RAM"); + Error::SetStringView(error, "Failed to map memory for RAM"); ReleaseMemory(); return false; } @@ -209,7 +206,7 @@ bool Bus::AllocateMemory() MemoryMap::BIOS_SIZE, PageProtect::ReadWrite)); if (!g_bios) { - Host::ReportFatalError("Memory Allocation Failed", "Failed to map memory for BIOS"); + Error::SetStringView(error, "Failed to map memory for BIOS"); ReleaseMemory(); return false; } @@ -220,7 +217,7 @@ bool Bus::AllocateMemory() MemoryMap::LUT_SIZE, PageProtect::ReadWrite)); if (!g_memory_handlers) { - Host::ReportFatalError("Memory Allocation Failed", "Failed to map memory for LUTs"); + Error::SetStringView(error, "Failed to map memory for LUTs"); ReleaseMemory(); return false; } @@ -232,8 +229,7 @@ bool Bus::AllocateMemory() #ifdef ENABLE_MMAP_FASTMEM if (!s_fastmem_arena.Create(FASTMEM_ARENA_SIZE)) { - // TODO: maybe make this non-fatal? - Host::ReportFatalError("Memory Allocation Failed", "Failed to create fastmem arena"); + Error::SetStringView(error, "Failed to create fastmem arena"); ReleaseMemory(); return false; } diff --git a/src/core/bus.h b/src/core/bus.h index 64ea4d212..319852f44 100644 --- a/src/core/bus.h +++ b/src/core/bus.h @@ -14,6 +14,8 @@ #include #include +class Error; + class StateWrapper; namespace Bus { @@ -109,7 +111,7 @@ enum : u32 static constexpr size_t FASTMEM_ARENA_SIZE = UINT64_C(0x100000000); #endif -bool AllocateMemory(); +bool AllocateMemory(Error* error); void ReleaseMemory(); bool Initialize(); diff --git a/src/core/controller.cpp b/src/core/controller.cpp index 191eef869..7a6e5f6c9 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -172,12 +172,6 @@ std::optional Controller::GetBindIndex(ControllerType type, std::string_vie return std::nullopt; } -Controller::VibrationCapabilities Controller::GetControllerVibrationCapabilities(std::string_view type) -{ - const ControllerInfo* info = GetControllerInfo(type); - return info ? info->vibration_caps : VibrationCapabilities::NoVibration; -} - std::tuple Controller::ConvertPadToPortAndSlot(u32 index) { if (index > 4) // [5,6,7] diff --git a/src/core/controller.h b/src/core/controller.h index 7ba686b7a..607b3b05b 100644 --- a/src/core/controller.h +++ b/src/core/controller.h @@ -104,9 +104,6 @@ public: /// Gets the integer code for an axis in the specified controller type. static std::optional GetBindIndex(ControllerType type, std::string_view bind_name); - /// Returns the vibration configuration for the specified controller type. - static VibrationCapabilities GetControllerVibrationCapabilities(std::string_view type); - /// Returns general information for the specified controller type. static const ControllerInfo* GetControllerInfo(ControllerType type); static const ControllerInfo* GetControllerInfo(std::string_view name); diff --git a/src/core/cpu_code_cache.cpp b/src/core/cpu_code_cache.cpp index dd8e0eb35..6bdeb4d74 100644 --- a/src/core/cpu_code_cache.cpp +++ b/src/core/cpu_code_cache.cpp @@ -72,8 +72,6 @@ static void SetRegAccess(InstructionInfo* inst, Reg reg, bool write); static void AddBlockToPageList(Block* block); static void RemoveBlockFromPageList(Block* block); -static Common::PageFaultHandler::HandlerResult ExceptionHandler(void* exception_pc, void* fault_address, bool is_write); - static Block* CreateCachedInterpreterBlock(u32 pc); [[noreturn]] static void ExecuteCachedInterpreter(); template @@ -99,8 +97,7 @@ static void UnlinkBlockExits(Block* block); static void ClearASMFunctions(); static void CompileASMFunctions(); static bool CompileBlock(Block* block); -static Common::PageFaultHandler::HandlerResult HandleFastmemException(void* exception_pc, void* fault_address, - bool is_write); +static PageFaultHandler::HandlerResult HandleFastmemException(void* exception_pc, void* fault_address, bool is_write); static void BackpatchLoadStore(void* host_pc, const LoadstoreBackpatchInfo& info); static void RemoveBackpatchInfoForRange(const void* host_code, u32 size); @@ -165,7 +162,7 @@ bool CPU::CodeCache::IsUsingFastmem() return IsUsingAnyRecompiler() && g_settings.cpu_fastmem_mode != CPUFastmemMode::Disabled; } -bool CPU::CodeCache::ProcessStartup() +bool CPU::CodeCache::ProcessStartup(Error* error) { AllocateLUTs(); @@ -178,24 +175,19 @@ bool CPU::CodeCache::ProcessStartup() #endif if (!has_buffer && !s_code_buffer.Allocate(RECOMPILER_CODE_CACHE_SIZE, RECOMPILER_FAR_CODE_CACHE_SIZE)) { - Host::ReportFatalError("Error", "Failed to initialize code space"); + Error::SetStringView(error, "Failed to initialize code space"); return false; } #endif - if (!Common::PageFaultHandler::InstallHandler(ExceptionHandler)) - { - Host::ReportFatalError("Error", "Failed to install page fault handler"); + if (!PageFaultHandler::Install(error)) return false; - } return true; } void CPU::CodeCache::ProcessShutdown() { - Common::PageFaultHandler::RemoveHandler(ExceptionHandler); - #ifdef ENABLE_RECOMPILER_SUPPORT s_code_buffer.Destroy(); #endif @@ -747,8 +739,8 @@ void CPU::CodeCache::ClearBlocks() std::memset(s_lut_block_pointers.get(), 0, sizeof(Block*) * GetLUTSlotCount(false)); } -Common::PageFaultHandler::HandlerResult CPU::CodeCache::ExceptionHandler(void* exception_pc, void* fault_address, - bool is_write) +PageFaultHandler::HandlerResult PageFaultHandler::HandlePageFault(void* exception_pc, void* fault_address, + bool is_write) { if (static_cast(fault_address) >= Bus::g_ram && static_cast(fault_address) < (Bus::g_ram + Bus::RAM_8MB_SIZE)) @@ -759,14 +751,14 @@ Common::PageFaultHandler::HandlerResult CPU::CodeCache::ExceptionHandler(void* e const u32 page_index = Bus::GetRAMCodePageIndex(guest_address); Log_DevFmt("Page fault on protected RAM @ 0x{:08X} (page #{}), invalidating code cache.", guest_address, page_index); - InvalidateBlocksWithPageIndex(page_index); - return Common::PageFaultHandler::HandlerResult::ContinueExecution; + CPU::CodeCache::InvalidateBlocksWithPageIndex(page_index); + return PageFaultHandler::HandlerResult::ContinueExecution; } #ifdef ENABLE_RECOMPILER_SUPPORT - return HandleFastmemException(exception_pc, fault_address, is_write); + return CPU::CodeCache::HandleFastmemException(exception_pc, fault_address, is_write); #else - return Common::PageFaultHandler::HandlerResult::ExecuteNextHandler; + return PageFaultHandler::HandlerResult::ExecuteNextHandler; #endif } @@ -1593,8 +1585,8 @@ void CPU::CodeCache::AddLoadStoreInfo(void* code_address, u32 code_size, u32 gue s_fastmem_backpatch_info.emplace(code_address, info); } -Common::PageFaultHandler::HandlerResult CPU::CodeCache::HandleFastmemException(void* exception_pc, void* fault_address, - bool is_write) +PageFaultHandler::HandlerResult CPU::CodeCache::HandleFastmemException(void* exception_pc, void* fault_address, + bool is_write) { // TODO: Catch general RAM writes, not just fastmem PhysicalMemoryAddress guest_address; @@ -1606,7 +1598,7 @@ Common::PageFaultHandler::HandlerResult CPU::CodeCache::HandleFastmemException(v (static_cast(fault_address) - static_cast(g_state.fastmem_base)) >= static_cast(Bus::FASTMEM_ARENA_SIZE)) { - return Common::PageFaultHandler::HandlerResult::ExecuteNextHandler; + return PageFaultHandler::HandlerResult::ExecuteNextHandler; } guest_address = static_cast( @@ -1618,7 +1610,7 @@ Common::PageFaultHandler::HandlerResult CPU::CodeCache::HandleFastmemException(v { Log_DevFmt("Ignoring fault due to RAM write @ 0x{:08X}", guest_address); InvalidateBlocksWithPageIndex(Bus::GetRAMCodePageIndex(guest_address)); - return Common::PageFaultHandler::HandlerResult::ContinueExecution; + return PageFaultHandler::HandlerResult::ContinueExecution; } } else @@ -1635,7 +1627,7 @@ Common::PageFaultHandler::HandlerResult CPU::CodeCache::HandleFastmemException(v if (iter == s_fastmem_backpatch_info.end()) { Log_ErrorFmt("No backpatch info found for {}", exception_pc); - return Common::PageFaultHandler::HandlerResult::ExecuteNextHandler; + return PageFaultHandler::HandlerResult::ExecuteNextHandler; } LoadstoreBackpatchInfo& info = iter->second; @@ -1670,7 +1662,7 @@ Common::PageFaultHandler::HandlerResult CPU::CodeCache::HandleFastmemException(v // and store the pc in the faulting list, so that we don't emit another fastmem loadstore s_fastmem_faulting_pcs.insert(info.guest_pc); s_fastmem_backpatch_info.erase(iter); - return Common::PageFaultHandler::HandlerResult::ContinueExecution; + return PageFaultHandler::HandlerResult::ContinueExecution; } bool CPU::CodeCache::HasPreviouslyFaultedOnPC(u32 guest_pc) diff --git a/src/core/cpu_code_cache.h b/src/core/cpu_code_cache.h index 329e5cb43..171e269df 100644 --- a/src/core/cpu_code_cache.h +++ b/src/core/cpu_code_cache.h @@ -6,6 +6,8 @@ #include "bus.h" #include "cpu_types.h" +class Error; + namespace CPU::CodeCache { /// Returns true if any recompiler is in use. @@ -15,7 +17,7 @@ bool IsUsingAnyRecompiler(); bool IsUsingFastmem(); /// Allocates resources, call once at startup. -bool ProcessStartup(); +bool ProcessStartup(Error* error); /// Frees resources, call once at shutdown. void ProcessShutdown(); diff --git a/src/core/system.cpp b/src/core/system.cpp index 516492d25..42fda198e 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -254,7 +254,7 @@ static TinyString GetTimestampStringForFileName() return TinyString::from_format("{:%Y-%m-%d-%H-%M-%S}", fmt::localtime(std::time(nullptr))); } -bool System::Internal::CPUThreadInitialize() +bool System::Internal::CPUThreadInitialize(Error* error) { #ifdef _WIN32 // On Win32, we have a bunch of things which use COM (e.g. SDL, Cubeb, etc). @@ -263,15 +263,15 @@ bool System::Internal::CPUThreadInitialize() HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); if (FAILED(hr)) { - Host::ReportErrorAsync("Error", fmt::format("CoInitializeEx() failed: {:08X}", static_cast(hr))); + Error::SetHResult(error, "CoInitializeEx() failed: ", hr); return false; } #endif - if (!Bus::AllocateMemory()) + if (!Bus::AllocateMemory(error)) return false; - if (!CPU::CodeCache::ProcessStartup()) + if (!CPU::CodeCache::ProcessStartup(error)) return false; // This will call back to Host::LoadSettings() -> ReloadSources(). diff --git a/src/core/system.h b/src/core/system.h index 39927f5d6..43aa69f84 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -490,7 +490,7 @@ void UpdateDiscordPresence(bool update_session_time); namespace Internal { /// Called on process startup. -bool CPUThreadInitialize(); +bool CPUThreadInitialize(Error* error); /// Called on process shutdown. void CPUThreadShutdown(); diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index 637f8b791..d22dd74ee 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -1639,10 +1639,14 @@ void EmuThread::run() m_started_semaphore.release(); // input source setup must happen on emu thread - if (!System::Internal::CPUThreadInitialize()) { - moveToThread(m_ui_thread); - return; + Error startup_error; + if (!System::Internal::CPUThreadInitialize(&startup_error)) + { + Host::ReportFatalError("Fatal Startup Error", startup_error.GetDescription()); + moveToThread(m_ui_thread); + return; + } } // bind buttons/axises diff --git a/src/duckstation-regtest/regtest_host.cpp b/src/duckstation-regtest/regtest_host.cpp index cc6443026..b9e82e800 100644 --- a/src/duckstation-regtest/regtest_host.cpp +++ b/src/duckstation-regtest/regtest_host.cpp @@ -658,8 +658,14 @@ int main(int argc, char* argv[]) return EXIT_FAILURE; } - if (!System::Internal::CPUThreadInitialize()) - return EXIT_FAILURE; + { + Error startup_error; + if (!System::Internal::CPUThreadInitialize(&startup_error)) + { + Log_ErrorFmt("CPUThreadInitialize() failed: {}", startup_error.GetDescription()); + return EXIT_FAILURE; + } + } RegTestHost::HookSignals(); diff --git a/src/util/page_fault_handler.cpp b/src/util/page_fault_handler.cpp index 189eb5189..0d809d9b1 100644 --- a/src/util/page_fault_handler.cpp +++ b/src/util/page_fault_handler.cpp @@ -1,9 +1,10 @@ -// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "page_fault_handler.h" #include "common/assert.h" +#include "common/error.h" #include "common/log.h" #include @@ -11,19 +12,15 @@ #include #include -Log_SetChannel(Common::PageFaultHandler); - #if defined(_WIN32) #include "common/windows_headers.h" #elif defined(__linux__) || defined(__ANDROID__) #include #include #include -#define USE_SIGSEGV 1 #elif defined(__APPLE__) || defined(__FreeBSD__) #include #include -#define USE_SIGSEGV 1 #endif #ifdef __APPLE__ @@ -32,14 +29,14 @@ Log_SetChannel(Common::PageFaultHandler); #include #endif -namespace Common::PageFaultHandler { - +namespace PageFaultHandler { static std::recursive_mutex s_exception_handler_mutex; -static Handler s_exception_handler_callback; -static bool s_in_exception_handler; +static bool s_in_exception_handler = false; +static bool s_installed = false; +} // namespace PageFaultHandler #if defined(CPU_ARCH_ARM64) -static bool IsStoreInstruction(const void* ptr) +[[maybe_unused]] static bool IsStoreInstruction(const void* ptr) { u32 bits; std::memcpy(&bits, ptr, sizeof(bits)); @@ -74,7 +71,7 @@ static bool IsStoreInstruction(const void* ptr) } } #elif defined(CPU_ARCH_RISCV64) -static bool IsStoreInstruction(const void* ptr) +[[maybe_unused]] static bool IsStoreInstruction(const void* ptr) { u32 bits; std::memcpy(&bits, ptr, sizeof(bits)); @@ -83,10 +80,13 @@ static bool IsStoreInstruction(const void* ptr) } #endif -#if defined(_WIN32) && (defined(CPU_ARCH_X64) || defined(CPU_ARCH_ARM64)) -static PVOID s_veh_handle; +#if defined(_WIN32) -static LONG ExceptionHandler(PEXCEPTION_POINTERS exi) +namespace PageFaultHandler { +static LONG ExceptionHandler(PEXCEPTION_POINTERS exi); +} + +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); @@ -112,21 +112,42 @@ static LONG ExceptionHandler(PEXCEPTION_POINTERS exi) s_in_exception_handler = true; - const HandlerResult handled = s_exception_handler_callback(exception_pc, exception_address, is_write); + 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; } -#elif defined(USE_SIGSEGV) +bool PageFaultHandler::Install(Error* error) +{ + std::unique_lock lock(s_exception_handler_mutex); + AssertMsg(!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; +} + +#else + +namespace PageFaultHandler { +static void SignalHandler(int sig, siginfo_t* info, void* ctx); +static void CallExistingSignalHandler(int signal, siginfo_t* siginfo, void* ctx); static struct sigaction s_old_sigsegv_action; #if defined(__APPLE__) || defined(__aarch64__) static struct sigaction s_old_sigbus_action; #endif +} // namespace PageFaultHandler -static void CallExistingSignalHandler(int signal, siginfo_t* siginfo, void* ctx) +void PageFaultHandler::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; @@ -152,7 +173,7 @@ static void CallExistingSignalHandler(int signal, siginfo_t* siginfo, void* ctx) } } -static void SignalHandler(int sig, siginfo_t* info, void* ctx) +void PageFaultHandler::SignalHandler(int sig, 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); @@ -222,7 +243,7 @@ static void SignalHandler(int sig, siginfo_t* info, void* ctx) s_in_exception_handler = true; - const HandlerResult result = s_exception_handler_callback(exception_pc, exception_address, is_write); + const HandlerResult result = HandlePageFault(exception_pc, exception_address, is_write); s_in_exception_handler = false; @@ -235,78 +256,39 @@ static void SignalHandler(int sig, siginfo_t* info, void* ctx) CallExistingSignalHandler(sig, info, ctx); } -#endif - -bool InstallHandler(Handler handler) +bool PageFaultHandler::Install(Error* error) { std::unique_lock lock(s_exception_handler_mutex); - AssertMsg(!s_exception_handler_callback, "A page fault handler is already registered."); - if (!s_exception_handler_callback) - { -#if defined(_WIN32) && (defined(CPU_ARCH_X64) || defined(CPU_ARCH_ARM64)) - s_veh_handle = AddVectoredExceptionHandler(1, ExceptionHandler); - if (!s_veh_handle) - { - Log_ErrorPrint("Failed to add vectored exception handler"); - return false; - } -#elif defined(USE_SIGSEGV) - struct sigaction sa; + AssertMsg(!s_installed, "Page fault handler has already been installed."); - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - sa.sa_sigaction = SignalHandler; + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = SignalHandler; #ifdef __linux__ - // Don't block the signal from executing recursively, we want to fire the original handler. - sa.sa_flags |= SA_NODEFER; + // Don't block the signal from executing recursively, we want to fire the original handler. + sa.sa_flags |= SA_NODEFER; #endif - if (sigaction(SIGSEGV, &sa, &s_old_sigsegv_action) != 0) - return false; + if (sigaction(SIGSEGV, &sa, &s_old_sigsegv_action) != 0) + { + Error::SetErrno(error, "sigaction() for SIGSEGV failed: ", errno); + return false; + } #if defined(__APPLE__) || defined(__aarch64__) - // MacOS uses SIGBUS for memory permission violations - if (sigaction(SIGBUS, &sa, &s_old_sigbus_action) != 0) - return false; + // MacOS uses SIGBUS for memory permission violations + if (sigaction(SIGBUS, &sa, &s_old_sigbus_action) != 0) + { + Error::SetErrno(error, "sigaction() for SIGBUS failed: ", errno); + return false; + } #endif #ifdef __APPLE__ - task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, MACH_PORT_NULL, EXCEPTION_DEFAULT, 0); + task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, MACH_PORT_NULL, EXCEPTION_DEFAULT, 0); #endif -#else - return false; -#endif - } - s_exception_handler_callback = handler; + s_installed = true; return true; } -bool RemoveHandler(Handler handler) -{ - std::unique_lock lock(s_exception_handler_mutex); - AssertMsg(!s_exception_handler_callback || s_exception_handler_callback == handler, - "Not removing the same handler previously registered."); - if (!s_exception_handler_callback) - return false; - - s_exception_handler_callback = nullptr; - -#if defined(_WIN32) && (defined(CPU_ARCH_X64) || defined(CPU_ARCH_ARM64)) - RemoveVectoredExceptionHandler(s_veh_handle); - s_veh_handle = nullptr; -#elif defined(USE_SIGSEGV) - struct sigaction sa; -#if defined(__APPLE__) || defined(__aarch64__) - sigaction(SIGBUS, &s_old_sigbus_action, &sa); - s_old_sigbus_action = {}; #endif -#if !defined(__APPLE__) || defined(__aarch64__) - sigaction(SIGSEGV, &s_old_sigsegv_action, &sa); - s_old_sigsegv_action = {}; -#endif -#else - return false; -#endif - - return true; -} - -} // namespace Common::PageFaultHandler diff --git a/src/util/page_fault_handler.h b/src/util/page_fault_handler.h index eeda11293..7869ff179 100644 --- a/src/util/page_fault_handler.h +++ b/src/util/page_fault_handler.h @@ -1,19 +1,18 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #pragma once #include "common/types.h" -namespace Common::PageFaultHandler { +class Error; + +namespace PageFaultHandler { enum class HandlerResult { ContinueExecution, ExecuteNextHandler, }; -using Handler = HandlerResult (*)(void* exception_pc, void* fault_address, bool is_write); - -bool InstallHandler(Handler callback); -bool RemoveHandler(Handler callback); - -} // namespace Common::PageFaultHandler +HandlerResult HandlePageFault(void* exception_pc, void* fault_address, bool is_write); +bool Install(Error* error = nullptr); +} // namespace PageFaultHandler