kit

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

commit 516e7b47cb094dd0efd52a836af95a5a45672102
parent 3e9ce3177b318a8cf1e867589ab9f7a4628715fc
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu,  4 Jun 2026 16:37:08 -0700

elf: add hosted FreeBSD relocation primitives

Diffstat:
Msrc/obj/elf/elf.h | 23+++++++++++++++++------
Msrc/obj/elf/read.c | 45+++++++++++++++++++++++++++++++++++----------
Msrc/obj/elf/reloc_aarch64.c | 8++++++++
Msrc/obj/format.h | 1+
Msrc/obj/obj.c | 3+++
Msrc/obj/obj.h | 11+++++++++++
Msrc/obj/registry.c | 6+++++-
Msrc/obj/reloc_apply.c | 29++++++++++++++++++++++-------
8 files changed, 102 insertions(+), 24 deletions(-)

diff --git a/src/obj/elf/elf.h b/src/obj/elf/elf.h @@ -46,6 +46,10 @@ 3 /* a.k.a. ELFOSABI_LINUX — required when \ the file uses STT_GNU_IFUNC / STB_GNU_UNIQUE. */ #define ELFOSABI_LINUX ELFOSABI_GNU +/* FreeBSD brand. The kernel matches EI_OSABI for hosted executables; + * without it a static binary is rejected even when crt1.o carries the + * FreeBSD ABI note. */ +#define ELFOSABI_FREEBSD 9 /* ---- e_type ---- */ #define ET_NONE 0 @@ -156,11 +160,11 @@ * symbol writer overrides COMMON to STT_NOTYPE at its call site. */ static inline u8 elf_st_type(u8 kind /* SymKind */) { static const u8 kElfStType[] = { - [SK_UNDEF] = STT_NOTYPE, [SK_FUNC] = STT_FUNC, - [SK_OBJ] = STT_OBJECT, [SK_SECTION] = STT_SECTION, - [SK_FILE] = STT_FILE, [SK_COMMON] = STT_OBJECT, - [SK_TLS] = STT_TLS, [SK_ABS] = STT_NOTYPE, - [SK_NOTYPE] = STT_NOTYPE, [SK_IFUNC] = STT_GNU_IFUNC, + [SK_UNDEF] = STT_NOTYPE, [SK_FUNC] = STT_FUNC, + [SK_OBJ] = STT_OBJECT, [SK_SECTION] = STT_SECTION, + [SK_FILE] = STT_FILE, [SK_COMMON] = STT_OBJECT, + [SK_TLS] = STT_TLS, [SK_ABS] = STT_NOTYPE, + [SK_NOTYPE] = STT_NOTYPE, [SK_IFUNC] = STT_GNU_IFUNC, }; if (kind >= (u8)(sizeof kElfStType / sizeof kElfStType[0])) return STT_NOTYPE; return kElfStType[kind]; @@ -183,7 +187,8 @@ static inline u8 elf_st_other(u8 vis /* SymVis */) { [SV_PROTECTED] = STV_PROTECTED, [SV_INTERNAL] = STV_INTERNAL, }; - if (vis >= (u8)(sizeof kElfStOther / sizeof kElfStOther[0])) return STV_DEFAULT; + if (vis >= (u8)(sizeof kElfStOther / sizeof kElfStOther[0])) + return STV_DEFAULT; return kElfStOther[vis]; } @@ -284,6 +289,9 @@ static inline u8 elf_st_other(u8 vis /* SymVis */) { #define ELF_R_AARCH64_LDST128_ABS_LO12_NC 299 #define ELF_R_AARCH64_ADR_GOT_PAGE 311 #define ELF_R_AARCH64_LD64_GOT_LO12_NC 312 +/* TLS Initial-Exec: ADRP/LDR of a GOT slot holding the symbol's tpoff. */ +#define ELF_R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 541 +#define ELF_R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC 542 /* AArch64 dynamic-only reloc types: generated by the linker into * .rela.dyn / .rela.plt and processed by the runtime loader. */ @@ -291,6 +299,7 @@ static inline u8 elf_st_other(u8 vis /* SymVis */) { #define ELF_R_AARCH64_GLOB_DAT 1025 #define ELF_R_AARCH64_JUMP_SLOT 1026 #define ELF_R_AARCH64_RELATIVE 1027 +#define ELF_R_AARCH64_IRELATIVE 1032 /* AArch64 TLS Local-Exec (static linking model: each TLV is at a fixed * offset from the thread pointer, computed at link time). */ @@ -333,6 +342,7 @@ u32 elf_aarch64_reloc_from(u32 elf_type); #define ELF_R_X86_64_GLOB_DAT 6 #define ELF_R_X86_64_JUMP_SLOT 7 #define ELF_R_X86_64_RELATIVE 8 +#define ELF_R_X86_64_IRELATIVE 37 #define ELF_R_X86_64_GOTPCREL 9 #define ELF_R_X86_64_32 10 #define ELF_R_X86_64_32S 11 @@ -369,6 +379,7 @@ u32 elf_x86_64_reloc_from(u32 elf_type); #define ELF_R_RISCV_RELATIVE 3 #define ELF_R_RISCV_COPY 4 #define ELF_R_RISCV_JUMP_SLOT 5 +#define ELF_R_RISCV_IRELATIVE 58 #define ELF_R_RISCV_BRANCH 16 #define ELF_R_RISCV_JAL 17 #define ELF_R_RISCV_CALL 18 diff --git a/src/obj/elf/read.c b/src/obj/elf/read.c @@ -250,6 +250,24 @@ static Sym intern_cstr(Compiler* c, const char* s) { return pool_intern_slice(c->global, (Slice){.s = s, .len = (u32)strlen(s)}); } +/* ELF default-version normalization. A symbol "base@@VERSION" is the *default* + * version of `base`: an unversioned reference binds to it. GNU as emits the + * literal "@@" into a relocatable object's .symtab string (e.g. FreeBSD + * libc.a's openat@@FBSD_1.2 / setcontext / swapcontext). Trim to the base so + * kit's name-based resolution matches plain references. A single-'@' + * (non-default) version is left intact -- those are inert compatibility + * aliases (e.g. fstat@FBSD_1.0) that must NOT shadow the modern base symbol. + * Shared-library exports keep their version in .gnu.version_d rather than the + * name string, so this only fires for relocatable .symtab reads. Returns the + * length of the base name (== nlen when there is no "@@"). */ +static u32 elf_default_version_namelen(const char* nm, u32 nlen) { + u32 i; + if (!nm) return nlen; + for (i = 1; i + 1 < nlen; ++i) + if (nm[i] == '@' && nm[i + 1] == '@') return i; + return nlen; +} + /* Populate the builder's ObjImage from an ET_EXEC / ET_DYN input: the * program-header segment table (+ interp + image base), the .dynamic * dependency view (DT_NEEDED / DT_SONAME / DT_RPATH / DT_RUNPATH), the @@ -614,7 +632,8 @@ ObjBuilder* read_elf(Compiler* c, const char* name, const u8* data, obj_section_ex(ob, sym, (SecKind)sec_kind, (SecSem)sec_sem, flags, align, (u32)sh->sh_entsize, sh->sh_link, sh->sh_info); if (id == OBJ_SEC_NONE) - compiler_panic(c, SRCLOC_NONE, "read_elf: obj_section_ex failed for '%.*s'", + compiler_panic(c, SRCLOC_NONE, + "read_elf: obj_section_ex failed for '%.*s'", SLICE_ARG(((Slice){.s = nm, .len = nlen}))); elf_to_obj[i] = id; @@ -673,8 +692,8 @@ ObjBuilder* read_elf(Compiler* c, const char* name, const u8* data, "read_elf: .symtab size %llu not a multiple of %u", (unsigned long long)sh->sh_size, sym_size); if (sh->sh_link >= e_shnum) - compiler_panic(c, SRCLOC_NONE, "read_elf: .symtab sh_link %u out of range", - sh->sh_link); + compiler_panic(c, SRCLOC_NONE, + "read_elf: .symtab sh_link %u out of range", sh->sh_link); const ShdrRec* str_sh = &shdrs[sh->sh_link]; if (str_sh->sh_offset + str_sh->sh_size > len) compiler_panic(c, SRCLOC_NONE, "read_elf: .strtab out of range"); @@ -699,6 +718,7 @@ ObjBuilder* read_elf(Compiler* c, const char* name, const u8* data, u32 nlen; const char* nm = strtab_lookup(strtab, strtab_sz, st_name, &nlen); + nlen = elf_default_version_namelen(nm, nlen); Sym sn = nlen ? pool_intern_slice(c->global, (Slice){.s = nm, .len = nlen}) : 0; @@ -881,7 +901,8 @@ ObjBuilder* read_elf_dso(Compiler* c, const char* name, const u8* data, if (soname_out) *soname_out = 0; if (len < ELF64_EHDR_SIZE) - compiler_panic(c, SRCLOC_NONE, "read_elf_dso: input shorter than ELF header"); + compiler_panic(c, SRCLOC_NONE, + "read_elf_dso: input shorter than ELF header"); if (data[EI_MAG0] != ELFMAG0 || data[EI_MAG1] != ELFMAG1 || data[EI_MAG2] != ELFMAG2 || data[EI_MAG3] != ELFMAG3) compiler_panic(c, SRCLOC_NONE, "read_elf_dso: bad ELF magic"); @@ -892,8 +913,8 @@ ObjBuilder* read_elf_dso(Compiler* c, const char* name, const u8* data, u16 e_type = rd_u16_le(data + 16); if (e_type != ET_DYN) - compiler_panic(c, SRCLOC_NONE, "read_elf_dso: expected ET_DYN, got e_type=%u", - (u32)e_type); + compiler_panic(c, SRCLOC_NONE, + "read_elf_dso: expected ET_DYN, got e_type=%u", (u32)e_type); u16 e_machine = rd_u16_le(data + 18); { @@ -937,7 +958,8 @@ ObjBuilder* read_elf_dso(Compiler* c, const char* name, const u8* data, } if (!dynsym_idx) - compiler_panic(c, SRCLOC_NONE, "read_elf_dso: no SHT_DYNSYM in shared object"); + compiler_panic(c, SRCLOC_NONE, + "read_elf_dso: no SHT_DYNSYM in shared object"); /* Parse PT_DYNAMIC for DT_SONAME. The .dynamic section gives us the * dynstr to resolve the SONAME's offset; if there's no .dynamic @@ -951,12 +973,14 @@ ObjBuilder* read_elf_dso(Compiler* c, const char* name, const u8* data, dsh->sh_link); const ShdrRec* str_sh = &shdrs[dsh->sh_link]; if (str_sh->sh_offset + str_sh->sh_size > len) - compiler_panic(c, SRCLOC_NONE, "read_elf_dso: .dynamic strtab out of range"); + compiler_panic(c, SRCLOC_NONE, + "read_elf_dso: .dynamic strtab out of range"); const u8* dynstr = data + str_sh->sh_offset; u64 dynstr_sz = str_sh->sh_size; if (dsh->sh_offset + dsh->sh_size > len) - compiler_panic(c, SRCLOC_NONE, "read_elf_dso: .dynamic body out of range"); + compiler_panic(c, SRCLOC_NONE, + "read_elf_dso: .dynamic body out of range"); const u8* dynp = data + dsh->sh_offset; u64 dynsz = dsh->sh_size; /* DT entries are 16 bytes: (d_tag: u64, d_un: u64). */ @@ -992,7 +1016,8 @@ ObjBuilder* read_elf_dso(Compiler* c, const char* name, const u8* data, compiler_panic(c, SRCLOC_NONE, "read_elf_dso: .dynsym size not multiple of entry size"); if (sh->sh_link >= e_shnum) - compiler_panic(c, SRCLOC_NONE, "read_elf_dso: .dynsym sh_link out of range"); + compiler_panic(c, SRCLOC_NONE, + "read_elf_dso: .dynsym sh_link out of range"); const ShdrRec* str_sh = &shdrs[sh->sh_link]; if (str_sh->sh_offset + str_sh->sh_size > len) compiler_panic(c, SRCLOC_NONE, "read_elf_dso: .dynstr out of range"); diff --git a/src/obj/elf/reloc_aarch64.c b/src/obj/elf/reloc_aarch64.c @@ -63,6 +63,10 @@ u32 elf_aarch64_reloc_to(u32 kind /* RelocKind */) { return ELF_R_AARCH64_ADR_GOT_PAGE; case R_AARCH64_LD64_GOT_LO12_NC: return ELF_R_AARCH64_LD64_GOT_LO12_NC; + case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: + return ELF_R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21; + case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: + return ELF_R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC; case R_AARCH64_TLSLE_ADD_TPREL_HI12: return ELF_R_AARCH64_TLSLE_ADD_TPREL_HI12; case R_AARCH64_TLSLE_ADD_TPREL_LO12: @@ -146,6 +150,10 @@ u32 elf_aarch64_reloc_from(u32 elf_type) { return R_AARCH64_ADR_GOT_PAGE; case ELF_R_AARCH64_LD64_GOT_LO12_NC: return R_AARCH64_LD64_GOT_LO12_NC; + case ELF_R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: + return R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21; + case ELF_R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: + return R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC; case ELF_R_AARCH64_TLSLE_ADD_TPREL_HI12: return R_AARCH64_TLSLE_ADD_TPREL_HI12; case ELF_R_AARCH64_TLSLE_ADD_TPREL_LO12: diff --git a/src/obj/format.h b/src/obj/format.h @@ -41,6 +41,7 @@ typedef struct ObjElfArchOps { u32 r_relative; u32 r_glob_dat; u32 r_jump_slot; + u32 r_irelative; /* R_*_IRELATIVE static-IFUNC resolver reloc (__rela_iplt). */ u32 (*reloc_to)(u32 kind); u32 (*reloc_from)(u32 wire_type); } ObjElfArchOps; diff --git a/src/obj/obj.c b/src/obj/obj.c @@ -1026,6 +1026,9 @@ const char* reloc_kind_name(RelocKind k) { _CASE(R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC); _CASE(R_AARCH64_TLSLE_LDST64_TPREL_LO12); _CASE(R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC); + _CASE(R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21); + _CASE(R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC); + _CASE(R_AARCH64_TPOFF64); _CASE(R_AARCH64_GLOB_DAT); _CASE(R_AARCH64_JUMP_SLOT); _CASE(R_AARCH64_RELATIVE); diff --git a/src/obj/obj.h b/src/obj/obj.h @@ -266,6 +266,17 @@ typedef enum RelocKind { * discarded — fine for any .tls section under 16 MiB. */ R_COFF_AARCH64_SECREL_LOW12A, R_COFF_AARCH64_SECREL_HIGH12A, + /* AArch64 TLS Initial-Exec. The ADRP/LDR pair loads the symbol's + * TP-relative offset from a GOT slot; the linker fills that slot with a + * 64-bit tpoff (R_AARCH64_TPOFF64) and redirects these to the slot, so they + * apply exactly like the regular ADR_GOT_PAGE / LD64_GOT_LO12_NC pair. + * Appended at the enum tail so the public KIT_RELOC_* values (object.h) + * keep their pinned numbering. */ + R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, + R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, + /* Internal-only: a raw 64-bit AArch64/RISC-V local-exec tpoff written into + * a TLS GOT slot ((target - tls_vaddr) + TCB). Never appears on the wire. */ + R_AARCH64_TPOFF64, } RelocKind; typedef struct Section { diff --git a/src/obj/registry.c b/src/obj/registry.c @@ -74,6 +74,7 @@ static const ObjElfArchOps obj_elf_arch_ops[] = { .r_relative = ELF_R_AARCH64_RELATIVE, .r_glob_dat = ELF_R_AARCH64_GLOB_DAT, .r_jump_slot = ELF_R_AARCH64_JUMP_SLOT, + .r_irelative = ELF_R_AARCH64_IRELATIVE, .reloc_to = elf_aarch64_reloc_to, .reloc_from = elf_aarch64_reloc_from, }, @@ -87,6 +88,7 @@ static const ObjElfArchOps obj_elf_arch_ops[] = { .r_relative = ELF_R_X86_64_RELATIVE, .r_glob_dat = ELF_R_X86_64_GLOB_DAT, .r_jump_slot = ELF_R_X86_64_JUMP_SLOT, + .r_irelative = ELF_R_X86_64_IRELATIVE, .reloc_to = elf_x86_64_reloc_to, .reloc_from = elf_x86_64_reloc_from, }, @@ -100,6 +102,7 @@ static const ObjElfArchOps obj_elf_arch_ops[] = { .r_relative = ELF_R_RISCV_RELATIVE, .r_glob_dat = ELF_R_RISCV_64, .r_jump_slot = ELF_R_RISCV_JUMP_SLOT, + .r_irelative = ELF_R_RISCV_IRELATIVE, .reloc_to = elf_riscv64_reloc_to, .reloc_from = elf_riscv64_reloc_from, }, @@ -117,11 +120,12 @@ static const ObjElfArchOps obj_elf_arch_ops[] = { .r_relative = ELF_R_RISCV_RELATIVE, .r_glob_dat = ELF_R_RISCV_32, .r_jump_slot = ELF_R_RISCV_JUMP_SLOT, + .r_irelative = ELF_R_RISCV_IRELATIVE, .reloc_to = elf_riscv32_reloc_to, .reloc_from = elf_riscv32_reloc_from, }, #endif -#if !KIT_ARCH_AA64_ENABLED && !KIT_ARCH_X64_ENABLED && \ +#if !KIT_ARCH_AA64_ENABLED && !KIT_ARCH_X64_ENABLED && \ !KIT_ARCH_RV64_ENABLED && !KIT_ARCH_RV32_ENABLED {.arch = KIT_ARCH_WASM}, #endif diff --git a/src/obj/reloc_apply.c b/src/obj/reloc_apply.c @@ -96,6 +96,7 @@ void link_reloc_apply(Compiler* c, RelocKind k, u8* P_bytes, u64 S, i64 A, } case R_ABS64: case R_X64_TPOFF64: + case R_AARCH64_TPOFF64: case R_X64_RELATIVE: { /* R_X64_RELATIVE: (S + A) — for static-with-relocs paths the * linker writes the relocated value directly; the dynamic @@ -123,7 +124,12 @@ void link_reloc_apply(Compiler* c, RelocKind k, u8* P_bytes, u64 S, i64 A, case R_X64_GOTPCREL: case R_X64_GOTPCRELX: case R_X64_REX_GOTPCRELX: - case R_X64_GOTPC32: { + case R_X64_GOTPC32: + case R_X64_GOTTPOFF: { + /* GOTTPOFF (TLS Initial-Exec) is a RIP-relative load of a GOT slot + * that the linker fills with the symbol's TP-relative offset; the + * fixup is identical to GOTPCREL once the target has been redirected + * to that slot (see link_layout_got). */ /* AArch64 ELF: PREL32 maps to either of these; both encode a * 32-bit signed PC-relative displacement. The kit-canonical * distinction (section-relative vs PC-relative) collapses on @@ -189,7 +195,8 @@ void link_reloc_apply(Compiler* c, RelocKind k, u8* P_bytes, u64 S, i64 A, if (disp & 3) compiler_panic(c, SRCLOC_NONE, "link: TSTBR14 misaligned displacement"); if (disp < -(i64)(1 << 15) || disp >= (i64)(1 << 15)) - compiler_panic(c, SRCLOC_NONE, "link: TSTBR14 out of range (need ±32KiB)"); + compiler_panic(c, SRCLOC_NONE, + "link: TSTBR14 out of range (need ±32KiB)"); imm14 = (u32)((disp >> 2) & 0x3fffu); instr = rd_u32_le(P_bytes); instr = (instr & ~(0x3fffu << 5)) | (imm14 << 5); @@ -223,7 +230,8 @@ void link_reloc_apply(Compiler* c, RelocKind k, u8* P_bytes, u64 S, i64 A, if (disp & 3) compiler_panic(c, SRCLOC_NONE, "link: CALL26 misaligned displacement"); if (disp < -(i64)(1 << 27) || disp >= (i64)(1 << 27)) - compiler_panic(c, SRCLOC_NONE, "link: CALL26 out of range (need ±128MiB)"); + compiler_panic(c, SRCLOC_NONE, + "link: CALL26 out of range (need ±128MiB)"); imm26 = (u32)((disp >> 2) & 0x3ffffffu); instr = rd_u32_le(P_bytes); instr = (instr & 0xfc000000u) | imm26; @@ -232,6 +240,7 @@ void link_reloc_apply(Compiler* c, RelocKind k, u8* P_bytes, u64 S, i64 A, } case R_AARCH64_TLVP_LOAD_PAGE21: case R_AARCH64_ADR_GOT_PAGE: + case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: case R_AARCH64_ADR_PREL_PG_HI21: case R_AARCH64_ADR_PREL_PG_HI21_NC: { /* ADRP — page-relative imm21, encoded as immlo[30:29] + @@ -288,6 +297,7 @@ void link_reloc_apply(Compiler* c, RelocKind k, u8* P_bytes, u64 S, i64 A, case R_AARCH64_LDST64_ABS_LO12_NC: case R_AARCH64_LDST128_ABS_LO12_NC: case R_AARCH64_LD64_GOT_LO12_NC: + case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: case R_AARCH64_TLVP_LOAD_PAGEOFF12: { /* LDR/STR with imm12 at bits [21:10]; the imm is scaled by the * access size, so we right-shift the low 12 bits of (S+A) by @@ -300,6 +310,7 @@ void link_reloc_apply(Compiler* c, RelocKind k, u8* P_bytes, u64 S, i64 A, : (k == R_AARCH64_LDST32_ABS_LO12_NC) ? 2u : (k == R_AARCH64_LDST64_ABS_LO12_NC || k == R_AARCH64_LD64_GOT_LO12_NC || + k == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC || k == R_AARCH64_TLVP_LOAD_PAGEOFF12) ? 3u : 4u; @@ -330,7 +341,8 @@ void link_reloc_apply(Compiler* c, RelocKind k, u8* P_bytes, u64 S, i64 A, return; } case R_RV_PCREL_HI20: - case R_RV_GOT_HI20: { + case R_RV_GOT_HI20: + case R_RV_TLS_GOT_HI20: { /* AUIPC pc-relative HI20: same encoding as HI20 but the * displacement is (S + A) - P. The paired PCREL_LO12 reloc at * the ADDI/load below recovers the low 12 bits of the same @@ -375,7 +387,8 @@ void link_reloc_apply(Compiler* c, RelocKind k, u8* P_bytes, u64 S, i64 A, u32 instr; u32 b; if (disp & 1) - compiler_panic(c, SRCLOC_NONE, "link: RV BRANCH misaligned displacement"); + compiler_panic(c, SRCLOC_NONE, + "link: RV BRANCH misaligned displacement"); if (disp < -(i64)(1 << 12) || disp >= (i64)(1 << 12)) compiler_panic(c, SRCLOC_NONE, "link: RV BRANCH out of range (need ±4KiB)"); @@ -400,7 +413,8 @@ void link_reloc_apply(Compiler* c, RelocKind k, u8* P_bytes, u64 S, i64 A, if (disp & 1) compiler_panic(c, SRCLOC_NONE, "link: RV JAL misaligned displacement"); if (disp < -(i64)(1 << 20) || disp >= (i64)(1 << 20)) - compiler_panic(c, SRCLOC_NONE, "link: RV JAL out of range (need ±1MiB)"); + compiler_panic(c, SRCLOC_NONE, + "link: RV JAL out of range (need ±1MiB)"); b = (u32)((u64)disp & 0x1ffffeu) | ((u32)(((u64)disp >> 11) & 1u) << 11) | ((u32)(((u64)disp >> 20) & 1u) << 20); instr = rd_u32_le(P_bytes); @@ -429,7 +443,8 @@ void link_reloc_apply(Compiler* c, RelocKind k, u8* P_bytes, u64 S, i64 A, u32 auipc = rd_u32_le(P_bytes); u32 jalr = rd_u32_le(P_bytes + 4); if (disp < -(i64)(1ll << 31) || disp >= (i64)(1ll << 31)) - compiler_panic(c, SRCLOC_NONE, "link: RV CALL out of range (need ±2GiB)"); + compiler_panic(c, SRCLOC_NONE, + "link: RV CALL out of range (need ±2GiB)"); auipc = (auipc & 0x00000fffu) | (hi20 << 12); jalr = (jalr & 0x000fffffu) | (lo12 << 20); wr_u32_le(P_bytes, auipc);