Limbo's bundled libSceFios2.prx (CUSA01664) has DTPMOD64 relocations
against a local section symbol with section index 0 and value 0, in a
module without section headers. As for a TLS relocation without a
symbol, the module and offset are the module's own TLS block, so a
DTPMOD64 or DTPOFF64 against such a symbol is passed on without it. A
section symbol that names a section or has a value, a TPOFF64, a module
without PT_TLS and a DTPMOD64 with an addend still fail.
Guest module discovery skipped the input executable and the
--executable check rejected an executable converted twice through
std::filesystem::equivalent, which MinGW reports as false for hard links
whose names give different permissions, such as .bin and .exe. Move the
GetFileInformationByHandle comparison to Io::SameFile and use it for
these checks and for SameOutputFile.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The code analysis trims an interpreter path from the start of an
executable segment only for images that have a PT_SCE_DYNLIBDATA block.
An executable without one keeps that path in the scanned range, so
/libexec/ld-elf.so.1 is walked as code: a RELATIVE relocation with
addend 0 roots the control flow graph at the first byte of the segment,
and the 0x64 of "ld-elf" decodes as an FS-prefixed instruction that the
Windows and macOS TLS rewriters refuse. The trim now runs for both
layouts, ending at the segment for an image without the block.
The Windows gs_traps group grew to 21 relinker invocations and ran close to its 30 s CTest timeout on a loaded host. Split the implicit-memory cases into gs_traps_implicit so each group stays well under the limit, and share the per-case check between them.
The Linux entry stub now passes each traced import through libc.prx's
TraceImport_nid_no_patch before it calls the guest entry, and stores the
returned thunk in the import's slot. The same imports are traced as on
Windows: functions bound through a PLT slot or a GOT slot, without an
addend, and not weak. TraceImport_nid_no_patch is bound eagerly through
an added GLOB_DAT relocation, so a libc.prx without it fails at load.
Each slot's page is made writable with mprotect first, so slots inside a
PT_GNU_RELRO range can be replaced.
--trace-imports now requires libc.prx among the libraries on Linux too,
and is refused with --macos.
buildGraph fell through from the entry points into the segment's zero
padding; an odd-length zero tail left a lone 0x00 the decoder needs a
ModRM byte for, and the non-speculative walk let that truncation escape
the pipeline, so unused-filter=1 exited 2 on input the other levels
convert. Bound the walk before the trailing zeros the way
StrictReachability does; mandatory roots still decode strictly.
macos_fixed_base relinks the fixed-base fixture with and without
--macos-fixed-base 0x400000 and checks that every segment and section
moves by the same amount, __PAGEZERO ends 64 KiB below the base, the
lowest load page is at the base, the entry offset is unchanged, and the
metadata load bias and the relocated slot hold base-relative addresses.
It also checks the lowest base and the refused options and images.
cli_args covers the parsing, range, repetition and --macos requirement.
WindowsTlsBuilder allocated the whole TLS block and thread control block
for .gtls, so a large p_memsz cost that much memory even though only the
p_filesz template is written. Store the template and reserve the rest as
the section's virtual size; the TLS directory still covers the whole
block, which the loader copies from the zeroed image.
#7147 added add_asan_test_options so the CPU-side tests pass in an
ANYPS5_ENABLE_ASAN build, and gave the relinker tests that load the
instrumented guest_module_args_host.prx into Python
verify_asan_link_order=0. guest_zero_initializer's Linux run loads the
same host library with ctypes.CDLL in a Python subprocess, so under ASan
it failed with "ASan runtime does not come first in initial library
list". It now runs with the same option.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
macos_gs_traps converts the forms Wine's ntdll in Prospero Win (PPSA99995) uses, a store, an ALU read and a register-address form shorter than a jump, and checks that each becomes ud2 and NOP padding of its own length before the next instruction, with none of the original bytes left.
In PS4 executables and modules the bytes between unwind functions are
16-byte slots of function tails and padding, and a tail can begin inside
an instruction, such as a lone REX byte before a 15-byte NOP. The
syscall and call-site scans decode the executable segment linearly, so
such a slot failed the relink with "Cannot decode instruction".
For images with a PT_SCE_DYNLIBDATA segment the scans now receive the
unwind function ranges. Outside every range an undecodable byte is
skipped and decoding resumes at the next byte, and a decoding that would
cross into a range ends at its start, so gap bytes cannot change how a
function is decoded. Inside a range a decode failure still fails the
relink, and images without unwind ranges are scanned as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A per-game relinker-patches.toml (or --patches) replaces verified bytes in
the input executable and bundled modules before conversion. Entries are
checked against the expected bytes and against every in-place writer,
the ELF header range and relocation targets. An <output-stem>.patches.toml
manifest records the applied entries, and --verify-patches checks an input
or a converted output. With no patch file the output is unchanged.
guest_preinit loads the instrumented guest_module_args_host.prx into Python, so in an ANYPS5_ENABLE_ASAN build it needs the same verify_asan_link_order=0 as the other module tests; without it, it fails with "ASan runtime does not come first in initial library list". add_asan_test_options does nothing outside ASan builds.
Signed-off-by: Ayamullah Khan <[email protected]>
Converted guest modules never ran their DT_PREINIT_ARRAY entries: the
reader validated the array and the unused-NID filter kept its targets,
but no writer emitted or ran it. ld.so runs a shared object's preinit
array only for the main executable, so the Linux output runs the
entries from the DT_INIT stub before the module initializer; the macOS
and Windows outputs run them before the initializer in their startup
stubs, and the deferred Windows output exports a thunk that runs them
before the initializer for runtime-loaded modules. A module with a
preinit array and no initializer is still rejected, since the Linux
stub exists only with one.