Commit Graph

354 Commits

Author SHA1 Message Date
Adrià abb2f8e4fc
Merge pull request #2986 from MarcZX100/fix/relinker-windows-unicode-2640
fix(relinker): support Unicode Windows startup paths
2026-10-11 01:46:23 +02:00
Adrià aa79dbdfe3
Merge pull request #2885 from MarcZX100/fix/relinker-linux-input-phdr-interp
fix(relinker): drop input Linux PT_PHDR and PT_INTERP
2026-10-11 01:13:32 +02:00
simonbbby cfd61a5977 fix(relinker): treat address-taken code as reachable in unused-filter=1
The CFG used by unused-filter=1 followed only direct branches, so code reached
through lea, relative-relocated pointers or jump tables was unreachable and its
imports were dropped. It now roots RIP-relative lea targets, relative relocation
targets and relative jump tables, and counts any RIP-relative reference to a GOT
slot as an access. Address-taken targets are walked after the direct control
flow, and a target that does not decode as an instruction is not code.
2026-10-11 00:35:27 +02:00
StarMrGamer 1a45dee292 fix(relinker): resolve the syscall number through prefixed instructions
The lookback classifies preceding instructions at their decoded opcode
offset, the same place the forbidden-instruction check looks since #1935,
so a legacy-prefixed mov rax, imm or mov r10, rcx is recognized. The REX
byte stays part of the patterns, so mov r8 and mov r8d cannot masquerade
as rax loads. Anything unrecognized still stops the inference.

Adds prefixed fixtures to both test suites: a prefixed constant stub that
must report syscall number 9, and a prefixed clobbered stub that must
report no number.

AI-assisted: yes. The submitting author is responsible for every changed line.
2026-10-11 00:27:44 +02:00
StarMrGamer f7a9629e68 fix(relinker): report the syscall number of a forbidden syscall instruction
The syscall check names only the code offset, so a title that links a FreeBSD
libc (the ps5-payload-dev SDK statically links one into every homebrew binary)
stops with no indication of which call it reached. Those stubs load the number
with a constant before the instruction (mov rax, 9 for link), so decode the
nearest constant load into rax and add it to the message:

  Forbidden syscall instruction at code offset 0xa: syscall number 9

The number is reported only when it can be established. The constant load must
be the instruction directly before the syscall, or one mov r10, rcx (the SDK's
first-argument move, which does not write rax) in front of it; any other
instruction stops the inference instead of returning a stale value, so
mov rax, 9; add rax, 1; syscall reports no number rather than 9. Only the three
invocation opcodes report a number: sysret is a return and keeps the current
message, as does a stub that loads rax from a register, like the SDK's generic
syscall() wrapper. The check still throws; only the text changes.

Tested: relinker-only build, full ctest on Linux x86-64 (45/45). The C++
syscall_scanner test covers the opcode matrix and the review regression; the
Python test_syscall_number covers the same contract through the harness the
relinker lane already runs on Ubuntu, Windows and macOS, where the C++ fixture
helpers (sys/mman.h, posix_spawn) do not build.

AI-assisted: yes
2026-10-11 00:27:44 +02:00
Adrià 4d27834ff9
Merge pull request #2119 from mugurc/macos-rosetta
feat(relinker): convert for Rosetta with --to-rosetta
2026-10-11 00:26:25 +02:00
Adrià Franch 36ffda9a98 test(relinker): check the declared-host import by membership, not as the module's only import 2026-10-11 00:14:26 +02:00
john.maia 5f4a28c2bc fix(relinker): keep Linux imports declared from host modules off guest exports
On Linux every executable or guest-module import whose NID a guest
module exports was renamed to the guest-suffixed name, whatever module
the import declares. A title that bundles a module exporting system
NIDs then had those imports bound to it: the libc.prx loader shim of
ps5-native-app-boilerplate exports placeholder functions under the
libSceLibcInternal NIDs, so DOOM PPSA99666's snprintf, vsnprintf,
memcpy and malloc (declared from libSceLibcInternal) returned 0 and the
title never found its WAD (#1497).

An import now binds to a guest export only when the module it declares
is a guest module, as on Windows. Imports without import-module
metadata keep the previous behaviour, and a title's own libc.prx
imported from libc still binds to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 00:14:19 +02:00
mugurc 92b20608e8 feat(relinker): convert for Rosetta with --to-rosetta
Rosetta runs AVX2, FMA, BMI1/2, F16C and MOVBE, but, unlike an Intel
host, it has neither RDSEED, CLWB nor RDPID. --to-rosetta converts
everything --to-intel does and lowers these three as well:

- RDSEED becomes RDRAND (the /6 form of the same opcode), which also
  returns a random value and sets CF.
- CLWB only writes a cache line back, so it becomes a NOP of the same
  length.
- RDPID becomes a load of 0 through an optional stub: Rosetta's TSC_AUX
  is a constant, not a processor index, so every thread reports
  processor 0. A site with no movable instruction after it for the jump
  is kept.

The flag implies --to-intel and conflicts with --windows. It is
independent of --macos: it only changes what the conversion produces, so
--macos alone leaves these instructions as they are, as on an Intel Mac,
and a title for Rosetta is relinked with --macos --to-rosetta. Intel
output is unchanged: the target is a parameter of the converter, Intel by
default.
2026-10-10 22:46:45 +03:00
Doston Abdullayev b1f0f1bff7 test(relinker): convert the non-ASCII working directory case in an ASCII directory
The relinker reads its arguments as narrow strings, so on a Windows
system whose ANSI code page cannot represent the directory it cannot
open the input, and the windows_working_directory case failed before
reaching the executable. Convert the case under an ASCII name and rename
the directory before starting the executable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 19:42:28 +00:00
Doston Abdullayev 28b1f1578f fix(relinker): walk Windows dependency diagnostics through the wide API
The --windows-diagnostics walker looked up modules and directories with
GetModuleHandleA, LoadLibraryExA, GetFileAttributesA,
GetModuleFileNameA and GetSystemDirectoryA, so under a directory outside
the ANSI code page it failed with "dependency diagnostics Windows API
failed" instead of naming the missing dependency.

Keep the walker's paths in UTF-8 and convert them at the Windows API
calls, which now use the wide functions. The entry stub passes it, and
prints, its paths as UTF-8 instead of the ANSI code page. The dependency
diagnostics test now builds its fixtures under a non-ASCII directory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 19:42:28 +00:00
Doston Abdullayev 19803c0086 fix(relinker): start Windows executables from directories outside the ANSI code page
The Windows entry stub read its path with GetModuleFileNameA, which
turns every character the ANSI code page cannot represent into '?', and
then used it with SetCurrentDirectoryA and LoadLibraryExA. A converted
executable in such a directory (Japanese on a Western-locale system, an
emoji, a script without an ANSI code page) stopped at start-up with
ERROR_INVALID_NAME.

Read the path with GetModuleFileNameW and enter the directory and load
the libraries with SetCurrentDirectoryW and LoadLibraryExW, with library
paths stored as UTF-16. Messages and the --windows-diagnostics walker
still get the paths in the ANSI code page, as recorded in TechnicalDebt.

Fixes #2640

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 19:42:28 +00:00
Dean 4a747c4872
Merge pull request #2882 from MarcZX100/codex/relinker-nonascii-2692
fix(relinker): handle Unicode paths on Windows
2026-10-11 07:56:44 +13:00
ctj01 b7d5f18c41 fix(relinker): drop the input PT_PHDR and PT_INTERP from Linux output
The Linux patcher writes its own PT_PHDR and PT_INTERP but also copied
the input's. glibc takes the load bias from the last PT_PHDR, which was
the input's entry with the pre-relink address, so such an executable
crashed at start. Both copies also took program header slots, so an
input with PT_PHDR and PT_INTERP and no spare slots failed to relink.
2026-10-10 17:33:59 +00:00
MarcZX100 c5ffebe15a fix(relinker): preserve empty Windows arguments 2026-10-10 17:28:07 +00:00
MarcZX100 fe81332b08 test(relinker): cover unicode guest module paths 2026-10-10 17:22:07 +00:00
MarcZX100 8cbebea6b7 fix(relinker): preserve unicode module paths 2026-10-10 17:22:07 +00:00
aplatogg b74774f713 fix(relinker): support non-ASCII input paths on Windows
The MinGW CRT delivers argv to main as ANSI code page bytes, and every narrow string became a filesystem path through the same code page, so a game in a directory with characters outside the code page failed to open (#2692). Read the command line as Unicode (wmain via -municode), keep paths as UTF-8 strings, convert at the filesystem boundaries through Io::NativePath and Io::Utf8Path, and run the --autorun output through CreateProcessW.

(cherry picked from commit 30a5181f8a71b177cf2dc03334e39998938b3472)
2026-10-10 17:22:07 +00:00
AlxcBTW fccfbee72b fix(relinker): convert Windows guest TLS reads after zero alignment padding
The Windows TLS rewriter only patches FS-relative instructions that
CodeInstructionCollector decodes. Code that no symbol, unwind range or
root reaches is covered by a linear sweep that starts after int3 padding
and stops at the first instruction beginning with two zero bytes, so
zero-filled bytes are not read as code.

The .NET NativeAOT homebrew titles pad between functions with zero bytes
instead of int3 and have unwind entries for only part of their code. In
Prospero Vibrate (PPSA99920) the sweep ended at the padding after the
first function. Functions reached only through switch jump tables, which
the control-flow graph does not follow, or through pointers it cannot
resolve kept 105 of 670 FS loads unpatched: 84 `mov reg, fs:[0x28]`
stack-guard reads (65 into rax, 19 into rcx) and 21 `mov rax, fs:[0]`
thread-pointer reads. The title faulted reading address 0x28 within a
second of start.

The sweep now treats a zero run that follows a jump, call, return or
trap and ends at the next 16-byte boundary as alignment padding, and
continues at that boundary. A call counts because a function can end in
a call to a noreturn function. Zero fill after straight-line code, or a
whole zero 16-byte block, still ends the sweep as before.

All 670 FS loads in PPSA99920 are now patched, as are the 102 of 685 in
Prospero Explorer (PPSA99109) and the 103 of 718 in Prospero Multi Tools
(PPSA99110). New tls_function_coverage padding cases: loads behind zero
padding after a return, a call and a jump, with 13, 2, 1 and 5 zero
bytes, are patched; a load behind zero fill after a nop is left
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 0306c677784b6c4272990ce65f6daf9fb2dbc3ca)
2026-10-10 16:08:54 +00:00
boykopovar 0bd2e4cb90 test(relinker): align module tests with nonrecursive discovery 2026-10-10 15:31:17 +03:00
boykopovar 336d481bff fix(relinker): default module path to input elf directory 2026-10-10 14:12:04 +03:00
boykopovar bec6d62b29 feat(relinker): add explicit guest module parent path option 2026-10-10 14:10:13 +03:00
boykopovar 776f7738d4 fix(relinker): stop requiring host dependencies in guest module directories 2026-10-10 14:04:37 +03:00
boykopovar 400a496792 refactor(relinker): unify guest module diagnostics 2026-10-10 14:02:00 +03:00
boykopovar 41126bfa5b fix(relinker): restrict guest module discovery to adjacent directories 2026-10-10 13:59:11 +03:00
Adrià ebe6c180bf
Merge pull request #2594 from morten-holm/pr/macos-clear-pie
fix(relinker): leave MH_PIE clear in the macOS executable
2026-10-10 11:15:15 +02:00
Adrià 3d1a7d90c7
Merge pull request #2619 from oneandonlydean/fix/carry-2525
fix(relinker): skip repeated DT_NEEDED libraries
2026-10-10 11:01:22 +02:00
Adrià 69e9a2685a
Merge pull request #2605 from elsi06/fix/relinker-self-guest-modules
fix(relinker): reject guest modules in SELF containers
2026-10-10 10:52:52 +02:00
nyavana 8b008a3e96 test(relinker): cover repeated guest module dependencies
Generate guest PRX dependencies with adjacent and separated repeats, equal names at distinct string offsets, and invalid-name controls. Check acceptance for Linux and Windows conversion and first-seen dependency order in ELF outputs.

Depends on the production fix by Hannah Lyons (babyhuey), c1a6696872153b8e41608f4cc8ab9a0d36f1d206. Written with AI assistance.
2026-10-10 21:23:19 +13:00
Yassine 6158444f71 fix(relinker): leave zero-filled PT_LOAD memory out of Windows files
WindowsPeWriter stored every PE section with SizeOfRawData equal to its
virtual size, so the zero-filled tail of a PT_LOAD segment (p_memsz above
p_filesz, the .bss) was written to the executable byte for byte. A segment
with 256 MiB of .bss produced a 269 MB .exe, while the Linux output of the
same image is under 5 KB.

Each section is now stored up to its last non-zero byte, aligned to the file
alignment, and VirtualSize keeps the full size. The PE loader zero-fills the
part of a section beyond SizeOfRawData (Microsoft PE format, section table:
SizeOfRawData). A section with no non-zero byte gets no raw data and a zero
PointerToRawData.

windows_bss relinks an image with 64 MiB of .bss, checks the section table
and the mapped image, and on Windows runs the executable, which reads the
last word of the .bss.
2026-10-10 21:23:19 +13:00
Hannah Lyons 0e03ad0f95 fix(relinker): skip repeated DT_NEEDED libraries 2026-10-10 21:23:19 +13:00
Patrick Elsasser bc444a675d fix(relinker): reject guest modules in SELF containers
A bundled module whose file starts with a SELF container magic
(4f 15 3d 1d or 54 14 f5 ee) was skipped like any other non-ELF file,
so the relink succeeded without it and the module was missing at run
time. The guest module scan now throws, naming the module, as
ElfReader already does for a SELF executable.

Fixes #2599

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 10:22:58 +02:00
gunerky b2ff4f4b02 test(relinker): split TLS coverage into bounded CTest groups 2026-10-10 10:07:33 +02:00
Hamidreza 41856ea6c5 perf(relinker): single-pass CFG instruction decoding and zero-copy text span 2026-10-10 09:48:02 +02:00
Adrià 2f250f83aa
Merge pull request #1815 from eliasbruno124-dev/fix/nested-required-modules-publication
fix(relinker): discover required modules in nested standard directories
2026-10-10 09:19:07 +02:00
Dean edbf36ad4c
Merge pull request #2574 from oneandonlydean/fix/carry-2464
fix(relinker): start Windows executables in their own directory
2026-10-10 20:07:53 +13:00
Morten Holm 31b6af028f fix(relinker): leave MH_PIE clear in the macOS executable
The executable is linked at a fixed base with a __PAGEZERO that fills the space below it, and
the guest assumes 16 KB pages. With MH_PIE set dyld slides the image by a multiple of 4 KB,
which breaks that assumption and ends in a SIGBUS (KERN_PROTECTION_FAILURE) in about half of
the launches under Rosetta 2. Clearing the flag keeps the image at its linked address.

test_macos_output.py now reads the header flags and checks that MH_PIE is clear.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-10 09:07:14 +02:00
Adrià 653e3d04d7
Merge pull request #2097 from treuenten/fix/relinker-entry-stub-rel32
fix(relinker): reject a Linux entry stub whose call cannot reach the entry
2026-10-10 08:49:35 +02:00
Dean 6f262eb0f4
Merge pull request #2568 from oneandonlydean/fix/relinker-decode-xop-2559
fix(relinker): decode XOP instructions
2026-10-10 19:47:50 +13:00
Get-fix 975475929e fix(relinker): start Windows executables in their own directory
The guest filesystem root and the save data directory are taken from the
current directory, so an executable started from another directory does
not find its title files and keeps saves elsewhere. Make the executable
directory the current directory in the Windows entry stub before the
system libraries are loaded.
2026-10-10 19:44:06 +13:00
Adrià d7f24e7652
Merge pull request #2294 from Vabian124/cursor/fix-relinker-reject-non-regular-input-9077
fix(relinker): reject non-regular input files
2026-10-10 08:35:29 +02:00
Adrià 8f693fc82c
Merge pull request #2274 from jweidenh/codex/fix-linux-program-header-count
fix(relinker): count all synthetic Linux program headers
2026-10-10 08:32:53 +02:00
Achraf Reyani ce8dbc977f fix(relinker): decode XOP instructions
The x86 decoder had no XOP (AMD 8F escape) support and read 8F E8.. as
POP r/m, losing instruction boundaries. Since #2238 roots code addresses
taken by RIP-relative LEA, code analysis reaches CPU-dispatched XOP
paths, such as the vpperm routines in Stremio Plus (PPSA74126), OpenNOW
(PPSA99082) and PPSA99420, and their Windows conversion failed with
"Code analysis: overlapping instruction boundaries". Decode 8F followed
by a map of 8 or more as a three-byte XOP prefix (8F can only be POP r/m
with ModRM.reg 0) with a ModRM operand, an 8-bit immediate in map 8 and
a 32-bit immediate in map 0Ah, in both the length and operand decoders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 19:27:24 +13:00
Cursor Agent eba1dec764 fix(relinker): reject non-regular input files
A directory opens and tellg reports an enormous size, so the relinker
tried to allocate it and failed with std::bad_alloc. Require a regular
file and a non-negative size before allocating the input buffer.
2026-10-10 08:26:32 +02:00
simonbbby fcb0b962a7 fix(relinker): collect init and fini array roots through the containing load segment
The unused-filter=1 entry point collector translated DT_INIT_ARRAY and
DT_FINI_ARRAY through PT_DYNAMIC and skipped arrays placed before it, which is
the usual linker layout, so constructors and destructors were not roots and
imports used only by them were dropped. Arrays are now translated through the
PT_LOAD that holds them, slot targets come from RELATIVE relocations when
present, and DT_PREINIT_ARRAY is collected as in the strict filter.
2026-10-10 08:25:50 +02:00
Adrià 019ca236fb
Merge pull request #1958 from arcwiser/fix/windows-pe-relocs-stripped
fix(relinker): keep Windows images without relocations loadable
2026-10-10 08:06:20 +02:00
eliasbruno124-dev e2401a8dc0 fix(relinker): discover required modules in nested standard directories 2026-10-10 08:05:01 +02:00
Adrià be775231a2
Merge pull request #1651 from emirplus/feat/relinker-tls-register-alu
feat(relinker): support Windows guest TLS reads by ALU register-address instructions
2026-10-10 07:58:52 +02:00
Dean 6635c7abbf
Merge pull request #2117 from krishalivaibhav/codex/relinker-help
feat(relinker): support explicit CLI help
2026-10-10 18:54:09 +13:00
Dean e31154aa90
Merge pull request #2196 from AchrafReyani/fix/relinker-entry-point-bounds
fix(relinker): bound the entry point collector's header and symbol reads
2026-10-10 18:54:02 +13:00