The Linux guest module writer emitted DT_HASH with a single bucket, so
every symbol lookup that reached a guest module walked its whole symbol
table. Guest modules come first in the global scope, so each host prx
lookup paid that walk at load and on every lazy PLT call. Build the
standard SysV table from the final symbol names with one bucket per
symbol, as the console's modules ship it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each in-place substitution went through X64InstructionRewriter::Rewrite,
which copies the whole code segment and decodes every instruction in it
to adjust references. The converter only accepts equal-length
replacements, so the adjustment is always zero and the copy and scan are
wasted: conversion time grew by one segment scan per substitution. Copy
the replacement bytes into the segment instead.
PUSH GS, POP GS and RSM (0F A8, 0F A9, 0F AA) have no ModRM byte, but fell in the 0F A3-AB ModRM range, so the decoder read the next byte as a ModRM and sized its displacement. MOV to and from control and debug registers (0F 20-23) ignore the ModRM mod field and always name a register, but were sized and reported as memory forms, including a RIP-relative disp32 for mod 00 r/m 101.
ElfReader::ReadHeader names 0x1D3D154F as a SELF container, but dumps also
carry a second container magic, 0xEEF51454 (bytes 54 14 f5 ee), which fell
through to "Invalid ELF magic number: 54 14 f5 ee". That message reads as a
corrupt input rather than an encrypted one, so a user cannot tell that
decryption is what stands between them and a relink.
Both magics now report the SELF message. Measured on two retail dumps:
PPSA03671's eboot.bin and libcohtml.Prospero.prx are 54 14 f5 ee; PPSA28997's
eboot and modules are 4f 15 3d 1d.
Tested: Linux (CachyOS, GCC, Release, BUILD_TESTING=ON), relinker rebuilt. The
new case fails on the revert of the reader change with the exact message above
and passes with it; elf_header, elf_offsets, dynamic_segment,
string_table_bounds, guest_needed_modules, guest_module_directories and
linux_load_alignment still pass.
The decoder adds the TEST immediate of opcodes F6 and F7 when ModRM.reg
is 0 or 1. The check excluded VEX and two-byte opcodes but not EVEX, so
an EVEX.0F F6 (VPSADBW) whose ModRM.reg is 0 or 1 was given an extra
imm8 and decoded one byte too long. VPSADBW has no immediate (Intel SDM
Vol. 2B, VPSADBW).
ReadProgramHeaders stepped through the table by e_phentsize without
checking it. With 0 it read the first header e_phnum times, and with
any other size it read misaligned fields, so a damaged header failed
much later as "No PT_DYNAMIC segment found", after building up to
65535 copies of one segment. A non-empty table must now use 56-byte
entries, as the guest module reader already requires.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WindowsLoadImage sized its data and page table from the end of the last
segment and checked for overlap while copying. A segment whose memory
size reached past the last segment's end wrote page flags past the
table before its overlap was found; the relinker crashed with SIGSEGV
on such an input. Overlap is now checked in a pass before sizing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tls_function_coverage ran its execution cases only on Windows; on
Linux it checked bytes. windows_image_runner.py maps the relinked PE,
points GS at a TEB whose TLS slot holds a block copied from the
template, runs the TLS callbacks for process attach with the Windows
argument registers and calls the guest entry. convert() uses it on
Linux x86-64 and expects 42, as on Windows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mov r64 and mov r32 from fs:[mem], where mem has a base or an index
register, now relink for Windows (64 48 8b 03, mov rax, fs:[rbx], in
#217). The stub computes the offset with lea on the original address,
adds the thread pointer and loads.
These sites can be shorter than the 5-byte jump. A short site moves the
following sequential instructions into its stub and relocates
RIP-relative ones; a branch, a return or another TLS access after it
fails the relink. A TLS site shorter than a jump is now rejected instead
of overwriting the next instruction.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A0-A3 (MOV AL/eAX/rAX to and from moffs) have no ModRM byte and carry an
8-byte absolute address in 64-bit mode, or 4 bytes with a 0x67 prefix.
The decoder added nothing for the address, so the linear scanner decoded
the address bytes as code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Linux patcher appends the DT_RUNPATH string after the copied
.dynstr data but kept DT_STRSZ and the .dynstr section size at the size
of the copied data, so DT_RUNPATH pointed at the first byte past the
declared string table.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In 64-bit mode REX.W takes precedence over the 0x66 operand-size prefix,
so the immediate of 81, C7, 69, 68, A9, the 05..3D accumulator forms and
the F7 TEST form stays imm32. The decoder only applied this to B8+r and
returned a length two bytes short for the others.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add header-only support for end-to-end relinker tests: a minimal PS5
executable builder, a PT_LOAD mapper that applies segment permissions,
and a posix_spawn runner for the relinker binary. Every
core/relinker/tests/e2e/*.cpp becomes its own test, so new tests do not
touch CMake.
The first test relinks an executable with EXTRQ and INSERTQ sites using
--to-intel, maps the output and calls the patched code, which runs both
stubs from their Linux segment and returns through their branches.
Co-Authored-By: Claude <noreply@anthropic.com>
The Windows TLS rewriter only patches instructions that
CodeInstructionCollector finds: symbol and unwind function ranges, plus
the code their branches, relocated pointers and initializers reach. A
fixed-address executable (ET_SCE_EXEC) has no relocations for function
pointers held in data, such as vtables or a thread entry loaded with
LEA, and its unwind tables need not cover every function. TLS accesses
in that code stayed `mov reg, fs:[0]`, which reads address 0 on Windows.
PPSA16064 (Minecraft Dungeons II, Unreal Engine) has 3689 such loads in
its executable segment; 478 were left unpatched, and its worker threads
faulted on their first TLS access at start-up.
The collector now also sweeps each executable segment linearly from the
first address that int3 padding precedes on a 16-byte boundary, and adds
every FS-prefixed instruction with an absolute disp32 operand as a root.
The sweep resynchronizes at the next padded 16-byte boundary when an
instruction does not decode, crosses such a boundary or starts with two
zero bytes, so zero-filled or undecodable bytes are not read as code.
The rewriter's own checks still decide which forms it can patch.
With this, all 3689 loads in PPSA16064 are patched. New cases in
tls_function_coverage: a padded function nothing references is patched,
and the same load behind zero fill after padding is left unchanged.
A qualified import whose module name matches no DT_NEEDED entry now uses
the unqualified search order instead of failing with "Import module is
not a dependency". Restore the Linux rejection of duplicate exports
within one guest module.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The relinker redefined 61 ELF constants 93 times across seven files,
38 of them already in elfpatcher's ElfConstants.hpp. Add the missing
dynamic tags, relocation types and symbol constants to that header and
remove the local copies; every value is unchanged. strict_nid_filter_tests
compiles these sources and now gets the elfpatcher include directory.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MBXJqzgohLeVAeHsn4Jpm7
The cmp-equal execution case computed ZF from the untouched destination operand instead of the comparison result, so the self-check took the failure path and the Windows CI job failed with exit code 8. Derive ZF from the real subtraction for sub/cmp and from the real sum for add.
loadPointer overwrites rax and rcx, so a destination of rax or rcx
computed the operation against the thread pointer or the TLS index
instead of the original register. Restore the destination before the
operation for both, and include rcx in the byte-level cases, which
previously covered only rax, rbx, r8 and r13.
Add self-checking execution cases for rax, rcx and rbx destinations:
each verifies the result, the preservation of the other two registers
and the ZF and CF bits.
On Linux every module resolves its references through one global scope,
and the guest modules are listed ahead of the host prx. When a title
bundles its own libc.prx, the host libc and the other host prx bound
their references to their own NIDs to the guest libc, so the host libc
built its standard streams on top of the guest _Stdout.
The exports of guest modules, and the guest and executable imports that
a guest module satisfies, now carry a #guest suffix. The title's imports
still reach the guest modules first, and a host prx only sees host
definitions, as on Windows. sceKernelDlsym also looks for the suffixed
name.
Recognize the read-direction ALU forms (add, or, adc, sbb, and, sub, xor,
cmp) that read a qword from fs:[disp32], such as the stack canary check
`xor rbx, fs:[0x28]` emitted by stack-protected functions. They were
rejected as unsupported, which blocked titles relinked on Windows.
The stub loads the TLS value and applies the operation in place, keeping
the final operation as the last flag-setting instruction and preserving
the accumulator and counter. A destination of rax uses rdx as the value
scratch so the original accumulator survives.
Refs #217
Guest modules a title opens with sceKernelLoadStartModule are looked up
through their own handle, so several modules may export the same name:
every Wwise plugin exports g_pAKPluginList. On Linux a duplicate now
fails only when the executable or a guest module imports it, as the
provider would be ambiguous. Windows keeps rejecting duplicates.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Unity titles keep modules the executable needs in Media/Modules
(PPSA12544: Il2cppUserAssemblies.prx and PS5Util.prx), outside sce_module
and prx, so they were left as host libraries and startup failed to load
them. A DT_NEEDED module of the executable that is not in a module
directory is now searched for under the input directory and relinked like
the others; two ELF files with the name are ambiguous. Guest outputs keep
their directory relative to the input instead of only its last component.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Guest modules may define symbols in SHN_ABS (0xfff1), for example the
151 library marker symbols of PPSA12544's Il2cppUserAssemblies.prx.
The reader no longer rejects them or checks their value as a mapped
address, relocations against them write the absolute value without a
base relocation, and they are not exported: a PE export is an RVA, so an
import of one fails at startup as an unresolved import. Other special
sections are still rejected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The PE writer marked relocatable executables HIGH_ENTROPY_VA. Windows
then places the main thread stack, TEB, PEB and the loader threads'
stacks, which exist before libc reserves the guest arena from
0x2_0000_0000, anywhere in the first TiB. Nearly half the time they
land inside the arena and stay there as holes that fixed guest
mappings cannot use.
DllCharacteristics is now 0x140 (DYNAMIC_BASE, NX_COMPAT). The image
is still relocated and bottom-up allocations keep the 8-bit
randomization, but they start near the bottom of the address space,
far below the arena. DLL placement does not depend on this flag.
windows_address_space checks the flags and, on Windows, runs a
relinked guest that returns the high dword of its stack pointer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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