120 KiB
libc technical debt
Silent stubs
- _Mtx_current_owns (libSceLibcInternal, implemented in libc, NID
VYQwFs4CC4Y) returns 1 and ignores its argument. This follows the PS5 12.00 body reading reported in PR8859, rather than inspecting host mutex ownership. The firmware observation is attributed to that contributor; other firmware versions and the argument's console type remain unverified.
Unknown function info
-
strtonum (libc) is unverified on the console. Its signature, decimal parsing, inclusive bounds, error strings, error precedence and errno preservation follow FreeBSD 11.4 strtonum.c. Parsing uses the host
strtollwith base 10. It is not exported bylibSceLibcInternal.sprxon a PS5 (firmware 13.60), so a payload process cannot call it there -
fegetexcept (libSceLibcInternal, implemented in libc) has not been read from the PS5 library or measured on a console. It follows FreeBSD 11.4 amd64 fenv.h, reporting the six unmasked x87 exceptions independently of MXCSR; whether the PS5 uses the same rule when the units disagree is unverified.
-
sceLibcBacktraceGetBufferSize, sceLibcBacktraceSelf (libc) - signatures and frame record layout inferred from a title (next pointer, u32 module offset, module name at +0x10); the u32 at +0xC is unknown and written as 0. Module names are host file names without the
.guest.prxsuffix, so their case may differ from the PS5 module name. On Windows, unwinding uses_Unwind_Backtraceand stops at a frame without.ehmetaor.ehframtables whose PE unwind data has an exception or termination handler, leaving the trace empty or cut short while still returning 0. Implemented only on Linux and Windows; other platforms throw -
ApplicationGuestHeapApi (libc) - on Linux, HLE application allocation uses a loaded guest SDK's ten allocator exports when the title's replacement table has an allocator entry and the exports belong to one native module. With an empty table it keeps the default heap, since a bundled
libc.prxmay export the allocator NIDs while forwarding them to HLE. Its CRT owns application allocator initialization and finalization; HLE retains the module until process exit. Partial or mixed providers throw. This avoids reinitializing an allocator already used by SDK startup; console behavior and other host platforms remain unverified. -
fdopen (libc) validates descriptor access modes as FreeBSD 11.4 fdopen.c does: a requested mode incompatible with a descriptor not opened for both reading and writing returns null with
EINVAL, leaving the descriptor open. Windows reads the handle's granted access through the same host helper asfcntl(F_GETFL); a closed descriptor returns null withEBADF. Console behavior is unverified. -
scriptingGetMem, scriptingFreeMem (libc) - runtime allocator hooks for Unity titles. The allocation signature
(alignment, size), minimum alignment 16 and null result for a non-power-of-two alignment follow KytyPS5; the free hook's one-pointer signature follows Raeen's recorded contract. Both use libc's own guest heap, not the title's registered allocator. A title that ships its ownlibc.prx(Metal Gear Solid Master Collection, PPSA18194) has already initialized that allocator, and reaching it from here would run its initializer a second time. That title's IL2CPP runtime looks up onlyscriptingGetMem, by name fromIl2cppUserAssemblies.prx, and asks it for 256 KiB blocks aligned to 4 KiB. Which console library exports the hooks is unknown, and there is no console measurement. Allocation failure and sizes that overflow return null. -
_Mtx_trylock (libc, NID
k6pGNMwJB08) - returns busy for a non-recursive mutex already owned by the caller. Thelibc.prxthat titles ship (read from disassembly of PPSA13934's copy, 0x5cf0, not measured on a console) passes its argument toscePthreadMutexTrylockand returns 0 for success,_Thrd_busy(3) forSCE_KERNEL_ERROR_EBUSYand_Thrd_error(4) for any other error, so busy for an owned non-recursive mutex comes from the kernel mutex there; this libc keeps its own owner and count instead, and its result for a kernel error other than busy is not compared. -
nearbyintf (libc, NID
c+4r-T-tEIc) rounds with the MXCSR rounding mode, as the console'slibSceLibcInternal.sprxdoes in a payload process on a PS5 (firmware 13.60): with the x87 mode set to upward and MXCSR to nearest it rounded 2.5 to 2, and with MXCSR upward to 3, and the four modes gave the results the host gives. The console also raisedFE_INEXACTfor those inexact inputs, which the host'snearbyintfdoes not; whether it does for exact inputs was not measured -
__std_find_trivial_unsized_N (libc) - matches the
libc.prxbundled with PPSA08427 (disassembly, and that binary run side by side on random inputs): when nothing matches betweenfirstand the end of its 32-byte-aligned block, the console rescans that whole block without masking and so can return a match up to 31 bytes beforefirst -
_Thrd_sleep (libc, NID
jfRI3snge3o) follows the Dinkumware threads library shipped with Visual C++ 2013 (xtime.c) and C11 Annex K, without console verification. It reads a 16-byte xtime (64-bit seconds and nanoseconds) on the guestCLOCK_REALTIME, normalizes the nanoseconds and sleeps throughnanosleepon POSIX, retrying onEINTR, or high-resolution waitable timers on Windows; a null xtime or one whose normalization overflows throws. Guest thread cancellation is not integrated with this wait. The console's_Thrd_sleep(disassembled from a title'slibc.prx) also takes an optionalxtime*of remaining time and returnsint; here the sleep is never cut short, so it returns 0 and leaves the remaining time untouched. -
setjmp, longjmp (libc) save the signal mask at 72 in the jump buffer and restore it, as FreeBSD 11.4's
setjmpandlongjmpdo, which is unverified on a console. They go through thesigprocmasklibkernel registers when it loads and mark the buffer at 88 assigsetjmpdoes, so a buffer saved before then is jumped to without changing the mask -
_Mtx_init_with_name (libc, NID
tgioGpKtmbE) forwards to_Mtx_initand ignores the mutex name parameter. -
_Cnd_timedwait (libc, NID
McaImWKXong) follows the console's_Cnd_timedwait(disassembled from a title'slibc.prx), where xtime is an absolute deadline converted against the current time: it returns_Thrd_timedout(2) on expiration and_Thrd_success(0) on notification. The console returns 4 for other internal pthread errors while this implementation throws. -
fegetround, fesetround, fetestexcept, feclearexcept, fegetexceptflag and fesetexceptflag (libSceLibcInternal, implemented in libc) follow FreeBSD 11.4 amd64 fenv.h: rounding modes use x87 control bits 10-11, exception flags include denormals (
0x3f), andfexcept_tis 16 bits. They access the current host thread's x87 and MXCSR state. Under Rosetta 2 the six MXCSR exception masks always read back set, so an SSE exception the guest unmasks stays masked; the x87 masks and the other MXCSR bits are kept. This console ABI is assumed, as forfegetenv, not measured on hardware. Null flag pointers throw instead of faulting. -
feenableexcept, fedisableexcept, __feenableexcept, __fedisableexcept, fegettrapenable and fesettrapenable (libc) change only the x87 masks under Rosetta 2: MXCSR keeps its six exception masks set there, so no SSE trap is enabled, and the enable and disable results, which report the traps enabled on both units, report none
-
fegetenv (libSceLibcInternal, implemented in libc, NID
psx0YiAKm7k) follows FreeBSD 11 amd64 fenv.c with the 32-bytefenv_tof x86/fenv.h:fnstenvfills the 28-byte x87 part, MXCSR follows at 0x1c, and the x87 control word is reloaded becausefnstenvmasks every x87 exception. That the console'slibSceLibcInternalhas the same code and layout is assumed, not read from its binary. A null pointer throws instead of faulting -
feholdexcept (libSceLibcInternal, implemented in libc, NID
1kduKXMqx7k) - console equivalence and module/library versions are unverified. The signature and 32-byte amd64 environment layout follow the public SDK fenv.h; saving both units, masking exceptions and clearing pending flags follows FreeBSD 11 amd64 fenv.c. The NID comes from the public 3.20 stub inventory, not a module examined here. Null storage throws before modifying the environment, where the reference dereferences it -
feupdateenv (libSceLibcInternal, implemented in libc, NID
YXQ4gXamCrY) follows FreeBSD 11.4 amd64 fenv.c: capture the old x87/MXCSR exception union (0x3f), restore the saved 32-byte environment, raise those flags in both units, thenfwait. The console's layout, behavior and module versions are unmeasured; state belongs to the current host thread. A null input or MXCSR bits unsupported by the host'sFXSAVEmask throw before changing floating-point state; the mask's architectural zero fallback is0xffbf. Console fault behavior for malformed environments is unknown. The separate__fe_dfl_envobject remains unimplemented; no hostFE_DFL_ENVsentinel is substituted. -
Guest arena (libc) - the application map area is assumed to end at 0xFC_0000_0000, as
SCE_KERNEL_APP_MAP_AREA_END_ADDRdoes on the PS4. On Windows, host allocations made in the arena before libc loads stay, and a fixed guest mapping over one throws. The console's low-address layout is not measured. Linux and Windows reserve free addresses from 64 KiB, except host mappings and low chunks the Linux address policy refuses; null hints still allocate from 8 GiB. macOS and ASan builds still start the arena at 8 GiB and cannot supply low 32-bit addresses. Guest thread creation ignores a stack address set in its attributes. A host-loaded executable is not moved, so a PIE above the arena gets no memory within 2 GiB of its code (#5217) -
sceLibcInternalBacktraceForGame (libSceLibcInternal, implemented in libc) -
(const char* heapName)and its return follow thelibc.prxASTRO BOT (PPSA21564 01.007.000) bundles: its four calls (0x6801, 0x7F46, 0xE75E, 0xEE02) are in themalloc_statsandsceLibcMspaceMallocStatspaths, each after a[%s] ...line printed with the same heap name buffer, and the caller ignores the result and continues. The console's backtrace output is not reproduced: the heap name and the guest return address are written to the host standard error -
sceLibcInternalHeapErrorReportForGame (libSceLibcInternal, implemented in libc) -
(void* heap, void* block, uint32 error)writes to the guest's standard output what a payload process on a PS5 (firmware 13.60) wrote:[SceLibc] A heap error is detected.,[SceLibc] -- Mspace Name --, then the C string at heap+0x3B0 followed by(%2u)of the 32-bit value at heap+0x3F4 when it is not 0, the reasonsegment out of range.for error 1 only (38 other measured values print none), andpid:<0x..> tid:<0x..>, and returns. The pid and tid are the host's, where the console prints its own process and kernel thread ids. The block's contents did not change the console's output and it is not read here; the values and the guest return address also go to the host's standard error. A null heap faulted on the console and throws here. What the other heap fields and error values mean is unknown -
sceLibcPafMspaceCreate, Destroy, Malloc, Memalign, PosixMemalign, Free, Calloc, Realloc, Reallocalign, MallocStats and MallocStatsFast - PAF exports delegate to
sceLibcMspacewithout malloc hooks;MemalignandPosixMemalignforward to theirMspacecounterparts with owner ignored and are unverified on console.Create's options block (version, type 1 or 2 at +2, field at +0x10) throws when non-null as its meaning is unknown. Block footers are not modelled, so owners are ignored: on PS5 firmware 13.60 mismatched owners inReallocalignor in-placeReallockill the process andMallocStatsreturns 1 if non-heap-owned blocks exist, which is not reproduced.MallocUsableSize,Trim,IsHeapEmpty,CheckMemoryBounds,ReportMemoryBlocksandGetFooterValueare not implemented. -
malloc_usable_size (libc) - with the default allocator it returns the size the block was requested with (the range registered for it), not the rounded-up chunk size Sony's allocator reports; both satisfy "at least the requested size", and bytes past the request would not be write-watched. With an application allocator it calls entry 9 of the replacement table (
malloc_usable_sizein KytyPS5'sMallocReplaceorder) and throws when the table leaves it null, likemalloc_stats_fast -
malloc_stats_fast (libc) - with the default allocator it fills the 0x28-byte managed-size block as
sceLibcMspaceMallocStatsFastdoes, from the default heap's own accounting, not Sony's allocator: the system sizes count the 1 MiB spans of its small-block classes and its committed large blocks, cached free ones included, and the in-use sizes count each handed-out block at its size-class or 64 KiB-granule size, header included. A block size below 0x28 or a null pointer returns 22, as in the mspace version; the version field is not checked. Ship of Harkinian (PPSA99320) calls it about 10 s after start-up. -
malloc, calloc, realloc, memalign, aligned_alloc, reallocalign and posix_memalign (libc) - the console's own heap was measured in a payload process on firmware 13.60, not in a title. A heap a title replaces was not measured and keeps the FreeBSD jemalloc behaviour:
ENOMEMon failure,EINVALfor an alignment that is zero or not a power of two, andrealloc(ptr, 0)frees the block. With the console's heap, failures setEINVAL, anderrnois left alone for a size aboveSIZE_MAX - 0x1008f(SIZE_MAX - (A + 0x100af)for an alignment over 32 rounded up to A), acallocoverflow or an alignment of 2^63 or more. Alignments round up to a power of two, andrealloc(ptr, 0)keeps the block with 0 usable bytes.reallocalign(ptr, 0, alignment)for a block without that alignment was not measured and throws -
reallocf (libSceLibcInternal, implemented in libc) - follows FreeBSD 11
lib/libc/stdlib/reallocf.con top of this libc'srealloc, as a payload process on a PS5 (firmware 13.60) does with the default heap (null and 0, size 0, 2^40 witherrno22 and the block freed,SIZE_MAXwitherrnountouched): size 0 does whatreallocdoes here, keeping the block with 0 usable bytes with the default heap and freeing it and returning null with an application allocator, so the block is not freed twice; whenreallocfails, the block is freed through the same allocator and null is returned with theerrnothatreallocleft. Other failures (a commit failure that is not out of memory, an unknown pointer) throw, as they do forrealloc -
sceLibcMspaceReallocalign (libSceLibcInternal, implemented in libc) takes
(void* heap, void* block, size_t alignment, size_t size), the order of the console'slibSceLibcInternal.sprx(SDK 4.5x build ofJ03950001, 0x13880, whose internal routine at 0x12390 treats the third argument as the alignment and, for a null block, passes it to the routinesceLibcMspaceMemalignuses). Measured on a PS5 (firmware 13.60) from a homebrew payload: a null block with alignment 4096 and size 100 returns a 4096-aligned block; an alignment of 0 or 3 succeeds and 100 gives a 128-aligned block, as the routine raises it to at least 32 and to the next power of two; an alignment of 2^63 or more returns null, keeps the block and leaveserrnoalone; and a size of 0 returns a block (the same one when it is already suitable) instead of freeing it. A block that is aligned and large enough is kept where it is, without giving back the bytes past the new size as the console's in-place resize does -
acosl (libc) - computed by the host's math library, like the other libc math functions here, not by the console's (FreeBSD msun ld80 code in Sony's
libc.prx): results can differ from the console's in the last bit, and on Linux glibc setserrnotoEDOMforacosloutside [-1, 1] where msun only raises the floating-point exception.long doubleis the x87 80-bit format, as Sony'sfrexpluses it (fldt 0x8(%rsp), result inst(0)) -
atanl, sinl, cosl, tanl, sinhl, coshl, tanhl, expl, exp2l, logl, log10l and powl (libc) - computed by the host's math library, as
acoslis, not by the console's FreeBSD msun ld80 code: results can differ from the console's in the last bit (glibc'slog10l(1000)is not exactly 3), and on Linux glibc setserrnotoEDOMorERANGEwhere msun only raises the floating-point exception.ldexplandtrunclare exact operations and give the console's result;logbandremaindergive the console's value too, but on Linux glibc setserrno(ERANGEforlogb(0),EDOMforremainderby zero or of an infinity) where msun only raises the floating-point exception -
Dinkumware atomics (libc) - every operation is sequentially consistent whatever memory order it is given, which is never weaker than the console. Matching the
libc.prxbundled with PPSA08427 (that binary run side by side on every order from 0 to 7),_Atomic_load_8returns 0 for a release, acq_rel or invalid order and the 8-byte exchange and fetch operations return 0 without touching memory for an order abovememory_order_seq_cst; the generic_Atomic_copy,_Atomic_exchangeand_Atomic_compare_exchange_*lock the byte flag passed first, as the console does -
Fetch{and,or,xor}seq_cst{1,2,4} (libc) follow the two-argument unsigned 8-, 16- and 32-bit helpers in Dinkumware VC12 xatomic.h, using sequentially consistent atomic read-modify-write operations and returning the original value. The firmware 3.20 export inventory supplies the names and NIDs; the reference defines inline namespace helpers, so the equivalence to the console exports and their calling convention are inferred, not console-measured. A live, suitably aligned object is required as in the reference; invalid-pointer and misalignment fault behavior on the console is unverified. Windows tests cover raw and patched exports, values, neighboring storage and concurrent updates; a finite ordering litmus cannot prove the memory model.
-
std_execute_once (libc) - assumed to be the plain-named twin of
std::_Execute_once(_ZSt13_Execute_onceRSt9once_flagPFiPvS1_PS1_ES1_, the callee of Dinkumwarecall_once): it delegates there and gets the [thread.once.callonce] semantics already verified for that symbol (one invocation per flag, concurrent callers block until it completes, and a failing or throwing invocation leaves the flag unready so the next caller runs it). The name equivalence is not confirmed on a title, and the callback convention (called with (null, arg, null), non-zero return meaning success) is the twin's, unverified for this symbol -
__atomic_load, __atomic_store, __atomic_exchange and __atomic_compare_exchange (libc) - the generic, size-parameterised libatomic entry points are a single
ud2in thelibc.prxbundled with PPSA08427 (disassembly), so the console traps on any call; these throw instead -
Sized __atomic calls (libc) - the 36
__atomic_load_N,__atomic_store_N,__atomic_exchange_N,__atomic_compare_exchange_Nand__atomic_fetch_{add,sub,and,or,xor}_Ncalls (N = 1, 2, 4, 8) are sequentially consistent whatever order they are given. Thelibc.prxbundled with PPSA08427, run side by side on random values and on every order from -1 to 7, switches on the order but always uses a locked instruction or CAS loop, except that a store with an order other thanmemory_order_seq_cstis a plain store, so these are never weaker than the console. Unlike_Atomic_load_8and the 8-byte_Atomic_*exchange and fetch calls above, the console accepts every order here, including release, acq_rel and invalid ones, and these do the same -
Dinkumware mutexes and conditions (libc) use pointer-to-handle arguments, matching the console aerolib signatures reported in #3079. The title's
_Mtx_lockmaps0to_Thrd_success(0),0x8002000bto_Thrd_busy(3), and other failures to_Thrd_error(4); its internal mutex init recognizes recursive flag0x100, while every other type uses default attributes. The observed relock failure for a plain mutex suggests those defaults are error-checking, but that has not been confirmed on hardware. The title's_Mtx_unlockand_Cnd_waitreturn success regardless of their internal result. The host implementation treats invalid ownership preconditions as no-ops, and throws for invalid handles, host wait errors and destroying a locked mutex as debug guards; those cases are not confirmed against the console. -
_Mtx_timedlock (libc, NID
hPzYSd5Nasc) follows the console'slibc.prxat0x5d50(PPSA02530): it takes an absolute xtime and returns_Thrd_timedout(2) on expiry (0x8002003c) or 5 on other failures. On non-Windows hosts the native mutex and condition arestd::timed_mutexandstd::condition_variable_any; Windows keeps theCRITICAL_SECTION/CONDITION_VARIABLEpair becausestd::condition_variable_anyfailedguest_libc_cnd_timed_waiton the Windows runner, and its_Mtx_timedlockpollsTryEnterCriticalSectionuntil the deadline. Windows timing is not measured on the console. -
Floating-point compiler-rt builtins (libc) follow the
libc.prxbundled with PPSA08427 (disassembly, and that binary run side by side on random inputs) where it differs from current compiler-rt: the soft-float__add,__sub,__muland__div(sf3,df3) round to nearest, raise no exception flags and ignore MXCSR, here done with SSE under a default MXCSR that is restored afterwards; division flushes a quotient below the smallest normal to a signed zero;__extendsfdf2and__truncdfsf2copy NaN payloads unshifted; float-to-integer conversions do not saturate but shift the significand by a count masked to the shift width.__mulsc3,__muldc3,__divsc3and__divdc3fuse the same products as the console withfmaand scale through the x87 like itsscalbn, so their values match, but NaN payloads and exception flags can differ, as the hostfmaandlogbare not the console's VEX FMA code and libm -
random, srandom, initstate, setstate (libc) - not measured: the state
setstatereturns and its null return for a rear position past the degree,errnoafterinitstatebelow 8 bytes, a null state (throws here), aninitstateseed other than 1, concurrent callers (one lock serialises every call here) and a title's own libc. Measured in a payload process on a PS5 (firmware 12.70): a fresh process draws thesrandom(1)sequence;srandomseeds an additive feedback generator through agood_randthat reads its argument as an int32, turns 0 into 123459876 and appliesx = 16807 * (x % 127773) - 2836 * (x / 127773), adding 0x7fffffff to a negative result, which reproduces seeds 0, 1, 2, 12345, 0xffffffff and 0x80000000;initstatereturns null at 0 and 7 bytes and selects types 0 to 4 by size as measured at 8, 15, 31, 32, 63, 64, 127, 128, 129, 255, 256, 257 and 1000 bytes. -
srandomdev (libSceLibcInternal, implemented in libc) is not verified against the console. The firmware 3.20 export inventory names NID
a2MOZf++Wjg; it follows FreeBSD 11 random.c, filling every active generator word from host OS entropy instead ofKERN_ARND, then resetting additive feedback positions without warming up. An entropy failure throws rather than leavingsysctl's failure unchecked. Calls share the generator lock. -
Integer compiler-rt builtins (libc) - the console traps in
__divsi3,__modsi3and the 64- and 128-bit divisions on a zero divisor (div) and in__negv*,__absv*and__mulv*on overflow (an abort helper, thenud2); these throw instead. As in thelibc.prxbundled with PPSA08427 (disassembly),__addv*and__subv*wrap without an overflow check, and__udivsi3,__umodsi3and__udivmodsi4divide in software, giving a quotient of 0 and the dividend as remainder for a zero divisor;__clzdi2and__ctzdi2of 0 return 32 and__clzti2and__ctzti2of 0 return 64 -
qsort (libc) follows FreeBSD 11 qsort.c: Bentley and McIlroy's quicksort, with insertion sort below 7 elements and after a partition pass that swaps nothing, so elements that compare equal end in FreeBSD's order and the comparator is called in the same sequence. That the console's libc uses this FreeBSD code is unverified
-
memcpy_s, memmove_s, strcpy_s, strncpy_s, strcat_s, strncat_s, wmemcpy_s, wmemmove_s, wcsncpy_s, wcscat_s, wcsncat_s (libc) use the C11 Annex K destination size constraints, with RSIZE_MAX taken as (SIZE_MAX >> 1) / sizeof(char16_t) for wide operations and SIZE_MAX >> 1 for byte operations. An oversized destination size returns ERANGE (34) without touching the destination; the console's RSIZE_MAX and exact error value are unverified. Constraint violations return an error without invoking the registered constraint handler, as before.
-
wcsncat_s and wcscat_s (libc) refuse a copy as overlapping only when the written range
[dest + len1, dest + len1 + len2)meets the read range[src, src + len2). The rule comes from a static reading of the console'swcsncat_sinlibc.prx(not run on a console);wcscat_sgets it throughwcsncat_s, and its own console code was not read. The narrowstrncat_skeeps a wider rule,[dest, dest + used + length + 1)against[src, src + length + 1), whose console code was not read either, so it refuses some copies whose wide equivalent proceeds. -
snwprintf_s (libc) - follows C11 K.3.9.1.3 with
RSIZE_MAXassumed to beSIZE_MAX >> 1. A runtime-constraint violation only returns a negative value: the handler installed withset_constraint_handler_sis not invoked, and the console's default handler is unknown. An invalid multibyte%sargument is copied byte by byte instead of being reported as an encoding error, as invswprintf -
swprintf_s and vswprintf_s (libc) - follow C11 K.3.9.1.4 with
RSIZE_MAXassumed to be(SIZE_MAX >> 1) / sizeof(char16_t): the complete output, including the terminating null, must fit in the buffer, otherwise it is a runtime-constraint violation that setsbuffer[0]to zero and returns a negative value, and the buffer is not partially written. Argument and encoding checks matchsnwprintf_s -
strerror (libc) - follows the
libc.prxthat CUSA04865 and CUSA12435 ship, loaded on a PS4 Pro (firmware 10.71): 0 gives "No error", 1 to 94 the FreeBSD texts, any other number "error_code=0x%08x", and errno is left alone. The console's system libc, which homebrew without a bundledlibc.prxuses, answers differently ("No error information" for 0 and unknown numbers, 95 and 96 named).strerror_randperrorkeep "No error: 0" and "Unknown error: %d"; they are not measured. -
strerror_s and strerrorlen_s (libc) follow C11 K.3.7.4.2 and K.3.7.4.3, using the existing FreeBSD error messages and unknown-error formatting. Truncation writes an ellipsis when capacity exceeds 3; constraint failures leave the destination untouched and invoke the registered guest-ABI handler. The console's
RSIZE_MAX(SIZE_MAX >> 1here), nonzero returns (EINVAL22 for constraints,ERANGE34 for truncation), handler arguments and default abort behavior are unverified. Messages remain in the C locale, and guest errno is preserved. -
getw and putw (libc) follow FreeBSD 11.4 getw.c and putw.c: one 32-bit little-endian word,
getwreturns -1 for EOF or an error, andputwreturns 0 on success or -1 on error while holding the stream lock. They use the existing host-backed streams, so buffering and error timing follow the host CRT. Null streams throw and closed streams return -1 withEBADF, as existing byte I/O does. Console behavior is unverified. A payload process on a PS5 (firmware 13.60) gave the same results: two words written little-endian, -1 with end of file after them and for a 2-byte file, and -1 with the error flag forputwon a read-only stream -
vsnwprintf_s (libc) -
va_listcounterpart ofsnwprintf_s(C11 K.3.9.1.3): truncates to the buffer and returns the full length, with the same constraints and argument checks -
strndup and strdup (libc) return null with
errnoset toEINVAL(22) whensourceis null, preventing a host segfault on invalid guest input. POSIX.1-2008 and FreeBSD 11.4 libc leavestrndup(NULL)andstrdup(NULL)undefined (faulting). Thelibc.prxthat titles ship has no null check either (read from disassembly of PPSA13934's copy, not measured on a console):strdup(0x6b440) callsstrlenandstrndup(0x6b650) callsstrnlenon the source before allocating, so both fault on a null source there, exceptstrndup(NULL, 0), whosestrnlenreads nothing and which returns an empty copy; here it returns null withEINVAL. -
snprintf_s and vsnprintf_s (libc) - follow C11 K.3.5.3.5 and K.3.5.3.12 with
RSIZE_MAXassumed to beSIZE_MAX >> 1, assnwprintf_sdoes: a null buffer or format,sizeof 0 or aboveRSIZE_MAX, any%nand a null%s,%lsor%Sargument return a negative value and setbuffer[0]to zero when the buffer and size allow it; otherwise the output is truncated to the buffer and the full length is returned. A violation only returns: the constraint handler is not invoked, and the console's error value is unverified. Not confirmed on a PS5 title -
vsscanf_s (libc) - returns the count of items converted before an undersized
%s/%c/%[destination (0 when it is the first), not EOF. Not confirmed on a PS5 title -
setlocale (libc) knows only the "C" locale, as the
libc.prxthat titles ship does: it ignores the category, returns "C" for a null, empty or "C" name and a null pointer for any other, "POSIX" included, without settingerrno. Checked by running the copies bundled with PPSA07923 and PPSA13934 side by side for categories -40 to 79 and 2^30, not measured on a console. -
localeconv (libc, re-exported by libSceAmpr) - the 0x90-byte Dinkumware struct (empty monetary strings,
CHAR_MAXfor the 14charfields,decimal_point".",_False/_True"false"/"true") is read from thelibc.prxthat Astro's Playroom ships. It is a fixed C-locale table: whether the console changes it withsetlocaleis not known, and_Frac_grouping,_Frac_sep,_No,_Yesare empty strings as in that dump -
set_constraint_handler_s (libc, re-exported by libSceAmpr) - the default handler is assumed to abort; each module keeps its own handler state, so a handler set through one library is not seen by the other. Within a module every
_sfunction calls the installed handler, with the guest's calling convention.ctime_sandasctime_sfollow the Dinkumware 26-byte format withEINVALfor a buffer under 26 bytes, a null pointer or an out-of-range weekday/month;ctime_suses the guest time zone oflocaltime_s, and the console's exact checks are not verified -
qsort_s (libc) sorts as
qsortdoes and passes its context as the comparator's third argument, as FreeBSD 13'sqsort_s(C11 Annex K) does. It returnsEINVALafter calling the constraint handler whennoresexceedsRSIZE_MAX, or whennis not 0 andaorcmpis null; FreeBSD 11, which the rest of libc follows, has noqsort_s. An element size of 0 sorts nothing, where FreeBSD loops over the same address. The console's behaviour is unverified; the Quake homebrew (#6698) imports it from itslibOSMesa.so.8 -
__udivti3 (libc, re-exported by libSceAmpr) - the hardware traps on a zero divisor; this throws instead
-
__umodti3 (libc) - the hardware traps on a zero divisor; this throws instead
-
__modti3 (libc) - the hardware traps on a zero divisor; this throws instead
-
snprintf and vsnprintf (libc) accept any size up to
SIZE_MAX, as a payload process on a PS5 (firmware 13.60) does. A null buffer with a nonzero size throws here, where the console writes through it, and a result longer thanINT_MAXthrows, which was not measured
Functional
-
_Cosh (libc) follows the two-argument Dinkumware CRT helper used by
std::complex, including a scaled large-finite-input path; the signature, exact result and floating-point exception behavior are not verified against the console. -
std::thread::hardware_concurrency (libc, NID
CHrhwd8QSBs) returns the host'sstd::thread::hardware_concurrency()(which may be 0 when unknown) instead of the console's processor count; no reference implements it and the value a title expects (PPSA34015 imports it) is unverified. -
fmodf (libc) - console
errno, exception flags and NaN payloads remain unverified. The wrapper preserveserrnolike the existing doublefmod, following FreeBSD 11.4e_fmodf.c; hoststd::fmodstill supplies the result and exception flags. -
_LCosh and _LSinh (libc) follow the two-argument Dinkumware CRT helpers
y * cosh(x)andy * sinh(x), including scaled large-finite-input paths; the signatures are confirmed by Dinkumware's publicymath.h, but the exact console results, range-error thresholds and floating-point exception behavior are not verified. -
_FSinh and _FCosh (libc) finite results are not verified bit for bit against the console: the scaled product uses the host's binary64 hyperbolic function before rounding to binary32, rather than the float rational and
_FExpsequence in PRAGMATA's bundledlibc.prx. NaN preference and payloads, signed zeros, infinite operands and the range-error thresholds follow that binary's instructions; the systemlibSceLibcInternalimplementation and nondefault flush-to-zero modes are unverified. AgainstlibSceLibcInternal.sprxon a PS5 (firmware 13.60), 12 of 36 measured results differ by one unit in the last place (_FSinh(0.5, y)and_FCosh(1, y),_FCosh(10, y)for y of 0.5, 1 and 2); zeros, the overflow to infinity at 89 and 90 and the tiny-argument results matched -
_Divide, _FDivide, _Recip and _FRecip (libc) follow the Dinkumware CRT division and reciprocal contracts with IEEE-754 signed-zero, infinity and NaN behavior. The exact console results, signaling-NaN handling and floating-point exception behavior are not verified.
-
sscanf_s and vsscanf_s (libc) return
EOFwhen a reached conversion has a null destination, stopping before further assignments, following C11 K.3.5.3.7 and K.3.5.3.14. Earlier assignments are retained. The constraint handler is not invoked, and console behaviour is unverified. -
memmem and memccpy (libc) -
memmemsearches for needle occurrences in memory buffers andmemccpycopies buffers until a character match, following FreeBSD and POSIX specifications; behavior is unverified on console. Neither is exported bylibSceLibcInternal.sprxon a PS5 (firmware 13.60), so a payload process cannot call them there. -
strtof and wcstof (libc) round to the nearest float, where the console's
_StofinlibSceLibcInternal.sprx(0x734a0) does not always: about a third of decimal inputs within a relative 2^-51 of a point halfway between two floats, and some hexadecimal inputs with more than 24 significant bits, come out one unit apart in the last place (1e-40is0x1.16c3p-133there and0x1.16c2p-133here). The console's own rounding is not reproduced -
strtof and wcstof (libc) -
ERANGEfollows thelibc.prxbundled with PPSA08427 (J03547016) and PPSA03671 (J03848509), both run side by side with this libc on a Linux host: a decimal input whose result is a nonzero subnormal sets it only when inexact, a hexadecimal one only when it lies below the smallest subnormal and rounds up to it, and underflow to zero and overflow set it. Both builds also take aNAN(...)payload instrtoffrom the sequence's leading digits (nan(0x123)is0x7fc00123,-nan(5)is0xffc00005); the sign is kept here, the payload is not.libSceLibcInternal's_Stofdiffers: noERANGEfor hexadecimal input, a positivenan, and after a minus sign a failed parse is-0.0for a lone sign and+0.0before aninf/nanspelling, the reverse oflibc.prx. DecimalstrtodsetsERANGEfor a nonzero subnormal in J03547016 but not in J03848509 orlibSceLibcInternal, whose result is kept. -
Guest time conversions rewrite the host process
TZwhen a guest changes its ownTZ, so other host consumers of that variable (including libSceRtc local time) shift too; code outside libc does not take the zone lock. Zone syntax follows the host C runtime, not FreeBSD: Windows only accepts thestd offset [dst]form, treats an empty guestTZasUTC0and reads:Europe/Berlinas an abbreviation (for examplerop); only Linux honours zone file names. If the guest accepts aTZvalue that the host rejects (on Windows, for example, one longer than the host environment limit), conversions throwstd::runtime_error. Atm_zonesaved before aTZchange can refer to storage now holding the new zone name;strftimedoes not read it. -
cbrtf (libc) is FreeBSD msun's
s_cbrtf.c: a 5-bit first guess from the exponent bits, then two Newton steps in double precision. It sets noerrno, as thelibc.prxthat titles ship does not. Floating-point exceptions are not compared. It gives the same bits as thecbrtfof thelibc.prxbundled with PPSA07923, PPSA05916 and PPSA13934 for all 2^32 arguments, run on a Windows host. -
__isfinitel - classifies the x87 80-bit exponent without narrowing to double or performing floating-point comparisons, following FreeBSD msun. The signature and classification follow FreeBSD; console behaviour is unmeasured. Requires little-endian x87 long double stored in 16 bytes, as the existing guest long-double ABI does. On a PS5 (firmware 13.60)
__isfinitelreturned 1 for 0, -0, 1, a denormal and ±1e4000, and 0 for a NaN and for the maximum exponent without the integer bit, which this classification gives too -
logf (libc) is FreeBSD msun's
e_logf.c, withvzeroread at run time as msun'svolatileread does. As in thelibc.prxthat titles ship,errnois set toERANGEfor ±0 and toEDOMfor every argument with the sign bit set, negative NaNs and -inf included; positive NaNs and +inf leave it unchanged. Floating-point exceptions are not compared. It gives the same bits anderrnoas thelogfof thelibc.prxbundled with PPSA07923 and PPSA05916 for all 2^32 arguments, run on a Windows host. The FMA-contracted build bundled with PPSA13934 differs from it by 1 ulp on 961,881 arguments, so no single port matches every title's last bit. -
Windows direct-memory backing reserves a paging-file section. A direct memory page mapped at one guest address is private write-watched memory at that address and is not committed in the section; a page mapped at a second address moves its whole resident run into the section (committed there) and every address becomes a view of it, tracked by write protection. An unmapped resident page that is not all zeros is kept in a private reservation until it is mapped again. Committed section contents remain across unmap/remap and are released when the backing and its remaining views are destroyed. A host import alias over part of a resident run is refused. Linux retains its existing backing. Peak gameplay memory and Windows versions other than the tested host remain unverified.
-
getc_unlocked, putc_unlocked, getchar_unlocked, putchar_unlocked (libc) - byte I/O uses the host unlocked operations; guest EOF/error flags and macro counters follow
FileStream. Linux status queries usefeof_unlocked/ferror_unlockedbecause glibcfeofandferrorcan acquire the stream lock. Windows uses the CRT no-lock byte operations andfeof/ferror, verified under a held stream lock on UCRT. Console behavior and other host CRTs remain unverified. -
strcasecmp and strncasecmp (libc) - console locale behavior is unmeasured. They use the guest's C-locale case conversion and return -1, 0 or 1, the sign of the folded unsigned-byte difference, as the
libc.prxbundled with eleven titles does (FreeBSD 11.4's strcasecmp.c returns the difference itself); other guest locales are not implemented. -
feof_unlocked and ferror_unlocked (libSceLibcInternal, implemented in libc) have no console measurement. The firmware 3.20 export inventory names NIDs
NuydofHcR1wandyxbGzBQC5xA. They follow FreeBSD 11 feof.c and ferror.c, returning 0 or 1 from the stream status without acquiring the stream lock. They inherit the host CRT limitations of the unlocked byte operations above. -
gets (libSceLibcInternal, implemented in libc, NID
Ok+SYcoL19Y) follows FreeBSD 11.4 gets.c for byte input, newline removal and EOF, without PS5 verification or known module/library versions. It reads the guest stdin under its stream lock and preserves the destination when EOF or a read error precedes any byte; a partial line is terminated and returned. A null destination throws; its console behavior is unverified. It emits no diagnostic; FreeBSD writes a warning once, and the console's diagnostic behavior and stream orientation are unverified. -
fgetws, getwc, getwchar, putwc, putwchar (libc) - wide stream I/O encodes and decodes wide characters as UTF-8 on the underlying byte stream;
fgetwsfollows thelibc.prxthat titles ship (read from disassembly of three titles' copies, PPSA13934, PPSA07923 and PPSA05916, not measured on a console): it returns null for a size below 2 without writing the buffer,errnoor the stream, stores eachfgetwcresult as its low 16 bits until a newline (compared on all 32 bits),-1or a full buffer, and null-terminates and returns the buffer whenever it stored a character, also after a read error. A null buffer or stream, which the console dereferences, setserrnoto 22 here. The calls the console makes before and after reading, and the one it makes when bits 0xC of the stream's second flag byte are set, are not reproduced. Console wide stream buffering andfgetwc's decoding remain unverified. -
Closed standard descriptors (libc) - console startup descriptors remain unmeasured. Halo: Combat Evolved's PS5 host expects 0 to 2 closed and opens its eboot on them; here it leaves them open. Missing descriptors are opened on
/dev/nullon Linux/macOS orNULon Windows, read-only for 0 and write-only for 1 and 2. Windows also reattaches the UCRT streams before any guestFileStreamcaptures their descriptor. Initialization runs before libraries depending on libc; no earlier libc initializer may consume a missing standard descriptor. Theguest_closed_standard_descriptorsandguest_closed_standard_descriptors_windowstests cover startup. -
Guest path names (libc) are checked against FreeBSD 11.4's
NAME_MAX(255 bytes per component) andMAXPATHLEN(1024 bytes with the terminating NUL) before they are resolved, and a longer name fails withENAMETOOLONG, asnameidoes. The host's own limit still applies below that: on Windows a path whose host form exceedsMAX_PATHfails withENOENT. A backslash counts as a path separator for the component length, because the guest path resolver turns it into/before the host call, while FreeBSD treats it as an ordinary name character, so a 301-byte name that contains one is accepted here.sceNgs2ParseWaveformFileresolves its path without a try block, so a long component ends in an uncaughtfilesystem_errorthere where a missing file ended in aruntime_error(both end the title). A path through a regular file (file/x,file/) is not alwaysENOTDIRon the calls that resolve it. None of this was measured on a console. -
Guest path names (libc) fail on macOS with
ENAMETOOLONGonce their host form, the root directory followed by the guest path, reaches 1024 bytes, so a guest path of 1023 bytes, which FreeBSD accepts, fails there under any root directory -
ftrylockfile (libc) - tries the host CRT stream lock that
flockfile,funlockfileand each locking host stdio call on the stream take, so it returns -1 while another thread holds the stream throughflockfileor is inside such a call, and 0 when it acquires the lock or already owns it (recursively, released by as manyfunlockfilecalls), as FreeBSD 11_ftrylockfiledoes. A libc export holds the lock only for each host call it makes, not across several host calls or while it updates the guestFILEprefix. Linux uses glibcftrylockfile. UCRT has no try variant of_lock_file, so Windows callsTryEnterCriticalSectionon theCRITICAL_SECTIONthat_lock_fileenters, found through the private UCRT stream layout (__crt_stdio_stream_datainucrt/inc/corecrt_internal_stdio.h, unchanged from Windows SDK 10.0.19041 to 10.0.26100); another CRT or a change of that layout breaks it. A null or closed stream throws where FreeBSD dereferences it -
sinf, cosf, tanf and sincosf (libc) are FreeBSD msun's
s_sinf.c,s_cosf.cands_tanf.cover its double-precision kernels ande_rem_pio2f.c, withk_rem_pio2.c(for|x| >= 2^28 * pi/2) ported only for one input word andprec0;sincosfreturns the two. As in thelibc.prxthat titles ship, only an infinite argument setserrno(EDOM); NaN leaves it unchanged. Floating-point exceptions are not compared. All four give the same bits anderrnoas the copies bundled with PPSA07923, PPSA05916 and PPSA13934 for all 2^32 arguments, run on a Windows host. -
fgetln (libSceLibcInternal, implemented in libc) throws: in the console's
libSceLibcInternal.sprx(SDK 4.5x build ofJ03950001, 0x5FE80) the export is ahltinstruction, which faults in user mode and ends the process -
Wide-character conversions (libc) - The wide
wcsto*functions parse the ASCII prefix of the guest string, as the C locale accepts only ASCII white space and digits. The multibyte conversions follow the C locale of the console'slibSceLibcInternal.sprx: UTF-8 of at most three bytes, so_Getmbcurmaxreturns 3, overlong forms and encoded surrogates decode, and four-byte forms are invalid.__mb_cur_maxstays 1 like the console's variable;___mb_cur_maxis not in that module and also stays 1, unverified. Other locales are not supported. With a null destination,wcstombsreturns the byte count; the console's count is wrong when the capacity argument equals the running count, because it reads past the terminator -
realpath (libc) follows FreeBSD 11's
realpath.cexcept for symlinks. The console's guest filesystem has none, so a host symlink under the guest root is followed for existence and type but not expanded, and..drops the previous component lexically, asResolvedoes for every guest path. Through a host linklinkdir -> sub/deep,linkdir/../xgivesxbeside the link, not beside its target. -
strtod, strtof, strtold (libc) -
strtodandwcstod(andatof, which callsstrtodhere) read aNAN(...)sequence as FreeBSD 11's gdtoahexnandoes: hexadecimal digits with an optional0x, blanks starting a new 32-bit word, the first 16 digits kept and the payload cut to 52 bits, a zero payload made 1, any other character giving the default NaN but consuming up to the closing parenthesis, and an unterminated sequence keeping its payload while consuming onlynan. The payload goes under the quiet bit with the sign, as in gdtoa'sstrtod.strtof,wcstof,strtoldandwcstoldreturn the positive default quiet NaN (0x7fc00000; exponent0x7fff, significand0xc000000000000000) whatever the sign and the sequence, and consume a sequence of letters, digits and underscores up to its closing parenthesis. The console'slibSceLibcInternal.sprx, called from a payload process on a PS5 (firmware 12.70), gives these results for the 64 NaN inputs tried throughstrtodandstrtof, 50 of them also throughstrtoldandwcstod, and 18 throughwcstofandwcstold, and, throughstrtod, fornan(1 2)and fornan(followed by NUL and); that a title's ownlibc.prxdoes the same is assumed. Inhexnan, a blank after a payload digit, while a 32-bit word of the payload is still free, starts a blank skip that has no NUL check, so when only blanks follow up to the terminating NUL the console'sstrtodreads past it:nan(1followed by NUL and)consumes the)after the NUL, so the result depends on the bytes after the string, and here that case throws. A blank right afternan(is skipped with a NUL check, and the console stopped at the NUL there -
remquol (libc) leaves
*quounchanged when the result is NaN; thelibc.prxbundled with PPSA08427 writes a value that depends on earlier x87 state (remquol(1, 0)stored 0 on a first call and 4 on later ones) -
_Stod, _Stodx, _Stofx, _Stoldx, _Stof and _Stold (libSceLibcInternal, implemented in libc) throw for hexadecimal text with a non-zero power-of-ten scale. The console's result there does not follow a simple rule:
0x3with a scale of -1 gives 0.29999999999999999. A nonzero subnormal result sets neithererrnonor the error argument, as measured in firmware 13.60'slibSceLibcInternal.sprxrun in a Linux host process, not on a console; there about a fifth of decimal inputs with a subnormal result come out one unit apart in the last place (_Stodx("2.2250738585072014e-308", 0)is the largest subnormal), which is not reproduced -
_LDivide, _LRecip and _LLog (libc) follow the public Dinkumware CRT contracts: long-double division and reciprocal, and the
_LLogbase flag (0natural, positive base 10, negative base 2). The exact console results, signaling-NaN behavior and floating-point exception behavior are not verified. -
Long double math in MathExact.cpp (libc) was compared with the
libc.prxbundled with PPSA08427 only for canonical 80-bit encodings; unnormals, pseudo-denormals and pseudo-infinities passed to arithmetic functions (not the classifiers) follow the host x87 instead of the console -
atanf (libc) is FreeBSD msun's
s_atanf.c, withatanlo[3]read at run time as msun'svolatiledoes. Tiny arguments returnxwithout raisingFE_INEXACT. It gives the same bits as theatanfof thelibc.prxbundled with PPSA07923, PPSA05916 and PPSA13934 for all 2^32 arguments, run on a Windows host, and none of those setserrno. Whether other titles' builds differ is unverified. -
rename, sceKernelRename (libc, libkernel) fail a last path component of
.or..withEINVAL, after FreeBSD 11.4'slookup, which refuses.and, through itsISDOTDOTandISLASTCNflags,..as the last component for theDELETEandRENAMEoperations.unlink,rmdir,sceKernelUnlinkandsceKernelRmdirfollow the same rule. Both arguments are checked before any host access, sorenameof a missing source ontodir/.answersEINVAL, where FreeBSD'skern_renameatlooks the source up first and answersENOENT. A backslash counts as a path separator here (the guest path resolver turns it into/); FreeBSD treats it as an ordinary name character. None of this was measured on a console. -
nan and nanf (libc) return fixed quiet NaN values (
0x7fc00000and0x7ff8000000000000), ignoring the tag argument as observed in the console'slibc.prx(both exports load a global constant viamov rax,[rip+..]; vmovsd xmm0,[rax]; ret); VLC (PPSA85300) imports nanf. -
jrand48 and mrand48 (libSceLibcInternal, implemented in libc) return the high 32 bits of the new 48-bit state zero-extended to 64 bits, as the console's
libSceLibcInternal.sprxdoes when called from a homebrew payload process on a PS5 (firmware 12.70): a result with bit 31 set comes back between 0x80000000 and 0xFFFFFFFF, as FreeBSD 11.0'sjrand48andmrand48return it; FreeBSD 11.1 and later cast it toint32_t(jrand48,mrand48), which makes it negative.lrand48andnrand48matched FreeBSD there. Whether a title's own libc does the same is unverified -
seed48 and lcong48 (libSceLibcInternal, implemented in libc) follow the console's
libSceLibcInternal.sprx(SDK 4.5x build ofJ03950001, 0x6F460 and 0x6F420): the multiplier and addend are shared by the wholerand48family, includingerand48,nrand48andjrand48on a caller's seed;lcong48sets them with the seed, andsrand48andseed48restore 0x5DEECE66D and 0xB. Unlike FreeBSD,seed48returns a static copy of the new seed rather than the previous one. A null argument, which the console dereferences, throws -
drand48 and erand48 (libc) return the upper 32 bits of the new 48-bit state over 2^32, as the
libc.prxbundled with eleven titles does on 10,000 values per function; FreeBSD returns all 48 bits. Whether the console'slibSceLibcInternal.sprxcopy does the same is unmeasured. -
lgamma and signgam (libc) -
lgammareturns the host'slgammaand stores the sign of Γ(x) in the exportedsigngam(NID5gOOC0kzW0c, starting at 0 as in FreeBSD 11'ss_signgam.c) by the rules ofe_lgamma_r.c: -1 for -0 and for a negative non-integer whose floor is odd, 1 otherwise, including poles, infinities and NaN. Taken from the FreeBSD source, not measured on the console. The NID is in the aerolib symbol list; no title is known to readsigngam. PPSA99004 importslgamma -
coshf (libc) follows FreeBSD msun's
e_coshf.c, using theexpm1fandexpfpaths already inlined forsinhfbelow; the Windows UCRT differed from thelibc.prxcopies in PPSA07923 and PPSA13934 on 500,554 and 500,614 of 20 million inputs in the #9707 survey. The exact systemlibSceLibcInternalbehavior and the FMA-contractedlibc.prxresults remain unverified -
expm1 (libc) does not compare floating-point exceptions, and its double inputs are checked by sampling, not exhaustively. It is FreeBSD msun's
s_expm1.cwithhugeread at run time, and setsERANGEonly when a finite argument overflows to infinity, as the titles'libc.prxdoes. It gives the same bits anderrnoas theexpm1bundled with PPSA07923 and PPSA05916 on 320 million random and 47,222 structured arguments on a Windows host; the FMA-contracted build bundled with PPSA13934 differs on 43,433 of the random ones. -
log1p (libc) does not compare floating-point exceptions, and its double inputs are checked by sampling, not exhaustively. It is FreeBSD msun's
s_log1p.c. As the titles'libc.prxdoes, it setsEDOMat msun's NaN return (arguments below -1,-infand negative NaNs) and leaveserrnoalone forlog1p(-1), where it returns-inf. It gives the same bits, NaN payloads included, anderrnoas thelog1pbundled with PPSA07923 and PPSA05916 on 320 million random and 68,216 structured arguments on a Windows host. The FMA-contracted build bundled with PPSA13934 differs on 73,682 of the random ones. -
nextafter and nextafterf (libc) do not compare floating-point exceptions, and their inputs are checked by sampling, not exhaustively. They are FreeBSD msun's
s_nextafter.cands_nextafterf.c, withERANGEat msun's overflow and underflow returns as the titles'libc.prxsets it; a step away from zero returns early and sets none. The copies bundled with eleven titles give the same bits anderrnoon 4,000,784 sampled pairs per function on a Windows host. -
tanh (libc) does not compare floating-point exceptions, and its double inputs are checked by sampling, not exhaustively. It is FreeBSD msun's
s_tanh.cover this file's msunExpm1, withtinyread at run time; it sets noerrno, as the titles'libc.prxdoes not. It gives the same bits as thetanhbundled with PPSA07923 and PPSA05916 on 16 million random and 68,211 structured arguments on a Windows host; the FMA-contracted build bundled with PPSA13934 differs on 1,054 of them. -
sinhf (libc) follows the
sinhfof thelibc.prxbundled with PPSA28319 (disassembly): FreeBSD msun'se_sinhf.cfrom before__ldexp_expf, which squaresexpf(|x| / 2)betweenlogf(FLT_MAX)and the overflow threshold, calling msun'sexpm1fandexpf, whose paths for the arguments it passes are inlined here. Past the threshold Sony's error helper setserrnotoERANGEbefore returningx * 1e37; msun itself sets noerrno. That code, run on a Windows host, gives the same bits as this implementation for every finite argument up to the threshold, of both signs; it was not run on Linux. That the systemlibSceLibcInternal, which Ruffle (PPSA68091) and RetroArch (PPSA99169) import while bundling a zero-returnlibc.prx, has the same code is assumed -
acoshf (libc) exact system
libSceLibcInternalbehavior and FMA-contracted copies remain unverified. Positive finite inputs use msun range handling and this file'slogf; the middle range divides -1 before addition under directed rounding. Bits, errno and SSE/x87 status and controls match the supplied PPSA03766/PPSA07923 copies on 236,592 positive finite calls (4,929 inputs, four rounding modes, DAZ/FTZ combinations and sticky flags); these samples are not exhaustive. Invalid-domain errno and SSE/x87 invalid flags are checked against two supplied bundled copies; those samples are not exhaustive either. -
acoshl, asinhl, atanhl, atan2l, cbrtl, erfl, erfcl, expm1l, hypotl, log1pl, log2l, lgammal, tgammal, j0f, j1f, jnf, y0f, y1f and ynf (libc) return the host C library's results (the float Bessel functions through its double ones), not those of the console's own code, so the last bits can differ (
y0f,y1f,ynffollow the console at+-0). As in the console'slibSceLibcInternal.sprx(SDK 4.5x build ofJ03950001, 0x56410),lgammaldoes not store the sign insigngam;hypotlreturns infinity for an infinite argument even when the other is a NaN, as C11 F.10.4.3 requires. hypot3, hypot3f and hypot3l follow that module (0x51D30, 0x520D0, 0x52420): the first NaN of x, y and z is returned unchanged, any infinity gives positive infinity, a zero argument gives the two-argument hypot of the other two, and otherwise the largest magnitude is combined with the hypot of the other two -
asinl (libc) follows the
asinlof thelibc.prxbundled with PPSA28319 (disassembly), FreeBSD msun'se_asinl.cwith the ld80invtrig.ccoefficients, which the binary holds as the same 80-bit constants. For |x| > 1, the infinities and NaN, Sony's error helper raisesFE_INVALIDand setserrnotoEDOMbefore returning(x - x) / (x - x), so a NaN argument also setsEDOM; msun itself sets noerrno. 20 million random arguments in (-1, 1) give the same bits as that code run on a Windows host. The result needs the x87 precision control at 64 bits, as FreeBSD amd64 starts threads (control word0x37f), but guest threads do not set it: on Windows a thread started by_beginthreadexruns at 53 bits (0x27f), whereasinl(0.75)returns mantissa0xd91a98ae3406e000instead of0xd91a98ae3406e041. The systemlibSceLibcInternalthat RetroArch (PPSA99169) imports is assumed to have the same code -
acosf (libc) - for |x| > 1 and NaN the console stores
EDOMand raisesFE_INVALIDbefore returning(x - x) / (x - x); here those return the same NaNs and leaveerrnoas it was (#3688). For |x| <= 1 it follows theacosfof thelibc.prxbundled with PPSA28319 (disassembly): FreeBSD msun'se_acosf.c, with the same constants,pio2_loread at run time and the0xfffff000mask onsqrtf(z), which the binary holds. Titles ship different builds of thatlibc.prx: PPSA13934's is FMA-contracted and differs from this one by 1 ulp on 129,170 inputs (#9668), so no single port matches every title's last bit. -
atanhf (libc) follows the
atanhfof thelibc.prxbundled with PPSA28319 (disassembly): FreeBSD msun'se_atanhf.ccalling msun'slog1pf, whose paths for the positive arguments it passes are inlined here; the exportedlog1pfis still the host's. For |x| > 1, the infinities and NaN, Sony's error helper raisesFE_INVALIDand setserrnotoEDOMbefore returning(x - x) / (x - x), and for |x| = 1 it raisesFE_DIVBYZEROand setserrnotoERANGEbefore returningx / 0; msun itself sets noerrno. That code, run on a Windows host, gives the same bits,errnoand floating-point exceptions as this implementation for all 2^32 arguments; it was not run on Linux. That the systemlibSceLibcInternal, which Dusklight (PPSA99305) imports while bundling a zero-returnlibc.prx, has the same code is assumed -
j0, j1, jn, y0, y1 and yn (libc) call the host's
sin,cos,sincosandlog, so the last bits can differ from the console. The rest follows the systemlibc.prxbuilt asJ03093718(disassembly), which given the console's ownsin,cos,sincosandloggave the same bits and exception flags for 90 million arguments: msun'se_j0.c,e_j1.cande_jn.cwith each multiply-add the console fuses done as one FMA. As in that release, before FreeBSD commit186f620727,y0,y1andynreturn-inffor ±0 and0x7ff8000000000000for a negative argument without raising an exception. The order wraps at 32 bits as on the console:jn(1 << 30, x)never returns for 2^-29 <= |x| < 2^30 -
getenv, setenv, unsetenv, putenv (libc) keep one guest environment that starts empty, like the
environlibkernel exports, and never reads or changes the host's. A title sees only the variables it sets itself; host variables such asHOMEandPATHname host paths a guest cannot use (PS5MC, PPSA99420, exits withXBMC_Run returned -1when it gets the host'sHOME). Whether the console starts a title with any variables set is not measured -
mergesort (libSceLibcInternal, implemented in libc) checks its arguments like the console's
libSceLibcInternal.sprx(SDK 4.5x build ofJ03950001, 0x762D0): an element size under 4 setserrnotoEINVAL(22) and returns -1, an empty array returns 0, and a failedmallocof the count times the size plus 8 bytes returns -1. The sort is a stable bottom-up merge, not FreeBSD's natural merge with its insertion sort for short runs, so the comparator is called in a different order and number of times; the result is the same for a consistent comparator. A null base or comparator and a size product that overflows, which the console does not check, throw -
On macOS the write watch write-protects watched pages and records the first write to each from a SIGBUS/SIGSEGV handler, because macOS has no counterpart to
userfaultfdwrite-protect orMEM_WRITE_WATCH. The protection is real, so:- a system call that writes into a protected page fails with
EFAULTinstead of faulting.GuestArena::HostWritelifts the protection for the calls it brackets (file reads and socket receives), as Host writes into guest memory requires; a host call that writes guest memory without it (clock_gettimeand the like into a watched page) fails; - code that changes the protection of watched pages, or maps over them, has to call
GuestWriteWatchProtectionChanged, and code that asks the kernel for a page's protection has to askGuestWriteWatchProtectionfirst, since a watched page is read-only between a collection and the next write. Nothing calls either yet: the macOS memory paths are not in the tree; - the handler is installed when libc loads and passes other faults to the handler installed before it. A handler installed later has to do the same.
- a system call that writes into a protected page fails with
-
On macOS the guest arena finds host regions with
mach_vm_regionand reserves the rest with a fixedmach_vm_map, which fails instead of replacing, as macOS has no/proc/self/mapsorMAP_FIXED_NOREPLACE. The kernel keeps the range up to 0x70_0000_0000 unmappable for every process (max protection none). It starts at 0x10_0000_0000 on macOS 26.5 (M1 Pro, arm64 and x86_64 under Rosetta) and at 0xF_C000_0000 on macOS 27 (27.0.1 on an M4 Pro, arm64 and x86_64 under Rosetta; 27.2 on an M2 Max, arm64). With dyld and the Rosetta runtime near 0x2_0200_0000 and 0x3_0400_0000, a guest mapping fixed inside those ranges fails where the console places it; mappings without a fixed address are placed around them. -
sceKernelSync (libkernel) flushes only the host files and directories recorded through
RecordWrittenPath_nid_no_patch(the libc, libkernel, save data and app content file functions) and their changed parent directories. A host write in a library that does not record its path is not flushed bysceKernelSync -
sceKernelSync (libkernel) on Windows skips a recorded file it cannot open for writing (read-only, or held open by a handle that denies write sharing), since
FlushFileBuffersneeds a writable handle. On Linux, it skips a recorded file it cannot open for reading. Such a file's data may not reach the disk before a host crash; the guest reads the same data either way -
fopenandfreopen(libc) on Windows:freopenonto a directory in a read mode fails withEACCES, where FreeBSD opens it. Reading a directory stream fromfopenfails withEISDIRand sets the error indicator on every host, as FreeBSD 11 tmpfs does; FreeBSD 11 FFS returns the directory's raw blocks instead, and what the console's filesystems return is unverified -
readdir and readdir_r (libc) on Windows report, on a file system whose directory listing carries no file ID (the entry's ID is 0, or the volume refuses
FileIdBothDirectoryInfo), a nonzerod_filenohashed from the entry name and the directory's file index, which differs fromlstat'sst_ino. Not reproduced: NTFS lists an ID for every entry -
Writes on an update stream (libc) on Windows go through the UCRT, which drops a write that directly follows a read without
fflush,fseek,fsetposorrewindunless the read reached end of file:fwritereturns 0, andfputcandfprintfreport success, without the error indicator. FreeBSD 11.4's__swsetupdiscards the read buffer and writes at the descriptor's offset, past a read-ahead ofst_blksizebytes for a regular file, so where the console writes depends on its block size. Reads directly after writes follow FreeBSD's__srefill -
fopenof /dev/random and /dev/urandom (libc) in a read mode returns a stream over the host null device whosefreadfills the buffer with host OS entropy, as the libkernel random device descriptors do. Only the exact paths are recognised, the stream has no descriptor,setvbuf,feof,ferror,clearerr,flockfile,ftrylockfile,funlockfileandfclosework on it,fflushreturns 0 as on a FreeBSD read-only stream, and every other stream function throws, as does opening the paths for writing.freopenonto these paths still fails withENOENT. ProsperoTV (PPSA99003) reads its phone pairing code this way -
RemoveTree (libc) removes a host tree itself on Windows instead of calling
std::filesystem::remove_all: MinGW libstdc++ up to GCC 16 does not treat a reparse point as a link, soremove_allrecurses through it and never terminates, and any tree that holds a symlink hangs. It builds the entry list fromGetFileAttributesW, removes a reparse point as the link it is rather than its target, clears the read-only attribute and retries asremove_alldoes, and reports the first native error. The libstdc++ fix is in GCC 17 (commit 88856ad855ae); other hosts callremove_all. -
The
_Unwind_Contextthat_Unwind_Backtracepasses to its callback starts with a table laid out like LLVM libunwind'sAbstractUnwindCursorvirtual table, because homebrew built with the ps5-payload-dev SDK links libunwind statically and reads the frames with its own_Unwind_GetIP(__unw_get_reg), which calls through that table. Only register reads and writes (validReg,getReg,setReg, withUNW_REG_IPandUNW_REG_SP),validFloatReg(always false) andisSignalFrameare answered; the other slots throw. The layout follows libunwind'sUnwindCursor.hpp, not the console's ownlibSceLibcInternal, which is not checked -
The Windows libc exception unwinder cannot run arbitrary Windows exception or termination handlers. It accepts GCC C and C++ handlers identified by a project's
.sehgccsection and interprets their LSDA with the guest personalities. A handler in an unmarked module, such as a C++ cleanup through__gxx_personality_seh0inlibstdc++-6.dllor a system DLL function likeqsortinucrtbase.dll(Windows 10 22H2), still stops unwinding and the exception is reported as unhandled. SjLj exceptions are unsupported; Scoop GCC 15.2.0 selects them when SEH is disabled and lacks the System V ABI SEH compiler fix carried by MSYS2. -
The libc SSE4a trap emulation on Windows is superseded by
--to-inteland remains only until the relinked title has been verified without it. -
getifaddrs (libc) lists host interfaces and IPv4/IPv6 addresses in FreeBSD's layouts. Windows and macOS omit per-interface AF_LINK entries and interfaces without an address; on Windows the flags are only
IFF_UP,IFF_RUNNING,IFF_LOOPBACK,IFF_BROADCASTandIFF_MULTICAST(PPP/tunnel adapters omit broadcast and point-to-point, and an unspecified prefix length gets a host mask). On Linux,AF_LINKif_datamaps non-Ethernet/loopback/PPP types toIFT_OTHER(1) and leavesifi_omcasts,ifi_noproto,ifi_hwassist,ifi_metric,ifi_epoch,ifi_lastchange, unknown baud rates and address-entryifa_dataas 0/null; a point-to-point address without a peer losesIFF_POINTOPOINT, and an omitted IPv4 broadcast is derived from the netmask. Interface names are the host's (if<index>on Windows above 15 bytes). -
gethostname (libc) reports an empty name without querying the host machine name, as a payload process read on a PS5 (firmware 13.60): 0 with only the terminator for a 256-byte and a 1-byte buffer. Buffer copying, null queries and
ENAMETOOLONGfollow FreeBSD gethostname; whether a title process or a console with a configured name reads another name, and the integration withsysctland name resolution, are unverified and not implemented. -
wctomb_s, wcrtomb_s, mbstowcs_s and mbsrtowcs_s (libc) follow the console's
libc.prx, but convert in the single-byte C locale ofwctomb,wcrtombandmbrtowc(MB_CUR_MAX1): a character above 0xFF isEILSEQinwctomb_sandwcrtomb_s, and each byte is one wide character inmbstowcs_sandmbsrtowcs_s. The locale of a title'slibc.prxwas not measured. Thewcrtomb_spath where a character does not fitsmaxand theEILSEQpaths ofmbstowcs_sandmbsrtowcs_sare not reached -
wcsrtombs_s (libc) follows the console's
libc.prx, but a runtime-constraint violation only returns its error: the default handler there writes the message and a newline tostderrbefore returning, which is not done here. Conversion uses the C locale ofwcrtomb(MB_CUR_MAX1), so the path where a multibyte character does not fit the remaining space is not reached -
c16rtomb, c32rtomb, mbrtoc16 and mbrtoc32 (libSceLibcInternal, implemented in libc) - a null state, which the console dereferences, throws. Otherwise they follow the console's
libSceLibcInternal.sprx(0x7D480 to 0x7D920), read from disassembly: UTF-8 whatever the locale, up to six bytes per sequence, with no surrogate or 0x10FFFF check when encoding. The state holds the value in its first eight bytes and the expected byte count (0x10 for a pending low surrogate, 1 for a stored high one) as 16 bits at 0xA.mbrtoc16builds a low surrogate from the value the state last held,c16rtombreturns -1 withouterrnofor an unpaired high surrogate, andEILSEQis 86. On a PS5 (13.60) the system library's default locale converts UTF-8 inwcrtomb,wctomb,mbrtowcandmblen; a title's bundledlibc.prxdoes too untilsetlocaleinstalls a table (disassembly) -
The narrow printf family (libc) follows a payload process on a PS5 (firmware 13.60): each 16-bit unit of a
%lsor%lcargument is encoded alone, so a surrogate pair gives two 3-byte sequences; a NaN keeps its sign and the+and space flags, and0pads it with spaces; argument positions above 99 work (up to 400 measured; above 65535 throw here); an unknown conversion such as%mor%kprints%and its letter, padded to the width, without taking an argument. The guest'swcrtomband locales are not used. On Linux the formats without those conversions or a floating-point conversion go to glibc, which prints NaN the same way. The_sforms throw for an unknown conversion,%mincluded, which was not measured -
%a and %A (libc) format a finite double as the
libc.prxthat titles ship does, instead of through the host: a subnormal is normalised (0x1p-1074), a shorter precision rounds half up on the dropped hexadecimal digits and a carry stays in the leading digit (%.3aof0x0.fffffffffffffp-1022is0x2.000p-1023), and-overrides0. Checked by running the copies of PPSA13934, PPSA07923 and PPSA05916 side by side on 7 million random formats, not measured on a console. Infinities andlong doublestill go to the host; NaN keeps its sign and flags through the formatter on current main. -
%e, %f and %g (libc) format a finite
doubleorlong doubleas thelibc.prxthat titles ship does, instead of through the host. It keeps the exact decimal digits down to position4 * floor(k / 4) - 20, wherekis thefrexpexponent times 30103 / 100000 truncated, and prints zeros after them; a shorter precision rounds half up on the kept digits, except that an exact tie rounds down whenkis at least 8 (%.0fof 67108865.5 is67108865). The console computes the kept digits in extended precision, so about 7% of outputs longer than 17 significant digits end one unit lower than here (%fof 1e22 is9999999999999999999999.000000). Checked by running the copies of PPSA07923, PPSA05916 and PPSA13934 side by side, not measured on a console. The wide printf family uses the same digits, as the titles'swprintfgives the same text as theirsnprintf. -
%p and %lp (libc) follow the console's
libSceLibcInternal.sprxmeasured from a homebrew payload on firmware 12.70 (#7549), not FreeBSD: bare lowercase hex with no0xprefix even under#,0for a null pointer,+and the space flag ignored, the width counting only the digits,-padding on the right,0padding with zeros when there is no precision, the precision as the minimum number of digits, nothing for a null pointer under%.0p, andlaccepted. Onlysnprintfandswprintfof that module were measured, from a payload process rather than a title process, so a width beside%.0p, the0flag beside a precision (which C17 ignores) and%.0pof a non-null pointer follow this formatter's own rules and are not measured;swprintfwas not run with%lp, so the wide family still refuses that conversion and returns -1. Linux routes%pand%lpto this formatter because glibc prints the FreeBSD form and(nil)for a null pointer, which means a format that combines%pwith a conversion this formatter refuses, such as an argument position above 99, throws there instead of reaching glibc -
collate and collate<wchar_t> _Getcat (libc) build the facet for the C locale only and throw for any other locale name, the wide entry point following the narrow one it sits next to. The wide facet carries its own vtable, whose
compare,transformandhashtake the guest's 16-bit characters, so the length of a range is a count of characters:compareorders by 16-bit value (the C locale, as Dinkumware'schar_traits<wchar_t>::comparedoes),transformcopies the characters into the result and records size and capacity in 16-bit elements (inline capacity 7, heap capacitylength | 7,(capacity + 1) * 2bytes including the terminator), following Dinkumware's VC12basic_string, andhashmixes one whole character per round, so wide text below 0x100 hashes like its narrow spelling and a character above 0xff hashes as one value instead of the two bytes it holds. The console'slibc.prxexports both_Getcat(NIDs BSVJqITGCyI and 2wz4rthdiy8) with identical 0x19d-byte bodies, believed to build the same 0x20-byte facet; the console bodies themselves are unreadable (obfuscated dynsym, and the text of the relinkedlibc.prxis zeroed), so the wide entries follow the narrow algorithm and the C locale instead of the console's wide code, and no console run checks the two. The console'slibc.prxstores pointers to its collation state tables at offsets 0x10 and 0x18 of the facet; ours leaves them null, as only the facet's own functions read them.do_transformthrows for input with embedded null characters, which the console drops depending on the output buffer size. The narrow result capacity (length | 15) and the over-aligned storage of results of 4080 characters or more follow the console'slibc.prxcode and are not checked on hardware. No module of the PPSA59322, PPSA76677 or PPSA97358 dumps in this tree imports either_Getcat, so neither has been exercised by a title. -
codecvt, numpunct, num_put and time_put _Getcat (libc) build facets matching a bundled console
libc.prx(dynsym decoded by NID, vtables disassembled): categories 2, 4, 4 and 5, sizes 0x10, 0x30, 0x10 and 0xb8.codecvt<char>reproduces the console virtuals (noconvconversions,lengththe shorter range);numpunct<char>copies its C-locale table ("",.,,,false,true).num_put/time_putformatting virtuals throw rather than fabricate output, andtime_put's_Locinfotable is unpopulated — the console'sdo_putbodies were not mirrored. The facets share the no-opdestroy/deleteObject/retain/releasebase used forcollate, sonumpunct's static C-locale strings are never freed. Unlikecollate, these_Getcatentries ignore the locale argument and always build the C-locale facet; the console's behaviour for a non-C name is unverified.std::clogandstd::wclogare 0x400-byte zeroed objects like the rest of theNSTDSTRstream set (the console'slibc.prxsizesstd::clogat 96 bytes, measured from its dynsymst_size), and thenumpunct<char>::idandtime_put<char>::idobjects are 8-byte zeros like the other facet ids. -
std::__throw_bad_variant_access (libc) throws
std::bad_variant_accesswith thewhat()stringbad_variant_access, the one LLVM libc++ returns, since the export carries libc++'s__throw_bad_variant_accessname; the console's string is unverified. Onimusha: Way of the Sword (PPSA27836 01.000.001) imports it fromlibc, and its bundledlibc.prxdoes not export it. -
std::bad_optional_access and std::_Throw_bad_optional_access (libc) provide the exception type, constructor, destructor, and
what()exports alongside_Throw_bad_optional_access, returning thewhat()stringbad_optional_accessmatching Dinkumware and libc++ conventions; the console libc.prx string is unverified. PPSA26578 (issue #7624) and PPSA34015 import_ZSt26_Throw_bad_optional_accessv(KwzEip7m6TM) fromlibc. -
The scanf family (libc), the
_sforms included, decodes the input of the 16-bit wide conversions (%ls,%lc,%l[,%S,%C) on Linux as UTF-8, mirroring the narrow printf family, where FreeBSD uses the locale'smbrtowc(one byte per character in the C locale); the console's locale encoding is unverified. Input that is not UTF-8, a character outside the BMP, a scanset member outside ASCII, positional arguments next to a wide conversion and wide conversions infscanfthrow. Each character is stored as one 16-bit unit, widths and_ssizes count characters, and a%lcfield cut short by the end of the input keeps what it read, as FreeBSD'sconvert_wchardoes. Windows hands%ls,%lcand%l[to the host, whosewchar_tis 16 bits -
The scanf family (libc) on Windows returns 0, as C17 and FreeBSD do, when input fails after only suppressed conversions have completed (
sscanf("12", "%*d%d", &value)). Linux hands the format to glibc, which returnsEOFthere. On Windows the host stops a%x,%X,%ior%pafter thexof a0xthat no hex digit follows (or that the width cuts off), and a float conversion (%e,%f,%g,%a) after theeof an exponent with no digits (1e), where C17 and FreeBSD stop before thexor thee -
The scanf family (libc) on Linux reads a
qlength asll, as glibc does. On Windows aqdirective ends the scan with nothing stored, as the console's own libc does for%qd; FreeBSD 11.4's source reads it asll. -
Guest path resolution falls back to ASCII case-insensitive component lookup, following shadPS4's host filesystem. PPSA03747 requests
Skyrim.INI,Data/Videoand lowercase archive/plugin names that differ from the dump's filenames. Exact host names take precedence; multiple fallback matches throw instead of selecting an arbitrary file. Existing parent names are resolved when creating a new file, and guest mount aliases use the same ASCII comparison. Results are not cached, so renames and newly created files remain visible. Unicode case folding and PS5 behaviour for case-colliding host entries are unverified; host roots are used as supplied. Explicit native Windows drive and UNC paths bypass guest parent-component validation.. A relinked module's<name>.guest.prxis matched the same way when the original module is not beside it: Borderlands 4 (PPSA01494) loads/app0/prx/libRenoirCore.PS5.prx, which it ships aslibrenoircore.ps5.prx. A relinked module request that misses both the exact and the case-insensitive<name>.guest.prxtakes the sibling whose stem (the name without its last extension, ASCII case ignored) equals the requested stem and that ends with.guest.prx, shortest name first: Unity IL2CPP titles loadMedia/Modules/<name>.prxandMedia/Plugins/<name>.prx, and the relinker names the converted module after the ELF it was built from,<name>.elf.guest.prx. The console's module namespace is assumed to ignore case and the extension; several stem matches pick the shortest name instead of throwing, which is not measured on a console -
Guest file names on Windows (libc) are not escaped or mapped before the host call, so names Windows treats specially differ from FreeBSD, where only
/and NUL are special.a*b,a?b,a<b>c,a|banda"bfail withEINVAL(22) where FreeBSD creates them.trail.andtrailboth create the host filetrail, so three FreeBSD names are one file here.NULand a reopen of it succeed, but no file is created.con,aux.txtandCOM1create ordinary files. Colons are under Colons in guest names. Measured on Windows 11 (10.0.26200), MinGW-w64 GCC 15.2.0 r7, withopen(O_CREAT|O_EXCL|O_WRONLY), a one-byte write and a listing of the host directory; not measured on Linux or a console. A fix needs a guest-to-host name mapping. -
Colons in guest names (libc) - on Windows (NTFS) a guest name containing
:is not a plain file name:name:streamopens or creates the alternate data streamstreamof the filename. Creating/savedata0/C:filemakes an empty fileCwhose streamfileholds the data, andstatof/savedata0/Cthen reports a regular file.mkdirof a name containing:, such asC:ordir:x, fails with EINVAL.name:streamalready behaved this way before drive-letter components were kept under their mount -
tzname (libc) points at the host's zone names, read at load and again by
tzset: the abbreviations aTZstring gives (EST5EDTisESTandEDTon both hosts), but on Windows withoutTZthe CRT's full names such asGMT Standard Time, which ICU's uprv_tzname (ES-DE in PPSA99169) cannot map to a zone ID. A payload process on a PS5 (firmware 13.60) read both names empty withTZunset, and its%Zprinted nothing whatevertm_zoneheld; the names a title process sees are not known -
localtime, mktime (libc) on Windows convert through the CRT's
localtime_sandmktime, which accept about 1970 to January 3001 UTC and apply the CRT's daylight-saving rule. A time outside that range is converted a whole number of 400-year Gregorian cycles away, where the calendar repeats, so it gets the rule the CRT applies to that year and not the zone's historical one. On macOSmktimeconverts through the host'smktime, which refuses every year before 1900. A local time before 1900 is instead solved for directly: the offsetlocaltime_rgives for a candidate time, which follows the zone's history (local mean time before its first transition), is subtracted from the local fields, and the step is repeated until the offset holds.tm_isdstis not consulted there, so a time withtm_isdstset where the zone had no daylight offset is taken as standard time. FreeBSD'stime1would instead shift it by the difference between one of the zone's daylight types and a standard one.gmtimeconverts every time whose year fits aninton both hosts. -
strftime and asctime (libc) follow a payload process on a PS5 (firmware 13.60) in the C locale.
%G,%gand%Vthrow for a negativetm_yday, atm_ydaypast the year or atm_wdaybelow -6, where no rule fits the console:%Vis 01, 00, 00 and 52 fortm_yday-25, -13, -12 and -1 (tm_wday5),tm_yday400 of 2026 is week 01 of 2027 but 415 of 1997 is week 59 and 365 of 2013 week 53, andtm_wday-12 withtm_yday18 is week 04.%k,%l,%P,%s,%+,%v, flags and widths are copied as written,EorObefore an unknown conversion is dropped, and a format ending in either throws, where the console crashed. Negative fields print 0 and fields wrap at 100 (%jat 1000,%uand%wat 10);%Yand%Gare the year mod 10000,%Cistm_year / 100 + 19. A negative weekday or month givesSunorJan.%zand%Zignoretm_gmtoffandtm_zoneand use the guest zone andtzname -
wcsftime (libc) converts the format to UTF-8, runs this library's
strftimeand converts the result back, where the console'slibSceLibcInternal.sprx(0x854e0) copies literal wide characters and expands each conversion straight into the buffer; the results match for the formats tried, and it has thestrftimegaps above. A result that does not fit returns 0 with the buffer untouched, where the console leaves the characters written so far. A null buffer returns 0, where the console writes through it, and a size too large for the narrow buffer returns 0 withEINVAL, where the console formats as usual. A null format ortmthrows -
syslog (libc) writes each message to
stderras[syslog:<priority>] <message>, with a newline added when missing, instead of sending it to the console's system log. Priority and facility handling follow FreeBSD'svsyslog:%mexpands to the guest errno's message, unknown bits are reported (as priority 0x23, since FreeBSD passesLOG_ERR|LOG_CONS|LOG_PERROR|LOG_PIDas the priority) and masked, a missing facility becomesLOG_USERor the facility set byopenlog, and errno is preserved.openlogapplies its identity andLOG_PID; a null identity retains the previous one, and a valid nonzero facility replaces the default.closelogclears the identity but retains the options and facility, as in FreeBSD 11; when no identity is set, FreeBSD falls back to the program name, which this backend does not emit.LOG_PERRORhas no extra effect because stderr is the emulated log sink;LOG_NDELAY/LOG_ODELAY,LOG_NOWAITandLOG_CONShave no daemon, child or console backend to control.setlogmaskfollows FreeBSD 11: the process-wide mask defaults to 0xff, zero queries it, and nonzero values replace it; masked messages are skipped before formatting, including the unknown-priority diagnostic. The console's mask behaviour is unverified -
fwscanf, vfwscanf, wscanf, vwscanf and their
_sforms (libc) run theswscanfscanner on characters read withfgetwc, so everything listed forswscanfin this file applies to them. As in the console'slibc.prx, a field takes characters while they can still begin a valid item, so a failed field such as0xfor%xstays consumed, and only the character that ended the scan goes back to the stream. That character is pushed back as its UTF-8 bytes withungetc, becauseungetwctakes single bytes only; the Windows C runtime guarantees one byte of push-back, so a non-ASCII lookahead can be lost there. A null stream or format returnsEOF; the console checks them only in the_sforms. -
daemon (libc) throws: forking into the background is not supported. PPSA85300 imports it and fails to load without it. VLC 3.0 calls it only when its
daemonoption is set (libvlc.c), and the title starts libVLC with--ignore-configand without--daemon(player.cc) -
_WStoul, _Iswctype, swscanf_s and swscanf (libc) -
_WStouliswcstouland throws for a base other than 0 or 2 to 36._Iswctypetakes the class numbers of Dinkumware's xwcc.h (1alnumto 11xdigit, 12blank) and throws for any other value, including the FreeBSD maskswctypereturns; the numbering is not confirmed on the console'slibc.prx.swscanf_sfollows C11 7.29.2.2 and K.3.9.1.5, but reads the size argument asunsigned intlikesscanf_swhere K.3.9.1.5 specifiesrsize_t; the console's width is unverified. A runtime-constraint violation (null buffer, format or receiving pointer) only returnsEOF, and a too small array gets a null first element as insscanf_s. A literal at end of input returns the number of assignments, including zero, rather thanEOFas FreeBSD does, as reported from the console scanner in #8479;%%at end of input still returnsEOF. A literal mismatch is a matching failure, where FreeBSD'svfwscanfreports an input failure. An incomplete floating exponent such as1e+converts its mantissa, as reported from the console scanner in #8479; other incomplete exponent forms are not separately verified. A short%cfield is currently a matching failure without storing its available prefix, while #9321 reports that the console stores a short%3lcfield; its return count, partial-output extent and precedence are unverified. An integer field that is only a prefix, such as0x, does not convert; FreeBSD'svfwscanfpushes back thexand converts the0. Integers out of range are truncated fromstrtoll/strtoull.%pfollows FreeBSD 11's vfwscanf.c: a hexadecimal integer with an optional0x, converted as unsigned and stored as a pointer; a length modifier on it throws, where FreeBSD ignores it.%Sand%C, with any length modifier or*, end the scan as a matching failure, as the console's Dinkumware scanner in PPSA21564'slibc.prxtreats them as invalid conversions; this was read from the module, not run on a console, and whether input ending before them is an input failure instead is unverified. It throws for unknown length modifiers, scanset ranges,%nwith suppression or width, and narrow%s,%cor%[input that the C-localewcrtombcannot convert.swscanfruns the same scanner without the size arguments of%s,%cand%[, as FreeBSD's swscanf.c passes its arguments tovswscanf, so everything above except the sizes applies to it; it also keeps the null buffer, format and receiving pointer checks returningEOF, where C leaves those undefined. Neither variant is confirmed on a PS5 title -
flock (libkernel) on Windows shares one lock state between duplicated descriptors, following FreeBSD 11.4's flock(2). Independent opens have separate states; closing a descriptor or reopening a stream drops only its reference. Lock transitions on one open file description are serialized. The lock is a host LockFileEx lock on one byte at offset 2^63 - 2, which no file reaches, so it excludes other
flockcalls and leaves reads, writes and mapped views alone, like FreeBSD's advisory locks. Console behavior, signal interruption and concurrent closure of a descriptor while its lock call is pending are unverified. -
std::_Throw_Cpp_error (libc) follows Dinkumware
thread0.cpp(VC12, STL), without console verification. Codes outside 0..6 throwstd::out_of_range, the deleting destructor throws, and STREAMS errnos absent from FreeBSD 11.4errno.h(ENODATA,ENOSR,ENOSTR,ETIME) are omitted. Codes 0..6 throw a 32-byte VC12system_errorwith FreeBSD errnos (16, 22, 3, 12, 1, 11, 35), a staticgeneric_category(not_Generic_object), and unverified 40-byte GuestLocale::String messages fromsyserror.cpp -
Call tracing (
--trace-imports,APS5_TRACE_CALLS) records only the entry of a call, not its arguments or result. One process-wide ring keeps the last 65536 calls, so a busy thread pushes the others out; the reporting thread's own calls are listed only while they are still in that ring. Calls are counted per traced target; an import bound to a target already traced under another name is counted under the first name. The thunk saves the integer argument registers andxmm0toxmm7; wider vector arguments survive only while the recording code avoids AVX, which is untested. Linux and macOS have no crash report, so they print the trace only onSIGABRT(guest or hostabort()), not onSIGSEGV; a caller in a module loaded after the last traced import prints without a module name, and a thread aborting while another writes the report waits for it at most 2 seconds. An arm64 libc build omits the recorder -
_Dtest (libc) follows Dinkumware double classification matching _FDtest: finite values return -1, subnormals -2, zeros 0, infinities 1, and NaNs 2.
-
_Exp and _FExp (libc) follow PRAGMATA PPSA02530's bundled
libc.prx(static disassembly): both operands are classified first and only finite/denormal pairs take the finite path. The strict x-only cutoffs at ±229/±1842 store0.0*ywithout an error below andy*infwith overflow above, so an offset does not compensate them. A zero multiplier keeps its sign, a NaN multiplier leaves*pxunchanged,+infwith zero or-infwith infinity is invalid, and finite multipliers with+inf/-infreturn signed infinity/zero without flags. Inside the cutoffs subnormals areFinitewithout flags, a zero from scaling raises underflow, and the negative cutoff itself raises nothing._FExpuses the measuredc1,c2,invln2,QandP;_Expfinite constants, exact finite bits, signaling NaNs, directed rounding and flush-to-zero modes remain unverified. -
exp2f (libc) is FreeBSD msun's
s_exp2f.c, withhugeandtwom100read at run time andx + reduxrounded to float, as msun'svolatilereads andSTRICT_ASSIGNdo. As in thelibc.prxthat titles ship,errnois set toERANGEonly in msun's overflow (x >= 128) and underflow (x <= -150) branches, so a subnormal result leaves it unchanged. Floating-point exceptions are not compared. It gives the same bits anderrnoas theexp2fof thelibc.prxbundled with PPSA07923, PPSA05916 and PPSA13934 for all 2^32 arguments, run on a Windows host. -
app0.zar (libc) - a ZArchive beside
app0/is a read-only layer under it: a path missing fromapp0/is looked up in the archive, case-insensitively. It is not a console format; titles ship their files in a PFS image instead. Resolving an archive-only path through any call other than the archive-aware libkernel ones throws, sofopen,stat,access,opendirand the rest do not read the archive yet, andopendirof a directory that exists in both places,app0/itself included, andfdopenof an archived descriptor throw instead of missing its entries or reading the null device. A damaged compressed block throws instead of returningEIO -
fopen_s (libc) - returns the errno of the failed open, as C11 K.3.5.2.1 leaves the nonzero value open and Microsoft's CRT returns errno; the console's Dinkumware
fopen_sis not verified -
codecvt<wchar_t,char,_Mbstatet> vtable (libc, NID
H-TDszhsYuY) is thirteen zeroeduintptr_tslots matchingst_size104 on titlelibc.prxfrom PPSA26344 and onlibSceLibcInternal.sprx(offset-to-top, typeinfo, two destructors,_Incref/_Decref, and seven conversion virtuals). Facet calls through the object fault until those methods are wired. -
num_put<wchar_t> vtable (libc, NID
59oywaaZbJk) is fourteen zeroeduintptr_tslots matchingst_size112 on the titlelibc.prxfrom PPSA16833 (offset-to-top, typeinfo, two destructors,_Incref/_Decref, and eightdo_putvirtuals). Facet calls through the object fault until those methods are wired. -
time_get vtable (libc, NID
o4kt51-uO48) is thirteen zeroeduintptr_tslots matchingst_size104 on consolelibSceLibcInternal.sprx(offset-to-top, typeinfo, two destructors,_Incref/_Decref, and sevendo_*virtuals). Facet calls through the object fault until those methods are wired. -
time_get<wchar_t> vtable (libc, NID
sEca1nUOueA) is thirteen zeroeduintptr_tslots matchingst_size104 on consolelibSceLibcInternal.sprx(offset-to-top, typeinfo, two destructors,_Incref/_Decref, and sevendo_*virtuals). Facet calls through the object fault until those methods are wired. -
num_get<wchar_t> vtable (libc, NID
Y9C9GeKyZ3A) is seventeen zeroeduintptr_tslots matchingst_size136 on consolelibSceLibcInternal.sprx(offset-to-top, typeinfo, two destructors,_Incref/_Decref, and elevendo_getvirtuals). Facet calls through the object fault until those methods are wired. -
ios_base standard streams (libc)
_Addstdqueues the stream in the standard stream table of Dinkumware ios.cpp (stdstr,stdopens,_Addstd,_Ios_base_dtor): it takes the first of slots 1 to 7 that is free or already holds the stream, and slot 8 (NSTDSTR) when none is, so slot 8 is shared by every stream past the first seven and registration never fails. It stores the 1-based slot index in the guestIosBase::standardStream(Dinkumware's_Stdstr, where 0 means not a standard stream) and counts the stream objects registered per slot; registering the same stream again reuses its slot and raises its count. Measured on a PS5 (firmware 13.60) from a homebrew payload calling the systemlibSceLibcInternalexport: in a process whose own standard streams already hold slots 1 to 7, twelve distinct streams and a repeated one all get 8, written as a 64-bit value at offset 0x8 with no other byte of the object touched; the assignment of slots 1 to 7 to fresh streams is not observable there and follows VC12. The counts areint, not VC12'schar, and the registration and the destructor lock the table where VC12 locks only_Addstd; a slot index past the table throws in the destructor instead of indexing out of bounds. The destructor decrements its slot's count and suppresses the rest of the destruction while the count stays positive, as_Ios_base_dtordoes, and the slots of destroyed streams keep their pointer and stay reserved for that stream, asstdstrdoes. The streams themselves are destroyed by the title's own code as it registers them through__cxa_atexit; when the last reference closes, the unsupported stream storage and callback cleanup is still the throw it was and the locale is released as for a non-standard stream. -
std::_Pad, _Thrd_join, _Thrd_detach, _Thrd_id (libc) follow Dinkumware V6.00
thr/xthreadandthr/threadcall.cpp(crt.vc12f1d47126), otherwise without console verification._Thrd_idreturnsscePthreadSelf()unchanged, as measured on a PS5 (firmware 13.40) in two threads (#4164, #8711);_Launchomits_Cnd_do_broadcast_at_thread_exit; the two-argument form starts with default attributes and the three-argument form forwards the caller's attribute pointer, as PPSA02530'slibc.prxdoes at0x18090. The 32-byte pad layout is assumed from VC12, unverified. Failing primitives or thread start throw the gueststd::system_error; null arguments throw._Thrd_joinreturns_Thrd_error(4) onpthread_joinfailure and truncates the exit code toint;_Thrd_detachreturns_Thrd_error(4) onpthread_detachfailure and throws when none is registered -
siginterrupt (libc) runs FreeBSD 11.4's
siginterrupt.csequence through thesigactionand the_sigintrset that libkernel registers at load: it reads the signal's disposition, sets or clears the signal's bit andSA_RESTART, and installs the disposition again. The set is written outside libkernel's registration lock, as FreeBSD's libc writes it without one; a call before libkernel has registered throws. -
collate vtable (libc, NID
ruZtIwbCFjk) is nine zeroeduintptr_tslots matchingst_size72 on the titlelibc.prxfrom PPSA16833 (offset-to-top, typeinfo, two destructors,_Incref/_Decref, and threedo_*virtuals). Facet calls through the object fault until those methods are wired;_Getcatstill installs the hostg_collateVtablefor facets it builds. -
num_get::id, time_get::id and the num_get vtable (libc) are data objects, as the public SDK's libSceLibcInternal table declares them and as the other facet ids and the
num_putandcodecvtvtables are kept: the ids start at zero and the vtable is 17 zeroed slots, the 136-byte size ofKfcTPbeaOqgin the console's libSceLibcInternal, whose relocations fill the offset-to-top and typeinfo entries, the two destructors,locale::facet::_Increfand_Decref, and the elevendo_getof Dinkumware's crt.vc12xlocnum, so a guest resolves the symbols and reads data, and a facet call through the vtable faults. libSceAmpr exported the three as throwing functions, which a data consumer reads as code; its qualified imports fall back to libc. -
printf family conversions (libc) - Windows
FormatWindowsfollows the titlelibc.prx_Printfmeasured from PPSA13934, PPSA07923, PPSA05916 and PPSA04158:%q,%D,%Oand%Uare unknown conversions; an unknown character is printed with its leading%, and width,-and discarded length modifiers behave as measured (%5yis%y,%hhzis%z).%%takes a width. A trailing%reads past the format's NUL and is not reproduced; it throws.#, precision and a0without a width are ignored, as a payload process on a PS5 (firmware 13.60) printed%#kand%.2kas%k;+, space,'and0with a width on an unknown conversion are unverified and throw. On Linux a format with an unknown conversion also goes to this formatter -
child process registry (libc) -
RegisterChildProcess_nid_no_patchandWaitChildProcess_nid_no_patchtrack the native handles of children created byposix_spawn(libSceLibcInternal) so libkernel can reap them on Windows, where the host has nowaitpid. The registry lives in libc because libSceLibcInternal imports libc but not libkernel.RegisterChildProcess_nid_no_patchstores the process ID and handle; only reaped handles are closed, and a process that exits without waiting leaks them. Registration without a live child is not validated, and a duplicate process ID is accepted. The decoder maps recognized structured-exception codes to guest signals, keeping the guestexitbyte layout for anything else. These exports are internal glue for libkernel, not console ABI: their behavior is unverified against any PS5 library and a title importing their NIDs directly is not expected. -
fprintf_s, vfprintf_s, vprintf_s, fwprintf_s, wprintf_s, vfwprintf_s and vwprintf_s (libc) follow C11 K.3.5.3.1, K.3.5.3.10 and K.3.9.1.1 with the constraints of
snprintf_sandswprintf_s: a null stream, format or argument list, any%n, and a null%s,%lsor%Sargument return -1 and write nothing, because the output is formatted completely before it is written. A violation only returns: the constraint handler is not invoked,errnois untouched, and the value is the one the narrowsnprintf_sreturns on this branch, not a result measured for a stream function on a PS5. A closed stream setsEBADF(9) asfprintfdoes.vprintf_sandvwprintf_swrite to the guest standard output stream, and the wide forms count characters while the narrow ones count bytes, as their unchecked siblings do -
scanf_s, fscanf_s, vscanf_s and vfscanf_s (libc) run the scanner of
sscanf_sover the stream, taking the size of%s,%cand%[asunsigned intafter the pointer, so everything listed forsscanf_sapplies, including the count of items converted before an undersized destination, whose first element is set to zero. Each directive is read from the stream with the hostfscanf, with one character of look-ahead pushed back, so the stream stops after the last character a conversion used. A null stream, null format or null destination returnsEOF, a closed stream also setsEBADF, and the constraint handler is not invoked. The 16-bit wide conversions (%ls,%lc,%l[,%S,%C) throw on Linux, as infscanf. None of the stream forms were measured on a PS5, and the size type followssscanf_srather thanrsize_t -
freopen_s (libc) follows C11 K.3.5.2.2 through
freopen: a null result, mode or stream returnsEINVAL(22) with*resultset to null when it exists, and a null name is passed tofreopen, which reports mode-only reopening as unsupported (ENOTSUP, 45) with*resultnull, and the constraint handler is not invoked, as infopen_s. A successful reopen stores the stream in*resultand returns 0, and a failed one stores null and returns the errnofreopenleft, closing the stream asfreopendoes. The console's return values are unverified