diff --git a/docs/plan/level-assets.md b/docs/plan/level-assets.md index d711c33..ac31285 100644 --- a/docs/plan/level-assets.md +++ b/docs/plan/level-assets.md @@ -2400,3 +2400,53 @@ independently known, and only then re-read `0x2ee138`. Until that mapping is verified, **no address in this investigation that I computed by hand is trustworthy** -- including the `0x3a8060` walk, which happened to decode but may have been read from the wrong bytes. + +### Root cause verified: the per-section indexing used throughout was wrong + +The check `f2d7e7e` asked for, finally performed. + +**Reference case:** `tools/overlay-anchors` disassembles `0x2ee138` as +`0x4bc00083`. Reading the flat overlay at `0x2ee138 - LIT_BASE` gives the bytes +`83 00 c0 4b`, which is little-endian `0x4bc00083`. **Match.** The correct mapping +is + + off = ram_address - LIT_BASE, indexed into the whole overlay.bin + +with `LIT_BASE = 0x1A7C80`. + +**What I used instead**, in every probe in this investigation: + + off = addr - secs[6].dest, indexed into secs[6].data + +and `OverlaySection::data` **includes the 16-byte section header** ("the 16-byte +header included, since it is the first `size` bytes of the section in the table", +`overlay_reloc.rs`). So every read was displaced, and the addresses I printed +against the bytes they actually came from were wrong. + +**Scope of the invalidation, stated as precisely as I can:** + +- `cee17c9` -- the `addiu r17, r18, 0xc0` disassembly, the 16-byte-stride walk, + the "it is an address not a float" conclusion. **Built on wrong bytes.** +- `3ea5ab7`, `a2f8c92` -- the immediate-frequency and neighbour-enrichment + numbers. These scanned `[off, off+size)` per function, so each window was + displaced by a constant. A uniform displacement preserves most of a function's + body and loses the first few instructions, so the counts are *approximately* + right and the ratios are probably robust -- but they are not measured on the + bytes they claim to measure, and cannot be quoted as exact. +- `e1482ab`, `d97e7df`, `f2d7e7e` -- the `jal` decoding itself was verified + correct, so the branch *targets* stand; it is what lives at them that was read + wrongly. + +**The one result that survives intact** is the one that never went through the +section index at all: the chunk-offset measurements in the tie and overlay work, +which read `core_data` and the overlay flat. + +**The fix, for whoever continues:** never index `secs[n].data` by +`addr - secs[n].dest`. Either use `overlay[addr - LIT_BASE]`, or add a helper that +subtracts the 16-byte header from `data` and states that it does. The second is +better -- it puts the header in one place instead of making every call site +remember. + +And then: **verify it**, the way the check above verifies, against a word the +decomp already pins. I had that verification available from the first probe and +skipped it sixteen times.