Skip to content

feat(libkernel_sys): export fork and implement setsid and umask - #3160

Merged
adriadam10 merged 3 commits into
boykopovar:mainfrom
babyhuey:feat/libkernel-sys-fork
Oct 11, 2026
Merged

adriadam10 merged 3 commits into
boykopovar:mainfrom
babyhuey:feat/libkernel-sys-fork

Conversation

@babyhuey

@babyhuey babyhuey commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

What

Three libkernel_sys exports that XashPS5 (PPSA19111) imports and fails to load without:

  • fork (cjt-4ySmYTs) throws NotImplemented, since creating a child process is not supported (the same as execvp in fix(libkernel): export execvp #2827).
  • setsid (edJoqV2FIWs) fails with EPERM, as FreeBSD's does for a process group leader. The guest is already taken to lead its own process group (TechnicalDebt for kill).
  • umask (Rb6ziJKvCCQ) keeps the guest's mask (initially 022, masked to 0777) and returns the previous one. Files are still created with the host process's mask; TechnicalDebt says so.

XashPS5's engine (Xash3D FWGS engine/platform/posix/sys_posix.c, as @SleglJan traced below) calls them from two functions: Posix_Daemonize (fork, setsid, umask), only with -daemonize, and Platform_ShellExecute (fork, then execvp("xdg-open")), which the main menu's Previews button reaches. Neither runs during start-up, but pressing Previews ends in the fork throw; TechnicalDebt says so.

On main ad33692 (catalog build, eboot.bin and sce_module/libc.prx unwrapped as #2605 requires; tracked in #1578), now that realpath (#3147) is in:

  • main, Linux: app.elf: symbol lookup error: app.elf: undefined symbol: cjt-4ySmYTs
  • main, Windows: FAIL: unresolved ELF import cjt-4ySmYTs (fork) from libkernel_sys.prx
  • fork alone: edJoqV2FIWs (setsid); with setsid too: Rb6ziJKvCCQ (umask)
  • with this PR on main 77bd928 (after ctime, feat(libc): implement ctime #3201), Linux: app.elf: symbol lookup error: app.elf: undefined symbol: 5AMS0IntU8I (creat)

Tested

Linux (Ubuntu 24.04, GCC 13, Release) on ad33692: guest_kernel_sys passes 5/5 (--repeat until-fail:5). The new cases check that setsid returns -1 with EPERM, and that umask returns 022 at first, keeps only the permission bits of 07777, and returns the previous mask each time. There is no test for the fork export, since it only throws. check_conventions.py passes. Windows 11 (MinGW-w64 GCC 15.2 from WinLibs, Release): guest_kernel_sys 5/5 with no warnings; with this PR XashPS5 gets past fork and the Windows loader reports FAIL: unresolved ELF import 5AMS0IntU8I (creat) from libkernel.prx first, which is also missing on main and is now the Linux stop as well.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • AI-assisted: yes

@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Progress report (833e5eb → d4b9808)

📉 System libraries: 85.78% (-0.02%, +2 implemented, +1 declared functions)

✅ 2 implemented
Library Name
libkernel_sys setsid_nid_postfix
libkernel_sys umask_nid_postfix
🆕 1 declared as stubs
Library Name
libkernel_sys fork_nid_postfix

@SleglJan

Copy link
Copy Markdown
Contributor

Linux cross-test of 6b7213a merged onto main c076388. Setup: CachyOS (kernel 7.2.9, GCC 16.2.1), RTX 3080 (NVIDIA 615.71.09), Release, SPIRV-Tools on.

  • Tests. guest_kernel_sys passes 5/5. Of the 746 tests in the full suite, 741 pass and 4 are skipped: video_out_flip_*, under the offscreen SDL driver. One fails: agc_driver_dcc_keys_first_write (agc_driver_dcc_keys_first_write fails where host imports stay write-watched (NVIDIA, Linux): cause and patch #2767). check_conventions.py passes.
  • XashPS5 (PPSA19111). Catalog build, with eboot.bin and sce_module/libc.prx unwrapped. On main the loader stops on cjt-4ySmYTs, with 17 absent imports in the audit. With this PR, 14 are absent and it stops on 0uAUs3hYuG4 (ctime), as in your Linux line.

Two notes:

  • Where fork is called. In the engine source, fork sits in two functions, not one. The source is Xash3D FWGS at 1414fc28, the engine commit xash3d-ps5 builds on, in engine/platform/posix/sys_posix.c:

    • Platform_ShellExecute is at 0x87250 in the eboot. It calls fork at 0x87275, then execvp("xdg-open").
    • Posix_Daemonize is at 0x872d0. It checks -daemonize, then calls fork at 0x872e8, setsid at 0x872f8 and umask at 0x87308.

    Your 0x87275 is the first of these fork calls. The main menu's Previews button calls Platform_ShellExecute through EngFuncs::ShellExecute (menus/Main.cpp), and the port's mainui patch keeps that button. Its URL defaults to the Steam store page (mainui string 402, which is in the eboot), so the button works unless a game's strings.lst blanks the string. Pressing Previews therefore reaches the throw. That may be worth a clause in the fork TechnicalDebt entry.

  • Pinning the throw. guest_execvp checks its message, and the same works for fork in GuestKernelSys.cpp. It needs #include <stdexcept> and int APS5_VABI fork_nid_postfix(void); in the extern "C" block:

    bool ForkReportsNotImplemented() {
        try {
            fork_nid_postfix();
        } catch (const std::runtime_error& error) {
            return std::string(error.what()) == "fork: child processes not implemented";
        }
        return false;
    }

    Then add Require(ForkReportsNotImplemented()); in main. I had the same check on a branch I didn't open once this PR appeared. There it fails if fork sets an error and returns -1 instead of throwing.

AI-assisted: yes (Claude Code)

@Dextroid17 Dextroid17 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bar is answered before anyone has to ask: XashPS5 (PPSA19111) imports all three, fails to load without them, and the routine that calls them is given by address. Everything after that follows from the fact that this is a system library with no child processes behind it.

fork throwing is the honest choice, and citing execvp in #2827 makes it a decision rather than a gap. setsid failing with EPERM as FreeBSD does for a process group leader — with the note that the guest is already taken to lead its own group, cross-referencing the kill entry — is the detail that stops that one looking arbitrary. And umask keeping the guest's mask, seeded at 022 and masked to 0777 with the previous value returned, is the right half of that call to implement.

The limitation is stated rather than hidden, which is the part worth keeping: files are still created with the host process's mask, and the entry says so. That is the one place a guest could notice — set a restrictive mask, create a file, read its mode back, and the answer comes from the host — so naming it is what makes this landable as a partial implementation rather than a wrong one. If a title ever reaches that check, the entry is where the next person will look first.

Written with AI assistance.

@babyhuey

Copy link
Copy Markdown
Contributor Author

@SleglJan thanks for tracing the call sites, my "one routine" was wrong. I pushed a commit that names both functions in the fork TechnicalDebt entry, including that the Previews button reaches the throw, and updated the description to match.

On the test: I left fork out on purpose, since @oneandonlydean asked on #2887 that exports which only throw don't get a test ("stubs don't need a test"). Happy to add yours if a maintainer would rather have it here.

@adriadam10

Copy link
Copy Markdown
Collaborator

Checked build and guest_kernel_sys on Linux (merged onto current main); the test doesn't link without the change. Third-party Linux/Windows runs in the thread cover XashPS5.

One fix, pushed as a commit on top:

  • umask masked the new value with 0777, but FreeBSD's sys_umask (sys/kern/vfs_syscalls.c) stores newmask & ALLPERMS (07777). Changed in Export.cpp, the test (umask(07777) then umask(022) returns 07777) and the TechnicalDebt line.

Merging once CI is green.

@adriadam10
adriadam10 merged commit 7b98a30 into boykopovar:main Oct 11, 2026
12 checks passed
@babyhuey babyhuey mentioned this pull request Oct 11, 2026
9 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants