Skip to content

fix(relinker): keep Linux imports declared from host modules off guest exports - #1529

Merged
adriadam10 merged 2 commits into
boykopovar:mainfrom
johnmaia:fix/relinker-declared-host-imports
Oct 10, 2026
Merged

adriadam10 merged 2 commits into
boykopovar:mainfrom
johnmaia:fix/relinker-declared-host-imports

Conversation

@johnmaia

@johnmaia johnmaia commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

What

Fixes #1497.

On Linux, GuestModuleBuilder renamed every import whose NID a guest module exports to its guest-suffixed name, whatever module the import declares: the executable's imports (GuestModuleBuilder.cpp:180 on main) and the guest modules' own (:218). A title that bundles a module exporting system NIDs then had those imports bound to it.

DOOM PPSA99666 declares snprintf, vsnprintf, strlen, memcpy, malloc and fopen from libSceLibcInternal (#G#H, module 7) and nothing from libc. The libc.prx loader shim it bundles (ps5-native-app-boilerplate) exports placeholder functions under the same NIDs, so 71 imports became <nid>#guest and returned 0: no notification text, 27 opens of uninitialized paths, no WAD. Windows already binds by the declared module (WindowsEntryStubBuilder) and was not affected.

An import now binds to a guest export only when the module it declares is a guest module:

  • Executable imports: the declared module of each symbol comes from ImportModules through its relocation. A symbol declared from a module that is not a guest keeps its plain name and binds to the host library.
  • Guest-module imports: the same rule with GuestSymbol::Library; such an import is not searched among guest exports, renamed, or counted as a guest dependency.
  • Unchanged: imports without import-module metadata, imports declared from a guest module (a title's own libc.prx imported from libc still binds to it, as in 7103f39 and test_guest_host_libc.py), and Windows (declaresHost is false there).

test_guest_symbol_names gets two cases, using the qualified-import fixtures from test_windows_import_modules: an executable that declares one NID from libc (a guest) and one from libSceLibcInternal (a host), both exported by the guest libc.prx; and a guest module importing shared from libSceLibcInternal while another guest exports it. On main the first fails with {'EEEEEEEEEEE#guest', 'AAAAAAAAAAA#guest'}.

Overlap: #1462 edits the lines next to the executable import loop, so whichever merges second needs a small rebase. #1403 and #1249 merge cleanly.

Tested

  • Linux (Ubuntu 24.04.5, GCC 13.3, RX 7600): full ctest 433/433, and the 152 guest/relinker tests again on the final revision; guest_symbol_names fails on main with the new cases and passes with this change.
  • Windows 11 (WinLibs GCC 15.2.0 r7): 157 guest/relinker/windows tests pass, and the 19 relinker tests (including windows_import_modules) again on the final revision.
  • DOOM PPSA99666 v1.1.1 on Linux, built from source (byte-identical to the release eboot.bin and libc.prx), relinked with its bundled libc.prx and no --exclude-sce-module, on this branch plus feat(libkernel): implement poll for guest sockets #1278 (poll), fix(libkernel): accept MSG_NOSIGNAL in send and sendto #1279 and fix(libkernel): translate scalar POSIX I/O errors #1307: 0 imports bound to the guest module (71 on main); the title opens /app0/wads/doom1.wad and stops at window creation on this headless machine, as with --exclude-sce-module libc.prx. Not run to E1M1 here (no display).

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 (DOOM itself also needs feat(libkernel): implement poll for guest sockets #1278 for poll)
  • AI-assisted: yes

🤖 Generated with Claude Code

@klosixgaming-arch klosixgaming-arch 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.

Excellent fix for a subtle but important relinker issue. The problem of guest modules incorrectly binding to imports declared from host modules is well-documented and the solution is clean.

Strengths:

  • Clear identification of the root cause: GuestModuleBuilder was renaming all imports matching exported NIDs regardless of declaring module
  • Proper use of relocation table scanning to determine import module provenance
  • Comprehensive test coverage including real-world DOOM example (PPSA99666)
  • Good overlap analysis with other open PRs (#1462, #1403, #1249)
  • Thorough testing on both Linux and Windows

Technical notes:

  • The declaresHost lambda is a clean way to encapsulate the host vs. guest module distinction
  • Building the executableModules mapping from relocation tables is the right approach for determining import provenance
  • Test cases properly cover both executable imports and guest-module imports with qualified NIDs

This will significantly improve compatibility with titles that bundle libc.prx loader shims. Ready to merge.

@SleglJan

SleglJan commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Confirmed on Linux with a display and a discrete GPU, on top of current main.

main 4b005b2 + d06fafd (clean merge), CachyOS (kernel 7.2.9, GCC 16.2.1), NVIDIA RTX 3080 (driver 615.71.09), full build with BUILD_TESTING=ON and ANYPS5_ENABLE_SPIRV_TOOLS=ON.

AI-assisted: yes (Claude Code); the runs and counts above were checked by hand.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 9, 2026
@oneandonlydean

Copy link
Copy Markdown
Collaborator

This conflicts with main after today's merges; could you rebase? It's otherwise ready.

@johnmaia
johnmaia force-pushed the fix/relinker-declared-host-imports branch from d06fafd to abb5c46 Compare October 9, 2026 22:50
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 9, 2026
oneandonlydean added a commit to oneandonlydean/AnyPS5 that referenced this pull request Oct 10, 2026
@oneandonlydean

Copy link
Copy Markdown
Collaborator

Merged with current main, guest_symbol_names fails at test_guest_symbol_names.py:175:

    assert importer == ["shared"], importer
AssertionError: ['shared', '__aps5_get_pending_module_args_nid_no_patch', '__aps5_set_module_init_result_nid_no_patch']

Since #1102 every guest module also imports those two __aps5_* symbols. Please rebase and check shared against the module's imports without requiring it to be the only one.

@MarcZX10

Copy link
Copy Markdown
Contributor

I reproduced the reported current-main failure on b5f5362d: the implementation passes its focused behavior once the test stops requiring shared to be the only import. main injects __aps5_get_pending_module_args_nid_no_patch and __aps5_set_module_init_result_nid_no_patch into each guest module, so the useful assertion here is that shared is present and shared#guest is absent (or an equivalent membership check), rather than exact list equality.\n\nOn a detached worktree at b5f5362d, I applied the PR diff and that test-only assertion adjustment; the full Linux relinker-only build succeeded and CTest passed 97/97. Conventions and git diff --check also pass. The official PR checks still need to run on the updated head after you rebase.

@DavidSamir DavidSamir mentioned this pull request Oct 10, 2026
@DavidSamir

Copy link
Copy Markdown
Contributor

@johnmaia @oneandonlydean the rebase and the test change asked for above are on DavidSamir:fix/relinker-declared-host-imports (12cd7d17, on main b5f5362), in case it saves a round trip. Take it into this PR or carry it, whichever is easier.

  • 8f5a66f9: this PR's commit, cherry-picked unchanged (author kept). It applied without conflicts.
  • 8ce04ddd: the consumer check no longer requires shared to be the module's only symbol, since every guest module imports the two __aps5_* helpers after fix(libkernel): pass module args and argp to runtime-loaded modules on Linux #1102. It checks that shared is there and shared#guest is not.
  • 12cd7d17: the declared-module case is converted with --macos as well. GuestModuleBuilder takes the non-Windows path for macOS, so the same binding happened there. The test reads the Mach-O bind names.

Tested

macOS 26.7.1 (Apple silicon M2), Apple clang 21.0.0, relinker-only Release build:

  • ctest 90/90. With GuestModuleBuilder.cpp restored to main, guest_symbol_names fails and the other 89 pass.
  • The new macOS check alone, against a relinker without the fix: _EEEEEEEEEEE#guest is bound. With the fix: _EEEEEEEEEEE.
  • tools/check_conventions.py --base origin/main: passed.
  • DOOM PPSA99666 v1.1.1 (catalog ZIP, unwrapped from SELF), executable imports bound to the bundled libc.prx: 70 of 170 on main b5f5362 and 0 with the branch for Linux output; 70 of 171 and 0 for --macos output.
  • The same title run on macOS (macos-port 92d7098 libraries, relinker with and without this PR's change): without it, two empty notifications and exit after 7 s; with it, doom1.wad opens and the demo loop runs. Details in macOS support #1 (comment).

Not tested: Linux and Windows runs (no such host here).

AI-assisted: yes

johnmaia and others added 2 commits October 11, 2026 00:14
…t exports

On Linux every executable or guest-module import whose NID a guest
module exports was renamed to the guest-suffixed name, whatever module
the import declares. A title that bundles a module exporting system
NIDs then had those imports bound to it: the libc.prx loader shim of
ps5-native-app-boilerplate exports placeholder functions under the
libSceLibcInternal NIDs, so DOOM PPSA99666's snprintf, vsnprintf,
memcpy and malloc (declared from libSceLibcInternal) returned 0 and the
title never found its WAD (boykopovar#1497).

An import now binds to a guest export only when the module it declares
is a guest module, as on Windows. Imports without import-module
metadata keep the previous behaviour, and a title's own libc.prx
imported from libc still binds to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@adriadam10

Copy link
Copy Markdown
Collaborator

Rebased on current main (one conflict: Io::Utf8Path in the rejectSharedImport calls) and added one commit on top, as asked by @oneandonlydean:

Checked: relinker build + 89 relinker tests green on the rebased tree; guest_symbol_names fails with the GuestModuleBuilder.cpp change reverted. Confirmed on a display + RTX 3080 by @SleglJan. Merging on green CI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linux: a bundled libc.prx loader shim (ps5-native-app-boilerplate) binds libc imports to placeholder exports; DOOM PPSA99666 finds no WAD

7 participants