kit

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

build.c (106875B)


      1 #include <kit/build.h>
      2 #include <kit/compile.h>
      3 #include <kit/core.h>
      4 #include <kit/link.h>
      5 #include <kit/object.h>
      6 #include <kit/preprocess.h>
      7 #include <stdint.h>
      8 #include <string.h>
      9 
     10 #include "archive_engine.h"
     11 #include "cflags.h"
     12 #include "driver.h"
     13 #include "hosted.h"
     14 #include "lib_resolve.h"
     15 #include "link_flags.h"
     16 #include "link_inputs.h"
     17 #include "runtime.h"
     18 
     19 /* `kit build-exe` / `build-lib` / `build-obj` — the kit-native build verbs.
     20  *
     21  * Each command is polyglot (C / asm / wasm resolved per file), compiles
     22  * entirely in memory, and writes no intermediate files. They share one
     23  * parse+run parameterized by output kind:
     24  *
     25  *   build-exe   link an executable           (link session, OUTPUT_EXE)
     26  *   build-lib   static .a / single C TU      (archive of compiled objects,
     27  *                                             semantic C amalgamation, or DSO)
     28  *   build-obj   one object; or a relocatable  (compile each source / link
     29  *               combine of N sources; or       session, OUTPUT_RELOCATABLE)
     30  *               --emit=asm|c|ir; -fsyntax-only
     31  *
     32  * build-obj fully replaces the retired `compile` tool. Two flag tiers:
     33  *   - global / per-output flags apply to the whole build (target, -O/-g, link
     34  *     flags, output flags); they may appear anywhere outside a --group.
     35  *   - scopable flags (-I/-isystem/-D/-U, -x, -X<lang>) form a global baseline
     36  *     and may be overridden inside a `--group [flags] -- sources...` block.
     37  *
     38  * Per-language frontend flags route through `-X<lang> FLAG` (e.g.
     39  * `-Xwasm -mfeature=simd128`). */
     40 
     41 /* Stand-in for "no -x; resolve language from the path suffix at compile time."
     42  */
     43 #define BUILD_LANG_AUTO KIT_LANG_AUTO
     44 
     45 typedef enum BuildOutputKind {
     46   BUILD_OUT_EXE,
     47   BUILD_OUT_LIB,
     48   BUILD_OUT_OBJ,
     49 } BuildOutputKind;
     50 
     51 typedef enum BuildEmit {
     52   BUILD_EMIT_OBJ = 0,
     53   BUILD_EMIT_ASM,
     54   BUILD_EMIT_C,
     55   BUILD_EMIT_IR,
     56 } BuildEmit;
     57 
     58 /* The link-input model (link items + object/archive/dso/lib arrays) is shared
     59  * with the cc driver; see driver/lib/link_inputs.h. */
     60 
     61 typedef struct BuildSource {
     62   const char* path; /* argv-borrowed */
     63   uint32_t group;   /* index into o->groups[]; 0 = global/bare baseline */
     64 } BuildSource;
     65 
     66 /* One -X<lang> frontend flag token, scoped to a language. */
     67 typedef struct BuildFeFlag {
     68   KitLanguage lang;
     69   char* tok; /* argv-borrowed */
     70 } BuildFeFlag;
     71 
     72 /* A compile-flag scope: the global baseline (groups[0]) plus one entry per
     73  * `--group`. Each carries its own preprocessor delta, language override, and
     74  * per-language frontend flags. */
     75 typedef struct BuildGroup {
     76   DriverCflags cf;
     77   int cf_inited;
     78   KitLanguage forced_lang; /* BUILD_LANG_AUTO if -x not set */
     79   BuildFeFlag* fe;
     80   uint32_t nfe;
     81   /* Merged preprocessor view (group delta over the global baseline), filled
     82    * after parsing. For groups[0] this borrows cf directly; for real groups it
     83    * points at the m_* arrays below. */
     84   KitPreprocessOptions pp;
     85   const char** m_inc;
     86   uint32_t m_ninc;
     87   const char** m_sys;
     88   uint32_t m_nsys;
     89   KitDefine* m_def;
     90   uint32_t m_ndef;
     91   uint32_t
     92       m_def_cap; /* allocation size; m_ndef <= cap once globals are shadowed */
     93   KitSlice* m_und;
     94   uint32_t m_nund;
     95 } BuildGroup;
     96 
     97 typedef struct BuildOptions {
     98   DriverEnv* env;
     99   KitFrontendRegistry* frontends;
    100   const char* tool;        /* "build-exe" | "build-lib" | "build-obj" */
    101   int kind;                /* BuildOutputKind */
    102   const char* driver_path; /* argv[0] */
    103   size_t argv_bound;
    104 
    105   /* Output / per-output state. */
    106   int emit; /* BuildEmit (build-obj) */
    107   int syntax_only;
    108   int opt_level;
    109   int debug_info;
    110   int dynamic;                /* -dynamic / -shared */
    111   int shared_requested;       /* -shared spelling, for build-exe diagnostics */
    112   int shared;                 /* computed: kind==lib && dynamic */
    113   int static_link;            /* -static */
    114   int pie;                    /* -pie */
    115   int pic_explicit;           /* -fPIC/-fPIE/-static/-pie/-no-pie */
    116   int function_sections;      /* -ffunction-sections */
    117   int data_sections;          /* -fdata-sections */
    118   int auto_var_init;          /* -ftrivial-auto-var-init= (KitAutoVarInit) */
    119   int stack_protector;        /* KitStackProtectorMode */
    120   int lto;                    /* -flto/-fno-lto */
    121   uint64_t disabled_backend_features;
    122   uint8_t default_visibility; /* KitSymVis */
    123   int warnings_are_errors;
    124   uint32_t max_errors;
    125   const char* output_path;
    126 
    127   KitTargetSpec target;
    128   DriverTargetFeatures target_features;
    129 
    130   /* Link-session options and owned -Wl state. */
    131   DriverLinkFlags link;
    132   uint64_t epoch;
    133 
    134   /* Hosted libc / sysroot state. */
    135   const char* sysroot;
    136   const char* support_dir;
    137   int freestanding;
    138   int nostdinc;
    139   int no_stdlib;
    140   int no_defaultlibs;
    141   int no_startfiles;
    142   int wants_hosted_libc;
    143   DriverHostedPlan hosted;
    144 
    145   /* Sources + compile-flag scopes. */
    146   BuildSource* sources;
    147   uint32_t nsources;
    148   BuildGroup* groups;
    149   uint32_t ngroups;
    150   uint32_t cur_group; /* scope for newly seen sources */
    151 
    152   /* Shared link-input model (build-exe / dynamic build-lib):
    153    * object/archive/dso/ lib arrays, the ordered link item list, -L search
    154    * paths, and the owned
    155    * `<sysroot>/lib` Windows slot. */
    156   DriverLinkInputSet inputs;
    157 } BuildOptions;
    158 
    159 static int build_record_framework(BuildOptions* o, const char* name);
    160 
    161 /* ===================================================================== */
    162 /* small parse helpers                                                    */
    163 /* ===================================================================== */
    164 
    165 static int build_lang_from_name(BuildOptions* o, const char* name,
    166                                 KitLanguage* out) {
    167   /* Resolve off the compile-time default frontend set (no compiler exists at
    168    * arg-parse time). Value-equivalent to the former explicit map:
    169    * c->C, asm/s->ASM, wasm/wat->WASM. */
    170   KitLanguage lang = kit_frontend_registry_language_for_name(o->frontends, name);
    171   if (lang == KIT_LANG_UNKNOWN) return 1;
    172   *out = lang;
    173   return 0;
    174 }
    175 
    176 static void build_err_unknown_language(BuildOptions* o, const char* flag,
    177                                        const char* value) {
    178   const char* const known[] = {"c", "asm", "assembler", "s",
    179                                "toy", "wasm", "wat"};
    180   const char* candidates[sizeof known / sizeof known[0]];
    181   DriverSuggestion suggestions[3];
    182   size_t i, count = 0, n;
    183   for (i = 0; i < sizeof known / sizeof known[0]; ++i) {
    184     KitLanguage lang;
    185     if (build_lang_from_name(o, known[i], &lang) == 0)
    186       candidates[count++] = known[i];
    187   }
    188   n = driver_suggest_values(value, candidates, count, suggestions, 3);
    189   if (n)
    190     driver_errf(o->tool, "unsupported %s language: %s; did you mean '%s'?",
    191                 flag, value, suggestions[0].value);
    192   else
    193     driver_errf(o->tool, "unsupported %s language: %s", flag, value);
    194 }
    195 
    196 /* ===================================================================== */
    197 /* allocation / lifetime                                                  */
    198 /* ===================================================================== */
    199 
    200 static int build_group_cf_init(BuildOptions* o, BuildGroup* g) {
    201   if (g->cf_inited) return 0;
    202   if (driver_cflags_init(&g->cf, o->env,
    203                          (int)(o->argv_bound + DRIVER_HOSTED_MAX_DEFINES +
    204                                DRIVER_HOSTED_MAX_INCLUDES)) != 0)
    205     return 1;
    206   g->cf_inited = 1;
    207   g->forced_lang = BUILD_LANG_AUTO;
    208   g->fe = driver_alloc_zeroed(o->env, o->argv_bound * sizeof(*g->fe));
    209   return g->fe ? 0 : 1;
    210 }
    211 
    212 static int build_alloc(BuildOptions* o, int argc) {
    213   size_t bound = (size_t)argc + 16u;
    214   o->argv_bound = bound;
    215   o->sources = driver_alloc_zeroed(o->env, bound * sizeof(*o->sources));
    216   o->groups = driver_alloc_zeroed(o->env, bound * sizeof(*o->groups));
    217   if (!o->sources || !o->groups) {
    218     driver_errf(o->tool, "out of memory");
    219     return 1;
    220   }
    221   if (driver_link_inputs_init(&o->inputs, o->env, o->tool, bound) != 0)
    222     return 1;
    223   /* groups[0] is the global / bare baseline. */
    224   o->ngroups = 1;
    225   if (driver_link_flags_init(&o->link, o->env, o->tool, (uint32_t)bound) != 0 ||
    226       build_group_cf_init(o, &o->groups[0]) != 0 ||
    227       driver_target_features_init(&o->target_features, o->env, argc) != 0) {
    228     driver_errf(o->tool, "out of memory");
    229     return 1;
    230   }
    231   return 0;
    232 }
    233 
    234 static void build_release(BuildOptions* o) {
    235   uint32_t i;
    236   size_t bound = o->argv_bound;
    237   for (i = 0; i < o->ngroups; ++i) {
    238     BuildGroup* g = &o->groups[i];
    239     if (g->cf_inited) driver_cflags_fini(&g->cf, o->env);
    240     if (g->fe) driver_free(o->env, g->fe, bound * sizeof(*g->fe));
    241     if (g->m_inc) driver_free(o->env, g->m_inc, g->m_ninc * sizeof(*g->m_inc));
    242     if (g->m_sys) driver_free(o->env, g->m_sys, g->m_nsys * sizeof(*g->m_sys));
    243     if (g->m_def)
    244       driver_free(o->env, g->m_def, g->m_def_cap * sizeof(*g->m_def));
    245     if (g->m_und) driver_free(o->env, g->m_und, g->m_nund * sizeof(*g->m_und));
    246   }
    247   driver_link_inputs_fini(&o->inputs);
    248   driver_hosted_plan_fini(o->env, &o->hosted);
    249   driver_link_flags_fini(&o->link);
    250   driver_target_features_fini(&o->target_features, o->env);
    251   if (o->sources) driver_free(o->env, o->sources, bound * sizeof(*o->sources));
    252   if (o->groups) driver_free(o->env, o->groups, bound * sizeof(*o->groups));
    253 }
    254 
    255 /* ===================================================================== */
    256 /* positional classification                                              */
    257 /* ===================================================================== */
    258 
    259 /* Routed through the shared driver_path_is_source authority (canonical
    260  * extension registry, headers excluded), so adding a frontend extension reaches
    261  * cc/build/run/dbg at once. build still treats .h as a header, not a source —
    262  * the helper excludes it. */
    263 static int build_is_source(BuildOptions* o, const char* s) {
    264   return kit_frontend_registry_path_kind(o->frontends, s, NULL) ==
    265          KIT_FRONTEND_PATH_SOURCE;
    266 }
    267 
    268 /* The language forced (via -x) for sources in group `gi`: the group's own -x,
    269  * else the global baseline's, else -1 (resolve by suffix). */
    270 static int build_scope_forced_lang(const BuildOptions* o, uint32_t gi) {
    271   if (gi != 0 && o->groups[gi].forced_lang != BUILD_LANG_AUTO)
    272     return (int)o->groups[gi].forced_lang;
    273   if (o->groups[0].forced_lang != BUILD_LANG_AUTO)
    274     return (int)o->groups[0].forced_lang;
    275   return -1;
    276 }
    277 
    278 static int build_classify_positional(BuildOptions* o, const char* a) {
    279   if (driver_streq(a, "-")) {
    280     driver_errf(o->tool, "stdin ('-') is not supported; pass a source file");
    281     return 1;
    282   }
    283   /* Explicit link-input suffixes are always link inputs — never reinterpreted
    284    * as sources, even when -x forces a language. */
    285   if (driver_has_suffix(a, ".o") || driver_has_suffix(a, ".obj")) {
    286     o->inputs.object_files[o->inputs.nobject_files++] = a;
    287     driver_link_inputs_push(&o->inputs, DRIVER_LINK_OBJECT,
    288                             o->inputs.nobject_files - 1u);
    289     return 0;
    290   }
    291   if (driver_has_suffix(a, ".a")) {
    292     DriverArchiveInput* ar = &o->inputs.archives[o->inputs.narchives++];
    293     ar->path = a;
    294     ar->whole_archive = o->inputs.cur_whole_archive;
    295     ar->link_mode = o->inputs.cur_link_mode;
    296     ar->group_id = o->inputs.cur_group_id;
    297     driver_link_inputs_push(&o->inputs, DRIVER_LINK_ARCHIVE,
    298                             o->inputs.narchives - 1u);
    299     return 0;
    300   }
    301   if (driver_is_dso_path(a)) {
    302     DriverDsoInput* d = &o->inputs.dsos[o->inputs.ndsos++];
    303     d->path = a;
    304     driver_link_inputs_push(&o->inputs, DRIVER_LINK_DSO, o->inputs.ndsos - 1u);
    305     return 0;
    306   }
    307   /* Otherwise a source: a recognized source suffix, or any file at all when a
    308    * language is forced in scope (so `-x c mykernel` compiles an extensionless
    309    * or odd-suffix file). */
    310   if (build_is_source(o, a) || build_scope_forced_lang(o, o->cur_group) >= 0) {
    311     BuildSource* s = &o->sources[o->nsources];
    312     s->path = a;
    313     s->group = o->cur_group;
    314     driver_link_inputs_push(&o->inputs, DRIVER_LINK_SOURCE, o->nsources);
    315     o->nsources++;
    316     return 0;
    317   }
    318   driver_errf(o->tool,
    319               "input does not have a recognized suffix: %.*s (use -x LANG)",
    320               KIT_SLICE_ARG(kit_slice_cstr(a)));
    321   return 1;
    322 }
    323 
    324 /* ===================================================================== */
    325 /* scopable-flag parsing (shared by global context and --group blocks)    */
    326 /* ===================================================================== */
    327 
    328 /* Try to consume a scopable flag (-I/-isystem/-D/-U, -x, -X<lang>) at argv[*i]
    329  * into group `g`. Returns 1 consumed, 0 not a scopable flag, -1 on error. */
    330 static int build_try_scopable(BuildOptions* o, BuildGroup* g, int argc,
    331                               char** argv, int* i) {
    332   const char* a = argv[*i];
    333   int r = driver_cflags_try_consume(&g->cf, o->env, o->tool, argc, argv, i);
    334   if (r != 0) return r;
    335 
    336   if (driver_streq(a, "-x")) {
    337     KitLanguage lang;
    338     if (++(*i) >= argc) {
    339       driver_errf(o->tool, "-x requires an argument");
    340       return -1;
    341     }
    342     if (driver_streq(argv[*i], "--")) {
    343       driver_errf(o->tool, "-x requires an argument before `--`");
    344       return -1;
    345     }
    346     if (build_lang_from_name(o, argv[*i], &lang) != 0) {
    347       build_err_unknown_language(o, "-x", argv[*i]);
    348       return -1;
    349     }
    350     g->forced_lang = lang;
    351     return 1;
    352   }
    353   if (driver_strneq(a, "-X", 2) && a[2] != '\0') {
    354     KitLanguage lang;
    355     BuildFeFlag* f;
    356     if (build_lang_from_name(o, a + 2, &lang) != 0) {
    357       build_err_unknown_language(o, "-X", a + 2);
    358       return -1;
    359     }
    360     if (++(*i) >= argc) {
    361       driver_errf(o->tool, "%.*s requires a following flag",
    362                   KIT_SLICE_ARG(kit_slice_cstr(a)));
    363       return -1;
    364     }
    365     if (driver_streq(argv[*i], "--")) {
    366       driver_errf(o->tool, "%.*s requires a following flag before `--`",
    367                   KIT_SLICE_ARG(kit_slice_cstr(a)));
    368       return -1;
    369     }
    370     f = &g->fe[g->nfe++];
    371     f->lang = lang;
    372     f->tok = argv[*i];
    373     return 1;
    374   }
    375   return 0;
    376 }
    377 
    378 /* Recognize the global / per-output flags, so a --group block can flag them as
    379  * misplaced with a pointed diagnostic. */
    380 static int build_is_global_flag(const char* a) {
    381   return driver_streq(a, "-o") || driver_strneq(a, "--output", 8) ||
    382          driver_strneq(a, "-O", 2) || driver_streq(a, "-g") ||
    383          driver_streq(a, "-S") || driver_strneq(a, "--emit=", 7) ||
    384          driver_streq(a, "-fsyntax-only") || driver_strneq(a, "-fPIC", 5) ||
    385          driver_strneq(a, "-fpic", 5) || driver_strneq(a, "-fPIE", 5) ||
    386          driver_strneq(a, "-fpie", 5) ||
    387          driver_streq(a, "-arch") || driver_streq(a, "-platform_version") ||
    388          driver_streq(a, "-macosx_version_min") ||
    389          driver_streq(a, "-ios_version_min") ||
    390          driver_streq(a, "-iphoneos_version_min") ||
    391          driver_streq(a, "-ios_simulator_version_min") ||
    392          driver_streq(a, "-iphonesimulator_version_min") ||
    393          driver_strneq(a, "-fvisibility=", 13) ||
    394          driver_streq(a, "-ffunction-sections") ||
    395          driver_streq(a, "-fdata-sections") ||
    396          driver_strneq(a, "-ftrivial-auto-var-init=", 24) ||
    397          driver_streq(a, "-fno-builtin") ||
    398          driver_strneq(a, "-fno-builtin-", 13) ||
    399          driver_streq(a, "-fno-stack-protector") ||
    400          driver_strneq(a, "-fstack-protector", 17) ||
    401          driver_streq(a, "-flto") || driver_streq(a, "-fno-lto") ||
    402          driver_streq(a, "-static") || driver_streq(a, "-dynamic") ||
    403          driver_streq(a, "-shared") || driver_streq(a, "-pie") ||
    404          driver_streq(a, "-no-pie") || driver_streq(a, "-target") ||
    405          driver_strneq(a, "--target", 8) || driver_strneq(a, "-l", 2) ||
    406          driver_strneq(a, "-L", 2) || driver_strneq(a, "-F", 2) ||
    407          driver_streq(a, "-framework") || driver_streq(a, "-e") ||
    408          driver_streq(a, "-T") ||
    409          driver_streq(a, "-Ttext") || driver_strneq(a, "-Ttext=", 7) ||
    410          driver_streq(a, "--gc-sections") ||
    411          driver_streq(a, "--no-gc-sections") ||
    412          driver_streq(a, "--no-undefined") ||
    413          driver_streq(a, "--allow-undefined") ||
    414          driver_streq(a, "--allow-shlib-undefined") ||
    415          driver_streq(a, "-z") ||
    416          (a[0] == '-' && a[1] == 'z' && a[2] != '\0') ||
    417          driver_streq(a, "--map") || driver_strneq(a, "--map=", 6) ||
    418          driver_streq(a, "-Map") || driver_strneq(a, "-Map=", 5) ||
    419          driver_streq(a, "--symbols") ||
    420          driver_strneq(a, "--symbols=", 10) ||
    421          driver_strneq(a, "--symbols-format=", 17) ||
    422          driver_strneq(a, "-Wl,", 4) || driver_strneq(a, "--build-id", 10) ||
    423          driver_streq(a, "-Werror") || driver_strneq(a, "-fmax-errors=", 13) ||
    424          driver_strneq(a, "-m", 2);
    425 }
    426 
    427 /* ===================================================================== */
    428 /* main parser                                                            */
    429 /* ===================================================================== */
    430 
    431 static int build_parse_group(BuildOptions* o, int argc, char** argv, int* i) {
    432   BuildGroup* g;
    433   uint32_t gid = o->ngroups++;
    434   g = &o->groups[gid];
    435   if (build_group_cf_init(o, g) != 0) {
    436     driver_errf(o->tool, "out of memory");
    437     return 1;
    438   }
    439   ++(*i); /* past --group */
    440   /* scopable flag section, terminated by `--` */
    441   for (; *i < argc && !driver_streq(argv[*i], "--"); ++(*i)) {
    442     int r = build_try_scopable(o, g, argc, argv, i);
    443     if (r < 0) return 1;
    444     if (r > 0) continue;
    445     if (build_is_global_flag(argv[*i])) {
    446       driver_errf(o->tool,
    447                   "%.*s is a per-output flag; place it before any --group",
    448                   KIT_SLICE_ARG(kit_slice_cstr(argv[*i])));
    449       return 1;
    450     }
    451     driver_errf(o->tool, "unsupported flag in --group: %.*s",
    452                 KIT_SLICE_ARG(kit_slice_cstr(argv[*i])));
    453     return 1;
    454   }
    455   if (*i >= argc) {
    456     driver_errf(o->tool, "--group requires `--` before its sources");
    457     return 1;
    458   }
    459   ++(*i);             /* past `--` */
    460   o->cur_group = gid; /* subsequent sources belong to this group */
    461   return 0;
    462 }
    463 
    464 static int build_parse(int argc, char** argv, BuildOptions* o) {
    465   int i;
    466   o->target = driver_host_target();
    467 
    468   for (i = 1; i < argc; ++i) {
    469     const char* a = argv[i];
    470 
    471     if (driver_streq(a, "--group")) {
    472       if (build_parse_group(o, argc, argv, &i) != 0) return 1;
    473       --i; /* parse_group leaves i at the next token; loop's ++i re-reads it */
    474       continue;
    475     }
    476 
    477     /* Scopable flags in the global context feed the baseline (groups[0]). */
    478     {
    479       int r = build_try_scopable(o, &o->groups[0], argc, argv, &i);
    480       if (r < 0) return 1;
    481       if (r > 0) continue;
    482     }
    483 
    484     /* Output form. */
    485     if (driver_streq(a, "-c")) {
    486       o->emit = BUILD_EMIT_OBJ;
    487       continue;
    488     }
    489     if (driver_streq(a, "-S") || driver_streq(a, "--emit=asm")) {
    490       o->emit = BUILD_EMIT_ASM;
    491       continue;
    492     }
    493     if (driver_streq(a, "--emit=obj")) {
    494       o->emit = BUILD_EMIT_OBJ;
    495       continue;
    496     }
    497     if (driver_streq(a, "--emit=c")) {
    498       o->emit = BUILD_EMIT_C;
    499       continue;
    500     }
    501     if (driver_streq(a, "--emit=ir")) {
    502       o->emit = BUILD_EMIT_IR;
    503       continue;
    504     }
    505     if (driver_streq(a, "-fsyntax-only")) {
    506       o->syntax_only = 1;
    507       continue;
    508     }
    509     if (driver_streq(a, "-o")) {
    510       if (++i >= argc) {
    511         driver_errf(o->tool, "-o requires an argument");
    512         return 1;
    513       }
    514       o->output_path = argv[i];
    515       continue;
    516     }
    517     if (driver_strneq(a, "-o", 2)) {
    518       o->output_path = a + 2;
    519       continue;
    520     }
    521     if (driver_strneq(a, "--output=", 9)) {
    522       o->output_path = a + 9;
    523       continue;
    524     }
    525     if (driver_streq(a, "--output")) {
    526       if (++i >= argc) {
    527         driver_errf(o->tool, "--output requires an argument");
    528         return 1;
    529       }
    530       o->output_path = argv[i];
    531       continue;
    532     }
    533 
    534     /* Optimization / debug / codegen knobs. */
    535     if (driver_streq(a, "-g")) {
    536       o->debug_info = 1;
    537       continue;
    538     }
    539     if (driver_streq(a, "-O0")) {
    540       o->opt_level = 0;
    541       continue;
    542     }
    543     if (driver_streq(a, "-O1")) {
    544       o->opt_level = 1;
    545       continue;
    546     }
    547     if (driver_streq(a, "-O2") || driver_streq(a, "-O") ||
    548         driver_streq(a, "-O3") || driver_streq(a, "-Os") ||
    549         driver_streq(a, "-Oz") || driver_streq(a, "-Ofast")) {
    550       o->opt_level = 1;
    551       continue;
    552     }
    553     if (driver_streq(a, "-Werror") || driver_strneq(a, "-Werror=", 8)) {
    554       o->warnings_are_errors = 1;
    555       continue;
    556     }
    557     if (driver_strneq(a, "-fmax-errors=", 13)) {
    558       uint64_t v;
    559       if (driver_parse_u64(a + 13, &v) != 0 || v > 0xFFFFFFFFu) {
    560         driver_errf(o->tool, "-fmax-errors= requires a non-negative integer");
    561         return 1;
    562       }
    563       o->max_errors = (uint32_t)v;
    564       continue;
    565     }
    566     if (driver_streq(a, "-fPIC") || driver_streq(a, "-fpic")) {
    567       o->target.pic = KIT_PIC_PIC;
    568       o->pic_explicit = 1;
    569       continue;
    570     }
    571     if (driver_streq(a, "-fPIE") || driver_streq(a, "-fpie")) {
    572       o->target.pic = KIT_PIC_PIE;
    573       o->pic_explicit = 1;
    574       continue;
    575     }
    576     if (driver_streq(a, "-fno-PIC") || driver_streq(a, "-fno-pic") ||
    577         driver_streq(a, "-fno-PIE") || driver_streq(a, "-fno-pie")) {
    578       o->target.pic = KIT_PIC_NONE;
    579       o->pic_explicit = 1;
    580       continue;
    581     }
    582     if (driver_streq(a, "-fvisibility=hidden")) {
    583       o->default_visibility = KIT_SV_HIDDEN;
    584       continue;
    585     }
    586     if (driver_streq(a, "-fvisibility=default")) {
    587       o->default_visibility = KIT_SV_DEFAULT;
    588       continue;
    589     }
    590     if (driver_strneq(a, "-fvisibility=", 13)) {
    591       driver_errf(o->tool, "unsupported visibility: %.*s",
    592                   KIT_SLICE_ARG(kit_slice_cstr(a + 13)));
    593       return 1;
    594     }
    595     if (driver_streq(a, "-ffunction-sections")) {
    596       o->function_sections = 1;
    597       continue;
    598     }
    599     if (driver_streq(a, "-fno-function-sections")) {
    600       o->function_sections = 0;
    601       continue;
    602     }
    603     if (driver_streq(a, "-fdata-sections")) {
    604       o->data_sections = 1;
    605       continue;
    606     }
    607     if (driver_strneq(a, "-ftrivial-auto-var-init=", 24)) {
    608       const char* mode = a + 24;
    609       if (driver_streq(mode, "zero")) {
    610         o->auto_var_init = KIT_AUTOVAR_ZERO;
    611       } else if (driver_streq(mode, "uninitialized")) {
    612         o->auto_var_init = KIT_AUTOVAR_UNINIT;
    613       } else if (driver_streq(mode, "pattern")) {
    614         driver_errf(o->tool,
    615                     "-ftrivial-auto-var-init=pattern is not yet supported; "
    616                     "use =zero");
    617         return 1;
    618       } else {
    619         driver_errf(o->tool,
    620                     "-ftrivial-auto-var-init=: unknown mode '%s' "
    621                     "(expected zero, pattern, or uninitialized)",
    622                     mode);
    623         return 1;
    624       }
    625       continue;
    626     }
    627     if (driver_streq(a, "-fno-data-sections")) {
    628       o->data_sections = 0;
    629       continue;
    630     }
    631     if (driver_streq(a, "-fno-builtin") ||
    632         driver_strneq(a, "-fno-builtin-", 13)) {
    633       continue;
    634     }
    635     if (driver_streq(a, "-fno-stack-protector")) {
    636       o->stack_protector = KIT_STACK_PROTECTOR_NONE;
    637       continue;
    638     }
    639     if (driver_streq(a, "-fstack-protector")) {
    640       o->stack_protector = KIT_STACK_PROTECTOR_BASIC;
    641       continue;
    642     }
    643     if (driver_streq(a, "-fstack-protector-strong")) {
    644       o->stack_protector = KIT_STACK_PROTECTOR_STRONG;
    645       continue;
    646     }
    647     if (driver_streq(a, "-fstack-protector-all")) {
    648       o->stack_protector = KIT_STACK_PROTECTOR_ALL;
    649       continue;
    650     }
    651     if (driver_strneq(a, "-fstack-protector", 17)) {
    652       driver_errf(o->tool, "unsupported stack protector mode: %.*s",
    653                   KIT_SLICE_ARG(kit_slice_cstr(a)));
    654       return 1;
    655     }
    656     if (driver_streq(a, "-flto")) {
    657       o->lto = 1;
    658       continue;
    659     }
    660     if (driver_streq(a, "-fno-lto")) {
    661       o->lto = 0;
    662       continue;
    663     }
    664     if (driver_streq(a, "-ffreestanding")) {
    665       o->freestanding = 1;
    666       continue;
    667     }
    668     if (driver_streq(a, "-fhosted")) {
    669       o->freestanding = 0;
    670       continue;
    671     }
    672     if (driver_streq(a, "-nostdinc")) {
    673       o->nostdinc = 1;
    674       continue;
    675     }
    676     if (driver_streq(a, "-nostdlib")) {
    677       o->no_stdlib = 1;
    678       continue;
    679     }
    680     if (driver_streq(a, "-nodefaultlibs")) {
    681       o->no_defaultlibs = 1;
    682       continue;
    683     }
    684     if (driver_streq(a, "-nostartfiles")) {
    685       o->no_startfiles = 1;
    686       continue;
    687     }
    688 
    689     /* Link kind. */
    690     if (driver_streq(a, "-static")) {
    691       o->static_link = 1;
    692       o->target.pic = KIT_PIC_NONE;
    693       o->pic_explicit = 1;
    694       o->inputs.cur_link_mode = KIT_LM_STATIC;
    695       continue;
    696     }
    697     if (driver_streq(a, "-dynamic")) {
    698       o->dynamic = 1;
    699       continue;
    700     }
    701     if (driver_streq(a, "-shared")) {
    702       o->dynamic = 1;
    703       o->shared_requested = 1;
    704       continue;
    705     }
    706     if (driver_streq(a, "-pie")) {
    707       o->target.pic = KIT_PIC_PIE;
    708       o->pie = 1;
    709       o->pic_explicit = 1;
    710       continue;
    711     }
    712     if (driver_streq(a, "-no-pie")) {
    713       o->target.pic = KIT_PIC_NONE;
    714       o->pie = 0;
    715       o->pic_explicit = 1;
    716       continue;
    717     }
    718 
    719     /* Link inputs / flags. */
    720     if (driver_strneq(a, "-L", 2)) {
    721       const char* dir = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
    722       if (!dir) {
    723         driver_errf(o->tool, "-L requires an argument");
    724         return 1;
    725       }
    726       o->inputs.lib_search_paths[o->inputs.nlib_search_paths++] = dir;
    727       continue;
    728     }
    729     if (driver_strneq(a, "-F", 2)) {
    730       const char* dir = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
    731       if (!dir) {
    732         driver_errf(o->tool, "-F requires an argument");
    733         return 1;
    734       }
    735       o->inputs.framework_search_paths[o->inputs.nframework_search_paths++] =
    736           dir;
    737       continue;
    738     }
    739     if (driver_strneq(a, "-l", 2)) {
    740       const char* name = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
    741       if (!name) {
    742         driver_errf(o->tool, "-l requires an argument");
    743         return 1;
    744       }
    745       if (driver_streq(name, "c") && !o->no_stdlib && !o->no_defaultlibs) {
    746         o->wants_hosted_libc = 1;
    747         continue;
    748       }
    749       {
    750         DriverPendingLib* pl =
    751             &o->inputs.pending_libs[o->inputs.npending_libs++];
    752         pl->name = name;
    753         pl->whole_archive = o->inputs.cur_whole_archive;
    754         pl->link_mode = o->inputs.cur_link_mode;
    755         pl->group_id = o->inputs.cur_group_id;
    756         driver_link_inputs_push(&o->inputs, DRIVER_LINK_LIB,
    757                                 o->inputs.npending_libs - 1u);
    758       }
    759       continue;
    760     }
    761     if (driver_streq(a, "-framework")) {
    762       if (++i >= argc) {
    763         driver_errf(o->tool, "-framework requires an argument");
    764         return 1;
    765       }
    766       if (build_record_framework(o, argv[i]) != 0) return 1;
    767       continue;
    768     }
    769     if (driver_streq(a, "-e")) {
    770       if (++i >= argc) {
    771         driver_errf(o->tool, "-e requires an argument");
    772         return 1;
    773       }
    774       o->link.entry = argv[i];
    775       continue;
    776     }
    777     if (driver_streq(a, "-T")) {
    778       if (++i >= argc) {
    779         driver_errf(o->tool, "-T requires an argument");
    780         return 1;
    781       }
    782       o->link.linker_script = argv[i];
    783       continue;
    784     }
    785     if (driver_streq(a, "-Ttext")) {
    786       if (++i >= argc) {
    787         driver_errf(o->tool, "-Ttext requires an argument");
    788         return 1;
    789       }
    790       if (driver_link_flags_record_text_base(&o->link, argv[i]) != 0)
    791         return 1;
    792       continue;
    793     }
    794     if (driver_strneq(a, "-Ttext=", 7)) {
    795       if (driver_link_flags_record_text_base(&o->link, a + 7) != 0) return 1;
    796       continue;
    797     }
    798     if (driver_streq(a, "-Tdata") || driver_streq(a, "-Tbss")) {
    799       const char* secname = driver_streq(a, "-Tdata") ? ".data" : ".bss";
    800       if (++i >= argc) {
    801         driver_errf(o->tool, "%s requires an address", a);
    802         return 1;
    803       }
    804       if (driver_link_flags_record_section_addr(&o->link, secname, argv[i]) != 0)
    805         return 1;
    806       continue;
    807     }
    808     if (driver_strneq(a, "-Tdata=", 7)) {
    809       if (driver_link_flags_record_section_addr(&o->link, ".data", a + 7) != 0)
    810         return 1;
    811       continue;
    812     }
    813     if (driver_strneq(a, "-Tbss=", 6)) {
    814       if (driver_link_flags_record_section_addr(&o->link, ".bss", a + 6) != 0)
    815         return 1;
    816       continue;
    817     }
    818     if (driver_streq(a, "--defsym")) {
    819       if (++i >= argc) {
    820         driver_errf(o->tool, "--defsym requires NAME=EXPR");
    821         return 1;
    822       }
    823       if (driver_link_flags_record_defsym(&o->link, argv[i],
    824                                           driver_strlen(argv[i])) != 0)
    825         return 1;
    826       continue;
    827     }
    828     if (driver_strneq(a, "--defsym=", 9)) {
    829       if (driver_link_flags_record_defsym(&o->link, a + 9,
    830                                           driver_strlen(a + 9)) != 0)
    831         return 1;
    832       continue;
    833     }
    834     if (driver_strneq(a, "--section-start=", 16)) {
    835       if (driver_link_flags_record_section_start(&o->link, a + 16,
    836                                                  driver_strlen(a + 16)) != 0)
    837         return 1;
    838       continue;
    839     }
    840     if (driver_streq(a, "--section-start")) {
    841       if (++i >= argc) {
    842         driver_errf(o->tool, "--section-start requires .NAME=ADDR");
    843         return 1;
    844       }
    845       if (driver_link_flags_record_section_start(&o->link, argv[i],
    846                                                  driver_strlen(argv[i])) != 0)
    847         return 1;
    848       continue;
    849     }
    850     if (driver_strneq(a, "--orphan-handling=", 18)) {
    851       if (driver_link_flags_record_orphan_handling(
    852               &o->link, a + 18, driver_strlen(a + 18)) != 0)
    853         return 1;
    854       continue;
    855     }
    856     if (driver_streq(a, "--orphan-handling")) {
    857       if (++i >= argc) {
    858         driver_errf(o->tool, "--orphan-handling requires a mode");
    859         return 1;
    860       }
    861       if (driver_link_flags_record_orphan_handling(
    862               &o->link, argv[i], driver_strlen(argv[i])) != 0)
    863         return 1;
    864       continue;
    865     }
    866     if (driver_streq(a, "--fatal-warnings")) {
    867       o->link.fatal_warnings = 1;
    868       continue;
    869     }
    870     if (driver_streq(a, "--no-fatal-warnings")) {
    871       o->link.fatal_warnings = 0;
    872       continue;
    873     }
    874     if (driver_streq(a, "--print-memory-usage")) {
    875       o->link.print_memory_usage = 1;
    876       continue;
    877     }
    878     if (driver_streq(a, "--cref")) {
    879       if (++i >= argc) {
    880         driver_errf(o->tool, "--cref requires a path");
    881         return 1;
    882       }
    883       if (driver_link_flags_record_cref(&o->link, argv[i],
    884                                         driver_strlen(argv[i])) != 0)
    885         return 1;
    886       continue;
    887     }
    888     if (driver_strneq(a, "--cref=", 7)) {
    889       if (driver_link_flags_record_cref(&o->link, a + 7,
    890                                         driver_strlen(a + 7)) != 0)
    891         return 1;
    892       continue;
    893     }
    894     if (driver_streq(a, "--gc-sections")) {
    895       o->link.gc_sections = 1;
    896       continue;
    897     }
    898     if (driver_streq(a, "--no-gc-sections")) {
    899       o->link.gc_sections = 0;
    900       continue;
    901     }
    902     if (driver_streq(a, "--no-undefined")) {
    903       o->link.allow_undefined = 0;
    904       continue;
    905     }
    906     if (driver_streq(a, "--allow-undefined") ||
    907         driver_streq(a, "--allow-shlib-undefined")) {
    908       o->link.allow_undefined = 1;
    909       continue;
    910     }
    911     if (driver_streq(a, "-z")) {
    912       if (++i >= argc) {
    913         driver_errf(o->tool, "-z requires an argument");
    914         return 1;
    915       }
    916       if (driver_link_flags_record_z(&o->link, argv[i],
    917                                      driver_strlen(argv[i])) != 0)
    918         return 1;
    919       continue;
    920     }
    921     if (a[0] == '-' && a[1] == 'z' && a[2] != '\0') {
    922       if (driver_link_flags_record_z(&o->link, a + 2,
    923                                      driver_strlen(a + 2)) != 0)
    924         return 1;
    925       continue;
    926     }
    927     if (driver_streq(a, "--map") || driver_streq(a, "-Map")) {
    928       if (++i >= argc) {
    929         driver_errf(o->tool, "%.*s requires an argument",
    930                     KIT_SLICE_ARG(kit_slice_cstr(a)));
    931         return 1;
    932       }
    933       if (driver_link_flags_record_map(&o->link, argv[i],
    934                                        driver_strlen(argv[i])) != 0)
    935         return 1;
    936       continue;
    937     }
    938     if (driver_strneq(a, "--map=", 6)) {
    939       if (driver_link_flags_record_map(&o->link, a + 6,
    940                                        driver_strlen(a + 6)) != 0)
    941         return 1;
    942       continue;
    943     }
    944     if (driver_strneq(a, "-Map=", 5)) {
    945       if (driver_link_flags_record_map(&o->link, a + 5,
    946                                        driver_strlen(a + 5)) != 0)
    947         return 1;
    948       continue;
    949     }
    950     if (driver_streq(a, "--symbols")) {
    951       if (++i >= argc) {
    952         driver_errf(o->tool, "--symbols requires an argument");
    953         return 1;
    954       }
    955       if (driver_link_flags_record_symbols(&o->link, argv[i],
    956                                            driver_strlen(argv[i])) != 0)
    957         return 1;
    958       continue;
    959     }
    960     if (driver_strneq(a, "--symbols=", 10)) {
    961       if (driver_link_flags_record_symbols(&o->link, a + 10,
    962                                            driver_strlen(a + 10)) != 0)
    963         return 1;
    964       continue;
    965     }
    966     if (driver_strneq(a, "--symbols-format=", 17)) {
    967       if (driver_link_flags_record_symbols_format(
    968               &o->link, a + 17, driver_strlen(a + 17)) != 0)
    969         return 1;
    970       continue;
    971     }
    972     if (driver_strneq(a, "-Wl,", 4)) {
    973       if (driver_link_flags_record_wl(&o->link, a + 4) != 0) return 1;
    974       continue;
    975     }
    976     if (driver_streq(a, "-Xlinker")) {
    977       if (++i >= argc) {
    978         driver_errf(o->tool, "-Xlinker requires an argument");
    979         return 1;
    980       }
    981       if (driver_link_flags_record_wl(&o->link, argv[i]) != 0) return 1;
    982       continue;
    983     }
    984     if (driver_strneq(a, "--build-id=", 11)) {
    985       if (driver_link_flags_record_build_id(&o->link, a + 11) != 0) return 1;
    986       continue;
    987     }
    988     if (driver_streq(a, "--build-id")) {
    989       if (driver_link_flags_record_build_id(&o->link, "sha256") != 0) return 1;
    990       continue;
    991     }
    992     if (driver_streq(a, "-mwindows")) {
    993       o->link.pe_subsystem = KIT_PE_SUBSYSTEM_WINDOWS_GUI;
    994       continue;
    995     }
    996     if (driver_streq(a, "-mconsole")) {
    997       o->link.pe_subsystem = KIT_PE_SUBSYSTEM_WINDOWS_CUI;
    998       continue;
    999     }
   1000     if (driver_strneq(a, "-mcmodel=", 9)) {
   1001       if (driver_record_mcmodel(&o->target, o->tool, a + 9) != 0) return 1;
   1002       continue;
   1003     }
   1004     if (driver_streq(a, "-mno-red-zone")) {
   1005       o->disabled_backend_features |= KIT_CG_BACKEND_RED_ZONE;
   1006       continue;
   1007     }
   1008     if (driver_streq(a, "-mred-zone")) {
   1009       o->disabled_backend_features &= ~KIT_CG_BACKEND_RED_ZONE;
   1010       continue;
   1011     }
   1012     if (driver_streq(a, "-mgeneral-regs-only")) {
   1013       o->disabled_backend_features |= KIT_CG_BACKEND_SIMD;
   1014       continue;
   1015     }
   1016     if (driver_streq(a, "-mno-general-regs-only")) {
   1017       o->disabled_backend_features &= ~KIT_CG_BACKEND_SIMD;
   1018       continue;
   1019     }
   1020 
   1021     /* Target. */
   1022     {
   1023       int dr = driver_darwin_platform_try_consume(
   1024           o->tool, argc, argv, &i, &o->target, o->pic_explicit);
   1025       if (dr < 0) return 1;
   1026       if (dr > 0) continue;
   1027     }
   1028     if (driver_streq(a, "-target") || driver_streq(a, "--target")) {
   1029       KitTargetSpec t;
   1030       uint8_t pic;
   1031       if (++i >= argc) {
   1032         driver_errf(o->tool, "%.*s requires an argument",
   1033                     KIT_SLICE_ARG(kit_slice_cstr(a)));
   1034         return 1;
   1035       }
   1036       if (driver_target_from_triple(argv[i], &t) != 0) {
   1037         driver_err_unknown_target(o->tool, argv[i]);
   1038         return 1;
   1039       }
   1040       pic = o->target.pic;
   1041       o->target = t;
   1042       if (o->pic_explicit)
   1043         o->target.pic = pic;
   1044       else
   1045         o->target.pic = driver_default_pic(o->target.obj, o->target.os);
   1046       continue;
   1047     }
   1048     if (driver_strneq(a, "--target=", 9)) {
   1049       KitTargetSpec t;
   1050       uint8_t pic;
   1051       if (driver_target_from_triple(a + 9, &t) != 0) {
   1052         driver_err_unknown_target(o->tool, a + 9);
   1053         return 1;
   1054       }
   1055       pic = o->target.pic;
   1056       o->target = t;
   1057       if (o->pic_explicit)
   1058         o->target.pic = pic;
   1059       else
   1060         o->target.pic = driver_default_pic(o->target.obj, o->target.os);
   1061       continue;
   1062     }
   1063     {
   1064       int tr = driver_target_features_try_consume(&o->target_features, o->env,
   1065                                                   o->tool, argc, argv, &i);
   1066       if (tr < 0) return 1;
   1067       if (tr > 0) continue;
   1068     }
   1069 
   1070     /* Support dir / sysroot. */
   1071     if (driver_streq(a, "-isysroot") || driver_streq(a, "--sysroot")) {
   1072       if (++i >= argc) {
   1073         driver_errf(o->tool, "%.*s requires an argument",
   1074                     KIT_SLICE_ARG(kit_slice_cstr(a)));
   1075         return 1;
   1076       }
   1077       o->sysroot = argv[i];
   1078       continue;
   1079     }
   1080     if (driver_strneq(a, "--sysroot=", 10)) {
   1081       o->sysroot = a + 10;
   1082       continue;
   1083     }
   1084     if (driver_streq(a, "--support-dir")) {
   1085       if (++i >= argc) {
   1086         driver_errf(o->tool, "--support-dir requires an argument");
   1087         return 1;
   1088       }
   1089       o->support_dir = argv[i];
   1090       continue;
   1091     }
   1092     if (driver_strneq(a, "--support-dir=", 14)) {
   1093       o->support_dir = a + 14;
   1094       continue;
   1095     }
   1096 
   1097     if (driver_streq(a, "--")) {
   1098       for (++i; i < argc; ++i)
   1099         if (build_classify_positional(o, argv[i]) != 0) return 1;
   1100       break;
   1101     }
   1102     if (a[0] == '-' && a[1] != '\0') {
   1103       driver_errf(o->tool, "unknown flag: %.*s",
   1104                   KIT_SLICE_ARG(kit_slice_cstr(a)));
   1105       return 1;
   1106     }
   1107     if (build_classify_positional(o, a) != 0) return 1;
   1108   }
   1109   return 0;
   1110 }
   1111 
   1112 /* ===================================================================== */
   1113 /* hosted libc / runtime orchestration (build-exe; shared build-lib)      */
   1114 /* ===================================================================== */
   1115 
   1116 static int build_is_link_output(const BuildOptions* o) {
   1117   if (o->kind == BUILD_OUT_EXE && o->target.obj == KIT_OBJ_WASM) return 0;
   1118   return o->kind == BUILD_OUT_EXE || (o->kind == BUILD_OUT_LIB && o->dynamic);
   1119 }
   1120 
   1121 /* A sysroot on its own engages the hosted compiler profile (matching
   1122  * clang/gcc). -ffreestanding / -nostdinc opt out of hosted headers; the
   1123  * -nostdlib family only suppresses link additions. */
   1124 static void build_enable_hosted_for_sysroot(BuildOptions* o) {
   1125   if (o->wants_hosted_libc) return;
   1126   if (o->shared && !driver_target_shared_uses_hosted(o->target)) return;
   1127   if (!o->sysroot || !o->sysroot[0]) return;
   1128   if (o->freestanding || o->nostdinc) return;
   1129   o->wants_hosted_libc = 1;
   1130 }
   1131 
   1132 static void build_apply_default_hosted_profile(BuildOptions* o) {
   1133   KitTargetSpec host;
   1134   int native_macos;
   1135   if (!driver_target_default_hosted_profile(o->target)) return;
   1136   if (o->wants_hosted_libc) return;
   1137   if (o->freestanding || o->nostdinc) return;
   1138   host = driver_host_target();
   1139   native_macos = host.os == KIT_OS_MACOS && o->target.os == KIT_OS_MACOS;
   1140   if ((!o->sysroot || !o->sysroot[0]) && !native_macos) return;
   1141   o->wants_hosted_libc = 1;
   1142 }
   1143 
   1144 static int build_apply_hosted_profile(BuildOptions* o) {
   1145   int link_action;
   1146   int shared_hosted = o->shared && driver_target_shared_uses_hosted(o->target);
   1147   if (!o->wants_hosted_libc || (o->shared && !shared_hosted)) return 0;
   1148   link_action = build_is_link_output(o) && !o->no_stdlib && !o->no_defaultlibs;
   1149   /* Hosted defines/includes apply to every build-* compile. Only an executable
   1150    * or dynamic-library output consumes CRT/libc link inputs. */
   1151   return driver_link_inputs_apply_hosted(
   1152       &o->inputs, &o->hosted, &o->groups[0].cf, &o->link, o->target, o->sysroot,
   1153       o->static_link, o->shared, o->no_startfiles, link_action);
   1154 }
   1155 
   1156 /* ===================================================================== */
   1157 /* merged preprocessor view per group                                     */
   1158 /* ===================================================================== */
   1159 
   1160 static int build_group_build_pp(BuildOptions* o, uint32_t gi) {
   1161   BuildGroup* g = &o->groups[gi];
   1162   BuildGroup* gl = &o->groups[0];
   1163   uint32_t k;
   1164   if (gi == 0) {
   1165     driver_cflags_fill_pp(&gl->cf, &g->pp);
   1166     return 0;
   1167   }
   1168   /* include = group then global (group searched first). */
   1169   g->m_ninc = g->cf.ninclude_dirs + gl->cf.ninclude_dirs;
   1170   g->m_nsys = g->cf.nsystem_include_dirs + gl->cf.nsystem_include_dirs;
   1171   g->m_def_cap = gl->cf.ndefines + g->cf.ndefines;
   1172   g->m_nund = gl->cf.nundefines + g->cf.nundefines;
   1173   if (g->m_ninc)
   1174     g->m_inc = driver_alloc_zeroed(o->env, g->m_ninc * sizeof(*g->m_inc));
   1175   if (g->m_nsys)
   1176     g->m_sys = driver_alloc_zeroed(o->env, g->m_nsys * sizeof(*g->m_sys));
   1177   if (g->m_def_cap)
   1178     g->m_def = driver_alloc_zeroed(o->env, g->m_def_cap * sizeof(*g->m_def));
   1179   if (g->m_nund)
   1180     g->m_und = driver_alloc_zeroed(o->env, g->m_nund * sizeof(*g->m_und));
   1181   if ((g->m_ninc && !g->m_inc) || (g->m_nsys && !g->m_sys) ||
   1182       (g->m_def_cap && !g->m_def) || (g->m_nund && !g->m_und)) {
   1183     driver_errf(o->tool, "out of memory");
   1184     return 1;
   1185   }
   1186   {
   1187     uint32_t p = 0, j;
   1188     for (k = 0; k < g->cf.ninclude_dirs; ++k)
   1189       g->m_inc[p++] = g->cf.include_dirs[k];
   1190     for (k = 0; k < gl->cf.ninclude_dirs; ++k)
   1191       g->m_inc[p++] = gl->cf.include_dirs[k];
   1192     p = 0;
   1193     for (k = 0; k < g->cf.nsystem_include_dirs; ++k)
   1194       g->m_sys[p++] = g->cf.system_include_dirs[k];
   1195     for (k = 0; k < gl->cf.nsystem_include_dirs; ++k)
   1196       g->m_sys[p++] = gl->cf.system_include_dirs[k];
   1197     /* defines: each global define survives only if the group does not redefine
   1198      * the same name; the group's defines then follow. This realizes "a group
   1199      * -D of an already-defined name overrides it for that group" without
   1200      * emitting a duplicate (which the preprocessor rejects as a conflicting
   1201      * redefinition). */
   1202     p = 0;
   1203     for (k = 0; k < gl->cf.ndefines; ++k) {
   1204       int shadowed = 0;
   1205       for (j = 0; j < g->cf.ndefines; ++j) {
   1206         if (kit_slice_eq(gl->cf.defines[k].name, g->cf.defines[j].name)) {
   1207           shadowed = 1;
   1208           break;
   1209         }
   1210       }
   1211       if (!shadowed) g->m_def[p++] = gl->cf.defines[k];
   1212     }
   1213     for (k = 0; k < g->cf.ndefines; ++k) g->m_def[p++] = g->cf.defines[k];
   1214     g->m_ndef = p;
   1215     p = 0;
   1216     for (k = 0; k < gl->cf.nundefines; ++k) g->m_und[p++] = gl->cf.undefines[k];
   1217     for (k = 0; k < g->cf.nundefines; ++k) g->m_und[p++] = g->cf.undefines[k];
   1218   }
   1219   {
   1220     KitPreprocessOptions z = {0};
   1221     g->pp = z;
   1222     g->pp.include_dirs = g->m_inc;
   1223     g->pp.ninclude_dirs = g->m_ninc;
   1224     g->pp.system_include_dirs = g->m_sys;
   1225     g->pp.nsystem_include_dirs = g->m_nsys;
   1226     g->pp.defines = g->m_def;
   1227     g->pp.ndefines = g->m_ndef;
   1228     g->pp.undefines = g->m_und;
   1229     g->pp.nundefines = g->m_nund;
   1230   }
   1231   return 0;
   1232 }
   1233 
   1234 /* ===================================================================== */
   1235 /* compile one source                                                     */
   1236 /* ===================================================================== */
   1237 
   1238 static KitLanguage build_resolve_lang(BuildOptions* o, KitCompiler* compiler,
   1239                                       uint32_t si) {
   1240   uint32_t gi = o->sources[si].group;
   1241   if (gi != 0 && o->groups[gi].forced_lang != BUILD_LANG_AUTO)
   1242     return o->groups[gi].forced_lang;
   1243   if (o->groups[0].forced_lang != BUILD_LANG_AUTO)
   1244     return o->groups[0].forced_lang;
   1245   return kit_language_for_path(compiler, o->sources[si].path);
   1246 }
   1247 
   1248 /* Collect the -X<lang> frontend tokens for a source into a fresh argv:
   1249  * global (groups[0]) tokens first, then the source's group's. */
   1250 static int build_collect_fe_argv(BuildOptions* o, uint32_t si, KitLanguage lang,
   1251                                  char*** out_argv, uint32_t* out_n) {
   1252   uint32_t gi = o->sources[si].group;
   1253   uint32_t n = 0, k, p = 0;
   1254   char** argv;
   1255   for (k = 0; k < o->groups[0].nfe; ++k)
   1256     if (o->groups[0].fe[k].lang == lang) n++;
   1257   if (gi != 0)
   1258     for (k = 0; k < o->groups[gi].nfe; ++k)
   1259       if (o->groups[gi].fe[k].lang == lang) n++;
   1260   *out_argv = NULL;
   1261   *out_n = 0;
   1262   if (n == 0) return 0;
   1263   argv = driver_alloc_zeroed(o->env, n * sizeof(*argv));
   1264   if (!argv) {
   1265     driver_errf(o->tool, "out of memory");
   1266     return 1;
   1267   }
   1268   for (k = 0; k < o->groups[0].nfe; ++k)
   1269     if (o->groups[0].fe[k].lang == lang)
   1270       argv[p++] = o->groups[0].fe[k].tok;
   1271   if (gi != 0)
   1272     for (k = 0; k < o->groups[gi].nfe; ++k)
   1273       if (o->groups[gi].fe[k].lang == lang)
   1274         argv[p++] = o->groups[gi].fe[k].tok;
   1275   *out_argv = argv;
   1276   *out_n = n;
   1277   return 0;
   1278 }
   1279 
   1280 /* Compile source[si]; emit to `emit_out` (per-source output) or return a
   1281  * builder via `obj_out` (link/archive). Exactly one of the two is non-NULL. */
   1282 static int build_compile_source(BuildOptions* o, KitCompiler* compiler,
   1283                                 const KitContext* ctx, uint32_t si,
   1284                                 const KitCodeOptions* code,
   1285                                 const KitDiagnosticOptions* diag,
   1286                                 KitWriter* emit_out, KitObjBuilder** obj_out) {
   1287   const char* path = o->sources[si].path;
   1288   uint32_t gi = o->sources[si].group;
   1289   DriverLoad load = {0};
   1290   KitSlice bytes = {0};
   1291   KitLanguage lang;
   1292   char** fe_argv = NULL;
   1293   uint32_t fe_n = 0;
   1294   void* lang_extra = NULL;
   1295   KitStatus st;
   1296   int rc = 1;
   1297 
   1298   if (driver_load_bytes(ctx->file_io, o->tool, path, &load, &bytes) != 0)
   1299     return 1;
   1300 
   1301   lang = build_resolve_lang(o, compiler, si);
   1302   if (lang == KIT_LANG_UNKNOWN) {
   1303     driver_errf(o->tool, "cannot determine language for %.*s (use -x LANG)",
   1304                 KIT_SLICE_ARG(kit_slice_cstr(path)));
   1305     goto out;
   1306   }
   1307   if (build_collect_fe_argv(o, si, lang, &fe_argv, &fe_n) != 0) goto out;
   1308   if (fe_n) {
   1309     if (kit_frontend_parse_options(compiler, lang, (int)fe_n, fe_argv,
   1310                                    &lang_extra) != KIT_OK) {
   1311       driver_errf(
   1312           o->tool, "unsupported -X%.*s frontend flag: %.*s",
   1313           KIT_SLICE_ARG(kit_slice_cstr(kit_language_name(compiler, lang))),
   1314           KIT_SLICE_ARG(kit_slice_cstr(fe_argv[0])));
   1315       goto out;
   1316     }
   1317   }
   1318 
   1319   st = kit_build_compile_one(compiler, lang, code, diag, &o->groups[gi].pp,
   1320                              lang_extra, kit_slice_cstr(path), &bytes, emit_out,
   1321                              obj_out);
   1322   rc = (st == KIT_OK) ? 0 : 1;
   1323 
   1324 out:
   1325   if (lang_extra) kit_frontend_free_options(compiler, lang, lang_extra);
   1326   if (fe_argv) driver_free(o->env, fe_argv, fe_n * sizeof(*fe_argv));
   1327   driver_release_bytes(ctx->file_io, &load);
   1328   return rc;
   1329 }
   1330 
   1331 static void build_fill_code(const BuildOptions* o, KitCodeOptions* code) {
   1332   KitCodeOptions z = {0};
   1333   *code = z;
   1334   code->opt_level = o->syntax_only ? 0 : o->opt_level;
   1335   code->debug_info = o->debug_info ? true : false;
   1336   code->check_only = o->syntax_only ? true : false;
   1337   code->default_visibility = o->default_visibility;
   1338   code->function_sections = o->function_sections ? true : false;
   1339   code->data_sections = o->data_sections ? true : false;
   1340   code->disabled_backend_features = o->disabled_backend_features;
   1341   code->trivial_auto_var_init = (uint8_t)o->auto_var_init;
   1342   code->stack_protector = (uint8_t)o->stack_protector;
   1343   code->lto = o->lto ? true : false;
   1344   code->epoch = o->epoch;
   1345 }
   1346 
   1347 /* ===================================================================== */
   1348 /* default output naming                                                  */
   1349 /* ===================================================================== */
   1350 
   1351 static char* build_default_obj_name(DriverEnv* env, const BuildOptions* o,
   1352                                     const char* src, size_t* out_size) {
   1353   const char* ext;
   1354   size_t ext_len, srclen = driver_strlen(src), dot = driver_strlen(src),
   1355                   slash = 0, k;
   1356   switch ((BuildEmit)o->emit) {
   1357     case BUILD_EMIT_ASM:
   1358       ext = ".s";
   1359       ext_len = 2u;
   1360       break;
   1361     case BUILD_EMIT_IR:
   1362       ext = ".ir";
   1363       ext_len = 3u;
   1364       break;
   1365     case BUILD_EMIT_C:
   1366       ext = ".c";
   1367       ext_len = 2u;
   1368       break;
   1369     default:
   1370       driver_default_obj_ext(o->target, &ext, &ext_len);
   1371       break;
   1372   }
   1373   for (k = srclen; k > 0; --k) {
   1374     if (src[k - 1] == '.') {
   1375       dot = k - 1;
   1376       break;
   1377     }
   1378     if (src[k - 1] == '/') break;
   1379   }
   1380   for (k = dot; k > 0; --k) {
   1381     if (src[k - 1] == '/') {
   1382       slash = k;
   1383       break;
   1384     }
   1385   }
   1386   {
   1387     size_t name_len = dot - slash;
   1388     size_t bufsz = name_len + ext_len + 1u;
   1389     char* buf = driver_alloc(env, bufsz);
   1390     if (!buf) return NULL;
   1391     driver_memcpy(buf, src + slash, name_len);
   1392     driver_memcpy(buf + name_len, ext, ext_len);
   1393     buf[name_len + ext_len] = '\0';
   1394     *out_size = bufsz;
   1395     return buf;
   1396   }
   1397 }
   1398 
   1399 /* Open the output: a file, or stdout for "-". */
   1400 static int build_open_output(const KitContext* ctx, DriverEnv* env,
   1401                              const char* tool, const char* path,
   1402                              KitWriter** out) {
   1403   if (driver_streq(path, "-")) {
   1404     *out = driver_stdout_writer(env);
   1405     if (!*out) {
   1406       driver_errf(tool, "out of memory");
   1407       return 1;
   1408     }
   1409     return 0;
   1410   }
   1411   if (ctx->file_io->open_writer(ctx->file_io->user, path, out) != KIT_OK) {
   1412     driver_errf(tool, "failed to open output: %.*s",
   1413                 KIT_SLICE_ARG(kit_slice_cstr(path)));
   1414     return 1;
   1415   }
   1416   return 0;
   1417 }
   1418 
   1419 typedef struct BuildSourceBatch {
   1420   DriverLoad* loads;
   1421   KitBuildSource* sources;
   1422   void** lang_extras;
   1423 } BuildSourceBatch;
   1424 
   1425 static void build_source_batch_fini(BuildOptions* o, KitCompiler* compiler,
   1426                                     const KitContext* ctx,
   1427                                     BuildSourceBatch* batch) {
   1428   uint32_t i;
   1429   if (batch->lang_extras && batch->sources) {
   1430     for (i = 0; i < o->nsources; ++i)
   1431       if (batch->lang_extras[i])
   1432         kit_frontend_free_options(compiler, batch->sources[i].lang,
   1433                                   batch->lang_extras[i]);
   1434   }
   1435   if (batch->loads)
   1436     for (i = 0; i < o->nsources; ++i)
   1437       driver_release_bytes(ctx->file_io, &batch->loads[i]);
   1438   if (batch->lang_extras)
   1439     driver_free(o->env, batch->lang_extras,
   1440                 o->nsources * sizeof(*batch->lang_extras));
   1441   if (batch->sources)
   1442     driver_free(o->env, batch->sources,
   1443                 o->nsources * sizeof(*batch->sources));
   1444   if (batch->loads)
   1445     driver_free(o->env, batch->loads, o->nsources * sizeof(*batch->loads));
   1446   memset(batch, 0, sizeof *batch);
   1447 }
   1448 
   1449 /* Load and resolve every source while retaining its independent preprocessing
   1450  * and frontend options. Both object batching and single-TU C emission consume
   1451  * this shape. */
   1452 static int build_source_batch_init(BuildOptions* o, KitCompiler* compiler,
   1453                                    const KitContext* ctx,
   1454                                    BuildSourceBatch* batch) {
   1455   uint32_t i;
   1456 
   1457   memset(batch, 0, sizeof *batch);
   1458   if (o->nsources == 0) return 0;
   1459 
   1460   batch->loads =
   1461       driver_alloc_zeroed(o->env, o->nsources * sizeof(*batch->loads));
   1462   batch->sources =
   1463       driver_alloc_zeroed(o->env, o->nsources * sizeof(*batch->sources));
   1464   batch->lang_extras = driver_alloc_zeroed(
   1465       o->env, o->nsources * sizeof(*batch->lang_extras));
   1466   if (!batch->loads || !batch->sources || !batch->lang_extras) {
   1467     driver_errf(o->tool, "out of memory");
   1468     build_source_batch_fini(o, compiler, ctx, batch);
   1469     return 1;
   1470   }
   1471 
   1472   for (i = 0; i < o->nsources; ++i) {
   1473     const char* path = o->sources[i].path;
   1474     uint32_t gi = o->sources[i].group;
   1475     KitLanguage lang;
   1476     char** fe_argv = NULL;
   1477     uint32_t fe_n = 0;
   1478 
   1479     if (driver_load_bytes(ctx->file_io, o->tool, path, &batch->loads[i],
   1480                           &batch->sources[i].bytes) != 0) {
   1481       build_source_batch_fini(o, compiler, ctx, batch);
   1482       return 1;
   1483     }
   1484     lang = build_resolve_lang(o, compiler, i);
   1485     if (lang == KIT_LANG_UNKNOWN) {
   1486       driver_errf(o->tool, "cannot determine language for %.*s (use -x LANG)",
   1487                   KIT_SLICE_ARG(kit_slice_cstr(path)));
   1488       build_source_batch_fini(o, compiler, ctx, batch);
   1489       return 1;
   1490     }
   1491     batch->sources[i].lang = lang;
   1492     if (build_collect_fe_argv(o, i, lang, &fe_argv, &fe_n) != 0) {
   1493       if (fe_argv) driver_free(o->env, fe_argv, fe_n * sizeof(*fe_argv));
   1494       build_source_batch_fini(o, compiler, ctx, batch);
   1495       return 1;
   1496     }
   1497     if (fe_n) {
   1498       if (kit_frontend_parse_options(compiler, lang, (int)fe_n, fe_argv,
   1499                                      &batch->lang_extras[i]) != KIT_OK) {
   1500         driver_errf(
   1501             o->tool, "unsupported -X%.*s frontend flag: %.*s",
   1502             KIT_SLICE_ARG(kit_slice_cstr(kit_language_name(compiler, lang))),
   1503             KIT_SLICE_ARG(kit_slice_cstr(fe_argv[0])));
   1504         if (fe_argv) driver_free(o->env, fe_argv, fe_n * sizeof(*fe_argv));
   1505         build_source_batch_fini(o, compiler, ctx, batch);
   1506         return 1;
   1507       }
   1508     }
   1509     if (fe_argv) driver_free(o->env, fe_argv, fe_n * sizeof(*fe_argv));
   1510     batch->sources[i].name = kit_slice_cstr(path);
   1511     batch->sources[i].pp = &o->groups[gi].pp;
   1512     batch->sources[i].lang_extra = batch->lang_extras[i];
   1513   }
   1514   return 0;
   1515 }
   1516 
   1517 /* Compile every source to an in-memory builder; objs[] is caller-owned. */
   1518 static int build_compile_all(BuildOptions* o, KitCompiler* compiler,
   1519                              const KitContext* ctx, const KitCodeOptions* code,
   1520                              const KitDiagnosticOptions* diag,
   1521                              KitObjBuilder** objs, uint32_t* source_obj_index,
   1522                              uint8_t* source_order_keep,
   1523                              const KitBuildBatchOptions* batch,
   1524                              KitBuildPendingLto* pending_lto,
   1525                              uint32_t* nobjs_out) {
   1526   BuildSourceBatch sources;
   1527   KitBuildObjects out;
   1528   KitStatus st;
   1529 
   1530   if (nobjs_out) *nobjs_out = 0;
   1531   if (o->nsources == 0) return 0;
   1532   if (build_source_batch_init(o, compiler, ctx, &sources) != 0) return 1;
   1533 
   1534   memset(&out, 0, sizeof out);
   1535   out.objs = objs;
   1536   out.source_obj_index = source_obj_index;
   1537   out.source_order_keep = source_order_keep;
   1538   out.pending_lto = pending_lto;
   1539   st = kit_build_compile(compiler, code, diag, sources.sources, o->nsources,
   1540                          batch, &out);
   1541   if (nobjs_out) *nobjs_out = out.nobjs;
   1542   build_source_batch_fini(o, compiler, ctx, &sources);
   1543   return st == KIT_OK ? 0 : 1;
   1544 }
   1545 
   1546 typedef struct BuildPreservedVec {
   1547   KitHeap* heap;
   1548   KitCgSym* syms;
   1549   uint32_t nsyms;
   1550   uint32_t cap;
   1551   int oom;
   1552 } BuildPreservedVec;
   1553 
   1554 static void build_preserved_vec_add(void* user, KitCgSym sym) {
   1555   BuildPreservedVec* v = (BuildPreservedVec*)user;
   1556   KitCgSym* ns;
   1557   uint32_t ncap;
   1558   if (!v || v->oom) return;
   1559   if (v->nsyms == v->cap) {
   1560     ncap = v->cap ? v->cap * 2u : 32u;
   1561     ns = (KitCgSym*)v->heap->realloc(
   1562         v->heap, v->syms, sizeof(*v->syms) * v->cap,
   1563         sizeof(*v->syms) * ncap, _Alignof(KitCgSym));
   1564     if (!ns) {
   1565       v->oom = 1;
   1566       return;
   1567     }
   1568     v->syms = ns;
   1569     v->cap = ncap;
   1570   }
   1571   v->syms[v->nsyms++] = sym;
   1572 }
   1573 
   1574 static KitStatus build_link_add_inputs(KitLinkSession* link,
   1575                                        const KitLinkInputs* in) {
   1576   KitStatus st = KIT_OK;
   1577   uint32_t i;
   1578   if (!link || !in) return KIT_INVALID;
   1579   for (i = 0; i < in->norder && st == KIT_OK; ++i) {
   1580     const KitLinkInputOrder* ord = &in->order[i];
   1581     switch ((KitLinkInputOrderKind)ord->kind) {
   1582       case KIT_LINK_INPUT_OBJ:
   1583         st = kit_link_session_add_obj(link, in->objs[ord->index]);
   1584         break;
   1585       case KIT_LINK_INPUT_OBJ_BYTES:
   1586         st = kit_link_session_add_obj_bytes(link, in->obj_names[ord->index],
   1587                                             &in->obj_bytes[ord->index]);
   1588         break;
   1589       case KIT_LINK_INPUT_ARCHIVE:
   1590         st =
   1591             kit_link_session_add_archive_bytes(link, &in->archives[ord->index]);
   1592         break;
   1593       case KIT_LINK_INPUT_DSO:
   1594         st = kit_link_session_add_dso_bytes(link, in->dso_names[ord->index],
   1595                                             &in->dso_bytes[ord->index]);
   1596         break;
   1597     }
   1598   }
   1599   return st;
   1600 }
   1601 
   1602 static int build_write_link_report(BuildOptions* o, const KitContext* ctx,
   1603                                    KitLinkSession* link, const char* path,
   1604                                    int symbols) {
   1605   KitWriter* w = NULL;
   1606   KitStatus st;
   1607   if (!path) return 0;
   1608   if (build_open_output(ctx, o->env, o->tool, path, &w) != 0) return 1;
   1609   st = symbols ? kit_link_session_write_symbols(link, o->link.symbols_format, w)
   1610                : kit_link_session_write_map(link, w);
   1611   if (st == KIT_OK) st = kit_writer_status(w);
   1612   kit_writer_close(w);
   1613   if (st != KIT_OK) {
   1614     driver_errf(o->tool, "failed to write %s: %.*s",
   1615                 symbols ? "symbols" : "map",
   1616                 KIT_SLICE_ARG(kit_slice_cstr(path)));
   1617     return 1;
   1618   }
   1619   return 0;
   1620 }
   1621 
   1622 static KitStatus build_link_with_lto_reports(
   1623     BuildOptions* o, KitCompiler* compiler, const KitContext* ctx,
   1624     const KitLinkSessionOptions* lopts, const KitLinkInputs* in,
   1625     KitBuildPendingLto* pending_lto, const KitBuildBatchOptions* batch,
   1626     KitWriter* out) {
   1627   KitLinkSession* link = NULL;
   1628   BuildPreservedVec preserved;
   1629   KitStatus st;
   1630   if (!compiler || !lopts || !in || !out) {
   1631     if (pending_lto && pending_lto->active) kit_build_lto_abort(pending_lto);
   1632     return KIT_INVALID;
   1633   }
   1634   memset(&preserved, 0, sizeof preserved);
   1635   preserved.heap = o->env->heap;
   1636   st = kit_link_session_new(compiler, lopts, &link);
   1637   if (st == KIT_OK) st = build_link_add_inputs(link, in);
   1638   if (st == KIT_OK && pending_lto && pending_lto->active) {
   1639     st = kit_link_session_visit_lto_preserved(
   1640         link, pending_lto->obj, pending_lto->cg, build_preserved_vec_add,
   1641         &preserved);
   1642     if (st == KIT_OK && preserved.oom) st = KIT_NOMEM;
   1643     if (st == KIT_OK)
   1644       st = kit_build_lto_finish(pending_lto, batch, preserved.syms,
   1645                                 preserved.nsyms);
   1646   }
   1647   if (st == KIT_OK) st = kit_link_session_emit(link, out);
   1648   if (st == KIT_OK &&
   1649       build_write_link_report(o, ctx, link, o->link.map_path, 0) != 0)
   1650     st = KIT_ERR;
   1651   if (st == KIT_OK &&
   1652       build_write_link_report(o, ctx, link, o->link.symbols_path, 1) != 0)
   1653     st = KIT_ERR;
   1654   /* --cref FILE: cross-reference table side file. */
   1655   if (st == KIT_OK && o->link.cref_path) {
   1656     KitWriter* w = NULL;
   1657     if (build_open_output(ctx, o->env, o->tool, o->link.cref_path, &w) != 0) {
   1658       st = KIT_ERR;
   1659     } else {
   1660       KitStatus cst = kit_link_session_write_cref(link, w);
   1661       if (cst == KIT_OK) cst = kit_writer_status(w);
   1662       kit_writer_close(w);
   1663       if (cst != KIT_OK) {
   1664         driver_errf(o->tool, "failed to write cref: %.*s",
   1665                     KIT_SLICE_ARG(kit_slice_cstr(o->link.cref_path)));
   1666         st = KIT_ERR;
   1667       }
   1668     }
   1669   }
   1670   /* --print-memory-usage: GNU-ld-style per-region summary to stdout. */
   1671   if (st == KIT_OK && o->link.print_memory_usage) {
   1672     KitWriter* w = NULL;
   1673     if (build_open_output(ctx, o->env, o->tool, "-", &w) != 0) {
   1674       st = KIT_ERR;
   1675     } else {
   1676       KitStatus mst = kit_link_session_write_memory_usage(link, w);
   1677       if (mst == KIT_OK) mst = kit_writer_status(w);
   1678       kit_writer_close(w);
   1679       if (mst != KIT_OK) {
   1680         driver_errf(o->tool, "failed to print memory usage");
   1681         st = KIT_ERR;
   1682       }
   1683     }
   1684   }
   1685   kit_link_session_free(link);
   1686   if (preserved.syms)
   1687     preserved.heap->free(preserved.heap, preserved.syms,
   1688                          sizeof(*preserved.syms) * preserved.cap);
   1689   if (st != KIT_OK && pending_lto && pending_lto->active)
   1690     kit_build_lto_abort(pending_lto);
   1691   return st;
   1692 }
   1693 
   1694 /* build-exe / shared build-lib: compile sources, load link inputs, link. */
   1695 static int build_run_link(BuildOptions* o, KitCompiler* compiler,
   1696                           const KitContext* ctx, const KitCodeOptions* code,
   1697                           const KitDiagnosticOptions* diag,
   1698                           uint8_t output_kind) {
   1699   DriverEnv* env = o->env;
   1700   const KitFileIO* io = ctx->file_io;
   1701   KitWriter* out_w = NULL;
   1702   DriverLoad* obj_lf = NULL;
   1703   DriverLoad* arch_lf = NULL;
   1704   DriverLoad* dso_lf = NULL;
   1705   DriverLoad script_lf = {0};
   1706   KitSlice* obj_in = NULL;
   1707   KitSlice* obj_names = NULL;
   1708   KitLinkArchiveInput* arch_in = NULL;
   1709   KitSlice* dso_in = NULL;
   1710   KitSlice* dso_names = NULL;
   1711   KitLinkInputOrder* order = NULL;
   1712   KitObjBuilder** objs = NULL;
   1713   KitBuildPendingLto pending_lto = {0};
   1714   uint32_t* source_obj_index = NULL;
   1715   uint8_t* source_order_keep = NULL;
   1716   KitLinkScript* script = NULL;
   1717   KitSlice* rpath_slices = NULL;
   1718   KitBuildBatchOptions lto_batch;
   1719   uint32_t nobjs = 0;
   1720   uint32_t i;
   1721   uint32_t norder = 0;
   1722   int rc = 1;
   1723   /* Strict-by-default freestanding policy: a `*-none-*` target, or an explicit
   1724    * -ffreestanding static non-PIE executable, must reject dynamic-link
   1725    * artifacts and cross-input target/format mismatches. Computed once here (the
   1726    * pie value mirrors what driver_link_flags_fill_options derives for lopts.pie)
   1727    * so the per-input arch/format guard below and lopts.freestanding_strict
   1728    * agree on the trigger — otherwise the hosted -ffreestanding path would set
   1729    * strict yet skip the guard. The relocatable partial-link lane never imposes
   1730    * it. */
   1731   int freestanding_strict =
   1732       output_kind == KIT_LINK_OUTPUT_EXE &&
   1733       (o->target.os == KIT_OS_FREESTANDING ||
   1734        (o->freestanding && o->static_link &&
   1735         !driver_link_pie(o->target, o->pie, o->shared, 0)));
   1736 
   1737   if (o->nsources) {
   1738     objs = driver_alloc_zeroed(env, o->nsources * sizeof(*objs));
   1739     source_obj_index =
   1740         driver_alloc_zeroed(env, o->nsources * sizeof(*source_obj_index));
   1741     source_order_keep =
   1742         driver_alloc_zeroed(env, o->nsources * sizeof(*source_order_keep));
   1743     if (!objs || !source_obj_index || !source_order_keep) goto oom;
   1744   }
   1745   if (o->inputs.nobject_files) {
   1746     obj_lf =
   1747         driver_alloc_zeroed(env, o->inputs.nobject_files * sizeof(*obj_lf));
   1748     obj_in =
   1749         driver_alloc_zeroed(env, o->inputs.nobject_files * sizeof(*obj_in));
   1750     obj_names =
   1751         driver_alloc_zeroed(env, o->inputs.nobject_files * sizeof(*obj_names));
   1752     if (!obj_lf || !obj_in || !obj_names) goto oom;
   1753   }
   1754   if (o->inputs.narchives) {
   1755     arch_lf = driver_alloc_zeroed(env, o->inputs.narchives * sizeof(*arch_lf));
   1756     arch_in = driver_alloc_zeroed(env, o->inputs.narchives * sizeof(*arch_in));
   1757     if (!arch_lf || !arch_in) goto oom;
   1758   }
   1759   if (o->inputs.ndsos) {
   1760     dso_lf = driver_alloc_zeroed(env, o->inputs.ndsos * sizeof(*dso_lf));
   1761     dso_in = driver_alloc_zeroed(env, o->inputs.ndsos * sizeof(*dso_in));
   1762     dso_names = driver_alloc_zeroed(env, o->inputs.ndsos * sizeof(*dso_names));
   1763     if (!dso_lf || !dso_in || !dso_names) goto oom;
   1764   }
   1765   if (o->inputs.nlink_items) {
   1766     order = driver_alloc_zeroed(env, o->inputs.nlink_items * sizeof(*order));
   1767     if (!order) goto oom;
   1768   }
   1769 
   1770   for (i = 0; i < o->inputs.nobject_files; ++i) {
   1771     if (driver_load_bytes(io, o->tool, o->inputs.object_files[i], &obj_lf[i],
   1772                           &obj_in[i]) != 0)
   1773       goto out;
   1774     obj_names[i] = kit_slice_cstr(o->inputs.object_files[i]);
   1775   }
   1776   /* Strict freestanding policy: pre-built object inputs must agree with the
   1777    * link target's arch and object format (a freestanding exe link must not
   1778    * silently accept a foreign-arch object). Gated on the same trigger as
   1779    * lopts.freestanding_strict so the hosted -ffreestanding -static -no-pie path
   1780    * runs this guard too (not only the `*-none-*` target). */
   1781   if (freestanding_strict) {
   1782     for (i = 0; i < o->inputs.nobject_files; ++i) {
   1783       KitTargetSpec t;
   1784       if (kit_detect_target(obj_in[i].data, obj_in[i].len, &t) != KIT_OK)
   1785         continue;
   1786       if (t.arch != o->target.arch || t.obj != o->target.obj) {
   1787         driver_errf(o->tool,
   1788                     "freestanding link: input '%s' arch/format does not match "
   1789                     "the link target",
   1790                     o->inputs.object_files[i]);
   1791         goto out;
   1792       }
   1793     }
   1794   }
   1795   for (i = 0; i < o->inputs.narchives; ++i) {
   1796     if (driver_load_bytes(io, o->tool, o->inputs.archives[i].path, &arch_lf[i],
   1797                           &arch_in[i].bytes) != 0)
   1798       goto out;
   1799     arch_in[i].name = kit_slice_cstr(o->inputs.archives[i].path);
   1800     arch_in[i].link_mode = o->inputs.archives[i].link_mode;
   1801     arch_in[i].whole_archive =
   1802         o->inputs.archives[i].whole_archive ? true : false;
   1803     arch_in[i].group_id = o->inputs.archives[i].group_id;
   1804   }
   1805   for (i = 0; i < o->inputs.ndsos; ++i) {
   1806     if (driver_load_bytes(io, o->tool, o->inputs.dsos[i].path, &dso_lf[i],
   1807                           &dso_in[i]) != 0)
   1808       goto out;
   1809     dso_names[i] = kit_slice_cstr(o->inputs.dsos[i].path);
   1810   }
   1811   if (o->link.linker_script) {
   1812     KitSlice dummy;
   1813     if (driver_load_bytes(io, o->tool, o->link.linker_script, &script_lf,
   1814                           &dummy) != 0)
   1815       goto out;
   1816   }
   1817 
   1818   if (script_lf.loaded) {
   1819     KitSlice text = {.s = (const char*)script_lf.fd.data,
   1820                      .len = script_lf.fd.size};
   1821     if (kit_link_script_parse(ctx, text, &script) != KIT_OK) goto out;
   1822   }
   1823 
   1824   {
   1825     memset(&lto_batch, 0, sizeof lto_batch);
   1826     lto_batch.output_kind = output_kind == KIT_LINK_OUTPUT_SHARED
   1827                                 ? KIT_CG_OUTPUT_SHARED
   1828                                 : KIT_CG_OUTPUT_EXECUTABLE;
   1829     lto_batch.interposition_policy =
   1830         output_kind == KIT_LINK_OUTPUT_SHARED
   1831             ? KIT_CG_INTERPOSITION_DEFAULT_VISIBILITY
   1832             : KIT_CG_INTERPOSITION_DEFAULT;
   1833     lto_batch.defer_lto_finish = 1;
   1834     if (build_compile_all(o, compiler, ctx, code, diag, objs, source_obj_index,
   1835                           source_order_keep, &lto_batch, &pending_lto,
   1836                           &nobjs) != 0)
   1837       goto out;
   1838   }
   1839 
   1840   if (build_open_output(ctx, env, o->tool, o->output_path, &out_w) != 0)
   1841     goto out;
   1842 
   1843   /* Translate the recorded link order into KitLinkInputOrder. (build never
   1844    * records SOURCE_MEMORY items, so nsource_files is immaterial here.) */
   1845   norder = driver_link_inputs_build_order(
   1846       &o->inputs, source_obj_index, source_order_keep, o->nsources, order);
   1847 
   1848   {
   1849     KitLinkSessionOptions lopts;
   1850     KitLinkInputs li;
   1851     KitStatus st;
   1852     if (driver_link_flags_fill_options(
   1853             &o->link, o->target, o->pie, o->shared,
   1854             output_kind == KIT_LINK_OUTPUT_RELOCATABLE, output_kind, script,
   1855             &lopts, &rpath_slices) != 0)
   1856       goto out;
   1857     /* Reject dynamic-link artifacts (PT_INTERP, .dynamic/.dynsym, PLT/GOT
   1858      * imports, DSO inputs) for the strict freestanding link. Same trigger as
   1859      * the per-input arch/format guard above (freestanding_strict). */
   1860     if (freestanding_strict) lopts.freestanding_strict = true;
   1861     memset(&li, 0, sizeof(li));
   1862     li.objs = objs;
   1863     li.nobjs = nobjs;
   1864     li.obj_names = obj_names;
   1865     li.obj_bytes = obj_in;
   1866     li.nobj_bytes = o->inputs.nobject_files;
   1867     li.archives = arch_in;
   1868     li.narchives = o->inputs.narchives;
   1869     li.dso_names = dso_names;
   1870     li.dso_bytes = dso_in;
   1871     li.ndsos = o->inputs.ndsos;
   1872     li.order = order;
   1873     li.norder = norder;
   1874     st = build_link_with_lto_reports(o, compiler, ctx, &lopts, &li,
   1875                                      &pending_lto, &lto_batch, out_w);
   1876     rc = (st == KIT_OK) ? 0 : 1;
   1877   }
   1878 
   1879 out:
   1880   if (out_w) kit_writer_close(out_w);
   1881   if (rc == 0 && output_kind == KIT_LINK_OUTPUT_EXE &&
   1882       !driver_streq(o->output_path, "-")) {
   1883     if (driver_mark_executable_output(o->output_path) != 0) {
   1884       driver_errf(o->tool, "failed to set executable mode: %.*s",
   1885                   KIT_SLICE_ARG(kit_slice_cstr(o->output_path)));
   1886       rc = 1;
   1887     }
   1888   }
   1889   if (script) kit_link_script_free(ctx, script);
   1890   kit_build_lto_abort(&pending_lto);
   1891   driver_link_flags_free_rpath_slices(&o->link, rpath_slices);
   1892   driver_release_bytes(io, &script_lf);
   1893   if (arch_lf)
   1894     for (i = 0; i < o->inputs.narchives; ++i)
   1895       driver_release_bytes(io, &arch_lf[i]);
   1896   if (dso_lf)
   1897     for (i = 0; i < o->inputs.ndsos; ++i) driver_release_bytes(io, &dso_lf[i]);
   1898   if (obj_lf)
   1899     for (i = 0; i < o->inputs.nobject_files; ++i)
   1900       driver_release_bytes(io, &obj_lf[i]);
   1901   if (arch_in)
   1902     driver_free(env, arch_in, o->inputs.narchives * sizeof(*arch_in));
   1903   if (arch_lf)
   1904     driver_free(env, arch_lf, o->inputs.narchives * sizeof(*arch_lf));
   1905   if (dso_in) driver_free(env, dso_in, o->inputs.ndsos * sizeof(*dso_in));
   1906   if (dso_names)
   1907     driver_free(env, dso_names, o->inputs.ndsos * sizeof(*dso_names));
   1908   if (dso_lf) driver_free(env, dso_lf, o->inputs.ndsos * sizeof(*dso_lf));
   1909   if (order) driver_free(env, order, o->inputs.nlink_items * sizeof(*order));
   1910   if (obj_in)
   1911     driver_free(env, obj_in, o->inputs.nobject_files * sizeof(*obj_in));
   1912   if (obj_names)
   1913     driver_free(env, obj_names, o->inputs.nobject_files * sizeof(*obj_names));
   1914   if (obj_lf)
   1915     driver_free(env, obj_lf, o->inputs.nobject_files * sizeof(*obj_lf));
   1916   /* The link session borrows the per-source builders (it frees only its own
   1917    * pointer array), so the caller still owns and must release them. */
   1918   if (objs) {
   1919     for (i = 0; i < nobjs; ++i) kit_obj_builder_free(objs[i]);
   1920     driver_free(env, objs, o->nsources * sizeof(*objs));
   1921   }
   1922   if (source_order_keep)
   1923     driver_free(env, source_order_keep,
   1924                 o->nsources * sizeof(*source_order_keep));
   1925   if (source_obj_index)
   1926     driver_free(env, source_obj_index, o->nsources * sizeof(*source_obj_index));
   1927   return rc;
   1928 
   1929 oom:
   1930   driver_errf(o->tool, "out of memory");
   1931   goto out;
   1932 }
   1933 
   1934 /* build-obj multi-source: combine into one relocatable object (ld -r). */
   1935 static int build_run_relocatable(BuildOptions* o, KitCompiler* compiler,
   1936                                  const KitContext* ctx,
   1937                                  const KitCodeOptions* code,
   1938                                  const KitDiagnosticOptions* diag) {
   1939   DriverEnv* env = o->env;
   1940   KitObjBuilder** objs = NULL;
   1941   uint32_t* source_obj_index = NULL;
   1942   uint8_t* source_order_keep = NULL;
   1943   KitLinkInputOrder* order = NULL;
   1944   KitBuildPendingLto pending_lto = {0};
   1945   KitWriter* out_w = NULL;
   1946   KitBuildBatchOptions lto_batch;
   1947   uint32_t nobjs = 0;
   1948   uint32_t norder = 0;
   1949   uint32_t i;
   1950   int rc = 1;
   1951 
   1952   objs = driver_alloc_zeroed(env, o->nsources * sizeof(*objs));
   1953   source_obj_index =
   1954       driver_alloc_zeroed(env, o->nsources * sizeof(*source_obj_index));
   1955   source_order_keep =
   1956       driver_alloc_zeroed(env, o->nsources * sizeof(*source_order_keep));
   1957   order = driver_alloc_zeroed(env, o->nsources * sizeof(*order));
   1958   if (!objs || !source_obj_index || !source_order_keep || !order) {
   1959     driver_errf(o->tool, "out of memory");
   1960     goto out;
   1961   }
   1962   {
   1963     memset(&lto_batch, 0, sizeof lto_batch);
   1964     lto_batch.output_kind = KIT_CG_OUTPUT_RELOCATABLE;
   1965     lto_batch.interposition_policy = KIT_CG_INTERPOSITION_DEFAULT;
   1966     lto_batch.defer_lto_finish = 1;
   1967     if (build_compile_all(o, compiler, ctx, code, diag, objs, source_obj_index,
   1968                           source_order_keep, &lto_batch, &pending_lto,
   1969                           &nobjs) != 0)
   1970       goto out;
   1971   }
   1972   for (i = 0; i < o->nsources; ++i) {
   1973     if (!source_order_keep[i]) continue;
   1974     order[norder].kind = KIT_LINK_INPUT_OBJ;
   1975     order[norder].index = source_obj_index[i];
   1976     ++norder;
   1977   }
   1978   if (build_open_output(ctx, env, o->tool, o->output_path, &out_w) != 0)
   1979     goto out;
   1980   {
   1981     KitLinkSessionOptions lopts;
   1982     KitLinkInputs li;
   1983     KitStatus st;
   1984     memset(&lopts, 0, sizeof(lopts));
   1985     lopts.output_kind = KIT_LINK_OUTPUT_RELOCATABLE;
   1986     lopts.allow_undefined = 1;
   1987     lopts.strip_debug = o->link.strip_debug ? true : false;
   1988     memset(&li, 0, sizeof(li));
   1989     li.objs = objs;
   1990     li.nobjs = nobjs;
   1991     li.order = order;
   1992     li.norder = norder;
   1993     st = kit_build_link_with_lto(compiler, &lopts, &li, &pending_lto,
   1994                                  &lto_batch, out_w);
   1995     rc = (st == KIT_OK) ? 0 : 1;
   1996   }
   1997 
   1998 out:
   1999   if (out_w) kit_writer_close(out_w);
   2000   kit_build_lto_abort(&pending_lto);
   2001   if (order) driver_free(env, order, o->nsources * sizeof(*order));
   2002   /* The link session borrows the builders; release them here (see
   2003    * build_run_link). */
   2004   if (objs) {
   2005     for (i = 0; i < nobjs; ++i) kit_obj_builder_free(objs[i]);
   2006     driver_free(env, objs, o->nsources * sizeof(*objs));
   2007   }
   2008   if (source_order_keep)
   2009     driver_free(env, source_order_keep,
   2010                 o->nsources * sizeof(*source_order_keep));
   2011   if (source_obj_index)
   2012     driver_free(env, source_obj_index, o->nsources * sizeof(*source_obj_index));
   2013   return rc;
   2014 }
   2015 
   2016 /* build-obj wasm multi-source: merge all inputs into one .wasm via CG merge. */
   2017 static int build_run_wasm_module(BuildOptions* o, KitCompiler* compiler,
   2018                                  const KitContext* ctx,
   2019                                  const KitCodeOptions* code,
   2020                                  const KitDiagnosticOptions* diag) {
   2021   DriverEnv* env = o->env;
   2022   KitObjBuilder** objs = NULL;
   2023   uint32_t* source_obj_index = NULL;
   2024   uint8_t* source_order_keep = NULL;
   2025   KitWriter* out_w = NULL;
   2026   KitCodeOptions code2 = *code;
   2027   uint32_t nobjs = 0;
   2028   uint32_t i;
   2029   int rc = 1;
   2030 
   2031   code2.lto = true;
   2032 
   2033   objs = driver_alloc_zeroed(env, o->nsources * sizeof(*objs));
   2034   source_obj_index =
   2035       driver_alloc_zeroed(env, o->nsources * sizeof(*source_obj_index));
   2036   source_order_keep =
   2037       driver_alloc_zeroed(env, o->nsources * sizeof(*source_order_keep));
   2038   if (!objs || !source_obj_index || !source_order_keep) {
   2039     driver_errf(o->tool, "out of memory");
   2040     goto out;
   2041   }
   2042   {
   2043     KitBuildBatchOptions batch;
   2044     memset(&batch, 0, sizeof batch);
   2045     batch.output_kind = KIT_CG_OUTPUT_RELOCATABLE;
   2046     batch.interposition_policy = KIT_CG_INTERPOSITION_DEFAULT;
   2047     if (build_compile_all(o, compiler, ctx, &code2, diag, objs,
   2048                           source_obj_index, source_order_keep, &batch, NULL,
   2049                           &nobjs) != 0)
   2050       goto out;
   2051   }
   2052   if (nobjs != 1) {
   2053     driver_errf(o->tool,
   2054                 "build-obj: wasm multi-input expects a single merged module");
   2055     goto out;
   2056   }
   2057   if (build_open_output(ctx, env, o->tool, o->output_path, &out_w) != 0)
   2058     goto out;
   2059   rc = (kit_obj_builder_emit(objs[0], out_w) == KIT_OK) ? 0 : 1;
   2060 
   2061 out:
   2062   if (out_w) kit_writer_close(out_w);
   2063   if (objs) {
   2064     for (i = 0; i < nobjs; ++i) kit_obj_builder_free(objs[i]);
   2065     driver_free(env, objs, o->nsources * sizeof(*objs));
   2066   }
   2067   if (source_order_keep)
   2068     driver_free(env, source_order_keep,
   2069                 o->nsources * sizeof(*source_order_keep));
   2070   if (source_obj_index)
   2071     driver_free(env, source_obj_index, o->nsources * sizeof(*source_obj_index));
   2072   return rc;
   2073 }
   2074 
   2075 /* build-exe wasm: merge all sources into one .wasm with DCE/internalization. */
   2076 static int build_run_wasm_exe(BuildOptions* o, KitCompiler* compiler,
   2077                               const KitContext* ctx, const KitCodeOptions* code,
   2078                               const KitDiagnosticOptions* diag) {
   2079   DriverEnv* env = o->env;
   2080   KitObjBuilder** objs = NULL;
   2081   uint32_t* source_obj_index = NULL;
   2082   uint8_t* source_order_keep = NULL;
   2083   KitBuildPendingLto pending_lto = {0};
   2084   KitWriter* out_w = NULL;
   2085   KitCodeOptions code2 = *code;
   2086   uint32_t nobjs = 0;
   2087   uint32_t i;
   2088   int rc = 1;
   2089 
   2090   code2.lto = true;
   2091 
   2092   objs = driver_alloc_zeroed(env, o->nsources * sizeof(*objs));
   2093   source_obj_index =
   2094       driver_alloc_zeroed(env, o->nsources * sizeof(*source_obj_index));
   2095   source_order_keep =
   2096       driver_alloc_zeroed(env, o->nsources * sizeof(*source_order_keep));
   2097   if (!objs || !source_obj_index || !source_order_keep) {
   2098     driver_errf(o->tool, "out of memory");
   2099     goto out;
   2100   }
   2101   {
   2102     KitBuildBatchOptions batch;
   2103     memset(&batch, 0, sizeof batch);
   2104     batch.output_kind = KIT_CG_OUTPUT_EXECUTABLE;
   2105     batch.interposition_policy = KIT_CG_INTERPOSITION_NONE;
   2106     batch.defer_lto_finish = 1;
   2107     if (build_compile_all(o, compiler, ctx, &code2, diag, objs,
   2108                           source_obj_index, source_order_keep, &batch,
   2109                           &pending_lto, &nobjs) != 0)
   2110       goto out;
   2111   }
   2112   if (nobjs != 1) {
   2113     driver_errf(o->tool, "build-exe: wasm expects a single merged module");
   2114     goto out;
   2115   }
   2116   {
   2117     const char* entry_name = o->link.entry ? o->link.entry : "main";
   2118     KitSym entry_interned =
   2119         kit_sym_intern(compiler, kit_slice_cstr(entry_name));
   2120     KitObjSymbol entry_sym = KIT_OBJ_SYMBOL_NONE;
   2121     KitBuildBatchOptions batch2;
   2122     KitCgSym csym;
   2123 
   2124     if (!entry_interned ||
   2125         kit_obj_builder_find_symbol(pending_lto.obj, entry_interned,
   2126                                     &entry_sym) != KIT_OK) {
   2127       driver_errf(o->tool, "build-exe: entry symbol '%s' not found in module",
   2128                   entry_name);
   2129       goto out;
   2130     }
   2131     csym = (KitCgSym)entry_sym;
   2132     memset(&batch2, 0, sizeof batch2);
   2133     batch2.output_kind = KIT_CG_OUTPUT_EXECUTABLE;
   2134     batch2.interposition_policy = KIT_CG_INTERPOSITION_NONE;
   2135     if (kit_build_lto_finish(&pending_lto, &batch2, &csym, 1) !=
   2136         KIT_OK) {
   2137       driver_errf(o->tool, "build-exe: LTO finish failed");
   2138       goto out;
   2139     }
   2140     kit_obj_builder_wasm_add_custom(objs[0], KIT_SLICE_LIT("kit-entry"),
   2141                                     kit_slice_cstr(entry_name));
   2142   }
   2143   if (build_open_output(ctx, env, o->tool, o->output_path, &out_w) != 0)
   2144     goto out;
   2145   rc = (kit_obj_builder_emit(objs[0], out_w) == KIT_OK) ? 0 : 1;
   2146 
   2147 out:
   2148   if (out_w) kit_writer_close(out_w);
   2149   if (pending_lto.active) kit_build_lto_abort(&pending_lto);
   2150   if (objs) {
   2151     for (i = 0; i < nobjs; ++i) kit_obj_builder_free(objs[i]);
   2152     driver_free(env, objs, o->nsources * sizeof(*objs));
   2153   }
   2154   if (source_order_keep)
   2155     driver_free(env, source_order_keep,
   2156                 o->nsources * sizeof(*source_order_keep));
   2157   if (source_obj_index)
   2158     driver_free(env, source_obj_index, o->nsources * sizeof(*source_obj_index));
   2159   return rc;
   2160 }
   2161 
   2162 /* build-lib static: archive every compiled source object. */
   2163 static int build_run_archive(BuildOptions* o, KitCompiler* compiler,
   2164                              const KitContext* ctx, const KitCodeOptions* code,
   2165                              const KitDiagnosticOptions* diag) {
   2166   DriverEnv* env = o->env;
   2167   KitObjBuilder** objs = NULL;
   2168   uint32_t* source_obj_index = NULL;
   2169   uint8_t* source_order_keep = NULL;
   2170   KitSlice* names = NULL;
   2171   char** owned_names = NULL;
   2172   size_t* owned_name_sizes = NULL;
   2173   KitWriter* out_w = NULL;
   2174   uint32_t nobjs = 0;
   2175   uint32_t i;
   2176   int rc = 1;
   2177 
   2178   objs = driver_alloc_zeroed(env, o->nsources * sizeof(*objs));
   2179   source_obj_index =
   2180       driver_alloc_zeroed(env, o->nsources * sizeof(*source_obj_index));
   2181   source_order_keep =
   2182       driver_alloc_zeroed(env, o->nsources * sizeof(*source_order_keep));
   2183   names = driver_alloc_zeroed(env, o->nsources * sizeof(*names));
   2184   owned_names = driver_alloc_zeroed(env, o->nsources * sizeof(*owned_names));
   2185   owned_name_sizes =
   2186       driver_alloc_zeroed(env, o->nsources * sizeof(*owned_name_sizes));
   2187   if (!objs || !source_obj_index || !source_order_keep || !names ||
   2188       !owned_names || !owned_name_sizes) {
   2189     driver_errf(o->tool, "out of memory");
   2190     goto out;
   2191   }
   2192   {
   2193     KitBuildBatchOptions batch;
   2194     memset(&batch, 0, sizeof batch);
   2195     batch.output_kind = KIT_CG_OUTPUT_ARCHIVE_MEMBER;
   2196     batch.interposition_policy = KIT_CG_INTERPOSITION_DEFAULT;
   2197     if (build_compile_all(o, compiler, ctx, code, diag, objs, source_obj_index,
   2198                           source_order_keep, &batch, NULL, &nobjs) != 0)
   2199       goto out;
   2200   }
   2201   /* build-lib always emits objects (validated), so build_default_obj_name
   2202    * yields the right `.o`/`.obj` member name. */
   2203   for (i = 0; i < o->nsources; ++i) {
   2204     uint32_t oi;
   2205     if (!source_order_keep[i]) continue;
   2206     oi = source_obj_index[i];
   2207     owned_names[oi] = build_default_obj_name(env, o, o->sources[i].path,
   2208                                              &owned_name_sizes[oi]);
   2209     if (!owned_names[oi]) {
   2210       driver_errf(o->tool, "out of memory");
   2211       goto out;
   2212     }
   2213     names[oi] = kit_slice_cstr(owned_names[oi]);
   2214   }
   2215   if (build_open_output(ctx, env, o->tool, o->output_path, &out_w) != 0)
   2216     goto out;
   2217   rc = driver_archive_emit(env, ctx, o->tool, objs, names, nobjs, o->epoch,
   2218                            out_w);
   2219 
   2220 out:
   2221   if (out_w) kit_writer_close(out_w);
   2222   if (objs)
   2223     for (i = 0; i < nobjs; ++i) kit_obj_builder_free(objs[i]);
   2224   if (owned_names) {
   2225     for (i = 0; i < o->nsources; ++i)
   2226       if (owned_names[i]) driver_free(env, owned_names[i], owned_name_sizes[i]);
   2227   }
   2228   if (objs) driver_free(env, objs, o->nsources * sizeof(*objs));
   2229   if (source_order_keep)
   2230     driver_free(env, source_order_keep,
   2231                 o->nsources * sizeof(*source_order_keep));
   2232   if (source_obj_index)
   2233     driver_free(env, source_obj_index, o->nsources * sizeof(*source_obj_index));
   2234   if (names) driver_free(env, names, o->nsources * sizeof(*names));
   2235   if (owned_names)
   2236     driver_free(env, owned_names, o->nsources * sizeof(*owned_names));
   2237   if (owned_name_sizes)
   2238     driver_free(env, owned_name_sizes, o->nsources * sizeof(*owned_name_sizes));
   2239   return rc;
   2240 }
   2241 
   2242 /* build-lib --emit=c: merge semantic sources into one C translation unit. */
   2243 static int build_run_c_tu(BuildOptions* o, KitCompiler* compiler,
   2244                           const KitContext* ctx, const KitCodeOptions* code,
   2245                           const KitDiagnosticOptions* diag) {
   2246   BuildSourceBatch sources;
   2247   KitBuildBatchOptions batch;
   2248   KitWriter* out_w = NULL;
   2249   uint32_t i;
   2250   int rc = 1;
   2251 
   2252   if (build_source_batch_init(o, compiler, ctx, &sources) != 0) return 1;
   2253   for (i = 0; i < o->nsources; ++i) {
   2254     KitFrontendCaps caps;
   2255     memset(&caps, 0, sizeof caps);
   2256     if (kit_frontend_caps(compiler, sources.sources[i].lang, &caps) != KIT_OK ||
   2257         caps.lto_mode != KIT_FRONTEND_LTO_CG) {
   2258       driver_errf(o->tool,
   2259                   "cannot emit C for %.*s: the %.*s frontend is object-only",
   2260                   KIT_SLICE_ARG(sources.sources[i].name),
   2261                   KIT_SLICE_ARG(kit_slice_cstr(kit_language_name(
   2262                       compiler, sources.sources[i].lang))));
   2263       goto out;
   2264     }
   2265   }
   2266   if (build_open_output(ctx, o->env, o->tool, o->output_path, &out_w) != 0)
   2267     goto out;
   2268 
   2269   memset(&batch, 0, sizeof batch);
   2270   batch.output_kind = KIT_CG_OUTPUT_ARCHIVE_MEMBER;
   2271   batch.interposition_policy = KIT_CG_INTERPOSITION_DEFAULT;
   2272   if (kit_build_emit_c(compiler, code, diag, sources.sources, o->nsources,
   2273                        &batch, out_w) != KIT_OK) {
   2274     driver_errf(o->tool, "failed to emit C translation unit");
   2275     goto out;
   2276   }
   2277   rc = 0;
   2278 
   2279 out:
   2280   if (out_w) kit_writer_close(out_w);
   2281   build_source_batch_fini(o, compiler, ctx, &sources);
   2282   return rc;
   2283 }
   2284 
   2285 /* build-obj per-source: check-only, or one output per source (obj/asm/c/ir). */
   2286 static int build_run_per_source(BuildOptions* o, KitCompiler* compiler,
   2287                                 const KitContext* ctx,
   2288                                 const KitCodeOptions* code_in,
   2289                                 const KitDiagnosticOptions* diag) {
   2290   DriverEnv* env = o->env;
   2291   KitCodeOptions code = *code_in;
   2292   uint32_t i;
   2293 
   2294   code.emit_asm_source = (o->emit == BUILD_EMIT_ASM) ? true : false;
   2295   code.emit_c_source = (o->emit == BUILD_EMIT_C) ? true : false;
   2296   code.emit_ir = (o->emit == BUILD_EMIT_IR) ? true : false;
   2297 
   2298   for (i = 0; i < o->nsources; ++i) {
   2299     if (o->syntax_only) {
   2300       KitObjBuilder* ob = NULL;
   2301       int rc =
   2302           build_compile_source(o, compiler, ctx, i, &code, diag, NULL, &ob);
   2303       kit_obj_builder_free(ob);
   2304       if (rc != 0) return rc;
   2305       continue;
   2306     }
   2307     {
   2308       const char* out_path = o->output_path;
   2309       char* owned = NULL;
   2310       size_t owned_size = 0;
   2311       KitWriter* w = NULL;
   2312       int rc;
   2313       if (!out_path) {
   2314         owned = build_default_obj_name(env, o, o->sources[i].path, &owned_size);
   2315         if (!owned) {
   2316           driver_errf(o->tool, "out of memory");
   2317           return 1;
   2318         }
   2319         out_path = owned;
   2320       }
   2321       if (build_open_output(ctx, env, o->tool, out_path, &w) != 0) {
   2322         if (owned) driver_free(env, owned, owned_size);
   2323         return 1;
   2324       }
   2325       rc = build_compile_source(o, compiler, ctx, i, &code, diag, w, NULL);
   2326       kit_writer_close(w);
   2327       if (owned) driver_free(env, owned, owned_size);
   2328       if (rc != 0) return rc;
   2329     }
   2330   }
   2331   return 0;
   2332 }
   2333 
   2334 /* ===================================================================== */
   2335 /* validation                                                             */
   2336 /* ===================================================================== */
   2337 
   2338 static int build_validate(BuildOptions* o) {
   2339   uint32_t total_link = o->inputs.nobject_files + o->inputs.narchives +
   2340                         o->inputs.ndsos + o->inputs.npending_libs +
   2341                         o->inputs.npending_frameworks;
   2342 
   2343   if (o->nsources == 0 && total_link == 0) {
   2344     driver_errf(o->tool, "no input files");
   2345     return 1;
   2346   }
   2347   if (o->kind != BUILD_OUT_EXE && (o->link.map_path || o->link.symbols_path)) {
   2348     driver_errf(o->tool, "--map/--symbols are only valid for build-exe");
   2349     return 1;
   2350   }
   2351 
   2352   /* -dynamic selects the default executable link mode for build-exe and the
   2353    * not-yet-supported shared-library mode for build-lib. -shared is the GCC
   2354    * spelling for producing a shared library, so build-exe rejects it. */
   2355 
   2356   if (o->kind == BUILD_OUT_EXE) {
   2357     if (o->shared_requested) {
   2358       driver_errf(o->tool, "-shared is not valid for build-exe");
   2359       return 1;
   2360     }
   2361     if (o->emit != BUILD_EMIT_OBJ || o->syntax_only) {
   2362       driver_errf(o->tool, "--emit/-S/-fsyntax-only are build-obj options");
   2363       return 1;
   2364     }
   2365     if (!o->output_path) {
   2366       o->output_path = (o->target.obj == KIT_OBJ_WASM)
   2367                            ? "a.wasm"
   2368                            : driver_default_exe_name(o->target);
   2369     }
   2370     if (o->target.obj == KIT_OBJ_WASM) {
   2371       uint32_t total_link = o->inputs.nobject_files + o->inputs.narchives +
   2372                             o->inputs.ndsos + o->inputs.npending_libs +
   2373                             o->inputs.npending_frameworks;
   2374       if (total_link) {
   2375         driver_errf(o->tool,
   2376                     "build-exe -target wasm32-none accepts only source files "
   2377                     "(no .o / .a / -l inputs)");
   2378         return 1;
   2379       }
   2380       if (o->link.map_path || o->link.symbols_path) {
   2381         driver_errf(o->tool,
   2382                     "--map/--symbols require the native linker path");
   2383         return 1;
   2384       }
   2385     }
   2386     return 0;
   2387   }
   2388 
   2389   if (o->kind == BUILD_OUT_LIB) {
   2390     if ((o->emit != BUILD_EMIT_OBJ && o->emit != BUILD_EMIT_C) ||
   2391         o->syntax_only) {
   2392       driver_errf(o->tool,
   2393                   "build-lib supports object output or --emit=c; "
   2394                   "-S/--emit=ir/-fsyntax-only are build-obj options");
   2395       return 1;
   2396     }
   2397     if (o->emit == BUILD_EMIT_C && o->dynamic) {
   2398       driver_errf(o->tool, "--emit=c is incompatible with -dynamic/-shared");
   2399       return 1;
   2400     }
   2401     if (total_link != 0) {
   2402       driver_errf(o->tool,
   2403                   "build-lib takes only sources; pass .o/.a/-l to build-exe");
   2404       return 1;
   2405     }
   2406     if (o->nsources == 0) {
   2407       driver_errf(o->tool, "no source files");
   2408       return 1;
   2409     }
   2410     if (!o->output_path) {
   2411       driver_errf(o->tool, "-o is required (no default library name)");
   2412       return 1;
   2413     }
   2414     return 0;
   2415   }
   2416 
   2417   /* build-obj */
   2418   if (total_link != 0) {
   2419     driver_errf(o->tool,
   2420                 "build-obj takes only sources; pass .o/.a/-l to build-exe");
   2421     return 1;
   2422   }
   2423   if (o->nsources == 0) {
   2424     driver_errf(o->tool, "no source files");
   2425     return 1;
   2426   }
   2427   if (o->dynamic) {
   2428     driver_errf(o->tool, "-dynamic/-shared is only valid for build-lib");
   2429     return 1;
   2430   }
   2431   if (o->syntax_only) {
   2432     if (o->output_path) {
   2433       driver_errf(o->tool, "-o is incompatible with -fsyntax-only");
   2434       return 1;
   2435     }
   2436     return 0;
   2437   }
   2438   if (o->emit == BUILD_EMIT_IR && o->opt_level < 1) {
   2439     driver_errf(o->tool,
   2440                 "--emit=ir requires -O1 or higher (the IR tape is only "
   2441                 "recorded when the optimizer runs)");
   2442     return 1;
   2443   }
   2444   if (o->emit == BUILD_EMIT_OBJ && o->nsources > 1) {
   2445     /* relocatable combine */
   2446     if (!o->output_path) {
   2447       driver_errf(o->tool,
   2448                   "-o is required to combine multiple sources into one object");
   2449       return 1;
   2450     }
   2451     return 0;
   2452   }
   2453   /* per-source emit (single obj, or asm/c/ir) */
   2454   if (o->output_path && o->nsources > 1) {
   2455     driver_errf(o->tool, "-o cannot be used with multiple sources");
   2456     return 1;
   2457   }
   2458   if (o->emit == BUILD_EMIT_C && !o->output_path) {
   2459     driver_errf(o->tool, "--emit=c requires -o");
   2460     return 1;
   2461   }
   2462   return 0;
   2463 }
   2464 
   2465 /* ===================================================================== */
   2466 /* driver                                                                 */
   2467 /* ===================================================================== */
   2468 
   2469 static int build_apply_env(BuildOptions* o) {
   2470   const char* sde = driver_getenv("SOURCE_DATE_EPOCH");
   2471   if (sde && driver_parse_u64(sde, &o->epoch) != 0) {
   2472     driver_errf(o->tool, "invalid SOURCE_DATE_EPOCH: %.*s",
   2473                 KIT_SLICE_ARG(kit_slice_cstr(sde)));
   2474     return 1;
   2475   }
   2476   return 0;
   2477 }
   2478 
   2479 static int build_record_framework(BuildOptions* o, const char* name) {
   2480   DriverPendingFramework* pf;
   2481   if (!name || !name[0]) {
   2482     driver_errf(o->tool, "-framework requires an argument");
   2483     return 1;
   2484   }
   2485   pf = &o->inputs.pending_frameworks[o->inputs.npending_frameworks++];
   2486   pf->name = name;
   2487   driver_link_inputs_push(&o->inputs, DRIVER_LINK_FRAMEWORK,
   2488                           o->inputs.npending_frameworks - 1u);
   2489   return 0;
   2490 }
   2491 
   2492 static int build_main(int argc, char** argv, int kind, const char* tool,
   2493                       const KitDriverExtension* ext) {
   2494   DriverEnv env;
   2495   BuildOptions o = {0};
   2496   DriverRuntimeSupport runtime = {0};
   2497   int runtime_resolved = 0;
   2498   KitContext ctx;
   2499   KitTarget* target = NULL;
   2500   KitCompiler* compiler = NULL;
   2501   KitCodeOptions code;
   2502   KitDiagnosticOptions diag = {0};
   2503   uint32_t gi;
   2504   int rc = 2;
   2505 
   2506   driver_env_init(&env);
   2507   ctx = driver_env_to_context(&env);
   2508   o.env = &env;
   2509   o.tool = tool;
   2510   o.kind = kind;
   2511   o.driver_path = argv[0];
   2512 
   2513   if (kit_frontend_registry_new(&ctx, &o.frontends) != KIT_OK ||
   2514       kit_frontend_registry_add_builtin(o.frontends) != KIT_OK ||
   2515       (ext && ext->register_frontends &&
   2516        ext->register_frontends(o.frontends) != KIT_OK)) {
   2517     driver_errf(tool, "failed to initialize frontend registry");
   2518     rc = 1;
   2519     goto done;
   2520   }
   2521 
   2522   if (build_alloc(&o, argc) != 0) {
   2523     rc = 2;
   2524     goto done;
   2525   }
   2526   if (build_parse(argc, argv, &o) != 0) goto done;
   2527   if (build_apply_env(&o) != 0) goto done;
   2528 
   2529   o.shared = (o.kind == BUILD_OUT_LIB && o.dynamic &&
   2530               o.emit == BUILD_EMIT_OBJ);
   2531   if (o.shared && !o.pic_explicit) o.target.pic = KIT_PIC_PIC;
   2532   if (o.shared && o.target.obj != KIT_OBJ_ELF) {
   2533     driver_errf(tool, "-shared output is supported only for ELF targets in v1");
   2534     goto done;
   2535   }
   2536   if (o.shared && o.target.pic == KIT_PIC_NONE) {
   2537     driver_errf(tool, "-shared requires PIC input; remove -fno-pic/-static");
   2538     goto done;
   2539   }
   2540 
   2541   if (build_validate(&o) != 0) goto done;
   2542 
   2543   /* Freestanding runtime headers so C/asm #includes resolve (mirrors compile);
   2544    * for link outputs, also the runtime archive + hosted libc wiring. */
   2545   if (driver_runtime_resolve(&env, o.support_dir, o.driver_path, &runtime) ==
   2546       0) {
   2547     runtime_resolved = 1;
   2548   } else {
   2549     driver_errf(tool, "support dir not found");
   2550     rc = 1;
   2551     goto done;
   2552   }
   2553 
   2554   /* Hosted defines/includes are a compile property and therefore apply to
   2555    * build-obj/build-lib as well as executable links.  Link-only search paths,
   2556    * CRT objects, and libraries stay inside the link-output branch below. */
   2557   build_enable_hosted_for_sysroot(&o);
   2558   build_apply_default_hosted_profile(&o);
   2559   if (build_apply_hosted_profile(&o) != 0) {
   2560     rc = 1;
   2561     goto done;
   2562   }
   2563   /* Hosted system headers precede Kit's freestanding fallback headers. This
   2564    * keeps <stdlib.h> on the native SDK while retaining compiler builtins that
   2565    * the SDK does not provide. */
   2566   if (!o.nostdinc &&
   2567       (o.hosted.profile_name
   2568            ? driver_runtime_append_freestanding_headers(&runtime,
   2569                                                          &o.groups[0].cf)
   2570            : driver_runtime_add_freestanding_headers(&runtime,
   2571                                                       &o.groups[0].cf)) != 0) {
   2572     driver_errf(tool, "failed to add freestanding headers");
   2573     rc = 1;
   2574     goto done;
   2575   }
   2576 
   2577   if (build_is_link_output(&o)) {
   2578     if (driver_link_inputs_append_windows_lib_dirs(&o.inputs, &o.sysroot,
   2579                                                    o.target) != 0) {
   2580       rc = 1;
   2581       goto done;
   2582     }
   2583     if (driver_link_inputs_append_sysroot_framework_dirs(&o.inputs, &o.sysroot,
   2584                                                          o.target) != 0) {
   2585       rc = 1;
   2586       goto done;
   2587     }
   2588     if (driver_link_inputs_resolve_pending(&o.inputs, o.target,
   2589                                            o.static_link) != 0) {
   2590       rc = 1;
   2591       goto done;
   2592     }
   2593     if (o.target.os == KIT_OS_FREESTANDING && o.inputs.ndsos) {
   2594       driver_errf(tool, "freestanding executable links do not accept DSO inputs");
   2595       rc = 1;
   2596       goto done;
   2597     }
   2598     if ((!o.shared || driver_target_shared_uses_hosted(o.target)) &&
   2599         !o.no_stdlib && !o.no_defaultlibs) {
   2600       DriverRuntimeArchive rt = {0};
   2601       if (driver_runtime_prepare_archive(&env, tool, &runtime, o.target,
   2602                                          o.epoch, &rt) != 0) {
   2603         driver_runtime_archive_fini(&env, &rt);
   2604         rc = 1;
   2605         goto done;
   2606       }
   2607       driver_link_inputs_insert_runtime_archives(
   2608           &o.inputs, &rt, o.target, o.hosted.nfinal, o.hosted.nafter);
   2609       driver_runtime_archive_fini(&env, &rt);
   2610     }
   2611   } else if (o.inputs.npending_libs || o.inputs.npending_frameworks) {
   2612     driver_errf(tool, "-l/-framework are only valid for build-exe");
   2613     rc = 1;
   2614     goto done;
   2615   }
   2616 
   2617   /* Build per-group merged preprocessor views now that the global baseline
   2618    * (groups[0]) holds the runtime/hosted includes and defines. */
   2619   for (gi = 0; gi < o.ngroups; ++gi) {
   2620     if (build_group_build_pp(&o, gi) != 0) {
   2621       rc = 1;
   2622       goto done;
   2623     }
   2624   }
   2625 
   2626   if (driver_target_new(&ctx, o.target, &o.target_features, tool, &target) !=
   2627       KIT_OK) {
   2628     driver_errf(tool, "failed to initialize compiler");
   2629     rc = 1;
   2630     goto done;
   2631   }
   2632   {
   2633     KitCompilerOptions copts;
   2634     memset(&copts, 0, sizeof copts);
   2635     copts.frontends = o.frontends;
   2636     if (kit_compiler_new_ex(target, &ctx, &copts, &compiler) != KIT_OK) {
   2637       driver_errf(tool, "failed to initialize compiler");
   2638       rc = 1;
   2639       goto done;
   2640     }
   2641     driver_diag_set_compiler(compiler);
   2642   }
   2643 
   2644   build_fill_code(&o, &code);
   2645   diag.warnings_are_errors = o.warnings_are_errors ? true : false;
   2646   diag.max_errors = o.max_errors;
   2647 
   2648   if (o.kind == BUILD_OUT_EXE) {
   2649     if (o.target.obj == KIT_OBJ_WASM)
   2650       rc = build_run_wasm_exe(&o, compiler, &ctx, &code, &diag);
   2651     else
   2652       rc =
   2653           build_run_link(&o, compiler, &ctx, &code, &diag, KIT_LINK_OUTPUT_EXE);
   2654   } else if (o.kind == BUILD_OUT_LIB) {
   2655     if (o.emit == BUILD_EMIT_C)
   2656       rc = build_run_c_tu(&o, compiler, &ctx, &code, &diag);
   2657     else if (o.shared)
   2658       rc = build_run_link(&o, compiler, &ctx, &code, &diag,
   2659                           KIT_LINK_OUTPUT_SHARED);
   2660     else
   2661       rc = build_run_archive(&o, compiler, &ctx, &code, &diag);
   2662   } else if (o.syntax_only) {
   2663     rc = build_run_per_source(&o, compiler, &ctx, &code, &diag);
   2664   } else if (o.emit == BUILD_EMIT_OBJ && o.nsources > 1 &&
   2665              o.target.obj == KIT_OBJ_WASM) {
   2666     rc = build_run_wasm_module(&o, compiler, &ctx, &code, &diag);
   2667   } else if (o.emit == BUILD_EMIT_OBJ && o.nsources > 1) {
   2668     rc = build_run_relocatable(&o, compiler, &ctx, &code, &diag);
   2669   } else {
   2670     rc = build_run_per_source(&o, compiler, &ctx, &code, &diag);
   2671   }
   2672 
   2673   if (rc == 0 &&
   2674       driver_diag_finish(&env, tool, o.warnings_are_errors, o.max_errors))
   2675     rc = 1;
   2676 
   2677 done:
   2678   if (compiler) driver_compiler_free(compiler);
   2679   kit_target_free(target);
   2680   if (runtime_resolved) driver_runtime_support_fini(&env, &runtime);
   2681   build_release(&o);
   2682   if (o.frontends) kit_frontend_registry_free(o.frontends);
   2683   driver_env_fini(&env);
   2684   return rc;
   2685 }
   2686 
   2687 /* ===================================================================== */
   2688 /* help + entry points                                                    */
   2689 /* ===================================================================== */
   2690 
   2691 void driver_help_build_exe(void) {
   2692   driver_printf(
   2693       "%.*s",
   2694       KIT_SLICE_ARG(KIT_SLICE_LIT(
   2695           "kit build-exe — link a polyglot source set into an executable\n"
   2696           "\n"
   2697           "USAGE\n"
   2698           "  kit build-exe [options] inputs...\n"
   2699           "\n"
   2700           "DESCRIPTION\n"
   2701           "  Compiles registered source languages (language per file) in memory\n"
   2702           "  and links them with any .o/.a/.so inputs into one executable. No\n"
   2703           "  intermediate files. For -target wasm32-none, inputs must be\n"
   2704           "  sources supplied in this invocation; .o/.a/-l Wasm linking is not\n"
   2705           "  a v1 feature.\n"
   2706           "\n"
   2707           "INPUTS\n"
   2708           "  Registered sources are C (.c), assembly (.s/.S), and WebAssembly\n"
   2709           "  (.wat/.wasm), selected by suffix or -x. Native links may also\n"
   2710           "  consume target-compatible .o, .a, and ELF .so inputs.\n"
   2711           "\n"
   2712           "OPTIONS\n"
   2713           "  -o PATH               Output (default a.out / a.exe)\n"
   2714           "  -O0 -O1 -O2  -g       Optimization / debug info (-O2 aliases "
   2715           "-O1)\n"
   2716           "  -target TRIPLE        Cross-compile target\n"
   2717           "  --sysroot DIR         User-supplied hosted cross sysroot\n"
   2718           "  -isysroot DIR         Hosted SDK/sysroot include root\n"
   2719           "  --support-dir DIR     Kit distribution support root\n"
   2720           "  -arch ARCH            Darwin-style target architecture\n"
   2721           "  -platform_version P MIN SDK\n"
   2722           "                        Darwin-style target platform\n"
   2723           "  -flto                 Link-time optimization for source inputs\n"
   2724           "  -static               Fully static executable\n"
   2725           "  -l NAME  -L DIR       Link a library / add a search dir\n"
   2726           "  -framework NAME  -F DIR\n"
   2727           "                        Link a Darwin framework / add a search dir\n"
   2728           "  -e SYM  -T script.ld  Entry symbol / linker script\n"
   2729           "  -Wl,...               Linker pass-through\n"
   2730           "  --group [flags] -- sources...   Scope compile flags to sources\n"
   2731           "  -X<lang> FLAG         Per-language frontend flag\n"
   2732           "  -h, --help            Show this help\n"
   2733           "  --version             Show Kit version\n"
   2734           "\n"
   2735           "DISCOVERY AND REQUIREMENTS\n"
   2736           "  Relocated distributions discover sibling support automatically;\n"
   2737           "  --support-dir remains an authoritative override. Native macOS uses\n"
   2738           "  the discovered SDK by default. Hosted cross builds require --sysroot.\n"
   2739           "\n"
   2740           "EXAMPLES\n"
   2741           "  kit build-exe main.c util.c -o app\n"
   2742           "\n"
   2743           "  # Flag-scoped C groups plus an assembly source.\n"
   2744           "  kit build-exe --group -DLEFT=20 -- left.c \\\n"
   2745           "    --group -DRIGHT=22 -- right.c main.c helper.s -o grouped\n"
   2746           "\n"
   2747           "  # Freestanding: provide startup code and a linker script.\n"
   2748           "  kit build-exe \\\n"
   2749           "    -target aarch64-none-elf -T link.ld -e _start \\\n"
   2750           "    start.s kernel.c -o kernel.elf\n"
   2751           "\n"
   2752           "EXIT CODES\n"
   2753           "  0   success    1   compile/link/I/O error    2   bad usage\n")));
   2754 }
   2755 
   2756 void driver_help_build_lib(void) {
   2757   driver_printf(
   2758       "%.*s",
   2759       KIT_SLICE_ARG(KIT_SLICE_LIT(
   2760           "kit build-lib — build a library or one C translation unit\n"
   2761           "\n"
   2762           "USAGE\n"
   2763           "  kit build-lib -o LIB.a [options] sources...\n"
   2764           "  kit build-lib --emit=c -o LIB.c [options] sources...\n"
   2765           "  kit build-lib -dynamic -o LIB.so [options] sources...\n"
   2766           "\n"
   2767           "DESCRIPTION\n"
   2768           "  Compiles a polyglot source set in memory and archives the "
   2769           "objects\n"
   2770           "  into a static library (.a), or links an ELF shared library with\n"
   2771           "  -dynamic/-shared. --emit=c merges semantic sources into one\n"
   2772           "  target-locked C translation unit. Non-ELF shared-library output\n"
   2773           "  is rejected.\n"
   2774           "\n"
   2775           "INPUTS\n"
   2776           "  Registered sources are C (.c), assembly (.s/.S), and WebAssembly\n"
   2777           "  (.wat/.wasm), selected by suffix or -x. --emit=c accepts semantic\n"
   2778           "  frontends (C, Toy, Wasm), not standalone assembly. Native ELF\n"
   2779           "  dynamic links may also consume compatible link inputs.\n"
   2780           "\n"
   2781           "OPTIONS\n"
   2782           "  -o PATH               Output path (required)\n"
   2783           "  --emit=c              Emit one C translation unit\n"
   2784           "  -dynamic, -shared     Build an ELF shared library\n"
   2785           "  -fPIC                 Position-independent code\n"
   2786           "  -O0 -O1 -O2  -g       Optimization / debug info (-O2 aliases "
   2787           "-O1)\n"
   2788           "  -flto                 Link-time optimization for source inputs\n"
   2789           "  -target TRIPLE        Cross-compile target\n"
   2790           "  --sysroot DIR         User-supplied hosted cross sysroot\n"
   2791           "  -isysroot DIR         Hosted SDK/sysroot include root\n"
   2792           "  --support-dir DIR     Kit distribution support root\n"
   2793           "  -L DIR, -l NAME       Shared-link library search/input\n"
   2794           "  --group [flags] -- sources...   Scope compile flags to sources\n"
   2795           "  -X<lang> FLAG         Per-language frontend flag\n"
   2796           "  -h, --help            Show this help\n"
   2797           "  --version             Show Kit version\n"
   2798           "\n"
   2799           "DISCOVERY AND REQUIREMENTS\n"
   2800           "  Relocated distributions discover sibling support automatically.\n"
   2801           "  Native macOS discovers its SDK; hosted cross targets require an\n"
   2802           "  explicit sysroot. Static .a output is the portable library shape;\n"
   2803           "  -dynamic/-shared is ELF-only. Generated C must be compiled for the\n"
   2804           "  same target selected here because its layouts are target-locked.\n"
   2805           "\n"
   2806           "EXAMPLES\n"
   2807           "  kit build-lib \\\n"
   2808           "    -o libanswer.a answer.c helper.s\n"
   2809           "\n"
   2810           "  kit build-lib --emit=c -o answer_amalgam.c answer.c helper.c\n"
   2811           "\n"
   2812           "  # Replace SYSROOT with a supplied x86-64 Linux sysroot.\n"
   2813           "  SYSROOT=/replace/with/x86_64-linux-sysroot\n"
   2814           "  kit build-lib \\\n"
   2815           "    -target x86_64-linux-gnu --sysroot \"$SYSROOT\" \\\n"
   2816           "    -dynamic -fPIC -o libanswer.so answer.c\n"
   2817           "\n"
   2818           "EXIT CODES\n"
   2819           "  0   success    1   compile/link/I/O error    2   bad usage\n")));
   2820 }
   2821 
   2822 void driver_help_build_obj(void) {
   2823   driver_printf(
   2824       "%.*s",
   2825       KIT_SLICE_ARG(KIT_SLICE_LIT(
   2826           "kit build-obj — compile sources to an object (or asm / C / IR)\n"
   2827           "\n"
   2828           "USAGE\n"
   2829           "  kit build-obj [options] sources...\n"
   2830           "\n"
   2831           "DESCRIPTION\n"
   2832           "  Compiles each source (registered language by suffix or -x) to an\n"
   2833           "  object. Multiple sources with --emit=obj combine into one\n"
   2834           "  relocatable object (ld -r), except -target wasm32-none merges the\n"
   2835           "  same source batch into one final wasm32 module. Separate Wasm\n"
   2836           "  object/static linking is not a v1 feature. The kit-native\n"
   2837           "  replacement for the retired `compile` tool.\n"
   2838           "\n"
   2839           "REGISTERED LANGUAGES\n"
   2840           "  C (.c), assembly (.s/.S), and WebAssembly (.wat/.wasm), selected\n"
   2841           "  by suffix or -x c|asm|wat|wasm. Frontend-specific options use\n"
   2842           "  -X<lang> FLAG.\n"
   2843           "\n"
   2844           "OPTIONS\n"
   2845           "  -o PATH               Output (default <base>.o; required for a\n"
   2846           "                        multi-source combine and for --emit=c)\n"
   2847           "  --emit=obj|asm|c|ir   Output form (ir requires -O1+)\n"
   2848           "  -S                    Alias for --emit=asm\n"
   2849           "  -fsyntax-only         Check only; write no output\n"
   2850           "  -O0 -O1 -O2  -g       Optimization / debug info (-O2 aliases "
   2851           "-O1)\n"
   2852           "  -flto                 Link-time optimization for multi-source "
   2853           "obj\n"
   2854           "  -target TRIPLE        Cross-compile target\n"
   2855           "  --sysroot DIR         User-supplied hosted cross sysroot\n"
   2856           "  -isysroot DIR         Hosted SDK/sysroot include root\n"
   2857           "  --support-dir DIR     Kit distribution support root\n"
   2858           "  -I/-isystem/-D/-U     Preprocessor flags (C/asm frontends)\n"
   2859           "  -x LANG               Force a registered language\n"
   2860           "  --group [flags] -- sources...   Scope compile flags to sources\n"
   2861           "  -X<lang> FLAG         Per-language frontend flag\n"
   2862           "  -o -                  Write the emit to stdout\n"
   2863           "  -h, --help            Show this help\n"
   2864           "  --version             Show Kit version\n"
   2865           "\n"
   2866           "OUTPUT AND RELEASE NOTES\n"
   2867           "  One-source object/assembly output defaults to <base>.o/.s. -o is\n"
   2868           "  required for multi-source object combination and --emit=c; use\n"
   2869           "  -o for stable IR filenames. -fsyntax-only writes nothing. A\n"
   2870           "  relocated distribution discovers sibling support automatically.\n"
   2871           "  Native macOS discovers its SDK; hosted cross targets require an\n"
   2872           "  explicit sysroot. Portable-C output uses the\n"
   2873           "  -O0 semantic pipeline; -O1/-O2 are accepted and normalized to -O0.\n"
   2874           "\n"
   2875           "EXAMPLES\n"
   2876           "  kit build-obj main.c\n"
   2877           "  kit build-obj \\\n"
   2878           "    main.c helper.c -o combined.o\n"
   2879           "  kit build-obj \\\n"
   2880           "    --emit=asm source.c -o source.s\n"
   2881           "  kit build-obj \\\n"
   2882           "    --emit=c -O0 source.c -o portable.c\n"
   2883           "  kit build-obj \\\n"
   2884           "    --emit=ir -O1 source.c -o source.ir\n"
   2885           "  kit build-obj \\\n"
   2886           "    -fsyntax-only -I include source.c\n"
   2887           "\n"
   2888           "EXIT CODES\n"
   2889           "  0   success    1   frontend/link/I/O error    2   bad usage\n")));
   2890 }
   2891 
   2892 int driver_build_exe_ex(int argc, char** argv, const KitDriverExtension* ext) {
   2893   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
   2894     driver_help_build_exe();
   2895     return 0;
   2896   }
   2897   return build_main(argc, argv, BUILD_OUT_EXE, "build-exe", ext);
   2898 }
   2899 
   2900 int driver_build_lib_ex(int argc, char** argv, const KitDriverExtension* ext) {
   2901   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
   2902     driver_help_build_lib();
   2903     return 0;
   2904   }
   2905   return build_main(argc, argv, BUILD_OUT_LIB, "build-lib", ext);
   2906 }
   2907 
   2908 int driver_build_obj_ex(int argc, char** argv, const KitDriverExtension* ext) {
   2909   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
   2910     driver_help_build_obj();
   2911     return 0;
   2912   }
   2913   return build_main(argc, argv, BUILD_OUT_OBJ, "build-obj", ext);
   2914 }
   2915 
   2916 int driver_build_exe(int argc, char** argv) {
   2917   return driver_build_exe_ex(argc, argv, NULL);
   2918 }
   2919 
   2920 int driver_build_lib(int argc, char** argv) {
   2921   return driver_build_lib_ex(argc, argv, NULL);
   2922 }
   2923 
   2924 int driver_build_obj(int argc, char** argv) {
   2925   return driver_build_obj_ex(argc, argv, NULL);
   2926 }