fix(libc): run atexit handlers in one order with __cxa_atexit destructors

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
This commit is contained in:
Claude 2026-10-11 21:05:51 +00:00
parent 0a8b1d32b1
commit fe4a2cf7d8
No known key found for this signature in database
2 changed files with 17 additions and 20 deletions

View File

@ -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<bool> exitRequested{false};
std::atomic<bool> exitStarted{false};
using GuestExitCallback = void (APS5_VABI*)();
std::mutex exitCallbackMutex;
std::vector<GuestExitCallback> 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<GuestExitCallback>(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<void*>(func), nullptr);
}
namespace {

View File

@ -6,6 +6,7 @@
#include <unistd.h>
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 <typename Setup>
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;
}