4.1 KiB
§176e — SYMBOL IDENTITY IS COMPUTABLE OFFLINE (P31 S52): tools/reloc_identity.py
§174 law 1c says match_one masks relocations and therefore cannot see a wrong callee or a wrong
global. That was recorded as a caution to the reader ("check every symbol by hand after MATCH").
It is actually a computation, and it should never have been a human's job:
- the target
.scomment column carries the final linked word, so the true address behind a masked field is recoverable —jal:((w & 0x03FFFFFF) << 2) | (pc & 0xF0000000);HI16/LO16:(hi & 0xFFFF) << 16 + sext16(lo & 0xFFFF); - the draft's own object names the symbol it will bind to;
config/symbols*.txtmaps addresses back to names.
Compare the two and every wrong-symbol draft is named, with its fix, for $0 and no rebuild. On the
88-draft "match_one MATCH but the gate rejected it" pile this returned 12 MISMATCH / 68 AGREE,
and --fix mechanically repaired 10 of the 12 (the other 2 correctly refused).
The two failure shapes it separates, which look identical from the gate:
- Uniform-delta stale seed symbols (§171): every relocation off by the SAME delta —
func_80130D48all five by0xD1EC,func_8016D688all five by0x65450. One rebase fixes the draft. A rename is only safe when every mismatch implies the same corrected base; when a symbol implies two different bases the draft is wrong in more than one way and the fixer must refuse (it did, twice). - Wrong field offset — small deltas (
+0x4,+0xC,+0x10). Same symbol, wrong member.
FOUR TRAPS, all of which bit me while building it — this is a tool with sharp edges:
- Splat-derived names are not in the symbol FILES.
func_800123F0/D_800A4ED4/jtbl_*are generated for everything without a curated name; their ADDRESS IS THEIR NAME. Miss this and the checker resolves nothing, checks zero relocations, and reports clean (R32 — the exact shape of a checker that looks green while checking nothing). - MIPS o32 uses REL, so the addend lives IN THE INSTRUCTION, not the reloc entry.
objdumpprints a bareD_801F9DACfor what is reallyD_801F9DAC+1. Read the addend from your own object's hi/lo immediates, exactly as the linker does — otherwise every struct-field and array access becomes a fabricated "symbol error" (my first run turned one byte-array walk into three). - Index alignment is a precondition. Instruction i corresponds to target i only while the streams are the same shape; one inserted instruction shifts everything after it and manufactures phantom mismatches. Refuse (or mark advisory) when the shapes differ.
- A nearest-symbol label needs a TIGHT window. At 0x4000 the tool cheerfully called
func_8001C9D0"SsGetMute+0xC50". A wrong label is worse than no label — the reader acts on it.
And the honest limit, measured to completion the same session — this is the important half of the entry. Symbol-verified + shape-verified is still NOT sufficient for a bank. Re-gating 20 such drafts across 5 groups banked 1 — 5%, which is statistically the same as the project's existing A10 stored-verdict law (a blind stored-draft re-gate converts at ~0–8%; P31 T1 measured 0/23 on the same kind of pile). So the finding is a clean null with real value:
Symbol verification does NOT improve stored-draft re-gate conversion. The reason a stored "match_one MATCH" draft was rejected is almost never symbol identity — it is TU plumbing (§176d) or simple staleness. Do not spend rebuilds re-gating an old pile because a new filter made it look clean; the lane that banks is fresh drafting, and this oracle's real value is as a pre-gate check on FRESH drafts (where it costs seconds and removes a whole failure class before the rebuild) — not as a resurrection tool for the backlog.
R38 note to my future self: the phase log already recorded the 0/23 result before I ran this probe. The probe was still worth it — the reloc filter was a genuinely new discriminator and "does it move the A10 rate?" was unanswered — but the prior should have been ~8%, not the optimistic read I started with.