Repair invalid DWARF scope ranges after transforms - #8964
Conversation
Preserve nonzero tombstones, reject lost or reversed low/high pairs, normalize range lists, and repair parent scope ranges from surviving children. Ambiguous sibling scopes are made unavailable instead of being assigned incorrect code ranges.
|
This looks large and complicated, and we don't have deep DWARF expertise here, so I am worried. But let me ask first, as background: what is a |
|
Thanks. “nonzero tombstone” was imprecise shorthand rather than a formal DWARF term. The all-ones address is documented by DWARF issue 200609.1, accepted for DWARF v6, as the reserved address for a non-existent entity: https://dwarfstd.org/issues/200609.1.html LLVM implements this as https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/BinaryFormat/Dwarf.h The max-minus-one value ( https://reviews.llvm.org/D81784 So Binaryen already recognizes I will update the PR wording to use the precise terms and references. I can also split the small tombstone-preservation change from the broader scope-range repair to make the review easier. |
Summary
Binaryen currently updates DWARF range endpoints independently. When optimization or Asyncify removes or reorders expressions, the resulting endpoints can wrap, overlap, or escape their parent scope. This is the same failure mode reported in #6406, extended to range lists and scope topology.
The repair is conservative: representable parent unions are preserved, while ambiguous scopes fail closed. Empty replacement range lists are appended rather than mutating lists that another DIE may share.
Implementation
Range-set normalization, union, containment, and overlap are isolated in
DwarfRangesand directly unit-tested. The DWARF adapter keeps encoding-specific constants and tombstone rules inwasm-debug.cpp, including the distinction between a validlow_pc = 0and range-list terminators.The repair builds an explicit parent/child index for each compilation unit. It first propagates malformed or unavailable scopes through that tree, then processes children before parents so sibling overlap checks see final ranges and range-list parents can be extended before their own containment check. This also avoids repeated descendant scans and avoids relying on default depths for null DIE terminators.
Tombstone handling
"Nonzero tombstone" was imprecise shorthand. DWARF issue 200609.1, accepted for DWARF v6, reserves the largest representable target address (for example,
0xfffffffffor wasm32) for a non-existent entity. LLVM implements this asdwarf::computeTombstoneAddressand has a WebAssembly-specific test for a dead wasm32 subprogram.The max-minus-one value (
-2) is an LLVM legacy compatibility encoding rather than a general DWARF value. It is recognized for legacy.debug_ranges/.debug_locdata because all-ones is already the base-address-selection marker and(0, 0)terminates the list. See LLVM D81784 and DWARFDebugRangeList.cpp.Binaryen already recognizes
0,-1, and-2in tombstone-aware contexts. This change preventsupdateDIEfrom passing-1/-2through the instruction-offset mapper, where they can be rewritten to zero and make a dead DIE appear to refer to address zero.Validation
DwarfRangestestspython3 check.py wasm-opt --no-torturesuiteDW_AT_low_pc = 0xffffffffvalues remain intactllvm-dwarfdump --verifyafter roundtrip forclass_with_dwarf_noprint,fannkuch3_manyopts_dwarf,fib2_dwarf,fib2_emptylocspan_dwarf,ignore_missing_func_dwarf,inlined_to_start_dwarf, andreverse_dwarf_abbrevsllvm-dwarfdump --verifyafter--asyncify -O -gforclass_with_dwarf_noprintA wasm32 object with
DW_AT_low_pc = 0xffffffffis reported by LLVM asdead code; before this fix,wasm-opt -O -grewrites it to0x00000000.Fixes #6406.