AnyPS5/docs/dev/debt/libkernel.md

78 KiB

libkernel technical debt

Build

  • winpthreads initializes a statically initialized rwlock on its first lock and fails a concurrent first lock with EINVAL, which std::shared_mutex and std::shared_timed_mutex ignore, so that lock does not exclude. Guest rwlocks (Pthread.hpp) and the host std::shared_mutex objects in Recompiler.cpp are locked once when created. A new host std::shared_mutex used on Windows needs the same.

Silent stubs

  • scePthreadMutexattrSetprotocol (libkernel) - accepts 0 to 2 as a PS5 (firmware 13.60) does and returns EINVAL otherwise, but the protocol is not stored or applied: priority inheritance and protection are not modelled. On the console a null or uninitialized mutex attribute returns EINVAL from the SCE attribute calls, scePthreadMutexInit takes an uninitialized attribute as the default one, and a destroyed mutex returns EINVAL from scePthreadMutexDestroy; a null mutex pointer crashed the payload and still throws
  • pthread_mutexattr_setprotocol, pthread_mutexattr_setprioceiling, pthread_mutexattr_getprioceiling (libkernel) - the protocol and the priority ceiling are stored and reported as in FreeBSD's libthr (the ceiling is set or read only under PTHREAD_PRIO_PROTECT, and setting a protocol resets it to the highest SCHED_RR priority), but mutexes do not apply them: a priority-inherit or priority-protect mutex locks like a normal one and does not change its owner's priority
  • sceKernelGetMainSocId and sceKernelGetCpumode (libkernel) both return 0, and sceKernelGetCpuFrequency returns the host TSC frequency measured for sceKernelGetTscFrequency, not the console CPU clock. The PS5's SoC id and CPU mode are unknown; shadPS4's PS4 values are 0x710f10/0x740f30 and 0, 2 or 5 from the param.sfo CPU-mode flags. PPSA99203 only logs them ([console] main SoC id: result=%#x out=%#x, CPU: %llu Hz, cpumode %d), passing a pointer that sceKernelGetMainSocId leaves untouched
  • sceKernelEnableDmemAliasing (libkernel) returns 0 and changes nothing: direct memory can always be mapped more than once here, as DoMapDirect in DirectMemory.cpp only checks that the physical pages are allocated, so there is no aliasing to turn on. shadPS4 (PS4) sets a flag there that lifts the EBUSY its sceKernelMapDirectMemory2, and sceKernelMapDirectMemory for SDK 2.50 and later, return for direct memory that is already mapped; that check is not applied here. The no-argument int signature follows shadPS4 and ProsperoEden (PPSA99008), which calls it before mapping the same direct memory again. Whether the PS5 refuses such a mapping without the call is unverified

Unknown function info

  • getlogin_r (libkernel) returns the host effective account name: GetUserNameW as UTF-8 on Windows and getpwuid_r(geteuid()) on POSIX. A valid account returns a name even without a login session; host getlogin_r, USER and LOGNAME are not consulted. The console's login name and behaviour without a session are unknown. Account lookup or encoding failures, a missing or empty account name, and POSIX account records exceeding the 16 KiB lookup buffer throw; there is no replacement name or retry. A short guest buffer returns ERANGE without modifying it, and a null output returns EFAULT.
  • sceCoredumpGetThreadContextInfo (libkernel, NID ShChva57wIM) - declared as a throwing stub. The pointer and size arguments and integer return type follow SharpProspero's PS5 bindings. The context structure and console behavior are unverified; the stub does not read or write caller memory. PPSA01736 imports it
  • sceKernelGetSystemSwVersion (libkernel) reports baseline system software version 0x01000000 ("01.000.000") for ProsperoStore (PPSA99000). The 0x28-byte layout (uint64_t size at 0, versionString at 8 and version at 0x24) matches ProsperoStore's caller, shadPS4, fpPS4 and OpenOrbis. These sources support the structure layout, but not the reported version value: the console firmware version remains unverified. The checks are unverified too: a null pointer returns SCE_KERNEL_ERROR_EFAULT and a size other than 0x28 returns SCE_KERNEL_ERROR_EINVAL, while shadPS4 returns success for null and never reads size.
  • kern.rng_pseudo (libkernel) - sysctlbyname fills the whole output buffer with bytes from the host's std::random_device and leaves the length unchanged; a write returns -1 with EPERM and a call without an output buffer throws. The only caller seen is mbedTLS's mbedtls_hardware_poll in the catalog's ProsperoLight boilerplate, which asks for 64-byte blocks and fails if fewer come back. The console's generator, its per-call maximum and its answer to a size query are unknown
  • kern.sdk_version (libkernel) - sysctlbyname reports the system software version as the 4-byte value 0x01000000 (1.00), the value sceKernelGetSystemSwVersion reports; a write returns -1 with EPERM. The major version is byte 3 and the minor version byte 2, in hex digits that read as decimal (13.60 is 0x1360xxxx), as Capy Mobile (PPSA92280), remote_lua_loader and the lapse payload read it. Capy Mobile only displays it. The console's firmware version is not modeled and the low two bytes are unknown
  • sceKernelGetProsperoSystemSwVersion (libkernel) reports system software version 0x01000000 ("01.000.000"); the console's firmware version is not modeled. The 0x28-byte block (a 64-bit size at 0, the version string at 8 and the packed version at 0x24) follows SharpProspero, which Prospero Multi Tools (PPSA99110) and Prospero Explorer (PPSA99109) are built with and which sets the size to 40, and the same fields in etaHEN and shsrv. A size other than 0x28 returns SCE_KERNEL_ERROR_EINVAL without writing; whether the console checks the size is unverified, since etaHEN and shsrv declare a 0x30-byte block and do not set it
  • sceKernelGetAllowedSdkVersionOnSystem (libkernel) reports the system software version with the 12 patch bits set, 0x01000fff, as shadPS4 does on the PS4 and as SharpProspero describes it, which Prospero Explorer (PPSA99109) reads it through; a null pointer returns SCE_KERNEL_ERROR_EINVAL. The console's value and its errors are unverified
  • getuid, geteuid, getgid, getegid and issetugid (libkernel) report the root identity (0) and a process that never changed credentials (issetugid 0), as a jailbroken process has; the host user and group are not exposed to the guest. The values a title sees on a console are unverified
  • sceKernelGetAppInfo (libkernel) - only app_id and the title ID (offset 0x10, up to 9 characters plus a terminator) are written; callers declare the structure with different sizes, so the rest is left alone. app_id is 1, the running title's id in libSceSystemService, which is a guess, not something measured. The error codes follow shadPS4: EPERM for another process, and 0 without writing anything for a null pointer.
  • AMPR object layout and compact gathers - the 24-byte object and offsets (type 0, cursor 4, count 8, capacity 12, pointer 16) follow ampr_emu assertions and its C declaration. AnyPS5 represents the 32-bit type word as low 16-bit type and high 16-bit recording flags. Command encoding is AnyPS5 internal: opcode 29 and three 32-bit words encode the high offset byte, length minus one and low offset word. The 12-byte size reduces HFW's 1 MiB buffer pressure; it is not a claim about console opcode encoding. Existing 40-bit offsets and lengths 1 through 2^32 are retained; console behavior outside those limits is unverified.
  • APR resolve ForEach result (libkernel) - sceKernelAprResolveFilepathsToIdsAndFileSizesForEach returns the number of paths that resolved, with the per-path status in results and the failed ids/sizes set to the invalid id and 0. The ampr_emu bridge passes the count of its resolver through and does not show what is counted; counting successes is not confirmed on a title. The guest errno of the failing non-ForEach resolves is set through __error_nid_postfix
  • AMPR counter access (libSceAmpr) - signatures and checks of WaitOnCounter_04_00, WriteCounter_04_00, ConstructNop and ConstructMarker taken from the ampr_emu reimplementation, which hands counter commands to the console and does not show their effect. The counters are taken as 32 bits each: the 8-byte access of counter n spans n (low) and n + 1 (high), the 2- and 1-byte accesses select a half or a byte of the counter by its offset, waits compare at the access width (signed and wrapped compares included), the AND mask applies to both sides and the atomic writes wrap within the access. The ConstructNop type is not recorded
  • APR gather and scatter reads (libSceAmpr) - signatures, argument checks and the read cursor taken from the ampr_emu reimplementation, not confirmed on a title: each read leaves the file, the next file offset and the next destination; a gather reads that file at a new offset into the next destination, a scatter continues in the file into a new destination, a gather-scatter takes both. Recording one needs a read file since the last ResetGatherScatterState; like ampr_emu, sceAmprCommandBufferReset keeps that state, and a gather or scatter with no read before it in the submitted buffer throws instead of returning SCE_AMPR_ERROR_APR_INVALIDGATHERSCATTERSTATE
  • AMM map, map direct and unmap (libSceAmpr) - signatures and the protection mask (CPU, GPU, AMPR and ACP read and write) taken from the ampr_emu reimplementation, which hands the remaining checks and the mapping to the console kernel; the mapper model taken from PS5PCEM: sceAmprAmmGiveDirectMemory usage 1 gives the allocated direct memory to the pool plain maps take pages from, usage 0 only allocates it. Not confirmed on a title. The AMM virtual address ranges are a 32 GiB range and a 32 GiB multimap range reserved on first use at the top of the guest arena, above the addresses that maps without MAP_FIXED are given lowest first and that titles derive their next hints from; the console's addresses are not known (ampr_emu takes them from sceKernelMapperGetParam), and a reservation that cannot be made there throws. Maps outside them throw. Addresses, sizes and direct offsets must be 16 KiB aligned. The memory type and the GPU mask are ignored, AMPR and ACP access maps as CPU access, unmapped pages go back to the pool and stay reserved. Submission runs the buffer before returning, the submit result is always 0 and a failing command throws; the AMM measures return errors sign-extended, as ampr_emu does. sceAmprAmmGetVirtualAddressRanges throws for a null output
  • AMM remap, multimap and protection changes (libSceAmpr) - signatures and the protection and mask checks taken from ampr_emu; the effects are inferred from the names, not confirmed on a title. A remap moves every page of a fully mapped range to the new address with the given protection and leaves the old range reserved; a multimap maps the same pages at a second address, anywhere in the AMM ranges, and a pool page goes back to the pool once its last mapping is unmapped; a multimap onto its own source range throws. A protection change sets the masked bits on every page of a fully mapped range; the memory type is recorded and ignored. A command on a range that is not fully mapped throws
  • AMM PRT ranges (libSceAmpr) - signatures, checks and their order taken from ampr_emu, where MapAsPrt is a map with the PRT flag and protection 0 and AllocatePaForPrt a memory type and protection change with mask 1019, both checking the buffer memory after its size; the effects are inferred from the names, not confirmed on a title. Unbacked PRT pages are zero-filled pages with GPU read access, so CPU reads see zeros instead of faulting and GPU writes fault instead of being dropped. AllocatePaForPrt backs each unbacked page with a pool page and sets the type and protection of backed ones; RemapIntoPrt moves a fully mapped range into a PRT range, its opcode argument (1011 when 0, as in ampr_emu) is recorded and ignored; UnmapToPrt returns the pages to the pool and zero-fills them again. A plain map, unmap or remap over a PRT range ends it; AllocatePaForPrt, RemapIntoPrt or UnmapToPrt outside a PRT range throws
  • AMM kernel write commands (libkernel) - signatures of sceKernelWriteModifyMtypeProtectCommand and sceKernelWriteRemapIntoPrtCommand taken from ampr_emu's libkernel hook targets, which forward to the real kernel; the measure mode (dst null reports the size, outSize null skips the report) follows ampr_emu's measure-then-record calls, not confirmed on a title. The range, protection and mask checks mirror the libSceAmpr siblings, and the recorded bytes match them exactly so the submitted buffer executes on the existing paths
  • sceCoredumpWriteUserString and sceSaveDataGetConvertProgress - imported by PPSA16064, not called during its start-up and added without an implementation; unknown signatures
  • sceKernelInternalMemoryGetModuleSegmentInfo (libkernel) - unknown signature
  • sceKernelGetModuleInfoForUnwind (libkernel) - on Linux the tables come from sceKernelGetModuleInfoFromAddr, whose eh_frame_size includes the zero terminator, as measured on firmware 13.60 (reported in #2419, not reproduced here); on Windows it is assumed to stop before the terminator. LLVM libunwind's CFI_Parser::findFDE stops at the terminator or the length, whichever comes first, so it finds the same FDEs either way. Only the main executable and .guest.prx images report tables; other host images report none, as on Windows
  • sceKernelGetModuleInfoFromAddr (libkernel) - the 0x1A8-byte info layout follows the shadPS4 reimplementation; a title's libc.prx reads only id (PPSA14632). The meaning of flags is unknown; only 2 is accepted. The input st_size is ignored, because PPSA21703's libc.prx passes an uninitialized 0x1A8-byte buffer; the full size is written back. The id is the sceKernelLoadStartModule handle of an image opened through the loader and otherwise a stable id from the same range. The name is the host file name without .guest.prx. An image with more than 4 load segments (the converted executable has 7) reports its first 4. tls_offset is 0 and ref_count is 1, as the host loader exposes neither. On Windows the module is the PE image holding the address: a relinked image reports its .elf sections as segments and the eh_frame_hdr recorded in .ehmeta, any other image its first 4 sections with memory access and the .ehfram section without an eh_frame_hdr; the TLS fields and init_proc_addr/fini_proc_addr are 0
  • sceKernelGetModuleList (libkernel) - lists the main program and every loaded .prx image in load order; host runtime libraries are left out. The handles are the ids sceKernelGetModuleInfoFromAddr reports, not the console's. The (int32_t* handles, size_t count, size_t* actual) signature and returning ENOMEM without writing actual after filling the first count handles when more images are loaded follow shadPS4; whether the console writes any handles in that case is unknown
  • sceKernelGetModuleInfo (libkernel) - the 0x160-byte layout (st_size, name[256], 4 segments, segment_count, fingerprint[20]) follows fpPS4 and shadPS4. Only that st_size is accepted, as in shadPS4; other sizes return EINVAL, a null pointer EFAULT and a handle sceKernelGetModuleList does not list ESRCH. Name and segments are the ones sceKernelGetModuleInfoFromAddr reports, on Windows from the same PE sections, so prot uses the read/write/execute bits 1/2/4, while fpPS4 and shadPS4 store the ELF p_flags. The fingerprint is left zero, as the host image does not carry it
  • sceKernelGetModuleList2 and sceKernelGetModuleInfo2 (libkernel) - the title-module variants follow shadPS4: sceKernelGetModuleList2 lists what sceKernelGetModuleList lists without system libraries, and sceKernelGetModuleInfo2 answers EPERM for a system library after the EFAULT, EINVAL and ESRCH checks of sceKernelGetModuleInfo. A system library here is a loaded .prx image that is not a relinked title module (.guest.prx); the main program and the title's own modules are listed. Not verified on the console. PPSA34063 imports both and fails to load without them
  • sceKernelReleaseFlexibleMemory (libkernel) - unmaps the range like sceKernelMunmap, as fpPS4 and prosper do; whether the console keeps the virtual range reserved after releasing its pages is unverified
  • sceKernelAvailableFlexibleMemorySize (libkernel) - the segments of the title's executable and of its .guest.prx modules count against the 448 MiB flexible budget while they are loaded, for this size and for the flexible maps, as shadPS4 maps every segment of a module outside its system module directory as flexible memory on the PS4. Counted are the segments the title's ELF gives permissions, each rounded to 16 KiB pages; not counted are segments without permissions, the relinker's own blocks (on Linux both are the read/write/execute PT_LOADs, plus the header block at file offset 0; on Windows everything outside the .elf sections) and the host system libraries. The Linux image carries no marker for the relinker's blocks, so they are told apart by those flags and that offset only: a title segment that is writable and executable, or starts at file offset 0, is not counted. A module whose segments exceed what is left still loads, and the available size then reads 0; shadPS4 fails the segment map instead (memory.cpp returns EINVAL, module.cpp asserts). Which segments the PS5 charges, its rounding, whether system modules use the title's budget and when an unload releases the charge are not measured. PPSA15508 subtracts the reported size and a 10 MiB module allowance from its own 448 MiB budget at start-up and stops when the result exceeds its 146 MiB code and data limit (#2883)
  • Memory protection bits 0x100 and 0x200 (libkernel) - taken as audio coprocessor (ACP) read and write, as in the KytyPS5 and etaHEN headers; the coprocessor work runs on the host CPU, so they grant host read and read/write access like the GPU bits
  • _umtx_op (libkernel) - UMTX_OP_WAIT, UMTX_OP_WAKE, UMTX_OP_WAIT_UINT, UMTX_OP_WAIT_UINT_PRIVATE, UMTX_OP_WAKE_PRIVATE and UMTX_OP_NWAKE_PRIVATE follow FreeBSD 11's kern_umtx.c: a timeout whose size argument is at most 16 is a relative timespec, a larger one an _umtx_time, and a wake of 0 or fewer wakes one waiter. UMTX_OP_NWAKE_PRIVATE reads the value as an int count of pointers at the object and wakes every waiter on each one; a count of 0 or fewer reads nothing, and a null list fails with EFAULT, where FreeBSD's copyin would also fail for any other unreadable list. Waiters share the queue of sceKernelSyncOnAddressWait. Operation 0 fails with EOPNOTSUPP and operations above 26 with EINVAL, as in FreeBSD 11; UMTX_OP_WAIT_UINT with a value that does not fit in 32 bits returns 0 at once, as FreeBSD's zero-extended compare does. Operation 1 throws, since PPSA12544's call_once uses it in a way FreeBSD rejects; the other operations throw, and so does a misaligned object. PPSA12544's libc.prx passes UMTX_OP_WAIT and UMTX_OP_WAIT_UINT a pointer to a 4-byte microsecond timeout with size argument 0, which FreeBSD reads as a timespec, and waits for a running call_once with operation 1; its PS5Util.prx passes a timespec. Whether the console reads a microsecond timeout there, and what operation 1 does on it, is unverified. A libc call cannot be told apart from a timespec call by the size argument alone, so a timeout with size argument 0 is read as a timespec and only throws when it is not a valid one (negative seconds, nanoseconds outside [0, 1e9)), which is where FreeBSD would return EINVAL; a microsecond value that happens to read as a valid timespec is waited on as one
  • sceKernelSyncOnAddressWait (libkernel) - the only known caller passes a null timeout and a name string as the fourth argument; the timeout is assumed to point to microseconds like the other kernel waits, and the name is ignored
  • sceKernelSyncOnAddressWait8 / sceKernelSyncOnAddressWait16 / sceKernelSyncOnAddressWait32 / sceKernelSyncOnAddressWait64 (libkernel) - libc's __std_atomic_wait_direct_8/16/32/64 pass a microsecond timeout or null and ignore the result; the by-value comparand, return values and the 2- and 8-byte alignment of Wait16 and Wait64 are assumed
  • scePthreadRwlockDestroy, pthread_rwlock_destroy (libkernel) - measured on a PS5 (firmware 13.60): destroying a rwlock returns 0 whether it is free or held for reading or writing, and leaves 1 in the handle; every call on that handle then returns EINVAL until it is initialized again, and a zero handle is initialized on first use by the scePthreadRwlock calls as by the POSIX ones. A rwlock destroyed while held is not freed, so its memory leaks. Destroying a rwlock while another thread is acquiring it is not guarded
  • scePthreadCondWait / scePthreadCondTimedwait (libkernel) - before waiting, FreeBSD libthr's cond_wait_common checks ownership through _mutex_owned: an unowned mutex returns EPERM, while its destroyed marker returns EINVAL. An untouched adaptive static initializer (slot value 1) therefore returns EPERM; the test covers this, an uninitialized slot, a mutex owned by another thread, and the destroyed marker for both timed and untimed sce and POSIX waits. A recursive mutex locked several times is released completely for the wait and relocked to the same depth afterwards, as FreeBSD libthr does (_mutex_cv_unlock, _mutex_cv_lock). These FreeBSD-derived behaviors remain unverified on console hardware
  • sceKernelAioSubmitReadCommandsMultiple / sceKernelAioSubmitWriteCommandsMultiple / sceKernelAioWaitRequests / sceKernelAioPollRequests / sceKernelAioCancelRequest / sceKernelAioCancelRequests / sceKernelAioDeleteRequests (libkernel) - behaviour from shadPS4 (one id per request for the Multiple submits, cancel turns any request aborted and id 0 reports processing, wait mode 2 returns once one request completed, poll copies every state without waiting); the 128 requests or ids per batch limit is from SharpProspero, with an unknown error, so larger batches throw; wait modes other than 1 (and) and 2 (or) throw; null pointers, negative counts and invalid ids return the codes the single-request siblings use, checked before any state is written
  • scePthreadAttr setters and getters (libkernel) - measured on a PS5 (firmware 13.60): a null or uninitialized attribute returns EINVAL, as does a null output of the stack size, detach state and scheduling parameter getters; a policy other than 1 and 3, an inheritance other than 0 and 4, or a priority outside 256 to 767 (or a null parameter) returns ENOTSUP (0x8002002D); a detach state other than 0 and 1 and a stack size below 16384 return EINVAL; any affinity mask is accepted. A null attribute pointer to scePthreadAttrInit and a null output of scePthreadAttrGetaffinity crashed the payload, so they still throw. A new attribute holds a 64 KiB stack size on the console and 1 MiB here, which the host code that runs on guest threads needs
  • sceKernelGetAvailableCpumask (libkernel) - returns the default thread affinity, 0x1FFF (CPUs 0 to 12), as KytyPS5 does. Titles pin threads within it (PPSA02664 passes 0x1FFB, Hades II PPSA36082 0x3 and 0x3F); whether CPU 13 is available to a title is not confirmed
  • sceKernelGetCurrentCpu (libkernel) - guest affinities are not applied to host threads, so the host CPU index is mapped into the calling thread's affinity clipped to sceKernelGetAvailableCpumask (the available mask when they do not overlap): host CPU n reports the (n modulo the CPU count)-th CPU of that mask, so a thread pinned to one CPU reports that CPU, as a FreeBSD cpuset guarantees on the console. Which CPU the console reports for a thread allowed on several is not known; shadPS4 returns 0
  • pthread_attr_setsolosched_np (libkernel) takes (attr, int) and stores the value through scePthreadAttrSetsolosched, returning EINVAL (22) for a null or uninitialized attribute like the other POSIX attribute setters. Only the name is public (aerolib 2+pVfgiEd7A); the signature and behaviour follow the SCE twin, which KytyPS5 and sharpemu store as an int without a range check, and PS5PCEM implements this alias over it. On a PS5 (firmware 13.60) only 0 and 16 are accepted; every other value returns ENOTSUP (45, 0x8002002D from the SCE call) and keeps the stored one, and the SCE calls return EINVAL for a null or uninitialized attribute. PPSA15878 passes 16. Threads created from the attribute are not scheduled differently; what solo scheduling does on the console is unknown. Returnal (PPSA01285 v03.000.005) stopped on it with what(): pthread_attr_setsolosched_np_nid_postfix not implemented (#2872, #1155); LEGO Horizon Adventures (PPSA14632, #734) and Palworld (PPSA23126, #2598) import it
  • sceKernelMapNamedFlexibleMemoryInternal (libkernel) - flag 0x8000 is accepted and ignored; its meaning is not established. KytyPS5 (GUEST_MAP_UNKNOWN_8000) and sharpemu accept it without reading it. Every known caller is the SDK 9 libc.prx heap (Astro Bot PPSA21564, PPSA22520), which passes it with read/write protection and uses the mapping as an older libc.prx uses sceKernelMapNamedFlexibleMemory with flags 0
  • sceKernelMtypeprotect (libkernel) - sets the memory type of the direct mappings in the page-rounded range and of the direct memory behind them, as shadPS4 and prosper do (KytyPS5 ignores the type); the type is not validated, flexible pages in the range only get the protection, and a later map of the retyped direct memory reports the type it was allocated with. sceKernelBatchMap2 operation 4 takes the same path with the entry type
  • Map hints inside a reservation (libkernel) - sceKernelMapDirectMemory and the flexible and anonymous maps place a mapping without MAP_FIXED at its hint address when the whole range lies in a range still reserved by sceKernelReserveVirtualRange, as if MAP_FIXED were set. Taken from PPSA01474, which reserves 64 GiB and then maps its heaps inside it with hints and no MAP_FIXED, and uses the hint addresses afterwards; not measured on the console. Hints outside a reservation, or over a range that is partly mapped, stay hints
  • _sceKernelRtldThreadAtexitIncrement, _sceKernelRtldThreadAtexitDecrement (libkernel) - the argument is the dso address a title's libc.prx passes to __cxa_thread_atexit (PPSA14632), which fails when Increment returns nonzero. As in the fpPS4 reimplementation, Increment takes a reference on the loaded image containing the address and Decrement releases one; the variants without the leading underscore are assumed to match
  • tcgetattr, tcsetattr (libkernel) - every file and pipe descriptor is taken as not a terminal (ENOTTY), and a socket gets EADDRNOTAVAIL for AF_INET and EOPNOTSUPP otherwise, as FreeBSD's in_control and pru_control_notsupp answer a termios ioctl; what descriptors 0 to 2 of a title are on the console, and whether any of them is a terminal there, is unknown
  • select (libkernel) - descriptor 0 is never reported readable and descriptors 1 and 2 are always writable, as for a console title with no input; reads from descriptor 0 still go to the host stdin. /dev/random and /dev/urandom are always readable and writable, as FreeBSD reports a seeded random device. Other descriptors that are not libSceNet sockets throw. Dungeons (PPSA16064) selects on descriptor 0
  • select (libkernel) - a descriptor the socket poller reports as unknown that is below GuestSockets::FirstDescriptor is polled on the host instead of throwing, except on Windows where it still throws; an invalid host descriptor fails with EBADF, and mixed host and socket sets are polled in short alternating slices so neither side waits out the full slice alone. Wobbly Life (PPSA29413) selects on file descriptors while streaming its bundles
  • __progname (libkernel) - unknown data export
  • environ (libkernel) - data export pointing to a fixed empty environment so titles that import it load; it is not the environment libc's getenv reads, so it lists none of those variables and setenv/putenv changes do not appear in it
  • seteuid, setegid, setuid, setreuid, setgroups, getgroups (libkernel) - the guest process is assumed to run as root, with real, effective and saved user and group ids 0. As in FreeBSD's sys_seteuid, sys_setegid, sys_setuid and sys_setreuid, id 0 (or -1 for an unchanged setreuid id) is then accepted without a privilege check and changes no credential, so 0 is returned; any other id would change an id, which is not modelled, and throws. setgroups keeps the supplementary group list as kern_setgroups does (NGROUPS_MAX 1023, an empty list leaves the effective group as the only entry, a first entry other than 0 would change the effective group and throws) and getgroups reports it. The ids and privileges a title has on a console are unverified
  • pthread_barrierattr_setpshared (libkernel) - PTHREAD_PROCESS_SHARED is accepted on the assumption that the console follows FreeBSD 11.0 libthr, which accepts it (9.0 rejects it with EINVAL); unverified on hardware. Guest code runs in one process, so a shared barrier behaves as a private one
  • pthread_getname_np (libkernel) - a thread that has already been joined returns ESRCH (3) on a PS5 (firmware 13.60), but its object is freed after the join here, so the pointer cannot be told apart from a live thread and its name is read as for a live one
  • sceKernelGetOperationMode (libkernel) - the signature (int* mode, int* submode) comes from PPSA12544, which logs both values after the call and ignores the result. A homebrew payload on a PS5 (firmware 13.60) reads mode 2 and submode 0, nothing else is written, and the result register holds no status, so the function is taken to return nothing. Whether a title process reads the same values is unverified. Null outputs throw
  • sceCoredumpAttachUserMemoryFile (libkernel) - unknown signature
  • sceCoredumpAttachMemoryRegion (libkernel) - unknown signature
  • sceKernelAddTimerEvent (libkernel) - usec is taken as the period in microseconds; re-adding an existing timer keeps its pending expiration count and restarts the period from the time of the call, where FreeBSD 12+ filt_timertouch clears pending expirations on EV_ADD; usec == 0 fires on every wait. None of this is confirmed on a PS5 title
  • sceKernelWaitEqueue (libkernel) - a level-triggered user event (sceKernelAddUserEvent) is reported at most once per call, as in FreeBSD kqueue_scan (sys/kern/kern_event.c), which re-queues a reported knote without EV_CLEAR or EV_DISPATCH behind the scan marker, and kevent(2), where several triggers do not produce several kevents. Not measured on a console. Events are scanned in list order and the list is not rotated, so when num is smaller than the number of ready level events the later ones can starve, where FreeBSD moves a reported level knote to the tail. Several completions of one AMPR or VideoOut event within one call are returned as separate entries, where FreeBSD would aggregate them; the Orbis behaviour is unconfirmed
  • AC1FtjqMCL0 (libkernel) - unknown name, signature
  • 0iEKNAvT600 (libkernel) - unknown name, signature
  • 2cELEBPdYQ0 (libkernel) - unknown name, signature
  • 5xYxiOtOccA (libkernel) - unknown name, signature
  • 6W+shIH315Q (libkernel) - unknown name, signature
  • 7ilObq815O4 (libkernel) - unknown name, signature
  • 7iq7Hfs8HBk (libkernel) - unknown name, signature
  • AMJZhpC+siY (libkernel) - unknown name, signature
  • E9XkHowt+dY (libkernel) - unknown name, signature
  • GADjreszOgM (libkernel) - unknown name, signature
  • Giyg2xGrXDU (libkernel) - unknown name, signature
  • KC3y21wrqzc (libkernel) - unknown name, signature
  • Qb8wS4GZUsw (libkernel) - unknown name, signature
  • aAQZSjb2fho (libkernel) - unknown name, signature
  • pnz21I6VjDs (libkernel) - unknown name, signature
  • qk043QPsrZU (libkernel) - unknown name, signature
  • uDGXwUKqi8s (libkernel) - unknown name, signature
  • sceKernelGetCpuTemperature (libkernel) - imported by PPSA99902, which fails to load without it; added without an implementation because its behavior is unverified.
  • LinuxProtFromSce (libkernel) - protection bits 0x100 and 0x200 are accepted and assumed not to affect host CPU access; host protection is derived only from the read, write and execute bits
  • sched_get_priority_max, sched_get_priority_min (libkernel) - measured on a PS5 (firmware 13.60): SCHED_FIFO (1) and SCHED_RR (3) report a maximum of 767 and a minimum of 256, and every other policy, SCHED_OTHER (2) included, returns -1 with EINVAL. pthread_setschedparam ignores the policy and does not check the priority, where the console rejects SCHED_OTHER and priorities below 256 with EINVAL
  • inet_pton, inet_ntop (libkernel) - the text forms are assumed to be those of FreeBSD 11.4 and are unverified on a console: inet_pton accepts only four decimal parts without leading zeros for AF_INET and the RFC 4291 forms for AF_INET6 (one :: standing for at least one group, a dotted-quad tail only as the last 32 bits, no zone id), and inet_ntop prints lowercase hex with the first longest run of two or more zero groups compressed, and a dotted tail only for ::a.b.c.d and ::ffff:a.b.c.d. They no longer depend on the host resolver library

Functional

  • sem_timedwait (libkernel) consumes an available count before checking the timeout and waits indefinitely for a null timeout, following FreeBSD 11's sem_timedwait. Console behavior for null and invalid timeouts when a count is available is unverified.
  • Windows file timestamps use the host filesystem's timestamp range and resolution. utimes and futimes reject timestamps outside nonnegative signed 64-bit Windows file-time ticks with EINVAL, including exactly 1601-01-01 00:00:00 UTC, whose zero value means "leave unchanged" to SetFileTime. Guest directory descriptors still resolve through their recorded path. Filesystems with coarser timestamp resolution cannot retain every guest microsecond
  • Windows file identity (libkernel) - stat and fstat report the volume serial number as st_dev and the 64-bit file index folded into the guest's 32-bit st_ino (never 0), so two different files can in principle share an st_ino; the fold is not taken from a reference. st_nlink is clamped to 65535, the width of the guest field. The call keeps the _wstat64 values when GetFileInformationByHandle fails
  • On Linux, direct memory lives in one sparse memfd at file offset = physical address, so the GPU imports can share one udmabuf buffer object per 64 MiB of it. Released direct memory is not punched out of the file, because a live import of its chunk pins the pages and the GPU would keep reading the old ones: its pages stay committed (the process keeps the high-water mark of the direct memory it used) and are zeroed when the range is allocated again.
  • system (libkernel) runs no command. It follows FreeBSD 11's lib/libc/stdlib/system.c for a sandbox without /bin/sh: a null command returns 1 unconditionally, as that version does, and any command returns the wait status of a child that could not exec the shell and called _exit(127) (0x7f00). Whether the console's application sandbox has no shell, and whether vfork succeeds there (FreeBSD returns -1 when it fails), is unverified
  • sceKernelWriteThrottlingStatus (libkernel) reports no write throttling: the host SSD has no write budget, so the first qword of the 32-byte status is set to UINT32_MAX and the rest is zeroed. PPSA17221 reads only that qword: it multiplies it by 64 KiB and compares the product with byte thresholds up to 0x4AF00000 to pick one of five tiers. When the call fails the title assumes 0x4B000001 bytes, which selects the same top tier as UINT32_MAX. What the value measures on hardware (a remaining budget or a rate) and the meaning of the other three qwords are unknown
  • Windows directory descriptors (libkernel) take one snapshot of the directory, on the first getdents, getdirentries or non-zero lseek (SEEK_SET or SEEK_CUR), and keep it until it is discarded; lseek to offset 0 only discards the snapshot, and the next read lists the directory again, so every rewind costs O(entries) on the next read. lseek takes the offset as an entry index and SEEK_CUR with offset 0 only reports the index. SEEK_END throws, where the NUL descriptor used to return 0, because FreeBSD defines it relative to the directory size, which is not modelled. A negative result returns EINVAL and a SEEK_CUR offset that overflows returns EOVERFLOW, as FreeBSD vn_seek reports them. Names whose UTF-8 encoding exceeds the one-byte d_namlen limit are not representable in this guest directory-record layout: getdents returns ENAMETOOLONG without consuming the entry. Repeated calls therefore report the same error and entries after that name cannot be reached through this descriptor; FreeBSD's getdents behavior for host filenames that exceed its MAXNAMLEN is not verified. getdirentries still reports 0 through basep instead of the position of the block it read, so only 0 and values returned by lseek are valid offsets; FreeBSD offsets are filesystem cookies
  • /dev/random and /dev/urandom (libkernel) opened for reading return a descriptor backed by the host null device whose reads are filled with host OS entropy (getentropy, rand_s on Windows); both paths behave the same, never block, and positioned reads ignore the offset (FreeBSD random(4)). Only the exact paths are recognised, a duped descriptor reads as an empty file, fstat, lseek, mmap and select see the null device, and opening them for writing throws
  • pthread_attr_getinheritsched, pthread_attr_setinheritsched, pthread_attr_getscope, pthread_attr_setscope, pthread_attr_getstackaddr (libkernel) follow FreeBSD 11 libthr (thr_attr.c, thr_create.c), not measured on the console: the scope defaults to PTHREAD_SCOPE_SYSTEM (2), only it and PTHREAD_SCOPE_PROCESS (0) are accepted and other values return EINVAL, and an inherit value other than PTHREAD_INHERIT_SCHED (4) or PTHREAD_EXPLICIT_SCHED (0) returns ENOTSUP. pthread_attr_getstackaddr returns the configured stack address pointer (null by default). A null output of pthread_attr_getinheritsched throws, since libthr writes through it unchecked. A thread keeps the scope it was created with, taken from the creating thread under PTHREAD_INHERIT_SCHED, and pthread_attr_get_np and scePthreadAttrGet report it, but every guest thread is a host thread at system scope, so process scope changes nothing
  • Shared memory (libkernel) on Linux implements anonymous and named objects, bounded descriptor writes and seeks, and shm_unlink with effective read/write permission checks from FreeBSD 11. Named paths up to 1023 bytes use /anyps5-shm- plus SHA-1; collisions are not detected, and the namespace is shared across processes in the host mount namespace. Named creation applies the guest umask without changing the host mask. Linux procfs and /dev/shm are required. Operations use an advisory directory lock; external host operations bypass it, unrelated objects serialize, and descriptor operations require two spare host descriptors and compare device/inode with the initial descriptor identity on both the pinned-object and ordinary-file fallback paths. Guest mappings support shared and private mappings of these descriptors through the allocation tracker and pin a duplicate descriptor during validation and mapping; mapping requires one spare host descriptor; file-backed mappings use the write-watch scan fallback because aliases and descriptor writes bypass per-mapping tracking. Named objects and file-backed mappings on Windows remain unsupported. Invalid non-null name pointers, console behaviour and title compatibility remain unverified.
  • sceKernelAddReadEvent and sceKernelAddWriteEvent (libkernel): a write event reports the socket's send buffer size (SO_SNDBUF) in data, not the free space left in it, as Windows has no query for that space. Only guest sockets are supported; other descriptors and a low watermark above 1 throw
  • close and setsockopt (libkernel) reach a libSceNet socket through hooks libSceNet registers, as select polls them; the other libkernel socket calls do not. libSceNet numbers its sockets from 32 in its own table, so a CRT file descriptor and a socket can share a number; close then closes the socket. On the console both are one kernel descriptor space
  • sceKernelPread, sceKernelPwrite, sceKernelPreadv, sceKernelPwritev (libkernel) on Windows transfer at the given offset with an overlapped ReadFile/WriteFile and restore the descriptor's file pointer afterwards, so a concurrent read/write on the same descriptor can see the moved pointer in between, unlike on the console. Handles that are not disk files return ESPIPE. readv on a pipe or console handle stops after the first iovec read, a short read the console is assumed to allow; each iovec is transferred separately, so a failure after the first transfer returns the bytes already done
  • sceKernelAioSubmitReadCommands, sceKernelAioSubmitWriteCommands and their Multiple variants (libkernel) on Windows transfer through the same positioned path as sceKernelPread/sceKernelPwrite and restore the descriptor's file pointer afterwards, so a concurrent read/write on the same descriptor can see the moved pointer in between, unlike on the console
  • sceKernelAioSetParam (libkernel) writes the five values into the 20-byte scheduling struct the first argument points to, following the declarations in Orbital, SharpProspero and ampr_emu. Whether the console checks the values is unknown, so a null struct, a scheduling window or delayed count limit outside 0..128, a split flag other than 0 or 1 and a split size or chunk size above 0x1000000 (SharpProspero's maximums) throw. The scheduling settings are not used: sceKernelAioInitializeImpl ignores the struct and requests run synchronously on submit
  • sceKernelGetFsSandboxRandomWord (libkernel) returns a fixed directory name, gkbHtXc3Wq. On the console the name is the process's randomized path (syscall 602, randomized_path), and the sandbox exposes system files under it, such as common/lib and common/cert. Nothing is mounted under the name here, so /gkbHtXc3Wq/common/... resolves under the guest root like any other path and does not exist
  • getcontext and setcontext (libkernel) and makecontext (libSceLibcInternal) use FreeBSD 11's amd64 ucontext_t (mcontext at 0x10, mc_len 800, uc_link at 0x330, uc_stack at 0x338, 0x370 bytes), the layout homebrew built against FreeBSD's headers passes: PPSA99039's OpenSSL ASYNC (crypto/async/arch/async_posix.c) captures into a 0x370-byte stack buffer. getcontext writes the 0x330 bytes before uc_link, as FreeBSD's UC_COPY_SIZE. The console's own layout for these functions is not verified; the exception handler's context is a different one (see sceKernelRaiseException)
  • getcontext and setcontext (libkernel) keep the 512-byte FXSAVE image in mc_fpstate (x87, MXCSR, XMM0-15), so the upper halves of YMM0-15 are neither saved nor restored, and getcontext leaves mc_xfpustate and mc_xfpustate_len 0. getcontext sets mc_fpformat and mc_ownedfp to FreeBSD's _MC_FPFMT_XMM and _MC_FPOWNED_FPU, mc_cs and mc_ss to the host selectors, and mc_flags, mc_fsbase and mc_gsbase to 0. setcontext loads mc_fpstate whatever mc_fpformat and mc_ownedfp say, and restores neither the segment selectors, mc_fsbase and mc_gsbase nor the extended state, which FreeBSD restores when mc_flags asks for them
  • setcontext (libkernel) and longjmp (libc) on Windows set the thread's stack bounds in the TEB to the stack they switch to: a makecontext stack, or else the committed region around the target stack pointer. A switch to any other guest stack, such as a context built by hand, gets the bounds of that region only
  • sceKernelInstallExceptionHandler records the handler, but a host fault still ends the process: only signals raised with sceKernelRaiseException are delivered. Unity's PS5Util module installs a SIGUSR1 handler that reads uc_mcontext.mc_rsp (offset 0xf8 of the context) and raises SIGUSR1 on other threads to stop them for garbage collection
  • sceKernelRaiseException on Linux sends the host SIGUSR1, which the library takes over, to the target thread, so the handler runs wherever the thread was interrupted, emulator code included. On the console kernel objects are system calls and libthr defers signals while a thread holds its internal locks; here a handler that needs a host lock the interrupted code holds deadlocks, and a thread that the handler keeps waiting keeps its host locks. Host waits resume after the handler. The host calls Linux does not restart end early: sleeps that do not retry return before their time, and the poll of sceNetEpollWait and socket calls with a send or receive timeout return EINTR to the guest
  • sceKernelRaiseException builds the handler's ucontext with FreeBSD's amd64 layout (16-byte sigmask, mcontext at 0x40, mc_rsp at 0xf8); the console's layout is not verified beyond the fields PS5Util reads
  • sceKernelRaiseException does not consult the target thread's signal mask
  • pthread_sigmask (libkernel) shares the process-wide mask of sigprocmask and _sigprocmask, which keeps only the first 32 signals; FreeBSD keeps a mask per thread
  • sceKernelRaiseException reaches a thread that waits in an alertable TimedWait wait through an APC. A thread blocked anywhere else, including all waits under APS5_COARSE_TIMED_WAITS and host waits inside the emulator, is redirected and runs the handler only when that wait returns
  • sceKernelRaiseException on Windows runs the handler wherever the target thread was interrupted. It waits only while the thread is inside winpthreads or restoring a context through NtContinue, and throws if that lasts 1 s, so a handler that calls into other host code the thread was interrupted in, such as the C runtime's heap or a lock of the emulator, can deadlock on it or reenter it
  • sigaltstack (libkernel) records each thread's alternate signal stack and reports it back, following FreeBSD's kern_sigaltstack (EINVAL for flags other than SS_DISABLE, ENOMEM below MINSIGSTKSZ 2048), but handlers never run on it, so SS_ONSTACK and EPERM are never reported
  • scePthreadJoin, scePthreadCreate (libkernel) - on a PS5 (firmware 13.60) joining a thread a second time returns ESRCH (0x80020003); here a joined thread is released, so a second join reads freed memory. A null entry crashed the payload and still throws. Joining the calling thread returns EDEADLK and a zeroed attribute creates a default thread, as on the console
  • Thread cancellation (libkernel) follows FreeBSD libthr (states and types 0/1 and 0/2, PTHREAD_CANCELED 1, the _pthread_cleanup_info node layout), not measured on the console. A request is acted on only in scePthreadTestcancel, the condition variable, sem and scePthreadSem waits, scePthreadJoin and the sleeps, and their POSIX names; file and socket calls are not cancellation points. A sleep is not interrupted: the thread exits when it ends. The asynchronous type acts like the deferred one, except at the two set calls.
  • cpuset_setaffinity (libkernel) - only the calling thread or the calling process is handled. Bits outside sceKernelGetAvailableCpumask are dropped, and a mask with none of those bits fails with EDEADLK, as FreeBSD does when the new set has no CPU in common with the parent set
  • cpuset_getaffinity (libkernel) - only the calling thread or the calling process is handled. For the process the mask is the union of the affinities of the live guest threads, as FreeBSD 11.4 ORs the mask of every thread in the process; threads the guest did not create through libkernel are not included, and the console result is unverified
  • dup and dup2 (libkernel) throw for the combinations they do not model: dup2 between a guest socket and a host descriptor in either direction, and on Windows duplicating a directory descriptor. Guest sockets share one socket object between the duplicates and the descriptor numbers come from the guest socket counter, not the lowest free number as in FreeBSD kern_dup. The errno values for descriptor exhaustion are not verified against the console.
  • waitpid (libkernel) returns -1 with ECHILD (EINVAL for unknown options): nothing here creates a child process; child processes would need a real waitpid.
  • execv (libkernel) throws: replacing the process image is not supported, the same as execvp. PPSA97358 imports it and fails to load without it.
  • kern.proc.pathname (libkernel) returns /app0/eboot.bin for pid -1 and the calling process, following FreeBSD's sysctl_kern_proc_pathname; the exact pathname on retail consoles remains unverified.
  • machdep.cacheline_size (libkernel) returns -1 with ENOENT, matching FreeBSD amd64 where this sysctl OID is not defined.
  • getrlimit (libkernel) - RLIMIT_CPU, RLIMIT_FSIZE, RLIMIT_CORE, RLIMIT_SBSIZE, RLIMIT_VMEM/AS, RLIMIT_NPTS, RLIMIT_SWAP, RLIMIT_KQUEUES and RLIMIT_UMTXP report infinity as in FreeBSD's process-0 defaults, and RLIMIT_DATA mirrors the host limit. RLIMIT_STACK, RLIMIT_RSS, RLIMIT_MEMLOCK, RLIMIT_NPROC and RLIMIT_NOFILE throw because no console values are established.
  • execvp (libkernel) throws: replacing the process image is not supported. PPSA85300 imports it and fails to load without it; its fork and vfork fail with ENOSYS (libc_missing.c), so the child process that would call it is never created.
  • socketpair (libkernel) - the pair is the host's AF_UNIX pair on Linux and, on Windows, a loopback TCP connection for SOCK_STREAM or two connected UDP sockets for SOCK_DGRAM; SOCK_SEQPACKET throws on Windows. A datagram sent to a full receiver gets ENOBUFS on the console, waits or gets EAGAIN on Linux and is dropped on Windows, and on Windows a datagram recv after the peer closed reports ECONNRESET instead of the console's ENOTCONN, and a stream send after the peer closed reports ECONNABORTED instead of the console's EPIPE. read and write on a pair end go to the host like on every guest socket. getpeername on a pair end succeeds after the peer closed, where the console returns ENOTCONN, and connect, bind and sendto with an address on a pair end report an address-family error instead of the console's EISCONN. On Windows poll on a stream end reports POLLHUP without POLLIN after the peer's shutdown(SHUT_WR) or close, where the console reports POLLIN, with POLLHUP only after close
  • UDP recv/recvfrom and sceNetRecv/sceNetRecvfrom translate Windows WSAEMSGSIZE into the copied byte count for datagram sockets, including MSG_PEEK, and retain the sender address. This follows FreeBSD 11.4 recv(2) and Winsock's documented truncated-datagram behavior; console behavior and existing title impact are unverified.
  • send, sendto (libkernel) - send and sendto without an address on an unconnected datagram socket map Windows WSAENOTCONN to FreeBSD's EDESTADDRREQ (39).
  • read, write and vector I/O (libkernel) on a guest socket use recv and send without flags; sceKernelReadv and sceKernelWritev gather or scatter through one socket operation, preserving stream short reads and datagram boundaries. The positioned pread, pwrite, preadv and pwritev forms, and their sceKernel forms, return ESPIPE for an open guest socket and EBADF for a closed one. Guest socket descriptors start at 0x10000000 and are allocated upward, so the descriptor range selects the guest socket table or the host descriptor table. EPIPE from a write raises the host's SIGPIPE on Linux, which the guest can neither ignore nor suppress: signal(SIGPIPE, SIG_IGN) and sigaction record the disposition, as they do for every number from 1 to 128, but only the six signals with host equivalents (2, 4, 6, 8, 11 and 15) take effect on the host or are delivered, so the host SIGPIPE is not stopped; SO_NOSIGPIPE is not an option here, and raises nothing on Windows; a write longer than INT_MAX bytes is EMSGSIZE where the console sends it. AIO on a guest socket still goes to the host, which has no such descriptor
  • poll (libkernel) waits only on guest sockets, through WSAPoll on Windows and poll on Linux; the pollfd layout and flag values are FreeBSD's. A descriptor below the socket range (files, pipes, stdio) throws, as do event bits other than POLLIN, POLLPRI, POLLOUT, POLLRDNORM, POLLRDBAND and POLLWRBAND (the POLLERR, POLLHUP and POLLNVAL bits are ignored in events), POLLPRI on Windows, which WSAPoll rejects, and an infinite timeout with no socket to wait on. A closed socket descriptor reports POLLNVAL; the console's limit on the descriptor count is unknown and not checked
  • Socket error numbers (libkernel) - a host socket error becomes the FreeBSD 11 errno.h number of the same name; the numbers are not checked on the console. Winsock WSAESHUTDOWN becomes EPIPE, as a send after shutdown does on FreeBSD, and a host error with no FreeBSD socket counterpart becomes EIO. A nonblocking connect in progress reports EINPROGRESS on Linux but EAGAIN on Windows (WSAEWOULDBLOCK), where FreeBSD reports EINPROGRESS. On Windows a refused nonblocking connect is never reported (SO_ERROR is unsupported), so a repeated one keeps reporting EALREADY where FreeBSD reports ECONNREFUSED. A receive after shutdown(SHUT_RD) reports EPIPE on Windows where FreeBSD returns 0
  • connect to AF_UNSPEC (libkernel) dissolves a datagram association as FreeBSD 11.4's udp_disconnect() does, which resets the local address to the wildcard address and keeps the port. On Linux a socket bound to an explicit port keeps its local address as well (127.0.0.1:<port> stays 127.0.0.1:<port>), because a bound socket cannot be bound again; one bound to port 0 is bound again to the wildcard address with the port it had
  • msync (libkernel) follows FreeBSD 11's kern_msync and vm_map_sync, not measured on the console: the address is rounded down to the page, a range that wraps is EINVAL, as is MS_ASYNC with MS_INVALIDATE, and a range whose start or any page is not mapped (as sceKernelVirtualQuery reports it, reservations included) is ENOMEM; a page unmapped from inside a larger mapping still counts as mapped there, since sceKernelVirtualQuery keeps reporting the whole mapping. The guest has no file mappings, so a successful call writes nothing back. MS_INVALIDATE on a mapped range throws: FreeBSD returns EBUSY when the range holds mlocked pages, and locked pages are not tracked
  • sigtimedwait (libkernel) throws: guest signals are delivered when raised and never left pending, so there is nothing to wait for or take. PPSA85300 imports it and fails to load without it. Its only call site is VLC's vlc_writev (filesystem.c), which polls with a zero timeout to drain a SIGPIPE only after writev fails with EPIPE
  • pause, sigwait, sigwaitinfo (libkernel) throw, as sigtimedwait does: guest signals are never left pending, so sigwait and sigwaitinfo have nothing to take, and pause would wait for a handler that raise, kill and pthread_kill only run on the calling thread. PPSA68091, PPSA99169, PPSA99420 and PPSA97358 import them and fail to load without them. PPSA68091 calls pause at one site (0x8F7266), in a loop that waits while a global holds another thread's errno address. PPSA99169 only stores the address of pause in a table of symbol addresses (0x30DB1). PPSA99420 calls pause at 0x3BD80F6 and 0x3C9E220, sigwait at 0x3C9E4C7 and sigwaitinfo at 0x3C9E588. PPSA97358 calls sigwait from QEMU's sigwait_compat thread (compatfd.c), which qemu_signal_init (main-loop.c) starts for SIGIO, SIGALRM and SIGBUS when the main loop is created, so it reaches the throw at start-up, and from the vCPU thread of the dummy accelerator (dummy-cpus.c)
  • getsid (libkernel) throws: process sessions are not modelled. PPSA74126 imports it and fails to load without it; the title made no call to it in a 60 s run
  • sigaction (libkernel) records the handler, flags and mask of every signal from 1 to 128 and reports them back, following FreeBSD's sigaction (EINVAL for other signals, and for a handler on SIGKILL or SIGSTOP); signal records its handler with SA_RESTART, as FreeBSD's libc does. Only handlers without SA_SIGINFO on the signals signal already delivers (SIGINT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGTERM) run, the same way signal handlers do; their mask and flags are not applied. SA_SIGINFO handlers are recorded but never called, since that needs FreeBSD's siginfo_t and ucontext_t, and neither are the handlers of other signals (SIGBUS, SIGHUP, ...), whether they are set by sigaction or by signal, which records them too (every number from 1 to 128, with SA_RESTART and an empty mask replacing what sigaction stored; SIGKILL and SIGSTOP accept only the default handler in both calls, as in FreeBSD's kern_sigaction). raise of an SA_SIGINFO handler on a signal signal delivers throws instead of terminating, and raise and kill of the other signals still fail with EINVAL, recorded handler or not
  • kill (libkernel) follows FreeBSD's kern_kill and killpg1 for the only guest process: EINVAL for signals outside 0 to 128, signal 0 only checks that the target exists, and its own pid, 0 and minus its own pid (the guest is taken to lead its own process group) signal it through raise, so they share raise's limits above. Any other pid, and -1, which skips the caller, report ESRCH, so EPERM is never reported
  • sceKernelFcntl (libkernel) runs the POSIX fcntl below and returns its guest errno as SCE_KERNEL_ERROR_*, the form the other sceKernel file exports use. The return values and error codes are unverified on a console
  • fcntl (libkernel) follows FreeBSD 11.4's kern_fcntl and kern_dup on file, directory, pipe and character device descriptors; a socket keeps the libSceNet path (F_GETFL, F_SETFL with O_NONBLOCK, EINVAL for every other command, F_GETFD and F_SETFD included). The close-on-exec flag and the status flags live in a guest table, the status flags shared by dup, dup2 and F_DUPFD as in an open file description; a descriptor the table has not seen, such as 0 to 2, starts from the host's flags, since the console's are not known. F_GETFL takes the access mode from the host (the granted access of the handle on Windows), so pipe ends report O_RDONLY and O_WRONLY where FreeBSD's bidirectional pipes report O_RDWR. F_DUPFD and F_DUPFD_CLOEXEC return the lowest free host descriptor at or above the start, so the bound is the host table (EMFILE on Windows, EINVAL past RLIMIT_NOFILE on Linux), not the console's unknown RLIMIT_NOFILE and kern.maxfilesperproc. O_NONBLOCK switches a host pipe (PIPE_NOWAIT on Windows, where an empty read and a full write then report EAGAIN) and is only recorded on files, directories and the random device, as their reads never block; on a Windows console or other character device it throws. Changing O_APPEND on a Windows file, changing O_FSYNC on a file, O_ASYNC on a pipe or character device (SIGIO is not delivered), F_GETOWN, F_SETOWN and F_SETLK_REMOTE throw; O_DIRECT, F_RDAHEAD and F_READAHEAD are recorded as the hints they are. Record locks (F_GETLK, F_SETLK, F_SETLKW and their forms without l_sysid) use the host's POSIX locks on Linux and throw on Windows, and F_GETLK with F_UNLCK throws. The return values and error codes are unverified on a console
  • ioctl, _ioctl (libkernel) handle only FIONBIO and FIONREAD on a socket, and any other request there fails with ENOTTY. On the other descriptors they follow FreeBSD's kern_ioctl, vn_ioctl and pipe_ioctl: FIONBIO sets O_NONBLOCK as fcntl does, and FIONREAD reports the size past the offset of a file, the queued bytes of a pipe's read end, 0 on its write end and ENOTTY on the random device, while it throws on a directory or another character device. Every termios request (group t, TIOCGWINSZ included) fails with ENOTTY, since those descriptors are taken as not a terminal, as in tcgetattr, and any other request throws. Stremio Plus (PPSA74126) imports _ioctl for its statically linked tc* functions, which send TIOCGETA, TIOCSETA, TIOCSPGRP and the other termios requests
  • sendfile (libkernel) follows FreeBSD 11's sendfile for a connected stream socket: headers, the file from offset (to its end when nbytes is 0) and trailers, the file offset left unchanged, EAGAIN with *sbytes set after a partial send on a non-blocking socket. It reads the file through sceKernelPread in 64 KiB blocks and sends each with send. A socket as the file fails with EINVAL, as on FreeBSD, but any other descriptor sceKernelPread reads, such as /dev/urandom, is sent where FreeBSD requires a regular file or shared memory, and a file descriptor as the socket fails with EBADF, as for send, where FreeBSD answers ENOTSOCK. flags are ignored: reads complete before sending, so SF_NODISKIO never fails with EBUSY, and SF_SYNC returns once the data is queued on the socket rather than sent. PS5MC (PPSA99420) imports it
  • pthread_kill (libkernel) returns the error number like FreeBSD's pthread_kill: EINVAL for signals outside 0 to 128 and ESRCH for a null thread. Signal 0 reports any other thread handle as existing, without checking that the thread is still alive. A signal to the calling thread goes through raise, so it shares raise's limits; a signal to another thread throws, since guest signals are not delivered to a chosen thread
  • select (libkernel) validates tv_usec but saturates a valid timeout beyond the remaining steady_clock range at time_point::max(), so the wait can continue effectively indefinitely. The console's behavior for a valid timeout outside the host clock's range is unknown.
  • _fstatfs (libkernel) fills the FreeBSD 11 struct statfs of the PS5 payload SDK with the host volume that holds the file: block size, transfer size, total, free and available blocks, maximum name length and the read-only, nosuid and noexec flags (on Windows only read-only, and f_files and f_ffree are 0, as NTFS has no fixed number of files). The filesystem type number and name, mount paths, owner, filesystem id and I/O counters are left 0 or empty, since they would describe a host mount, not a console one. Sockets and pipes return EINVAL, as FreeBSD's getvnode does; a Windows descriptor that is neither a file nor a pipe throws. Stremio Plus (PPSA74126) reads the free space before a download and only logs the other fields
  • pthread_spin_init, pthread_spin_destroy, pthread_spin_lock, pthread_spin_trylock, pthread_spin_unlock (libkernel) follow FreeBSD 11.4 libthr thr_pspinlock.c and the owner checks of its thr_umtx.h, unverified on the console: EINVAL for a null or destroyed lock and a pshared other than 0 or 1, EBUSY from trylock while any thread holds it, EPERM from unlock by a thread that does not hold it. PTHREAD_PROCESS_SHARED allocates a private lock, as guest code runs in one process. libthr spins forever when the holder locks again; here that call throws
  • pthread_cond_reltimedwait_np (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). 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 (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.