kit

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

commit 4cff1a6f663d2b9cc7efb6e2cf4305b38ca840db
parent c346c505e43d82f2b3ff1141d9fcae1a5927e903
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 16 Jul 2026 10:11:07 -0700

runtime: build rv32 ABI variants on demand

Diffstat:
Mdriver/lib/runtime.c | 160+++++++++++++++++++++++++++++++++++--------------------------------------------
Mscripts/kit_cross.sh | 3+++
Mtest/smoke/rv32.sh | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 179 insertions(+), 89 deletions(-)

diff --git a/driver/lib/runtime.c b/driver/lib/runtime.c @@ -370,122 +370,104 @@ static int rt_try_support_root(DriverEnv* env, const char* root, return ok ? 0 : 1; } -/* Directory-separator test for splitting a program path. On Windows the image - * path (argv[0] / GetModuleFileNameW) uses '\\', so binary-relative rt - * discovery must accept it as well as '/'; otherwise kit.exe never finds its - * <bindir>/support/rt or <bindir>/../rt and falls back to the cwd-only ./rt. */ +/* Directory-separator test for splitting the canonical program path. */ static int rt_is_path_sep(char c) { -#if defined(_WIN32) return c == '/' || c == '\\'; -#else - return c == '/'; -#endif } -static int rt_try_argv0_support(DriverEnv* env, const char* argv0, - DriverRuntimeSupport* out) { +static char* rt_exe_dir(DriverEnv* env, const char* exe, size_t* out_size) { const char* slash; size_t dir_len; char* dir; - char* support; size_t dir_size; - size_t support_size; - int rc; - - if (!argv0) return 1; - slash = argv0 + driver_strlen(argv0); - while (slash > argv0 && !rt_is_path_sep(slash[-1])) --slash; - if (slash == argv0) return 1; - dir_len = (size_t)(slash - argv0); - if (dir_len == 0) return 1; + if (!exe) return NULL; + slash = exe + driver_strlen(exe); + while (slash > exe && !rt_is_path_sep(slash[-1])) --slash; + if (slash == exe) return NULL; + dir_len = (size_t)(slash - exe); + if (dir_len == 0) return NULL; dir_size = dir_len + 1u; dir = (char*)driver_alloc(env, dir_size); - if (!dir) return 1; - driver_memcpy(dir, argv0, dir_len); + if (!dir) return NULL; + driver_memcpy(dir, exe, dir_len); dir[dir_len] = '\0'; - - support = driver_path_join(env, dir, "support", &support_size); - driver_free(env, dir, dir_size); - if (!support) return 1; - rc = rt_try_support_root(env, support, out); - driver_free(env, support, support_size); - return rc; + if (out_size) *out_size = dir_size; + return dir; } -static int rt_try_argv0_checkout_root(DriverEnv* env, const char* argv0, - DriverRuntimeSupport* out) { - const char* slash; - size_t dir_len; - char* dir; - char* parent; - size_t dir_size; - size_t parent_size; +static int rt_try_canonical_support(DriverEnv* env, const char* candidate, + DriverRuntimeSupport* out) { + char* canonical = NULL; + size_t canonical_size = 0; int rc; - - if (!argv0) return 1; - slash = argv0 + driver_strlen(argv0); - while (slash > argv0 && !rt_is_path_sep(slash[-1])) --slash; - if (slash == argv0) return 1; - dir_len = (size_t)(slash - argv0); - if (dir_len == 0) return 1; - - dir_size = dir_len + 1u; - dir = (char*)driver_alloc(env, dir_size); - if (!dir) return 1; - driver_memcpy(dir, argv0, dir_len); - dir[dir_len] = '\0'; - - parent = driver_path_join(env, dir, "..", &parent_size); - driver_free(env, dir, dir_size); - if (!parent) return 1; - rc = rt_try_support_root(env, parent, out); - driver_free(env, parent, parent_size); + if (driver_path_canonicalize(env, candidate, &canonical, &canonical_size) != + 0) + return 1; + rc = rt_try_support_root(env, canonical, out); + driver_free(env, canonical, canonical_size); return rc; } int driver_runtime_resolve(DriverEnv* env, const char* explicit_support_dir, const char* argv0, DriverRuntimeSupport* out) { - int rc; - DriverRuntimeSupport zero = {0}; - *out = zero; - char* self = NULL; + char* exe_dir = NULL; + char* candidate = NULL; + char* parent = NULL; size_t self_size = 0; - const char* tool = argv0; + size_t exe_dir_size = 0; + size_t candidate_size = 0; + size_t parent_size = 0; + int rc = 1; + DriverRuntimeSupport zero = {0}; + *out = zero; + /* An explicit root is authoritative. Normalize and validate it; never hide + * a bad explicit value by falling back to discovery. */ if (explicit_support_dir) { - rc = rt_try_support_root(env, explicit_support_dir, out); - } else if (rt_try_support_root(env, ".", out) == 0) { - rc = 0; - } else if (rt_try_argv0_checkout_root(env, argv0, out) == 0) { - rc = 0; - } else if (rt_try_argv0_support(env, argv0, out) == 0) { - /* rt found as a <bindir>/support/rt sibling: this is a distribution - * install, not a build tree. Mark it so the on-demand rt archive lands - * in the user cache dir rather than polluting the install. */ + rc = rt_try_canonical_support(env, explicit_support_dir, out); + if (rc == 0 && driver_self_exe_path(env, &self, &self_size) == 0) + out->tool_path = rt_dup(env, self, &out->tool_path_size); + else if (rc == 0 && argv0) + out->tool_path = rt_dup(env, argv0, &out->tool_path_size); + if (self) driver_free(env, self, self_size); + return rc; + } + + /* Default discovery has one identity and two ordered layouts: + * <exe-dir>/support development build + * <exe-dir>/../support packaged distribution + * No cwd/source-checkout fallback is permitted. */ + if (driver_self_exe_path(env, &self, &self_size) != 0 || !self) goto out; + exe_dir = rt_exe_dir(env, self, &exe_dir_size); + if (!exe_dir) goto out; + + candidate = driver_path_join(env, exe_dir, "support", &candidate_size); + if (!candidate) goto out; + if (rt_try_canonical_support(env, candidate, out) == 0) { rc = 0; + goto found; + } + driver_free(env, candidate, candidate_size); + candidate = NULL; + candidate_size = 0; + + parent = driver_path_join(env, exe_dir, "..", &parent_size); + if (!parent) goto out; + candidate = driver_path_join(env, parent, "support", &candidate_size); + if (!candidate) goto out; + if (rt_try_canonical_support(env, candidate, out) == 0) { out->install_layout = 1; - } else if (driver_self_exe_path(env, &self, &self_size) == 0 && self) { - /* argv[0] wasn't a usable path for binary-relative discovery — a bare - * program name when kit is on PATH, a relative invocation, or a tool - * symlink whose directory holds no rt. Retry against the real image path - * (GetModuleFileNameW on Windows, /proc/self/exe on Linux, ...). */ - tool = self; - if (rt_try_argv0_checkout_root(env, self, out) == 0) { - rc = 0; - } else if (rt_try_argv0_support(env, self, out) == 0) { - rc = 0; - out->install_layout = 1; - } else { - rc = 1; - } - } else { - rc = 1; + rc = 0; } - /* Own the located image path: it outlives `self`'s free below and the - * caller's argv, and is stat'd later for rt-archive cache invalidation. */ - if (rc == 0) out->tool_path = rt_dup(env, tool, &out->tool_path_size); + +found: + if (rc == 0) out->tool_path = rt_dup(env, self, &out->tool_path_size); +out: + if (candidate) driver_free(env, candidate, candidate_size); + if (parent) driver_free(env, parent, parent_size); + if (exe_dir) driver_free(env, exe_dir, exe_dir_size); if (self) driver_free(env, self, self_size); return rc; } diff --git a/scripts/kit_cross.sh b/scripts/kit_cross.sh @@ -150,6 +150,9 @@ make bin -j"$JOBS" \ RELEASE=1 HOST_OPTFLAGS="${KIT_CROSS_OPT:--O0}" HOST_MODE_CPPFLAGS= \ HOST_MODE_LDFLAGS="$GC_LDFLAG" \ HOST_SYSROOT_CFLAGS= HOST_SYSROOT_LDFLAGS= \ + KIT_UPDATE_INDEX_URL="${KIT_UPDATE_INDEX_URL:-https://example.invalid/kit-development.index}" \ + KIT_RELEASE_PUBKEYS="${KIT_RELEASE_PUBKEYS:-test/dist/keys/nonrelease.pub}" \ + KIT_RELEASE_ALLOW_TEST_KEY="${KIT_RELEASE_ALLOW_TEST_KEY:-1}" \ CC="$CC_W" AR="$AR_W" OUT="$BUILD_DIR/kit" diff --git a/test/smoke/rv32.sh b/test/smoke/rv32.sh @@ -211,6 +211,111 @@ kitld_lane() { # <name> kitld_lane "ilp32f" kitld_lane "ilp32" +# Relocated, read-only distribution + empty user-cache regression. Two links +# per ABI race the same cache key: the driver must build entirely with Kit, +# publish one complete archive atomically, and keep soft/ilp32f variants +# separate. The linked images and an intermediate -r object retain the merged +# psABI e_flags and execute under qemu. +DIST="$BUILD_DIR/Runtime Cache Distribution" +CACHE="$BUILD_DIR/empty user cache" +chmod -R u+w "$DIST" 2>/dev/null || true +rm -rf "$DIST" "$CACHE" +mkdir -p "$DIST/bin" "$DIST/support" +cp "$KIT" "$DIST/bin/kit" +cp -R "$ROOT/rt" "$DIST/support/rt" +chmod -R a-w "$DIST" + +cache_link_lane() { # <name> <suffix> + local name="$1" suffix="$2" + local starto="$BUILD_DIR/$name.start.o" appo="$BUILD_DIR/$name-O1.o" + local elf="$BUILD_DIR/cache-$name-$suffix.elf" + env HOME="$BUILD_DIR/cache-home" XDG_CACHE_HOME="$CACHE" \ + "$DIST/bin/kit" ld -T "$BUILD_DIR/link.ld" -e _start "$starto" "$appo" \ + -o "$elf" >"$BUILD_DIR/cache-$name-$suffix.out" \ + 2>"$BUILD_DIR/cache-$name-$suffix.err" +} + +pids= names= +for lane in ilp32f ilp32; do + for suffix in a b; do + cache_link_lane "$lane" "$suffix" & + pids="$pids $!" + names="$names $lane-$suffix" + done +done +set -- $pids +set -- "$@" # keep shellcheck from treating the expansion as an array +i=1 +for name in $names; do + eval pid='${'$i'}' + if wait "$pid"; then ok "runtime-cache concurrent $name"; + else not_ok "runtime-cache concurrent $name" "$BUILD_DIR/cache-$name.err"; fi + i=$((i + 1)) +done + +for variant in riscv32-elf-hardfloat riscv32-elf; do + archive="$CACHE/kit/$variant/libkit_rt.a" + if [ -s "$archive" ] && "$DIST/bin/kit" ar t "$archive" \ + >"$BUILD_DIR/cache-$variant.members" 2>"$BUILD_DIR/cache-$variant.ar.err" \ + && [ -s "$BUILD_DIR/cache-$variant.members" ]; then + ok "runtime-cache $variant atomically published archive" + else + not_ok "runtime-cache $variant archive" "$BUILD_DIR/cache-$variant.ar.err" + fi +done +if [ ! -e "$DIST/support/build" ]; then + ok "runtime-cache read-only distribution stayed unmodified" +else + not_ok "runtime-cache read-only distribution" "support/build was created" +fi + +READELF="$(command -v llvm-readelf 2>/dev/null || command -v readelf 2>/dev/null || true)" +check_flags() { # <label> <file> <hex> + local label="$1" file="$2" want="$3" flags + if [ -z "$READELF" ]; then + skip_test "$label" "readelf unavailable"; return + fi + flags="$($READELF -h "$file" 2>"$file.readelf.err" | \ + sed -n 's/^[[:space:]]*Flags:[[:space:]]*//p')" + if printf '%s\n' "$flags" | grep -q "^$want\(,\|$\)"; then + ok "$label ($flags)" + else + not_ok "$label" "expected flags $want, got: $flags" + fi +} + +if "$DIST/bin/kit" ld -r "$BUILD_DIR/ilp32f.start.o" \ + "$BUILD_DIR/ilp32f-O1.o" -o "$BUILD_DIR/merged-ilp32f.o" \ + >"$BUILD_DIR/merged-ilp32f.out" 2>"$BUILD_DIR/merged-ilp32f.err"; then + check_flags "rv32 ilp32f relocatable e_flags" "$BUILD_DIR/merged-ilp32f.o" "0x3" +else + not_ok "rv32 ilp32f relocatable link" "$BUILD_DIR/merged-ilp32f.err" +fi +check_flags "rv32 ilp32f final e_flags" "$BUILD_DIR/cache-ilp32f-a.elf" "0x3" +check_flags "rv32 ilp32 final e_flags" "$BUILD_DIR/cache-ilp32-a.elf" "0x1" + +if env HOME="$BUILD_DIR/cache-home" XDG_CACHE_HOME="$CACHE" \ + "$DIST/bin/kit" ld -T "$BUILD_DIR/link.ld" -e _start \ + "$BUILD_DIR/ilp32f.start.o" "$BUILD_DIR/ilp32-O1.o" \ + -o "$BUILD_DIR/mixed-abi.elf" >"$BUILD_DIR/mixed-abi.out" \ + 2>"$BUILD_DIR/mixed-abi.err"; then + not_ok "rv32 mixed float ABI rejection" "link unexpectedly succeeded" +elif grep -q 'ilp32-O1.o.*float ABI\|float ABI.*ilp32-O1.o' \ + "$BUILD_DIR/mixed-abi.err"; then + ok "rv32 mixed float ABI rejection names input" +else + not_ok "rv32 mixed float ABI diagnostic" "$BUILD_DIR/mixed-abi.err" +fi + +for lane in ilp32f ilp32; do + rc=0 + timeout 20 "$QEMU" -machine virt -bios none \ + -kernel "$BUILD_DIR/cache-$lane-a.elf" -nographic -no-reboot \ + >"$BUILD_DIR/cache-$lane.qemu.out" 2>"$BUILD_DIR/cache-$lane.qemu.err" || rc=$? + if [ "$rc" -eq 0 ]; then ok "runtime-cache $lane qemu rc=0"; + else not_ok "runtime-cache $lane qemu" "expected exit 0, got $rc"; fi +done + # Negative control: a deliberately wrong result must produce a nonzero exit. sed 's/if (acc != 45) return 1;/if (acc != 45) return 1; return 99;/' "$BUILD_DIR/app.c" > "$BUILD_DIR/bad.c" if "$KIT" cc -target riscv32-none-elf -march=rv32imac_zicsr_zifencei -mabi=ilp32 -O1 \