From ea3327a7cdbafcbb0390df858253553fa043d943 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Sat, 12 Sep 2026 17:34:02 +0200 Subject: [PATCH] runtime: jump indirectly to longjmp on Xtensa tinygo_longjmp ended with a j instruction, which reaches only 128 KB. picolibc puts longjmp in the plain .text group of the linker script, while all TinyGo code goes into the .text.* group after it. In a large program the two are more than 128 KB apart and the link stops with an out of range R_XTENSA_SLOT0_OP relocation. Small programs link correctly, thus the smoke tests do not find this. Load the address of longjmp from a literal and jump to it with jx. The literal carries an R_XTENSA_32 relocation, which has no range limit. Place the literal by hand in the same section as the code. Automatic literals go into a separate section, which this tree already records as a cause of bad l32r offsets with LLVM 22 and lld. See the comments in src/device/esp/esp32.S and src/device/esp/esp32s3.S. Keep the jump instead of a call. tinygo_longjmp has no entry instruction and runs in the register window of its caller. A call would rotate the window a second time and give longjmp bad arguments. a8 is free in both ABIs. In the windowed ABI it becomes a0 of longjmp, which longjmp overwrites at once from the jmp_buf. In the call0 ABI it is a scratch register and longjmp reads only a2 and a3. The recover test passes on ESP32 in QEMU and on ESP32-S3 hardware. --- src/runtime/asm_xtensa.S | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/runtime/asm_xtensa.S b/src/runtime/asm_xtensa.S index 352fb32a4..38951ea5f 100644 --- a/src/runtime/asm_xtensa.S +++ b/src/runtime/asm_xtensa.S @@ -21,6 +21,10 @@ __xtensa_libgcc_window_spill: #endif .section .text.tinygo_longjmp,"ax",@progbits +// Literal data for l32r (must be at a lower address than the l32r). +.balign 4 +.Llongjmp_addr: + .word longjmp .global tinygo_longjmp .type tinygo_longjmp, %function tinygo_longjmp: @@ -30,5 +34,8 @@ tinygo_longjmp: #else movi a3, 1 #endif - j longjmp + // j only reaches 128 KB (Xtensa ISA Reference Manual, J instruction) and + // longjmp can be further away. a8 is free to use in both ABIs. + l32r a8, .Llongjmp_addr + jx a8 .size tinygo_longjmp, .-tinygo_longjmp