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
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.
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>
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.
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.
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>
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.
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>
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>
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>
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.
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>
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>
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)
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>
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>
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>
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
ReadHeader reads fields up to offset 0x40 but only rejected files
shorter than 20 bytes, so a truncated ELF failed with
"FileByteOffset out of bounds" instead of "File too small for ELF header".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5WJS4ZdQynAJesCZKH5hb
Lower INSERTQ xmm1, xmm2 (F2 0F 79) out of line with SSE2 only instead of
failing the --to-intel relink. Length and index are read from bits 69:64
and 77:72 of the source; dst[127:64] is zeroed as on hardware.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only the lines this branch added lose their comments; the commits before
keep them, with the reasoning behind each change, for whoever digs into
that code later.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- --exclude-sce-module <file> (repeatable) drops one title module from
guest conversion, e.g. a title libc.prx, while the rest of sce_module
is still relinked as guest modules
- the instruction scanner stops at zero padding that ends a code segment
with an odd byte instead of failing on a truncated `add [rax], al`
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The project's coding conventions restrict inline comments to
technical-debt markers, #endif, and namespace-close trailers. Remove
the explanatory comment on writeGotStub's DIR64 write; the same
rationale (why the slot must hold the preferred-VA form, and why the
base-relocation pass was moved after lazy-stub writes) is already
captured in the fix(relinker) commit message and PR description.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The Windows PE lazy-import GOT slot was written as a bare 4-byte RVA,
but the .reloc entries generated for the .gtls/data sections use
IMAGE_REL_BASED_DIR64, which only adds the load delta to whatever is
already stored at the slot. A bare RVA therefore resolved at runtime
to (loadBase - ImageBase + rva) instead of the intended stub address,
pointing into unmapped memory whenever the module was not loaded at
its preferred base.
Write the full 8-byte preferred-VA form (ImageBase + stubRva) instead,
matching DIR64 semantics, and generate a base relocation entry for
each lazy-stub GOT slot. The .reloc section is now built after the
lazy stubs are written into their target sections rather than before,
since it previously captured only the relocations known at that
point and missed the newly patched slots entirely.
Resolve conflicts with boykopovar/AnyPS5#10 (sce module loading) and
later upstream changes.
- Pthread Cond/Mutex: take upstream. It already releases mutex
ownership across condition waits, returns ETIMEDOUT 0x8002003C, and
rejects unlocks from non-owners for every mutex type.
- Pthread Thread: keep upstream's lifecycle, Windows adoption and job
affinity; re-apply the POSIX fixes on top: reference-counted
release on join/detach/exit, self-join rejection on both platforms,
and scePthreadSelf adoption for threads not created by
scePthreadCreate.
- Semaphore: keep upstream's TimedWait::Condition and wait tracing;
re-apply sceKernelDeleteSema waking waiters with EACCES.
- Relinker: keep --windows-gui alongside upstream's --skip-sce-module,
trampoline-aware Patch signature and new Python tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018emyZ4Rk8yoSJvxTFBe4DL
feat(relinker): lower register-form EXTRQ and relocate following instructions for short sites
- **Lowering:** register-form EXTRQ is emitted out of line with SSE2 only. Two scratch XMM registers (distinct from both operands) are spilled; one gets the index (`src >> 8 & 63`), the other `(64 - length) & 63` (`length` masked to 6 bits, so 0 means 64). The destination is shifted right by the index, then left and right by the complement, which keeps the low `length` bits. INSERTQ register form is still reported as unsupported.
- **Short sites:** when a site is shorter than a `jmp rel32`, the converter extends it over the instructions that follow it until it's 5 bytes long:
- following EXTRQ/INSERTQ instructions (before any other instruction) are lowered into the same stub, via a new `MatchSequence`;
- other instructions are copied after the lowered body (`LowerOutOfLine`'s new `trailing` bytes), but only if they're sequential, have no RIP-relative operand and no branch target;
- anything else (a return, a branch, RIP-relative code, the segment ending) still fails the relink, as does a branch target inside the extended site.
- `docs/TechnicalDebt.md` now lists only the INSERTQ register form, MONITORX/MWAITX/CLZERO/RDPRU/MCOMMIT and SHA-NI.
GuestModuleWriter::WriteLinux adds read permission to every PT_LOAD, including
the SCE dynlib data segment, which has no flags and is never mapped on the PS5.
When that segment starts inside the last page of the preceding RW segment, the
read-only mapping covers the tail of its bss and the module's initializer faults
on its first write there. The symbol and relocation tables it holds are already
copied into the appended segment, so it no longer needs to be mapped.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ButHENCbXXt8h4EZuWUabJ
Brings in main up to 9958d26 (homebrew runtime, POSIX wrappers in
libkernel, sce_module guest modules and --skip-sce-module, the --to-intel
general pipeline, libSceRandom, libSceRtc, libScePosixForWebKit,
libRenoirCore, test wiring).
Resolutions where both sides implemented the same thing:
- libkernel pthread: main's structure (lazy static initialisation,
destroyed markers, MutexOperations/CondOperations, mutex protocol and
condattr clock) with this branch's TimedWait deadlines and condition
(precise timed lock/wait on Windows), wait tracing, the adaptive mutex
type 4, POSIX default ERRORCHECK mutexes, EDEADLK/EPERM results instead
of throws, and condition clocks honoured for absolute timeouts.
- libc locale: the guest Dinkumware ABI (_Locimp, facet vtables, short
ctype/case tables, locale ids, stream storage) is kept because guest code
inlines those layouts; main's C character functions are kept on top.
- libc stdio/strings/math: main's stream functions (guest FILE status
sync), strerror/perror, remove, formatting, string and math exports win
over this branch's duplicates.
- libc paths: main's working directory with this branch's path aliases
resolved on the normalised guest path.
- libkernel file: this branch's sceKernel* results as SCE errors, main's
POSIX wrappers and both fstat helpers.
- relinker: this branch's stub lowering keeps its trampolines through the
main executable path; guest modules refuse lowerings that need stubs.
- AGC driver: main's rendering wait is executed by this branch's workers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177gthrWEBBfBu5xJDXgBfo
Generated executables always used the console subsystem and opened a
console window on launch. The opt-in flag writes IMAGE_SUBSYSTEM_WINDOWS_GUI.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
std::system returns a wait status on POSIX, so returning it from main
truncated a guest exit code of 1 (status 256) to 0. The prompt also
required pressing Enter twice because cin.get followed cin.ignore.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
EXTRQ, INSERTQ, MOVNTSS and MOVNTSD are rewritten at relink time: same-length in-place forms
where they exist, otherwise a jmp rel32 into an appended executable section holding a
scratch-free SSE body and a jump back, so no code shifts and no RIP-relative or jump-table
operand needs adjusting. Decoder fixes for POPCNT and 0F BB (BTC), codegen and elfpatcher
tests, README and technical-debt notes. The runtime illegal-instruction trap stays as a
fallback.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177gthrWEBBfBu5xJDXgBfo