Merge pull request #3415 from MarcZX100/codex/fix-windows-runtime-unwind-3411

test(libc): keep Windows exception cleanup coverage on guest frames
This commit is contained in:
Dean 2026-10-12 08:59:34 +13:00 committed by GitHub
commit 0a8b1d32b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 14 additions and 14 deletions

View File

@ -156,25 +156,24 @@ static void testUncaughtExceptions() {
if (seen != 1) throw std::runtime_error("unwinding did not count its exception");
}
static int onceDestroyed;
struct OnceGuard { ~OnceGuard() { ++onceDestroyed; } };
static void ThrowThroughOnce(int depth) {
OnceGuard guard;
if (depth) { ThrowThroughOnce(depth - 1); return; }
std::once_flag flag;
std::call_once(flag, [] { throw std::runtime_error("through runtime frames"); });
static int guestFrameDestroyed;
struct GuestFrameGuard { ~GuestFrameGuard() { ++guestFrameDestroyed; } };
static void ThrowThroughGuestFrames(int depth) {
GuestFrameGuard guard;
if (depth) { ThrowThroughGuestFrames(depth - 1); return; }
throw std::runtime_error("through guest frames");
}
static void testUnwindThroughRuntimeModules() {
static void testUnwindThroughGuestFrames() {
for (int depth : {0, 1, 7, 64}) {
onceDestroyed = 0;
guestFrameDestroyed = 0;
try {
ThrowThroughOnce(depth);
throw std::logic_error("call_once did not propagate");
ThrowThroughGuestFrames(depth);
throw std::logic_error("guest frame did not propagate");
} catch (const std::runtime_error& error) {
if (std::strcmp(error.what(), "through runtime frames")) throw;
if (std::strcmp(error.what(), "through guest frames")) throw;
}
if (onceDestroyed != depth + 1) throw std::runtime_error("unwinding through runtime DLL frames skipped a destructor");
if (guestFrameDestroyed != depth + 1) throw std::runtime_error("unwinding through guest frames skipped a destructor");
}
}
@ -184,7 +183,7 @@ int main() {
testFutureException();
testMadeExceptionPointer();
testUncaughtExceptions();
testUnwindThroughRuntimeModules();
testUnwindThroughGuestFrames();
try {
Rethrow();
return 1;

View File

@ -395,6 +395,7 @@ Throughout the project, every function at every stage either **does exactly what
- [makecontext](../../core/libs/prx/libSceLibcInternal/UserContext.cpp) (libSceLibcInternal) on Windows pins its stack writable until a later `makecontext` stack overlaps it, since nothing tells the library when the guest frees the stack
- [setcontext](../../core/libs/prx/libkernel/System/src/UserContext.cpp) (libkernel) and [longjmp](../../core/libs/prx/libc/src/JumpBuffer.cpp) (libc) on Windows set the thread's stack bounds in the TEB to the stack they switch to: a `makecontext` stack, or else the committed region around the target stack pointer. A switch to any other guest stack, such as a context built by hand, gets the bounds of that region only
- [sceKernelInstallExceptionHandler](../../core/libs/prx/libkernel/System/src/Exception.cpp) records the handler, but a host fault still ends the process: only signals raised with `sceKernelRaiseException` are delivered. Unity's PS5Util module installs a SIGUSR1 handler that reads `uc_mcontext.mc_rsp` (offset 0xf8 of the context) and raises SIGUSR1 on other threads to stop them for garbage collection
- The Windows libc exception unwinder reads the project's `.ehmeta` and `.ehfram` tables, not PE `.pdata`/`.xdata`. WinLibs GCC 15.2's `libstdc++-6.dll` and `libwinpthread-1.dll` have empty `.eh_frame` sections, so guest exceptions cannot currently cross their frames (for example, the `std::call_once` path through `pthread_once`); `RtlVirtualUnwind` stepping and Windows cleanup handlers are not implemented
- [sceKernelRaiseException](../../core/libs/prx/libkernel/System/src/Exception.cpp) on Linux sends the host SIGUSR1, which the library takes over, to the target thread, so the handler runs wherever the thread was interrupted, emulator code included. On the console kernel objects are system calls and libthr defers signals while a thread holds its internal locks; here a handler that needs a host lock the interrupted code holds deadlocks, and a thread that the handler keeps waiting keeps its host locks. Host waits resume after the handler. The host calls Linux does not restart end early: sleeps that do not retry return before their time, and the poll of `sceNetEpollWait` and socket calls with a send or receive timeout return `EINTR` to the guest
- [sceKernelRaiseException](../../core/libs/prx/libkernel/System/src/Exception.cpp) builds the handler's `ucontext` with FreeBSD's amd64 layout (16-byte sigmask, `mcontext` at 0x40, `mc_rsp` at 0xf8); the console's layout is not verified beyond the fields PS5Util reads
- [sceKernelRaiseException](../../core/libs/prx/libkernel/System/src/Exception.cpp) does not consult the target thread's signal mask