kit

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

ld.c (64367B)


      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 "runtime.h"
     11 
     12 /* `kit ld` — link object/archive inputs into an executable, shared
     13  * library, or relocatable object. The driver loads each input via
     14  * env.file_io, optionally parses a `-T` linker script into the structured
     15  * form, and calls the public link APIs directly so the full link surface
     16  * (per-archive flags, group cycling, build-id, soname/rpath/exports) is
     17  * reachable without thickening the convenience KitOptions struct.
     18  *
     19  * Supported flags:
     20  *   -o out                            output path (required, exactly one)
     21  *   -e symbol                         entry symbol
     22  *   -T script.ld                      linker script (parsed, not raw)
     23  *   --support-dir DIR                 kit support root for compiler rt
     24  *   --sysroot DIR                     hosted C runtime/sysroot root
     25  *   -L dir                            library search path (-l targets)
     26  *   -l c                              enable hosted CRT/libc expansion
     27  *   -l name                           resolves via -L (.so preferred unless
     28  *                                     -Bstatic/-static)
     29  *   -static / -pie / -no-pie          target.pic
     30  *   -shared                           emit a shared library / dylib
     31  *   -r / --relocatable                emit a relocatable partial-link object
     32  *   -soname NAME                      DT_SONAME / LC_ID_DYLIB
     33  *   -rpath DIR                        DT_RPATH/DT_RUNPATH entry (repeatable)
     34  *   -rpath-link DIR                   shared-lib search at link time
     35  * (repeatable; accepted, currently advisory)
     36  *   --enable-new-dtags                rpath entries emit as DT_RUNPATH
     37  * (default)
     38  *   --disable-new-dtags               rpath entries emit as DT_RPATH
     39  *   -E / --export-dynamic             promote all defined globals to the
     40  * dynamic symbol table
     41  *   --whole-archive / --no-whole-archive
     42  *                                     positional state for following .a
     43  *   --gc-sections / --no-gc-sections  drop unreferenced sections
     44  *   -Bstatic / -Bdynamic              positional link-mode for following .a
     45  *   --as-needed / --no-as-needed      positional link-mode for following .a
     46  *   --start-group / --end-group       cyclic-resolution group of archives
     47  *   --build-id={none|sha256|uuid|0xHEX}
     48  */
     49 
     50 #define LD_TOOL "ld"
     51 
     52 /* Per-archive metadata mirroring the relevant subset of
     53  * KitLinkArchiveInput plus driver-side ownership info. */
     54 typedef struct LdArchive {
     55   const char* path;      /* path used for both open and KitSliceInput.name */
     56   int owned;             /* 1 if `path` was alloc'd by lib_resolve         */
     57   size_t owned_size;     /* allocation size (for driver_free)              */
     58   uint8_t whole_archive; /* nonzero == --whole-archive                     */
     59   uint8_t link_mode;     /* KitLinkMode (-Bstatic/-Bdynamic/--as-needed) */
     60   uint8_t group_id;      /* cyclic resolution group id; 0 = single-pass    */
     61 } LdArchive;
     62 
     63 /* Per-DSO ownership info. The DSO bytes are loaded straight off disk
     64  * via env->file_io into the KitSlice passed to libkit; only
     65  * the path itself may need to be free'd if it came from -l<name>
     66  * resolution. */
     67 typedef struct LdDso {
     68   const char* path;  /* path used for both open and KitSliceInput.name */
     69   int owned;         /* 1 if `path` was alloc'd by lib_resolve            */
     70   size_t owned_size; /* allocation size (for driver_free)                 */
     71 } LdDso;
     72 
     73 typedef struct LdOptions {
     74   DriverEnv* env;
     75   const char* driver_path;
     76   size_t argv_bound;
     77 
     78   KitTargetSpec target;
     79 
     80   const char* output_path; /* -o */
     81   int output_seen;
     82   const char* entry;       /* -e */
     83   const char* script_path; /* -T */
     84   int text_base_set;       /* -Ttext seen */
     85   uint64_t text_base;      /* -Ttext ADDR: static ET_EXEC image base */
     86   int no_default_libs;     /* -nostdlib / --no-default-libs */
     87   int pic_explicit;        /* -static / -pie / -no-pie / -shared seen */
     88   const char* support_dir; /* --support-dir */
     89   const char* sysroot;     /* --sysroot / KIT_SYSROOT */
     90   uint16_t pe_subsystem;   /* KitPeSubsystem */
     91   /* PT_INTERP path. NULL means "let libkit pick the target default
     92    * (e.g. /lib/ld-musl-aarch64.so.1)". Set by -dynamic-linker /
     93    * --dynamic-linker. */
     94   const char* interp_path;
     95   int pie; /* -pie was requested */
     96 
     97   const char** object_files;
     98   uint32_t nobject_files;
     99 
    100   LdArchive* archives;
    101   uint32_t narchives;
    102 
    103   /* Shared-object inputs (positional .so / .so.N or `-l<name>` under
    104    * -Bdynamic). The runtime loader resolves these by SONAME at link
    105    * time → DT_NEEDED entries. */
    106   LdDso* dsos;
    107   uint32_t ndsos;
    108   KitLinkInputOrder* order;
    109   uint32_t norder;
    110 
    111   const char** lib_dirs; /* -L */
    112   uint32_t nlib_dirs;
    113   char** owned_paths; /* sysroot-expanded argv/search paths */
    114   size_t* owned_path_sizes;
    115   uint32_t nowned_paths;
    116 
    117   /* Shared-library output state. */
    118   int shared;          /* -shared */
    119   int relocatable;     /* -r / --relocatable */
    120   const char* soname;  /* -soname NAME */
    121   const char** rpaths; /* -rpath DIR (repeatable) */
    122   uint32_t nrpaths;
    123   const char** rpath_links; /* -rpath-link DIR (advisory) */
    124   uint32_t nrpath_links;
    125   int new_dtags;       /* 1=DT_RUNPATH (default), 0=DT_RPATH */
    126   int export_dynamic;  /* -E / --export-dynamic */
    127   int gc_sections;     /* --gc-sections / --no-gc-sections */
    128   int strip_debug;     /* -S / --strip-debug */
    129   int allow_undefined; /* shared output undefined-symbol policy */
    130   int static_link;     /* -static: hosted libc should pick static profile */
    131   int wants_hosted_libc; /* -lc: expand crt + libc through hosted resolver */
    132   DriverHostedPlan hosted;
    133 
    134   /* --build-id state */
    135   uint8_t build_id_mode;   /* KitBuildIdMode */
    136   uint8_t* build_id_bytes; /* USER mode: parsed hex, owned */
    137   uint32_t build_id_len;
    138   size_t build_id_alloc; /* allocation size for free */
    139 
    140   /* Mutable parse state for positional archive flags / groups. */
    141   uint8_t cur_whole_archive; /* pending --whole-archive flag  */
    142   uint8_t cur_link_mode;     /* KitLinkMode for following inputs */
    143   uint8_t cur_group_id;      /* 0 outside any --start-group   */
    144   uint8_t next_group_id;     /* increments on --start-group   */
    145 
    146   /* Opt-in: treat `/...` arguments as MSVC link.exe flags. Off by
    147    * default so legacy paths like `/usr/lib/foo.o` remain inputs. */
    148   int ms_link_driver;
    149 } LdOptions;
    150 
    151 static void ld_usage(void) {
    152   driver_errf(LD_TOOL, "%.*s",
    153               KIT_SLICE_ARG(KIT_SLICE_LIT(
    154                   "usage: kit ld -o out [options...] inputs.o|inputs.a...\n"
    155                   "       kit ld --help    for full option reference")));
    156 }
    157 
    158 void driver_help_ld(void) {
    159   driver_printf(
    160       "%.*s",
    161       KIT_SLICE_ARG(KIT_SLICE_LIT(
    162           "kit ld — link objects/archives into an executable or shared "
    163           "library\n"
    164           "\n"
    165           "USAGE\n"
    166           "  kit ld -o OUT [options] inputs.o ... inputs.a ...\n"
    167           "  kit ld -shared -o libfoo.so [options] inputs.o ...\n"
    168           "  kit ld -r -o partial.o inputs.o ...\n"
    169           "\n"
    170           "DESCRIPTION\n"
    171           "  Loads each input via host file I/O, optionally parses a -T "
    172           "linker\n"
    173           "  script, and emits an executable (default) or shared library\n"
    174           "  (-shared). The full link surface is exposed: per-archive flags,\n"
    175           "  cyclic-resolution groups, build-id, soname/rpath/exports.\n"
    176           "\n"
    177           "OUTPUT\n"
    178           "  -o PATH                   Output path (required, exactly one)\n"
    179           "  -shared                   Emit a position-independent shared "
    180           "library "
    181           "/\n"
    182           "                            dylib instead of an executable\n"
    183           "  -r, --relocatable         Emit a relocatable partial-link object\n"
    184           "\n"
    185           "ENTRY / SCRIPT\n"
    186           "  -e SYMBOL                 Entry symbol\n"
    187           "  --subsystem NAME          PE subsystem: console or windows\n"
    188           "  -T SCRIPT.ld              Use a linker script (parsed, not raw)\n"
    189           "\n"
    190           "TARGET\n"
    191           "  -static                   Non-PIC, non-PIE\n"
    192           "  -pie                      Position-independent executable\n"
    193           "  -no-pie                   Disable PIE\n"
    194           "  --support-dir DIR         kit support root for compiler "
    195           "runtime\n"
    196           "  --sysroot DIR             hosted C runtime/sysroot root\n"
    197           "  (target is otherwise auto-detected from the first object input)\n"
    198           "\n"
    199           "LIBRARY RESOLUTION\n"
    200           "  -L DIR                    Add library search path\n"
    201           "  -l c                      Add hosted CRT objects and libc\n"
    202           "  -l NAME                   Resolve via -L (.so preferred unless "
    203           "static)\n"
    204           "\n"
    205           "POSITIONAL ARCHIVE STATE (apply to following .a inputs)\n"
    206           "  --whole-archive           Pull every member of following "
    207           "archives\n"
    208           "  --no-whole-archive        Reset (default behaviour)\n"
    209           "  -Bstatic                  Force static link mode\n"
    210           "  -Bdynamic                 Force dynamic link mode\n"
    211           "  --as-needed               Link only if a real reference exists\n"
    212           "  --no-as-needed            Reset to dynamic\n"
    213           "  --start-group ...\n"
    214           "    --end-group             Cyclic-resolution archive group\n"
    215           "\n"
    216           "SHARED-LIBRARY OPTIONS (require -shared)\n"
    217           "  -soname NAME              DT_SONAME / LC_ID_DYLIB\n"
    218           "  -soname=NAME              equivalent attached form\n"
    219           "  -rpath DIR                DT_RPATH/DT_RUNPATH entry (repeatable)\n"
    220           "  -rpath=DIR                attached form\n"
    221           "  -rpath-link DIR           Link-time-only search path (advisory)\n"
    222           "  --enable-new-dtags        rpaths land in DT_RUNPATH (default)\n"
    223           "  --disable-new-dtags       rpaths land in DT_RPATH\n"
    224           "\n"
    225           "SECTION GC\n"
    226           "  --gc-sections             Drop sections unreferenced from the "
    227           "entry\n"
    228           "                            symbol, init/fini arrays, "
    229           "SHF_GNU_RETAIN,\n"
    230           "                            and __start_/__stop_ section "
    231           "references\n"
    232           "  --no-gc-sections          Disable section GC (default)\n"
    233           "  -E, --export-dynamic      Promote defined globals into dynsym\n"
    234           "                            (no-op for -shared; recorded for exe)\n"
    235           "  -S, --strip-debug         Omit debug info from linked output\n"
    236           "  --no-undefined            Reject unresolved symbols in -shared "
    237           "output\n"
    238           "  -z defs                   Same as --no-undefined\n"
    239           "\n"
    240           "BUILD ID\n"
    241           "  --build-id                Same as --build-id=sha256\n"
    242           "  --build-id=MODE           none | sha256 | uuid | 0xHEX\n"
    243           "\n"
    244           "GETTING HELP\n"
    245           "  -h, --help                Show this help and exit\n"
    246           "\n"
    247           "EXIT CODES\n"
    248           "  0   success           1   link error           2   bad usage\n")));
    249 }
    250 
    251 /* ---------- argv-sized scratch arrays ---------- */
    252 
    253 static int ld_alloc_arrays(LdOptions* o, int argc) {
    254   size_t bound = (size_t)argc + 32u;
    255   o->argv_bound = bound;
    256   o->object_files =
    257       driver_alloc_zeroed(o->env, bound * sizeof(*o->object_files));
    258   o->archives = driver_alloc_zeroed(o->env, bound * sizeof(*o->archives));
    259   o->dsos = driver_alloc_zeroed(o->env, bound * sizeof(*o->dsos));
    260   o->order = driver_alloc_zeroed(o->env, bound * sizeof(*o->order));
    261   o->lib_dirs = driver_alloc_zeroed(o->env, bound * sizeof(*o->lib_dirs));
    262   o->owned_paths = driver_alloc_zeroed(o->env, bound * sizeof(*o->owned_paths));
    263   o->owned_path_sizes =
    264       driver_alloc_zeroed(o->env, bound * sizeof(*o->owned_path_sizes));
    265   o->rpaths = driver_alloc_zeroed(o->env, bound * sizeof(*o->rpaths));
    266   o->rpath_links = driver_alloc_zeroed(o->env, bound * sizeof(*o->rpath_links));
    267   if (!o->object_files || !o->archives || !o->dsos || !o->order ||
    268       !o->lib_dirs || !o->owned_paths || !o->owned_path_sizes || !o->rpaths ||
    269       !o->rpath_links) {
    270     driver_errf(LD_TOOL, "out of memory");
    271     return 1;
    272   }
    273   o->new_dtags = 1;
    274   o->allow_undefined = 1;
    275   return 0;
    276 }
    277 
    278 /* ---------- positional archive bookkeeping ---------- */
    279 
    280 static void ld_push_order(LdOptions* o, uint8_t kind, uint32_t index) {
    281   KitLinkInputOrder* slot = &o->order[o->norder++];
    282   slot->kind = kind;
    283   slot->index = index;
    284 }
    285 
    286 static void ld_insert_order(LdOptions* o, uint32_t pos, uint8_t kind,
    287                             uint32_t index) {
    288   uint32_t i;
    289   if (pos > o->norder) pos = o->norder;
    290   for (i = o->norder; i > pos; --i) o->order[i] = o->order[i - 1u];
    291   o->order[pos].kind = kind;
    292   o->order[pos].index = index;
    293   o->norder++;
    294 }
    295 
    296 static void ld_push_object(LdOptions* o, const char* path) {
    297   o->object_files[o->nobject_files++] = path;
    298   ld_push_order(o, KIT_LINK_INPUT_OBJ_BYTES, o->nobject_files - 1u);
    299 }
    300 
    301 static void ld_insert_object(LdOptions* o, const char* path, uint32_t pos) {
    302   o->object_files[o->nobject_files++] = path;
    303   ld_insert_order(o, pos, KIT_LINK_INPUT_OBJ_BYTES, o->nobject_files - 1u);
    304 }
    305 
    306 static void ld_push_archive(LdOptions* o, const char* path, int owned,
    307                             size_t owned_size) {
    308   LdArchive* a = &o->archives[o->narchives++];
    309   a->path = path;
    310   a->owned = owned;
    311   a->owned_size = owned_size;
    312   a->whole_archive = o->cur_whole_archive;
    313   a->link_mode = o->cur_link_mode;
    314   a->group_id = o->cur_group_id;
    315   ld_push_order(o, KIT_LINK_INPUT_ARCHIVE, o->narchives - 1u);
    316 }
    317 
    318 static void ld_push_runtime_archive(LdOptions* o, DriverRuntimeArchive* rt) {
    319   LdArchive* a = &o->archives[o->narchives++];
    320   a->path = rt->path;
    321   a->owned = 1;
    322   a->owned_size = rt->path_size;
    323   a->whole_archive = rt->whole_archive;
    324   a->link_mode = rt->link_mode;
    325   a->group_id = rt->group_id;
    326   rt->path = NULL;
    327   rt->path_size = 0;
    328   ld_push_order(o, KIT_LINK_INPUT_ARCHIVE, o->narchives - 1u);
    329 }
    330 
    331 static void ld_push_dso(LdOptions* o, const char* path, int owned,
    332                         size_t owned_size) {
    333   LdDso* d = &o->dsos[o->ndsos++];
    334   d->path = path;
    335   d->owned = owned;
    336   d->owned_size = owned_size;
    337   ld_push_order(o, KIT_LINK_INPUT_DSO, o->ndsos - 1u);
    338 }
    339 
    340 /* Filename ends in `.so` (with no further extension) or in `.so.N`
    341  * for some run of digits and dots. */
    342 static int driver_is_so_filename(const char* path) {
    343   size_t n = driver_strlen(path);
    344   size_t i;
    345   /* Walk from the end: trim trailing ".N" / ".N.N" sequences if any,
    346    * then check that we land on ".so". */
    347   i = n;
    348   while (i > 0) {
    349     /* Strip a trailing ".<digits>" cluster (e.g. ".1", ".26"). */
    350     size_t end = i;
    351     size_t j = i;
    352     while (j > 0) {
    353       char c = path[j - 1];
    354       if (c >= '0' && c <= '9') {
    355         --j;
    356         continue;
    357       }
    358       break;
    359     }
    360     if (j < end && j > 0 && path[j - 1] == '.') {
    361       i = j - 1;
    362       continue;
    363     }
    364     break;
    365   }
    366   if (i >= 3 && path[i - 3] == '.' && path[i - 2] == 's' && path[i - 1] == 'o')
    367     return 1;
    368   return 0;
    369 }
    370 
    371 static char* ld_join2(DriverEnv* env, const char* a, const char* b,
    372                       size_t* out_size) {
    373   size_t alen = driver_strlen(a);
    374   size_t blen = driver_strlen(b);
    375   size_t slash = (alen > 0 && a[alen - 1] != '/') ? 1u : 0u;
    376   size_t bytes = alen + slash + blen + 1u;
    377   char* out = driver_alloc(env, bytes);
    378   size_t off = 0;
    379   if (!out) return NULL;
    380   if (alen) {
    381     driver_memcpy(out + off, a, alen);
    382     off += alen;
    383   }
    384   if (slash) out[off++] = '/';
    385   if (blen) {
    386     driver_memcpy(out + off, b, blen);
    387     off += blen;
    388   }
    389   out[off] = '\0';
    390   if (out_size) *out_size = bytes;
    391   return out;
    392 }
    393 
    394 static int ld_own_path(LdOptions* o, char* path, size_t size,
    395                        const char** out) {
    396   if (!path) {
    397     driver_errf(LD_TOOL, "out of memory");
    398     return 1;
    399   }
    400   if (o->nowned_paths >= o->argv_bound) {
    401     driver_free(o->env, path, size);
    402     driver_errf(LD_TOOL, "too many sysroot-expanded paths");
    403     return 1;
    404   }
    405   o->owned_paths[o->nowned_paths] = path;
    406   o->owned_path_sizes[o->nowned_paths] = size;
    407   o->nowned_paths++;
    408   if (out) *out = path;
    409   return 0;
    410 }
    411 
    412 static int ld_add_sysroot_libdir(LdOptions* o) {
    413   char* path;
    414   size_t size;
    415   const char* owned;
    416   if (!o->sysroot || !o->sysroot[0]) return 0;
    417   path = ld_join2(o->env, o->sysroot, "lib", &size);
    418   if (ld_own_path(o, path, size, &owned) != 0) return 1;
    419   o->lib_dirs[o->nlib_dirs++] = owned;
    420   return 0;
    421 }
    422 
    423 static int ld_sysroot_rewrite_path(LdOptions* o, const char* path,
    424                                    const char** out) {
    425   const char* tail;
    426   char* joined;
    427   size_t size;
    428   if (!path || path[0] != '=' || !o->sysroot || !o->sysroot[0]) {
    429     *out = path;
    430     return 0;
    431   }
    432   tail = path + 1;
    433   if (tail[0] == '/') tail++;
    434   joined = ld_join2(o->env, o->sysroot, tail, &size);
    435   return ld_own_path(o, joined, size, out);
    436 }
    437 
    438 static int ld_note_library_request(LdOptions* o, const char* name) {
    439   if (driver_streq(name, "c") && !o->no_default_libs) {
    440     o->wants_hosted_libc = 1;
    441     return 1;
    442   }
    443   return 0;
    444 }
    445 
    446 /* ---------- --build-id parsing ---------- */
    447 
    448 static int hex_nibble(char c) {
    449   if (c >= '0' && c <= '9') return c - '0';
    450   if (c >= 'a' && c <= 'f') return 10 + (c - 'a');
    451   if (c >= 'A' && c <= 'F') return 10 + (c - 'A');
    452   return -1;
    453 }
    454 
    455 /* Parse a -Ttext address: 0x<hex> or decimal. Returns 0 on success. */
    456 static int ld_parse_addr(const char* s, uint64_t* out) {
    457   uint64_t v = 0;
    458   const char* p;
    459   if (!s || !s[0]) return 1;
    460   if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
    461     p = s + 2;
    462     if (!*p) return 1;
    463     for (; *p; ++p) {
    464       int n = hex_nibble(*p);
    465       if (n < 0) return 1;
    466       v = (v << 4) | (uint64_t)n;
    467     }
    468   } else {
    469     for (p = s; *p; ++p) {
    470       if (*p < '0' || *p > '9') return 1;
    471       v = v * 10u + (uint64_t)(*p - '0');
    472     }
    473   }
    474   *out = v;
    475   return 0;
    476 }
    477 
    478 /* Parse `--build-id=...` argument into options. Accepts "none", "sha256",
    479  * "uuid", or "0x<even-hex>". Returns 0 on success, 1 on bad value. */
    480 static int ld_parse_build_id(LdOptions* o, const char* val) {
    481   if (driver_streq(val, "none")) {
    482     o->build_id_mode = KIT_BUILDID_NONE;
    483     return 0;
    484   }
    485   if (driver_streq(val, "sha256")) {
    486     o->build_id_mode = KIT_BUILDID_SHA256;
    487     return 0;
    488   }
    489   if (driver_streq(val, "uuid")) {
    490     o->build_id_mode = KIT_BUILDID_UUID;
    491     return 0;
    492   }
    493 
    494   /* "0x<hex>" — must be even number of hex digits, at least one byte. */
    495   if (val[0] == '0' && (val[1] == 'x' || val[1] == 'X')) {
    496     const char* hex = val + 2;
    497     size_t hex_len = driver_strlen(hex);
    498     size_t nbytes;
    499     size_t i;
    500     uint8_t* buf;
    501     if (hex_len == 0 || (hex_len & 1u)) {
    502       driver_errf(LD_TOOL,
    503                   "--build-id=0xHEX requires an even number of hex digits");
    504       return 1;
    505     }
    506     nbytes = hex_len / 2;
    507     buf = driver_alloc(o->env, nbytes);
    508     if (!buf) {
    509       driver_errf(LD_TOOL, "out of memory");
    510       return 1;
    511     }
    512     for (i = 0; i < nbytes; ++i) {
    513       int hi = hex_nibble(hex[2 * i]);
    514       int lo = hex_nibble(hex[2 * i + 1]);
    515       if (hi < 0 || lo < 0) {
    516         driver_errf(LD_TOOL, "--build-id: invalid hex digit");
    517         driver_free(o->env, buf, nbytes);
    518         return 1;
    519       }
    520       buf[i] = (uint8_t)((hi << 4) | lo);
    521     }
    522     o->build_id_mode = KIT_BUILDID_USER;
    523     o->build_id_bytes = buf;
    524     o->build_id_len = (uint32_t)nbytes;
    525     o->build_id_alloc = nbytes;
    526     return 0;
    527   }
    528 
    529   driver_errf(LD_TOOL, "--build-id: unknown value: %.*s",
    530               KIT_SLICE_ARG(kit_slice_cstr(val)));
    531   return 1;
    532 }
    533 
    534 /* ---------- argv parser ---------- */
    535 
    536 /* If `arg` starts with `prefix` followed by '=', returns the tail past the
    537  * '='; otherwise returns NULL. */
    538 static const char* arg_eq_value(const char* arg, const char* prefix) {
    539   size_t n = driver_strlen(prefix);
    540   if (!driver_strneq(arg, prefix, n)) return NULL;
    541   if (arg[n] != '=') return NULL;
    542   return arg + n + 1;
    543 }
    544 
    545 /* Compare an MSVC-style flag against `arg`, case-insensitive on the
    546  * key. MSVC accepts both `/` and `-` as the lead char and is
    547  * case-insensitive in the key part — we accept `/KEY:val`, `/key:val`,
    548  * `-KEY:val`. Returns the tail past the colon, or NULL on mismatch. */
    549 static const char* ms_flag_value(const char* arg, const char* key) {
    550   size_t klen = driver_strlen(key);
    551   size_t i;
    552   if (arg[0] != '/' && arg[0] != '-') return NULL;
    553   for (i = 0; i < klen; ++i) {
    554     char a = arg[1 + i];
    555     char k = key[i];
    556     if (a >= 'a' && a <= 'z') a = (char)(a - 'a' + 'A');
    557     if (k >= 'a' && k <= 'z') k = (char)(k - 'a' + 'A');
    558     if (a != k) return NULL;
    559   }
    560   if (arg[1 + klen] != ':') return NULL;
    561   return arg + 1 + klen + 1;
    562 }
    563 
    564 /* Same shape as ms_flag_value but for bare flags (no `:value`). */
    565 static int ms_flag_match(const char* arg, const char* key) {
    566   size_t klen = driver_strlen(key);
    567   size_t i;
    568   if (arg[0] != '/' && arg[0] != '-') return 0;
    569   for (i = 0; i < klen; ++i) {
    570     char a = arg[1 + i];
    571     char k = key[i];
    572     if (a >= 'a' && a <= 'z') a = (char)(a - 'a' + 'A');
    573     if (k >= 'a' && k <= 'z') k = (char)(k - 'a' + 'A');
    574     if (a != k) return 0;
    575   }
    576   return arg[1 + klen] == '\0';
    577 }
    578 
    579 static int ld_subsystem_value_eq(const char* val, const char* want) {
    580   size_t i;
    581   for (i = 0; want[i]; ++i) {
    582     char a = val[i];
    583     char b = want[i];
    584     if (a >= 'a' && a <= 'z') a = (char)(a - 'a' + 'A');
    585     if (b >= 'a' && b <= 'z') b = (char)(b - 'a' + 'A');
    586     if (a != b) return 0;
    587   }
    588   return val[i] == '\0' || val[i] == ',';
    589 }
    590 
    591 static int ld_parse_pe_subsystem(LdOptions* o, const char* val) {
    592   if (ld_subsystem_value_eq(val, "CONSOLE") ||
    593       ld_subsystem_value_eq(val, "CUI")) {
    594     o->pe_subsystem = KIT_PE_SUBSYSTEM_WINDOWS_CUI;
    595     return 0;
    596   }
    597   if (ld_subsystem_value_eq(val, "WINDOWS") ||
    598       ld_subsystem_value_eq(val, "GUI")) {
    599     o->pe_subsystem = KIT_PE_SUBSYSTEM_WINDOWS_GUI;
    600     return 0;
    601   }
    602   driver_errf(LD_TOOL, "unsupported subsystem: %.*s",
    603               KIT_SLICE_ARG(kit_slice_cstr(val)));
    604   return 1;
    605 }
    606 
    607 /* Parse one MSVC-style argument. Recognized subset (others warn and
    608  * skip — match-but-no-op so legacy build scripts pass cleanly):
    609  *   /OUT:path                  → o->output_path
    610  *   /ENTRY:sym                 → o->entry
    611  *   /SUBSYSTEM:CONSOLE|WINDOWS → PE optional-header subsystem; WINDOWS
    612  *                                also defaults entry to WinMainCRTStartup.
    613  *   /DEFAULTLIB:name           → equivalent to -l<name>; resolved
    614  *                                lazily in the same path as -l.
    615  *   /LIBPATH:dir               → equivalent to -L dir
    616  *
    617  * Returns 1 if consumed, 0 if not a recognized MS flag (caller falls
    618  * through to its existing behaviour), -1 on hard error. */
    619 static int ld_try_ms_flag(LdOptions* o, const char* a) {
    620   const char* val;
    621   if (!o->ms_link_driver) return 0;
    622   if (a[0] != '/' && a[0] != '-') return 0;
    623 
    624   if ((val = ms_flag_value(a, "OUT")) != NULL) {
    625     if (o->output_seen) {
    626       driver_errf(LD_TOOL, "/OUT specified after -o");
    627       return -1;
    628     }
    629     o->output_path = val;
    630     o->output_seen = 1;
    631     return 1;
    632   }
    633   if ((val = ms_flag_value(a, "ENTRY")) != NULL) {
    634     o->entry = val;
    635     return 1;
    636   }
    637   if ((val = ms_flag_value(a, "LIBPATH")) != NULL) {
    638     o->lib_dirs[o->nlib_dirs++] = val;
    639     return 1;
    640   }
    641   if ((val = ms_flag_value(a, "DEFAULTLIB")) != NULL) {
    642     /* Resolve eagerly like -l does, using whatever current link-mode
    643      * state is in effect. Windows mode triggers the .lib/.dll.a/.a
    644      * suffix list. */
    645     char* resolved;
    646     size_t resolved_size;
    647     LibResolveKind kind;
    648     LibResolveMode mode = (o->cur_link_mode == KIT_LM_STATIC)
    649                               ? LIB_RESOLVE_STATIC_ONLY
    650                               : LIB_RESOLVE_DYNAMIC_PREFER;
    651     if (driver_lib_resolve_for_os(o->env, val, mode, LIB_RESOLVE_OS_WINDOWS,
    652                                   o->lib_dirs, o->nlib_dirs, &resolved,
    653                                   &resolved_size, &kind) != 0) {
    654       driver_errf(LD_TOOL, "/DEFAULTLIB: cannot find %.*s",
    655                   KIT_SLICE_ARG(kit_slice_cstr(val)));
    656       return -1;
    657     }
    658     if (kind == LIB_RESOLVE_KIND_SHARED || kind == LIB_RESOLVE_KIND_TBD) {
    659       ld_push_dso(o, resolved, 1, resolved_size);
    660     } else {
    661       ld_push_archive(o, resolved, 1, resolved_size);
    662     }
    663     return 1;
    664   }
    665   if ((val = ms_flag_value(a, "SUBSYSTEM")) != NULL) {
    666     if (ld_parse_pe_subsystem(o, val) != 0) return -1;
    667     return 1;
    668   }
    669   if (ms_flag_match(a, "NOLOGO") || ms_flag_match(a, "VERBOSE") ||
    670       ms_flag_match(a, "INCREMENTAL") || ms_flag_match(a, "DEBUG") ||
    671       ms_flag_match(a, "DYNAMICBASE") || ms_flag_match(a, "NXCOMPAT")) {
    672     /* Common flags every Windows build script sets; silently accept. */
    673     return 1;
    674   }
    675 
    676   /* Any other `/key[:val]` shape under --ms-link-driver: warn + skip.
    677    * We treat the entire arg as consumed so it doesn't fall through to
    678    * the positional path and try to open a file. */
    679   driver_errf(LD_TOOL, "ignoring unsupported MS-style flag: %.*s",
    680               KIT_SLICE_ARG(kit_slice_cstr(a)));
    681   return 1;
    682 }
    683 
    684 static int ld_parse(int argc, char** argv, LdOptions* o) {
    685   int i;
    686   if (ld_alloc_arrays(o, argc) != 0) return 1;
    687 
    688   o->target = driver_host_target();
    689 
    690   /* First pass: detect --ms-link-driver up front so the option can
    691    * appear anywhere on the command line and still affect earlier
    692    * `/...` tokens. Also capture --sysroot before resolving any -l
    693    * entries; GNU ld treats the sysroot as a global search-prefix
    694    * setting, not positional state. Also detect -lc so the hosted lib
    695    * dirs can be pre-populated before -lm / -lpthread etc. are resolved. */
    696   {
    697     int has_lc = 0;
    698     for (i = 1; i < argc; ++i) {
    699       if (driver_streq(argv[i], "--ms-link-driver")) {
    700         o->ms_link_driver = 1;
    701         continue;
    702       }
    703       if (driver_streq(argv[i], "--sysroot")) {
    704         if (i + 1 < argc) {
    705           o->sysroot = argv[i + 1];
    706           i++;
    707         }
    708         continue;
    709       }
    710       if (driver_strneq(argv[i], "--sysroot=", 10)) {
    711         o->sysroot = argv[i] + 10;
    712         continue;
    713       }
    714       /* Detect -lc / -l c so hosted lib dirs are available for later -l
    715        * flags regardless of where -lc appears on the command line. */
    716       if (driver_streq(argv[i], "-lc")) {
    717         has_lc = 1;
    718         continue;
    719       }
    720       if ((driver_streq(argv[i], "-l") || driver_streq(argv[i], "--library")) &&
    721           i + 1 < argc && driver_streq(argv[i + 1], "c")) {
    722         has_lc = 1;
    723         i++;
    724         continue;
    725       }
    726     }
    727     if (!o->sysroot || !o->sysroot[0]) {
    728       const char* env_sysroot = driver_getenv("KIT_SYSROOT");
    729       if (env_sysroot && env_sysroot[0]) o->sysroot = env_sysroot;
    730     }
    731     if (ld_add_sysroot_libdir(o) != 0) return 1;
    732     /* Pre-populate the hosted lib dirs so user -l flags are resolved
    733      * against the correct sysroot even when they appear before -lc. */
    734     if (has_lc) {
    735       DriverHostedRequest req;
    736       DriverHostedDirs dirs;
    737       uint32_t j;
    738       memset(&req, 0, sizeof req);
    739       req.env = o->env;
    740       req.tool = LD_TOOL;
    741       req.target = o->target;
    742       req.sysroot = o->sysroot;
    743       if (driver_hosted_dirs_resolve(&req, &dirs) == 0) {
    744         for (j = 0; j < dirs.nlibdirs; ++j) {
    745           /* Transfer ownership of each dir string to o->owned_paths so
    746            * it is freed in ld_options_release; then point lib_dirs at it. */
    747           const char* p;
    748           if (ld_own_path(o, dirs.libdirs[j], dirs.libdir_sizes[j], &p) == 0) {
    749             dirs.libdirs[j] = NULL;
    750             dirs.libdir_sizes[j] = 0;
    751             o->lib_dirs[o->nlib_dirs++] = p;
    752           }
    753         }
    754         driver_hosted_dirs_fini(&dirs);
    755       }
    756     }
    757   }
    758 
    759   for (i = 1; i < argc; ++i) {
    760     const char* a = argv[i];
    761     const char* val;
    762     int ms_rc;
    763 
    764     if (driver_streq(a, "--ms-link-driver")) {
    765       o->ms_link_driver = 1;
    766       continue;
    767     }
    768     ms_rc = ld_try_ms_flag(o, a);
    769     if (ms_rc < 0) return 1;
    770     if (ms_rc > 0) continue;
    771 
    772     if (driver_streq(a, "-o")) {
    773       if (++i >= argc) {
    774         driver_errf(LD_TOOL, "-o requires an argument");
    775         return 1;
    776       }
    777       if (o->output_seen) {
    778         driver_errf(LD_TOOL, "-o specified more than once");
    779         return 1;
    780       }
    781       o->output_path = argv[i];
    782       o->output_seen = 1;
    783       continue;
    784     }
    785     if ((val = arg_eq_value(a, "--output")) != NULL) {
    786       if (o->output_seen) {
    787         driver_errf(LD_TOOL, "-o specified more than once");
    788         return 1;
    789       }
    790       o->output_path = val;
    791       o->output_seen = 1;
    792       continue;
    793     }
    794     if (driver_streq(a, "--output")) {
    795       if (++i >= argc) {
    796         driver_errf(LD_TOOL, "--output requires an argument");
    797         return 1;
    798       }
    799       if (o->output_seen) {
    800         driver_errf(LD_TOOL, "-o specified more than once");
    801         return 1;
    802       }
    803       o->output_path = argv[i];
    804       o->output_seen = 1;
    805       continue;
    806     }
    807     if (driver_streq(a, "-e")) {
    808       if (++i >= argc) {
    809         driver_errf(LD_TOOL, "-e requires an argument");
    810         return 1;
    811       }
    812       o->entry = argv[i];
    813       continue;
    814     }
    815     if ((val = arg_eq_value(a, "--entry")) != NULL) {
    816       o->entry = val;
    817       continue;
    818     }
    819     if (driver_streq(a, "--entry")) {
    820       if (++i >= argc) {
    821         driver_errf(LD_TOOL, "--entry requires an argument");
    822         return 1;
    823       }
    824       o->entry = argv[i];
    825       continue;
    826     }
    827     if ((val = arg_eq_value(a, "--subsystem")) != NULL) {
    828       if (ld_parse_pe_subsystem(o, val) != 0) return 1;
    829       continue;
    830     }
    831     if (driver_streq(a, "--subsystem")) {
    832       if (++i >= argc) {
    833         driver_errf(LD_TOOL, "--subsystem requires an argument");
    834         return 1;
    835       }
    836       if (ld_parse_pe_subsystem(o, argv[i]) != 0) return 1;
    837       continue;
    838     }
    839     if (driver_streq(a, "-T")) {
    840       if (++i >= argc) {
    841         driver_errf(LD_TOOL, "-T requires an argument");
    842         return 1;
    843       }
    844       o->script_path = argv[i];
    845       continue;
    846     }
    847     if ((val = arg_eq_value(a, "--script")) != NULL) {
    848       o->script_path = val;
    849       continue;
    850     }
    851     if (driver_streq(a, "--script")) {
    852       if (++i >= argc) {
    853         driver_errf(LD_TOOL, "--script requires an argument");
    854         return 1;
    855       }
    856       o->script_path = argv[i];
    857       continue;
    858     }
    859     /* -Ttext ADDR / -Ttext=ADDR: set the static ET_EXEC image (text) base, for
    860      * freestanding images that must load at a fixed address (e.g. the qemu
    861      * `virt` RAM base 0x80000000). Matched here; the bare "-T" above is an exact
    862      * match, so it never swallows -Ttext. No effect on PIE/scripted layouts. */
    863     {
    864       const char* tval = NULL;
    865       if (driver_streq(a, "-Ttext")) {
    866         if (++i >= argc) {
    867           driver_errf(LD_TOOL, "-Ttext requires an argument");
    868           return 1;
    869         }
    870         tval = argv[i];
    871       } else if ((tval = arg_eq_value(a, "-Ttext")) != NULL) {
    872         /* -Ttext=ADDR */
    873       }
    874       if (tval) {
    875         uint64_t v;
    876         if (ld_parse_addr(tval, &v) != 0) {
    877           driver_errf(LD_TOOL, "-Ttext: invalid address: %s", tval);
    878           return 1;
    879         }
    880         o->text_base = v;
    881         o->text_base_set = 1;
    882         continue;
    883       }
    884     }
    885     /* -nostdlib / --no-default-libs: do not auto-resolve and link kit's
    886      * compiler runtime. A freestanding image (e.g. riscv32-none-elf) supplies
    887      * its own libkit_rt.a on the command line, and a pure-linker invocation
    888      * should not require a per-target runtime archive to exist. */
    889     if (driver_streq(a, "-nostdlib") || driver_streq(a, "--no-default-libs") ||
    890         driver_streq(a, "-nodefaultlibs")) {
    891       o->no_default_libs = 1;
    892       continue;
    893     }
    894     if (driver_streq(a, "--support-dir")) {
    895       if (++i >= argc) {
    896         driver_errf(LD_TOOL, "--support-dir requires an argument");
    897         return 1;
    898       }
    899       o->support_dir = argv[i];
    900       continue;
    901     }
    902     if (driver_strneq(a, "--support-dir=", 14)) {
    903       o->support_dir = a + 14;
    904       continue;
    905     }
    906     if (driver_streq(a, "--sysroot")) {
    907       if (++i >= argc) {
    908         driver_errf(LD_TOOL, "--sysroot requires an argument");
    909         return 1;
    910       }
    911       o->sysroot = argv[i];
    912       continue;
    913     }
    914     if (driver_strneq(a, "--sysroot=", 10)) {
    915       o->sysroot = a + 10;
    916       continue;
    917     }
    918 
    919     if (driver_strneq(a, "-L", 2)) {
    920       const char* dir = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
    921       const char* rewritten;
    922       if (!dir) {
    923         driver_errf(LD_TOOL, "-L requires an argument");
    924         return 1;
    925       }
    926       if (ld_sysroot_rewrite_path(o, dir, &rewritten) != 0) return 1;
    927       o->lib_dirs[o->nlib_dirs++] = rewritten;
    928       continue;
    929     }
    930     if ((val = arg_eq_value(a, "--library-path")) != NULL) {
    931       const char* rewritten;
    932       if (ld_sysroot_rewrite_path(o, val, &rewritten) != 0) return 1;
    933       o->lib_dirs[o->nlib_dirs++] = rewritten;
    934       continue;
    935     }
    936     if (driver_streq(a, "--library-path")) {
    937       const char* rewritten;
    938       if (++i >= argc) {
    939         driver_errf(LD_TOOL, "--library-path requires an argument");
    940         return 1;
    941       }
    942       if (ld_sysroot_rewrite_path(o, argv[i], &rewritten) != 0) return 1;
    943       o->lib_dirs[o->nlib_dirs++] = rewritten;
    944       continue;
    945     }
    946 
    947     if (driver_strneq(a, "-l", 2)) {
    948       const char* name = a[2] ? a + 2 : (++i < argc ? argv[i] : NULL);
    949       char* resolved;
    950       size_t resolved_size;
    951       LibResolveKind kind;
    952       LibResolveMode mode;
    953       LibResolveOS resolve_os;
    954       if (!name) {
    955         driver_errf(LD_TOOL, "-l requires an argument");
    956         return 1;
    957       }
    958       if (ld_note_library_request(o, name)) continue;
    959       /* -Bstatic forces .a only; everything else (default,
    960        * -Bdynamic, --as-needed) prefers .so but falls back to .a. */
    961       mode = (o->cur_link_mode == KIT_LM_STATIC) ? LIB_RESOLVE_STATIC_ONLY
    962                                                  : LIB_RESOLVE_DYNAMIC_PREFER;
    963       resolve_os = (o->target.os == KIT_OS_WINDOWS) ? LIB_RESOLVE_OS_WINDOWS
    964                                                     : LIB_RESOLVE_OS_POSIX;
    965       if (driver_lib_resolve_for_os(o->env, name, mode, resolve_os, o->lib_dirs,
    966                                     o->nlib_dirs, &resolved, &resolved_size,
    967                                     &kind) != 0) {
    968         driver_errf(LD_TOOL, "cannot find -l%.*s",
    969                     KIT_SLICE_ARG(kit_slice_cstr(name)));
    970         return 1;
    971       }
    972       if (kind == LIB_RESOLVE_KIND_SHARED || kind == LIB_RESOLVE_KIND_TBD) {
    973         ld_push_dso(o, resolved, 1, resolved_size);
    974       } else {
    975         ld_push_archive(o, resolved, 1, resolved_size);
    976       }
    977       continue;
    978     }
    979     if ((val = arg_eq_value(a, "--library")) != NULL) {
    980       char* resolved;
    981       size_t resolved_size;
    982       LibResolveKind kind;
    983       LibResolveMode mode = (o->cur_link_mode == KIT_LM_STATIC)
    984                                 ? LIB_RESOLVE_STATIC_ONLY
    985                                 : LIB_RESOLVE_DYNAMIC_PREFER;
    986       LibResolveOS resolve_os = (o->target.os == KIT_OS_WINDOWS)
    987                                     ? LIB_RESOLVE_OS_WINDOWS
    988                                     : LIB_RESOLVE_OS_POSIX;
    989       if (ld_note_library_request(o, val)) continue;
    990       if (driver_lib_resolve_for_os(o->env, val, mode, resolve_os, o->lib_dirs,
    991                                     o->nlib_dirs, &resolved, &resolved_size,
    992                                     &kind) != 0) {
    993         driver_errf(LD_TOOL, "cannot find -l%.*s",
    994                     KIT_SLICE_ARG(kit_slice_cstr(val)));
    995         return 1;
    996       }
    997       if (kind == LIB_RESOLVE_KIND_SHARED || kind == LIB_RESOLVE_KIND_TBD) {
    998         ld_push_dso(o, resolved, 1, resolved_size);
    999       } else {
   1000         ld_push_archive(o, resolved, 1, resolved_size);
   1001       }
   1002       continue;
   1003     }
   1004     if (driver_streq(a, "--library")) {
   1005       char* resolved;
   1006       size_t resolved_size;
   1007       LibResolveKind kind;
   1008       LibResolveMode mode;
   1009       LibResolveOS resolve_os;
   1010       if (++i >= argc) {
   1011         driver_errf(LD_TOOL, "--library requires an argument");
   1012         return 1;
   1013       }
   1014       if (ld_note_library_request(o, argv[i])) continue;
   1015       mode = (o->cur_link_mode == KIT_LM_STATIC) ? LIB_RESOLVE_STATIC_ONLY
   1016                                                  : LIB_RESOLVE_DYNAMIC_PREFER;
   1017       resolve_os = (o->target.os == KIT_OS_WINDOWS) ? LIB_RESOLVE_OS_WINDOWS
   1018                                                     : LIB_RESOLVE_OS_POSIX;
   1019       if (driver_lib_resolve_for_os(o->env, argv[i], mode, resolve_os,
   1020                                     o->lib_dirs, o->nlib_dirs, &resolved,
   1021                                     &resolved_size, &kind) != 0) {
   1022         driver_errf(LD_TOOL, "cannot find -l%.*s",
   1023                     KIT_SLICE_ARG(kit_slice_cstr(argv[i])));
   1024         return 1;
   1025       }
   1026       if (kind == LIB_RESOLVE_KIND_SHARED || kind == LIB_RESOLVE_KIND_TBD) {
   1027         ld_push_dso(o, resolved, 1, resolved_size);
   1028       } else {
   1029         ld_push_archive(o, resolved, 1, resolved_size);
   1030       }
   1031       continue;
   1032     }
   1033 
   1034     if (driver_streq(a, "-static")) {
   1035       o->target.pic = KIT_PIC_NONE;
   1036       o->static_link = 1;
   1037       o->cur_link_mode = KIT_LM_STATIC;
   1038       o->pic_explicit = 1;
   1039       continue;
   1040     }
   1041     if (driver_streq(a, "-pie")) {
   1042       o->target.pic = KIT_PIC_PIE;
   1043       o->pie = 1;
   1044       o->pic_explicit = 1;
   1045       continue;
   1046     }
   1047     if (driver_streq(a, "-dynamic-linker") ||
   1048         driver_streq(a, "--dynamic-linker")) {
   1049       if (++i >= argc) {
   1050         driver_errf(LD_TOOL, "-dynamic-linker requires an argument");
   1051         return 1;
   1052       }
   1053       o->interp_path = argv[i];
   1054       continue;
   1055     }
   1056     if ((val = arg_eq_value(a, "--dynamic-linker")) != NULL) {
   1057       o->interp_path = val;
   1058       continue;
   1059     }
   1060     if (driver_streq(a, "-no-pie")) {
   1061       o->target.pic = KIT_PIC_NONE;
   1062       o->pie = 0;
   1063       o->pic_explicit = 1;
   1064       continue;
   1065     }
   1066 
   1067     if (driver_streq(a, "-shared")) {
   1068       o->shared = 1;
   1069       /* Shared objects must be position-independent. Force PIC unless
   1070        * the caller has explicitly chosen PIE (which is also fine). */
   1071       if (o->target.pic == KIT_PIC_NONE) o->target.pic = KIT_PIC_PIC;
   1072       o->pic_explicit = 1;
   1073       continue;
   1074     }
   1075     if (driver_streq(a, "-r") || driver_streq(a, "--relocatable")) {
   1076       o->relocatable = 1;
   1077       continue;
   1078     }
   1079     if (driver_streq(a, "-soname")) {
   1080       if (++i >= argc) {
   1081         driver_errf(LD_TOOL, "-soname requires an argument");
   1082         return 1;
   1083       }
   1084       o->soname = argv[i];
   1085       continue;
   1086     }
   1087     if ((val = arg_eq_value(a, "-soname")) != NULL) {
   1088       o->soname = val;
   1089       continue;
   1090     }
   1091     if (driver_streq(a, "-rpath")) {
   1092       if (++i >= argc) {
   1093         driver_errf(LD_TOOL, "-rpath requires an argument");
   1094         return 1;
   1095       }
   1096       o->rpaths[o->nrpaths++] = argv[i];
   1097       continue;
   1098     }
   1099     if ((val = arg_eq_value(a, "-rpath")) != NULL) {
   1100       o->rpaths[o->nrpaths++] = val;
   1101       continue;
   1102     }
   1103     if (driver_streq(a, "-rpath-link")) {
   1104       if (++i >= argc) {
   1105         driver_errf(LD_TOOL, "-rpath-link requires an argument");
   1106         return 1;
   1107       }
   1108       o->rpath_links[o->nrpath_links++] = argv[i];
   1109       continue;
   1110     }
   1111     if ((val = arg_eq_value(a, "-rpath-link")) != NULL) {
   1112       o->rpath_links[o->nrpath_links++] = val;
   1113       continue;
   1114     }
   1115     if (driver_streq(a, "--enable-new-dtags")) {
   1116       o->new_dtags = 1;
   1117       continue;
   1118     }
   1119     if (driver_streq(a, "--disable-new-dtags")) {
   1120       o->new_dtags = 0;
   1121       continue;
   1122     }
   1123     if (driver_streq(a, "--gc-sections")) {
   1124       o->gc_sections = 1;
   1125       continue;
   1126     }
   1127     if (driver_streq(a, "--no-gc-sections")) {
   1128       o->gc_sections = 0;
   1129       continue;
   1130     }
   1131     if (driver_streq(a, "-S") || driver_streq(a, "--strip-debug")) {
   1132       o->strip_debug = 1;
   1133       continue;
   1134     }
   1135     if (driver_streq(a, "-E") || driver_streq(a, "--export-dynamic")) {
   1136       o->export_dynamic = 1;
   1137       continue;
   1138     }
   1139     if (driver_streq(a, "--no-undefined")) {
   1140       o->allow_undefined = 0;
   1141       continue;
   1142     }
   1143     if (driver_streq(a, "--allow-shlib-undefined")) {
   1144       o->allow_undefined = 1;
   1145       continue;
   1146     }
   1147     if (driver_streq(a, "-z")) {
   1148       if (++i >= argc) {
   1149         driver_errf(LD_TOOL, "-z requires an argument");
   1150         return 1;
   1151       }
   1152       if (driver_streq(argv[i], "defs")) {
   1153         o->allow_undefined = 0;
   1154         continue;
   1155       }
   1156       driver_errf(LD_TOOL, "unsupported -z option: %.*s",
   1157                   KIT_SLICE_ARG(kit_slice_cstr(argv[i])));
   1158       return 1;
   1159     }
   1160 
   1161     if (driver_streq(a, "--whole-archive")) {
   1162       o->cur_whole_archive = 1;
   1163       continue;
   1164     }
   1165     if (driver_streq(a, "--no-whole-archive")) {
   1166       o->cur_whole_archive = 0;
   1167       continue;
   1168     }
   1169     if (driver_streq(a, "-Bstatic")) {
   1170       o->cur_link_mode = KIT_LM_STATIC;
   1171       continue;
   1172     }
   1173     if (driver_streq(a, "-Bdynamic")) {
   1174       o->cur_link_mode = KIT_LM_DYNAMIC;
   1175       continue;
   1176     }
   1177     if (driver_streq(a, "--as-needed")) {
   1178       o->cur_link_mode = KIT_LM_AS_NEEDED;
   1179       continue;
   1180     }
   1181     if (driver_streq(a, "--no-as-needed")) {
   1182       o->cur_link_mode = KIT_LM_DYNAMIC;
   1183       continue;
   1184     }
   1185     if (driver_streq(a, "--start-group")) {
   1186       if (o->cur_group_id != 0) {
   1187         driver_errf(LD_TOOL, "nested --start-group is not supported");
   1188         return 1;
   1189       }
   1190       if (o->next_group_id == UINT8_MAX) {
   1191         driver_errf(LD_TOOL, "too many --start-group occurrences");
   1192         return 1;
   1193       }
   1194       o->next_group_id++;
   1195       o->cur_group_id = o->next_group_id;
   1196       continue;
   1197     }
   1198     if (driver_streq(a, "--end-group")) {
   1199       if (o->cur_group_id == 0) {
   1200         driver_errf(LD_TOOL, "--end-group without --start-group");
   1201         return 1;
   1202       }
   1203       o->cur_group_id = 0;
   1204       continue;
   1205     }
   1206 
   1207     if ((val = arg_eq_value(a, "--build-id")) != NULL) {
   1208       if (ld_parse_build_id(o, val) != 0) return 1;
   1209       continue;
   1210     }
   1211     if (driver_streq(a, "--build-id")) {
   1212       /* Bareword form defaults to sha256, matching GNU ld. */
   1213       o->build_id_mode = KIT_BUILDID_SHA256;
   1214       continue;
   1215     }
   1216 
   1217     if (a[0] == '-' && a[1] != '\0') {
   1218       driver_errf(LD_TOOL, "unknown flag: %.*s",
   1219                   KIT_SLICE_ARG(kit_slice_cstr(a)));
   1220       return 1;
   1221     }
   1222 
   1223     {
   1224       const char* path;
   1225       if (ld_sysroot_rewrite_path(o, a, &path) != 0) return 1;
   1226       if (driver_has_suffix(path, ".a")) {
   1227         ld_push_archive(o, path, 0, 0);
   1228       } else if (driver_is_so_filename(path)) {
   1229         ld_push_dso(o, path, 0, 0);
   1230       } else {
   1231         ld_push_object(o, path);
   1232       }
   1233     }
   1234   }
   1235 
   1236   if (!o->output_path) {
   1237     driver_errf(LD_TOOL, "-o is required");
   1238     return 1;
   1239   }
   1240   if (o->cur_group_id != 0) {
   1241     driver_errf(LD_TOOL, "missing --end-group");
   1242     return 1;
   1243   }
   1244   if (o->nobject_files == 0 && o->narchives == 0 && o->ndsos == 0) {
   1245     driver_errf(LD_TOOL, "no input files");
   1246     ld_usage();
   1247     return 1;
   1248   }
   1249   if (o->wants_hosted_libc && o->no_default_libs) {
   1250     driver_errf(LD_TOOL,
   1251                 "-lc hosted expansion is disabled by -nostdlib/"
   1252                 "--no-default-libs");
   1253     return 1;
   1254   }
   1255   if (o->shared) {
   1256     driver_errf(LD_TOOL,
   1257                 "creating dynamic/shared libraries is not yet supported");
   1258     return 1;
   1259   }
   1260   if (o->relocatable) {
   1261     if (o->shared) {
   1262       driver_errf(LD_TOOL, "-r and -shared are incompatible");
   1263       return 1;
   1264     }
   1265     if (o->wants_hosted_libc) {
   1266       driver_errf(LD_TOOL, "-lc hosted expansion requires executable output");
   1267       return 1;
   1268     }
   1269     /* Only an explicit -pie conflicts; a default PIE target.pic is simply
   1270      * overridden by -r (driver_link_pie suppresses pie when relocatable). */
   1271     if (o->pie) {
   1272       driver_errf(LD_TOOL, "-r and -pie are incompatible");
   1273       return 1;
   1274     }
   1275     if (o->interp_path) {
   1276       driver_errf(LD_TOOL, "-dynamic-linker requires executable output");
   1277       return 1;
   1278     }
   1279     if (o->soname || o->nrpaths || o->nrpath_links) {
   1280       driver_errf(LD_TOOL, "-soname/-rpath/-rpath-link require -shared");
   1281       return 1;
   1282     }
   1283     if (o->ndsos) {
   1284       driver_errf(LD_TOOL, "-r does not support shared-object inputs");
   1285       return 1;
   1286     }
   1287     if (o->script_path) {
   1288       driver_errf(LD_TOOL, "-r does not support linker scripts yet");
   1289       return 1;
   1290     }
   1291   }
   1292   return 0;
   1293 }
   1294 
   1295 /* ---------- options teardown ---------- */
   1296 
   1297 static void ld_options_release(LdOptions* o) {
   1298   size_t bound = o->argv_bound;
   1299   uint32_t i;
   1300   for (i = 0; i < o->narchives; ++i) {
   1301     LdArchive* a = &o->archives[i];
   1302     if (a->owned && a->path) {
   1303       driver_free(o->env, (void*)a->path, a->owned_size);
   1304     }
   1305   }
   1306   for (i = 0; i < o->ndsos; ++i) {
   1307     LdDso* d = &o->dsos[i];
   1308     if (d->owned && d->path) {
   1309       driver_free(o->env, (void*)d->path, d->owned_size);
   1310     }
   1311   }
   1312   for (i = 0; i < o->nowned_paths; ++i) {
   1313     if (o->owned_paths[i])
   1314       driver_free(o->env, o->owned_paths[i], o->owned_path_sizes[i]);
   1315   }
   1316   if (o->build_id_bytes) {
   1317     driver_free(o->env, o->build_id_bytes, o->build_id_alloc);
   1318   }
   1319   driver_hosted_plan_fini(o->env, &o->hosted);
   1320   driver_free(o->env, o->object_files, bound * sizeof(*o->object_files));
   1321   driver_free(o->env, o->archives, bound * sizeof(*o->archives));
   1322   driver_free(o->env, o->dsos, bound * sizeof(*o->dsos));
   1323   driver_free(o->env, o->order, bound * sizeof(*o->order));
   1324   driver_free(o->env, o->lib_dirs, bound * sizeof(*o->lib_dirs));
   1325   driver_free(o->env, o->owned_paths, bound * sizeof(*o->owned_paths));
   1326   driver_free(o->env, o->owned_path_sizes,
   1327               bound * sizeof(*o->owned_path_sizes));
   1328   driver_free(o->env, o->rpaths, bound * sizeof(*o->rpaths));
   1329   driver_free(o->env, o->rpath_links, bound * sizeof(*o->rpath_links));
   1330 }
   1331 
   1332 /* ---------- input loading ---------- */
   1333 
   1334 typedef struct LoadedFile {
   1335   const KitFileIO* io;
   1336   KitFileData data;
   1337   int loaded;
   1338 } LoadedFile;
   1339 
   1340 static int load_file(const KitFileIO* io, const char* path, LoadedFile* out) {
   1341   out->io = io;
   1342   out->loaded = 0;
   1343   out->data.data = NULL;
   1344   out->data.size = 0;
   1345   out->data.token = NULL;
   1346   if (io->read_all(io->user, path, &out->data) != KIT_OK) return 1;
   1347   out->loaded = 1;
   1348   return 0;
   1349 }
   1350 
   1351 static void release_file(LoadedFile* lf) {
   1352   if (lf->loaded && lf->io && lf->io->release) {
   1353     lf->io->release(lf->io->user, &lf->data);
   1354   }
   1355   lf->loaded = 0;
   1356 }
   1357 
   1358 static void release_all(LoadedFile* arr, uint32_t n) {
   1359   uint32_t i;
   1360   if (!arr) return;
   1361   for (i = 0; i < n; ++i) release_file(&arr[i]);
   1362 }
   1363 
   1364 static int ld_append_hosted_input(LdOptions* o, const DriverHostedInput* in,
   1365                                   uint32_t insert_pos, int insert) {
   1366   switch ((DriverHostedInputKind)in->kind) {
   1367     case DRIVER_HOSTED_INPUT_OBJECT:
   1368       if (insert)
   1369         ld_insert_object(o, in->path, insert_pos);
   1370       else
   1371         ld_push_object(o, in->path);
   1372       return 0;
   1373     case DRIVER_HOSTED_INPUT_ARCHIVE: {
   1374       LdArchive* a = &o->archives[o->narchives++];
   1375       a->path = in->path;
   1376       a->owned = 0;
   1377       a->owned_size = 0;
   1378       a->whole_archive = 0;
   1379       a->link_mode = KIT_LM_DEFAULT;
   1380       a->group_id = 0;
   1381       if (insert)
   1382         ld_insert_order(o, insert_pos, KIT_LINK_INPUT_ARCHIVE,
   1383                         o->narchives - 1u);
   1384       else
   1385         ld_push_order(o, KIT_LINK_INPUT_ARCHIVE, o->narchives - 1u);
   1386       return 0;
   1387     }
   1388     case DRIVER_HOSTED_INPUT_DSO: {
   1389       LdDso* d = &o->dsos[o->ndsos++];
   1390       d->path = in->path;
   1391       d->owned = 0;
   1392       d->owned_size = 0;
   1393       if (insert)
   1394         ld_insert_order(o, insert_pos, KIT_LINK_INPUT_DSO, o->ndsos - 1u);
   1395       else
   1396         ld_push_order(o, KIT_LINK_INPUT_DSO, o->ndsos - 1u);
   1397       return 0;
   1398     }
   1399     default:
   1400       driver_errf(LD_TOOL, "internal error: unknown hosted input kind");
   1401       return 1;
   1402   }
   1403 }
   1404 
   1405 static int ld_apply_hosted_before_after(LdOptions* o) {
   1406   DriverHostedRequest req;
   1407   uint32_t i;
   1408   uint32_t insert_pos = 0;
   1409   if (!o->wants_hosted_libc || o->shared) return 0;
   1410   memset(&req, 0, sizeof req);
   1411   req.env = o->env;
   1412   req.tool = LD_TOOL;
   1413   req.target = o->target;
   1414   req.sysroot = o->sysroot;
   1415   req.static_link = o->static_link;
   1416   req.link_inputs = 1;
   1417   if (driver_hosted_resolve(&req, &o->hosted) != 0) return 1;
   1418   /* Add hosted lib search dirs so user -l flags resolve against the sysroot. */
   1419   for (i = 0; i < o->hosted.nlib_search_dirs; ++i)
   1420     o->lib_dirs[o->nlib_dirs++] = o->hosted.lib_search_dirs[i];
   1421   for (i = 0; i < o->hosted.nbefore; ++i) {
   1422     if (ld_append_hosted_input(o, &o->hosted.before[i], insert_pos, 1) != 0)
   1423       return 1;
   1424     insert_pos++;
   1425   }
   1426   for (i = 0; i < o->hosted.nafter; ++i) {
   1427     if (ld_append_hosted_input(o, &o->hosted.after[i], 0, 0) != 0) return 1;
   1428   }
   1429   if (!o->interp_path && o->hosted.interp_path)
   1430     o->interp_path = o->hosted.interp_path;
   1431   return 0;
   1432 }
   1433 
   1434 static int ld_apply_hosted_final(LdOptions* o) {
   1435   uint32_t i;
   1436   if (!o->hosted.profile_name) return 0;
   1437   for (i = 0; i < o->hosted.nfinal; ++i) {
   1438     if (ld_append_hosted_input(o, &o->hosted.final[i], 0, 0) != 0) return 1;
   1439   }
   1440   return 0;
   1441 }
   1442 
   1443 /* ---------- link execution ---------- */
   1444 
   1445 /* Run the link. Returns 0 on success, nonzero on error. The caller owns
   1446  * `o` and any allocations within; this function only owns transient
   1447  * loaded-file buffers and the writer/compiler it constructs. */
   1448 static int ld_run_link(LdOptions* o) {
   1449   KitContext ctx = driver_env_to_context(o->env);
   1450   const KitFileIO* io = ctx.file_io;
   1451   KitTarget* target = NULL;
   1452   KitCompiler* compiler = NULL;
   1453   KitWriter* writer = NULL;
   1454   LoadedFile* obj_lf = NULL;
   1455   LoadedFile* arch_lf = NULL;
   1456   LoadedFile* dso_lf = NULL;
   1457   LoadedFile script_lf = {0};
   1458   KitSlice* obj_in = NULL;
   1459   KitLinkArchiveInput* arch_in = NULL;
   1460   KitSlice* dso_in = NULL;
   1461   KitLinkScript* script = NULL;
   1462   KitLinkSession* link = NULL;
   1463   DriverRuntimeSupport runtime = {0};
   1464   DriverRuntimeArchive rt_archive = {0};
   1465   uint32_t i;
   1466   uint32_t initial_nobject_files;
   1467   int runtime_resolved = 0;
   1468   int rc = 1;
   1469 
   1470   if (!io || !io->read_all || !io->open_writer) {
   1471     driver_errf(LD_TOOL, "host file I/O unavailable");
   1472     return 1;
   1473   }
   1474 
   1475   /* Load the caller's object files first so the final target is known before
   1476    * deciding which hosted CRT/libc profile and compiler-runtime archive to
   1477    * add. The arrays are sized to the argv bound because hosted expansion may
   1478    * append start files after target detection. */
   1479   initial_nobject_files = o->nobject_files;
   1480   if (o->argv_bound) {
   1481     obj_lf = driver_alloc_zeroed(o->env, o->argv_bound * sizeof(*obj_lf));
   1482     obj_in = driver_alloc_zeroed(o->env, o->argv_bound * sizeof(*obj_in));
   1483     if (!obj_lf || !obj_in) {
   1484       driver_errf(LD_TOOL, "out of memory");
   1485       goto out;
   1486     }
   1487   }
   1488   for (i = 0; i < initial_nobject_files; ++i) {
   1489     const char* path = o->object_files[i];
   1490     if (load_file(io, path, &obj_lf[i]) != 0) {
   1491       driver_errf(LD_TOOL, "failed to read: %.*s",
   1492                   KIT_SLICE_ARG(kit_slice_cstr(path)));
   1493       goto out;
   1494     }
   1495     obj_in[i].data = obj_lf[i].data.data;
   1496     obj_in[i].len = obj_lf[i].data.size;
   1497   }
   1498 
   1499   /* Auto-detect target from the object inputs, falling back to the host target.
   1500    * The PIC default is a property of the *target*, not the host: hosted targets
   1501    * default to PIE, freestanding ones to no-PIE (driver_default_pic). The host's
   1502    * default PIC must never leak onto a detected cross target; only an EXPLICIT
   1503    * -static/-pie/-no-pie/-shared wins.
   1504    *
   1505    * Detection takes the arch from the first object, but the OS from any object:
   1506    * a freestanding (`*-none-elf`, EI_OSABI=STANDALONE) object means a freestanding
   1507    * link even if a foreign object (e.g. a clang-assembled startup stub, which
   1508    * stamps EI_OSABI=SysV and so decodes as Linux) appears first. */
   1509   if (initial_nobject_files > 0) {
   1510     KitTargetSpec detected;
   1511     if (kit_detect_target(obj_lf[0].data.data, obj_lf[0].data.size,
   1512                           &detected) == KIT_OK) {
   1513       uint8_t pic = o->target.pic;
   1514       uint32_t oi;
   1515       for (oi = 1; oi < initial_nobject_files; ++oi) {
   1516         KitTargetSpec t;
   1517         if (kit_detect_target(obj_lf[oi].data.data, obj_lf[oi].data.size, &t) ==
   1518             KIT_OK) {
   1519           if (t.os == KIT_OS_FREESTANDING) detected.os = KIT_OS_FREESTANDING;
   1520           /* Reconcile the float ABI across inputs. A foreign startup stub (e.g.
   1521            * a clang-assembled .S) may not carry the float-ABI e_flags that the
   1522            * kit-compiled objects do, so prefer any object's hardware ABI
   1523            * (single/double) over a soft/unset reading. This picks the runtime
   1524            * variant (rv32 ilp32 vs ilp32f) the real code was compiled for even
   1525            * when obj[0] is the stub. */
   1526           if (detected.float_abi != KIT_FLOAT_ABI_SINGLE &&
   1527               detected.float_abi != KIT_FLOAT_ABI_DOUBLE &&
   1528               (t.float_abi == KIT_FLOAT_ABI_SINGLE ||
   1529                t.float_abi == KIT_FLOAT_ABI_DOUBLE))
   1530             detected.float_abi = t.float_abi;
   1531         }
   1532       }
   1533       o->target = detected;
   1534       if (o->pic_explicit)
   1535         o->target.pic = pic;
   1536       else
   1537         o->target.pic = driver_default_pic(o->target.obj, o->target.os);
   1538     }
   1539   }
   1540 
   1541   if (ld_apply_hosted_before_after(o) != 0) goto out;
   1542 
   1543   /* Hosted dynamic link: default to PIE, matching what `kit cc` does.
   1544    * Without PIE the link_dyn path (PLT/GOT/.rela.plt) is not entered and
   1545    * any CALL26/PLT32 reloc against an imported symbol faults at apply time.
   1546    * Only override when the user has not made an explicit PIC choice.
   1547    * Non-PIE (ET_EXEC) dynamic linking is not yet supported; reject early. */
   1548   if (o->wants_hosted_libc && !o->static_link && !o->shared &&
   1549       !o->relocatable) {
   1550     if (o->pic_explicit && !o->pie) {
   1551       driver_errf(LD_TOOL,
   1552                   "-lc with -no-pie is not supported; use -pie (default) "
   1553                   "or -static for a static link");
   1554       goto out;
   1555     }
   1556     if (!o->pic_explicit)
   1557       o->pie = 1;
   1558   }
   1559 
   1560   /* Auto-link kit's compiler runtime for any target that has a variant —
   1561    * including the freestanding riscv32-none-elf / riscv64-none-elf targets,
   1562    * whose runtime (and, for rv32, the float-ABI it was detected with) is
   1563    * resolved like every other target's. A target with no variant is implicitly
   1564    * -nostdlib and supplies its own libkit_rt.a on the command line, so skip
   1565    * resolution rather than erroring. -nostdlib forces the skip for any target. */
   1566   if (!o->relocatable && !o->no_default_libs &&
   1567       driver_runtime_has_variant(o->target)) {
   1568     if (driver_runtime_resolve(o->env, o->support_dir, o->driver_path,
   1569                                &runtime) != 0) {
   1570       driver_errf(LD_TOOL, "support dir not found");
   1571       goto out;
   1572     }
   1573     runtime_resolved = 1;
   1574     if (driver_runtime_prepare_archive(o->env, LD_TOOL, &runtime, o->target, 0,
   1575                                        &rt_archive) != 0)
   1576       goto out;
   1577     ld_push_runtime_archive(o, &rt_archive);
   1578   }
   1579 
   1580   if (ld_apply_hosted_final(o) != 0) goto out;
   1581 
   1582   for (i = initial_nobject_files; i < o->nobject_files; ++i) {
   1583     const char* path = o->object_files[i];
   1584     if (load_file(io, path, &obj_lf[i]) != 0) {
   1585       driver_errf(LD_TOOL, "failed to read: %.*s",
   1586                   KIT_SLICE_ARG(kit_slice_cstr(path)));
   1587       goto out;
   1588     }
   1589     obj_in[i].data = obj_lf[i].data.data;
   1590     obj_in[i].len = obj_lf[i].data.size;
   1591   }
   1592 
   1593   if (o->narchives) {
   1594     arch_lf = driver_alloc_zeroed(o->env, o->narchives * sizeof(*arch_lf));
   1595     arch_in = driver_alloc_zeroed(o->env, o->narchives * sizeof(*arch_in));
   1596     if (!arch_lf || !arch_in) {
   1597       driver_errf(LD_TOOL, "out of memory");
   1598       goto out;
   1599     }
   1600   }
   1601   if (o->ndsos) {
   1602     dso_lf = driver_alloc_zeroed(o->env, o->ndsos * sizeof(*dso_lf));
   1603     dso_in = driver_alloc_zeroed(o->env, o->ndsos * sizeof(*dso_in));
   1604     if (!dso_lf || !dso_in) {
   1605       driver_errf(LD_TOOL, "out of memory");
   1606       goto out;
   1607     }
   1608   }
   1609 
   1610   /* Load archives. */
   1611   for (i = 0; i < o->narchives; ++i) {
   1612     const LdArchive* a = &o->archives[i];
   1613     if (load_file(io, a->path, &arch_lf[i]) != 0) {
   1614       driver_errf(LD_TOOL, "failed to read: %.*s",
   1615                   KIT_SLICE_ARG(kit_slice_cstr(a->path)));
   1616       goto out;
   1617     }
   1618     arch_in[i].name = kit_slice_cstr(a->path);
   1619     arch_in[i].bytes.data = arch_lf[i].data.data;
   1620     arch_in[i].bytes.len = arch_lf[i].data.size;
   1621     arch_in[i].whole_archive = a->whole_archive;
   1622     arch_in[i].link_mode = a->link_mode;
   1623     arch_in[i].group_id = a->group_id;
   1624   }
   1625   /* Load shared objects. */
   1626   for (i = 0; i < o->ndsos; ++i) {
   1627     const LdDso* d = &o->dsos[i];
   1628     if (load_file(io, d->path, &dso_lf[i]) != 0) {
   1629       driver_errf(LD_TOOL, "failed to read: %.*s",
   1630                   KIT_SLICE_ARG(kit_slice_cstr(d->path)));
   1631       goto out;
   1632     }
   1633     dso_in[i].data = dso_lf[i].data.data;
   1634     dso_in[i].len = dso_lf[i].data.size;
   1635   }
   1636 
   1637   /* Load and parse the linker script (if any). The structured script is
   1638    * arena-owned by the compiler; we free it explicitly before the
   1639    * compiler is destroyed. */
   1640   if (o->script_path) {
   1641     if (load_file(io, o->script_path, &script_lf) != 0) {
   1642       driver_errf(LD_TOOL, "failed to read: %.*s",
   1643                   KIT_SLICE_ARG(kit_slice_cstr(o->script_path)));
   1644       goto out;
   1645     }
   1646   }
   1647 
   1648   {
   1649     KitTargetOptions topts;
   1650     memset(&topts, 0, sizeof topts);
   1651     topts.spec = o->target;
   1652     if (kit_target_new(&ctx, &topts, &target) != KIT_OK ||
   1653         driver_compiler_new(target, &ctx, &compiler) != KIT_OK) {
   1654       driver_errf(LD_TOOL, "failed to initialize compiler");
   1655       goto out;
   1656     }
   1657   }
   1658 
   1659   if (script_lf.loaded) {
   1660     KitSlice script_text = {.s = (const char*)script_lf.data.data,
   1661                             .len = script_lf.data.size};
   1662     if (kit_link_script_parse(&ctx, script_text, &script) != KIT_OK) {
   1663       /* The parser reports a diagnostic via env.diag. */
   1664       goto out;
   1665     }
   1666   }
   1667 
   1668   if (io->open_writer(io->user, o->output_path, &writer) != KIT_OK) {
   1669     driver_errf(LD_TOOL, "failed to open output: %.*s",
   1670                 KIT_SLICE_ARG(kit_slice_cstr(o->output_path)));
   1671     goto out;
   1672   }
   1673 
   1674   if (o->soname || o->nrpaths || o->nrpath_links) {
   1675     if (!o->shared) {
   1676       driver_errf(LD_TOOL, "-soname/-rpath/-rpath-link require -shared");
   1677       goto out;
   1678     }
   1679   }
   1680 
   1681   {
   1682     KitLinkSessionOptions lopts;
   1683     KitStatus st;
   1684     KitSlice* rpath_slices = NULL;
   1685     /* Lift the argv-derived -rpath dirs into slices for the linker. */
   1686     if (o->nrpaths) {
   1687       rpath_slices =
   1688           driver_alloc_zeroed(o->env, o->nrpaths * sizeof(*rpath_slices));
   1689       if (!rpath_slices) {
   1690         driver_errf(LD_TOOL, "out of memory");
   1691         goto out;
   1692       }
   1693       for (i = 0; i < o->nrpaths; ++i)
   1694         rpath_slices[i] = kit_slice_cstr(o->rpaths[i]);
   1695     }
   1696     memset(&lopts, 0, sizeof(lopts));
   1697     lopts.output_kind = o->relocatable ? KIT_LINK_OUTPUT_RELOCATABLE
   1698                         : o->shared    ? KIT_LINK_OUTPUT_SHARED
   1699                                        : KIT_LINK_OUTPUT_EXE;
   1700     lopts.entry = kit_slice_cstr(o->entry);
   1701     lopts.text_base_set = o->text_base_set;
   1702     lopts.text_base = o->text_base;
   1703     lopts.linker_script = script;
   1704     lopts.build_id_mode = o->build_id_mode;
   1705     lopts.build_id_bytes = o->build_id_bytes;
   1706     lopts.build_id_len = o->build_id_len;
   1707     lopts.gc_sections = o->gc_sections;
   1708     lopts.strip_debug = o->strip_debug;
   1709     /* GNU ld and lld default to a non-PIE ET_EXEC; PIE is opt-in via -pie.
   1710      * The linker imposes no hosted-PIE default the way a compiler driver
   1711      * does (kit cc still defaults hosted executables to PIE through the link
   1712      * API) — a freestanding or static link has no dynamic loader to apply a
   1713      * PIE image's relocations or choose its base, so a PIE default produces a
   1714      * binary that faults under a direct/qemu loader (its writable segments
   1715      * sit below mmap_min_addr at the vaddr-0 PIE base). -shared/-r never PIE.
   1716      * Callers that want a hosted PIE executable pass -pie explicitly (as the
   1717      * musl dynamic lane and real compiler drivers do). */
   1718     lopts.pie = o->pie && !o->shared && !o->relocatable;
   1719     lopts.pe_subsystem = o->pe_subsystem;
   1720     lopts.interp_path = kit_slice_cstr(o->interp_path);
   1721     lopts.soname = kit_slice_cstr(o->soname);
   1722     /* Per --enable-new-dtags / --disable-new-dtags: when new_dtags is
   1723      * set (the default), -rpath entries land in DT_RUNPATH; otherwise
   1724      * in DT_RPATH. -rpath-link is link-time-only and is forwarded as
   1725      * runpaths so the library has a record of the search paths used
   1726      * (advisory; ELF runtime ignores DT_RUNPATH entries it didn't
   1727      * write itself, so this matches GNU-ld behaviour where rpath-link
   1728      * does not appear in DT_*PATH). */
   1729     if (o->new_dtags) {
   1730       lopts.runpaths = rpath_slices;
   1731       lopts.nrunpaths = o->nrpaths;
   1732     } else {
   1733       lopts.rpaths = rpath_slices;
   1734       lopts.nrpaths = o->nrpaths;
   1735     }
   1736     /* By default shared output may resolve symbols against its loader at
   1737      * runtime; --no-undefined / -z defs tightens that policy. */
   1738     lopts.allow_undefined = o->allow_undefined ? 1 : 0;
   1739     (void)o->export_dynamic;
   1740 
   1741     st = kit_link_session_new(compiler, &lopts, &link);
   1742     if (o->order && o->norder) {
   1743       for (i = 0; i < o->norder && st == KIT_OK; ++i) {
   1744         const KitLinkInputOrder* ord = &o->order[i];
   1745         switch ((KitLinkInputOrderKind)ord->kind) {
   1746           case KIT_LINK_INPUT_OBJ:
   1747           case KIT_LINK_INPUT_OBJ_BYTES:
   1748             st = kit_link_session_add_obj_bytes(
   1749                 link, kit_slice_cstr(o->object_files[ord->index]),
   1750                 &obj_in[ord->index]);
   1751             break;
   1752           case KIT_LINK_INPUT_ARCHIVE:
   1753             st = kit_link_session_add_archive_bytes(link, &arch_in[ord->index]);
   1754             break;
   1755           case KIT_LINK_INPUT_DSO:
   1756             st = kit_link_session_add_dso_bytes(
   1757                 link, kit_slice_cstr(o->dsos[ord->index].path),
   1758                 &dso_in[ord->index]);
   1759             break;
   1760         }
   1761       }
   1762     } else {
   1763       for (i = 0; i < o->nobject_files && st == KIT_OK; ++i)
   1764         st = kit_link_session_add_obj_bytes(
   1765             link, kit_slice_cstr(o->object_files[i]), &obj_in[i]);
   1766       for (i = 0; i < o->narchives && st == KIT_OK; ++i)
   1767         st = kit_link_session_add_archive_bytes(link, &arch_in[i]);
   1768       for (i = 0; i < o->ndsos && st == KIT_OK; ++i)
   1769         st = kit_link_session_add_dso_bytes(
   1770             link, kit_slice_cstr(o->dsos[i].path), &dso_in[i]);
   1771     }
   1772     if (st == KIT_OK) st = kit_link_session_emit(link, writer);
   1773     rc = st == KIT_OK ? 0 : 1;
   1774   }
   1775 
   1776 out:
   1777   if (writer) kit_writer_close(writer);
   1778   kit_link_session_free(link);
   1779   /* Match compiler/linker drivers: successful link outputs get executable
   1780    * file modes while still respecting the process umask. Done after closing
   1781    * the writer so the bits are stable on disk. */
   1782   if (rc == 0 && o->output_path && !o->relocatable) {
   1783     if (driver_mark_executable_output(o->output_path) != 0) {
   1784       driver_errf(LD_TOOL, "failed to set executable mode: %.*s",
   1785                   KIT_SLICE_ARG(kit_slice_cstr(o->output_path)));
   1786       rc = 1;
   1787     }
   1788   }
   1789   if (script) kit_link_script_free(&ctx, script);
   1790   if (compiler) driver_compiler_free(compiler);
   1791   kit_target_free(target);
   1792   driver_runtime_archive_fini(o->env, &rt_archive);
   1793   if (runtime_resolved) driver_runtime_support_fini(o->env, &runtime);
   1794   release_file(&script_lf);
   1795   release_all(arch_lf, o->narchives);
   1796   release_all(obj_lf, o->nobject_files);
   1797   release_all(dso_lf, o->ndsos);
   1798   if (arch_in) driver_free(o->env, arch_in, o->narchives * sizeof(*arch_in));
   1799   if (arch_lf) driver_free(o->env, arch_lf, o->narchives * sizeof(*arch_lf));
   1800   if (obj_in) driver_free(o->env, obj_in, o->argv_bound * sizeof(*obj_in));
   1801   if (obj_lf) driver_free(o->env, obj_lf, o->argv_bound * sizeof(*obj_lf));
   1802   if (dso_in) driver_free(o->env, dso_in, o->ndsos * sizeof(*dso_in));
   1803   if (dso_lf) driver_free(o->env, dso_lf, o->ndsos * sizeof(*dso_lf));
   1804   return rc;
   1805 }
   1806 
   1807 int driver_ld(int argc, char** argv) {
   1808   DriverEnv env;
   1809   LdOptions lo = {0};
   1810   int rc;
   1811 
   1812   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
   1813     driver_help_ld();
   1814     return 0;
   1815   }
   1816 
   1817   driver_env_init(&env);
   1818   lo.env = &env;
   1819   lo.driver_path = argv[0];
   1820 
   1821   if (ld_parse(argc, argv, &lo) != 0) {
   1822     ld_options_release(&lo);
   1823     driver_env_fini(&env);
   1824     return 2;
   1825   }
   1826 
   1827   rc = ld_run_link(&lo);
   1828 
   1829   ld_options_release(&lo);
   1830   driver_env_fini(&env);
   1831   return rc;
   1832 }