Add header-only support for end-to-end relinker tests: a minimal PS5
executable builder, a PT_LOAD mapper that applies segment permissions,
and a posix_spawn runner for the relinker binary. Every
core/relinker/tests/e2e/*.cpp becomes its own test, so new tests do not
touch CMake.
The first test relinks an executable with EXTRQ and INSERTQ sites using
--to-intel, maps the output and calls the patched code, which runs both
stubs from their Linux segment and returns through their branches.
Co-Authored-By: Claude <noreply@anthropic.com>
A qualified import whose module name matches no DT_NEEDED entry now uses
the unqualified search order instead of failing with "Import module is
not a dependency". Restore the Linux rejection of duplicate exports
within one guest module.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The relinker redefined 61 ELF constants 93 times across seven files,
38 of them already in elfpatcher's ElfConstants.hpp. Add the missing
dynamic tags, relocation types and symbol constants to that header and
remove the local copies; every value is unchanged. strict_nid_filter_tests
compiles these sources and now gets the elfpatcher include directory.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MBXJqzgohLeVAeHsn4Jpm7
The cmp-equal execution case computed ZF from the untouched destination operand instead of the comparison result, so the self-check took the failure path and the Windows CI job failed with exit code 8. Derive ZF from the real subtraction for sub/cmp and from the real sum for add.
loadPointer overwrites rax and rcx, so a destination of rax or rcx
computed the operation against the thread pointer or the TLS index
instead of the original register. Restore the destination before the
operation for both, and include rcx in the byte-level cases, which
previously covered only rax, rbx, r8 and r13.
Add self-checking execution cases for rax, rcx and rbx destinations:
each verifies the result, the preservation of the other two registers
and the ZF and CF bits.
On Linux every module resolves its references through one global scope,
and the guest modules are listed ahead of the host prx. When a title
bundles its own libc.prx, the host libc and the other host prx bound
their references to their own NIDs to the guest libc, so the host libc
built its standard streams on top of the guest _Stdout.
The exports of guest modules, and the guest and executable imports that
a guest module satisfies, now carry a #guest suffix. The title's imports
still reach the guest modules first, and a host prx only sees host
definitions, as on Windows. sceKernelDlsym also looks for the suffixed
name.
Recognize the read-direction ALU forms (add, or, adc, sbb, and, sub, xor,
cmp) that read a qword from fs:[disp32], such as the stack canary check
`xor rbx, fs:[0x28]` emitted by stack-protected functions. They were
rejected as unsupported, which blocked titles relinked on Windows.
The stub loads the TLS value and applies the operation in place, keeping
the final operation as the last flag-setting instruction and preserving
the accumulator and counter. A destination of rax uses rdx as the value
scratch so the original accumulator survives.
Refs #217
Guest modules a title opens with sceKernelLoadStartModule are looked up
through their own handle, so several modules may export the same name:
every Wwise plugin exports g_pAKPluginList. On Linux a duplicate now
fails only when the executable or a guest module imports it, as the
provider would be ambiguous. Windows keeps rejecting duplicates.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Unity titles keep modules the executable needs in Media/Modules
(PPSA12544: Il2cppUserAssemblies.prx and PS5Util.prx), outside sce_module
and prx, so they were left as host libraries and startup failed to load
them. A DT_NEEDED module of the executable that is not in a module
directory is now searched for under the input directory and relinked like
the others; two ELF files with the name are ambiguous. Guest outputs keep
their directory relative to the input instead of only its last component.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Guest modules may define symbols in SHN_ABS (0xfff1), for example the
151 library marker symbols of PPSA12544's Il2cppUserAssemblies.prx.
The reader no longer rejects them or checks their value as a mapped
address, relocations against them write the absolute value without a
base relocation, and they are not exported: a PE export is an RVA, so an
import of one fails at startup as an unresolved import. Other special
sections are still rejected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The PE writer marked relocatable executables HIGH_ENTROPY_VA. Windows
then places the main thread stack, TEB, PEB and the loader threads'
stacks, which exist before libc reserves the guest arena from
0x2_0000_0000, anywhere in the first TiB. Nearly half the time they
land inside the arena and stay there as holes that fixed guest
mappings cannot use.
DllCharacteristics is now 0x140 (DYNAMIC_BASE, NX_COMPAT). The image
is still relocated and bottom-up allocations keep the 8-bit
randomization, but they start near the bottom of the address space,
far below the arena. DLL placement does not depend on this flag.
windows_address_space checks the flags and, on Windows, runs a
relinked guest that returns the high dword of its stack pointer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A title that ships sce_module/libc.prx makes addHost drop the host
libc.prx from the executable's dependencies. Every host prx needs it, but
their DT_RUNPATH is empty, so loading fails with "libc.prx: cannot open
shared object file".
Mirror the Windows rule from WindowsImportBuilder: when libSceLibcInternal.prx
is a host dependency and no host libc.prx remains, append libc.prx as the
last host dependency. The title's own imports still bind to the guest
modules first.
Refs #476
Preserve each bundled module directory under app0 and resolve Linux dependencies across directories. Add integration coverage for both output formats and Windows startup. Refs #556; duplicate-export rejection remains unchanged.
SHA1RNDS4/NEXTE/MSG1/MSG2 with a memory source made `--to-intel` fail
the whole relink. The stub now spills a scratch xmm, loads the operand
into it with the unaligned load already used for SHA-256 and runs the
register-form lowering. The SHA1RNDS4 immediate is read after the SIB
byte and displacement. RIP-relative operands still fail: the stub runs
at a different address.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The unsupported-instruction error now lists the instruction bytes, and the relink failure names the input file, the main executable or the guest module, on an extra Input: line.
Loads at fs:1 through fs:0x27 read tcb_dtv and tcb_thread on the FreeBSD layout, which the Windows thread control block does not model, so they returned zero. Accept only the TLS block, fs:0 and the stack guard at fs:0x28, and fail the relink for the rest.
SHA256RNDS2/MSG1/MSG2 with a memory source made `--to-intel` fail the
whole relink. The stub now spills a scratch xmm, loads the operand into
it with MOVDQU (AMD does not fault on a misaligned SHA operand, so the
unaligned load keeps that behaviour) and runs the register-form
lowering. RSP-based operands get the spill depth added to their
displacement. RIP-relative operands still fail: the stub runs at a
different address.
Co-Authored-By: Claude <noreply@anthropic.com>
RSQRTPS and RCPPS are approximations whose low bits differ between Intel
and AMD. Intel returns 0x3F7FF000 for 1/sqrt(1.0), and two Newton-Raphson
steps on it settle on 0.99999994 instead of 1.0. PPSA21564 renormalises
its spring quaternions that way: an identity quaternion keeps w at
0.99999994 with a zero axis, its axis-angle conversion then normalises the
zero axis into NaNs, and the NaN bone poses reach keyframed physics bodies.
A NaN body later moves to an invalid broadphase cell and the game crashes
a few seconds into its hub level.
--to-intel now replaces the VEX.128 register forms with an out-of-line stub
that computes 1/sqrt(x) with SQRTPS and DIVPS (1/x with DIVPS), keeping
the upper half of the destination cleared and the scratch register intact.
A site with no room for the jump, including one followed by a branch
target or a RIP-relative instruction, keeps the native instruction and is
reported as kept instead of failing the relink.