PPSA35943 (Graveyard Keeper 2) imports sceHttp2DeleteCookieBox, which no
library exported. On current main the loader stops the title there:
./eboot.elf: symbol lookup error: ./eboot.elf: undefined symbol:
O9ync3F-JVI
sceHttp2DeleteCookieBox(cookieBoxId) erases the box from the handle
registry sceHttp2CreateCookieBox adds it to and returns 0, or the
library's invalid-id error (0x817B1100) for an id that is not a live
cookie box. The signature follows SharpProspero and the behaviour
prosper's sceHttp2DeleteCookieBox; the error code is listed in
TechnicalDebt as not measured on a console.
guest_http2 deletes a box twice, a context id, a box whose context was
terminated, and the two boxes it uses before sceHttp2Term.
With this change the title loads and runs into its asset loading.
Minecraft (PPSA17221) fails to load on nine absent imports. The authorized-app
dialog runs the common-dialog state machine without UI and reports the user
as having cancelled, like the login and VR setup dialogs. The other two,
sceNpAuthGetAuthorizedAppCode and the unnamed libSceSaveData.native NID
RvID8BVeg+g, are declared as throwing stubs with unknown signatures.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
RetroArch 1.0.0-beta.1 (PPSA99169) stops on main 0f863c10 at
FAIL: unresolved ELF import Nx-F5v0-qU8 from libSceLibcInternal.prx
Nx-F5v0-qU8 is asinl (tools/nid_names.py, aerolib).
The implementation follows the asinl of the libc.prx bundled with
PPSA28319, read from its disassembly: FreeBSD msun's e_asinl.c with
the ld80 invtrig.c coefficients (9f043204), which the binary holds as
the same 80-bit constants, computed in the x87 long double the guest
uses. +-1 returns +-pi/2 rounded to 64 bits and +-0 keeps its sign.
For |x| > 1, the infinities and NaN, Sony's error helper raises
FE_INVALID and sets errno to EDOM before returning (x - x) / (x - x),
so a NaN argument sets EDOM too.
20 million random arguments in (-1, 1) plus the branch edges give the
same bits as that code from the title's libc.prx run on a Windows
host. guest_math pins a set of those bits and checks the domain
errors' errno and FE_INVALID, and that errno is untouched otherwise.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Ruffle Flash 1.2.0 (PPSA68091) stops on main 0f863c10 at
FAIL: unresolved ELF import 1t1-JoZ0sZQ from libSceLibcInternal.prx
1t1-JoZ0sZQ is sinhf (tools/nid_names.py, aerolib). RetroArch
1.0.0-beta.1 (PPSA99169) imports it as well.
The implementation follows the sinhf of the libc.prx bundled with
PPSA28319, read from its disassembly: FreeBSD msun's e_sinhf.c as of
5aa554c7 (before __ldexp_expf, so expf(|x| / 2) is squared between
logf(FLT_MAX) and the overflow threshold), calling msun stable/9
expm1f and expf (59bc2f09), whose paths for the arguments sinhf passes
are inlined. Past the threshold Sony's error helper sets errno to
ERANGE before returning x * 1e37; NaN and the infinities return x + x
and leave errno alone.
Running that code from the title's libc.prx on a Windows host gives
the same bits as this implementation for every finite argument up to
the threshold, both signs (2,238,032,378 inputs). guest_math pins a
set of those bits, the overflow errno and FE_OVERFLOW, and checks that
errno is untouched otherwise.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
On Linux sscanf, vsscanf, fscanf and sscanf_s hand their conversions to
glibc. Since glibc 2.38, code built as GNU C++ calls the C23 entry
points (__isoc23_vsscanf, __isoc23_vfscanf, __isoc23_sscanf), whose %i
reads a 0b/0B binary prefix. On main,
sscanf("0b101 x", "%i%n%7s", &value, &end, rest) stores value 5 and
end 5 and leaves "x" for the %s; " -0B11" gives -3. TechnicalDebt
records this.
FreeBSD's %i knows only the 0 and 0x prefixes. FreeBSD 11.4
lib/libc/stdio/vfscanf.c, parseint, lines 345-353 and 371-380:
case '0':
if (base == 0) {
base = 8;
flags |= PFXOK;
}
...
/* letters ok iff hex */
case 'A': case 'B': case 'C':
case 'D': case 'E': case 'F':
case 'a': case 'b': case 'c':
case 'd': case 'e': case 'f':
/* no need to fix base here */
if (base <= 10)
break; /* not legal here */
so "0b101" stores 0, consumes the "0" and leaves "b101", as C17 does
and as the strtol family here already does.
With glibc the scanf family now calls __isoc99_vsscanf and
__isoc99_vfscanf, the entry points GNU C++ used before glibc 2.38
(exported since 2.7), which read %i without the binary prefix. Other
hosts keep std::vsscanf and std::vfscanf. The TechnicalDebt entry is
removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaskedSelectEliminator walked forward from each select to see whether the
lanes its mask excludes are read. Since fda4aa90 the walks pass explicit-LOD
gathers, size queries and guarded image writes, so in a large pixel shader
most of them cover the rest of the program, and the cost grows with the
square of its size (#3504).
The pass now computes, for every value, the set of masks under which it is
unread, from its uses backwards: a use guarded by a condition contributes the
masks that condition implies, a lane-local use adds what its result is unread
under, and any other use contributes nothing. A select is dropped when its
mask is in its own set. Loops are solved as strongly connected components.
The rules for guards, lane-local uses, loops, whole quad masks, selected
execs, rebuilt mask words and guarded first-lane reads are the ones the walk
applied. A first-lane read passes the masks its read mask implies when its
own result is unread outside that read mask, which is solved once per read in
the same way. The walk's step limit, its 64-level implication depth and its
first-lane nesting depth are gone, so a proof no longer depends on the order
of the questions.
Goat Simulator 3 draws a triangle list whose indices are all 0xffffffff
with primitive restart off. Each fetch past a descriptor's records reads
zeros (out-of-bounds select 0 or 1), but the zero-padded vertex copies
were sized to the highest index: 160 GiB, which vkAllocateMemory refused.
When the highest index is at least 64Ki vertices past the largest
per-vertex record count, the draw now uses a copy of its indices with
every index past that count moved to it. Each attribute reads zeros
there as at the original index; only the vertex index the shader sees
changes, so indices closer to the records are left as they are.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ioctl and _ioctl answered EBADF for every descriptor outside the
libSceNet socket table. Decoded call sites: PS5MC (PPSA99420, FIONBIO x3,
FIONREAD x2, TIOCGWINSZ x3), ProsperoTV (PPSA99003, FIONBIO on a pipe
once poll on pipes is in), XPSemu (PPSA97358, 21 sites).
Following FreeBSD 11.4 kern_ioctl (sys/kern/sys_generic.c:758),
vn_ioctl (sys/kern/vfs_vnops.c:1488), pipe_ioctl (sys/kern/sys_pipe.c:1346)
and randomdev_ioctl (sys/dev/random/randomdev.c:301), release/11.4.0
8c639945: FIONBIO sets or clears O_NONBLOCK through the same status
flags as fcntl F_SETFL (EFAULT for a null argument before EBADF);
FIONREAD reports the size past the offset of a file, the queued bytes of
a pipe's read end, 0 on its write end, ENOTTY on the random device, and
throws on a directory or another character device; termios requests
(group 't', TIOCGWINSZ included) fail with ENOTTY, as tcgetattr already
takes these descriptors as not a terminal; any other request throws
naming the request and descriptor kind. Sockets keep their path
unchanged.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
fcntl and sceKernelFcntl answered EBADF (sceKernelFcntl threw) for
every descriptor outside the libSceNet socket table, so files, pipes,
directories and descriptors 0 to 2 could not be queried or changed.
Observed: Yamagi Quake II (PPSA99007) stops with
"Sys_Error: change stdin to blocking Bad file descriptor", from
fcntl(fileno(stdin), F_SETFL, fcntl(0, F_GETFL, 0) & ~FNDELAY)
(yquake2 src/backends/unix/system.c:81-86).
Predicted from the titles' sources and decoded call sites: XPSemu
(PPSA97358, 97 fcntl sites: F_GETFD 12, F_SETFD 20, F_GETFL 8,
F_SETFL 9, F_DUPFD 1, F_DUPFD_CLOEXEC 7, F_GETLK 9) fails in
qemu_init_main_loop, where its pipe2 (pipe plus F_SETFD FD_CLOEXEC) and
glib's g_unix_set_fd_nonblocking (F_GETFL, F_SETFL) build the event
notifier (util/event_notifier-posix.c:44-67); VLC (PPSA85300, 43 sites,
F_RDAHEAD on every file it opens), RetroArch (PPSA99169, 17),
Porpoise (PPSA99764, 13) and Ruffle (9) call it on files and pipes.
The commands follow FreeBSD 11.4 kern_fcntl and kern_dup
(sys/kern/kern_descrip.c:479-799, 812, release/11.4.0 8c639945):
F_GETFD/F_SETFD keep the close-on-exec flag per descriptor, F_GETFL
reports the host access mode with the status flags, F_SETFL changes
only O_NONBLOCK, O_APPEND, O_ASYNC, O_FSYNC, O_DIRECT and FRDAHEAD,
F_DUPFD/F_DUPFD_CLOEXEC give the lowest free descriptor at or above the
start through dup (EINVAL for a negative start), F_DUP2FD goes through
dup2, F_RDAHEAD/F_READAHEAD set FRDAHEAD on files (EBADF on pipes,
ENOTTY elsewhere), and unknown commands fail with EINVAL. Status flags
live in a guest table shared by dup, dup2 and F_DUPFD like an open file
description. O_NONBLOCK switches a pipe (PIPE_NOWAIT on Windows, where an
empty read and a full write now report EAGAIN; the host flag on Linux).
Record locks use the host POSIX locks on Linux and throw on Windows.
Changes the host cannot model throw naming the flag and descriptor kind.
Sockets keep their path unchanged.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Draws 32_ABGR exports into a tiled and a linear format 17 target and compares every texel with
the layout X in bits 0-4, Y in 5-9, Z in 10-14, W in bit 15, including the X/Z-swapped pairs, rounding
edges and texels outside the scissor. Reverting the X/Z exchange fails it.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Direct memory can already be mapped more than once, so there is no
aliasing to enable and the call returns 0. ProsperoEden (PPSA99008)
imports it and calls it before mapping the same direct memory again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
readlink threw for every Windows reparse point, including the links
symlink creates. Read the target of a relative symbolic link from its
reparse data and return it with forward slashes. Absolute targets,
junctions and other reparse points still throw.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FreeBSD's printf formats %m as the message for the errno value at the
time of the call. On Linux a format with %m went to glibc, which looks
the number up in Linux's table, so guest errno values above 34 got
another error's text: with errno 35 (EAGAIN) "[%m]" printed
"[Resource deadlock avoided]" instead of "[Resource temporarily
unavailable]", and errno 63 (ENAMETOOLONG) printed "Out of streams
resources". On Windows, and on Linux when the format also had a wide
argument, the guest formatter threw on %m.
FreeBSD 11.4, lib/libc/stdio/vfprintf.c:
saved_errno = errno;
...
case 'm':
cp = strerror(saved_errno);
size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp);
sign = '\0';
break;
The guest formatter now saves errno when it starts and formats %m as a
string with the guest strerror_r text, so width, precision and the -
flag apply as FreeBSD's string path applies them. On Linux a format with
%m goes to the guest formatter like one with a wide argument.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SaveDataDirNameSearchResult.infos points at an array the title reads one
entry per returned directory name, but the search never wrote it, and
SaveDataSearchInfo was declared as 128 opaque bytes. A title that asks for
the infos read whatever its buffer held.
SaveDataSearchInfo is now the 48-byte record shadPS4 declares as
OrbisSaveDataSearchInfo (blocks, free blocks, 32 reserved bytes), and the
search fills one per returned name. Every native save reports
SAVE_DATA_BLOCKS_MAX blocks, as sceSaveDataGetMountInfo and the block sort
keys already assume, so both counts are SAVE_DATA_BLOCKS_MAX and the
reserved bytes are zero.
guest_savedata_search_tests checks the two records, their zero reserved
bytes and that the search writes nothing past them.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VFVE5WbaUJ2vyj8NAW44CS
std::random_device in libstdc++ reads RDSEED on x86 Linux. On an AMD
EPYC 9V45 (Zen 5) CI runner its 32-bit results repeat about 130 times
in 65536 draws, so guest_posix_random fails. libc now exports
HostEntropy_nid_no_patch, which uses getentropy (rand_s on Windows) and
throws when the OS cannot provide entropy. arc4random, arc4random_buf,
sceRandomGetRandomNumber, /dev/random and /dev/urandom reads,
std::_Random_device and sceKernelUuidCreate use it, and the arc4random
and random device TechnicalDebt entries say so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a PS5 (firmware 13.60) scePthreadJoin on the calling thread returns
EDEADLK and scePthreadCreate with a zeroed attribute creates a thread
with the default attributes. Here both threw.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a PS5 (firmware 13.60) the SCE mutex attribute calls return EINVAL
for a null or uninitialized attribute, scePthreadMutexattrSetprotocol
accepts 0 to 2, scePthreadMutexInit takes an uninitialized attribute as
the default one, and destroying a destroyed mutex returns EINVAL. Here
these threw, including for the priority protection protocol.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a PS5 (firmware 13.60) the scePthreadAttr setters and getters return
EINVAL for a null or uninitialized attribute and for a null output,
ENOTSUP for a policy other than SCHED_FIFO and SCHED_RR, an inheritance
other than explicit and inherit, and a priority outside 256 to 767, and
EINVAL for an invalid detach state. Here they threw, or stored any
policy, inheritance and priority.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sceAgcDcbSetWorkloadsActive writes an 18-dword IT_NOP packet (stream id, 64-bit workload mask) and sceAgcDcbSetWorkloadComplete a 12-dword one (stream id, workload id, 64-bit clear mask), following KytyPS5. Both return null for a null buffer, an unregistered or out-of-range stream, a workload id above 63, and for active a duplicate id or a count of 0 or above 63. sceAgcDriverRegisterWorkloadStream and sceAgcDriverUnregisterWorkloadStream now record the stream ids the DCB calls check.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
sceAgcDcbSetWorkloadsActive and sceAgcDcbSetWorkloadComplete threw. They now write a plain two-dword NOP and keep no workload state.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
On a PS5 (firmware 13.60) a user with every accessibility setting at
its default reads chat transcription 0, press-and-hold delay 0, trigger
effect 1, vibration 1, zoom 0 and zoom follow focus 1; all six read 0
here. A user who is not logged in returns 0x80960009 with or without an
output, as sceUserServiceGetAgeLevel does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A title creating a socket with sceNetSocket sets its options with the BSD
setsockopt, which looked the descriptor up only among libkernel's own
sockets and returned EBADF. libSceNet now registers an option setter with
libkernel, as it does for close and select, and accepts SO_REUSEADDR,
SO_BROADCAST, SO_SNDBUF and SO_RCVBUF like libkernel's own sockets.
PPSA01289 (Sackboy: A Big Adventure) sets SO_BROADCAST on its UDP P2P
socket this way in UIpNetDriver::InitBase. The failure made every listen
fail, and the engine travelled back to its default map with ?closed,
reloading it about every two seconds behind the loading screen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A libSceNet socket is a kernel descriptor on the console, so titles close it
with close(). libSceNet keeps its sockets in its own table, and libkernel's
close passed their numbers to the CRT, which reported an invalid parameter
and left the socket open. libSceNet now registers a closer with libkernel,
as it registers its poller for select. PPSA01289 (Sackboy: A Big Adventure)
closes its P2P socket this way each time it tears its network session down,
leaking one open socket per attempt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>