ReadProgramHeaders now rejects a PT_LOAD whose file range exceeds the
input, so TranslateVirtualAddress no longer needs its own check. An
uncovered PT_SCE_RELRO outside the file reports the same "Segment offset
out of bounds" as a PT_LOAD, and a test pins that a load or uncovered
PT_SCE_RELRO ending exactly at the end of the file is still accepted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P9JfvrCsztJtbkd5SATAzL
ElfReader::ReadProgramHeaders checked that a PT_LOAD's file size does
not exceed its memory size, but not that its file range lies inside the
input. The Windows and macOS writers reject such a segment later; the
Linux patcher keeps the original PT_LOADs and appends its own block at
the old end of the file, so a load reaching past the input mapped the
relinker's .dynstr, .dynsym and entry stub into the guest segment.
A PT_SCE_RELRO that no PT_LOAD covers is turned into a PT_LOAD after
the per-header checks, so it skipped them: a file size larger than its
memory size was accepted for Linux, and Windows and macOS reported a
file-range error instead.
Reject a PT_LOAD whose file range exceeds the input, and run the same
two checks on an uncovered PT_SCE_RELRO before converting it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P9JfvrCsztJtbkd5SATAzL
The Windows startup stub stopped at the first import it could not
resolve, so a title missing several functions showed one
"FAIL: unresolved ELF import" line per run and needed a run per
missing function to list them all.
The stub now counts unresolved imports, writes the FAIL line of each
and goes on with the next import. After the last import it fails once,
with the searched library list and the last Windows error as before,
and exits with STATUS_ENTRYPOINT_NOT_FOUND. Weak imports, lazy binding
and a startup where every import resolves are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV
relinker_executables converts the picker case with --macos too and
requires both Mach-O outputs to be executable. It fails when the
permission change is limited to Linux outputs.
The executables were written with the default file mode (0644 under a
022 umask). The documented chmod covered app.elf, but not the outputs of
--executable, so RetroArch (PPSA99169) converted with
--executable picker/picker.bin --executable es-de/es-de.bin stopped as
soon as it started its picker:
sceSystemServiceLoadExec: execv failed: Permission denied
Every executable output now gets execute permission for owner, group and
others after it is written, as --autorun already did for app.elf. With
it RetroArch starts the picker, and the picker starts ES-DE.
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.
Add a unit test for the layout the relinker prints after a conversion:
the fixed header and library entries, extra executables in order, and
module paths merged into one sorted tree with directories marked and
duplicates collapsed.