kit

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

cc.c (113272B)


      1 #include <kit/asm_emit.h>
      2 #include <kit/build.h>
      3 #include <kit/compile.h>
      4 #include <kit/core.h>
      5 #include <kit/link.h>
      6 #include <kit/preprocess.h>
      7 #include <stdint.h>
      8 #include <string.h>
      9 
     10 #include "cflags.h"
     11 #include "driver.h"
     12 #include "hosted.h"
     13 #include "lib_resolve.h"
     14 #include "link_flags.h"
     15 #include "link_inputs.h"
     16 #include "runtime.h"
     17 
     18 /* `kit cc` — C compiler driver. With -c produces a single object;
     19  * without -c compiles all C sources, links any .o/.a inputs alongside, and
     20  * emits an executable. The flag surface is a GCC subset:
     21  *
     22  *   -c -E -o -O0/1 (-O2 aliases -O1 for v1) -g
     23  *   -fsyntax-only
     24  *   -I -isystem -D -U
     25  *   -M -MM -MD -MMD -MF -MT -MQ -MP
     26  *   -target TRIPLE
     27  *   -fPIC -fPIE -fpic -fpie -mcmodel=small|medium|large|kernel
     28  *   -Werror -fmax-errors=N
     29  *   --build-id=none|sha256|uuid|0xHEX
     30  *   -ffile-prefix-map=old=new
     31  *   SOURCE_DATE_EPOCH (env)
     32  *   -e symbol -T script.ld
     33  *   -static -pie -no-pie
     34  *   -l name -L dir -framework name -F dir
     35  *   -x c|asm|s|asm-cpp|S|wasm|wat
     36  *   - (stdin source)
     37  *   .c/.cc -> source; .o/.obj -> object inputs; .a -> archive inputs.
     38  *
     39  * Library resolution (-lfoo against -L paths) happens here and produces
     40  * concrete archive paths for libkit. */
     41 
     42 #define CC_TOOL "cc"
     43 
     44 /* Header-dependency emission mode (subset of GCC's -M family).
     45  *   M    — print all deps; do not compile.
     46  *   MM   — like M but skip headers resolved via system (-isystem) dirs.
     47  *   MD   — compile normally AND write all deps to a file.
     48  *   MMD  — like MD but skip headers resolved via system (-isystem) dirs.
     49  * The MM/MMD filter keys on the resolved include directory (GCC semantics),
     50  * not the <...>-vs-"..." spelling, so a <foo.h> found via a plain -I dir is
     51  * still reported. Multi-source modes keep one rule and output path per source. */
     52 typedef enum CcDepMode {
     53   CC_DEP_NONE = 0,
     54   CC_DEP_M,
     55   CC_DEP_MM,
     56   CC_DEP_MD,
     57   CC_DEP_MMD,
     58 } CcDepMode;
     59 
     60 typedef enum CcProbeKind {
     61   CC_PROBE_NONE = 0,
     62   CC_PROBE_PRINT_SEARCH_DIRS,
     63   CC_PROBE_PRINT_FILE_NAME,
     64   CC_PROBE_PRINT_PROG_NAME,
     65   CC_PROBE_PRINT_LIBGCC_FILE_NAME,
     66   CC_PROBE_PRINT_MULTI_OS_DIRECTORY,
     67   CC_PROBE_PRINT_RESOURCE_DIR,
     68   CC_PROBE_PRINT_SYSROOT,
     69   CC_PROBE_DUMPMACHINE,
     70   CC_PROBE_DUMPVERSION,
     71   CC_PROBE_DUMPSPECS,
     72 } CcProbeKind;
     73 
     74 /* Link-input model (link items + object/archive/dso/lib arrays) is shared with
     75  * the build-* drivers; see driver/lib/link_inputs.h. */
     76 
     77 typedef struct CcOptions {
     78   DriverEnv* env;
     79   const char* driver_path;
     80   size_t argv_bound; /* upper bound on per-array list size */
     81 
     82   int compile_only;        /* -c               */
     83   int preprocess_only;     /* -E               */
     84   int syntax_only;         /* -fsyntax-only / check */
     85   int emit_c_source;       /* --emit=c         */
     86   int emit_ir;             /* --emit=ir        */
     87   int emit_asm_source;     /* -S               */
     88   int opt_level;           /* -O0/-O1; -O2 aliases -O1 */
     89   int debug_info;          /* -g               */
     90   int function_sections;   /* -ffunction-sections */
     91   int data_sections;       /* -fdata-sections     */
     92   int auto_var_init;       /* -ftrivial-auto-var-init= (KitAutoVarInit) */
     93   int stack_protector;     /* KitStackProtectorMode */
     94   int lto;                 /* -flto/-fno-lto      */
     95   uint64_t disabled_backend_features;
     96   int warnings_are_errors; /* -Werror     */
     97   uint32_t max_errors;     /* -fmax-errors=N   */
     98   KitTargetSpec target;    /* -target / host   */
     99   int target_set;          /* did -target appear */
    100   const char* output_path; /* -o               */
    101   char* owned_output_path;
    102   size_t owned_output_path_size;
    103   const char* sysroot;        /* --sysroot / -isysroot */
    104   int freestanding;           /* -ffreestanding (suppresses sysroot headers) */
    105   uint8_t default_visibility; /* KitSymVis; -fvisibility=... */
    106   int nostdinc;               /* -nostdinc (suppresses sysroot headers) */
    107   const char* support_dir;    /* --support-dir    */
    108   int probe_kind;             /* CcProbeKind      */
    109   const char* probe_arg;      /* -print-file-name= / -print-prog-name= */
    110 
    111   /* -ffile-prefix-map=old=new entries; old is heap-owned (split out), new
    112    * aliases argv. */
    113   KitPathPrefixMap* path_map;
    114   uint32_t npath_map;
    115   char** owned_path_map_olds;
    116   size_t* owned_path_map_old_sizes;
    117 
    118   /* Reproducibility: SOURCE_DATE_EPOCH parsed at end-of-parse. */
    119   uint64_t epoch;
    120 
    121   /* Cflags via shared helper. */
    122   DriverCflags cf;
    123   DriverTargetFeatures target_features;
    124 
    125   /* Positional inputs split by suffix. */
    126   const char** source_files; /* .c paths */
    127   KitLanguage* source_langs;
    128   uint32_t nsource_files;
    129   KitSourceInput* source_memory; /* "-" stdin slurp   */
    130   uint32_t nsource_memory;
    131   uint8_t* stdin_buf; /* owning storage for the one stdin */
    132   size_t stdin_size;
    133   /* Shared link-input model: object/archive/dso/lib arrays, the ordered link
    134    * item list, -L search paths, and the owned `<sysroot>/lib` Windows slot. */
    135   DriverLinkInputSet inputs;
    136   uint8_t next_group_id;
    137 
    138   /* -M family */
    139   int dep_mode;             /* CcDepMode */
    140   int dep_phony;            /* -MP       */
    141   const char* dep_file;     /* -MF       */
    142   const char** dep_targets; /* -MT/-MQ   */
    143   char** owned_dep_targets; /* quoted -MQ storage */
    144   size_t* owned_dep_target_sizes;
    145   uint32_t ndep_targets;
    146 
    147   /* Link-session options and owned -Wl state. */
    148   DriverLinkFlags link;
    149   int shared; /* -shared */
    150   int static_link;
    151   int pie;
    152   int pic_explicit;
    153   int no_stdlib;
    154   int no_defaultlibs;
    155   int no_startfiles;
    156   int wants_hosted_libc;
    157   DriverHostedPlan hosted;
    158 } CcOptions;
    159 
    160 static void cc_usage(void) {
    161   driver_errf(CC_TOOL, "%.*s",
    162               KIT_SLICE_ARG(KIT_SLICE_LIT(
    163                   "usage: kit cc [-c|-E|-shared] [-o out] "
    164                   "[options...] inputs...\n"
    165                   "       kit cc --help    for full option reference")));
    166 }
    167 
    168 void driver_help_cc(void) {
    169   driver_printf(
    170       "%.*s",
    171       KIT_SLICE_ARG(KIT_SLICE_LIT(
    172           "kit cc — C compiler driver\n"
    173           "\n"
    174           "USAGE\n"
    175           "  kit cc [options] INPUT...                  compile and link\n"
    176           "  kit cc -c [options] INPUT.c               emit an object\n"
    177           "  kit cc -S [options] INPUT.c               emit assembly\n"
    178           "  kit cc -E [options] INPUT.c               preprocess only\n"
    179           "  kit cc -M|-MM [options] INPUT.c           print dependencies\n"
    180           "  kit cc --emit=c|ir [options] INPUT.c      emit C or IR\n"
    181           "  kit cc -fsyntax-only [options] INPUT...   check only\n"
    182           "\n"
    183           "DESCRIPTION\n"
    184           "  Compiles C and assembly sources, optionally linking compatible\n"
    185           "  objects, archives, and shared libraries into a program. It can\n"
    186           "  also stop after preprocessing, dependency generation, semantic\n"
    187           "  checks, assembly, object, portable C, or semantic IR output.\n"
    188           "\n"
    189           "  .c is C source; .S is preprocessed assembly; .s is assembly.\n"
    190           "  Target-compatible .o, .a, and ELF .so files are accepted by the\n"
    191           "  link stage. A single `-` input reads stdin; use `-x c -` when an\n"
    192           "  input suffix is unavailable. This release does not accept `--`\n"
    193           "  before compiler operands; spell a leading-dash path as ./-name.c.\n"
    194           "\n"
    195           "  Linking defaults to a.out (a.exe for Windows). -c and -S derive\n"
    196           "  <base>.o and <base>.s for one source. -E, -M, and -MM write stdout\n"
    197           "  when -o is absent. Use -o for --emit=c/ir and combined outputs.\n"
    198           "\n"
    199           "OPTIONS\n"
    200           "  -I DIR, -isystem DIR       Add user/system include directories\n"
    201           "  -D NAME[=BODY], -U NAME    Define or undefine a macro\n"
    202           "  -E                         Preprocess without compiling\n"
    203           "  -M, -MM                    Dependencies with/without system headers\n"
    204           "  -MD, -MMD, -MF FILE        Side-effect dependencies/output file\n"
    205           "  -MT TARGET, -MQ TARGET     Dependency target spelling\n"
    206           "\n"
    207           "  -M/-MM emit one rule per source in input order. -MD/-MMD write\n"
    208           "  per-source dependency files during compile and compile-link.\n"
    209           "\n"
    210           "COMPILATION\n"
    211           "  -std=c11                    Select the supported C language level\n"
    212           "  -fsyntax-only               Diagnose only; write no output\n"
    213           "  -c, -S                      Stop after object or assembly emission\n"
    214           "  --emit=c                    Portable C output (-O1/-O2 normalize to -O0)\n"
    215           "  --emit=ir                   Semantic IR dump; requires -O1 or -O2\n"
    216           "  -O0, -O1, -O2              Optimize (-O2 aliases -O1)\n"
    217           "  -g                          Emit debug information\n"
    218           "  -flto                       Record source inputs for LTO at link\n"
    219           "  -fPIC/-fpic, -fPIE/-fpie   Position-independent code/executable\n"
    220           "  -Werror, -fmax-errors=N     Diagnostic policy\n"
    221           "  -ffreestanding              Freestanding language/runtime mode\n"
    222           "  -fstack-protector[-strong|-all]\n"
    223           "                              Select conventional stack canaries\n"
    224           "  -fno-stack-protector       Disable stack canaries\n"
    225           "  -x c|assembler|assembler-with-cpp\n"
    226           "                              Override suffix classification\n"
    227           "\n"
    228           "LINKING\n"
    229           "  -o PATH                     Output path\n"
    230           "  -L DIR, -l NAME             Library search/input\n"
    231           "  -static, -shared            Static executable / ELF shared library\n"
    232           "  -nostdlib, -nodefaultlibs, -nostartfiles\n"
    233           "                              Suppress default link components\n"
    234           "  -T SCRIPT, -e SYMBOL        Linker script and entry symbol\n"
    235           "  -Wl,...                     Forward supported linker tokens\n"
    236           "  -framework NAME, -F DIR     Darwin framework/search directory\n"
    237           "\n"
    238           "  Linker scripts and entries accept direct joined/equal forms,\n"
    239           "  -Wl comma lists, and equivalent -Xlinker sequences. Static/shared\n"
    240           "  availability follows the target\n"
    241           "  platform; non-ELF shared-library output is not provided here.\n")));
    242   driver_printf(
    243       "%.*s",
    244       KIT_SLICE_ARG(KIT_SLICE_LIT(
    245           "\n"
    246           "TARGET AND SEARCH PATHS\n"
    247           "  -target TRIPLE              Select a canonical target below\n"
    248           "  -arch ARCH                  Darwin-style architecture selection\n"
    249           "  -march=ISA, -mabi=ABI       Architecture/ABI selection when supported\n"
    250           "  --sysroot DIR, -isysroot DIR\n"
    251           "                              Hosted SDK/sysroot supplied by the user\n"
    252           "  --support-dir DIR           Kit distribution support root\n"
    253           "  -print-search-dirs          Print compiler/library search paths\n"
    254           "  -print-sysroot              Print the native SDK Kit can discover\n"
    255           "  -print-resource-dir         Print the current compiler resource path\n"
    256           "\n"
    257           "  Hosted: aarch64-linux-gnu, x86_64-linux-gnu, riscv64-linux-gnu;\n"
    258           "    aarch64-linux-musl, x86_64-linux-musl, riscv64-linux-musl;\n"
    259           "    aarch64-freebsd, x86_64-freebsd, riscv64-freebsd;\n"
    260           "    aarch64-windows, x86_64-windows; aarch64-linux-android21;\n"
    261           "    aarch64-apple-darwin, x86_64-apple-darwin.\n"
    262           "  Freestanding: aarch64-none-elf, x86_64-none-elf,\n"
    263           "    riscv64-none-elf, riscv32-none-elf, arm-none-eabi.\n"
    264           "  WebAssembly source target: wasm32-none (source-batch module; this\n"
    265           "    release does not link separate Wasm .o/.a inputs).\n"
    266           "\n"
    267           "DISCOVERY AND LIMITS\n"
    268           "  Kit resolves support relative to its canonical executable path;\n"
    269           "  --support-dir is an authoritative override. Native macOS applies\n"
    270           "  the discovered SDK automatically. Cross-hosted compilation still\n"
    271           "  requires an explicit user-supplied sysroot.\n"
    272           "\n"
    273           "  Portable-C output currently uses the -O0 semantic pipeline; explicit\n"
    274           "  -O1/-O2 requests are accepted and normalized before code generation.\n"
    275           "\n"
    276           "GETTING HELP\n"
    277           "  -h, --help                  Show this help and exit\n"
    278           "  --version                   Show Kit version and exit\n"
    279           "\n"
    280           "EXAMPLES\n"
    281           "  # Native compile.\n"
    282           "  printf 'int main(void) { return 0; }\\n' > hello.c\n"
    283           "  kit cc hello.c -o hello\n"
    284           "  ./hello\n"
    285           "\n"
    286           "  # Replace SYSROOT with a supplied hosted cross sysroot.\n"
    287           "  SYSROOT=/replace/with/aarch64-linux-sysroot\n"
    288           "  kit cc -target aarch64-linux-gnu --sysroot \"$SYSROOT\" \\\n"
    289           "    hello.c -o hello.aa64\n"
    290           "\n"
    291           "  # Freestanding final link; provide startup object and linker script.\n"
    292           "  kit cc -target aarch64-none-elf \\\n"
    293           "    -ffreestanding -c kernel.c -o kernel.o\n"
    294           "  kit cc -target aarch64-none-elf \\\n"
    295           "    -ffreestanding -T link.ld -e _start start.o kernel.o -o kernel.elf\n"
    296           "\n"
    297           "  kit cc -E -DVALUE=42 input.c\n"
    298           "  kit cc -MM input.c -MF input.d\n"
    299           "  kit cc --emit=c -O0 input.c -o input.out.c\n"
    300           "  kit cc --emit=ir -O1 input.c -o input.ir\n"
    301           "\n"
    302           "EXIT CODES\n"
    303           "  0   success    1   compile/link/I/O error    2   bad usage\n")));
    304 }
    305 
    306 void driver_help_check(void) {
    307   driver_printf(
    308       "%.*s",
    309       KIT_SLICE_ARG(KIT_SLICE_LIT(
    310           "kit check — run C frontend checks without emitting code\n"
    311           "\n"
    312           "USAGE\n"
    313           "  kit check [options] INPUT.c...\n"
    314           "\n"
    315           "DESCRIPTION\n"
    316           "  Equivalent to the preprocessing and semantic-check portion of\n"
    317           "  `kit cc -fsyntax-only`. It writes diagnostics only: no object,\n"
    318           "  assembly, executable, or dependency output is produced.\n"
    319           "\n"
    320           "OPTIONS\n"
    321           "  Accepts the cc preprocessing, target, sysroot, diagnostic, and\n"
    322           "  language options described by `kit cc --help`, including -I,\n"
    323           "  -isystem, -D, -U, -std=c11, -Werror, -fmax-errors=N,\n"
    324           "  -target, --sysroot/-isysroot, and --support-dir. Inputs are C\n"
    325           "  sources; each is preprocessed and checked independently.\n"
    326           "\n"
    327           "  Relocated distributions discover their sibling support tree. Native\n"
    328           "  macOS discovers SDK headers automatically; cross-hosted checks use\n"
    329           "  an explicit --sysroot/-isysroot as described by `kit cc --help`.\n"
    330           "\n"
    331           "GETTING HELP\n"
    332           "  -h, --help        Show this help and exit\n"
    333           "  --version         Show Kit version and exit\n"
    334           "\n"
    335           "EXAMPLES\n"
    336           "  kit check \\\n"
    337           "    -I include -DDEBUG=1 src/main.c src/util.c\n"
    338           "\n"
    339           "EXIT CODES\n"
    340           "  0   checks passed    1   frontend/I/O error    2   bad usage\n")));
    341 }
    342 
    343 static int cc_alloc_arrays(CcOptions* o, int argc) {
    344   size_t bound = (size_t)argc + 16u;
    345   o->argv_bound = bound;
    346   o->source_files =
    347       driver_alloc_zeroed(o->env, bound * sizeof(*o->source_files));
    348   o->source_langs =
    349       driver_alloc_zeroed(o->env, bound * sizeof(*o->source_langs));
    350   o->source_memory =
    351       driver_alloc_zeroed(o->env, bound * sizeof(*o->source_memory));
    352   o->dep_targets = driver_alloc_zeroed(o->env, bound * sizeof(*o->dep_targets));
    353   o->owned_dep_targets =
    354       driver_alloc_zeroed(o->env, bound * sizeof(*o->owned_dep_targets));
    355   o->owned_dep_target_sizes = driver_alloc_zeroed(
    356       o->env, bound * sizeof(*o->owned_dep_target_sizes));
    357   o->path_map = driver_alloc_zeroed(o->env, bound * sizeof(*o->path_map));
    358   o->owned_path_map_olds =
    359       driver_alloc_zeroed(o->env, bound * sizeof(*o->owned_path_map_olds));
    360   o->owned_path_map_old_sizes =
    361       driver_alloc_zeroed(o->env, bound * sizeof(*o->owned_path_map_old_sizes));
    362   if (!o->source_files || !o->source_langs || !o->source_memory ||
    363       !o->dep_targets || !o->owned_dep_targets ||
    364       !o->owned_dep_target_sizes || !o->path_map || !o->owned_path_map_olds ||
    365       !o->owned_path_map_old_sizes) {
    366     driver_errf(CC_TOOL, "out of memory");
    367     return 1;
    368   }
    369   if (driver_link_inputs_init(&o->inputs, o->env, CC_TOOL, bound) != 0)
    370     return 1;
    371   if (driver_link_flags_init(&o->link, o->env, CC_TOOL, (uint32_t)bound) != 0 ||
    372       driver_cflags_init(&o->cf, o->env,
    373                          (int)(bound + DRIVER_HOSTED_MAX_DEFINES +
    374                                DRIVER_HOSTED_MAX_INCLUDES)) != 0 ||
    375       driver_target_features_init(&o->target_features, o->env, argc) != 0) {
    376     driver_errf(CC_TOOL, "out of memory");
    377     return 1;
    378   }
    379   return 0;
    380 }
    381 
    382 static void cc_options_release(CcOptions* o) {
    383   uint32_t i;
    384   size_t bound = o->argv_bound;
    385   for (i = 0; i < o->npath_map; ++i) {
    386     if (o->owned_path_map_olds[i]) {
    387       driver_free(o->env, o->owned_path_map_olds[i],
    388                   o->owned_path_map_old_sizes[i]);
    389     }
    390   }
    391   for (i = 0; i < o->ndep_targets; ++i) {
    392     if (o->owned_dep_targets[i]) {
    393       driver_free(o->env, o->owned_dep_targets[i],
    394                   o->owned_dep_target_sizes[i]);
    395     }
    396   }
    397   if (o->stdin_buf) driver_free(o->env, o->stdin_buf, o->stdin_size);
    398   if (o->owned_output_path)
    399     driver_free(o->env, o->owned_output_path, o->owned_output_path_size);
    400   driver_link_inputs_fini(&o->inputs);
    401   driver_hosted_plan_fini(o->env, &o->hosted);
    402   driver_link_flags_fini(&o->link);
    403   driver_target_features_fini(&o->target_features, o->env);
    404   driver_cflags_fini(&o->cf, o->env);
    405   driver_free(o->env, o->source_files, bound * sizeof(*o->source_files));
    406   driver_free(o->env, o->source_langs, bound * sizeof(*o->source_langs));
    407   driver_free(o->env, o->source_memory, bound * sizeof(*o->source_memory));
    408   driver_free(o->env, o->dep_targets, bound * sizeof(*o->dep_targets));
    409   driver_free(o->env, o->owned_dep_targets,
    410               bound * sizeof(*o->owned_dep_targets));
    411   driver_free(o->env, o->owned_dep_target_sizes,
    412               bound * sizeof(*o->owned_dep_target_sizes));
    413   driver_free(o->env, o->path_map, bound * sizeof(*o->path_map));
    414   driver_free(o->env, o->owned_path_map_olds,
    415               bound * sizeof(*o->owned_path_map_olds));
    416   driver_free(o->env, o->owned_path_map_old_sizes,
    417               bound * sizeof(*o->owned_path_map_old_sizes));
    418 }
    419 
    420 static int cc_apply_hosted_profile(CcOptions* o);
    421 static int cc_record_framework(CcOptions* o, const char* name) {
    422   DriverPendingFramework* pf;
    423   if (!name || !name[0]) {
    424     driver_errf(CC_TOOL, "-framework requires an argument");
    425     return 1;
    426   }
    427   pf = &o->inputs.pending_frameworks[o->inputs.npending_frameworks++];
    428   pf->name = name;
    429   driver_link_inputs_push(&o->inputs, DRIVER_LINK_FRAMEWORK,
    430                           o->inputs.npending_frameworks - 1u);
    431   return 0;
    432 }
    433 
    434 /* Routed through the shared driver_path_is_source authority (canonical
    435  * extension registry, headers excluded), so adding a frontend extension reaches
    436  * cc/build/run/dbg at once. cc still treats .h as a header, not a source — the
    437  * helper excludes it. */
    438 static int cc_is_c_source(const char* s) { return driver_path_is_source(s); }
    439 
    440 static int cc_record_path_map(CcOptions* o, const char* arg) {
    441   const char* eq = driver_strchr(arg, '=');
    442   KitPathPrefixMap* m = &o->path_map[o->npath_map];
    443   if (!eq) {
    444     driver_errf(CC_TOOL, "-ffile-prefix-map requires old=new");
    445     return 1;
    446   }
    447   {
    448     size_t n = (size_t)(eq - arg);
    449     size_t bytes = n + 1;
    450     char* old_ = driver_alloc(o->env, bytes);
    451     if (!old_) {
    452       driver_errf(CC_TOOL, "out of memory");
    453       return 1;
    454     }
    455     driver_memcpy(old_, arg, n);
    456     old_[n] = '\0';
    457     o->owned_path_map_olds[o->npath_map] = old_;
    458     o->owned_path_map_old_sizes[o->npath_map] = bytes;
    459     m->old_prefix = old_;
    460     m->new_prefix = eq + 1;
    461   }
    462   o->npath_map++;
    463   return 0;
    464 }
    465 
    466 static int cc_set_probe(CcOptions* o, int kind, const char* arg) {
    467   if (o->probe_kind != CC_PROBE_NONE) {
    468     driver_errf(CC_TOOL, "only one compiler-information probe is supported");
    469     return 1;
    470   }
    471   o->probe_kind = kind;
    472   o->probe_arg = arg;
    473   return 0;
    474 }
    475 
    476 /* Emit one element of a colon-separated search path. *first tracks whether a
    477  * leading separator is needed; NULL/empty entries are skipped. */
    478 static void cc_print_path_elem(const char* dir, int* first) {
    479   if (!dir || !dir[0]) return;
    480   driver_printf("%s%.*s", *first ? "" : ":",
    481                 KIT_SLICE_ARG(kit_slice_cstr(dir)));
    482   *first = 0;
    483 }
    484 
    485 static int cc_run_probe(CcOptions* o) {
    486   char triple[64];
    487   if (driver_target_to_triple(o->target, triple, sizeof(triple)) != 0) {
    488     driver_errf(CC_TOOL, "failed to render target triple");
    489     return 1;
    490   }
    491 
    492   switch (o->probe_kind) {
    493     case CC_PROBE_PRINT_SEARCH_DIRS: {
    494       DriverRuntimeSupport rt = {0};
    495       int have_rt = (driver_runtime_resolve(o->env, o->support_dir,
    496                                             o->driver_path, &rt) == 0);
    497       DriverHostedDirs dirs;
    498       int have_dirs = 0;
    499       uint32_t i;
    500       int first;
    501       /* Resolve the hosted include/library dirs directly: a probe run skips the
    502        * parse-time hosted profile (no compile happens), so they are not yet in
    503        * cf. Gated on hosted libc being engaged (-lc / sysroot). */
    504       if (o->wants_hosted_libc) {
    505         DriverHostedRequest req = {0};
    506         req.env = o->env;
    507         req.tool = CC_TOOL;
    508         req.target = o->target;
    509         req.sysroot = o->sysroot;
    510         req.static_link = o->static_link;
    511         req.link_inputs = 1;
    512         have_dirs = (driver_hosted_dirs_resolve(&req, &dirs) == 0);
    513       }
    514       driver_printf("install: %.*s\n",
    515                     KIT_SLICE_ARG(kit_slice_cstr(
    516                         have_rt ? rt.support_root
    517                                 : (o->support_dir ? o->support_dir : ""))));
    518       driver_printf("programs: =\n");
    519       /* libraries: hosted crt/libc search dirs, then user -L dirs, then the kit
    520        * runtime dir holding libkit_rt.a. */
    521       driver_printf("libraries: =");
    522       first = 1;
    523       if (have_dirs)
    524         for (i = 0; i < dirs.nlibdirs; ++i)
    525           cc_print_path_elem(dirs.libdirs[i], &first);
    526       for (i = 0; i < o->inputs.nlib_search_paths; ++i)
    527         cc_print_path_elem(o->inputs.lib_search_paths[i], &first);
    528       if (have_rt) cc_print_path_elem(rt.rt_root, &first);
    529       driver_printf("\n");
    530       /* includes: user -I/-isystem, then the hosted system headers, then the
    531        * freestanding runtime headers. (kit extension to GCC's output.) */
    532       driver_printf("includes: =");
    533       first = 1;
    534       for (i = 0; i < o->cf.ninclude_dirs; ++i)
    535         cc_print_path_elem(o->cf.include_dirs[i], &first);
    536       for (i = 0; i < o->cf.nsystem_include_dirs; ++i)
    537         cc_print_path_elem(o->cf.system_include_dirs[i], &first);
    538       if (have_dirs)
    539         for (i = 0; i < dirs.nincdirs; ++i)
    540           cc_print_path_elem(dirs.incdirs[i], &first);
    541       if (have_rt) cc_print_path_elem(rt.include_dir, &first);
    542       driver_printf("\n");
    543       if (have_dirs) driver_hosted_dirs_fini(&dirs);
    544       if (have_rt) driver_runtime_support_fini(o->env, &rt);
    545       return 0;
    546     }
    547     case CC_PROBE_PRINT_FILE_NAME:
    548       driver_printf(
    549           "%.*s\n",
    550           KIT_SLICE_ARG(kit_slice_cstr(o->probe_arg ? o->probe_arg : "")));
    551       return 0;
    552     case CC_PROBE_PRINT_PROG_NAME:
    553       driver_printf(
    554           "%.*s\n",
    555           KIT_SLICE_ARG(kit_slice_cstr(o->probe_arg ? o->probe_arg : "")));
    556       return 0;
    557     case CC_PROBE_PRINT_LIBGCC_FILE_NAME:
    558       driver_printf("libkit_rt.a\n");
    559       return 0;
    560     case CC_PROBE_PRINT_MULTI_OS_DIRECTORY:
    561       driver_printf(".\n");
    562       return 0;
    563     case CC_PROBE_PRINT_RESOURCE_DIR: {
    564       /* clang convention: the resource-dir root, with builtin/freestanding
    565        * headers under <resource-dir>/include. */
    566       DriverRuntimeSupport rt = {0};
    567       if (driver_runtime_resolve(o->env, o->support_dir, o->driver_path, &rt) ==
    568           0) {
    569         driver_printf("%.*s\n", KIT_SLICE_ARG(kit_slice_cstr(rt.rt_root)));
    570         driver_runtime_support_fini(o->env, &rt);
    571       } else {
    572         driver_printf("%.*s\n", KIT_SLICE_ARG(kit_slice_cstr(
    573                                     o->support_dir ? o->support_dir : "")));
    574       }
    575       return 0;
    576     }
    577     case CC_PROBE_PRINT_SYSROOT: {
    578       /* The effective sysroot root: command-line --sysroot or KIT_SYSROOT, else
    579        * the native host probe when it resolves to a single tree (the macOS
    580        * SDK). Empty for a multi-dir probe (Linux/FreeBSD) or a cross target
    581        * with no sysroot, which have no single root. */
    582       DriverHostedRequest req = {0};
    583       DriverHostedDirs dirs;
    584       req.env = o->env;
    585       req.tool = CC_TOOL;
    586       req.target = o->target;
    587       req.sysroot = o->sysroot;
    588       req.static_link = o->static_link;
    589       req.link_inputs = 1;
    590       if (driver_hosted_dirs_resolve(&req, &dirs) == 0) {
    591         driver_printf(
    592             "%.*s\n",
    593             KIT_SLICE_ARG(kit_slice_cstr(dirs.root ? dirs.root : "")));
    594         driver_hosted_dirs_fini(&dirs);
    595       } else {
    596         /* An explicitly selected (argv or KIT_SYSROOT) path is authoritative.
    597          * The resolver has already diagnosed an invalid one; do not turn that
    598          * failure into a successful empty probe.  A cross target with no
    599          * supplied sysroot still resolves successfully with dirs.root == NULL
    600          * and prints the conventional empty line above. */
    601         return 1;
    602       }
    603       return 0;
    604     }
    605     case CC_PROBE_DUMPMACHINE:
    606       driver_printf("%.*s\n", KIT_SLICE_ARG(kit_slice_cstr(triple)));
    607       return 0;
    608     case CC_PROBE_DUMPVERSION:
    609       driver_printf("0\n");
    610       return 0;
    611     case CC_PROBE_DUMPSPECS:
    612       driver_printf("*kit:\n");
    613       driver_printf("%%{!S:%%{!E:%%{!c:%%{!r:link}}}}\n");
    614       return 0;
    615     default:
    616       break;
    617   }
    618 
    619   return 0;
    620 }
    621 
    622 static int cc_record_stdin(CcOptions* o, int forced_lang) {
    623   KitSourceInput* in;
    624   if (o->stdin_buf) {
    625     driver_errf(CC_TOOL, "'-' (stdin) may appear at most once");
    626     return 1;
    627   }
    628   if (!driver_read_stdin(o->env, &o->stdin_buf, &o->stdin_size)) {
    629     driver_errf(CC_TOOL, "failed to read stdin");
    630     return 1;
    631   }
    632   in = &o->source_memory[o->nsource_memory++];
    633   in->name = KIT_SLICE_LIT("<stdin>");
    634   in->bytes.data = o->stdin_buf;
    635   in->bytes.len = o->stdin_size;
    636   in->lang = forced_lang >= 0 ? (KitLanguage)forced_lang : KIT_LANG_C;
    637   driver_link_inputs_push(&o->inputs, DRIVER_LINK_SOURCE_MEMORY,
    638                           o->nsource_memory - 1u);
    639   return 0;
    640 }
    641 
    642 /* Stored in source_langs[] during arg parsing to mean "no -x override —
    643  * resolve from the path at compile time, once a compiler is around to
    644  * consult its frontend extension registry." */
    645 #define CC_LANG_AUTO KIT_LANG_AUTO
    646 
    647 static KitLanguage cc_resolve_lang(KitCompiler* c, const char* path,
    648                                    KitLanguage stored) {
    649   if (stored != CC_LANG_AUTO) return stored;
    650   return kit_language_for_path(c, path);
    651 }
    652 
    653 static int cc_classify_positional(CcOptions* o, const char* a,
    654                                   int forced_lang) {
    655   if (driver_streq(a, "-")) return cc_record_stdin(o, forced_lang);
    656   if (forced_lang >= 0 || cc_is_c_source(a)) {
    657     o->source_langs[o->nsource_files] =
    658         forced_lang >= 0 ? (KitLanguage)forced_lang : CC_LANG_AUTO;
    659     o->source_files[o->nsource_files++] = a;
    660     driver_link_inputs_push(&o->inputs, DRIVER_LINK_SOURCE,
    661                             o->nsource_files - 1u);
    662     return 0;
    663   }
    664   if (driver_has_suffix(a, ".o") || driver_has_suffix(a, ".obj")) {
    665     o->inputs.object_files[o->inputs.nobject_files++] = a;
    666     driver_link_inputs_push(&o->inputs, DRIVER_LINK_OBJECT,
    667                             o->inputs.nobject_files - 1u);
    668     return 0;
    669   }
    670   if (driver_has_suffix(a, ".a")) {
    671     DriverArchiveInput* ar = &o->inputs.archives[o->inputs.narchives++];
    672     ar->path = a;
    673     ar->whole_archive = o->inputs.cur_whole_archive;
    674     ar->link_mode = o->inputs.cur_link_mode;
    675     ar->group_id = o->inputs.cur_group_id;
    676     driver_link_inputs_push(&o->inputs, DRIVER_LINK_ARCHIVE,
    677                             o->inputs.narchives - 1u);
    678     return 0;
    679   }
    680   if (driver_is_dso_path(a)) {
    681     DriverDsoInput* d = &o->inputs.dsos[o->inputs.ndsos++];
    682     d->path = a;
    683     driver_link_inputs_push(&o->inputs, DRIVER_LINK_DSO, o->inputs.ndsos - 1u);
    684     return 0;
    685   }
    686   driver_errf(CC_TOOL, "input does not have a recognized suffix: %.*s",
    687               KIT_SLICE_ARG(kit_slice_cstr(a)));
    688   return 1;
    689 }
    690 
    691 static int cc_apply_hosted_profile(CcOptions* o) {
    692   /* A link action (not -c/-E/-M/-MM) gets crt files + interpreter; the include
    693    * + define + lib-search-dir parts apply regardless. */
    694   int link_action = !o->syntax_only && !o->compile_only &&
    695                     !o->preprocess_only &&
    696                     o->dep_mode != CC_DEP_M && o->dep_mode != CC_DEP_MM;
    697   int shared_hosted = o->shared && driver_target_shared_uses_hosted(o->target);
    698   if (!o->wants_hosted_libc || (o->shared && !shared_hosted)) return 0;
    699   /* -nostdlib/-nodefaultlibs suppress the link-time CRT/libc additions, not
    700    * the hosted compiler profile.  Native compilation still needs the SDK's
    701    * defines and headers (matching clang/gcc). */
    702   if (o->no_stdlib || o->no_defaultlibs) link_action = 0;
    703   return driver_link_inputs_apply_hosted(
    704       &o->inputs, &o->hosted, &o->cf, &o->link, o->target, o->sysroot,
    705       o->static_link, o->shared, o->no_startfiles, link_action);
    706 }
    707 
    708 static int cc_apply_env(CcOptions* o) {
    709   const char* sde = driver_getenv("SOURCE_DATE_EPOCH");
    710   if (sde && driver_parse_u64(sde, &o->epoch) != 0) {
    711     driver_errf(CC_TOOL, "invalid SOURCE_DATE_EPOCH: %.*s",
    712                 KIT_SLICE_ARG(kit_slice_cstr(sde)));
    713     return 1;
    714   }
    715   return 0;
    716 }
    717 
    718 static int cc_has_link_action(const CcOptions* o) {
    719   return !o->syntax_only && !o->compile_only && !o->preprocess_only &&
    720          o->dep_mode != CC_DEP_M && o->dep_mode != CC_DEP_MM;
    721 }
    722 
    723 /* A sysroot on its own means "compile/link hosted against that root" — the
    724  * same default clang and gcc take for -isysroot/--sysroot. Engage the hosted
    725  * libc profile so the host headers, their feature-test defines (__GNUC__,
    726  * __APPLE__, __has_builtin, ...) and (for link actions) the host C runtime are
    727  * brought in. -ffreestanding and -nostdinc opt back out, keeping the
    728  * freestanding rt/include set standalone; -shared keeps its existing meaning.
    729  * -nostdlib/-nodefaultlibs only suppress link additions. The Windows-COFF
    730  * default profile and an explicit
    731  * -lc already set the flag, so this is a no-op in those cases. */
    732 static void cc_enable_hosted_for_sysroot(CcOptions* o) {
    733   if (o->wants_hosted_libc) return;
    734   if (o->shared && !driver_target_shared_uses_hosted(o->target)) return;
    735   if (!o->sysroot || !o->sysroot[0]) return;
    736   if (o->freestanding || o->nostdinc) return;
    737   o->wants_hosted_libc = 1;
    738 }
    739 
    740 static void cc_apply_default_hosted_profile(CcOptions* o) {
    741   KitTargetSpec host;
    742   int native_macos;
    743   if (!driver_target_default_hosted_profile(o->target)) return;
    744   if (o->wants_hosted_libc) return;
    745   if (o->freestanding || o->nostdinc) return;
    746   host = driver_host_target();
    747   native_macos = host.os == KIT_OS_MACOS && o->target.os == KIT_OS_MACOS;
    748   if ((!o->sysroot || !o->sysroot[0]) && !native_macos) return;
    749   if (!cc_has_link_action(o) && o->nsource_files + o->nsource_memory == 0)
    750     return;
    751   o->wants_hosted_libc = 1;
    752 }
    753 
    754 static char* cc_dep_default_target(DriverEnv* env, const CcOptions* o,
    755                                    size_t* out_size);
    756 
    757 /* GNU make quoting used by -MQ and by driver-derived dependency paths. -MT is
    758  * intentionally left verbatim. */
    759 static char* cc_dep_quote_make_alloc(DriverEnv* env, const char* s,
    760                                      size_t* out_size) {
    761   size_t n = driver_strlen(s);
    762   size_t i;
    763   size_t pos = 0;
    764   char* out = driver_alloc(env, n * 2u + 1u);
    765   if (!out) return NULL;
    766   for (i = 0; i < n; ++i) {
    767     char c = s[i];
    768     if (c == '$') {
    769       out[pos++] = '$';
    770       out[pos++] = '$';
    771     } else if (c == ' ' || c == '\t' || c == '#' || c == '\\') {
    772       out[pos++] = '\\';
    773       out[pos++] = c;
    774     } else {
    775       out[pos++] = c;
    776     }
    777   }
    778   out[pos] = '\0';
    779   if (out_size) *out_size = n * 2u + 1u;
    780   return out;
    781 }
    782 
    783 static int cc_parse(int argc, char** argv, CcOptions* o) {
    784   int forced_lang = -1;
    785   int i;
    786 
    787   if (cc_alloc_arrays(o, argc) != 0) return 1;
    788   o->target = driver_host_target();
    789 
    790   for (i = 1; i < argc; ++i) {
    791     const char* a = argv[i];
    792 
    793     {
    794       int r =
    795           driver_cflags_try_consume(&o->cf, o->env, CC_TOOL, argc, argv, &i);
    796       if (r < 0) return 1;
    797       if (r > 0) continue;
    798     }
    799 
    800     if (driver_streq(a, "-c")) {
    801       o->compile_only = 1;
    802       continue;
    803     }
    804     if (driver_streq(a, "-v") || driver_streq(a, "-###")) {
    805       continue;
    806     }
    807     if (driver_streq(a, "-E")) {
    808       o->preprocess_only = 1;
    809       continue;
    810     }
    811     if (driver_streq(a, "-S")) {
    812       o->emit_asm_source = 1;
    813       o->compile_only = 1;
    814       continue;
    815     }
    816     if (driver_streq(a, "-fsyntax-only")) {
    817       o->syntax_only = 1;
    818       continue;
    819     }
    820     if (driver_streq(a, "--emit=c")) {
    821       /* C-source output instead of object bytes. Forces -c-style single-input
    822        * compile (no link). See doc/CBACKEND.md. */
    823       o->emit_c_source = 1;
    824       o->compile_only = 1;
    825       continue;
    826     }
    827     if (driver_streq(a, "--emit=ir")) {
    828       /* Textual semantic-IR dump instead of object bytes. The IR tape is only
    829        * recorded when the optimizer runs, so this requires -O1+ (validated
    830        * after argument parsing). Forces a single-input, no-link compile. */
    831       o->emit_ir = 1;
    832       o->compile_only = 1;
    833       continue;
    834     }
    835     if (driver_streq(a, "-g")) {
    836       o->debug_info = 1;
    837       continue;
    838     }
    839     if (driver_streq(a, "-O0")) {
    840       o->opt_level = 0;
    841       continue;
    842     }
    843     if (driver_streq(a, "-O1")) {
    844       o->opt_level = 1;
    845       continue;
    846     }
    847     if (driver_streq(a, "-O2")) {
    848       o->opt_level = 1;
    849       continue;
    850     }
    851     if (driver_streq(a, "-O") || driver_streq(a, "-O3") ||
    852         driver_streq(a, "-Os") || driver_streq(a, "-Oz") ||
    853         driver_streq(a, "-Ofast")) {
    854       o->opt_level = 1;
    855       continue;
    856     }
    857 
    858     if (driver_streq(a, "-Werror")) {
    859       o->warnings_are_errors = 1;
    860       continue;
    861     }
    862     if (driver_strneq(a, "-Werror=", 8)) {
    863       o->warnings_are_errors = 1;
    864       continue;
    865     }
    866     if (driver_streq(a, "-Wall") || driver_streq(a, "-Wextra") ||
    867         driver_streq(a, "-Wpedantic") || driver_streq(a, "-pedantic") ||
    868         driver_streq(a, "-pedantic-errors") || driver_streq(a, "-w") ||
    869         driver_strneq(a, "-Wno-", 5) ||
    870         (driver_strneq(a, "-W", 2) && !driver_strneq(a, "-Wl,", 4))) {
    871       continue;
    872     }
    873     if (driver_strneq(a, "-fmax-errors=", 13)) {
    874       uint64_t v;
    875       if (driver_parse_u64(a + 13, &v) != 0 || v > 0xFFFFFFFFu) {
    876         driver_errf(CC_TOOL, "-fmax-errors= requires a non-negative integer");
    877         return 1;
    878       }
    879       o->max_errors = (uint32_t)v;
    880       continue;
    881     }
    882     if (driver_strneq(a, "-std=", 5) || driver_streq(a, "-ansi")) {
    883       continue;
    884     }
    885     if (driver_streq(a, "-ffreestanding")) {
    886       o->freestanding = 1;
    887       continue;
    888     }
    889     if (driver_streq(a, "-fvisibility=hidden")) {
    890       o->default_visibility = KIT_SV_HIDDEN;
    891       continue;
    892     }
    893     if (driver_streq(a, "-fvisibility=default")) {
    894       o->default_visibility = KIT_SV_DEFAULT;
    895       continue;
    896     }
    897     if (driver_strneq(a, "-fvisibility=", 13)) {
    898       driver_errf(CC_TOOL, "unsupported visibility: %.*s",
    899                   KIT_SLICE_ARG(kit_slice_cstr(a + 13)));
    900       return 1;
    901     }
    902     if (driver_streq(a, "-fhosted")) {
    903       o->freestanding = 0;
    904       continue;
    905     }
    906     if (driver_streq(a, "-ffunction-sections")) {
    907       o->function_sections = 1;
    908       continue;
    909     }
    910     if (driver_streq(a, "-fno-function-sections")) {
    911       o->function_sections = 0;
    912       continue;
    913     }
    914     if (driver_streq(a, "-fdata-sections")) {
    915       o->data_sections = 1;
    916       continue;
    917     }
    918     if (driver_strneq(a, "-ftrivial-auto-var-init=", 24)) {
    919       const char* mode = a + 24;
    920       if (driver_streq(mode, "zero")) {
    921         o->auto_var_init = KIT_AUTOVAR_ZERO;
    922       } else if (driver_streq(mode, "uninitialized")) {
    923         o->auto_var_init = KIT_AUTOVAR_UNINIT;
    924       } else if (driver_streq(mode, "pattern")) {
    925         driver_errf(CC_TOOL,
    926                     "-ftrivial-auto-var-init=pattern is not yet supported; "
    927                     "use =zero");
    928         return 1;
    929       } else {
    930         driver_errf(CC_TOOL,
    931                     "-ftrivial-auto-var-init=: unknown mode '%s' "
    932                     "(expected zero, pattern, or uninitialized)",
    933                     mode);
    934         return 1;
    935       }
    936       continue;
    937     }
    938     if (driver_streq(a, "-fno-data-sections")) {
    939       o->data_sections = 0;
    940       continue;
    941     }
    942     if (driver_streq(a, "-fno-stack-protector")) {
    943       o->stack_protector = KIT_STACK_PROTECTOR_NONE;
    944       continue;
    945     }
    946     if (driver_streq(a, "-fstack-protector")) {
    947       o->stack_protector = KIT_STACK_PROTECTOR_BASIC;
    948       continue;
    949     }
    950     if (driver_streq(a, "-fstack-protector-strong")) {
    951       o->stack_protector = KIT_STACK_PROTECTOR_STRONG;
    952       continue;
    953     }
    954     if (driver_streq(a, "-fstack-protector-all")) {
    955       o->stack_protector = KIT_STACK_PROTECTOR_ALL;
    956       continue;
    957     }
    958     if (driver_strneq(a, "-fstack-protector", 17)) {
    959       driver_errf(CC_TOOL, "unsupported stack protector mode: %.*s",
    960                   KIT_SLICE_ARG(kit_slice_cstr(a)));
    961       return 1;
    962     }
    963     if (driver_streq(a, "-flto")) {
    964       o->lto = 1;
    965       continue;
    966     }
    967     if (driver_streq(a, "-fno-lto")) {
    968       o->lto = 0;
    969       continue;
    970     }
    971     if (driver_streq(a, "-nostdinc")) {
    972       o->nostdinc = 1;
    973       continue;
    974     }
    975     if (driver_streq(a, "-fno-builtin") ||
    976         driver_strneq(a, "-fno-builtin-", 13) || driver_streq(a, "-pipe") ||
    977         driver_streq(a, "-pthread")) {
    978       continue;
    979     }
    980     if (driver_streq(a, "-nostdlib")) {
    981       o->no_stdlib = 1;
    982       continue;
    983     }
    984     if (driver_streq(a, "-nodefaultlibs")) {
    985       o->no_defaultlibs = 1;
    986       continue;
    987     }
    988     if (driver_streq(a, "-nostartfiles")) {
    989       o->no_startfiles = 1;
    990       continue;
    991     }
    992     if (driver_streq(a, "-isysroot") || driver_streq(a, "--sysroot")) {
    993       if (++i >= argc) {
    994         driver_errf(CC_TOOL, "%.*s requires an argument",
    995                     KIT_SLICE_ARG(kit_slice_cstr(a)));
    996         return 1;
    997       }
    998       o->sysroot = argv[i];
    999       continue;
   1000     }
   1001     if (driver_strneq(a, "--sysroot=", 10)) {
   1002       o->sysroot = a + 10;
   1003       continue;
   1004     }
   1005     if (driver_streq(a, "--support-dir")) {
   1006       if (++i >= argc) {
   1007         driver_errf(CC_TOOL, "--support-dir requires an argument");
   1008         return 1;
   1009       }
   1010       o->support_dir = argv[i];
   1011       continue;
   1012     }
   1013     if (driver_strneq(a, "--support-dir=", 14)) {
   1014       o->support_dir = a + 14;
   1015       continue;
   1016     }
   1017     if (driver_streq(a, "-print-search-dirs")) {
   1018       if (cc_set_probe(o, CC_PROBE_PRINT_SEARCH_DIRS, NULL) != 0) return 1;
   1019       continue;
   1020     }
   1021     if (driver_strneq(a, "-print-file-name=", 17)) {
   1022       if (cc_set_probe(o, CC_PROBE_PRINT_FILE_NAME, a + 17) != 0) return 1;
   1023       continue;
   1024     }
   1025     if (driver_strneq(a, "-print-prog-name=", 17)) {
   1026       if (cc_set_probe(o, CC_PROBE_PRINT_PROG_NAME, a + 17) != 0) return 1;
   1027       continue;
   1028     }
   1029     if (driver_streq(a, "-print-libgcc-file-name")) {
   1030       if (cc_set_probe(o, CC_PROBE_PRINT_LIBGCC_FILE_NAME, NULL) != 0) return 1;
   1031       continue;
   1032     }
   1033     if (driver_streq(a, "-print-multi-os-directory")) {
   1034       if (cc_set_probe(o, CC_PROBE_PRINT_MULTI_OS_DIRECTORY, NULL) != 0)
   1035         return 1;
   1036       continue;
   1037     }
   1038     if (driver_streq(a, "-print-resource-dir")) {
   1039       if (cc_set_probe(o, CC_PROBE_PRINT_RESOURCE_DIR, NULL) != 0) return 1;
   1040       continue;
   1041     }
   1042     if (driver_streq(a, "-print-sysroot")) {
   1043       if (cc_set_probe(o, CC_PROBE_PRINT_SYSROOT, NULL) != 0) return 1;
   1044       continue;
   1045     }
   1046     if (driver_streq(a, "-dumpmachine")) {
   1047       if (cc_set_probe(o, CC_PROBE_DUMPMACHINE, NULL) != 0) return 1;
   1048       continue;
   1049     }
   1050     if (driver_streq(a, "-dumpversion")) {
   1051       if (cc_set_probe(o, CC_PROBE_DUMPVERSION, NULL) != 0) return 1;
   1052       continue;
   1053     }
   1054     if (driver_streq(a, "-dumpspecs")) {
   1055       if (cc_set_probe(o, CC_PROBE_DUMPSPECS, NULL) != 0) return 1;
   1056       continue;
   1057     }
   1058     if (driver_streq(a, "-include")) {
   1059       if (++i >= argc) {
   1060         driver_errf(CC_TOOL, "%.*s requires an argument",
   1061                     KIT_SLICE_ARG(kit_slice_cstr(a)));
   1062         return 1;
   1063       }
   1064       /* The preprocessor has no force-include hook yet (KitPreprocessOptions
   1065        * carries no prefix-include list), so honoring this would silently drop
   1066        * the file and miscompile. Fail loudly rather than ignore it. */
   1067       driver_errf(CC_TOOL, "-include is unimplemented: %.*s",
   1068                   KIT_SLICE_ARG(kit_slice_cstr(argv[i])));
   1069       return 1;
   1070     }
   1071 
   1072     if (driver_streq(a, "-fPIC") || driver_streq(a, "-fpic")) {
   1073       o->target.pic = KIT_PIC_PIC;
   1074       o->pic_explicit = 1;
   1075       continue;
   1076     }
   1077     if (driver_streq(a, "-fPIE") || driver_streq(a, "-fpie")) {
   1078       o->target.pic = KIT_PIC_PIE;
   1079       o->pic_explicit = 1;
   1080       continue;
   1081     }
   1082     if (driver_streq(a, "-fno-PIC") || driver_streq(a, "-fno-pic") ||
   1083         driver_streq(a, "-fno-PIE") || driver_streq(a, "-fno-pie")) {
   1084       o->target.pic = KIT_PIC_NONE;
   1085       o->pic_explicit = 1;
   1086       continue;
   1087     }
   1088     if (driver_streq(a, "-static")) {
   1089       o->target.pic = KIT_PIC_NONE;
   1090       o->static_link = 1;
   1091       o->pic_explicit = 1;
   1092       o->inputs.cur_link_mode = KIT_LM_STATIC;
   1093       continue;
   1094     }
   1095     if (driver_streq(a, "-pie")) {
   1096       o->target.pic = KIT_PIC_PIE;
   1097       o->pie = 1;
   1098       o->pic_explicit = 1;
   1099       continue;
   1100     }
   1101     if (driver_streq(a, "-no-pie")) {
   1102       o->target.pic = KIT_PIC_NONE;
   1103       o->pie = 0;
   1104       o->pic_explicit = 1;
   1105       continue;
   1106     }
   1107     if (driver_streq(a, "-rdynamic") || driver_streq(a, "-export-dynamic")) {
   1108       /* GCC/clang: ask the linker to promote defined globals into .dynsym
   1109        * (--export-dynamic). FreeBSD's HOST_ENV_LDFLAGS passes -rdynamic so a
   1110        * dynamically linked exe re-exports symbols (e.g. `environ`) for libc.so
   1111        * and for runtime symbolization. kit's ELF linker already exports the
   1112        * defined symbols that the DSOs it links reference, which is what hosted
   1113        * libc startup needs, so accept the flag for toolchain compatibility
   1114        * rather than reject it. (The standalone `kit ld` carries the explicit
   1115        * -E/--export-dynamic option for the full all-globals promotion.) */
   1116       continue;
   1117     }
   1118     if (driver_streq(a, "-Bstatic")) {
   1119       o->inputs.cur_link_mode = KIT_LM_STATIC;
   1120       continue;
   1121     }
   1122     if (driver_streq(a, "-Bdynamic")) {
   1123       o->inputs.cur_link_mode = KIT_LM_DYNAMIC;
   1124       continue;
   1125     }
   1126     if (driver_streq(a, "--as-needed")) {
   1127       o->inputs.cur_link_mode = KIT_LM_AS_NEEDED;
   1128       continue;
   1129     }
   1130     if (driver_streq(a, "--no-as-needed")) {
   1131       o->inputs.cur_link_mode = KIT_LM_DYNAMIC;
   1132       continue;
   1133     }
   1134     if (driver_streq(a, "--whole-archive")) {
   1135       o->inputs.cur_whole_archive = 1;
   1136       continue;
   1137     }
   1138     if (driver_streq(a, "--no-whole-archive")) {
   1139       o->inputs.cur_whole_archive = 0;
   1140       continue;
   1141     }
   1142     if (driver_streq(a, "--start-group")) {
   1143       if (o->inputs.cur_group_id != 0) {
   1144         driver_errf(CC_TOOL, "nested --start-group is not supported");
   1145         return 1;
   1146       }
   1147       if (o->next_group_id == UINT8_MAX) {
   1148         driver_errf(CC_TOOL, "too many --start-group occurrences");
   1149         return 1;
   1150       }
   1151       o->inputs.cur_group_id = ++o->next_group_id;
   1152       continue;
   1153     }
   1154     if (driver_streq(a, "--end-group")) {
   1155       if (o->inputs.cur_group_id == 0) {
   1156         driver_errf(CC_TOOL, "--end-group without --start-group");
   1157         return 1;
   1158       }
   1159       o->inputs.cur_group_id = 0;
   1160       continue;
   1161     }
   1162 
   1163     if (driver_streq(a, "-shared")) {
   1164       o->shared = 1;
   1165       continue;
   1166     }
   1167     if (driver_streq(a, "-mwindows")) {
   1168       o->link.pe_subsystem = KIT_PE_SUBSYSTEM_WINDOWS_GUI;
   1169       continue;
   1170     }
   1171     if (driver_streq(a, "-mconsole")) {
   1172       o->link.pe_subsystem = KIT_PE_SUBSYSTEM_WINDOWS_CUI;
   1173       continue;
   1174     }
   1175     if (driver_strneq(a, "-Wl,", 4)) {
   1176       if (driver_strneq(a, "-Wl,-framework,", 15)) {
   1177         if (cc_record_framework(o, a + 15) != 0) return 1;
   1178         continue;
   1179       }
   1180       if (driver_link_flags_record_wl(&o->link, a + 4) != 0) return 1;
   1181       continue;
   1182     }
   1183 
   1184     if (driver_strneq(a, "-mcmodel=", 9)) {
   1185       if (driver_record_mcmodel(&o->target, CC_TOOL, a + 9) != 0) return 1;
   1186       continue;
   1187     }
   1188     if (driver_streq(a, "-mno-red-zone")) {
   1189       o->disabled_backend_features |= KIT_CG_BACKEND_RED_ZONE;
   1190       continue;
   1191     }
   1192     if (driver_streq(a, "-mred-zone")) {
   1193       o->disabled_backend_features &= ~KIT_CG_BACKEND_RED_ZONE;
   1194       continue;
   1195     }
   1196     if (driver_streq(a, "-mgeneral-regs-only")) {
   1197       o->disabled_backend_features |= KIT_CG_BACKEND_SIMD;
   1198       continue;
   1199     }
   1200     if (driver_streq(a, "-mno-general-regs-only")) {
   1201       o->disabled_backend_features &= ~KIT_CG_BACKEND_SIMD;
   1202       continue;
   1203     }
   1204     {
   1205       int tr = driver_target_features_try_consume(&o->target_features, o->env,
   1206                                                   CC_TOOL, argc, argv, &i);
   1207       if (tr < 0) return 1;
   1208       if (tr > 0) continue;
   1209     }
   1210     if (driver_strneq(a, "--build-id=", 11)) {
   1211       if (driver_link_flags_record_build_id(&o->link, a + 11) != 0) return 1;
   1212       continue;
   1213     }
   1214     if (driver_strneq(a, "-ffile-prefix-map=", 18)) {
   1215       if (cc_record_path_map(o, a + 18) != 0) return 1;
   1216       continue;
   1217     }
   1218 
   1219     if (driver_streq(a, "-o")) {
   1220       if (++i >= argc) {
   1221         driver_errf(CC_TOOL, "-o requires an argument");
   1222         return 1;
   1223       }
   1224       o->output_path = argv[i];
   1225       continue;
   1226     }
   1227     if (driver_strneq(a, "--output=", 9)) {
   1228       o->output_path = a + 9;
   1229       continue;
   1230     }
   1231     if (driver_streq(a, "--output")) {
   1232       if (++i >= argc) {
   1233         driver_errf(CC_TOOL, "--output requires an argument");
   1234         return 1;
   1235       }
   1236       o->output_path = argv[i];
   1237       continue;
   1238     }
   1239     if (driver_streq(a, "-e")) {
   1240       if (++i >= argc) {
   1241         driver_errf(CC_TOOL, "-e requires an argument");
   1242         return 1;
   1243       }
   1244       if (driver_link_flags_record_entry(&o->link, argv[i],
   1245                                          driver_strlen(argv[i])) != 0)
   1246         return 1;
   1247       continue;
   1248     }
   1249     if (driver_strneq(a, "--entry=", 8)) {
   1250       if (driver_link_flags_record_entry(&o->link, a + 8,
   1251                                          driver_strlen(a + 8)) != 0)
   1252         return 1;
   1253       continue;
   1254     }
   1255     if (driver_streq(a, "--entry")) {
   1256       if (++i >= argc) {
   1257         driver_errf(CC_TOOL, "--entry requires an argument");
   1258         return 1;
   1259       }
   1260       if (driver_link_flags_record_entry(&o->link, argv[i],
   1261                                          driver_strlen(argv[i])) != 0)
   1262         return 1;
   1263       continue;
   1264     }
   1265     if (a[0] == '-' && a[1] == 'e' && a[2] != '\0') {
   1266       if (driver_link_flags_record_entry(&o->link, a + 2,
   1267                                          driver_strlen(a + 2)) != 0)
   1268         return 1;
   1269       continue;
   1270     }
   1271     if (driver_streq(a, "-T")) {
   1272       if (++i >= argc) {
   1273         driver_errf(CC_TOOL, "-T requires an argument");
   1274         return 1;
   1275       }
   1276       if (driver_link_flags_record_script(&o->link, argv[i],
   1277                                           driver_strlen(argv[i])) != 0)
   1278         return 1;
   1279       continue;
   1280     }
   1281     if (driver_strneq(a, "--script=", 9)) {
   1282       if (driver_link_flags_record_script(&o->link, a + 9,
   1283                                           driver_strlen(a + 9)) != 0)
   1284         return 1;
   1285       continue;
   1286     }
   1287     if (driver_streq(a, "--script")) {
   1288       if (++i >= argc) {
   1289         driver_errf(CC_TOOL, "--script requires an argument");
   1290         return 1;
   1291       }
   1292       if (driver_link_flags_record_script(&o->link, argv[i],
   1293                                           driver_strlen(argv[i])) != 0)
   1294         return 1;
   1295       continue;
   1296     }
   1297     if (a[0] == '-' && a[1] == 'T' && a[2] != '\0' &&
   1298         !driver_strneq(a, "-Ttext", 6) &&
   1299         !driver_strneq(a, "-Tdata", 6) &&
   1300         !driver_strneq(a, "-Tbss", 5)) {
   1301       if (driver_link_flags_record_script(&o->link, a + 2,
   1302                                           driver_strlen(a + 2)) != 0)
   1303         return 1;
   1304       continue;
   1305     }
   1306     if (driver_streq(a, "-target")) {
   1307       if (++i >= argc) {
   1308         driver_errf(CC_TOOL, "-target requires an argument");
   1309         return 1;
   1310       }
   1311       if (driver_target_from_triple(argv[i], &o->target) != 0) {
   1312         driver_err_unknown_target(CC_TOOL, argv[i]);
   1313         return 1;
   1314       }
   1315       o->target_set = 1;
   1316       continue;
   1317     }
   1318     if (driver_strneq(a, "--target=", 9)) {
   1319       if (driver_target_from_triple(a + 9, &o->target) != 0) {
   1320         driver_err_unknown_target(CC_TOOL, a + 9);
   1321         return 1;
   1322       }
   1323       o->target_set = 1;
   1324       continue;
   1325     }
   1326     if (driver_streq(a, "--target")) {
   1327       if (++i >= argc) {
   1328         driver_errf(CC_TOOL, "--target requires an argument");
   1329         return 1;
   1330       }
   1331       if (driver_target_from_triple(argv[i], &o->target) != 0) {
   1332         driver_err_unknown_target(CC_TOOL, argv[i]);
   1333         return 1;
   1334       }
   1335       o->target_set = 1;
   1336       continue;
   1337     }
   1338     if (driver_streq(a, "-Xlinker")) {
   1339       const char* xarg;
   1340       if (++i >= argc) {
   1341         driver_errf(CC_TOOL, "-Xlinker requires an argument");
   1342         return 1;
   1343       }
   1344       xarg = argv[i];
   1345       if (driver_streq(argv[i], "-framework")) {
   1346         if (++i >= argc) {
   1347           driver_errf(CC_TOOL, "-framework requires an argument");
   1348           return 1;
   1349         }
   1350         if (driver_streq(argv[i], "-Xlinker")) {
   1351           if (++i >= argc) {
   1352             driver_errf(CC_TOOL, "-framework requires an argument");
   1353             return 1;
   1354           }
   1355         }
   1356         if (cc_record_framework(o, argv[i]) != 0) return 1;
   1357         continue;
   1358       }
   1359       if (driver_streq(xarg, "-T") || driver_streq(xarg, "--script") ||
   1360           driver_streq(xarg, "-e") || driver_streq(xarg, "--entry")) {
   1361         const char* value;
   1362         if (++i >= argc) {
   1363           driver_errf(CC_TOOL, "%s passed via -Xlinker requires an argument",
   1364                       xarg);
   1365           return 1;
   1366         }
   1367         if (driver_streq(argv[i], "-Xlinker")) {
   1368           if (++i >= argc) {
   1369             driver_errf(CC_TOOL,
   1370                         "%s passed via -Xlinker requires an argument", xarg);
   1371             return 1;
   1372           }
   1373         }
   1374         value = argv[i];
   1375         if ((xarg[1] == 'T' || driver_streq(xarg, "--script"))
   1376                 ? driver_link_flags_record_script(&o->link, value,
   1377                                                   driver_strlen(value))
   1378                 : driver_link_flags_record_entry(&o->link, value,
   1379                                                  driver_strlen(value)))
   1380           return 1;
   1381         continue;
   1382       }
   1383       if (driver_link_flags_record_wl(&o->link, xarg) != 0) return 1;
   1384       continue;
   1385     }
   1386     if (driver_streq(a, "-x")) {
   1387       if (++i >= argc) {
   1388         driver_errf(CC_TOOL, "-x requires an argument");
   1389         return 1;
   1390       }
   1391       if (driver_streq(argv[i], "none")) {
   1392         forced_lang = -1;
   1393         continue;
   1394       }
   1395       /* cc-only CLI aliases that have no frontend `-x` name: GCC's "asm-cpp"
   1396        * and the case-sensitive "S" (distinct from "s") both select asm. */
   1397       if (driver_streq(argv[i], "asm-cpp") || driver_streq(argv[i], "S")) {
   1398         forced_lang = KIT_LANG_ASM;
   1399         continue;
   1400       }
   1401       {
   1402         KitLanguage lang = kit_language_for_name(NULL, argv[i]);
   1403         if (lang == KIT_LANG_UNKNOWN) {
   1404           const char* const known[] = {"c",   "asm",  "assembler",
   1405                                        "s",   "toy",  "wasm",
   1406                                        "wat", "none", "asm-cpp"};
   1407           const char* candidates[sizeof known / sizeof known[0]];
   1408           DriverSuggestion suggestions[3];
   1409           size_t k, count = 0, n;
   1410           for (k = 0; k < sizeof known / sizeof known[0]; ++k) {
   1411             if (driver_streq(known[k], "none") ||
   1412                 driver_streq(known[k], "asm-cpp") ||
   1413                 kit_language_for_name(NULL, known[k]) != KIT_LANG_UNKNOWN)
   1414               candidates[count++] = known[k];
   1415           }
   1416           n = driver_suggest_values(argv[i], candidates, count, suggestions, 3);
   1417           if (n)
   1418             driver_errf(CC_TOOL,
   1419                         "unsupported -x language: %s; did you mean '%s'?",
   1420                         argv[i], suggestions[0].value);
   1421           else
   1422             driver_errf(CC_TOOL, "unsupported -x language: %s", argv[i]);
   1423           return 1;
   1424         }
   1425         forced_lang = lang;
   1426         continue;
   1427       }
   1428     }
   1429     if (driver_strneq(a, "-L", 2)) {
   1430       const char* dir = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
   1431       if (!dir) {
   1432         driver_errf(CC_TOOL, "-L requires an argument");
   1433         return 1;
   1434       }
   1435       o->inputs.lib_search_paths[o->inputs.nlib_search_paths++] = dir;
   1436       continue;
   1437     }
   1438     if (driver_strneq(a, "-F", 2)) {
   1439       const char* dir = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
   1440       if (!dir) {
   1441         driver_errf(CC_TOOL, "-F requires an argument");
   1442         return 1;
   1443       }
   1444       o->inputs.framework_search_paths[o->inputs.nframework_search_paths++] =
   1445           dir;
   1446       continue;
   1447     }
   1448     if (driver_strneq(a, "-l", 2)) {
   1449       const char* name = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
   1450       if (!name) {
   1451         driver_errf(CC_TOOL, "-l requires an argument");
   1452         return 1;
   1453       }
   1454       if (driver_streq(name, "c") && !o->no_stdlib && !o->no_defaultlibs) {
   1455         o->wants_hosted_libc = 1;
   1456         continue;
   1457       }
   1458       {
   1459         DriverPendingLib* pl =
   1460             &o->inputs.pending_libs[o->inputs.npending_libs++];
   1461         pl->name = name;
   1462         pl->whole_archive = o->inputs.cur_whole_archive;
   1463         pl->link_mode = o->inputs.cur_link_mode;
   1464         pl->group_id = o->inputs.cur_group_id;
   1465         driver_link_inputs_push(&o->inputs, DRIVER_LINK_LIB,
   1466                                 o->inputs.npending_libs - 1u);
   1467       }
   1468       continue;
   1469     }
   1470     if (driver_streq(a, "-framework")) {
   1471       if (++i >= argc) {
   1472         driver_errf(CC_TOOL, "-framework requires an argument");
   1473         return 1;
   1474       }
   1475       if (cc_record_framework(o, argv[i]) != 0) return 1;
   1476       continue;
   1477     }
   1478 
   1479     if (driver_streq(a, "-M")) {
   1480       o->dep_mode = CC_DEP_M;
   1481       continue;
   1482     }
   1483     if (driver_streq(a, "-MM")) {
   1484       o->dep_mode = CC_DEP_MM;
   1485       continue;
   1486     }
   1487     if (driver_streq(a, "-MD")) {
   1488       o->dep_mode = CC_DEP_MD;
   1489       continue;
   1490     }
   1491     if (driver_streq(a, "-MMD")) {
   1492       o->dep_mode = CC_DEP_MMD;
   1493       continue;
   1494     }
   1495     if (driver_streq(a, "-MP")) {
   1496       o->dep_phony = 1;
   1497       continue;
   1498     }
   1499     if (driver_streq(a, "-MF")) {
   1500       if (++i >= argc) {
   1501         driver_errf(CC_TOOL, "-MF requires an argument");
   1502         return 1;
   1503       }
   1504       o->dep_file = argv[i];
   1505       continue;
   1506     }
   1507     if (driver_streq(a, "-MT") || driver_streq(a, "-MQ")) {
   1508       int quote = driver_streq(a, "-MQ");
   1509       uint32_t slot = o->ndep_targets;
   1510       if (++i >= argc) {
   1511         driver_errf(CC_TOOL, "%.*s requires an argument",
   1512                     KIT_SLICE_ARG(kit_slice_cstr(a)));
   1513         return 1;
   1514       }
   1515       if (quote) {
   1516         o->owned_dep_targets[slot] = cc_dep_quote_make_alloc(
   1517             o->env, argv[i], &o->owned_dep_target_sizes[slot]);
   1518         if (!o->owned_dep_targets[slot]) {
   1519           driver_errf(CC_TOOL, "out of memory");
   1520           return 1;
   1521         }
   1522         o->dep_targets[slot] = o->owned_dep_targets[slot];
   1523       } else {
   1524         o->dep_targets[slot] = argv[i];
   1525       }
   1526       o->ndep_targets++;
   1527       continue;
   1528     }
   1529 
   1530     if (driver_streq(a, "--")) {
   1531       for (++i; i < argc; ++i) {
   1532         if (cc_classify_positional(o, argv[i], forced_lang) != 0) return 1;
   1533       }
   1534       break;
   1535     }
   1536     if (driver_streq(a, "-")) {
   1537       if (cc_classify_positional(o, a, forced_lang) != 0) return 1;
   1538       continue;
   1539     }
   1540     if (a[0] == '-' && a[1] != '\0') {
   1541       const char* const valid[] = {
   1542           "--help",     "-h",          "--version", "-target",
   1543           "--target",   "-o",          "--output",  "-T",
   1544           "--script",   "-e",          "--entry",   "-x",
   1545           "-c",         "-S",          "-E",        "-shared",
   1546           "-static",    "-nostdlib",   "-isysroot", "--sysroot",
   1547           "--start-group", "--end-group"};
   1548       DriverSuggestion suggestions[3];
   1549       size_t n = driver_suggest_values(a, valid,
   1550                                        sizeof valid / sizeof valid[0],
   1551                                        suggestions, 3);
   1552       if (n)
   1553         driver_errf(CC_TOOL, "unknown flag: %s; did you mean '%s'?", a,
   1554                     suggestions[0].value);
   1555       else
   1556         driver_errf(CC_TOOL, "unknown flag: %s", a);
   1557       return 1;
   1558     }
   1559 
   1560     if (cc_classify_positional(o, a, forced_lang) != 0) return 1;
   1561   }
   1562 
   1563   if (o->probe_kind != CC_PROBE_NONE) return 0;
   1564 
   1565   if (cc_apply_env(o) != 0) return 1;
   1566   if (driver_link_inputs_append_windows_lib_dirs(&o->inputs, &o->sysroot,
   1567                                                  o->target) != 0)
   1568     return 1;
   1569   if (driver_link_inputs_append_sysroot_framework_dirs(&o->inputs, &o->sysroot,
   1570                                                        o->target) != 0)
   1571     return 1;
   1572   if (o->shared && !o->pic_explicit) o->target.pic = KIT_PIC_PIC;
   1573 
   1574   {
   1575     uint32_t total_sources = o->nsource_files + o->nsource_memory;
   1576     uint32_t total_link = o->inputs.nobject_files + o->inputs.narchives +
   1577                           o->inputs.ndsos + o->inputs.npending_libs +
   1578                           o->inputs.npending_frameworks;
   1579 
   1580     if (total_sources == 0 && total_link == 0) {
   1581       driver_errf(CC_TOOL, "no input files");
   1582       cc_usage();
   1583       return 1;
   1584     }
   1585     if ((o->compile_only && o->preprocess_only) ||
   1586         (o->emit_asm_source && o->preprocess_only) ||
   1587         (o->syntax_only &&
   1588          (o->compile_only || o->preprocess_only || o->emit_asm_source))) {
   1589       driver_errf(CC_TOOL,
   1590                   "-c, -S, -E, and -fsyntax-only are mutually exclusive");
   1591       return 1;
   1592     }
   1593     if (o->shared && (o->compile_only || o->emit_asm_source ||
   1594                       o->preprocess_only || o->syntax_only)) {
   1595       driver_errf(CC_TOOL,
   1596                   "-shared is incompatible with -c/-S/-E/-fsyntax-only");
   1597       return 1;
   1598     }
   1599     if (o->shared && o->lto) {
   1600       driver_errf(CC_TOOL,
   1601                   "-shared -flto is not supported yet "
   1602                   "(shared-library LTO output is not exercised)");
   1603       return 1;
   1604     }
   1605     if (o->shared && o->target.obj != KIT_OBJ_ELF) {
   1606       driver_errf(CC_TOOL,
   1607                   "-shared output is supported only for ELF targets in v1");
   1608       return 1;
   1609     }
   1610     if (o->shared && o->target.pic == KIT_PIC_NONE) {
   1611       driver_errf(CC_TOOL, "-shared requires PIC input; remove -fno-pic/-static");
   1612       return 1;
   1613     }
   1614     if (o->emit_ir && o->opt_level < 1) {
   1615       driver_errf(CC_TOOL,
   1616                   "--emit=ir requires -O1 or higher "
   1617                   "(the IR tape is only recorded when the optimizer runs)");
   1618       return 1;
   1619     }
   1620     if (o->syntax_only) {
   1621       if (total_sources == 0 || total_link != 0) {
   1622         driver_errf(CC_TOOL,
   1623                     "-fsyntax-only requires source inputs and no link inputs");
   1624         return 1;
   1625       }
   1626       if (o->output_path) {
   1627         driver_errf(CC_TOOL, "-o is incompatible with -fsyntax-only");
   1628         return 1;
   1629       }
   1630       if (o->dep_mode != CC_DEP_NONE) {
   1631         driver_errf(CC_TOOL, "-M* is incompatible with -fsyntax-only");
   1632         return 1;
   1633       }
   1634     }
   1635     if (!o->shared && o->link.soname) {
   1636       driver_errf(CC_TOOL, "-Wl,-soname requires -shared");
   1637       return 1;
   1638     }
   1639     if (o->inputs.cur_group_id != 0) {
   1640       driver_errf(CC_TOOL, "missing --end-group");
   1641       return 1;
   1642     }
   1643     if (o->compile_only) {
   1644       if (total_sources == 0 || total_link != 0) {
   1645         driver_errf(CC_TOOL, "-c requires source inputs and no link inputs");
   1646         return 1;
   1647       }
   1648       if (o->output_path && total_sources > 1) {
   1649         driver_errf(CC_TOOL, "-o cannot be used with -c and multiple sources");
   1650         return 1;
   1651       }
   1652     }
   1653     if (o->preprocess_only) {
   1654       if (total_sources != 1 || total_link != 0) {
   1655         driver_errf(CC_TOOL,
   1656                     "-E requires exactly one C source and no .o/.a inputs");
   1657         return 1;
   1658       }
   1659     }
   1660     {
   1661       int dep_only = (o->dep_mode == CC_DEP_M || o->dep_mode == CC_DEP_MM);
   1662       if (o->dep_mode != CC_DEP_NONE && o->preprocess_only) {
   1663         driver_errf(CC_TOOL, "-M* is incompatible with -E");
   1664         return 1;
   1665       }
   1666       if (dep_only && (total_sources == 0 || total_link != 0)) {
   1667         driver_errf(CC_TOOL,
   1668                     "-M/-MM requires source inputs and no link inputs");
   1669         return 1;
   1670       }
   1671       if (!o->output_path && !dep_only) {
   1672         if (o->syntax_only) {
   1673           /* no output */
   1674         } else if (o->compile_only) {
   1675           if (total_sources == 1) {
   1676             o->owned_output_path =
   1677                 cc_dep_default_target(o->env, o, &o->owned_output_path_size);
   1678             if (!o->owned_output_path) {
   1679               driver_errf(CC_TOOL, "out of memory");
   1680               return 1;
   1681             }
   1682             o->output_path = o->owned_output_path;
   1683           }
   1684         } else if (o->preprocess_only) {
   1685           /* stdout */
   1686         } else {
   1687           o->output_path = driver_default_exe_name(o->target);
   1688         }
   1689       }
   1690       /* Windows PE/COFF executables conventionally carry a `.exe` suffix;
   1691        * GCC/clang on a mingw target append it to an -o output that has no
   1692        * extension. Mirror that so `kit cc -o foo` for a windows target
   1693        * produces foo.exe, matching the rest of the toolchain. Only for
   1694        * executable links (the default name already carries .exe; shared
   1695        * libraries use the .dll convention; non-link actions are skipped). */
   1696       if (o->output_path && o->target.os == KIT_OS_WINDOWS && !o->shared &&
   1697           !o->syntax_only && !o->compile_only && !o->preprocess_only &&
   1698           !dep_only) {
   1699         const char* base = o->output_path;
   1700         for (const char* q = o->output_path; *q; q++)
   1701           if (*q == '/' || *q == '\\') base = q + 1;
   1702         int has_ext = 0;
   1703         for (const char* q = base; *q; q++)
   1704           if (*q == '.') {
   1705             has_ext = 1;
   1706             break;
   1707           }
   1708         if (*base && !has_ext) {
   1709           size_t len = strlen(o->output_path);
   1710           size_t nsize = len + 5; /* ".exe" + NUL */
   1711           char* buf = driver_alloc(o->env, nsize);
   1712           if (!buf) {
   1713             driver_errf(CC_TOOL, "out of memory");
   1714             return 1;
   1715           }
   1716           memcpy(buf, o->output_path, len);
   1717           memcpy(buf + len, ".exe", 4);
   1718           buf[len + 4] = '\0';
   1719           o->owned_output_path = buf;
   1720           o->owned_output_path_size = nsize;
   1721           o->output_path = o->owned_output_path;
   1722         }
   1723       }
   1724     }
   1725   }
   1726   cc_enable_hosted_for_sysroot(o);
   1727   cc_apply_default_hosted_profile(o);
   1728   if (cc_apply_hosted_profile(o) != 0) return 1;
   1729   if (!o->syntax_only && driver_link_inputs_resolve_pending(
   1730                              &o->inputs, o->target, o->static_link) != 0)
   1731     return 1;
   1732   return 0;
   1733 }
   1734 
   1735 static const char* cc_primary_source_name(const CcOptions* o);
   1736 
   1737 /* Borrow the single source as a KitSlice. `*loaded` is set nonzero only
   1738  * when the file was opened via file_io and must be released. */
   1739 static int cc_load_single_source(const KitContext* ctx, const CcOptions* o,
   1740                                  KitSlice* in, KitFileData* fd, int* loaded) {
   1741   *loaded = 0;
   1742   if (o->nsource_memory == 1) {
   1743     *in = o->source_memory[0].bytes;
   1744     return 0;
   1745   }
   1746   if (ctx->file_io->read_all(ctx->file_io->user, o->source_files[0], fd) !=
   1747       KIT_OK) {
   1748     driver_errf(CC_TOOL, "failed to read: %.*s",
   1749                 KIT_SLICE_ARG(kit_slice_cstr(o->source_files[0])));
   1750     return 1;
   1751   }
   1752   *loaded = 1;
   1753   in->data = fd->data;
   1754   in->len = fd->size;
   1755   return 0;
   1756 }
   1757 
   1758 static KitStatus cc_compiler_new(const CcOptions* o, const KitContext* ctx,
   1759                                  KitTarget** target_out,
   1760                                  KitCompiler** compiler_out) {
   1761   KitStatus st;
   1762   if (target_out) *target_out = NULL;
   1763   if (compiler_out) *compiler_out = NULL;
   1764   if (!o || !ctx || !target_out || !compiler_out) return KIT_INVALID;
   1765   st = driver_target_new(ctx, o->target, &o->target_features, CC_TOOL,
   1766                          target_out);
   1767   if (st != KIT_OK) return st;
   1768   st = driver_compiler_new(*target_out, ctx, compiler_out);
   1769   if (st != KIT_OK) {
   1770     kit_target_free(*target_out);
   1771     *target_out = NULL;
   1772   }
   1773   return st;
   1774 }
   1775 
   1776 static void cc_close_output(KitWriter** writer, const char* output_path,
   1777                             int* rc) {
   1778   if (writer && *writer && rc && kit_writer_status(*writer) != KIT_OK) *rc = 1;
   1779   if (writer && *writer && rc && *rc != 0 && output_path)
   1780     driver_writer_abort(*writer);
   1781   if (writer && *writer) {
   1782     kit_writer_close(*writer);
   1783     *writer = NULL;
   1784   }
   1785 }
   1786 
   1787 static int cc_preprocess(DriverEnv* env, const CcOptions* o,
   1788                          const KitPreprocessOptions* pp_opts) {
   1789   KitContext ctx = driver_env_to_context(env);
   1790   KitTarget* target = NULL;
   1791   KitCompiler* compiler = NULL;
   1792   KitWriter* writer = NULL;
   1793   KitFileData fd = {0};
   1794   KitSlice input = {0};
   1795   int rc = 1;
   1796   int loaded = 0;
   1797 
   1798   if (cc_load_single_source(&ctx, o, &input, &fd, &loaded) != 0) goto out;
   1799 
   1800   if (o->output_path) {
   1801     if (ctx.file_io->open_writer(ctx.file_io->user, o->output_path, &writer) !=
   1802         KIT_OK) {
   1803       driver_errf(CC_TOOL, "failed to open output: %.*s",
   1804                   KIT_SLICE_ARG(kit_slice_cstr(o->output_path)));
   1805       goto out;
   1806     }
   1807   } else {
   1808     writer = driver_stdout_writer(env);
   1809     if (!writer) {
   1810       driver_errf(CC_TOOL, "out of memory");
   1811       goto out;
   1812     }
   1813   }
   1814 
   1815   if (cc_compiler_new(o, &ctx, &target, &compiler) != KIT_OK) {
   1816     driver_errf(CC_TOOL, "failed to initialize compiler");
   1817     goto out;
   1818   }
   1819 
   1820   rc = kit_cpp_preprocess(compiler, pp_opts,
   1821                           kit_slice_cstr(cc_primary_source_name(o)), &input,
   1822                           writer) == KIT_OK
   1823            ? 0
   1824            : 1;
   1825 
   1826 out:
   1827   if (compiler) driver_compiler_free(compiler);
   1828   kit_target_free(target);
   1829   cc_close_output(&writer, o->output_path, &rc);
   1830   if (loaded) ctx.file_io->release(ctx.file_io->user, &fd);
   1831   return rc;
   1832 }
   1833 
   1834 /* ---- header-dependency output (-M family) ---- */
   1835 
   1836 typedef struct CcDepList {
   1837   const char** items;
   1838   uint32_t n;
   1839   uint32_t cap;
   1840 } CcDepList;
   1841 
   1842 typedef struct CcDiscardWriter {
   1843   KitWriter base;
   1844   DriverEnv* env;
   1845   uint64_t pos;
   1846 } CcDiscardWriter;
   1847 
   1848 static KitStatus cc_disc_write(KitWriter* w, const void* d, size_t n) {
   1849   (void)d;
   1850   ((CcDiscardWriter*)w)->pos += (uint64_t)n;
   1851   return KIT_OK;
   1852 }
   1853 static KitStatus cc_disc_seek(KitWriter* w, uint64_t off) {
   1854   ((CcDiscardWriter*)w)->pos = off;
   1855   return KIT_OK;
   1856 }
   1857 static uint64_t cc_disc_tell(KitWriter* w) {
   1858   return ((CcDiscardWriter*)w)->pos;
   1859 }
   1860 static KitStatus cc_disc_status(KitWriter* w) {
   1861   (void)w;
   1862   return KIT_OK;
   1863 }
   1864 static void cc_disc_close(KitWriter* w) {
   1865   CcDiscardWriter* dw = (CcDiscardWriter*)w;
   1866   driver_free(dw->env, dw, sizeof(*dw));
   1867 }
   1868 
   1869 static KitWriter* cc_discard_writer_new(DriverEnv* env) {
   1870   CcDiscardWriter* dw = (CcDiscardWriter*)driver_alloc_zeroed(env, sizeof(*dw));
   1871   if (!dw) return NULL;
   1872   dw->base.write = cc_disc_write;
   1873   dw->base.seek = cc_disc_seek;
   1874   dw->base.tell = cc_disc_tell;
   1875   dw->base.status = cc_disc_status;
   1876   dw->base.close = cc_disc_close;
   1877   dw->env = env;
   1878   return &dw->base;
   1879 }
   1880 
   1881 static void cc_write_str(KitWriter* w, const char* s) {
   1882   kit_writer_write(w, s, driver_strlen(s));
   1883 }
   1884 
   1885 static int cc_dep_filters_system(int mode) {
   1886   return mode == CC_DEP_MM || mode == CC_DEP_MMD;
   1887 }
   1888 
   1889 static int cc_dep_list_push(DriverEnv* env, CcDepList* l, const char* s) {
   1890   uint32_t i;
   1891   for (i = 0; i < l->n; ++i) {
   1892     if (driver_streq(l->items[i], s)) return 0;
   1893   }
   1894   if (l->n == l->cap) {
   1895     uint32_t newcap = l->cap ? l->cap * 2 : 16;
   1896     const char** ni = driver_alloc_zeroed(env, newcap * sizeof(*ni));
   1897     if (!ni) return 1;
   1898     if (l->items) {
   1899       driver_memcpy(ni, l->items, l->n * sizeof(*l->items));
   1900       driver_free(env, l->items, l->cap * sizeof(*l->items));
   1901     }
   1902     l->items = ni;
   1903     l->cap = newcap;
   1904   }
   1905   l->items[l->n++] = s;
   1906   return 0;
   1907 }
   1908 
   1909 static void cc_dep_list_free(DriverEnv* env, CcDepList* l) {
   1910   if (l->items) driver_free(env, l->items, l->cap * sizeof(*l->items));
   1911   l->items = NULL;
   1912   l->n = 0;
   1913   l->cap = 0;
   1914 }
   1915 
   1916 static char* cc_dep_default_target(DriverEnv* env, const CcOptions* o,
   1917                                    size_t* out_size) {
   1918   const char* base = o->output_path;
   1919   size_t len;
   1920   char* buf;
   1921   if (base) {
   1922     len = driver_strlen(base);
   1923     buf = driver_alloc(env, len + 1);
   1924     if (!buf) return NULL;
   1925     driver_memcpy(buf, base, len);
   1926     buf[len] = '\0';
   1927     *out_size = len + 1;
   1928     return buf;
   1929   }
   1930   if (o->nsource_memory == 1) {
   1931     const char* fallback = "<stdin>.o";
   1932     size_t flen = driver_strlen(fallback);
   1933     buf = driver_alloc(env, flen + 1);
   1934     if (!buf) return NULL;
   1935     driver_memcpy(buf, fallback, flen);
   1936     buf[flen] = '\0';
   1937     *out_size = flen + 1;
   1938     return buf;
   1939   }
   1940   {
   1941     const char* src = o->source_files[0];
   1942     size_t srclen = driver_strlen(src);
   1943     size_t dot = srclen;
   1944     size_t slash = 0;
   1945     size_t k;
   1946     for (k = srclen; k > 0; --k) {
   1947       if (src[k - 1] == '.') {
   1948         dot = k - 1;
   1949         break;
   1950       }
   1951       if (src[k - 1] == '/') break;
   1952     }
   1953     for (k = dot; k > 0; --k) {
   1954       if (src[k - 1] == '/') {
   1955         slash = k;
   1956         break;
   1957       }
   1958     }
   1959     {
   1960       const char* ext;
   1961       size_t ext_len;
   1962       size_t name_len = dot - slash;
   1963       size_t bufsz;
   1964       driver_default_obj_ext(o->target, &ext, &ext_len);
   1965       bufsz = name_len + ext_len + 1u;
   1966       buf = driver_alloc(env, bufsz);
   1967       if (!buf) return NULL;
   1968       driver_memcpy(buf, src + slash, name_len);
   1969       driver_memcpy(buf + name_len, ext, ext_len);
   1970       buf[name_len + ext_len] = '\0';
   1971       *out_size = bufsz;
   1972       return buf;
   1973     }
   1974   }
   1975 }
   1976 
   1977 static char* cc_default_obj_path_for_name(DriverEnv* env, const CcOptions* o,
   1978                                           const char* src, size_t* out_size) {
   1979   /* -S/--emit override the object suffix; otherwise the canonical
   1980    * platform object extension (Windows `.obj`, else `.o`) via the shared
   1981    * per-target helper. */
   1982   const char* ext;
   1983   size_t ext_len;
   1984   if (o && o->emit_asm_source) {
   1985     ext = ".s";
   1986     ext_len = 2u;
   1987   } else if (o && o->emit_ir) {
   1988     ext = ".ir";
   1989     ext_len = 3u;
   1990   } else {
   1991     driver_default_obj_ext(o->target, &ext, &ext_len);
   1992   }
   1993   size_t srclen = driver_strlen(src);
   1994   size_t dot = srclen;
   1995   size_t slash = 0;
   1996   size_t k;
   1997   char* buf;
   1998   for (k = srclen; k > 0; --k) {
   1999     if (src[k - 1] == '.') {
   2000       dot = k - 1;
   2001       break;
   2002     }
   2003     if (src[k - 1] == '/') break;
   2004   }
   2005   for (k = dot; k > 0; --k) {
   2006     if (src[k - 1] == '/') {
   2007       slash = k;
   2008       break;
   2009     }
   2010   }
   2011   {
   2012     size_t name_len = dot - slash;
   2013     size_t bufsz = name_len + ext_len + 1u;
   2014     buf = driver_alloc(env, bufsz);
   2015     if (!buf) return NULL;
   2016     driver_memcpy(buf, src + slash, name_len);
   2017     driver_memcpy(buf + name_len, ext, ext_len);
   2018     buf[name_len + ext_len] = '\0';
   2019     *out_size = bufsz;
   2020     return buf;
   2021   }
   2022 }
   2023 
   2024 static char* cc_dep_default_path(DriverEnv* env, const char* out_path,
   2025                                  size_t* out_size) {
   2026   size_t len = driver_strlen(out_path);
   2027   size_t dot = len;
   2028   size_t k;
   2029   for (k = len; k > 0; --k) {
   2030     if (out_path[k - 1] == '.') {
   2031       dot = k - 1;
   2032       break;
   2033     }
   2034     if (out_path[k - 1] == '/') break;
   2035   }
   2036   {
   2037     size_t bufsz = dot + 3;
   2038     char* buf = driver_alloc(env, bufsz);
   2039     if (!buf) return NULL;
   2040     driver_memcpy(buf, out_path, dot);
   2041     buf[dot] = '.';
   2042     buf[dot + 1] = 'd';
   2043     buf[dot + 2] = '\0';
   2044     *out_size = bufsz;
   2045     return buf;
   2046   }
   2047 }
   2048 
   2049 /* Collect the include-dependency edges into `list`. Returns 0 on success,
   2050  * CC_DEP_COLLECT_OOM on allocation failure (caller reports it generically), or
   2051  * CC_DEP_COLLECT_DIAGNOSED when the iterator faulted mid-scan -- in that case a
   2052  * specific diagnostic naming the offending dependency has already been emitted,
   2053  * so the caller must not overwrite it with a generic message. */
   2054 #define CC_DEP_COLLECT_OOM 1
   2055 #define CC_DEP_COLLECT_DIAGNOSED 2
   2056 
   2057 static int cc_dep_collect(DriverEnv* env, KitCompiler* compiler,
   2058                           int system_filter, CcDepList* list) {
   2059   KitDepIter* it = NULL;
   2060   KitDepEdge e = {0};
   2061   if (kit_dep_iter_new(compiler, &it) != KIT_OK) return CC_DEP_COLLECT_OOM;
   2062   for (;;) {
   2063     KitIterResult r = kit_dep_iter_next(it, &e);
   2064     if (r == KIT_ITER_ERROR) {
   2065       /* Name the most recently scanned dependency so the failure is actionable
   2066        * instead of a silent short read of the dep list. `e` is zero-initialized
   2067        * and only ever written by kit_dep_iter_next, so it is never garbage: on
   2068        * an error before any edge it is empty (-> generic message), otherwise it
   2069        * holds the last edge we saw. */
   2070       if (e.included_name.len)
   2071         driver_errf(CC_TOOL, "failed while scanning dependency: %.*s",
   2072                     KIT_SLICE_ARG(e.included_name));
   2073       else
   2074         driver_errf(CC_TOOL, "failed to scan include dependencies");
   2075       kit_dep_iter_free(it);
   2076       return CC_DEP_COLLECT_DIAGNOSED;
   2077     }
   2078     if (r != KIT_ITER_ITEM) break;
   2079     if (system_filter && e.from_system_path) continue;
   2080     if (cc_dep_list_push(env, list, e.included_name.s) != 0) {
   2081       kit_dep_iter_free(it);
   2082       return CC_DEP_COLLECT_OOM;
   2083     }
   2084   }
   2085   kit_dep_iter_free(it);
   2086   return 0;
   2087 }
   2088 
   2089 static void cc_dep_emit_rule(KitWriter* w, const char* const* targets,
   2090                              uint32_t ntargets, const char* primary_src,
   2091                              const CcDepList* deps, int phony,
   2092                              int quote_default_target) {
   2093   uint32_t i;
   2094   for (i = 0; i < ntargets; ++i) {
   2095     const char* s;
   2096     if (i) cc_write_str(w, " ");
   2097     s = targets[i];
   2098     if (!quote_default_target) {
   2099       cc_write_str(w, s);
   2100     } else {
   2101       for (; *s; ++s) {
   2102         char c = *s;
   2103         if (c == '$') {
   2104           cc_write_str(w, "$$");
   2105         } else if (c == ' ' || c == '\t' || c == '#' || c == '\\') {
   2106           kit_writer_write(w, "\\", 1);
   2107           kit_writer_write(w, &c, 1);
   2108         } else {
   2109           kit_writer_write(w, &c, 1);
   2110         }
   2111       }
   2112     }
   2113   }
   2114   cc_write_str(w, ":");
   2115   if (primary_src) {
   2116     const char* s = primary_src;
   2117     cc_write_str(w, " ");
   2118     for (; *s; ++s) {
   2119       char c = *s;
   2120       if (c == '$') {
   2121         cc_write_str(w, "$$");
   2122       } else if (c == ' ' || c == '\t' || c == '#' || c == '\\') {
   2123         kit_writer_write(w, "\\", 1);
   2124         kit_writer_write(w, &c, 1);
   2125       } else {
   2126         kit_writer_write(w, &c, 1);
   2127       }
   2128     }
   2129   }
   2130   for (i = 0; i < deps->n; ++i) {
   2131     const char* s = deps->items[i];
   2132     cc_write_str(w, " \\\n  ");
   2133     for (; *s; ++s) {
   2134       char c = *s;
   2135       if (c == '$') {
   2136         cc_write_str(w, "$$");
   2137       } else if (c == ' ' || c == '\t' || c == '#' || c == '\\') {
   2138         kit_writer_write(w, "\\", 1);
   2139         kit_writer_write(w, &c, 1);
   2140       } else {
   2141         kit_writer_write(w, &c, 1);
   2142       }
   2143     }
   2144   }
   2145   cc_write_str(w, "\n");
   2146   if (phony) {
   2147     for (i = 0; i < deps->n; ++i) {
   2148       const char* s = deps->items[i];
   2149       cc_write_str(w, "\n");
   2150       for (; *s; ++s) {
   2151         char c = *s;
   2152         if (c == '$') {
   2153           cc_write_str(w, "$$");
   2154         } else if (c == ' ' || c == '\t' || c == '#' || c == '\\') {
   2155           kit_writer_write(w, "\\", 1);
   2156           kit_writer_write(w, &c, 1);
   2157         } else {
   2158           kit_writer_write(w, &c, 1);
   2159         }
   2160       }
   2161       cc_write_str(w, ":\n");
   2162     }
   2163   }
   2164 }
   2165 
   2166 static const char* cc_primary_source_name(const CcOptions* o) {
   2167   if (o->nsource_memory == 1) return o->source_memory[0].name.s;
   2168   return o->source_files[0];
   2169 }
   2170 
   2171 static char* cc_dep_default_target_for(DriverEnv* env, const CcOptions* o,
   2172                                        const char* source_name,
   2173                                        const char* output_path,
   2174                                        size_t* out_size) {
   2175   CcOptions view = *o;
   2176   const char* one_file[1];
   2177   KitSourceInput one_memory[1];
   2178   view.output_path = output_path;
   2179   view.nsource_files = 0;
   2180   view.nsource_memory = 0;
   2181   if (driver_streq(source_name, "<stdin>")) {
   2182     memset(one_memory, 0, sizeof(one_memory));
   2183     one_memory[0].name = kit_slice_cstr(source_name);
   2184     view.source_memory = one_memory;
   2185     view.nsource_memory = 1;
   2186   } else {
   2187     one_file[0] = source_name;
   2188     view.source_files = one_file;
   2189     view.nsource_files = 1;
   2190   }
   2191   return cc_dep_default_target(env, &view, out_size);
   2192 }
   2193 
   2194 static int cc_dep_finish_to(DriverEnv* env, KitCompiler* compiler,
   2195                             const CcOptions* o, const char* source_name,
   2196                             const char* output_path, KitWriter* dep_w) {
   2197   CcDepList deps = {0};
   2198   char* owned_target = NULL;
   2199   size_t owned_target_size = 0;
   2200   const char* one_target[1];
   2201   const char* const* targets;
   2202   uint32_t ntargets;
   2203   int rc = 1;
   2204 
   2205   {
   2206     int cr = cc_dep_collect(env, compiler, cc_dep_filters_system(o->dep_mode),
   2207                             &deps);
   2208     if (cr == CC_DEP_COLLECT_OOM) {
   2209       driver_errf(CC_TOOL, "out of memory");
   2210       goto out;
   2211     }
   2212     if (cr != 0) goto out; /* CC_DEP_COLLECT_DIAGNOSED: already reported */
   2213   }
   2214 
   2215   targets = o->dep_targets;
   2216   ntargets = o->ndep_targets;
   2217   if (ntargets == 0) {
   2218     owned_target = cc_dep_default_target_for(
   2219         env, o, source_name, output_path, &owned_target_size);
   2220     if (!owned_target) {
   2221       driver_errf(CC_TOOL, "out of memory");
   2222       goto out;
   2223     }
   2224     one_target[0] = owned_target;
   2225     targets = one_target;
   2226     ntargets = 1;
   2227   }
   2228 
   2229   cc_dep_emit_rule(dep_w, targets, ntargets, source_name, &deps, o->dep_phony,
   2230                    o->ndep_targets == 0);
   2231   rc = kit_writer_status(dep_w) == KIT_OK ? 0 : 1;
   2232 
   2233 out:
   2234   if (owned_target) driver_free(env, owned_target, owned_target_size);
   2235   cc_dep_list_free(env, &deps);
   2236   return rc;
   2237 }
   2238 
   2239 static KitWriter* cc_dep_buffer_new(const KitContext* ctx) {
   2240   KitWriter* w = NULL;
   2241   if (!ctx || !ctx->heap || kit_writer_mem(ctx->heap, &w) != KIT_OK) return NULL;
   2242   return w;
   2243 }
   2244 
   2245 static int cc_dep_commit_buffer(DriverEnv* env, const KitContext* ctx,
   2246                                 KitWriter* mem, const char* path) {
   2247   KitWriter* out = NULL;
   2248   const uint8_t* bytes;
   2249   size_t len = 0;
   2250   int rc = 1;
   2251   bytes = kit_writer_mem_bytes(mem, &len);
   2252   if (path) {
   2253     if (ctx->file_io->open_writer(ctx->file_io->user, path, &out) != KIT_OK) {
   2254       driver_errf(CC_TOOL, "failed to open dep output: %.*s",
   2255                   KIT_SLICE_ARG(kit_slice_cstr(path)));
   2256       return 1;
   2257     }
   2258   } else {
   2259     out = driver_stdout_writer(env);
   2260     if (!out) {
   2261       driver_errf(CC_TOOL, "out of memory");
   2262       return 1;
   2263     }
   2264   }
   2265   if (kit_writer_write(out, bytes, len) == KIT_OK &&
   2266       kit_writer_status(out) == KIT_OK)
   2267     rc = 0;
   2268   if (rc != 0 && path) driver_writer_abort(out);
   2269   kit_writer_close(out);
   2270   return rc;
   2271 }
   2272 
   2273 static int cc_dep_finish_default_file(DriverEnv* env, const KitContext* ctx,
   2274                                       KitCompiler* compiler,
   2275                                       const CcOptions* o,
   2276                                       const char* source_name,
   2277                                       const char* output_path) {
   2278   KitWriter* mem = NULL;
   2279   char* dep_path = NULL;
   2280   size_t dep_path_size = 0;
   2281   int rc = 1;
   2282   mem = cc_dep_buffer_new(ctx);
   2283   if (!mem) {
   2284     driver_errf(CC_TOOL, "out of memory");
   2285     goto out;
   2286   }
   2287   if (cc_dep_finish_to(env, compiler, o, source_name, output_path, mem) != 0)
   2288     goto out;
   2289   dep_path = cc_dep_default_path(env, output_path, &dep_path_size);
   2290   if (!dep_path) {
   2291     driver_errf(CC_TOOL, "out of memory");
   2292     goto out;
   2293   }
   2294   rc = cc_dep_commit_buffer(env, ctx, mem, dep_path);
   2295 out:
   2296   if (dep_path) driver_free(env, dep_path, dep_path_size);
   2297   if (mem) kit_writer_close(mem);
   2298   return rc;
   2299 }
   2300 
   2301 static void cc_fill_c_opts(const CcOptions* o, KitCCompileOptions* copts) {
   2302   KitCCompileOptions zero = {0};
   2303   *copts = zero;
   2304   copts->code.opt_level = o->syntax_only ? 0 : o->opt_level;
   2305   copts->code.debug_info = o->debug_info;
   2306   copts->code.check_only = o->syntax_only ? true : false;
   2307   copts->code.default_visibility = o->default_visibility;
   2308   copts->code.emit_c_source = o->emit_c_source ? true : false;
   2309   copts->code.emit_ir = o->emit_ir ? true : false;
   2310   copts->code.emit_asm_source = o->emit_asm_source ? true : false;
   2311   copts->code.function_sections = o->function_sections ? true : false;
   2312   copts->code.data_sections = o->data_sections ? true : false;
   2313   copts->code.disabled_backend_features = o->disabled_backend_features;
   2314   copts->code.trivial_auto_var_init = (uint8_t)o->auto_var_init;
   2315   copts->code.stack_protector = (uint8_t)o->stack_protector;
   2316   copts->code.lto = o->lto ? true : false;
   2317   copts->code.epoch = o->epoch;
   2318   copts->code.path_map = o->npath_map ? o->path_map : NULL;
   2319   copts->code.npath_map = o->npath_map;
   2320   copts->diagnostics.warnings_are_errors = o->warnings_are_errors;
   2321   copts->diagnostics.max_errors = o->max_errors;
   2322 }
   2323 
   2324 static const char* cc_source_name(const CcOptions* o, int is_memory,
   2325                                   uint32_t index) {
   2326   return is_memory ? o->source_memory[index].name.s : o->source_files[index];
   2327 }
   2328 
   2329 static int cc_dep_scan_one(DriverEnv* env, const CcOptions* o,
   2330                            const KitPreprocessOptions* pp, int is_memory,
   2331                            uint32_t index, const char* output_path,
   2332                            KitWriter* rules) {
   2333   KitContext ctx = driver_env_to_context(env);
   2334   KitTarget* target = NULL;
   2335   KitCompiler* compiler = NULL;
   2336   KitWriter* discard = NULL;
   2337   KitFileData fd = {0};
   2338   KitSlice input = {0};
   2339   int loaded = 0;
   2340   int rc = 1;
   2341   const char* source_name = cc_source_name(o, is_memory, index);
   2342 
   2343   if (is_memory) {
   2344     input = o->source_memory[index].bytes;
   2345   } else {
   2346     if (ctx.file_io->read_all(ctx.file_io->user, o->source_files[index], &fd) !=
   2347         KIT_OK) {
   2348       driver_errf(CC_TOOL, "failed to read: %.*s",
   2349                   KIT_SLICE_ARG(kit_slice_cstr(o->source_files[index])));
   2350       goto out;
   2351     }
   2352     loaded = 1;
   2353     input.data = fd.data;
   2354     input.len = fd.size;
   2355   }
   2356 
   2357   discard = cc_discard_writer_new(env);
   2358   if (!discard) {
   2359     driver_errf(CC_TOOL, "out of memory");
   2360     goto out;
   2361   }
   2362 
   2363   if (cc_compiler_new(o, &ctx, &target, &compiler) != KIT_OK) {
   2364     driver_errf(CC_TOOL, "failed to initialize compiler");
   2365     goto out;
   2366   }
   2367 
   2368   if (kit_cpp_preprocess(compiler, pp, kit_slice_cstr(source_name), &input,
   2369                          discard) != KIT_OK)
   2370     goto out;
   2371 
   2372   rc = cc_dep_finish_to(env, compiler, o, source_name, output_path, rules);
   2373 
   2374 out:
   2375   if (compiler) driver_compiler_free(compiler);
   2376   kit_target_free(target);
   2377   if (discard) kit_writer_close(discard);
   2378   if (loaded) ctx.file_io->release(ctx.file_io->user, &fd);
   2379   return rc;
   2380 }
   2381 
   2382 static int cc_run_deps_only(DriverEnv* env, const CcOptions* o,
   2383                             const KitPreprocessOptions* pp) {
   2384   KitContext ctx = driver_env_to_context(env);
   2385   KitWriter* rules = NULL;
   2386   uint32_t i;
   2387   int rc = 1;
   2388   rules = cc_dep_buffer_new(&ctx);
   2389   if (!rules) {
   2390     driver_errf(CC_TOOL, "out of memory");
   2391     return 1;
   2392   }
   2393   for (i = 0; i < o->inputs.nlink_items; ++i) {
   2394     const DriverLinkItem* item = &o->inputs.link_items[i];
   2395     if (item->kind == DRIVER_LINK_SOURCE) {
   2396       if (cc_dep_scan_one(env, o, pp, 0, item->index, NULL, rules) != 0)
   2397         goto out;
   2398     } else if (item->kind == DRIVER_LINK_SOURCE_MEMORY) {
   2399       if (cc_dep_scan_one(env, o, pp, 1, item->index, NULL, rules) != 0)
   2400         goto out;
   2401     }
   2402   }
   2403   rc = cc_dep_commit_buffer(env, &ctx, rules, o->dep_file);
   2404 out:
   2405   kit_writer_close(rules);
   2406   return rc;
   2407 }
   2408 
   2409 static int cc_dep_scan_default_file(DriverEnv* env, const CcOptions* o,
   2410                                     const KitPreprocessOptions* pp,
   2411                                     int is_memory, uint32_t index,
   2412                                     const char* output_path) {
   2413   KitContext ctx = driver_env_to_context(env);
   2414   KitWriter* rules = NULL;
   2415   char* dep_path = NULL;
   2416   size_t dep_path_size = 0;
   2417   int rc = 1;
   2418   rules = cc_dep_buffer_new(&ctx);
   2419   if (!rules) {
   2420     driver_errf(CC_TOOL, "out of memory");
   2421     goto out;
   2422   }
   2423   if (cc_dep_scan_one(env, o, pp, is_memory, index, output_path, rules) != 0)
   2424     goto out;
   2425   dep_path = cc_dep_default_path(env, output_path, &dep_path_size);
   2426   if (!dep_path) {
   2427     driver_errf(CC_TOOL, "out of memory");
   2428     goto out;
   2429   }
   2430   rc = cc_dep_commit_buffer(env, &ctx, rules, dep_path);
   2431 out:
   2432   if (dep_path) driver_free(env, dep_path, dep_path_size);
   2433   if (rules) kit_writer_close(rules);
   2434   return rc;
   2435 }
   2436 
   2437 static int cc_run_link_dependencies(DriverEnv* env, const CcOptions* o,
   2438                                     const KitPreprocessOptions* pp) {
   2439   KitContext ctx = driver_env_to_context(env);
   2440   KitWriter* shared = NULL;
   2441   uint32_t i;
   2442   int rc = 1;
   2443   if (o->dep_file) {
   2444     shared = cc_dep_buffer_new(&ctx);
   2445     if (!shared) {
   2446       driver_errf(CC_TOOL, "out of memory");
   2447       return 1;
   2448     }
   2449   }
   2450   for (i = 0; i < o->inputs.nlink_items; ++i) {
   2451     const DriverLinkItem* item = &o->inputs.link_items[i];
   2452     int is_memory;
   2453     const char* source_name;
   2454     const char* obj_path;
   2455     char* owned_obj = NULL;
   2456     size_t owned_obj_size = 0;
   2457     if (item->kind != DRIVER_LINK_SOURCE &&
   2458         item->kind != DRIVER_LINK_SOURCE_MEMORY)
   2459       continue;
   2460     is_memory = item->kind == DRIVER_LINK_SOURCE_MEMORY;
   2461     source_name = cc_source_name(o, is_memory, item->index);
   2462     if (is_memory) {
   2463       obj_path = "<stdin>.o";
   2464     } else {
   2465       owned_obj =
   2466           cc_default_obj_path_for_name(env, o, source_name, &owned_obj_size);
   2467       if (!owned_obj) {
   2468         driver_errf(CC_TOOL, "out of memory");
   2469         goto out;
   2470       }
   2471       obj_path = owned_obj;
   2472     }
   2473     if (shared) {
   2474       rc = cc_dep_scan_one(env, o, pp, is_memory, item->index, obj_path,
   2475                            shared);
   2476     } else {
   2477       rc = cc_dep_scan_default_file(env, o, pp, is_memory, item->index,
   2478                                     obj_path);
   2479     }
   2480     if (owned_obj) driver_free(env, owned_obj, owned_obj_size);
   2481     if (rc != 0) goto out;
   2482   }
   2483   if (shared) rc = cc_dep_commit_buffer(env, &ctx, shared, o->dep_file);
   2484   else rc = 0;
   2485 out:
   2486   if (shared) kit_writer_close(shared);
   2487   return rc;
   2488 }
   2489 
   2490 /* Compile one source to an object builder via the shared engine. cc never
   2491  * passes frontend-specific language_options (it has no flag surface for them);
   2492  * those are the `compile` tool's domain. */
   2493 static KitStatus cc_compile_source_obj(KitCompiler* compiler, KitLanguage lang,
   2494                                        const KitCCompileOptions* copts,
   2495                                        const KitPreprocessOptions* pp,
   2496                                        KitSlice name, const KitSlice* input,
   2497                                        KitObjBuilder** out) {
   2498   return kit_build_compile_one(compiler, lang, &copts->code,
   2499                                &copts->diagnostics, pp, NULL, name, input, NULL,
   2500                                out);
   2501 }
   2502 
   2503 static KitStatus cc_compile_source_emit(KitCompiler* compiler, KitLanguage lang,
   2504                                         const KitCCompileOptions* copts,
   2505                                         const KitPreprocessOptions* pp,
   2506                                         KitSlice name, const KitSlice* input,
   2507                                         KitWriter* out) {
   2508   return kit_build_compile_one(compiler, lang, &copts->code,
   2509                                &copts->diagnostics, pp, NULL, name, input, out,
   2510                                NULL);
   2511 }
   2512 
   2513 static int cc_run_compile_one(DriverEnv* env, const CcOptions* o,
   2514                               const KitPreprocessOptions* pp, int is_memory,
   2515                               uint32_t index, const char* out_path,
   2516                               KitWriter* dep_rules) {
   2517   KitContext ctx = driver_env_to_context(env);
   2518   KitTarget* target = NULL;
   2519   KitCompiler* compiler = NULL;
   2520   KitWriter* obj_w = NULL;
   2521   KitFileData fd = {0};
   2522   KitSlice input = {0};
   2523   KitCCompileOptions copts;
   2524   int loaded = 0;
   2525   int rc = 1;
   2526   const char* source_name = cc_source_name(o, is_memory, index);
   2527 
   2528   if (is_memory) {
   2529     input = o->source_memory[index].bytes;
   2530   } else {
   2531     if (ctx.file_io->read_all(ctx.file_io->user, o->source_files[index], &fd) !=
   2532         KIT_OK) {
   2533       driver_errf(CC_TOOL, "failed to read: %.*s",
   2534                   KIT_SLICE_ARG(kit_slice_cstr(o->source_files[index])));
   2535       goto out;
   2536     }
   2537     loaded = 1;
   2538     input.data = fd.data;
   2539     input.len = fd.size;
   2540   }
   2541 
   2542   if (ctx.file_io->open_writer(ctx.file_io->user, out_path, &obj_w) != KIT_OK) {
   2543     driver_errf(CC_TOOL, "failed to open output: %.*s",
   2544                 KIT_SLICE_ARG(kit_slice_cstr(out_path)));
   2545     goto out;
   2546   }
   2547 
   2548   if (cc_compiler_new(o, &ctx, &target, &compiler) != KIT_OK) {
   2549     driver_errf(CC_TOOL, "failed to initialize compiler");
   2550     goto out;
   2551   }
   2552 
   2553   cc_fill_c_opts(o, &copts);
   2554   if (copts.code.emit_c_source) {
   2555     /* --emit=c routes the output writer to the C-source CGTarget instead of
   2556      * the object emitter. The downstream `kit_compile_*_emit` path will
   2557      * skip the object-serialize step when this is set. */
   2558     copts.code.c_source_writer = obj_w;
   2559   }
   2560   if (copts.code.emit_ir) {
   2561     /* --emit=ir routes the output writer to the semantic-IR dumper in the opt
   2562      * recorder instead of the object emitter. The downstream emit path skips
   2563      * the object-serialize step when this is set. */
   2564     copts.code.ir_dump_writer = obj_w;
   2565   }
   2566   {
   2567     KitLanguage lang = is_memory
   2568                            ? o->source_memory[index].lang
   2569                            : cc_resolve_lang(compiler, o->source_files[index],
   2570                                              o->source_langs[index]);
   2571     KitSlice in_name = kit_slice_cstr(source_name);
   2572     KitStatus st;
   2573     st = cc_compile_source_emit(compiler, lang, &copts, pp, in_name, &input,
   2574                                 obj_w);
   2575     if (st != KIT_OK) goto out;
   2576   }
   2577 
   2578   if (o->dep_mode == CC_DEP_MD || o->dep_mode == CC_DEP_MMD) {
   2579     rc = dep_rules ? cc_dep_finish_to(env, compiler, o, source_name, out_path,
   2580                                       dep_rules)
   2581                    : cc_dep_finish_default_file(env, &ctx, compiler, o,
   2582                                                 source_name, out_path);
   2583   } else {
   2584     rc = 0;
   2585   }
   2586 
   2587 out:
   2588   if (compiler) driver_compiler_free(compiler);
   2589   kit_target_free(target);
   2590   cc_close_output(&obj_w, out_path, &rc);
   2591   if (loaded) ctx.file_io->release(ctx.file_io->user, &fd);
   2592   return rc;
   2593 }
   2594 
   2595 static int cc_run_compile_objs(DriverEnv* env, const CcOptions* o,
   2596                                const KitPreprocessOptions* pp) {
   2597   KitContext ctx = driver_env_to_context(env);
   2598   KitWriter* dep_rules = NULL;
   2599   uint32_t i;
   2600   int rc = 1;
   2601   if ((o->dep_mode == CC_DEP_MD || o->dep_mode == CC_DEP_MMD) &&
   2602       o->dep_file) {
   2603     dep_rules = cc_dep_buffer_new(&ctx);
   2604     if (!dep_rules) {
   2605       driver_errf(CC_TOOL, "out of memory");
   2606       return 1;
   2607     }
   2608   }
   2609   for (i = 0; i < o->inputs.nlink_items; ++i) {
   2610     const DriverLinkItem* item = &o->inputs.link_items[i];
   2611     int is_memory;
   2612     const char* source_name;
   2613     const char* out;
   2614     char* owned_out = NULL;
   2615     size_t owned_out_size = 0;
   2616     if (item->kind != DRIVER_LINK_SOURCE &&
   2617         item->kind != DRIVER_LINK_SOURCE_MEMORY)
   2618       continue;
   2619     is_memory = item->kind == DRIVER_LINK_SOURCE_MEMORY;
   2620     source_name = cc_source_name(o, is_memory, item->index);
   2621     if (o->output_path) {
   2622       out = o->output_path;
   2623     } else if (is_memory) {
   2624       out = o->emit_asm_source ? "<stdin>.s"
   2625             : o->emit_ir       ? "<stdin>.ir"
   2626                                : "<stdin>.o";
   2627     } else {
   2628       owned_out = cc_default_obj_path_for_name(env, o, source_name,
   2629                                                &owned_out_size);
   2630       if (!owned_out) {
   2631         driver_errf(CC_TOOL, "out of memory");
   2632         goto out;
   2633       }
   2634       out = owned_out;
   2635     }
   2636     rc = cc_run_compile_one(env, o, pp, is_memory, item->index, out,
   2637                             dep_rules);
   2638     if (owned_out) driver_free(env, owned_out, owned_out_size);
   2639     if (rc != 0) goto out;
   2640   }
   2641   if (dep_rules) rc = cc_dep_commit_buffer(env, &ctx, dep_rules, o->dep_file);
   2642   else rc = 0;
   2643 out:
   2644   if (dep_rules) kit_writer_close(dep_rules);
   2645   return rc;
   2646 }
   2647 
   2648 static int cc_run_check(DriverEnv* env, const CcOptions* o,
   2649                         const KitPreprocessOptions* pp) {
   2650   KitContext ctx = driver_env_to_context(env);
   2651   const KitFileIO* io = ctx.file_io;
   2652   KitTarget* target = NULL;
   2653   KitCompiler* compiler = NULL;
   2654   DriverLoad* src_lf = NULL;
   2655   KitSlice* src_bytes = NULL;
   2656   KitCCompileOptions copts;
   2657   uint32_t i;
   2658   int rc = 1;
   2659 
   2660   if (!io || !io->read_all) {
   2661     driver_errf(CC_TOOL, "host file I/O unavailable");
   2662     return 1;
   2663   }
   2664 
   2665   if (o->nsource_files) {
   2666     src_lf = driver_alloc_zeroed(env, o->nsource_files * sizeof(*src_lf));
   2667     src_bytes = driver_alloc_zeroed(env, o->nsource_files * sizeof(*src_bytes));
   2668     if (!src_lf || !src_bytes) {
   2669       driver_errf(CC_TOOL, "out of memory");
   2670       goto out;
   2671     }
   2672   }
   2673 
   2674   for (i = 0; i < o->nsource_files; ++i) {
   2675     if (driver_load_bytes(io, CC_TOOL, o->source_files[i], &src_lf[i],
   2676                           &src_bytes[i]) != 0)
   2677       goto out;
   2678   }
   2679 
   2680   if (cc_compiler_new(o, &ctx, &target, &compiler) != KIT_OK) {
   2681     driver_errf(CC_TOOL, "failed to initialize compiler");
   2682     goto out;
   2683   }
   2684 
   2685   cc_fill_c_opts(o, &copts);
   2686   for (i = 0; i < o->nsource_files; ++i) {
   2687     KitObjBuilder* ob = NULL;
   2688     KitLanguage lang =
   2689         cc_resolve_lang(compiler, o->source_files[i], o->source_langs[i]);
   2690     KitStatus st = cc_compile_source_obj(compiler, lang, &copts, pp,
   2691                                          kit_slice_cstr(o->source_files[i]),
   2692                                          &src_bytes[i], &ob);
   2693     kit_obj_builder_free(ob);
   2694     if (st != KIT_OK) goto out;
   2695   }
   2696   for (i = 0; i < o->nsource_memory; ++i) {
   2697     KitObjBuilder* ob = NULL;
   2698     KitStatus st = cc_compile_source_obj(compiler, o->source_memory[i].lang,
   2699                                          &copts, pp, o->source_memory[i].name,
   2700                                          &o->source_memory[i].bytes, &ob);
   2701     kit_obj_builder_free(ob);
   2702     if (st != KIT_OK) goto out;
   2703   }
   2704 
   2705   rc = 0;
   2706 
   2707 out:
   2708   if (compiler) driver_compiler_free(compiler);
   2709   kit_target_free(target);
   2710   if (src_lf) {
   2711     for (i = 0; i < o->nsource_files; ++i) driver_release_bytes(io, &src_lf[i]);
   2712   }
   2713   if (src_bytes)
   2714     driver_free(env, src_bytes, o->nsource_files * sizeof(*src_bytes));
   2715   if (src_lf) driver_free(env, src_lf, o->nsource_files * sizeof(*src_lf));
   2716   return rc;
   2717 }
   2718 
   2719 /* Side-report writer for the link session: -Wl,-Map / --symbols / --cref FILE
   2720  * and --print-memory-usage. cc routes these through driver_link_flags but the
   2721  * link runs inside kit_build_link_with_lto_report, which hands back the live
   2722  * session here so the reports actually get written (rather than being silently
   2723  * dropped). Kept parallel to ld and build-exe's report writers. */
   2724 typedef struct CcLinkReports {
   2725   DriverEnv* env;
   2726   const KitFileIO* io;
   2727   const CcOptions* o;
   2728 } CcLinkReports;
   2729 
   2730 static KitStatus cc_write_link_report(const CcLinkReports* r,
   2731                                       KitLinkSession* link, const char* path,
   2732                                       int symbols) {
   2733   KitWriter* w = NULL;
   2734   KitStatus st;
   2735   if (!path) return KIT_OK;
   2736   if (r->io->open_writer(r->io->user, path, &w) != KIT_OK) {
   2737     driver_errf(CC_TOOL, "failed to open %s output: %.*s",
   2738                 symbols ? "symbols" : "map",
   2739                 KIT_SLICE_ARG(kit_slice_cstr(path)));
   2740     return KIT_IO;
   2741   }
   2742   st = symbols
   2743            ? kit_link_session_write_symbols(link, r->o->link.symbols_format, w)
   2744            : kit_link_session_write_map(link, w);
   2745   if (st == KIT_OK) st = kit_writer_status(w);
   2746   kit_writer_close(w);
   2747   if (st != KIT_OK)
   2748     driver_errf(CC_TOOL, "failed to write %s: %.*s", symbols ? "symbols" : "map",
   2749                 KIT_SLICE_ARG(kit_slice_cstr(path)));
   2750   return st;
   2751 }
   2752 
   2753 static KitStatus cc_link_write_reports(void* user, KitLinkSession* link) {
   2754   const CcLinkReports* r = (const CcLinkReports*)user;
   2755   KitStatus st;
   2756   st = cc_write_link_report(r, link, r->o->link.map_path, 0);
   2757   if (st == KIT_OK)
   2758     st = cc_write_link_report(r, link, r->o->link.symbols_path, 1);
   2759   if (st == KIT_OK && r->o->link.cref_path) {
   2760     KitWriter* w = NULL;
   2761     if (r->io->open_writer(r->io->user, r->o->link.cref_path, &w) != KIT_OK) {
   2762       driver_errf(CC_TOOL, "failed to open cref output: %.*s",
   2763                   KIT_SLICE_ARG(kit_slice_cstr(r->o->link.cref_path)));
   2764       st = KIT_IO;
   2765     } else {
   2766       st = kit_link_session_write_cref(link, w);
   2767       if (st == KIT_OK) st = kit_writer_status(w);
   2768       kit_writer_close(w);
   2769       if (st != KIT_OK)
   2770         driver_errf(CC_TOOL, "failed to write cref: %.*s",
   2771                     KIT_SLICE_ARG(kit_slice_cstr(r->o->link.cref_path)));
   2772     }
   2773   }
   2774   if (st == KIT_OK && r->o->link.print_memory_usage) {
   2775     KitWriter* w = driver_stdout_writer(r->env);
   2776     if (!w) {
   2777       driver_errf(CC_TOOL, "failed to print memory usage");
   2778       st = KIT_IO;
   2779     } else {
   2780       st = kit_link_session_write_memory_usage(link, w);
   2781       if (st == KIT_OK) st = kit_writer_status(w);
   2782       kit_writer_close(w);
   2783       if (st != KIT_OK) driver_errf(CC_TOOL, "failed to print memory usage");
   2784     }
   2785   }
   2786   return st;
   2787 }
   2788 
   2789 /* exe/shared path: compile every source via a single KitCompiler, load
   2790  * .o/.a/script inputs, and link. The link session borrows the per-source
   2791  * KitObjBuilders; this function keeps ownership and frees them after the
   2792  * session is done. */
   2793 static int cc_run_link_exe(DriverEnv* env, const CcOptions* o,
   2794                            const KitPreprocessOptions* pp) {
   2795   KitContext ctx = driver_env_to_context(env);
   2796   const KitFileIO* io = ctx.file_io;
   2797   KitTarget* target = NULL;
   2798   KitCompiler* compiler = NULL;
   2799   KitWriter* out_w = NULL;
   2800   DriverLoad* src_lf = NULL;
   2801   DriverLoad* obj_lf = NULL;
   2802   DriverLoad* arch_lf = NULL;
   2803   DriverLoad script_lf = {0};
   2804   DriverLoad* dso_lf = NULL;
   2805   KitSlice* src_bytes = NULL;
   2806   KitSlice* obj_in = NULL;
   2807   KitSlice* obj_names = NULL;
   2808   KitLinkArchiveInput* arch_in = NULL;
   2809   KitSlice* dso_in = NULL;
   2810   KitSlice* dso_names = NULL;
   2811   KitLinkInputOrder* order = NULL;
   2812   KitObjBuilder** objs = NULL;
   2813   KitBuildSource* sources = NULL;
   2814   KitBuildPendingLto pending_lto = {0};
   2815   uint32_t* source_obj_index = NULL;
   2816   uint8_t* source_order_keep = NULL;
   2817   KitLinkScript* script = NULL;
   2818   KitSlice* rpath_slices = NULL;
   2819   KitCCompileOptions copts;
   2820   KitBuildBatchOptions lto_batch;
   2821   uint32_t nsrc = o->nsource_files + o->nsource_memory;
   2822   uint32_t i;
   2823   uint32_t nobjs = 0;
   2824   uint32_t norder = 0;
   2825   int rc = 1;
   2826 
   2827   if (!io || !io->read_all || !io->open_writer) {
   2828     driver_errf(CC_TOOL, "host file I/O unavailable");
   2829     return 1;
   2830   }
   2831 
   2832   if (o->nsource_files) {
   2833     src_bytes = driver_alloc_zeroed(env, o->nsource_files * sizeof(*src_bytes));
   2834     src_lf = driver_alloc_zeroed(env, o->nsource_files * sizeof(*src_lf));
   2835     if (!src_bytes || !src_lf) {
   2836       driver_errf(CC_TOOL, "out of memory");
   2837       goto out;
   2838     }
   2839   }
   2840   if (nsrc) {
   2841     objs = driver_alloc_zeroed(env, nsrc * sizeof(*objs));
   2842     sources = driver_alloc_zeroed(env, nsrc * sizeof(*sources));
   2843     source_obj_index =
   2844         driver_alloc_zeroed(env, nsrc * sizeof(*source_obj_index));
   2845     source_order_keep =
   2846         driver_alloc_zeroed(env, nsrc * sizeof(*source_order_keep));
   2847     if (!objs || !sources || !source_obj_index || !source_order_keep) {
   2848       driver_errf(CC_TOOL, "out of memory");
   2849       goto out;
   2850     }
   2851   }
   2852   if (o->inputs.nobject_files) {
   2853     obj_lf =
   2854         driver_alloc_zeroed(env, o->inputs.nobject_files * sizeof(*obj_lf));
   2855     obj_in =
   2856         driver_alloc_zeroed(env, o->inputs.nobject_files * sizeof(*obj_in));
   2857     obj_names =
   2858         driver_alloc_zeroed(env, o->inputs.nobject_files * sizeof(*obj_names));
   2859     if (!obj_lf || !obj_in || !obj_names) {
   2860       driver_errf(CC_TOOL, "out of memory");
   2861       goto out;
   2862     }
   2863   }
   2864   if (o->inputs.narchives) {
   2865     arch_lf = driver_alloc_zeroed(env, o->inputs.narchives * sizeof(*arch_lf));
   2866     arch_in = driver_alloc_zeroed(env, o->inputs.narchives * sizeof(*arch_in));
   2867     if (!arch_lf || !arch_in) {
   2868       driver_errf(CC_TOOL, "out of memory");
   2869       goto out;
   2870     }
   2871   }
   2872   if (o->inputs.ndsos) {
   2873     dso_lf = driver_alloc_zeroed(env, o->inputs.ndsos * sizeof(*dso_lf));
   2874     dso_in = driver_alloc_zeroed(env, o->inputs.ndsos * sizeof(*dso_in));
   2875     dso_names = driver_alloc_zeroed(env, o->inputs.ndsos * sizeof(*dso_names));
   2876     if (!dso_lf || !dso_in || !dso_names) {
   2877       driver_errf(CC_TOOL, "out of memory");
   2878       goto out;
   2879     }
   2880   }
   2881   if (o->inputs.nlink_items) {
   2882     order = driver_alloc_zeroed(env, o->inputs.nlink_items * sizeof(*order));
   2883     if (!order) {
   2884       driver_errf(CC_TOOL, "out of memory");
   2885       goto out;
   2886     }
   2887   }
   2888 
   2889   for (i = 0; i < o->nsource_files; ++i) {
   2890     if (driver_load_bytes(io, CC_TOOL, o->source_files[i], &src_lf[i],
   2891                           &src_bytes[i]) != 0)
   2892       goto out;
   2893   }
   2894 
   2895   for (i = 0; i < o->inputs.nobject_files; ++i) {
   2896     if (driver_load_bytes(io, CC_TOOL, o->inputs.object_files[i], &obj_lf[i],
   2897                           &obj_in[i]) != 0)
   2898       goto out;
   2899     obj_names[i] = kit_slice_cstr(o->inputs.object_files[i]);
   2900   }
   2901   for (i = 0; i < o->inputs.narchives; ++i) {
   2902     if (driver_load_bytes(io, CC_TOOL, o->inputs.archives[i].path, &arch_lf[i],
   2903                           &arch_in[i].bytes) != 0)
   2904       goto out;
   2905     arch_in[i].name = kit_slice_cstr(o->inputs.archives[i].path);
   2906     arch_in[i].link_mode = o->inputs.archives[i].link_mode;
   2907     arch_in[i].whole_archive =
   2908         o->inputs.archives[i].whole_archive ? true : false;
   2909     arch_in[i].group_id = o->inputs.archives[i].group_id;
   2910   }
   2911   for (i = 0; i < o->inputs.ndsos; ++i) {
   2912     if (driver_load_bytes(io, CC_TOOL, o->inputs.dsos[i].path, &dso_lf[i],
   2913                           &dso_in[i]) != 0)
   2914       goto out;
   2915     dso_names[i] = kit_slice_cstr(o->inputs.dsos[i].path);
   2916   }
   2917 
   2918   if (o->link.linker_script) {
   2919     KitSlice dummy;
   2920     if (driver_load_bytes(io, CC_TOOL, o->link.linker_script, &script_lf,
   2921                           &dummy) != 0)
   2922       goto out;
   2923   }
   2924 
   2925   if (cc_compiler_new(o, &ctx, &target, &compiler) != KIT_OK) {
   2926     driver_errf(CC_TOOL, "failed to initialize compiler");
   2927     goto out;
   2928   }
   2929 
   2930   if (script_lf.loaded) {
   2931     KitSlice script_text = {.s = (const char*)script_lf.fd.data,
   2932                             .len = script_lf.fd.size};
   2933     if (kit_link_script_parse(&ctx, script_text, &script) != KIT_OK) goto out;
   2934   }
   2935 
   2936   cc_fill_c_opts(o, &copts);
   2937   memset(&lto_batch, 0, sizeof lto_batch);
   2938   lto_batch.output_kind =
   2939       o->shared ? KIT_CG_OUTPUT_SHARED : KIT_CG_OUTPUT_EXECUTABLE;
   2940   lto_batch.interposition_policy = o->shared
   2941                                        ? KIT_CG_INTERPOSITION_DEFAULT_VISIBILITY
   2942                                        : KIT_CG_INTERPOSITION_DEFAULT;
   2943   lto_batch.defer_lto_finish = 1;
   2944   for (i = 0; i < o->nsource_files; ++i) {
   2945     KitLanguage lang =
   2946         cc_resolve_lang(compiler, o->source_files[i], o->source_langs[i]);
   2947     if (lang == KIT_LANG_UNKNOWN) {
   2948       driver_errf(CC_TOOL, "cannot determine language for %.*s (use -x LANG)",
   2949                   KIT_SLICE_ARG(kit_slice_cstr(o->source_files[i])));
   2950       goto out;
   2951     }
   2952     sources[i].lang = lang;
   2953     sources[i].name = kit_slice_cstr(o->source_files[i]);
   2954     sources[i].bytes = src_bytes[i];
   2955     sources[i].pp = pp;
   2956   }
   2957   for (i = 0; i < o->nsource_memory; ++i) {
   2958     uint32_t si = o->nsource_files + i;
   2959     if (o->source_memory[i].lang == KIT_LANG_UNKNOWN) {
   2960       driver_errf(CC_TOOL, "cannot determine language for %.*s (use -x LANG)",
   2961                   KIT_SLICE_ARG(o->source_memory[i].name));
   2962       goto out;
   2963     }
   2964     sources[si].lang = o->source_memory[i].lang;
   2965     sources[si].name = o->source_memory[i].name;
   2966     sources[si].bytes = o->source_memory[i].bytes;
   2967     sources[si].pp = pp;
   2968   }
   2969   if (nsrc) {
   2970     KitBuildObjects cout;
   2971     KitStatus st;
   2972     memset(&cout, 0, sizeof cout);
   2973     cout.objs = objs;
   2974     cout.source_obj_index = source_obj_index;
   2975     cout.source_order_keep = source_order_keep;
   2976     cout.pending_lto = &pending_lto;
   2977     st = kit_build_compile(compiler, &copts.code, &copts.diagnostics, sources,
   2978                            nsrc, &lto_batch, &cout);
   2979     nobjs = cout.nobjs;
   2980     if (st != KIT_OK) goto out;
   2981   }
   2982 
   2983   if ((o->dep_mode == CC_DEP_MD || o->dep_mode == CC_DEP_MMD) &&
   2984       cc_run_link_dependencies(env, o, pp) != 0)
   2985     goto out;
   2986 
   2987   if (io->open_writer(io->user, o->output_path, &out_w) != KIT_OK) {
   2988     driver_errf(CC_TOOL, "failed to open output: %.*s",
   2989                 KIT_SLICE_ARG(kit_slice_cstr(o->output_path)));
   2990     goto out;
   2991   }
   2992 
   2993   {
   2994     KitLinkSessionOptions lopts;
   2995     KitStatus st;
   2996     if (driver_link_flags_fill_options(
   2997             &o->link, o->target, o->pie, o->shared, /*relocatable=*/0,
   2998             o->shared ? KIT_LINK_OUTPUT_SHARED : KIT_LINK_OUTPUT_EXE, script,
   2999             &lopts, &rpath_slices) != 0)
   3000       goto out;
   3001 
   3002     /* Translate the command-line link order into the engine's public
   3003      * KitLinkInputOrder list. The dead-simple fallback for
   3004      * o->inputs.nlink_items == 0 never fires (a link action always has at least
   3005      * one input), so every add flows through the ordered path. */
   3006     {
   3007       KitLinkInputs li;
   3008       norder = driver_link_inputs_build_order(&o->inputs, source_obj_index,
   3009                                               source_order_keep,
   3010                                               o->nsource_files, order);
   3011       memset(&li, 0, sizeof(li));
   3012       li.objs = objs;
   3013       li.nobjs = nobjs;
   3014       li.obj_names = obj_names;
   3015       li.obj_bytes = obj_in;
   3016       li.nobj_bytes = o->inputs.nobject_files;
   3017       li.archives = arch_in;
   3018       li.narchives = o->inputs.narchives;
   3019       li.dso_names = dso_names;
   3020       li.dso_bytes = dso_in;
   3021       li.ndsos = o->inputs.ndsos;
   3022       li.order = order;
   3023       li.norder = norder;
   3024       {
   3025         /* Honor the link side-reports (-Wl,-Map / --symbols / --cref /
   3026          * --print-memory-usage) that flowed in via driver_link_flags by
   3027          * writing them from the live session — matching ld and build-exe. */
   3028         CcLinkReports reports;
   3029         int want_reports = o->link.map_path || o->link.symbols_path ||
   3030                            o->link.cref_path || o->link.print_memory_usage;
   3031         reports.env = env;
   3032         reports.io = io;
   3033         reports.o = o;
   3034         st = kit_build_link_with_lto_report(
   3035             compiler, &lopts, &li, &pending_lto, &lto_batch, out_w,
   3036             want_reports ? cc_link_write_reports : NULL,
   3037             want_reports ? (void*)&reports : NULL);
   3038       }
   3039     }
   3040     rc = st == KIT_OK ? 0 : 1;
   3041   }
   3042 
   3043 out:
   3044   cc_close_output(&out_w, o->output_path, &rc);
   3045   if (rc == 0 && o->output_path) {
   3046     if (driver_mark_executable_output(o->output_path) != 0) {
   3047       driver_errf(CC_TOOL, "failed to set executable mode: %.*s",
   3048                   KIT_SLICE_ARG(kit_slice_cstr(o->output_path)));
   3049       rc = 1;
   3050     }
   3051   }
   3052   if (script) kit_link_script_free(&ctx, script);
   3053   kit_build_lto_abort(&pending_lto);
   3054   driver_link_flags_free_rpath_slices(&o->link, rpath_slices);
   3055   if (compiler) driver_compiler_free(compiler);
   3056   kit_target_free(target);
   3057   driver_release_bytes(io, &script_lf);
   3058   if (arch_lf) {
   3059     for (i = 0; i < o->inputs.narchives; ++i)
   3060       driver_release_bytes(io, &arch_lf[i]);
   3061   }
   3062   if (dso_lf) {
   3063     for (i = 0; i < o->inputs.ndsos; ++i) driver_release_bytes(io, &dso_lf[i]);
   3064   }
   3065   if (obj_lf) {
   3066     for (i = 0; i < o->inputs.nobject_files; ++i)
   3067       driver_release_bytes(io, &obj_lf[i]);
   3068   }
   3069   if (src_lf) {
   3070     for (i = 0; i < o->nsource_files; ++i) driver_release_bytes(io, &src_lf[i]);
   3071   }
   3072   if (arch_in)
   3073     driver_free(env, arch_in, o->inputs.narchives * sizeof(*arch_in));
   3074   if (arch_lf)
   3075     driver_free(env, arch_lf, o->inputs.narchives * sizeof(*arch_lf));
   3076   if (dso_in) driver_free(env, dso_in, o->inputs.ndsos * sizeof(*dso_in));
   3077   if (dso_names)
   3078     driver_free(env, dso_names, o->inputs.ndsos * sizeof(*dso_names));
   3079   if (dso_lf) driver_free(env, dso_lf, o->inputs.ndsos * sizeof(*dso_lf));
   3080   if (order) driver_free(env, order, o->inputs.nlink_items * sizeof(*order));
   3081   if (obj_in)
   3082     driver_free(env, obj_in, o->inputs.nobject_files * sizeof(*obj_in));
   3083   if (obj_names)
   3084     driver_free(env, obj_names, o->inputs.nobject_files * sizeof(*obj_names));
   3085   if (obj_lf)
   3086     driver_free(env, obj_lf, o->inputs.nobject_files * sizeof(*obj_lf));
   3087   if (src_lf) driver_free(env, src_lf, o->nsource_files * sizeof(*src_lf));
   3088   if (src_bytes)
   3089     driver_free(env, src_bytes, o->nsource_files * sizeof(*src_bytes));
   3090   if (objs) {
   3091     for (i = 0; i < nobjs; ++i) kit_obj_builder_free(objs[i]);
   3092     driver_free(env, objs, nsrc * sizeof(*objs));
   3093   }
   3094   if (source_order_keep)
   3095     driver_free(env, source_order_keep, nsrc * sizeof(*source_order_keep));
   3096   if (source_obj_index)
   3097     driver_free(env, source_obj_index, nsrc * sizeof(*source_obj_index));
   3098   if (sources) driver_free(env, sources, nsrc * sizeof(*sources));
   3099   return rc;
   3100 }
   3101 
   3102 static int driver_cc_main(int argc, char** argv, int force_check) {
   3103   DriverEnv env;
   3104   CcOptions co = {0};
   3105   DriverRuntimeSupport runtime = {0};
   3106   KitPreprocessOptions pp;
   3107   int rc;
   3108   int runtime_resolved = 0;
   3109   int link_action;
   3110 
   3111   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
   3112     if (force_check) {
   3113       driver_help_check();
   3114       return 0;
   3115     }
   3116     driver_help_cc();
   3117     return 0;
   3118   }
   3119 
   3120   driver_env_init(&env);
   3121   co.env = &env;
   3122   co.driver_path = argv[0];
   3123   co.syntax_only = force_check ? 1 : 0;
   3124 
   3125   if (cc_parse(argc, argv, &co) != 0) {
   3126     cc_options_release(&co);
   3127     driver_env_fini(&env);
   3128     return 2;
   3129   }
   3130   if (co.probe_kind != CC_PROBE_NONE) {
   3131     rc = cc_run_probe(&co);
   3132     cc_options_release(&co);
   3133     driver_env_fini(&env);
   3134     return rc;
   3135   }
   3136 
   3137   link_action = !co.compile_only && !co.preprocess_only && !co.syntax_only &&
   3138                 co.dep_mode != CC_DEP_M && co.dep_mode != CC_DEP_MM;
   3139   if (driver_runtime_resolve(&env, co.support_dir, co.driver_path, &runtime) ==
   3140       0) {
   3141     runtime_resolved = 1;
   3142     if ((co.nsource_files || co.nsource_memory) && !co.nostdinc) {
   3143       int add_headers;
   3144       if (co.hosted.profile_name) {
   3145         add_headers =
   3146             driver_runtime_append_freestanding_headers(&runtime, &co.cf);
   3147       } else {
   3148         add_headers = driver_runtime_add_freestanding_headers(&runtime, &co.cf);
   3149       }
   3150       if (add_headers != 0) {
   3151         driver_errf(CC_TOOL, "failed to add freestanding headers");
   3152         driver_runtime_support_fini(&env, &runtime);
   3153         cc_options_release(&co);
   3154         driver_env_fini(&env);
   3155         return 1;
   3156       }
   3157     }
   3158   } else if (co.support_dir || link_action || co.nsource_files ||
   3159              co.nsource_memory) {
   3160     driver_errf(CC_TOOL, "support dir not found");
   3161     cc_options_release(&co);
   3162     driver_env_fini(&env);
   3163     return 1;
   3164   }
   3165 
   3166   if (link_action &&
   3167       (!co.shared || driver_target_shared_uses_hosted(co.target)) &&
   3168       !co.no_stdlib && !co.no_defaultlibs) {
   3169     DriverRuntimeArchive rt_archive = {0};
   3170     if (!runtime_resolved) {
   3171       driver_errf(CC_TOOL, "support dir not found");
   3172       cc_options_release(&co);
   3173       driver_env_fini(&env);
   3174       return 1;
   3175     }
   3176     if (driver_runtime_prepare_archive(&env, CC_TOOL, &runtime, co.target,
   3177                                        co.epoch, &rt_archive) != 0) {
   3178       driver_runtime_archive_fini(&env, &rt_archive);
   3179       driver_runtime_support_fini(&env, &runtime);
   3180       cc_options_release(&co);
   3181       driver_env_fini(&env);
   3182       return 1;
   3183     }
   3184     driver_link_inputs_insert_runtime_archives(
   3185         &co.inputs, &rt_archive, co.target, co.hosted.nfinal, co.hosted.nafter);
   3186     driver_runtime_archive_fini(&env, &rt_archive);
   3187   }
   3188 
   3189   driver_cflags_fill_pp(&co.cf, &pp);
   3190 
   3191   if (co.preprocess_only) {
   3192     rc = cc_preprocess(&env, &co, &pp);
   3193   } else if (co.dep_mode == CC_DEP_M || co.dep_mode == CC_DEP_MM) {
   3194     rc = cc_run_deps_only(&env, &co, &pp);
   3195   } else if (co.syntax_only) {
   3196     rc = cc_run_check(&env, &co, &pp);
   3197   } else if (co.compile_only) {
   3198     rc = cc_run_compile_objs(&env, &co, &pp);
   3199   } else {
   3200     rc = cc_run_link_exe(&env, &co, &pp);
   3201   }
   3202 
   3203   if (rc == 0 &&
   3204       driver_diag_finish(&env, CC_TOOL, co.warnings_are_errors, co.max_errors))
   3205     rc = 1;
   3206 
   3207   cc_options_release(&co);
   3208   if (runtime_resolved) driver_runtime_support_fini(&env, &runtime);
   3209   driver_env_fini(&env);
   3210   return rc;
   3211 }
   3212 
   3213 int driver_cc(int argc, char** argv) { return driver_cc_main(argc, argv, 0); }
   3214 
   3215 int driver_check(int argc, char** argv) {
   3216   return driver_cc_main(argc, argv, 1);
   3217 }