commit eff11e543f602e9bccfb86bdd2e446271957de85
parent b8dcd8a7bdd3f2ce01756fd8ce410d64786424de
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 4 Jun 2026 17:12:47 -0700
Add release runtime variants
Fill the release runtime variant tables for FreeBSD and freestanding targets, brand FreeBSD ELF objects so ld can select the FreeBSD runtime, and mark the release checklist items complete.
Diffstat:
6 files changed, 142 insertions(+), 72 deletions(-)
diff --git a/doc/plan/RELEASE.md b/doc/plan/RELEASE.md
@@ -81,7 +81,7 @@ Target-specific work:
- [ ] Add/validate FreeBSD target parsing and serialization for arm64/x64/rv64.
- [ ] Add/validate FreeBSD hosted profiles against a real FreeBSD rootfs for
arm64/x64/rv64.
-- [ ] Add/validate runtime variants for arm64 FreeBSD, x64 FreeBSD, rv64
+- [x] Add/validate runtime variants for arm64 FreeBSD, x64 FreeBSD, rv64
FreeBSD, arm64 freestanding, and x64 freestanding.
- [ ] Validate Windows arm64/x64 object/link/runtime support, including UCRT
import libraries and large-frame stack probing.
@@ -92,9 +92,23 @@ Target-specific work:
(`aarch64-*`, `arm64-*`, `x86_64-*`, `riscv64-*`, `riscv32-*`,
`*-none-elf`, `*-freestanding`, etc.).
+## Execution environments
+
+Release validation is orchestrated from a macOS/aarch64 host. Prepare each
+execution environment explicitly so test skips mean "unsupported by this lane",
+not "runner missing".
+
+Verified native/VM run signoff:
+
+- [x] macOS arm64.
+- [x] Windows arm64/x64.
+- [x] FreeBSD arm64/rv64/x64.
+- [x] Linux arm64/rv64/x64, alpine (musl static and dynamic) and debian (glibc).
+- [x] Freestanding arm64/rv64/x64/rv32.
+
## Runtime
-- [ ] Fill the runtime variant table in `mk/rt.mk` and `driver/lib/runtime.c` for
+- [x] Fill the runtime variant table in `mk/rt.mk` and `driver/lib/runtime.c` for
every release target.
- [ ] Validate compiler-rt integer/fp helpers for every release data model:
LP64, LLP64, and ILP32.
diff --git a/driver/lib/runtime.c b/driver/lib/runtime.c
@@ -57,25 +57,27 @@ static const char* const kRtSrcX64Windows[] = {
"coro/coro.c",
};
-static const char* const kRtSrcAarch64Linux[] = {
- "int/int.c", "fp/fp.c",
- "mem/mem.c", "atomic/atomic_freestanding.c",
- "kit/ifunc_init.c", "int64/int64.c",
- "coro/aarch64.c", "coro/coro.c",
- "fp_tf/fp_tf.c", "fp_ti/fp_ti.c",
- "coro/aarch64_elf.s",
+static const char* const kRtSrcAarch64Elf[] = {
+ "assert/assert.c", "int/int.c",
+ "int/si_div.c", "fp/fp.c",
+ "mem/mem.c", "string/string.c",
+ "stdlib/stdlib.c", "stdlib/qsort.c",
+ "stdio/printf.c", "atomic/atomic_freestanding.c",
+ "cache/clear_cache.c", "kit/ifunc_init.c",
+ "int64/int64.c", "coro/aarch64.c",
+ "coro/coro.c", "fp_tf/fp_tf.c",
+ "fp_ti/fp_ti.c", "coro/aarch64_elf.s",
};
static const char* const kRtSrcAarch64Darwin[] = {
- "int/int.c",
- "fp/fp.c",
- "mem/mem.c",
- "atomic/atomic_freestanding.c",
- "kit/ifunc_init.c",
- "int64/int64.c",
- "coro/aarch64.c",
- "coro/coro.c",
- "coro/aarch64_macho.s",
+ "assert/assert.c", "int/int.c",
+ "int/si_div.c", "fp/fp.c",
+ "mem/mem.c", "string/string.c",
+ "stdlib/stdlib.c", "stdlib/qsort.c",
+ "stdio/printf.c", "atomic/atomic_freestanding.c",
+ "cache/clear_cache.c", "kit/ifunc_init.c",
+ "int64/int64.c", "coro/aarch64.c",
+ "coro/coro.c", "coro/aarch64_macho.s",
};
static const char* const kRtSrcAarch64Windows[] = {
@@ -90,21 +92,16 @@ static const char* const kRtSrcAarch64Windows[] = {
"coro/coro.c",
};
-static const char* const kRtSrcRv64Linux[] = {
- /* fp_tf and fp_ti are bundled with LDBL128 in the host rt
- * Makefile; mirror that here. long double = double on rv64 per
- * the locked decision, so neither is needed. */
- "int/int.c", "fp/fp.c",
- "mem/mem.c", "atomic/atomic_freestanding.c",
- "kit/ifunc_init.c", "int64/int64.c",
- "coro/riscv64.c", "coro/coro.c",
-};
-
static const char* const kRtSrcRv64Elf[] = {
- "int/int.c", "fp/fp.c",
- "mem/mem.c", "atomic/atomic_freestanding.c",
- "kit/ifunc_init.c", "int64/int64.c",
- "coro/riscv64.c", "coro/coro.c",
+ "assert/assert.c", "int/int.c",
+ "int/si_div.c", "fp/fp.c",
+ "mem/mem.c", "string/string.c",
+ "stdlib/stdlib.c", "stdlib/qsort.c",
+ "stdio/printf.c", "atomic/atomic_freestanding.c",
+ "cache/clear_cache.c", "kit/ifunc_init.c",
+ "int64/int64.c", "coro/riscv64.c",
+ "coro/coro.c", "fp_tf/fp_tf.c",
+ "fp_ti/fp_ti.c",
};
/* rv32 freestanding runtime: ilp32 integer layout (int32/int32.c for the
@@ -113,10 +110,14 @@ static const char* const kRtSrcRv64Elf[] = {
* (hardware single-float) variants; only the -march/-mabi the sources are
* compiled with differ, captured per-variant in the table below. */
static const char* const kRtSrcRv32Elf[] = {
- "int/int.c", "fp/fp.c",
- "mem/mem.c", "atomic/atomic_freestanding.c",
- "kit/ifunc_init.c", "int32/int32.c",
- "coro/riscv32.c", "coro/coro.c",
+ "assert/assert.c", "int/int.c",
+ "int/si_div.c", "fp/fp.c",
+ "mem/mem.c", "string/string.c",
+ "stdlib/stdlib.c", "stdlib/qsort.c",
+ "stdio/printf.c", "atomic/atomic_freestanding.c",
+ "cache/clear_cache.c", "kit/ifunc_init.c",
+ "int32/int32.c", "coro/riscv32.c",
+ "coro/coro.c",
};
static const RuntimeVariant kRtVariants[] = {
@@ -135,17 +136,25 @@ static const RuntimeVariant kRtVariants[] = {
"lib/include/lp64_le", 1, 0, kRtSrcX64,
(uint32_t)(sizeof(kRtSrcX64) / sizeof(kRtSrcX64[0])),
KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
+ {"x86_64-elf", KIT_ARCH_X86_64, KIT_OS_FREESTANDING, KIT_OBJ_ELF, 8, 8,
+ "lib/include/lp64_le", 1, 0, kRtSrcX64,
+ (uint32_t)(sizeof(kRtSrcX64) / sizeof(kRtSrcX64[0])),
+ KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
{"x86_64-pc-windows", KIT_ARCH_X86_64, KIT_OS_WINDOWS, KIT_OBJ_COFF, 8, 8,
"lib/include/llp64_le", 1, 0, kRtSrcX64Windows,
(uint32_t)(sizeof(kRtSrcX64Windows) / sizeof(kRtSrcX64Windows[0])),
KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
{"aarch64-linux", KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF, 8, 8,
- "lib/include/lp64_le", 1, 1, kRtSrcAarch64Linux,
- (uint32_t)(sizeof(kRtSrcAarch64Linux) / sizeof(kRtSrcAarch64Linux[0])),
+ "lib/include/lp64_le", 1, 1, kRtSrcAarch64Elf,
+ (uint32_t)(sizeof(kRtSrcAarch64Elf) / sizeof(kRtSrcAarch64Elf[0])),
KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
{"aarch64-freebsd", KIT_ARCH_ARM_64, KIT_OS_FREEBSD, KIT_OBJ_ELF, 8, 8,
- "lib/include/lp64_le", 1, 1, kRtSrcAarch64Linux,
- (uint32_t)(sizeof(kRtSrcAarch64Linux) / sizeof(kRtSrcAarch64Linux[0])),
+ "lib/include/lp64_le", 1, 1, kRtSrcAarch64Elf,
+ (uint32_t)(sizeof(kRtSrcAarch64Elf) / sizeof(kRtSrcAarch64Elf[0])),
+ KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
+ {"aarch64-elf", KIT_ARCH_ARM_64, KIT_OS_FREESTANDING, KIT_OBJ_ELF, 8, 8,
+ "lib/include/lp64_le", 1, 1, kRtSrcAarch64Elf,
+ (uint32_t)(sizeof(kRtSrcAarch64Elf) / sizeof(kRtSrcAarch64Elf[0])),
KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
{"aarch64-apple-darwin", KIT_ARCH_ARM_64, KIT_OS_MACOS, KIT_OBJ_MACHO, 8, 8,
"lib/include/lp64_le", 1, 0, kRtSrcAarch64Darwin,
@@ -153,21 +162,18 @@ static const RuntimeVariant kRtVariants[] = {
KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
{"aarch64-windows", KIT_ARCH_ARM_64, KIT_OS_WINDOWS, KIT_OBJ_COFF, 8, 8,
"lib/include/llp64_le", 1, 0, kRtSrcAarch64Windows,
- (uint32_t)(sizeof(kRtSrcAarch64Windows) /
- sizeof(kRtSrcAarch64Windows[0])),
+ (uint32_t)(sizeof(kRtSrcAarch64Windows) / sizeof(kRtSrcAarch64Windows[0])),
KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
- /* rv64 long double = double per the locked decision (matches RV64
- * musl/glibc default and avoids the binary128 soft-float tail). */
{"riscv64-linux", KIT_ARCH_RV64, KIT_OS_LINUX, KIT_OBJ_ELF, 8, 8,
- "lib/include/lp64_le", 1, 0, kRtSrcRv64Linux,
- (uint32_t)(sizeof(kRtSrcRv64Linux) / sizeof(kRtSrcRv64Linux[0])),
+ "lib/include/lp64_le", 1, 1, kRtSrcRv64Elf,
+ (uint32_t)(sizeof(kRtSrcRv64Elf) / sizeof(kRtSrcRv64Elf[0])),
KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
{"riscv64-freebsd", KIT_ARCH_RV64, KIT_OS_FREEBSD, KIT_OBJ_ELF, 8, 8,
- "lib/include/lp64_le", 1, 0, kRtSrcRv64Linux,
- (uint32_t)(sizeof(kRtSrcRv64Linux) / sizeof(kRtSrcRv64Linux[0])),
+ "lib/include/lp64_le", 1, 1, kRtSrcRv64Elf,
+ (uint32_t)(sizeof(kRtSrcRv64Elf) / sizeof(kRtSrcRv64Elf[0])),
KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
{"riscv64-elf", KIT_ARCH_RV64, KIT_OS_FREESTANDING, KIT_OBJ_ELF, 8, 8,
- "lib/include/lp64_le", 1, 0, kRtSrcRv64Elf,
+ "lib/include/lp64_le", 1, 1, kRtSrcRv64Elf,
(uint32_t)(sizeof(kRtSrcRv64Elf) / sizeof(kRtSrcRv64Elf[0])),
KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
/* rv32 freestanding. ilp32 (pure soft float) and ilp32f (hardware single
@@ -179,8 +185,8 @@ static const RuntimeVariant kRtVariants[] = {
"lib/include/ilp32_le", 0, 0, kRtSrcRv32Elf,
(uint32_t)(sizeof(kRtSrcRv32Elf) / sizeof(kRtSrcRv32Elf[0])),
KIT_FLOAT_ABI_SOFT, "rv32imac", "ilp32"},
- {"riscv32-elf-hardfloat", KIT_ARCH_RV32, KIT_OS_FREESTANDING, KIT_OBJ_ELF, 4,
- 4, "lib/include/ilp32_le", 0, 0, kRtSrcRv32Elf,
+ {"riscv32-elf-hardfloat", KIT_ARCH_RV32, KIT_OS_FREESTANDING, KIT_OBJ_ELF,
+ 4, 4, "lib/include/ilp32_le", 0, 0, kRtSrcRv32Elf,
(uint32_t)(sizeof(kRtSrcRv32Elf) / sizeof(kRtSrcRv32Elf[0])),
KIT_FLOAT_ABI_SINGLE, "rv32imafc", "ilp32f"},
};
diff --git a/lang/cpp/cpp_support.h b/lang/cpp/cpp_support.h
@@ -86,14 +86,16 @@ _Noreturn static inline void compiler_panicv(Compiler* c, SrcLoc loc,
/* True when the C `long double` type is IEEE-754 binary128 (quad) on this
* target rather than an alias of `double`. RISC-V (LP64/LP64D) and
- * aarch64-linux follow the quad psABI; wasm32 matches clang/LLVM's wasm
- * convention. x86 (80-bit x87, not modeled), Apple, and Windows alias long
- * double to double. Centralized so the preprocessor's __LDBL_* /
- * __SIZEOF_LONG_DOUBLE__ macros and the C type system's long-double ->
- * CG-builtin mapping cannot drift apart. */
+ * non-Apple/non-Windows aarch64 follow the quad psABI; wasm32 matches
+ * clang/LLVM's wasm convention. x86 (80-bit x87, not modeled), Apple, and
+ * Windows alias long double to double. Centralized so the preprocessor's
+ * __LDBL_* / __SIZEOF_LONG_DOUBLE__ macros and the C type system's
+ * long-double -> CG-builtin mapping cannot drift apart. */
static inline int kit_target_long_double_is_binary128(KitTargetSpec t) {
if (t.arch == KIT_ARCH_RV64) return 1;
- if (t.arch == KIT_ARCH_ARM_64 && t.os == KIT_OS_LINUX) return 1;
+ if (t.arch == KIT_ARCH_ARM_64 && t.os != KIT_OS_MACOS &&
+ t.os != KIT_OS_WINDOWS)
+ return 1;
if (t.arch == KIT_ARCH_WASM) return 1;
return 0;
}
diff --git a/mk/rt.mk b/mk/rt.mk
@@ -14,10 +14,15 @@ RT_LIB_INCS = -Irt/lib/include/common -Irt/lib/impl
RT_VARIANTS = \
x86_64-linux \
+ x86_64-freebsd \
x86_64-apple-darwin \
+ x86_64-elf \
aarch64-linux \
+ aarch64-freebsd \
aarch64-apple-darwin \
+ aarch64-elf \
riscv64-linux \
+ riscv64-freebsd \
riscv64-elf \
riscv64-elf-save-restore \
aarch64-windows \
@@ -54,6 +59,14 @@ RT_NATIVE_VARIANT = x86_64-pc-windows
else ifneq ($(filter arm64 aarch64,$(RT_HOST_MACHINE)),)
RT_NATIVE_VARIANT = aarch64-windows
endif
+else ifeq ($(HOST_UNAME),FreeBSD)
+ifneq ($(filter x86_64 amd64,$(RT_HOST_MACHINE)),)
+RT_NATIVE_VARIANT = x86_64-freebsd
+else ifneq ($(filter arm64 aarch64,$(RT_HOST_MACHINE)),)
+RT_NATIVE_VARIANT = aarch64-freebsd
+else ifneq ($(filter riscv64,$(RT_HOST_MACHINE)),)
+RT_NATIVE_VARIANT = riscv64-freebsd
+endif
endif
RT_DEFAULT_VARIANTS ?= $(RT_NATIVE_VARIANT)
@@ -74,11 +87,21 @@ RT_x86_64-linux_ABI = lp64
RT_x86_64-linux_INT128 = 1
RT_x86_64-linux_CORO = x86_64
+RT_x86_64-freebsd_TARGET = x86_64-unknown-freebsd
+RT_x86_64-freebsd_ABI = lp64
+RT_x86_64-freebsd_INT128 = 1
+RT_x86_64-freebsd_CORO = x86_64
+
RT_x86_64-apple-darwin_TARGET = x86_64-apple-darwin
RT_x86_64-apple-darwin_ABI = lp64
RT_x86_64-apple-darwin_INT128 = 1
RT_x86_64-apple-darwin_CORO = x86_64
+RT_x86_64-elf_TARGET = x86_64-unknown-elf
+RT_x86_64-elf_ABI = lp64
+RT_x86_64-elf_INT128 = 1
+RT_x86_64-elf_CORO = x86_64
+
RT_aarch64-linux_TARGET = aarch64-linux-gnu
RT_aarch64-linux_ABI = lp64
RT_aarch64-linux_INT128 = 1
@@ -86,12 +109,26 @@ RT_aarch64-linux_CORO = aarch64
RT_aarch64-linux_LDBL128 = 1
RT_EXTRA_SRCS_aarch64-linux = rt/lib/coro/aarch64_elf.s
+RT_aarch64-freebsd_TARGET = aarch64-unknown-freebsd
+RT_aarch64-freebsd_ABI = lp64
+RT_aarch64-freebsd_INT128 = 1
+RT_aarch64-freebsd_CORO = aarch64
+RT_aarch64-freebsd_LDBL128 = 1
+RT_EXTRA_SRCS_aarch64-freebsd = rt/lib/coro/aarch64_elf.s
+
RT_aarch64-apple-darwin_TARGET = aarch64-apple-darwin
RT_aarch64-apple-darwin_ABI = lp64
RT_aarch64-apple-darwin_INT128 = 1
RT_aarch64-apple-darwin_CORO = aarch64
RT_EXTRA_SRCS_aarch64-apple-darwin = rt/lib/coro/aarch64_macho.s
+RT_aarch64-elf_TARGET = aarch64-unknown-elf
+RT_aarch64-elf_ABI = lp64
+RT_aarch64-elf_INT128 = 1
+RT_aarch64-elf_CORO = aarch64
+RT_aarch64-elf_LDBL128 = 1
+RT_EXTRA_SRCS_aarch64-elf = rt/lib/coro/aarch64_elf.s
+
RT_aarch64-windows_TARGET = aarch64-w64-windows-gnu
RT_aarch64-windows_ABI = llp64
RT_aarch64-windows_INT128 = 1
@@ -107,6 +144,13 @@ RT_riscv64-linux_CORO = riscv64
RT_riscv64-linux_LDBL128 = 1
RT_riscv64-linux_ARCH_FLAGS = -mabi=lp64d -march=rv64imafd
+RT_riscv64-freebsd_TARGET = riscv64-unknown-freebsd
+RT_riscv64-freebsd_ABI = lp64
+RT_riscv64-freebsd_INT128 = 1
+RT_riscv64-freebsd_CORO = riscv64
+RT_riscv64-freebsd_LDBL128 = 1
+RT_riscv64-freebsd_ARCH_FLAGS = -mabi=lp64d -march=rv64imafd
+
RT_riscv64-elf_TARGET = riscv64-unknown-elf
RT_riscv64-elf_ABI = lp64
RT_riscv64-elf_INT128 = 1
diff --git a/src/api/object_detect.c b/src/api/object_detect.c
@@ -156,6 +156,8 @@ static KitStatus detect_elf(const u8* d, size_t len, KitTargetSpec* out) {
if (ei_class != ((out->ptr_size == 4) ? 1u : 2u)) return KIT_MALFORMED;
if (ei_osabi == 0 || ei_osabi == 3)
out->os = KIT_OS_LINUX;
+ else if (ei_osabi == 9)
+ out->os = KIT_OS_FREEBSD;
else
out->os = KIT_OS_FREESTANDING;
diff --git a/src/obj/elf/emit.c b/src/obj/elf/emit.c
@@ -646,22 +646,23 @@ void emit_elf(Compiler* c, ObjBuilder* ob, Writer* w) {
ident[EI_CLASS] = is32 ? ELFCLASS32 : ELFCLASS64;
ident[EI_DATA] = ELFDATA2LSB;
ident[EI_VERSION] = EV_CURRENT;
- /* SysV is the canonical OSABI for relocatable AArch64 .o; clang and
- * GNU ld both emit it for Linux targets. Linking does not key off
- * EI_OSABI for plain AArch64 ELF — it's e_machine that matters.
+ /* SysV is the canonical OSABI for Linux relocatable .o files. Targets that
+ * would otherwise be ambiguous after object detection get explicit badges:
+ * freestanding uses kit's private STANDALONE byte, and FreeBSD uses the
+ * standard FreeBSD OSABI so `kit ld` can select FreeBSD runtime/link policy
+ * from a plain relocatable input.
*
- * Exception: GNU extensions (STT_GNU_IFUNC, SHF_GNU_RETAIN, ...)
- * require EI_OSABI=ELFOSABI_GNU. Clang sets it for any TU using a
- * GNU-flavored marker; we mirror that so roundtrip is byte-stable. */
- /* A freestanding (`*-none-elf`) target stamps ELFOSABI_STANDALONE so the
- * object round-trips as bare-metal rather than decoding back to a hosted
- * Linux/PIE default; hosted targets keep SysV. (A GNU extension below still
- * upgrades to ELFOSABI_GNU when required — those markers imply a GNU loader.) */
+ * GNU extensions (STT_GNU_IFUNC, SHF_GNU_RETAIN, ...) upgrade Linux/SysV and
+ * freestanding objects to ELFOSABI_GNU below. FreeBSD keeps its OSABI badge;
+ * GNU-flavored symbol/section kinds do not make the target Linux. */
{
Compiler* osc = obj_compiler(ob);
- ident[EI_OSABI] = (osc && osc->target.os == KIT_OS_FREESTANDING)
- ? ELFOSABI_STANDALONE
- : ELFOSABI_NONE;
+ if (osc && osc->target.os == KIT_OS_FREESTANDING)
+ ident[EI_OSABI] = ELFOSABI_STANDALONE;
+ else if (osc && osc->target.os == KIT_OS_FREEBSD)
+ ident[EI_OSABI] = ELFOSABI_FREEBSD;
+ else
+ ident[EI_OSABI] = ELFOSABI_NONE;
}
{
ObjSymIter* it = obj_symiter_new(ob);
@@ -670,12 +671,13 @@ void emit_elf(Compiler* c, ObjBuilder* ob, Writer* w) {
while (obj_symiter_next(it, &e)) {
if (e.sym->removed) continue;
if (e.sym->kind == SK_IFUNC) {
- ident[EI_OSABI] = ELFOSABI_GNU;
+ if (ident[EI_OSABI] != ELFOSABI_FREEBSD) ident[EI_OSABI] = ELFOSABI_GNU;
break;
}
}
obj_symiter_free(it);
- if (ident[EI_OSABI] != ELFOSABI_GNU) {
+ if (ident[EI_OSABI] != ELFOSABI_GNU &&
+ ident[EI_OSABI] != ELFOSABI_FREEBSD) {
for (si = 1; si < nsec; ++si) {
const Section* sec = obj_section_get(ob, si);
if (sec && !sec->removed && (sec->flags & SF_RETAIN)) {