Skip to content

fix(relinker): reject macOS fixups outside writable segments - #11045

Merged
adriadam10 merged 3 commits into
boykopovar:mainfrom
roboterspinne:fix/relinker-macos-fixups-writable
Oct 27, 2026
Merged

adriadam10 merged 3 commits into
boykopovar:mainfrom
roboterspinne:fix/relinker-macos-fixups-writable

Conversation

@roboterspinne

@roboterspinne roboterspinne commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

What

The macOS image writer emitted a dyld fixup for any target inside a segment, whatever the segment's protection: on untouched 4017310, import_fixture() with an added R_X86_64_RELATIVE at 0x4100 (inside the PF_R|PF_X PT_LOAD) converts with relinker --macos --skip-sce-module, exits 0, and its LC_DYLD_INFO_ONLY rebase stream holds a rebase at __ELF0+0x100, a segment with initprot 5. maximilianfv ran such an output on an Apple M1 Pro with macOS 27.0.1 (x86-64 under Rosetta 2, LC_BUILD_VERSION minos and sdk 11.0): it ends with SIGBUS in dyld4::Loader::applyFixupsGeneric before main, and dyld_info -fixups reports pointer rebase is in non-writable segment '__ELF0'.

  • Rule. Locate in core/relinker/elfpatcher/src/macos/MacOsImage.cpp, which both the rebase loop and the bind loop call, throws Mach-O fixup target is not in a writable segment with the target RVA when the segment holding the 8-byte target lacks VM_PROT_WRITE; the CLI exits 2 and writes no output. Only an executable R_X86_64_RELATIVE into a non-writable PT_LOAD reaches it today: WindowsRelocationBuilder::Apply adds it to the base relocations without RequireWritable, while executable imports (R_X86_64_64, GLOB_DAT, JUMP_SLOT) and every guest-module relocation in MacOsGuestModuleWriter pass RequireWritable first, an executable R_X86_64_64, GLOB_DAT or JUMP_SLOT against a defined symbol is refused, and the metadata and TLS descriptor fixups lie in the RW __APS5DATA. The Windows output is unchanged: PE base relocations into read-only sections are valid. Pointer fixups in executable segments, which dyld_info -fixups also reports, are not rejected: it would require changes to seven existing fixtures that rebase into RWX segments, and on the same M1 Pro dyld loads and runs a PF_R|PF_W|PF_X case; this is recorded with those measurements in docs/dev/debt/relinker.md after the macOS GS entry.
  • Tests. test_macos_output.py gets a rebase opcode decoder (rebases) and check_fixup_protection, called after check_runpath_lists: the fixture adds a RELATIVE at file offset 0x5C30 and sets DT_RELASZ to 72. Target 0x5200 (PF_R|PF_W) must exit 0 with a rebase at ('__ELF1', 0x200); target 0x4100 (PF_R|PF_X) must exit 2 with FAIL: Mach-O fixup target is not in a writable segment (offset 0x10100) on stderr and no output. test_interpreter_in_code.py: fixture takes the flags of the data PT_LOAD that its RELATIVE targets; the --macos runs use 6, the --windows runs keep 4, so the PE output still covers a RELATIVE into a read-only PT_LOAD. With 4 it was one of two existing tests with a fixup into a non-writable segment of macOS output (__ELF2, initprot 1). The other came with fix(relinker): accept PT_DYNAMIC data after DT_NULL #10561: strict_fixture in test_dynamic_segment.py marks its single PT_LOAD PF_R|PF_X but kept the RELATIVE at 0x300 of optional_plt_fixture, and its strict-trailing-bytes-after-null case also runs with --macos unused-filter=2, which this check refuses (jotitadev's merge-skew report). 9e66ff6, jotitadev's suggested line applied by adriadam10, sets that fixture's DT_RELASZ to 0, so both strict cases still test the DT_NULL tail, now without a RELATIVE relocation in the Linux, --windows and --macos runs alike.
  • Overlap. pr_overlap.py reports no shared lines with open pull requests that merge with main. It cannot compare this branch with feat(aot): consolidate native ARM64 compiler, runtime and consumers #5021 and fix(relinker): mask embedded data tables in inter-FDE gaps #8076 (docs/dev/debt/relinker.md) and with fix: define decoded AGC state and bound size arithmetic #10249 (MacOsImage.cpp), because those do not merge with main. Checked by hand: the debt entry of this PR is a new bullet after the one on GS accesses in macOS output (line 41 on main); feat(aot): consolidate native ARM64 compiler, runtime and consumers #5021 adds a macOS TLS bullet before the --trace-imports bullet and fix(relinker): mask embedded data tables in inter-FDE gaps #8076 adds a bullet on embedded data and relative jump tables after the sce_module syscall scan bullet, both near the top of the list. fix: define decoded AGC state and bound size arithmetic #10249 changes one expression in WriteMacOsImage, the displacement of a TLS stub jump (line 595 of its base), not Locate (line 222 on main), where this PR adds the writable-segment check. The trial merges of this branch with each of them conflict in the same files and regions as main does, none of them in lines of this PR.

Tested

Windows 11, GCC 15.2.0 (WinLibs UCRT), relinker-only Release build (-DANYPS5_RELINKER_ONLY=ON -DBUILD_TESTING=ON); synthetic fixtures only, no title or homebrew.

  • Counter-check: macos_output with the new test and without the Locate check fails with fixup-code: rebase into a non-writable segment accepted: 0 after the 0x5200 control passed (e05ba6f with the Locate line removed). With the change it passes in 0.6 s.
  • interpreter_in_code with flags 6 for --macos and 4 for --windows passes with the change and with e05ba6f's MacOsImage.cpp; with flags 4 for both targets it passes without the change and fails with it on its first --macos case, after the three --windows cases, with FAIL: Mach-O fixup target is not in a writable segment (offset 0x18300); with flags 6 for both it passes.
  • ctest -R on 15 tests: macos_output and interpreter_in_code; the seven with rebases into RWX segments (elf_offsets, none_relocations, optional_rela, overlapping_relocations, sce_code_extent, windows_bss, macos_fixed_base); relinker_conversion and relinker_game_patch (C++ suites that convert to macOS); relinker_executables, guest_none_relocations, game_patches and cli_args: 15 passed in 32.8 s.
  • ctest -R 'macos|tls|sce_relro': 50 passed in 116.2 s (machine under load from parallel builds).
  • Rebased onto e05ba6f (no relinker changes since 4017310); the rebased branch was rebuilt and the counter-check and the passing runs above were rerun on it; the interpreter_in_code runs above ran on the follow-up commit 397d534, with and without the Locate check.
  • Full suite: ctest --test-dir build-relinker -j 4 on e05ba6f: 292 tests, 289 passed, long_paths skipped; failures known on this machine on main as well: select_tests (Windows symlink fixture) and patch_advisor_unit (path with spaces, fixed by open test(tools): compare quoted and resolved paths in the patch advisor test #10667); both fail again when run alone.
  • Follow-up commit 397d534 on d42c3dc after maximilianfv's review (debt entry and What paragraph reworded to his macOS measurements, interpreter_in_code flags per target), rebuilt: ctest -R on 18 tests (macos_output, interpreter_in_code, the seven with rebases into RWX segments, relinker_conversion, relinker_game_patch, guest_none_relocations and the six other macos_ tests) passed in 50.4 s; full suite ctest --test-dir build-relinker -j 6: 292 tests, 288 passed, long_paths skipped, select_tests and patch_advisor_unit failed as above, and shader_cache_runtime_sources timed out at 180 s, alone as well, and passes in 235 s when run without the ctest timeout; it builds the shader recompiler, which this PR does not touch. git merge-tree of the branch with upstream/main (6bc1139), fix(relinker): reject --lazy-binding for macOS output #10712, fix(relinker): accept a zero-size TLS export at the end of the macOS TLS block #10946, fix(relinker): reject Windows executable imports bound into the TLS template #10947, fix(relinker): reject nonzero GLOB_DAT and JUMP_SLOT addends in Linux output #10971, fix(relinker): analyze referenced code beyond SCE scan boundaries #10861 and fix(relinker): lower ADCX and ADOX for Rosetta #7173: clean.
  • adriadam10 rebased the branch onto main 1392606 (e529726 and 0326845, the same changed lines as d42c3dc and 397d534) and added 9e66ff6. That head merged with main 8f25510: full suite ctest --test-dir build-relinker -j 6: 368 tests, 365 passed, long_paths skipped, only select_tests and patch_advisor_unit failed as above; dynamic_segment, macos_output and interpreter_in_code passed. On macOS, jotitadev ran the full suite of 9e66ff6 merged onto main 5d52b06 on arm64 (361/361), and maximilianfv ran it without audio_in|voice on main 801d5b9 as x86_64 under Rosetta 2 (359/359).
  • python tools/check_conventions.py --base upstream/main with this title and description passed.
  • Not tested by me on Linux or macOS; CI runs the tests on all three hosts.

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 (Claude Code)

@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 d42c3dc7fd72722939c9c1ae6d0af7e2b4e065bc. The shared Locate guard checks that the full 8-byte fixup target is inside a segment and that the segment has write permission, covering both rebase and bind emission. The new writable-data success case and read-execute rejection case exercise the boundary; the existing interpreter fixture is made writable for its relocation. The author reports a negative control and 292-test relinker run; I reviewed the test/code and hosted Linux, Windows, macOS arm64/x86_64, ASan and conventions checks pass. Product-specific jobs are skipped by path policy. I did not independently run the macOS-target fixture locally.

@github-actions

github-actions Bot commented Oct 25, 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 docs/dev/debt/relinker.md
PR 10249 core/relinker/elfpatcher/src/macos/MacOsImage.cpp
PR 10861 core/relinker/elfpatcher/src/macos/MacOsImage.cpp
docs/dev/debt/relinker.md

@maximilianfv maximilianfv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The Locate check is correct and needed: on macOS 27.0.1 the output it now rejects dies inside dyld before main, and every other test and fixture still converts and runs. The code needs no change. The only change requested is one sentence in the debt entry, which says dyld refuses fixups in executable segments, but here such an output loads and runs.

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

Applied at d42c3dc7fd to mugurc's macOS series (27e968974) merged with main at 2742004ed2, with other open macOS fixes, then toggled only this PR. Full rebuild each way (the relinker, relinker_conversion_tests, relinker_game_patch_tests and the other librelinker_core users relinked). The direct runs use relinker --macos --skip-sce-module on import_fixture() with the extra RELATIVE at 0x5C30 and DT_RELASZ 72 that check_fixup_protection builds, run with libc.prx and libkernel.prx from the same build in libs/.

Without this PR With this PR
63 relinker tests (macos_*, relinker_*, *tls*, interpreter_in_code, sce_relro, sce_code_extent, elf_offsets, none_relocations, optional_rela, overlapping_relocations, windows_bss, game_patches, cli_args, ...) 63/63 passed 63/63 passed
macos_output with only the test changes (no Locate line) Failed: fixup-code: rebase into a non-writable segment accepted: 0 Passed
No extra fixup exit 0, prints hello from a PS5 ELF on macOS the same
RELATIVE to 0x5200 (PF_R|PF_W) exit 0, runs and prints the same
RELATIVE to 0x4100 (PF_R|PF_X) exit 0. dyld_info -fixups: pointer rebase is in non-writable segment '__ELF0'. Running it: SIGBUS, EXC_BAD_ACCESS (code=2, address=0x140010100) in dyld4::Loader::applyFixupsGeneric exit 2, FAIL: Mach-O fixup target is not in a writable segment (offset 0x10100), no output
RELATIVE to 0x4100 with the code PT_LOAD set to PF_R|PF_W|PF_X exit 0. dyld_info -fixups: pointer rebase is in executable segment '__ELF0'. Runs and prints, exit 0 the same

So on this host, dyld does not refuse the non-writable rebase with a message. It writes the rebase and faults on the read-only page. Either way the output never reaches main, which justifies the rejection.

  1. docs/dev/debt/relinker.md:37: "macOS output still writes pointer fixups into executable segments, which dyld refuses ... Not run on macOS." On macOS 27.0.1 under Rosetta, an output with a rebase into an RWX __ELF0 loads, applies the fixup and runs. Only dyld_info reports it, as a diagnostic. The output declares minos 11.0, sdk 11.0 in LC_BUILD_VERSION, which may be why dyld enforced neither rule here: the PF_R|PF_X case died on the page fault, not on a refusal. Fix: state what was measured (dyld_info flags it, and dyld on macOS 27.0.1 x86_64 under Rosetta loads and runs it), or name the dyld version and declared SDK that refuse it.
  2. Optional, core/relinker/elfpatcher/src/macos/MacOsImage.cpp:192: the message gives the writer's RVA (0x10100 for the ELF address 0x4100). The other relocation errors, Runtime import target is not writable from WindowsLoadImage::RequireWritable, give the ELF address, so 0x10100 cannot be found with readelf -r. Fix: report the ELF address.
  3. Optional, core/relinker/relinker/tests/test_interpreter_in_code.py:27: this test also converts with --windows. With flags 6 it no longer covers a RELATIVE into a read-only PT_LOAD in PE output, which the PR keeps valid on purpose. I did not check whether another test covers that. Fix: keep flags 4 for the --windows run and use 6 only for --macos.

AI-assisted: yes (Claude Code applied the PR, ran the tests and the fixtures, read the code and wrote this review).

@roboterspinne

Copy link
Copy Markdown
Contributor Author

Thanks for the macOS measurements, they are the first runs of these fixup cases on a Mac. Addressed in 397d534, a new commit on top of d42c3dc.

Point 1 taken: the debt entry no longer claims a refusal. It now states what you measured, that dyld_info -fixups reports both cases, that dyld on an M1 Pro running macOS 27.0.1 under Rosetta 2 loads and runs the PF_R|PF_W|PF_X case with the declared minos and sdk 11.0, and that the PF_R|PF_X case ended with SIGBUS in dyld4::Loader::applyFixupsGeneric rather than being refused; dylibs and other macOS or SDK versions are marked as not measured. The What paragraph of the description is corrected the same way.

Point 3 taken: test_interpreter_in_code.py now passes flags 6 only to the --macos run and keeps flags 4 for --windows, so the PE output still covers a RELATIVE into a read-only PT_LOAD. With the change, the split fixture passes, flags 4 for both targets fails on the first --macos case after the three --windows cases (offset 0x18300), and flags 6 for both passes; with the MacOsImage.cpp of e05ba6f all three pass.

Point 2 not taken: Locate only sees RVAs. MacOsImageInput::Rebases is a vector of RVAs filled from WindowsRelocationBuilder::Apply, which drops the ELF target address, and the writer's own rebases and binds in __APS5DATA have no ELF address at all, so reporting one would mean carrying address pairs through the input and the guest module writer. The RVA matches the two existing Locate messages and SegmentAt; it converts as rva - 0x10000 + (lowest PT_LOAD vaddr rounded down to 4 KiB), so 0x10100 is 0x4100 in the fixture.

Rerun on Windows 11 with a relinker-only Release build: the 18 macOS and rebase tests (ctest -R 'macos|interpreter_in_code|windows_bss|elf_offsets|none_relocations|optional_rela|overlapping_relocations|sce_code_extent|relinker_conversion|relinker_game_patch') pass, and the full suite shows only failures unrelated to this change (two that fail on main on this machine, and a shader recompiler build test that exceeds its ctest timeout here). The branch still merges cleanly with main.

@wachi35676

Copy link
Copy Markdown
Contributor

Tested head 397d534 merged onto main aeff26b (clean) on Windows 11 (Ryzen 5 9600X), WinLibs GCC 15.2.0, Release.

  • macos_output and interpreter_in_code pass. With main's MacOsImage.cpp and the new tests, macos_output fails.
  • Real inputs: I mapped every RELATIVE, R_X86_64_64, GLOB_DAT and JUMP_SLOT relocation in 24 PS5 ELF files to its PT_LOAD. These are the homebrew eboots of PPSA99005, PPSA47402, PPSA99581 and PPSA99995, their sce_module PRX files, and 16 wine PRX modules of PPSA99995. All 129,623 targets are in PF_W segments, so the new refusal does not reach real output.
  • The same four titles converted with --macos --to-rosetta, --windows --to-intel and to Linux all exit 0, with output byte-identical to main's.

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

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

Approving at 397d534c8. The requested change is in: the debt entry now states what was measured instead of claiming that dyld refuses the case.

  • Point 1 (requested): resolved. docs/dev/debt/relinker.md no longer says dyld refuses fixups in executable segments. It records the dyld_info -fixups diagnostics, the RWX case that loads and runs here, and the RX case that ends with SIGBUS in dyld4::Loader::applyFixupsGeneric, and marks other versions as not measured.
  • Point 3 (optional): resolved. test_interpreter_in_code.py passes flags 6 only for --macos, so the --windows run keeps covering a RELATIVE into a read-only PT_LOAD.
  • Point 2 (optional): your reasoning holds. Locate only has RVAs, and the writer's own __APS5DATA fixups have no ELF address, so the RVA, which matches the other Locate messages and converts as you describe, is fine.

MacOsImage.cpp and the other code are unchanged since d42c3dc7fd, so my earlier macOS results still apply to this head. Those were 63/63 relinker tests passing both ways, and the PF_R|PF_X fixture going from a SIGBUS in dyld to a clean conversion error.

AI-assisted: yes (Claude Code compared the two heads, checked the replies against the code and wrote this review).

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

Merge-skew check: head 397d534c8 merged onto current main (1a7ac56d27, also reproduced on abed035468) breaks dynamic_segment. The PR's CI is green because it ran before #10561 landed.

Apple M2 Max, macOS 27.2, Xcode 27, relinker-only Release arm64 (-DANYPS5_RELINKER_ONLY=ON -DBUILD_TESTING=ON), full suite.

  • main alone (8aa1f9ef7f): 355/355.
  • main (8aa1f9ef7f) + this PR: 354/355, only dynamic_segment fails (3/3 runs, not flaky):
('strict-trailing-bytes-after-null', ['--macos', 'unused-filter=2'], 2, ...
 'FAIL: Mach-O fixup target is not in a writable segment (offset 0x10300)')

Cause: #10561 (68f5474, merged in 47d389e) added strict-trailing-bytes-after-null to test_dynamic_segment.py, and it also runs in --macos unused-filter=2. Its strict_fixture() sets the single PT_LOAD to PF_R|PF_X (flags = 5) so the strict filter accepts the layout, but the inherited optional_plt_fixture() keeps a R_X86_64_RELATIVE at 0x300 inside that segment. The new Locate check correctly rejects that rebase. Making the segment writable (7) does not work, because the strict filter then fails with unsupported executable segment layout.

Minimal fix in the test, keeping what the case checks (DT_NULL tail handling under the strict filter): drop the RELATIVE relocation from the strict fixture.

 def strict_fixture():
     image = optional_plt_fixture([(4, 0x6a0)])
     struct.pack_into("<I", image, 64 + 4, 5)
+    # No RELATIVE fixup: a Mach-O fixup into this read-only executable segment is rejected.
+    struct.pack_into("<Q", image, 0x400 + 5 * 16 + 8, 0)
     struct.pack_into("<IIII", image, 0x6a0, 1, 1, 0, 0)
     return image

(0x400 + 5 * 16 + 8 is the value of DT_RELASZ.) With it, dynamic_segment passes 3/3 with this PR, dynamic_segment|macos|interpreter_in_code is 11/11, and the test still passes on main without the PR.

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

roboterspinne and others added 3 commits October 27, 2026 08:18
The macOS image writer emitted dyld rebase and bind opcodes for any
target inside a segment, so an R_X86_64_RELATIVE into an R-X PT_LOAD
became a rebase into a read-execute segment. dyld-1378 refuses fixups
in segments without write access (enforceFixupsInWritableSegments), so
Locate now throws when the target segment lacks VM_PROT_WRITE. Such
inputs now fail the macOS conversion instead of writing an output that
dyld would not load.
The debt entry on pointer fixups in executable segments claimed that
dyld refuses them, which no run had shown. It now states what was
measured on an M1 Pro running macOS 27.0.1 under Rosetta 2: dyld_info
reports both cases, the PF_R|PF_W|PF_X case loads and runs, and a
fixup into a PF_R|PF_X segment ends with SIGBUS in dyld. The
interpreter_in_code fixture keeps a read-only data PT_LOAD for the
Windows runs, so PE output still covers a RELATIVE into it.
…nt fixture

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@adriadam10
adriadam10 force-pushed the fix/relinker-macos-fixups-writable branch from 397d534 to 9e66ff6 Compare October 27, 2026 07:20

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

Follow-up on the merge skew I reported: fixed at head 9e66ff6b2.

Apple M2 Max, macOS 27.2, Xcode 27, relinker-only Release arm64 (-DANYPS5_RELINKER_ONLY=ON -DBUILD_TESTING=ON), this head merged onto current main 5d52b06ff3 (clean merge).

  • Full suite: 361/361.
  • dynamic_segment: 3/3 pass.
  • Control: same tree with test_dynamic_segment.py reverted to main's version, dynamic_segment fails again with Mach-O fixup target is not in a writable segment (offset 0x10300) on strict-trailing-bytes-after-null, so the fixture change is what makes it pass.

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

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

Re-review at 9e66ff6b2: approve. The Locate check and its tests are unchanged since 397d534c8. The new fixture commit removes the clash with #10561, and the full relinker suite passes on current main with this PR.

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

A relinker-only Release build (-DANYPS5_RELINKER_ONLY=ON -DBUILD_TESTING=ON -DCMAKE_OSX_ARCHITECTURES=x86_64) of main at 801d5b9bc1, with and without the diff at 9e66ff6b2. The full suite ran with -E 'audio_in|voice'.

full relinker suite macos_output dynamic_segment interpreter_in_code
Without this PR 359/359 passed Passed Passed Passed
With this PR 359/359 passed Passed (3/3 repeats) Passed (3/3 repeats) Passed (3/3 repeats)
PR, but test_dynamic_segment.py from main Failed: strict-trailing-bytes-after-null, --macos unused-filter=2, FAIL: Mach-O fixup target is not in a writable segment (offset 0x10300)
PR tests, but MacOsImage.cpp from main Failed: fixup-code: rebase into a non-writable segment accepted Passed Passed

So the fixture change in 9e66ff6b2 is required once #10561 is in, and the new macos_output case fails without the Locate check. The x86_64 build adds a second architecture to jotitadev's arm64 run.

9e66ff6b2 sets DT_RELASZ to 0: index 5 of test_optional_plt.fixture's tag list is (8, 24). That leaves the DT_NULL-tail parsing that both strict cases test unchanged, and convert() never runs the output.

Non-blocking:

  1. The PR body is stale for this head. What does not mention test_dynamic_segment.py or the clash with #10561. The sentence "With 4 it was the only existing test with a fixup into a non-writable segment of macOS output" no longer holds, because strict_fixture was a second one. Tested still says "Rebased onto e05ba6f" and has no run of this head. The line numbers in the Overlap bullet also need updating: the GS debt bullet is at line 41 of docs/dev/debt/relinker.md now, and Locate starts at line 222 of MacOsImage.cpp.
  2. core/relinker/relinker/tests/test_dynamic_segment.py:24 is jotitadev's suggested line, word for word, from their review. The commit and body do not credit them. Suggestion: add a Co-authored-by: Jota <139649225+jotitadev@users.noreply.github.com> trailer or a line in the body.
  3. Minor: the line zeroes DT_RELASZ, so the strict Linux and Windows runs also lose their RELATIVE relocation, not only --macos. The subject "drop the RELATIVE fixup" describes the effect; StrictNidFilterTests still covers the relative-relocation index directly.

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

@roboterspinne

Copy link
Copy Markdown
Contributor Author

@maximilianfv thanks. I updated the description for 9e66ff6: the Tests bullet now describes the clash with #10561's strict_fixture and the fix in 9e66ff6, which drops the RELATIVE from all three strict runs, and credits jotitadev, whose line adriadam10 applied; the Overlap line numbers and the Tested section match this head. I did not rewrite adriadam10's commit to add a trailer.

@adriadam10
adriadam10 merged commit 254240c into boykopovar:main Oct 27, 2026
14 checks passed
@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the Locate guard against both the rebase and bind paths, the new tests (each fails without the check), the debt entry, and the macOS/Windows/Linux relinker CI on this head. The M1 Pro measurements from @maximilianfv and the real-ELF relocation survey from @wachi35676 cover what we cannot run here.

Added one commit on top, from @jotitadev's merge-skew report: since #10561, strict-trailing-bytes-after-null in test_dynamic_segment.py runs under --macos, and its PF_R|PF_X fixture kept a RELATIVE relocation that the new check correctly rejects. strict_fixture() now zeroes DT_RELASZ, so the case still checks the DT_NULL tail under the strict filter. Relinker tests pass on the rebased head (192/192); merged.

@roboterspinne
roboterspinne deleted the fix/relinker-macos-fixups-writable branch October 27, 2026 18:00
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.

6 participants