scripts/mutants_d68.py applies each mutant, runs the tests that should kill it, restores the file,
and exits 1 where a mutant lives that should die or dies that should live. 22 die: the lane pick
unfiltered (with the check of the facts, and with it skipped), the check unwired, the guard unwired,
never told its block, letting a name through or naming the wrong cause, each of the validator's
eight fact families dropped, defined_before off by one, with its phi case or its entry case wrong or
always true, and the production write order changed three ways. Three live, each with its reason:
the guard with r2ssa's pick and check both gone (by design, the guard holds alone), strictly_dominates
for dominates in defined_before (equivalent: the same block returns above), and the block-end read
placed at the block's start in the validator (no fact the fixtures hold reads a value its own block
defines). 25 edits apply once each (--check).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
fingerprint() returned None for a refused record, so a function refused the same way under two
orders was equal whatever its ledger or refusal reason did. It now carries the refusal (kind and
reason, which counts the obligations and names the first unaccounted one) and the ledger, and
verdict_moves compares them; a function refused on one side only is a move in either direction
(only "refused only under the mutant" counted before). Guards to tip moved 7 refused functions
toward residual (ls-toybox 0x5323c rendered 807 to 806, residual 214 to 216; libc-2.31 0xcd010,
0xcd190, 0xcd320; libc-2.32 0xcd540, 0xcd6c0, 0xcd850), which the gate could not see.
Tests: scripts/test_census.py (17 tests).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
`census.py orders` runs `pddj` over every function of the given binaries with the natural order and
the three `placement_mutants` orders (`R2SLEIGH_WRITE_ORDER` = o1, o2, o3) and compares per
function: the ledger's columns, the names read as never assigned (from the proof line), the residual
sites by instruction address, cause, gap kind, callee and reason, the declared variables and links,
and the set of statement lines apart from control (labels, jumps, braces) and site numbers. It
fails when a verdict differs from natural's, or when an order changes the text of fewer functions
than `--min-live` (a knob that moves nothing proves nothing), and reports a function refused on one
side only. A difference that only the selection pass explains (`if (c) x = a; else x = b;` against
`x = c ? a : b;`, a structure stage that reads the layout) is counted apart, not as a move.
Measured over bash, ls-toybox and pigz (2,508 functions) with the mutant build of each commit of the
branch: the mutants alone (base behaviour) 0 differ under O1, 120 under O2 and 187 under O3 (the
skeptic's 119 and 183 by ledger alone); the spelling pass 0, 0, 0; the forward-edge rule 0, 0, 0. Text
changes in 64 functions under O1 (an exit is inlined only in a loop that has one), 338 under O2 and
722 under O3. Python tests for the fingerprint, the selection rule and the refusal parity.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
scripts/structure-report.sh over the stack exits 0: no count rose, too_many_arguments stays
72, too_many_lines 248, cognitive_complexity 18, excessive_nesting 457, and only
long_comments fell. comment_runs.py against the base finds no added run.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
The reload table, the table binding and the stub guard add no function over the line limit (pointer_table's table construction moved to `captured`), and the touched doc comments shrank.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
Q's formed-address rule now names hello-aae's `adrp x0, 0x100000000`. r2s's prefix is already a function of the stated kind (r2image types a code-section symbol outside executable code Other, which names a place, `loc.`). radare2 6.2.4 types every non-import Mach-O symbol FUNC; b5df79912a (after 6.2.4) types by section and a symbol in none is NOTYPE, so radare2 6.2.5 prints `loc.__mh_execute_header` too and the row matches nothing against it.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
radare2 runs each symbol and relocation flag through r_name_filter (`$` and `-` become `_`, other invalid characters go), so its `f` and `pd` spell `obj.__OBJC_METACLASS_RO___Test` where r2s spelled `obj.__OBJC_METACLASS_RO_$_Test`. `Name::spelled` and the spelling the table seeks by now share the one filter the string names already used; the text stays the container's, which prototypes are keyed by. The Swift judgment matches the filtered `$s` (`_s`).
diff_r2 on mach0 against radare2 6.2.5: pd 78 agree to 80, the two hello-macos-arm64-chained rows agree; elf and pe unmoved apart from pe pd 179 to 184.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
A `pairs` mode compares the posture keys both tools print; a key radare2
prints and r2s does not is counted with the unfilled values (elf 33, mach0
489, pe 822 on CI's TESTBINS_REF, which the baseline now holds). The one
disagreement class is `static`: radare2 prints it for any image with an empty
DT_NEEDED, a shared object included; r2s says it of an image with neither
PT_INTERP nor PT_DYNAMIC (38 images on the local corpus, judged r2s-right).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
long_comments counts the total, so a run lengthened by one line moved nothing and review bots
flagged four such threads. comment_runs.py reads the change: a head run fails when it is longer
than every base run it keeps a line of, so an edit inside a long run or a shortening passes.
The structure report counts through the same script, so the definition has one owner.
Passes on review-threads against ls-atomic-rmw; 2580 runs by the script and by the old awk.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
918acd744d's SYSCALL writes RCX (the next RIP) and R11 (RFLAGS), as the SDM
states (Vol. 2B, SYSCALL): radare2's ESIL writes neither, so seven x86-64
binaries' `aoj` disagree, judged radare2's defect; Sleigh reads its `rflags`
register without packing the flags into it, noted as a Ghidra issue owed.
GP-7018 (f9ed6f4170) moves the selector into the 32-bit register for 8c /r
at 32-bit operand size, so the pd and aoj judgments of the 16-bit spelling
matched nothing. Under radare2 6.2.4 over CI's elf, mach0 and pe testbins:
no unjudged disagreement, no stale judgment, every agree count as the base's.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
The rise is 9e41ddac's, and radare2's defect. corkami's ownexports.exe and
ownexportsdot.exe state an export address table [0x1008, 0, 0]. radare2 lists
slots 1 and 2 as Ordinal_1 and Ordinal_2 at vaddr = ImageBase (0x400000) and
paddr 0, the DOS header; r2s states no export for a null slot. Each file adds
two values (paddr 0x0, vaddr 0x400000), so +4. refused 37 and the other pe
counts are unchanged; elf and mach0 hold no PE export.
r2s is right: the format defines an entry as an address in code or data, or a
forwarder inside the export section, and 0 is neither. Wine's loader returns
NULL for a zero slot (find_ordinal_export), and lld zero-fills the table and
asserts no export it writes has RVA 0 (AddressTableChunk::writeTo), so a 0 is
a gap. Rizin 0.9.1 and radare2 6.2.5 list the same rows, and master's pe.c
has no zero check.
No judgment entry: diff_r2_judged.toml judges values r2s reports and radare2
does not, and this is the other direction, which the baseline ratchets as "a
floor, not a to-do list" (62bfe6de blessed the same kind of rise). The
upstream text is in the scratchpad for the radare2 steward.
Measured on the pe corpus at TESTBINS_REF 3bc992e against radare2 6.2.4:
r2s without the filter: `is` radare2-only 26113; with it 26117; ie 309, iS 385,
unjudged 0;
the gate exits 0 after the bless.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
The LS row records the item: AArch64's bracket in the vendored
specification, the lift's typing of it and of x86's LOCK, eval past LOCK
(the x86-64 oracle's two LOCK cases agree with the CPU), the C11 helpers
r2dec renders, `IndivisibleAccess` for what stays untyped, the oracle,
the gold source and both equivalence targets, where loop_carried.c's
exchange_after_store now passes on x86-64 with #287 below it. Its
follow-ups: the fork pull request for `AtomicBegin`/`AtomicEnd` (next),
`casp` of X registers and x86 `lock adc/sbb/not/neg` typed
(conditional), an atomic maximum, minimum and 16-byte compare-and-exchange
rendered (deferred: no GCC builtin). The order section's step 6 marks the
typed atomic RMW done.
scripts/structure-report.sh exits 0; only excessive_nesting moved, and it
fell.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
scripts/spec_evidence.py reads git diff base...HEAD: a changed .sinc, .slaspec or .pspec under
vendor/sleigh-config needs a change under crates/r2sleigh-lift/tests/ or the lift's tests.rs.
Replayed over the 14 commits that changed a vendored specification, it refuses none.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
The radare2 steward's check on radare2 master: the halt at unmapped
memory is radare2's defect, fixed by radare2#26987 (open), while the
fold of a call to a get_pc_thunk into `reg = return address` is right in
its net effect across the call and return, so it is its own entry,
deliberate (32-bit x86, a direct call; all 44 in CI's testbins are
thunk calls, radare2's W naming the thunk's register alone). The
still-standing r2s-right entries name the upstream change that flips
them when it merges and the pin moves: MOVS's flags radare2#26988,
LDR [pc, rN] radare2#26984, RET Xn radare2#26991, the failing-condition
skip issue radare2#26992.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
The listing spelled `ldmia sp!, {pc}` as `pop {pc}`, which aoj's judgment
called r2s-right; the radare2 steward's check says otherwise: the Arm
ARM makes POP (LDM) and PUSH (STMDB) the preferred disassembly only when
BitCount(register_list) > 1, GNU binutils prints `ldmfd sp!, {pc}`, and
radare2 prints `ldm sp!, {pc}` and `pop {r0, pc}`. arm_alias now aliases
a list of two or more registers, so one register reads `ldm sp!, {pc}`
or `stmdb sp!, {lr}`; the single-register LDR and STR forms stay POP and
PUSH. The judgment matches nothing now and is deleted; the aoj and pd
differentials over CI's testbins are otherwise unmoved.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
Over CI's testbins (elf, mach0 and pe) against radare2 6.2.4, aoj with
the x86 AF writes and the ARM and AArch64 scratch registers fixed (VC,
M, AO): the AF entry and the scratch-register entry match nothing and
are deleted, and binaries agreeing in full rise from 5 to 17. Of the
C and V reads that fed the scratch, four remain, all flag-setting: C
under Sleigh's `count == 0` select with a constant count (lsls, asrs,
an ANDS immediate's rotation carry) and V written back unchanged by
ANDS; the entry now names those and the lift's instruction-local fold
as owner.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
Over CI's testbins (3bc992e: elf, mach0, pe) against radare2 6.2.4 (CI's
pin, built here) and 6.2.5, each alike: 11,820 instructions
sampled, 10,935 disagreements, every one judged by 66 entries. The
large ones: radare2 writes the program counter on a transfer and reads
it for a pc-relative operand, which Sleigh folds to a constant
(deliberate, 2,689); Sleigh's ARM and AArch64 scratch registers
(r2-right, 2,771 with the flag reads they cause; owners VC, M, AO);
x86 AF, which Ghidra's spec leaves unwritten (r2-right, 1,100; VC);
radare2's aea stopping at unmapped memory (r2s-right, 594, upstream);
x29/x30 by radare2's fp/lr aliases (deliberate, 1,200); a shift by an
immediate reading the flags under Sleigh's count==0 select (r2-right,
265; the lift). Instruction-level classes reuse pd's reasons where the
cause is the same (fat files, PE mappings, W5 objects, BE8).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
A registers mode: the entry points ie states and four FUNC symbols is
states (not a library's import slot), evenly spaced, 8 instructions
from each, no discovery run. radare2's side is aoj 1 with aeaj 1's I
(read before written, as r2s's reads are) and W, after aei; aeim, each
pair fenced. One disagreement per register a side lacks; a size, bytes,
mnemonic, refusal or ESIL that never reached the IL's memory is one at
the instruction and compares no set. Judgments take fields, registers
or register, lacking, arch, refused, ours and theirs, with {register}
and {suffix} in a pattern. test_diff_r2.py covers each.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
The unfilled encoder entry sat after the broad adrp page-base entry, so
aarch64-got-etrel.o's and -addend.o's unwritten ADR_GOT_PAGE fields were
still judged deliberate; it now leads the table, and all nine arm-*.o and
aarch64-*.o objects' unwritten fields are unfilled (five of them
claimed before by W5's or the page-base entry; ROADMAP I says nine). The
data words entry matched `ffffffff invalid` fill rows too; ` ff +invalid`
takes only the one-byte lines radare2 lists past a data word, so the
fill rows go back to the fill class.
diff_r2.py on elf, mach0 and pe: no unjudged line, no judgment
unmatched, no count moved.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
Every rise is the new files', summed per binary from the verbose run:
elf refused 81 to 96: the 15 new top-level text files (13 .s, mkmemimg.py,
x86_64-tailrela.lds), each "Unknown file magic"; every new ELF opens.
elf iS 249 to 287 (+38): radare2's SHT_NULL row, vsize 0 (8 binaries);
an object's NULL row at the base (16 values on 6 objects); a size-0 .bss
radare2 puts at the file's end, r2s leaves unplaced (arm-thumb-bit.o
0x37c, arm-thumb-movw-be.o 0x334, x86_64-common-code.o 0x448: 6);
abs_func_mapped's NULL row (3); x86_64-abs-sym-noshdr.so's .rel.plt that
radare2 makes from DT_RELA with no section headers (5).
elf ie 53 to 73 (+20): five shared objects stating e_entry 0
(aarch64-relr-linkbase, arm-plt-lld-be8, i386-aps2-rel, libhermes,
x86_64-abs-sym), where radare2 guesses .text's start, 4 values each.
elf is 9991 to 10020 (+29): SHN_ABS and common symbols radare2 lists at
an object's base (12 on 6 objects); STT_SECTION rows r2s does not list
(arm-thumb-bit.o, arm-thumb-movw-be.o: 8); SHN_ABS values radare2 prints
as paddr (x86_64-abs-sym.so: 2); x86_64-abs-sym-noshdr.so's dynamic
symbols, reachable only through DT_SYMTAB (ROADMAP I's M6: 7);
abs_func_mapped's absfn is now placed, so its paddr is not counted.
mach0 is 102042 to 102046 (+4): anal-globals' N_OSO stab, whose mtime
0x6acd0331 radare2 lists as an address (2), and anal-globals-stripped's
func.100000460 from LC_FUNCTION_STARTS (2), the existing class.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
6.2.4 already reads the 24-byte .plt stride the old reason blamed. An
import whose address is also taken lost its PLT address because radare2
parsed the Android packed relocations first; radare2#26982 (c282ff9d42,
merged after 6.2.4) fixes it, so the entry flips when the pin moves.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
Each against the fixture's assembler source, readelf/objdump and radare2
6.2.4 and master:
- SHN_ABS symbols in an object (absval, abshit, romfn) and common
symbols (cbuf, lbuf): r2s-right; radare2, master too, lists each at
the object's base, paddr 0, its value dropped.
- ARM and AArch64 objects' instruction fields (THM_CALL, JUMP19/24,
ADR_GOT_PAGE, LD_PREL_LO19): unfilled, ROADMAP I's encoders. r2image
types them and nothing writes them, so r2s decodes the file's field
(bl to itself, beq.w by its addend, adrp to its own page). W5's
deliberate entry claimed these as written fields on arm-thumb-movw.o,
arm-thumb-movw-addend.o and aarch64-got-etrel*.o; it now covers x86
objects alone (plus `mov r, qword [` for the common symbols' loads),
and the W26 bne.w entry folds into the new one.
- a data section's relocated words: unfilled (P11's listing); radare2
lists them .qword/.dword, r2s decodes them as code.
- the section-less e_entry-0 object: r2s-right (radare2 dumps its fill
at an unmapped 0); x86_64-abs-sym.so joins the e_entry-0 entries.
- tailrela's [ext_obj]: r2s-right, radare2's reloc.ext_obj flag sits on
the ext_obj+8 word; its loc.imp. stub joins libcurl's entry.
- `rtf` (radare2's register substitution), SHT_ANDROID_REL, a 32-bit
load named only where claimed, adrp's obj. data symbol (master spells
obj. too): existing classes widened.
diff_r2.py on elf, mach0 and pe at 3bc992e: no unjudged line and no
judgment unmatched.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
An object of data alone (x86_64-abs-sym.o, x86_64-common-sym.o: an empty
.text) and a shared object with e_entry 0 and an empty .text gave
OpenProgram::start nothing, so the session sat at 0, which nothing maps,
and px and pd at entry0 printed nothing. The start now falls back to the
lowest mapped segment, which for an object is its first placed section
(0x8000040, where radare2 starts too).
Seen: r2s -q -c 's; px 16; pd 1' x86_64-abs-sym.o was "nothing mapped at
0x0"; now 0x8000040, the file's zeros, and xor al, 0x12 (absval's 0x1234
written unchanged). Tests: start.rs fails before (None), the
relocatable golden pins the object. long_comments 2592 to 2591 (the doc
comment shortened).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
Its rest: the object's MOVW/MOVT and JUMP19 fields (I's encoders), a
jump table read in the lift's memory order on BE8 and aarch64_be
(conditional), AArch64's big-endian language (deferred), radare2's BE8
decode, an upstream pull request owed, and a TESTBINS_REF bump that
brings the BE ARM testbins into CI's differential (next).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM
Over the local testbins' four big-endian ELFs (the BE ARM two are not at
CI's pinned ref): arm-thumb-movw-be.o went from 8 unjudged `pd` lines to
one, `bne.w reloc.back` against r2s's `bne.w 0x800003e`, the JUMP19
addend r2image places no value for (unfilled, ROADMAP I's encoders).
arm-plt-lld-be8.so's five are radare2 decoding BE8 big-endian, the
armeb_hello_static judgment widened to it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01456qmqCGpCWJkrg6JUZNNM