Skip to content

fix(relinker): convert stack-guard and thread-pointer reads with an empty PT_TLS for macOS - #6448

Merged
adriadam10 merged 4 commits into
boykopovar:mainfrom
DavidSamir:fix/relinker-macos-empty-tls
Oct 20, 2026
Merged

adriadam10 merged 4 commits into
boykopovar:mainfrom
DavidSamir:fix/relinker-macos-empty-tls

Conversation

@DavidSamir

@DavidSamir DavidSamir commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

What

Four catalog titles cannot be converted for macOS on main: ccleste (PPSA59322), Mortalfin (PPSA99141), Dusklight (PPSA99305) and Wind Waker (PPSA99309).

FAIL: Guest TLS access without a non-empty PT_TLS

Each has a PT_TLS with file and memory size 0 (alignment 1) and reads the stack guard at %fs:0x28. This is the case #1911 fixed for Windows output, with ccleste as its title: the guard (%fs:0x28) and the thread pointer (%fs:0) live in the thread control block, not in the TLS block. The macOS writer in MacOsImage.cpp treats a module as having TLS only when PT_TLS is non-empty, and refuses any access otherwise.

An empty PT_TLS with accesses now takes the normal TLS path, as on Windows: the descriptor describes a block of size 0, and the existing displacement check still refuses every read into thread-local data, because a block of size 0 has none. An empty PT_TLS without accesses still gets no descriptor, and a module without PT_TLS is refused as before.

Four commits:

  1. fix(relinker): convert stack-guard and thread-pointer reads with an empty PT_TLS for macOS.
  2. test(relinker): convert stack-guard and thread-pointer reads with an empty PT_TLS for macOS: a new macos_empty_tls test converts both reads with an empty PT_TLS and checks the access is gone and the descriptor has template and block size 0.
  3. test(relinker): keep refusing thread-local data reads with an empty PT_TLS for macOS: %fs:-8 fails with is not in the thread TLS block.
  4. test(relinker): give an empty PT_TLS without accesses no macOS TLS descriptor.

Tested

Windows host (14fb570 on main 0afa6a6, Windows 11 10.0.26300, WinLibs GCC 15.2.0 r7, Release). The relinker writes macOS output on any host:

  • tools/check_conventions.py --base origin/main: passed. tools/pr_overlap.py: no overlap with open pull requests.
  • With a relinker-only build (-DANYPS5_RELINKER_ONLY=ON -DBUILD_TESTING=ON), the relinker builds at each of the four commits and every test that runs passes. The 27 that fail are the same at every commit and come from that build: 25 test programs it does not compile, windows_diagnostic_fixture_isolation, which copies one of them, and check_main_sync, which reads git's UTF-8 output as cp1252 on this host (sp ace é.txt) and fails the same way on main. The change is in MacOsImage.cpp, which only the relinker links, so the relinker-only build runs every test that can see it.
  • macos_empty_tls fails without the fix: with main's relinker, empty-guard stops with Guest TLS access without a non-empty PT_TLS.
  • Linux (WSL2 Ubuntu 24.04, GCC 13.3.0): the relinker builds and the macos|tls tests pass (guest_force_tls_destructor is not run because only the relinker target was built).
  • macOS conversion of every catalog title (79), main's relinker against this branch's, same arguments: ccleste (PPSA59322), Dusklight (PPSA99305) and Wind Waker (PPSA99309) now convert, and the other 76 outputs are byte-identical. Mortalfin (PPSA99141) stops first on the refusal fix(relinker): skip prefixed decodings inside an earlier instruction in the macOS TLS scan #6410 removes, and Morrowind (PPSA99630) on the one fix(relinker): rewrite ALU reads of guest TLS through %fs in macOS output #6419 removes; with fix(relinker): skip prefixed decodings inside an earlier instruction in the macOS TLS scan #6410, fix(relinker): rewrite ALU reads of guest TLS through %fs in macOS output #6419 and this PR merged together, both convert, and 78 of the 79 titles convert (47 on main, whose 47 outputs are byte-identical).

macOS 26.7.1, Apple M2, x86-64 under Rosetta, added 2026-10-16, head 14fb570, with --macos --to-rosetta and the runtime of a local tree of main with the macOS port work merged: Spider-Man: Edge of Time (PPSA99300), new in the catalog, fails to convert on main (Guest TLS access without a non-empty PT_TLS) and converts with this branch. Under Rosetta it runs 8 s and then stops on an unexported llrint (symbol not found in flat namespace '_-431A-YBAks'), which #6345 adds. Dusklight (PPSA99305) runs 4 s and stops in its own renderer (aurora::gpu: No supported adapters).

Not tested past the title's own renderer. Dusklight's stop is in its own aurora::gpu renderer (No supported adapters), not an AnyPS5 error. Like Twilight Princess (PPSA99640), it looks for a GPU adapter from inside the title, and I haven't found the cause, so Dusklight is not tested past that point. Spider-Man's next stop, llrint, is a missing export on every platform (#6345).

Checklist

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

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 5021 core/relinker/CMakeLists.txt
PR 6044 core/relinker/CMakeLists.txt
PR 6618 core/relinker/CMakeLists.txt
PR 6879 core/relinker/CMakeLists.txt
PR 6977 core/relinker/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.

Tested on macOS, which the description lists as not run.

Machine: Apple M2 Max, macOS 27.2, Xcode 27, Rosetta 2. Head 14fb570 merged onto main 03c4cb5, relinker-only build (-DANYPS5_RELINKER_ONLY=ON -DBUILD_TESTING=ON), Release.

  • arm64: 174/174 pass, including macos_empty_tls.
  • x86_64 under Rosetta with /usr/bin/python3 (3.9.6): 171/174. The 3 failures (pr_overlap_tools, release_package, linux_fixed_base) fail the same way on main 03c4cb5 and are unrelated to this change.
  • test_macos_empty_tls.py with main's relinker fails on empty-guard with Guest TLS access without a non-empty PT_TLS, so the test covers the regression.

Runtime with a block of size 0: main does not have the macOS guest TLS runtime yet, so I used GuestTlsMacOs.cpp from mugurc's macos-port branch (b7eb6b6). I built it as an x86_64 libs/libkernel.prx, with a small Aps5StartGuest_nid_no_patch that calls the guest entry and prints rax. Then I converted the test's empty-guard and empty-thread-pointer images with this PR's relinker (--macos --skip-sce-module) and ran both under Rosetta:

  • Aps5GuestTlsRegister accepts the descriptor (version 1, template 0, block 0, alignment 1). Aps5GuestTlsAllocate allocates only the thread control block.
  • %fs:0 returns a thread pointer that points at itself (rax=0x7fa315704c80, *rax=0x7fa315704c80).
  • %fs:0x28 returns 0, the zeroed stack guard slot of the new TCB.
  • Both exit 0.

So the macOS runtime handles the empty block as expected. I did not test with real titles; I don't have ccleste, Dusklight or Wind Waker.

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

@DavidSamir

Copy link
Copy Markdown
Contributor Author

Hardware run on Apple silicon.

macOS 26.7.1, Apple M2 (8 GB), Rosetta 2, AppleClang 21.0.0. Relinkers built (-DANYPS5_RELINKER_ONLY=ON, Release) from main 5d9dcc4 alone and with this branch merged; catalog releases, sha256 checked, eboot and modules unwrapped with tools/unwrap_self.py, converted with --macos --to-rosetta. For the runs, libraries from a local tree of main ce991f1 with #4627, #4630, #6124, #6028, #6135 and mugurc's macOS branches merged, so they build on macOS; MoltenVK from the LunarG SDK 1.4.363.

AI-assisted: yes

ycarogabriel-lgtm added a commit to ycarogabriel-lgtm/AnyPS5 that referenced this pull request Oct 18, 2026
…rt stack-guard and thread-pointer reads with an empty PT_TLS for macOS (@DavidSamir)

Taken from the open upstream pull request so the macOS integration branch carries every piece of the port that is already under review, instead of re-implementing it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the unblocks title Names a title ID on the Unblocks line label Oct 18, 2026
@adriadam10 adriadam10 self-assigned this Oct 18, 2026
@DavidSamir

Copy link
Copy Markdown
Contributor Author

Retest on main 05171bc with this PR, #6410, #6419 and #7651 merged locally (Apple M2, macOS 26, Rosetta 2, --macos --to-rosetta with each title's sce_module). Every catalog title that failed relinking with Guest TLS access without a non-empty PT_TLS on main now relinks, including two not listed above:

Title Relink First run (20 s)
ccleste PPSA59322 passes no Vulkan device on this M2 (direct-memory placement, issue #1); with that worked around locally, the MoltenVK pipeline error #6235 covers
PPSA99142 (new) passes same as ccleste
PPSA99300 Spider-Man: Edge of Time passes llrint missing from these older libraries; it is on main
PPSA99305 Dusklight passes the title's own Error creating window (no device, as above)
PPSA99309 Wind Waker passes exits 0 at its own data check
PPSA99314 Eternal Darkness (new) passes Error creating window, as Dusklight

Nothing past the first stop is tested. The run libraries are the macOS port series from 2026-10-12, as current main does not build on macOS with it yet.

AI-assisted: yes (Claude Code)

@adriadam10
adriadam10 merged commit 3d54c5b into boykopovar:main Oct 20, 2026
12 checks passed
@adriadam10

Copy link
Copy Markdown
Collaborator

Re-checked against current main: the merge is clean, the one-line change in MacOsImage.cpp and the macos_empty_tls test (registered with a timeout) are unchanged, and the relinker jobs on macOS, Linux and Windows are green. Thanks for the thorough testing; merged.

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

Labels

build relinker unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants