commit ee05a9bd27505aa0728952971e0a686ccecbfd75
parent 03954357a132585b90688f2e6551880974081941
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Sun, 19 Jul 2026 11:11:29 -0700
fix(rv64): repair backend refactor regressions
Diffstat:
8 files changed, 402 insertions(+), 16 deletions(-)
diff --git a/doc/BACKEND_REFACTOR_CROSS_TEST.md b/doc/BACKEND_REFACTOR_CROSS_TEST.md
@@ -36,3 +36,35 @@ Linux x64 QEMU-user processes reported SIGSEGV and then hung; the affected test
Full log: `build/test-logs/test-cross-all-full.log`
The tested binaries were built from clean revision `8d7927db`. Workspace edits made after that build were not included in this run.
+
+## RV64 follow-up
+
+The RV64 failures above had three separate causes:
+
+- The optimized emitter forwarded folded integer constants to every native
+ intrinsic as `NATIVE_LOC_IMM`, but the RISC-V intrinsic hook consumes value
+ operands as registers. The NativeTarget boundary now materializes value
+ immediates by default and has an explicit per-target capability for the few
+ backends that accept them; shared control immediates remain target-neutral.
+- The hosted RISC-V TLS lowering already followed the psABI: `tp` points at the
+ start of the TLS image. The executable test harness copied `.tdata` 16 bytes
+ past `tp`, so initialized TLS read the zeroed guard area. The harness now sets
+ `tp` to the actual image address.
+- FreeBSD's RISC-V `libc.so.7` identifies itself with a FreeBSD ELF note while
+ leaving `EI_OSABI` as System V. ELF detection now recognizes a bounded
+ `NT_FREEBSD_ABI_TAG` note in either a `PT_NOTE` segment or `SHT_NOTE` section,
+ with a synthetic unit test covering positive and negative identities.
+
+Targeted validation after the fixes:
+
+- RV64 C/parser overflow and initialized-TLS cases, plus the Toy intrinsic
+ overflow case, pass at O0 and O1.
+- `test-rv64-tls-link` and `test-rv64-inline` pass.
+- RV64 assembler encode/decode coverage passes 43 cases with the one existing
+ missing-golden skip; the decoder unit passes 101 checks.
+- A dynamically linked FreeBSD RISC-V toy executable links and runs in the VM
+ at O0 and O1.
+- The shared native-boundary unit tests pass, and the overflow case remains
+ green at O0/O1 on x86-64 and AArch64.
+
+This was a targeted RV64 follow-up, not a repeat of the full 19-target matrix.
diff --git a/src/arch/native_target.h b/src/arch/native_target.h
@@ -726,6 +726,15 @@ struct NativeTarget {
KitCgTypeId type);
void (*va_end_)(NativeTarget*, NativeLoc ap_ptr);
void (*va_copy_)(NativeTarget*, NativeLoc dst_ap_ptr, NativeLoc src_ap_ptr);
+ /* Value arguments reach the backend as NATIVE_LOC_REG unless the backend
+ * explicitly accepts an immediate for that intrinsic operand. The shared
+ * target-sequence/control operands recognized by
+ * native_intrinsic_arg_accepts_imm below also remain NATIVE_LOC_IMM when the
+ * IR supplied a constant. Keeping this capability explicit prevents a
+ * backend from accidentally decoding an immediate's union payload as a
+ * register while allowing targets with private materialization registers to
+ * avoid unnecessary pressure on the optimizer's operand-temp bank. */
+ int (*intrinsic_arg_accepts_imm)(NativeTarget*, IntrinKind, u32 arg_index);
void (*intrinsic)(NativeTarget*, IntrinKind, const NativeLoc* dsts, u32 ndst,
const NativeLoc* args, u32 narg);
void (*asm_block)(NativeTarget*, const char* tmpl, const AsmConstraint* outs,
@@ -767,6 +776,40 @@ static inline u32 native_target_callee_saved_mask(NativeTarget* t,
return ci ? ci->callee_saved_mask : 0u;
}
+/* Intrinsic operands whose immediate form is shared across every NativeTarget.
+ * Other immediates are scalar values: the optimized emitter materializes them
+ * unless the target explicitly advertises support through its capability
+ * hook. */
+static inline int native_intrinsic_arg_accepts_imm(NativeTarget* t,
+ IntrinKind kind,
+ u32 arg_index) {
+ switch (kind) {
+ case INTRIN_MEMMOVE:
+ return arg_index == 2u; /* constant byte count */
+ case INTRIN_PREFETCH:
+ return arg_index >= 1u; /* rw, locality */
+ case INTRIN_ASSUME_ALIGNED:
+ return arg_index >= 1u; /* alignment, offset */
+ case INTRIN_EXPECT:
+ return arg_index == 1u; /* expected value hint */
+ case INTRIN_SYSCALL:
+ /* Syscall hooks already use their target's parallel ABI-argument mover,
+ * which accepts both registers and immediates and avoids requiring up to
+ * seven simultaneous emitter-temp leases. */
+ return 1;
+ case INTRIN_DMB:
+ case INTRIN_DSB:
+ return arg_index == 0u; /* KitCgBarrierScope */
+ case INTRIN_FRAME_ADDRESS:
+ case INTRIN_RETURN_ADDRESS:
+ return arg_index == 0u; /* constant frame-chain level */
+ default:
+ return t && t->intrinsic_arg_accepts_imm
+ ? t->intrinsic_arg_accepts_imm(t, kind, arg_index)
+ : 0;
+ }
+}
+
/* Location constructors. A designated compound literal initializes the named
* fields and zero-fills the rest (so it is value-identical to the former
* memset + field stores) but lets the compiler emit only the needed stores
diff --git a/src/arch/x64/native.c b/src/arch/x64/native.c
@@ -3984,6 +3984,25 @@ static u32 x64_intrinsic_arg_reg(X64NativeTarget* a, NativeLoc arg,
x64_panic(a, "arithmetic intrinsic operand is not register/immediate");
}
+static int x64_intrinsic_arg_accepts_imm(NativeTarget* t, IntrinKind kind,
+ u32 arg_index) {
+ (void)t;
+ if (arg_index > 1u) return 0;
+ switch (kind) {
+ case INTRIN_SADD_OVERFLOW:
+ case INTRIN_UADD_OVERFLOW:
+ case INTRIN_SSUB_OVERFLOW:
+ case INTRIN_USUB_OVERFLOW:
+ case INTRIN_SMUL_OVERFLOW:
+ case INTRIN_UMUL_OVERFLOW:
+ case INTRIN_SMUL_HIGH:
+ case INTRIN_UMUL_HIGH:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
static void x64_intrinsic(NativeTarget* t, IntrinKind kind,
const NativeLoc* dsts, u32 ndst,
const NativeLoc* args, u32 narg) {
@@ -4782,6 +4801,7 @@ NativeTarget* x64_native_target_new(Compiler* c, ObjBuilder* obj,
t->va_arg_ = x64_va_arg_native;
t->va_end_ = x64_va_end_native;
t->va_copy_ = x64_va_copy_native;
+ t->intrinsic_arg_accepts_imm = x64_intrinsic_arg_accepts_imm;
t->intrinsic = x64_intrinsic;
t->asm_block = x64_asm_block_native;
t->file_scope_asm = native_file_scope_asm;
diff --git a/src/obj/elf/elf.h b/src/obj/elf/elf.h
@@ -35,6 +35,7 @@
#define ELFCLASS32 1
#define ELFCLASS64 2
#define ELFDATA2LSB 1
+#define ELFDATA2MSB 2
#define EV_CURRENT 1
#define ELFOSABI_NONE 0
/* Bare-metal / freestanding (`*-none-elf`). kit stamps this so a freestanding
@@ -210,6 +211,9 @@ static inline u8 elf_st_other(u8 vis /* SymVis */) {
#define PF_W 0x2u
#define PF_R 0x4u
+/* ---- note types ---- */
+#define NT_FREEBSD_ABI_TAG 1u
+
/* ---- e_flags (ARM EABI bits, EM_ARM) ----
* EABI version is the top byte; the float-ABI is two independent flag bits
* (a soft object sets SOFT, a hard object sets HARD, an old object neither). */
diff --git a/src/obj/registry.c b/src/obj/registry.c
@@ -368,6 +368,140 @@ static void detect_set_ptr(KitTargetSpec* t, KitArchKind arch) {
#endif
#if KIT_OBJ_ELF_ENABLED
+static int detect_elf_read_uint(const u8* d, size_t len, u64 off, u32 width,
+ int big_endian, u64* out) {
+ u64 value = 0;
+ size_t pos;
+ if (!out || (width != 2u && width != 4u && width != 8u) || off > len ||
+ width > len - (size_t)off)
+ return 0;
+ pos = (size_t)off;
+ for (u32 i = 0; i < width; ++i) {
+ u32 shift = big_endian ? (width - i - 1u) * 8u : i * 8u;
+ value |= (u64)d[pos + i] << shift;
+ }
+ *out = value;
+ return 1;
+}
+
+/* FreeBSD toolchains commonly leave EI_OSABI at System V, including on
+ * shared libraries such as libc.so.7, and carry the platform identity in an
+ * NT_FREEBSD_ABI_TAG note instead. The object registry owns target identity,
+ * so recognize that standard note here rather than teaching the linker a
+ * filename/sysroot exception. */
+static int detect_elf_note_region_is_freebsd(const u8* d, size_t len, u64 off,
+ u64 size, int big_endian) {
+ static const u8 owner[8] = {'F', 'r', 'e', 'e', 'B', 'S', 'D', 0};
+ size_t pos, end;
+ if (off > len || size > len - (size_t)off) return 0;
+ pos = (size_t)off;
+ end = pos + (size_t)size;
+ while (end - pos >= 12u) {
+ u64 namesz64, descsz64, type64;
+ size_t namesz, descsz, name_span, desc_span, entry_size;
+ if (!detect_elf_read_uint(d, len, pos, 4u, big_endian, &namesz64) ||
+ !detect_elf_read_uint(d, len, pos + 4u, 4u, big_endian, &descsz64) ||
+ !detect_elf_read_uint(d, len, pos + 8u, 4u, big_endian, &type64))
+ return 0;
+ namesz = (size_t)namesz64;
+ descsz = (size_t)descsz64;
+ name_span = (namesz + 3u) & ~(size_t)3u;
+ desc_span = (descsz + 3u) & ~(size_t)3u;
+ if (name_span < namesz || desc_span < descsz ||
+ name_span > end - pos - 12u ||
+ desc_span > end - pos - 12u - name_span)
+ return 0;
+ entry_size = 12u + name_span + desc_span;
+ if (type64 == NT_FREEBSD_ABI_TAG && namesz == sizeof owner &&
+ memcmp(d + pos + 12u, owner, sizeof owner) == 0)
+ return 1;
+ pos += entry_size;
+ }
+ return 0;
+}
+
+static int detect_elf_table_has_freebsd_note(
+ const u8* d, size_t len, u64 table_off, u64 entry_size, u64 entry_count,
+ u32 type_off, u32 note_type, u32 offset_off, u32 size_off, u32 value_width,
+ int big_endian) {
+ u64 min_size = (u64)size_off + value_width;
+ if (entry_size < min_size || table_off > len || entry_size == 0u) return 0;
+ for (u64 i = 0; i < entry_count; ++i) {
+ u64 entry_off, kind, note_off, note_size;
+ if (i > ((u64)len - table_off) / entry_size) break;
+ entry_off = table_off + i * entry_size;
+ if (entry_off > len || entry_size > (u64)len - entry_off) break;
+ if (!detect_elf_read_uint(d, len, entry_off + type_off, 4u, big_endian,
+ &kind) ||
+ kind != note_type)
+ continue;
+ if (!detect_elf_read_uint(d, len, entry_off + offset_off, value_width,
+ big_endian, ¬e_off) ||
+ !detect_elf_read_uint(d, len, entry_off + size_off, value_width,
+ big_endian, ¬e_size))
+ continue;
+ if (detect_elf_note_region_is_freebsd(d, len, note_off, note_size,
+ big_endian))
+ return 1;
+ }
+ return 0;
+}
+
+static int detect_elf_has_freebsd_note(const u8* d, size_t len, u8 ei_class,
+ u8 ei_data) {
+ int big_endian = ei_data == ELFDATA2MSB;
+ u64 phoff, shoff, phentsize, phnum, shentsize, shnum;
+ u32 phoff_off, shoff_off, phentsize_off, phnum_off, shentsize_off,
+ shnum_off, word_width, ph_offset_off, ph_size_off, sh_offset_off,
+ sh_size_off;
+ if (ei_class == ELFCLASS64) {
+ phoff_off = 32u;
+ shoff_off = 40u;
+ phentsize_off = 54u;
+ phnum_off = 56u;
+ shentsize_off = 58u;
+ shnum_off = 60u;
+ word_width = 8u;
+ ph_offset_off = 8u;
+ ph_size_off = 32u;
+ sh_offset_off = 24u;
+ sh_size_off = 32u;
+ } else if (ei_class == ELFCLASS32) {
+ phoff_off = 28u;
+ shoff_off = 32u;
+ phentsize_off = 42u;
+ phnum_off = 44u;
+ shentsize_off = 46u;
+ shnum_off = 48u;
+ word_width = 4u;
+ ph_offset_off = 4u;
+ ph_size_off = 16u;
+ sh_offset_off = 16u;
+ sh_size_off = 20u;
+ } else {
+ return 0;
+ }
+ if (!detect_elf_read_uint(d, len, phoff_off, word_width, big_endian,
+ &phoff) ||
+ !detect_elf_read_uint(d, len, shoff_off, word_width, big_endian,
+ &shoff) ||
+ !detect_elf_read_uint(d, len, phentsize_off, 2u, big_endian,
+ &phentsize) ||
+ !detect_elf_read_uint(d, len, phnum_off, 2u, big_endian, &phnum) ||
+ !detect_elf_read_uint(d, len, shentsize_off, 2u, big_endian,
+ &shentsize) ||
+ !detect_elf_read_uint(d, len, shnum_off, 2u, big_endian, &shnum))
+ return 0;
+
+ if (detect_elf_table_has_freebsd_note(
+ d, len, phoff, phentsize, phnum, 0u, PT_NOTE, ph_offset_off,
+ ph_size_off, word_width, big_endian))
+ return 1;
+ return detect_elf_table_has_freebsd_note(
+ d, len, shoff, shentsize, shnum, 4u, SHT_NOTE, sh_offset_off,
+ sh_size_off, word_width, big_endian);
+}
+
static KitStatus detect_elf(const u8* d, size_t len, KitTargetSpec* out) {
u8 ei_class, ei_data, ei_osabi;
u16 e_machine;
@@ -409,9 +543,12 @@ static KitStatus detect_elf(const u8* d, size_t len, KitTargetSpec* out) {
* by class above; this also rejects a class/machine mismatch such as a
* 64-bit arch object whose EI_CLASS byte claims ELFCLASS32. */
if (ei_class != ((out->ptr_size == 4) ? 1u : 2u)) return KIT_MALFORMED;
- if (ei_osabi == 0 || ei_osabi == 3)
+ if (ei_osabi == ELFOSABI_NONE &&
+ detect_elf_has_freebsd_note(d, len, ei_class, ei_data))
+ out->os = KIT_OS_FREEBSD;
+ else if (ei_osabi == ELFOSABI_NONE || ei_osabi == ELFOSABI_GNU)
out->os = KIT_OS_LINUX;
- else if (ei_osabi == 9)
+ else if (ei_osabi == ELFOSABI_FREEBSD)
out->os = KIT_OS_FREEBSD;
else
out->os = KIT_OS_FREESTANDING;
diff --git a/src/opt/pass_native_emit.c b/src/opt/pass_native_emit.c
@@ -2397,7 +2397,8 @@ static void emit_inst_body(NativeEmitCtx* e, u32 block, u32 order_index,
? arena_array(e->f->arena, NativeLoc, aux->narg)
: NULL;
for (u32 i = 0; aux && i < aux->narg; ++i) {
- if (aux->args[i].kind == OPK_IMM) {
+ if (aux->args[i].kind == OPK_IMM &&
+ native_intrinsic_arg_accepts_imm(e->target, aux->kind, i)) {
args[i] = loc_from_operand(e, &aux->args[i], in->loc);
} else {
args[i] = materialize_operand(
diff --git a/test/elf/unit/freebsd_note_target.c b/test/elf/unit/freebsd_note_target.c
@@ -0,0 +1,154 @@
+/* A FreeBSD shared object may leave EI_OSABI at System V and carry its
+ * platform identity only in an NT_FREEBSD_ABI_TAG note. Target detection
+ * must consult that note before the linker performs OS compatibility checks. */
+
+#include <kit/object.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "lib/kit_unit.h"
+
+enum {
+ ELF64_EHDR_SIZE = 64,
+ ELF64_PHDR_SIZE = 56,
+ ELF64_SHDR_SIZE = 64,
+ NOTE_SIZE = 24,
+ NOTE_OFFSET = ELF64_EHDR_SIZE + ELF64_PHDR_SIZE,
+ SECTION_NOTE_OFFSET = ELF64_EHDR_SIZE + ELF64_SHDR_SIZE,
+ IMAGE_SIZE = SECTION_NOTE_OFFSET + NOTE_SIZE,
+};
+
+static void put16le(uint8_t* p, uint16_t v) {
+ p[0] = (uint8_t)v;
+ p[1] = (uint8_t)(v >> 8);
+}
+
+static void put32le(uint8_t* p, uint32_t v) {
+ p[0] = (uint8_t)v;
+ p[1] = (uint8_t)(v >> 8);
+ p[2] = (uint8_t)(v >> 16);
+ p[3] = (uint8_t)(v >> 24);
+}
+
+static void put64le(uint8_t* p, uint64_t v) {
+ put32le(p, (uint32_t)v);
+ put32le(p + 4, (uint32_t)(v >> 32));
+}
+
+static void make_rv64_elf_with_note(uint8_t image[IMAGE_SIZE],
+ const char owner[8], uint32_t type) {
+ uint8_t* ph;
+ uint8_t* note;
+ memset(image, 0, IMAGE_SIZE);
+
+ image[0] = 0x7f;
+ image[1] = 'E';
+ image[2] = 'L';
+ image[3] = 'F';
+ image[4] = 2; /* ELFCLASS64 */
+ image[5] = 1; /* ELFDATA2LSB */
+ image[6] = 1; /* EV_CURRENT */
+ image[7] = 0; /* ELFOSABI_NONE / System V */
+ put16le(image + 16, 3); /* ET_DYN */
+ put16le(image + 18, 243); /* EM_RISCV */
+ put32le(image + 20, 1); /* EV_CURRENT */
+ put64le(image + 32, ELF64_EHDR_SIZE);
+ put16le(image + 52, ELF64_EHDR_SIZE);
+ put16le(image + 54, ELF64_PHDR_SIZE);
+ put16le(image + 56, 1);
+
+ ph = image + ELF64_EHDR_SIZE;
+ put32le(ph, 4); /* PT_NOTE */
+ put64le(ph + 8, NOTE_OFFSET);
+ put64le(ph + 32, NOTE_SIZE);
+ put64le(ph + 40, NOTE_SIZE);
+ put64le(ph + 48, 4);
+
+ note = image + NOTE_OFFSET;
+ put32le(note, 8); /* namesz: "FreeBSD\0" */
+ put32le(note + 4, 4); /* descsz: ABI version */
+ put32le(note + 8, type);
+ memcpy(note + 12, owner, 8);
+ put32le(note + 20, 1500068);
+}
+
+static void make_rv64_elf_with_section_note(uint8_t image[IMAGE_SIZE],
+ const char owner[8],
+ uint32_t type) {
+ uint8_t* sh;
+ uint8_t* note;
+ make_rv64_elf_with_note(image, owner, type);
+ put64le(image + 32, 0); /* no program-header table */
+ put16le(image + 54, 0);
+ put16le(image + 56, 0);
+ put64le(image + 40, ELF64_EHDR_SIZE);
+ put16le(image + 58, ELF64_SHDR_SIZE);
+ put16le(image + 60, 1);
+
+ sh = image + ELF64_EHDR_SIZE;
+ memset(sh, 0, ELF64_SHDR_SIZE);
+ put32le(sh + 4, 7); /* SHT_NOTE */
+ put64le(sh + 24, SECTION_NOTE_OFFSET);
+ put64le(sh + 32, NOTE_SIZE);
+ put64le(sh + 48, 4);
+
+ note = image + SECTION_NOTE_OFFSET;
+ put32le(note, 8);
+ put32le(note + 4, 4);
+ put32le(note + 8, type);
+ memcpy(note + 12, owner, 8);
+ put32le(note + 20, 1500068);
+}
+
+int main(void) {
+ const char freebsd_owner[8] = {'F', 'r', 'e', 'e',
+ 'B', 'S', 'D', '\0'};
+ const char other_owner[8] = {'N', 'o', 't', 'F',
+ 'r', 'e', 'e', '\0'};
+ uint8_t image[IMAGE_SIZE];
+ KitTargetSpec target;
+ KitUnit u;
+
+ kit_unit_init(&u);
+
+ make_rv64_elf_with_note(image, freebsd_owner, 1);
+ memset(&target, 0, sizeof target);
+ CU_EXPECT(&u, kit_detect_target(image, sizeof image, &target) == KIT_OK,
+ "SysV-branded FreeBSD ELF is detected");
+ CU_EXPECT(&u, target.arch == KIT_ARCH_RV64, "RISC-V arch is preserved");
+ CU_EXPECT(&u, target.obj == KIT_OBJ_ELF, "ELF format is preserved");
+ CU_EXPECT(&u, target.os == KIT_OS_FREEBSD,
+ "NT_FREEBSD_ABI_TAG supplies FreeBSD identity");
+
+ make_rv64_elf_with_note(image, other_owner, 1);
+ memset(&target, 0, sizeof target);
+ CU_EXPECT(&u, kit_detect_target(image, sizeof image, &target) == KIT_OK,
+ "generic SysV ELF is detected");
+ CU_EXPECT(&u, target.os == KIT_OS_LINUX,
+ "unrecognized notes do not change the SysV default");
+
+ make_rv64_elf_with_note(image, freebsd_owner, 2);
+ memset(&target, 0, sizeof target);
+ CU_EXPECT(&u, kit_detect_target(image, sizeof image, &target) == KIT_OK,
+ "ELF with a non-ABI FreeBSD note is detected");
+ CU_EXPECT(&u, target.os == KIT_OS_LINUX,
+ "only NT_FREEBSD_ABI_TAG changes target identity");
+
+ make_rv64_elf_with_section_note(image, freebsd_owner, 1);
+ memset(&target, 0, sizeof target);
+ CU_EXPECT(&u, kit_detect_target(image, sizeof image, &target) == KIT_OK,
+ "section-only FreeBSD ELF is detected");
+ CU_EXPECT(&u, target.os == KIT_OS_FREEBSD,
+ "SHT_NOTE FreeBSD ABI tag supplies target identity");
+
+ make_rv64_elf_with_note(image, freebsd_owner, 1);
+ put32le(image + NOTE_OFFSET, UINT32_MAX);
+ memset(&target, 0, sizeof target);
+ CU_EXPECT(&u, kit_detect_target(image, sizeof image, &target) == KIT_OK,
+ "malformed note does not invalidate an otherwise detectable ELF");
+ CU_EXPECT(&u, target.os == KIT_OS_LINUX,
+ "out-of-bounds note payload is ignored safely");
+
+ kit_unit_summary(&u, "freebsd_note_target");
+ return kit_unit_status(&u);
+}
diff --git a/test/link/harness/start.c b/test/link/harness/start.c
@@ -35,14 +35,12 @@ extern char __tbss_size[]; /* SK_ABS: address-of yields the byte count */
/* TLS-block prologue layout — per-arch ABI dictates whether the TCB sits
* before or after .tdata in the thread-pointer-relative image. AArch64
* keeps a 16-byte reserved TCB; SysV-x86_64 uses TLS variant II (negative
- * offsets from the thread pointer, see below); RISC-V LP64 follows
- * variant I and points the thread pointer at the TCB end. */
+ * offsets from the thread pointer, see below); RISC-V LP64 follows variant I
+ * but points the thread pointer at the TLS image itself. */
#define AARCH64_TCB_SIZE 16
-/* Per-thread TLS image; the test harness is single-threaded so a
- * file-scope buffer is enough. Sized generously for any test we run
- * here. Layout: [TCB | .tdata copy | .tbss zero-fill] for variants
- * that put the TCB first. */
+/* Per-thread TLS workspace; the test harness is single-threaded so a
+ * file-scope buffer is enough. Sized generously for any test we run here. */
static char g_tls_block[4096] __attribute__((aligned(16)));
/* IFUNC startup init. Mirrors rt/lib/kit/ifunc_init.c — duplicated
@@ -137,16 +135,13 @@ static void tls_init(void) {
: "r"(rdi), "r"(rsi)
: "rcx", "r11", "memory");
#elif defined(__riscv) && __riscv_xlen == 64
- /* Variant I: tp -> [TCB | tdata | tbss], TCB is reserved (here just
- * the first 16 bytes of the block); RISC-V psABI puts tp 16 bytes
- * past the start of the static TLS block convention varies, but
- * the unwind/glibc convention used by linker-generated code
- * resolves &var via tp + offset_from_TLS_image_start. We place
- * .tdata immediately after a 16-byte reservation. */
+ /* RISC-V variant I: tp points at the TLS image start and TPREL offsets are
+ * relative to that address. Keep a little guard space ahead of the image,
+ * but point tp at the same byte where the .tdata copy begins. */
char* dst = g_tls_block + 16;
for (i = 0; i < td_n; ++i) dst[i] = __tdata_start[i];
for (i = 0; i < bs_n; ++i) dst[td_n + i] = 0;
- __asm__ volatile("mv tp, %0" ::"r"(g_tls_block) : "memory");
+ __asm__ volatile("mv tp, %0" ::"r"(dst) : "memory");
#else
#error "start.c: unsupported architecture"
#endif