mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 16:43:05 -05:00
A guest handler that called exit threw out of the libc exit path because the exiting thread waited on itself. Like FreeBSD, the nested call now runs the handlers that are still registered and ends the process with the status it passed.
202 lines
5.7 KiB
C++
202 lines
5.7 KiB
C++
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <cerrno>
|
|
#include <condition_variable>
|
|
#include <exception>
|
|
#include <mutex>
|
|
#include <thread>
|
|
#include <vector>
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#endif
|
|
#include <atomic>
|
|
#include <stdexcept>
|
|
#include "prx/libc/include/Shutdown.hpp"
|
|
|
|
#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;
|
|
std::condition_variable shutdownChanged;
|
|
std::vector<void (*)()> shutdownCallbacks;
|
|
std::thread::id shutdownThread;
|
|
bool shutdownStarted = false;
|
|
bool shutdownFinished = false;
|
|
std::exception_ptr shutdownFailure;
|
|
std::stop_source shutdownSource;
|
|
std::atomic<bool> exitRequested{false};
|
|
std::atomic<bool> exitStarted{false};
|
|
using GuestExitCallback = void (APS5_VABI*)();
|
|
thread_local bool runningExit = false;
|
|
|
|
void APS5_VABI RunGuestExitCallback(void* callback) {
|
|
reinterpret_cast<GuestExitCallback>(callback)();
|
|
}
|
|
|
|
}
|
|
|
|
extern "C" std::stop_token LibcShutdownToken_nid_postfix() {
|
|
return shutdownSource.get_token();
|
|
}
|
|
|
|
extern "C" void LibcRequestShutdown_nid_postfix() {
|
|
shutdownSource.request_stop();
|
|
}
|
|
|
|
extern "C" void LibcRequestExit_nid_postfix(int code) {
|
|
if (exitRequested.exchange(true)) return;
|
|
LibcRequestShutdown_nid_postfix();
|
|
std::thread([code] { LibcExit_nid_no_patch(code); }).detach();
|
|
}
|
|
|
|
extern "C" [[noreturn]] void LibcAwaitExit_nid_postfix() {
|
|
if (!exitRequested.load()) throw ProcessShutdown{};
|
|
{
|
|
std::lock_guard lock(shutdownMutex);
|
|
if (shutdownThread == std::this_thread::get_id()) throw std::runtime_error("libc: exit thread cannot wait for itself");
|
|
}
|
|
for (;;) exitRequested.wait(true);
|
|
}
|
|
|
|
extern "C" void LibcRegisterShutdown_nid_postfix(void (*callback)()) {
|
|
std::lock_guard lock(shutdownMutex);
|
|
if (callback == nullptr || shutdownStarted) throw std::runtime_error("libc: invalid shutdown registration");
|
|
shutdownCallbacks.push_back(callback);
|
|
}
|
|
|
|
extern "C" void LibcRunShutdown_nid_postfix() {
|
|
std::unique_lock lock(shutdownMutex);
|
|
if (shutdownStarted) {
|
|
if (!shutdownFinished && shutdownThread == std::this_thread::get_id()) throw std::runtime_error("libc: recursive shutdown");
|
|
shutdownChanged.wait(lock, [] { return shutdownFinished; });
|
|
if (shutdownFailure) std::rethrow_exception(shutdownFailure);
|
|
return;
|
|
}
|
|
shutdownStarted = true;
|
|
shutdownThread = std::this_thread::get_id();
|
|
auto callbacks = std::move(shutdownCallbacks);
|
|
lock.unlock();
|
|
LibcRequestShutdown_nid_postfix();
|
|
std::exception_ptr error;
|
|
for (auto it = callbacks.rbegin(); it != callbacks.rend(); ++it) {
|
|
try { (*it)(); }
|
|
catch (...) { if (!error) error = std::current_exception(); }
|
|
}
|
|
lock.lock();
|
|
shutdownFailure = error;
|
|
shutdownFinished = true;
|
|
lock.unlock();
|
|
shutdownChanged.notify_all();
|
|
if (error) std::rethrow_exception(error);
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
[[noreturn]] void APS5_VABI _Exit_nid_postfix(int code) {
|
|
static const bool trace = std::getenv("APS5_TRACE_EXIT") != nullptr;
|
|
if (trace) {
|
|
std::fprintf(stderr, "[libc] _Exit(%d) called from %p\n", code, __builtin_return_address(0));
|
|
std::fflush(stderr);
|
|
}
|
|
LibcTerminate_nid_no_patch(code);
|
|
}
|
|
|
|
[[noreturn]] void LibcTerminate_nid_no_patch(int code) {
|
|
#ifdef _WIN32
|
|
TerminateProcess(GetCurrentProcess(), static_cast<UINT>(code));
|
|
#endif
|
|
std::_Exit(code);
|
|
}
|
|
|
|
[[noreturn]] void LibcExit_nid_no_patch(int code) {
|
|
exitRequested.store(true);
|
|
if (exitStarted.exchange(true)) {
|
|
if (!runningExit) LibcAwaitExit_nid_postfix();
|
|
} else {
|
|
runningExit = true;
|
|
LibcRunShutdown_nid_postfix();
|
|
}
|
|
CxaFinalize_nid_no_patch(nullptr);
|
|
std::exit(code);
|
|
}
|
|
|
|
void APS5_VABI exit_nid_postfix(int code) {
|
|
static const bool trace = std::getenv("APS5_TRACE_EXIT") != nullptr;
|
|
if (trace) {
|
|
std::fprintf(stderr, "[libc] exit(%d) called from %p\n", code, __builtin_return_address(0));
|
|
std::fflush(stderr);
|
|
}
|
|
LibcExit_nid_no_patch(code);
|
|
}
|
|
|
|
[[noreturn]] void APS5_VABI catchReturnFromMain_nid_postfix(int status) {
|
|
LibcExit_nid_no_patch(status);
|
|
}
|
|
|
|
[[noreturn]] void abort_nid_postfix(
|
|
uint64_t arg0, uint64_t arg1, uint64_t arg2,
|
|
uint64_t arg3, uint64_t arg4, uint64_t arg5
|
|
) {
|
|
(void)arg0; (void)arg1; (void)arg2;
|
|
(void)arg3; (void)arg4; (void)arg5;
|
|
std::fprintf(stderr, "[libc] abort() called from %p\n", __builtin_return_address(0));
|
|
std::fflush(nullptr);
|
|
std::abort();
|
|
}
|
|
|
|
int* APS5_VABI libc_error_nid_postfix() {
|
|
return &errno;
|
|
}
|
|
|
|
int* APS5_VABI __error_nid_postfix() {
|
|
return &errno;
|
|
}
|
|
|
|
[[noreturn]] void __stack_chk_fail_nid_postfix() {
|
|
std::abort();
|
|
}
|
|
|
|
int APS5_VABI atexit_nid_postfix(GuestExitCallback func) {
|
|
if (func == nullptr)
|
|
return 0;
|
|
return __cxa_atexit_nid_postfix(RunGuestExitCallback, reinterpret_cast<void*>(func), nullptr);
|
|
}
|
|
|
|
namespace {
|
|
std::mutex quickExitMutex;
|
|
std::vector<GuestExitCallback> quickExitCallbacks;
|
|
}
|
|
|
|
int APS5_VABI at_quick_exit_nid_postfix(GuestExitCallback func) {
|
|
if (func == nullptr)
|
|
return 0;
|
|
std::lock_guard lock(quickExitMutex);
|
|
quickExitCallbacks.push_back(func);
|
|
return 0;
|
|
}
|
|
|
|
[[noreturn]] void APS5_VABI quick_exit_nid_postfix(int status) {
|
|
std::vector<GuestExitCallback> callbacks;
|
|
{
|
|
std::lock_guard lock(quickExitMutex);
|
|
callbacks = std::move(quickExitCallbacks);
|
|
}
|
|
for (auto it = callbacks.rbegin(); it != callbacks.rend(); ++it) (*it)();
|
|
_Exit_nid_postfix(status);
|
|
}
|
|
|
|
int APS5_VABI daemon_nid_postfix(int noChdir, int noClose) {
|
|
(void)noChdir;
|
|
(void)noClose;
|
|
NotImplemented_nid_no_patch("daemon: forking into the background");
|
|
return -1;
|
|
}
|
|
|
|
}
|