Skip to content

RQ-59-A64PANIC (#1013): aarch64 ELF builder refuses cleanly (exit 1 + reason naming the declined symbol) instead of panicking - #1045

Merged
avrabe merged 3 commits into
mainfrom
fix/a64-elf-panic-1013
Aug 25, 2026
Merged

avrabe merged 3 commits into
mainfrom
fix/a64-elf-panic-1013

Conversation

@avrabe

@avrabe avrabe commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

RQ-59-A64PANIC (#1013): aarch64 ELF builder refuses cleanly (exit 1 + reason) instead of panicking on a dangling reloc against a declined function

Resumed lane. The previous run stalled with uncommitted work; the coordinator salvaged it onto this branch (salvage(#1013) commit, explicitly marked UNREVIEWED). This run reviewed the salvage on merit, re-verified red-first from a rebuilt binary, kept it, and completed the lane (rivet status flip + measured-sibling correction + class survey).

Red-first transcript (re-verified this run, freshly built binaries)

BEFORE (sources at 708ae34b = pre-fix, test's own generated 20-target br_table fixture — the minimized gale httparse shape):

warning: skipping function 'func_0': ... br_table with 19 targets exceeds the aarch64
         compare-chain threshold (16) ... loud-declining (VCR-A64-CF-001)
warning: 1 of 2 functions were skipped (not in output): func_0
thread 'main' panicked at crates/synth-backend-aarch64/src/elf.rs:189:17:
aarch64 ELF builder: relocation at .text+20 targets symbol 'func_0', which this
object does not place — refusing to ship an unrelocated placeholder (#851)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
EXIT=101        # and no output object

AFTER (this branch):

warning: skipping function 'func_0': ... loud-declining (VCR-A64-CF-001)
warning: 1 of 2 functions were skipped (not in output): func_0
Error: compilation failed: aarch64 ELF builder: relocation at .text+20 targets
symbol 'func_0', which this object does not place — refusing to ship an
unrelocated placeholder (#851). The symbol was declined earlier; see the
preceding warning (#1013).
EXIT=1          # clean refusal, no panic markers, no output object

The DECISION is unchanged — an unrelocated bl #0/adrp #0 placeholder never ships. Only the MECHANISM changed: panic! → Err(BackendError::CompilationFailed(...)), propagated through both CLI call sites (build_aarch64_elf, build_multi_func_aarch64_elf) to the anyhow exit-1 path.

The test (crates/synth-cli/tests/a64_dangling_reloc_decline_1013.rs) asserts the full refusal contract, not "does not crash": premise anchor (the VCR-A64-CF-001 decline actually happened), exit code exactly 1 (101 = still panicking, 0 = dangling reloc shipped), no panicked at/RUST_BACKTRACE on stderr, reason string naming func_0 and the #851 class, and no partial object left behind. Plus a negative control: the same decline with NO dangling reloc stays a routine exit-0 skip with the object emitted.

Reference behaviour — measured, and a correction to #1013's table

#1013 says ARM and RISC-V exit 1 cleanly on the identical module. Measured (symtab-verified with pyelftools), neither does:

aarch64's ELF builder cannot express undefined externals yet (that is #1017-adjacent v0.60 capability work, explicitly out of scope). So the fix matches aarch64's own known-good refusal shape — the #851 active-data-segment decline in backend.rs (BackendError::CompilationFailed, reason naming the class, clean exit 1) — exactly the house style #1041 credits aarch64 for. No new policy invented; the rivet entry's description got a MEASURED CORRECTION paragraph recording this.

Panic-site class survey (aarch64 crate, non-test code)

Grepped panic!/unreachable!/unwrap()/expect(/direct indexing across crates/synth-backend-aarch64/src/:

  • Fixed (was the only one reachable from a declined symbol): elf.rs dangling-symbol lookup (formerly unwrap_or_else(panic!) at line 189) — now Err.
  • Remaining: 2, both genuine internal invariants over data the backend itself produced, NOT reachable from "retained function references declined function" (a declined function contributes no relocations and no branch placeholders):
    • elf.rs:195 — reloc-KIND wildcard (RelocKind other than the four AArch64 kinds reaching the aarch64 builder would be a cross-backend plumbing bug); defensive-panic convention.
    • selector.rs:2846 — patch_branch unreachable! on a non-placeholder word the selector itself emitted.
  • backend.rs, encoder.rs, substrate.rs, lib.rs: zero non-test panic sites.
  • The funcref-table path (substrate.table) feeds the SAME builder loop, so a table relocation against a declined function is covered by the same Err, not a sibling panic.

Salvage verdict

KEPT, with completion. The salvaged fix + test were judged correct: mechanism matches the house refusal style, test asserts the full contract, builder signature change is Ok-wrapping only on the success path (emitted bytes untouched — frozen anchors did not move; full workspace suite green). Added this run: red/green re-verification from rebuilt binaries, sibling measurement, class survey, rivet RQ-59-A64PANIC status proposed → implemented with the measured-sibling correction.

Gates

🤖 Generated with Claude Code

https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L

avrabe and others added 3 commits August 25, 2026 22:20
…ust refuse cleanly, not panic

Asserts the full refusal contract, not merely 'does not crash': exit code
exactly 1 (not Rust's panic 101), no panic markers on stderr, and a
machine-readable reason naming the declined symbol (func_0) and the #851
unrelocated-placeholder class. Negative control: the same decline with no
dangling relocation stays a routine exit-0 skip.

Currently FAILS on the exit-code assertion (measured: exit 101, panic at
crates/synth-backend-aarch64/src/elf.rs:189) — the fix lands next.

Sibling behaviour measured while writing this (a finding vs #1013's table):
arm and riscv both exit 0 on the identical module — ARM compiles the
95-target br_table outright; RISC-V declines func_0 and emits the object
with synth_func_0 as an UNDEFINED symbol, relocations retained (loud at
link time, #871). Full undefined-external parity for aarch64 is #1017/v0.60
scope; this lane only fixes the refusal mechanism.

Refs #1013

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…ncommitted changes

Committed by the hub coordinator so the work survives. NOT REVIEWED and NOT
COMPLETE: the lane stopped mid-task and had not committed in 15 hours. This is
a restore point, not a claim that anything is verified.

Touches crates/synth-backend-aarch64/src/{backend,elf}.rs,
crates/synth-cli/src/main.rs and the lane's own
crates/synth-cli/tests/a64_dangling_reloc_decline_1013.rs (99 insertions).

Refs #1013.
…ng correction

Status proposed -> implemented. The entry's parity table said ARM and RISC-V
exit 1 cleanly on the identical module; measured (symtab-verified), neither
does: ARM compiles the wide br_table outright (func_0 placed, exit 0), RISC-V
declines func_0 but emits the object with synth_func_0 as SHN_UNDEF and the
relocation retained (loud at link time, exit 0). aarch64 cannot express
undefined externals yet (#1017-adjacent v0.60 scope), so the landed fix
matches aarch64's OWN known-good refusal shape — the #851 data-segment
decline: clean exit 1, reason naming the dangling symbol and class, no
partial object. Recorded in the description so the requirement stops
asserting the unmeasured table.

rivet validate: 50 pre-existing errors before AND after this edit (the #1012
class); this commit adds none.

Refs #1013

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
@codecov

codecov Bot commented Aug 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.67442% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/synth-backend-aarch64/src/backend.rs 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe
avrabe merged commit 9c7681a into main Aug 25, 2026
62 checks passed
@avrabe
avrabe deleted the fix/a64-elf-panic-1013 branch August 25, 2026 23:19
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