kit

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

ld.c (116595B)


      1 #include <kit/core.h>
      2 #include <kit/link.h>
      3 #include <kit/object.h>
      4 #include <stdint.h>
      5 #include <string.h>
      6 
      7 #include "driver.h"
      8 #include "hosted.h"
      9 #include "lib_resolve.h"
     10 #include "link_flags.h"
     11 #include "link_inputs.h"
     12 #include "runtime.h"
     13 
     14 /* `kit ld` — link object/archive inputs into an executable, shared
     15  * library, or relocatable object. The driver loads each input via
     16  * env.file_io, optionally parses a `-T` linker script into the structured
     17  * form, and calls the public link APIs directly so the full link surface
     18  * (per-archive flags, group cycling, build-id, soname/rpath/exports) is
     19  * reachable without thickening the convenience KitOptions struct.
     20  *
     21  * Supported flags:
     22  *   -o out                            output path (required, exactly one)
     23  *   -e symbol                         entry symbol
     24  *   -T script.ld                      linker script (parsed, not raw)
     25  *   --support-dir DIR                 kit support root for compiler rt
     26  *   --sysroot DIR / -syslibroot DIR   hosted C runtime/sysroot root
     27  *   -L dir                            library search path (-l targets)
     28  *   -F dir                            framework search path
     29  *   -l c                              enable hosted CRT/libc expansion
     30  *   -l name                           resolves via -L (.so preferred unless
     31  *                                     -Bstatic/-static)
     32  *   -framework name                   resolves via -F / SDK framework dirs
     33  *   -static / -pie / -no-pie          target.pic
     34  *   -shared                           emit a shared library / dylib
     35  *   -r / --relocatable                emit a relocatable partial-link object
     36  *   -soname NAME                      DT_SONAME / LC_ID_DYLIB
     37  *   -rpath DIR                        DT_RPATH/DT_RUNPATH entry (repeatable)
     38  *   -rpath-link DIR                   shared-lib search at link time
     39  * (repeatable; accepted, currently advisory)
     40  *   --enable-new-dtags                rpath entries emit as DT_RUNPATH
     41  * (default)
     42  *   --disable-new-dtags               rpath entries emit as DT_RPATH
     43  *   -E / --export-dynamic             promote all defined globals to the
     44  * dynamic symbol table
     45  *   --eh-frame-hdr                    accepted for GNU ld compatibility
     46  *   --fix-cortex-a53-843419           accepted for GNU ld compatibility
     47  *   --whole-archive / --no-whole-archive
     48  *                                     positional state for following .a
     49  *   --gc-sections / --no-gc-sections  drop unreferenced sections
     50  *   -Bstatic / -Bdynamic              positional link-mode for following .a
     51  *   --as-needed / --no-as-needed      positional link-mode for following .a
     52  *   --start-group / --end-group       cyclic-resolution group of archives
     53  *   -z OPT / -zOPT                    GNU linker option compatibility
     54  *   --build-id={none|sha256|uuid|0xHEX}
     55  *   -m i386pep                        PE/COFF emulation selector (x86_64)
     56  *   --dynamicbase / --nxcompat        accepted for PE GNU ld compatibility
     57  */
     58 
     59 #define LD_TOOL "ld"
     60 
     61 typedef struct LdOptions {
     62   DriverEnv* env;
     63   const char* driver_path;
     64   size_t argv_bound;
     65 
     66   KitTargetSpec target;
     67 
     68   const char* output_path; /* -o */
     69   int output_seen;
     70   const char* script_path; /* -T */
     71   int no_default_libs; /* -nostdlib / -nodefaultlibs / --no-default-libs */
     72   int nostdlib;        /* -nostdlib: no CRT startup or default libraries */
     73   int nostartfiles;    /* -nostartfiles: caller supplies CRT startup */
     74   int pic_explicit;        /* -static / -pie / -no-pie / -shared seen */
     75   int target_explicit;     /* -target / --target seen */
     76   const char* support_dir; /* --support-dir */
     77   const char* sysroot;     /* --sysroot / KIT_SYSROOT */
     78   int pie; /* -pie was requested */
     79 
     80   /* Shared linker-flag model: entry, pe_subsystem, interp/soname/rpath, the
     81    * -Wl, comma flags (defsym/section-start/build-id/map/symbols/cref/...), and
     82    * the owned FLAG-string pool that backs them. Populated via the
     83    * driver_link_flags_record_* family and consumed by
     84    * driver_link_flags_fill_options. */
     85   DriverLinkFlags lf;
     86 
     87   /* Shared link-input model: object/archive/dso arrays, the ordered link-item
     88    * list, the -L/-F search paths, and the positional archive flag state
     89    * (cur_whole_archive/cur_link_mode/cur_group_id). ld resolves -l/-framework
     90    * eagerly into archives/dsos rather than via the pending-lib model, to
     91    * preserve GNU GROUP / positional link ordering. */
     92   DriverLinkInputSet inputs;
     93 
     94   int lib_dirs_precollected; /* first pass collected argv -L / --library-path */
     95   char** owned_paths; /* INPUT-path strings: sysroot-rewritten -L/-F dirs and
     96                        * hosted libdir strings (the FLAG strings live in lf). */
     97   size_t* owned_path_sizes;
     98   uint32_t nowned_paths;
     99   size_t cap_owned_paths; /* grows independently of argv_bound */
    100 
    101   /* Shared-library output state. */
    102   int shared;          /* -shared */
    103   int relocatable;     /* -r / --relocatable */
    104   const char** rpath_links; /* -rpath-link DIR (advisory) */
    105   uint32_t nrpath_links;
    106   int export_dynamic;    /* -E / --export-dynamic */
    107   int static_link;       /* -static: hosted libc should pick static profile */
    108   int wants_hosted_libc; /* libc request: expand crt + libc via hosted resolver */
    109   int windows_ucrt_target; /* *-windows-gnullvm: MinGW UCRT hosted profile */
    110   int explicit_crt_start; /* caller supplied crt1/Scrt1/rcrt1-style start */
    111   int has_compiler_runtime; /* caller supplied compiler builtins/runtime */
    112   DriverHostedPlan hosted;
    113   /* Sink for the hosted profile's system includes/defines required by the
    114    * shared driver_link_inputs_apply_hosted contract. ld never compiles, so
    115    * these are populated but unused (only the plan's lib dirs + crt inputs +
    116    * interp matter to the linker). */
    117   DriverCflags cf;
    118 
    119   uint8_t next_group_id; /* increments on --start-group / GROUP() */
    120 
    121   /* Opt-in: treat `/...` arguments as MSVC link.exe flags. Off by
    122    * default so legacy paths like `/usr/lib/foo.o` remain inputs. */
    123   int ms_link_driver;
    124 } LdOptions;
    125 
    126 static void ld_usage(void) {
    127   driver_errf(LD_TOOL, "%.*s",
    128               KIT_SLICE_ARG(KIT_SLICE_LIT(
    129                   "usage: kit ld -o out [options...] inputs.o|inputs.a...\n"
    130                   "       kit ld --help    for full option reference")));
    131 }
    132 
    133 void driver_help_ld(void) {
    134   driver_printf(
    135       "%.*s",
    136       KIT_SLICE_ARG(KIT_SLICE_LIT(
    137           "kit ld — link objects and archives into native-format outputs\n"
    138           "\n"
    139           "USAGE\n"
    140           "  kit ld -o OUT [options] inputs.o ... inputs.a ...\n"
    141           "  kit ld -shared -o libfoo.so [options] inputs.o ...\n"
    142           "  kit ld -r -o partial.o inputs.o ...\n"
    143           "\n"
    144           "DESCRIPTION\n"
    145           "  Loads each input directly, optionally parses a -T linker script,\n"
    146           "  and emits an executable (default), partial-link object (-r), or\n"
    147           "  ELF shared library (-shared). The full link surface is exposed:\n"
    148           "  per-archive flags,\n"
    149           "  cyclic-resolution groups, build-id, soname/rpath/exports.\n"
    150           "\n"
    151           "INPUTS AND FORMATS\n"
    152           "  Relocatable ELF, Mach-O, and COFF objects and POSIX ar archives\n"
    153           "  are accepted. An explicit -target is authoritative; otherwise\n"
    154           "  the first target-bearing input selects the output target. The\n"
    155           "  public link boundary validates raw objects, builders, every\n"
    156           "  archive member, and dynamic/import inputs before creating output.\n"
    157           "  ELF supports executable, shared, and partial-link output. Mach-O\n"
    158           "  and PE/COFF support executable and partial-link shapes; -shared\n"
    159           "  is ELF-only.\n"
    160           "\n"
    161           "OUTPUT\n"
    162           "  -o PATH                   Output path (required, exactly one)\n"
    163           "  -shared                   Emit a position-independent ELF shared\n"
    164           "                            library instead of an executable\n"
    165           "  -r, --relocatable         Emit a relocatable partial-link object\n"
    166           "\n"
    167           "ENTRY / SCRIPT\n"
    168           "  -e SYMBOL                 Entry symbol\n"
    169           "  --subsystem NAME          PE subsystem: console or windows\n"
    170           "  -T SCRIPT.ld              Use a linker script (parsed, not raw)\n"
    171           "\n"
    172           "TARGET\n"
    173           "  -static                   Non-PIC, non-PIE\n"
    174           "  -pie                      Position-independent executable\n"
    175           "  -no-pie                   Disable PIE\n"
    176           "  -target TRIPLE            Authoritative output target\n"
    177           "  --support-dir DIR         kit support root for compiler "
    178           "runtime\n"
    179           "  --sysroot DIR             hosted C runtime/sysroot root\n"
    180           "  (target is otherwise auto-detected from the first object input)\n"
    181           "  Support is resolved from the canonical executable path; a\n"
    182           "  relocated release discovers its sibling support automatically.\n"
    183           "  --support-dir remains an authoritative override. -l c engages\n"
    184           "  hosted resolution; native macOS discovers its SDK automatically.\n"
    185           "  Other cross-hosted links need --sysroot unless the caller supplies\n"
    186           "  its own startup objects and system libraries.\n"
    187           "\n"
    188           "LIBRARY RESOLUTION\n"
    189           "  -L DIR                    Add library search path\n"
    190           "  -l c                      Add hosted CRT objects and libc\n"
    191           "  -l NAME                   Resolve via -L (.so preferred unless "
    192           "static)\n"
    193           "\n"
    194           "POSITIONAL ARCHIVE STATE (apply to following .a inputs)\n"
    195           "  --whole-archive           Pull every member of following "
    196           "archives\n"
    197           "  --no-whole-archive        Reset (default behaviour)\n"
    198           "  -Bstatic                  Force static link mode\n"
    199           "  -Bdynamic                 Force dynamic link mode\n"
    200           "  --as-needed               Link only if a real reference exists\n"
    201           "  --no-as-needed            Reset to dynamic\n"
    202           "  --start-group ...\n"
    203           "    --end-group             Cyclic-resolution archive group\n"
    204           "  Outside a group, archives are searched left to right: place an\n"
    205           "  object that references a symbol before the archive defining it.\n")));
    206   driver_printf(
    207       "%.*s",
    208       KIT_SLICE_ARG(KIT_SLICE_LIT(
    209           "\n"
    210           "SHARED-LIBRARY OPTIONS (require -shared)\n"
    211           "  -soname NAME              DT_SONAME / LC_ID_DYLIB\n"
    212           "  -soname=NAME              equivalent attached form\n"
    213           "  -rpath DIR                DT_RPATH/DT_RUNPATH entry (repeatable)\n"
    214           "  -rpath=DIR                attached form\n"
    215           "  -rpath-link DIR           Link-time-only search path (advisory)\n"
    216           "  --enable-new-dtags        rpaths land in DT_RUNPATH (default)\n"
    217           "  --disable-new-dtags       rpaths land in DT_RPATH\n"
    218           "\n"
    219           "SECTION GC\n"
    220           "  --gc-sections             Drop sections unreferenced from the "
    221           "entry\n"
    222           "                            symbol, init/fini arrays, "
    223           "SHF_GNU_RETAIN,\n"
    224           "                            and __start_/__stop_ section "
    225           "references\n"
    226           "  --no-gc-sections          Disable section GC (default)\n"
    227           "  -E, --export-dynamic      Promote defined globals into dynsym\n"
    228           "                            (no-op for -shared; recorded for exe)\n"
    229           "  --eh-frame-hdr            Accepted for GNU ld compatibility\n"
    230           "  -S, --strip-debug         Omit debug info from linked output\n"
    231           "  --no-undefined            Reject unresolved symbols\n"
    232           "  --allow-undefined         Permit unresolved strong symbols\n"
    233           "  -z defs                   Same as --no-undefined\n"
    234           "  --map FILE, -Map FILE     Write a deterministic link map\n"
    235           "  --symbols FILE            Write post-link absolute symbols\n"
    236           "  --symbols-format=nm       Symbol output format (default)\n"
    237           "\n"
    238           "BUILD ID\n"
    239           "  --build-id                Same as --build-id=sha256\n"
    240           "  --build-id=MODE           none | sha256 | uuid | 0xHEX\n"
    241           "\n"
    242           "EXAMPLES\n"
    243           "  # Hosted link with a user-supplied sysroot.\n"
    244           "  SYSROOT=/replace/with/hosted-sysroot\n"
    245           "  kit ld --sysroot \"$SYSROOT\" -l c -o hello hello.o\n"
    246           "  # Archive order: main.o references symbols in libfoo.a.\n"
    247           "  kit ld -o app main.o libfoo.a\n"
    248           "  kit ld -r -o combined.o a.o b.o\n"
    249           "  kit ld -shared -soname libfoo.so.1 -o libfoo.so foo.pic.o\n"
    250           "  # Freestanding final link; link.ld must define the memory map.\n"
    251           "  kit ld -T link.ld \\\n"
    252           "    -e _start -o kernel.elf start.o\n"
    253           "\n"
    254           "GETTING HELP\n"
    255           "  -h, --help                Show this help and exit\n"
    256           "\n"
    257           "EXIT CODES\n"
    258           "  0   success           1   link error           2   bad usage\n")));
    259 }
    260 
    261 /* ---------- argv-sized scratch arrays ---------- */
    262 
    263 static int ld_alloc_arrays(LdOptions* o, int argc) {
    264   size_t bound = (size_t)argc + 32u;
    265   o->argv_bound = bound;
    266   o->owned_paths = driver_alloc_zeroed(o->env, bound * sizeof(*o->owned_paths));
    267   o->owned_path_sizes =
    268       driver_alloc_zeroed(o->env, bound * sizeof(*o->owned_path_sizes));
    269   o->cap_owned_paths = bound;
    270   o->rpath_links = driver_alloc_zeroed(o->env, bound * sizeof(*o->rpath_links));
    271   if (!o->owned_paths || !o->owned_path_sizes || !o->rpath_links) {
    272     driver_errf(LD_TOOL, "out of memory");
    273     return 1;
    274   }
    275   if (driver_link_flags_init(&o->lf, o->env, LD_TOOL, (uint32_t)bound) != 0)
    276     return 1;
    277   if (driver_link_inputs_init(&o->inputs, o->env, LD_TOOL, bound) != 0)
    278     return 1;
    279   /* Sized for the hosted profile's includes/defines only (ld parses no -D/-I);
    280    * see the cf comment in LdOptions. */
    281   if (driver_cflags_init(&o->cf, o->env,
    282                          (int)(DRIVER_HOSTED_MAX_DEFINES +
    283                                DRIVER_HOSTED_MAX_INCLUDES)) != 0) {
    284     driver_errf(LD_TOOL, "out of memory");
    285     return 1;
    286   }
    287   return 0;
    288 }
    289 
    290 /* ---------- positional archive bookkeeping ---------- */
    291 
    292 static int ld_str_prefix(const char* s, const char* prefix) {
    293   size_t n = driver_strlen(prefix);
    294   return driver_strneq(s, prefix, n);
    295 }
    296 
    297 static const char* ld_basename(const char* path) {
    298   const char* slash = strrchr(path, '/');
    299   return slash ? slash + 1 : path;
    300 }
    301 
    302 static int ld_is_compiler_runtime_archive(const char* path) {
    303   const char* base = ld_basename(path);
    304   if ((ld_str_prefix(base, "libcompiler_builtins") &&
    305        (driver_has_suffix(base, ".rlib") || driver_has_suffix(base, ".a"))) ||
    306       (ld_str_prefix(base, "libclang_rt.builtins") &&
    307        driver_has_suffix(base, ".a")) ||
    308       driver_streq(base, "libgcc.a") || driver_streq(base, "libgcc_eh.a")) {
    309     return 1;
    310   }
    311   return 0;
    312 }
    313 
    314 static int ld_is_compiler_runtime_libname(const char* name) {
    315   return driver_streq(name, "gcc") || driver_streq(name, "gcc_eh") ||
    316          driver_streq(name, "compiler_builtins") ||
    317          ld_str_prefix(name, "clang_rt.builtins");
    318 }
    319 
    320 static int ld_is_crt_start_object(const char* path) {
    321   const char* base = ld_basename(path);
    322   return driver_streq(base, "crt1.o") || driver_streq(base, "Scrt1.o") ||
    323          driver_streq(base, "rcrt1.o") || driver_streq(base, "gcrt1.o") ||
    324          driver_streq(base, "crt0.o");
    325 }
    326 
    327 static void ld_note_object_path(LdOptions* o, const char* path) {
    328   if (ld_is_crt_start_object(path)) o->explicit_crt_start = 1;
    329 }
    330 
    331 /* Fill the next free archive slot in the shared input set (without ordering)
    332  * and return its index, noting compiler-runtime archives along the way. */
    333 static uint32_t ld_add_archive_slot(LdOptions* o, const char* path, int owned,
    334                                     size_t owned_size, uint8_t whole_archive,
    335                                     uint8_t link_mode, uint8_t group_id) {
    336   DriverArchiveInput* a = &o->inputs.archives[o->inputs.narchives++];
    337   if (ld_is_compiler_runtime_archive(path)) o->has_compiler_runtime = 1;
    338   a->path = path;
    339   a->owned = owned;
    340   a->owned_size = owned_size;
    341   a->whole_archive = whole_archive;
    342   a->link_mode = link_mode;
    343   a->group_id = group_id;
    344   return o->inputs.narchives - 1u;
    345 }
    346 
    347 static uint32_t ld_add_dso_slot(LdOptions* o, const char* path, int owned,
    348                                 size_t owned_size) {
    349   DriverDsoInput* d = &o->inputs.dsos[o->inputs.ndsos++];
    350   d->path = path;
    351   d->owned = owned;
    352   d->owned_size = owned_size;
    353   return o->inputs.ndsos - 1u;
    354 }
    355 
    356 static uint32_t ld_add_object_slot(LdOptions* o, const char* path) {
    357   ld_note_object_path(o, path);
    358   o->inputs.object_files[o->inputs.nobject_files++] = path;
    359   return o->inputs.nobject_files - 1u;
    360 }
    361 
    362 static void ld_push_object(LdOptions* o, const char* path) {
    363   uint32_t idx = ld_add_object_slot(o, path);
    364   driver_link_inputs_push(&o->inputs, DRIVER_LINK_OBJECT, idx);
    365 }
    366 
    367 static void ld_push_archive(LdOptions* o, const char* path, int owned,
    368                             size_t owned_size) {
    369   uint32_t idx = ld_add_archive_slot(
    370       o, path, owned, owned_size, o->inputs.cur_whole_archive,
    371       o->inputs.cur_link_mode, o->inputs.cur_group_id);
    372   driver_link_inputs_push(&o->inputs, DRIVER_LINK_ARCHIVE, idx);
    373 }
    374 
    375 static void ld_push_dso(LdOptions* o, const char* path, int owned,
    376                         size_t owned_size) {
    377   uint32_t idx = ld_add_dso_slot(o, path, owned, owned_size);
    378   driver_link_inputs_push(&o->inputs, DRIVER_LINK_DSO, idx);
    379 }
    380 
    381 static int ld_order_capacity(LdOptions* o, uint32_t nmore) {
    382   if (o->inputs.nlink_items + nmore <= o->argv_bound) return 0;
    383   driver_errf(LD_TOOL, "too many linker inputs");
    384   return 1;
    385 }
    386 
    387 static int ld_archive_capacity(LdOptions* o) {
    388   if (o->inputs.narchives < o->argv_bound) return 0;
    389   driver_errf(LD_TOOL, "too many archive inputs");
    390   return 1;
    391 }
    392 
    393 static int ld_dso_capacity(LdOptions* o) {
    394   if (o->inputs.ndsos < o->argv_bound) return 0;
    395   driver_errf(LD_TOOL, "too many shared-object inputs");
    396   return 1;
    397 }
    398 
    399 static int ld_object_capacity(LdOptions* o) {
    400   if (o->inputs.nobject_files < o->argv_bound) return 0;
    401   driver_errf(LD_TOOL, "too many object inputs");
    402   return 1;
    403 }
    404 
    405 /* Filename ends in `.so` (with no further extension) or in `.so.N`
    406  * for some run of digits and dots. */
    407 static int driver_is_so_filename(const char* path) {
    408   size_t n = driver_strlen(path);
    409   size_t i;
    410   /* Walk from the end: trim trailing ".N" / ".N.N" sequences if any,
    411    * then check that we land on ".so". */
    412   i = n;
    413   while (i > 0) {
    414     /* Strip a trailing ".<digits>" cluster (e.g. ".1", ".26"). */
    415     size_t end = i;
    416     size_t j = i;
    417     while (j > 0) {
    418       char c = path[j - 1];
    419       if (c >= '0' && c <= '9') {
    420         --j;
    421         continue;
    422       }
    423       break;
    424     }
    425     if (j < end && j > 0 && path[j - 1] == '.') {
    426       i = j - 1;
    427       continue;
    428     }
    429     break;
    430   }
    431   if (i >= 3 && path[i - 3] == '.' && path[i - 2] == 's' && path[i - 1] == 'o')
    432     return 1;
    433   return 0;
    434 }
    435 
    436 static char* ld_join2(DriverEnv* env, const char* a, const char* b,
    437                       size_t* out_size) {
    438   size_t alen = driver_strlen(a);
    439   size_t blen = driver_strlen(b);
    440   size_t slash = (alen > 0 && a[alen - 1] != '/') ? 1u : 0u;
    441   size_t bytes = alen + slash + blen + 1u;
    442   char* out = driver_alloc(env, bytes);
    443   size_t off = 0;
    444   if (!out) return NULL;
    445   if (alen) {
    446     driver_memcpy(out + off, a, alen);
    447     off += alen;
    448   }
    449   if (slash) out[off++] = '/';
    450   if (blen) {
    451     driver_memcpy(out + off, b, blen);
    452     off += blen;
    453   }
    454   out[off] = '\0';
    455   if (out_size) *out_size = bytes;
    456   return out;
    457 }
    458 
    459 /* Ensure room for one more owned string. The owned-string pool is NOT bounded
    460  * by argc: a single -Wl token (--section-start / --defsym) mints two or three
    461  * owned cstrings, so a fixed argv+N cap would reject valid input. Double the
    462  * two parallel arrays together when full. */
    463 static int ld_grow_owned(LdOptions* o) {
    464   size_t nc;
    465   char** np;
    466   size_t* ns;
    467   if (o->nowned_paths < o->cap_owned_paths) return 0;
    468   nc = o->cap_owned_paths ? o->cap_owned_paths * 2u : 8u;
    469   if (nc <= o->cap_owned_paths) {
    470     driver_errf(LD_TOOL, "out of memory");
    471     return 1;
    472   }
    473   np = driver_alloc_zeroed(o->env, nc * sizeof(*np));
    474   ns = driver_alloc_zeroed(o->env, nc * sizeof(*ns));
    475   if (!np || !ns) {
    476     if (np) driver_free(o->env, np, nc * sizeof(*np));
    477     if (ns) driver_free(o->env, ns, nc * sizeof(*ns));
    478     driver_errf(LD_TOOL, "out of memory");
    479     return 1;
    480   }
    481   if (o->owned_paths) {
    482     driver_memcpy(np, o->owned_paths, o->nowned_paths * sizeof(*np));
    483     driver_free(o->env, o->owned_paths, o->cap_owned_paths * sizeof(*np));
    484   }
    485   if (o->owned_path_sizes) {
    486     driver_memcpy(ns, o->owned_path_sizes, o->nowned_paths * sizeof(*ns));
    487     driver_free(o->env, o->owned_path_sizes, o->cap_owned_paths * sizeof(*ns));
    488   }
    489   o->owned_paths = np;
    490   o->owned_path_sizes = ns;
    491   o->cap_owned_paths = nc;
    492   return 0;
    493 }
    494 
    495 static int ld_own_path(LdOptions* o, char* path, size_t size,
    496                        const char** out) {
    497   if (!path) {
    498     driver_errf(LD_TOOL, "out of memory");
    499     return 1;
    500   }
    501   if (ld_grow_owned(o) != 0) {
    502     driver_free(o->env, path, size);
    503     return 1;
    504   }
    505   o->owned_paths[o->nowned_paths] = path;
    506   o->owned_path_sizes[o->nowned_paths] = size;
    507   o->nowned_paths++;
    508   if (out) *out = path;
    509   return 0;
    510 }
    511 
    512 static int ld_add_lib_dir(LdOptions* o, const char* dir) {
    513   if (o->inputs.nlib_search_paths >= o->argv_bound) {
    514     driver_errf(LD_TOOL, "too many library search paths");
    515     return 1;
    516   }
    517   o->inputs.lib_search_paths[o->inputs.nlib_search_paths++] = dir;
    518   return 0;
    519 }
    520 
    521 static int ld_add_framework_dir(LdOptions* o, const char* dir) {
    522   if (o->inputs.nframework_search_paths >= o->argv_bound) {
    523     driver_errf(LD_TOOL, "too many framework search paths");
    524     return 1;
    525   }
    526   o->inputs.framework_search_paths[o->inputs.nframework_search_paths++] = dir;
    527   return 0;
    528 }
    529 
    530 static int ld_add_sysroot_libdir(LdOptions* o) {
    531   char* path;
    532   size_t size;
    533   const char* owned;
    534   if (!o->sysroot || !o->sysroot[0]) return 0;
    535   path = ld_join2(o->env, o->sysroot, "lib", &size);
    536   if (ld_own_path(o, path, size, &owned) != 0) return 1;
    537   if (ld_add_lib_dir(o, owned) != 0) return 1;
    538   path = ld_join2(o->env, o->sysroot, "usr/lib", &size);
    539   if (ld_own_path(o, path, size, &owned) != 0) return 1;
    540   if (ld_add_lib_dir(o, owned) != 0) return 1;
    541   if (o->target.os == KIT_OS_WINDOWS) {
    542     path = ld_join2(o->env, o->sysroot, "lib/windows", &size);
    543     if (ld_own_path(o, path, size, &owned) != 0) return 1;
    544     if (ld_add_lib_dir(o, owned) != 0) return 1;
    545   }
    546   return 0;
    547 }
    548 
    549 static int ld_add_sysroot_framework_dirs(LdOptions* o) {
    550   char* path;
    551   size_t size;
    552   const char* owned;
    553   if (!driver_target_uses_framework_dirs(o->target)) return 0;
    554   if (!o->sysroot || !o->sysroot[0]) return 0;
    555   path = ld_join2(o->env, o->sysroot, "System/Library/Frameworks", &size);
    556   if (ld_own_path(o, path, size, &owned) != 0) return 1;
    557   if (ld_add_framework_dir(o, owned) != 0) return 1;
    558   path =
    559       ld_join2(o->env, o->sysroot, "System/Library/PrivateFrameworks", &size);
    560   if (ld_own_path(o, path, size, &owned) != 0) return 1;
    561   if (ld_add_framework_dir(o, owned) != 0) return 1;
    562   return 0;
    563 }
    564 
    565 static int ld_sysroot_rewrite_path(LdOptions* o, const char* path,
    566                                    const char** out) {
    567   const char* tail;
    568   char* joined;
    569   size_t size;
    570   if (!path || path[0] != '=' || !o->sysroot || !o->sysroot[0]) {
    571     *out = path;
    572     return 0;
    573   }
    574   tail = path + 1;
    575   if (tail[0] == '/') tail++;
    576   joined = ld_join2(o->env, o->sysroot, tail, &size);
    577   return ld_own_path(o, joined, size, out);
    578 }
    579 
    580 static int ld_note_library_request(LdOptions* o, const char* name) {
    581   if (ld_is_compiler_runtime_libname(name)) o->has_compiler_runtime = 1;
    582   if (driver_streq(name, "c") && !o->nostdlib && !o->nostartfiles &&
    583       !o->explicit_crt_start) {
    584     o->wants_hosted_libc = 1;
    585     return 1;
    586   }
    587   if (o->windows_ucrt_target &&
    588       (driver_streq(name, "msvcrt") || driver_streq(name, "ucrt")) &&
    589       !o->nostdlib && !o->nostartfiles && !o->explicit_crt_start) {
    590     o->wants_hosted_libc = 1;
    591     return 1;
    592   }
    593   return 0;
    594 }
    595 
    596 static const char* ld_windows_runtime_alias(LdOptions* o, const char* name) {
    597   if (o->target.os != KIT_OS_WINDOWS) return NULL;
    598   if (driver_streq(name, "gcc_eh")) return "unwind";
    599   if (!driver_streq(name, "gcc")) return NULL;
    600   switch (o->target.arch) {
    601     case KIT_ARCH_X86_64:
    602       return "clang_rt.builtins-x86_64";
    603     case KIT_ARCH_ARM_64:
    604       return "clang_rt.builtins-aarch64";
    605     default:
    606       return NULL;
    607   }
    608 }
    609 
    610 static LibResolveKind ld_kind_for_exact_lib(const char* leaf) {
    611   if (driver_has_suffix(leaf, ".tbd")) return LIB_RESOLVE_KIND_TBD;
    612   if (driver_has_suffix(leaf, ".so") || driver_has_suffix(leaf, ".dylib"))
    613     return LIB_RESOLVE_KIND_SHARED;
    614   return LIB_RESOLVE_KIND_ARCHIVE;
    615 }
    616 
    617 static int ld_resolve_exact_library(LdOptions* o, const char* leaf,
    618                                     char** out_path, size_t* out_size,
    619                                     LibResolveKind* out_kind) {
    620   uint32_t i;
    621   if (!leaf || !leaf[0]) return 1;
    622   for (i = 0; i < o->inputs.nlib_search_paths; ++i) {
    623     size_t size = 0;
    624     char* cand =
    625         driver_path_join(o->env, o->inputs.lib_search_paths[i], leaf, &size);
    626     if (!cand) return 1;
    627     if (driver_path_exists(cand)) {
    628       *out_path = cand;
    629       *out_size = size;
    630       if (out_kind) *out_kind = ld_kind_for_exact_lib(leaf);
    631       return 0;
    632     }
    633     driver_free(o->env, cand, size);
    634   }
    635   if (driver_path_exists(leaf)) {
    636     size_t n = driver_strlen(leaf);
    637     char* cand = driver_alloc(o->env, n + 1u);
    638     if (!cand) return 1;
    639     driver_memcpy(cand, leaf, n + 1u);
    640     *out_path = cand;
    641     *out_size = n + 1u;
    642     if (out_kind) *out_kind = ld_kind_for_exact_lib(leaf);
    643     return 0;
    644   }
    645   return 1;
    646 }
    647 
    648 static int ld_resolve_library(LdOptions* o, const char* name,
    649                               LibResolveMode mode, LibResolveOS resolve_os,
    650                               char** out_path, size_t* out_size,
    651                               LibResolveKind* out_kind) {
    652   const char* alias;
    653   if (name && name[0] == ':')
    654     return ld_resolve_exact_library(o, name + 1, out_path, out_size, out_kind);
    655   if (driver_lib_resolve_for_os(o->env, name, mode, resolve_os,
    656                                 o->inputs.lib_search_paths,
    657                                 o->inputs.nlib_search_paths, out_path, out_size,
    658                                 out_kind) == 0)
    659     return 0;
    660   if (resolve_os != LIB_RESOLVE_OS_WINDOWS) return 1;
    661   alias = ld_windows_runtime_alias(o, name);
    662   if (!alias) return 1;
    663   return driver_lib_resolve_for_os(o->env, alias, mode, resolve_os,
    664                                    o->inputs.lib_search_paths,
    665                                    o->inputs.nlib_search_paths, out_path,
    666                                    out_size, out_kind);
    667 }
    668 
    669 static int ld_resolve_framework(LdOptions* o, const char* name,
    670                                 char** out_path, size_t* out_size) {
    671   LibResolveKind kind;
    672   return driver_framework_resolve(o->env, name, o->inputs.framework_search_paths,
    673                                   o->inputs.nframework_search_paths, out_path,
    674                                   out_size, &kind);
    675 }
    676 
    677 static int ld_push_framework(LdOptions* o, const char* name) {
    678   char* resolved;
    679   size_t resolved_size;
    680   if (!name || !name[0]) {
    681     driver_errf(LD_TOOL, "-framework requires an argument");
    682     return 1;
    683   }
    684   if (ld_resolve_framework(o, name, &resolved, &resolved_size) != 0) {
    685     driver_errf(LD_TOOL, "cannot find -framework %.*s",
    686                 KIT_SLICE_ARG(kit_slice_cstr(name)));
    687     return 1;
    688   }
    689   ld_push_dso(o, resolved, 1, resolved_size);
    690   return 0;
    691 }
    692 
    693 static int ld_tok_eq(const char* tok, size_t n, const char* lit) {
    694   size_t ln = driver_strlen(lit);
    695   return n == ln && driver_strneq(tok, lit, ln);
    696 }
    697 
    698 static int ld_tok_prefix(const char* tok, size_t n, const char* lit) {
    699   size_t ln = driver_strlen(lit);
    700   return n >= ln && driver_strneq(tok, lit, ln);
    701 }
    702 
    703 static int ld_span_to_owned_cstr(LdOptions* o, const char* s, size_t n,
    704                                  const char** out) {
    705   size_t bytes = n + 1u;
    706   char* buf;
    707   if (bytes == 0) {
    708     driver_errf(LD_TOOL, "out of memory");
    709     return 1;
    710   }
    711   buf = driver_alloc(o->env, bytes);
    712   if (!buf) {
    713     driver_errf(LD_TOOL, "out of memory");
    714     return 1;
    715   }
    716   if (n) driver_memcpy(buf, s, n);
    717   buf[n] = '\0';
    718   return ld_own_path(o, buf, bytes, out);
    719 }
    720 
    721 static int ld_set_target_triple(LdOptions* o, const char* triple) {
    722   KitTargetSpec t;
    723   uint8_t pic;
    724   if (!triple) {
    725     driver_errf(LD_TOOL, "-target requires an argument");
    726     return 1;
    727   }
    728   if (driver_target_from_triple(triple, &t) != 0) {
    729     driver_err_unknown_target(LD_TOOL, triple);
    730     return 1;
    731   }
    732   o->windows_ucrt_target = strstr(triple, "windows-gnullvm") != NULL;
    733   pic = o->target.pic;
    734   o->target = t;
    735   if (o->pic_explicit)
    736     o->target.pic = pic;
    737   else
    738     o->target.pic = driver_default_pic(o->target.obj, o->target.os);
    739   o->target_explicit = 1;
    740   return 0;
    741 }
    742 
    743 static int ld_set_gnu_emulation(LdOptions* o, const char* emu) {
    744   uint8_t pic;
    745   KitTargetSpec t;
    746   if (!emu) {
    747     driver_errf(LD_TOOL, "-m requires an argument");
    748     return 1;
    749   }
    750   if (driver_streq(emu, "i386pep")) {
    751     memset(&t, 0, sizeof t);
    752     t.arch = KIT_ARCH_X86_64;
    753     t.os = KIT_OS_WINDOWS;
    754     t.obj = KIT_OBJ_COFF;
    755     t.ptr_size = 8;
    756     t.ptr_align = 8;
    757   } else if (driver_streq(emu, "arm64pe")) {
    758     memset(&t, 0, sizeof t);
    759     t.arch = KIT_ARCH_ARM_64;
    760     t.os = KIT_OS_WINDOWS;
    761     t.obj = KIT_OBJ_COFF;
    762     t.ptr_size = 8;
    763     t.ptr_align = 8;
    764   } else {
    765     driver_errf(LD_TOOL, "unsupported GNU ld emulation: %.*s",
    766                 KIT_SLICE_ARG(kit_slice_cstr(emu)));
    767     return 1;
    768   }
    769   if (o->target_explicit) return 0;
    770   pic = o->target.pic;
    771   o->target = t;
    772   if (o->pic_explicit)
    773     o->target.pic = pic;
    774   else
    775     o->target.pic = driver_default_pic(o->target.obj, o->target.os);
    776   return 0;
    777 }
    778 
    779 static int ld_is_pe_gnu_noop(const char* tok, size_t n) {
    780   return ld_tok_eq(tok, n, "--dynamicbase") ||
    781          ld_tok_eq(tok, n, "--disable-auto-image-base") ||
    782          ld_tok_eq(tok, n, "--enable-auto-image-base") ||
    783          ld_tok_eq(tok, n, "--high-entropy-va") ||
    784          ld_tok_eq(tok, n, "--nxcompat");
    785 }
    786 
    787 static int ld_is_cc_driver_noop(const char* tok, size_t n) {
    788   return ld_tok_eq(tok, n, "-m32") || ld_tok_eq(tok, n, "-m64") ||
    789          ld_tok_eq(tok, n, "-fno-use-linker-plugin") ||
    790          ld_tok_eq(tok, n, "-nolibc") ||
    791          ld_tok_prefix(tok, n, "--unwindlib=");
    792 }
    793 
    794 static int ld_parse_wl(LdOptions* o, const char* arg) {
    795   const char* p = arg;
    796   int expect_rpath = 0;
    797   int expect_soname = 0;
    798   int expect_interp = 0;
    799   int expect_z = 0;
    800   int expect_emulation = 0;
    801   int expect_map = 0;
    802   int expect_symbols = 0;
    803   int expect_defsym = 0;
    804   int expect_section_start = 0;
    805   int expect_cref = 0;
    806   int expect_orphan = 0;
    807   int expect_framework = 0;
    808   int expect_framework_dir = 0;
    809   while (*p) {
    810     const char* tok = p;
    811     size_t n = 0;
    812     while (p[n] && p[n] != ',') ++n;
    813     p = tok + n + (tok[n] == ',' ? 1u : 0u);
    814 
    815     if (expect_rpath || expect_soname || expect_interp || expect_z ||
    816         expect_emulation || expect_map || expect_symbols || expect_defsym ||
    817         expect_section_start || expect_cref || expect_orphan ||
    818         expect_framework || expect_framework_dir) {
    819       if (expect_defsym) {
    820         if (driver_link_flags_record_defsym(&o->lf, tok, n) != 0) return 1;
    821       } else if (expect_section_start) {
    822         if (driver_link_flags_record_section_start(&o->lf, tok, n) != 0)
    823           return 1;
    824       } else if (expect_framework) {
    825         const char* owned;
    826         if (ld_span_to_owned_cstr(o, tok, n, &owned) != 0) return 1;
    827         if (ld_push_framework(o, owned) != 0) return 1;
    828       } else if (expect_framework_dir) {
    829         const char* owned;
    830         if (ld_span_to_owned_cstr(o, tok, n, &owned) != 0) return 1;
    831         if (ld_add_framework_dir(o, owned) != 0) return 1;
    832       } else if (expect_cref) {
    833         if (driver_link_flags_record_cref(&o->lf, tok, n) != 0) return 1;
    834       } else if (expect_orphan) {
    835         if (driver_link_flags_record_orphan_handling(&o->lf, tok, n) != 0)
    836           return 1;
    837       } else if (expect_rpath) {
    838         const char* owned;
    839         if (ld_span_to_owned_cstr(o, tok, n, &owned) != 0) return 1;
    840         o->lf.rpaths[o->lf.nrpaths++] = owned;
    841       } else if (expect_soname) {
    842         const char* owned;
    843         if (ld_span_to_owned_cstr(o, tok, n, &owned) != 0) return 1;
    844         o->lf.soname = owned;
    845       } else if (expect_interp) {
    846         const char* owned;
    847         if (ld_span_to_owned_cstr(o, tok, n, &owned) != 0) return 1;
    848         o->lf.interp_path = owned;
    849       } else if (expect_z) {
    850         if (driver_link_flags_record_z(&o->lf, tok, n) != 0) return 1;
    851       } else if (expect_emulation) {
    852         const char* owned;
    853         if (ld_span_to_owned_cstr(o, tok, n, &owned) != 0) return 1;
    854         if (ld_set_gnu_emulation(o, owned) != 0) return 1;
    855       } else if (expect_map) {
    856         if (driver_link_flags_record_map(&o->lf, tok, n) != 0) return 1;
    857       } else if (expect_symbols) {
    858         if (driver_link_flags_record_symbols(&o->lf, tok, n) != 0) return 1;
    859       }
    860       expect_rpath = expect_soname = expect_interp = expect_z =
    861           expect_emulation = expect_map = expect_symbols = expect_defsym =
    862               expect_section_start = expect_cref = expect_orphan =
    863                   expect_framework = expect_framework_dir = 0;
    864       continue;
    865     }
    866 
    867     if (ld_tok_eq(tok, n, "-dead_strip") ||
    868         ld_tok_eq(tok, n, "--gc-sections")) {
    869       o->lf.gc_sections = 1;
    870       continue;
    871     }
    872     if (ld_tok_eq(tok, n, "--no-gc-sections")) {
    873       o->lf.gc_sections = 0;
    874       continue;
    875     }
    876     if (ld_tok_eq(tok, n, "--no-undefined")) {
    877       o->lf.allow_undefined = 0;
    878       continue;
    879     }
    880     if (ld_tok_eq(tok, n, "--allow-undefined") ||
    881         ld_tok_eq(tok, n, "--allow-shlib-undefined")) {
    882       o->lf.allow_undefined = 1;
    883       continue;
    884     }
    885     if (ld_tok_eq(tok, n, "--map") || ld_tok_eq(tok, n, "-Map")) {
    886       expect_map = 1;
    887       continue;
    888     }
    889     if (ld_tok_prefix(tok, n, "--map=")) {
    890       if (driver_link_flags_record_map(&o->lf, tok + 6, n - 6u) != 0) return 1;
    891       continue;
    892     }
    893     if (ld_tok_prefix(tok, n, "-Map=")) {
    894       if (n == 5) {
    895         driver_errf(LD_TOOL, "-Map requires a non-empty path");
    896         return 1;
    897       }
    898       if (driver_link_flags_record_map(&o->lf, tok + 5, n - 5u) != 0) return 1;
    899       continue;
    900     }
    901     if (ld_tok_eq(tok, n, "--symbols")) {
    902       expect_symbols = 1;
    903       continue;
    904     }
    905     if (ld_tok_prefix(tok, n, "--symbols=")) {
    906       if (driver_link_flags_record_symbols(&o->lf, tok + 10, n - 10u) != 0)
    907         return 1;
    908       continue;
    909     }
    910     if (ld_tok_prefix(tok, n, "--symbols-format=")) {
    911       if (driver_link_flags_record_symbols_format(&o->lf, tok + 17, n - 17u) !=
    912           0)
    913         return 1;
    914       continue;
    915     }
    916     if (ld_tok_eq(tok, n, "-Bstatic")) {
    917       o->inputs.cur_link_mode = KIT_LM_STATIC;
    918       continue;
    919     }
    920     if (ld_tok_eq(tok, n, "-Bdynamic")) {
    921       o->inputs.cur_link_mode = KIT_LM_DYNAMIC;
    922       continue;
    923     }
    924     if (ld_tok_eq(tok, n, "--as-needed")) {
    925       o->inputs.cur_link_mode = KIT_LM_AS_NEEDED;
    926       continue;
    927     }
    928     if (ld_tok_eq(tok, n, "--no-as-needed")) {
    929       o->inputs.cur_link_mode = KIT_LM_DYNAMIC;
    930       continue;
    931     }
    932     if (ld_tok_eq(tok, n, "--whole-archive")) {
    933       o->inputs.cur_whole_archive = 1;
    934       continue;
    935     }
    936     if (ld_tok_eq(tok, n, "--no-whole-archive")) {
    937       o->inputs.cur_whole_archive = 0;
    938       continue;
    939     }
    940     if (ld_tok_eq(tok, n, "-z")) {
    941       expect_z = 1;
    942       continue;
    943     }
    944     if (n > 2 && tok[0] == '-' && tok[1] == 'z') {
    945       if (driver_link_flags_record_z(&o->lf, tok + 2, n - 2) != 0) return 1;
    946       continue;
    947     }
    948     if (ld_tok_eq(tok, n, "--eh-frame-hdr") ||
    949         ld_tok_eq(tok, n, "--fix-cortex-a53-843419"))
    950       continue;
    951     if (ld_is_cc_driver_noop(tok, n)) continue;
    952     if (ld_is_pe_gnu_noop(tok, n)) continue;
    953     if (ld_tok_eq(tok, n, "-m")) {
    954       expect_emulation = 1;
    955       continue;
    956     }
    957     if (ld_tok_eq(tok, n, "-nostdlib")) {
    958       o->no_default_libs = 1;
    959       o->nostdlib = 1;
    960       o->nostartfiles = 1;
    961       continue;
    962     }
    963     if (ld_tok_eq(tok, n, "-nostartfiles")) {
    964       o->nostartfiles = 1;
    965       continue;
    966     }
    967     if (ld_tok_eq(tok, n, "-nodefaultlibs") ||
    968         ld_tok_eq(tok, n, "--no-default-libs")) {
    969       o->no_default_libs = 1;
    970       continue;
    971     }
    972     if (ld_tok_eq(tok, n, "-S") || ld_tok_eq(tok, n, "--strip-debug")) {
    973       o->lf.strip_debug = 1;
    974       continue;
    975     }
    976     if (ld_tok_eq(tok, n, "-E") || ld_tok_eq(tok, n, "--export-dynamic")) {
    977       o->export_dynamic = 1;
    978       continue;
    979     }
    980     if (ld_tok_eq(tok, n, "--enable-new-dtags")) {
    981       o->lf.new_dtags = 1;
    982       continue;
    983     }
    984     if (ld_tok_eq(tok, n, "--disable-new-dtags")) {
    985       o->lf.new_dtags = 0;
    986       continue;
    987     }
    988     if (ld_tok_eq(tok, n, "-rpath")) {
    989       expect_rpath = 1;
    990       continue;
    991     }
    992     if (ld_tok_eq(tok, n, "-framework")) {
    993       expect_framework = 1;
    994       continue;
    995     }
    996     if (ld_tok_eq(tok, n, "-F")) {
    997       expect_framework_dir = 1;
    998       continue;
    999     }
   1000     if (n > 2 && tok[0] == '-' && tok[1] == 'F') {
   1001       const char* owned;
   1002       if (ld_span_to_owned_cstr(o, tok + 2, n - 2u, &owned) != 0) return 1;
   1003       if (ld_add_framework_dir(o, owned) != 0) return 1;
   1004       continue;
   1005     }
   1006     if (ld_tok_prefix(tok, n, "-rpath=")) {
   1007       const char* owned;
   1008       if (ld_span_to_owned_cstr(o, tok + 7, n - 7u, &owned) != 0) return 1;
   1009       o->lf.rpaths[o->lf.nrpaths++] = owned;
   1010       continue;
   1011     }
   1012     if (ld_tok_eq(tok, n, "-soname")) {
   1013       expect_soname = 1;
   1014       continue;
   1015     }
   1016     if (ld_tok_prefix(tok, n, "-soname=")) {
   1017       const char* owned;
   1018       if (ld_span_to_owned_cstr(o, tok + 8, n - 8u, &owned) != 0) return 1;
   1019       o->lf.soname = owned;
   1020       continue;
   1021     }
   1022     if (ld_tok_eq(tok, n, "-dynamic-linker") ||
   1023         ld_tok_eq(tok, n, "--dynamic-linker")) {
   1024       expect_interp = 1;
   1025       continue;
   1026     }
   1027     if (ld_tok_prefix(tok, n, "-dynamic-linker=")) {
   1028       const char* owned;
   1029       if (ld_span_to_owned_cstr(o, tok + 16, n - 16u, &owned) != 0) return 1;
   1030       o->lf.interp_path = owned;
   1031       continue;
   1032     }
   1033     /* -Tdata / -Tbss section base overrides. */
   1034     if (ld_tok_eq(tok, n, "-Tdata") || ld_tok_eq(tok, n, "-Tbss")) {
   1035       const char* secname = ld_tok_eq(tok, n, "-Tdata") ? ".data" : ".bss";
   1036       const char* atok = p;
   1037       size_t an = 0;
   1038       const char* addr_owned;
   1039       if (!*atok) {
   1040         driver_errf(LD_TOOL, "%.*s requires an address", (int)n, tok);
   1041         return 1;
   1042       }
   1043       while (atok[an] && atok[an] != ',') ++an;
   1044       p = atok + an + (atok[an] == ',' ? 1u : 0u);
   1045       if (ld_span_to_owned_cstr(o, atok, an, &addr_owned) != 0) return 1;
   1046       if (driver_link_flags_record_section_addr(&o->lf, secname, addr_owned) !=
   1047           0)
   1048         return 1;
   1049       continue;
   1050     }
   1051     if (ld_tok_prefix(tok, n, "-Tdata=")) {
   1052       const char* addr_owned;
   1053       if (ld_span_to_owned_cstr(o, tok + 7, n - 7u, &addr_owned) != 0) return 1;
   1054       if (driver_link_flags_record_section_addr(&o->lf, ".data", addr_owned) !=
   1055           0)
   1056         return 1;
   1057       continue;
   1058     }
   1059     if (ld_tok_prefix(tok, n, "-Tbss=")) {
   1060       const char* addr_owned;
   1061       if (ld_span_to_owned_cstr(o, tok + 6, n - 6u, &addr_owned) != 0) return 1;
   1062       if (driver_link_flags_record_section_addr(&o->lf, ".bss", addr_owned) !=
   1063           0)
   1064         return 1;
   1065       continue;
   1066     }
   1067     if (ld_tok_prefix(tok, n, "--defsym=")) {
   1068       if (driver_link_flags_record_defsym(&o->lf, tok + 9, n - 9u) != 0)
   1069         return 1;
   1070       continue;
   1071     }
   1072     if (ld_tok_eq(tok, n, "--defsym")) {
   1073       expect_defsym = 1;
   1074       continue;
   1075     }
   1076     if (ld_tok_prefix(tok, n, "--section-start=")) {
   1077       if (driver_link_flags_record_section_start(&o->lf, tok + 16, n - 16u) != 0)
   1078         return 1;
   1079       continue;
   1080     }
   1081     if (ld_tok_eq(tok, n, "--section-start")) {
   1082       expect_section_start = 1;
   1083       continue;
   1084     }
   1085     if (ld_tok_prefix(tok, n, "--cref=")) {
   1086       if (driver_link_flags_record_cref(&o->lf, tok + 7, n - 7u) != 0) return 1;
   1087       continue;
   1088     }
   1089     if (ld_tok_eq(tok, n, "--cref")) {
   1090       expect_cref = 1;
   1091       continue;
   1092     }
   1093     if (ld_tok_prefix(tok, n, "--orphan-handling=")) {
   1094       if (driver_link_flags_record_orphan_handling(&o->lf, tok + 18, n - 18u) !=
   1095           0)
   1096         return 1;
   1097       continue;
   1098     }
   1099     if (ld_tok_eq(tok, n, "--orphan-handling")) {
   1100       expect_orphan = 1;
   1101       continue;
   1102     }
   1103     if (ld_tok_eq(tok, n, "--fatal-warnings")) {
   1104       o->lf.fatal_warnings = 1;
   1105       continue;
   1106     }
   1107     if (ld_tok_eq(tok, n, "--no-fatal-warnings")) {
   1108       o->lf.fatal_warnings = 0;
   1109       continue;
   1110     }
   1111     if (ld_tok_eq(tok, n, "--print-memory-usage")) {
   1112       o->lf.print_memory_usage = 1;
   1113       continue;
   1114     }
   1115 
   1116     driver_errf(LD_TOOL, "unsupported -Wl, token: %.*s", (int)n, tok);
   1117     return 1;
   1118   }
   1119   if (expect_rpath || expect_soname || expect_interp || expect_z ||
   1120       expect_emulation || expect_map || expect_symbols || expect_defsym ||
   1121       expect_section_start || expect_cref || expect_orphan ||
   1122       expect_framework || expect_framework_dir) {
   1123     driver_errf(LD_TOOL, "-Wl option requires another comma argument");
   1124     return 1;
   1125   }
   1126   return 0;
   1127 }
   1128 
   1129 /* ---------- GNU input-script expansion (.so text scripts) ---------- */
   1130 
   1131 typedef struct LdInputScript {
   1132   LdOptions* o;
   1133   const char* script_path;
   1134   const char* script_dir;
   1135   const char* p;
   1136   const char* end;
   1137   uint32_t order_pos;
   1138   uint32_t insert_pos;
   1139   uint8_t saw_input_directive;
   1140   uint8_t added;
   1141 } LdInputScript;
   1142 
   1143 static int ld_is_space_char(char c) {
   1144   return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' ||
   1145          c == '\v';
   1146 }
   1147 
   1148 static int ld_is_ident_char(char c) {
   1149   return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
   1150          (c >= '0' && c <= '9') || c == '_';
   1151 }
   1152 
   1153 static char ld_ascii_upper(char c) {
   1154   return (c >= 'a' && c <= 'z') ? (char)(c - 'a' + 'A') : c;
   1155 }
   1156 
   1157 static int ld_span_eq_ci(const char* s, size_t n, const char* lit) {
   1158   size_t ln = driver_strlen(lit);
   1159   size_t i;
   1160   if (n != ln) return 0;
   1161   for (i = 0; i < n; ++i)
   1162     if (ld_ascii_upper(s[i]) != ld_ascii_upper(lit[i])) return 0;
   1163   return 1;
   1164 }
   1165 
   1166 static void ld_script_skip_ws(LdInputScript* s) {
   1167   for (;;) {
   1168     while (s->p < s->end && ld_is_space_char(*s->p)) ++s->p;
   1169     if (s->p + 1 < s->end && s->p[0] == '/' && s->p[1] == '*') {
   1170       s->p += 2;
   1171       while (s->p + 1 < s->end && !(s->p[0] == '*' && s->p[1] == '/')) ++s->p;
   1172       if (s->p + 1 < s->end) s->p += 2;
   1173       continue;
   1174     }
   1175     if (s->p < s->end && *s->p == '#') {
   1176       while (s->p < s->end && *s->p != '\n') ++s->p;
   1177       continue;
   1178     }
   1179     if (s->p < s->end && (*s->p == ';' || *s->p == ',')) {
   1180       ++s->p;
   1181       continue;
   1182     }
   1183     break;
   1184   }
   1185 }
   1186 
   1187 static int ld_script_try_kw(LdInputScript* s, const char* kw) {
   1188   const char* p;
   1189   size_t n = driver_strlen(kw);
   1190   ld_script_skip_ws(s);
   1191   if ((size_t)(s->end - s->p) < n) return 0;
   1192   if (!ld_span_eq_ci(s->p, n, kw)) return 0;
   1193   p = s->p + n;
   1194   if (p < s->end && ld_is_ident_char(*p)) return 0;
   1195   s->p = p;
   1196   return 1;
   1197 }
   1198 
   1199 static void ld_script_skip_balanced(LdInputScript* s) {
   1200   int depth = 0;
   1201   if (s->p >= s->end || *s->p != '(') return;
   1202   while (s->p < s->end) {
   1203     char c = *s->p++;
   1204     if (c == '"' || c == '\'') {
   1205       char q = c;
   1206       while (s->p < s->end) {
   1207         c = *s->p++;
   1208         if (c == '\\' && s->p < s->end) {
   1209           ++s->p;
   1210           continue;
   1211         }
   1212         if (c == q) break;
   1213       }
   1214       continue;
   1215     }
   1216     if (c == '(') {
   1217       ++depth;
   1218     } else if (c == ')') {
   1219       --depth;
   1220       if (depth == 0) return;
   1221     }
   1222   }
   1223 }
   1224 
   1225 static int ld_script_next_token(LdInputScript* s, const char** out,
   1226                                 size_t* out_len) {
   1227   const char* start;
   1228   char quote;
   1229   ld_script_skip_ws(s);
   1230   if (s->p >= s->end || *s->p == ')') return 0;
   1231   if (*s->p == '(') return 0;
   1232   quote = *s->p;
   1233   if (quote == '"' || quote == '\'') {
   1234     ++s->p;
   1235     start = s->p;
   1236     while (s->p < s->end && *s->p != quote) {
   1237       if (*s->p == '\\' && s->p + 1 < s->end)
   1238         s->p += 2;
   1239       else
   1240         ++s->p;
   1241     }
   1242     *out = start;
   1243     *out_len = (size_t)(s->p - start);
   1244     if (s->p < s->end) ++s->p;
   1245     return 1;
   1246   }
   1247   start = s->p;
   1248   while (s->p < s->end && !ld_is_space_char(*s->p) && *s->p != '(' &&
   1249          *s->p != ')' && *s->p != ';' && *s->p != ',')
   1250     ++s->p;
   1251   if (s->p == start) {
   1252     ++s->p;
   1253     return 0;
   1254   }
   1255   *out = start;
   1256   *out_len = (size_t)(s->p - start);
   1257   return 1;
   1258 }
   1259 
   1260 static int ld_path_has_slash(const char* s) {
   1261   while (*s) {
   1262     if (*s == '/' || *s == '\\') return 1;
   1263     ++s;
   1264   }
   1265   return 0;
   1266 }
   1267 
   1268 static int ld_script_dirname(LdOptions* o, const char* path, const char** out) {
   1269   const char* slash = strrchr(path, '/');
   1270   size_t n;
   1271   if (!slash) {
   1272     *out = "";
   1273     return 0;
   1274   }
   1275   n = (size_t)(slash - path);
   1276   if (n == 0) n = 1; /* root directory */
   1277   return ld_span_to_owned_cstr(o, path, n, out);
   1278 }
   1279 
   1280 static int ld_script_resolve_file(LdInputScript* s, const char* name,
   1281                                   const char** out_path, int* out_owned,
   1282                                   size_t* out_size) {
   1283   LdOptions* o = s->o;
   1284   const char* rewritten;
   1285   uint32_t i;
   1286   *out_path = NULL;
   1287   *out_owned = 0;
   1288   *out_size = 0;
   1289 
   1290   if (name[0] == '/' || name[0] == '=' || ld_path_has_slash(name)) {
   1291     if (ld_sysroot_rewrite_path(o, name, &rewritten) != 0) return 1;
   1292     if (!driver_path_exists(rewritten)) {
   1293       driver_errf(LD_TOOL, "%.*s: linker script input not found: %.*s",
   1294                   KIT_SLICE_ARG(kit_slice_cstr(s->script_path)),
   1295                   KIT_SLICE_ARG(kit_slice_cstr(rewritten)));
   1296       return 1;
   1297     }
   1298     *out_path = rewritten;
   1299     return 0;
   1300   }
   1301 
   1302   if (s->script_dir && s->script_dir[0]) {
   1303     size_t size = 0;
   1304     char* cand = driver_path_join(o->env, s->script_dir, name, &size);
   1305     if (!cand) {
   1306       driver_errf(LD_TOOL, "out of memory");
   1307       return 1;
   1308     }
   1309     if (driver_path_exists(cand)) {
   1310       *out_path = cand;
   1311       *out_owned = 1;
   1312       *out_size = size;
   1313       return 0;
   1314     }
   1315     driver_free(o->env, cand, size);
   1316   }
   1317 
   1318   for (i = 0; i < o->inputs.nlib_search_paths; ++i) {
   1319     size_t size = 0;
   1320     char* cand =
   1321         driver_path_join(o->env, o->inputs.lib_search_paths[i], name, &size);
   1322     if (!cand) {
   1323       driver_errf(LD_TOOL, "out of memory");
   1324       return 1;
   1325     }
   1326     if (driver_path_exists(cand)) {
   1327       *out_path = cand;
   1328       *out_owned = 1;
   1329       *out_size = size;
   1330       return 0;
   1331     }
   1332     driver_free(o->env, cand, size);
   1333   }
   1334 
   1335   if (driver_path_exists(name)) {
   1336     *out_path = name;
   1337     return 0;
   1338   }
   1339 
   1340   driver_errf(LD_TOOL, "%.*s: linker script input not found: %.*s",
   1341               KIT_SLICE_ARG(kit_slice_cstr(s->script_path)),
   1342               KIT_SLICE_ARG(kit_slice_cstr(name)));
   1343   return 1;
   1344 }
   1345 
   1346 /* The GNU INPUT()/GROUP() expander replaces the DSO-script link item at
   1347  * `order_pos` with the script's first input, then inserts the rest just after
   1348  * it — preserving the script's position in the overall link order. */
   1349 static int ld_script_add_order(LdInputScript* s, uint8_t kind, uint32_t index) {
   1350   if (!s->added) {
   1351     s->o->inputs.link_items[s->order_pos].kind = kind;
   1352     s->o->inputs.link_items[s->order_pos].index = index;
   1353     s->insert_pos = s->order_pos + 1u;
   1354     s->added = 1;
   1355     return 0;
   1356   }
   1357   if (ld_order_capacity(s->o, 1) != 0) return 1;
   1358   driver_link_inputs_insert(&s->o->inputs, s->insert_pos, kind, index);
   1359   s->insert_pos++;
   1360   return 0;
   1361 }
   1362 
   1363 static int ld_script_add_path(LdInputScript* s, const char* path, int owned,
   1364                               size_t owned_size, int as_needed,
   1365                               uint8_t group_id) {
   1366   LdOptions* o = s->o;
   1367   if (driver_has_suffix(path, ".a") || driver_has_suffix(path, ".rlib")) {
   1368     uint32_t idx;
   1369     if (ld_archive_capacity(o) != 0) goto fail;
   1370     idx = ld_add_archive_slot(
   1371         o, path, owned, owned_size, o->inputs.cur_whole_archive,
   1372         as_needed ? KIT_LM_AS_NEEDED : o->inputs.cur_link_mode, group_id);
   1373     return ld_script_add_order(s, DRIVER_LINK_ARCHIVE, idx);
   1374   }
   1375   if (driver_is_so_filename(path) || driver_has_suffix(path, ".dylib") ||
   1376       driver_has_suffix(path, ".tbd")) {
   1377     uint32_t idx;
   1378     if (as_needed) goto skip;
   1379     if (ld_dso_capacity(o) != 0) goto fail;
   1380     idx = ld_add_dso_slot(o, path, owned, owned_size);
   1381     return ld_script_add_order(s, DRIVER_LINK_DSO, idx);
   1382   }
   1383   {
   1384     uint32_t idx;
   1385     const char* keep = path;
   1386     if (owned) {
   1387       if (ld_own_path(o, (char*)path, owned_size, &keep) != 0) return 1;
   1388       owned = 0;
   1389       owned_size = 0;
   1390     }
   1391     if (ld_object_capacity(o) != 0) return 1;
   1392     idx = ld_add_object_slot(o, keep);
   1393     return ld_script_add_order(s, DRIVER_LINK_OBJECT, idx);
   1394   }
   1395 
   1396 skip:
   1397   if (owned) driver_free(o->env, (void*)path, owned_size);
   1398   return 0;
   1399 fail:
   1400   if (owned) driver_free(o->env, (void*)path, owned_size);
   1401   return 1;
   1402 }
   1403 
   1404 static int ld_script_add_token(LdInputScript* s, const char* tok, size_t n,
   1405                                int as_needed, uint8_t group_id) {
   1406   LdOptions* o = s->o;
   1407   const char* owned_tok = NULL;
   1408   const char* path = NULL;
   1409   int owned_path = 0;
   1410   size_t path_size = 0;
   1411 
   1412   if (n == 0) return 0;
   1413 
   1414   if (n > 2 && tok[0] == '-' && tok[1] == 'l' && tok[2] != ':') {
   1415     char* resolved = NULL;
   1416     size_t resolved_size = 0;
   1417     LibResolveKind kind;
   1418     LibResolveMode mode = (o->inputs.cur_link_mode == KIT_LM_STATIC)
   1419                               ? LIB_RESOLVE_STATIC_ONLY
   1420                               : LIB_RESOLVE_DYNAMIC_PREFER;
   1421     LibResolveOS resolve_os = (o->target.os == KIT_OS_WINDOWS)
   1422                                   ? LIB_RESOLVE_OS_WINDOWS
   1423                                   : LIB_RESOLVE_OS_POSIX;
   1424     if (ld_span_to_owned_cstr(o, tok + 2, n - 2u, &owned_tok) != 0) return 1;
   1425     if (ld_resolve_library(o, owned_tok, mode, resolve_os, &resolved,
   1426                            &resolved_size, &kind) != 0) {
   1427       driver_errf(LD_TOOL, "%.*s: cannot resolve linker script input -l%.*s",
   1428                   KIT_SLICE_ARG(kit_slice_cstr(s->script_path)),
   1429                   KIT_SLICE_ARG(kit_slice_cstr(owned_tok)));
   1430       return 1;
   1431     }
   1432     if ((kind == LIB_RESOLVE_KIND_SHARED || kind == LIB_RESOLVE_KIND_TBD) &&
   1433         as_needed) {
   1434       driver_free(o->env, resolved, resolved_size);
   1435       return 0;
   1436     }
   1437     return ld_script_add_path(s, resolved, 1, resolved_size, as_needed,
   1438                               group_id);
   1439   }
   1440 
   1441   if (n > 3 && tok[0] == '-' && tok[1] == 'l' && tok[2] == ':') {
   1442     tok += 3;
   1443     n -= 3;
   1444   } else if (tok[0] == '-') {
   1445     driver_errf(LD_TOOL, "%.*s: unsupported linker script token: %.*s",
   1446                 KIT_SLICE_ARG(kit_slice_cstr(s->script_path)), (int)n, tok);
   1447     return 1;
   1448   }
   1449 
   1450   if (ld_span_to_owned_cstr(o, tok, n, &owned_tok) != 0) return 1;
   1451   if (ld_script_resolve_file(s, owned_tok, &path, &owned_path, &path_size) != 0)
   1452     return 1;
   1453   return ld_script_add_path(s, path, owned_path, path_size, as_needed,
   1454                             group_id);
   1455 }
   1456 
   1457 static int ld_script_parse_list(LdInputScript* s, int as_needed,
   1458                                 uint8_t group_id, int group_directive);
   1459 
   1460 static int ld_script_parse_nested(LdInputScript* s, int as_needed,
   1461                                   uint8_t group_id) {
   1462   if (ld_script_try_kw(s, "AS_NEEDED"))
   1463     return ld_script_parse_list(s, 1, group_id, 0) == 0 ? 1 : -1;
   1464   if (ld_script_try_kw(s, "INPUT"))
   1465     return ld_script_parse_list(s, as_needed, group_id, 0) == 0 ? 1 : -1;
   1466   if (ld_script_try_kw(s, "GROUP"))
   1467     return ld_script_parse_list(s, as_needed, group_id, 1) == 0 ? 1 : -1;
   1468   return 0;
   1469 }
   1470 
   1471 static int ld_script_parse_list(LdInputScript* s, int as_needed,
   1472                                 uint8_t group_id, int group_directive) {
   1473   LdOptions* o = s->o;
   1474   ld_script_skip_ws(s);
   1475   if (s->p >= s->end || *s->p != '(') {
   1476     driver_errf(LD_TOOL, "%.*s: expected '(' in linker script input list",
   1477                 KIT_SLICE_ARG(kit_slice_cstr(s->script_path)));
   1478     return 1;
   1479   }
   1480   ++s->p;
   1481   s->saw_input_directive = 1;
   1482   if (group_directive && group_id == 0) {
   1483     if (o->next_group_id == UINT8_MAX) {
   1484       driver_errf(LD_TOOL, "too many GROUP directives in linker scripts");
   1485       return 1;
   1486     }
   1487     group_id = ++o->next_group_id;
   1488   }
   1489   for (;;) {
   1490     const char* tok;
   1491     size_t n;
   1492     ld_script_skip_ws(s);
   1493     if (s->p >= s->end) {
   1494       driver_errf(LD_TOOL, "%.*s: unterminated linker script input list",
   1495                   KIT_SLICE_ARG(kit_slice_cstr(s->script_path)));
   1496       return 1;
   1497     }
   1498     if (*s->p == ')') {
   1499       ++s->p;
   1500       return 0;
   1501     }
   1502     {
   1503       int nested = ld_script_parse_nested(s, as_needed, group_id);
   1504       if (nested < 0) return 1;
   1505       if (nested > 0) continue;
   1506     }
   1507     ld_script_skip_ws(s);
   1508     if (s->p < s->end && *s->p == ')') continue;
   1509     if (!ld_script_next_token(s, &tok, &n)) {
   1510       driver_errf(LD_TOOL, "%.*s: malformed linker script input",
   1511                   KIT_SLICE_ARG(kit_slice_cstr(s->script_path)));
   1512       return 1;
   1513     }
   1514     if (ld_script_add_token(s, tok, n, as_needed, group_id) != 0) return 1;
   1515   }
   1516 }
   1517 
   1518 static int ld_script_parse_input(LdInputScript* s) {
   1519   while (s->p < s->end) {
   1520     const char* tok;
   1521     size_t n;
   1522     ld_script_skip_ws(s);
   1523     if (s->p >= s->end) break;
   1524     if (ld_script_try_kw(s, "INPUT")) {
   1525       if (ld_script_parse_list(s, 0, 0, 0) != 0) return 1;
   1526       continue;
   1527     }
   1528     if (ld_script_try_kw(s, "GROUP")) {
   1529       if (ld_script_parse_list(s, 0, 0, 1) != 0) return 1;
   1530       continue;
   1531     }
   1532     if (ld_script_try_kw(s, "AS_NEEDED")) {
   1533       if (ld_script_parse_list(s, 1, 0, 0) != 0) return 1;
   1534       continue;
   1535     }
   1536     if (!ld_script_next_token(s, &tok, &n)) {
   1537       if (s->p < s->end && *s->p == '(')
   1538         ld_script_skip_balanced(s);
   1539       else if (s->p < s->end)
   1540         ++s->p;
   1541       continue;
   1542     }
   1543     ld_script_skip_ws(s);
   1544     if (s->p < s->end && *s->p == '(') ld_script_skip_balanced(s);
   1545   }
   1546   if (s->saw_input_directive && !s->added) {
   1547     driver_errf(LD_TOOL, "%.*s: linker script did not add any inputs",
   1548                 KIT_SLICE_ARG(kit_slice_cstr(s->script_path)));
   1549     return 1;
   1550   }
   1551   return 0;
   1552 }
   1553 
   1554 static int ld_try_expand_dso_script(LdOptions* o, uint32_t order_pos,
   1555                                     int* changed) {
   1556   const KitFileIO* io = &o->env->file_io;
   1557   const DriverLinkItem* ord = &o->inputs.link_items[order_pos];
   1558   const char* path;
   1559   KitFileData fd;
   1560   LdInputScript s;
   1561   const char* dir = NULL;
   1562   int rc = 0;
   1563   if (ord->kind != DRIVER_LINK_DSO) return 0;
   1564   if (ord->index >= o->inputs.ndsos) return 0;
   1565   path = o->inputs.dsos[ord->index].path;
   1566   if (!path || !io->read_all) return 0;
   1567   memset(&fd, 0, sizeof fd);
   1568   if (io->read_all(io->user, path, &fd) != KIT_OK) {
   1569     driver_errf(LD_TOOL, "failed to read: %.*s",
   1570                 KIT_SLICE_ARG(kit_slice_cstr(path)));
   1571     return 1;
   1572   }
   1573   if (kit_detect_fmt(fd.data, fd.size) != KIT_BIN_UNKNOWN) {
   1574     if (io->release) io->release(io->user, &fd);
   1575     return 0;
   1576   }
   1577   if (ld_script_dirname(o, path, &dir) != 0) {
   1578     if (io->release) io->release(io->user, &fd);
   1579     return 1;
   1580   }
   1581   memset(&s, 0, sizeof s);
   1582   s.o = o;
   1583   s.script_path = path;
   1584   s.script_dir = dir;
   1585   s.p = (const char*)fd.data;
   1586   s.end = s.p + fd.size;
   1587   s.order_pos = order_pos;
   1588   s.insert_pos = order_pos + 1u;
   1589   if (ld_script_parse_input(&s) != 0) {
   1590     rc = 1;
   1591   } else if (s.added) {
   1592     *changed = 1;
   1593   }
   1594   if (io->release) io->release(io->user, &fd);
   1595   return rc;
   1596 }
   1597 
   1598 static int ld_expand_dso_scripts(LdOptions* o) {
   1599   uint32_t pass;
   1600   for (pass = 0; pass < 8u; ++pass) {
   1601     uint32_t i;
   1602     int changed = 0;
   1603     for (i = 0; i < o->inputs.nlink_items; ++i) {
   1604       if (ld_try_expand_dso_script(o, i, &changed) != 0) return 1;
   1605     }
   1606     if (!changed) return 0;
   1607   }
   1608   driver_errf(LD_TOOL, "too many nested linker-script inputs");
   1609   return 1;
   1610 }
   1611 
   1612 /* ---------- argv parser ---------- */
   1613 
   1614 /* If `arg` starts with `prefix` followed by '=', returns the tail past the
   1615  * '='; otherwise returns NULL. */
   1616 static const char* arg_eq_value(const char* arg, const char* prefix) {
   1617   size_t n = driver_strlen(prefix);
   1618   if (!driver_strneq(arg, prefix, n)) return NULL;
   1619   if (arg[n] != '=') return NULL;
   1620   return arg + n + 1;
   1621 }
   1622 
   1623 /* Compare an MSVC-style flag against `arg`, case-insensitive on the
   1624  * key. MSVC accepts both `/` and `-` as the lead char and is
   1625  * case-insensitive in the key part — we accept `/KEY:val`, `/key:val`,
   1626  * `-KEY:val`. Returns the tail past the colon, or NULL on mismatch. */
   1627 static const char* ms_flag_value(const char* arg, const char* key) {
   1628   size_t klen = driver_strlen(key);
   1629   size_t i;
   1630   if (arg[0] != '/' && arg[0] != '-') return NULL;
   1631   for (i = 0; i < klen; ++i) {
   1632     char a = arg[1 + i];
   1633     char k = key[i];
   1634     if (a >= 'a' && a <= 'z') a = (char)(a - 'a' + 'A');
   1635     if (k >= 'a' && k <= 'z') k = (char)(k - 'a' + 'A');
   1636     if (a != k) return NULL;
   1637   }
   1638   if (arg[1 + klen] != ':') return NULL;
   1639   return arg + 1 + klen + 1;
   1640 }
   1641 
   1642 /* Same shape as ms_flag_value but for bare flags (no `:value`). */
   1643 static int ms_flag_match(const char* arg, const char* key) {
   1644   size_t klen = driver_strlen(key);
   1645   size_t i;
   1646   if (arg[0] != '/' && arg[0] != '-') return 0;
   1647   for (i = 0; i < klen; ++i) {
   1648     char a = arg[1 + i];
   1649     char k = key[i];
   1650     if (a >= 'a' && a <= 'z') a = (char)(a - 'a' + 'A');
   1651     if (k >= 'a' && k <= 'z') k = (char)(k - 'a' + 'A');
   1652     if (a != k) return 0;
   1653   }
   1654   return arg[1 + klen] == '\0';
   1655 }
   1656 
   1657 /* Parse one MSVC-style argument. Recognized subset (others warn and
   1658  * skip — match-but-no-op so legacy build scripts pass cleanly):
   1659  *   /OUT:path                  → o->output_path
   1660  *   /ENTRY:sym                 → o->entry
   1661  *   /SUBSYSTEM:CONSOLE|WINDOWS → PE optional-header subsystem; WINDOWS
   1662  *                                also defaults entry to WinMainCRTStartup.
   1663  *   /DEFAULTLIB:name           → equivalent to -l<name>; resolved
   1664  *                                lazily in the same path as -l.
   1665  *   /LIBPATH:dir               → equivalent to -L dir
   1666  *
   1667  * Returns 1 if consumed, 0 if not a recognized MS flag (caller falls
   1668  * through to its existing behaviour), -1 on hard error. */
   1669 static int ld_try_ms_flag(LdOptions* o, const char* a) {
   1670   const char* val;
   1671   if (!o->ms_link_driver) return 0;
   1672   if (a[0] != '/' && a[0] != '-') return 0;
   1673 
   1674   if ((val = ms_flag_value(a, "OUT")) != NULL) {
   1675     if (o->output_seen) {
   1676       driver_errf(LD_TOOL, "/OUT specified after -o");
   1677       return -1;
   1678     }
   1679     o->output_path = val;
   1680     o->output_seen = 1;
   1681     return 1;
   1682   }
   1683   if ((val = ms_flag_value(a, "ENTRY")) != NULL) {
   1684     o->lf.entry = val;
   1685     return 1;
   1686   }
   1687   if ((val = ms_flag_value(a, "LIBPATH")) != NULL) {
   1688     if (ld_add_lib_dir(o, val) != 0) return -1;
   1689     return 1;
   1690   }
   1691   if ((val = ms_flag_value(a, "DEFAULTLIB")) != NULL) {
   1692     /* Resolve eagerly like -l does, using whatever current link-mode
   1693      * state is in effect. Windows mode triggers the .lib/.dll.a/.a
   1694      * suffix list. */
   1695     char* resolved;
   1696     size_t resolved_size;
   1697     LibResolveKind kind;
   1698     LibResolveMode mode = (o->inputs.cur_link_mode == KIT_LM_STATIC)
   1699                               ? LIB_RESOLVE_STATIC_ONLY
   1700                               : LIB_RESOLVE_DYNAMIC_PREFER;
   1701     if (ld_resolve_library(o, val, mode, LIB_RESOLVE_OS_WINDOWS, &resolved,
   1702                            &resolved_size, &kind) != 0) {
   1703       driver_errf(LD_TOOL, "/DEFAULTLIB: cannot find %.*s",
   1704                   KIT_SLICE_ARG(kit_slice_cstr(val)));
   1705       return -1;
   1706     }
   1707     if (kind == LIB_RESOLVE_KIND_SHARED || kind == LIB_RESOLVE_KIND_TBD) {
   1708       ld_push_dso(o, resolved, 1, resolved_size);
   1709     } else {
   1710       ld_push_archive(o, resolved, 1, resolved_size);
   1711     }
   1712     return 1;
   1713   }
   1714   if ((val = ms_flag_value(a, "SUBSYSTEM")) != NULL) {
   1715     if (driver_link_flags_record_pe_subsystem(&o->lf, val,
   1716                                               driver_strlen(val)) != 0)
   1717       return -1;
   1718     return 1;
   1719   }
   1720   if (ms_flag_match(a, "NOLOGO") || ms_flag_match(a, "VERBOSE") ||
   1721       ms_flag_match(a, "INCREMENTAL") || ms_flag_match(a, "DEBUG") ||
   1722       ms_flag_match(a, "DYNAMICBASE") || ms_flag_match(a, "NXCOMPAT")) {
   1723     /* Common flags every Windows build script sets; silently accept. */
   1724     return 1;
   1725   }
   1726 
   1727   /* Any other `/key[:val]` shape under --ms-link-driver: warn + skip.
   1728    * We treat the entire arg as consumed so it doesn't fall through to
   1729    * the positional path and try to open a file. */
   1730   driver_errf(LD_TOOL, "ignoring unsupported MS-style flag: %.*s",
   1731               KIT_SLICE_ARG(kit_slice_cstr(a)));
   1732   return 1;
   1733 }
   1734 
   1735 static int ld_parse(int argc, char** argv, LdOptions* o) {
   1736   int i;
   1737   if (ld_alloc_arrays(o, argc) != 0) return 1;
   1738 
   1739   o->target = driver_host_target();
   1740 
   1741   /* First pass: detect --ms-link-driver up front so the option can
   1742    * appear anywhere on the command line and still affect earlier
   1743    * `/...` tokens. Also capture --sysroot before resolving any -l
   1744    * entries; GNU ld treats the sysroot as a global search-prefix
   1745    * setting, not positional state. Also detect -lc so the hosted lib
   1746    * dirs can be pre-populated before -lm / -lpthread etc. are resolved. */
   1747   {
   1748     int has_lc = 0;
   1749     for (i = 1; i < argc; ++i) {
   1750       if (driver_streq(argv[i], "--ms-link-driver")) {
   1751         o->ms_link_driver = 1;
   1752         continue;
   1753       }
   1754       if (driver_streq(argv[i], "-nostdlib")) {
   1755         o->no_default_libs = 1;
   1756         o->nostdlib = 1;
   1757         o->nostartfiles = 1;
   1758         continue;
   1759       }
   1760       if (driver_streq(argv[i], "-nostartfiles")) {
   1761         o->nostartfiles = 1;
   1762         continue;
   1763       }
   1764       if (driver_streq(argv[i], "--no-default-libs") ||
   1765           driver_streq(argv[i], "-nodefaultlibs")) {
   1766         o->no_default_libs = 1;
   1767         continue;
   1768       }
   1769       if (driver_strneq(argv[i], "-Wl,", 4)) {
   1770         const char* wl = argv[i] + 4;
   1771         if (strstr(wl, "-nostdlib")) {
   1772           o->no_default_libs = 1;
   1773           o->nostdlib = 1;
   1774           o->nostartfiles = 1;
   1775           continue;
   1776         }
   1777         if (strstr(wl, "-nostartfiles")) {
   1778           o->nostartfiles = 1;
   1779           continue;
   1780         }
   1781         if (strstr(wl, "--no-default-libs") ||
   1782             strstr(wl, "-nodefaultlibs")) {
   1783           o->no_default_libs = 1;
   1784           continue;
   1785         }
   1786         continue;
   1787       }
   1788       if (driver_streq(argv[i], "-target") ||
   1789           driver_streq(argv[i], "--target")) {
   1790         if (i + 1 < argc) {
   1791           if (ld_set_target_triple(o, argv[i + 1]) != 0) return 1;
   1792           i++;
   1793         }
   1794         continue;
   1795       }
   1796       if (driver_strneq(argv[i], "--target=", 9)) {
   1797         if (ld_set_target_triple(o, argv[i] + 9) != 0) return 1;
   1798         continue;
   1799       }
   1800       {
   1801         int dr = driver_darwin_platform_try_consume(
   1802             LD_TOOL, argc, argv, &i, &o->target, o->pic_explicit);
   1803         if (dr < 0) return 1;
   1804         if (dr > 0) continue;
   1805       }
   1806       if (ld_is_cc_driver_noop(argv[i], driver_strlen(argv[i]))) {
   1807         continue;
   1808       }
   1809       if (driver_streq(argv[i], "-m")) {
   1810         if (i + 1 < argc) {
   1811           if (ld_set_gnu_emulation(o, argv[i + 1]) != 0) return 1;
   1812           i++;
   1813         }
   1814         continue;
   1815       }
   1816       if (driver_strneq(argv[i], "-m", 2) && argv[i][2] != '\0') {
   1817         if (ld_set_gnu_emulation(o, argv[i] + 2) != 0) return 1;
   1818         continue;
   1819       }
   1820       if (driver_streq(argv[i], "--sysroot") ||
   1821           driver_streq(argv[i], "-syslibroot")) {
   1822         if (i + 1 < argc) {
   1823           o->sysroot = argv[i + 1];
   1824           i++;
   1825         }
   1826         continue;
   1827       }
   1828       if (driver_strneq(argv[i], "--sysroot=", 10)) {
   1829         o->sysroot = argv[i] + 10;
   1830         continue;
   1831       }
   1832       if (argv[i][0] != '-' && ld_is_crt_start_object(argv[i])) {
   1833         o->explicit_crt_start = 1;
   1834         continue;
   1835       }
   1836       /* Detect -lc / -l c so hosted lib dirs are available for later -l
   1837        * flags regardless of where -lc appears on the command line. */
   1838       if (driver_streq(argv[i], "-lc")) {
   1839         has_lc = 1;
   1840         continue;
   1841       }
   1842       if ((driver_streq(argv[i], "-l") || driver_streq(argv[i], "--library")) &&
   1843           i + 1 < argc && driver_streq(argv[i + 1], "c")) {
   1844         has_lc = 1;
   1845         i++;
   1846         continue;
   1847       }
   1848     }
   1849     if (!o->sysroot || !o->sysroot[0]) {
   1850       const char* env_sysroot = driver_getenv("KIT_SYSROOT");
   1851       if (env_sysroot && env_sysroot[0]) o->sysroot = env_sysroot;
   1852     }
   1853     /* GNU ld treats -L / --library-path as global library-search state, not
   1854      * as a positional option that only affects later -l flags. Pre-collect
   1855      * those directories now so Rust-style invocations can put self-contained
   1856      * runtime libdirs after an earlier -l. */
   1857     for (i = 1; i < argc; ++i) {
   1858       const char* a = argv[i];
   1859       const char* val;
   1860       if (driver_strneq(a, "-L", 2)) {
   1861         const char* dir = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
   1862         const char* rewritten;
   1863         if (!dir) {
   1864           driver_errf(LD_TOOL, "-L requires an argument");
   1865           return 1;
   1866         }
   1867         if (ld_sysroot_rewrite_path(o, dir, &rewritten) != 0) return 1;
   1868         if (ld_add_lib_dir(o, rewritten) != 0) return 1;
   1869         continue;
   1870       }
   1871       if ((val = arg_eq_value(a, "--library-path")) != NULL) {
   1872         const char* rewritten;
   1873         if (ld_sysroot_rewrite_path(o, val, &rewritten) != 0) return 1;
   1874         if (ld_add_lib_dir(o, rewritten) != 0) return 1;
   1875         continue;
   1876       }
   1877       if (driver_streq(a, "--library-path")) {
   1878         const char* rewritten;
   1879         if (++i >= argc) {
   1880           driver_errf(LD_TOOL, "--library-path requires an argument");
   1881           return 1;
   1882         }
   1883         if (ld_sysroot_rewrite_path(o, argv[i], &rewritten) != 0) return 1;
   1884         if (ld_add_lib_dir(o, rewritten) != 0) return 1;
   1885         continue;
   1886       }
   1887       if (driver_strneq(a, "-F", 2)) {
   1888         const char* dir = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
   1889         const char* rewritten;
   1890         if (!dir) {
   1891           driver_errf(LD_TOOL, "-F requires an argument");
   1892           return 1;
   1893         }
   1894         if (ld_sysroot_rewrite_path(o, dir, &rewritten) != 0) return 1;
   1895         if (ld_add_framework_dir(o, rewritten) != 0) return 1;
   1896         continue;
   1897       }
   1898     }
   1899     o->lib_dirs_precollected = 1;
   1900     if (ld_add_sysroot_libdir(o) != 0) return 1;
   1901     if (ld_add_sysroot_framework_dirs(o) != 0) return 1;
   1902     /* Pre-populate the hosted lib dirs so user -l flags are resolved
   1903      * against the correct sysroot even when they appear before -lc. */
   1904     if (has_lc && !o->explicit_crt_start) {
   1905       DriverHostedRequest req;
   1906       DriverHostedDirs dirs;
   1907       uint32_t j;
   1908       memset(&req, 0, sizeof req);
   1909       req.env = o->env;
   1910       req.tool = LD_TOOL;
   1911       req.target = o->target;
   1912       req.sysroot = o->sysroot;
   1913       if (driver_hosted_dirs_resolve(&req, &dirs) == 0) {
   1914         for (j = 0; j < dirs.nlibdirs; ++j) {
   1915           /* Transfer ownership of each dir string to o->owned_paths so
   1916            * it is freed in ld_options_release; then point lib_dirs at it. */
   1917           const char* p;
   1918           if (ld_own_path(o, dirs.libdirs[j], dirs.libdir_sizes[j], &p) == 0) {
   1919             dirs.libdirs[j] = NULL;
   1920             dirs.libdir_sizes[j] = 0;
   1921             if (ld_add_lib_dir(o, p) != 0) {
   1922               driver_hosted_dirs_fini(&dirs);
   1923               return 1;
   1924             }
   1925           }
   1926         }
   1927         driver_hosted_dirs_fini(&dirs);
   1928       }
   1929     }
   1930   }
   1931 
   1932   for (i = 1; i < argc; ++i) {
   1933     const char* a = argv[i];
   1934     const char* val;
   1935     int ms_rc;
   1936 
   1937     if (driver_streq(a, "--ms-link-driver")) {
   1938       o->ms_link_driver = 1;
   1939       continue;
   1940     }
   1941     ms_rc = ld_try_ms_flag(o, a);
   1942     if (ms_rc < 0) return 1;
   1943     if (ms_rc > 0) continue;
   1944 
   1945     if (driver_strneq(a, "-Wl,", 4)) {
   1946       if (ld_parse_wl(o, a + 4) != 0) return 1;
   1947       continue;
   1948     }
   1949 
   1950     if (driver_streq(a, "-o")) {
   1951       if (++i >= argc) {
   1952         driver_errf(LD_TOOL, "-o requires an argument");
   1953         return 1;
   1954       }
   1955       if (o->output_seen) {
   1956         driver_errf(LD_TOOL, "-o specified more than once");
   1957         return 1;
   1958       }
   1959       o->output_path = argv[i];
   1960       o->output_seen = 1;
   1961       continue;
   1962     }
   1963     if ((val = arg_eq_value(a, "--output")) != NULL) {
   1964       if (o->output_seen) {
   1965         driver_errf(LD_TOOL, "-o specified more than once");
   1966         return 1;
   1967       }
   1968       o->output_path = val;
   1969       o->output_seen = 1;
   1970       continue;
   1971     }
   1972     if (driver_streq(a, "--output")) {
   1973       if (++i >= argc) {
   1974         driver_errf(LD_TOOL, "--output requires an argument");
   1975         return 1;
   1976       }
   1977       if (o->output_seen) {
   1978         driver_errf(LD_TOOL, "-o specified more than once");
   1979         return 1;
   1980       }
   1981       o->output_path = argv[i];
   1982       o->output_seen = 1;
   1983       continue;
   1984     }
   1985     if (driver_streq(a, "-e")) {
   1986       if (++i >= argc) {
   1987         driver_errf(LD_TOOL, "-e requires an argument");
   1988         return 1;
   1989       }
   1990       o->lf.entry = argv[i];
   1991       continue;
   1992     }
   1993     if ((val = arg_eq_value(a, "--entry")) != NULL) {
   1994       o->lf.entry = val;
   1995       continue;
   1996     }
   1997     if (driver_streq(a, "--entry")) {
   1998       if (++i >= argc) {
   1999         driver_errf(LD_TOOL, "--entry requires an argument");
   2000         return 1;
   2001       }
   2002       o->lf.entry = argv[i];
   2003       continue;
   2004     }
   2005     if ((val = arg_eq_value(a, "--subsystem")) != NULL) {
   2006       if (driver_link_flags_record_pe_subsystem(&o->lf, val,
   2007                                                 driver_strlen(val)) != 0)
   2008         return 1;
   2009       continue;
   2010     }
   2011     if (driver_streq(a, "--subsystem")) {
   2012       if (++i >= argc) {
   2013         driver_errf(LD_TOOL, "--subsystem requires an argument");
   2014         return 1;
   2015       }
   2016       if (driver_link_flags_record_pe_subsystem(&o->lf, argv[i],
   2017                                                 driver_strlen(argv[i])) != 0)
   2018         return 1;
   2019       continue;
   2020     }
   2021     {
   2022       int dr = driver_darwin_platform_try_consume(
   2023           LD_TOOL, argc, argv, &i, &o->target, o->pic_explicit);
   2024       if (dr < 0) return 1;
   2025       if (dr > 0) continue;
   2026     }
   2027     if (driver_streq(a, "-target") || driver_streq(a, "--target")) {
   2028       if (++i >= argc) {
   2029         driver_errf(LD_TOOL, "%s requires an argument", a);
   2030         return 1;
   2031       }
   2032       if (ld_set_target_triple(o, argv[i]) != 0) return 1;
   2033       continue;
   2034     }
   2035     if (driver_strneq(a, "--target=", 9)) {
   2036       if (ld_set_target_triple(o, a + 9) != 0) return 1;
   2037       continue;
   2038     }
   2039     if (driver_streq(a, "-flavor")) {
   2040       if (++i >= argc) {
   2041         driver_errf(LD_TOOL, "-flavor requires an argument");
   2042         return 1;
   2043       }
   2044       if (!driver_streq(argv[i], "gnu")) {
   2045         driver_errf(LD_TOOL, "unsupported linker flavor: %s", argv[i]);
   2046         return 1;
   2047       }
   2048       continue;
   2049     }
   2050     if (ld_is_cc_driver_noop(a, driver_strlen(a))) {
   2051       continue;
   2052     }
   2053     if (driver_streq(a, "-m")) {
   2054       if (++i >= argc) {
   2055         driver_errf(LD_TOOL, "-m requires an argument");
   2056         return 1;
   2057       }
   2058       if (ld_set_gnu_emulation(o, argv[i]) != 0) return 1;
   2059       continue;
   2060     }
   2061     if (driver_strneq(a, "-m", 2) && a[2] != '\0') {
   2062       if (ld_set_gnu_emulation(o, a + 2) != 0) return 1;
   2063       continue;
   2064     }
   2065 
   2066     if (driver_streq(a, "-T")) {
   2067       if (++i >= argc) {
   2068         driver_errf(LD_TOOL, "-T requires an argument");
   2069         return 1;
   2070       }
   2071       o->script_path = argv[i];
   2072       continue;
   2073     }
   2074     if ((val = arg_eq_value(a, "--script")) != NULL) {
   2075       o->script_path = val;
   2076       continue;
   2077     }
   2078     if (driver_streq(a, "--script")) {
   2079       if (++i >= argc) {
   2080         driver_errf(LD_TOOL, "--script requires an argument");
   2081         return 1;
   2082       }
   2083       o->script_path = argv[i];
   2084       continue;
   2085     }
   2086     /* -Ttext ADDR / -Ttext=ADDR: set the static ET_EXEC image (text) base, for
   2087      * freestanding images that must load at a fixed address (e.g. the qemu
   2088      * `virt` RAM base 0x80000000). Matched here; the bare "-T" above is an
   2089      * exact match, so it never swallows -Ttext. No effect on PIE/scripted
   2090      * layouts. */
   2091     {
   2092       const char* tval = NULL;
   2093       if (driver_streq(a, "-Ttext")) {
   2094         if (++i >= argc) {
   2095           driver_errf(LD_TOOL, "-Ttext requires an argument");
   2096           return 1;
   2097         }
   2098         tval = argv[i];
   2099       } else if ((tval = arg_eq_value(a, "-Ttext")) != NULL) {
   2100         /* -Ttext=ADDR */
   2101       }
   2102       if (tval) {
   2103         if (driver_link_flags_record_text_base(&o->lf, tval) != 0) return 1;
   2104         continue;
   2105       }
   2106     }
   2107     /* -Tdata / -Tbss ADDR (or =ADDR): per-section address override. Folded
   2108      * into the section_starts table as `.data` / `.bss`. */
   2109     {
   2110       const char* secname = NULL;
   2111       const char* addr = NULL;
   2112       if (driver_streq(a, "-Tdata") || driver_streq(a, "-Tbss")) {
   2113         secname = driver_streq(a, "-Tdata") ? ".data" : ".bss";
   2114         if (++i >= argc) {
   2115           driver_errf(LD_TOOL, "%s requires an address", a);
   2116           return 1;
   2117         }
   2118         addr = argv[i];
   2119       } else if ((addr = arg_eq_value(a, "-Tdata")) != NULL) {
   2120         secname = ".data";
   2121       } else if ((addr = arg_eq_value(a, "-Tbss")) != NULL) {
   2122         secname = ".bss";
   2123       }
   2124       if (secname) {
   2125         if (driver_link_flags_record_section_addr(&o->lf, secname, addr) != 0)
   2126           return 1;
   2127         continue;
   2128       }
   2129     }
   2130     if (driver_streq(a, "--defsym")) {
   2131       if (++i >= argc) {
   2132         driver_errf(LD_TOOL, "--defsym requires NAME=EXPR");
   2133         return 1;
   2134       }
   2135       if (driver_link_flags_record_defsym(&o->lf, argv[i],
   2136                                           driver_strlen(argv[i])) != 0)
   2137         return 1;
   2138       continue;
   2139     }
   2140     if ((val = arg_eq_value(a, "--defsym")) != NULL) {
   2141       if (driver_link_flags_record_defsym(&o->lf, val, driver_strlen(val)) != 0)
   2142         return 1;
   2143       continue;
   2144     }
   2145     if (driver_streq(a, "--section-start")) {
   2146       if (++i >= argc) {
   2147         driver_errf(LD_TOOL, "--section-start requires .NAME=ADDR");
   2148         return 1;
   2149       }
   2150       if (driver_link_flags_record_section_start(
   2151               &o->lf, argv[i], driver_strlen(argv[i])) != 0)
   2152         return 1;
   2153       continue;
   2154     }
   2155     if ((val = arg_eq_value(a, "--section-start")) != NULL) {
   2156       if (driver_link_flags_record_section_start(&o->lf, val,
   2157                                                  driver_strlen(val)) != 0)
   2158         return 1;
   2159       continue;
   2160     }
   2161     if (driver_streq(a, "--orphan-handling")) {
   2162       if (++i >= argc) {
   2163         driver_errf(LD_TOOL, "--orphan-handling requires a mode");
   2164         return 1;
   2165       }
   2166       if (driver_link_flags_record_orphan_handling(
   2167               &o->lf, argv[i], driver_strlen(argv[i])) != 0)
   2168         return 1;
   2169       continue;
   2170     }
   2171     if ((val = arg_eq_value(a, "--orphan-handling")) != NULL) {
   2172       if (driver_link_flags_record_orphan_handling(&o->lf, val,
   2173                                                    driver_strlen(val)) != 0)
   2174         return 1;
   2175       continue;
   2176     }
   2177     if (driver_streq(a, "--fatal-warnings")) {
   2178       o->lf.fatal_warnings = 1;
   2179       continue;
   2180     }
   2181     if (driver_streq(a, "--no-fatal-warnings")) {
   2182       o->lf.fatal_warnings = 0;
   2183       continue;
   2184     }
   2185     if (driver_streq(a, "--print-memory-usage")) {
   2186       o->lf.print_memory_usage = 1;
   2187       continue;
   2188     }
   2189     if (driver_streq(a, "--cref")) {
   2190       if (++i >= argc) {
   2191         driver_errf(LD_TOOL, "--cref requires a path");
   2192         return 1;
   2193       }
   2194       if (driver_link_flags_record_cref(&o->lf, argv[i],
   2195                                         driver_strlen(argv[i])) != 0)
   2196         return 1;
   2197       continue;
   2198     }
   2199     if ((val = arg_eq_value(a, "--cref")) != NULL) {
   2200       if (driver_link_flags_record_cref(&o->lf, val, driver_strlen(val)) != 0)
   2201         return 1;
   2202       continue;
   2203     }
   2204     /* -nostdlib / --no-default-libs: do not auto-resolve and link kit's
   2205      * compiler runtime. A freestanding image (e.g. riscv32-none-elf) supplies
   2206      * its own libkit_rt.a on the command line, and a pure-linker invocation
   2207      * should not require a per-target runtime archive to exist. */
   2208     if (driver_streq(a, "-nostdlib")) {
   2209       o->no_default_libs = 1;
   2210       o->nostdlib = 1;
   2211       o->nostartfiles = 1;
   2212       continue;
   2213     }
   2214     if (driver_streq(a, "-nostartfiles")) {
   2215       o->nostartfiles = 1;
   2216       continue;
   2217     }
   2218     if (driver_streq(a, "--no-default-libs") ||
   2219         driver_streq(a, "-nodefaultlibs")) {
   2220       o->no_default_libs = 1;
   2221       continue;
   2222     }
   2223     if (driver_streq(a, "--support-dir")) {
   2224       if (++i >= argc) {
   2225         driver_errf(LD_TOOL, "--support-dir requires an argument");
   2226         return 1;
   2227       }
   2228       o->support_dir = argv[i];
   2229       continue;
   2230     }
   2231     if (driver_strneq(a, "--support-dir=", 14)) {
   2232       o->support_dir = a + 14;
   2233       continue;
   2234     }
   2235     /* --sysroot is authoritative in the first pass (GNU ld treats it as a
   2236      * global search-prefix that must be known before any -l / "=" rewrite), so
   2237      * o->sysroot is already set. Here we only consume the tokens so they are
   2238      * not misread as input files; do not re-apply the value. */
   2239     if (driver_streq(a, "--sysroot") || driver_streq(a, "-syslibroot")) {
   2240       if (++i >= argc) {
   2241         driver_errf(LD_TOOL, "%s requires an argument", a);
   2242         return 1;
   2243       }
   2244       continue;
   2245     }
   2246     if (driver_strneq(a, "--sysroot=", 10)) {
   2247       continue;
   2248     }
   2249 
   2250     if (driver_strneq(a, "-L", 2)) {
   2251       const char* dir = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
   2252       const char* rewritten;
   2253       if (!dir) {
   2254         driver_errf(LD_TOOL, "-L requires an argument");
   2255         return 1;
   2256       }
   2257       if (!o->lib_dirs_precollected) {
   2258         if (ld_sysroot_rewrite_path(o, dir, &rewritten) != 0) return 1;
   2259         if (ld_add_lib_dir(o, rewritten) != 0) return 1;
   2260       }
   2261       continue;
   2262     }
   2263     if ((val = arg_eq_value(a, "--library-path")) != NULL) {
   2264       const char* rewritten;
   2265       if (!o->lib_dirs_precollected) {
   2266         if (ld_sysroot_rewrite_path(o, val, &rewritten) != 0) return 1;
   2267         if (ld_add_lib_dir(o, rewritten) != 0) return 1;
   2268       }
   2269       continue;
   2270     }
   2271     if (driver_streq(a, "--library-path")) {
   2272       const char* rewritten;
   2273       if (++i >= argc) {
   2274         driver_errf(LD_TOOL, "--library-path requires an argument");
   2275         return 1;
   2276       }
   2277       if (!o->lib_dirs_precollected) {
   2278         if (ld_sysroot_rewrite_path(o, argv[i], &rewritten) != 0) return 1;
   2279         if (ld_add_lib_dir(o, rewritten) != 0) return 1;
   2280       }
   2281       continue;
   2282     }
   2283     if (driver_strneq(a, "-F", 2)) {
   2284       const char* dir = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
   2285       const char* rewritten;
   2286       if (!dir) {
   2287         driver_errf(LD_TOOL, "-F requires an argument");
   2288         return 1;
   2289       }
   2290       if (!o->lib_dirs_precollected) {
   2291         if (ld_sysroot_rewrite_path(o, dir, &rewritten) != 0) return 1;
   2292         if (ld_add_framework_dir(o, rewritten) != 0) return 1;
   2293       }
   2294       continue;
   2295     }
   2296 
   2297     if (driver_strneq(a, "-l", 2)) {
   2298       const char* name = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
   2299       char* resolved;
   2300       size_t resolved_size;
   2301       LibResolveKind kind;
   2302       LibResolveMode mode;
   2303       LibResolveOS resolve_os;
   2304       if (!name) {
   2305         driver_errf(LD_TOOL, "-l requires an argument");
   2306         return 1;
   2307       }
   2308       if (ld_note_library_request(o, name)) continue;
   2309       /* -Bstatic forces .a only; everything else (default,
   2310        * -Bdynamic, --as-needed) prefers .so but falls back to .a. */
   2311       mode = (o->inputs.cur_link_mode == KIT_LM_STATIC) ? LIB_RESOLVE_STATIC_ONLY
   2312                                                  : LIB_RESOLVE_DYNAMIC_PREFER;
   2313       resolve_os = (o->target.os == KIT_OS_WINDOWS) ? LIB_RESOLVE_OS_WINDOWS
   2314                                                     : LIB_RESOLVE_OS_POSIX;
   2315       if (ld_resolve_library(o, name, mode, resolve_os, &resolved,
   2316                              &resolved_size, &kind) != 0) {
   2317         driver_errf(LD_TOOL, "cannot find -l%.*s",
   2318                     KIT_SLICE_ARG(kit_slice_cstr(name)));
   2319         return 1;
   2320       }
   2321       if (kind == LIB_RESOLVE_KIND_SHARED || kind == LIB_RESOLVE_KIND_TBD) {
   2322         ld_push_dso(o, resolved, 1, resolved_size);
   2323       } else {
   2324         ld_push_archive(o, resolved, 1, resolved_size);
   2325       }
   2326       continue;
   2327     }
   2328     if ((val = arg_eq_value(a, "--library")) != NULL) {
   2329       char* resolved;
   2330       size_t resolved_size;
   2331       LibResolveKind kind;
   2332       LibResolveMode mode = (o->inputs.cur_link_mode == KIT_LM_STATIC)
   2333                                 ? LIB_RESOLVE_STATIC_ONLY
   2334                                 : LIB_RESOLVE_DYNAMIC_PREFER;
   2335       LibResolveOS resolve_os = (o->target.os == KIT_OS_WINDOWS)
   2336                                     ? LIB_RESOLVE_OS_WINDOWS
   2337                                     : LIB_RESOLVE_OS_POSIX;
   2338       if (ld_note_library_request(o, val)) continue;
   2339       if (ld_resolve_library(o, val, mode, resolve_os, &resolved,
   2340                              &resolved_size, &kind) != 0) {
   2341         driver_errf(LD_TOOL, "cannot find -l%.*s",
   2342                     KIT_SLICE_ARG(kit_slice_cstr(val)));
   2343         return 1;
   2344       }
   2345       if (kind == LIB_RESOLVE_KIND_SHARED || kind == LIB_RESOLVE_KIND_TBD) {
   2346         ld_push_dso(o, resolved, 1, resolved_size);
   2347       } else {
   2348         ld_push_archive(o, resolved, 1, resolved_size);
   2349       }
   2350       continue;
   2351     }
   2352     if (driver_streq(a, "--library")) {
   2353       char* resolved;
   2354       size_t resolved_size;
   2355       LibResolveKind kind;
   2356       LibResolveMode mode;
   2357       LibResolveOS resolve_os;
   2358       if (++i >= argc) {
   2359         driver_errf(LD_TOOL, "--library requires an argument");
   2360         return 1;
   2361       }
   2362       if (ld_note_library_request(o, argv[i])) continue;
   2363       mode = (o->inputs.cur_link_mode == KIT_LM_STATIC) ? LIB_RESOLVE_STATIC_ONLY
   2364                                                  : LIB_RESOLVE_DYNAMIC_PREFER;
   2365       resolve_os = (o->target.os == KIT_OS_WINDOWS) ? LIB_RESOLVE_OS_WINDOWS
   2366                                                     : LIB_RESOLVE_OS_POSIX;
   2367       if (ld_resolve_library(o, argv[i], mode, resolve_os, &resolved,
   2368                              &resolved_size, &kind) != 0) {
   2369         driver_errf(LD_TOOL, "cannot find -l%.*s",
   2370                     KIT_SLICE_ARG(kit_slice_cstr(argv[i])));
   2371         return 1;
   2372       }
   2373       if (kind == LIB_RESOLVE_KIND_SHARED || kind == LIB_RESOLVE_KIND_TBD) {
   2374         ld_push_dso(o, resolved, 1, resolved_size);
   2375       } else {
   2376         ld_push_archive(o, resolved, 1, resolved_size);
   2377       }
   2378       continue;
   2379     }
   2380     if (driver_streq(a, "-framework")) {
   2381       if (++i >= argc) {
   2382         driver_errf(LD_TOOL, "-framework requires an argument");
   2383         return 1;
   2384       }
   2385       if (ld_push_framework(o, argv[i]) != 0) return 1;
   2386       continue;
   2387     }
   2388 
   2389     if (driver_streq(a, "-static")) {
   2390       o->target.pic = KIT_PIC_NONE;
   2391       o->static_link = 1;
   2392       o->inputs.cur_link_mode = KIT_LM_STATIC;
   2393       o->pic_explicit = 1;
   2394       continue;
   2395     }
   2396     if (driver_streq(a, "-static-pie")) {
   2397       o->target.pic = KIT_PIC_PIE;
   2398       o->pie = 1;
   2399       o->static_link = 1;
   2400       o->inputs.cur_link_mode = KIT_LM_STATIC;
   2401       o->pic_explicit = 1;
   2402       continue;
   2403     }
   2404     if (driver_streq(a, "-pie")) {
   2405       o->target.pic = KIT_PIC_PIE;
   2406       o->pie = 1;
   2407       o->pic_explicit = 1;
   2408       continue;
   2409     }
   2410     if (driver_streq(a, "-dynamic-linker") ||
   2411         driver_streq(a, "--dynamic-linker")) {
   2412       if (++i >= argc) {
   2413         driver_errf(LD_TOOL, "-dynamic-linker requires an argument");
   2414         return 1;
   2415       }
   2416       o->lf.interp_path = argv[i];
   2417       continue;
   2418     }
   2419     if (driver_streq(a, "--no-dynamic-linker")) {
   2420       o->lf.interp_path = "";
   2421       continue;
   2422     }
   2423     if ((val = arg_eq_value(a, "--dynamic-linker")) != NULL) {
   2424       o->lf.interp_path = val;
   2425       continue;
   2426     }
   2427     if (driver_streq(a, "-no-pie")) {
   2428       o->target.pic = KIT_PIC_NONE;
   2429       o->pie = 0;
   2430       o->pic_explicit = 1;
   2431       continue;
   2432     }
   2433 
   2434     if (driver_streq(a, "-shared")) {
   2435       o->shared = 1;
   2436       /* Shared objects must be position-independent. Force PIC unless
   2437        * the caller has explicitly chosen PIE (which is also fine). */
   2438       if (o->target.pic == KIT_PIC_NONE) o->target.pic = KIT_PIC_PIC;
   2439       o->pic_explicit = 1;
   2440       continue;
   2441     }
   2442     if (driver_streq(a, "-r") || driver_streq(a, "--relocatable")) {
   2443       o->relocatable = 1;
   2444       continue;
   2445     }
   2446     if (driver_streq(a, "-soname")) {
   2447       if (++i >= argc) {
   2448         driver_errf(LD_TOOL, "-soname requires an argument");
   2449         return 1;
   2450       }
   2451       o->lf.soname = argv[i];
   2452       continue;
   2453     }
   2454     if ((val = arg_eq_value(a, "-soname")) != NULL) {
   2455       o->lf.soname = val;
   2456       continue;
   2457     }
   2458     if (driver_streq(a, "-rpath")) {
   2459       if (++i >= argc) {
   2460         driver_errf(LD_TOOL, "-rpath requires an argument");
   2461         return 1;
   2462       }
   2463       o->lf.rpaths[o->lf.nrpaths++] = argv[i];
   2464       continue;
   2465     }
   2466     if ((val = arg_eq_value(a, "-rpath")) != NULL) {
   2467       o->lf.rpaths[o->lf.nrpaths++] = val;
   2468       continue;
   2469     }
   2470     if (driver_streq(a, "-rpath-link")) {
   2471       if (++i >= argc) {
   2472         driver_errf(LD_TOOL, "-rpath-link requires an argument");
   2473         return 1;
   2474       }
   2475       o->rpath_links[o->nrpath_links++] = argv[i];
   2476       continue;
   2477     }
   2478     if ((val = arg_eq_value(a, "-rpath-link")) != NULL) {
   2479       o->rpath_links[o->nrpath_links++] = val;
   2480       continue;
   2481     }
   2482     if (driver_streq(a, "--enable-new-dtags")) {
   2483       o->lf.new_dtags = 1;
   2484       continue;
   2485     }
   2486     if (driver_streq(a, "--disable-new-dtags")) {
   2487       o->lf.new_dtags = 0;
   2488       continue;
   2489     }
   2490     if (driver_streq(a, "--gc-sections")) {
   2491       o->lf.gc_sections = 1;
   2492       continue;
   2493     }
   2494     if (driver_streq(a, "-dead_strip")) {
   2495       o->lf.gc_sections = 1;
   2496       continue;
   2497     }
   2498     if (driver_streq(a, "--no-gc-sections")) {
   2499       o->lf.gc_sections = 0;
   2500       continue;
   2501     }
   2502     if (driver_streq(a, "--map") || driver_streq(a, "-Map")) {
   2503       if (++i >= argc) {
   2504         driver_errf(LD_TOOL, "%s requires an argument", a);
   2505         return 1;
   2506       }
   2507       if (!argv[i][0]) {
   2508         driver_errf(LD_TOOL, "%s requires a non-empty path", a);
   2509         return 1;
   2510       }
   2511       o->lf.map_path = argv[i];
   2512       continue;
   2513     }
   2514     if ((val = arg_eq_value(a, "--map")) != NULL) {
   2515       if (!val[0]) {
   2516         driver_errf(LD_TOOL, "--map requires a non-empty path");
   2517         return 1;
   2518       }
   2519       o->lf.map_path = val;
   2520       continue;
   2521     }
   2522     if ((val = arg_eq_value(a, "-Map")) != NULL) {
   2523       if (!val[0]) {
   2524         driver_errf(LD_TOOL, "-Map requires a non-empty path");
   2525         return 1;
   2526       }
   2527       o->lf.map_path = val;
   2528       continue;
   2529     }
   2530     if (driver_streq(a, "--symbols")) {
   2531       if (++i >= argc) {
   2532         driver_errf(LD_TOOL, "--symbols requires an argument");
   2533         return 1;
   2534       }
   2535       if (!argv[i][0]) {
   2536         driver_errf(LD_TOOL, "--symbols requires a non-empty path");
   2537         return 1;
   2538       }
   2539       o->lf.symbols_path = argv[i];
   2540       continue;
   2541     }
   2542     if ((val = arg_eq_value(a, "--symbols")) != NULL) {
   2543       if (!val[0]) {
   2544         driver_errf(LD_TOOL, "--symbols requires a non-empty path");
   2545         return 1;
   2546       }
   2547       o->lf.symbols_path = val;
   2548       continue;
   2549     }
   2550     if ((val = arg_eq_value(a, "--symbols-format")) != NULL) {
   2551       if (driver_link_flags_record_symbols_format(&o->lf, val,
   2552                                                   driver_strlen(val)) != 0)
   2553         return 1;
   2554       continue;
   2555     }
   2556     if (driver_streq(a, "-S") || driver_streq(a, "--strip-debug")) {
   2557       o->lf.strip_debug = 1;
   2558       continue;
   2559     }
   2560     if (driver_streq(a, "-E") || driver_streq(a, "--export-dynamic")) {
   2561       o->export_dynamic = 1;
   2562       continue;
   2563     }
   2564     if (driver_streq(a, "--eh-frame-hdr")) {
   2565       /* Rust/GNU drivers request this for unwind-table acceleration. Kit's
   2566        * ELF writer does not currently synthesize PT_GNU_EH_FRAME, so accept it
   2567        * as advisory until that feature is implemented. */
   2568       continue;
   2569     }
   2570     if (driver_streq(a, "--fix-cortex-a53-843419")) {
   2571       /* AArch64 GNU/LLD compatibility. Kit does not currently implement this
   2572        * erratum-rewrite pass, so accept the request as advisory. */
   2573       continue;
   2574     }
   2575     if (ld_is_pe_gnu_noop(a, driver_strlen(a))) {
   2576       continue;
   2577     }
   2578     if (driver_streq(a, "--no-undefined")) {
   2579       o->lf.allow_undefined = 0;
   2580       continue;
   2581     }
   2582     if (driver_streq(a, "--allow-undefined") ||
   2583         driver_streq(a, "--allow-shlib-undefined")) {
   2584       o->lf.allow_undefined = 1;
   2585       continue;
   2586     }
   2587     if (driver_streq(a, "-z")) {
   2588       if (++i >= argc) {
   2589         driver_errf(LD_TOOL, "-z requires an argument");
   2590         return 1;
   2591       }
   2592       if (driver_link_flags_record_z(&o->lf, argv[i], driver_strlen(argv[i])) !=
   2593           0)
   2594         return 1;
   2595       continue;
   2596     }
   2597     if (a[0] == '-' && a[1] == 'z' && a[2] != '\0') {
   2598       if (driver_link_flags_record_z(&o->lf, a + 2, driver_strlen(a + 2)) != 0)
   2599         return 1;
   2600       continue;
   2601     }
   2602 
   2603     if (driver_streq(a, "--whole-archive")) {
   2604       o->inputs.cur_whole_archive = 1;
   2605       continue;
   2606     }
   2607     if (driver_streq(a, "--no-whole-archive")) {
   2608       o->inputs.cur_whole_archive = 0;
   2609       continue;
   2610     }
   2611     if (driver_streq(a, "-Bstatic")) {
   2612       o->inputs.cur_link_mode = KIT_LM_STATIC;
   2613       continue;
   2614     }
   2615     if (driver_streq(a, "-Bdynamic")) {
   2616       o->inputs.cur_link_mode = KIT_LM_DYNAMIC;
   2617       continue;
   2618     }
   2619     if (driver_streq(a, "--as-needed")) {
   2620       o->inputs.cur_link_mode = KIT_LM_AS_NEEDED;
   2621       continue;
   2622     }
   2623     if (driver_streq(a, "--no-as-needed")) {
   2624       o->inputs.cur_link_mode = KIT_LM_DYNAMIC;
   2625       continue;
   2626     }
   2627     if (driver_streq(a, "--start-group")) {
   2628       if (o->inputs.cur_group_id != 0) {
   2629         driver_errf(LD_TOOL, "nested --start-group is not supported");
   2630         return 1;
   2631       }
   2632       if (o->next_group_id == UINT8_MAX) {
   2633         driver_errf(LD_TOOL, "too many --start-group occurrences");
   2634         return 1;
   2635       }
   2636       o->next_group_id++;
   2637       o->inputs.cur_group_id = o->next_group_id;
   2638       continue;
   2639     }
   2640     if (driver_streq(a, "--end-group")) {
   2641       if (o->inputs.cur_group_id == 0) {
   2642         driver_errf(LD_TOOL, "--end-group without --start-group");
   2643         return 1;
   2644       }
   2645       o->inputs.cur_group_id = 0;
   2646       continue;
   2647     }
   2648 
   2649     if ((val = arg_eq_value(a, "--build-id")) != NULL) {
   2650       if (driver_link_flags_record_build_id(&o->lf, val) != 0) return 1;
   2651       continue;
   2652     }
   2653     if (driver_streq(a, "--build-id")) {
   2654       /* Bareword form defaults to sha256, matching GNU ld. */
   2655       o->lf.build_id_mode = KIT_BUILDID_SHA256;
   2656       continue;
   2657     }
   2658 
   2659     if (a[0] == '-' && a[1] != '\0') {
   2660       driver_errf(LD_TOOL, "unknown flag: %.*s",
   2661                   KIT_SLICE_ARG(kit_slice_cstr(a)));
   2662       return 1;
   2663     }
   2664 
   2665     {
   2666       const char* path;
   2667       if (ld_sysroot_rewrite_path(o, a, &path) != 0) return 1;
   2668       if (driver_has_suffix(path, ".a") || driver_has_suffix(path, ".rlib")) {
   2669         ld_push_archive(o, path, 0, 0);
   2670       } else if (driver_is_so_filename(path)) {
   2671         ld_push_dso(o, path, 0, 0);
   2672       } else {
   2673         ld_push_object(o, path);
   2674       }
   2675     }
   2676   }
   2677 
   2678   if (!o->output_path) {
   2679     driver_errf(LD_TOOL, "-o is required");
   2680     return 1;
   2681   }
   2682   if (o->inputs.cur_group_id != 0) {
   2683     driver_errf(LD_TOOL, "missing --end-group");
   2684     return 1;
   2685   }
   2686   if (o->inputs.nobject_files == 0 && o->inputs.narchives == 0 &&
   2687       o->inputs.ndsos == 0) {
   2688     driver_errf(LD_TOOL, "no input files");
   2689     ld_usage();
   2690     return 1;
   2691   }
   2692   if (o->wants_hosted_libc && (o->nostdlib || o->nostartfiles)) {
   2693     driver_errf(LD_TOOL,
   2694                 "-lc hosted expansion is disabled by -nostdlib/"
   2695                 "-nostartfiles");
   2696     return 1;
   2697   }
   2698   if (o->shared && o->target.obj != KIT_OBJ_ELF) {
   2699     driver_errf(LD_TOOL,
   2700                 "-shared output is supported only for ELF targets in v1");
   2701     return 1;
   2702   }
   2703   if (o->relocatable) {
   2704     if (o->shared) {
   2705       driver_errf(LD_TOOL, "-r and -shared are incompatible");
   2706       return 1;
   2707     }
   2708     if (o->wants_hosted_libc) {
   2709       driver_errf(LD_TOOL, "-lc hosted expansion requires executable output");
   2710       return 1;
   2711     }
   2712     /* Only an explicit -pie conflicts; a default PIE target.pic is simply
   2713      * overridden by -r (driver_link_pie suppresses pie when relocatable). */
   2714     if (o->pie) {
   2715       driver_errf(LD_TOOL, "-r and -pie are incompatible");
   2716       return 1;
   2717     }
   2718     if (o->lf.interp_path) {
   2719       driver_errf(LD_TOOL, "-dynamic-linker requires executable output");
   2720       return 1;
   2721     }
   2722     if (o->lf.soname || o->lf.nrpaths || o->nrpath_links) {
   2723       driver_errf(LD_TOOL, "-soname/-rpath/-rpath-link require -shared");
   2724       return 1;
   2725     }
   2726     if (o->inputs.ndsos) {
   2727       driver_errf(LD_TOOL, "-r does not support shared-object inputs");
   2728       return 1;
   2729     }
   2730     if (o->script_path) {
   2731       driver_errf(LD_TOOL, "-r does not support linker scripts yet");
   2732       return 1;
   2733     }
   2734     if (o->lf.map_path || o->lf.symbols_path) {
   2735       driver_errf(LD_TOOL, "--map/--symbols require executable output");
   2736       return 1;
   2737     }
   2738   }
   2739   return 0;
   2740 }
   2741 
   2742 /* ---------- options teardown ---------- */
   2743 
   2744 static void ld_options_release(LdOptions* o) {
   2745   size_t bound = o->argv_bound;
   2746   uint32_t i;
   2747   for (i = 0; i < o->nowned_paths; ++i) {
   2748     if (o->owned_paths[i])
   2749       driver_free(o->env, o->owned_paths[i], o->owned_path_sizes[i]);
   2750   }
   2751   /* The shared flag model owns its FLAG strings (defsym/section/map/...), the
   2752    * build-id bytes, and the rpath/defsym/section-start vectors. The shared
   2753    * input set owns the resolved archive/dso paths and its parallel arrays. */
   2754   driver_link_flags_fini(&o->lf);
   2755   driver_link_inputs_fini(&o->inputs);
   2756   driver_cflags_fini(&o->cf, o->env);
   2757   driver_hosted_plan_fini(o->env, &o->hosted);
   2758   /* owned_paths/owned_path_sizes grow independently of argv_bound. */
   2759   driver_free(o->env, o->owned_paths,
   2760               o->cap_owned_paths * sizeof(*o->owned_paths));
   2761   driver_free(o->env, o->owned_path_sizes,
   2762               o->cap_owned_paths * sizeof(*o->owned_path_sizes));
   2763   driver_free(o->env, o->rpath_links, bound * sizeof(*o->rpath_links));
   2764 }
   2765 
   2766 /* ---------- input loading ---------- */
   2767 
   2768 typedef struct LoadedFile {
   2769   const KitFileIO* io;
   2770   KitFileData data;
   2771   int loaded;
   2772 } LoadedFile;
   2773 
   2774 static int load_file(const KitFileIO* io, const char* path, LoadedFile* out) {
   2775   out->io = io;
   2776   out->loaded = 0;
   2777   out->data.data = NULL;
   2778   out->data.size = 0;
   2779   out->data.token = NULL;
   2780   if (io->read_all(io->user, path, &out->data) != KIT_OK) return 1;
   2781   out->loaded = 1;
   2782   return 0;
   2783 }
   2784 
   2785 static void release_file(LoadedFile* lf) {
   2786   if (lf->loaded && lf->io && lf->io->release) {
   2787     lf->io->release(lf->io->user, &lf->data);
   2788   }
   2789   lf->loaded = 0;
   2790 }
   2791 
   2792 static void release_all(LoadedFile* arr, uint32_t n) {
   2793   uint32_t i;
   2794   if (!arr) return;
   2795   for (i = 0; i < n; ++i) release_file(&arr[i]);
   2796 }
   2797 
   2798 static KitStatus ld_write_link_report(const LdOptions* o, const KitFileIO* io,
   2799                                       KitLinkSession* link, const char* path,
   2800                                       int symbols) {
   2801   KitWriter* w = NULL;
   2802   KitStatus st;
   2803   if (!path) return KIT_OK;
   2804   if (io->open_writer(io->user, path, &w) != KIT_OK) {
   2805     driver_errf(LD_TOOL, "failed to open %s output: %.*s",
   2806                 symbols ? "symbols" : "map",
   2807                 KIT_SLICE_ARG(kit_slice_cstr(path)));
   2808     return KIT_IO;
   2809   }
   2810   st = symbols ? kit_link_session_write_symbols(link, o->lf.symbols_format, w)
   2811                : kit_link_session_write_map(link, w);
   2812   if (st == KIT_OK) st = kit_writer_status(w);
   2813   kit_writer_close(w);
   2814   if (st != KIT_OK) {
   2815     driver_errf(LD_TOOL, "failed to write %s: %.*s",
   2816                 symbols ? "symbols" : "map",
   2817                 KIT_SLICE_ARG(kit_slice_cstr(path)));
   2818   }
   2819   return st;
   2820 }
   2821 
   2822 /* --cref FILE: cross-reference table side file. */
   2823 static KitStatus ld_write_cref(const LdOptions* o, const KitFileIO* io,
   2824                                KitLinkSession* link) {
   2825   KitWriter* w = NULL;
   2826   KitStatus st;
   2827   if (!o->lf.cref_path) return KIT_OK;
   2828   if (io->open_writer(io->user, o->lf.cref_path, &w) != KIT_OK) {
   2829     driver_errf(LD_TOOL, "failed to open cref output: %.*s",
   2830                 KIT_SLICE_ARG(kit_slice_cstr(o->lf.cref_path)));
   2831     return KIT_IO;
   2832   }
   2833   st = kit_link_session_write_cref(link, w);
   2834   if (st == KIT_OK) st = kit_writer_status(w);
   2835   kit_writer_close(w);
   2836   if (st != KIT_OK)
   2837     driver_errf(LD_TOOL, "failed to write cref: %.*s",
   2838                 KIT_SLICE_ARG(kit_slice_cstr(o->lf.cref_path)));
   2839   return st;
   2840 }
   2841 
   2842 /* --print-memory-usage: GNU-ld-style per-region summary to stdout. */
   2843 static KitStatus ld_print_memory_usage(LdOptions* o, KitLinkSession* link) {
   2844   KitWriter* w;
   2845   KitStatus st;
   2846   if (!o->lf.print_memory_usage) return KIT_OK;
   2847   w = driver_stdout_writer(o->env);
   2848   if (!w) {
   2849     driver_errf(LD_TOOL, "failed to print memory usage");
   2850     return KIT_IO;
   2851   }
   2852   st = kit_link_session_write_memory_usage(link, w);
   2853   if (st == KIT_OK) st = kit_writer_status(w);
   2854   kit_writer_close(w);
   2855   if (st != KIT_OK) driver_errf(LD_TOOL, "failed to print memory usage");
   2856   return st;
   2857 }
   2858 
   2859 /* Resolve and apply the hosted libc profile: hosted lib-search dirs (so user
   2860  * -l flags resolve against the sysroot), the before/after/final crt inputs in
   2861  * link order, and the interpreter. The shared helper folds all of this (plus
   2862  * the system includes/defines that ld discards) into one call; the runtime
   2863  * archive is later inserted before the final group via
   2864  * driver_link_inputs_insert_runtime_archives. The wants-hosted / -shared guard
   2865  * stays here. */
   2866 static int ld_apply_hosted(LdOptions* o) {
   2867   int shared_hosted = o->shared && driver_target_shared_uses_hosted(o->target);
   2868   if (!o->wants_hosted_libc || (o->shared && !shared_hosted)) return 0;
   2869   return driver_link_inputs_apply_hosted(&o->inputs, &o->hosted, &o->cf, &o->lf,
   2870                                          o->target, o->sysroot, o->static_link,
   2871                                          o->shared, o->nostartfiles,
   2872                                          /*link_action=*/1);
   2873 }
   2874 
   2875 /* ---------- link execution ---------- */
   2876 
   2877 /* Run the link. Returns 0 on success, nonzero on error. The caller owns
   2878  * `o` and any allocations within; this function only owns transient
   2879  * loaded-file buffers and the writer/compiler it constructs. */
   2880 static int ld_run_link(LdOptions* o) {
   2881   KitContext ctx = driver_env_to_context(o->env);
   2882   const KitFileIO* io = ctx.file_io;
   2883   KitTarget* target = NULL;
   2884   KitCompiler* compiler = NULL;
   2885   KitWriter* writer = NULL;
   2886   LoadedFile* obj_lf = NULL;
   2887   LoadedFile* arch_lf = NULL;
   2888   LoadedFile* dso_lf = NULL;
   2889   LoadedFile script_lf = {0};
   2890   KitSlice* obj_in = NULL;
   2891   KitLinkArchiveInput* arch_in = NULL;
   2892   KitSlice* dso_in = NULL;
   2893   KitLinkInputOrder* order = NULL;
   2894   KitLinkScript* script = NULL;
   2895   KitLinkSession* link = NULL;
   2896   DriverRuntimeSupport runtime = {0};
   2897   DriverRuntimeArchive rt_archive = {0};
   2898   uint32_t i;
   2899   uint32_t norder = 0;
   2900   uint32_t initial_nobject_files;
   2901   int runtime_resolved = 0;
   2902   int rc = 1;
   2903 
   2904   if (!io || !io->read_all || !io->open_writer) {
   2905     driver_errf(LD_TOOL, "host file I/O unavailable");
   2906     return 1;
   2907   }
   2908 
   2909   /* Load the caller's object files first so the final target is known before
   2910    * deciding which hosted CRT/libc profile and compiler-runtime archive to
   2911    * add. The arrays are sized to the argv bound because hosted expansion may
   2912    * append start files after target detection. */
   2913   initial_nobject_files = o->inputs.nobject_files;
   2914   if (o->argv_bound) {
   2915     obj_lf = driver_alloc_zeroed(o->env, o->argv_bound * sizeof(*obj_lf));
   2916     obj_in = driver_alloc_zeroed(o->env, o->argv_bound * sizeof(*obj_in));
   2917     if (!obj_lf || !obj_in) {
   2918       driver_errf(LD_TOOL, "out of memory");
   2919       goto out;
   2920     }
   2921   }
   2922   for (i = 0; i < initial_nobject_files; ++i) {
   2923     const char* path = o->inputs.object_files[i];
   2924     if (load_file(io, path, &obj_lf[i]) != 0) {
   2925       driver_errf(LD_TOOL, "failed to read: %.*s",
   2926                   KIT_SLICE_ARG(kit_slice_cstr(path)));
   2927       goto out;
   2928     }
   2929     obj_in[i].data = obj_lf[i].data.data;
   2930     obj_in[i].len = obj_lf[i].data.size;
   2931   }
   2932 
   2933   /* Auto-detect target from the object inputs, falling back to the host target.
   2934    * The PIC default is a property of the *target*, not the host: hosted targets
   2935    * default to PIE, freestanding ones to no-PIE (driver_default_pic). The
   2936    * host's default PIC must never leak onto a detected cross target; only an
   2937    * EXPLICIT -static/-pie/-no-pie/-shared wins.
   2938    *
   2939    * Detection takes the arch from the first object, but the OS from any object:
   2940    * a freestanding (`*-none-elf`, EI_OSABI=STANDALONE) object means a
   2941    * freestanding link even if a foreign object (e.g. a clang-assembled startup
   2942    * stub, which stamps EI_OSABI=SysV and so decodes as Linux) appears first. */
   2943   if (!o->target_explicit && initial_nobject_files > 0) {
   2944     KitTargetSpec detected;
   2945     if (kit_detect_target(obj_lf[0].data.data, obj_lf[0].data.size,
   2946                           &detected) == KIT_OK) {
   2947       uint8_t pic = o->target.pic;
   2948       uint32_t oi;
   2949       for (oi = 1; oi < initial_nobject_files; ++oi) {
   2950         KitTargetSpec t;
   2951         if (kit_detect_target(obj_lf[oi].data.data, obj_lf[oi].data.size, &t) ==
   2952             KIT_OK) {
   2953           if (t.os == KIT_OS_FREESTANDING) detected.os = KIT_OS_FREESTANDING;
   2954           /* Reconcile the float ABI across inputs. A foreign startup stub (e.g.
   2955            * a clang-assembled .S) may not carry the float-ABI e_flags that the
   2956            * kit-compiled objects do, so prefer any object's hardware ABI
   2957            * (single/double) over a soft/unset reading. This picks the runtime
   2958            * variant (rv32 ilp32 vs ilp32f) the real code was compiled for even
   2959            * when obj[0] is the stub. */
   2960           if (detected.float_abi != KIT_FLOAT_ABI_SINGLE &&
   2961               detected.float_abi != KIT_FLOAT_ABI_DOUBLE &&
   2962               (t.float_abi == KIT_FLOAT_ABI_SINGLE ||
   2963                t.float_abi == KIT_FLOAT_ABI_DOUBLE))
   2964             detected.float_abi = t.float_abi;
   2965         }
   2966       }
   2967       o->target = detected;
   2968       if (o->pic_explicit)
   2969         o->target.pic = pic;
   2970       else
   2971         o->target.pic = driver_default_pic(o->target.obj, o->target.os);
   2972     }
   2973   }
   2974 
   2975   if (!o->relocatable && o->target.os == KIT_OS_FREESTANDING &&
   2976       o->inputs.ndsos) {
   2977     driver_errf(LD_TOOL,
   2978                 "freestanding executable links do not accept DSO inputs");
   2979     goto out;
   2980   }
   2981 
   2982   /* Strict freestanding policy: every input object must agree with the link
   2983    * target's arch and object format. A `*-none-*` link silently mis-linking a
   2984    * foreign-arch object (the default detection trusts obj[0]'s arch) is a hard
   2985    * error here. */
   2986   if (!o->relocatable && o->target.os == KIT_OS_FREESTANDING) {
   2987     uint32_t oi;
   2988     for (oi = 0; oi < initial_nobject_files; ++oi) {
   2989       KitTargetSpec t;
   2990       if (kit_detect_target(obj_lf[oi].data.data, obj_lf[oi].data.size, &t) !=
   2991           KIT_OK)
   2992         continue;
   2993       if (t.arch != o->target.arch || t.obj != o->target.obj) {
   2994         driver_errf(LD_TOOL,
   2995                     "freestanding link: input '%s' arch/format does not match "
   2996                     "the link target",
   2997                     o->inputs.object_files[oi]);
   2998         goto out;
   2999       }
   3000     }
   3001   }
   3002 
   3003   if (ld_apply_hosted(o) != 0) goto out;
   3004 
   3005   /* Hosted dynamic link: default to PIE, matching what `kit cc` does.
   3006    * Without PIE the link_dyn path (PLT/GOT/.rela.plt) is not entered and
   3007    * any CALL26/PLT32 reloc against an imported symbol faults at apply time.
   3008    * Only override when the user has not made an explicit PIC choice.
   3009    * Non-PIE (ET_EXEC) dynamic linking is not yet supported; reject early. */
   3010   if (o->wants_hosted_libc && !o->static_link && !o->shared &&
   3011       !o->relocatable) {
   3012     if (o->pic_explicit && !o->pie) {
   3013       driver_errf(LD_TOOL,
   3014                   "-lc with -no-pie is not supported; use -pie (default) "
   3015                   "or -static for a static link");
   3016       goto out;
   3017     }
   3018     if (!o->pic_explicit) o->pie = 1;
   3019   }
   3020 
   3021   /* Auto-link kit's compiler runtime for any target that has a variant unless
   3022    * the caller has already supplied compiler builtins (Rust's
   3023    * libcompiler_builtins*.rlib, libgcc.a, clang_rt.builtins, ...). This keeps
   3024    * `kit ld object.o` useful while letting `kit ld` behave like a system
   3025    * linker under external compiler drivers. A target with no variant is
   3026    * implicitly -nostdlib and supplies its own libkit_rt.a on the command line,
   3027    * so skip resolution rather than erroring. -nostdlib forces the skip for any
   3028    * target.
   3029    */
   3030   if (!o->relocatable && !o->no_default_libs && !o->has_compiler_runtime &&
   3031       driver_runtime_has_variant(o->target)) {
   3032     if (driver_runtime_resolve(o->env, o->support_dir, o->driver_path,
   3033                                &runtime) != 0) {
   3034       driver_errf(LD_TOOL, "support dir not found");
   3035       goto out;
   3036     }
   3037     runtime_resolved = 1;
   3038     if (driver_runtime_prepare_archive(o->env, LD_TOOL, &runtime, o->target, 0,
   3039                                        &rt_archive) != 0)
   3040       goto out;
   3041     /* Insert before the hosted plan's `final` (crt-end) group — the shared
   3042      * helper also does the Windows two-position insert that the old single
   3043      * append lacked. ld's runtime is never whole-archive, matching cc/build. */
   3044     driver_link_inputs_insert_runtime_archives(
   3045         &o->inputs, &rt_archive, o->target, o->hosted.nfinal, o->hosted.nafter);
   3046   }
   3047 
   3048   if (ld_expand_dso_scripts(o) != 0) goto out;
   3049 
   3050   for (i = initial_nobject_files; i < o->inputs.nobject_files; ++i) {
   3051     const char* path = o->inputs.object_files[i];
   3052     if (load_file(io, path, &obj_lf[i]) != 0) {
   3053       driver_errf(LD_TOOL, "failed to read: %.*s",
   3054                   KIT_SLICE_ARG(kit_slice_cstr(path)));
   3055       goto out;
   3056     }
   3057     obj_in[i].data = obj_lf[i].data.data;
   3058     obj_in[i].len = obj_lf[i].data.size;
   3059   }
   3060 
   3061   if (o->inputs.narchives) {
   3062     arch_lf =
   3063         driver_alloc_zeroed(o->env, o->inputs.narchives * sizeof(*arch_lf));
   3064     arch_in =
   3065         driver_alloc_zeroed(o->env, o->inputs.narchives * sizeof(*arch_in));
   3066     if (!arch_lf || !arch_in) {
   3067       driver_errf(LD_TOOL, "out of memory");
   3068       goto out;
   3069     }
   3070   }
   3071   if (o->inputs.ndsos) {
   3072     dso_lf = driver_alloc_zeroed(o->env, o->inputs.ndsos * sizeof(*dso_lf));
   3073     dso_in = driver_alloc_zeroed(o->env, o->inputs.ndsos * sizeof(*dso_in));
   3074     if (!dso_lf || !dso_in) {
   3075       driver_errf(LD_TOOL, "out of memory");
   3076       goto out;
   3077     }
   3078   }
   3079 
   3080   /* Load archives. */
   3081   for (i = 0; i < o->inputs.narchives; ++i) {
   3082     const DriverArchiveInput* a = &o->inputs.archives[i];
   3083     if (load_file(io, a->path, &arch_lf[i]) != 0) {
   3084       driver_errf(LD_TOOL, "failed to read: %.*s",
   3085                   KIT_SLICE_ARG(kit_slice_cstr(a->path)));
   3086       goto out;
   3087     }
   3088     arch_in[i].name = kit_slice_cstr(a->path);
   3089     arch_in[i].bytes.data = arch_lf[i].data.data;
   3090     arch_in[i].bytes.len = arch_lf[i].data.size;
   3091     arch_in[i].whole_archive = a->whole_archive;
   3092     arch_in[i].link_mode = a->link_mode;
   3093     arch_in[i].group_id = a->group_id;
   3094   }
   3095   /* Load shared objects. */
   3096   for (i = 0; i < o->inputs.ndsos; ++i) {
   3097     const DriverDsoInput* d = &o->inputs.dsos[i];
   3098     if (load_file(io, d->path, &dso_lf[i]) != 0) {
   3099       driver_errf(LD_TOOL, "failed to read: %.*s",
   3100                   KIT_SLICE_ARG(kit_slice_cstr(d->path)));
   3101       goto out;
   3102     }
   3103     dso_in[i].data = dso_lf[i].data.data;
   3104     dso_in[i].len = dso_lf[i].data.size;
   3105   }
   3106 
   3107   /* Load and parse the linker script (if any). The structured script is
   3108    * arena-owned by the compiler; we free it explicitly before the
   3109    * compiler is destroyed. */
   3110   if (o->script_path) {
   3111     if (load_file(io, o->script_path, &script_lf) != 0) {
   3112       driver_errf(LD_TOOL, "failed to read: %.*s",
   3113                   KIT_SLICE_ARG(kit_slice_cstr(o->script_path)));
   3114       goto out;
   3115     }
   3116   }
   3117 
   3118   {
   3119     KitTargetOptions topts;
   3120     memset(&topts, 0, sizeof topts);
   3121     topts.spec = o->target;
   3122     /* A linker target auto-detected from RISC-V ELF inputs already carries
   3123      * the psABI in spec.float_abi.  kit_target_new normally derives that
   3124      * field from -march/-mabi, so reproduce the detected profile here rather
   3125      * than letting the rv32 default (ilp32 soft-float) overwrite ilp32f/d.
   3126      * The ISA matters for rv32 hardware ABIs because its conservative default
   3127      * deliberately omits F/D; rv64's default already includes both. */
   3128     if (o->target.arch == KIT_ARCH_RV32) {
   3129       if (o->target.float_abi == KIT_FLOAT_ABI_SINGLE) {
   3130         topts.isa = kit_slice_cstr("rv32imafc_zicsr_zifencei");
   3131         topts.abi = kit_slice_cstr("ilp32f");
   3132       } else if (o->target.float_abi == KIT_FLOAT_ABI_DOUBLE) {
   3133         topts.isa = kit_slice_cstr("rv32imafdc_zicsr_zifencei");
   3134         topts.abi = kit_slice_cstr("ilp32d");
   3135       } else if (o->target.float_abi == KIT_FLOAT_ABI_SOFT) {
   3136         topts.abi = kit_slice_cstr("ilp32");
   3137       }
   3138     } else if (o->target.arch == KIT_ARCH_RV64) {
   3139       if (o->target.float_abi == KIT_FLOAT_ABI_SINGLE)
   3140         topts.abi = kit_slice_cstr("lp64f");
   3141       else if (o->target.float_abi == KIT_FLOAT_ABI_DOUBLE)
   3142         topts.abi = kit_slice_cstr("lp64d");
   3143       else if (o->target.float_abi == KIT_FLOAT_ABI_SOFT)
   3144         topts.abi = kit_slice_cstr("lp64");
   3145     }
   3146     if (kit_target_new(&ctx, &topts, &target) != KIT_OK ||
   3147         driver_compiler_new(target, &ctx, &compiler) != KIT_OK) {
   3148       driver_errf(LD_TOOL, "failed to initialize compiler");
   3149       goto out;
   3150     }
   3151   }
   3152 
   3153   if (script_lf.loaded) {
   3154     KitSlice script_text = {.s = (const char*)script_lf.data.data,
   3155                             .len = script_lf.data.size};
   3156     if (kit_link_script_parse(&ctx, script_text, &script) != KIT_OK) {
   3157       /* The parser reports a diagnostic via env.diag. */
   3158       goto out;
   3159     }
   3160   }
   3161 
   3162   /* Translate the recorded link-item order into the engine's KitLinkInputOrder
   3163    * list. ld records no compiled-source items, so the source args are NULL. */
   3164   if (o->inputs.nlink_items) {
   3165     order =
   3166         driver_alloc_zeroed(o->env, o->inputs.nlink_items * sizeof(*order));
   3167     if (!order) {
   3168       driver_errf(LD_TOOL, "out of memory");
   3169       goto out;
   3170     }
   3171   }
   3172   norder = driver_link_inputs_build_order(&o->inputs, NULL, NULL, 0, order);
   3173 
   3174   if (o->lf.soname || o->lf.nrpaths || o->nrpath_links) {
   3175     if (!o->shared) {
   3176       driver_errf(LD_TOOL, "-soname/-rpath/-rpath-link require -shared");
   3177       goto out;
   3178     }
   3179   }
   3180 
   3181   {
   3182     KitLinkSessionOptions lopts;
   3183     KitStatus st;
   3184     KitSlice* rpath_slices = NULL;
   3185     uint8_t output_kind = o->relocatable ? KIT_LINK_OUTPUT_RELOCATABLE
   3186                           : o->shared    ? KIT_LINK_OUTPUT_SHARED
   3187                                          : KIT_LINK_OUTPUT_EXE;
   3188     /* The shared flag model fills entry/text-base/defsym/section-start/orphan/
   3189      * fatal-warnings/build-id/gc/strip/pe-subsystem/interp/soname/rpath. It also
   3190      * derives lopts.pie via driver_link_pie, but ld overrides that below. */
   3191     if (driver_link_flags_fill_options(&o->lf, o->target, o->pie, o->shared,
   3192                                        o->relocatable, output_kind, script,
   3193                                        &lopts, &rpath_slices) != 0)
   3194       goto out;
   3195     /* GNU ld and lld default to a non-PIE ET_EXEC; PIE is opt-in via -pie. The
   3196      * linker imposes no hosted-PIE default the way a compiler driver does (kit
   3197      * cc still defaults hosted executables to PIE through the link API) — a
   3198      * freestanding or static link has no dynamic loader to apply a PIE image's
   3199      * relocations or choose its base, so a PIE default produces a binary that
   3200      * faults under a direct/qemu loader (its writable segments sit below
   3201      * mmap_min_addr at the vaddr-0 PIE base). -shared/-r never PIE. Callers that
   3202      * want a hosted PIE executable pass -pie explicitly (as the musl dynamic
   3203      * lane and real compiler drivers do); the hosted-libc dynamic lane forces
   3204      * o->pie = 1 above. So override the shared driver_link_pie decision (which
   3205      * would default hosted target.pic == KIT_PIC_PIE targets to PIE) back to
   3206      * ld's policy. */
   3207     lopts.pie = o->pie && !o->shared && !o->relocatable;
   3208     /* Strict freestanding policy: a `*-none-*` (FREESTANDING) static non-PIE
   3209      * executable must reject dynamic-link artifacts and cross-input
   3210      * target/format mismatches. Only the EXE lane imposes it; -r/-shared do
   3211      * not. --allow-undefined still relaxes undef references but not these
   3212      * structural rejections. Set after fill_options, which derives lopts.pie. */
   3213     if (lopts.output_kind == KIT_LINK_OUTPUT_EXE && !lopts.pie &&
   3214         o->target.os == KIT_OS_FREESTANDING)
   3215       lopts.freestanding_strict = true;
   3216     (void)o->export_dynamic;
   3217 
   3218     st = kit_link_session_new(compiler, &lopts, &link);
   3219     for (i = 0; i < norder && st == KIT_OK; ++i) {
   3220       const KitLinkInputOrder* ord = &order[i];
   3221       switch ((KitLinkInputOrderKind)ord->kind) {
   3222         case KIT_LINK_INPUT_OBJ:
   3223         case KIT_LINK_INPUT_OBJ_BYTES:
   3224           st = kit_link_session_add_obj_bytes(
   3225               link, kit_slice_cstr(o->inputs.object_files[ord->index]),
   3226               &obj_in[ord->index]);
   3227           break;
   3228         case KIT_LINK_INPUT_ARCHIVE:
   3229           st = kit_link_session_add_archive_bytes(link, &arch_in[ord->index]);
   3230           break;
   3231         case KIT_LINK_INPUT_DSO:
   3232           st = kit_link_session_add_dso_bytes(
   3233               link, kit_slice_cstr(o->inputs.dsos[ord->index].path),
   3234               &dso_in[ord->index]);
   3235           break;
   3236       }
   3237     }
   3238     /* Parse and validate the complete input set before an output writer is
   3239      * opened.  Apart from preserving the primary named-input diagnostic, this
   3240      * means a malformed object/archive/DSO or a link-resolution failure cannot
   3241      * create even a temporary output artifact. */
   3242     if (st == KIT_OK) st = kit_link_session_resolve(link);
   3243     if (st == KIT_OK &&
   3244         io->open_writer(io->user, o->output_path, &writer) != KIT_OK) {
   3245       driver_errf(LD_TOOL, "failed to open output: %.*s",
   3246                   KIT_SLICE_ARG(kit_slice_cstr(o->output_path)));
   3247       st = KIT_IO;
   3248     }
   3249     if (st == KIT_OK) st = kit_link_session_emit(link, writer);
   3250     if (st == KIT_OK) st = ld_write_link_report(o, io, link, o->lf.map_path, 0);
   3251     if (st == KIT_OK)
   3252       st = ld_write_link_report(o, io, link, o->lf.symbols_path, 1);
   3253     if (st == KIT_OK) st = ld_write_cref(o, io, link);
   3254     if (st == KIT_OK) st = ld_print_memory_usage(o, link);
   3255     rc = st == KIT_OK ? 0 : 1;
   3256     driver_link_flags_free_rpath_slices(&o->lf, rpath_slices);
   3257   }
   3258 
   3259 out:
   3260   if (writer) {
   3261     if (rc != 0) driver_writer_abort(writer);
   3262     kit_writer_close(writer);
   3263   }
   3264   kit_link_session_free(link);
   3265   /* Match compiler/linker drivers: successful link outputs get executable
   3266    * file modes while still respecting the process umask. Done after closing
   3267    * the writer so the bits are stable on disk. */
   3268   if (rc == 0 && o->output_path && !o->relocatable) {
   3269     if (driver_mark_executable_output(o->output_path) != 0) {
   3270       driver_errf(LD_TOOL, "failed to set executable mode: %.*s",
   3271                   KIT_SLICE_ARG(kit_slice_cstr(o->output_path)));
   3272       rc = 1;
   3273     }
   3274   }
   3275   if (script) kit_link_script_free(&ctx, script);
   3276   if (compiler) driver_compiler_free(compiler);
   3277   kit_target_free(target);
   3278   driver_runtime_archive_fini(o->env, &rt_archive);
   3279   if (runtime_resolved) driver_runtime_support_fini(o->env, &runtime);
   3280   release_file(&script_lf);
   3281   release_all(arch_lf, o->inputs.narchives);
   3282   release_all(obj_lf, o->inputs.nobject_files);
   3283   release_all(dso_lf, o->inputs.ndsos);
   3284   if (arch_in)
   3285     driver_free(o->env, arch_in, o->inputs.narchives * sizeof(*arch_in));
   3286   if (arch_lf)
   3287     driver_free(o->env, arch_lf, o->inputs.narchives * sizeof(*arch_lf));
   3288   if (obj_in) driver_free(o->env, obj_in, o->argv_bound * sizeof(*obj_in));
   3289   if (obj_lf) driver_free(o->env, obj_lf, o->argv_bound * sizeof(*obj_lf));
   3290   if (dso_in) driver_free(o->env, dso_in, o->inputs.ndsos * sizeof(*dso_in));
   3291   if (dso_lf) driver_free(o->env, dso_lf, o->inputs.ndsos * sizeof(*dso_lf));
   3292   if (order)
   3293     driver_free(o->env, order, o->inputs.nlink_items * sizeof(*order));
   3294   return rc;
   3295 }
   3296 
   3297 int driver_ld(int argc, char** argv) {
   3298   DriverEnv env;
   3299   LdOptions lo = {0};
   3300   int rc;
   3301 
   3302   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
   3303     driver_help_ld();
   3304     return 0;
   3305   }
   3306 
   3307   driver_env_init(&env);
   3308   lo.env = &env;
   3309   lo.driver_path = argv[0];
   3310 
   3311   if (ld_parse(argc, argv, &lo) != 0) {
   3312     ld_options_release(&lo);
   3313     driver_env_fini(&env);
   3314     return 2;
   3315   }
   3316 
   3317   rc = ld_run_link(&lo);
   3318 
   3319   ld_options_release(&lo);
   3320   driver_env_fini(&env);
   3321   return rc;
   3322 }