#include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include #include extern "C" { int APS5_VABI _Mtx_init_nid_postfix(void** handle, int type); void APS5_VABI _Mtx_destroy_nid_postfix(void** handle); int APS5_VABI _Mtx_lock_nid_postfix(void** handle); int APS5_VABI _Mtx_unlock_nid_postfix(void** handle); int APS5_VABI _Cnd_init_nid_postfix(void** handle); void APS5_VABI _Cnd_destroy_nid_postfix(void** handle); int APS5_VABI _Cnd_wait_nid_postfix(void** condition, void** mutex); int APS5_VABI _Cnd_broadcast_nid_postfix(void** handle); int APS5_VABI _Cnd_signal_nid_postfix(void** handle); struct GuestPad { unsigned (APS5_VABI* const* vtable)(GuestPad*); void* condition; void* mutex; bool started; }; void APS5_VABI _ZNSt4_PadC2Ev_nid_postfix(GuestPad* pad); void APS5_VABI _ZNSt4_PadD2Ev_nid_postfix(GuestPad* pad); void APS5_VABI _ZNSt4_Pad8_ReleaseEv_nid_postfix(GuestPad* pad); void APS5_VABI _ZNSt4_Pad7_LaunchEPP7pthread_nid_postfix(GuestPad* pad, void** thread); int APS5_VABI _Thrd_join_nid_postfix(void* thread, int* code); int APS5_VABI _Thrd_detach_nid_postfix(void* thread); void RegisterPthreadCreate_nid_no_patch(int (APS5_VABI* fn)(void**, const void*, void* (APS5_VABI*)(void*), void*)); void RegisterPthreadJoin_nid_no_patch(int (APS5_VABI* fn)(void*, void**)); void RegisterPthreadDetach_nid_no_patch(int (APS5_VABI* fn)(void*)); struct ThreadForwards { int (APS5_VABI* once)(std::int32_t*, void (APS5_VABI*)(void)); int (APS5_VABI* keyCreate)(int*, void (APS5_VABI*)(void*)); int (APS5_VABI* keyDelete)(int); void* (APS5_VABI* getSpecific)(int); int (APS5_VABI* setSpecific)(int, void*); int (APS5_VABI* equal)(void*, void*); void* (APS5_VABI* self)(); void (APS5_VABI* exit)(void*); }; void RegisterThreadForwards_nid_no_patch(const ThreadForwards* forwards); void APS5_VABI _Call_once_nid_postfix(std::int32_t* flag, void (APS5_VABI* function)(void)); int APS5_VABI _Tss_create_nid_postfix(int* key, void (APS5_VABI* destructor)(void*)); int APS5_VABI _Tss_delete_nid_postfix(int key); void* APS5_VABI _Tss_get_nid_postfix(int key); int APS5_VABI _Tss_set_nid_postfix(int key, void* value); int APS5_VABI _Thrd_equal_nid_postfix(void* first, void* second); void* APS5_VABI _Thrd_current_nid_postfix(void); int APS5_VABI _Mtx_current_owns_nid_postfix(void** handle); void* APS5_VABI scePthreadSelf(); } static void Require(bool value) { if (!value) std::abort(); } template static void RequireThrows(TCall call) { try { call(); } catch (const TException&) { return; } std::abort(); } constexpr int success = 0; constexpr int busy = 3; constexpr int plain = 0x01; constexpr int tryable = 0x02; constexpr int timed = 0x04; constexpr int recursive = 0x100; static void* CreateMutex(int type) { void* mutex = reinterpret_cast(std::uintptr_t{1}); Require(_Mtx_init_nid_postfix(&mutex, type) == success && mutex != nullptr); return mutex; } static void MutexBasics() { void* mutex = CreateMutex(tryable); Require(_Mtx_lock_nid_postfix(&mutex) == success); Require(_Mtx_lock_nid_postfix(&mutex) == busy); RequireThrows([&] { _Mtx_destroy_nid_postfix(&mutex); }); std::thread([&] { Require(_Mtx_unlock_nid_postfix(&mutex) == success); }).join(); Require(_Mtx_unlock_nid_postfix(&mutex) == success); Require(_Mtx_unlock_nid_postfix(&mutex) == success); void* invalidMutex = nullptr; RequireThrows([&] { _Mtx_lock_nid_postfix(&invalidMutex); }); void* foreignCondition = nullptr; Require(_Cnd_init_nid_postfix(&foreignCondition) == success && foreignCondition != nullptr); void* foreignMutex = foreignCondition; RequireThrows([&] { _Mtx_lock_nid_postfix(&foreignMutex); }); _Cnd_destroy_nid_postfix(&foreignCondition); _Mtx_destroy_nid_postfix(&mutex); Require(mutex == nullptr); RequireThrows([&] { _Mtx_lock_nid_postfix(&mutex); }); void* emptyMutex = nullptr; _Mtx_destroy_nid_postfix(&emptyMutex); RequireThrows([] { _Mtx_destroy_nid_postfix(nullptr); }); void* plainMutex = CreateMutex(plain); Require(_Mtx_lock_nid_postfix(&plainMutex) == success && _Mtx_lock_nid_postfix(&plainMutex) == busy); Require(_Mtx_unlock_nid_postfix(&plainMutex) == success); _Mtx_destroy_nid_postfix(&plainMutex); void* timedMutex = CreateMutex(timed | tryable); Require(_Mtx_lock_nid_postfix(&timedMutex) == success && _Mtx_lock_nid_postfix(&timedMutex) == busy); Require(_Mtx_unlock_nid_postfix(&timedMutex) == success); _Mtx_destroy_nid_postfix(&timedMutex); void* defaultMutex = nullptr; Require(_Mtx_init_nid_postfix(&defaultMutex, 0) == success && defaultMutex != nullptr); Require(_Mtx_lock_nid_postfix(&defaultMutex) == success && _Mtx_lock_nid_postfix(&defaultMutex) == busy); Require(_Mtx_unlock_nid_postfix(&defaultMutex) == success); _Mtx_destroy_nid_postfix(&defaultMutex); void* unknownBitsMutex = nullptr; Require(_Mtx_init_nid_postfix(&unknownBitsMutex, tryable | 0x8) == success && unknownBitsMutex != nullptr); Require(_Mtx_lock_nid_postfix(&unknownBitsMutex) == success && _Mtx_lock_nid_postfix(&unknownBitsMutex) == busy); Require(_Mtx_unlock_nid_postfix(&unknownBitsMutex) == success); _Mtx_destroy_nid_postfix(&unknownBitsMutex); RequireThrows([] { _Mtx_init_nid_postfix(nullptr, tryable); }); RequireThrows([] { _Mtx_lock_nid_postfix(nullptr); }); RequireThrows([] { _Mtx_unlock_nid_postfix(nullptr); }); } static void RecursiveMutex() { void* mutex = CreateMutex(tryable | recursive); for (int i = 0; i < 3; ++i) Require(_Mtx_lock_nid_postfix(&mutex) == success); std::atomic acquired{false}; std::thread other([&] { Require(_Mtx_lock_nid_postfix(&mutex) == success); acquired = true; Require(_Mtx_unlock_nid_postfix(&mutex) == success); }); for (int i = 0; i < 2; ++i) Require(_Mtx_unlock_nid_postfix(&mutex) == success); std::this_thread::sleep_for(std::chrono::milliseconds(20)); Require(!acquired); Require(_Mtx_unlock_nid_postfix(&mutex) == success); other.join(); Require(acquired); _Mtx_destroy_nid_postfix(&mutex); } static void MutualExclusion() { void* mutex = CreateMutex(tryable); long counter = 0; std::array workers; for (auto& worker : workers) worker = std::thread([&] { for (int i = 0; i < 20000; ++i) { Require(_Mtx_lock_nid_postfix(&mutex) == success); ++counter; Require(_Mtx_unlock_nid_postfix(&mutex) == success); } }); for (auto& worker : workers) worker.join(); Require(counter == 80000); _Mtx_destroy_nid_postfix(&mutex); } static void ConditionBroadcast() { void* condition = reinterpret_cast(std::uintptr_t{1}); Require(_Cnd_init_nid_postfix(&condition) == success && condition != nullptr); void* mutex = CreateMutex(tryable); bool ready = false; int waiting = 0, woken = 0; std::array waiters; for (auto& waiter : waiters) waiter = std::thread([&] { Require(_Mtx_lock_nid_postfix(&mutex) == success); ++waiting; while (!ready) Require(_Cnd_wait_nid_postfix(&condition, &mutex) == success); ++woken; Require(_Mtx_lock_nid_postfix(&mutex) == busy); Require(_Mtx_unlock_nid_postfix(&mutex) == success); }); for (;;) { Require(_Mtx_lock_nid_postfix(&mutex) == success); const bool all = waiting == 2; Require(_Mtx_unlock_nid_postfix(&mutex) == success); if (all) break; std::this_thread::yield(); } Require(_Mtx_lock_nid_postfix(&mutex) == success); ready = true; Require(_Cnd_broadcast_nid_postfix(&condition) == success); Require(_Mtx_unlock_nid_postfix(&mutex) == success); for (auto& waiter : waiters) waiter.join(); Require(woken == 2); Require(_Cnd_broadcast_nid_postfix(&condition) == success); Require(_Cnd_wait_nid_postfix(&condition, &mutex) == success); void* recursiveMutex = CreateMutex(tryable | recursive); Require(_Mtx_lock_nid_postfix(&recursiveMutex) == success && _Mtx_lock_nid_postfix(&recursiveMutex) == success); Require(_Cnd_wait_nid_postfix(&condition, &recursiveMutex) == success); Require(_Mtx_unlock_nid_postfix(&recursiveMutex) == success && _Mtx_unlock_nid_postfix(&recursiveMutex) == success); _Mtx_destroy_nid_postfix(&recursiveMutex); RequireThrows([&] { _Cnd_wait_nid_postfix(&mutex, &mutex); }); void* invalidCondition = nullptr; RequireThrows([&] { _Cnd_broadcast_nid_postfix(&invalidCondition); }); RequireThrows([] { _Cnd_broadcast_nid_postfix(nullptr); }); RequireThrows([] { _Cnd_init_nid_postfix(nullptr); }); _Cnd_destroy_nid_postfix(&condition); Require(condition == nullptr); RequireThrows([&] { _Cnd_broadcast_nid_postfix(&condition); }); void* emptyCondition = nullptr; _Cnd_destroy_nid_postfix(&emptyCondition); RequireThrows([] { _Cnd_destroy_nid_postfix(nullptr); }); _Mtx_destroy_nid_postfix(&mutex); } static void ConditionSignal() { void* condition = nullptr; Require(_Cnd_init_nid_postfix(&condition) == success); Require(_Cnd_signal_nid_postfix(&condition) == success); _Cnd_destroy_nid_postfix(&condition); } struct TestPad { unsigned (APS5_VABI* const* vtable)(GuestPad*); void* condition; void* mutex; bool started; std::atomic ran{false}; }; struct HostPadThread { std::thread worker; void* result = nullptr; }; static int APS5_VABI HostPthreadCreate(void** thread, const void*, void* (APS5_VABI* entry)(void*), void* arg) { auto* handle = new HostPadThread{}; handle->worker = std::thread([handle, entry, arg] { handle->result = entry(arg); }); *thread = handle; return 0; } static int APS5_VABI HostPthreadJoin(void* thread, void** value) { if (!thread) return 22; auto* handle = static_cast(thread); handle->worker.join(); if (value) *value = handle->result; delete handle; return 0; } static int APS5_VABI HostPthreadDetach(void* thread) { if (!thread) return 22; static_cast(thread)->worker.detach(); return 0; } static unsigned APS5_VABI TestPadGo(GuestPad* base) { auto* pad = reinterpret_cast(base); pad->ran.store(true); _ZNSt4_Pad8_ReleaseEv_nid_postfix(base); return 42; } static void PadLaunchAndJoin() { unsigned (APS5_VABI* vtable[1])(GuestPad*) = {TestPadGo}; TestPad unregPad{}; unregPad.vtable = vtable; _ZNSt4_PadC2Ev_nid_postfix(reinterpret_cast(&unregPad)); RegisterPthreadCreate_nid_no_patch(nullptr); RegisterPthreadJoin_nid_no_patch(nullptr); void* unregThread = nullptr; RequireThrows([&] { _ZNSt4_Pad7_LaunchEPP7pthread_nid_postfix(reinterpret_cast(&unregPad), &unregThread); }); RequireThrows([&] { int code = 0; _Thrd_join_nid_postfix(&unregPad, &code); }); _ZNSt4_PadD2Ev_nid_postfix(reinterpret_cast(&unregPad)); RegisterPthreadCreate_nid_no_patch(HostPthreadCreate); RegisterPthreadJoin_nid_no_patch(HostPthreadJoin); TestPad pad{}; pad.vtable = vtable; _ZNSt4_PadC2Ev_nid_postfix(reinterpret_cast(&pad)); void* thread = nullptr; _ZNSt4_Pad7_LaunchEPP7pthread_nid_postfix(reinterpret_cast(&pad), &thread); Require(thread != nullptr && pad.started && pad.ran.load()); _ZNSt4_PadD2Ev_nid_postfix(reinterpret_cast(&pad)); int code = 0; Require(_Thrd_join_nid_postfix(thread, &code) == success && code == 42); Require(_Thrd_join_nid_postfix(nullptr, &code) == 4); RegisterPthreadCreate_nid_no_patch(nullptr); RegisterPthreadJoin_nid_no_patch(nullptr); } static void PadDetach() { unsigned (APS5_VABI* vtable[1])(GuestPad*) = {TestPadGo}; RegisterPthreadDetach_nid_no_patch(nullptr); RequireThrows([] { _Thrd_detach_nid_postfix(nullptr); }); RegisterPthreadCreate_nid_no_patch(HostPthreadCreate); RegisterPthreadDetach_nid_no_patch(HostPthreadDetach); TestPad pad{}; pad.vtable = vtable; _ZNSt4_PadC2Ev_nid_postfix(reinterpret_cast(&pad)); void* thread = nullptr; _ZNSt4_Pad7_LaunchEPP7pthread_nid_postfix(reinterpret_cast(&pad), &thread); Require(thread != nullptr && pad.ran.load()); _ZNSt4_PadD2Ev_nid_postfix(reinterpret_cast(&pad)); Require(_Thrd_detach_nid_postfix(thread) == success); Require(_Thrd_detach_nid_postfix(nullptr) == 4); RegisterPthreadCreate_nid_no_patch(nullptr); RegisterPthreadDetach_nid_no_patch(nullptr); } static void KernelKeys() { Require(_Thrd_current_nid_postfix() == scePthreadSelf()); int key = -1; Require(_Tss_create_nid_postfix(&key, nullptr) == success && key >= 0); int value = 0; Require(_Tss_get_nid_postfix(key) == nullptr); Require(_Tss_set_nid_postfix(key, &value) == success && _Tss_get_nid_postfix(key) == &value); Require(_Tss_delete_nid_postfix(key) == success); } struct ForwardLog { std::int32_t* flag; void (APS5_VABI* function)(void); int key; void* value; void* first; void* second; }; static ForwardLog forwardLog; static int APS5_VABI FakeOnce(std::int32_t* flag, void (APS5_VABI* function)(void)) { forwardLog.flag = flag; forwardLog.function = function; return 7; } static int APS5_VABI FakeKeyCreate(int* key, void (APS5_VABI*)(void*)) { *key = 41; return 0x80020016; } static int APS5_VABI FakeKeyDelete(int key) { forwardLog.key = key; return 0x80020002; } static void* APS5_VABI FakeGetSpecific(int key) { forwardLog.key = key; return &forwardLog; } static int APS5_VABI FakeSetSpecific(int key, void* value) { forwardLog.key = key; forwardLog.value = value; return 0; } static int APS5_VABI FakeEqual(void* first, void* second) { forwardLog.first = first; forwardLog.second = second; return 1; } static void* APS5_VABI FakeSelf() { return &forwardLog.key; } static void APS5_VABI FakeExit(void*) { std::abort(); } static void APS5_VABI OnceTarget() {} static void ForwardedCalls() { static const ThreadForwards fakes{FakeOnce, FakeKeyCreate, FakeKeyDelete, FakeGetSpecific, FakeSetSpecific, FakeEqual, FakeSelf, FakeExit}; RegisterThreadForwards_nid_no_patch(&fakes); std::int32_t flag = 0; _Call_once_nid_postfix(&flag, OnceTarget); Require(forwardLog.flag == &flag && forwardLog.function == OnceTarget); int key = 0; Require(_Tss_create_nid_postfix(&key, nullptr) == static_cast(0x80020016) && key == 41); Require(_Tss_delete_nid_postfix(5) == static_cast(0x80020002) && forwardLog.key == 5); Require(_Tss_get_nid_postfix(6) == &forwardLog && forwardLog.key == 6); Require(_Tss_set_nid_postfix(8, &flag) == success && forwardLog.key == 8 && forwardLog.value == &flag); Require(_Thrd_equal_nid_postfix(&flag, &key) == 1 && forwardLog.first == &flag && forwardLog.second == &key); Require(_Thrd_current_nid_postfix() == &forwardLog.key); Require(_Mtx_current_owns_nid_postfix(nullptr) == 1); RegisterThreadForwards_nid_no_patch(nullptr); RequireThrows([] { _Thrd_current_nid_postfix(); }); RequireThrows([] { _Tss_get_nid_postfix(0); }); } int main() { KernelKeys(); ForwardedCalls(); MutexBasics(); RecursiveMutex(); MutualExclusion(); ConditionBroadcast(); ConditionSignal(); PadLaunchAndJoin(); PadDetach(); }