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.
OpenOrbis modules keep read-only data after their code, as executables
do. The syscall scan of sce_module modules decoded it, and the module of
the OpenOrbis library sample (libExample.prx) failed with "Forbidden
syscall instruction". For modules with a PT_SCE_DYNLIBDATA segment, the
scan now stops at the end of the PLT after the last unwind function,
found by FindSceCodeEnd as for executables.
The same modules begin their executable segment with the
/libexec/ld-elf.so.1 string but have no PT_INTERP header, so code
analysis decoded the string and failed with "Unsupported Windows guest
TLS instruction (bytes: 64 2d 65 6c 66 2e)", which is the text "d-elf.".
When no PT_INTERP header covers the start of a PS4 code segment, code
analysis now starts after that exact string.
DecodeInstruction returned before reading the ModRM of FF /3 and FF /5,
so a far CALL or JMP through a RIP-relative pointer had no RIP-relative
displacement. A stub that moved such a JMP copied the displacement
without relocating it.
The decoder was compared with iced-x86 (AMD decoding) over 3.99 million
encodings: every opcode of the legacy, 0F, 0F38 and 0F3A maps under 34
prefix combinations and the VEX 0F, 0F38 and 0F3A maps, each with 48
ModRM forms. Apart from this, it agrees on length, RIP-relative offset,
control flow kind and branch target for every valid Zen 2 instruction
except near branches with a 66 prefix, recorded as technical debt, and
kernel-only returns, which it treats as sequential.
Add the comparison's metadata as cases: one RIP-relative encoding per
opcode, map and immediate size, every FF /r form, and the branch forms
with their flow kind and target. The existing cases check lengths only;
a loop or jrcxz decoded as an unconditional jump passed the whole suite.
A leaf's body ran from its start to the next known start or int3 padding,
so a function reached only through a pointer, placed right after the leaf
without int3 padding, was framed as part of the leaf: its rsp operands were
moved and its exits raised rsp although nothing had lowered it.
The body now also ends at 16-byte-aligned code after a ret or ud2, and at
the target of a jmp when only padding lies between them (a tail call to the
next function). Code past such a point is framed only from a real start.
A leaf whose code continues past it is skipped as branching out of the
function. Aligned code after any other jmp stays in the body: loop headers
are aligned there, and cutting after every jmp skipped 2,129 of 2,131
framed leaves in PPSA01556. With this cut 2,132 are framed, and none of
them has rewritten bytes in another function of the unwind table.
Windows writes an exception frame directly below RSP, so a fault resumed on
a guest thread overwrites values a System V leaf function keeps in its red
zone. With --frame-red-zone, a leaf function with a
push rbp; mov rbp, rsp; push ... prologue that reads or writes below its
pushes gets two kinds of trampoline: the prologue lowers RSP by 128 bytes
after its pushes, and each pop ...; pop rbp; ret exit raises it back first.
Sites a branch enters, sites shorter than a jump and sites over a --to-intel
stub are skipped; the conversion reports the framed and skipped counts.
With --trace-imports the entry stub resolves TraceImport_nid_no_patch
in libc.prx and passes each function import through it before writing
the slot: every PLT slot, and a GOT slot only when the original symbol
is a function, so data imports such as __stdoutp keep their address.
Without the option the output is unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>