From e6b3e0b091bb07dd8c2c33b589ebc0f901e1a65a Mon Sep 17 00:00:00 2001 From: ReaperOfVeriod Date: Wed, 14 Oct 2026 06:14:14 +0200 Subject: [PATCH] Root cause: the per-section indexing used throughout this investigation was wrong The verification f2d7e7e asked for. Reference case: the tool disassembles 0x2ee138 as 0x4bc00083; reading the flat overlay at 0x2ee138 - LIT_BASE gives 83 00 c0 4b, little-endian 0x4bc00083. Match. Correct mapping is off = ram - LIT_BASE indexed into the whole overlay.bin. What I used in every probe: off = addr - secs[6].dest indexed into secs[6].data, and OverlaySection::data includes the 16-byte section header. Every read was displaced. Invalidated: - cee17c9's addiu r17, r18, 0xc0 disassembly, the 16-byte-stride walk, and the 'address not float' conclusion. Built on wrong bytes. - the immediate-frequency and neighbour-enrichment numbers in 3ea5ab7 and a2f8c92. Scanned per-function windows, so uniformly displaced: ratios are probably robust, exact counts are not measured on the bytes they claim. - e1482ab, d97e7df, f2d7e7e: the jal decoding was verified correct, so branch targets stand; what lives at them was read wrongly. Survives intact: every chunk-offset measurement in the tie and overlay work, which read core_data and the overlay flat and never went through the section index. Fix: never index secs[n].data by addr - secs[n].dest. Either use overlay[addr - LIT_BASE], or add a helper that accounts for the 16-byte header so call sites cannot forget it -- and verify it against a word the decomp pins, as above. This was available from the first probe and skipped sixteen times. 284 passing with the disc present, clippy clean. --- docs/plan/level-assets.md | 50 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) 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.