mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 00:22:57 -05:00
Metal Gear Solid 4 (PPSA18194) aborts on main when its launcher starts the game: sceSystemServiceLoadExec runs the guest shutdown, a render thread that then calls into the stopped AGC driver gets ProcessShutdown back from LibcAwaitExit, and the exception unwinds into title code, which terminates the process before the new executable starts. LibcAwaitExit parks a thread only once an exit has been requested, and LoadExec ran the shutdown without requesting one. It now starts the shutdown the way exit does, so such threads wait until the process is replaced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
256 lines
7.2 KiB
C++
256 lines
7.2 KiB
C++
#include <csignal>
|
|
#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] { LibcExitWithoutHandlers_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);
|
|
}
|
|
|
|
namespace {
|
|
|
|
std::atomic<void (*)()> endFlush{nullptr};
|
|
void (*previousAbortHandler)(int) = SIG_DFL;
|
|
|
|
void FlushOnAbort(int number) {
|
|
LibcRunEndFlush_nid_no_patch();
|
|
if (previousAbortHandler == SIG_IGN) return;
|
|
if (previousAbortHandler != SIG_DFL && previousAbortHandler != SIG_ERR) {
|
|
previousAbortHandler(number);
|
|
return;
|
|
}
|
|
#ifndef _WIN32
|
|
std::signal(number, SIG_DFL);
|
|
std::raise(number);
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
extern "C" void LibcSetEndFlush_nid_no_patch(void (*flush)()) {
|
|
if (flush == nullptr) throw std::runtime_error("libc: invalid end flush registration");
|
|
void (*registered)() = nullptr;
|
|
if (!endFlush.compare_exchange_strong(registered, flush) && registered != flush) throw std::runtime_error("libc: end flush already registered");
|
|
static std::once_flag installed;
|
|
std::call_once(installed, [] { previousAbortHandler = std::signal(SIGABRT, FlushOnAbort); });
|
|
}
|
|
|
|
extern "C" void LibcRunEndFlush_nid_no_patch() {
|
|
if (const auto flush = endFlush.load(std::memory_order_acquire)) flush();
|
|
}
|
|
|
|
namespace {
|
|
|
|
void StartExit() {
|
|
exitRequested.store(true);
|
|
if (exitStarted.exchange(true)) {
|
|
if (!runningExit) LibcAwaitExit_nid_postfix();
|
|
} else {
|
|
runningExit = true;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
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) {
|
|
LibcRunEndFlush_nid_no_patch();
|
|
#ifdef _WIN32
|
|
TerminateProcess(GetCurrentProcess(), static_cast<UINT>(code));
|
|
#endif
|
|
std::_Exit(code);
|
|
}
|
|
|
|
void LibcStartExit_nid_postfix() {
|
|
StartExit();
|
|
}
|
|
|
|
[[noreturn]] void LibcExit_nid_no_patch(int code) {
|
|
StartExit();
|
|
CxaFinalize_nid_no_patch(nullptr);
|
|
LibcRunShutdown_nid_postfix();
|
|
std::exit(code);
|
|
}
|
|
|
|
[[noreturn]] void LibcExitWithoutHandlers_nid_no_patch(int code) {
|
|
StartExit();
|
|
LibcRunShutdown_nid_postfix();
|
|
std::fflush(nullptr);
|
|
LibcTerminate_nid_no_patch(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;
|
|
LibcRunEndFlush_nid_no_patch();
|
|
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;
|
|
}
|
|
|
|
}
|