A deferred module that only another deferred module names (PSNCommon, needed by
PSNCore) started after the plugins that use it (SPFXUnityPlugin) and crashed
Soul Hackers 2 on Windows. The first title load of any deferred module now starts
every deferred module some module depends on, in dependency order, with that
load's start arguments, and logs the sweep. Boot is unchanged.
On Windows a module the executable needs or imports from stays deferred, but its
dependencies were deferred too and started only when the title loads it, so a title
that calls it first ran them uninitialised (Soul Hackers 2, #15370 rerun). They start
at startup again, as on main. The loader prints one line per deferred start.
Since #12621 a Windows runtime module that another runtime module needs is deferred instead of started at boot, but loading a deferred module only started that module. A Unity plugin such as SPFXUnityPlugin that calls into a deferred module it needs (Il2CppUserAssemblies in Media/Modules) ran against code whose initializers had never run, and PPSA03743 crashed about 2.6 s in.
A deferred module's initialization table now lists the deferred modules it depends on (names, after the initializer array, behind a magic number so older output reads as having none). DynamicLoader starts those first, transitively and once each through the existing per-module state, with the start arguments of the load that began it, then starts the module itself. The module start result stays the module's own. Boot still starts no deferred module.
Linux is unchanged: its deferred modules still start only when loaded.
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.