kit

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

commit 2a2dc7af1f422ffa8f7bf62f229b9f448f65a380
parent 28f2a06838d8f794b707e3e6dd2f70eadd14fc02
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon,  8 Jun 2026 14:13:57 -0700

freebsd: x64/rv64 cross-compile, -lm/-lpthread, hosted lib search

1. Fix cross-compile for non-aarch64 hosts: gate LIB_SRCS_JIT_ASM on
   HOST_ARCH==aarch64 in mk/lib_srcs.mk; add LIB_SRCS_JIT_STUB
   (src/jit/tlv_thunk_stub.c) for other hosts, with a matching compile
   rule in Makefile.

2. Fix -lm / -lpthread / other user-specified -l flags not found when
   using the hosted profile:
   - Add lib_search_dirs[] to DriverHostedPlan so the resolved hosted
     lib dirs (e.g. /usr/lib, /lib on FreeBSD) are exposed to callers.
   - driver_hosted_resolve: transfer libdir ownership from DriverHostedDirs
     into the plan; driver_hosted_plan_fini frees them.
   - cc.c: populate lib_search_paths from plan lib_search_dirs in
     cc_apply_hosted_profile; move cc_resolve_pending_libs to after
     cc_apply_hosted_profile so hosted dirs are present when -l flags
     are resolved.
   - ld.c: pre-scan for -lc in ld_parse first pass and call
     driver_hosted_dirs_resolve to pre-populate o->lib_dirs before the
     inline -l resolution loop; also add hosted lib_search_dirs in
     ld_apply_hosted_before_after.

3. Mark RELEASE.md items done: FreeBSD target parsing/serialization,
   FreeBSD hosted profiles (arm64/x64/rv64 cross-compiled + VM-verified),
   and FreeBSD ucontext/register marshalling.

Verified on FreeBSD 15 VMs (aarch64, amd64, riscv64):
  kit cc -c / kit cc / kit ld -lc / kit cc -lm / kit cc -lpthread /
  kit ld -lm / kit cc -static all pass.
test-freebsd: 6/6 pass (static+dynamic metadata for all three arches).

Diffstat:
MMakefile | 6++++++
Mdoc/plan/RELEASE.md | 6+++---
Mdriver/cmd/cc.c | 7++++++-
Mdriver/cmd/ld.c | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Mdriver/lib/hosted.c | 24+++++++++++++++++++-----
Mdriver/lib/hosted.h | 7+++++++
Mmk/lib_srcs.mk | 10+++++++++-
7 files changed, 112 insertions(+), 28 deletions(-)

diff --git a/Makefile b/Makefile @@ -78,6 +78,12 @@ endef $(foreach s,$(LIB_SRCS_C_GENERAL) $(LIB_ASMS),$(eval $(call libkit_obj_rule,$(s),src,lib))) $(call flatobjs,$(LIB_SRCS_C_GENERAL) $(LIB_ASMS),src,lib): RULE_CFLAGS = $(LIB_CFLAGS) +# JIT TLV thunk stub (non-aarch64 only; aarch64 uses the .S version above) +ifneq ($(LIB_SRCS_JIT_STUB),) +$(foreach s,$(LIB_SRCS_JIT_STUB),$(eval $(call libkit_obj_rule,$(s),src,lib))) +$(call flatobjs,$(LIB_SRCS_JIT_STUB),src,lib): RULE_CFLAGS = $(LIB_CFLAGS) +endif + # lang_registry.c is the one libkit source that crosses into lang/*; it # uses -Ilang so the frontend headers can be reached as "c/c.h" etc. ifneq ($(LIB_SRCS_LANGREG),) diff --git a/doc/plan/RELEASE.md b/doc/plan/RELEASE.md @@ -78,8 +78,8 @@ For each target in the release set: 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 +- [x] Add/validate FreeBSD target parsing and serialization for arm64/x64/rv64. +- [x] Add/validate FreeBSD hosted profiles against a real FreeBSD rootfs for arm64/x64/rv64. - [x] Add/validate runtime variants for arm64 FreeBSD, x64 FreeBSD, rv64 FreeBSD, arm64 freestanding, and x64 freestanding. @@ -208,7 +208,7 @@ Verified native/VM run signoff: - [ ] Bring x64 and rv64 session integration to parity with arm64: fault classification, trap PC normalization, register marshalling, and displaced single-step. -- [ ] Add or extend ucontext/register marshalling for FreeBSD x64/arm64/rv64. +- [x] Add or extend ucontext/register marshalling for FreeBSD x64/arm64/rv64. - [ ] Handle known declined displaced-step forms or diagnose them cleanly. - [ ] Add scripted transcript tests and low-level unit tests for breakpoint patch round-trip, guarded copy, displaced stepping, and source stepping. diff --git a/driver/cmd/cc.c b/driver/cmd/cc.c @@ -802,6 +802,11 @@ static int cc_apply_hosted_profile(CcOptions* o) { for (i = 0; i < o->hosted.ndefines; ++i) { o->cf.defines[o->cf.ndefines++] = o->hosted.defines[i]; } + /* Add hosted lib search dirs so user -l flags (-lm, -lpthread, etc.) can be + * resolved. The strings are owned by o->hosted and outlive lib_search_paths. + * Insert before any user -L dirs so sysroot libs take precedence. */ + for (i = 0; i < o->hosted.nlib_search_dirs; ++i) + o->lib_search_paths[o->nlib_search_paths++] = o->hosted.lib_search_dirs[i]; if (!link_action) return 0; for (i = 0; i < o->hosted.nbefore; ++i) { if (o->no_startfiles) break; @@ -1468,7 +1473,6 @@ static int cc_parse(int argc, char** argv, CcOptions* o) { if (cc_apply_env(o) != 0) return 1; if (cc_append_windows_lib_dirs(o) != 0) return 1; - if (!o->syntax_only && cc_resolve_pending_libs(o) != 0) return 1; { uint32_t total_sources = o->nsource_files + o->nsource_memory; @@ -1590,6 +1594,7 @@ static int cc_parse(int argc, char** argv, CcOptions* o) { cc_enable_hosted_for_sysroot(o); cc_apply_default_hosted_profile(o); if (cc_apply_hosted_profile(o) != 0) return 1; + if (!o->syntax_only && cc_resolve_pending_libs(o) != 0) return 1; return 0; } diff --git a/driver/cmd/ld.c b/driver/cmd/ld.c @@ -691,29 +691,70 @@ static int ld_parse(int argc, char** argv, LdOptions* o) { * appear anywhere on the command line and still affect earlier * `/...` tokens. Also capture --sysroot before resolving any -l * entries; GNU ld treats the sysroot as a global search-prefix - * setting, not positional state. */ - for (i = 1; i < argc; ++i) { - if (driver_streq(argv[i], "--ms-link-driver")) { - o->ms_link_driver = 1; - continue; - } - if (driver_streq(argv[i], "--sysroot")) { - if (i + 1 < argc) { - o->sysroot = argv[i + 1]; + * setting, not positional state. Also detect -lc so the hosted lib + * dirs can be pre-populated before -lm / -lpthread etc. are resolved. */ + { + int has_lc = 0; + for (i = 1; i < argc; ++i) { + if (driver_streq(argv[i], "--ms-link-driver")) { + o->ms_link_driver = 1; + continue; + } + if (driver_streq(argv[i], "--sysroot")) { + if (i + 1 < argc) { + o->sysroot = argv[i + 1]; + i++; + } + continue; + } + if (driver_strneq(argv[i], "--sysroot=", 10)) { + o->sysroot = argv[i] + 10; + continue; + } + /* Detect -lc / -l c so hosted lib dirs are available for later -l + * flags regardless of where -lc appears on the command line. */ + if (driver_streq(argv[i], "-lc")) { + has_lc = 1; + continue; + } + if ((driver_streq(argv[i], "-l") || driver_streq(argv[i], "--library")) && + i + 1 < argc && driver_streq(argv[i + 1], "c")) { + has_lc = 1; i++; + continue; } - continue; } - if (driver_strneq(argv[i], "--sysroot=", 10)) { - o->sysroot = argv[i] + 10; - continue; + if (!o->sysroot || !o->sysroot[0]) { + const char* env_sysroot = driver_getenv("KIT_SYSROOT"); + if (env_sysroot && env_sysroot[0]) o->sysroot = env_sysroot; + } + if (ld_add_sysroot_libdir(o) != 0) return 1; + /* Pre-populate the hosted lib dirs so user -l flags are resolved + * against the correct sysroot even when they appear before -lc. */ + if (has_lc) { + DriverHostedRequest req; + DriverHostedDirs dirs; + uint32_t j; + memset(&req, 0, sizeof req); + req.env = o->env; + req.tool = LD_TOOL; + req.target = o->target; + req.sysroot = o->sysroot; + if (driver_hosted_dirs_resolve(&req, &dirs) == 0) { + for (j = 0; j < dirs.nlibdirs; ++j) { + /* Transfer ownership of each dir string to o->owned_paths so + * it is freed in ld_options_release; then point lib_dirs at it. */ + const char* p; + if (ld_own_path(o, dirs.libdirs[j], dirs.libdir_sizes[j], &p) == 0) { + dirs.libdirs[j] = NULL; + dirs.libdir_sizes[j] = 0; + o->lib_dirs[o->nlib_dirs++] = p; + } + } + driver_hosted_dirs_fini(&dirs); + } } } - if (!o->sysroot || !o->sysroot[0]) { - const char* env_sysroot = driver_getenv("KIT_SYSROOT"); - if (env_sysroot && env_sysroot[0]) o->sysroot = env_sysroot; - } - if (ld_add_sysroot_libdir(o) != 0) return 1; for (i = 1; i < argc; ++i) { const char* a = argv[i]; @@ -1369,6 +1410,9 @@ static int ld_apply_hosted_before_after(LdOptions* o) { req.static_link = o->static_link; req.link_inputs = 1; if (driver_hosted_resolve(&req, &o->hosted) != 0) return 1; + /* Add hosted lib search dirs so user -l flags resolve against the sysroot. */ + for (i = 0; i < o->hosted.nlib_search_dirs; ++i) + o->lib_dirs[o->nlib_dirs++] = o->hosted.lib_search_dirs[i]; for (i = 0; i < o->hosted.nbefore; ++i) { if (ld_append_hosted_input(o, &o->hosted.before[i], insert_pos, 1) != 0) return 1; diff --git a/driver/lib/hosted.c b/driver/lib/hosted.c @@ -411,11 +411,7 @@ static int hosted_resolve_linux(const DriverHostedRequest* req, return 1; } -/* FreeBSD base-system hosted profile. UNTESTED on the macOS dev host: the - * interp path, the libc.so.7-vs-linker-script binding, the crt set, and the - * __FreeBSD__ version are best-effort and want validation against a real - * FreeBSD root (a base.txz extraction harness paralleling - * test/libc/{glibc,musl} is the follow-up). libc binds libc.so.7 directly -- +/* FreeBSD base-system hosted profile. libc binds libc.so.7 directly -- * /usr/lib/libc.so is a GNU ld linker script kit's linker cannot parse. */ static int hosted_resolve_freebsd(const DriverHostedRequest* req, const DriverHostedDirs* dirs, @@ -738,6 +734,19 @@ int driver_hosted_resolve(const DriverHostedRequest* req, driver_errf(req->tool, "no hosted libc profile for target"); rc = 1; } + if (rc == 0) { + /* Transfer libdir ownership from dirs into the plan so callers can add + * them to their lib search path for user-specified -l resolution. */ + uint32_t j; + for (j = 0; j < dirs.nlibdirs && + j < DRIVER_HOSTED_MAX_LIB_SEARCH_DIRS; ++j) { + out->lib_search_dirs[j] = dirs.libdirs[j]; + out->lib_search_dir_sizes[j] = dirs.libdir_sizes[j]; + dirs.libdirs[j] = NULL; + dirs.libdir_sizes[j] = 0; + } + out->nlib_search_dirs = j; + } driver_hosted_dirs_fini(&dirs); if (rc != 0) driver_hosted_plan_fini(req->env, out); return rc; @@ -763,5 +772,10 @@ void driver_hosted_plan_fini(DriverEnv* env, DriverHostedPlan* plan) { driver_free(env, plan->owned_system_includes[i], plan->owned_system_include_sizes[i]); } + for (i = 0; i < plan->nlib_search_dirs; ++i) { + if (plan->lib_search_dirs[i]) + driver_free(env, plan->lib_search_dirs[i], + plan->lib_search_dir_sizes[i]); + } memset(plan, 0, sizeof(*plan)); } diff --git a/driver/lib/hosted.h b/driver/lib/hosted.h @@ -25,6 +25,7 @@ typedef struct DriverHostedInput { #define DRIVER_HOSTED_MAX_FINAL 2 #define DRIVER_HOSTED_MAX_INCLUDES 4 #define DRIVER_HOSTED_MAX_DEFINES 20 +#define DRIVER_HOSTED_MAX_LIB_SEARCH_DIRS 8 typedef struct DriverHostedPlan { const char* profile_name; @@ -41,6 +42,12 @@ typedef struct DriverHostedPlan { uint32_t nsystem_includes; KitDefine defines[DRIVER_HOSTED_MAX_DEFINES]; uint32_t ndefines; + /* Library search directories from the hosted dirs resolution; added to the + * driver's lib search path so user-specified -l flags (e.g. -lm, -lpthread) + * can be found. Ownership is transferred from DriverHostedDirs. */ + char* lib_search_dirs[DRIVER_HOSTED_MAX_LIB_SEARCH_DIRS]; + size_t lib_search_dir_sizes[DRIVER_HOSTED_MAX_LIB_SEARCH_DIRS]; + uint32_t nlib_search_dirs; } DriverHostedPlan; typedef struct DriverHostedRequest { diff --git a/mk/lib_srcs.mk b/mk/lib_srcs.mk @@ -128,7 +128,14 @@ LIB_SRCS_LINK := $(shell find src/link -name '*.c' 2>/dev/null) ifneq ($(KIT_JIT_ENABLED),1) LIB_SRCS_LINK := $(filter-out %/link_jit.c,$(LIB_SRCS_LINK)) endif +# The JIT TLV thunk is aarch64-specific asm; every other target uses the C stub. +ifeq ($(HOST_ARCH),aarch64) LIB_SRCS_JIT_ASM := $(shell find src/jit -name '*.S' 2>/dev/null) +LIB_SRCS_JIT_STUB := +else +LIB_SRCS_JIT_ASM := +LIB_SRCS_JIT_STUB := src/jit/tlv_thunk_stub.c +endif LIB_SRC_ABI_AAPCS64 = src/abi/abi_aapcs64.c LIB_SRC_ABI_APPLE_ARM64 = src/abi/abi_apple_arm64.c @@ -274,5 +281,6 @@ LIB_OBJS = $(call flatobjs,$(LIB_SRCS_C_GENERAL),src,lib) \ $(call flatobjs,$(LIB_SRCS_LANGREG),src,lib) \ $(call flatobjs,$(LIB_SRCS_VENDOR),vendor,vendor) \ $(LANG_OBJS) \ - $(call flatobjs,$(LIB_ASMS),src,lib) + $(call flatobjs,$(LIB_ASMS),src,lib) \ + $(call flatobjs,$(LIB_SRCS_JIT_STUB),src,lib) LIB_DEPS = $(LIB_OBJS:.o=.d)