Skip to content

fix(relinker): preserve weak guest exports on macOS - #8118

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
RockENZO:codex/macos-guest-weak-exports
Oct 27, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
RockENZO:codex/macos-guest-weak-exports

Conversation

@RockENZO

@RockENZO RockENZO commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

What

A converted macOS guest module currently emits every definition as strong, even when its ELF symbol has STB_WEAK. dyld therefore does not include those definitions in its weak-definition coalescing. A native consumer with the same weak function and global keeps its own copies instead of sharing the converted provider's definitions.

Preserve STB_WEAK on ordinary and TLS exports using the existing Mach-O export flags. The writer already derives MH_WEAK_DEFINES from those flags. Strong definitions remain strong. This changes only the macOS guest writer; the executable-export implementation is already on main through #7361.

The regression test compares the converted provider with a native x86-64 dylib built by AppleClang, loading both through the real macOS loader in separate probe processes. The native weak provider's function/global give 18; before the fix the converted provider gives 165, and after the fix it gives 18. A strong-provider control matches its native counterpart as well. Structural checks cover weak and strong function, data and TLS exports, including the Mach-O header flag, on every test host.

No title-specific workaround or claim of a newly playable game is included.

Tested

macOS 27.2, Apple Silicon, AppleClang 21.0.0.21000334, Python 3.9.6, CMake 4.4.4 and Ninja 1.13.2, Release. Based on upstream 0d8c51a54e04c165f108b7bedbba3867e503118a.

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 under Silent stubs in their library's technical debt file
  • 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
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

@github-actions

github-actions Bot commented Oct 19, 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 6713 core/relinker/CMakeLists.txt
PR 10861 core/relinker/CMakeLists.txt
core/relinker/elfpatcher/src/macos/MacOsGuestModuleWriter.cpp
PR 11366 core/relinker/CMakeLists.txt

@wachi35676

Copy link
Copy Markdown
Contributor

Verdict: confirm. These are results from Windows and Linux hosts, plus the effect on real guest modules.

Tested at 50bc39a50, merged onto main 02fb1ce53. Relinker-only Release builds on Windows 11 Home 10.0.26300 (Ryzen 5 9600X, WinLibs GCC 15.2.0 r7) and WSL Ubuntu 24.04 (GCC 13.3).

  • ctest: Windows 222/222, WSL 227/227 (main: 221/221 and 226/226). macos_guest_weak_exports runs its structural checks on both hosts.

  • Negative control: the PR's test_macos_guest_weak_exports.py against main's relinker fails with AssertionError: (2, False, {'_Sample#guest': (0, 65536), '_Data#guest': (0, 70528)}).

  • PS5 homebrew (PPSA99005, PPSA47402, PPSA99581, PPSA99995 with 16 Wine modules), relinked with --windows --to-intel, --macos --to-rosetta and Linux --to-intel. The Windows and Linux outputs are byte-identical to main (8 of 8 sets). In the macOS output, only the guest modules change. The export counts below are from the export trie of each .guest.prx, with main → this PR:

    module exports weak MH_WEAK_DEFINES bytes changed
    libc.prx (same file in all four titles) 2566 0 → 1010 off → on 1027
    libvulkan.prx 9907 0 → 731 off → on 748
    ntdll.prx 766 0 → 53 off → on 70
    wineps5.prx 342 0 → 11 off → on 28
    libgnutls.prx 2707 0 → 2 off → on 19
    wow64native, wowprospero, ws2_32 5 / 70 / 15 0 → 1 each off → on 18 each

    The other Wine modules and every app.macho are unchanged. File sizes are unchanged, and export counts don't change. I have no Mac, so I can't run these under dyld; this PR now marks about 1000 libc.prx exports as weak definitions for coalescing.

AI-assisted: yes (Claude Code); results above were run on this machine.

@maximilianfv

Copy link
Copy Markdown
Contributor

Tested on Apple M1 Pro, macOS 27.0.1 (x86_64 build under Rosetta 2)

Applied at 50bc39a50 to mugurc's macOS series (27e968974) with other open macOS fixes. First I applied only the new test and its CMake entry, then added the MacOsGuestModuleWriter.cpp change.

macos_guest_weak_exports
Test only, without the writer change Fails at test_macos_guest_weak_exports.py:113: AssertionError: (2, ['18', '165']) (native weak provider 18, converted provider 165)
With this PR Passed (2 of 2 runs)

The other macOS and weak-symbol relinker tests (macos_tls_alu, macos_empty_tls, macos_output, macos_tls_overlap, macos_gs_traps, macos_fixed_base, weak_imports, windows_guest_weak_imports, macos_executable_exports, macos_guest_weak_imports) pass before and after.

I also ran the full test suite with this PR (-j 6, microphone tests excluded). My 10-minute limit stopped the run after 1783 of 1793 tests. Most failures are known from earlier runs of this tree, and I did not retest them without this PR: agc_static_buffer, agc_write_tracking, guest_ssl_transport_*, guest_memory_lifetime, guest_kernel_app_archive_mmap, guest_filesystem, font_face_lifetime* (timeouts) and the flaky guest_refused_connect. Three more failed: guest_math, guest_math_exact and guest_avplayer. I reverted only the writer change and rebuilt; all three failed again in 2 of 2 runs, so this PR does not cause them.

AI-assisted: yes (Claude Code applied the PR, ran the tests and wrote this report).

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

Source-focused review of exact head 50bc39a50db7b91298dbac24963d1925a1f9845e. No blocking findings.

The new binding check sets ExportWeakDefinition only for STB_WEAK and carries the flag through both ordinary and TLS exports. This matches the existing executable-export path in MacOsMachOPatcher.cpp. MacOsImage.cpp carries the TLS descriptor's flags into the final export entries before deriving MH_WEAK_DEFINES, so TLS-only weakness reaches the header too. The existing filtering of local, undefined, absolute, hidden and internal symbols remains in place; strong exports keep zero flags.

I reviewed all three changed files and the structural/native-control test paths. The test covers strong/weak function, data and TLS metadata on every host, and on supported macOS hosts compares function/data coalescing with native weak and strong providers. Its CI guard prevents missing Rosetta from silently skipping the native probe there. TLS runtime coalescing is not covered or claimed.

My local checks: tools/check_conventions.py --base origin/main --head refs/review/8118, whitespace checks, a clean merge-tree against main 2742004ed28f765c7147db0e0269ff5868e9eb6f, and Python syntax compilation of the new regression. I did not build or execute this PR's tests or run macOS/dyld locally.

The existing Windows/Linux report and macOS/Rosetta report already provide independent runtime/structural results and negative controls at this head; those are their reported results, not new runs by me. This approval adds a source review. No new title, TLS-runtime or full-integration claim is made. AI-assisted review: yes (OpenAI Codex).

@oneandonlydean
oneandonlydean merged commit b4445c5 into boykopovar:main Oct 27, 2026
15 checks passed
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.

5 participants