diff --git a/core/libs/prx/libSceSystemService/CMakeLists.txt b/core/libs/prx/libSceSystemService/CMakeLists.txt index ea197fc40..3674b130b 100644 --- a/core/libs/prx/libSceSystemService/CMakeLists.txt +++ b/core/libs/prx/libSceSystemService/CMakeLists.txt @@ -24,14 +24,17 @@ if(BUILD_TESTING) add_test(NAME guest_immediate_exit COMMAND guest_system_exit_tests --immediate) add_test(NAME guest_return_from_main COMMAND guest_system_exit_tests --return-from-main) add_test(NAME guest_system_relaunch COMMAND guest_system_exit_tests --relaunch-driver) + add_test(NAME guest_nested_exit COMMAND guest_system_exit_tests --nested-exit-driver) set_tests_properties(guest_system_exit PROPERTIES PASS_REGULAR_EXPRESSION "Guest shutdown and atexit completed") set_tests_properties(guest_quick_exit PROPERTIES PASS_REGULAR_EXPRESSION "Guest quick_exit handlers completed") set_tests_properties(guest_system_relaunch PROPERTIES PASS_REGULAR_EXPRESSION "Guest relaunch completed" TIMEOUT 30) + set_tests_properties(guest_nested_exit PROPERTIES + PASS_REGULAR_EXPRESSION "Guest nested exit completed" TIMEOUT 30) if(WIN32) - set_tests_properties(guest_system_exit guest_system_exit_parameters guest_immediate_exit guest_return_from_main guest_quick_exit guest_system_relaunch PROPERTIES ENVIRONMENT_MODIFICATION + set_tests_properties(guest_system_exit guest_system_exit_parameters guest_immediate_exit guest_return_from_main guest_quick_exit guest_system_relaunch guest_nested_exit PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() add_test_executable(guest_system_service_tests ${LIBS_INCLUDE_DIR}/tests/GuestSystemService.cpp) diff --git a/core/libs/prx/libc/src/Process.cpp b/core/libs/prx/libc/src/Process.cpp index fd810843e..76a702a2a 100644 --- a/core/libs/prx/libc/src/Process.cpp +++ b/core/libs/prx/libc/src/Process.cpp @@ -33,6 +33,7 @@ std::stop_source shutdownSource; std::atomic exitRequested{false}; std::atomic exitStarted{false}; using GuestExitCallback = void (APS5_VABI*)(); +thread_local bool runningExit = false; void APS5_VABI RunGuestExitCallback(void* callback) { reinterpret_cast(callback)(); @@ -115,8 +116,12 @@ extern "C" { [[noreturn]] void LibcExit_nid_no_patch(int code) { exitRequested.store(true); - if (exitStarted.exchange(true)) LibcAwaitExit_nid_postfix(); - LibcRunShutdown_nid_postfix(); + if (exitStarted.exchange(true)) { + if (!runningExit) LibcAwaitExit_nid_postfix(); + } else { + runningExit = true; + LibcRunShutdown_nid_postfix(); + } CxaFinalize_nid_no_patch(nullptr); std::exit(code); } diff --git a/core/libs/tests/GuestSystemExit.cpp b/core/libs/tests/GuestSystemExit.cpp index 56c414635..2f712816f 100644 --- a/core/libs/tests/GuestSystemExit.cpp +++ b/core/libs/tests/GuestSystemExit.cpp @@ -20,6 +20,7 @@ extern "C" int APS5_VABI sceSystemServiceLoadExec(const char*, const char* const*); extern "C" int APS5_VABI __cxa_atexit_nid_postfix(void (APS5_VABI *func)(void*), void* arg, void* dsoHandle); extern "C" void APS5_VABI _Exit_nid_postfix(int); +extern "C" void APS5_VABI exit_nid_postfix(int); extern "C" void APS5_VABI catchReturnFromMain_nid_postfix(int); using GuestQuickExitCallback = void (APS5_VABI*)(); extern "C" int APS5_VABI at_quick_exit_nid_postfix(GuestQuickExitCallback); @@ -71,9 +72,11 @@ void AppendMarker(const std::string& line) { } void MainShutdown() { AppendMarker("shutdown"); } void SecondaryShutdown() { AppendMarker("secondary shutdown"); } -int RunRelaunchingChild(const std::filesystem::path& self) { +void APS5_VABI RemainingHandler(void*) { AppendMarker("remaining handler"); } +void APS5_VABI ExitingHandler(void*) { exit_nid_postfix(7); } +int RunChild(const std::filesystem::path& self, const char* mode) { #ifdef _WIN32 - std::wstring commandLine = L"\"" + self.wstring() + L"\" --relaunch"; + std::wstring commandLine = L"\"" + self.wstring() + L"\" " + std::wstring(mode, mode + std::strlen(mode)); STARTUPINFOW startup{}; startup.cb = sizeof(startup); PROCESS_INFORMATION process{}; @@ -88,7 +91,7 @@ int RunRelaunchingChild(const std::filesystem::path& self) { const pid_t child = fork(); Require(child >= 0); if (child == 0) { - execl(self.c_str(), self.c_str(), "--relaunch", static_cast(nullptr)); + execl(self.c_str(), self.c_str(), mode, static_cast(nullptr)); std::_Exit(127); } int status = 0; @@ -96,6 +99,23 @@ int RunRelaunchingChild(const std::filesystem::path& self) { return WIFEXITED(status) ? WEXITSTATUS(status) : 1; #endif } +int DriveNestedExit() { + const auto self = OwnExecutable(); + const auto marker = std::filesystem::temp_directory_path() / ("anyps5-nested-exit-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()) + ".txt"); + SetVariable(RelaunchMarker, marker.string().c_str()); + const int code = RunChild(self, "--nested-exit"); + std::string text; + { + std::ifstream file(marker); + text.assign(std::istreambuf_iterator(file), std::istreambuf_iterator()); + } + std::error_code ignored; + std::filesystem::remove(marker, ignored); + Require(code == 7); + Require(text == "remaining handler\n"); + std::puts("Guest nested exit completed"); + return 0; +} int DriveRelaunch() { const auto self = OwnExecutable(); const auto secondary = self.parent_path() / ("picker.picker.bin" + self.extension().string()); @@ -103,7 +123,7 @@ int DriveRelaunch() { const auto marker = std::filesystem::temp_directory_path() / ("anyps5-relaunch-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()) + ".txt"); SetVariable(RelaunchMarker, marker.string().c_str()); SetVariable(MainExecutable, self.string().c_str()); - Require(RunRelaunchingChild(self) == 0); + Require(RunChild(self, "--relaunch") == 0); std::string text; for (int attempt = 0; attempt < 200 && text.find("relaunched") == std::string::npos; ++attempt) { std::this_thread::sleep_for(std::chrono::milliseconds(50)); @@ -140,6 +160,15 @@ int main(int argc, char** argv) { quick_exit_nid_postfix(0); return 2; } + if (argc > 1 && std::strcmp(argv[1], "--nested-exit-driver") == 0) { + return DriveNestedExit(); + } + if (argc > 1 && std::strcmp(argv[1], "--nested-exit") == 0) { + Require(__cxa_atexit_nid_postfix(RemainingHandler, nullptr, nullptr) == 0); + Require(__cxa_atexit_nid_postfix(ExitingHandler, nullptr, nullptr) == 0); + exit_nid_postfix(0); + return 2; + } if (argc > 1 && std::strcmp(argv[1], "--relaunch-driver") == 0) { return DriveRelaunch(); }