diff --git a/Source/Core/Common/MemoryUtil.cpp b/Source/Core/Common/MemoryUtil.cpp index b473705411..2cd393d098 100644 --- a/Source/Core/Common/MemoryUtil.cpp +++ b/Source/Core/Common/MemoryUtil.cpp @@ -160,18 +160,25 @@ void* AllocateAlignedMemory(size_t size, size_t alignment) return ptr; } -void FreeMemoryPages(void* ptr, size_t size) +bool FreeMemoryPages(void* ptr, size_t size) { if (ptr) { #ifdef _WIN32 if (!VirtualFree(ptr, 0, MEM_RELEASE)) + { PanicAlertFmt("FreeMemoryPages failed!\nVirtualFree: {}", GetLastErrorString()); + return false; + } #else if (munmap(ptr, size) != 0) + { PanicAlertFmt("FreeMemoryPages failed!\nmunmap: {}", LastStrerrorString()); + return false; + } #endif } + return true; } void FreeAlignedMemory(void* ptr) @@ -186,39 +193,56 @@ void FreeAlignedMemory(void* ptr) } } -void ReadProtectMemory(void* ptr, size_t size) +bool ReadProtectMemory(void* ptr, size_t size) { #ifdef _WIN32 DWORD oldValue; if (!VirtualProtect(ptr, size, PAGE_NOACCESS, &oldValue)) + { PanicAlertFmt("ReadProtectMemory failed!\nVirtualProtect: {}", GetLastErrorString()); + return false; + } #else if (mprotect(ptr, size, PROT_NONE) != 0) + { PanicAlertFmt("ReadProtectMemory failed!\nmprotect: {}", LastStrerrorString()); + return false; + } #endif + return true; } -void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) +bool WriteProtectMemory(void* ptr, size_t size, bool allowExecute) { #ifdef _WIN32 DWORD oldValue; if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue)) + { PanicAlertFmt("WriteProtectMemory failed!\nVirtualProtect: {}", GetLastErrorString()); + return false; + } #elif !(defined(_M_ARM_64) && defined(__APPLE__)) // MacOS 11.2 on ARM does not allow for changing the access permissions of pages // that were marked executable, instead it uses the protections offered by MAP_JIT // for write protection. if (mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ) != 0) + { PanicAlertFmt("WriteProtectMemory failed!\nmprotect: {}", LastStrerrorString()); + return false; + } #endif + return true; } -void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) +bool UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) { #ifdef _WIN32 DWORD oldValue; if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldValue)) + { PanicAlertFmt("UnWriteProtectMemory failed!\nVirtualProtect: {}", GetLastErrorString()); + return false; + } #elif !(defined(_M_ARM_64) && defined(__APPLE__)) // MacOS 11.2 on ARM does not allow for changing the access permissions of pages // that were marked executable, instead it uses the protections offered by MAP_JIT @@ -227,8 +251,10 @@ void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ) != 0) { PanicAlertFmt("UnWriteProtectMemory failed!\nmprotect: {}", LastStrerrorString()); + return false; } #endif + return true; } size_t MemPhysical() diff --git a/Source/Core/Common/MemoryUtil.h b/Source/Core/Common/MemoryUtil.h index b6489e9546..11ee1c4cbb 100644 --- a/Source/Core/Common/MemoryUtil.h +++ b/Source/Core/Common/MemoryUtil.h @@ -27,12 +27,12 @@ struct ScopedJITPageWriteAndNoExecute ~ScopedJITPageWriteAndNoExecute() { JITPageWriteDisableExecuteEnable(); } }; void* AllocateMemoryPages(size_t size); -void FreeMemoryPages(void* ptr, size_t size); +bool FreeMemoryPages(void* ptr, size_t size); void* AllocateAlignedMemory(size_t size, size_t alignment); void FreeAlignedMemory(void* ptr); -void ReadProtectMemory(void* ptr, size_t size); -void WriteProtectMemory(void* ptr, size_t size, bool executable = false); -void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false); +bool ReadProtectMemory(void* ptr, size_t size); +bool WriteProtectMemory(void* ptr, size_t size, bool executable = false); +bool UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false); size_t MemPhysical(); } // namespace Common diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 9ca298a837..5396c8439b 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -391,9 +391,10 @@ static void CpuThread(const std::optional& savestate_path, bool del static_cast(IDCache::GetEnvForThread()); #endif - const bool fastmem_enabled = Config::Get(Config::MAIN_FASTMEM); - if (fastmem_enabled) - EMM::InstallExceptionHandler(); // Let's run under memory watch + // The JIT need to be able to intercept faults, both for fastmem and for the BLR optimization. + const bool exception_handler = EMM::IsExceptionHandlerSupported(); + if (exception_handler) + EMM::InstallExceptionHandler(); #ifdef USE_MEMORYWATCHER s_memory_watcher = std::make_unique(); @@ -441,7 +442,7 @@ static void CpuThread(const std::optional& savestate_path, bool del s_is_started = false; - if (fastmem_enabled) + if (exception_handler) EMM::UninstallExceptionHandler(); if (GDBStub::IsActive()) diff --git a/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp b/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp index 7c9c707fbc..bcdafd9725 100644 --- a/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp +++ b/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp @@ -18,6 +18,7 @@ #include "Core/Core.h" #include "Core/CoreTiming.h" #include "Core/HW/CPU.h" +#include "Core/MemTools.h" #include "Core/PowerPC/PPCAnalyst.h" #include "Core/PowerPC/PowerPC.h" #include "Core/System.h" @@ -132,7 +133,8 @@ void JitBase::RefreshConfig() analyzer.SetDivByZeroExceptionsEnabled(m_enable_div_by_zero_exceptions); bool any_watchpoints = m_system.GetPowerPC().GetMemChecks().HasAny(); - jo.fastmem = m_fastmem_enabled && jo.fastmem_arena && (m_ppc_state.msr.DR || !any_watchpoints); + jo.fastmem = m_fastmem_enabled && jo.fastmem_arena && (m_ppc_state.msr.DR || !any_watchpoints) && + EMM::IsExceptionHandlerSupported(); jo.memcheck = m_system.IsMMUMode() || m_system.IsPauseOnPanicMode() || any_watchpoints; jo.fp_exceptions = m_enable_float_exceptions; jo.div_by_zero_exceptions = m_enable_div_by_zero_exceptions; @@ -140,7 +142,8 @@ void JitBase::RefreshConfig() void JitBase::InitBLROptimization() { - m_enable_blr_optimization = jo.enableBlocklink && m_fastmem_enabled && !m_enable_debugging; + m_enable_blr_optimization = + jo.enableBlocklink && !m_enable_debugging && EMM::IsExceptionHandlerSupported(); m_cleanup_after_stackfault = false; } @@ -151,7 +154,12 @@ void JitBase::ProtectStack() #ifdef _WIN32 ULONG reserveSize = SAFE_STACK_SIZE; - SetThreadStackGuarantee(&reserveSize); + if (!SetThreadStackGuarantee(&reserveSize)) + { + PanicAlertFmt("Failed to set thread stack guarantee"); + m_enable_blr_optimization = false; + return; + } #else auto [stack_addr, stack_size] = Common::GetCurrentThreadStack(); @@ -184,7 +192,12 @@ void JitBase::ProtectStack() } m_stack_guard = reinterpret_cast(stack_guard_addr); - Common::ReadProtectMemory(m_stack_guard, GUARD_SIZE); + if (!Common::ReadProtectMemory(m_stack_guard, GUARD_SIZE)) + { + m_stack_guard = nullptr; + m_enable_blr_optimization = false; + return; + } #endif }