Repository navigation
feat(libkernel): support fcntl and ioctl on non-socket descriptors - #3840
Conversation
| #ifdef _WIN32 | ||
| __builtin_sysv_va_list arguments; | ||
| __builtin_sysv_va_start(arguments, command); | ||
| const auto argument = __builtin_va_arg(arguments, long long); |
There was a problem hiding this comment.
Could this only read the variadic argument for commands that require one, and read the command’s actual argument type? F_GETFL and F_GETFD have no third argument, but the non-socket path always calls va_arg here (and reads long at the POSIX branch below). The new tests call fcntl_nid_postfix(file, GetFlags) without a third argument at GuestKernelFcntl.cpp:102 and :175; fetching a missing vararg is undefined behavior. Commands that do take an argument also use different promoted types: flag/descriptor commands pass int, while lock commands pass a pointer. A command-specific extraction would avoid both mismatches on Windows and POSIX.
There was a problem hiding this comment.
Good catch, thanks. Fixed in the latest commit: File::FcntlArgumentOf maps each command to no argument, an int or a pointer, and fcntl and sceKernelFcntl now read only that. F_GETFD, F_GETFL, F_GETOWN and unknown commands read nothing; the flag, descriptor and read-ahead commands read an int; the lock commands read a pointer.
It also fixed a real case: F_READAHEAD with -1 was read as 4294967295 and enabled read-ahead. guest_kernel_fcntl now checks -1 and 4096 through both entry points; it fails before this commit and passes after (Linux, GCC 13.3). The nine related tests pass, the changed files compile with MinGW-w64, and screamm’s two commits are unchanged.
7d1c02e to
50e40f4
Compare
|
Rebased onto current main (11d9c00). Two conflicts: the |
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>
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 read a third argument as a 64-bit integer for every command on a non-socket descriptor. F_GETFD and F_GETFL pass no third argument, and the commands that take one pass an int or a pointer. They now read nothing, an int or a pointer, by command. An int read as a 64-bit value kept whatever the caller left in the upper half of the register, so F_READAHEAD -1 enabled read-ahead instead of disabling it.
50e40f4 to
99b1b32
Compare
|
Rebased again onto current main (d06c065). #3968 (openat) moved sceKernelOpen's open path into |
|
Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.
|
|
Title test for the fcntl/ioctl change (this PR; the commits come from screamm's branch in #3757), Windows 10, MinGW GCC, RTX 4080 SUPER: this PR merged locally onto current PPSA24423 (0 absent imports; stopped on So the title gets past AI-assisted (Claude Code). |
ProsperoRadio (PPSA99001) stops at 33.7 s on Windows, with boykopovar#3649 applied so that it gets past poll, on the throw from boykopovar#3840: what(): fcntl: record locks are not implemented on Windows, command=12, fd=3 F_GETLK, F_SETLK, F_SETLKW and the F_OGETLK, F_OSETLK and F_OSETLKW forms now follow FreeBSD 11.4's kern_fcntl and lf_advlockasync (8c639945): an SEEK_CUR start is resolved against the descriptor offset (EOVERFLOW past OFF_MAX), F_RDLCK needs read access and F_WRLCK write access (EBADF), another type is EINVAL, and the range is checked against the file size for SEEK_END (EINVAL for a negative start or an unknown whence, EOVERFLOW past OFF_MAX). The guest is one process, and lf_blocks never lets locks of one owner conflict, so whichever descriptor or thread asks, F_SETLK and F_SETLKW succeed without waiting and F_GETLK reports F_UNLCK, as on the console. Nothing is held on the host: Windows byte-range locks are mandatory and would fail the guest's own reads and writes through other descriptors. A lock from the end of a directory throws. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ProsperoRadio (PPSA99001) stops at 33.7 s on Windows, with boykopovar#3649 applied so that it gets past poll, on the throw from boykopovar#3840: what(): fcntl: record locks are not implemented on Windows, command=12, fd=3 F_GETLK, F_SETLK, F_SETLKW and the F_OGETLK, F_OSETLK and F_OSETLKW forms now follow FreeBSD 11.4's kern_fcntl and lf_advlockasync (8c639945): an SEEK_CUR start is resolved against the descriptor offset (EOVERFLOW past OFF_MAX), F_RDLCK needs read access and F_WRLCK write access (EBADF), another type is EINVAL, and the range is checked against the file size for SEEK_END (EINVAL for a negative start or an unknown whence, EOVERFLOW past OFF_MAX). The guest is one process, and lf_blocks never lets locks of one owner conflict, so whichever descriptor or thread asks, F_SETLK and F_SETLKW succeed without waiting and F_GETLK reports F_UNLCK, as on the console. Nothing is held on the host: Windows byte-range locks are mandatory and would fail the guest's own reads and writes through other descriptors. A lock from the end of a directory throws. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
#467) * fix(libc): return the default quiet NaN from the strtod family C17 7.22.1.3 and 7.29.4.1.1 interpret NAN(n-char-sequence) as a quiet NaN. With MinGW-w64, std::strtod, strtof and strtold are mingw-w64's gdtoa functions, which copy the hex digits into the significand without the quiet bit, and for long double without the x87 integer bit: strtod("nan(0x123)") returned the signaling NaN 0x7ff0000000000123 and strtold a pseudo-NaN. A payload whose only set bit is the implicit or x87 integer bit gave infinity: strtof("nan(0x800000)"), strtod("nan(0x10000000000000)") and strtold("nan(0x8000000000000000)") returned +inf. glibc returns quiet NaNs that keep the payload. C17 7.12.11.2p2 makes nan("n-char-sequence") equivalent to strtod("NAN(n-char-sequence)", NULL), and nan and nanf already return the console's fixed quiet NaNs, ignoring the tag. ParseFloat, which strtod, strtof, strtold, atof and the wide variants share, now returns the same values, 0x7ff8000000000000 and 0x7fc00000, negated after a minus sign, on Windows and Linux. strtold returns the x87 default quiet NaN, since no console nanl was observed. (cherry picked from commit aa4a9b4) * fix(libc): clear exhausted strtok continuation state (cherry picked from commit 3cb4347) * test(libc): cover exhausted tokenizer state (cherry picked from commit 0950b19) * fix(libc): terminate truncated wcsxfrm results (cherry picked from commit 22d03d4) * refactor(libc): place the NaN helpers and test includes apart from neighbouring changes (cherry picked from commit c55f377) * fix(libkernel): fail lseek on a pipe with ESPIPE on Windows (cherry picked from commit 61fbec8) * fix(libc): append through a descriptor opened by fdopen in an append mode on Windows (cherry picked from commit dbbd6d5) * fix(libkernel): throw for unsupported directory entry names (cherry picked from commit d31e283) * fix(libc): keep the host standard handles when a guest replaces descriptor 0, 1 or 2 on Windows (cherry picked from commit c226541) * feat(libc): implement floor (cherry picked from commit f8e2c64) * fix(libc): keep host diagnostics on stderr when a guest redirects it on Linux On Linux a guest stderr redirect also moves the host's: guest descriptors are host descriptors, and the guest stderr stream wraps the host stderr FILE. After dup2(fd, 2), or freopen(path, mode, stderr) with full buffering, the libc terminate report, the host fprintf(stderr) diagnostics and libstdc++'s terminate message go into the title's pipe or file, and abort() discards what is still buffered. RetroArch (PPSA99169) reopens stderr onto app0/trace.txt with full buffering, so its stop on main left no message anywhere. Before the first guest redirect of descriptor 2 (libkernel dup2, libc freopen of a stream on descriptor 2), keep a close-on-exec duplicate of the host descriptor 2 and point the host stderr at an unbuffered stream on it. The guest stream keeps its own FILE and follows the redirect. The libc terminate report writes to the kept descriptor with write() instead of the stderr stream, and libkernel's sceKernelDebugOutText uses the same kept descriptor. (cherry picked from commit 438d410) * feat(libkernel): implement sceCoredumpGetStopInfoCpu PPSA34063 (NHL 27) calls it with a 16-byte buffer outside a coredump handler and expects 0x81180003 there. The registered handler is never invoked, so every call is outside one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit d04f5d6) (cherry picked from commit 02ed289) * fix(libc): replace an open or read-only destination in rename on Windows (cherry picked from commit 69b9d18) * fix(libc): fail fseek and ftell on a pipe with ESPIPE on Windows (cherry picked from commit 7aabb90) * fix(libc): check for a pipe on every stream seek on Windows The pipe check cached its first answer in the FileStream, so a stream first used on a regular file stayed classified as a file after its descriptor was replaced with a pipe (dup2 onto fileno(stream)), and a later fseek reached the C runtime again and dropped buffered bytes. fseeko and ftello now ask GetFileType on each call, as lseek on a pipe does since boykopovar#4670, and FileStream no longer keeps the state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 0dda76b) * fix(libkernel): getsockname on an unbound socket returns the zero address on Windows FreeBSD's in_getsockaddr and in6_getsockaddr succeed for a socket that has no local address and return the wildcard address with port 0. Winsock fails getsockname with WSAEINVAL until the socket is bound, which the guest saw as EINVAL. Return the zero address of the socket's family for that error. (cherry picked from commit 0438621) * feat(libc): implement _Mtx_init_with_name (cherry picked from commit ab4ccad) * feat(libc): implement wcsftime PS5MC (PPSA99420) imports wcsftime (XbVXpf5WF28, aerolib) as the last absent libSceLibcInternal import in its registry order (index 481), so its predicted stop is: FAIL: unresolved ELF import XbVXpf5WF28 from libSceLibcInternal.prx The stop is predicted from the import order, not observed in a run. wcsftime follows FreeBSD 11.4 lib/libc/locale/wcsftime.c (8c639945): the wide format is converted with wcsrtombs, strftime fills a buffer of maxsize * MB_CUR_MAX bytes limited to maxsize, and mbsrtowcs converts the result back. A format unit that the single-byte C locale of lib/libc/locale/none.c cannot encode returns 0 with EILSEQ, a maxsize whose product with MB_CUR_MAX overflows returns 0 with EINVAL, and an empty or overflowing result or a null buffer returns 0 with errno unchanged; the ERANGE or EINVAL set by the Windows CRT's strftime is discarded. A null format or tm throws, as FreeBSD dereferences it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit 78ffdf0) * fix(libc): hand out the unwind cursor table through a _nid_no_patch export _Unwind_Context took its cursor table from LibcUnwind::CursorSlots, a C++ data symbol of libc. On Windows LibcUnwind::Frame holds an _Unwind_Context, so libSceAgcDriver, which builds frames for its worker sampler, imported _ZN10LibcUnwind11CursorSlotsE from libc.prx. The NID patcher renames that export in the patched libc and leaves the import alone, so the patched libSceAgcDriver, libSceAgc and libSceVideoOut failed to load with error 127 and patched_libraries failed. The table is now file-local and the context reads it through UnwindCursorSlots_nid_no_patch, which keeps its name on both sides. (cherry picked from commit 3a9cc63) * fix(libc): print NaN without a sign in the guest printf formatter The guest formatter handed NaN to the host's snprintf, which prints -nan for a negative NaN and +nan or " nan" with the + and space flags. FreeBSD 11's vfprintf clears the sign of every NaN and prints nan (NAN for the upper-case conversions), keeping the width and the - flag and ignoring 0. The formatter now does the same for a, e, f and g and their long double forms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VZWLdTjf1S57iLgn2rcaFb (cherry picked from commit 13444c8) * feat(libc): implement atanhf Dusklight (PPSA99305) imports atanhf (cPGyc5FGjy0) from libSceLibcInternal.prx; the libc.prx it bundles only has a zero-return stub for it. The implementation follows the atanhf of the libc.prx bundled with PPSA28319 (0x438a0), read from its disassembly: FreeBSD msun's e_atanhf.c calling msun's s_log1pf.c, both as of 59bc2f09 (stable/9, the revision the sinhf port took expm1f and expf from). The log1pf paths for the positive arguments atanhf passes are inlined, as the exported log1pf is still the host's. 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); for |x| = 1 it sets errno to ERANGE before returning x / 0. Other arguments leave errno alone. Running that code from the title's libc.prx on a Windows host gives the same bits, errno and floating-point exceptions as the built libc.prx for all 2^32 arguments, and msun's own e_atanhf.c and s_log1pf.c built with the host compiler give the same bits. With the host log1pf instead of the msun one, 4.7% of the arguments in [0, 1) differ. guest_math pins bits for every branch reachable from atanhf, both signs, and the errno and exception behaviour. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit 3fe04d4) * feat(libc): implement _Cnd_timedwait (cherry picked from commit 572d330) * fix(libc): match FreeBSD acosf errno acosf returned 0xffc00000 (errno 33) for 2.0 (0x40000000), where FreeBSD lib/msun/src/math.h returns 0xffc00000 (errno 0). It now returns the FreeBSD result for that case. A guest test covers it and fails on main. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com> (cherry picked from commit 64e244e) * fix(libc): keep the acosf result and leave errno unchanged The previous commit replaced the out-of-range result with the default quiet NaN 0x7fc00000, but main and FreeBSD e_acosf.c return 0xffc00000. acosf now calls the host and restores errno, so only errno differs from main. The guest test pins the result bits for 2.0f and -2.0f and keeps the in-range case acosf(0.5f). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit 988d304) * fix(libc): honor openlog identity and facility (cherry picked from commit 653c544) * docs(libc): record syslog tag fallback gap (cherry picked from commit 7313cf5) * test(libc): match syslog PID formatting (cherry picked from commit 81f727c) * fix(libkernel): harden DirectMemory pool bounds and rollback (cherry picked from commit 4f87ac0) * feat(libc): implement wcscat (cherry picked from commit cd0e9dc) * feat(libc): implement generic error category messages (cherry picked from commit e3cef33) * fix(libc): reject invalid setlocale categories (cherry picked from commit 099fa80) (cherry picked from commit 5eb99f8) * fix(libc): validate guest setlocale categories (cherry picked from commit 2ec20db) * fix(libc): drop unused clocale include Co-Authored-By: Claude <noreply@anthropic.com> (cherry picked from commit 1a0de9e) * feat(libc): implement __powidf2 The H+8UBOwfScI import was a throwing stub (libcCyberUnknown08). It is the NID of "__powidf2", the double-precision integer-power helper that complements __powisf2. (cherry picked from commit b4e2ee0) * fix(libc): export __cxa_thread_atexit (cherry picked from commit 90c64af) * fix(libc): ignore the guest DSO of thread atexit on Windows in the shared bridge MinGW's __cxa_thread_atexit asserts dso is null or its own __dso_handle, so every guest export that forwards a guest module pointer needs the null rule, not only LibcInternalExtCxaThreadAtexit. The test now passes a non-host DSO on Windows too. Co-Authored-By: Claude <noreply@anthropic.com> (cherry picked from commit 9c383e3) * test(libc): allow the __cxa_thread_atexit export to call the MinGW registration Co-Authored-By: Claude <noreply@anthropic.com> (cherry picked from commit 1ab578a) * fix(libkernel): place guest thread stacks inside the application area On Linux, glibc maps the stack of every guest thread near the top of the host address space (around 0x7fff00000000). On the console, application stacks lie inside the application area below 0xFC00000000, and guest code relies on that: ChakraCore's garbage collector indexes a 4 MiB table with address >> 18, so it only covers addresses below 1 TiB and reports Js::OutOfMemoryException for a stack above it. Map each guest thread's stack with DoMapAnon, which allocates from the guest arena, behind a PROT_NONE guard page, and pass it to pthread_attr_setstack. The stack is freed after a join; the stack of a detached thread is freed once its host thread is gone from /proc/self/task. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 7637cac) * fix(libkernel): protect host-only guest stack pages (cherry picked from commit 9b6e7f6) * test(libkernel): check the host-only stack guard on Linux only Windows guest threads keep their normal stacks, so there is no host-only guard page below the guest stack to query. (cherry picked from commit 7458574) * feat(libc): record traced import calls on Windows TraceImport_nid_no_patch wraps a function of a library named in APS5_TRACE_CALLS in a thunk that records the import, the thread and the caller in a ring and counts the call, then jumps to the function. One target keeps one thunk, so function pointers compare equal. The crash and abort reports print the last 64 calls and the 32 most called imports. Nothing calls TraceImport_nid_no_patch yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 3953a8d) * fix(libc): choose the acosf NaN bits instead of the host libm FreeBSD msun e_acosf.c returns (x-x)/(x-x) for |x| > 1 and NaN inputs: the x86 default NaN 0xffc00000, or the quieted input NaN. std::acos left it to the host libm, and guest_math failed on the runner's glibc 2.39. Co-Authored-By: Claude <noreply@anthropic.com> (cherry picked from commit 0265f33) * test(libc): cover wide transform output bounds (cherry picked from commit 79d88af) * feat(libkernel): implement fcntl record locks on Windows ProsperoRadio (PPSA99001) stops at 33.7 s on Windows, with boykopovar#3649 applied so that it gets past poll, on the throw from boykopovar#3840: what(): fcntl: record locks are not implemented on Windows, command=12, fd=3 F_GETLK, F_SETLK, F_SETLKW and the F_OGETLK, F_OSETLK and F_OSETLKW forms now follow FreeBSD 11.4's kern_fcntl and lf_advlockasync (8c639945): an SEEK_CUR start is resolved against the descriptor offset (EOVERFLOW past OFF_MAX), F_RDLCK needs read access and F_WRLCK write access (EBADF), another type is EINVAL, and the range is checked against the file size for SEEK_END (EINVAL for a negative start or an unknown whence, EOVERFLOW past OFF_MAX). The guest is one process, and lf_blocks never lets locks of one owner conflict, so whichever descriptor or thread asks, F_SETLK and F_SETLKW succeed without waiting and F_GETLK reports F_UNLCK, as on the console. Nothing is held on the host: Windows byte-range locks are mandatory and would fail the guest's own reads and writes through other descriptors. A lock from the end of a directory throws. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit 43ffba8) * feat(libkernel): accept MADV_PROTECT advisory hint in madvise (cherry picked from commit 59bd0c1) * feat(libkernel): implement lutimes (cherry picked from commit 8d08caa) * fix(libkernel): validate CPU stop-info buffers and cover handler state (cherry picked from commit c18daed) * feat(libkernel): implement pthread_atfork pthread_atfork (U9t5kJAWPnA, aerolib) appends the prepare, parent and child handlers to a process-wide list under a lock and returns 0, or ENOMEM when the entry cannot be allocated, as FreeBSD 11.4's _pthread_atfork does (lib/libthr/thread/thr_fork.c:78-100, freebsd-src 8c639945a44588f6cfdddfb057bb5aad88735da0). Null handlers are accepted; FreeBSD's fork skips them. The handlers never run: fork (libkernel_sys) throws and vfork is absent, so no child process is created. The list is kept, in registration order, for a later fork (PthreadAtforkHandlers_nid_no_patch); recorded in TechnicalDebt. XPSemu (PPSA97358) calls it from a constructor, before main (ZiZc3/XPSemu 8eec07b4, util/rcu.c:488-495): static void __attribute__((__constructor__)) rcu_init(void) { smp_mb_global_init(); #ifdef CONFIG_POSIX pthread_atfork(rcu_init_lock, rcu_init_unlock, rcu_init_child); #endif rcu_init_complete(); } so a throwing export would stop the title before main. Predicted stop removed (registry index 255, not run): FAIL: unresolved ELF import U9t5kJAWPnA from libkernel.prx guest_pthread_atfork checks registration order, null handlers, concurrent registration from four threads and that no handler runs. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit f37bccf) (cherry picked from commit e8fd542) * feat(libkernel): add mlockall mlockall (x7g7Ebeo8-U, aerolib) follows FreeBSD 11.4's sys_mlockall (sys/vm/vm_mmap.c:1067-1131, freebsd-src 8c639945a44588f6cfdddfb057bb5aad88735da0) for the flag check: 0 or bits outside MCL_CURRENT (1) and MCL_FUTURE (2) fail with EINVAL. The title process is taken to pass priv_check(PRIV_VM_MLOCK), as mlock already does. Valid flags throw. MCL_FUTURE would need every later guest and host mapping to be locked; nothing in the mapping paths does that, and passing it to the host would make later host allocations fail above the host RLIMIT_MEMLOCK. MCL_CURRENT would have to lock the whole process, which on AnyPS5 also holds host stacks, heaps, modules and driver mappings (sceKernelVirtualQuery reports them as guest memory), with ENOMEM above a guest RLIMIT_MEMLOCK that is not known (getrlimit throws for it). mlock pins host pages (VirtualLock, mlock) range by range and keeps no record, so there is no state to extend. Recorded in TechnicalDebt. XPSemu (PPSA97358) calls it only with -overcommit mem-lock=on (ZiZc3/XPSemu 8eec07b4, os-posix.c:330-355, reached from system/vl.c:831-835): int flags = MCL_CURRENT | MCL_FUTURE; ... ret = mlockall(flags); if (ret < 0) { error_report("mlockall: %s", strerror(errno)); } and exits on failure, so that option ends in the throw; without it the title is not affected. Predicted stop removed (registry index 201, not run): FAIL: unresolved ELF import x7g7Ebeo8-U from libkernel.prx guest_mlock checks EINVAL for 0, 4, 5 and -1 and the throw for 1, 2 and 3. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit 87f4aa3) (cherry picked from commit a7ea2bd) * fix(libc): format strftime and asctime as FreeBSD 11 does strftime handed most conversions to the host's strftime, which differs from FreeBSD 11's for E and O modifiers on conversions glibc does not pair with them (%Ea was printed literally), padding flags on %+ and %v, years outside 1000 to 9999 (%Y printed 899 and -1 where FreeBSD prints 0899 and -001), and out-of-range fields (%I of hour 100, %u of weekday 8). On Windows the padding flags printed nothing. strftime now follows FreeBSD 11 strftime.c with the C locale on every host, and asctime follows FreeBSD 11 asctime.c, padding years to four digits and setting longer years apart. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VZWLdTjf1S57iLgn2rcaFb (cherry picked from commit d0df62b) * feat(libkernel): return ESRCH and EINVAL for unsupported cpuset affinity targets Allow calling threads to query and set affinity using their own thread ID in addition to -1. Return ESRCH for non-matching thread or process IDs and EINVAL for invalid level/which arguments instead of throwing NotImplemented, matching FreeBSD cpuset semantics. (cherry picked from commit 9efabff) * fix(libkernel): check and keep the scheduling policy of a thread On a PS5 (firmware 13.60) pthread_setschedparam and scePthreadSetschedparam accept only SCHED_FIFO and SCHED_RR with a priority of 256 to 767 or 896 to 959, return EINVAL otherwise, and keep the policy for pthread_getschedparam. A created thread takes the policy of explicit scheduling attributes, or its creator's when it inherits. Here any policy and priority were accepted and SCHED_FIFO was always reported. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 5c6a9d2) * feat(libkernel): implement execv and execve as throwing exports XPSemu PPSA97358 imports both from libkernel.prx; with the imports ahead of them covered, its predicted next stops (relink registry order, not an observed run) are: FAIL: unresolved ELF import KaK52lYTBIs from libkernel.prx (execv, 7 call sites) FAIL: unresolved ELF import kdguLiAheLI from libkernel.prx (execve, 4 call sites) Today the title stops earlier at the observed FAIL: unresolved ELF import aRo9AhFUXcM from libkernel.prx (sigsetjmp, merged in boykopovar#3486) As for execvp (boykopovar#2827), the process image cannot be replaced here, so both throw with the execvp message pattern once FreeBSD 11's execve would start loading the image (freebsd-src 8c639945, sys/kern/kern_exec.c exec_copyin_args, do_execve, exec_check_permissions; lib/libc/gen/exec.c execv passes environ). Before that they fail as FreeBSD does: EFAULT for a null argument vector or path, ENOENT and ENOTDIR from the lookup as stat reports it, EACCES for a path that is not a regular file. Paths of PATH_MAX bytes or more and strings over ARG_MAX throw instead of guessing the console's limits. The execv calls in XPSemu's sources run in a fork child (net/tap.c), and its fork fails with ENOSYS (ZiZc3/XPSemu 8eec07b4, ps5/compat/libc_missing.c, ps5/build-title.sh --defsym). NIDs computed with tools/nid_names.py and confirmed in aerolib. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit 57f0fa3) (cherry picked from commit 4f1ae1c) * fix(libkernel): limit execve to unsupported behavior (cherry picked from commit 77016ca) * fix(libkernel): keep the execv export and its debt line The rename to execve dropped execv, which PPSA97358 imports. Co-Authored-By: Claude <noreply@anthropic.com> (cherry picked from commit cdf4b17) * feat(libkernel): add clock_settime with FreeBSD argument checks PS5MC (PPSA99420, Kodi 22) imports clock_settime (d7nUj1LOdDU) from libkernel.prx, one reference, after sigwaitinfo in its import order. Predicted stop once the imports ahead of it are covered (not a run): FAIL: unresolved ELF import d7nUj1LOdDU from libkernel.prx The NID is computed with tools/nid_names.py and matches aerolib. Follows FreeBSD 11.4 sys/kern/kern_time.c sys_clock_settime and kern_clock_settime (8c639945a44588f6cfdddfb057bb5aad88735da0) for a root caller: EFAULT for a null time (copyin), EINVAL for any clock but CLOCK_REALTIME and for a negative tv_sec or a tv_nsec outside 0 to 999999999. A valid CLOCK_REALTIME request throws with the requested time named, since the host clock must not be set and no guest clock offset is kept. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: David <david@example.com> (cherry picked from commit ae9978a) * feat(libkernel): report the host SoC and CPU temperature PPSA99058 and PPSA99420 import sceKernelGetCpuTemperature and sceKernelGetSocSensorTemperature and stop at start-up on main. Read the host CPU/package, board and NVMe sensors (hwmon/thermal on Linux, the ACPI thermal zones and the storage temperature property on Windows) and report an unsupported sensor with EINVAL. (cherry picked from commit 0f73f5f) * feat(libkernel): answer the coredump user-file attach calls sceCoredumpAttachUserFile (5nc2gdLNsok) and sceCoredumpAttachMemoryRegionAsUserFile (MEJ7tc7ThwM) get the signatures of Orbital's db_types.json (cc408367) and SharpProspero's KernelSystem.cs (5dfdb4b0): (uint32_t userValue, const char* path) and (uint32_t userValue, const void* mem, size_t size, const char* name). The handler is never invoked here, so both return SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER (0x81180003, shadPS4 coredump_error.h e0a46675) and attach nothing; a null path or name and a null, empty or wrapping region throw because the order of the console's checks is unknown. Carried from @screamm with author attribution preserved. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit 204355c) * feat(libkernel_sys): implement mkdirat, unlinkat, renameat, fchmodat and chroot Hand-merged from upstream: ec0d61e 55ddffe 2a8e745 9ceb6d1 dc0d316 41b0ca5 e3c1d01 603d94a 7e5bec0 f8a269e. The fork's libkernel_sys Export.cpp and its test were an older shape of the file, so the upstream versions were taken whole, together with the RenameGuestPath export in libc Rename.cpp/General.hpp. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(libc): call __dynamic_cast and __cxa_init_primary_exception with the guest ABI Every other export of the C++ runtime is declared APS5_VABI, but these two were not. On Windows a title calls them with the System V convention while they read the Microsoft one, so __dynamic_cast took its fourth argument, the src2dst hint, as the source pointer: an ordinary downcast (hint 0) returned null, and the negative hints made it read through an invalid pointer. guest_dynamic_cast calls both through APS5_VABI declarations, as a title does, and checks a single-inheritance downcast, a failed cross cast, a cast of a base object and the header __cxa_init_primary_exception fills in. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit f4be036) * test(libc): register guest_dynamic_cast beside guest_array_callbacks Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit 7154138) * fix(libc): keep the caller errno when localtime cannot represent a time Adapted from upstream b1cf014 (POSIX and Windows localtime failure paths; the Windows year-range handling and its test are not ported). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(libc): implement the float rounding and classification exports PPSA99611 imports ceilf, floorf, truncf, isnan and isinf from libc.prx and stops at start-up on main. Implement them next to the existing double forms: the rounding results delegate to the standard library like the other float exports, and isnan/isinf reuse the __isnan/__isinf helpers isnanf already uses. (cherry picked from commit 00f0589) * fix(libc): build the guest category test and the dynamic_cast registration on macOS Cast away const on the category vtable entries (Apple clang rejects the reinterpret_cast), register guest_dynamic_cast once, use duration_cast for the _Cnd_timedwait deadline (libc++ system_clock is microsecond based), include unistd.h for the libkernel_sys at-calls, and stub GuestThreadStackHostRangeOverlaps in the direct memory test harness. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(libs): find the executable directory on macOS libSceFont and libSceNpEntitlementAccess located the executable through /proc/self/exe, which macOS does not have, so finding the system fonts and the entitlements file threw there. Use _NSGetExecutablePath and resolve the path with std::filesystem::canonical on macOS. Linux and Windows are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit f30f3e8) * fix(libSceFont): include vector for the macOS executable lookup Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Yunus Shaikh <75296019+yunusshaikh7@users.noreply.github.com> Co-authored-by: QRMOE <mohandmahdi44@gmail.com> Co-authored-by: David <David.Samir.WD@gmail.com> Co-authored-by: ArjenS-LA <arjensingh@outlook.com> Co-authored-by: BIGFERRY999 <bigferry24@gmail.com> Co-authored-by: marshmll <renandasilvaoliveiraandrade@gmail.com> Co-authored-by: poonasor <rickypoon89@icloud.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: milescoviello <kitslayer29@gmail.com> Co-authored-by: Hussein Kadhim <husseinkad.dev@gmail.com> Co-authored-by: Feuris <sebastian6@live.com.ar> Co-authored-by: David <david@example.com> Co-authored-by: Dean Galvin <dean.galvin@live.com> Co-authored-by: Rick <99503318+0xRickx@users.noreply.github.com> Co-authored-by: Alex <270190108+CouchRiv@users.noreply.github.com> Co-authored-by: MarcZX100 <marczxrr@gmail.com> Co-authored-by: Yaash Singhh <YASHVSINGH27+git@GMAIL.COM> Co-authored-by: Evgeniy Kazarin <evgkaz0@gmail.com> Co-authored-by: Adrià Franch <adriadam10@gmail.com> Co-authored-by: Akul Tyagi <69496404+AkulTyagi11@users.noreply.github.com> Co-authored-by: Cesar Rodriguez <11452991+Tamalero@users.noreply.github.com> Co-authored-by: codingreapermaps <233954859+codingreapermaps@users.noreply.github.com> Co-authored-by: screamm <davidrydgren@gmail.com> Co-authored-by: jagrat Singh <jagratsingh9899@gmail.com> Co-authored-by: lorkydey <lorky.contact@gmail.com> Co-authored-by: hugodearaujo <hugoaraujocamara@gmail.com> Co-authored-by: nabilfp <nabillgithub@gmail.com> Co-authored-by: Nixitov <Nixitov@users.noreply.github.com> Co-authored-by: Ahir Barman Maji <191682168+ahir061@users.noreply.github.com>
What
Carries screamm's
screamm/AnyPS5:feat/libkernel-fcntl-filesfrom #3757 unchanged, with their authorship (two commits onmaina11c8f7f):70dd0c00feat(libkernel): support fcntl on non-socket descriptors88c9ee8dfeat(libkernel): support ioctl on non-socket descriptorsfcntl,sceKernelFcntl,ioctland_ioctlworked only on libSceNet sockets, so every file, pipe and standard stream gotEBADF. Yamagi Quake II stops on it (Sys_Error: change stdin to blocking Bad file descriptor), and XPSemu, VLC, RetroArch, Porpoise and Ruffle callfcntlon such descriptors (#3757).The new
File/src/DescriptorControl.cppkeeps a close-on-exec flag per descriptor and a status-flag set shared by duplicates, following FreeBSD 11.4kern_fcntl/kern_dup. It coversF_GETFD/F_SETFD,F_GETFL/F_SETFL,F_DUPFD/F_DUPFD_CLOEXEC,F_RDAHEAD/F_READAHEADand record locks on Linux.O_NONBLOCKon a pipe makes empty reads and full writes returnEAGAIN.ioctlanswersFIONBIO,FIONREAD(file size minus offset; queued bytes on a pipe's read end) andENOTTYfor the terminal requests. What it does not model throws with the flag and descriptor kind; #3757 lists the cases. Sockets keep their existing path. Thefcntl,sceKernelFcntlandioctlTechnicalDebt entries are updated.It touches the same
close/dup/pipecode inStdio.cppandOpen.cppas #3649, #3644 and #3681, so whichever merges second needs a rebase; I'll keep this one current.Closes #3757.
Tested
BUILD_TESTING=ON, full build includinglibs:guest_kernel_fcntl,guest_dup,guest_udp,guest_socket_pair,guest_socket_read_write,guest_poll,guest_terminal,guest_file_vector,guest_aio3/3.BUILD_TESTING=ON, head88c9ee8d:guest_kernel_fcntl: record locks through the host's POSIX locks, and clearingO_APPENDon a file.main's libkernel,guest_kernel_fcntlaborts (sceKernelFcntl: file descriptors are not implemented, fd=5) andguest_dupaborts; both pass with these commits.guest_truncateandguest_kernel_stack_protect(linux CI: guest_kernel_stack_protect aborts on main since #2982 (libc unwinder, GCC 13 only) #3459) abort the same way on unchangedmainin this environment.guest_raise_exceptionpasses but takes 72 s with these commits and 71 s onmain, over its 30 s timeout only because the test machine has a single core.tools/check_conventions.py --base mainpasses.Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt.mdfiles or images added to the repository