Skip to content

feat(libkernel): map app0.zar archive-backed files - #11625

Open
Felipebmaitan wants to merge 11 commits into
boykopovar:mainfrom
Felipebmaitan:feat/archive-file-mapping
Open

Felipebmaitan wants to merge 11 commits into
boykopovar:mainfrom
Felipebmaitan:feat/archive-file-mapping

Conversation

@Felipebmaitan

@Felipebmaitan Felipebmaitan commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

What

November 1 fix at f5d5756c799d2257de279a0963d93068e9f43fff, published in current head 91f8d4b237b99a1d996824de43090f452dbce40e: publish the selected mmap errno after exception/resource cleanup. The macOS report is reproduced; the catch selected EIO (5), then exception cleanup changed the host/guest errno slot to ENOENT (2) before return. Mapping now computes its result/error internally and the public wrapper publishes the error after that work has ended. The error classification stays intact: host backing-directory failures and damaged archive data produce EIO, while supported access/resource failures retain their own codes. A missing host temporary path does not mean the already-open guest archive file is missing. Added SCE wrapper checks require the same error and an untouched output pointer on failure.

Bounded requested-window storage, permission/EOF/lifetime semantics and checked reads are retained. The supplied read-cost explanation/benchmark awaits acceptance; this update makes no speed claim. #11732/#12296 ordering is satisfied by their merges; #12682 remains open and is the declared predecessor. Its inspected current head is 0e88eeebc91c233aaba95c056c970a1d8b273b36. No I/O cancellation-stack branch (#12682/#12688/#12700/#12965) was changed.

Implement archive-backed file mappings through the regular-file mapping paths. Loose app0 entries still override app0.zar. This is AnyPS5 host ZArchive packaging, not console PFS.

The backing now contains only the requested guest-page-rounded window, clipped at EOF, stored at native offset zero. Linux uses a sealed memfd reopened read-only; Windows/macOS use read-only temporary disk backing. Each live mapping owns independent native storage. No hidden descriptor remains after mmap returns, preserving close/reuse and dup2 behavior. Failed backing writes retain resource errors and clean up without installing a mapping.

Private writable copies, shared read-only permissions, protections, partial unmaps, offsets and EOF retain the established native mapping contract. Linux requires memfd_create and /proc/self/fd. Windows retains regular-file private snapshots and whole-page EOF rejection; macOS execution evidence uses the separately pinned support composition above.

Read copies remain deliberately checked. I audited all actual ReadArchivedFile/ReadArchivedIovecs callers: sceKernelRead, POSIX pread, both host implementations of sceKernelPread, Windows TransferIovecs, POSIX sceKernelReadv/Preadv, and AIO NativePread. Each holds HostWrite/OpenIovecs over its destination. Those guards open watched pages; they do not validate arbitrary pointers. On Linux GuestArenaBeginHostWrite returns true and the write-watch hook is a no-op; on Windows BeginHostWrite checks tracked views, not all arbitrary private/foreign memory. ZArchive copies with CPU stores, so restoring direct destination reads would reintroduce host faults for protected/unmapped pointers. Page-bounded GuestCopyOut preserves EFAULT-before-progress and partial count/cursor semantics. The nested guards also cover direct internal-helper callers. New scalar/positioned/vector/AIO regressions exercise invalid pointers, partial vectors and watched destinations. The measured cost is recorded below, without a title speed claim.

Ordering: #12682 first (replacement for #11872), then this PR, as previously declared. Its current published head is 55161f97476a8bd413c279829cabb6390307e4d5; no candidate is consumed here, and its branches remain untouched. #12296 merged during validation; its Windows shared-memory checks are retained in this rebase, alongside the archive source descriptor and native window offset. #11732 is also merged; its newer VirtualQuery debt text is preserved. Both older order requests are therefore already resolved by main, and neither is an open prerequisite. Direct replies to the outstanding review and ordering comments are recorded in the conversation below.

Tested

November 1 exact published head: 91f8d4b237b99a1d996824de43090f452dbce40e. Preserves the remote maintainer merge 4cb2bc9b2cceab3ef2ff7b8d736f7e04bc89ab9e; integrates upstream main 66e1f7f993c9c1339c2de11264b76bab03218cd2 without rewriting history. Runtime validation built source at fix commit f5d5756c799d2257de279a0963d93068e9f43fff; the final main merge changes none of libc/libkernel/libSceLibcInternal, their test sources or SceTypes.hpp, confirmed by tree comparison.

  • Linux Ubuntu 24.04.5/GCC 13.3, Release, LinuxKit 7.0.12 linux/amd64 container on Apple silicon: rebuilt focused executables and production dependencies. 21/22 focused cases passed, including all archive modes, scalar/vector/positioned read, AIO, duplication and error tests. The remaining guest_memory failure is the zero-length mincore user-boundary errno assertion, now GuestMemory.cpp:4225. A fresh build of main 66e1f7f993c9c1339c2de11264b76bab03218cd2 reproduces the identical assertion. userfaultfd returns EPERM here, so actual Linux write-watch collection is unavailable.
  • macOS 27.0.1 (26A434), Apple M5 Pro, Apple clang 21.0.0, x86-64 Release under Rosetta: matched support composition 05bd71a66bed21dea323da545849781b883fe7f0 + this PR's original a294df9 own diff against de824c2 + the two-file f5d5756 fix. Both reported modes fail before the fix at the original line 924; ordinary mappings pass. After the fix, 15/15 native tests pass: all guest_app_archive and guest_kernel_app_archive modes, guest_file_vector/guest_file_vector_memory, guest_dup, guest_positioned_race, guest_memory and guest_aio. Both fix(libkernel): build the file, module and rtld calls on macOS hosts #10552 Apple empty-iovec blocks are preserved after archive read error conversion in this support composition. This is support-branch composition evidence, not an assertion that standalone main or the full published tree builds/runs on macOS.
  • Patched macOS coverage: copied the rebuilt libc/libkernel/libSceLibcInternal, applied production nid_patcher and ad-hoc signing, and ran a temporary public-API driver with patched guest imports (--exec --only-sce). All three modes passed: normal mapping (nonzero offset contents, scalar/vector positioned reads, lifetime after descriptor close), missing temporary directory, and damaged archive. Each failure mode repeats both POSIX and SCE boundary checks eight times, checking EIO and unchanged SCE output/cursor. Fixture compression is linked into the driver so its native codec symbols stay local. The complete host-linked regression executable has native helper/weak-import resolution requirements; patched coverage is this focused driver, while the complete archive suite above runs unpatched.
  • Conventions (python3 tools/check_conventions.py --base origin/main), whitespace and public-header audit pass (399 libraries / 26,750 exports). Overlap scan of f5d5756 against main 66e1f7f completed across 1,848 open PRs, with confirmed adjacency at fix(libkernel): build the file, module and rtld calls on macOS hosts #10552 (Stdio) and fix(libc): sort with FreeBSD 11.4's merge.c in mergesort #12271 (debt text). Other conflicting-main comparisons, including feat(libkernel): support deferred cancellation in POSIX accept #12965, remain inconclusive and the scanner exits nonzero; no clean-overlap claim or new author agreement is made. fix(libkernel): build the file, module and rtld calls on macOS hosts #10552 composition was inspected and tested as above. Other threads' branches remain untouched.

The macOS support composition is reproducible from patch hashes: original PR diff SHA256 83a1be116e75086554aaaadfe53494e2374d5b29b60eeabcc10dfcc1f00a87ba; two-file fix diff SHA256 677d4d05aed6d41fcb18d79ebab60a0981ec3e1f1b7ea7fc94ed68172d3bdf4e. Temporary instrumentation, driver and logs are outside the repository. No fresh Windows, complete full-suite, GPU, title/console, performance or combined cancellation-stack run is claimed. Earlier evidence below remains pinned to its original heads.

October 31 refresh: exact head a294df92fa9d61d291251a114691a1e689f4d6b0, preserving published head ebf93ca4f62a55df2ef1001b811d1a013a8ef495 and merging upstream main de824c2919ca65086c6324a1e0388866a7327eea. The bounded backing fix was already published in 1440791c77a7e1bbee1684f0726a903ed999dd94; this refresh retains it, the checked reads, and maintainer changes. No history was rewritten and no other I/O head was pushed.

Fresh validation: Ubuntu 24.04.5 / GCC 13.3 / Release / LinuxKit 7.0.12, linux/amd64 container on Apple silicon. Built the selected test targets and their production libc, libSceLibcInternal, libkernel and libkernel_sys dependencies. Focused CTest selection below: 21/22 passed. Every archive, scalar/vector/positioned-read, AIO and descriptor test passed. guest_memory aborted at GuestMemory.cpp:4084 (mincore(0x800000000000, 0, nullptr) errno expectation); after overlaying upstream main and explicitly rebuilding its changed production sources, the identical assertion failed on main too. This is a reproduced baseline/container limitation, not a passing memory suite. userfaultfd returned EPERM, so write-watch collection was unavailable; no actual Linux write-watch coverage is claimed. The archive mmap test also passed a verbose run, including executable mappings with noexec temporary storage.

ctest --test-dir /tmp/anyps5-build -R "^(guest_kernel_app_archive_.*|guest_app_archive_.*|guest_memory|guest_dup|guest_file_vector.*|guest_positioned_race|guest_aio.*|guest_kernel_sys_aio)$" --output-on-failure

Conventions (--base origin/main), whitespace and public-header audit passed (393 libraries / 24,536 exports). Fresh overlap scan ran in an isolated clone at this exact head against 1,558 open PRs: confirmed adjacency with #12271 and #12597 (debt text) and #12868 (Stdio readv/preadv return lines). Manually inspected #12868 at 619546539e534ea4c05e66460f4b79b30d92895a: retain its private-descriptor admission while keeping this PR's archive result-to-SCE conversion. These are integration seams, not duplicate implementations. No new author agreement is claimed; conflicting-main comparisons remain inconclusive and the scan exits nonzero, as designed. The earlier #11732/#12296 order requests are resolved by their merges; #12682 remains the declared predecessor.

Native macOS configuration was attempted but stopped on pre-existing empty LibAtrac9 checkout contents; the original dirty submodules were preserved. No fresh Windows, macOS runtime, full-suite, GPU, title/console, performance, or combined unmerged-stack execution is claimed. The Windows/WSL runs and synthetic benchmark below are historical, pinned to their stated heads; the archive read/backing sources and archive regression file are unchanged between ebf93ca and this refresh.

Historical evidence follows.

Maintenance source head: ebf93ca4f62a55df2ef1001b811d1a013a8ef495, rebased on fetched main 08a10d225306ad102b57408e2ef7566bcfb829b2. The bounded-window implementation is commit 1440791c77a (before the latest main refresh: 9c840020b8e3b0252cf18c9206f39ee6a42cc37c). Earlier approvals and external bare-metal results at e97bbf0 are historical, not validation of this head.

On that exact integrated head:

  • Windows 11, WinLibs GCC 15.2, Release, BUILD_TESTING=ON: full production build (libs and relinker) plus focused test executables passed. CTest selection: 24/24 passed, zero skips, longest 2.50 s.
  • Ubuntu 24.04 under WSL2, kernel 6.6.87.2-microsoft-standard-WSL2, GCC 13.3, Release: full production build (libs and relinker) plus focused test executables passed. Same applicable selection: 22/22 passed, zero skips, longest 0.94 s. The two Windows-only cases are excluded.
  • Selection: guest_kernel_app_archive_* (archive/plain/mmap/mmap-failure/mmap-damaged), guest_app_archive_*, guest_memory, guest_dup, guest_file_vector*, guest_positioned_race, guest_aio*, guest_kernel_sys_aio, and Windows windows_write_watch. This covers requested-window backing size/content, offset rebasing, COW/shared permissions, protections, partial unmaps, concurrent maps, descriptor duplication/close/reuse, native-handle cleanup, bad pointers and partial vector progress, HostWrite and AIO destinations. Linux additionally constrains RLIMIT_FSIZE: a one-page window of a larger entry succeeds; a two-page request fails with EFBIG, without leaks/cursor changes, and retries after restoring the limit.
  • Actual Windows write collection passed. WSL asynchronous userfaultfd write watch is unavailable (EINVAL), so Linux uses the comparison fallback; no Linux write-watch support is claimed. Executable mappings with noexec temporary storage are exercised by the Linux archive mmap test.
  • tools/check_conventions.py --base origin/main, git diff --check, and public-header audit passed (373 libraries, 14,243 exports). Required overlap scan completed. The complete GPU/non-GPU CTest suite was not run; unrelated test executables are not part of the full production build command.

Same-machine synthetic archive read measurements, Windows 11, Ryzen 7 9800X3D, WinLibs GCC 15.2, Release. Benchmark source pins: main 1189ec3003672bb364dd9f3119ead7b3976e3c83 versus final integrated implementation ebf93ca4f62a55df2ef1001b811d1a013a8ef495. Main is the baseline fetched at the start of this maintenance pass; it is not relabeled as the later main revision. identical benchmark executable, archive and APIs (sceKernelOpen/Read/Lseek/Close). Three alternating main/change runs, one warm-up per workload per process, seven recorded samples each (21 samples per cell). Large workload: 256 MiB in 256 reads of 1 MiB. Small workload: 65,536 sequential reads of 64 bytes (4 MiB). First/last bytes and checksums are verified per call; archive creation is outside timing. Host compiler jobs were active, so ranges include contention and these are synthetic cost measurements, not controlled game/scene performance evidence. No trace_digest/game speed claim is made.

Workload main median / range (ms) change median / range (ms)
256 MiB, 1 MiB reads 30.205 / 25.129–40.496 122.651 / 110.839–148.501
65,536 × 64-byte reads 10.802 / 9.907–11.638 74.214 / 66.172–84.626

The checked copy has a substantial measured cost. It is retained to preserve the fault/partial-progress contract; HostWrite alone is insufficient. The implementation bounds mapping storage independently of this read-path tradeoff.

No macOS runtime, PS5/console, title, GPU or combined unmerged-dependency runtime result is claimed unless separately pinned below.

Checklist

Reproducible synthetic read benchmark
#include "prx/libkernel/include/Api.hpp"
#include "prx/libkernel/File/include/FileFlags.hpp"
#include "prx/libkernel/File/include/File.hpp"
#include "AppArchiveFixture.hpp"
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <vector>

int main(int argc, char** argv) {
    if (argc != 3) return 2;
    const auto root = std::filesystem::absolute(argv[1]);
    constexpr std::size_t size = 256 * 1024 * 1024;
    if (std::strcmp(argv[2], "setup") == 0) {
        std::filesystem::create_directories(root / "app0" / "sce_sys");
        AppArchiveFixture::WriteArchive(root / "app0.zar", {{"bench.bin", AppArchiveFixture::Pattern(size)}});
        return 0;
    }
    std::filesystem::current_path(root);
    const int fd = sceKernelOpen("/app0/bench.bin", SCE_KERNEL_O_RDONLY, 0);
    if (fd < 0) return 3;
    for (const std::size_t chunk : {std::size_t{1024 * 1024}, std::size_t{64}}) {
        const std::size_t count = chunk == 64 ? 65536 : size / chunk;
        std::vector<char> buffer(chunk);
        for (int sample = -1; sample < 7; ++sample) {
            if (sceKernelLseek(fd, 0, 0) != 0) return 4;
            std::uint64_t checksum = 0;
            const auto begin = std::chrono::steady_clock::now();
            for (std::size_t call = 0; call < count; ++call) {
                if (sceKernelRead(fd, buffer.data(), chunk) != static_cast<std::int64_t>(chunk)) return 5;
                const auto offset = call * chunk;
                if (buffer.front() != static_cast<char>((offset * 7 + offset / 4096) & 0xff) ||
                    buffer.back() != static_cast<char>(((offset + chunk - 1) * 7 + (offset + chunk - 1) / 4096) & 0xff)) return 6;
                checksum += static_cast<unsigned char>(buffer.front()) + static_cast<unsigned char>(buffer.back());
            }
            const auto seconds = std::chrono::duration<double>(std::chrono::steady_clock::now() - begin).count();
            if (sample >= 0) std::printf("{\"sample\":%d,\"chunk\":%zu,\"bytes\":%zu,\"calls\":%zu,\"seconds\":%.9f,\"checksum\":%llu}\n", sample, chunk, chunk * count, count, seconds, static_cast<unsigned long long>(checksum));
        }
    }
    return sceKernelClose(fd) == 0 ? 0 : 7;
}
main 1048576 21 median_ms 30.2049 min 25.1288 max 40.495999999999995
main 64 21 median_ms 10.801699999999999 min 9.9066 max 11.6379
change 1048576 21 median_ms 122.6511 min 110.8392 max 148.5008
change 64 21 median_ms 74.2136 min 66.1716 max 84.6263
archive-read-bench.cpp 409adbe25a2953bca74f574e81d7f1389449c79da1cf4d970769f9968902e8a6
archive-read-bench.exe 2d65d0ed373c3bcc54ccf167e001f4624687cb5a950e89e70fbb0aea2c4aab53
bench-root/app0.zar 06277dbaeed6c5b4b726114361b7a0cd8c55f8661910a8eac072bcd56d06bb47
bench-main/libc.prx 8bf7bd86fc5a488e0eebe2495638f9629038159477ba270de07d224da0726fe7
bench-main/libkernel.prx 23945a6bc4c968f6166d2236da3ab08aec4aded58cf21cb8d6cb452db6d5e9fe
bench-change/libc.prx bbe7f79999dacb46061c39f52e02f2c3d34da0a60d4ba9792dbd9226d5ddb40b
bench-change/libkernel.prx 498587106aff954a7ba581e7d1cff83b939156dae794918cfa81a2c89c45a47b

Historical overlap scan on ebf93ca (isolated scan clone, branch alias archive-maint-review, same exact SHA): completed against 1,453 open PRs on main 08a10d225306. Confirmed overlaps are this PR itself (same new file), #12682 (Stdio.cpp, declared predecessor), and docs adjacency with #8965 and #12271. The doc-adjacency order is not agreed; no agreement is invented and no contributor message was sent. Other shared-file comparisons that conflict with main remain inconclusive. The earlier #12296 and #11732 requests are already resolved by their merges.

For the later #12682 integration, retain its POSIX Io, descriptor-admission and HostWrite/OpenIovecs scopes, applying the archive native-error conversion separately at the POSIX and SCE return boundaries. The published predecessor head inspected on October 31 is 55161f97476a8bd413c279829cabb6390307e4d5. #12688 transport and #12700 writes remain open downstream of it; they are not additional direct prerequisites of this archive PR. No combined execution with an unpublished refresh is claimed.

@MarcZX10 MarcZX10 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.

Reviewed exact head e97bbf045122e4326b0f5332a9844df37bd1d5dc. Archive mappings materialize a request-local read-only backing object, preserve loose-file precedence and descriptor cursor/lifetime semantics, and retain the regular-file mapping path for validation, protections and unmaps. The archive read changes scatter through checked guest writes and translate EFAULT at the public wrappers. The tests cover offset/alignment/EOF behavior, private/shared protections, descriptor replacement/close, partial unmaps, damage and backing failures. Hosted conventions, eligibility, intake, labels, report, register-context, relinker, ASan and write-watch checks pass; product build jobs are skipped by path policy. The author reports Windows and WSL full builds and focused tests, with the DrvFS-only inotify failure explicitly excluded; I did not independently rerun the local suite. I found no blocking issue.

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

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.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 15556 docs/dev/debt/libkernel.md L369

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 4563 core/libs/prx/libkernel/CMakeLists.txt
PR 4630 docs/dev/debt/libkernel.md
PR 5021 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/File/src/Open.cpp
core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libc.md
docs/dev/debt/libkernel.md
PR 5303 docs/dev/debt/libc.md
PR 6149 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 6819 core/libs/prx/libc/CMakeLists.txt
docs/dev/debt/libc.md
PR 6936 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/File/src/ArchivedFile.cpp
core/libs/prx/libkernel/File/src/Open.cpp
core/libs/prx/libkernel/File/src/Stdio.cpp
core/libs/tests/GuestKernelAppArchive.cpp
docs/dev/debt/libkernel.md
PR 7081 core/libs/prx/libc/CMakeLists.txt
docs/dev/debt/libc.md
PR 7088 docs/dev/debt/libkernel.md
PR 7105 docs/dev/debt/libkernel.md
PR 7121 core/libs/prx/libkernel/File/src/Open.cpp
core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 7135 docs/dev/debt/libkernel.md
PR 7149 docs/dev/debt/libkernel.md
PR 7386 docs/dev/debt/libc.md
PR 7447 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libc/include/AppArchive.hpp
core/libs/prx/libc/src/AppArchive.cpp
core/libs/prx/libkernel/File/src/ArchivedFile.cpp
core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libc.md
docs/dev/debt/libkernel.md
PR 7464 docs/dev/debt/libkernel.md
PR 7715 core/libs/prx/libc/CMakeLists.txt
docs/dev/debt/libc.md
PR 7804 docs/dev/debt/libkernel.md
PR 7851 core/libs/prx/libc/CMakeLists.txt
docs/dev/debt/libc.md
PR 8150 core/libs/prx/libc/CMakeLists.txt
docs/dev/debt/libc.md
PR 8965 core/libs/prx/libc/CMakeLists.txt
docs/dev/debt/libc.md
PR 9156 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9199 docs/dev/debt/libkernel.md
PR 9293 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 9297 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 9302 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 9340 docs/dev/debt/libc.md
PR 9371 docs/dev/debt/libc.md
PR 9383 core/libs/prx/libkernel/CMakeLists.txt
PR 9389 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9447 docs/dev/debt/libc.md
PR 9526 docs/dev/debt/libc.md
PR 9557 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9566 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9938 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 10397 docs/dev/debt/libc.md
PR 10405 docs/dev/debt/libc.md
PR 10442 docs/dev/debt/libkernel.md
PR 10780 docs/dev/debt/libc.md
PR 11172 docs/dev/debt/libc.md
PR 11260 docs/dev/debt/libc.md
PR 11262 docs/dev/debt/libc.md
PR 11266 docs/dev/debt/libc.md
PR 11332 docs/dev/debt/libc.md
PR 11724 docs/dev/debt/libc.md
PR 11726 docs/dev/debt/libc.md
PR 11896 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 11939 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 12849 core/libs/prx/libc/CMakeLists.txt
docs/dev/debt/libc.md
PR 13449 core/libs/prx/libc/CMakeLists.txt
docs/dev/debt/libc.md
PR 13486 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp
core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libc.md
docs/dev/debt/libkernel.md
PR 13560 docs/dev/debt/libkernel.md
PR 13794 docs/dev/debt/libc.md
PR 13804 docs/dev/debt/libc.md
PR 13809 core/libs/prx/libc/CMakeLists.txt
docs/dev/debt/libc.md
PR 14105 docs/dev/debt/libkernel.md
PR 14128 docs/dev/debt/libkernel.md
PR 14212 docs/dev/debt/libc.md
docs/dev/debt/libkernel.md
PR 14299 core/libs/prx/libkernel/CMakeLists.txt
PR 14832 docs/dev/debt/libkernel.md
PR 14854 docs/dev/debt/libkernel.md
PR 14886 docs/dev/debt/libkernel.md
PR 14894 docs/dev/debt/libkernel.md
PR 15035 docs/dev/debt/libkernel.md
PR 15041 core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp
docs/dev/debt/libkernel.md
PR 15749 docs/dev/debt/libkernel.md
PR 15781 docs/dev/debt/libkernel.md
PR 15785 docs/dev/debt/libkernel.md
PR 15794 docs/dev/debt/libkernel.md
PR 15899 core/libs/prx/libc/CMakeLists.txt
core/libs/prx/libkernel/CMakeLists.txt

@jotitadev jotitadev 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.

First bare-metal Linux run, since the Linux results in the PR are from WSL2 and the memfd + /proc/self/fd backing is Linux-only.

Exact head e97bbf045122e4326b0f5332a9844df37bd1d5dc, merged on main cb45ae2dd. Linux x86_64 (bare metal, ext4), i7-8700, Ubuntu 26.04, kernel 7.0, GCC 15.2, glibc 2.43, CMake 4.2.3, Ninja, Release, BUILD_TESTING=ON, full build.

  • The 5 guest_kernel_app_archive_* tests (archive, plain, mmap, mmap-failure, mmap-damaged) pass, 3 of 3 runs.
  • guest_filesystem passes here, so the inotify assertion that failed on DrvFS is fine on a native filesystem.
  • A wider subset of 76 file, stdio, mmap and app-archive tests (guest_kernel*, guest_stdio*, guest_file*, guest_mmap*, guest_app*, ...) passes. Full non-GPU suite on the same main without the PR: 1361/1361.
  • With the implementation reverted to main (AppArchive*, the libc CMakeLists.txt, PosixMemory.cpp, ArchivedFile.cpp, Open.cpp, Stdio.cpp; new tests and their CMake modes kept), mmap, mmap-failure and mmap-damaged abort with mmap on a file read from app0.zar, fd=7 not implemented; archive and plain still pass.

Written with AI assistance (Claude Code). I ran the builds and tests above on my machine.

@milescoviello

Copy link
Copy Markdown
Contributor

@Felipebmaitan my docs-only #11732 appends to the sceKernelVirtualQuery entry in docs/dev/debt/libkernel.md, the line just above the app0.zar files and directories entry this PR rewrites, so git merge-tree reports a one-line conflict. I'm happy for this PR to land first: I've marked #11732 Depends on: #11625 and will rebase it once this one merges. If you'd prefer the other order, say so and I'll switch.

AI-assisted: yes (Claude Code)

@Felipebmaitan

Felipebmaitan commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

For coordination with #11872, I am selecting #11872 first, then #11625 for these two PRs under my account. The later archive-mapping rebase must retain the POSIX Io/descriptor admission and HostWrite scopes while applying this PR's archive result-to-POSIX/SCE error mapping, keeping the two return conventions separate. Archive mmap work remains in this PR. Combined checks should retain archive read/positioned/vector/mapping and descriptor close/reuse cases. This records the merge-order decision; I have updated only this PR's Depends on line to #11872 for merge ordering. Its branch head and source files are unchanged; no combined runtime test is claimed. AI-assisted coordination.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@Felipebmaitan the mapping side reads right to us: the sealed memfd reopened read-only on Linux, the helper descriptor closed before mmap returns, no descriptor that a guest dup2 can replace, and resource errors kept as their own codes. Two questions before it lands:

  1. Each mmap copies the whole decompressed entry. The backing holds the full entry whatever length and offset the guest asked for, and repeated mappings copy again, with no cap or sharing. A loader that maps small windows of a large asset pays the full entry size every time (on Linux that is memory). Please either back only the requested window (mapped at offset 0 of the backing), or share one backing per entry while any mapping of it lives, or state the limit you accept and what happens when an entry is larger than the available storage.
  2. The read path now goes through a 4 KiB bounce buffer. AppArchiveDescriptorRead does one AppArchiveRead plus one GuestCopyOut (and so one GuestArena::HostWrite open/close) per page-aligned chunk, for every app0.zar read, not just mapping. Every caller already opens a HostWrite (or OpenIovecs) over the whole destination before it gets there: sceKernelRead, pread, both sceKernelPreads, sceKernelReadv/Preadv, TransferIovecs and the AIO read. What does the nested per-page check catch that the outer one does not? If it is needed, please give same-machine numbers for main and this PR on an archived file: one large sequential read (say 256 MiB in reads of 1 MiB or more) and many small reads. If the outer guard already covers it, keeping the direct read into the destination would avoid the question.

@JJenkx

JJenkx commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

@Felipebmaitan a merge-order question from #12296 (Windows: map shared-memory descriptors with the regular-file checks). The repository's overlap check pairs the two PRs on core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp, where this PR renames the line above #12296's change to const int mappingDescriptor = sourceDescriptor; while #12296 removes the shared-memory gate right below it. Merged, the line takes your name and keeps #12296's block; nothing else overlaps.

CONTRIBUTING asks overlapping authors to agree on an order and record it under Depends on. #12296 is small and its reviewer otherwise found no defect, so I'd propose #12296 lands first and the rebase here is the small textual merge described above. If you'd rather this PR go first, say so and I'll set Depends on: #11625 on #12296 and do the rebase myself after yours lands. Either works for me.

AI-assisted: yes (Claude Code).

@Felipebmaitan
Felipebmaitan force-pushed the feat/archive-file-mapping branch from e97bbf0 to ebf93ca Compare October 30, 2026 03:08
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 30, 2026
@Felipebmaitan

Copy link
Copy Markdown
Contributor Author

@oneandonlydean addressed both questions at published head a294df92fa9d61d291251a114691a1e689f4d6b0 (published bounded-window fix 1440791c77a7e1bbee1684f0726a903ed999dd94, retained through the current-main merge).

  1. AppArchiveMaterialize now copies only min(guest-page-rounded length, entry size - offset) bytes, starting at the requested archive offset; native backing offset is zero. There is no whole-entry materialization per mmap. Independent live maps own independent requested windows. Tests assert native backing size and exact contents at zero, nonzero and EOF offsets, preserve descriptor cursor/close/reuse, and cover simultaneous maps, protections, COW/shared permissions, partial unmaps and resource/damage cleanup. The Linux RLIMIT_FSIZE regression proves a one-page window of a larger entry succeeds and a two-page request fails with EFBIG without leaks or cursor movement.

  2. I retained checked reads and their nested guards. The outer HostWrite/OpenIovecs guards make watched destinations writable; they do not validate every arbitrary address. On POSIX, GuestArenaBeginHostWrite returns true after invoking the watch hook; on Windows it handles tracked views. GuestCopyOut additionally performs process_vm_writev on Linux, mach_vm_write on macOS, or VirtualQuery permission checks on Windows. A direct ZArchive CPU copy would lose EFAULT-before-progress and page-boundary partial-progress handling for protected/unmapped destinations. Scalar, positioned, vector and AIO invalid-pointer tests, unchanged cursor checks and partial vectors exercise that distinction. Direct helper callers also remain protected.

The requested same-machine cost measurements are retained in the PR body, with reproducible benchmark source/hashes: Windows 11 / Ryzen 7 9800X3D / WinLibs GCC 15.2, baseline main 1189ec3003672bb364dd9f3119ead7b3976e3c83 versus ebf93ca4f62a55df2ef1001b811d1a013a8ef495, three alternating processes and 21 samples per workload. Medians: 256 MiB in 1 MiB reads, 30.205 ms baseline / 122.651 ms change; 65,536 reads of 64 bytes, 10.802 ms / 74.214 ms. That is a substantial accepted checked-copy cost, not a speed improvement or a controlled title benchmark. These are historical measurements, not rerun on this refresh; the archive read and backing sources are unchanged.

Fresh exact-head validation: Ubuntu 24.04.5/GCC 13.3 Release in a linux/amd64 container on Apple silicon. Rebuilt focused executables and their production library dependencies: 21/22 selected tests passed, including all five guest_kernel_app_archive modes, four guest_app_archive modes, read/vector/positioned/AIO/dup cases. The broader guest_memory test fails at GuestMemory.cpp:4084 (zero-length mincore errno at the user-address boundary); rebuilding upstream main de824c2919ca65086c6324a1e0388866a7327eea reproduces the identical assertion. userfaultfd is denied with EPERM here, so actual write-watch collection is not claimed. Native macOS configuration stopped on pre-existing empty submodule contents; no fresh Windows/macOS/title/console or combined unmerged-stack run is claimed. Conventions, whitespace and public-header audit passed. The PR body records the full selection and current overlap limits.

@MarcZX10 @jotitadev thank you for the earlier reviews and bare-metal run. Those remain evidence for e97bbf045122e4326b0f5332a9844df37bd1d5dc, not approval or runtime validation of this newer head. AI-assisted: yes.

@Felipebmaitan

Copy link
Copy Markdown
Contributor Author

@milescoviello replying explicitly to your #11732 ordering request: the live state has overtaken the proposed #11625-first order. #11732 merged on October 28, so the actual order is #11732 first, then #11625. Published head a294df92fa9d61d291251a114691a1e689f4d6b0 includes current main de824c2919ca65086c6324a1e0388866a7327eea and preserves the newer sceKernelVirtualQuery debt text next to the archive entry. #11732 is therefore satisfied, not an open Depends on prerequisite. This PR still lists #12682 first (the replacement for closed #11872); #12688/#12700 remain downstream I/O topics, owned separately. No other branch was changed. The PR body distinguishes fresh Linux-container validation from historical Windows/WSL evidence and records the baseline mincore failure. AI-assisted: yes.

@Felipebmaitan

Copy link
Copy Markdown
Contributor Author

@JJenkx agreed to #12296 first, then #11625, and live state now satisfies that order: #12296 merged on October 30. Published archive head a294df92fa9d61d291251a114691a1e689f4d6b0 preserves its Windows shared-memory/regular-file checks, with mappingDescriptor = sourceDescriptor and archive mappingOffset = 0 for the bounded native backing. #12296 is a satisfied prerequisite and is not listed as an open dependency. The remaining declared order is #12682 then #11625, replacing closed/superseded #11872; #12688/#12700 stay with their dedicated owner. Fresh focused Linux-container validation is 21/22 (all archive/vector/positioned/AIO/dup tests pass; the broader guest_memory mincore assertion also fails on freshly rebuilt upstream main). Historical Windows results remain pinned to ebf93ca; no fresh Windows or combined unmerged-I/O execution is claimed. AI-assisted: yes.

@milescoviello

Copy link
Copy Markdown
Contributor

@Felipebmaitan thanks, agreed. #11732 landed first on October 28, so my ordering request is satisfied and nothing is left for me on this PR.

AI-assisted: yes (Claude Code)

@imellag

imellag commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

A macOS runtime run of head a294df92fa, since the description says none was possible. Apple M4 Pro, macOS 27.2, Apple clang 21.0.0, x86-64 Release under Rosetta 2. main doesn't build its libraries on macOS on its own, so both sides use our support branch support/macos-boot-1030c (05bd71a66b, main 21a556f567 + the open macOS PRs). This head is based on a newer main, so I applied only this PR's own diff (12 files, from its merge base de824c2919). The one conflict was in Stdio.cpp sceKernelReadv, with #10552's iovcnt == 0 block for __APPLE__: I kept this PR's archived-read line, then that block.

ctest -R app_archive:

  • Base: 6 of 7 pass. guest_kernel_app_archive_mmap aborts (mmap on a file read from app0.zar, fd=4 not implemented).
  • This PR: 7 of 9 pass, 3 of 3 runs. guest_kernel_app_archive_mmap now passes. The two new registrations, guest_kernel_app_archive_mmap-failure and -mmap-damaged, fail at app0.zar kernel check failed at line 924, the initialFailure check in MappingFailures. With a local print before it, both get address == MAP_FAILED but error == 2 (ENOENT), where the test expects 5 off Linux. In lldb, mmap_nid_postfix reaches AppArchiveMappingDescriptor_nid_no_patch. I didn't find where ENOENT escapes the GuestIoError mapping in PosixMemory.cpp. On macOS the non-Linux branch of TemporaryBacking (temp_directory_path + mkstemp) is the one that sees the missing or damaged TMPDIR.
  • The 10 guest_(stdio|file|mmap|readv|pread|kernel_file|fs) tests pass on both sides.

AI-assisted: yes

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Nov 1, 2026
@Felipebmaitan

Copy link
Copy Markdown
Contributor Author

@imellag reproduced and fixed in published head 91f8d4b237b99a1d996824de43090f452dbce40e (fix f5d5756c799d2257de279a0963d93068e9f43fff). Your expected EIO was correct; I did not change it to accept ENOENT.

On macOS 27.0.1 / Apple M5 Pro / Apple clang 21 / x86-64 Release under Rosetta, I used your exact support branch pin 05bd71a66bed21dea323da545849781b883fe7f0, applied a294df9's own diff against de824c2, and retained both #10552 Apple empty-iovec guards after the archive conversion. Both failures reproduce at the original line 924; ordinary mappings pass. Temporary instrumentation of the original boundary shows:

failed input=5 native=2
failed published=5
archive destructor fd=-1 errno=2

The catch selects EIO and writes it correctly; exception cleanup then changes the shared host/guest errno slot back to ENOENT before mmap returns. The fix keeps the selected error in a local result while the mapping helper finishes its catches and resource destruction, then publishes errno in the public wrapper. TemporaryBacking still reports its native temp_directory_path/mkstemp errors internally. The abstraction stays general: a missing host backing directory is an I/O failure mapping an already-open guest file; access and supported resource errors keep their existing classifications, and damaged data remains EIO. Added regression checks require sceKernelMmap to return the matching SCE code and leave its output pointer untouched, alongside the existing POSIX/cursor/cleanup assertions.

After removing instrumentation, the matched support composition passes 15/15 native cases (all nine archive cases, file-vector and file-vector-memory, dup, positioned race, memory and AIO). A focused public-API driver against NID-patched/ad-hoc-signed libc/libkernel/libSceLibcInternal passes normal, missing-temp and damaged modes; it checks both POSIX and SCE errors repeatedly, nonzero offset contents, positioned/vector reads and mapping lifetime after close. Patched coverage uses that narrow driver; the complete archive executable above uses unpatched libraries. This validates the fix on the pinned support composition, not standalone main or the entire published tree on macOS.

The refreshed production source also builds in Linux Ubuntu 24.04.5/GCC 13.3 Release (linux/amd64 container on Apple silicon): 21/22 selected cases pass, including every archive/read/vector/positioned/AIO/dup case. The remaining guest_memory zero-length mincore errno assertion at line 4225 also fails on a freshly rebuilt main 66e1f7f993c9c1339c2de11264b76bab03218cd2. Linux userfaultfd is denied here, so actual write-watch collection is unavailable. Conventions, whitespace and public-header audit pass. Overlaps and their inconclusive comparisons are recorded in the updated full PR template.

Remote maintainer history is preserved, bounded window backing and checked reads remain, and no cancellation-stack branch was changed. #12682 stays the open predecessor. Prior read-cost evidence remains supplied and awaits acceptance; no new speed claim, fresh Windows/title/console or combined unmerged-stack validation is made. The PR body pins the support composition, patch hashes and platform limits. AI-assisted: yes.

@imellag

imellag commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

@Felipebmaitan confirmed on macOS at 91f8d4b237: the two mmap-failure / mmap-damaged failures I reported are gone.

Same setup and method as my earlier run: Apple M4 Pro, macOS 27.2, Apple clang 21.0.0, x86-64 Release under Rosetta 2, support branch support/macos-boot-1030c (05bd71a66b). The change side is that branch plus only this PR's own diff (12 files, from its merge base 66e1f7f993). The one conflict was Stdio.cpp sceKernelReadv/sceKernelPreadv against #10552's iovcnt == 0 block: I kept your new archived-read line (with the SceErrorFromErrno conversion), then that block.

ctest -R 'app_archive|guest_(stdio|file|mmap|readv|pread|kernel_file|fs)':

  • Base: 16 of 17 pass; guest_kernel_app_archive_mmap aborts (mmap on a file read from app0.zar ... not implemented), as before.
  • This PR: 19 of 19 pass, including guest_kernel_app_archive_mmap, -mmap-failure and -mmap-damaged.
  • ctest -R app_archive 3 more times: 9 of 9 each time.

I didn't rerun the title-level or Windows parts; this covers the macOS path only.

AI-assisted: yes

# Conflicts:
#	core/libs/prx/libkernel/File/src/Stdio.cpp
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Nov 2, 2026
@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ AMD GPU test: passed

PR head 783098a merged onto main a4eca2a · tested 2026-11-03 02:22 UTC · 22 min

GPU AMD Radeon RX 6300 (RADV NAVI24), driver Mesa 25.2.8-0ubuntu0.24.04.4, Vulkan 1.4.318
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-42-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 3,484 passed · 14 failed (0 new, 13 also fail on main, 1 unstable on main) · 59 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 3,557 of 3,591 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (1311 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_graphics_maps_fallback 120.2 s, ⚠️ agc_driver_pipeline_journal 120.1 s, ⚠️ agc_driver_graphics 115.7 s, ⚠️ public_headers 64.5 s, ⚠️ agc_driver_bda_device 48.1 s
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_pipeline_journal: failed in 5 of the last 7 main runs
Failing on main too (13, not counted against this PR)
agc_driver_guest_memory_lifetime_direct
agc_driver_image_table
agc_driver_job_manager_compute_capacity
agc_driver_native_depth_bda_clear
agc_driver_native_depth_buffer_clear
agc_driver_native_depth_clears
agc_driver_native_multisample_depth
agc_driver_submit_progress
agc_idle_publish_wait
agc_submit_progress
agc_submit_progress_no_timeline
guest_mmap_executable
guest_socket_options_multicast
Passed on retry (1, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)
agc_driver_graphics_maps_fallback: Timeout in the parallel run; also passed only on retry in 67 other runs in the last 7 days
Validation IDs already present on main (4 IDs in 753 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (753 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, and 745 more
VUID-vkCmdCopyImage-srcImage-01548 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-vkCmdCopyImage-srcImage-01551 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
Skipped (59)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_copied_writer_label_order: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_copied_writer_label_order_drain: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_float16_unused_nan_payloads: skipped, the device has no native f16 conversions with RTE rounding and kept f16 denormals
agc_driver_image_table_vouched_lookups: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a watched imported table cannot be vouched for
agc_driver_linear_target_direct_write_completion: skipped, the device does not keep host imports write-watched, so no completion stamp over the target is tracked
agc_driver_ngg_vertex_wave_reduction: skipped, s3 describes a 32-lane wave only on 32-lane subgroups, and the device has 64 lanes
agc_driver_scattered_runs: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_driver_unit_shadow_budget: skipped, the device does not import watched host memory for unit shadows
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_shadowed_key_stores: skipped, the device imports no watched host memory under a unit shadow
agc_small_tiled_shadow_label: skipped, host imports are unavailable: the test block is not imported and watched
agc_storage_clear_hand_over: skipped, the device imports no watched host memory for the clear-key hand-over
agc_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWi
Reproduce
git fetch origin 783098aabe1d930b25a10c85280ed02a4c219f1f a4eca2a056bf3bbbec98ab42e9d28163e4ea9cb8
git checkout a4eca2a056bf3bbbec98ab42e9d28163e4ea9cb8 && git merge --no-ff 783098aabe1d930b25a10c85280ed02a4c219f1f
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 12 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 16 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@XairRrR XairRrR 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.

Reviewed — approved

mmap on app0.zar files now goes through the regular-file mapping path, backed by a separate read-only copy of the requested window for each mapping. The change builds, the focused tests pass on Linux, and both negative controls fail as expected. I found nothing blocking. There are three non-blocking notes; the main one is the cost of the read path.

What I ran

Head 783098aabe1d930b25a10c85280ed02a4c219f1f, main 086c0686c8adff3ee648fdbe34e0b0b647e1d511, merged with --no-ff in a clean worktree (merged tree e34c6e0da86340e7048a4bdd82ea4c60d3f5f5d1). Debian 13 VM, 2× Xeon Gold 6136, GCC 14.2.0, Release, Ninja, BUILD_TESTING=ON.

git diff --check 086c0686c8adff3ee648fdbe34e0b0b647e1d511...783098aabe1d930b25a10c85280ed02a4c219f1f
python3 tools/check_conventions.py --base 086c0686c8adff3ee648fdbe34e0b0b647e1d511
ctest --test-dir build -j8 --test-load 32 --output-on-failure -R "^(guest_kernel_app_archive_.*|guest_app_archive_.*|guest_memory|guest_dup|guest_file_vector.*|guest_positioned_race|guest_aio.*|guest_kernel_sys_aio)$"
ctest --test-dir build -V -R "^guest_kernel_app_archive_mmap"
# negative control A: PosixMemory.cpp from main
git show 086c0686c8adff3ee648fdbe34e0b0b647e1d511:core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp > core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp
ninja -C build -j32 -l32 guest_kernel_app_archive_tests
ctest --test-dir build -R "^guest_kernel_app_archive_" --output-on-failure
# negative control B: merged tree, but AppArchiveDescriptorRead_nid_no_patch body replaced by main's direct read
ninja -C build -j32 -l32 guest_kernel_app_archive_tests
ctest --test-dir build -R "^guest_kernel_app_archive_" --output-on-failure
# restore merged files, rebuild, rerun
git show e34c6e0da86340e7048a4bdd82ea4c60d3f5f5d1:<path> > <path>
ninja -C build -j32 -l32 guest_kernel_app_archive_tests
ctest --test-dir build -R "^guest_kernel_app_archive_"
git status --porcelain

Results

Check Result
Merge into main clean
git diff --check clean
check_conventions.py All conventions checks passed
Configure / build / libs (script) ok
Script test run (3759 tests selected) 0 new failures, 0 timeouts; 9 known failures that also fail on main; guest_net_ctl skip also happens on main
Focused selection (22 tests) 22/22 passed, total 1.82 s
guest_kernel_app_archive_mmap, -mmap-failure, -mmap-damaged passed, 0.01–0.03 s each
Vulkan validation not applicable (no AGC, shader or VideoOut files changed)
Notes from the verbose mmap run
3486: no writable noexec temporary filesystem available; missing-directory backing independence passed
1/3 Test #3486: guest_kernel_app_archive_mmap ...........   Passed    0.03 sec
2/3 Test #3487: guest_kernel_app_archive_mmap-failure ...   Passed    0.02 sec
3/3 Test #3488: guest_kernel_app_archive_mmap-damaged ...   Passed    0.02 sec

On this machine the noexec-storage branch of MappingStorageIndependence was skipped. Only the missing-directory variant ran.

guest_memory passed here. The author's container run failed it on the zero-length mincore assertion, which they also reproduced on main. That failure does not happen on this machine.

Negative control

  • A: mapping path reverted (PosixMemory.cpp from main, everything else merged). All three mmap modes fail, and archive and plain still pass:
    3/5 Test #3486: guest_kernel_app_archive_mmap ...........Subprocess aborted***Exception
      what():  mmap on a file read from app0.zar, fd=7 not implemented
    4/5 Test #3487: guest_kernel_app_archive_mmap-failure ...Subprocess aborted***Exception
    5/5 Test #3488: guest_kernel_app_archive_mmap-damaged ...Subprocess aborted***Exception
    
  • B: checked read reverted. Merged tree, but AppArchiveDescriptorRead_nid_no_patch uses main's direct AppArchiveRead into the destination. guest_kernel_app_archive_mmap then crashes the host process (***Exception: SegFault), and the other four modes pass. So the bounce-buffer copy is what keeps the new invalid-destination cases (MappingReadDestinations) from crashing. This adds evidence to the second question in oneandonlydean's comment: the outer HostWrite guard alone does not prevent the crash.
  • After restoring the merged files and rebuilding, all 5 archive tests pass again, and git status --porcelain is empty.

Findings

Blocking

None.

Non-blocking

  1. Every app0.zar read now pays the per-page checked copy (core/libs/prx/libc/src/AppArchive.cpp:258 and :277). This affects all archive reads, not only mappings. Each 4 KiB chunk costs one AppArchiveRead plus one GuestCopyOut. The author's own figures show about 4× slower large reads and about 7× slower small reads. Control B confirms the check is needed for safety. However, partial progress only has to be page-exact when a copy fails.
    • Suggested fix: copy through a larger buffer (for example 64 KiB, the size AppArchiveMaterialize_nid_no_patch already uses), and fall back to page-by-page copying only when a chunk's GuestCopyOut fails. That keeps the EFAULT-before-progress and partial-count behaviour and should recover most of the cost on valid destinations.
    • Scope: this read-path hardening is also a separate topic from mapping. The maintainer's acceptance of the cost is still open in the thread.
  2. Window materialization holds the descriptor mutex (core/libs/prx/libc/src/AppArchive.cpp:244-246). AppArchiveMaterialize_nid_no_patch decompresses and writes the whole requested window while holding file->mutex. Reads, seeks and other mappings on the same descriptor, and on its duplicates, which share the attachment, block for the whole copy. That can be a long wait for a large window. It is correct, but the file node could be copied under the lock and the materialization run after releasing it.
  3. The libc debt entry is narrower than the code (docs/dev/debt/libc.md:273 in the merged tree). The rewritten entry lists only fopen, access, lstat and fdopen as unsupported. However, ResolvePath_nid_no_patch and ResolvePathOrRelinkedModule_nid_no_patch (core/libs/prx/libc/src/General.cpp:862-872) still throw for any archive-only path, and so does the chdir path (General.cpp:920). The previous wording ("any call other than the archive-aware libkernel ones throws") was the accurate gap statement. Consider keeping that general sentence instead of the short list.

Not verified

  • Windows (CreateFileW / ReOpenFile delete-on-close backing, private snapshot path) and macOS (mkstemp + unlink backing, the errno-publication fix in f5d5756c79). This machine is Linux only. The macOS fix is covered by imellag's confirmation on the support branch, not by me.
  • The noexec temporary-storage branch of MappingStorageIndependence: no writable noexec filesystem was available here.
  • The author's read benchmark figures. I did not rerun them.
  • Combined behaviour with the declared predecessor #12682 (still open). This PR builds and passes on main without it, so the dependency looks like merge ordering only.
  • Title or console behaviour.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Nov 4, 2026
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Nov 4, 2026
@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ AMD GPU test: passed

PR head afe480c merged onto main 5b1d381 · tested 2026-11-04 13:37 UTC · 25 min

GPU AMD Radeon RX 6300 (RADV NAVI24), driver Mesa 25.2.8-0ubuntu0.24.04.4, Vulkan 1.4.318
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-42-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 3,770 passed · 18 failed (0 new, 12 also fail on main, 6 unstable on main) · 62 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 3,850 of 3,883 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (1444 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.5 s, ⚠️ agc_driver_graphics_maps_fallback 120.2 s, ⚠️ agc_driver_graphics 120.2 s, ⚠️ agc_driver_bda_device 57.5 s, ⚠️ agc_driver_lazy_resolved_abi_switch_set 53.8 s
Unstable on main (6, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_alpha_to_coverage_sample_mask_export: failed in 4 of the last 10 main runs
agc_driver_array_layer_target_cpu: failed in 3 of the last 10 main runs
agc_driver_graphics: failed in 3 of the last 10 main runs
agc_driver_graphics_maps_fallback: failed in 2 of the last 10 main runs
agc_driver_pipeline_journal: failed in 8 of the last 10 main runs
agc_driver_two_sample_alpha_to_coverage_sample_mask_export: failed in 3 of the last 10 main runs
Failing on main too (12, not counted against this PR)
agc_driver_guest_memory_lifetime_direct
agc_driver_image_table
agc_driver_job_manager_compute_capacity
agc_driver_native_depth_bda_clear
agc_driver_native_depth_buffer_clear
agc_driver_native_depth_clears
agc_driver_native_multisample_depth
agc_driver_submit_progress
agc_submit_progress
agc_submit_progress_no_timeline
guest_mmap_executable
guest_socket_options_multicast
Validated but not compared (64, not in the main baseline's validation pass)
public_headers
public_headers_compile_0
public_headers_compile_1
public_headers_compile_10
public_headers_compile_11
public_headers_compile_12
public_headers_compile_13
public_headers_compile_14
public_headers_compile_15
public_headers_compile_16
public_headers_compile_17
public_headers_compile_18
public_headers_compile_2
public_headers_compile_20
public_headers_compile_21
public_headers_compile_22
public_headers_compile_23
public_headers_compile_24
public_headers_compile_25
public_headers_compile_26
public_headers_compile_27
public_headers_compile_28
public_headers_compile_29
public_headers_compile_3
public_headers_compile_30
public_headers_compile_31
public_headers_compile_32
public_headers_compile_33
public_headers_compile_34
public_headers_compile_35
public_headers_compile_36
public_headers_compile_37
public_headers_compile_38
public_headers_compile_39
public_headers_compile_4
public_headers_compile_40
public_headers_compile_41
public_headers_compile_42
public_headers_compile_43
public_headers_compile_44
public_headers_compile_45
public_headers_compile_46
public_headers_compile_47
public_headers_compile_48
public_headers_compile_49
public_headers_compile_5
public_headers_compile_50
public_headers_compile_51
public_headers_compile_52
public_headers_compile_53
public_headers_compile_54
public_headers_compile_55
public_headers_compile_56
public_headers_compile_57
public_headers_compile_58
public_headers_compile_59
public_headers_compile_6
public_headers_compile_60
public_headers_compile_61
public_headers_compile_62
public_headers_compile_63
public_headers_compile_7
public_headers_compile_8
public_headers_compile_9
Validation IDs already present on main (8 IDs in 797 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (797 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_alpha_to_coverage_sample_mask_export, and 789 more
VUID-vkCmdCopyImage-srcImage-01548 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store_image_copy, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_plane_partially_unmapped_plane, agc_driver_depth_plane_unmapped_plane, and 3 more
VUID-vkCmdCopyImage-srcImage-01551 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store_image_copy, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_plane_partially_unmapped_plane, agc_driver_depth_plane_unmapped_plane, and 3 more
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
VUID-VkSamplerCreateInfo-addressModeU-01079 (1 test): agc_driver_image_gather_volume
VUID-VkSamplerCreateInfo-addressModeU-parameter (1 test): agc_driver_image_gather_volume
VUID-VkSamplerCreateInfo-addressModeV-parameter (1 test): agc_driver_image_gather_volume
VUID-VkSamplerCreateInfo-addressModeW-parameter (1 test): agc_driver_image_gather_volume
Skipped (62)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_copied_writer_label_order: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_copied_writer_label_order_drain: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_float16_unused_nan_payloads: skipped, the device has no native f16 conversions with RTE rounding and kept f16 denormals
agc_driver_gcn_display_present: skipped, no display: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
agc_driver_image_table_vouched_lookups: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a watched imported table cannot be vouched for
agc_driver_linear_target_direct_write_completion: skipped, the device does not keep host imports write-watched, so no completion stamp over the target is tracked
agc_driver_ngg_vertex_wave_reduction: skipped, s3 describes a 32-lane wave only on 32-lane subgroups, and the device has 64 lanes
agc_driver_scattered_runs: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_driver_unit_shadow_budget: skipped, the device does not import watched host memory for unit shadows
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_idle_publish_wait: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a forced watch of the import reads its submissions as CPU stores
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_shadowed_key_stores: skipped, the device imports no watched host memory under a unit shadow
agc_small_tiled_shadow_label: skipped, host imports are unavailable: the test block is not imported and watched
agc_storage_clear_hand_over: skipped, the device imports no watched host memory for the clear-key hand-over
agc_storage_pending_key_refresh: skipped, refreshes over a pending key store are not testable here
agc_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergp
Reproduce
git fetch origin afe480cff0ab5128712df07cb49b815f2f94e07b 5b1d381d5ad5d9f923326b18479c3bd889cdd588
git checkout 5b1d381d5ad5d9f923326b18479c3bd889cdd588 && git merge --no-ff afe480cff0ab5128712df07cb49b815f2f94e07b
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 12 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 16 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

This branch has not been deployed

No deployments
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.

9 participants