fix(libc): run guest exit handlers before the host exit handlers
LibcExit called std::exit, which ran the static destructors of the libraries before the guest exit handlers. A guest handler that freed memory then locked a destroyed std::mutex, which throws, and a noexcept guest destructor ended in std::terminate. The guest handlers now run first, while the library objects are alive. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
4b005b229e
commit
20e8bb09ba
|
|
@ -296,6 +296,10 @@ if(BUILD_TESTING)
|
|||
target_include_directories(exception_personality_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(exception_personality_tests PRIVATE libc)
|
||||
add_test(NAME exception_personality COMMAND exception_personality_tests)
|
||||
add_test_executable(guest_exit_order_tests ${LIBS_INCLUDE_DIR}/tests/GuestExitOrder.cpp)
|
||||
target_include_directories(guest_exit_order_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_exit_order_tests PRIVATE libc)
|
||||
add_test(NAME guest_exit_order COMMAND guest_exit_order_tests)
|
||||
if(NOT APPLE)
|
||||
enable_language(C)
|
||||
add_test_executable(exception_c_cleanup_tests ${LIBS_INCLUDE_DIR}/tests/ExceptionCCleanup.cpp ${LIBS_INCLUDE_DIR}/tests/ExceptionCCleanup.c)
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ extern "C" {
|
|||
exitRequested.store(true);
|
||||
if (exitStarted.exchange(true)) LibcAwaitExit_nid_postfix();
|
||||
LibcRunShutdown_nid_postfix();
|
||||
CxaFinalize_nid_no_patch(nullptr);
|
||||
std::exit(code);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
#include "prx/libc/include/Shutdown.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" int APS5_VABI __cxa_atexit_nid_postfix(void (APS5_VABI *func)(void*), void* arg, void* dsoHandle);
|
||||
|
||||
namespace {
|
||||
|
||||
int events[2];
|
||||
|
||||
void Record(char value) {
|
||||
static_cast<void>(!write(events[1], &value, 1));
|
||||
}
|
||||
|
||||
void APS5_VABI GuestHandler(void*) { Record('G'); }
|
||||
|
||||
void HostHandler() { Record('H'); }
|
||||
|
||||
bool GuestHandlersRunFirst() {
|
||||
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);
|
||||
LibcExit_nid_no_patch(0);
|
||||
}
|
||||
close(events[1]);
|
||||
char order[3] = {};
|
||||
ssize_t length = 0;
|
||||
while (length < 2) {
|
||||
const ssize_t count = read(events[0], order + length, 2 - length);
|
||||
if (count <= 0) break;
|
||||
length += count;
|
||||
}
|
||||
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';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
if (!GuestHandlersRunFirst()) {
|
||||
std::fputs("guest exit handlers must run before the host exit handlers\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue