kit

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

runtime.c (39334B)


      1 #include "runtime.h"
      2 
      3 #include <kit/archive.h>
      4 #include <kit/compile.h>
      5 #include <kit/core.h>
      6 #include <kit/preprocess.h>
      7 #include <stdint.h>
      8 #include <string.h>
      9 
     10 typedef struct RuntimeVariant {
     11   const char* key;
     12   KitArchKind arch;
     13   KitOSKind os;
     14   KitObjFmt obj;
     15   uint8_t ptr_size;
     16   uint8_t ptr_align;
     17   const char* abi_include;
     18   uint8_t has_int128;
     19   uint8_t ldbl128;
     20   const char* const* sources;
     21   uint32_t nsources;
     22   /* Float-ABI axis (orthogonal to XLEN). A single arch+ptr_size can have
     23    * several float ABIs (rv32 ilp32 soft vs ilp32f single), each needing its
     24    * own runtime build, so the ABI is part of the variant identity. DEFAULT (0)
     25    * means "wildcard" — it matches any target.float_abi (every non-RISC-V
     26    * target and rv64, whose runtime is float-ABI-agnostic, leave it 0). */
     27   uint8_t float_abi; /* KitFloatAbi */
     28   /* -march / -mabi the runtime is compiled with. NULL falls back to the arch
     29    * defaults (correct for every existing variant). rv32 sets them explicitly
     30    * because the default profile (rv32imafc/ilp32f) is wrong for the soft
     31    * ilp32 build. */
     32   const char* isa;
     33   const char* abi;
     34 } RuntimeVariant;
     35 
     36 static const char* const kRtSrcX64[] = {
     37     "assert/assert.c",
     38     "int/int.c",
     39     "int/si_div.c",
     40     "fp/fp.c",
     41     "mem/mem.c",
     42     "string/string.c",
     43     "ctype/ctype.c",
     44     "stdlib/stdlib.c",
     45     "stdlib/qsort.c",
     46     "stdio/printf.c",
     47     "atomic/atomic_freestanding.c",
     48     "cache/clear_cache.c",
     49     "kit/ifunc_init.c",
     50     "int64/int64.c",
     51     "coro/x86_64.c",
     52     "coro/coro.c",
     53     "stack/backtrace.c",
     54     "stack/print_backtrace.c",
     55 };
     56 
     57 static const char* const kRtSrcX64Windows[] = {
     58     "int/int.c",
     59     "int/si_div.c",
     60     "fp/fp.c",
     61     "atomic/atomic_freestanding.c",
     62     "cache/clear_cache.c",
     63     "kit/ifunc_init.c",
     64     "int64/int64.c",
     65     "stack/chkstk_x86_64_win.c",
     66     "coro/x86_64_win.c",
     67     "coro/coro.c",
     68 };
     69 
     70 static const char* const kRtSrcAarch64Elf[] = {
     71     "assert/assert.c",
     72     "int/int.c",
     73     "int/si_div.c",
     74     "fp/fp.c",
     75     "mem/mem.c",
     76     "string/string.c",
     77     "ctype/ctype.c",
     78     "stdlib/stdlib.c",
     79     "stdlib/qsort.c",
     80     "stdio/printf.c",
     81     "atomic/atomic_freestanding.c",
     82     "cache/clear_cache.c",
     83     "kit/ifunc_init.c",
     84     "int64/int64.c",
     85     "coro/aarch64.c",
     86     "coro/coro.c",
     87     "fp_tf/fp_tf.c",
     88     "fp_ti/fp_ti.c",
     89     "coro/aarch64_elf.s",
     90     "stack/backtrace.c",
     91     "stack/print_backtrace.c",
     92 };
     93 
     94 static const char* const kRtSrcAarch64Darwin[] = {
     95     "assert/assert.c",
     96     "int/int.c",
     97     "int/si_div.c",
     98     "fp/fp.c",
     99     "mem/mem.c",
    100     "string/string.c",
    101     "ctype/ctype.c",
    102     "stdlib/stdlib.c",
    103     "stdlib/qsort.c",
    104     "stdio/printf.c",
    105     "atomic/atomic_freestanding.c",
    106     "cache/clear_cache.c",
    107     "kit/ifunc_init.c",
    108     "int64/int64.c",
    109     "coro/aarch64.c",
    110     "coro/coro.c",
    111     "coro/aarch64_macho.s",
    112     "stack/backtrace.c",
    113     "stack/print_backtrace.c",
    114 };
    115 
    116 static const char* const kRtSrcAarch64Windows[] = {
    117     "int/int.c",
    118     "int/si_div.c",
    119     "fp/fp.c",
    120     "atomic/atomic_freestanding.c",
    121     "cache/clear_cache.c",
    122     "kit/ifunc_init.c",
    123     "int64/int64.c",
    124     "coro/aarch64.c",
    125     "coro/coro.c",
    126 };
    127 
    128 static const char* const kRtSrcRv64Elf[] = {
    129     "assert/assert.c",
    130     "int/int.c",
    131     "int/si_div.c",
    132     "fp/fp.c",
    133     "mem/mem.c",
    134     "string/string.c",
    135     "ctype/ctype.c",
    136     "stdlib/stdlib.c",
    137     "stdlib/qsort.c",
    138     "stdio/printf.c",
    139     "atomic/atomic_freestanding.c",
    140     "cache/clear_cache.c",
    141     "kit/ifunc_init.c",
    142     "int64/int64.c",
    143     "coro/riscv64.c",
    144     "coro/coro.c",
    145     "fp_tf/fp_tf.c",
    146     "fp_ti/fp_ti.c",
    147     "stack/backtrace.c",
    148     "stack/print_backtrace.c",
    149 };
    150 
    151 /* rv32 freestanding runtime: ilp32 integer layout (int32/int32.c for the
    152  * 64-bit-on-32-bit helpers — __muldi3/__ashldi3/...; int/int.c for the i64
    153  * div/mod) and soft double (fp/fp.c). Shared by the ilp32 (soft) and ilp32f
    154  * (hardware single-float) variants; only the -march/-mabi the sources are
    155  * compiled with differ, captured per-variant in the table below. */
    156 static const char* const kRtSrcRv32Elf[] = {
    157     "assert/assert.c",
    158     "int/int.c",
    159     "int/si_div.c",
    160     "fp/fp.c",
    161     "mem/mem.c",
    162     "string/string.c",
    163     "ctype/ctype.c",
    164     "stdlib/stdlib.c",
    165     "stdlib/qsort.c",
    166     "stdio/printf.c",
    167     "atomic/atomic_freestanding.c",
    168     "cache/clear_cache.c",
    169     "kit/ifunc_init.c",
    170     "int32/int32.c",
    171     "coro/riscv32.c",
    172     "coro/coro.c",
    173     "stack/backtrace.c",
    174     "stack/print_backtrace.c",
    175 };
    176 
    177 /* arm32 (ARMv7-M Thumb-2) freestanding runtime: the same ilp32 integer layout
    178  * (int32/int32.c for the 64-bit-on-32-bit helpers, int/int.c for i64 div/mod)
    179  * and soft double (fp/fp.c) as rv32. Kept in sync with the kit-self-built
    180  * `make rt-arm-eabi-thumb2` set (RT_SRCS_arm-eabi-thumb2 in mk/rt.mk): the
    181  * hand-written ARM AEABI alias layer (arm/aeabi_thumb2.S + arm/aeabi.c) and the
    182  * coroutine register-switch asm (coro/arm32.c + coro/coro.c's file-scope asm)
    183  * are EXCLUDED because the kit assembler does not yet cover those idioms (a
    184  * follow-on); kit-compiled code references the libgcc-named helpers the C
    185  * sources provide (__udivdi3 / __adddf3 / ...), not the __aeabi_* aliases. */
    186 static const char* const kRtSrcArm32[] = {
    187     "assert/assert.c",
    188     "int/int.c",
    189     "int/si_div.c",
    190     "fp/fp.c",
    191     "mem/mem.c",
    192     "string/string.c",
    193     "ctype/ctype.c",
    194     "stdlib/stdlib.c",
    195     "stdlib/qsort.c",
    196     "stdio/printf.c",
    197     "atomic/atomic_freestanding.c",
    198     "cache/clear_cache.c",
    199     "kit/ifunc_init.c",
    200     "int32/int32.c",
    201     "stack/backtrace.c",
    202     "stack/print_backtrace.c",
    203 };
    204 
    205 static const RuntimeVariant kRtVariants[] = {
    206     {"x86_64-linux", KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF, 8, 8,
    207      "lib/include/lp64_le", 1, 0, kRtSrcX64,
    208      (uint32_t)(sizeof(kRtSrcX64) / sizeof(kRtSrcX64[0])),
    209      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    210     /* FreeBSD ELF runtime mirrors the Linux ELF runtime per-arch: the
    211      * compiler-rt helpers and coroutine asm are ABI/ELF-based, not kernel
    212      * based, so the same source set and data model apply. */
    213     {"x86_64-freebsd", KIT_ARCH_X86_64, KIT_OS_FREEBSD, KIT_OBJ_ELF, 8, 8,
    214      "lib/include/lp64_le", 1, 0, kRtSrcX64,
    215      (uint32_t)(sizeof(kRtSrcX64) / sizeof(kRtSrcX64[0])),
    216      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    217     {"x86_64-apple-darwin", KIT_ARCH_X86_64, KIT_OS_MACOS, KIT_OBJ_MACHO, 8, 8,
    218      "lib/include/lp64_le", 1, 0, kRtSrcX64,
    219      (uint32_t)(sizeof(kRtSrcX64) / sizeof(kRtSrcX64[0])),
    220      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    221     {"x86_64-apple-ios-simulator", KIT_ARCH_X86_64, KIT_OS_IOS_SIMULATOR,
    222      KIT_OBJ_MACHO, 8, 8, "lib/include/lp64_le", 1, 0, kRtSrcX64,
    223      (uint32_t)(sizeof(kRtSrcX64) / sizeof(kRtSrcX64[0])),
    224      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    225     {"x86_64-elf", KIT_ARCH_X86_64, KIT_OS_FREESTANDING, KIT_OBJ_ELF, 8, 8,
    226      "lib/include/lp64_le", 1, 0, kRtSrcX64,
    227      (uint32_t)(sizeof(kRtSrcX64) / sizeof(kRtSrcX64[0])),
    228      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    229     {"x86_64-pc-windows", KIT_ARCH_X86_64, KIT_OS_WINDOWS, KIT_OBJ_COFF, 8, 8,
    230      "lib/include/llp64_le", 1, 0, kRtSrcX64Windows,
    231      (uint32_t)(sizeof(kRtSrcX64Windows) / sizeof(kRtSrcX64Windows[0])),
    232      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    233     {"aarch64-linux", KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF, 8, 8,
    234      "lib/include/lp64_le", 1, 1, kRtSrcAarch64Elf,
    235      (uint32_t)(sizeof(kRtSrcAarch64Elf) / sizeof(kRtSrcAarch64Elf[0])),
    236      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    237     {"aarch64-linux-android", KIT_ARCH_ARM_64, KIT_OS_ANDROID, KIT_OBJ_ELF, 8,
    238      8, "lib/include/lp64_le", 1, 1, kRtSrcAarch64Elf,
    239      (uint32_t)(sizeof(kRtSrcAarch64Elf) / sizeof(kRtSrcAarch64Elf[0])),
    240      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    241     {"aarch64-freebsd", KIT_ARCH_ARM_64, KIT_OS_FREEBSD, KIT_OBJ_ELF, 8, 8,
    242      "lib/include/lp64_le", 1, 1, kRtSrcAarch64Elf,
    243      (uint32_t)(sizeof(kRtSrcAarch64Elf) / sizeof(kRtSrcAarch64Elf[0])),
    244      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    245     {"aarch64-elf", KIT_ARCH_ARM_64, KIT_OS_FREESTANDING, KIT_OBJ_ELF, 8, 8,
    246      "lib/include/lp64_le", 1, 1, kRtSrcAarch64Elf,
    247      (uint32_t)(sizeof(kRtSrcAarch64Elf) / sizeof(kRtSrcAarch64Elf[0])),
    248      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    249     {"aarch64-apple-darwin", KIT_ARCH_ARM_64, KIT_OS_MACOS, KIT_OBJ_MACHO, 8, 8,
    250      "lib/include/lp64_le", 1, 0, kRtSrcAarch64Darwin,
    251      (uint32_t)(sizeof(kRtSrcAarch64Darwin) / sizeof(kRtSrcAarch64Darwin[0])),
    252      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    253     {"aarch64-apple-ios", KIT_ARCH_ARM_64, KIT_OS_IOS, KIT_OBJ_MACHO, 8, 8,
    254      "lib/include/lp64_le", 1, 0, kRtSrcAarch64Darwin,
    255      (uint32_t)(sizeof(kRtSrcAarch64Darwin) / sizeof(kRtSrcAarch64Darwin[0])),
    256      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    257     {"aarch64-apple-ios-simulator", KIT_ARCH_ARM_64, KIT_OS_IOS_SIMULATOR,
    258      KIT_OBJ_MACHO, 8, 8, "lib/include/lp64_le", 1, 0, kRtSrcAarch64Darwin,
    259      (uint32_t)(sizeof(kRtSrcAarch64Darwin) / sizeof(kRtSrcAarch64Darwin[0])),
    260      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    261     {"aarch64-windows", KIT_ARCH_ARM_64, KIT_OS_WINDOWS, KIT_OBJ_COFF, 8, 8,
    262      "lib/include/llp64_le", 1, 0, kRtSrcAarch64Windows,
    263      (uint32_t)(sizeof(kRtSrcAarch64Windows) / sizeof(kRtSrcAarch64Windows[0])),
    264      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    265     {"riscv64-linux", KIT_ARCH_RV64, KIT_OS_LINUX, KIT_OBJ_ELF, 8, 8,
    266      "lib/include/lp64_le", 1, 1, kRtSrcRv64Elf,
    267      (uint32_t)(sizeof(kRtSrcRv64Elf) / sizeof(kRtSrcRv64Elf[0])),
    268      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    269     {"riscv64-freebsd", KIT_ARCH_RV64, KIT_OS_FREEBSD, KIT_OBJ_ELF, 8, 8,
    270      "lib/include/lp64_le", 1, 1, kRtSrcRv64Elf,
    271      (uint32_t)(sizeof(kRtSrcRv64Elf) / sizeof(kRtSrcRv64Elf[0])),
    272      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    273     {"riscv64-elf", KIT_ARCH_RV64, KIT_OS_FREESTANDING, KIT_OBJ_ELF, 8, 8,
    274      "lib/include/lp64_le", 1, 1, kRtSrcRv64Elf,
    275      (uint32_t)(sizeof(kRtSrcRv64Elf) / sizeof(kRtSrcRv64Elf[0])),
    276      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    277     /* rv32 freestanding. ilp32 (pure soft float) and ilp32f (hardware single
    278      * float, soft double) share one arch+ptr_size, so float_abi is part of the
    279      * variant identity and each is built with its own -march/-mabi. Keys match
    280      * the `make rt` variant dirs (riscv32-elf / riscv32-elf-hardfloat) so a
    281      * checkout shares build/rt with the Makefile. */
    282     {"riscv32-elf", KIT_ARCH_RV32, KIT_OS_FREESTANDING, KIT_OBJ_ELF, 4, 4,
    283      "lib/include/ilp32_le", 0, 0, kRtSrcRv32Elf,
    284      (uint32_t)(sizeof(kRtSrcRv32Elf) / sizeof(kRtSrcRv32Elf[0])),
    285      KIT_FLOAT_ABI_SOFT, "rv32imac", "ilp32"},
    286     {"riscv32-elf-hardfloat", KIT_ARCH_RV32, KIT_OS_FREESTANDING, KIT_OBJ_ELF,
    287      4, 4, "lib/include/ilp32_le", 0, 0, kRtSrcRv32Elf,
    288      (uint32_t)(sizeof(kRtSrcRv32Elf) / sizeof(kRtSrcRv32Elf[0])),
    289      KIT_FLOAT_ABI_SINGLE, "rv32imafc", "ilp32f"},
    290     /* arm32 freestanding (ARMv7-M Thumb-2, soft float). Key matches the
    291      * `make rt` variant dir `arm-eabi-thumb2` (RT_arm-eabi-thumb2 in mk/rt.mk)
    292      * so a checkout shares build/rt with the Makefile and the on-demand build
    293      * target is `make rt-arm-eabi-thumb2`. Soft-float arm objects do NOT stamp a
    294      * float-ABI e_flag (ARM toolchain convention: only hard-float sets a bit, so
    295      * elf_arm_float_abi_from_e_flags returns DEFAULT for kit's soft objects).
    296      * There is no hard-float arm runtime to disambiguate against, so this
    297      * variant is float-ABI-agnostic (DEFAULT wildcard) and resolves for any
    298      * detected arm float_abi — unlike rv32, whose objects do carry the ABI bits
    299      * and so pin SOFT vs SINGLE. The default `-target arm-none-eabi` profile is
    300      * correct (the make recipe sets no -march/-mabi), so isa/abi stay NULL. */
    301     {"arm-eabi-thumb2", KIT_ARCH_ARM_32, KIT_OS_FREESTANDING, KIT_OBJ_ELF, 4, 4,
    302      "lib/include/ilp32_le", 0, 0, kRtSrcArm32,
    303      (uint32_t)(sizeof(kRtSrcArm32) / sizeof(kRtSrcArm32[0])),
    304      KIT_FLOAT_ABI_DEFAULT, NULL, NULL},
    305 };
    306 
    307 static char* rt_dup(DriverEnv* env, const char* s, size_t* out_size) {
    308   size_t len = driver_strlen(s);
    309   char* p = (char*)driver_alloc(env, len + 1u);
    310   if (!p) return NULL;
    311   driver_memcpy(p, s, len + 1u);
    312   if (out_size) *out_size = len + 1u;
    313   return p;
    314 }
    315 
    316 static char* rt_join_slot(DriverEnv* env, char** slot, size_t* size_slot,
    317                           const char* a, const char* b) {
    318   char* p = driver_path_join(env, a, b, size_slot);
    319   *slot = p;
    320   return p;
    321 }
    322 
    323 static void rt_store_const_ptr(const char** slot, const char* value) {
    324   *slot = value;
    325 }
    326 
    327 static void rt_free_str(DriverEnv* env, char** p, size_t* n) {
    328   if (*p) driver_free(env, *p, *n);
    329   *p = NULL;
    330   *n = 0;
    331 }
    332 
    333 static int rt_has_layout(const char* rt_root) {
    334   char inc[4096];
    335   char lib[4096];
    336   size_t n = driver_strlen(rt_root);
    337   if (n + 13u >= sizeof(inc)) return 0;
    338   driver_memcpy(inc, rt_root, n);
    339   inc[n] = '/';
    340   driver_memcpy(inc + n + 1u, "include", 8u);
    341   driver_memcpy(lib, rt_root, n);
    342   lib[n] = '/';
    343   driver_memcpy(lib + n + 1u, "lib", 4u);
    344   return driver_path_exists(inc) && driver_path_exists(lib);
    345 }
    346 
    347 static int rt_set_layout(DriverEnv* env, DriverRuntimeSupport* out,
    348                          const char* support_root, const char* rt_root) {
    349   out->support_root = rt_dup(env, support_root, &out->support_root_size);
    350   out->rt_root = rt_dup(env, rt_root, &out->rt_root_size);
    351   out->include_dir =
    352       driver_path_join(env, rt_root, "include", &out->include_dir_size);
    353   if (!out->support_root || !out->rt_root || !out->include_dir) {
    354     driver_runtime_support_fini(env, out);
    355     return 1;
    356   }
    357   return 0;
    358 }
    359 
    360 static int rt_try_support_root(DriverEnv* env, const char* root,
    361                                DriverRuntimeSupport* out) {
    362   char* rt_root;
    363   size_t rt_root_size = 0;
    364   int ok;
    365 
    366   rt_root = driver_path_join(env, root, "rt", &rt_root_size);
    367   if (!rt_root) return 1;
    368   ok = rt_has_layout(rt_root) && rt_set_layout(env, out, root, rt_root) == 0;
    369   driver_free(env, rt_root, rt_root_size);
    370   return ok ? 0 : 1;
    371 }
    372 
    373 /* Directory-separator test for splitting the canonical program path. */
    374 static int rt_is_path_sep(char c) {
    375   return c == '/' || c == '\\';
    376 }
    377 
    378 static char* rt_exe_dir(DriverEnv* env, const char* exe, size_t* out_size) {
    379   const char* slash;
    380   size_t dir_len;
    381   char* dir;
    382   size_t dir_size;
    383   if (!exe) return NULL;
    384   slash = exe + driver_strlen(exe);
    385   while (slash > exe && !rt_is_path_sep(slash[-1])) --slash;
    386   if (slash == exe) return NULL;
    387   dir_len = (size_t)(slash - exe);
    388   if (dir_len == 0) return NULL;
    389 
    390   dir_size = dir_len + 1u;
    391   dir = (char*)driver_alloc(env, dir_size);
    392   if (!dir) return NULL;
    393   driver_memcpy(dir, exe, dir_len);
    394   dir[dir_len] = '\0';
    395   if (out_size) *out_size = dir_size;
    396   return dir;
    397 }
    398 
    399 static int rt_try_canonical_support(DriverEnv* env, const char* candidate,
    400                                     DriverRuntimeSupport* out) {
    401   char* canonical = NULL;
    402   size_t canonical_size = 0;
    403   int rc;
    404   if (driver_path_canonicalize(env, candidate, &canonical, &canonical_size) !=
    405       0)
    406     return 1;
    407   rc = rt_try_support_root(env, canonical, out);
    408   driver_free(env, canonical, canonical_size);
    409   return rc;
    410 }
    411 
    412 int driver_runtime_resolve(DriverEnv* env, const char* explicit_support_dir,
    413                            const char* argv0, DriverRuntimeSupport* out) {
    414   char* self = NULL;
    415   char* exe_dir = NULL;
    416   char* candidate = NULL;
    417   char* parent = NULL;
    418   size_t self_size = 0;
    419   size_t exe_dir_size = 0;
    420   size_t candidate_size = 0;
    421   size_t parent_size = 0;
    422   int rc = 1;
    423   DriverRuntimeSupport zero = {0};
    424   *out = zero;
    425 
    426   /* An explicit root is authoritative. Normalize and validate it; never hide
    427    * a bad explicit value by falling back to discovery. */
    428   if (explicit_support_dir) {
    429     rc = rt_try_canonical_support(env, explicit_support_dir, out);
    430     if (rc == 0 && driver_self_exe_path(env, &self, &self_size) == 0)
    431       out->tool_path = rt_dup(env, self, &out->tool_path_size);
    432     else if (rc == 0 && argv0)
    433       out->tool_path = rt_dup(env, argv0, &out->tool_path_size);
    434     if (self) driver_free(env, self, self_size);
    435     return rc;
    436   }
    437 
    438   /* Default discovery has one identity and two ordered layouts:
    439    *   <exe-dir>/support       development build
    440    *   <exe-dir>/../support    packaged distribution
    441    * No cwd/source-checkout fallback is permitted. */
    442   if (driver_self_exe_path(env, &self, &self_size) != 0 || !self) goto out;
    443   exe_dir = rt_exe_dir(env, self, &exe_dir_size);
    444   if (!exe_dir) goto out;
    445 
    446   candidate = driver_path_join(env, exe_dir, "support", &candidate_size);
    447   if (!candidate) goto out;
    448   if (rt_try_canonical_support(env, candidate, out) == 0) {
    449     rc = 0;
    450     goto found;
    451   }
    452   driver_free(env, candidate, candidate_size);
    453   candidate = NULL;
    454   candidate_size = 0;
    455 
    456   parent = driver_path_join(env, exe_dir, "..", &parent_size);
    457   if (!parent) goto out;
    458   candidate = driver_path_join(env, parent, "support", &candidate_size);
    459   if (!candidate) goto out;
    460   if (rt_try_canonical_support(env, candidate, out) == 0) {
    461     out->install_layout = 1;
    462     rc = 0;
    463   }
    464 
    465 found:
    466   if (rc == 0) out->tool_path = rt_dup(env, self, &out->tool_path_size);
    467 out:
    468   if (candidate) driver_free(env, candidate, candidate_size);
    469   if (parent) driver_free(env, parent, parent_size);
    470   if (exe_dir) driver_free(env, exe_dir, exe_dir_size);
    471   if (self) driver_free(env, self, self_size);
    472   return rc;
    473 }
    474 
    475 void driver_runtime_support_fini(DriverEnv* env, DriverRuntimeSupport* s) {
    476   rt_free_str(env, &s->include_dir, &s->include_dir_size);
    477   rt_free_str(env, &s->rt_root, &s->rt_root_size);
    478   rt_free_str(env, &s->support_root, &s->support_root_size);
    479   if (s->tool_path && s->tool_path_size) {
    480     driver_free(env, (void*)s->tool_path, s->tool_path_size);
    481     s->tool_path = NULL;
    482     s->tool_path_size = 0;
    483   }
    484 }
    485 
    486 int driver_runtime_add_freestanding_headers(const DriverRuntimeSupport* s,
    487                                             DriverCflags* cf) {
    488   uint32_t i;
    489   if (!s || !s->include_dir || !cf) return 1;
    490   for (i = cf->nsystem_include_dirs; i > 0; --i)
    491     cf->system_include_dirs[i] = cf->system_include_dirs[i - 1u];
    492   cf->system_include_dirs[0] = s->include_dir;
    493   cf->nsystem_include_dirs++;
    494   return 0;
    495 }
    496 
    497 int driver_runtime_append_freestanding_headers(const DriverRuntimeSupport* s,
    498                                                DriverCflags* cf) {
    499   uint32_t i;
    500   if (!s || !s->include_dir || !cf) return 1;
    501   i = cf->nsystem_include_dirs;
    502   cf->system_include_dirs[i] = s->include_dir;
    503   cf->nsystem_include_dirs = i + 1u;
    504   return 0;
    505 }
    506 
    507 static const RuntimeVariant* rt_variant_for_target(KitTargetSpec target) {
    508   uint32_t i;
    509   for (i = 0; i < (uint32_t)(sizeof(kRtVariants) / sizeof(kRtVariants[0]));
    510        ++i) {
    511     const RuntimeVariant* v = &kRtVariants[i];
    512     /* A variant with float_abi DEFAULT is float-ABI-agnostic and matches any
    513      * target (every non-RISC-V variant and rv64). A variant that pins a float
    514      * ABI (the two rv32 entries) matches only that ABI, so ilp32 vs ilp32f
    515      * targets select distinct runtimes. */
    516     if (target.arch == v->arch && target.os == v->os && target.obj == v->obj &&
    517         target.ptr_size == v->ptr_size && target.ptr_align == v->ptr_align &&
    518         (v->float_abi == KIT_FLOAT_ABI_DEFAULT ||
    519          v->float_abi == target.float_abi))
    520       return v;
    521   }
    522   return NULL;
    523 }
    524 
    525 int driver_runtime_has_variant(KitTargetSpec target) {
    526   return rt_variant_for_target(target) != NULL;
    527 }
    528 
    529 static int rt_prepare_pp(DriverEnv* env, const DriverRuntimeSupport* support,
    530                          const RuntimeVariant* variant, int assembler,
    531                          KitPreprocessOptions* pp, char*** owned_dirs,
    532                          size_t** owned_sizes, uint32_t* owned_count) {
    533   static const KitSlice def_int128_1 = KIT_SLICE_LIT("1");
    534   static const KitSlice def_int128_0 = KIT_SLICE_LIT("0");
    535   char** dirs = NULL;
    536   size_t* sizes = NULL;
    537   const char** include_dirs;
    538   const char** system_dirs;
    539   KitDefine* defs;
    540   uint32_t ninc = variant->ldbl128 ? 4u : 3u;
    541   uint32_t nsys = 2u;
    542   uint32_t ndefs = 1u + (variant->ldbl128 ? 1u : 0u) + (assembler ? 1u : 0u);
    543   uint32_t d = 0;
    544   KitPreprocessOptions z = {0};
    545 
    546   *pp = z;
    547   *owned_dirs = NULL;
    548   *owned_sizes = NULL;
    549   *owned_count = 0;
    550 
    551   include_dirs = (const char**)driver_alloc_zeroed(
    552       env, (size_t)ninc * sizeof(*include_dirs));
    553   system_dirs = (const char**)driver_alloc_zeroed(
    554       env, (size_t)nsys * sizeof(*system_dirs));
    555   defs = (KitDefine*)driver_alloc_zeroed(env, (size_t)ndefs * sizeof(*defs));
    556   dirs =
    557       (char**)driver_alloc_zeroed(env, (size_t)(ninc + nsys) * sizeof(*dirs));
    558   sizes =
    559       (size_t*)driver_alloc_zeroed(env, (size_t)(ninc + nsys) * sizeof(*sizes));
    560   if (!include_dirs || !system_dirs || !defs || !dirs || !sizes) {
    561     if (include_dirs)
    562       driver_free(env, (void*)include_dirs,
    563                   (size_t)ninc * sizeof(*include_dirs));
    564     if (system_dirs)
    565       driver_free(env, (void*)system_dirs, (size_t)nsys * sizeof(*system_dirs));
    566     if (defs) driver_free(env, defs, (size_t)ndefs * sizeof(*defs));
    567     if (dirs) driver_free(env, dirs, (size_t)(ninc + nsys) * sizeof(*dirs));
    568     if (sizes) driver_free(env, sizes, (size_t)(ninc + nsys) * sizeof(*sizes));
    569     return 1;
    570   }
    571 
    572   rt_join_slot(env, &dirs[0], &sizes[0], support->rt_root,
    573                "lib/include/common");
    574   rt_store_const_ptr(&include_dirs[0], dirs[0]);
    575   rt_join_slot(env, &dirs[1], &sizes[1], support->rt_root, "lib/impl");
    576   rt_store_const_ptr(&include_dirs[1], dirs[1]);
    577   rt_join_slot(env, &dirs[2], &sizes[2], support->rt_root,
    578                variant->abi_include);
    579   rt_store_const_ptr(&include_dirs[2], dirs[2]);
    580   if (variant->ldbl128) {
    581     rt_join_slot(env, &dirs[3], &sizes[3], support->rt_root,
    582                  "lib/include/lp64_le_ldbl128");
    583     rt_store_const_ptr(&include_dirs[3], dirs[3]);
    584     rt_join_slot(env, &dirs[4], &sizes[4], support->rt_root, "include");
    585     rt_store_const_ptr(&system_dirs[0], dirs[4]);
    586     rt_join_slot(env, &dirs[5], &sizes[5], support->rt_root, "include/libc");
    587     rt_store_const_ptr(&system_dirs[1], dirs[5]);
    588     d = 6;
    589   } else {
    590     rt_join_slot(env, &dirs[3], &sizes[3], support->rt_root, "include");
    591     rt_store_const_ptr(&system_dirs[0], dirs[3]);
    592     rt_join_slot(env, &dirs[4], &sizes[4], support->rt_root, "include/libc");
    593     rt_store_const_ptr(&system_dirs[1], dirs[4]);
    594     d = 5;
    595   }
    596   for (uint32_t i = 0; i < d; ++i) {
    597     if (!dirs[i]) {
    598       uint32_t j;
    599       for (j = 0; j < d; ++j)
    600         if (dirs[j]) driver_free(env, dirs[j], sizes[j]);
    601       driver_free(env, (void*)include_dirs,
    602                   (size_t)ninc * sizeof(*include_dirs));
    603       driver_free(env, (void*)system_dirs, (size_t)nsys * sizeof(*system_dirs));
    604       driver_free(env, defs, (size_t)ndefs * sizeof(*defs));
    605       driver_free(env, dirs, (size_t)(ninc + nsys) * sizeof(*dirs));
    606       driver_free(env, sizes, (size_t)(ninc + nsys) * sizeof(*sizes));
    607       return 1;
    608     }
    609   }
    610 
    611   defs[0].name = KIT_SLICE_LIT("HAS_INT128");
    612   defs[0].body = variant->has_int128 ? def_int128_1 : def_int128_0;
    613   uint32_t i = 1;
    614   if (variant->ldbl128) {
    615     defs[i].name = KIT_SLICE_LIT("KITRT_LDBL128");
    616     defs[i].body = KIT_SLICE_LIT("1");
    617     ++i;
    618   }
    619   if (assembler) {
    620     defs[i].name = KIT_SLICE_LIT("__ASSEMBLER__");
    621     defs[i].body = KIT_SLICE_LIT("1");
    622   }
    623 
    624   pp->include_dirs = include_dirs;
    625   pp->ninclude_dirs = ninc;
    626   pp->system_include_dirs = system_dirs;
    627   pp->nsystem_include_dirs = nsys;
    628   pp->defines = defs;
    629   pp->ndefines = ndefs;
    630   *owned_dirs = dirs;
    631   *owned_sizes = sizes;
    632   *owned_count = d;
    633   return 0;
    634 }
    635 
    636 static void rt_free_pp(DriverEnv* env, KitPreprocessOptions* pp,
    637                        char** owned_dirs, size_t* owned_sizes,
    638                        uint32_t owned_count) {
    639   uint32_t i;
    640   for (i = 0; i < owned_count; ++i)
    641     if (owned_dirs[i]) driver_free(env, owned_dirs[i], owned_sizes[i]);
    642   if (owned_dirs)
    643     driver_free(env, owned_dirs, (size_t)owned_count * sizeof(*owned_dirs));
    644   if (owned_sizes)
    645     driver_free(env, owned_sizes, (size_t)owned_count * sizeof(*owned_sizes));
    646   if (pp->include_dirs)
    647     driver_free(env, (void*)pp->include_dirs,
    648                 (size_t)pp->ninclude_dirs * sizeof(*pp->include_dirs));
    649   if (pp->system_include_dirs)
    650     driver_free(
    651         env, (void*)pp->system_include_dirs,
    652         (size_t)pp->nsystem_include_dirs * sizeof(*pp->system_include_dirs));
    653   if (pp->defines)
    654     driver_free(env, (void*)pp->defines,
    655                 (size_t)pp->ndefines * sizeof(*pp->defines));
    656 }
    657 
    658 static int rt_compile_source(DriverEnv* env,
    659                              const DriverRuntimeSupport* support,
    660                              const RuntimeVariant* variant, const char* tool,
    661                              KitCompiler* compiler, const char* rel,
    662                              KitArInput* member, KitWriter** obj_writer) {
    663   KitContext ctx = driver_env_to_context(env);
    664   char* lib_path = NULL;
    665   char* src_path = NULL;
    666   size_t lib_path_size = 0;
    667   size_t src_path_size = 0;
    668   DriverLoad src_load = {0};
    669   KitSlice input = {0};
    670   KitWriter* writer = NULL;
    671   KitWriter* pp_writer = NULL;
    672   const uint8_t* obj_data;
    673   size_t obj_len = 0;
    674   KitStatus st;
    675   int is_asm;
    676   int preprocess_asm;
    677   int rc = 1;
    678 
    679   *obj_writer = NULL;
    680   lib_path = driver_path_join(env, support->rt_root, "lib", &lib_path_size);
    681   if (!lib_path) goto out;
    682   src_path = driver_path_join(env, lib_path, rel, &src_path_size);
    683   if (!src_path) goto out;
    684   if (driver_load_bytes(ctx.file_io, tool, src_path, &src_load, &input) != 0)
    685     goto out;
    686   if (kit_writer_mem(ctx.heap, &writer) != KIT_OK) goto out;
    687 
    688   is_asm = driver_has_suffix(rel, ".s") || driver_has_suffix(rel, ".S");
    689   preprocess_asm = driver_has_suffix(rel, ".S");
    690   if (is_asm) {
    691     KitAsmCompileOptions aopts = {0};
    692     KitSlice asm_input = input;
    693     if (preprocess_asm) {
    694       KitPreprocessOptions pp;
    695       char** owned_dirs = NULL;
    696       size_t* owned_sizes = NULL;
    697       uint32_t owned_count = 0;
    698       if (rt_prepare_pp(env, support, variant, 1, &pp, &owned_dirs,
    699                         &owned_sizes, &owned_count) != 0)
    700         goto out;
    701       if (kit_writer_mem(ctx.heap, &pp_writer) != KIT_OK) {
    702         rt_free_pp(env, &pp, owned_dirs, owned_sizes, owned_count);
    703         goto out;
    704       }
    705       st = kit_cpp_preprocess(compiler, &pp, kit_slice_cstr(src_path), &input,
    706                               pp_writer);
    707       rt_free_pp(env, &pp, owned_dirs, owned_sizes, owned_count);
    708       if (st != KIT_OK || kit_writer_status(pp_writer) != KIT_OK) goto out;
    709       asm_input.data = kit_writer_mem_bytes(pp_writer, &asm_input.len);
    710     }
    711     {
    712       KitCompileSessionOptions sopts;
    713       KitCompileSession* session = NULL;
    714       KitSourceInput sin;
    715       KitObjBuilder* ob = NULL;
    716       memset(&sopts, 0, sizeof(sopts));
    717       sopts.lang = KIT_LANG_ASM;
    718       sopts.compile.code = aopts.code;
    719       sopts.compile.diagnostics = aopts.diagnostics;
    720       sopts.compile.language_options = &aopts;
    721       memset(&sin, 0, sizeof(sin));
    722       sin.name = kit_slice_cstr(src_path);
    723       sin.bytes = asm_input;
    724       sin.lang = KIT_LANG_ASM;
    725       st = kit_compile_session_new(compiler, &sopts, &session);
    726       if (st == KIT_OK) st = kit_compile_session_compile(session, &sin, &ob);
    727       if (st == KIT_OK) st = kit_obj_builder_emit(ob, writer);
    728       kit_obj_builder_free(ob);
    729       kit_compile_session_free(session);
    730     }
    731   } else {
    732     KitCCompileOptions copts = {0};
    733     KitPreprocessOptions pp = {0};
    734     KitCompileSessionOptions sopts;
    735     KitCompileSession* session = NULL;
    736     KitSourceInput sin;
    737     KitObjBuilder* ob = NULL;
    738     char** owned_dirs = NULL;
    739     size_t* owned_sizes = NULL;
    740     uint32_t owned_count = 0;
    741     if (rt_prepare_pp(env, support, variant, 0, &pp, &owned_dirs, &owned_sizes,
    742                       &owned_count) != 0)
    743       goto out;
    744     memset(&sopts, 0, sizeof(sopts));
    745     sopts.lang = KIT_LANG_C;
    746     sopts.compile.code = copts.code;
    747     sopts.compile.diagnostics = copts.diagnostics;
    748     sopts.compile.preprocess = pp;
    749     memset(&sin, 0, sizeof(sin));
    750     sin.name = kit_slice_cstr(src_path);
    751     sin.bytes = input;
    752     sin.lang = KIT_LANG_C;
    753     st = kit_compile_session_new(compiler, &sopts, &session);
    754     if (st == KIT_OK) st = kit_compile_session_compile(session, &sin, &ob);
    755     if (st == KIT_OK) st = kit_obj_builder_emit(ob, writer);
    756     kit_obj_builder_free(ob);
    757     kit_compile_session_free(session);
    758     rt_free_pp(env, &pp, owned_dirs, owned_sizes, owned_count);
    759   }
    760   if (st != KIT_OK || kit_writer_status(writer) != KIT_OK) {
    761     driver_errf(tool, "failed to build runtime source: %.*s",
    762                 KIT_SLICE_ARG(kit_slice_cstr(src_path)));
    763     goto out;
    764   }
    765 
    766   obj_data = kit_writer_mem_bytes(writer, &obj_len);
    767   member->name = kit_slice_cstr(driver_basename(rel));
    768   member->bytes.data = obj_data;
    769   member->bytes.len = obj_len;
    770   *obj_writer = writer;
    771   writer = NULL;
    772   rc = 0;
    773 
    774 out:
    775   if (pp_writer) kit_writer_close(pp_writer);
    776   if (writer) kit_writer_close(writer);
    777   driver_release_bytes(ctx.file_io, &src_load);
    778   if (src_path) driver_free(env, src_path, src_path_size);
    779   if (lib_path) driver_free(env, lib_path, lib_path_size);
    780   return rc;
    781 }
    782 
    783 static int rt_build_archive(DriverEnv* env, const DriverRuntimeSupport* support,
    784                             const RuntimeVariant* variant, const char* tool,
    785                             uint64_t epoch, const char* archive_path) {
    786   KitContext ctx = driver_env_to_context(env);
    787   KitTarget* resolved_target = NULL;
    788   KitCompiler* compiler = NULL;
    789   KitWriter* archive_writer = NULL;
    790   KitArInput* members = NULL;
    791   KitWriter** obj_writers = NULL;
    792   KitArWriteOptions ar_opts = {0};
    793   KitTargetSpec target;
    794   uint32_t i;
    795   int rc = 1;
    796 
    797   members = (KitArInput*)driver_alloc_zeroed(
    798       env, (size_t)variant->nsources * sizeof(*members));
    799   obj_writers = (KitWriter**)driver_alloc_zeroed(
    800       env, (size_t)variant->nsources * sizeof(*obj_writers));
    801   if (!members || !obj_writers) {
    802     driver_errf(tool, "out of memory");
    803     goto out;
    804   }
    805 
    806   target.arch = variant->arch;
    807   target.os = variant->os;
    808   target.obj = variant->obj;
    809   target.ptr_size = variant->ptr_size;
    810   target.ptr_align = variant->ptr_align;
    811   target.big_endian = 0;
    812   target.pic = KIT_PIC_NONE;
    813   target.code_model = KIT_CM_DEFAULT;
    814   target.float_abi = KIT_FLOAT_ABI_DEFAULT; /* resolved from abi below */
    815 
    816   {
    817     KitTargetOptions topts;
    818     memset(&topts, 0, sizeof topts);
    819     topts.spec = target;
    820     /* Build the runtime with the variant's -march/-mabi. NULL leaves the arch
    821      * defaults (every non-rv32 variant); rv32 pins them so the soft ilp32 and
    822      * hard ilp32f archives are each compiled correctly, and kit_target_new
    823      * resolves spec.float_abi from the ABI string to match the call sites. */
    824     if (variant->isa) topts.isa = kit_slice_cstr(variant->isa);
    825     if (variant->abi) topts.abi = kit_slice_cstr(variant->abi);
    826     if (kit_target_new(&ctx, &topts, &resolved_target) != KIT_OK ||
    827         driver_compiler_new(resolved_target, &ctx, &compiler) != KIT_OK) {
    828       driver_errf(tool, "failed to initialize compiler for runtime");
    829       goto out;
    830     }
    831   }
    832 
    833   for (i = 0; i < variant->nsources; ++i) {
    834     if (rt_compile_source(env, support, variant, tool, compiler,
    835                           variant->sources[i], &members[i],
    836                           &obj_writers[i]) != 0)
    837       goto out;
    838   }
    839 
    840   if (ctx.file_io->open_writer(ctx.file_io->user, archive_path,
    841                                &archive_writer) != KIT_OK) {
    842     driver_errf(tool, "failed to open runtime archive: %.*s",
    843                 KIT_SLICE_ARG(kit_slice_cstr(archive_path)));
    844     goto out;
    845   }
    846 
    847   ar_opts.epoch = epoch;
    848   ar_opts.long_names = 1;
    849   if (kit_ar_write(archive_writer, members, variant->nsources, &ar_opts) !=
    850       KIT_OK) {
    851     driver_errf(tool, "failed to write runtime archive: %.*s",
    852                 KIT_SLICE_ARG(kit_slice_cstr(archive_path)));
    853     goto out;
    854   }
    855   rc = 0;
    856 
    857 out:
    858   if (archive_writer) kit_writer_close(archive_writer);
    859   if (compiler) driver_compiler_free(compiler);
    860   kit_target_free(resolved_target);
    861   if (obj_writers) {
    862     for (i = 0; i < variant->nsources; ++i)
    863       if (obj_writers[i]) kit_writer_close(obj_writers[i]);
    864     driver_free(env, obj_writers,
    865                 (size_t)variant->nsources * sizeof(*obj_writers));
    866   }
    867   if (members)
    868     driver_free(env, members, (size_t)variant->nsources * sizeof(*members));
    869   return rc;
    870 }
    871 
    872 static int rt_is_archive_stale(DriverEnv* env,
    873                                const DriverRuntimeSupport* support,
    874                                const RuntimeVariant* variant,
    875                                const char* archive_path) {
    876   int64_t archive_mtime;
    877   int64_t tool_mtime;
    878   uint32_t i;
    879   if (driver_path_mtime_ns(archive_path, &archive_mtime) != 0) return 1;
    880   if (support->tool_path &&
    881       driver_path_mtime_ns(support->tool_path, &tool_mtime) == 0 &&
    882       tool_mtime > archive_mtime)
    883     return 1;
    884   for (i = 0; i < variant->nsources; ++i) {
    885     char* lib_path;
    886     char* src_path;
    887     size_t lib_path_size = 0;
    888     size_t src_path_size = 0;
    889     int64_t src_mtime;
    890     int stale = 0;
    891     lib_path = driver_path_join(env, support->rt_root, "lib", &lib_path_size);
    892     if (!lib_path) return 1;
    893     src_path =
    894         driver_path_join(env, lib_path, variant->sources[i], &src_path_size);
    895     driver_free(env, lib_path, lib_path_size);
    896     if (!src_path) return 1;
    897     if (driver_path_mtime_ns(src_path, &src_mtime) != 0 ||
    898         src_mtime > archive_mtime)
    899       stale = 1;
    900     driver_free(env, src_path, src_path_size);
    901     if (stale) return 1;
    902   }
    903   return 0;
    904 }
    905 
    906 static char* rt_archive_dir_for_root(DriverEnv* env, const char* root,
    907                                      const RuntimeVariant* variant,
    908                                      size_t* out_size) {
    909   char* build_rt;
    910   char* dir = NULL;
    911   size_t build_rt_size = 0;
    912   size_t dir_size = 0;
    913   build_rt = driver_path_join(env, root, "build/rt", &build_rt_size);
    914   if (!build_rt) return NULL;
    915   dir = driver_path_join(env, build_rt, variant->key, &dir_size);
    916   driver_free(env, build_rt, build_rt_size);
    917   if (out_size) *out_size = dir ? dir_size : 0;
    918   return dir;
    919 }
    920 
    921 static int rt_archive_try_dir(DriverEnv* env, const char* tool,
    922                               const DriverRuntimeSupport* support,
    923                               const RuntimeVariant* variant, uint64_t epoch,
    924                               const char* cache_dir, char** out_path,
    925                               size_t* out_path_size) {
    926   char* archive_path;
    927   size_t archive_path_size = 0;
    928 
    929   archive_path =
    930       driver_path_join(env, cache_dir, "libkit_rt.a", &archive_path_size);
    931   if (!archive_path) {
    932     driver_errf(tool, "out of memory");
    933     return 1;
    934   }
    935 
    936   if (!driver_path_exists(archive_path) ||
    937       rt_is_archive_stale(env, support, variant, archive_path)) {
    938     if (driver_mkdir_p(env, cache_dir) != 0) {
    939       driver_free(env, archive_path, archive_path_size);
    940       return 2;
    941     }
    942     if (rt_build_archive(env, support, variant, tool, epoch, archive_path) !=
    943         0) {
    944       driver_errf(tool, "compiler runtime for %.*s could not be built",
    945                   KIT_SLICE_ARG(kit_slice_cstr(variant->key)));
    946       driver_errf(tool, "support dir: %.*s",
    947                   KIT_SLICE_ARG(kit_slice_cstr(support->support_root)));
    948       driver_free(env, archive_path, archive_path_size);
    949       return 1;
    950     }
    951   }
    952 
    953   *out_path = archive_path;
    954   *out_path_size = archive_path_size;
    955   return 0;
    956 }
    957 
    958 int driver_runtime_ensure_archive(DriverEnv* env, const char* tool,
    959                                   const DriverRuntimeSupport* support,
    960                                   KitTargetSpec target, uint64_t epoch,
    961                                   char** out_path, size_t* out_path_size) {
    962   const RuntimeVariant* variant = rt_variant_for_target(target);
    963   const char* diag_tool = tool ? tool : "rt";
    964   char* in_tree_dir = NULL; /* <support_root>/build/rt/<variant> */
    965   char* cache_dir = NULL;   /* <env->cache_dir>/<variant>        */
    966   size_t in_tree_dir_size = 0;
    967   size_t cache_dir_size = 0;
    968   char* primary_dir;
    969   char* fallback_dir;
    970   int primary_rc;
    971   int fallback_rc = 1;
    972 
    973   *out_path = NULL;
    974   *out_path_size = 0;
    975 
    976   if (!variant) {
    977     driver_errf(diag_tool, "compiler runtime is not available for this target");
    978     return 1;
    979   }
    980 
    981   /* In-tree location, shared with `make rt` when support_root is a checkout. */
    982   in_tree_dir = rt_archive_dir_for_root(env, support->support_root, variant,
    983                                         &in_tree_dir_size);
    984   if (!in_tree_dir) {
    985     driver_errf(diag_tool, "out of memory");
    986     return 1;
    987   }
    988   /* User cache location, outside any support/install tree. */
    989   if (env->cache_dir) {
    990     cache_dir =
    991         driver_path_join(env, env->cache_dir, variant->key, &cache_dir_size);
    992     if (!cache_dir) {
    993       driver_free(env, in_tree_dir, in_tree_dir_size);
    994       driver_errf(diag_tool, "out of memory");
    995       return 1;
    996     }
    997   }
    998 
    999   /* A distribution install is not a build tree: prefer the user cache dir and
   1000    * keep the in-tree dir only as a fallback so the build never writes into the
   1001    * install.  A checkout (or explicit --support-dir) keeps the historical
   1002    * in-tree-first order, sharing `make rt`'s build/rt cache per checkout. */
   1003   if (support->install_layout && cache_dir) {
   1004     primary_dir = cache_dir;
   1005     fallback_dir = in_tree_dir;
   1006   } else {
   1007     primary_dir = in_tree_dir;
   1008     fallback_dir = cache_dir;
   1009   }
   1010 
   1011   primary_rc = rt_archive_try_dir(env, diag_tool, support, variant, epoch,
   1012                                   primary_dir, out_path, out_path_size);
   1013   if (primary_rc == 0) {
   1014     if (cache_dir) driver_free(env, cache_dir, cache_dir_size);
   1015     driver_free(env, in_tree_dir, in_tree_dir_size);
   1016     return 0;
   1017   }
   1018 
   1019   if (primary_rc != 2) {
   1020     if (cache_dir) driver_free(env, cache_dir, cache_dir_size);
   1021     driver_free(env, in_tree_dir, in_tree_dir_size);
   1022     return 1;
   1023   }
   1024 
   1025   if (fallback_dir) {
   1026     fallback_rc = rt_archive_try_dir(env, diag_tool, support, variant, epoch,
   1027                                      fallback_dir, out_path, out_path_size);
   1028   }
   1029 
   1030   if (fallback_rc == 2 || !fallback_dir) {
   1031     driver_errf(diag_tool, "failed to create runtime cache: %.*s",
   1032                 KIT_SLICE_ARG(kit_slice_cstr(primary_dir)));
   1033     if (fallback_dir) {
   1034       driver_errf(diag_tool, "failed to create runtime cache: %.*s",
   1035                   KIT_SLICE_ARG(kit_slice_cstr(fallback_dir)));
   1036     }
   1037   }
   1038   if (cache_dir) driver_free(env, cache_dir, cache_dir_size);
   1039   driver_free(env, in_tree_dir, in_tree_dir_size);
   1040   return fallback_rc == 0 ? 0 : 1;
   1041 }
   1042 
   1043 int driver_runtime_prepare_archive(DriverEnv* env, const char* tool,
   1044                                    const DriverRuntimeSupport* support,
   1045                                    KitTargetSpec target, uint64_t epoch,
   1046                                    DriverRuntimeArchive* out) {
   1047   DriverRuntimeArchive z = {0};
   1048   if (!out) return 1;
   1049   *out = z;
   1050   if (driver_runtime_ensure_archive(env, tool, support, target, epoch,
   1051                                     &out->path, &out->path_size) != 0)
   1052     return 1;
   1053   out->whole_archive = 0;
   1054   out->link_mode = KIT_LM_STATIC;
   1055   out->group_id = 0;
   1056   return 0;
   1057 }
   1058 
   1059 void driver_runtime_archive_fini(DriverEnv* env, DriverRuntimeArchive* a) {
   1060   if (!a) return;
   1061   if (a->path) driver_free(env, a->path, a->path_size);
   1062   a->path = NULL;
   1063   a->path_size = 0;
   1064   a->whole_archive = 0;
   1065   a->link_mode = 0;
   1066   a->group_id = 0;
   1067 }