From 805d5cbfdb8e8ef31904c387a9d5b94ebae7da74 Mon Sep 17 00:00:00 2001 From: Firat17aze Date: Thu, 12 Nov 2026 21:20:20 -0500 Subject: [PATCH] avr: preserve symbols after byte selectors The whole-template assembler treated lo8 and the other AVR byte-selection operators as undefined symbols. In a multi-statement inline asm block, that occupied the external-symbol slot before a following call or jump and left its real target unrelocatable. Classify the AVR expression operators as assembler words, alongside the numeric I/O register names. The regression uses the two 18-character EmbLinkRTOS entry points, failed before this change, and now verifies that both full names reach the relocation table. Validation: PATH=/opt/homebrew/opt/llvm/bin:$PATH sh tests/golden/avr-inline-asm.sh (passed, including QEMU at -O0/-O1/-O2/-Os). Host build passed with -std=c99 -Wall -Wextra -Werror. make check could not run the corpus because the configured x86-64 newlib and reference libstdc++ trees are absent. --- src/arch/avr/asm.c | 16 +++++++++++++++- tests/golden/avr-inline-asm.sh | 23 +++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/arch/avr/asm.c b/src/arch/avr/asm.c index 7b67a15c..31ee2dc5 100644 --- a/src/arch/avr/asm.c +++ b/src/arch/avr/asm.c @@ -188,8 +188,22 @@ static long io_name(const char *s, int len) * inline asm statement does. */ int avrasm_is_word(const char *stmt, const char *w, int len) { + static const char *const op[] = { + "lo8", "hi8", "hlo8", "hh8", + "pm_lo8", "pm_hi8", "pm_hh8", "gs" + }; (void)stmt; - return io_name(w, len) >= 0; + if (io_name(w, len) >= 0) + return 1; + /* These are expression operators, not symbols. Without naming them + * here, gas.c records (for example) `lo8` as the first undefined symbol + * in a multi-statement inline template, then cannot relocate the real + * `call` or `jmp` symbol that follows it. */ + for (unsigned i = 0; i < sizeof op / sizeof op[0]; i++) + if ((int)strlen(op[i]) == len && + strncmp(w, op[i], (size_t)len) == 0) + return 1; + return 0; } static long expr_prim(const char **p, struct expr_ctx *x) diff --git a/tests/golden/avr-inline-asm.sh b/tests/golden/avr-inline-asm.sh index 441f2a91..94556209 100755 --- a/tests/golden/avr-inline-asm.sh +++ b/tests/golden/avr-inline-asm.sh @@ -74,6 +74,29 @@ printf 'unsigned int f(unsigned char a, unsigned char b)\n'\ with 'clr r1' is the idiom -- refusing it forbids using mul at all:" head -4 "$out/m.err"; exit 1; } +# A constant byte-selection expression in one statement must not change how +# the following statement's symbol is scanned. The 18-character names are +# the two EmbLinkRTOS AVR entry points that exposed the cut at 16 characters. +cat > "$out/long-symbol.c" <<'EOF' +void emb_arch_switch_to(void); +void embk_thread_launch(void); +void f(void) +{ + __asm__ volatile("ldi r18, lo8(2303)\n\t" + "call emb_arch_switch_to\n\t" + "jmp embk_thread_launch" ::: "r18"); +} +EOF +"$EMBCC" --target=avr -Os -c "$out/long-symbol.c" \ + -o "$out/long-symbol.o" 2> "$out/long-symbol.err" || { + echo "lo8() made a following long symbol fail to assemble:" + cat "$out/long-symbol.err"; exit 1; } +for s in emb_arch_switch_to embk_thread_launch; do + llvm-readelf -r "$out/long-symbol.o" | grep -q "$s" || { + echo "the inline asm's long symbol was cut: $s" + llvm-readelf -r "$out/long-symbol.o"; exit 1; } +done + QEMU=${EMBCC_QEMU_AVR:-qemu-system-avr} command -v "$QEMU" >/dev/null 2>&1 || { echo "the refusals hold (SKIP: no $QEMU to run the rest)"; exit 0; }