Merge pull request #9279 from YossiYad/feat/kernel-sys-thread-defaults

feat(libkernel_sys): implement pthread_set_defaultstacksize_np, scePthreadSetDefaultstacksize and scePthreadSetName
This commit is contained in:
Dean authored and GitHub committed 2026-10-22 19:16:30 +13:00
commit d6eafa54d0
8 files changed
+125 -19

No files matched your search

+7 -1
View File
@@ -309,12 +309,18 @@ if(BUILD_TESTING)
target_link_libraries(guest_kernel_sys_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_kernel_sys_tests)
add_test(NAME guest_kernel_sys COMMAND guest_kernel_sys_tests)
add_test_executable(guest_kernel_sys_thread_defaults_tests tests/GuestKernelSysThreadDefaults.cpp)
target_include_directories(guest_kernel_sys_thread_defaults_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_kernel_sys_thread_defaults_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_kernel_sys_thread_defaults_tests)
add_test(NAME guest_kernel_sys_thread_defaults COMMAND guest_kernel_sys_thread_defaults_tests)
set_tests_properties(guest_kernel_sys_thread_defaults PROPERTIES TIMEOUT 20)
if(Python3_Interpreter_FOUND)
add_test(NAME nid_verneed COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/nid/tests/test_verneed.py $<TARGET_FILE:nid_patcher>)
endif()
if(WIN32)
set_tests_properties(guest_kernel_sys PROPERTIES ENVIRONMENT_MODIFICATION
set_tests_properties(guest_kernel_sys guest_kernel_sys_thread_defaults PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel_sys>")
endif()
if(APPLE)
@@ -182,5 +182,7 @@ std::unique_lock<std::mutex> LockLiveGuestThread(const PthreadPrivate* thread);
bool LiveGuestThreadExists(std::int64_t tid);
bool GetLiveGuestThreadAffinity_nid_no_patch(std::int64_t tid, KernelCpumask& mask);
bool SetLiveGuestThreadAffinity_nid_no_patch(std::int64_t tid, KernelCpumask mask);
extern "C" std::size_t GetDefaultStackSize_nid_no_patch();
extern "C" bool SetDefaultStackSize_nid_no_patch(std::size_t size);
#endif
+13 -1
View File
@@ -12,6 +12,18 @@ static constexpr int DETACH_JOINABLE = 0;
static constexpr int DETACH_DETACHED = 1;
static constexpr int SCHED_FIFO_PS5 = 1;
static std::atomic<std::size_t> defaultStackSize{DEFAULT_STACK_SIZE};
std::size_t GetDefaultStackSize_nid_no_patch() {
return defaultStackSize.load(std::memory_order_relaxed);
}
bool SetDefaultStackSize_nid_no_patch(std::size_t size) {
if (size <= MIN_STACK_SIZE) return false;
defaultStackSize.store(size, std::memory_order_relaxed);
return true;
}
#ifdef _WIN32
#include <windows.h>
#include <limits>
@@ -23,7 +35,7 @@ int APS5_VABI scePthreadAttrInit(PthreadAttr* attr) {
if (!attr) throw std::runtime_error(std::string(__func__) + ": null attr");
auto* p = new (std::nothrow) PthreadAttrPrivate{};
if (!p) return SCE_KERNEL_ERROR_ENOMEM;
p->_stacksize = DEFAULT_STACK_SIZE;
p->_stacksize = GetDefaultStackSize_nid_no_patch();
p->_detachstate = DETACH_JOINABLE;
p->_schedpriority = 700;
p->_schedpolicy = SCHED_FIFO_PS5;
@@ -35,7 +35,6 @@
static constexpr int SCE_OK = 0;
static constexpr std::size_t DEFAULT_STACK_SIZE = 1u << 20;
static constexpr std::size_t HOST_STACK_MARGIN = 1u << 20;
static constexpr int DETACH_DETACHED = 1;
static constexpr std::size_t THREAD_NAME_CAPACITY = 32;
@@ -825,7 +824,7 @@ int APS5_VABI scePthreadCreate(Pthread* thread, const PthreadAttr* attr, Pthread
bool detached = false;
if (attr && *attr) detached = ((*attr)->_detachstate == DETACH_DETACHED);
p->_detached = detached;
p->stackSize = attr ? (*attr)->_stacksize : DEFAULT_STACK_SIZE;
p->stackSize = attr ? (*attr)->_stacksize : GetDefaultStackSize_nid_no_patch();
if (attr) {
if ((*attr)->_inheritsched != GUEST_INHERIT_SCHED)
p->scope = (*attr)->_scope;
@@ -483,11 +483,6 @@ int APS5_VABI pthread_rwlockattr_settype_np_nid_postfix(void) {
return 0;
}
int APS5_VABI pthread_set_defaultstacksize_np_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI pthread_set_user_context_np_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -2428,16 +2423,6 @@ int APS5_VABI scePthreadSetBesteffort(void) {
return 0;
}
int APS5_VABI scePthreadSetDefaultstacksize(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI scePthreadSetName(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI scePthreadSetconcurrency(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
+17
View File
@@ -23,6 +23,7 @@
#include "prx/libkernel/File/include/DirectoryDescriptor.hpp"
#include "prx/libc/include/CreationMask.hpp"
#include "prx/libkernel/File/include/FileFlags.hpp"
#include "prx/libkernel/KernelErrors.hpp"
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
@@ -688,4 +689,20 @@ int APS5_VABI renameat_nid_postfix(int oldfd, const char* from, int newfd, const
return 0;
}
bool SetDefaultStackSize_nid_no_patch(std::size_t size);
void APS5_VABI pthread_set_name_np_nid_postfix(void* thread, const char* name);
int APS5_VABI pthread_set_defaultstacksize_np_nid_postfix(std::size_t size) {
return SetDefaultStackSize_nid_no_patch(size) ? 0 : GuestEinval;
}
int APS5_VABI scePthreadSetDefaultstacksize(std::size_t size) {
return SetDefaultStackSize_nid_no_patch(size) ? 0 : SceKernelError(GuestEinval);
}
void APS5_VABI scePthreadSetName(void* thread, const char* name) {
pthread_set_name_np_nid_postfix(thread, name);
}
}
@@ -0,0 +1,84 @@
#include "SceTypes.hpp"
#include <cstddef>
#include <cstdlib>
#include <cstring>
extern "C" {
int APS5_VABI pthread_set_defaultstacksize_np_nid_postfix(std::size_t size);
int APS5_VABI scePthreadSetDefaultstacksize(std::size_t size);
void APS5_VABI scePthreadSetName(Pthread thread, const char* name);
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(void);
int APS5_VABI scePthreadGetname(Pthread thread, char* name);
int APS5_VABI scePthreadAttrInit(PthreadAttr* attr);
int APS5_VABI scePthreadAttrDestroy(PthreadAttr* attr);
int APS5_VABI scePthreadAttrGet(Pthread thread, PthreadAttr* attr);
int APS5_VABI scePthreadAttrGetstacksize(const PthreadAttr* attr, std::size_t* stackSize);
}
static constexpr int SCE_KERNEL_ERROR_EINVAL = static_cast<int>(0x80020016);
static constexpr int POSIX_EINVAL = 22;
static constexpr std::size_t INITIAL_DEFAULT = 1u << 20;
static void Require(bool value) { if (!value) std::abort(); }
static std::size_t NewAttrStackSize() {
PthreadAttr attr = nullptr;
Require(scePthreadAttrInit(&attr) == 0);
std::size_t size = 0;
Require(scePthreadAttrGetstacksize(&attr, &size) == 0);
Require(scePthreadAttrDestroy(&attr) == 0);
return size;
}
static void* APS5_VABI ReportStackSize(void* out) {
PthreadAttr attr = nullptr;
Require(scePthreadAttrInit(&attr) == 0);
Require(scePthreadAttrGet(scePthreadSelf(), &attr) == 0);
Require(scePthreadAttrGetstacksize(&attr, static_cast<std::size_t*>(out)) == 0);
Require(scePthreadAttrDestroy(&attr) == 0);
return nullptr;
}
static std::size_t DefaultThreadStackSize() {
std::size_t size = 0;
Pthread thread = nullptr;
Require(scePthreadCreate(&thread, nullptr, ReportStackSize, &size, "stack") == 0);
Require(scePthreadJoin(thread, nullptr) == 0);
return size;
}
static void CheckDefaultStackSize() {
Require(NewAttrStackSize() == INITIAL_DEFAULT);
Require(pthread_set_defaultstacksize_np_nid_postfix(0) == POSIX_EINVAL);
Require(pthread_set_defaultstacksize_np_nid_postfix(16384) == POSIX_EINVAL);
Require(scePthreadSetDefaultstacksize(16384) == SCE_KERNEL_ERROR_EINVAL);
Require(NewAttrStackSize() == INITIAL_DEFAULT);
Require(pthread_set_defaultstacksize_np_nid_postfix(16385) == 0);
Require(NewAttrStackSize() == 16385);
Require(scePthreadSetDefaultstacksize(0x40000) == 0);
Require(NewAttrStackSize() == 0x40000);
Require(DefaultThreadStackSize() == 0x40000);
Require(scePthreadSetDefaultstacksize(INITIAL_DEFAULT) == 0);
Require(DefaultThreadStackSize() == INITIAL_DEFAULT);
}
static void CheckSetName() {
char name[32] = {};
scePthreadSetName(scePthreadSelf(), "renamed main");
Require(scePthreadGetname(scePthreadSelf(), name) == 0);
Require(std::strcmp(name, "renamed main") == 0);
scePthreadSetName(scePthreadSelf(), nullptr);
Require(scePthreadGetname(scePthreadSelf(), name) == 0);
Require(name[0] == '\0');
}
int main() {
CheckDefaultStackSize();
CheckSetName();
return 0;
}
+1
View File
@@ -18,3 +18,4 @@
- [fork](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) throws: creating a child process is not supported. PPSA19111 imports it and fails to load without it. Its engine calls `fork` in `Posix_Daemonize`, only with `-daemonize`, and in `Platform_ShellExecute` before `execvp("xdg-open")`, which the main menu's Previews button reaches, so pressing Previews ends in this throw. [setsid](../../../core/libs/prx/libkernel_sys/Export.cpp) fails with `EPERM`, as FreeBSD's does for a process group leader, since the guest is taken to lead its own process group (see `kill`). [umask](../../../core/libs/prx/libkernel_sys/Export.cpp) keeps the guest's mask (initially 022, masked to 07777) and returns the previous one, Linux named shared-memory creation applies it, but ordinary files are still created with the host process's mask
- [chroot](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) follows FreeBSD 11.4's [`sys_chroot`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/vfs_syscalls.c#L776-L807) and [`pwd_chroot`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_descrip.c#L3059-L3085) for a root caller: `EFAULT` for a null path, `ENOENT` for an empty or missing one, `ENAMETOOLONG` for a path of 1024 bytes or more or a component over 255, and `ENOTDIR` for a path that is not a directory or passes through a file. A directory that is the guest root (`/`, `/..`, or `.` after `fchdir` to a descriptor of `/`) succeeds and changes nothing, as the root stays the same directory; the guest is taken never to have changed its root, so open directory descriptors do not make it fail with `EPERM` (`kern.chroot_allow_open_directories` 1). Any other directory throws, naming the path: the guest path resolver has a fixed root and mount aliases (`/app0`, `/savedata0`) that do not move with it. On the console a title starts in a sandbox root, where `pwd_chroot` refuses with `EPERM` while a directory is open; whether that holds for titles, and whether a title holds `PRIV_VFS_CHROOT`, are unverified. Porpoise (PPSA99764) weak-imports it and calls it only as `fchdir` to its saved descriptor of `/` then `chroot(".")`, to put its sandbox root back after a jailbreak daemon replaced it ([`put_root_back`](https://github.com/elripalda/Porpoise-Dolphin-Emulator-for-PS5/blob/2ed1ab5a06995aa198f7bccd4da0f90c3fc29d39/src/porpoise_jailbreak.cpp#L303-L331))
- [fchown](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) - ownership is not modelled: the guest's own owner and group (0) and the unchanged value (-1) are accepted, a descriptor that is not open fails with `EBADF`, and any other owner or group throws, naming it. The check reads the descriptor with `fstat`, ignoring the CRT's invalid-parameter handler on Windows as `DescriptorControl` does. The console's ownership rules are unverified. Morrowind (`mshivam019/morrowind-ps5`) imports it
- [pthread_set_defaultstacksize_np and scePthreadSetDefaultstacksize](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) set the stack size that `scePthreadAttrInit` and a thread created without attributes use, as [fpPS4's `pthread_set_defaultstacksize_np`](https://github.com/red-prig/fpPS4/blob/04cefd4/kernel/ps4_pthread_attr.pas#L89-L102) does: a size of 16384 bytes or less fails with `EINVAL` (`0x80020016` from the sce form) and there is no upper limit, so a default the host cannot allocate fails only when a thread is created. Attributes initialised earlier keep their size. Not checked on a PS5