From fe4a2cf7d803eee36a2108c04e3ab7b097a8f8ea Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 21:05:51 +0000 Subject: [PATCH] fix(libc): run atexit handlers in one order with __cxa_atexit destructors Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA --- core/libs/prx/libc/src/Process.cpp | 20 +++++--------------- core/libs/tests/GuestExitOrder.cpp | 17 ++++++++++++----- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/core/libs/prx/libc/src/Process.cpp b/core/libs/prx/libc/src/Process.cpp index 0e2341b02..fd810843e 100644 --- a/core/libs/prx/libc/src/Process.cpp +++ b/core/libs/prx/libc/src/Process.cpp @@ -18,6 +18,8 @@ #include "prx/libc/include/General.hpp" #include "SceTypes.hpp" +extern "C" int APS5_VABI __cxa_atexit_nid_postfix(void (APS5_VABI *func)(void*), void* arg, void* dsoHandle); + namespace { std::mutex shutdownMutex; @@ -31,17 +33,9 @@ std::stop_source shutdownSource; std::atomic exitRequested{false}; std::atomic exitStarted{false}; using GuestExitCallback = void (APS5_VABI*)(); -std::mutex exitCallbackMutex; -std::vector exitCallbacks; -void runGuestExitCallback() { - GuestExitCallback callback; - { - std::lock_guard lock(exitCallbackMutex); - callback = exitCallbacks.back(); - exitCallbacks.pop_back(); - } - callback(); +void APS5_VABI RunGuestExitCallback(void* callback) { + reinterpret_cast(callback)(); } } @@ -166,11 +160,7 @@ int* APS5_VABI __error_nid_postfix() { int APS5_VABI atexit_nid_postfix(GuestExitCallback func) { if (func == nullptr) return 0; - std::lock_guard lock(exitCallbackMutex); - exitCallbacks.push_back(func); - const int result = std::atexit(runGuestExitCallback); - if (result != 0) exitCallbacks.pop_back(); - return result; + return __cxa_atexit_nid_postfix(RunGuestExitCallback, reinterpret_cast(func), nullptr); } namespace { diff --git a/core/libs/tests/GuestExitOrder.cpp b/core/libs/tests/GuestExitOrder.cpp index 067bea90f..889e6f0f2 100644 --- a/core/libs/tests/GuestExitOrder.cpp +++ b/core/libs/tests/GuestExitOrder.cpp @@ -6,6 +6,7 @@ #include extern "C" int APS5_VABI __cxa_atexit_nid_postfix(void (APS5_VABI *func)(void*), void* arg, void* dsoHandle); +extern "C" int APS5_VABI atexit_nid_postfix(void (APS5_VABI *func)()); namespace { @@ -19,14 +20,16 @@ void APS5_VABI GuestHandler(void*) { Record('G'); } void HostHandler() { Record('H'); } -bool GuestHandlersRunFirst() { +void APS5_VABI AtexitHandler() { Record('A'); } + +template +bool ExitOrder(Setup setup, const char* expected) { if (pipe(events) != 0) return false; const pid_t child = fork(); if (child < 0) return false; if (child == 0) { close(events[0]); - __cxa_atexit_nid_postfix(GuestHandler, nullptr, nullptr); - std::atexit(HostHandler); + setup(); LibcExit_nid_no_patch(0); } close(events[1]); @@ -39,15 +42,19 @@ bool GuestHandlersRunFirst() { } close(events[0]); int status = 0; - return waitpid(child, &status, 0) == child && WIFEXITED(status) && WEXITSTATUS(status) == 0 && length == 2 && order[0] == 'G' && order[1] == 'H'; + return waitpid(child, &status, 0) == child && WIFEXITED(status) && WEXITSTATUS(status) == 0 && length == 2 && order[0] == expected[0] && order[1] == expected[1]; } } int main() { - if (!GuestHandlersRunFirst()) { + if (!ExitOrder([] { __cxa_atexit_nid_postfix(GuestHandler, nullptr, nullptr); std::atexit(HostHandler); }, "GH")) { std::fputs("guest exit handlers must run before the host exit handlers\n", stderr); return 1; } + if (!ExitOrder([] { __cxa_atexit_nid_postfix(GuestHandler, nullptr, nullptr); atexit_nid_postfix(AtexitHandler); }, "AG")) { + std::fputs("atexit and __cxa_atexit handlers must run in reverse order of registration\n", stderr); + return 1; + } return 0; }