Files
BFM-decomp/config/symbols.us.ram.txt
T
Drew T af2f40d153 fix(phase-26a): A4/A5 — 193 unmatchable slices dissolved; the closeness oracle stops lying
R22 CLEAN-FLEET: make clean -> extract 136 -> build 136 -> check-all = 136 PASSED, 0 FAILED.
make audit-corpus: 0 PHANTOM + 0 TRUNCATED (was 193).

=== A4: a CORPUS defect the byte-gate could never have caught ===
config/symbols.us.txt:981 declared `listCdBuffer = 0x80180000` — a correct Phase-3 name for MAIN's
LIST.CD RAM buffer. But that address is OUTSIDE main's image and INSIDE the overlay slot, and every
overlay's splat config stacks symbols.us.txt. High RAM is REUSED: an address that is a buffer to main
is live CODE to an overlay. So splat saw a symbol boundary mid-code and, across 97 of 134 overlays:
  * CUT 97 REAL FUNCTIONS IN HALF (a head ending on a `lui`, no return), and
  * INVENTED 96 PHANTOM ONES      (a tail beginning by reading the assembler temp $at).
193 slices NOBODY COULD EVER MATCH — not "hard", not "a compiler wall": unmatchable by construction.
They sat in the harvest queue as ordinary work, so agents would burn on them forever and the failures
would be filed as intrinsic compiler residuals.

The phantom listCdBuffer.s in ov_SC01_005 literally begins:
    lw $ra, 0x10($sp) / addiu $sp, $sp, 0x18 / jr $ra
splat cut a function immediately before its EPILOGUE and called the epilogue a function.

AND IT HAD ALREADY CONTAMINATED REAL WORK: in ov_SC03_031 the cut landed where the epilogue was
exactly `jr $ra; nop`, so the Phase-26 x134 sweep innocently BANKED the phantom as
`void listCdBuffer(void) {}` — byte-correct, gate-green, entirely fictitious — while leaving
func_8017FFC4 permanently unmatchable. Removed.

WHY NO GATE CAUGHT IT, AND WHY THAT IS THE POINT: INCLUDE_ASM pastes the two .s halves back VERBATIM
in original order, so the image is byte-identical either way. The byte-gate was green the whole time
and always would have been. It is a perfect CORRECTNESS oracle and a NULL COVERAGE oracle. No
assertion added INSIDE it could ever have found this. What found it was a SECOND, INDEPENDENT oracle:
tools/sig_image.py derives boundaries from the ORIGINAL bytes without splat, and DISAGREED with the
corpus (58,524/58,621 agreement with spimdisasm; correct on all 97 disagreements).
  => When one oracle is structurally blind to a class of error, the answer is not a better assertion
     inside it. It is a SECOND ORACLE THAT CAN DISAGREE WITH IT.  (`make audit-corpus` is now that.)

THE RULE (the mirror of R13/R15, never written down): a symbol whose address falls inside ANOTHER
binary's vram window must never enter that binary's symbol stack.
FIX: config/symbols.us.ram.txt — main-scoped symbols outside main's image — stacked ONLY by
config/splat.us.exe.yaml. Main keeps the name it needs (10 %hi / 11 %lo refs; 143dbb89 byte-identical);
the overlays never see it. Exactly one symbol was in scope fleet-wide; the resident window was clean.

AND A REAL FUNCTION THE ACCIDENT WAS HIDING: in ov_SC01_084 / ov_SC02_041 / ov_SC03_094 / ov_SC06_008
there IS a genuine function at 0x80180000 (111 / 35 / 28 / 74 ins), reachable ONLY via a fn-pointer
table (.word func_80180000) and never by `jal` — so splat cannot find it and needs the boundary
DECLARED. listCdBuffer had been supplying it by luck. Now declared honestly, per-overlay, in
config/symbols.<ov>.txt — exactly where R13/R15 says an overlay-scoped symbol belongs.

=== A5: the closeness oracle every crack agent trusts was lying on 155 functions ===
masked_diff._reloc_kind() knew 26/HI16/LO16. An over-approximating sweep of every reloc objdump emits
across all 3,367 build objects found FOUR: R_MIPS_26, HI16, LO16 — and R_MIPS_PC16 (211). PC16 fell
through to a FULL-WORD compare, but the object holds an UNRESOLVED PLACEHOLDER in the branch
displacement, so that compare can NEVER succeed.
DECISIVE TEST (derived from the invariant, not from reading the regex): INCLUDE_ASM pastes the
ORIGINAL asm, so for every stub diff_object_s() MUST be 0. Measured, coverage-asserted:
    2,741 functions scored — old mask: 150 LIES;  PC16 masked: 4 LIES.
(The 4 survivors are the separate length-delta defect.) A phantom non-zero sends an agent to grind at
a wall that is not there, and the wasted attempt is then booked as a MATCHING failure, feeding
reserved_walls() and PERMANENTLY BLACKLISTING a function that was never broken.

=== NEW FINDING (found by cutting the R22 corner): a STALE OBJECT CAN PRODUCE A FALSE PASS ===
`.o <- .s` is not a dependency make can see: assembly arrives via INCLUDE_ASM, expanded to a `.include`
consumed by maspsx/as AFTER cpp, while -MMD tracks headers only. Re-extract, build incrementally, and
make links a STALE object. This is not merely slow — INCLUDE_ASM pastes the ORIGINAL bytes, so a stale
object still yields the original image: SHA1 GOES GREEN while the split just changed is never exercised.
A broken config change can be "verified" by an incremental build. Live proof: 8 of 136 binaries linked
stale objects here; they failed LOUDLY ONLY BY LUCK (the dead symbol was an undefined reference) — a
merely-different-but-valid split would have gone green on all 136.
R22/H3 already legislate this, and I broke them. But a rule that needs a human to remember it is not a
gate. FIX: `extract` now invalidates the objects that include what it just rewrote (main's are top-level,
so -maxdepth 1 — verified it cannot clobber the other 1,605 objects). Structural, not advisory.

R14 self-catch, recorded: my first A5 test passed `fn=` to diff_object_s(), which takes two args; the
TypeError was swallowed by my own `except Exception: continue` and it reported 0 scored / 0 lies. I
wrote the exact bug I was auditing, inside the test for it. Caught only because 0 looked wrong. The
test now asserts its own coverage.
2026-07-14 10:12:19 -06:00

48 lines
3.0 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// symbols.us.ram.txt — MAIN-EXE-SCOPED symbols that live OUTSIDE main's image.
//
// STACKED ONLY BY config/splat.us.exe.yaml. NEVER by an overlay or the resident.
//
// WHY THIS FILE EXISTS (Phase 26-A tooling audit — a corpus defect the byte-gate could not see)
// ---------------------------------------------------------------------------------------------
// `config/symbols.us.txt` is stacked by EVERY binary: main, the resident, and all 134 overlays.
// That is correct for a symbol inside main's image (0x80010000–0x80074800) — those addresses mean
// the same thing everywhere, because nothing else is loaded there.
//
// It is NOT correct for a main-scoped symbol whose address falls inside ANOTHER binary's vram
// window. High RAM is REUSED: the resident occupies 0x800CEDF8–0x80128154 and a location overlay is
// streamed into 0x80128158–~0x801DAB30. An address that is a RAM buffer to main is live CODE to an
// overlay.
//
// `listCdBuffer = 0x80180000` is exactly that. Main writes LIST.CD's contents there at runtime (a
// buffer, correctly named in Phase 3 — see docs/memory-map.md). But 0x80180000 is INSIDE the overlay
// slot, and because every overlay's splat config stacked symbols.us.txt, splat saw a symbol boundary
// in the middle of overlay code and:
//
// * CUT 97 REAL FUNCTIONS IN HALF (a truncated head that ends on a `lui` with no return), and
// * INVENTED 96 PHANTOM ONES (a tail that begins by reading the assembler temp $at),
//
// across 97 of the 134 overlays. **193 slices that NOBODY CAN EVER MATCH** — you cannot write C for
// either half. They sat in the harvest queue as ordinary work items, so agents burned on them
// indefinitely and the failure read as an intrinsic compiler wall.
//
// AND THE FULL-BINARY BYTE-GATE STAYED GREEN THE ENTIRE TIME, because INCLUDE_ASM pastes the two .s
// halves back verbatim in original order — the image is byte-identical either way. This is the
// project's cleanest example of the audit's thesis: *a perfect correctness oracle and a null
// coverage oracle.* What exposed it was a SECOND, INDEPENDENT oracle (tools/sig_image.py, which
// derives function boundaries from the ORIGINAL bytes without splat) disagreeing with the corpus —
// see `make audit-corpus`.
//
// THE RULE (the mirror of R13/R15, which nobody had written down)
// --------------------------------------------------------------
// R13/R15 say: overlay-derived symbols are overlay-REGION ONLY and must NEVER be merged into
// symbols.us.txt. The reverse is equally true and was never stated:
//
// A symbol whose address falls inside ANOTHER binary's vram window must never enter that
// binary's symbol stack.
//
// Enforced by `make audit-corpus` (tools/corpus.py), which fails loud when splat's function
// boundaries disagree with sig_image's.
listCdBuffer = 0x80180000; // data — LIST.CD RAM cache (Phase 3). OUTSIDE main's
// image; INSIDE the overlay slot. Main-only, by rule.