kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit e4406133899dbe7188e20d4f269d259b2df0d6a2
parent 98ca5ca79027bd63e9609ddcfc45d7ddcb020709
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Fri, 29 May 2026 13:22:00 -0700

rv64: emit TLS Local-Exec only, matching aa64/x64

rv_tls_addr_of routed extern-via-GOT _Thread_local symbols through an
Initial-Exec sequence emitting R_RV_TLS_GOT_HI20. Under -fPIE (the hosted
default) this reloc is reachable for ordinary extern TLS access, but the
linker has no layout or apply for it (reloc_uses_got omits it, reloc_apply
hits the default panic), so linking failed with 'unsupported reloc kind 80'
rather than producing a binary. The JIT HI20-pairing also doesn't recognize
it. cfree links the whole module statically, so Local-Exec (TPREL) is always
correct — exactly what aa64 (aa_tls_addr_of) and x64 (x64_tls_addr_of) already
do unconditionally.

Drop the IE/GOT branch (and the now-dead rv_pcrel_anchor helper). Add
test-rv64-tls-link, a host-agnostic link-only regression, to the default set.

Verified: extern _Thread_local for riscv64-linux now emits RV_TPREL_HI20/
LO12_I and links cleanly.

Diffstat:
Msrc/arch/rv64/native.c | 37++++++++-----------------------------
Atest/smoke/rv64_tls_link.sh | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/test.mk | 6++++++
3 files changed, 63 insertions(+), 29 deletions(-)

diff --git a/src/arch/rv64/native.c b/src/arch/rv64/native.c @@ -2123,40 +2123,19 @@ static void rv_alloca(NativeTarget* t, NativeLoc dst, NativeLoc size, /* ============================ TLS / bitfield / atomics ============================ */ -/* Define a fresh local .LpcrelHi anchor pointing at `ap`, for the - * R_RV_PCREL_LO12_I follow-on that pairs with an AUIPC-relative HI20 reloc. */ -static ObjSymId rv_pcrel_anchor(RvNativeTarget* a, ObjSecId sec, u32 ap) { - NativeTarget* t = &a->base; - Sym an = pool_intern_slice(t->c->global, SLICE_LIT(".LpcrelHi")); - return obj_symbol(t->obj, an, SB_LOCAL, SK_OBJ, sec, (u64)ap, 0); -} - static void rv_tls_addr_of(NativeTarget* t, NativeLoc dst, ObjSymId sym, i64 addend) { - RvNativeTarget* a = rv_of(t); MCEmitter* mc = t->mc; u32 sec = mc->section_id; u32 rd = loc_reg(dst); - if (obj_symbol_extern_via_got(t->c, t->obj, sym)) { - /* Initial-Exec: auipc t0, %tls_ie_pcrel_hi(sym) - * ld t0, %pcrel_lo(.Ltmp)(t0) - * add dst, tp, t0 - * GOT relocs disallow an addend, so apply it after the GOT load. */ - u32 ap = mc->pos(mc); - rv64_emit32(mc, rv_auipc(RV_TMP0, 0)); - mc->emit_reloc_at(mc, sec, ap, R_RV_TLS_GOT_HI20, sym, 0, 0, 0); - { - ObjSymId anchor = rv_pcrel_anchor(a, sec, ap); - u32 lp = mc->pos(mc); - rv64_emit32(mc, rv_ld(RV_TMP0, RV_TMP0, 0)); - mc->emit_reloc_at(mc, sec, lp, R_RV_PCREL_LO12_I, anchor, 0, 0, 0); - } - rv64_emit32(mc, rv_add(rd, RV_TP, RV_TMP0)); - if (addend) rv_emit_addr_adjust(mc, rd, rd, (i32)addend); - return; - } - /* Local-Exec: lui t0, %tprel_hi(sym); add t0, tp, t0; addi dst, t0, - * %tprel_lo(sym). */ + /* Local-Exec only, matching aa64 (aa_tls_addr_of) and x64 (x64_tls_addr_of): + * cfree links the whole module statically, so every _Thread_local symbol is + * resolved within the image and TPREL is always valid. An Initial-Exec GOT + * path (R_RV_TLS_GOT_HI20) used to be emitted for extern-via-GOT symbols + * under -fPIE (the hosted default), but the linker has no layout/apply for + * that reloc, so it produced a hard "unsupported reloc kind" link failure + * rather than a working binary. */ + /* lui t0, %tprel_hi(sym); add t0, tp, t0; addi dst, t0, %tprel_lo(sym). */ { u32 hp = mc->pos(mc); rv64_emit32(mc, rv_lui(RV_TMP0, 0)); diff --git a/test/smoke/rv64_tls_link.sh b/test/smoke/rv64_tls_link.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# test/smoke/rv64_tls_link.sh — regression for rv64 TLS Local-Exec lowering. +# +# rv64 used to emit R_RV_TLS_GOT_HI20 (Initial-Exec) for an extern +# _Thread_local symbol under -fPIE (the hosted default). The linker has no +# layout/apply for that reloc, so linking failed hard with +# "link: unsupported reloc kind 80" instead of producing a binary. cfree +# links whole-module/static, so the fix is to always emit Local-Exec +# (R_RV_TPREL_HI20/LO12_I), matching the aa64 and x64 backends. +# +# This is link-only (no execution), so it runs on any host without qemu. + +set -u +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +CFREE="${CFREE:-$ROOT/build/cfree}" +WORK="$ROOT/build/test/rv64-tls-link" +mkdir -p "$WORK" + +if [ ! -x "$CFREE" ]; then + echo "SKIP rv64_tls_link: $CFREE not built" + exit 0 +fi + +cat > "$WORK/tls_def.c" <<'EOF' +_Thread_local int g = 7; +EOF +cat > "$WORK/tls_ie.c" <<'EOF' +extern _Thread_local int g; +int read_g(void) { return g; } +int *addr_g(void) { return &g; } +EOF + +fail() { printf 'FAIL rv64_tls_link: %s\n' "$1"; exit 1; } + +"$CFREE" cc -c -target riscv64-linux "$WORK/tls_ie.c" -o "$WORK/tls_ie.o" \ + || fail "compile tls_ie.c" +"$CFREE" cc -c -target riscv64-linux "$WORK/tls_def.c" -o "$WORK/tls_def.o" \ + || fail "compile tls_def.c" + +# The extern _Thread_local access must lower to TPREL, never TLS_GOT. +relocs="$("$CFREE" objdump -r "$WORK/tls_ie.o" 2>&1)" +echo "$relocs" | grep -q 'RV_TPREL_HI20' || fail "expected RV_TPREL_HI20 reloc; got: $relocs" +if echo "$relocs" | grep -q 'TLS_GOT'; then fail "unexpected TLS_GOT reloc (Initial-Exec regressed): $relocs"; fi + +# And the whole-module link must succeed (previously: unsupported reloc kind 80). +"$CFREE" ld --entry read_g "$WORK/tls_ie.o" "$WORK/tls_def.o" -o "$WORK/tls_out" \ + || fail "link extern _Thread_local" + +echo "OK rv64_tls_link" diff --git a/test/test.mk b/test/test.mk @@ -78,6 +78,7 @@ TEST_TARGETS = \ test-rt-runtime \ test-rv64-inline \ test-rv64-jit \ + test-rv64-tls-link \ test-smoke-rv64 \ test-smoke-x64 \ test-toy \ @@ -105,6 +106,7 @@ DEFAULT_TEST_TARGETS = \ test-aa64-inline \ test-rv64-inline \ test-rv64-jit \ + test-rv64-tls-link \ test-emu \ test-x64-inline \ test-x64-dbg \ @@ -346,6 +348,10 @@ $(RV64_JIT_TEST_BIN): test/link/rv64_jit_test.c $(LIB_AR) @mkdir -p $(dir $@) $(CC) $(TEST_HOST_CFLAGS) test/link/rv64_jit_test.c $(LIB_AR) -o $@ +# Link-only regression for rv64 TLS Local-Exec lowering (runs on any host). +test-rv64-tls-link: bin + @CFREE='$(abspath $(BIN))' bash test/smoke/rv64_tls_link.sh + X64_INLINE_TEST_BIN = build/test/x64_inline_test test-x64-inline: $(X64_INLINE_TEST_BIN)