Merge pull request #4377 from MarcZX100/codex/carry-pthread-atfork-3628
feat(libkernel): implement pthread_atfork registration
This commit is contained in:
commit
40047f46bd
|
|
@ -31,6 +31,7 @@ add_library(libkernel SHARED EXCLUDE_FROM_ALL
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/Pthread/src/Tsd.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Pthread/src/Rwlock.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Pthread/src/Sem.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Pthread/Posix/Atfork.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Pthread/Posix/Attr.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Pthread/Posix/Barrier.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Pthread/Posix/Cond.cpp
|
||||
|
|
@ -651,6 +652,15 @@ if(BUILD_TESTING)
|
|||
set_tests_properties(guest_parent_process PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_test_executable(guest_pthread_atfork_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadAtfork.cpp)
|
||||
target_include_directories(guest_pthread_atfork_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_pthread_atfork_tests PRIVATE libkernel libc)
|
||||
configure_windows_unwind(guest_pthread_atfork_tests)
|
||||
add_test(NAME guest_pthread_atfork COMMAND guest_pthread_atfork_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_pthread_atfork PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_test_executable(guest_kill_tests ${LIBS_INCLUDE_DIR}/tests/GuestKill.cpp)
|
||||
target_include_directories(guest_kill_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_kill_tests PRIVATE libkernel libc)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
#include "../include/Atfork.hpp"
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
|
||||
namespace {
|
||||
constexpr int GuestNoMemory = 12;
|
||||
std::mutex atforkLock;
|
||||
std::vector<PthreadAtforkHandlers> atforkHandlers;
|
||||
}
|
||||
|
||||
extern "C" int APS5_VABI pthread_atfork_nid_postfix(PthreadAtforkHandler prepare, PthreadAtforkHandler parent, PthreadAtforkHandler child) {
|
||||
std::lock_guard lock(atforkLock);
|
||||
try {
|
||||
atforkHandlers.push_back({prepare, parent, child});
|
||||
} catch (const std::bad_alloc&) {
|
||||
return GuestNoMemory;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<PthreadAtforkHandlers> PthreadAtforkHandlers_nid_no_patch() {
|
||||
std::lock_guard lock(atforkLock);
|
||||
return atforkHandlers;
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBKERNEL_PTHREAD_ATFORK_HPP
|
||||
#define CORE_LIBS_PRX_LIBKERNEL_PTHREAD_ATFORK_HPP
|
||||
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <vector>
|
||||
|
||||
using PthreadAtforkHandler = void (APS5_VABI*)();
|
||||
|
||||
struct PthreadAtforkHandlers {
|
||||
PthreadAtforkHandler prepare;
|
||||
PthreadAtforkHandler parent;
|
||||
PthreadAtforkHandler child;
|
||||
};
|
||||
|
||||
extern "C" int APS5_VABI pthread_atfork_nid_postfix(PthreadAtforkHandler prepare, PthreadAtforkHandler parent, PthreadAtforkHandler child);
|
||||
|
||||
std::vector<PthreadAtforkHandlers> PthreadAtforkHandlers_nid_no_patch();
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
#include "prx/libkernel/Pthread/include/Atfork.hpp"
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
|
||||
static std::atomic<int> calls{0};
|
||||
|
||||
static void APS5_VABI Prepare() { ++calls; }
|
||||
static void APS5_VABI Parent() { ++calls; }
|
||||
static void APS5_VABI Child() { ++calls; }
|
||||
|
||||
static bool Same(const PthreadAtforkHandlers& entry, PthreadAtforkHandler prepare, PthreadAtforkHandler parent, PthreadAtforkHandler child) {
|
||||
return entry.prepare == prepare && entry.parent == parent && entry.child == child;
|
||||
}
|
||||
|
||||
int main() {
|
||||
Require(PthreadAtforkHandlers_nid_no_patch().empty());
|
||||
Require(pthread_atfork_nid_postfix(Prepare, Parent, Child) == 0);
|
||||
Require(pthread_atfork_nid_postfix(nullptr, Parent, nullptr) == 0);
|
||||
Require(pthread_atfork_nid_postfix(nullptr, nullptr, nullptr) == 0);
|
||||
Require(pthread_atfork_nid_postfix(Prepare, Parent, Child) == 0);
|
||||
auto handlers = PthreadAtforkHandlers_nid_no_patch();
|
||||
Require(handlers.size() == 4);
|
||||
Require(Same(handlers[0], Prepare, Parent, Child));
|
||||
Require(Same(handlers[1], nullptr, Parent, nullptr));
|
||||
Require(Same(handlers[2], nullptr, nullptr, nullptr));
|
||||
Require(Same(handlers[3], Prepare, Parent, Child));
|
||||
|
||||
constexpr std::size_t threadCount = 4;
|
||||
constexpr std::size_t perThread = 64;
|
||||
std::vector<std::thread> threads;
|
||||
for (std::size_t index = 0; index < threadCount; ++index) {
|
||||
threads.emplace_back([] {
|
||||
for (std::size_t count = 0; count < perThread; ++count) Require(pthread_atfork_nid_postfix(nullptr, nullptr, Child) == 0);
|
||||
});
|
||||
}
|
||||
for (auto& thread : threads) thread.join();
|
||||
handlers = PthreadAtforkHandlers_nid_no_patch();
|
||||
Require(handlers.size() == 4 + threadCount * perThread);
|
||||
Require(Same(handlers[3], Prepare, Parent, Child));
|
||||
for (std::size_t index = 4; index < handlers.size(); ++index) Require(Same(handlers[index], nullptr, nullptr, Child));
|
||||
Require(calls == 0);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -166,5 +166,6 @@
|
|||
- [sceKernelGetEventError](../../../core/libs/prx/libkernel/Equeue/src/EventAccessors.cpp) (libkernel) returns the event's `data` as an `int` when `EV_ERROR` (0x4000) is set in `flags` and 0 otherwise, as [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/947627d10d4ef4b68ba3c0189aab6c368e70a8d2/src/kernel/eventQueue.cpp#L663) does and as FreeBSD 11's [kevent](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/kern/kern_event.c#L1005) reports a failed change (`flags = EV_ERROR`, errno in `data`). [fpPS4](https://github.com/red-prig/fpPS4/blob/04cefd43e6fddd1ab033e7980cd356d14c964905/kernel/ps4_queue.pas#L643) instead returns `fflags` plus `0x80020000` when `fflags` is nonzero. Whether the console reads `data` or `fflags` and returns the raw errno or an SCE error code is unverified. No event delivered by the equeue here carries `EV_ERROR`, so the call returns 0 for every event `sceKernelWaitEqueue` reports
|
||||
- [pthread_cond_reltimedwait_np](../../../core/libs/prx/libkernel/Pthread/Posix/Cond.cpp) (libkernel) takes `(cond, mutex, usec)`, a relative timeout in microseconds, and is `scePthreadCondTimedwait` with POSIX error numbers. FreeBSD 11.4 has no such function; the signature comes only from shadPS4, which registers one function for this NID and for `scePthreadCondTimedwait` ([`condvar.cpp`](https://github.com/shadps4-emu/shadPS4/blob/3a9f57265d28975658f1ca941daa5c879c7a74bd/src/core/libraries/kernel/threads/condvar.cpp#L229-L234)). shadPS4 reads a 64-bit count; here it is the 32-bit `SceKernelUseconds` of the SCE twin, as for `sem_reltimedwait_np`. Unverified on the console
|
||||
- [sysctl kern.arandom](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) serves FreeBSD 11's MIB {1, 37} (CTL_KERN, KERN_ARANDOM) and the "kern.arandom" sysctlbyname lookup through std::random_device, returning min(*oldLength, 256) random bytes. Following FreeBSD's sysctl_kern_arnd, a null buffer reports min(*oldLength, 256) without writing, a null length returns 0 without writing, and requests with a new value return EPERM (1) as the node is read-only. Cryptographic quality depends on the host's random device implementation.
|
||||
- [pthread_atfork](../../../core/libs/prx/libkernel/Pthread/Posix/Atfork.cpp) (libkernel) appends the three handlers to a process-wide list and returns 0, or `ENOMEM` when the entry cannot be allocated, as FreeBSD 11.4's [`_pthread_atfork`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libthr/thread/thr_fork.c#L78-L100) does; null handlers are accepted. The handlers never run: `fork` throws and `vfork` is absent, so no child process is created. Handlers of a module that is unloaded stay in the list, where FreeBSD's `__pthread_cxa_finalize` removes them. PPSA97358 has a source-level call from the constructor `rcu_init` ([rcu.c](https://github.com/ZiZc3/XPSemu/blob/8eec07b4e31f66dce8e0b05c1ca1281af303e933/util/rcu.c#L488-L495)), but current-main runtime reachability is unverified; XPSemu currently stops first at the unresolved `mlockall` import (#4386)
|
||||
- [sceKernelGetPageTableStats](../../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) - reports 2048 page-table entries on each side (a 4 GiB pool of 2 MiB entries, the constants of [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/kernel/memory.cpp)) and all of them available: no entries are counted as used, so a title never sees page-table pressure. KytyPS5 counts the 2 MiB entries of the committed CPU- or GPU-accessible ranges as used, which this port does not do. The four `int*` arguments follow OpenOrbis' `libkernel.h`, the Orbital type database and SharpProspero; a null pointer returns `SCE_KERNEL_ERROR_EFAULT` as in KytyPS5. None of the values is measured on a console. PPSA30601 (Resonance) calls it during start-up
|
||||
- [clock_settime](../../../core/libs/prx/libkernel/Time/Time.cpp) (libkernel) validates like FreeBSD 11.4's [`kern_clock_settime`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_time.c#L396-L412) for a root caller: `EFAULT` for a null time, `EINVAL` for any clock but `CLOCK_REALTIME`, a negative second count or a nanosecond count outside 0 to 999999999. A valid `CLOCK_REALTIME` request throws, naming the requested time, since the host clock is never set and no guest clock offset is kept. FreeBSD checks `PRIV_CLOCK_SETTIME` first, which a jailed process fails with `EPERM`; whether a title holds it on the console is unverified. PS5MC (PPSA99420) imports it; no call site is known
|
||||
|
|
|
|||
Loading…
Reference in New Issue