Standalone eh_frame_hdr row after the existing table overflow case:
encoding 0x31 (uleb128 | datarel) with a ten-byte ULEB128 whose last
byte overflows 64 bits, on segments at 0xFFFFFFFFFFFFF000. The reader
must throw "overflowing LEB128 value" from EhFrameReader::leb.
Co-Authored-By: Claude Haiku 5.5 <[email protected]>
The set-based control-flow walk inserted the addresses it found into the
caller's set from a destructor. Destructors are noexcept, so a failed
allocation there called std::terminate instead of reaching the
conversion's error handling as std::bad_alloc. The addresses are now
inserted after the walk returns, and before an exception from the walk
is rethrown, as the walk did before.
The new test arms a failing operator new from the progress callback
during a walk of 8,192 instructions and requires std::bad_alloc to reach
the caller; with the destructor it terminated.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The control-flow walk kept the reachable instructions of a code segment
in a std::unordered_set: every decoded instruction looked itself up,
was inserted, and was inserted into and erased from a visited set. The
instruction collector also handed the same growing set to each pass of
its root loop and walked all of it again every time.
The walk now marks each text offset in a byte map: reached, queued or
not seen. Queued marks are cleared when a walk ends, as the per-call
visited set was. ExtendControlFlowGraph takes a caller's map and
returns only the addresses that one walk added, in address order. The
collector keeps one map per segment and handles just those addresses.
BuildControlFlowGraph with a reachable set fills the map from the set
and inserts the new addresses in the order they were found.
Converting Goat Simulator 3's (PPSA04158) eboot.elf with
relinker --windows takes 27.56 s instead of 45.26 s (median of six
alternating runs on a Ryzen 9 5950X). Four more titles' eboot.elf
convert 35-54% faster, and every run produces the same output files.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Standalone EH table fixture after the unwind record loop: CIE zPLR with
LSDA encoding 0x4B (sdata4 funcrel), FDE augmentation 0xFFFFEFFF with
functionBase at the FDE begin 0x1000. The signed branch of
EhFrameReader::encoded must reject it with "unwind pointer address
overflowed".
Co-Authored-By: Claude Haiku 5.5 <[email protected]>
Standalone block after the main table overflow case: encoding 0x33
(udata4 + datarel), u32 0xFFFFFFFF displacements at the real table
entry offsets 16 and 20, data base 0xFFFFFFFFFFFFF000 so max - base
is 0xFFF, and an assert on the overflow RelinkerException message.
Co-Authored-By: Claude Haiku 5.5 <[email protected]>
An island or relocation window could start in the int3 or nop padding after a jump or return and run into the next function. A function that only a pointer reaches, such as a virtual method, is not a known branch target, so its first bytes became the middle of a jump to a stub. An instruction that follows a jump, a return, a trap or padding after one of them can now only start a window. An int3 inside a window does not count as such an instruction, so a window that lies entirely in int3 padding is still used, as before; nop padding stays excluded, since a function built with -fpatchable-function-entry can begin with a nop.