Skip to content

fix(dwarf): remap DW_AT_ranges lists to the fused layout (#319 inc 2)#321

Merged
avrabe merged 2 commits into
mainfrom
fix/319-inc2-die-ranges
Jul 1, 2026
Merged

fix(dwarf): remap DW_AT_ranges lists to the fused layout (#319 inc 2)#321
avrabe merged 2 commits into
mainfrom
fix/319-inc2-die-ranges

Conversation

@avrabe

@avrabe avrabe commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

What

Second increment for #319. Inc 1 fixed DW_AT_high_pc lengths; this fixes the residual "DIE address ranges are not contained in its parent's ranges" errors.

Root cause

DWARF v4 .debug_ranges entries are offset pairs relative to a base (the CU's DW_AT_low_pc, nonzero for wasi-libc CUs). gimli parses them as AddressOrOffsetPair and routes the offsets through convert_address, which treats a small offset (e.g. 0x141) as an absolute code address and remaps it to an unrelated output location — so the resolved range escapes its parent (the .debug_ranges analogue of the inc-1 high_pc bug, one level down). Verified empirically: input inlined child ranges resolve inside write [0xb398,0xb53c); output rendered [0x1c439,…] outside [0x133b6,0x1353d).

Fix

correct_die_ranges — a post-convert pass that walks read↔write DIEs in lockstep pre-order, resolves each DIE's absolute input ranges via gimli's die_ranges (base already applied), translates both endpoints to the fused layout, and re-emits them as OffsetPair relative to the output CU base. Unmappable sub-ranges (dropped/tombstoned code) are dropped; an emptied list deletes DW_AT_ranges — never a wrong/escaping range (LS-D-1).

Effect

The not-contained class is eliminated: records-only 4→0, records+variants 11→0. Cumulative with inc 1: 1049→35 (records+variants), ~510→8 (records-only). The remaining 35/8 are Cause #2 (.debug_loc tombstone vs base-address-selection escape) = inc 3.

Test

inc2_die_ranges_stay_within_enclosing_subprogram — fuses records.wasm, re-parses the fused DWARF, asserts every DW_AT_ranges entry sits inside its enclosing subprogram (the invariant that failed). No llvm-dwarfdump dependency. 2 witness + 24 dwarf unit tests pass.

Refs: #319.

🤖 Generated with Claude Code

The residual "DIE address ranges are not contained in its parent's ranges"
errors after inc 1 came from base-relative .debug_ranges offsets.

Root cause: DWARF v4 .debug_ranges entries are offset pairs relative to a
base (the CU's DW_AT_low_pc, which is nonzero for wasi-libc CUs). gimli
parses them as AddressOrOffsetPair and routes the *offsets* through
convert_address, which treats a small offset (e.g. 0x141) as an absolute
code address and remaps it to an unrelated output location — so the resolved
range escapes its parent (the .debug_ranges analogue of the inc-1 high_pc
bug, one level down). The CU base (an address form) is remapped correctly,
so consumer = out_base + garbage_offset → e.g. [0x1c439,..) outside the
parent [0x133b6,0x1353d).

Fix: correct_die_ranges — a post-convert pass that, walking read↔write DIEs
in lockstep pre-order, resolves each DIE's absolute input ranges via
gimli read `die_ranges` (base already applied), translates both endpoints to
the fused layout, and re-emits them as OffsetPair relative to the output CU
base. Sub-ranges whose endpoints can't be mapped (dropped/tombstoned code)
are dropped; an emptied list deletes DW_AT_ranges — never a wrong or
escaping range (LS-D-1).

Effect: the "not contained" class is eliminated — records-only 4->0,
records+variants 11->0 (cumulative with inc 1: 1049->35 multi, ~510->8
single). The remaining 35/8 are Cause #2 (.debug_loc tombstone vs
base-address-selection escape) = inc 3.

Test: inc2_die_ranges_stay_within_enclosing_subprogram — fuses records.wasm,
re-parses the fused DWARF, asserts every DW_AT_ranges entry sits inside its
enclosing subprogram (the invariant that failed). 2 witness + 24 dwarf unit
tests pass.

Refs: #319.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

LS-N verification gate

58/58 approved LS entries verified

count
Passed (≥1 test, all green) 58
Failed (≥1 test failure) 0
Missing (no ls_*_NN_* test found) 0

Approved loss-scenarios.yaml entries are expected to have a
regression test named ls_<letter>_<num>_* (e.g. LS-A-11
ls_a_11_*). The gate runs each prefix via cargo test --lib --no-fail-fast and aggregates pass/fail/missing.

Failed LS entries

(none)

Missing regression tests

(none)

Updated automatically by tools/post_verification_comment.py.
Source of truth: safety/stpa/loss-scenarios.yaml.

…c 2)

Mythos delta-pass robustness: a degenerate begin==end OffsetPair is rejected
by gimli's range writer (InvalidRange), which would abort the whole DWARF
write and fall back to strip. Require strict oe > ob; a zero-length range
carries no info, so dropping it is lossless. (No mis-attribution — this is a
write-abort/reliability guard.)

Refs: #319.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@avrabe

avrabe commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Mythos delta-pass — NO FINDINGS (meld-core/src/dwarf.rs)

Ran the discover protocol on correct_die_ranges / write_dies_preorder. Five hypotheses; the highest-risk one cleared by source and empirically:

  1. Read↔write DIE lockstep misalignment (would mis-attribute ranges to the wrong DIE) — refuted. gimli 0.31.1 UnitTable::fromconvert_entry is a faithful, order-preserving 1:1 pre-order copy with no tag-based skip/add/reorder (any unsupported DIE fails from() wholesale, no partial-skip desync). Instrumented the real code on the fused fixture: 261 units, 195,497 DIEs, 0 tag desyncs, 0 count mismatches.
  2. Wrong/absent CU base — the 23/284 units skipped for a non-constant root low_pc keep gimli's converted ranges; --verify shows none escape (pre-existing boundary, not a regression).
  3. Offset guardob >= out_cu_base drops pre-base sub-ranges (fidelity, never mis-attribution). The begin==end degenerate that gimli's writer rejects (InvalidRange, would abort→strip) is now hardened to strict oe > ob (commit in this PR).
  4. low/high reprocessing — the DW_AT_ranges.is_some() gate correctly excludes low/high-only subprograms.
  5. Tombstone sub-ranges — dropped, so a list only ever describes less code, never wrong code (LS-D-1).

Oracle: llvm-dwarfdump --verify on the fused output → 0 "not contained" errors (the inc-2 target class gone); residual is the untouched .debug_loc class (inc 3). Adding mythos-pass-done.

@avrabe avrabe added the mythos-pass-done Mythos delta-pass completed on Tier-5 file changes; findings (or NO FINDINGS) attached to PR label Jul 1, 2026
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

Mythos delta-pass (auto)

NO FINDINGS across 1 Tier-5 file(s)

File Verdict Hypothesis
`` ✅ NO FINDINGS

Auto-run via anthropics/claude-code-action@v1
(SHA-pinned) on the touched Tier-5 files, using the
maintainer's Max-plan OAuth token. See
.github/workflows/mythos-auto.yml and
scripts/mythos/discover.md.

@avrabe avrabe merged commit bb43044 into main Jul 1, 2026
19 of 23 checks passed
@avrabe avrabe deleted the fix/319-inc2-die-ranges branch July 1, 2026 18:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mythos-pass-done Mythos delta-pass completed on Tier-5 file changes; findings (or NO FINDINGS) attached to PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant