← board

--emit-obj: @proc fixups require an iram/interrupt routine — plain callbacks can't be registered

Symptom

Taking a plain (non-iram;, non-interrupt;) procedure's address in a program compiled to a relocatable object fails:

procedure Tick(arg: Pointer);   { plain proc — esp_timer callback, task context }
begin ... end;
...
p := Pointer(@Tick);            { or a proc-type value handed to the SDK }
pascal26:2: error: --emit-obj: @proc fixups need an iram/interrupt routine present (non-iram .o path not wired)

Marking the routine iram; compiles fine (the existing writeELF32RelIram path emits the R_RISCV_32 reloc against the proc symbol).

Why it matters

esp_timer / GPIO / ADC callbacks on ESP-IDF are task-context callbacks — they specifically do NOT want IRAM placement (the whole point of feature-esp-peripheral-callback-api slice 1). Today every SDK callback must be mislabeled iram; just to get its address into the .o, wasting IRAM and contradicting the "no iram; in app code" acceptance of the callback-API ticket. The interim example examples/esp32/timer-c3 carries exactly that workaround + a pointer to this ticket.

Acceptance

2026-07-14 — RESOLVED (b360)

A program with @proc fixups but no iram routine now routes through the extended two-text-section writer (which already relocates ProcAddrFix against the proc's LOCAL symbol); with no iram procs its .iram1.text is simply empty. Verified on qemu esp32c3 IDF: a PLAIN routine's address assigned to a proc-type variable and called indirectly runs (tick via ptr 1/2, hits=2). timer-c3's callback dropped its iram; and still builds and boots.

Honest residual: timer-c3 still reports ticks=0 status=2 — the esp_timer callback never fires under qemu. Identical with and without iram;, and the @proc reloc is proven good by the indirect-call probe, so it is the esptimer library ↔ qemu emulation interaction, not the .o writer. Belongs to feature-esp-peripheral-callback-api's validation, not this ticket.

Log