mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 00:22:57 -05:00
A title's own libc.prx implements __cxa_thread_atexit and the destructor pass of exit() through _sceLibcInternalThreadAtexit and _sceLibcInternalThreadDtors. Its __cxa_finalize calls _sceLibcInternalForceTlsDestructor. Destructors are kept per thread and run newest first, including those registered while they run, as FreeBSD's __cxa_thread_call_dtors does. A null destructor or one outside every loaded image throws instead of being skipped. The first registration installs the pass as the libkernel thread exit hook, so it runs on every guest thread before the thread can be joined. _sceLibcInternalForceTlsDestructor throws. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
88 lines
2.6 KiB
C++
88 lines
2.6 KiB
C++
#include "SceTypes.hpp"
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <stdexcept>
|
|
#include <vector>
|
|
|
|
extern "C" {
|
|
int APS5_VABI scePthreadCreate(Pthread* thread, const PthreadAttr* attr, PthreadEntry entry, void* arg, const char* name);
|
|
int APS5_VABI scePthreadJoin(Pthread thread, void** retval);
|
|
Pthread APS5_VABI scePthreadSelf();
|
|
int APS5_VABI _sceLibcInternalThreadAtexit_nid_postfix(void (APS5_VABI* destructor)(void*), void* object, void* dsoSymbol);
|
|
void APS5_VABI _sceLibcInternalThreadDtors_nid_postfix();
|
|
}
|
|
|
|
namespace {
|
|
|
|
using Destructor = void (APS5_VABI*)(void*);
|
|
|
|
void Require(bool value) { if (!value) std::abort(); }
|
|
|
|
int dsoHandle = 0;
|
|
std::vector<std::intptr_t> calls;
|
|
std::vector<Pthread> callers;
|
|
|
|
void* Object(std::intptr_t value) { return reinterpret_cast<void*>(value); }
|
|
|
|
int Register(Destructor destructor, std::intptr_t object) {
|
|
return _sceLibcInternalThreadAtexit_nid_postfix(destructor, Object(object), &dsoHandle);
|
|
}
|
|
|
|
void APS5_VABI Record(void* object) {
|
|
calls.push_back(reinterpret_cast<std::intptr_t>(object));
|
|
callers.push_back(scePthreadSelf());
|
|
}
|
|
|
|
void APS5_VABI RecordAndRegister(void* object) {
|
|
Record(object);
|
|
Require(Register(Record, 9) == 0);
|
|
}
|
|
|
|
void* APS5_VABI Worker(void*) {
|
|
Require(Register(Record, 4) == 0);
|
|
Require(Register(Record, 5) == 0);
|
|
return nullptr;
|
|
}
|
|
|
|
bool DestructorsThrow() {
|
|
try {
|
|
_sceLibcInternalThreadDtors_nid_postfix();
|
|
} catch (const std::runtime_error&) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
int main() {
|
|
const Pthread mainThread = scePthreadSelf();
|
|
Require(Register(Record, 1) == 0);
|
|
Require(Register(RecordAndRegister, 2) == 0);
|
|
Require(Register(Record, 3) == 0);
|
|
_sceLibcInternalThreadDtors_nid_postfix();
|
|
Require(calls == std::vector<std::intptr_t>{3, 2, 9, 1});
|
|
Require(callers == std::vector<Pthread>(4, mainThread));
|
|
_sceLibcInternalThreadDtors_nid_postfix();
|
|
Require(calls.size() == 4);
|
|
|
|
calls.clear();
|
|
callers.clear();
|
|
Pthread worker = nullptr;
|
|
Require(scePthreadCreate(&worker, nullptr, Worker, nullptr, "ThreadAtexit") == 0);
|
|
Require(scePthreadJoin(worker, nullptr) == 0);
|
|
Require(calls == std::vector<std::intptr_t>{5, 4});
|
|
Require(callers == std::vector<Pthread>(2, worker));
|
|
|
|
calls.clear();
|
|
std::vector<unsigned char> heap(64);
|
|
Require(Register(Record, 6) == 0);
|
|
Require(Register(nullptr, 7) == 0);
|
|
Require(Register(reinterpret_cast<Destructor>(heap.data()), 8) == 0);
|
|
Require(DestructorsThrow());
|
|
Require(DestructorsThrow());
|
|
Require(calls.empty());
|
|
_sceLibcInternalThreadDtors_nid_postfix();
|
|
Require(calls == std::vector<std::intptr_t>{6});
|
|
}
|