Since #8162 a --module-dir module that the executable needs is deferred
on Windows: the entry stub maps it and the title starts it with
sceKernelLoadStartModule. A module the executable imports from but the
title never loads gets no module_start and no initializers, silently.
The entry stub now prints one line at startup for each deferred guest
module that an import of the executable names. Guest module bytes are
unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
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 raw-byte check that runs before a syscall pattern between PS4 unwind
functions is skipped now also accepts mov rax, imm64, merges a following
mov al, imm8 or mov ax, imm16 into the earlier constant, and walks back
over any number of register moves or zeroings that leave rax alone
instead of at most seven. Visited states keep the walk linear in the
bytes it covers.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A relocation or lea target outside the unwind functions of a PS4 image
is now decoded up to its first jump, return or trap, or to the start of
the next unwind function, which also bounds the transfer check of the
tail rule. A target whose run holds an fs:[disp32] access, the form the
linear TLS sweep roots, is followed as code. The other targets taken as
data are counted in one message and kept as entry points that the
Windows TLS rewrite may not overwrite. The analysis merges nested
unwind ranges like the scans.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The skip of syscall patterns outside the unwind functions let some real
syscalls through. The scan now merges nested and overlapping unwind
ranges with the instruction scanner's MergeCodeRanges, so a pattern
inside an outer function is refused. A constant syscall number is also
looked for in the bytes in front of the pattern, whatever alignment the
decoding took: a mov eax, mov rax or xor eax, eax that ends at the
pattern or before register moves and zeroings that leave rax alone. The
decoded lookback requires each instruction to end where the next one
starts. Guest modules without PT_SCE_DYNLIBDATA get no unwind ranges,
so their scan stays strict even when their unwind table is read.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The code analysis of the Windows and macOS outputs followed every
relocation and lea target inside the code range as code. PS4 images keep
read-only data between unwind functions, and strings and tables there
decoded into garbage, failing the conversion. Outside the unwind functions
such a target is now code only when it is 16-byte aligned, starts less
than 256 bytes after an unwind function and decodes to a jump, return or
trap; lea targets go through the same rule instead of the graph's own.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A --module-dir module that only the executable needs was initialized by
the Windows entry stub with no start arguments, so a later
sceKernelLoadStartModule with arguments found it started and its
module_start never saw them. Unity's mono-ps4.sprx keeps those
arguments for the plugins it loads. Such a module is now deferred like
any other runtime module: mapped at startup and started, with its
arguments, when the title loads it. Modules another guest module needs
are still started at startup, and Linux and macOS are unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
MCOMMIT becomes STC plus NOP padding and RDPRU goes through a
runtime-checked stub returning the host TSC for MPERF/APERF while
other selectors yield zero, with converter and execution tests.
docs/dev/debt/relinker.md records the new lowering and its unverified
assumptions.
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.
MinGW's std::filesystem::equivalent returns false for hard links whose
names give different permissions, such as .elf and .exe. The temporary
hard-link probe that worked around it failed on read-only files and on
directories that refuse the link. Compare the volume serial number and
file index from GetFileInformationByHandle instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
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]>
Resolve the docs/dev/debt/relinker.md conflict with #5994: keep its
sce_module syscall-scan entry, which now also stops at the end of the
last unwind function when the PLT comes before the code (it calls the
same FindSceCodeEnd), and its /libexec/ld-elf.so.1 start in the code
analysis entry, with this PR's end for a PLT before the code.
Co-Authored-By: Claude Opus 5.5 <[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.
Rosetta sets RDTSCP's ECX to its constant TSC_AUX, 0xAAAAAAAB, which is
not a processor index. --to-rosetta already makes RDPID return processor
0 for the same reason, so code reading the processor through RDTSCP saw
a different value from code reading it through RDPID.
--to-rosetta now moves RDTSCP into an optional stub that runs the
instruction unchanged and then loads 0 into ECX with a MOV, which
leaves the counter in EDX:EAX and the flags as they were. A site with no
movable instruction after it for the jump is kept, as for RDPID. The
Intel target and a plain relink are unchanged.
Homebrew built with the ps5-payload-dev SDK imports by symbol name and
names its system libraries with .sprx, while the libraries in libs/
are .prx files that export NIDs. Quake from the websrv launcher
stopped with 457 unresolved imports such as strlen and malloc.
An import without a module that no searched library exports under its
name is now looked up again under the NID of that name. A dependency
named with .sprx, .suprx or .debug_prx loads the .prx library of the
same stem, and libkernel_web also loads libkernel.prx, as
libSceLibcInternal already loads libc.prx.
66 E8, 66 E9 and 66 0F 80-8F without REX.W take a 16-bit displacement on
AMD processors, the console included. Every near relative branch with that
prefix truncates its target to 16 bits, so the decoder reports no branch
target for it and stubs refuse to move it.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_016TFMtUC4L4wMS4eeEqaNSR
The Rosetta conversion limits were measured on an M1 running macOS 15.3.1
and an M5 Max. A survey on an M5 running macOS 26.4 executes ANDN, RDRAND,
the rest of BMI1 and BMI2, LZCNT, PREFETCHW, CLFLUSHOPT, AVX, AVX2, FMA,
F16C and MOVBE while CPUID reports none of them, and raises an illegal
instruction for RDSEED, CLWB, RDPID, ADCX and ADOX.
FindSceCodeEnd looked for the PLT only after the last function of the
unwind table and failed with "PLT not found after the last unwind
function" otherwise. The libSceFios2.prx and libc.prx that Until Dawn
(CUSA02636) bundles have their PLT at 0xc8, at the start of the text
segment, before the code. In both, the last unwind function is
followed by the module's DT_FINI routine, which has no unwind entry,
and then by read-only data.
When no PLT follows the last unwind function, the search now also looks
before it. A PLT found there must not overlap an unwind function, and
the code extent then ends at the last unwind function, so what follows
it stays out of the scans and the code analysis, including code
without an unwind entry such as the DT_FINI routine of both modules
(recorded in the debt file). A missing PLT now fails with "PLT not
found before or after the unwind functions".
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Since ab3e92605 the entry stub gives each missing standard handle
anyps5.log beside the executable, and the crash report writes to the
standard error handle, so it reaches the log too. The entry now records
only what is still lost: the stub's errors about the executable path or
about waiting for the previous LoadExec process, written before the log
is opened, and output to the missing handles when the log cannot be
created.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_0154rPujpieMaCnZ6WMobg7M
AnyPS5 exports some libkernel functions, such as __error, from
libc.prx, which libkernel.prx links. On Linux they resolve through that
dependency. On Windows, the import stubs search only the listed
modules, and libc.prx was listed only when libSceLibcInternal.prx was
needed. A PS4 homebrew that needs libkernel.prx but no C library (the
OpenOrbis graphics sample) failed with "unresolved ELF import
9BcDykPmo1I". libc.prx is now also listed when libkernel.prx is needed.
PS5 executables need libSceLibcInternal.prx and convert unchanged.