diff --git a/README.md b/README.md index 49968c7a0..b9d33908b 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,7 @@ Execution reaches `_start`, stack unwinding and exception handling tables are bu Shader initialization via `sceAgcCreate*` passes. Audio output and video output initialization pass -Now: `sceAgcDcbResetQueue not implemented`. - -On Windows, after printing an unhandled exception the process exits with STATUS_STACK_BUFFER_OVERRUN due to the difficulty of manually [implementing proper exception handling](core/libs/prx/libc/src/exception). +Now: `sceAgcCbSetShRegisterRangeDirect: null or misaligned address`. ## Disclaimer diff --git a/core/libs/CMakeLists.txt b/core/libs/CMakeLists.txt index 8a7060c6d..55f0e8ed6 100644 --- a/core/libs/CMakeLists.txt +++ b/core/libs/CMakeLists.txt @@ -14,11 +14,24 @@ if(MINGW) add_compile_options(-fno-asynchronous-unwind-tables) endif() +function(configure_windows_unwind target) + if(MINGW) + if(NOT EXISTS "${CMAKE_OBJCOPY}") + message(FATAL_ERROR "Windows DWARF unwinding requires objcopy") + endif() + add_custom_command(TARGET ${target} POST_BUILD + COMMAND "${CMAKE_OBJCOPY}" --rename-section .eh_frame=.ehfram "$" + VERBATIM + ) + endif() +endfunction() + set(LIBC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/prx/libc) add_library(libc SHARED EXCLUDE_FROM_ALL) set(LIBS_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) add_subdirectory(${LIBC_DIR} ${CMAKE_CURRENT_BINARY_DIR}/prx/libc) target_include_directories(libc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +configure_windows_unwind(libc) set(UNPATCHED_DIR ${CMAKE_CURRENT_BINARY_DIR}/libs/unpatched) set(PATCHED_DIR ${CMAKE_CURRENT_BINARY_DIR}/libs) @@ -56,6 +69,7 @@ foreach(LIB_DIR ${LIBS_DIRS}) else() add_library(${LIB_NAME} SHARED EXCLUDE_FROM_ALL ${LIB_DIR}/Export.cpp) target_include_directories(${LIB_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + configure_windows_unwind(${LIB_NAME}) endif() if(LIB_NAME STREQUAL "libkernel") @@ -87,3 +101,13 @@ foreach(LIB_DIR ${LIBS_DIRS}) endforeach() add_custom_target(libs DEPENDS ${PATCHED_OUTPUTS}) + +if(MINGW) + foreach(runtimeLinkFlag IN ITEMS -static-libgcc -static-libstdc++ -static) + string(REPLACE "${runtimeLinkFlag}" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") + endforeach() + add_executable(windows_exception_tests EXCLUDE_FROM_ALL tests/WindowsExceptions.cpp) + set_target_properties(windows_exception_tests PROPERTIES CXX_EXTENSIONS OFF) + target_link_libraries(windows_exception_tests PRIVATE libc) + configure_windows_unwind(windows_exception_tests) +endif() diff --git a/core/libs/prx/libSceAgc/CMakeLists.txt b/core/libs/prx/libSceAgc/CMakeLists.txt index fd46496a1..7db105c54 100644 --- a/core/libs/prx/libSceAgc/CMakeLists.txt +++ b/core/libs/prx/libSceAgc/CMakeLists.txt @@ -63,3 +63,5 @@ endforeach() add_executable(agc_command_tests EXCLUDE_FROM_ALL tests/Commands.cpp) target_include_directories(agc_command_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(agc_command_tests PRIVATE libSceAgc libc) +configure_windows_unwind(agc_command_tests) +configure_windows_unwind(libSceAgc) diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index 6a7378196..538dfb59d 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -12,3 +12,4 @@ add_library(libSceAgcDriver SHARED EXCLUDE_FROM_ALL target_include_directories(libSceAgcDriver PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(libSceAgcDriver PRIVATE libc) +configure_windows_unwind(libSceAgcDriver) diff --git a/core/libs/prx/libSceAudioOut/CMakeLists.txt b/core/libs/prx/libSceAudioOut/CMakeLists.txt index 1b53ed9f3..d56668ce9 100644 --- a/core/libs/prx/libSceAudioOut/CMakeLists.txt +++ b/core/libs/prx/libSceAudioOut/CMakeLists.txt @@ -13,3 +13,4 @@ target_include_directories(libSceAudioOut PRIVATE ${CMAKE_SOURCE_DIR}/3rdparty/SDL2/include ) target_link_libraries(libSceAudioOut PRIVATE libc libkernel SDL2-static) +configure_windows_unwind(libSceAudioOut) diff --git a/core/libs/prx/libSceNpTrophy2/CMakeLists.txt b/core/libs/prx/libSceNpTrophy2/CMakeLists.txt index 97d668921..c86b204c3 100644 --- a/core/libs/prx/libSceNpTrophy2/CMakeLists.txt +++ b/core/libs/prx/libSceNpTrophy2/CMakeLists.txt @@ -10,3 +10,4 @@ target_include_directories(libSceNpTrophy2 PRIVATE ${LIBS_INCLUDE_DIR} ) target_link_libraries(libSceNpTrophy2 PRIVATE libc) +configure_windows_unwind(libSceNpTrophy2) diff --git a/core/libs/prx/libSceNpUniversalDataSystem/CMakeLists.txt b/core/libs/prx/libSceNpUniversalDataSystem/CMakeLists.txt index f4339aea4..ffa5b0803 100644 --- a/core/libs/prx/libSceNpUniversalDataSystem/CMakeLists.txt +++ b/core/libs/prx/libSceNpUniversalDataSystem/CMakeLists.txt @@ -12,3 +12,4 @@ target_include_directories(libSceNpUniversalDataSystem PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) target_link_libraries(libSceNpUniversalDataSystem PRIVATE libc) +configure_windows_unwind(libSceNpUniversalDataSystem) diff --git a/core/libs/prx/libSceVideoOut/CMakeLists.txt b/core/libs/prx/libSceVideoOut/CMakeLists.txt index f0b60433a..5337ccacd 100644 --- a/core/libs/prx/libSceVideoOut/CMakeLists.txt +++ b/core/libs/prx/libSceVideoOut/CMakeLists.txt @@ -10,3 +10,4 @@ target_include_directories(libSceVideoOut PRIVATE ${CMAKE_SOURCE_DIR}/3rdparty/SDL2/include ) target_link_libraries(libSceVideoOut PRIVATE libc libkernel SDL2-static) +configure_windows_unwind(libSceVideoOut) diff --git a/core/libs/prx/libc/src/exception/Unwind.cpp b/core/libs/prx/libc/src/exception/Unwind.cpp index 96c1ea9cf..371803ac8 100644 --- a/core/libs/prx/libc/src/exception/Unwind.cpp +++ b/core/libs/prx/libc/src/exception/Unwind.cpp @@ -182,7 +182,7 @@ bool DecodeFrame(_Unwind_Context& context, Frame& frame) { while (end - p >= 8) { const Byte* record = p; const auto length = Read(p); - if (!length) break; + if (!length) continue; if (length == 0xffffffff || length < 4 || Word(end - p) < length) return false; const Byte* next = p + length; if (Read(p)) { @@ -357,6 +357,11 @@ bool Step(_Unwind_Context& context) { if (rule.kind == 1) std::memcpy(destination, reinterpret_cast(context.cfa + rule.value), 16); else if (rule.kind == 2 && rule.value >= 17 && rule.value < RuleCount) std::memcpy(destination, context.vectorRegisters[rule.value - 17], 16); + else if (rule.kind == 3) { + Word address; + if (!Expression(rule.expression, context, context.cfa, address)) return false; + std::memcpy(destination, reinterpret_cast(address), 16); + } else if (rule.kind == 5) std::memset(destination, 0, 16); else if (rule.kind != 0) return false; } diff --git a/core/libs/prx/libkernel/CMakeLists.txt b/core/libs/prx/libkernel/CMakeLists.txt index a43e956a1..22e484a6c 100644 --- a/core/libs/prx/libkernel/CMakeLists.txt +++ b/core/libs/prx/libkernel/CMakeLists.txt @@ -40,3 +40,4 @@ target_link_libraries(libkernel PRIVATE libc) if(WIN32) target_compile_definitions(libkernel PRIVATE _WIN32_WINNT=0x0602) endif() +configure_windows_unwind(libkernel) diff --git a/core/libs/tests/WindowsExceptions.cpp b/core/libs/tests/WindowsExceptions.cpp index 77430f5bf..727b3904c 100644 --- a/core/libs/tests/WindowsExceptions.cpp +++ b/core/libs/tests/WindowsExceptions.cpp @@ -6,11 +6,11 @@ extern "C" void NotImplemented_nid_no_patch(const char*); static int destroyed; struct Guard { ~Guard() { ++destroyed; } }; -__attribute__((noinline)) static void ThrowNested() { +static void ThrowNested() { Guard guard; throw std::runtime_error("native own unwind"); } -__attribute__((noinline)) static void Rethrow() { +static void Rethrow() { Guard guard; try { ThrowNested(); } catch (const std::runtime_error&) { throw; } @@ -25,8 +25,29 @@ int main() { try { NotImplemented_nid_no_patch("cross DLL"); } catch (const std::runtime_error& error) { if (std::strcmp(error.what(), "cross DLL not implemented")) return 3; - std::puts("Own Windows exceptions: catch, rethrow, destructors, cross DLL passed"); + try { + Guard guard; + throw; + } catch (const std::logic_error&) { + return 4; + } catch (const std::exception& rethrown) { + if (&rethrown != &error || destroyed != 3) return 5; + } + try { + Guard guard; + throw 42; + } catch (const std::exception&) { + return 6; + } catch (...) { + if (destroyed != 4) return 7; + try { + throw; + } catch (int value) { + if (value != 42) return 8; + } + } + std::puts("Own Windows exceptions: typed catch, base catch, catch-all, rethrow, destructors, cross DLL passed"); return 0; } - return 4; + return 9; }