Derive traced slot page protections from the final output LOAD layout, including normalized overlaps and host page rounding. Keep data pages non-executable and retain execute on shared code and flagless LOAD pages.
Add native permission and hook regressions for default, explicit and above-4-GiB fixed bases.
Fixes#14423
Moving a PT_LOAD to the end of the file sets its p_align to
min(p_align, 0x1000), since the copy honours only the page. When that
segment carried the largest p_align, the output's largest PT_LOAD
alignment, which Linux uses to align the ET_DYN load bias, fell with
it. SeparateSharedLoadPages now returns the largest power-of-two
p_align above the page that it lowered, and WriteLayout aligns the
header block PT_LOAD to at least that value, as it already does for
the largest kept p_align.
Review feedback: the three mount-family patterns now carry their exact numbers (21, 378, 22) like the mman patterns, so a stub with another number is not matched. The fixture check that changed only the masked bytes could not fail for the unmount fixture, whose number is also 22; it now rewrites the number field to 0, 1, 21, 22 and 378 and requires every other number to be rejected. Technical debt says the numbers are pinned.
Recognize the inlined __syscall6 wrappers of ps5-payload-dev SDK libc/mount.c (mount, nmount, unmount) under --trap-sdk-syscall-stubs, with the four-byte syscall number immediate masked so an SDK build that renumbers the call still matches. The three wrappers differ in argument preparation, so each sequence is matched separately.
Observed in ftpsrv v0.21.1 (0x709a, 0x70ec, 0x713c) and websrv v0.35 (0xae6a, 0xaebc, 0xaf0c). Related: issue #8624, open draft PR #8729 covers mount only.
MarcZX10 found that this branch's RELRO fix doesn't hold when the
original image has more than one disjoint PT_SCE_RELRO range: ElfReader
retypes each one into its own PT_GNU_RELRO header, but glibc's loader
only tracks one PT_GNU_RELRO segment per image, so a later entry
silently overwrites the earlier one in the loader's state and only the
last range actually ends up protected. LLD's own linker has the same
one-segment assumption. Widening a single range to span the gap
between them isn't a safe fix either, since it would also protect
memory that was never part of any RELRO range.
Until a title is found that actually needs more than one disjoint
range, the program header layout builder now throws a clear error
instead of silently protecting only one of them.
Tested: two new program_layout_ranges cases (a single RELRO range
still reserves its header slot; two disjoint ranges are rejected).
Full relinker-only ctest suite: 245/246 pass, the one failure
(input_magic) is the same pre-existing, unrelated ASan allocation
issue seen on this branch before this change. Verified by hand with a
real two-range fixture converted through the actual relinker binary:
the Linux target now exits 2 with a clear message and writes no
output file, instead of silently succeeding with only one range
protected. check_conventions.py and git diff --check both pass.
Addresses the #12591 review. Because the Windows image ORs page permissions,
refuse a congruent overlap when one segment is executable and the other
writable and the later segment does not start on a page boundary, which would
otherwise share a read-write-execute page; the rule is applied on both loaders
so their acceptance decision matches. Bound the congruence check to the
file-backed part of the overlap and compare spans rather than walking the
implicit zero bytes of a large bss overlap.
Expand test_executable_segments.py: byte-congruent tail and bss overlaps
accepted with their trimmed sizes and no output overlap; a write-execute
overlap refused at an unaligned boundary and accepted at an aligned one with
the Windows image carrying no write-execute section; differing bytes, an
overlap past the later end, a non-zero file byte over the later segment's bss,
and a shared start address refused; and a no-overlap control. Keep only the
PPSA ids in the relinker.md note.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Some homebrew toolchains emit an eboot whose read-only PT_LOAD runs over the
GNU_RELRO start of the following read-write PT_LOAD, with byte-identical content
over the overlap. The relinker refused every such title with "Overlapping
PT_LOAD memory ranges" on both the Linux and Windows paths (Photo Splat VR
PPSA99770 #12183, FoxHollow PPSA99573, WWE 13 PPSA92013).
When an earlier PT_LOAD's tail overlaps a later one, the overlapping bytes are
identical, and the overlap does not extend past the later segment, trim the
earlier segment to end where the later one starts so the later segment owns the
shared range. On Linux the preserved PT_GNU_RELRO makes that range read-only
after relocation, matching the earlier read-only mapping; the Windows image
builder has no RELRO step, so the shared range stays writable (noted in
docs/dev/debt/relinker.md).
Overlaps whose bytes differ (the #10564 case), that extend past the later
segment, or that share a start address with it are still refused on every
target; the last case would otherwise trim the earlier segment to zero length.
test_executable_segments.py covers the accepted tail overlap and all three
refused cases.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Family fallback entries appended for kernel-variant imports were marked
imported, so the startup stub hard-required every family member; a
partial libs/ without the unused sibling aborted with a load failure.
Track fallback positions separately so only the declared module and
canonical libkernel.prx stay required while siblings probe and skip.
Adds missing-sibling cases pinning expected providers (11 for the
declared variant, 22 for canonical libkernel.prx) and trims the debt
entry below the 1000-character limit.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>