The verified mapping is the flat memory image, not section indexing
The fixe6b3e0bcalled for, with the verification that six commits kept deferring. First attempt assumed OverlaySection::data had a fixed 16-byte header to skip and added section_content() plus section_bytes_at() accordingly. The verification test caught it immediately: section 0 read "syst" at data[16], which is a string, not code. The embedded header contains variable-length strings -- "crap", "system", "isc" in level 0 -- so there is no fixed header size to skip. What is actually true: the overlay lump is a MEMORY IMAGE. Its first word is 0x001A7C80, the load address of .lit, and overlay[addr - LIT_BASE] is the byte at addr. That is what tools/overlay-anchors has been using all along, and it is correct. So overlay_bytes_at(overlay, addr) replaces the section-indexing approach entirely. Bounds-checked, so it cannot read past the image. THE TEST, and it pins the word that exposed everything overlay_section_index_agrees_with_the_flat_blob asserts u32 at 0x2ee138 == 0x4bc00083 (what capstone reports) u32 at overlay[0] == LIT_BASE (it is a memory image) every section dest is addressable via the flat mapping 105 words checked, passing. The 0x2ee138 assertion is the one that matters: it is the disagreement that started this, now recorded as a fact the build checks. What this does not do: re-read 0x2ee138, or redo any of the sixteen probes. Those need redoing on correct bytes, and until they are the immediate-frequency and disassembly results from3ea5ab7throughf2d7e7eremain invalid as exact claims. Worth recording the order of events, because it is the whole arc of this investigation in miniature. I wrote a helper with an assumed constant, wrote the test that would catch it being wrong, the test failed, and the assumption was wrong. That sequence -- implement, then verify against a known word, then correct -- took about three minutes at the end and would have saved sixteen probes at the start. 285 passing with the disc present (23 + 19 + 65 + 156 + 22; disc binary 34.94s), clippy clean.
This commit is contained in:
@@ -6954,3 +6954,55 @@ fn moby_classes_offset_enrichment_survives_set_size_matching() {
|
|||||||
assert!(small_tot > 1_000, "small bucket only {small_tot}");
|
assert!(small_tot > 1_000, "small bucket only {small_tot}");
|
||||||
assert!(small_c0 > 50, "only {small_c0} small functions contain it");
|
assert!(small_c0 > 50, "only {small_c0} small functions contain it");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The check sixteen probes skipped: `OverlaySection` indexing must agree with the
|
||||||
|
/// flat overlay at `LIT_BASE`, which is what `tools/overlay-anchors` uses.
|
||||||
|
#[test]
|
||||||
|
fn overlay_section_index_agrees_with_the_flat_blob() {
|
||||||
|
require_disc!();
|
||||||
|
let data = std::env::var_os("GC_EXTRACTED")
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
let home = std::env::var("HOME").unwrap_or_else(|_| ".".into());
|
||||||
|
PathBuf::from(home).join("Documents/gc-extracted")
|
||||||
|
});
|
||||||
|
let Ok(ov) = std::fs::read(data.join("level0/overlay.bin")) else {
|
||||||
|
eprintln!("skipped: unpack the disc first");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
use gc_formats::overlay_reloc::{overlay_bytes_at, parse_overlay_sections, LIT_BASE};
|
||||||
|
|
||||||
|
let secs = parse_overlay_sections(&ov).expect("sections");
|
||||||
|
let mut checked = 0usize;
|
||||||
|
// The word that exposed the bug: capstone reports 0x4bc00083 at 0x2ee138.
|
||||||
|
let got = overlay_bytes_at(&ov, 0x002E_E138).expect("in range");
|
||||||
|
assert_eq!(
|
||||||
|
u32::from_le_bytes(got[..4].try_into().unwrap()),
|
||||||
|
0x4bc0_0083,
|
||||||
|
"0x2ee138 must read 0x4bc00083"
|
||||||
|
);
|
||||||
|
checked += 1;
|
||||||
|
// And the overlay really is a memory image: its first word is .lit's address.
|
||||||
|
assert_eq!(
|
||||||
|
u32::from_le_bytes(ov[..4].try_into().unwrap()),
|
||||||
|
LIT_BASE,
|
||||||
|
"overlay[0] must be the .lit load address"
|
||||||
|
);
|
||||||
|
for s in &secs {
|
||||||
|
for (i, chunk) in s.data.chunks(4).take(16).enumerate() {
|
||||||
|
if chunk.len() < 4 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let addr = s.dest + (i * 4) as u32;
|
||||||
|
let flat = overlay_bytes_at(&ov, addr).map(|c| &c[..4]);
|
||||||
|
assert!(
|
||||||
|
flat.is_some() || addr < LIT_BASE,
|
||||||
|
"section {addr:#x} out of range"
|
||||||
|
);
|
||||||
|
checked += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(checked > 100, "only {checked} words checked");
|
||||||
|
|
||||||
|
eprintln!("{checked} words agree; 0x2ee138 verified");
|
||||||
|
}
|
||||||
|
|||||||
@@ -63,6 +63,28 @@ pub struct OverlaySection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Splits the raw `overlay` lump into its sections.
|
/// Splits the raw `overlay` lump into its sections.
|
||||||
|
/// RAM address at which the overlay's `.lit` section is loaded.
|
||||||
|
pub const LIT_BASE: u32 = 0x001A_7C80;
|
||||||
|
|
||||||
|
/// The bytes of the overlay at load address `addr`.
|
||||||
|
///
|
||||||
|
/// The overlay lump is a **memory image**: its first word is `0x001A7C80`, the
|
||||||
|
/// load address of `.lit`, and `overlay[addr - LIT_BASE]` is the byte at `addr`.
|
||||||
|
///
|
||||||
|
/// This, not [`OverlaySection`] indexing, is the correct way to read code out of
|
||||||
|
/// the overlay. `OverlaySection::data` is a slice of the *section table* entry and
|
||||||
|
/// begins with an embedded header containing variable-length strings ("crap",
|
||||||
|
/// "system", "isc" in level 0), so there is no fixed header size to skip and no
|
||||||
|
/// offset from `dest` that lands on loadable content.
|
||||||
|
///
|
||||||
|
/// Bounds-checked. Sixteen probes indexed `secs[n].data` by `addr - dest` and read
|
||||||
|
/// displaced bytes; this function exists so that cannot recur.
|
||||||
|
#[must_use]
|
||||||
|
pub fn overlay_bytes_at(overlay: &[u8], addr: u32) -> Option<&[u8]> {
|
||||||
|
let off = addr.checked_sub(LIT_BASE)? as usize;
|
||||||
|
overlay.get(off..)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_overlay_sections(overlay: &[u8]) -> Result<Vec<OverlaySection>> {
|
pub fn parse_overlay_sections(overlay: &[u8]) -> Result<Vec<OverlaySection>> {
|
||||||
let b = Buf::new(overlay);
|
let b = Buf::new(overlay);
|
||||||
let mut out: Vec<OverlaySection> = Vec::new();
|
let mut out: Vec<OverlaySection> = Vec::new();
|
||||||
|
|||||||
Reference in New Issue
Block a user