fix(libs): ex from libc prx: unwind for catch fix (mingw)

This commit is contained in:
boykopovar 2026-09-13 19:21:31 +03:00
parent bdff4874de
commit bd0969c3a4
11 changed files with 64 additions and 8 deletions

View File

@ -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

View File

@ -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 "$<TARGET_FILE:${target}>"
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()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -10,3 +10,4 @@ target_include_directories(libSceNpTrophy2 PRIVATE
${LIBS_INCLUDE_DIR}
)
target_link_libraries(libSceNpTrophy2 PRIVATE libc)
configure_windows_unwind(libSceNpTrophy2)

View File

@ -12,3 +12,4 @@ target_include_directories(libSceNpUniversalDataSystem PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
target_link_libraries(libSceNpUniversalDataSystem PRIVATE libc)
configure_windows_unwind(libSceNpUniversalDataSystem)

View File

@ -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)

View File

@ -182,7 +182,7 @@ bool DecodeFrame(_Unwind_Context& context, Frame& frame) {
while (end - p >= 8) {
const Byte* record = p;
const auto length = Read<std::uint32_t>(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<std::uint32_t>(p)) {
@ -357,6 +357,11 @@ bool Step(_Unwind_Context& context) {
if (rule.kind == 1) std::memcpy(destination, reinterpret_cast<void*>(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<const void*>(address), 16);
}
else if (rule.kind == 5) std::memset(destination, 0, 16);
else if (rule.kind != 0) return false;
}

View File

@ -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)

View File

@ -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;
}