Skip to content

fix(elf): .ARM.attributes + thumb auto-detect disasm (#637); internal func_N STB_LOCAL, symtab locals-first + real sh_info (#656) - #693

Merged
avrabe merged 1 commit into
mainfrom
fix/637-656-elf-tooling
Jul 10, 2026
Merged

fix(elf): .ARM.attributes + thumb auto-detect disasm (#637); internal func_N STB_LOCAL, symtab locals-first + real sh_info (#656)#693
avrabe merged 1 commit into
mainfrom
fix/637-656-elf-tooling

Conversation

@avrabe

@avrabe avrabe commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Fixes two ELF tooling/linkability issues in one lane: #637 and #656 (+ the #430 sh_info lineage).

#637synth disasm decoded synth's own Thumb-2 output as A32 garbage

Two independent levers, both landed:

  1. .ARM.attributes emission (elf_builder::arm_attributes_section, SHT_ARM_ATTRIBUTES): Tag_CPU_arch / Tag_CPU_arch_profile / Tag_ARM_ISA_use / Tag_THUMB_ISA_use derived from the selected -t target (cortex-m3 = v7/M/Thumb-2, thumbv7em = v7E-M, thumbv8.1m = v8.1-M, cortex-m0 = v6-M/Thumb-1, cortex-r5 = v7/R/A32-only). Emitted on the relocatable-object path and both Cortex-M image paths, appended as the LAST section so every existing section index (.text=4, .data/.bss=5/6, DWARF tail) is untouched. Synth objects are now self-describing: plain objdump -d with no --triple already prints correct Thumb-2.
  2. synth disasm ISA auto-detect instead of the A32 default: .ARM.attributes Tag_THUMB_ISA_use when present → the STT_FUNC thumb bit (odd st_value, the standard interworking convention the issue pointed at) → e_entry bit 0 → A32. Non-ARM/non-ELF inputs keep the previous behavior.
$ synth disasm add.o          # cortex-m3 --relocatable
Detected triple: thumbv7m-none-eabi
00000000 <func_0>:
       0: b510   push {r4, lr}
       2: 1842   adds r2, r0, r1
...
$ arm-none-eabi-readelf -A add.o
  Tag_CPU_arch: v7
  Tag_CPU_arch_profile: Microcontroller
  Tag_THUMB_ISA_use: Thumb-2

#656 — internal func_N emitted STB_GLOBAL → multiple-definition collisions co-linking two dissolved objects

Kill-criterion from the issue, verified live:

$ arm-none-eabi-nm gpio.o
00000000 t func_0            # internal helper — lowercase t
00000008 t func_1            # export's call alias — local too
00000008 T gpio_configure    # export — global
$ arm-none-eabi-ld -r -o merged.o gpio.o spi.o && echo LINK OK
LINK OK                      # was: multiple definition of 'func_0'/'func_1'
$ arm-none-eabi-readelf -r merged.o   # each BL still pinned to its OWN local helper
00000018  0000040a R_ARM_THM_CALL  00000001  func_0
00000044  0000070a R_ARM_THM_CALL  00000025  func_0

Oracles

Closes #637
Closes #656

🤖 Generated with Claude Code

… func_N goes STB_LOCAL, symtab locals-first + real sh_info (#656)

#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>
@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.14130% with 51 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/synth-cli/src/main.rs 69.37% 49 Missing ⚠️
crates/synth-backend/src/elf_builder.rs 99.03% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe
avrabe merged commit 9ec3cf2 into main Jul 10, 2026
34 of 35 checks passed
@avrabe
avrabe deleted the fix/637-656-elf-tooling branch July 10, 2026 17:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant