Commit Graph

443 Commits

Author SHA1 Message Date
Dean ae3574bb0d
Merge pull request #2238 from mrk1987/fix/relinker-address-taken-tls-roots
fix(relinker): root code addresses taken by RIP-relative LEA
2026-10-10 17:45:39 +13:00
Dean 5545196d0e
Merge pull request #2222 from EshaanJaiswal/fix-relative-relocation-bounds
fix(relinker): validate relative relocation table bounds
2026-10-10 17:45:25 +13:00
Dean 661295e736
Merge pull request #2154 from gunerky/codex/diagnostic-fixture
test(relinker): isolate Windows diagnostic fixtures
2026-10-10 17:44:31 +13:00
Dean 1b5ebc6986
Merge pull request #2091 from treuenten/fix/relinker-callsite-range-wrap
fix(relinker): reject a relocation target whose range wraps the address space
2026-10-10 17:43:52 +13:00
Dean 9992a5d5e5
Merge pull request #1944 from babyhuey/fix/relinker-lsda-without-personality
fix(relinker): read LSDA call-site tables only under a personality routine
2026-10-10 17:43:06 +13:00
Dean 300bd79439
Merge pull request #1935 from Zinebyte/fix/prefixed-syscall-validation
fix(relinker): reject prefixed system-call instructions
2026-10-10 17:42:59 +13:00
Dean a92b3db3d9
Merge pull request #1911 from TheAggie/fix/relinker-empty-tls-guard-access
fix(relinker): convert stack-guard and thread-pointer reads with an empty PT_TLS
2026-10-10 17:42:20 +13:00
Dean fdd2d26077
Merge pull request #1830 from tv7/fix/relinker-needed-debug-prx
fix(relinker): match needed module names that differ only in the extension
2026-10-10 17:41:41 +13:00
Dean 8c62529cad
Merge pull request #1822 from eliasbruno124-dev/fix/guest-runpath-depth
fix(relinker): rebase guest runpaths from their output directories
2026-10-10 17:41:33 +13:00
Dean 07633df7fb
Merge pull request #1549 from LeandroLP23/fix/gcc-warnings
fix: clear the GCC warnings in ProfileOutput, fopen, Ajm batch jobs and the SHA-256 test
2026-10-10 17:39:38 +13:00
JohnM d1c14102ab fix(relinker): reject a relocation target whose range wraps the address space
A jump slot whose r_offset is 0xFFFFFFFFFFFFFFF8 or higher makes
targetGotOrPltAddress + 8 wrap, so the upper lower_bound lands before
the lower one and the call-site loop walks past the end of the map
(SIGSEGV; ASan heap-buffer-overflow in ResolveCallSites).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 11:03:16 +08:00
arcwiser a18955ee97 fix(relinker): keep fixed Windows images loadable 2026-10-10 12:26:02 +11:00
Hannah Lyons c970ca73a0 fix(relinker): read LSDA call-site tables only under a personality routine 2026-10-09 21:23:27 -04:00
Adrià c9da0f93cf
Merge pull request #1559 from ctj01/fix/relinker-pt-load-order
fix(relinker): write Linux PT_LOAD headers in ascending address order
2026-10-10 03:16:06 +02:00
Adrià 3181e7116d
Merge pull request #1597 from mugurc/macos-relinker
feat(relinker): write Mach-O output for macOS with --macos
2026-10-10 03:13:21 +02:00
Adrià 69cee68d4b
Merge pull request #1522 from silentone12725/perf/relinker-strict-reachability
perf(relinker): replace O(n²) zero-padding scan in StrictReachability
2026-10-10 03:08:57 +02:00
Adrià 395d24697c
Merge pull request #1506 from kali113/fix/linux-guest-stack-header
fix(relinker): give Linux guest modules a default non-executable stack
2026-10-10 03:05:35 +02:00
Adrià be3441903b
Merge pull request #1503 from eliasbruno124-dev/fix/guest-required-identities
fix(relinker): discover required guest module identities
2026-10-10 03:05:15 +02:00
Adrià b993145df1
Merge pull request #1446 from sktharunbalaji13-cmd/contrib/rela-table-bounds
fix(relinker): bound DT_RELA table range
2026-10-10 03:01:42 +02:00
Adrià ff8961dfbc
Merge pull request #1102 from jeffrey12cali/fix/module-args-on-linux
fix(libkernel): pass module args and argp to runtime-loaded modules on Linux
2026-10-10 01:27:59 +02:00
Tharun Balaji 204a5e7a42 test(relinker): cover relocation table bounds in C++ under both output modes 2026-10-10 01:26:27 +02:00
Tharun Balaji bef3286e81 fix(relinker): bound DT_RELA table range 2026-10-10 01:26:27 +02:00
Adrià 32064d31fc
Merge pull request #1401 from xsvm/perf/relinker-in-place-substitutions
perf(relinker): write in-place --to-intel substitutions directly
2026-10-10 01:21:28 +02:00
Adrià Franch 8868d563b5 test(relinker): make guest_module_args run its native path
The machine check compared against 'x86-64' (uname reports 'x86_64'), so the
native execution was always skipped. With it enabled the fixture itself was
wrong: the initialiser's rip-relative call used the wrong displacement, and
the pending block was set on a different copy of the host library than the
one the module binds to.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-10 00:57:52 +02:00
jeffrey12cali 7d1821e3ab fix(relinker): emit the relocation table when the guest module has none
The DT_RELA tags were still guarded on the input module's relocation table, while the
new accessor relocations were appended to a copy of it. A module with an initialiser and
no imports therefore kept the argument stub but lost both relocations that fill its GOT
slots, so the stub called through a null pointer at load time.

Guard on the extended table instead, and drop the unused start parameter from the
displacement helper. The test now converts a module whose DT_RELASZ is zero and asserts
that the output carries both accessor imports and a 48-byte relocation table.
2026-10-10 00:57:52 +02:00
jeffrey12cali 7b8b2047b8 test(relinker): assert converted modules receive the module argument block
Add a hand-built guest module with an explicit DT_INIT that forwards rdi and rsi to a
host recorder without touching them and returns a known value. The test sets a known
argument count and argument pointer, loads the converted module, and asserts that the
initialiser received both values and that the stub forwarded its return value.

The host recorder also provides the two accessor symbols, so the test resolves them
without loading libkernel. The converted module must import both accessors, which is
what distinguishes the new behaviour from the previous stub that zeroed the arguments.
2026-10-10 00:57:52 +02:00
jeffrey12cali 36c979b369 fix(libkernel): pass module args and argp to runtime-loaded modules on Linux
sceKernelLoadStartModule discarded its arguments, and the relinker's Linux guest writer
synthesized an initialisation trampoline that also cleared the argument registers before
jumping to the module entry point, so a converted module always reached its own start-up
code with args = 0 and argp = NULL.

On PPSA28997 two of the title's own modules validate that parameter block as their first
act and dereference it unconditionally: SaveData faults on `mov (%rsi),%eax` with rsi = 0
beside its `cmp $0x10` size checks, and PSN faults the same way at PSN+0x3ee7 with rbx = 0.
The Windows branch delivers the block, so identical code reports its parameter-version
error and continues instead of faulting.

sceKernelLoadStartModule now records the pair before dlopen and clears it afterwards, and
the emitted DT_INIT stub reads it through two accessors exported by libkernel. The
initialiser is called rather than tail-jumped so its return value can be reported through
*res as the PS5 API specifies. The state is thread_local, which makes the handoff per-call:
ld.so runs a module's initialiser on the thread that called dlopen. DT_FINI keeps a plain
call-through, so unloading neither reads stale arguments nor reports a result.

The accessor names use the _nid_no_patch suffix so the NID resolver leaves them alone and a
guest import can name them exactly.
2026-10-10 00:57:51 +02:00
Adrià cb9734cd8a
Merge pull request #1272 from Dextroid17/fix/relinker-dynamic-table-bounds
fix(relinker): bound the symbol and relocation table reads
2026-10-10 00:42:33 +02:00
john.maia e4dcf3e6c0 fix(relinker): end the guest frame-pointer chain at the entry stub
The entry stubs called the title's entry with rbp left as the process
started it (0 on Linux) or as a loop counter left it (0 on Windows). The
title's start code pushes rbp, so the frame-pointer chain of its main
thread ended in a null frame pointer. Unreal Engine walks that chain to
capture call stacks (for an ensure, for example) and reads the return
address at [rbp + 8], so the walk read address 8 and crashed.

Both stubs now push a zero frame record (saved rbp 0, return address 0)
and point rbp at it before the call, so frame-pointer walks stop at the
entry. The Linux stub pushes it below the aligned stack pointer, keeping
the 16-byte alignment at the call. The Windows stub writes it into free
slots of its own frame at rsp+0x40.

PPSA15657 crashed in its stack walk at the title screen in about half of
its runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0134RWCyiyydJBbfKz8g9Bwe
2026-10-09 23:42:31 +01:00
Adrià 3692f56f09
Merge pull request #1249 from Raghav314/fix/relinker-guest-hash
fix(relinker): write a bucketed hash table for Linux guest modules
2026-10-10 00:37:47 +02:00
Dean b2714175b4
Merge pull request #2227 from rmz-24/test/relinker-gui-fixtures-without-wer
test(relinker): launch the GUI startup fixtures without Windows Error Reporting
2026-10-10 11:06:52 +13:00
Lexroid 0745d803e5 fix(relinker): fold case when matching needed module stems on Windows
Palworld's eboot needs EOSSDK-PS5-Shipping.debug_prx while the dump ships
prx/eossdk-ps5-shipping.prx. On Windows, compare module stems without
ASCII case, and let a name that already resolves to a guest image (exact
or case-folded filename, or soname) win over a stem alias.

Reported and diagnosed by @Rohan03x in #1830.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:29:45 +03:00
Lexroid 1a9ef2f25d fix(relinker): match needed module names that differ only in the extension
A title's executable can name a bundled module differently from the file it
ships: Dying Light 2 needs EOSSDK-PS5-Shipping.debug_prx and ships
EOSSDK-PS5-Shipping.prx. Needed names were matched by exact filename, so the
module was skipped without an error and start-up failed to load it. Renaming
the file does not help either: the title later loads the module at run time
by its shipped name (/app0//EOSSDK-PS5-Shipping.prx), and dlopen looks for
that name's .guest.prx (#1794).

When no file has the needed name, a module whose name differs only in its
.prx, .sprx or .debug_prx extension now satisfies it, in the module
directories or elsewhere under the input. The output keeps the shipped file
name, and the needed name is added as another name of that guest module.
Two such candidates for one needed name are ambiguous, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:24:07 +03:00
mugurc 703989b94b feat(relinker): write Mach-O output for macOS with --macos
The guest image reader and the module builder already produce a
platform-neutral GuestImage; --macos adds a third writer next to the ELF
and PE ones. The executable and every bundled module become x86-64 Mach-O
files (modules as dylibs in app0/sce_module) that run under Rosetta on
Apple silicon.

FreeBSD code reaches its thread pointer through %fs, which macOS cannot
set, so the writer rewrites each %fs access into a stub that reads the
image's thread pointer from a pthread key and allocates the TLS block on
a thread's first access. Dynamic TLS goes through the image's descriptor
instead of __tls_get_addr. Imports bind by NID to the prx libraries
beside the output, exports and TLS imports use the dynamic symbol names,
and the image's metadata lives in __APS5DATA,__meta.

--macos conflicts with --windows. The macOS runtime that consumes the
output (TLS allocation, libraries built for macOS) comes in separate
changes.
2026-10-10 00:13:39 +03:00
gunerky a58fe0cd72 test(relinker): isolate Windows diagnostic fixtures 2026-10-09 23:59:29 +03:00
abderahmane-ai bd750b5954 feat(relinker): write a registry for each converted bundled module
--registry only described the imports of the executable, so a title whose
own modules import system functions (PSN.prx, libc.prx and others) could
not be audited completely. With --registry the relinker now also writes
<output-stem>.<module>.registry.json for every converted guest module.

An entry is written for each relocation against an undefined symbol that
no other bundled module exports, the same test the module builder uses to
decide that a dependency is satisfied by a sibling. Entries carry nid,
library and targetOffset; the symbol name no longer has the # suffix by the
time the module is read, so the NID is the bare 11 characters. The
executable's registry is unchanged.

The test converts two modules where one is provided by its sibling, for
Linux and Windows output, covers the DT_RELA and DT_JMPREL tables, checks
that the same module imports the sibling's function once the sibling is
absent, and that nothing is written without --registry. Registered in
CTest as guest_registry.
2026-10-09 22:44:09 +02:00
Tomás Ortellado a213a26b47 fix(relinker): validate ELF identification before parsing 2026-10-09 10:44:31 -03:00
Tomás Ortellado a406ad4df7 fix(relinker): keep input checks compatible with module discovery 2026-10-09 10:38:17 -03:00
Tomás Ortellado ec5a2019b2 fix(relinker): reject truncated EH frame headers 2026-10-09 10:24:04 -03:00
Tomás Ortellado 682d6f62a3 fix(relinker): reject overflowing signed unwind LEB128 values 2026-10-09 10:05:26 -03:00
Tomás Ortellado dc9c75b576 fix(relinker): protect executable inputs from output overwrites 2026-10-09 09:50:16 -03:00
Andreas ba5256333d fix(relinker): root code addresses taken by RIP-relative LEA
Functions referenced only through a REX.W RIP-relative LEA (constructors,
callbacks, thread entries called indirectly) were not reached by code
analysis, so their guest TLS accesses stayed unpatched on Windows.

Add LEA targets inside executable code as roots, both in the instruction
walk and when following the control-flow graph of unreached code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:45:29 +02:00
Ramzi 115a8b050a test(relinker): launch the GUI startup fixtures without Windows Error Reporting
The GUI missing-symbol and missing-module fixtures were started with
ShellExecuteExW. A process the shell starts gets the default error mode,
so the status the entry stub raises reaches Windows Error Reporting as an
APPCRASH. Collecting each report took about 2 s, sometimes over 10 s,
and under a parallel ctest run it could pass the 30 s wait, failing
windows_import_modules with "ShellExecuteExW child did not exit" while
WerFault still held the fixture's files.

The fixtures now start through CreateProcessW with STARTF_USESTDHANDLES
and no standard handles, so the stub's diagnostic still has nowhere to
write, and with the test's error mode, which keeps the report away.
2026-10-09 13:24:01 +01:00
Eshaan Jaiswal 98b449f655 fix(relinker): allow empty relocation tables 2026-10-09 17:52:07 +05:30
Eshaan Jaiswal efe54ad6cb fix(relinker): validate relative relocation table bounds 2026-10-09 17:31:29 +05:30
eliasbruno124-dev 98c47df9e5 fix(relinker): discover required guest module identities 2026-10-09 13:53:48 +02:00
kali113 d5241d282f fix(relinker): give Linux guest modules a default non-executable stack 2026-10-09 13:45:37 +02:00
ctj01 1352b9b34d fix(relinker): write Linux PT_LOAD headers in ascending address order
The header-block PT_LOAD has the highest address but was the first
loadable entry in the table. Linux before 5.18 sizes the ET_DYN mapping
from the first and last PT_LOAD entries in table order, so the output
failed to exec with SIGSEGV. Write PT_PHDR and PT_INTERP first, sort the
kept PT_LOAD entries by address, and place the extra and header blocks
after them.
2026-10-09 06:27:23 -05:00
Dextroid17 84d851bf81 fix(relinker): bound the symbol table entry read 2026-10-09 06:54:33 -04:00
Dean 51f69aa218
Merge pull request #1586 from awake-devel/fix/relinker-keep-eh-frame-hdr
fix(relinker): keep PT_GNU_EH_FRAME in the relinked executable
2026-10-09 23:25:35 +13:00
Dean 2bf781eabb
Merge pull request #1856 from sarohy03/test/cli-args-coverage
test(relinker): cover CLI argument parsing
2026-10-09 22:41:25 +13:00
LeandroLP23 2bdb979e69 fix: clear the GCC warnings in ProfileOutput, fopen, Ajm batch jobs and the SHA-256 test
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:37:04 +02:00
Dean b38abc0a69
Merge pull request #1774 from 011010/test/guest-intel-trampolines-e2e
test(relinker): execute the guest Intel trampolines end to end
2026-10-09 21:04:06 +13:00
Dean 7988ed27c9
Merge pull request #1510 from Kikoarl/fix/unique-test-temp-paths-cpu-topology-icon
test: give cpu topology and icon resource temp paths a random suffix
2026-10-09 21:03:51 +13:00
Zinebyte 2381e7fc64 fix(relinker): reject prefixed system-call instructions
Use the decoded opcode offset when checking forbidden system calls.
Add synthetic regressions for prefixed forms, immediate decoys, and the skip option.

AI-assisted: Codex. The submitting author is responsible for every changed line.
2026-10-08 20:34:40 -04:00
Scott Schuster 4bc84d6b44 fix(relinker): convert stack-guard and thread-pointer reads with an empty PT_TLS
A title built with -fstack-protector reads its guard from fs:0x28 even when
it has no thread-local data and its linker emits an empty PT_TLS. The
Windows TLS builder refused any guest TLS access in that case. The guard and
the thread pointer live in the thread control block, which the builder
already emits, so an empty PT_TLS now takes the normal path, where a
zero-sized TLS block still rejects every other displacement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:08:06 -05:00
sarohy f765b27cc0 test(relinker): cover CLI argument parsing
Cli::ParseArgs had no test coverage: its only sibling test
(AutorunExitTests) exercises the autorun launcher, not the parser.

Add cli_args_tests covering every option and its side effects, the
--rpath and --exclude-sce-module value consumption (including that the
following token is taken verbatim rather than re-parsed as a flag), the
unused-filter value and single-specification validation, the
--exclude-sce-module/--skip-sce-module conflict in both orders, the
order-independent --windows-diagnostics/--windows-gui requirement on
--windows, unknown-option and extra-positional rejection, and the usage
message when an input or output path is missing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-10-09 02:40:19 +05:00
eliasbruno124-dev ac2b0f5deb fix(relinker): rebase guest runpaths from their output directories 2026-10-08 22:31:03 +02:00
SilentOne 1b5d406a2f fix(relinker): add regression test for fall-through after zero padding and tighten reset condition 2026-10-09 01:02:14 +05:30
SilentOne f8031f7c9e fix(relinker): keep fall-through edge after trailing zero padding in StrictReachability 2026-10-09 00:58:57 +05:30
SilentOne e55209d274 perf(relinker): replace O(n²) zero-padding scan in StrictReachability
addGap and buildEdges each called isZeroPadding(address, end) at every
instruction boundary. isZeroPadding scanned from the current address to
the region end, so the total work for a region with N instructions and a
trailing-zero suffix of length Z was O(N*Z), quadratic in the worst case.

Replace with trailingZeroStart, which scans backwards from the region end
once to find the start of the trailing-zero suffix. The loop then stops at
that position, eliminating the per-instruction zero check entirely.
2026-10-09 00:58:57 +05:30
husari.orozco 4d13e99a0d test(relinker): execute the guest Intel trampolines end to end 2026-10-08 12:46:05 -06:00
emirplus 8c45db7136 fix(relinker): restore full rcx for 32-bit register alu
Restore full 64-bit RCX before 32-bit ALU operation so CMP and other non-writing/carry instructions do not zero-extend and clear upper 32 bits. Add test coverage for 32-bit CMP, ADC, and SBB.
2026-10-08 21:36:42 +03:00
emirplus b4cc6d26b9 feat(relinker): support Windows guest TLS reads by ALU register-address instructions
Recognize read-direction ALU forms (add, or, adc, sbb, and, sub, xor, cmp) that read from fs:[mem] where mem has a base or index register (such as 64 48 33 03, xor rax, fs:[rbx], in #217). The stub loads the TLS value through the computed address, performs the ALU operation, and preserves scratch registers and flags.

Extend test_tls_function_coverage with execution and stub coverage cases for register-addressed ALU operations, including rax, rcx, rbx and r13 destinations, wide and 32-bit forms, and verification of register and flag preservation.

Refs #217

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:39:24 +03:00
Adrià 9e2ffe0da4
Merge pull request #1457 from drk1rd/fix/relinker-ud1-trap
fix(relinker): decode UD1 as a trap
2026-10-08 16:17:06 +02:00
Adrià 94d03f29fa
Merge pull request #1454 from mrk1987/fix/windows-internal-guest-libc
fix(relinker): resolve internal libc imports from guest libc
2026-10-08 16:07:33 +02:00
clem-gb d16cac0ab2 fix(relinker): keep PT_GNU_EH_FRAME in the relinked executable
The segment filter dropped every header in the PT_LOOS..PT_HIOS range,
which includes PT_GNU_EH_FRAME. The unwinders find the executable's
unwind tables through that header, so no C++ exception thrown in the
main executable could be caught on Linux.

The header is kept only when the output program header table still fits
in the input's table; otherwise it is dropped as before, with a warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
2026-10-08 15:47:39 +02:00
Adrià 63c57d2222
Merge pull request #1432 from ToyuAU/build/standalone-relinker
build(relinker): add a dependency-free conversion build mode
2026-10-08 15:43:51 +02:00
Adrià 033a46360d
Merge pull request #1424 from muhzain05/fix/relinker-jmprel-bounds
fix(relinker): bound the jump relocation table range
2026-10-08 15:39:32 +02:00
Adrià fd5f63a2d0
Merge pull request #1393 from xsvm/fix/relinker-rewriter-reference-sites
fix(relinker): classify rewrite reference sites with the instruction decoder
2026-10-08 15:03:48 +02:00
Adrià 2a906716f9
Merge pull request #1403 from overlordxrz-source/refactor/relinker-read-needed-once
refactor(relinker): read the executable's DT_NEEDED entries in one place
2026-10-08 15:00:52 +02:00
Arvind Frøiland 0858f01429 test(relinker): check decoder lengths against an LLVM-generated table
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:09:18 +09:00
muhzain05 19f2fcf059 fix(relinker): bound the jump relocation table range
DT_JMPREL and DT_PLTRELSZ were never range-checked against the input
file. With DT_OS_JMPREL the offset is used raw, so an offset outside the
image reached BuildDynamicSection and CompactPlt. When DT_PLTRELSZ is
zero the per-entry loops never run, so nothing rejected the table and
the relink produced an output executable.

Validate the declared range with the subtraction-based check already
used for DT_STRTAB and DT_STRSZ, next to it and before the table is
read.
2026-10-08 05:25:08 -06:00
Gjon Gojcevic 90dcd6256c fix(relinker): tolerate unwind ranges that split an instruction
A static branch into a skipped function tail is accepted when an
instruction decodes there within its executable segment, so a range that
ends inside an instruction no longer stops code analysis.

The Windows TLS rewriter skips a non-FS segment-prefixed instruction that
starts inside an earlier collected instruction: it is an alternative
decoding from a range that begins mid-instruction, not a TLS access.
2026-10-08 13:20:32 +02:00
Dean Galvin 2096c66a91 revert: undo #1156's bundled libc lifecycle suppression
Since #1156 a title's bundled sce_module/libc.prx is loaded for its
exports but its init is suppressed, while titles still call its malloc,
which then returns NULL (#1500). On Linux, titles that bundle libc crash
at launch: one segfaults within 0.2 s, another stops with bad_alloc.

This reverts commit 2d6aff7a1e, reversing
changes made to e82e03f11e.
2026-10-08 23:49:41 +13:00
Kikoarl 319ac9ba78 test: give cpu topology and icon resource temp paths a random suffix 2026-10-08 12:40:39 +02:00
xsvm 22ebf5e608 fix(relinker): classify rewrite reference sites with the instruction decoder
ClassifyInstruction assumed a ModRM byte right after every opcode that
was not a near branch. A length-changing rewrite therefore adjusted
immediates such as MOVABS imm64 that looked like a RIP-relative ModRM,
and missed RIP-relative operands of VEX and 0F 38 / 0F 3A instructions
and the rel8 targets of LOOP and JRCXZ. Take the RIP-relative
displacement and branch target from DecodeInstruction instead.
2026-10-08 18:20:14 +08:00
Suryansh Prajapati 7a3ec9f015 fix(relinker): decode UD1 as a trap
UD1 (0F B9 /r) always raises #UD, like UD2, but DecodeInstruction
reported it as sequential. Reachability analysis then followed the
bytes after it, so an import reachable only through that fallthrough
was kept by the strict unused NID filter. UD1 keeps its ModRM, so a
RIP-relative operand is still reported.
2026-10-08 15:26:54 +05:30
Adrià 73ac9ef7f4
Merge pull request #1218 from hashaam1217/feat/relinker-rip-relative-stubs
feat(relinker): relocate RIP-relative operands into --to-intel stubs
2026-10-08 11:40:17 +02:00
Dean 1bd8f07ccd
Merge pull request #1317 from Dextroid17/fix/name-both-self-container-magics
fix(relinker): name the second SELF container magic as a SELF input
2026-10-08 22:36:28 +13:00
Dean 9e5ade70fb
Merge pull request #1381 from overlordxrz-source/fix/relinker-decoder-0f-no-modrm
fix(relinker): decode 0F A8-AA without a ModRM and MOV CR/DR without a memory operand
2026-10-08 22:35:44 +13:00
Dean a753d0de45
Merge pull request #1303 from overlordxrz-source/fix/relinker-evex-grp3-immediate
fix(relinker): do not add the F6/F7 TEST immediate to EVEX instructions
2026-10-08 22:35:39 +13:00
Adrià 0802d02713
Merge pull request #1242 from hashaam1217/fix/program-header-entry-size
fix(relinker): reject a program header entry size other than 56 bytes
2026-10-08 11:28:29 +02:00
Adrià 296d68a31d
Merge pull request #1238 from hashaam1217/fix/windows-overlapping-load-segments
fix(relinker): reject overlapping PT_LOAD segments before sizing the Windows image
2026-10-08 11:14:58 +02:00
Adrià 71a0eab968
Merge pull request #1233 from hashaam1217/feat/windows-tls-register-loads
feat(relinker): load Windows guest TLS values through register addresses
2026-10-08 11:11:10 +02:00
Hashaam Zafar a571a7f100 feat(relinker): relocate RIP-relative operands into --to-intel stubs
SHA-1 and SHA-256 instructions with a [rip+disp32] operand, and
instructions with one that a short site absorbs, now move into the
stub. The stub records each displacement as a relocation relative to
the site, and the Linux, guest and Windows placements resolve it once
the stub address is known. Indirect branches through [rip+disp32]
still fail the relink.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 11:10:38 +02:00
Adrià 2d6aff7a1e
Merge pull request #1156 from ahir061/fix/linux-bundled-libc-lifecycle
fix(relinker): suppress duplicate system library lifecycle
2026-10-08 11:03:20 +02:00
Adrià 0703d818d0
Merge pull request #1194 from ASVLCII/fix/relinker-input-validation-20261007
fix(relinker): reject incomplete RELA entries
2026-10-08 10:49:19 +02:00
Adrià 2959dc5318
Merge pull request #1168 from oguzhanozfe/fix/decoder-moffs-length
fix(relinker): decode the 64-bit address of MOV moffs instructions
2026-10-08 10:39:59 +02:00
Adrià Franch 402cd1545e test(relinker): give the lifecycle fixture a non-executable stack header
glibc 2.41+ refuses to dlopen a module without PT_GNU_STACK, so the native
part of guest_lifecycle_ownership failed on current distributions.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-08 10:27:52 +02:00
Adrià 8388121a4f
Merge pull request #1153 from ebarr24/codex/windows-gui-error-status
fix(relinker): preserve GUI startup failure status without stderr
2026-10-08 10:26:56 +02:00
Adrià 17440abe24
Merge pull request #1165 from oguzhanozfe/fix/linux-dynstr-runpath-size
fix(relinker): include the run path in the Linux DT_STRSZ
2026-10-08 10:25:19 +02:00
Adrià 61896dea23
Merge pull request #1163 from oguzhanozfe/fix/decoder-rexw-immediate-size
fix(relinker): let REX.W override 0x66 when sizing decoder immediates
2026-10-08 10:23:35 +02:00
Ahir Barman Maji b12022bb4e test(relinker): fix Windows lifecycle fixture build 2026-10-08 10:22:40 +02:00
Ahir Barman Maji 9517cc33f8 fix(relinker): suppress duplicate system library lifecycle 2026-10-08 10:22:40 +02:00
Adrià f51eeff059
Merge pull request #1117 from TideOPL/fix/relinker-tls-unreached-code
fix(relinker): find Windows guest TLS accesses in code no root reaches
2026-10-08 09:54:40 +02:00
mrk1987 2050d84595 test(relinker): isolate guest libc resolution coverage 2026-10-08 09:43:52 +02:00
mrk1987 b64bb679cc fix(relinker): resolve internal libc imports from guest libc 2026-10-08 09:23:13 +02:00
ToyuAU 2d59fffdb7 build(relinker): add a dependency-free conversion build mode 2026-10-08 18:21:30 +11:00
Adrià 66b8abd8e6
Merge pull request #1101 from eliasbruno124-dev/fix/relinker-windows-guest-filenames
fix(relinker): resolve Windows guest filename casing
2026-10-08 08:43:35 +02:00
Adrià 7a178e2276
Merge pull request #1089 from AaravMehta-07/fix/relinker-registry-json
fix(relinker): escape control characters in call registry JSON
2026-10-08 08:10:07 +02:00
Adrià Franch 4e69584edf test(relinker): pin RELA bounds check with malformed-ELF cases
Co-authored-by: Dextroid17 <noreply@github.com>
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-08 07:52:32 +02:00
Fynn Jannis Schulz 9ff9d4325c fix(relinker): share RELA entry bounds check with extractRelative 2026-10-08 07:52:23 +02:00
Fynn Jannis Schulz cfba9c09d2 fix(relinker): overflow-safe RELA entry bounds check 2026-10-08 07:52:23 +02:00
Dean 4a435af58a
Merge pull request #1103 from ctz23/test/windows-icon-distinct-output-20261007
test(relinker): use distinct executables in Windows icon tests
2026-10-08 18:28:01 +13:00
raghav314 dfa179f43f fix(relinker): write a bucketed hash table for Linux guest modules
The Linux guest module writer emitted DT_HASH with a single bucket, so
every symbol lookup that reached a guest module walked its whole symbol
table. Guest modules come first in the global scope, so each host prx
lookup paid that walk at load and on every lazy PLT call. Build the
standard SysV table from the final symbol names with one bucket per
symbol, as the console's modules ship it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 10:20:45 +05:30
overlordxrz-source cd51d8b1bf refactor(relinker): read the executable's DT_NEEDED entries in one place 2026-10-07 22:31:35 -04:00
xsvm 2daa034e87 perf(relinker): write in-place --to-intel substitutions directly
Each in-place substitution went through X64InstructionRewriter::Rewrite,
which copies the whole code segment and decodes every instruction in it
to adjust references. The converter only accepts equal-length
replacements, so the adjustment is always zero and the copy and scan are
wasted: conversion time grew by one segment scan per substitution. Copy
the replacement bytes into the segment instead.
2026-10-08 10:19:39 +08:00
overlordxrz-source e66d78c626 fix(relinker): decode 0F A8-AA without a ModRM and MOV CR/DR without a memory operand
PUSH GS, POP GS and RSM (0F A8, 0F A9, 0F AA) have no ModRM byte, but fell in the 0F A3-AB ModRM range, so the decoder read the next byte as a ModRM and sized its displacement. MOV to and from control and debug registers (0F 20-23) ignore the ModRM mod field and always name a register, but were sized and reported as memory forms, including a RIP-relative disp32 for mod 00 r/m 101.
2026-10-07 21:14:30 -04:00
Dextroid17 9fce570e95 fix(relinker): name the second SELF container magic as a SELF input
ElfReader::ReadHeader names 0x1D3D154F as a SELF container, but dumps also
carry a second container magic, 0xEEF51454 (bytes 54 14 f5 ee), which fell
through to "Invalid ELF magic number: 54 14 f5 ee". That message reads as a
corrupt input rather than an encrypted one, so a user cannot tell that
decryption is what stands between them and a relink.

Both magics now report the SELF message. Measured on two retail dumps:
PPSA03671's eboot.bin and libcohtml.Prospero.prx are 54 14 f5 ee; PPSA28997's
eboot and modules are 4f 15 3d 1d.

Tested: Linux (CachyOS, GCC, Release, BUILD_TESTING=ON), relinker rebuilt. The
new case fails on the revert of the reader change with the exact message above
and passes with it; elf_header, elf_offsets, dynamic_segment,
string_table_bounds, guest_needed_modules, guest_module_directories and
linux_load_alignment still pass.
2026-10-07 19:28:04 -04:00
overlordxrz-source f4ef1cd6ec fix(relinker): do not add the F6/F7 TEST immediate to EVEX instructions
The decoder adds the TEST immediate of opcodes F6 and F7 when ModRM.reg
is 0 or 1. The check excluded VEX and two-byte opcodes but not EVEX, so
an EVEX.0F F6 (VPSADBW) whose ModRM.reg is 0 or 1 was given an extra
imm8 and decoded one byte too long. VPSADBW has no immediate (Intel SDM
Vol. 2B, VPSADBW).
2026-10-07 18:51:05 -04:00
Hashaam Zafar 33ab7c90f9 fix(relinker): reject a program header entry size other than 56 bytes
ReadProgramHeaders stepped through the table by e_phentsize without
checking it. With 0 it read the first header e_phnum times, and with
any other size it read misaligned fields, so a damaged header failed
much later as "No PT_DYNAMIC segment found", after building up to
65535 copies of one segment. A non-empty table must now use 56-byte
entries, as the guest module reader already requires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:08:14 -07:00
Hashaam Zafar a8f862b6df fix(relinker): reject overlapping PT_LOAD segments before sizing the Windows image
WindowsLoadImage sized its data and page table from the end of the last
segment and checked for overlap while copying. A segment whose memory
size reached past the last segment's end wrote page flags past the
table before its overlap was found; the relinker crashed with SIGSEGV
on such an input. Overlap is now checked in a pass before sizing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:05:05 -07:00
Hashaam Zafar 71b9cf3d0d test(relinker): run the Windows TLS cases on Linux x86-64
tls_function_coverage ran its execution cases only on Windows; on
Linux it checked bytes. windows_image_runner.py maps the relinked PE,
points GS at a TEB whose TLS slot holds a block copied from the
template, runs the TLS callbacks for process attach with the Windows
argument registers and calls the guest entry. convert() uses it on
Linux x86-64 and expects 42, as on Windows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 11:52:14 -07:00
Hashaam Zafar ace7999dce feat(relinker): load Windows guest TLS values through register addresses
mov r64 and mov r32 from fs:[mem], where mem has a base or an index
register, now relink for Windows (64 48 8b 03, mov rax, fs:[rbx], in
#217). The stub computes the offset with lea on the original address,
adds the thread pointer and loads.

These sites can be shorter than the 5-byte jump. A short site moves the
following sequential instructions into its stub and relocates
RIP-relative ones; a branch, a return or another TLS access after it
fails the relink. A TLS site shorter than a jump is now rejected instead
of overwriting the next instruction.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 11:44:34 -07:00
Nguyen Phuong Thuy 3b35a674f3 fix(relinker): reject incomplete RELA entries 2026-10-07 16:41:20 +00:00
Ethan Barr 2ed81aebe4 fix(relinker): preserve GUI startup failure status without stderr 2026-10-07 09:38:56 -07:00
Oguzhan Ozdemir e8f73a5531 fix(relinker): decode the 64-bit address of MOV moffs instructions
A0-A3 (MOV AL/eAX/rAX to and from moffs) have no ModRM byte and carry an
8-byte absolute address in 64-bit mode, or 4 bytes with a 0x67 prefix.
The decoder added nothing for the address, so the linear scanner decoded
the address bytes as code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 19:00:37 +03:00
Oguzhan Ozdemir cf8b7315b6 fix(relinker): include the run path in the Linux DT_STRSZ
The Linux patcher appends the DT_RUNPATH string after the copied
.dynstr data but kept DT_STRSZ and the .dynstr section size at the size
of the copied data, so DT_RUNPATH pointed at the first byte past the
declared string table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 18:58:42 +03:00
ctz23 80120faf8e test(relinker): use distinct executables in Windows icon tests 2026-10-07 17:57:06 +02:00
eliasbruno124-dev ba3e75a100 fix(relinker): resolve Windows guest filename casing 2026-10-07 17:55:28 +02:00
Oguzhan Ozdemir d64c0605ea fix(relinker): let REX.W override 0x66 when sizing decoder immediates
In 64-bit mode REX.W takes precedence over the 0x66 operand-size prefix,
so the immediate of 81, C7, 69, 68, A9, the 05..3D accumulator forms and
the F7 TEST form stays imm32. The decoder only applied this to B8+r and
returned a length two bytes short for the others.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 18:51:56 +03:00
Adrià f71bf7fd40
Merge pull request #716 from merwie/feat/relinker-needed-modules-outside-module-dirs
feat(relinker): relink needed modules outside the module directories
2026-10-07 17:51:47 +02:00
Adrià Franch 9f3ac9a5bd test(relinker): register the --to-intel stub test next to the relinker tests
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-07 16:03:41 +02:00
Adrià Franch 14dc9b31ab test(relinker): add C++ end-to-end fixture and --to-intel Linux stub test
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>
2026-10-07 16:03:26 +02:00
TideOPL e6467c4dff fix(relinker): find Windows guest TLS accesses in code no root reaches
The Windows TLS rewriter only patches instructions that
CodeInstructionCollector finds: symbol and unwind function ranges, plus
the code their branches, relocated pointers and initializers reach. A
fixed-address executable (ET_SCE_EXEC) has no relocations for function
pointers held in data, such as vtables or a thread entry loaded with
LEA, and its unwind tables need not cover every function. TLS accesses
in that code stayed `mov reg, fs:[0]`, which reads address 0 on Windows.

PPSA16064 (Minecraft Dungeons II, Unreal Engine) has 3689 such loads in
its executable segment; 478 were left unpatched, and its worker threads
faulted on their first TLS access at start-up.

The collector now also sweeps each executable segment linearly from the
first address that int3 padding precedes on a 16-byte boundary, and adds
every FS-prefixed instruction with an absolute disp32 operand as a root.
The sweep resynchronizes at the next padded 16-byte boundary when an
instruction does not decode, crosses such a boundary or starts with two
zero bytes, so zero-filled or undecodable bytes are not read as code.
The rewriter's own checks still decide which forms it can patch.

With this, all 3689 loads in PPSA16064 are patched. New cases in
tls_function_coverage: a padded function nothing references is patched,
and the same load behind zero fill after padding is left unchanged.
2026-10-07 14:08:12 +01:00
Aarav 8ab22b0954 fix(relinker): escape control characters in call registry JSON 2026-10-07 16:05:40 +05:30
Dean 0e58c7034f
Merge pull request #837 from MathiasDevelopes/claude/relinker-shared-elf-constants
refactor(relinker): use the shared ELF constants
2026-10-07 15:24:05 +13:00
Dean c0eb929c09
Merge pull request #930 from ebarr24/fix/relinker-dynamic-segment-bounds
fix(relinker): reject malformed dynamic segments
2026-10-07 15:16:45 +13:00
Dean 293d1218e9
Merge pull request #684 from nicolasabosch/fix/windows-import-module-binding
fix(relinker): bind Windows imports to their declared modules
2026-10-07 15:16:03 +13:00
Arjen Singh 6ac3ad5d1d fix(relinker): quote literal autorun paths on POSIX 2026-10-06 16:18:01 -07:00
Nicolas Abosch b12ef2cf20 fix(relinker): fall back to unqualified lookup for unmatched import modules
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>
2026-10-06 16:48:10 -03:00
Ethan Barr cd0e534db3 fix(relinker): reject malformed dynamic segments 2026-10-06 10:42:19 -07:00
Claude 897d538649
refactor(relinker): use the shared ELF constants
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
2026-10-06 08:26:50 +00:00
Dean 109a3281f8
Merge pull request #767 from 011010/feat/relinker-windows-tls-alu-reads
feat(relinker): support Windows guest TLS reads by ALU instructions
2026-10-06 16:35:21 +13:00
Nicolas Abosch e96e9c18cc fix(relinker): map module name aliases to declared dependencies 2026-10-05 22:47:35 -03:00
Nicolas Abosch bf0ee4019d fix(relinker): bind Windows imports to their declared modules 2026-10-05 22:43:25 -03:00
011010 7edf5d2ec2 fix(relinker): derive ZF from the subtraction in ALU execution tests
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.
2026-10-05 19:39:50 -06:00
Dean bd8aa57775
Merge pull request #698 from merwie/fix/relinker-guest-absolute-symbols
fix(relinker): accept absolute symbols in guest modules
2026-10-06 13:19:30 +13:00
husari.orozco 31e9e67f54 fix(relinker): keep the original rax and rcx in ALU TLS reads
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.
2026-10-05 17:50:50 -06:00
Nahuel Genchi 5fab03e4e5 fix(relinker): keep host prx references out of guest modules on Linux
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.
2026-10-05 19:58:39 -03:00
husari.orozco abdf673b1e feat(relinker): support Windows guest TLS reads by ALU instructions
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
2026-10-05 16:16:46 -06:00
Dean bc24783e5b
Merge pull request #733 from alfalfa-star/fix/relinker-duplicate-guest-exports
fix(relinker): allow duplicate Linux guest exports that no module imports
2026-10-06 10:34:07 +13:00
Dean 7580b9e44c
Merge pull request #679 from Celegans12/fix/relinker-windows-low-entropy-stacks
fix(relinker): keep Windows startup stacks below the guest arena
2026-10-06 07:57:17 +13:00
Dean cd8b6f3fcb
Merge pull request #662 from NumberGuan/codex/fix-autorun-exit-status
fix(relinker): decode autorun exit status for the host
2026-10-06 07:56:24 +13:00
alfalfa-star ee2b3be52f fix(relinker): allow duplicate Linux guest exports that no module imports
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>
2026-10-05 20:21:11 +02:00
Merijn d52a5f0169 feat(relinker): relink needed modules outside the module directories
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>
2026-10-05 19:41:59 +02:00
Merijn f524a27a8f fix(relinker): accept absolute symbols in guest modules
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>
2026-10-05 18:37:35 +02:00
Celegans12 823c3caf4f fix(relinker): keep Windows startup stacks below the guest arena
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>
2026-10-05 17:51:50 +02:00
Tomás Ortellado 1fa44c7b5c fix(relinker): decode autorun exit status for the host 2026-10-05 09:23:47 -03:00
Tomás Ortellado 9f9e4e28b8 fix(relinker): reject invalid and overflowing alignment 2026-10-05 08:44:55 -03:00
Dean Galvin 7103f390cf fix(relinker): keep the host libc.prx needed on Linux when a title bundles its own
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
2026-10-05 20:36:29 +13:00
Nicolas Abosch 93efde8056 fix(relinker): merge function symbol alias extents 2026-10-05 01:50:14 -03:00
NumberGuan 75f407b926 fix(relinker): discover bundled modules in prx directories
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.
2026-10-04 23:22:50 -03:00
Dean bc0726c486
Merge pull request #583 from DiulioAires/fix/bytewriter-bounds
fix(relinker): check byte writer bounds
2026-10-05 14:30:37 +13:00
DotDebian e11b4b5554 feat(relinker): lower SHA-1 instructions with a memory operand
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>
2026-10-05 02:02:13 +02:00
Dean d220149542
Merge pull request #299 from Pouare514/fix/relinker-tls-tail
fix(relinker): tolerate trailing data in TLS scan
2026-10-05 12:44:41 +13:00
DiulioAires b6c97084ed fix(relinker): check byte writer bounds 2026-10-04 20:17:21 -03:00
Pouare514 0b5fe3c23c fix(relinker): detect FS-prefixed previous instruction via SegmentPrefix decode 2026-10-05 00:56:11 +02:00
Pouare514 49feb79476 fix(relinker): scan overlapping span from address and cover FS-prefixed previous instruction 2026-10-04 15:41:17 +02:00
Pouare514 52587f8cf1 fix(relinker): tolerate trailing data in TLS scan 2026-10-04 15:27:57 +02:00
QDarkRex 47f0bf93ef fix(relinker): identify unsupported Windows TLS instructions
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.
2026-10-04 19:09:10 +07:00
Dean 9f9a8f5a53
Merge pull request #340 from QDarkRex/fix/relinker-tls-fs-disp32
fix(relinker): load Windows guest TLS values at signed displacements
2026-10-04 20:39:50 +13:00
Dean 8a64778581
Merge pull request #486 from RomainROCH/codex/fix-executable-segments
fix(relinker): validate all executable segments and preserve live imports
2026-10-04 20:09:49 +13:00
Dean 3237b6b548
Merge pull request #484 from RomainROCH/codex/fix-elf-string-bounds
fix(relinker): bound dynamic strings by the declared table size
2026-10-04 20:09:33 +13:00
QDarkRex ab5c339039 fix(relinker): reject guest TLS loads from unmodelled control block fields
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.
2026-10-04 14:09:19 +07:00
Adrià Franch 5fc16a9548 feat(relinker): lower SHA-256 instructions with a memory operand
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>
2026-10-04 08:21:40 +02:00
RomainROCH a35e0cfc84 test(relinker): register segment coverage beside load alignment checks 2026-10-04 05:59:16 +02:00
RomainROCH 85d036ca19 perf(relinker): reuse each segment scan across import records 2026-10-04 05:37:13 +02:00
QDarkRex d8684cb184 test(relinker): add C++ unit test for WindowsIconResourceBuilder 2026-10-04 10:29:20 +07:00
QDarkRex 9642d1b3b0 fix(relinker): add icons to generated Windows executables 2026-10-04 10:29:20 +07:00
RomainROCH 328def4bab fix(relinker): validate all executable segments and preserve live imports 2026-10-04 05:26:01 +02:00
RomainROCH e63524ae34 fix(relinker): bound dynamic strings by the declared table size 2026-10-04 05:23:18 +02:00
Dean Galvin 097566d547 fix(relinker): lower VRSQRTPS and VRCPPS to correctly rounded results for --to-intel
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.
2026-10-04 14:40:16 +13:00
Dean 40a9e455ea
Merge pull request #430 from adriadam10/feat/relinker-sha1-lowering
feat(relinker): lower SHA-1 instructions for --to-intel
2026-10-04 14:14:08 +13:00
Dean d43b12c34e
Merge pull request #433 from adriadam10/fix/relinker-sse4a-upper-qword
fix(relinker): zero dst[127:64] in EXTRQ and INSERTQ lowerings
2026-10-04 14:05:41 +13:00
Dean 40b1c7a6f9
Merge pull request #432 from adriadam10/fix/relinker-vex-rip-relative
fix(relinker): report RIP-relative operands of VEX, EVEX and 0F38/0F3A instructions
2026-10-04 14:05:25 +13:00
Adrià Franch b066079628 fix(relinker): zero dst[127:64] in EXTRQ and INSERTQ lowerings
The APM leaves bits 127:64 of the destination undefined after EXTRQ and
INSERTQ, but hardware zeroes them, and the register-form INSERTQ stub
already does the same (#185). The other paths kept the old high qword, so
a following register-form INSERTQ that reads its length and index from
that qword computed the wrong low qword too.

Every stub path that left the high qword now ends with movq dst, dst.
The in-place PSRLQ, PMOVZX and PBLENDW forms leave it non-zero and have
no room for the movq, so those sites now take a stub. The two in-place
forms that emitted nothing now emit movq dst, dst.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-03 08:21:33 +02:00
Adrià Franch ee3b215f33 fix(relinker): report RIP-relative operands of VEX, EVEX and 0F38/0F3A instructions
DecodeInstruction returned before the ModRM byte for VEX and EVEX and
read the third opcode byte of 0F 38/0F 3A as ModRM, so their
RIP-relative operands were never reported. --to-intel then moved such an
instruction after a short EXTRQ/INSERTQ into the stub with its
displacement unchanged, and the strict unused-import filter missed its
references.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-03 08:10:29 +02:00
Adrià Franch 71ca3ff4b8 feat(relinker): lower SHA-1 instructions for --to-intel
SHA1RNDS4, SHA1NEXTE, SHA1MSG1 and SHA1MSG2 passed through --to-intel
unchanged, so a relinked title that executes them faults on Intel CPUs
without SHA-NI. Their register forms are now lowered out of line with
SSE2 only through the shared stub builder; short sites share stubs via
MatchSequence. Memory forms fail the relink with the file offset, as for
SHA-256.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-03 07:40:41 +02:00
Dean Galvin d8b8aafc65 fix(relinker): hand the guest the real argv on Linux
The Linux entry stub built a copy of the entry parameter block holding
argc and a pointer to the host argv array, so the guest read that pointer
as argv[0] and uninitialised stack as argv[1..]. The process stack at
entry already has the guest's layout (argc as a qword, then the argv
pointers and a null), so pass rsp itself.

Test: linux_entry_argv relinks a fixture whose entry checks argc and
argv and runs the output on Linux x86-64.
2026-10-03 13:40:06 +13:00
QDarkRex 508a9710ae fix(relinker): report TLS bounds failures at file offsets 2026-10-02 16:14:54 +07:00
QDarkRex 102e042a49 fix(relinker): bound guest TLS loads to the thread block 2026-10-02 15:45:50 +07:00
QDarkRex 721984a28f fix(relinker): load Windows guest TLS at signed displacements 2026-10-02 15:19:25 +07:00
boykopovar c00bc19c0d
Merge pull request #222 from YossiYad/feat/windows-tls-load-registers
feat(relinker): load the fs:[0] thread pointer into any register in Windows guest code
2026-10-02 10:06:12 +03:00
Yossi Yadgar 53d4ef41d2 feat(relinker): load the fs:[0] thread pointer into any register in Windows guest code 2026-10-01 18:42:14 +03:00
ljkx d54dfb1595 fix(relinker): ignore a REX prefix that is followed by another prefix
In 64-bit mode a REX prefix only takes effect when it immediately
precedes the opcode; a REX followed by a legacy prefix or another REX is
ignored. The instruction decoder, the rewriter's reference classifier,
DecodedInstruction and the SSE4a, SHA-256 and CLZERO operand decoders
read legacy prefixes and then at most one REX. `48 F3 0F 2B 00`
(MOVNTSS) was therefore scanned as `[48 F3][0F 2B 00]` and kept, and
`48 67 0F 01 FC` lost its address-size prefix.

All of them now accept REX bytes among the prefixes and keep only one
that directly precedes the opcode. The converter's guard against
prefix-only fragments is removed, since the decoder no longer produces
them, and the TechnicalDebt entry describing the split is dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 13:09:11 +02:00
ljkx 09cb86b076 feat(relinker): lower CLZERO for --to-intel
Lower CLZERO out of line instead of failing the relink: the 64-byte line
containing rAX (EAX with 67h) is cleared with four MOVNTDQ stores of
zero, the same non-temporal, weakly ordered store. The line address is
masked in xmm0 so RFLAGS stays untouched; xmm0 and rcx are spilled below
the red zone. Short sites share stubs through MatchSequence. CLZERO with
LOCK, an FS/GS override, a 66/F2/F3 prefix or longer than 15 bytes fails
the relink.

The decoder splits a REX followed by another prefix into a separate
instruction, which would hide a prefix that changes what CLZERO or the
next instruction does. The converter now fails the relink when such a
prefix-only fragment directly precedes an AMD-only instruction or would
be moved into a stub as a trailing instruction.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 12:30:35 +02:00
boykopovar c7fb2575a3
Merge pull request #208 from boykopovar/refactor/split-libs-tests-cmake
Refactor/split libs tests cmake
- Moved test inclusions from `core/libs/CMakeLists.txt` to local library CMakeLists.txt files.
- Created CMakeLists.txt files for `libSceSystemService, libSceContentExport, libSceNet, libSceNetCtl, libSceRandom, libSceRtc, libSceJson2, and libSceFiber`.
- The shared libSceNet/libSceNetCtl test is included in libSceNet.
- Added the shared function add_test_executable for hosting test executables.
- Added add_png_decoder(target runtimeTarget) and add_jpeg_decoder(target runtimeTarget). The runtimeTarget parameter is required; libc is currently passed. A non-existent target causes a CMake error. - The decoder is used by the PRX libraries, meaning exceptions from there should be thrown through our libc.prx.

Changed file locations relative to the build folder (previously they were stored in core/libs):
- tests/ - all test executables
- `core/libs/libs/implib/` - PRX import libraries (*.dll.a)

The number of tests found has not changed (88)
2026-10-01 12:55:05 +03:00
boykopovar f694c1387c
Merge pull request #201 from ljkx/feat/monitorx-mwaitx-to-intel
feat(relinker): lower MONITORX and MWAITX for --to-intel
2026-10-01 12:50:04 +03:00
boykopovar 61ba4d661a refactor(cmake): place all test executables in build tests directory 2026-10-01 12:28:06 +03:00
SamG-Coder ad6b37ed42 fix(relinker): resolve guest dependencies and lifecycle arrays 2026-10-01 11:57:13 +03:00
SamG-Coder 3c5fe188b7 fix(relinker): forward Windows arguments to guest startup 2026-10-01 11:57:13 +03:00
Yossi Yadgar 058ba1e08d fix(relinker): name the SELF container and the bytes read when the input is not an ELF 2026-10-01 11:47:55 +03:00
ljkx f994b4f89f feat(relinker): lower MONITORX and MWAITX for --to-intel
Replace MONITORX with a NOP and MWAITX with PAUSE in place, padded with
NOPs to the original length, instead of failing the relink. MWAITX may
return before its timer or a store to the monitored line, and without an
armed monitor it does not wait, so callers already re-check their
condition; the replacement takes that path every time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:35:23 +02:00
boykopovar 261272909c
Merge pull request #200 from ljkx/feat/sha256-to-intel
feat(relinker): lower SHA-256 instructions for --to-intel
2026-10-01 11:31:02 +03:00
boykopovar a7fdb497fe
Merge pull request #195 from MichonGoddijn231849/fix/guest-module-tls
fix(relinker): give Windows guest modules with TLS an import directory
2026-10-01 11:15:39 +03:00
ljkx e66b0b34d7 feat(relinker): lower SHA-256 instructions for --to-intel
Lower SHA256RNDS2, SHA256MSG1 and SHA256MSG2 (register forms) out of line
with legacy SSE2 instructions only, so relinked code no longer needs
SHA-NI. Consecutive short SHA-256 and SSE4a sites share one stub. The
SSE4a stub builder moves to StubBodyBuilder for reuse; IsShaNi now also
recognises SHA256RNDS2 (0F 38 CB). Memory operand forms fail the relink.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 00:20:43 +02:00
Michon d29a1296cd fix(relinker): give Windows guest modules with TLS an import directory
The Windows loader sets up TLS for a dynamically loaded DLL only when it snaps
the DLL's imports. Guest modules had no import directory, so their TLS
directory was never processed: every guest module kept TLS index 0 and read
and wrote the executable's TLS block. A guest libc's thread-locals (gmtime's
buffer at +0x39c in Marvel's Wolverine) then ran past the executable's
0x150-byte block into the process heap, and corrupted a loader node that
crashed the next thread start in ntdll. Guest modules with TLS now import from
KERNEL32, as the executable does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 22:47:50 +02:00
Alberto Ruiz 46e63f2ec1 fix(relinker): keep ELF bounds checks from overflowing
ElfReader checked ranges as offset + size > buffer size. An offset near
UINT64_MAX wrapped the sum, passed the check and read outside the buffer,
which AddressSanitizer reports as a heap-buffer-overflow. The relinker
then failed later with unrelated messages built from the stray bytes.
The checks now compare without adding to the offset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5WJS4ZdQynAJesCZKH5hb
2026-09-30 21:06:43 +02:00