The relinker's repeatable --executable converts another executable of the
title beside the output, sharing its libs, app0 and guest modules.
sceSystemServiceLoadExec maps /app0 paths to those executables, passes the
argument list on the new command line and finds the main executable through
APS5_EBOOT. RetroArch (PPSA99169) starts its frontend picker this way.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
mlockall (x7g7Ebeo8-U, aerolib) follows FreeBSD 11.4's sys_mlockall
(sys/vm/vm_mmap.c:1067-1131, freebsd-src
8c639945a44588f6cfdddfb057bb5aad88735da0) for the flag check: 0 or
bits outside MCL_CURRENT (1) and MCL_FUTURE (2) fail with EINVAL. The
title process is taken to pass priv_check(PRIV_VM_MLOCK), as mlock
already does.
Valid flags throw. MCL_FUTURE would need every later guest and host
mapping to be locked; nothing in the mapping paths does that, and
passing it to the host would make later host allocations fail above
the host RLIMIT_MEMLOCK. MCL_CURRENT would have to lock the whole
process, which on AnyPS5 also holds host stacks, heaps, modules and
driver mappings (sceKernelVirtualQuery reports them as guest memory),
with ENOMEM above a guest RLIMIT_MEMLOCK that is not known (getrlimit
throws for it). mlock pins host pages (VirtualLock, mlock) range by
range and keeps no record, so there is no state to extend. Recorded
in TechnicalDebt.
XPSemu (PPSA97358) calls it only with -overcommit mem-lock=on
(ZiZc3/XPSemu 8eec07b4, os-posix.c:330-355, reached from
system/vl.c:831-835):
int flags = MCL_CURRENT | MCL_FUTURE;
...
ret = mlockall(flags);
if (ret < 0) {
error_report("mlockall: %s", strerror(errno));
}
and exits on failure, so that option ends in the throw; without it
the title is not affected. Predicted stop removed (registry index 201,
not run):
FAIL: unresolved ELF import x7g7Ebeo8-U from libkernel.prx
guest_mlock checks EINVAL for 0, 4, 5 and -1 and the throw for 1, 2
and 3.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
(cherry picked from commit 87f4aa331b02d098fe27516809495447ff6a9fa6)
sceAudioOut2PortGetState returned the two channels of the host output for
every port. It now reports the channel count of the port's data format and
clears the rest of the state, so the reserved fields no longer keep the
caller's stack contents. The speaker info and system state calls are
unchanged; a new test covers all three, since none had one.
The channel count follows SharpEmu, another emulator; no console
measurement exists, recorded in TechnicalDebt.md. ASTRO BOT and ASTRO's
PLAYROOM reportedly stop on an assertion that depends on the reported
state (#4317); which field matters is not isolated.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
pthread_atfork (U9t5kJAWPnA, aerolib) appends the prepare, parent and
child handlers to a process-wide list under a lock and returns 0, or
ENOMEM when the entry cannot be allocated, as FreeBSD 11.4's
_pthread_atfork does (lib/libthr/thread/thr_fork.c:78-100, freebsd-src
8c639945a44588f6cfdddfb057bb5aad88735da0). Null handlers are accepted;
FreeBSD's fork skips them. The handlers never run: fork
(libkernel_sys) throws and vfork is absent, so no child process is
created. The list is kept, in registration order, for a later fork
(PthreadAtforkHandlers_nid_no_patch); recorded in TechnicalDebt.
XPSemu (PPSA97358) calls it from a constructor, before main
(ZiZc3/XPSemu 8eec07b4, util/rcu.c:488-495):
static void __attribute__((__constructor__)) rcu_init(void)
{
smp_mb_global_init();
#ifdef CONFIG_POSIX
pthread_atfork(rcu_init_lock, rcu_init_unlock, rcu_init_child);
#endif
rcu_init_complete();
}
so a throwing export would stop the title before main. Predicted stop
removed (registry index 255, not run):
FAIL: unresolved ELF import U9t5kJAWPnA from libkernel.prx
guest_pthread_atfork checks registration order, null handlers,
concurrent registration from four threads and that no handler runs.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
(cherry picked from commit f37bccf6efd76edcde5d582800c89b83fdd30472)
PS5MC (PPSA99420) imports wcsftime (XbVXpf5WF28, aerolib) as the last
absent libSceLibcInternal import in its registry order (index 481), so
its predicted stop is:
FAIL: unresolved ELF import XbVXpf5WF28 from libSceLibcInternal.prx
The stop is predicted from the import order, not observed in a run.
wcsftime follows FreeBSD 11.4 lib/libc/locale/wcsftime.c (8c639945):
the wide format is converted with wcsrtombs, strftime fills a buffer of
maxsize * MB_CUR_MAX bytes limited to maxsize, and mbsrtowcs converts
the result back. A format unit that the single-byte C locale of
lib/libc/locale/none.c cannot encode returns 0 with EILSEQ, a maxsize
whose product with MB_CUR_MAX overflows returns 0 with EINVAL, and an
empty or overflowing result or a null buffer returns 0 with errno
unchanged; the ERANGE or EINVAL set by the Windows CRT's strftime is
discarded. A null format or tm throws, as FreeBSD dereferences it.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Black Myth: Wukong (PPSA23226) exits at start-up with
STATUS_DLL_NOT_FOUND after "Failed to load module:
libs\libSceDbgThreadSanitizer.prx". The executable does not need the
library: its bundled sce_module/libSceJobManager_nosubmission.prx lists
it in DT_NEEDED and imports __tsan_acquire and __tsan_release from it,
plus sceKernelIsThreadSanitizerEnabled from libkernel. The relinker
converts every module in sce_module and loads it at start-up (only
--module-dir directories are deferred), and GuestModuleBuilder adds each
module's host dependencies to the executable's DT_NEEDED, which the
Windows start-up stub loads.
libSceDbgThreadSanitizer now exports __tsan_acquire and __tsan_release
with the void(void* addr) signatures of LLVM compiler-rt's
tsan_interface.h (llvm-project 87f0227cb601, LLVM 20.1.8). Both throw:
AnyPS5 runs no race detector, and the module calls them only after
sceKernelIsThreadSanitizerEnabled reports the sanitizer enabled.
libkernel's sceKernelIsThreadSanitizerEnabled returns 0, as
sceKernelIsAddressSanitizerEnabled does; its int(void) signature is
assumed and recorded in the libkernel debt file.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Homebrew built with the ps5-payload-dev SDK links LLVM libunwind
statically but imports _Unwind_Backtrace from libSceLibcInternal. Its
trace callback then reads each frame with its own _Unwind_GetIP, which
treats the context as a libunwind cursor and calls through the virtual
table in its first word. AnyPS5's context started with the saved RAX, so
the call jumped to a register value and the title crashed on its error
path (vkQuake, PPSA99010, without its game data). The context now starts
with a table in AbstractUnwindCursor's slot order that answers register
reads and writes, validFloatReg and isSignalFrame; other slots throw.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EvDbpwUPTa78oCAJqe7m9K
sceCoredumpWriteUserData (Dbbkj6YHWdo) and sceCoredumpDebugTextOut
(dei8oUx6DbU) get the signatures of Orbital's db_types.json (cc408367)
and SharpProspero's KernelSystem.cs (5dfdb4b0): ssize_t (const void*,
size_t) and void (const char*, int). WriteUserData moves out of
Unimplemented.cpp.
User data belongs to the core dump handler: the Vita's
sceCoredumpWriteUserData returns 0x800A0003 ("No user-data capture is
active") outside it (vita-headers e66ebe90), and the RED engine's PS4
crash handler makes every coredump call from its handler (Vangaurd
4f86d2b0). The handler is never invoked here, so WriteUserData returns
SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER (0x81180003, shadPS4
coredump_error.h e0a46675) sign-extended and keeps nothing; a null,
empty or wrapping buffer throws because the order of the console's
checks is unknown. DebugTextOut prints its text to stderr with the
[coredump] prefix; a negative length or a null text with a nonzero
length throws.
Evidence: Broken Sword Reforged (PPSA21480) reaches the family through
its sibling, #3704 comment 6113687542: "`ERROR agc_graphics.cpp:266
Sony call failed with code 8a6c9018` (the resource registration error,
followed by the error dialog), then `sceCoredumpWriteUserString not
implemented`." WriteUserString has no declaration in any source and
keeps throwing; its TechnicalDebt entry records the call. 007 First
Light (PPSA11386) imports WriteUserData (#2523) without calling it
before its stop.
Test: guest_coredump_user_data.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
libkernel numbered guest sockets from GuestSockets::FirstDescriptor
(0x10000000), so a title that puts a socket in a 1024-bit fd_set writes
fd / 8 bytes past it. The Epic Online Services SDK does this on its
RtcNetworkThread and three titles stop there (#1794):
Palworld PPSA23126 (queeeef, main a11c8f7f7):
FATAL: unhandled exception 0xc0000005 on thread 37164 'RtcNetworkThread'
rip 0x00007ffcdb1ea2be eossdk-ps5-shipping.prx.guest.prx+0xdea2be
rcx 0000000010000005 rsi 0000000000400000 r12 0000000010000005
The Mound PPSA26223 (xXPBSXx, main 0a8b1d32b):
FATAL: unhandled exception 0xc0000005 on thread 'RtcNetworkThread',
rip eossdk-ps5-shipping.prx.guest.prx+0xefc6f4, rcx = 0x10000005,
rsi = 0x400000
Dying Light 2 PPSA02262 (tv7):
FATAL: unhandled exception 0xc0000005 on thread 'RtcNetworkThread'
rcx 0000000010000005 ... rsi 0000000000400000 ... r12 0000000010000005
On FreeBSD 11.4 (8c639945) FD_SETSIZE is 1024 (sys/sys/select.h:58-59)
and sockets take the lowest free entry of the one process descriptor
table that files use (kern_descrip.c:1643-1660 fdalloc,
uipc_syscalls.c kern_socket through falloc).
Every socket number is now a real host descriptor: a zero-access NUL
handle registered with _open_osfhandle on Windows (as directory
descriptors already are) and an F_DUPFD_CLOEXEC duplicate of the host
socket on Linux. The socket table maps that number to the socket, so
"is this a socket" is a table lookup instead of a range check at the 31
former FirstDescriptor comparisons. close releases both the socket entry
and the number, dup and dup2 duplicate the number through the host table
and keep the socket shared, dup2 of a file onto a socket number drops the
socket entry, and sceKernelClose now closes sockets too. sceNetSocket and
sceNetAccept take their ids from the same table instead of a counter from
32, so libkernel and libSceNet numbers never collide.
select polls guest sockets alongside libSceNet sockets, the random device,
stdio and (on Linux) host descriptors; poll throws only for an open
descriptor that is not a guest socket and reports POLLNVAL otherwise.
File calls that used to fail with EBADF on the socket range return
FreeBSD's answer for a socket: lseek ESPIPE (kern_lseek), ftruncate and
getdirentries EINVAL (invfo_truncate, getvnode), flock EOPNOTSUPP;
fstat on a socket throws. pread on a closed descriptor returns EBADF.
title failing on host descriptor numbers; these three titles are that
case, and this change keeps host numbering for files and only moves
sockets into it.
Tests: guest_socket_descriptors (new: socket below 1024, select on a
socket and a file together, close frees the number for a file, dup of a
socket, sceKernelClose), guest_select (socket and pipe together, Linux),
guest_poll, guest_dup, guest_socket_pair, guest_fstatfs,
guest_posix_filesystem and guest_sce_net updated.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
(cherry picked from commit 20378375b31eb5db922db6e55f45e44a4dae6445)
The NID -q-vbO859Tw, declared as sceVideoOutUnknown_q_vbO859Tw for
PPSA02530, hashes to sceVideoOutDeleteVrrActiveStatusEvent. It removes the
handle's VRR status registration from the queue like the other Delete*Event
calls, the counterpart of sceVideoOutAddVrrActiveStatusEvent.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017WeVmjnnPPKdxVTpr2Vjtw
ApplyFastClear took the color fill before the CMASK clear, so a partial
fill of a multisampled target's color memory threw at the next use even
when a CMASK fast clear was pending over the whole target. The CB reads
a cleared tile's color from the clear registers whatever the memory
holds, so the CMASK clear is applied and the recorded fill dropped.
DecodeColorMetadataPass treated a CB_COLOR_CONTROL resolve as one only
when PA_SC_AA_CONFIG has a sample count, and checked the source against
it. A title that resolves with the rasterizer single-sample fell through
to the draw path and threw "only normal color rendering with copy ROP is
supported (... mode resolve)".
A color target's sample count is CB_COLOR_ATTRIB.NUM_SAMPLES, separate
from the rasterizer's; radeonsi itself writes PA_SC_AA_CONFIG 0 for a
multisampled framebuffer when rasterizer multisampling and line
smoothing are off. So a resolve is decoded when either the rasterizer or
CB0 is multisampled, and its sample count is CB0's. When the rasterizer
is multisampled, the source still has to match it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A sampled copy of a depth plane is kept while the surface's write
serial is unchanged. Storing a written storage view back into the
depth image did not bump the serial, so a later draw or dispatch that
sampled the plane as an array or cube kept the values from before the
store. The store-back bumps it now.
The dispatch test samples the plane through a copy, stores to it in a
dispatch and samples it again.