Skip to content

feat(dwarf): emit .rel.debug_* so a linker fixes up DWARF .text addresses (VCR-DBG-001 PR C, #394) - #430

Merged
avrabe merged 1 commit into
mainfrom
feat/dwarf-relocations-pr-c-394
Jun 22, 2026
Merged

feat(dwarf): emit .rel.debug_* so a linker fixes up DWARF .text addresses (VCR-DBG-001 PR C, #394)#430
avrabe merged 1 commit into
mainfrom
feat/dwarf-relocations-pr-c-394

Conversation

@avrabe

@avrabe avrabe commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

What

PR C of v0.12.0 (VCR-DBG-001, epic #242). PR A captured the per-instruction machine_offset → wasm_op_index map; PR B (#429) emitted debugger-reachable DWARF — but with object-relative (.text base 0) addresses and no relocations. Correct in the unlinked ET_REL object, silently wrong on a linked image: a --debug-line consumer (jess, debugging gust on silicon via gdb/lldb) would get wrong source lines after ld. This wires the relocations that make the linker shift them — the property the v0.12.0 tag was held for.

How

C1 — elf_builder per-section REL generalization

  • ElfBuilder gains extra_relocations + add_section_relocations(target, ..), emitting .rel.<name> sections (REL form, sh_link=symtab, sh_info=target section).
  • Byte-identical when empty: every new byte (shstrtab names, rel tables, headers, +N to e_shnum) is appended after .rel.text and only when non-empty.

C2 — gimli RelocWriter + .text base symbol

  • A custom write::Writer delegates to EndianVec but overrides only write_address, so just the two .text references (the line program's DW_LNE_set_address anchor + the CU DW_AT_low_pc) become relocations; internal DWARF cross-refs stay concrete (no section symbols needed).
  • Single-base-reloc + addend 0 ⇒ the .debug_* bytes are byte-identical to PR B (Address::Symbol{_,0} writes the same in-place 0 as Address::Constant(0)); rows stay text-relative deltas, so one relocation per section covers all 110 rows.
  • A dedicated global __synth_text_base (st_shndx=.text, value 0) anchors the R_ARM_ABS32 records; appended last ⇒ no symbol-index shift, .symtab sh_info untouched, present only under --debug-line.
  • --debug-line on a self-contained ET_EXEC / RISC-V build (where DWARF isn't emitted) now warns loudly rather than silently dropping (native-pointer-abi: size the wasm .bss to data high-water-mark, not the full declared memory page (blocks tiny-RAM targets) #383 honest-fail).

Oracle (the gate the advisor required)

rel_debug_relocations_shift_addresses_to_correct_line_394: asserts .debug_line/.debug_info each carry exactly one R_ARM_ABS32 against __synth_text_base with in-place addend 0, then applies the .debug_line reloc at base 0x08000000 and re-walks the line program — every row shifts by exactly the base and its source line is preserved (relocate-at-nonzero-base, correct line, not just "a reloc exists").

Confirmed end-to-end with a real linker:

arm-none-eabi-ld -Ttext=0x08000000 → readelf --debug-dump=decodedline
synth.wasm  175  0x800023c
synth.wasm  175  0x8000260   …

A real toolchain links it and GNU's own DWARF reader resolves correct linked addresses.

Frozen-safe

  • All 4 dwarf_debug_line_emit_394 oracles green; PR B additivity/no-op oracles unchanged (the gust whole-.o no-op oracle is the end-to-end C1 byte-identity proof).
  • cabi_arena_realloc_linkability_418 .rel.text fixture byte-identical.
  • fmt + clippy clean; rivet gate (non-xref) = 0; bazel build //crates:synth green.

Known limitation (EXPERIMENTAL, documented in --debug-line help)

__synth_text_base is a global symbol, so linking >1 synth --debug-line object into one image collides (multiple definition) — fails loud, not the silent-wrong-line shape. Common case (one synth .o + the C kiln bridge) is correct and verified. Idiomatic fix (a local STT_SECTION symbol) is a fast-follow blocked by the hardcoded .symtab sh_info=1; jumps priority if jess's image links multiple synth objects.

Closes the v0.12.0 tag-hold on VCR-DBG-001.

🤖 Generated with Claude Code

…sses (VCR-DBG-001 PR C, #242, #394)

PR B emitted DWARF whose .text addresses were object-relative (base 0) with
NO relocations — correct in the ET_REL object but silently WRONG on a linked
image, so a `--debug-line` consumer (jess, debugging gust on silicon) would get
wrong source lines after `ld`. This wires the relocations that make the linker
shift them.

C1 — elf_builder per-section REL generalization:
  - ElfBuilder gains `extra_relocations` + `add_section_relocations(target, ..)`,
    emitting `.rel.<name>` sections (REL form, sh_link=symtab, sh_info=target).
  - Byte-identical when empty: every new byte (shstrtab names, rel tables,
    section headers, +N to e_shnum) is appended after `.rel.text` and only when
    non-empty. The frozen cabi_arena_realloc_linkability_418 `.rel.text` fixture
    stays byte-identical.

C2 — gimli RelocWriter + .text base symbol:
  - A custom `write::Writer` delegates to EndianVec but overrides ONLY
    write_address, so just the two `.text` references (the line program's
    DW_LNE_set_address anchor + the CU DW_AT_low_pc) become relocations; internal
    DWARF section cross-refs stay concrete (no section symbols needed).
  - Single-base-reloc + addend 0 ⇒ the `.debug_*` bytes are byte-identical to
    PR B (Address::Symbol{_,0} writes the same in-place 0 as Address::Constant(0));
    rows stay text-relative deltas, so one relocation per section covers all rows.
  - A dedicated GLOBAL `__synth_text_base` (st_shndx=.text, value 0) anchors the
    R_ARM_ABS32 records; global + appended last ⇒ no symbol-index shift, symtab
    sh_info untouched, present only under --debug-line.
  - `--debug-line` on a self-contained ET_EXEC / RISC-V build (where DWARF is not
    emitted) now warns LOUDLY rather than silently dropping (#383 honest-fail).

Oracle C (relocate-at-nonzero-base, correct line): asserts `.debug_line` /
`.debug_info` each carry exactly one R_ARM_ABS32 against __synth_text_base with
in-place addend 0, then APPLIES the `.debug_line` reloc at base 0x08000000 and
re-walks the line program — every row shifts by exactly the base and its source
line is preserved. Confirmed end-to-end with a real `arm-none-eabi-ld -Ttext=
0x08000000` link: readelf --debug-dump=decodedline shows rows at 0x800023c… on
the correct source lines.

All four dwarf_debug_line_emit_394 oracles green; PR B additivity/no-op oracles
unchanged (bytes identical); fmt + clippy clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.45455% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/synth-cli/src/main.rs 88.23% 4 Missing ⚠️
crates/synth-backend/src/elf_builder.rs 97.05% 2 Missing ⚠️
crates/synth-core/src/dwarf_line.rs 98.07% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe
avrabe merged commit 9a34c8d into main Jun 22, 2026
15 checks passed
@avrabe
avrabe deleted the feat/dwarf-relocations-pr-c-394 branch June 22, 2026 22:14
avrabe added a commit that referenced this pull request Jun 22, 2026
, #394) (#440)

First release of the north-star verified-codegen campaign. Pin sweep
0.11.51 -> 0.12.0 (workspace + all path-dep versions + MODULE.bazel +
Cargo.lock), CHANGELOG, and VCR-DBG-001 roadmap status -> implemented.

Ships:
- VCR-DBG-001 `--debug-line` DWARF emission (PR A #427 / B #429 / C #430):
  debugger-readable .debug_* mapping ARM .text -> wasm source lines, with
  .rel.debug_* relocations verified end-to-end against arm-none-eabi-ld.
  Purely additive; default build bit-identical.
- VCR-MEM-001 layer-2 frozen-safe budget decision logic + oracles (#421-#425).
- gimli 0.33 adaptation (#439) + wast 252 (#438).

Minor bump: new user-facing `--debug-line` feature (semver-correct).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
avrabe added a commit that referenced this pull request Jul 10, 2026
… func_N goes STB_LOCAL, symtab locals-first + real sh_info (#656) (#693)

#637 — synth disasm decoded synth's own Cortex-M Thumb-2 output as A32
garbage, and synth objects carried no ISA marker for other tools either:

- The ELF builder gains `arm_attributes_section()` (SHT_ARM_ATTRIBUTES,
  'A' + "aeabi" + Tag_File): Tag_CPU_arch / Tag_CPU_arch_profile /
  Tag_ARM_ISA_use / Tag_THUMB_ISA_use derived from the selected target
  (cortex-m3 = v7/M/Thumb-2, thumbv7em = v7E-M, thumbv8.1m = v8.1-M,
  cortex-r5 = v7/R/A32). Emitted on the relocatable-object path and both
  Cortex-M image paths, appended last so every section index is stable.
  Standard toolchains now auto-select the Thumb decoder — plain
  `objdump -d` (no --triple) already reads synth objects correctly.
- `synth disasm` auto-detects the ISA instead of defaulting to A32:
  .ARM.attributes Tag_THUMB_ISA_use when present, else the STT_FUNC
  thumb bit (odd st_value), else e_entry bit 0.

#656 — internal (non-exported) functions and the `func_{wasm_index}`
call aliases were STB_GLOBAL, so co-linking two independently-dissolved
objects failed with `multiple definition of 'func_N'`:

- Non-exported functions and every func_N alias are now STB_LOCAL;
  exported names stay STB_GLOBAL. `arm-none-eabi-ld -r a.o b.o` links
  clean, each object keeps its own local helpers, exports resolve
  cross-object.
- That drags in the ELF ordering rule that blocked local symbols since
  the DWARF work (#430): ElfBuilder now stable-sorts locals before
  globals at build time, sets `.symtab` sh_info to the first-non-local
  index (was hardcoded 1), and rewrites every relocation's symbol index
  through the permutation (.rel.text and the .rel.debug_* tables), so
  the #167/#173 R_ARM_THM_CALL machinery stays consistent. With zero
  locals the permutation is the identity — pre-#656 objects are
  byte-identical.

Oracles: new elf_tooling_637_656.rs (bindings + sh_info + reloc
reindex + co-link via arm-none-eabi-ld/ld.lld when present + attributes
bytes + disasm thumb mnemonics on the #682 masked-shift shape), new
elf_builder unit tests (locals-first sort, reloc remap, identity
freeze, attributes blob). Frozen .text anchors 10/10 (symtab/attributes
only — .text untouched); control_step (13/13) and flight_seam
(0x07FDF307) symtab-based differentials re-run green on the new layout.

Closes #637
Closes #656

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant