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:
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)