kit

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

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:
Mdoc/BACKEND_REFACTOR_CROSS_TEST.md | 32++++++++++++++++++++++++++++++++
Msrc/arch/native_target.h | 43+++++++++++++++++++++++++++++++++++++++++++
Msrc/arch/x64/native.c | 20++++++++++++++++++++
Msrc/obj/elf/elf.h | 4++++
Msrc/obj/registry.c | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/opt/pass_native_emit.c | 3++-
Atest/elf/unit/freebsd_note_target.c | 154+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/link/harness/start.c | 21++++++++-------------
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, &note_off) || + !detect_elf_read_uint(d, len, entry_off + size_off, value_width, + big_endian, &note_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