Commit Graph
146 Commits
Author SHA1 Message Date
Dean 1748d26517 Merge pull request #10871 from Chronyyx/feat/relinker-frame-short-exits
feat(relinker): frame red-zone leaves whose prologue or exit is shorter than a jump
2026-10-27 10:55:26 +13:00
Dean 613a877ab9 Merge pull request #11232 from MarcZX10/marc/fix-relinker-island-overflow
fix(relinker): place 256-bit store islands across 2^63 without signed overflow
2026-10-27 10:39:59 +13:00
Dean ece00a8bfd Merge pull request #11178 from LuPetro/test/windows-red-zone-execution
test(relinker): execute red-zone framing regressions on Windows
2026-10-27 10:38:45 +13:00
Dean 728266059d Merge pull request #11063 from roboterspinne/fix/relinker-red-zone-pop-rm-xop
fix(relinker): stop reading pop r/m without REX as XOP in red-zone framing
2026-10-27 05:40:19 +13:00
Dean fb5afb3ef7 Merge pull request #8236 from javierUDP/test/rosetta-inplace-execution
test(relinker): execute the in-place Rosetta replacements
2026-10-26 21:59:04 +13:00
Adrià 2b5225605b Merge pull request #9719 from AasishDairelSahayaGrinspan/fix/relinker-gs-non-memory-operands
fix(relinker): leave GS-prefixed LEA and NOP instructions unchanged
2026-10-26 00:15:58 +01:00
MarcZX10 9da4efcced test(relinker): lower a 256-bit store through an island across 2^63 2026-10-25 22:13:34 +00:00
MarcZX10 8ce7f0f657 fix(relinker): encode the short jump to a 256-bit store's island without signed overflow 2026-10-25 22:08:32 +00:00
MarcZX10 98584c547d fix(relinker): seek and return in a 256-bit store's island without signed overflow 2026-10-25 22:08:32 +00:00
MarcZX10 23d20e4405 fix(relinker): measure a 256-bit store's island reach without signed overflow 2026-10-25 22:08:32 +00:00
Dean 1926a56886 Merge pull request #9454 from cocoa55/fix/rewriter-branch-into-cut
fix(codegen): reject branches into a rewritten instruction
2026-10-26 11:00:51 +13:00
Dean Galvin e27a9d3e01 test: restore the function layout of the rosetta in-place execution tests 2026-10-26 10:37:03 +13:00
Dean Galvin 099b865f82 Merge branch 'main' into test/rosetta-inplace-execution 2026-10-26 10:32:38 +13:00
Luka Petrovic 492db378ae test(relinker): execute red-zone framing regressions on Windows 2026-10-25 21:27:13 +01:00
Yunus Shaikh 597f1392a8 test(relinker): cover short-prologue branch refusals, allocations, two-exit leaves, VEX operands and rbp bodies in the red-zone framer 2026-10-25 15:48:43 -04:00
Adrià 6bc1139698 Merge pull request #9184 from javierUDP/test/scalar-compare-execution
test(relinker): execute the moved scalar compare
2026-10-25 20:36:37 +01:00
Dean d7493d7226 Merge pull request #10713 from Filip722/fix/reject-lock-movnts-20261024
fix(relinker): reject LOCK-prefixed MOVNTSS and MOVNTSD
2026-10-26 05:57:30 +13:00
roboterspinne 3398046c3c fix(relinker): stop reading pop r/m without REX as XOP in red-zone framing
extensionBits and memoryUse in the red-zone framer took every 8F lead
byte without REX for an XOP prefix. For pop r/m (8F /0) that read the
ModRM byte as XOP's inverted R, X and B bits, so the base of
"pop qword [rsp-0x10]" looked like R12, and "pop rsp" through 8F was
not seen as a use of RSP. A leaf with a frame pointer that pops to an
RSP operand was framed with the operand left in place, and leaves that
pop into RSP through 8F were framed, while the REX.W forms were refused
as other use of rsp. Both now take 8F for XOP only when the map field
of the next byte is 8 or more, the rule the instruction decoder
applies.
2026-10-25 17:18:47 +01:00
javierUDP 65a98e7f31 test(relinker): execute the moved scalar compare
The stub that moves a hazard scalar compare out of line is checked
through its bytes but never run, so nothing confirms that the compare
still produces the mask it would inline. The x86-64 execution section
now calls the stub for both outcomes of a CMPSS with an unsafe predicate
and checks the destination's lanes.
2026-10-25 11:28:03 +01:00
ChronyyxandClaude Opus 5.5 07798774b6 fix(relinker): frame red-zone leaves whose only push is rbp
The check that refuses leaves without pushes that push or pop also refused
`push rbp; mov rbp, rsp` leaves without other pushes, whose `pop rbp` it
counted, since rbp is not in their list of pushes. A unit test and an
execution test now cover such a leaf with a 4-byte prologue and a 2-byte
exit. The debt entry also notes that the check refuses shrink-wrapped leaves
whose later pushes and pops balance.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-25 03:23:34 -04:00
ChronyyxandClaude Opus 5.5 c697fb0ba9 feat(relinker): frame red-zone leaves whose prologue or exit is shorter than a jump
--frame-red-zone replaces a leaf's prologue and each exit with a 5-byte jump
to a stub, so it skipped leaves whose pushes or pop-and-ret exit were shorter
than that, and leaves with neither pushes nor an allocation. A short prologue
now takes in the straight-line instructions after it and a short exit those
before it, re-encoded in the stub with the moved displacement. Widening stops
at branches, rets, RIP-relative instructions, pushes and pops, and the leaf is
skipped when a branch targets the instructions taken in or the first
instruction of a widened prologue, or when a leaf without pushes pushes or
pops.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-25 02:47:12 -04:00
Dean a512780d58 Merge pull request #10340 from Filip722/submit/lock-20261023
fix(relinker): reject LOCK-prefixed SIMD lowerings
2026-10-25 13:28:53 +13:00
Filip Michalski 50dc1c93c8 fix(relinker): reject LOCK-prefixed MOVNTSS and MOVNTSD 2026-10-24 20:43:16 +02:00
Filip Michalski cfa8c10d21 fix(relinker): clarify LOCK SHA failures and cover conversion 2026-10-24 20:43:14 +02:00
Adrià d01070d6c3 Merge pull request #5640 from EagleBearerYS/feat/relinker-frame-frameless-leaves
feat(relinker): frame red-zone leaves without a frame pointer by moving their rsp operands
2026-10-24 18:14:53 +02:00
cocoa55 dfc705eb41 fix(codegen): reject branches into a rewritten instruction 2026-10-24 13:46:52 +00:00
Adrià b84322da6a Merge pull request #7918 from Kernel-Hunter/fix/rosetta-xsaveopt
fix(relinker): rewrite XSAVEOPT to XSAVE under --to-rosetta
2026-10-24 14:14:59 +02:00
Adrià 3fcacfbc0d Merge pull request #7822 from yash27git/fix/refuse-truncated-branch-rewrites
fix(relinker): guard length-changing rewrites around truncated branches
2026-10-24 14:14:54 +02:00
Dean a011db7048 Merge pull request #8142 from Kernel-Hunter/fix/rosetta-masked-stores
fix(relinker): probe masked store elements before the store under --to-rosetta
2026-10-25 00:56:00 +13:00
Karim Masmoudi ce6c2a0c66 fix(relinker): rewrite XSAVEOPT to XSAVE under --to-rosetta
XSAVEOPT raises an illegal instruction under Rosetta on an M5 running
macOS 27.0.1, as XSAVEC does, while XSAVE and XRSTOR save and restore
x87, SSE and AVX state in the standard format. The PS5's Zen 2 has
XSAVEOPT, so code built for it can use the instruction without a CPUID
check.

XSAVEOPT differs from XSAVE only in the init and modified optimizations:
it may skip writing state that is in its initial configuration or
unchanged since the last XRSTOR from the same area. XRSTOR restores the
same state from the area XSAVE writes, so --to-rosetta rewrites
XSAVEOPT (NP 0F AE /6, memory operand) to XSAVE (/4) in place, keeping
the prefixes and the operand, as RDSEED becomes RDRAND. CLWB (66 0F AE
/6), MFENCE, UMONITOR, UMWAIT and the Intel target are left alone.
XSAVEC is not converted: it writes the compacted format.

The converter tests check the rewrite for the plain, REX.W,
RSP-relative, extended-base, RIP-relative and FS-relative forms, check
that the neighbouring 0F AE encodings are not taken, and execute the
rewritten XSAVEOPT64 followed by XRSTOR on YMM3 in the x86-64 build.
The --to-rosetta integration test now covers XSAVEOPT as well.
2026-10-24 11:01:44 +02:00
Adrià FranchandClaude Sonnet 5.5 e1fea2ebc8 fix(relinker): drop stray blank line and document the moving-branch guard
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-24 10:58:12 +02:00
Yaash Singhh f5fbf2f98d fix(relinker): rebase truncated branch guard onto current main 2026-10-24 10:58:00 +02:00
Filip Michalski 1e00b460b9 fix(relinker): reject LOCK-prefixed SIMD lowerings 2026-10-23 20:16:51 +02:00
Yunus Shaikh cec1f82d76 test(relinker): frame inflate_fast's cmp at rsp before a branch through the instructions before it
red_zone_framer frames two leaves whose 4-byte cmp %rdi,(%rsp) sits
before a jne: after an add (inflate_fast's shape) and after a load below
rsp that heads the loop and moves by the shift inside the stub, and no
operand in a stub is also moved in place. A cmp whose loop branches back
to it stays refused, the widened stub covering the branch target. The
execution test runs both framed leaves under writes below rsp.
2026-10-23 08:36:08 -07:00
Yunus Shaikhandtibzejoker e410c38b42 feat(relinker): extend a short rsp operand's stub over the instructions before it
When the instruction after an rsp operand that cannot move in place is
a branch, a rip-relative instruction or a freeing add, the operand's stub
could stay shorter than a jump and the leaf was skipped. The stub now
also takes the contiguous sequential instructions before the operand,
moving their own rsp operands by the shift; the branch-target, site and
existing-stub checks run on the widened range, and the instructions it
covers leave the in-place moves. zlib's inflate_fast, whose only operand
left is a 4-byte cmp %rdi,(%rsp) between an add and a jne, frames this
way.

The backward extension is tibzejoker's, from their comment on
boykopovar/AnyPS5#5640: with it, 243 of Grand Theft Auto V's 991
red-zone leaves frame (240 before), inflate_fast among them, and one
leaf is left with no room for a stub.

red_zone_framer's leaf whose operand at rsp sits before a rip-relative
load now frames; the one before an exit stays refused, its forward
extension reaching the exit's pops.

Co-authored-by: tibzejoker <[email protected]>
2026-10-23 08:36:06 -07:00
Nciibi 191266170b fix(relinker): move a following in-place replacement into a 256-bit store's stub
A 4-byte 256-bit store at the start of a segment followed by an AMD-only
site that can only be emitted in place (RDSEED, CLWB, MOVNTSS, ...) had no
preceding instruction to move, so WindowPlacer gave up with "No room for a
jump to the stub of 256-bit store within its neighbours or a nearby island".

Try a last-resort window placement that allows EmitOutOfLine to use the
in-place lowering for the follower. Preferred preceding and following moves
are unchanged.
2026-10-23 16:11:00 +01:00
Dean 3b6613eb58 Merge pull request #8597 from javierUDP/test/wide-store-execution
test(relinker): execute the split 256-bit store
2026-10-24 03:28:26 +13:00
Yunus Shaikh a376e37b88 fix(relinker): decode each red-zone leaf over its whole FDE range 2026-10-23 05:22:26 +00:00
Josip Parlov 606fa2ef91 fix(relinker): take red-zone framing's function bounds from .eh_frame FDEs
Function starts were direct call targets plus 16-byte-aligned code after
int3 padding. A function reached only through a pointer and not preceded
by int3 then ended up inside the previous region, and its pop rbp; ret
exit could get the epilogue lea without a matching prologue.

The bounds now come from the executable's .eh_frame FDEs (the existing
EhFrameReader), each function is decoded from its FDE start to its FDE
end, and code outside every FDE is not framed. Conversion stops with an
error when the executable has no FDE. Sites added in one pass also count
as taken for the next function.
2026-10-23 05:19:10 +00:00
Yunus Shaikh 259bb69d65 feat(relinker): move rsp operands that cannot keep their encoding to stubs
A leaf whose rsp operand could not keep its encoding after the shift
was skipped: an 8-bit displacement that overflows, an operand at (%rsp)
without a displacement, or an indexed (%rsp,%idx,s) operand. Such an
instruction now moves to a stub, re-encoded with the moved displacement
in 8 bits when it fits and in 32 bits otherwise, followed by a jump
back.

The site covers the instruction and the sequential instructions after
it until it is 5 bytes long. It is refused when a branch lands inside
it, when it would have to cover a branch, ret or rip-relative
instruction (these are not relocated), or when it overlaps the
prologue, an exit, another operand site or an existing stub. Legacy,
REX and VEX forms are re-encoded; EVEX operands, whose 8-bit
displacement is scaled, are still refused. A leaf with no room for a
site is skipped as "rsp operand that cannot move and has no room for a
stub". Operands inside a site that could move in place are carried by
the stub instead, so the site's bytes stay as they were.

The report counts the operand stubs and the operands they carry.
2026-10-23 04:24:32 +00:00
Yunus Shaikh e0ba27fc60 test(relinker): run red-zone leaves under writes below rsp, framed and unframed
The framer's tests check its sites, stub bytes and moved displacements,
but nothing yet showed that framed code survives a write below rsp.

red_zone_framer_execution frames synthetic leaves with
FrameRedZoneFunctions in a writable and executable buffer, writes the
moved displacements and the stubs as Conversion.cpp and the trampoline
builders do, and calls each leaf with the trap flag set. A SIGTRAP
handler fills [rsp-0x80, rsp) of the interrupted context at every
instruction, as Windows exception dispatch may.

Five leaves each loop over slots they spilled, so a clobbered slot
changes the result: #5640's frameless shape, a leaf reaching 0x28 below
rsp, a leaf that also reads its saved rbx at +0x10 (moved in place), a
leaf that takes the address of a slot below rsp (shifted by the whole red
zone), and the inflate_fast-shaped leaf that allocates with sub after six
pushes. Each must return the right value unframed without the clobber
and a wrong one with it, and the right value framed with it.

The test is built and registered on Linux x86-64 only; elsewhere its main
returns 77.
2026-10-23 04:14:20 +00:00
Yunus Shaikh 132597c6e1 feat(relinker): frame red-zone leaves that allocate with sub after their pushes 2026-10-23 03:37:41 +00:00
Yunus Shaikh 1a262894ad feat(relinker): lower rsp only as far as a frameless red-zone leaf reaches below it 2026-10-23 03:34:55 +00:00
Yunus Shaikh ff16118b63 Merge branch 'main' into feat/relinker-frame-frameless-leaves-next 2026-10-23 03:13:53 +00:00
aasishdairel 8b86ca3984 fix(relinker): leave GS-prefixed LEA and NOP unchanged
LEA computes an effective address and the multi-byte NOP ignores its operand, so neither accesses memory and a GS prefix on them is ignored. The Windows TLS builder and macOS image writer treated every ModRM memory form as an access and replaced these instructions with UD2 and NOP padding.
2026-10-22 21:01:29 +05:30
Kang, Harry Zixuan 4e9854ea2b test(relinker): report SHA-1 preservation failures precisely 2026-10-21 20:29:55 -07:00
Dean 46bfbe4e25 Merge pull request #8063 from AasishDairelSahayaGrinspan/fix/relinker-gs-memory-only-traps
fix(relinker): trap only GS memory accesses in Windows and macOS output
2026-10-21 16:35:25 +13:00
javierUDP 5702e1f938 test(relinker): include algorithm for the converter test std::all_of
The new checks call std::all_of, which the file reached only through
another header.
2026-10-20 18:25:33 -03:00
javierUDP eab9b23a52 test(relinker): execute the split 256-bit store
The stub that splits an unaligned 256-bit store is checked through its
bytes but never run, so nothing confirms that the two halves land where
the original store would, including across a page boundary, which is the
case the split exists for. The x86-64 execution section now runs the
stub at three operand offsets and checks the 32 bytes it writes and the
pages around them.
2026-10-20 14:54:02 -03:00
Dean b9731811ba Merge pull request #8001 from mopga/fix/relinker-amd-only-claimed-followers
fix(relinker): do not take instructions another AMD-only stub moved as a short instruction's followers
2026-10-21 06:30:39 +13:00