Forwards _sceUltWaitingQueueResourcePoolCreate and _sceUltSemaphoreCreate to the existing calls, like the other underscore Ult exports. Declares sceNetShowIfconfig, sceNpWebApi2GetMemoryPoolStats, Json::Value::setNullAccessCallBack, kqueue, kevent and one pad call of unknown name without an implementation.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Capture errno before the ENOTDIR lookup, translate EINVAL from a name
under a regular file to ENOTDIR, check the parent only once and strip a
trailing separator before taking it, and split the dense rmdir return.
On Windows mkdir_nid_postfix returned ENOENT and sceKernelMkdir and
sceKernelRmdir returned ENOENT or EIO when a regular file was used as a
directory in the path. FreeBSD mkdir(2) and rmdir(2) return ENOTDIR for
a path component that is not a directory.
FreeBSD's kernel_sigaction rejects SIGKILL and SIGSTOP only for a handler other than SIG_DFL, so signal(9, SIG_DFL) and signal(17, SIG_DFL) succeed. Drop the SIG_ERR rejection, which has no kernel basis and which sigaction does not apply.
Add an optional input helper for test runs without desktop input injection. Preserve physical input and release injected buttons automatically.
Validation: guest_pad tests and Python helper CLI smoke checks passed. Written with AI assistance.
signal() returned SIG_ERR with EINVAL for any signal without a host
equivalent, such as SIGHUP, SIGPIPE and SIGUSR1, while sigaction()
accepted them. FreeBSD's signal() is sigaction() with SA_RESTART and fails
only for numbers outside 1..128 and for SIGKILL and SIGSTOP.
The Windows formatter passed %p to the host CRT, which prints 16
zero-padded digits without a prefix. FreeBSD prints 0x followed by the
lowercase hex digits (0x0 for a null pointer), as if by %#lx.
Add standard quiet NaN generators nanf (DZU+K1wozGI) and nan (zck+6bVj5pA) to libc.
Resolves startup failure in VLC for PS5 (PPSA85300) where execution halts at nanf on Linux once strsep is available.
PPSA21703's Unity PSN plugin imports sceNpWebApi2GetMemoryPoolStats
(Xweb+naPZ8Y), so the title did not start.
sceNpWebApi2Initialize now records the pool size of each context and
sceNpWebApi2Terminate forgets it. The statistics report that size with
no peak or current use, as sceHttp2GetMemoryPoolStats does, since nothing
is allocated from the pool. A null output or an unknown context throws.
The signature and layout follow the Http2 statistics and are recorded in
TechnicalDebt.
The library gets its own CMakeLists.txt, equivalent to the generic rule,
to register its test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Report the thread's affinity mask as a 32-byte FreeBSD cpuset_t, with
FreeBSD 11's errors: ERANGE for any other set size, ESRCH for a null
thread and EFAULT for a null set.
EVO-PLAYER (PPSA99039 v0.11.0) imports it (Jb2uGFMr688) and calls it
once with a 32-byte set on pthread_self().
sceSharePlayGetCurrentConnectionInfoA now writes a 72-byte
SceSharePlayConnectionInfoA that reports no session: status DORMANT,
mode 0, empty online IDs, account IDs 0 and both user IDs invalid (-1).
A null pointer returns SCE_SHARE_PLAY_ERROR_INVALID_ARGS (0x810E0001)
instead of throwing. KytyPS5 and fpPS4 agree on the layout and the error
code; KytyPS5 sets the user IDs to -1, fpPS4 leaves them 0.
Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
MinGW-w64's UCRT has no aligned_alloc, so the Windows build of
guest_job_manager_tests fails since #3170. Use the aligned operator new
and delete instead, which are standard C++17 on every host.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Measured on a PS5 (firmware 13.60):
- sceSslGetMemoryPoolStats returns 0x8095F007 for a null output, before
the id check, and 0x8095F006 for an unknown or terminated context,
zeroing the 32-byte output.
- sceHttp2GetMemoryPoolStats returns 0x817B1225 for a null output,
before the id check, and 0x817B1100 for an unknown or terminated
context, zeroing the 32-byte output.
- sceNetGetMemoryPoolStats checks the pool before the output, so an
unknown pool with a null output returns EBADF.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PS5 dumps carry no playgo-chunkdefs.xml (only sce_sys/playgo-scenario.json, which
lists no chunks), so the valid set fell back to chunk 0 and every query for another
chunk returned SCE_PLAYGO_ERROR_BAD_CHUNK_ID. A title that asks for all of its chunks
in one scePlayGoGetLocus call then never sees them as installed and waits forever.
Without definitions the chunk table is unknown and a dump is fully installed, so any
chunk id is accepted and reported as local. With playgo-chunkdefs.xml present,
validation is unchanged. scePlayGoGetChunkId still reports the known set.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VLC (PPSA85300) imports daemon (Wn6I3wVATUE) and fails to load because
no library exports it. Forking into the background is not supported,
so it throws if called. VLC 3.0 calls daemon only when its daemon
option is set, and the title starts libVLC with --ignore-config and
without --daemon.
A job's inputs were joined into one vector before decoding, copying every
input buffer at its stated size. PPSA36082's FMOD submits Opus run jobs
whose one input buffer states 0xFFFFFE74 bytes (a remaining size that
went below zero) ahead of a single packet, so each such job copied about
4 GiB and sceAjmBatchStart took about a second; the mixer missed its
deadline and the title pushed silence. A job with one input buffer now
decodes from that buffer in place, reading only the packets it decodes;
jobs with several input buffers are joined as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A stack base need not be page aligned (it is not on Linux), so rounding the
range down first could leave the stack and miss the stack path. The range the
guest asked for is now checked against the guest stack, and the rounded
pages are protected, as FreeBSD rounds the address down.
A thread that exits with part of its stack protected left those pages to the
next thread that reused the stack. Protected stack pages are recorded, and a
thread's stack is made readable and writable again when it is unregistered at
exit.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Windows a guest thread runs on its host thread's stack, which is not a
registered guest allocation, so mprotect on any of its pages failed with
EINVAL. A guest that guards the bottom of its own stack (Dolphin's JIT
read-protects 64 KiB there to catch stack overflows) got an error where the
PS5 changes the protection.
A range that lies inside a live guest thread stack, with no GPU access
requested, now takes the native protection directly. Ranges that cross the
stack's bounds are still rejected.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Return a host module handle for a loaded sysmodule id so callers can
sceKernelDlsym after sceSysmoduleLoadModule. Unknown or null inputs
throw like neighbouring internal load paths; unloaded ids return
0x805A1001. Unblocks Prospero Win past NID D8cuU4d72xM at load.
Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
The bundled libc.prx of PPSA21703 calls sceKernelGetModuleInfoFromAddr
with a 0x1A8-byte stack buffer and never sets st_size, so the field holds
whatever was on the stack (983040 in one run) and the call threw. The
console cannot require the field on input. The full size is still
written back. The flags check is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The original fix predates llroundf, which was added in #2933. Use the same guest range and exception behavior for the fourth wrapper and include it in the edge-case checks.
FreeBSD amd64 converts the rounded value with a plain cast, so NaN, infinities and values outside the 64-bit range give 0x8000000000000000 with FE_INVALID raised. The Windows host llround returns 0 for them.
(cherry picked from commit 0ee2741534bdfcc9d818b1d93f2eb793a68f111e)
FreeBSD's memset_s returns EINVAL for every constraint violation and writes nothing when smax or n exceeds RSIZE_MAX. The guest returned ERANGE when n exceeded smax and wrote up to min(n, smax) bytes even when smax or n exceeded RSIZE_MAX.
(cherry picked from commit bf08837cf929e40f1b646df82e140a33076d5587)
Kena: Bridge of Spirits (PPSA01802) refuses ten pixel shaders with
packed pixel ancillary input has an unsupported live use (used by SelectU32 ? ?)
at any visit limit. All ten have 30 to 33 phis on loop back edges (reported
on Linux with an RX 9070 XT).
Two rules stopped the proof at a loop, and the first one fires before the
second is ever asked:
- The walk from the select gave up at the first use inside a loop. A value
derived from the select reaches a loop phi as soon as its register is
written inside the loop, because the masked write becomes
Select(loopExec, new, phi). A loop that leaves exec alone needs nothing
more than this.
- An exec that a loop narrows and carries around its back edge is a phi
inside the loop, and such phis counted as not implying the write's exec.
The walk now follows uses into loops. A phi inside a loop implies the
write's exec when all of its incoming values do, where the phi itself counts
as implying it while its back-edge values are checked. This is sound because
each rule of the proof takes a lane bit of one value from a value computed
earlier: a phi's value on each iteration is the incoming value computed
before the edge was taken, a set lane of an And is set in both operands, a
set lane of a ballot is a lane whose predicate held. If any value in the
proof had a lane set where the write's exec is clear, the earliest such
value would take that lane from an earlier value of the proof that is set
there too, which contradicts it being the earliest. Two conditions keep this
true:
- The write's exec must be computed once, outside every loop. Otherwise a
phi can carry a ballot of it from an earlier iteration, which is not the
value the select used. Masks computed inside a loop keep the old rules.
- A result may only be kept if the assumptions it used held. When a value
that was assumed turns out not to be implied, every result recorded since
it was assumed is dropped. Results that say "not implied" are kept; they
only make the proof refuse more.
A lane that is turned back on inside the loop makes the back-edge exec wider
than the write's exec, so the phi is not implied and a read under it keeps
the select. A lane that is turned back on but never reads the derived value
does not.
New tests cover a loop that carries the write with exec saved, narrowed and
restored (read inside and after the loop), a loop that leaves exec alone,
and the helper-only ancillary select feeding such loops. The refusing tests
each fail when the matching check is removed: a revived lane that reads the
write inside the loop (ignoring back-edge values), a result proven under an
assumption that later fails (keeping it), and a write mask computed inside
an earlier loop (allowing such masks). Two existing tests expected a loop to
keep the select although every read is under the write's own exec; they now
expect it to go, next to new counterparts with a wider exec.
On the Taxi Chaos 2 requests, compute shaders with a loop inside a branch now
lose the branch's masked writes: 0x194000b400 removes 207 selects instead of
36, 0x19002bd200 111 instead of 28, and seven more gain 5 to 43. In
0x16400cf700 the 26 new removals are writes under the branch condition
tid < count; the loop's exec starts as a ballot of that condition, only loses
lanes at the latch, and every store and atomic inside the loop runs under it.
No proof on these requests had to drop a result.
Walks now go on into loops, so the pass takes longer on loop shaders, but it
stays below main. Time in MaskedSelectEliminator per prepare (median of 3,
the pass runs twice per prepare), 54 Taxi Chaos 2 requests, one run:
shader previous commit this commit main 5edae353
0x1ac0413000 334 ms 349 ms 424 ms
0x19002bd200 38 ms 103 ms 112 ms
0x19c016aa00 9 ms 74 ms 16 ms
0x194000b400 13 ms 31 ms 32 ms
all 54 547 ms 805 ms 977 ms
0x19c016aa00 removes nothing more; its walks only end later inside its
loops. The 144 requests of the NVIDIA dump go from 1283 to 1589 ms and gain
removals in the copies of the same shaders. 197 of the 198 requests prepare,
as before.
Related: #2548
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LEGO Horizon Adventures (PPSA14632) still refuses a pixel shader with
packed pixel ancillary input has an unsupported live use (used by SelectU32 ? ?)
although it has the form the previous commit handles: the only whole-word
reader is SelectU32(IEqual32(HelperInvocation, 0), BitFieldUExtract(x, 0, 11),
ancillary), the exec words are Ballot(not helper) & Ballot(true), and there
are no image ops, stores or loop phis. About 10,250 IR values derive from the
word, so the walk from that select stops at its fixed limit of 4096 visited
values and the select is kept. With the limit raised to 8192 the shader
prepares (reported on Linux with an RX 9070 XT).
A walk may now take max(4096, 2 * instructions) steps. A step is a value
visited for the first time or an implication evaluated for the first time.
The walk only visits values derived from the select, at most one per
instruction, so a walk is only cut short when the implications it evaluates
cost as much as the program itself. On the 198 Taxi Chaos 2 requests no walk
needs more than 0.79 * instructions steps. One walk stays linear in the
program, O(instructions + operands): each value is expanded once, and with
the per-walk memo each implication is evaluated once.
The walk is also cheaper, so that the larger limit costs nothing:
- visited values are marked in a table indexed by value id instead of a hash
set, and the table, the work list and the memo are reused across walks;
- blocks outside loops are looked up in a table indexed by block id instead
of a hash set. This lookup ran once per use and was most of the pass's time.
Time in MaskedSelectEliminator per prepare (median of 3, the pass runs twice
per prepare), 54 Taxi Chaos 2 requests:
shader before 40a8a5a7 this commit main 5edae353
0x1ac0413000 689 ms 285 ms 401 ms
0x19002bd200 102 ms 34 ms 100 ms
0x18c07bf000 191 ms 52 ms (refused)
all 54 1294 ms 484 ms 919 ms
0x1ac0413000 and its three copies in the NVIDIA dump now remove 659 selects
instead of 625: their walks no longer stop at 4096. No other request changes
what it removes. 197 of the 198 requests prepare before and after; the other
one fails the same way in both (v_readlane_b32 of lane 63 on a 32-lane host
subgroup).
Related: #2548
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Taxi Chaos 2 (PPSA30279) pixel shader 0x18c07bf000 is refused when it is
registered, and the title stops:
packed pixel ancillary input has an unsupported live use (used by SelectU32 ? ?)
The shader reads the ancillary VGPR (v2) once, with v_bfe_u32 v3, v2, 16, 11,
which #1904 lowers to Layer. The next write to v2, v_rcp_f32 v2, s33, runs
before any kill under the initial exec and becomes
SelectU32(IEqual32(HelperInvocation, 0), rcp, ancillary). The raw word
therefore survives only in helper lanes. The shader has no s_wqm, no DPP,
readlane or swizzle, no stores, and only *_lz samples and gathers. Every
helper-lane value that comes from v2 is dropped by a later exec select, a
compare that is ANDed with exec before its ballot, or an export whose exec
excludes helper lanes.
MaskedSelectEliminator exists to remove selects like this one, but it could
not prove the case. The kill paths save and restore exec, so the later execs
are phis at block merges, and the AND chains the kills build go deeper than
its eight-level limit.
When it checks whether a later exec implies the write's exec, it now:
- accepts a phi when the phi and each incoming value lie outside loops and
every incoming value implies that exec;
- pairs low and high exec words merged by phis in one block, predecessor by
predecessor;
- memoises results for each select and follows up to 64 levels. A cycle
counts as not implied.
Once the select is gone, nothing reads the ancillary word, so no bit of it
is produced. A select whose false lanes can still be observed is kept and
the shader is still refused. That covers a data test that leaves the word in
non-helper lanes that later export, an export under the full wave, and an
unmasked ballot.
Related: #2548
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port FreeBSD 11 lib/libc/stdlib/random.c (freebsd-src 4ab2e064): the
additive feedback generator with the default 128-byte TYPE_3 table,
good_rand seeding, state sizes rounded down to 8, 32, 64, 128 or 256
bytes, setstate returning the previous state, and initstate returning
null below 8 bytes without setting errno. Calls are serialised by one
lock; a null state throws.
The import audit of the homebrew catalog on main 262c0316 lists them as
absent libSceLibcInternal imports:
- random: PPSA74126 Stremio Plus, PPSA85300 VLC, PPSA99169 RetroArch,
PPSA99177 PS5VR, PPSA99420 PS5MC, PPSA99505 Jelly5, PPSA99515 Emby5,
PPSA99810 PSBox Cloud Gaming
- srandom: PPSA85300, PPSA99169, PPSA99420, PPSA99810
- initstate, setstate: PPSA99177, PPSA99420, PPSA99505, PPSA99515
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Covers code, stack and an anonymous page, and an unmapped address. On Linux the test runs again with APS5_NO_PROCMAP_QUERY so the line scan stays covered.
A title calls our exports on stacks it sized for the console. With lazy
binding the first call through a PLT slot runs the dynamic linker's symbol
lookup on that stack: on the 2 KiB fiber context Astro's Playroom uses it
wrote below the context into the neighbouring heap chunks.
The title id now comes from GetAppTitleId, so param.json has one parser and
its validation (a missing file or titleId throws). Another pid returns EPERM
and a null info returns 0 without writing, as shadPS4 does. app_id stays 1,
the running title's id in libSceSystemService, which is inferred, not
measured.
guest_app_info runs the call without param.json in a child process and
checks that the field between app_id and the title id is left alone.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sceKernelGetAppInfo fills the app id and the title id of the calling
process: app id 1, the running title's id in libSceSystemService, and the
titleId of /app0/sce_sys/param.json at offset 0x10, up to 9 characters and a
terminator. Only those fields are written, since callers declare the
structure with different sizes. Another pid returns ESRCH and a null info
EFAULT.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The AMM ranges were reserved address-less on first use, so they took the
lowest free address of the arena, directly after the title's last mapping,
where the title's next hinted maps go. They are now reserved at a fixed
place, the last 64 GiB of the guest arena, which maps without MAP_FIXED and
the hints derived from them do not reach; a reservation that cannot be made
there throws.
Fixes#3146
Split at the first delimiter like FreeBSD's strsep: empty fields are
returned, and the cursor becomes null after the last token.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Share rand's generator step with rand_r, which advances the caller's
state instead of the global one, as FreeBSD's rand and rand_r share
do_rand.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on a PS5 (firmware 13.60):
- sceRudpInit rejects a null pool or a size of 0 or less with
INVALID_ARGUMENT, and a pool under 0xC58 bytes or not 8-byte aligned
with MEMORY (0x80770007).
- sceRudpGetStatus rejects a null pointer or a byte count of 0 or above
0xF8 with INVALID_ARGUMENT and reports the live context count at 0x64.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Export sinh, cosh, coshf, asinh, asinhf, acosh, acoshf and atanh,
forwarding to the C++ standard library like tanh and tanhf.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The POSIX alias takes (attr, int), returns EINVAL (22) for a null or
uninitialized attribute like the other pthread_attr setters, and
stores the value through scePthreadAttrSetsolosched, returning its
result in errno form. The signature and the stored int follow the SCE
twin in KytyPS5 and sharpemu; PS5PCEM implements the alias the same
way. The value is not applied to scheduling, recorded in TechnicalDebt.
Returnal (PPSA01285 v03.000.005) stops on it after eager binding
(#2872, #1155):
what(): pthread_attr_setsolosched_np_nid_postfix not implemented
LEGO Horizon Adventures (PPSA14632, #734) and Palworld (PPSA23126,
#2598) import it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
guest_filesystem creates symbolic links for the Windows lstat checks.
Without administrator rights or Developer Mode, CreateSymbolicLinkW
fails with ERROR_PRIVILEGE_NOT_HELD even with
SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE, and the test aborted there
(reported as a stale guest errno). Skip those checks with a note in that
case; any other failure to create the first link still fails the test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#2839 decides f32 product tininess after rounding, in all four f32
round modes, from the exponents and a UMulHi and IMul of the
significands, and builds the flushed value from them. To nearest even,
the mode of shaders without a known float mode and of the 0xc0 most
titles use, one multiply decides it: one factor times 2^64, which is
exact below 2^64, times the other factor rounds the exact product once
to 24 bits in the normal range, and 0 < |that| < 2^-62 is tininess
after rounding. A product that rounds up to 2^-126 is then not tiny and
keeps the emitted product, which is 2^-126 there, so nothing has to be
built from the significands.
The flush of v_mul_f32 v5, v6, v7 takes 12 IR instructions instead of
34, counted as the instructions the flush select needs beyond the
product. vector_constant_factor_tininess pins 12 to nearest and 36 for
the directed path of the same instruction, and checks that the
directed modes and v_mad_legacy_f32 (before rounding) keep #2839's
check. Its round-to-nearest branches are removed as unreachable.
The f32 denormals entry says which rounding the check inherits: the
multiply is a plain OpFMul like the product, with no f32 rounding mode
requested, so it relies on the driver rounding f32 to nearest even, as
the products do. F32ConstantFactorTininess names the row a check before
rounding flushes, 0x3f7ffffe * 0x00800001, which keeps the emitted
0x00800000.
Against #2839 to nearest even, a model of both checks agrees on
1,074,977,546 operand pairs (every 24-bit significand at both rounding
thresholds, random pairs, specials) with each other and with an exact
reference. On an RTX 5070 the outputs of #2839's own tininess and
mixed-source shaders are bit-identical with either lowering over
165,298 and 164,008 generated rows in all six float modes tested.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A wait on an equeue that holds socket events now blocks in poll/WSAPoll on
those sockets plus a per-thread loopback wake socket, instead of rechecking
every millisecond. Adding, triggering or deleting an event and closing the
equeue wake the blocked waiters. Closing a socket removes its read and write
events, as on FreeBSD. A read event also reports EV_EOF once the peer has
shut down its sending side.
sceKernelAddReadEvent/DeleteReadEvent and sceKernelAddWriteEvent/
DeleteWriteEvent register EVFILT_READ and EVFILT_WRITE events on guest
sockets. The equeue polls the host socket for readiness on each wait
and reports the readable byte count or the send buffer size in data,
with EV_EOF on hang-up or error.
pthread_cond_wait and pthread_cond_timedwait, and their sce counterparts, threw a
host exception when the mutex was unlocked, locked by another thread, or not yet
initialized, and dereferenced the adaptive static initializer. FreeBSD returns
EPERM for these (EINVAL for a destroyed mutex) before touching the condition.
(cherry picked from commit a78bb8a855ca3b277f128c6a9e52ff3949b9cb80)
Resolves a guest path the way FreeBSD 11's realpath does: a relative
path starts from the guest working directory, "." and empty components
are skipped, ".." drops the last component and never climbs above "/",
every other component must exist, and one that is not a directory with
anything after it is ENOTDIR. Backslashes separate components as in the
rest of the path layer. The result is the guest path; the host is
consulted only for existence and type, so host symlinks are followed but
not expanded, and ".." is lexical, as in the rest of the path layer. A
null buffer is PATH_MAX bytes from the application heap, a null path is
EINVAL, an empty one ENOENT, and 1024 bytes or more in the input or the
result is ENAMETOOLONG. On failure the caller's buffer holds the path
that failed.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fnrk5Dazjb2tLooVSpdRQq
sceFontSetScriptLanguage, sceFontGetScriptLanguage,
sceFontSetTypographicDesign and sceFontGetTypographicDesign threw. With
the signatures SharpProspero declares, a font handle now keeps the
language set for each script and the feature set for each typographic
design, under the handle's lock, and returns them, with 0 for one never
set. They do not change shaping or rendering yet; TechnicalDebt records
the assumptions.
Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
(cherry picked from commit c4dcbe4403dc635fde2a823dec311a939816cefc)
RetroArch (PPSA99169) stops at alarm on main: `FAIL: unresolved ELF import DQXJraCc1rA from libSceLibcInternal.prx`.
alarm(seconds) arms one process-wide real-time alarm and returns the seconds left on the previous one, rounded up like FreeBSD's alarm.c (any partial second counts); alarm(0) cancels it. When it expires, SIGALRM's disposition as recorded by sigaction or signal applies: ignored, nothing happens; default, the process terminates as on FreeBSD (exit status 128 + 14); a guest handler throws, because asynchronous delivery to a guest handler is not implemented.
The timer lives in libkernel next to the signal dispositions (GuestAlarm_nid_no_patch); libSceLibcInternal, which the titles import alarm from, forwards to it.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
libSceCdlgG2P and libSceVrSetupDialog threw from every export. They now
run the common-dialog state machine libSceLoginDialog uses: Initialize,
Open, the status calls, GetResult, Close and Terminate follow it, the
dialog finishes at Open without showing anything, and GetResult reports
that the user cancelled, writing only the leading int32 result. The
signatures are assumed from the other common dialogs, as TechnicalDebt
records. 13 exports move from stubs to implemented.
Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
(cherry picked from commit 07992adf80bae9b2d7da4b751cd21c040fa1c5b8)
PPSA11386 registers 3840x2160 tiled display buffers in pixel format
0xC001000622000000, R16G16B16A16 float in KytyPS5 and SharpProspero
(0xC001000600000000 is the blue-first variant), and stops on "VideoOut:
unsupported display pixel format". The buffer is eight bytes per texel; its
tiled layout is assumed to be the 8-byte SW_64KB_R_X equation applied to
full coordinates, as addrlib applies it. The values are taken as linear
light and encoded with the sRGB curve for the 8-bit output. The encoding
compares the raw half-float bits against two tables of 255 thresholds
(color and alpha) that the CPU decode and the color transfer shader share,
so both give the same bytes on any driver; negative and NaN values give 0.
Guest-memory detile, the resident image path and the CPU decode all handle
both formats; DCC metadata on a float buffer throws. Neither the layout nor
the transfer is verified against console frames.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fnrk5Dazjb2tLooVSpdRQq
The registry calls an observer on the mapping thread, after the registry
lock is released, with the ranges a kernel mapping gave GPU access:
direct and flexible maps with it, and fixed remaps with it over a
registered range (a reservation). The mutation looks them up while it
still holds the lock and hands them over weakly, with the registry
generation it ended at, so the observer does not keep a range alive.
The driver imports those ranges on its current device, so an
address-based build finds the import instead of pinning the memory on
the queue that first uses it. A range that gains GPU access through
sceKernelMprotect, and AnyPS5's own libc heap, are still imported on
first use.
The import at map does not reconcile the other imports with the
registry, so its cost does not grow with the number of imports. It skips
a range that is gone, and any range when the imports were reconciled
with a newer registry since; the next lookup imports those as before.
It installs the pin waiter as the other import paths do, so unmapping a
range imported at map retires its import. The live import bytes are kept as a
running total instead of being summed over every import. Once the import
table belongs to the current device the import takes only the table's
lock; the first import on a device (which runs the write-watch probe)
and a device that was just replaced go through the GPU lock. Clearing
the observer waits for calls already running.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ProsperoRadio (PPSA99001) imports six sceOpusCeltDec functions from
libSceOpusCeltDec and fails to load because no such module exists. It
opens this decoder only as a fallback when libSceOpusDec reports a
retryable error for a CELT packet, which libSceOpusDec here never
returns, so the module is exported without an implementation and each
function throws if it is called, as for sceSaveDataCancel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Twenty-five of the ninety-seven errno messages differed from FreeBSD releng/11.4 lib/libc/gen/errlst.c (for example errno 0, 30, 47, 62, 70 and 84).
(cherry picked from commit ce14c9ad7dc639335ff72b60122bae2bd0fee302)
sceSystemServiceParamGetInt always reported English (United States) as
the console language, so a title that follows the console's language,
such as Jelly5 (PPSA99505) with its Arabic text, can only show English.
ANYPS5_LANGUAGE now selects a console language number from 0 to 30
(21 is Arabic). Unset or empty keeps English (United States); any other
value throws at the language query instead of being ignored.
(cherry picked from commit 9bcf196daa01b07a22be876a7f1143149503e054)
AI-assisted: yes. Verify AnyPS5 rather than the native PC hostname. Cover buffer capacities, guard bytes, maximum size_t capacity, null queries, success errno and thread-local isolation. The old implementation fails this regression; the final fixed tree passes the complete Windows build and five repeated runs each of guest_hostname and guest_errors on main 5edae3536. Linux and title tests of this new revision are pending.
AI-assisted: yes. Add a deterministic backend that overwrites errno on success; verify restoration on success and retained failure errors. Extend native tests for current-time errno preservation. The new regression fails on original ca87abb and passes with this fix.
PPSA11386 calls sceSharePlayInitialize(NULL, 0x1800) at start-up and stops
on any non-zero result, so the console accepts a null heap. The library
keeps no state and does not use the heap; a zero size still throws, as its
error is unknown. libSceSharePlay gets its own CMakeLists for the test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Export it next to remainderf, stepping to the adjacent float in the
direction of the second argument.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Export it next to lroundf and llround, rounding halfway cases away
from zero into the 64-bit guest long long.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both parse like strtol and strtoll in base 10. The guest long is 64
bits wide on every host, so atol uses strtoll rather than the host
atol, whose long is 32 bits on Windows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Export the double version next to __isnan and __isfinite, returning
1 for either infinity like __isinff.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PokeMMO Prospero (PPSA98001) stops at siglongjmp (pebqbE5ws8s) from
libkernel. The title links its own FreeBSD sigsetjmp, so siglongjmp
follows FreeBSD's amd64 __siglongjmp and its sigjmp_buf layout: when the
save-mask flag at offset 88 is set it restores the mask saved at 72
through _sigprocmask, then reloads rbx, rsp, rbp, r12-r15 and the x87
control word and returns to the saved address with val, or 1 for 0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FreeBSD's unlink ignores the file's own permission bits, so it succeeds
on a file the guest made read-only with chmod(0400). On Windows the guest
chmod sets the read-only attribute and _wunlink then failed with EACCES,
which sceKernelUnlink and unlink reported. When the path is a regular file
without write permission, make it writable before the single _wunlink and
restore the read-only attribute if the unlink fails, as remove() does
since #2342.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The mutex attribute now keeps its protocol and priority ceiling the way
FreeBSD's libthr does: the ceiling can be set and read only under
PTHREAD_PRIO_PROTECT (EINVAL otherwise), and setting a protocol resets it
to the highest SCHED_RR priority. Mutexes still do not apply priority
protection; that is listed as a silent stub.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit d10c024f8fdbcdfced0187550f272333264f07df)
A map without MAP_FIXED whose hint lies wholly inside a range still
reserved by sceKernelReserveVirtualRange is placed at the hint, as a fixed
map into the reservation would be. Hints outside reservations and ranges
that are partly mapped keep their hint behaviour. The reservation is
checked under the registry mutation, so it cannot be released and its
address reused between the check and the map (MarcZX100, #2687).
Fixes#2687
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A2cY3SsTYnh4u4ZtEr2Adx
sceAvPlayerAddSource queues an auto start for a player created with auto
start or without an event callback. When the title enabled its streams
and called sceAvPlayerStart before the controller thread ran it, the auto
start called Start again on the playing player, which stopped it, released
every video buffer, including the frame the title held, and started the
film over. guest_avplayer occasionally caught this as "video frame is not
a texture allocation"; right after the test's own start, 8 texture
buffers were released in 15 of 15 runs.
The auto start now starts the player only while it is still ready, and
checks that under the player lock together with the start.
TestAutoStartKeepsStartedPlayback starts a callback-less player itself and
checks that no texture buffer is released afterwards and that the frame
it holds stays allocated. It fails on every run without the fix.
Validate timeout arguments and track live requests so unknown or deleted
ids return REQUEST_NOT_FOUND. This supplies the remaining absent load
import alongside the dialog Close export; timeouts follow the existing
offline network model and are not stored.
Co-Authored-By: GPT-6.1-sol <noreply@openai.com>
sceAjmBatchJobDecodeSplit queues a split run job with the stream
sideband, as sceAjmBatchJobDecode does for a single buffer pair.
sceAjmBatchJobSetResampleParametersEx with a zero ratio change per
sample sets the ratio as sceAjmBatchJobSetResampleParameters does; a
non-zero change throws, since its ramp is unknown.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A guest path whose bytes the host cannot convert to a native path made
ResolvePath throw std::filesystem::filesystem_error, which aborted the
process. No file can have such a name on the host, so stat reports
ENOENT, as for any other missing file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VLC (PPSA85300) imports execvp (0NJDcQ2B8vE) from libkernel and fails
to load because no library exports it. Replacing the process image is
not supported, so it throws if called, like sceSystemServiceLoadExec
does for executable replacement. VLC itself cannot reach the call: its
fork and vfork fail with ENOSYS, so the child that would call execvp is
never created.
sceKernelSignalSema threw invalid_argument for a count of 0. Marvel's
Wolverine (PPSA03671) signals with a count computed as min(a, b) - c,
which can be 0, and ignores the result, so the throw ended the title a
few minutes in. A zero count now adds no tokens and returns SCE_OK, as
in shadPS4 and Kyty. A null semaphore and a negative count still throw.
guest_kernel_errors signals 0 on a full and on an empty semaphore and
checks that the count is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 7191d34c4d79622ba71087f0da2b75eb81ad2c23)
sceKernelMkdir checked that the parent of the resolved path exists, but
for "dir/" std::filesystem::path reports "dir" itself as the parent, so
the call failed with ENOENT and created nothing, while FreeBSD's mkdir
(and the POSIX mkdir export) accept it. Strip trailing separators first,
and report ENOTDIR when the parent exists but is not a directory.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 5f74ddba8b346e6c552cf02554d47382708206f3)
PPSA11386 imports sceSslGetCaList and sceSslFreeCaList and PPSA15508
imports sceSslUnloadCert; on main all three are missing and the loaders of
both titles stop on them. No root certificate bundle is shipped and no
loaded certificate is kept, so an SSL context holds no certificates:
sceSslGetCaList clears the list and returns NOT_FOUND, as sceSslGetCaCerts
does; sceSslFreeCaList accepts only that cleared list; sceSslUnloadCert has
nothing to drop and is assumed to return 0. A null list returns the
invalid-argument code that sceSslGetCaCerts and KytyPS5 return. An unknown
context and a list sceSslGetCaList did not return throw, as their codes are
not confirmed. Signatures and the SceSslCaList layout follow SharpProspero
and shadPS4.
guest_ssl_tests now links libc, as guest_http2_tests does, so an exception
thrown from libSceSsl unwinds with the same unwinder that resumes it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add the guest socket descriptor guard to sceKernelFdatasync before any
native syscall, matching the existing sceKernelFchmod pattern. This
prevents high guest socket descriptors (e.g. 0x10000000) from reaching
the Windows CRT _commit, which would trigger the invalid-parameter
handler, and ensures the correct SCE error is returned on all platforms.
Refactor fdatasync_nid_postfix to delegate to sceKernelFdatasync via
PosixResult, removing the duplicated socket guard and native syscall.
Extend GuestFilesystem tests to cover:
- sceKernelFdatasync on a valid guest socket returns SCE EINVAL (0x80020016)
- sceKernelFdatasync on a closed guest socket returns SCE EBADF (0x80020009)
- sceKernelFdatasync on a high invalid descriptor (0x7fffffff) returns SCE EBADF
- closed-ordinary-descriptor tests now run on Windows (verified _commit on
a closed descriptor returns EBADF without provoking the CRT handler)
TestUnsyncedVideoKeepsUpWithAudio built its player without an event
callback, which enables auto-start (autoStartEnabled = init.auto_start ||
callback.event_callback == nullptr). The controller thread then enables the
streams and starts playback shortly after the source is added, racing the
test's own sceAvPlayerEnableStream. When the controller won,
Source::EnableStream saw started == true and the test failed with "enable
video failed".
The test now passes an Events object, as the other tests that drive the
player by hand do.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Without MAP_FIXED the address is a hint, as for FreeBSD's mmap: the reservation goes at the hint when that range is free, else at the first free range above it. The hint was dropped, so every hinted reservation landed at the lowest free address.
RetroArch (PPSA99169) reserves its cores' direct-memory heap near 0x2000000000 outside the GPU window (0x200000000-0x300000000) and refuses a range inside it; with every reservation at 0x200400000 the heap was never made, and Dolphin's first aligned allocation returned null.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Windows sceKernelPread stopped going through pread when it learned to
return errors, and with it lost the random device read, so a pread on
/dev/urandom reached ReadFile on a handle that is not a disk and failed
with ESPIPE. Read random devices first, as the Linux path does.
A pread into a read-only guest mapping now returns EFAULT instead of
throwing, matching Linux and the AIO read beside it, so the Windows
guest memory test checks for that return.
CheckUnmappedArenaRangeStaysReserved uses mmap, PROT_READ, PROT_WRITE,
MAP_FIXED_NOREPLACE and EEXIST directly, so the Windows build fails to compile it.
The probe exists to show that an unmapped arena range still rejects a hostile
MAP_FIXED_NOREPLACE mapping, which is Linux-specific behaviour.
Guard the function and its call site with __linux__. The other tests in the file
compile on both platforms.
The arena constructor was Windows-only, so on Linux Available() was false: the guest heap fell back to the host allocator and direct memory landed wherever the kernel pleased. Titles that require a low virtual address rejected the mapping and re-issued the identical request, which surfaced as a start-up assert inside their own allocator.
Seed the window from the mappings that already exist instead of reserving it up front, because a PROT_NONE reservation of the whole range starves the kernel's own top-down mmap, which picks addresses just below the stack inside that range. Commit, reset and set-protection become portable so the arena owns its used ranges on both platforms, and fixed mappings are checked against the arena's used ranges rather than against EEXIST from a reservation.
The window-placement and no-overwrite checks in guest_memory were compiled only for Windows; they now run on Linux too, and the window check fails without this change.
Fixes#692
Since guest threads get exactly the stack size they request, host code
that a guest thread calls runs on that small stack too. sceAgcCreateShader
reaches Driver::RegisterShader, whose frame alone is about 50 KiB, so a
title whose job threads ask for 48 KiB overflows the stack (#2603).
Each guest thread now gets a fixed 1 MiB of host stack below the range it
asked for. The guest still sees the requested size and address range
through scePthreadAttrGet and sceKernelIsStack; only host calls made from
the thread use the extra room. Running the prepared-shader driver tests on
a limited main-thread stack needs between 128 and 192 KiB for shader
registration, Vulkan setup included, so 1 MiB leaves several times that.
Switching to a separate host stack at each HLE entry point would need a
trampoline on every export that may go deep, handling of stack-passed
arguments and unwinding across the switch, and a list of which exports
qualify. A margin below the guest range covers every host call with no
per-export list.
On Windows the committed size was the requested size, and the margin
below it depended on the executable's default stack reserve; requests at
or above that reserve had none. The commit now includes the same margin.
macOS does not run guest threads.
guest_pthread_stack now also calls host code that uses 256 KiB of stack
below each thread's guest range; without the margin it crashes on the
16 KiB thread.
Fixes#2603
scePthreadCancel and pthread_cancel threw, scePthreadTestcancel did
nothing and the cleanup handler exports did not exist.
A cancel request is now recorded on the thread and acted on at
scePthreadTestcancel, the condition variable waits, scePthreadSem and
sem waits, scePthreadJoin and the sleeps, and under their POSIX names.
A thread blocked in one of the waits is woken; a condition wait takes
its mutex back before the thread exits. The thread exits with
PTHREAD_CANCELED through scePthreadExit, which now runs the handlers
of __pthread_cleanup_push_imp and __pthread_cleanup_pop_imp.
scePthreadSetcancelstate and scePthreadSetcanceltype reject unknown
values, and pthread_setcanceltype is added.
Constants, the handler node layout and the order of events follow
FreeBSD libthr. A sleep is not interrupted, file and socket calls are
not cancellation points and the asynchronous type acts like the
deferred one except at the two set calls (TechnicalDebt). New test:
guest_pthread_cancel.
Astro's Playroom (PPSA01325) calls sceRudpActivate during start-up, where the throwing stub stopped it. The host has no reliable-UDP stack to activate, so the export returns success and does nothing, matching the OK-returning stubs shadPS4 keeps for the whole library. The guest_offline_imports test now checks the success path with and without sceRudpInit, and still checks that the sceRudpGetStatus sibling throws and writes nothing.
st_ino was always 0 and st_dev differed between stat (drive number) and
fstat (0), so the usual a.st_dev == b.st_dev && a.st_ino == b.st_ino
identity check gave wrong answers. Read the volume serial, the file index
and the link count from the file handle, which works the same for path and
descriptor lookups.
Fixes#2296
ProsperoStore (PPSA99000) imports kill from libkernel and stops at it on
current main. Its crash reporter resets a crash signal to SIG_DFL and
sends it again with kill(getpid(), signal).
kill follows FreeBSD's kern_kill and killpg1 for the only guest process:
EINVAL for signals outside 0 to 128, signal 0 only checks the target,
and the caller's own pid, 0 and minus its pid deliver through raise.
Any other pid, and -1, which skips the caller, report ESRCH.
The registry records whether a range was mapped or protected with GPU
access (0x10/0x20), and an address-based build leaves out ranges without
it, as the console's GPU page tables do. The main image is registered
without GPU access; the title's own mprotect with GPU bits adds it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop IFF_POINTOPOINT from an address without a peer and compute an IPv4 broadcast address the host omits from the netmask, since FreeBSD always provides the address when the flag is set. Check zero port, flowinfo and padding in the test.
Return -1 with the FreeBSD errno for a null list and for expected host and allocation failures, as getcwd does. On Windows, give an unspecified prefix length a host mask, leave PPP and tunnel adapters without broadcast or point-to-point flags since their peer address is not reported, fall back to if<index> for long NDIS names, and bound the adapter query retries. Compare every entry with the host's own list in the test.
List the host's IPv4 and IPv6 addresses in FreeBSD's struct ifaddrs layout, with FreeBSD sockaddrs and interface flags, from getifaddrs on Linux and GetAdaptersAddresses on Windows. The list is one allocation, so freeifaddrs is a single free.
Move the image unregistration out of the Module destructor into
dlclose_nid_postfix. It runs while the handle is still registered, so a
failure (the image stays leased by GPU work) propagates to the caller and
leaves the module loaded and usable. Images shared by another open
handle stay registered until the last handle closes.
guest_memory now checks that dlclose under a held lease throws and keeps
the module, its symbols and its protection.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A .guest.prx image registered by sceKernelMprotect stayed in the guest
allocation registry after dlclose freed it, so its dead ranges blocked
later mappings at that address and could reach GPU builds.
The Windows module destructor now unregisters the image before
FreeLibrary. Unregistering waits for outstanding leases on the image like
unmap and protect do; if the wait fails the module stays loaded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Windows, sceKernelMprotect accepted only the executable's image and threw
"Memory protection of a foreign image is not supported" for every other
image. Bundled title modules are relinked into DLLs named <module>.guest.prx,
so a title that reprotects its own module pages failed. Dying Light 2's
bundled libNRD does this at start-up (#1794).
DoMprotect now also accepts images whose file name ends in .guest.prx, and
registers their committed pages like the executable's. Other images are
still refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Windows, sceKernelPread and sceKernelPwrite called the POSIX pread and
pwrite wrappers, which throw on every failure, so a bad descriptor, a
negative offset or a write to a read-only descriptor aborted the process
instead of returning an error as the Linux build does. ftruncate and
sceKernelFtruncate threw on any host failure on both platforms.
Give the Windows sceKernelPread and sceKernelPwrite the same checks and
SceErrorFromErrno returns as the Linux ones, and accept a zero-length
transfer with a null buffer. Report a positioned transfer on a handle
opened without the needed access as EBADF instead of EIO. Return EINVAL
from ftruncate for a negative length and for a descriptor opened without
write access, as FreeBSD does, and pass other host errors to the guest.
Fixes#2423
sceFontGetCharGlyphMetrics and sceFontGetRenderCharGlyphMetrics set the
vertical bearing and advance to zero although the glyph is loaded with
FT_LOAD_VERTICAL_LAYOUT. The library's own text writing steps the pen by
that vertical advance, so every character of vertical text was placed on
the previous one.
The vertical metrics now follow the FreeType driver's metrics of a
rendered glyph: FreeType's vertical advance, and for a face without
vertical metrics a bearing of half the horizontal advance left of the
horizontal bearing and of half the advance left over by the glyph height.
guest_system_font checks every metric of a square glyph at 100 pixels.
(cherry picked from commit 04dd69f806379df8b52cfdeb2979f2595973a9b3)
(cherry picked from commit 11f6f9e954756ee8d4a3fc2be574ab3b57ecd7c5)
Palworld (PPSA23126) imports sceSaveDataCancel (e6y3WMBYbiM) from
libSceSaveData and fails to load because no library exports it. Its
signature is unknown, so it is exported without an implementation and
throws if it is called, like sceSaveDataConvert.
On Windows, getargc and getargv reported only the executable path, read
with GetModuleFileNameA in the ANSI code page, and dropped every other
argument. Linux returns the whole command line from /proc/self/cmdline,
and the relinker's Windows entry stub already passes the guest entry all
arguments converted to UTF-8.
Parse GetCommandLineW with CommandLineToArgvW and convert each argument
to UTF-8 with WC_ERR_INVALID_CHARS, as the entry stub does, so getargv
matches the entry's argv and the Linux behaviour. The Linux path is
unchanged.
On Windows libc exports __cxa_init_primary_exception, std::uncaught_exception
and std::uncaught_exceptions only as _nid_postfix symbols (GCC_GLOBAL_ALIAS
is empty there), so host modules bind libstdc++-6.dll's versions:
- std::make_exception_ptr, which std::promise's destructor uses for
broken_promise, allocated the exception with libc and initialized it with
libstdc++'s __cxa_init_primary_exception, whose header layout differs.
A test that makes one and rethrows it dies with an access violation.
- std::uncaught_exceptions read libstdc++'s thread state, which libc's
throws never touch, so it returned 0 while an exception unwound. The
AGC driver's deferred-label guard relies on it to tell a failed record
from a finished one.
The three are now exported natively, like __cxa_throw and the rest.
__cxa_init_primary_exception also sets the reference count to 0, as the
Itanium ABI does: the exception_ptr created next takes the only
reference, so a made exception is destroyed once instead of leaking.
On Windows std::_Exit ends in ExitProcess, which still runs every module's
DLL_PROCESS_DETACH and TLS callbacks after the other threads are gone. A
static destructor that joins a worker then waits forever: a title that calls
_Exit while libSceVideoOut's flip queue runs (ps5-opengl's retirement
fail-stop) stays alive in FlipQueue's destructor. _Exit and libkernel's _exit
now terminate the process with TerminateProcess, as _exit ends a POSIX
process: no atexit handlers, stream flushes or module destructors.
Unknown options are now checked for every pid, with 0x80 and 0x80000040 added, and so are valid combinations such as WNOHANG | WUNTRACED. Each call is repeated with a null status. The TechnicalDebt entry cites kern_wait, which adds WEXITED and WTRAPPED, and lists pdfork.
waitpid returns -1 with ECHILD, or EINVAL for an option FreeBSD does not know, and leaves the status unwritten: AnyPS5 runs the title as one process and exports nothing that creates a child. VLC PPSA85300 stops on main at this import.
AI-assisted: yes. Validated with the final library change on Windows; full build and eight focused runtime tests pass. Expected rename behavior comes from the public firmware 13.60 probe in PR #1798.
In SCE_AVPLAYER_AV_SYNC_MODE_NONE every decoded frame was handed out in
order, so a title that requests video slower than the film's frame rate
fell behind its audio (about 52 s by the end of the PPSA12544 intro).
While the title pulls audio, frames the audio clock has already passed are
now skipped; frames that are not late are handed out as before.
Fixes#1635
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Neither existed under the guest root, so opening them failed and titles
that then read from the descriptor crashed. Opening either for reading
now returns a descriptor whose reads, positioned reads and vector reads
fill the buffer from the host's random device, as
sceRandomGetRandomNumber does. Opening them for writing throws.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
#2279 made agc_driver_f32_arithmetic_nan fail on NVIDIA (Windows):
v_fmamk_f32 returns a quiet NaN where the signalling one is expected
(lane 1 no float mode v_fmamk_f32 is 0x7fc12345, expected 0x7f812345),
5/5 runs on main and 5/5 passing with #2279 reverted. CI's Windows job
skips the AGC execution tests, so it did not show there.
v_fma_legacy was rewritten as a split multiply and add after #2279; it
keeps that and gets back the invalid-product argument it passed before.
Fixes#2581
scePthreadCreate on Linux started guest threads with std::thread, so every
thread got the host default stack (RLIMIT_STACK, usually 8 MiB) whatever the
title asked for. A thread that asks for more than that and uses it crashes
with SIGSEGV. Once the thread ran, scePthreadAttrGet also reported the host
size instead of the requested one.
Create the thread with pthread_create and a stack of the requested size plus
what glibc keeps at its top (static TLS, the thread control block and the
start_thread frame). That reserve is measured once with a probe thread.
The thread reports [top - reserve - size, top - reserve) as its stack, as
Windows reports [top - size, top), and the creator waits until the range is
set, as on Windows.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The f16 forms of v_fma_mix computed an f32 FMA and converted it to f16,
so a result that lands on an f16 tie after the f32 rounding was rounded
twice. RDNA2 rounds the exact product-sum once. They now use the exact
f64 path of v_interp_p2_f16 and flush f32 sources like v_fma_mix_f32.
They follow the f16 round and denormal fields on hardware, so another
f16 mode throws, as packed f16 does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
libc implements the C++ exception runtime and already defines
exception_ptr's out-of-line members, so host modules that link libc
reference-count libc's exceptions. Code inside libstdc++-6.dll does not
see those definitions on Windows: std::__future_base::_Result_base's
destructor is one of them, and it releases the stored exception with
libstdc++'s own exception_ptr::_M_release. That decrements a count 0xa0
bytes before the object, while libc keeps it 0x80 bytes before, so the
decrement lands in the heap memory in front of libc's allocation and
the exception is never freed.
VideoOutDriver reports the presentation thread's start-up failure
through a std::promise, so video_out_flip_lifetime sometimes ended with
0xc0000374 when SDL's offscreen driver refused the window.
libc now defines the base destructor, with a _nid_postfix twin so that
the NID-patched libc keeps its name, and modules bind it there.
A zero, unaligned, overflowing or out-of-range length reached the physical pool, where a sub-page length silently freed a whole page and fragmented the block map, and an out-of-range length walked the page table past the backing store. Validate alignment and bounds up front and return EINVAL instead.
VOP3 0x140 is v_mad_legacy_f32 on gfx10.1. Measured on gfx1010 (RX 5700
XT) in wave32 and wave64, IEEE 0 and 1 and f32 denormal modes 0 to 3, it
rounds the product before the add, flushes f32 denormal sources, product
and result in every mode, and gives a +0 product for a zero factor.
The translation multiplies, applies the zero rule and flushTinyProduct,
adds, and keeps the nanResultF32 wrapper. f32DenormalFlushFor returns 3
for the instruction before reading FLOAT_MODE. A sum of two zeros takes
its sign on the bits, as lavapipe returned -0 for +0 + -0. The execution
test pins the measured results and compares NaN bits exactly. RDNA2
(gfx1031), where the encoding is v_fma_legacy_f32 and fused, is noted in
TechnicalDebt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Importing guest memory collected the write watch over the whole import,
which write-protected every page of it, so pages nothing had collected yet
took a write fault on their first CPU write. The import now collects only
the parts a collect already protects (GuestWriteWatchCollectArmed). The
rest stays unprotected until something collects it, which reports it as
written; a GPU write recorded into such memory (MarkWritten) first collects
the never-collected parts of its 64 KiB blocks
(GuestWriteWatchCollectFresh), so that report is older than the GPU's
results and does not read as a CPU write over them.
Those pages are stamped as written and their CPU pages are noted, but the
block's CPU stamp is left alone, so a GPU label record or a pending storage
image elsewhere in the block does not see a CPU write. StoredOver and
UnchangedSinceCollected read the noted pages whenever a block has them, so
a store to such a page is still seen by a check over that page. The extra
generation step happens only when something was collected; a block whose
write protection fails is unwatched and the write returns no generation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The padded, exact-fit and invalid-conversion swprintf checks come from #1512.
Co-Authored-By: Noface07 <yuvrajsoni7@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ULF8vCbJFyzxVsW3SfiWC5
flock_nid_postfix threw on every failure, so a LOCK_NB request on a lock
held through another descriptor ended the process. FreeBSD flock(2)
returns -1 with EWOULDBLOCK there. Return guest errno 35 when LOCK_NB is
set and the host reports the lock as held: ERROR_LOCK_VIOLATION on
Windows, EWOULDBLOCK on Linux. Other failures still throw.
FreeBSD's __sflags reads the access character, then b, +, x, e and v, and
stops at the first other character without failing, so "rt" opens for
reading. On Windows fopen inserted b after the first character and passed
the rest on, so "rt" became "rbt", the CRT rejected it with EINVAL, and
fopen threw. vkQuake opens history.txt with "rt" at startup. freopen
accepted only a fixed list of modes.
Both now parse the mode like __sflags. An unknown access character, or x
on a read-only stream, returns NULL with EINVAL; x on an existing file
returns NULL with EEXIST; e and v are accepted and ignored.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An anonymous private mapping with MAP_ALIGNED_SUPER (FreeBSD MAP_ALIGNED(1))
is placed on a 2 MiB superpage boundary. Yamagi Quake II requests it for its
hunk allocations of 2 MiB and more.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SDL queries the SCHED_OTHER range before it sets a thread priority. It gets
the same 256 to 767 range as SCHED_FIFO and SCHED_RR, which
pthread_setschedparam applies regardless of policy. The console's answer is
unverified and recorded in TechnicalDebt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FreeBSD sys_flock takes LOCK_UN over LOCK_EX over LOCK_SH, ignores other
bits and fails with EBADF when none of the three is set. The operation
reached the host unchanged, so malformed operations differed between
hosts: Linux rejects them with EINVAL and the Windows lock tracking threw.
Decode the operation before either host sees it.
flock mapped each call straight onto LockFileEx/UnlockFileEx, whose
byte-range locks stack per handle. LOCK_UN without a lock failed with
ERROR_NOT_LOCKED and aborted the process, a repeated LOCK_EX blocked
forever, converting between shared and exclusive failed, and a repeated
LOCK_SH needed as many unlocks.
Track the one lock each descriptor holds: a repeated mode and LOCK_UN
without a lock succeed, a conversion releases the old lock before taking
the new one, and close forgets the descriptor's lock.
On Windows the existence check accepts "file/", then _wstat64 rejects
it and FillFileStat throws, aborting the title. On Linux the host
reports ENOTDIR, which became ENOENT. A trailing slash on something
that is not a directory now returns ENOTDIR on both hosts, as on
FreeBSD.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Format the address bytes in network order as dotted decimal into a
per-thread buffer, like FreeBSD's inet_ntoa returns its static one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FreeBSD's gettimeofday stores the time only when tp is not null, and
clock_getres the resolution only when res is not null; both succeed
otherwise. Both threw, which aborted a title probing a clock or only
asking for the time zone. Write into a local result instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The unopened-handle checks call sceVideoOutIsOutputSupported before
sceVideoOutOpen, and that first call builds the VideoOut driver, whose
window creation throws without a Vulkan-capable SDL driver. The throw
left main before the skip, so the test aborted on the Linux runner:
'DisplayWindow: Vulkan support is either not configured in SDL or not
available in current SDL video driver (offscreen) or platform'.
Run those checks inside the same skip.
guest_video_out_output_mode (#1496) opens VideoOut directly, which needs
a Vulkan-capable SDL window, and aborts on the CI runners that have none.
Skip with 77 like the other VideoOut window tests, unless
ANYPS5_REQUIRE_DISPLAY is set.
AJM's instance and batch tables and APR's file, directory and AMM
state were namespace-scope statics, destroyed by std::exit while the
title's threads keep calling the libraries. A decoder thread then read
a freed instance (an unknown codec), sceAjmBatchWait no longer found
its batch, and APR resolved file ids through a freed vector. The state
is now allocated once and never destroyed.
Every direct memory allocation had a memfd of its own, so each one the
GPU used became its own udmabuf and its own imported buffer object. An
imported buffer object cannot be always valid in the GPU VM: amdgpu
validates every one of them at every command submission. PPSA14632
(LEGO Horizon Adventures) imports about 7000 allocations, 5800 of them
64 KiB, and each vkQueueSubmit took about 2.3 ms (5 s per 10 s, held
under the GPU mutex). A standalone test shows the cost follows the
count of imported dma-buf buffer objects, not their size or the count
of ordinary allocations.
- libkernel: on Linux all direct memory lives in one sparse memfd of
DIRECT_MEMORY_SIZE, at file offset = physical address
(PhysicalBacking keeps the file and its offset; SharedBacking returns
that offset). A range allocated again is zeroed through a temporary
mapping, as the old per-allocation memfd was.
- Driver: an import of shared direct memory that lies inside one
64 MiB chunk of the file binds its own VkBuffer into that chunk's
memory, one udmabuf import per chunk (HostImport::chunk holds it, so
it lives until the last buffer bound to it retires). Ranges that
cross a chunk or do not meet the buffer alignment are imported alone
as before.
On PPSA14632 vkQueueSubmit drops from about 2400 us to 46 us on
average, the GPU idles 36 ms instead of 172 ms per present, and
presents go from about 40 to 80-97 per 10 s.
Released direct memory stays committed in the file (see
TechnicalDebt.md): a live chunk import pins its pages.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
macOS has no counterpart to userfaultfd write-protect or
MEM_WRITE_WATCH, so GuestWriteWatch had no backend there and the driver
re-read every guest buffer on each use.
The macOS backend write-protects watched pages. The first write to a
page faults, the SIGBUS/SIGSEGV handler records the page and restores
its protection, and Collect reports the recorded pages and protects
them again. A page is protected before its record is cleared, under the
lock the handler takes, so a write that lands in between is recorded.
The lock hands out turns in order: an unfair one starved writers under
Rosetta.
The protection is real, which adds three entry points:
- GuestWriteWatchBeginHostWrite/EndHostWrite lift it around a system
call that writes guest memory, which would otherwise fail with
EFAULT. GuestArena::HostWrite calls them, as it calls the Windows
equivalent.
- GuestWriteWatchProtectionChanged tells the watch the protection given
to watched pages by mprotect or a new mapping.
- GuestWriteWatchProtection reports that protection, since a watched
page is read-only between a collection and the next write.
Register keeps the recorded protection of a page it already watches:
read from the kernel it would be the watch's own read-only protection,
and the page would never be watched again.
The tests build without the prx libraries and run natively and through
Rosetta.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main's Linux job fails since #1028 and #1714: guest_video_out_resolution_status aborts when SDL's offscreen
driver has no Vulkan window, and check_skips.py rejects the skips of guest_video_out_wqhd_detection and the
flip tests because "no display" is not among the device limitations it accepts.
The resolution status test now skips with code 77 like its wqhd sibling, the flip tests print the same skip
reason, and check_skips.py accepts "no display" as a device limitation. ANYPS5_REQUIRE_DISPLAY still turns
the skip into a failure.
gmtime, localtime, mktime, strftime and asctime took and returned the
host std::tm. The guest's FreeBSD tm has tm_gmtoff and tm_zone after the
nine calendar fields, so on Windows, where std::tm is 36 bytes, a title
reading those fields read past the thread-local result, and gmtime_s left
them unwritten. Windows gmtime_s also accepts only 1969-12-31 12:00 to
3001-01-19 21:59 UTC, where the guest libc converts any time.
Convert through a GuestTm with the guest layout on both hosts. Compute
the UTC fields from the day count, so every time whose year fits an int
converts, and on Windows take the local offset and zone name from the
CRT. gmtime reports the zone as UTC like the guest libc. localtime and
mktime keep the CRT's range on Windows; recorded in TechnicalDebt.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
As requested in review: a failed sceKernelGetModuleInfoFromAddr no longer
reports an empty answer. Adds a failure test (symbol interposition) and
extends the metadata test (EH record walk, terminator, no-header DSO).
Co-authored-by: jweidenh <38249793+jweidenh@users.noreply.github.com>
Co-Authored-By: Claude <noreply@anthropic.com>
A title that ships its own libc.prx unwinds C++ exceptions with that
libc's libunwind, which asks sceKernelGetModuleInfoForUnwind where each
frame's eh_frame is. On Linux the call always reported no tables, so
no C++ exception thrown in guest code could be caught. For the main
executable and .guest.prx images, report the PT_GNU_EH_FRAME tables
that sceKernelGetModuleInfoFromAddr finds, as Windows does from
.ehmeta. Other host images still report none.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
Winsock fails send, sendto and sendmsg on a socket shut down for writing
with WSAESHUTDOWN, which neither Windows error map handled, so libkernel
and libSceNet reported EIO. FreeBSD reports EPIPE there, and the Linux
maps already translate the host's EPIPE to 32. Map WSAESHUTDOWN to 32
in both.
Measured on a PS5 (firmware 13.60) through dlopen of
libSceVideoOut.sprx on a handle the payload opened.
sceVideoOutInitializeOutputOptions writes 0xFF to byte 2 of the 64-byte
options and returns INVALID_ADDRESS for a null pointer.
sceVideoOutIsOutputSupported checks the handle, then the reserved
arguments, then the mode, then the options. It never answers 0: a mode
the display lacks returns UNSUPPORTED_OUTPUT_MODE, and a value that is
not a mode returns 0x8029001E. Options must keep the initialized words,
except word 3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fill takes either info struct, so sceKernelGetModuleInfo reports the
name and segments sceKernelGetModuleInfoFromAddr reports for the same
image, on Linux and Windows.
sigprocmask keeps the first word of the title's set as it is, where
FreeBSD keeps signal n at bit n - 1 (_SIG_BIT), but Dispatch tested bit
n. A title that blocked SIGTERM with a FreeBSD-built set (bit 14) was
not protected, and a set with bit 15 blocked SIGTERM instead of SIGURG.
Dispatch now tests bit n - 1; the stored mask and the set returned by
sigprocmask are unchanged.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
EVO-PLAYER (PPSA99039) v0.11.0 stops at sigaction once its earlier
imports resolve: it installs a crash handler for SIGSEGV, SIGBUS and
SIGABRT and a quit handler for SIGTERM, SIGINT and SIGHUP at start-up.
sigaction records the handler, flags and mask of signals 1 to 128 and
reports them back, with FreeBSD's EINVAL cases. signal shares the table
and records SA_RESTART, as FreeBSD's libc does. Plain handlers on the
signals signal already delivers run as signal handlers do; SA_SIGINFO
handlers and other signals are recorded only, and raise of such a
signal throws.
Assisted by Claude Code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DwFnKjMxmwGDWiuYYf1v4D
Each Windows AIO request is an overlapped ReadFile/WriteFile at the
request's offset, which on a synchronous handle also moves the
descriptor's file pointer; on the console AIO transfers leave it alone.
The requests now go through the positioned path sceKernelPread and
sceKernelPwrite use (NativePositioned in Stdio.cpp, exposed as
NativePositioned_nid_no_patch), which restores the pointer afterwards.
Negative offsets still fail with EINVAL before the transfer.
GuestAio sets the offset to 2 before a write batch and checks it is
still 2 after. TechnicalDebt records that a concurrent read or write on
the same descriptor can see the moved pointer in between, as for the
pread family.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTjpbMX7BUFMZZaxJE98zV
A use of the packed ancillary VGPR other than a constant field extract (the word passed through a
select before its sample number is extracted) reads the word rebuilt from the sample number (bit 8)
and the render target index (bit 16). Extracts of other bits straight from the word stay refused, and
the refusal now names the uses.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Use the current registered range after protection splits and partial unmaps, including FIND_NEXT lookups. Add a regression covering extents, protection and surviving contents.
AI-assisted: yes
Keeps both new TechnicalDebt entries: this branch's note that a raise
can still reenter host code it interrupted, and #2107's sigaltstack note.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
read, write, _read and _write on a guest socket descriptor went to the
host, which has no such descriptor, so sceKernelRead and sceKernelWrite
threw. They now reach the socket as recv and send without flags, with
the socket layer's errno (EAGAIN 35, EPIPE 32, EBADF 9), a zero-length
read returns 0 as dofileread does, and the two sceKernel forms return
the kernel error for the same cases. The host's SIGPIPE on EPIPE, a
write above INT_MAX bytes, readv, writev and the positioned forms are
recorded in TechnicalDebt.
OpenSSL's QUIC notifier signals its socketpair with write and read.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
msync follows FreeBSD 11's kern_msync and vm_map_sync over the guest's
mappings as sceKernelVirtualQuery reports them: the address is rounded
down to the page, a range that wraps and MS_ASYNC with MS_INVALIDATE are
EINVAL, an unmapped start or page is ENOMEM, and length 0 checks the
mapping that holds the address. The guest has no file mappings, so a
successful call has nothing to write back. MS_INVALIDATE on a mapped
range throws, since FreeBSD's EBUSY for mlocked pages needs lock state
that is not tracked.
EVO-PLAYER PPSA99039 v0.11.0 imports it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The IME panel cannot be open while sceImeOpen is unimplemented, so the
query returns SCE_IME_ERROR_NOT_OPENED and leaves the output untouched,
like sceImeSetCaret, sceImeSetText and sceImeSetTextGeometry. As with
sceImeSetCaret, the closed panel is reported before a null output.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The guest's unsigned long is 64 bits. _Stoul was declared with the
host's unsigned long and returned std::strtoul, which are 32 bits on
Windows (LLP64): "4294967296" clamped to 4294967295 with ERANGE and
"-1" gave 0xffffffff, so a guest std::stoul threw out_of_range on values
the console accepts. It now returns std::uint64_t from std::strtoull,
as strtoul_nid_postfix and _Stoull already do; on Linux nothing changes.
The binary-prefix check stays in front of the host call.
Reported in #2424 by AchrafReyani; the same change was proposed in #2442
by SurajKhanna, whose two guest_math cases are taken here. Folded in at
MarcZX100's suggestion on #1459.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Both exports are imported by titles that link libSceAudio3d; without them the loader fails with an unresolved import before the title runs. Following the offline model, object attributes are validated but produce no audio and PortFlush drops the frames queued by sceAudio3dPortAdvance.
wcstol, wcstoll, wcstoul and wcstoull parse the ASCII prefix of the
guest's UTF-16 text with std::strtoll and std::strtoull, which glibc 2.38
and later bind to the C23 __isoc23_* functions: wcstol(u"0b101", &end, 0)
returned 5 with the end pointer after the whole string. They now check
StopAtBinaryPrefix_nid_no_patch first, like the narrow functions, and the
end pointer maps back through ParseAsciiPrefix as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
C17 has no binary prefix: with base 0 or 2, strtol("0b101", &end, base)
reads the subject "0", returns 0 and leaves end at the 'b'. With glibc
2.38 or later, g++ (which defines _GNU_SOURCE) binds std::strtoll,
strtoull, strtoul, strtoimax and strtoumax to the C23 __isoc23_*
functions, which accept 0b/0B: the Linux exports returned 5 and
consumed the whole string. The Windows UCRT already follows C17, so the
same guest call gave different results on the two hosts.
strtol, strtoul, strtoll, strtoull, _Stoull, strtoimax, strtoumax and
_Stoul now return 0 with the end pointer after the '0' when, after
white space and an optional sign, the text starts with 0b or 0B and a
binary digit, in base 0 or 2. Everything else still goes to the host
function. The scanf family's %i has the same glibc behaviour; it is
recorded in TechnicalDebt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FreeBSD vsyslog logs the complaint with LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID passed as the priority (0x23), not LOG_ERR.
Co-Authored-By: Claude <noreply@anthropic.com>