kit

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

link_resolve.c (56423B)


      1 /* link_resolve.c — archive ingest, symbol resolution, --gc-sections liveness.
      2  *
      3  * Phase 1 of the link pipeline:
      4  *   link_ingest_archives  — pull archive members into l->inputs
      5  *   link_resolve_symbols  — register every ObjSym, build img->globals
      6  *   link_resolve_undefs   — satisfy remaining undefs (globals/DSOs/resolver)
      7  *   link_gc_compute       — mark live sections (or mark all live if disabled)
      8  *   link_gc_drop_dead_globals — clear `defined` on syms in dropped sections
      9  */
     10 
     11 #include <kit/core.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 
     15 #include "core/buf.h"
     16 #include "core/bytes.h"
     17 #include "core/heap.h"
     18 #include "core/pool.h"
     19 #include "core/slice.h"
     20 #include "core/util.h"
     21 #include "core/vec.h"
     22 #include "link/link.h"
     23 #include "link/link_arch.h"
     24 #include "link/link_internal.h"
     25 
     26 /* ---- per-input symbol/section maps ---- */
     27 
     28 typedef struct AtomSortRec {
     29   ObjAtomId id;
     30   ObjSecId section_id;
     31   u32 offset;
     32 } AtomSortRec;
     33 
     34 static int atom_sort_rec_cmp(const void* av, const void* bv) {
     35   const AtomSortRec* a = (const AtomSortRec*)av;
     36   const AtomSortRec* b = (const AtomSortRec*)bv;
     37   if (a->section_id < b->section_id) return -1;
     38   if (a->section_id > b->section_id) return 1;
     39   if (a->offset < b->offset) return -1;
     40   if (a->offset > b->offset) return 1;
     41   if (a->id < b->id) return -1;
     42   if (a->id > b->id) return 1;
     43   return 0;
     44 }
     45 
     46 static ObjAtomId input_map_find_atom(const InputMap* m, ObjBuilder* ob,
     47                                      ObjSecId sid, u32 offset) {
     48   u32 first, count, i;
     49   if (!link_input_section_has_atoms(m, sid)) return OBJ_ATOM_NONE;
     50   link_input_section_atoms(m, sid, &first, &count);
     51   for (i = 0; i < count; ++i) {
     52     ObjAtomId aid = m->section_atom_ids[first + i];
     53     const ObjAtom* a = obj_atom_get(ob, aid);
     54     u64 begin, end;
     55     if (!a || a->removed) continue;
     56     begin = a->offset;
     57     end = begin + a->size;
     58     if (a->size != 0 && (u64)offset >= begin && (u64)offset < end) return aid;
     59   }
     60   for (i = 0; i < count; ++i) {
     61     ObjAtomId aid = m->section_atom_ids[first + i];
     62     const ObjAtom* a = obj_atom_get(ob, aid);
     63     if (!a || a->removed) continue;
     64     if (a->size == 0 && offset == a->offset) return aid;
     65   }
     66   return OBJ_ATOM_NONE;
     67 }
     68 
     69 static ObjAtomId input_map_find_symbol_atom(const InputMap* m, ObjBuilder* ob,
     70                                             ObjSymId sym) {
     71   const ObjSym* s;
     72   ObjAtomId aid;
     73   u32 i;
     74   if (!ob || sym == OBJ_SYM_NONE) return OBJ_ATOM_NONE;
     75   s = obj_symbol_get(ob, sym);
     76   if (!s || s->section_id == OBJ_SEC_NONE) return OBJ_ATOM_NONE;
     77   aid = input_map_find_atom(m, ob, s->section_id, (u32)s->value);
     78   if (aid != OBJ_ATOM_NONE) return aid;
     79   for (i = 0; i < m->nsection_atom_ids; ++i) {
     80     const ObjAtom* a = obj_atom_get(ob, m->section_atom_ids[i]);
     81     if (a && !a->removed && a->signature == sym) return m->section_atom_ids[i];
     82   }
     83   return OBJ_ATOM_NONE;
     84 }
     85 
     86 void link_input_map_alloc(LinkImage* img, InputMap* m, ObjBuilder* ob,
     87                           u32 nsym) {
     88   Heap* h = img->heap;
     89   u32 nsection = obj_section_count(ob);
     90   u32 natom = obj_atom_count(ob);
     91   u32 nreloc = obj_reloc_total(ob);
     92   AtomSortRec* atoms = NULL;
     93   u32 nactive = 0;
     94   u32 i;
     95 
     96   memset(m, 0, sizeof(*m));
     97   m->nsym = nsym;
     98   m->sym = (LinkSymId*)h->alloc(h, sizeof(*m->sym) * nsym, _Alignof(LinkSymId));
     99   if (!m->sym)
    100     compiler_panic(img->c, SRCLOC_NONE, "link: oom on input symbol map");
    101   memset(m->sym, 0, sizeof(*m->sym) * nsym);
    102   m->nsection = nsection;
    103   m->section = (LinkSectionId*)h->alloc(h, sizeof(*m->section) * nsection,
    104                                         _Alignof(LinkSectionId));
    105   if (!m->section)
    106     compiler_panic(img->c, SRCLOC_NONE, "link: oom on input section map");
    107   memset(m->section, 0, sizeof(*m->section) * nsection);
    108   m->natom = natom;
    109   m->atom = (LinkSectionId*)h->alloc(h, sizeof(*m->atom) * (natom ? natom : 1u),
    110                                      _Alignof(LinkSectionId));
    111   if (!m->atom)
    112     compiler_panic(img->c, SRCLOC_NONE, "link: oom on input atom map");
    113   memset(m->atom, 0, sizeof(*m->atom) * (natom ? natom : 1u));
    114   m->sym_atom = (ObjAtomId*)h->alloc(
    115       h, sizeof(*m->sym_atom) * (nsym ? nsym : 1u), _Alignof(ObjAtomId));
    116   if (!m->sym_atom)
    117     compiler_panic(img->c, SRCLOC_NONE, "link: oom on input symbol atom map");
    118   memset(m->sym_atom, 0, sizeof(*m->sym_atom) * (nsym ? nsym : 1u));
    119   m->nreloc = nreloc;
    120   m->reloc_atom = (ObjAtomId*)h->alloc(
    121       h, sizeof(*m->reloc_atom) * (nreloc ? nreloc : 1u), _Alignof(ObjAtomId));
    122   if (!m->reloc_atom)
    123     compiler_panic(img->c, SRCLOC_NONE, "link: oom on input reloc atom map");
    124   memset(m->reloc_atom, 0, sizeof(*m->reloc_atom) * (nreloc ? nreloc : 1u));
    125   m->section_has_atoms = (u8*)h->alloc(h, nsection ? nsection : 1u, 1);
    126   if (!m->section_has_atoms)
    127     compiler_panic(img->c, SRCLOC_NONE, "link: oom on input section atom map");
    128   memset(m->section_has_atoms, 0, nsection ? nsection : 1u);
    129   m->section_atom_first = (u32*)h->alloc(
    130       h, sizeof(*m->section_atom_first) * (nsection ? nsection : 1u),
    131       _Alignof(u32));
    132   m->section_atom_count = (u32*)h->alloc(
    133       h, sizeof(*m->section_atom_count) * (nsection ? nsection : 1u),
    134       _Alignof(u32));
    135   if (!m->section_atom_first || !m->section_atom_count)
    136     compiler_panic(img->c, SRCLOC_NONE,
    137                    "link: oom on input section atom ranges");
    138   memset(m->section_atom_first, 0,
    139          sizeof(*m->section_atom_first) * (nsection ? nsection : 1u));
    140   memset(m->section_atom_count, 0,
    141          sizeof(*m->section_atom_count) * (nsection ? nsection : 1u));
    142   m->comdat_discarded = (u8*)h->alloc(h, nsection ? nsection : 1u, 1);
    143   if (!m->comdat_discarded)
    144     compiler_panic(img->c, SRCLOC_NONE, "link: oom on input comdat map");
    145   memset(m->comdat_discarded, 0, nsection ? nsection : 1u);
    146 
    147   if (natom > 1u) {
    148     atoms = (AtomSortRec*)h->alloc(h, sizeof(*atoms) * natom,
    149                                    _Alignof(AtomSortRec));
    150     if (!atoms)
    151       compiler_panic(img->c, SRCLOC_NONE, "link: oom on atom sort map");
    152     for (i = 1; i < natom; ++i) {
    153       const ObjAtom* a = obj_atom_get(ob, (ObjAtomId)i);
    154       if (!a || a->removed || a->section_id == OBJ_SEC_NONE ||
    155           a->section_id >= nsection)
    156         continue;
    157       atoms[nactive].id = (ObjAtomId)i;
    158       atoms[nactive].section_id = a->section_id;
    159       atoms[nactive].offset = a->offset;
    160       ++nactive;
    161     }
    162     if (nactive > 1u) qsort(atoms, nactive, sizeof(*atoms), atom_sort_rec_cmp);
    163   }
    164 
    165   m->nsection_atom_ids = nactive;
    166   if (nactive) {
    167     ObjSecId cur = OBJ_SEC_NONE;
    168     m->section_atom_ids = (ObjAtomId*)h->alloc(
    169         h, sizeof(*m->section_atom_ids) * nactive, _Alignof(ObjAtomId));
    170     if (!m->section_atom_ids)
    171       compiler_panic(img->c, SRCLOC_NONE, "link: oom on section atom ids");
    172     for (i = 0; i < nactive; ++i) {
    173       ObjSecId sid = atoms[i].section_id;
    174       m->section_atom_ids[i] = atoms[i].id;
    175       if (sid != cur) {
    176         m->section_has_atoms[sid] = 1;
    177         m->section_atom_first[sid] = i;
    178         cur = sid;
    179       }
    180       m->section_atom_count[sid]++;
    181     }
    182   }
    183   if (atoms) h->free(h, atoms, sizeof(*atoms) * natom);
    184 
    185   for (i = 1; i < nsym; ++i)
    186     m->sym_atom[i] = input_map_find_symbol_atom(m, ob, (ObjSymId)i);
    187   for (i = 0; i < nreloc; ++i) {
    188     const Reloc* r = obj_reloc_at(ob, i);
    189     if (!r || r->section_id == OBJ_SEC_NONE) continue;
    190     m->reloc_atom[i] = input_map_find_atom(m, ob, r->section_id, r->offset);
    191   }
    192 }
    193 
    194 /* ---- pass 1: collect symbols ---- */
    195 
    196 /* A symbol with no home section and no absolute/common pseudo-value: the
    197  * importer's view of an undefined reference (or a DSO export). Distinct
    198  * from link_sym_is_def — SK_FILE symbols are logical undefs here but
    199  * defs there. Used only by scan_presence_before to split logical undefs
    200  * from logical defs; the spurious-undef prune routes through the shared
    201  * link_sym_is_spurious_undef. */
    202 static int obj_sym_is_logical_undef(const ObjSym* s) {
    203   return s && s->section_id == OBJ_SEC_NONE && s->kind != SK_ABS &&
    204          s->kind != SK_COMMON;
    205 }
    206 
    207 /* COFF/PE SELECTANY: a duplicate strong global is acceptable iff both
    208  * definitions live in COMDAT (SF_GROUP-tagged) sections.  When that
    209  * holds, the earlier-processed definition wins and the new section is
    210  * marked for discard so its bytes never reach layout. */
    211 static int obj_sym_defined_in_comdat(ObjBuilder* ob, const ObjSym* s) {
    212   const Section* sec;
    213   if (!s || s->section_id == OBJ_SEC_NONE) return 0;
    214   sec = obj_section_get(ob, s->section_id);
    215   return sec && (sec->flags & SF_GROUP);
    216 }
    217 
    218 void link_resolve_symbols(Linker* l, LinkImage* img) {
    219   u32 ii;
    220   for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
    221     LinkInput* in = LinkInputs_at(&l->inputs, ii);
    222     ObjBuilder* ob = in->obj;
    223     InputMap* m = &img->input_maps[ii];
    224     u32 nsym = obj_section_count(ob);
    225     (void)nsym;
    226     ObjSymIter* it;
    227     ObjSymEntry e;
    228 
    229     if (in->kind == LINK_INPUT_DSO_BYTES) continue;
    230 
    231     u32 nsyms_in_input = 0;
    232     it = obj_symiter_new(ob);
    233     while (obj_symiter_next(it, &e)) ++nsyms_in_input;
    234     obj_symiter_free(it);
    235 
    236     link_input_map_alloc(img, m, ob,
    237                          nsyms_in_input + 1u /* +1 for id-0 slot */);
    238 
    239     /* Pre-size the global symbol map for this input's symbols so the bulk
    240      * insert below never rehashes mid-stream. Only globals/weaks actually land
    241      * in img->globals, so this slightly over-reserves; it kills the resize
    242      * cascade (256 -> N) on symbol-heavy inputs (e.g. a large linked object). */
    243     symhash_reserve(&img->globals, img->globals.used + nsyms_in_input);
    244 
    245     it = obj_symiter_new(ob);
    246     while (obj_symiter_next(it, &e)) {
    247       const ObjSym* s = e.sym;
    248       LinkSymbol rec;
    249       LinkSymId existing;
    250       /* Tombstoned symbols are deleted, not just undefined: a file-format
    251        * emitter (which runs obj_sweep_dead, then `if (s->removed) continue`)
    252        * never writes them, so a serialized-then-re-read input carries none.
    253        * An in-memory ObjBuilder linked directly (the one-shot compile+link
    254        * path) still holds them, so honor the same `removed` contract here.
    255        * Without this, a deferred-but-never-materialized local rodata constant
    256        * (`obj_symbol_defer` leaves `.Lkit_ro.N` as a removed local SK_OBJ when
    257        * its function is optimized away at -O1) survives as an undefined local
    258        * and trips the "undefined reference to '.Lkit_ro.N'" panic below.
    259        * Leaving such a symbol unregistered (m->sym stays LINK_SYM_NONE); a
    260        * reloc that still targets it is dead and is dropped in the reloc passes
    261        * (matching obj_sweep_dead pass 3, which retires those relocs on write). */
    262       if (s->removed) continue;
    263       if (link_sym_is_spurious_undef(s)) continue;
    264       int is_def = link_sym_is_def(s);
    265 
    266       memset(&rec, 0, sizeof(rec));
    267       rec.name = s->name;
    268       rec.input_id = in->id;
    269       rec.obj_sym = e.id;
    270       rec.section_id = LINK_SEC_NONE;
    271       rec.atom_id = is_def ? link_input_sym_atom(m, e.id) : OBJ_ATOM_NONE;
    272       rec.value = s->value;
    273       rec.size = s->size;
    274       rec.common_align = (s->kind == SK_COMMON) ? (u32)s->common_align : 0u;
    275       rec.bind = (u8)s->bind;
    276       rec.kind = (u8)s->kind;
    277       rec.vis = (u8)s->vis;
    278       rec.defined = (u8)is_def;
    279       rec.vaddr = 0;
    280 
    281       if (is_def && (s->bind == SB_GLOBAL || s->bind == SB_WEAK) &&
    282           s->name != 0) {
    283         LinkSymId fresh = (LinkSymId)(LinkSyms_count(&img->syms) + 1u);
    284         if (symhash_insert(&img->globals, s->name, fresh, &existing)) {
    285           m->sym[e.id] = link_append_symbol(img, &rec);
    286         } else {
    287           /* A second definition of an existing global/weak name: hand the
    288            * binding-precedence decision to the shared policy module. The
    289            * COMDAT lookup (does prev's section carry SF_GROUP?) is the
    290            * caller-side bookkeeping symresolve deliberately leaves out. */
    291           LinkSymbol* prev = LinkSyms_at(&img->syms, existing - 1);
    292           ObjBuilder* prev_ob =
    293               (prev->input_id != LINK_INPUT_NONE)
    294                   ? LinkInputs_at(&l->inputs, prev->input_id - 1)->obj
    295                   : NULL;
    296           const ObjSym* prev_os =
    297               prev_ob ? obj_symbol_get(prev_ob, prev->obj_sym) : NULL;
    298           SymAttrs ex_a = {0};
    299           SymAttrs inc_a = {0};
    300           SymMergeResult mr;
    301           ex_a.bind = prev->bind;
    302           ex_a.kind = prev->kind;
    303           ex_a.size = prev->size;
    304           ex_a.common_align = prev->common_align;
    305           ex_a.in_comdat = (prev_ob && prev_os)
    306                                ? (u8)obj_sym_defined_in_comdat(prev_ob, prev_os)
    307                                : 0u;
    308           inc_a.bind = rec.bind;
    309           inc_a.kind = rec.kind;
    310           inc_a.size = rec.size;
    311           inc_a.common_align = rec.common_align;
    312           inc_a.in_comdat = (u8)obj_sym_defined_in_comdat(ob, s);
    313           mr = symresolve_merge(ex_a, inc_a);
    314           switch (mr.kind) {
    315             case SYM_MERGE_REPLACE:
    316               rec.id = existing;
    317               *prev = rec;
    318               m->sym[e.id] = existing;
    319               break;
    320             case SYM_MERGE_COMMON:
    321               rec.id = existing;
    322               rec.common_align = mr.merged_align;
    323               *prev = rec;
    324               m->sym[e.id] = existing;
    325               break;
    326             case SYM_MERGE_COMDAT_DISCARD:
    327               m->sym[e.id] = existing;
    328               if (s->section_id < m->nsection)
    329                 m->comdat_discarded[s->section_id] = 1;
    330               break;
    331             case SYM_MERGE_ODR_ERROR: {
    332               Slice nm_s = pool_slice(l->c->global, s->name);
    333               compiler_panic(l->c, SRCLOC_NONE,
    334                              "link: duplicate definition of "
    335                              "global symbol '%.*s'",
    336                              (int)nm_s.len, nm_s.s);
    337               break;
    338             }
    339             case SYM_MERGE_KEEP_EXISTING:
    340             default:
    341               m->sym[e.id] = existing;
    342               break;
    343           }
    344         }
    345       } else {
    346         m->sym[e.id] = link_append_symbol(img, &rec);
    347       }
    348     }
    349     obj_symiter_free(it);
    350   }
    351 }
    352 
    353 static int obj_sym_is_dso_export(const ObjSym* s) {
    354   return s && s->name != 0 && s->kind != SK_UNDEF && s->bind != SB_LOCAL;
    355 }
    356 
    357 static void dso_exports_build(Linker* l, SymHash* exports) {
    358   u32 ii;
    359   u32 nexports = 0;
    360   symhash_init(exports, l->heap);
    361   for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
    362     LinkInput* in = LinkInputs_at(&l->inputs, ii);
    363     ObjSymIter* it;
    364     ObjSymEntry e;
    365     if (in->kind != LINK_INPUT_DSO_BYTES) continue;
    366     it = obj_symiter_new(in->obj);
    367     while (obj_symiter_next(it, &e)) {
    368       if (obj_sym_is_dso_export(e.sym)) ++nexports;
    369     }
    370     obj_symiter_free(it);
    371   }
    372   symhash_reserve(exports, nexports);
    373   for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
    374     LinkInput* in = LinkInputs_at(&l->inputs, ii);
    375     ObjSymIter* it;
    376     ObjSymEntry e;
    377     if (in->kind != LINK_INPUT_DSO_BYTES) continue;
    378     it = obj_symiter_new(in->obj);
    379     while (obj_symiter_next(it, &e)) {
    380       LinkSymId existing;
    381       if (!obj_sym_is_dso_export(e.sym)) continue;
    382       /* Preserve command-line order: the old lookup returned the first DSO
    383        * exporting a name, so duplicate exports keep the earliest provider. */
    384       (void)symhash_insert(exports, e.sym->name, in->id, &existing);
    385     }
    386     obj_symiter_free(it);
    387   }
    388 }
    389 
    390 static int elf_split_versioned_undef(Compiler* c, Sym full, Sym* base_out,
    391                                      Sym* version_out) {
    392   Slice nm;
    393   u32 i;
    394   if (!c || full == 0 || !base_out || !version_out) return 0;
    395   nm = pool_slice(c->global, full);
    396   if (!nm.s || nm.len < 3u) return 0;
    397   for (i = 1u; i + 1u < nm.len; ++i) {
    398     u32 j;
    399     if (nm.s[i] != '@') continue;
    400     if (nm.s[i + 1u] == '@') return 0;
    401     for (j = i + 1u; j < nm.len; ++j)
    402       if (nm.s[j] == '@') return 0;
    403     *base_out =
    404         pool_intern_slice(c->global, (Slice){.s = nm.s, .len = i});
    405     *version_out = pool_intern_slice(
    406         c->global, (Slice){.s = nm.s + i + 1u, .len = nm.len - i - 1u});
    407     return *base_out != 0 && *version_out != 0;
    408   }
    409   return 0;
    410 }
    411 
    412 static const ObjImageSym* dso_dynsym_version(LinkInput* in, Sym name,
    413                                              Sym version) {
    414   const ObjImage* im;
    415   u32 i, n;
    416   if (!in || !in->obj || name == 0 || version == 0) return NULL;
    417   im = obj_image(in->obj);
    418   n = obj_image_ndynsyms(im);
    419   for (i = 0; i < n; ++i) {
    420     const ObjImageSym* s = obj_image_dynsym(im, i);
    421     if (!s || s->name != name) continue;
    422     if (s->section == OBJ_SEC_NONE && s->kind == SK_UNDEF) continue;
    423     if (s->bind == SB_LOCAL) continue;
    424     if (s->version == version) return s;
    425   }
    426   return NULL;
    427 }
    428 
    429 static LinkInputId find_dso_export_version(Linker* l, Sym name, Sym version,
    430                                            const ObjImageSym** sym_out) {
    431   u32 ii;
    432   if (sym_out) *sym_out = NULL;
    433   if (name == 0 || version == 0) return LINK_INPUT_NONE;
    434   for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
    435     LinkInput* in = LinkInputs_at(&l->inputs, ii);
    436     const ObjImageSym* s;
    437     if (in->kind != LINK_INPUT_DSO_BYTES) continue;
    438     s = dso_dynsym_version(in, name, version);
    439     if (!s) continue;
    440     if (sym_out) *sym_out = s;
    441     return in->id;
    442   }
    443   return LINK_INPUT_NONE;
    444 }
    445 
    446 static int resolve_elf_versioned_dso_undef(Linker* l, LinkSymbol* s) {
    447   Sym base = 0;
    448   Sym version = 0;
    449   const ObjImageSym* ds = NULL;
    450   LinkInputId dso;
    451   if (!l || !s || s->name == 0) return 0;
    452   if (l->c->target.obj != KIT_OBJ_ELF) return 0;
    453   if (!elf_split_versioned_undef(l->c, s->name, &base, &version)) return 0;
    454   dso = find_dso_export_version(l, base, version, &ds);
    455   if (dso == LINK_INPUT_NONE) return 0;
    456   s->name = base;
    457   s->kind = ds ? (u8)ds->kind : s->kind;
    458   s->imported = 1;
    459   s->dso_input_id = dso;
    460   s->elf_version = version;
    461   return 1;
    462 }
    463 
    464 /* Resolve undefined symbol `s` to the symbol named `alias` (a defined image
    465  * global or a DSO export), copying the target's binding into `s`. Returns 1 on
    466  * success. Shared by the recorded-alias path and the underscore heuristic. */
    467 static int resolve_to_alias(LinkImage* img, const SymHash* dso_exports,
    468                             LinkSymbol* s, Sym alias) {
    469   if (alias == 0) return 0;
    470   LinkSymId hit = symhash_get(&img->globals, alias);
    471   if (hit != LINK_SYM_NONE) {
    472     LinkSymbol* def = LinkSyms_at(&img->syms, hit - 1);
    473     if (def->defined || def->imported) {
    474       s->name = def->name;
    475       s->section_id = def->section_id;
    476       s->value = def->value;
    477       s->vaddr = def->vaddr;
    478       s->kind = def->kind;
    479       s->defined = def->defined;
    480       s->imported = def->imported;
    481       s->dso_input_id = def->dso_input_id;
    482       s->elf_version = def->elf_version;
    483       if (!s->defined && !s->imported) {
    484         s->kind = SK_ABS;
    485         s->vaddr = 0;
    486         s->defined = 1;
    487       }
    488       return 1;
    489     }
    490   }
    491   LinkInputId dso = symhash_get(dso_exports, alias);
    492   if (dso != LINK_INPUT_NONE) {
    493     s->name = alias;
    494     s->imported = 1;
    495     s->dso_input_id = dso;
    496     s->elf_version = 0;
    497     return 1;
    498   }
    499   return 0;
    500 }
    501 
    502 static int resolve_elf_loader_owned_undef(Linker* l, LinkSymbol* s) {
    503   Slice nm;
    504   if (!l || !s || !s->name) return 0;
    505   if (!l->emit_pie || l->c->target.obj != KIT_OBJ_ELF) return 0;
    506   nm = pool_slice(l->c->global, s->name);
    507   if (slice_eq_cstr(nm, "__tls_get_addr")) {
    508     if (l->c->target.arch != KIT_ARCH_X86_64 &&
    509         l->c->target.arch != KIT_ARCH_RV64 &&
    510         l->c->target.arch != KIT_ARCH_RV32)
    511       return 0;
    512     /* Some glibc dynamic TLS helpers are provided by the ELF interpreter
    513      * itself (ld-linux), not by a regular DT_NEEDED DSO. GNU ld permits this
    514      * unresolved-looking reference in dynamic executables; model it as a
    515      * function import with no provider DSO so the PLT/dynsym machinery can
    516      * emit any needed JUMP_SLOT without adding the interpreter as DT_NEEDED.
    517      * RISC-V TLS-GD references that relax fully to local-exec will skip their
    518      * call relocations later, leaving this dynsym entry harmlessly unused. */
    519     s->kind = SK_FUNC;
    520   } else if (slice_eq_cstr(nm, "__stack_chk_guard")) {
    521     /* glibc's AArch64 and RISC-V ABIs expose the process canary from
    522      * ld-linux, while libc only carries an undefined versioned reference to
    523      * it. Treat the guard as an interpreter-owned object import. This keeps
    524      * the loader out of DT_NEEDED (it is already PT_INTERP) and lets the
    525      * ordinary GOT/dynsym machinery bind the address at startup. x86-64 reads
    526      * its guard directly from fs:0x28 and never reaches this path. */
    527     if (l->c->target.os != KIT_OS_LINUX ||
    528         (l->c->target.arch != KIT_ARCH_ARM_64 &&
    529          l->c->target.arch != KIT_ARCH_RV64 &&
    530          l->c->target.arch != KIT_ARCH_RV32))
    531       return 0;
    532     s->kind = SK_OBJ;
    533   } else {
    534     return 0;
    535   }
    536   s->imported = 1;
    537   s->dso_input_id = LINK_INPUT_NONE;
    538   s->elf_version = 0;
    539   return 1;
    540 }
    541 
    542 void link_resolve_undefs(Linker* l, LinkImage* img) {
    543   u32 i;
    544 
    545   /* Cross-input COFF WEAK_EXTERNAL alias map: alias-declarator name -> target
    546    * name (SymHash's value slot holds an interned Sym, never a real LinkSymId
    547    * here; 0 = absent). Populated from every input's recorded aliases so a
    548    * reference to the aliased name resolves to the target regardless of which
    549    * input the reference vs. the declarator came from. Empty for non-COFF. */
    550   SymHash alias_map;
    551   symhash_init(&alias_map, l->heap);
    552   if (l->c->target.obj == KIT_OBJ_COFF) {
    553     for (u32 ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
    554       LinkInput* in = LinkInputs_at(&l->inputs, ii);
    555       if (!in->obj || in->kind == LINK_INPUT_DSO_BYTES) continue;
    556       u32 na = obj_weak_alias_count(in->obj);
    557       for (u32 ai = 0; ai < na; ++ai) {
    558         ObjSymId asym = OBJ_SYM_NONE;
    559         Sym target = 0;
    560         if (!obj_weak_alias_at(in->obj, ai, &asym, &target) || target == 0)
    561           continue;
    562         const ObjSym* os = obj_symbol_get(in->obj, asym);
    563         if (os && os->name != 0) symhash_set(&alias_map, os->name, target);
    564       }
    565     }
    566   }
    567   SymHash dso_exports;
    568   dso_exports_build(l, &dso_exports);
    569 
    570   for (i = 0; i < LinkSyms_count(&img->syms); ++i) {
    571     LinkSymbol* s = LinkSyms_at(&img->syms, i);
    572     if (s->defined) continue;
    573     if (s->name != 0) {
    574       LinkSymId hit = symhash_get(&img->globals, s->name);
    575       if (hit != LINK_SYM_NONE && hit != s->id) {
    576         LinkSymbol* def = LinkSyms_at(&img->syms, hit - 1);
    577         if (def->defined) {
    578           s->section_id = def->section_id;
    579           s->value = def->value;
    580           s->vaddr = def->vaddr;
    581           s->kind = def->kind;
    582           s->bind = def->bind;
    583           s->vis = def->vis;
    584           s->defined = 1;
    585           continue;
    586         }
    587       }
    588     }
    589     if (s->name != 0) {
    590       if (resolve_elf_versioned_dso_undef(l, s)) {
    591         continue;
    592       }
    593       LinkInputId dso = symhash_get(&dso_exports, s->name);
    594       if (dso != LINK_INPUT_NONE) {
    595         s->imported = 1;
    596         s->dso_input_id = dso;
    597         s->elf_version = 0;
    598         continue;
    599       }
    600       if (resolve_elf_loader_owned_undef(l, s)) continue;
    601     }
    602     if (l->resolver && s->name != 0) {
    603       Slice nm_s = pool_slice(l->c->global, s->name);
    604       void* p = l->resolver(l->resolver_user, nm_s);
    605       if (p) {
    606         s->kind = SK_ABS;
    607         s->vaddr = (u64)(uintptr_t)p;
    608         s->defined = 1;
    609         continue;
    610       }
    611     }
    612     /* COFF WEAK_EXTERNAL alias: resolve to the recorded fall-back symbol (the
    613      * aux TagIndex target captured by read_coff, collected into `alias_map`
    614      * keyed by the alias-declarator's name). This is the precise relationship —
    615      * e.g. mingw x86_64's `_setjmp` aliasing `__intrinsic_setjmp`, which the
    616      * single-underscore heuristic below cannot derive. It applies to ANY undef
    617      * of the aliased name, not just the declarator symbol itself: the strong
    618      * reference (sj.o's `_setjmp`) and the weak declarator are distinct undefs,
    619      * and the reference is what needs redirecting. The declarator member is
    620      * pulled by member_satisfies (weak undef under PE/COMDAT semantics), which
    621      * brings in the target's own undef and pulls its short-import DSO, so the
    622      * target is resolvable by now. Follow a short chain in case the target is
    623      * itself an alias. */
    624     if (s->name != 0) {
    625       int resolved = 0;
    626       Sym cur = s->name;
    627       for (u32 hop = 0; hop < 8u; ++hop) {
    628         Sym target = symhash_get(&alias_map, cur);
    629         if (target == 0) break;
    630         if (resolve_to_alias(img, &dso_exports, s, target)) {
    631           resolved = 1;
    632           break;
    633         }
    634         cur = target;
    635       }
    636       if (resolved) continue;
    637     }
    638     /* COFF WEAK_EXTERNAL alias fallback for references that carry no recorded
    639      * aux TagIndex (GLOBAL undefs like crt2.o's call to `__set_app_type`, or
    640      * inputs read before alias capture): recover the relationship via the mingw
    641      * single-underscore naming convention.  e.g. `__set_app_type` aliases to
    642      * `_set_app_type`; `__imp___set_app_type` aliases to `__imp__set_app_type`.
    643      * Try the de-underscored variant first, then the re-underscored one. */
    644     if (obj_format_weak_extern_underscore_alias(l->c) && s->name != 0) {
    645       Slice nm_s = pool_slice(l->c->global, s->name);
    646       const char* nm = nm_s.s;
    647       size_t nlen = nm_s.len;
    648       Sym candidates[2] = {0, 0};
    649       u32 ncand = 0;
    650       if (nm && nlen >= 2 && nm[0] == '_') {
    651         candidates[ncand++] = pool_intern_slice(
    652             l->c->global, (Slice){.s = nm + 1, .len = (u32)(nlen - 1u)});
    653       }
    654       if (nm && nlen > 0) {
    655         char* buf = (char*)arena_array(l->c->scratch, char, nlen + 1u);
    656         buf[0] = '_';
    657         memcpy(buf + 1, nm, nlen);
    658         candidates[ncand++] = pool_intern_slice(
    659             l->c->global, (Slice){.s = buf, .len = (u32)(nlen + 1u)});
    660       }
    661       int resolved = 0;
    662       for (u32 ci = 0; !resolved && ci < ncand; ++ci) {
    663         if (resolve_to_alias(img, &dso_exports, s, candidates[ci]))
    664           resolved = 1;
    665       }
    666       if (resolved) continue;
    667     }
    668     if (s->bind == SB_WEAK) {
    669       s->kind = SK_ABS;
    670       s->vaddr = 0;
    671       s->defined = 1;
    672       continue;
    673     }
    674     /* JIT lane: format pseudo-symbols the JIT image satisfies internally.
    675      * Mach-O inputs (including clang-produced .o files) carry a non-weak undef
    676      * `__tlv_bootstrap` on every TLV var; kit_jit_from_image rewrites every
    677      * descriptor's slot[0] to our thunk, so the resolved value never gets read.
    678      * Windows COFF inputs carry `_tls_index` (normally supplied by the OS
    679      * loader via the TLS directory); the JIT relaxes every TLS access to
    680      * in-image addressing, so it is never read. Treat such a symbol as
    681      * weak-undef (vaddr = 0, SK_ABS) in JIT mode only; AOT lanes keep the
    682      * strict "undefined external" semantics. The obj layer is the single
    683      * arbiter (shared with link_jit.c), so src/link names no pseudo-symbol. */
    684     if (l->jit_mode && obj_format_jit_undef_internal(l->c, s->name)) {
    685       s->kind = SK_ABS;
    686       s->vaddr = 0;
    687       s->defined = 1;
    688       continue;
    689     }
    690     if (l->allow_undefined) {
    691       s->kind = SK_ABS;
    692       s->vaddr = 0;
    693       s->defined = 1;
    694       continue;
    695     }
    696     if (l->emit_shared && s->name != 0 && s->bind != SB_LOCAL) {
    697       s->imported = 1;
    698       s->dso_input_id = LINK_INPUT_NONE;
    699       s->elf_version = 0;
    700       continue;
    701     }
    702     /* --defsym NAME=EXPR: a defsym whose NAME matches this undef satisfies the
    703      * reference. Defsyms are applied AFTER undef resolution (link_apply_defsyms
    704      * runs in link_resolve, once the alias target's vaddr is settled), so we
    705      * cannot define it here; instead, skip the panic and leave the record
    706      * undefined-for-now. link_apply_defsyms then UPDATES this same record in
    707      * place (its `existing != LINK_SYM_NONE` branch) — leaving s->defined=0
    708      * avoids the duplicate-symbol artifact a synthetic definition would add. */
    709     if (s->name != 0 && l->ndefsyms) {
    710       int is_defsym = 0;
    711       u32 k;
    712       for (k = 0; k < l->ndefsyms; ++k) {
    713         const KitLinkDefsym* d = &l->defsyms[k];
    714         Sym dn;
    715         if (!d->name.s || d->name.len == 0) continue;
    716         dn = pool_intern_slice(l->c->global,
    717                                (Slice){.s = d->name.s, .len = d->name.len});
    718         if (dn == s->name) {
    719           is_defsym = 1;
    720           break;
    721         }
    722       }
    723       if (is_defsym) continue;
    724     }
    725     {
    726       Slice nm_s = s->name ? pool_slice(l->c->global, s->name) : SLICE_NULL;
    727       const char* nm = nm_s.s ? nm_s.s : "";
    728       size_t namelen = nm_s.len;
    729       obj_format_demangle_c(l->c, &nm, &namelen);
    730       compiler_panic(l->c, SRCLOC_NONE, "link: undefined reference to '%.*s'",
    731                      (int)namelen, nm);
    732     }
    733   }
    734   symhash_fini(&dso_exports);
    735   symhash_fini(&alias_map);
    736 }
    737 
    738 /* ---- pass 1b: --gc-sections liveness ---- */
    739 
    740 #define GC_ATOM_BIT 0x80000000u
    741 #define GC_PACK(ii, j, is_atom) \
    742   (((u64)(u32)(ii) << 32) | ((is_atom) ? GC_ATOM_BIT : 0u) | (u32)(j))
    743 #define GC_II(p) ((u32)((p) >> 32))
    744 #define GC_IS_ATOM(p) (((u32)(p) & GC_ATOM_BIT) != 0)
    745 #define GC_J(p) ((u32)((p) & ~GC_ATOM_BIT))
    746 
    747 static void gc_queue_push(GcQueue* q, Heap* h, u32 ii, u32 j, int is_atom) {
    748   if (VEC_GROW(h, q->items, q->cap, q->n + 1u)) return;
    749   q->items[q->n++] = GC_PACK(ii, j, is_atom);
    750 }
    751 
    752 void link_gc_live_alloc(GcLive* g, Linker* l, Heap* h) {
    753   u32 ii;
    754   g->ninputs = LinkInputs_count(&l->inputs);
    755   g->marks =
    756       LinkInputs_count(&l->inputs)
    757           ? (u8**)h->alloc(h, sizeof(*g->marks) * LinkInputs_count(&l->inputs),
    758                            _Alignof(u8*))
    759           : NULL;
    760   g->atom_marks =
    761       LinkInputs_count(&l->inputs)
    762           ? (u8**)h->alloc(
    763                 h, sizeof(*g->atom_marks) * LinkInputs_count(&l->inputs),
    764                 _Alignof(u8*))
    765           : NULL;
    766   g->nsec =
    767       LinkInputs_count(&l->inputs)
    768           ? (u32*)h->alloc(h, sizeof(*g->nsec) * LinkInputs_count(&l->inputs),
    769                            _Alignof(u32))
    770           : NULL;
    771   g->natom =
    772       LinkInputs_count(&l->inputs)
    773           ? (u32*)h->alloc(h, sizeof(*g->natom) * LinkInputs_count(&l->inputs),
    774                            _Alignof(u32))
    775           : NULL;
    776   if (LinkInputs_count(&l->inputs) &&
    777       (!g->marks || !g->atom_marks || !g->nsec || !g->natom))
    778     compiler_panic(l->c, SRCLOC_NONE, "link: oom on gc live map");
    779   for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
    780     u32 nsec = obj_section_count(LinkInputs_at(&l->inputs, ii)->obj);
    781     u32 natom = obj_atom_count(LinkInputs_at(&l->inputs, ii)->obj);
    782     g->nsec[ii] = nsec;
    783     g->natom[ii] = natom;
    784     g->marks[ii] = (u8*)h->alloc(h, nsec ? nsec : 1u, 1);
    785     if (!g->marks[ii])
    786       compiler_panic(l->c, SRCLOC_NONE, "link: oom on gc marks");
    787     memset(g->marks[ii], 0, nsec);
    788     g->atom_marks[ii] = (u8*)h->alloc(h, natom ? natom : 1u, 1);
    789     if (!g->atom_marks[ii])
    790       compiler_panic(l->c, SRCLOC_NONE, "link: oom on gc atom marks");
    791     memset(g->atom_marks[ii], 0, natom);
    792   }
    793 }
    794 
    795 void link_gc_live_free(GcLive* g, Heap* h) {
    796   u32 ii;
    797   if (g->marks) {
    798     for (ii = 0; ii < g->ninputs; ++ii)
    799       if (g->marks[ii])
    800         h->free(h, g->marks[ii], g->nsec[ii] ? g->nsec[ii] : 1u);
    801     h->free(h, g->marks, sizeof(*g->marks) * g->ninputs);
    802   }
    803   if (g->atom_marks) {
    804     for (ii = 0; ii < g->ninputs; ++ii)
    805       if (g->atom_marks[ii])
    806         h->free(h, g->atom_marks[ii], g->natom[ii] ? g->natom[ii] : 1u);
    807     h->free(h, g->atom_marks, sizeof(*g->atom_marks) * g->ninputs);
    808   }
    809   if (g->nsec) h->free(h, g->nsec, sizeof(*g->nsec) * g->ninputs);
    810   if (g->natom) h->free(h, g->natom, sizeof(*g->natom) * g->ninputs);
    811 }
    812 
    813 int link_gc_live_get(const GcLive* g, u32 ii, ObjSecId j) {
    814   if (ii >= g->ninputs || j == OBJ_SEC_NONE || j >= g->nsec[ii]) return 0;
    815   return g->marks[ii][j];
    816 }
    817 
    818 int link_gc_atom_live_get(const GcLive* g, u32 ii, ObjAtomId j) {
    819   if (ii >= g->ninputs || j == OBJ_ATOM_NONE || j >= g->natom[ii]) return 0;
    820   return g->atom_marks[ii][j];
    821 }
    822 
    823 static void gc_mark(GcLive* g, GcQueue* q, Heap* h, u32 ii, ObjSecId j) {
    824   if (ii >= g->ninputs || j == OBJ_SEC_NONE || j >= g->nsec[ii]) return;
    825   if (g->marks[ii][j]) return;
    826   g->marks[ii][j] = 1;
    827   if (q) gc_queue_push(q, h, ii, j, 0);
    828 }
    829 
    830 static void gc_mark_atom(GcLive* g, GcQueue* q, Heap* h, u32 ii, ObjAtomId j) {
    831   if (ii >= g->ninputs || j == OBJ_ATOM_NONE || j >= g->natom[ii]) return;
    832   if (g->atom_marks[ii][j]) return;
    833   g->atom_marks[ii][j] = 1;
    834   if (q) gc_queue_push(q, h, ii, j, 1);
    835 }
    836 
    837 static void gc_mark_section_or_atoms(GcLive* g, GcQueue* q, Heap* h,
    838                                      ObjBuilder* ob, const InputMap* m, u32 ii,
    839                                      ObjSecId sid) {
    840   u32 first, count, i;
    841   int marked = 0;
    842   if (!link_input_section_has_atoms(m, sid)) {
    843     gc_mark(g, q, h, ii, sid);
    844     return;
    845   }
    846   link_input_section_atoms(m, sid, &first, &count);
    847   for (i = 0; i < count; ++i) {
    848     ObjAtomId aid = m->section_atom_ids[first + i];
    849     const ObjAtom* a = obj_atom_get(ob, aid);
    850     if (!a || a->removed) continue;
    851     gc_mark_atom(g, q, h, ii, aid);
    852     marked = 1;
    853   }
    854   if (!marked) gc_mark(g, q, h, ii, sid);
    855 }
    856 
    857 /* From a LinkSymId, find the (input_idx, obj_sec_id) of its defining section.
    858  * Returns 1 on hit. */
    859 static int gc_def_site(LinkImage* img, Linker* l, LinkSymId id, u32* out_ii,
    860                        ObjSecId* out_sid, ObjAtomId* out_aid) {
    861   const LinkSymbol* s;
    862   ObjBuilder* ob;
    863   const ObjSym* osym;
    864   if (id == LINK_SYM_NONE || id > LinkSyms_count(&img->syms)) return 0;
    865   s = LinkSyms_at(&img->syms, id - 1);
    866   if (!s->defined) {
    867     LinkSymId hit;
    868     if (s->name == 0) return 0;
    869     hit = symhash_get(&img->globals, s->name);
    870     if (hit == LINK_SYM_NONE || hit == s->id) return 0;
    871     return gc_def_site(img, l, hit, out_ii, out_sid, out_aid);
    872   }
    873   if (s->kind == SK_ABS || s->kind == SK_COMMON) return 0;
    874   if (s->input_id == LINK_INPUT_NONE) return 0;
    875   ob = LinkInputs_at(&l->inputs, s->input_id - 1)->obj;
    876   osym = obj_symbol_get(ob, s->obj_sym);
    877   if (!osym || osym->section_id == OBJ_SEC_NONE) return 0;
    878   *out_ii = (u32)(s->input_id - 1u);
    879   *out_sid = osym->section_id;
    880   *out_aid = link_input_sym_atom(&img->input_maps[*out_ii], s->obj_sym);
    881   return 1;
    882 }
    883 
    884 /* Detect __start_<X> / __stop_<X> with <X> a valid C identifier. */
    885 int link_gc_split_start_stop(const char* s, size_t n, size_t* out_off,
    886                              size_t* out_len, int* out_is_start) {
    887   static const char START[] = "__start_";
    888   static const char STOP[] = "__stop_";
    889   size_t off, len, i;
    890   int is_start;
    891   if (n > sizeof(START) - 1u && memcmp(s, START, sizeof(START) - 1u) == 0) {
    892     off = sizeof(START) - 1u;
    893     is_start = 1;
    894   } else if (n > sizeof(STOP) - 1u && memcmp(s, STOP, sizeof(STOP) - 1u) == 0) {
    895     off = sizeof(STOP) - 1u;
    896     is_start = 0;
    897   } else {
    898     return 0;
    899   }
    900   len = n - off;
    901   if (len == 0) return 0;
    902   {
    903     char c = s[off];
    904     if (!(c == '_' || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')))
    905       return 0;
    906   }
    907   for (i = 1; i < len; ++i) {
    908     char c = s[off + i];
    909     if (!(c == '_' || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
    910           (c >= '0' && c <= '9')))
    911       return 0;
    912   }
    913   *out_off = off;
    914   *out_len = len;
    915   if (out_is_start) *out_is_start = is_start;
    916   return 1;
    917 }
    918 
    919 static void gc_promote_by_section_name(Linker* l, LinkImage* img, GcLive* g,
    920                                        GcQueue* q, Heap* h, Sym section_name) {
    921   u32 ii, j;
    922   for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
    923     ObjBuilder* ob = LinkInputs_at(&l->inputs, ii)->obj;
    924     u32 nsec = obj_section_count(ob);
    925     for (j = 1; j < nsec; ++j) {
    926       const Section* s = obj_section_get(ob, j);
    927       if (!s || !link_section_kept(s)) continue;
    928       if (s->name != section_name) continue;
    929       gc_mark_section_or_atoms(g, q, h, ob, &img->input_maps[ii], ii, j);
    930     }
    931   }
    932 }
    933 
    934 static int gc_match_glob(const char* pat, const char* name) {
    935   size_t plen, nlen;
    936   if (!pat || !name) return 0;
    937   plen = slice_from_cstr(pat).len;
    938   nlen = slice_from_cstr(name).len;
    939   if (plen == 1 && pat[0] == '*') return 1;
    940   if (plen >= 2 && pat[plen - 1] == '*') {
    941     if (nlen + 1 < plen) return 0;
    942     return memcmp(pat, name, plen - 1) == 0;
    943   }
    944   if (plen >= 2 && pat[0] == '*') {
    945     if (nlen + 1 < plen) return 0;
    946     return memcmp(pat + 1, name + (nlen - (plen - 1)), plen - 1) == 0;
    947   }
    948   return plen == nlen && memcmp(pat, name, plen) == 0;
    949 }
    950 
    951 static const char* gc_basename(const char* s) {
    952   const char* base = s;
    953   if (!s) return NULL;
    954   for (; *s; ++s)
    955     if (*s == '/' || *s == '\\') base = s + 1;
    956   return base;
    957 }
    958 
    959 static int gc_match_input_name(Linker* l, u32 ii,
    960                                const KitLinkInputMatch* m) {
    961   const LinkInput* in;
    962   Slice nm = SLICE_NULL;
    963   const char* full;
    964   const char* base;
    965   u32 i;
    966   if (!m->file_pattern.s && m->nexclude_file_patterns == 0) return 1;
    967   in = LinkInputs_at(&l->inputs, ii);
    968   if (in->name) nm = pool_slice(l->c->global, in->name);
    969   full = nm.s;
    970   base = gc_basename(full);
    971   if (m->file_pattern.s) {
    972     if (!full) return 0;
    973     if (!gc_match_glob(m->file_pattern.s, full) &&
    974         !gc_match_glob(m->file_pattern.s, base))
    975       return 0;
    976   }
    977   for (i = 0; i < m->nexclude_file_patterns; ++i) {
    978     const char* pat = m->exclude_file_patterns[i].s;
    979     if (!pat || !full) continue;
    980     if (gc_match_glob(pat, full) || gc_match_glob(pat, base)) return 0;
    981   }
    982   return 1;
    983 }
    984 
    985 static void gc_mark_script_keep_roots(Linker* l, LinkImage* img, GcLive* g,
    986                                       GcQueue* q, Heap* h) {
    987   u32 si, mi, ii, j;
    988   if (!l->script) return;
    989   for (si = 0; si < l->script->nsections; ++si) {
    990     const KitLinkOutputSection* os = &l->script->sections[si];
    991     for (mi = 0; mi < os->ninputs; ++mi) {
    992       const KitLinkInputMatch* im = &os->inputs[mi];
    993       if (!im->keep) continue;
    994       for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
    995         ObjBuilder* ob = LinkInputs_at(&l->inputs, ii)->obj;
    996         InputMap* m = &img->input_maps[ii];
    997         if (!gc_match_input_name(l, ii, im)) continue;
    998         for (j = 1; j < obj_section_count(ob); ++j) {
    999           const Section* s = obj_section_get(ob, j);
   1000           Slice sn;
   1001           if (!s || !link_section_kept(s)) continue;
   1002           if (m->comdat_discarded[j]) continue;
   1003           sn = pool_slice(l->c->global, s->name);
   1004           if (!gc_match_glob(im->section_pattern.s, sn.s)) continue;
   1005           gc_mark_section_or_atoms(g, q, h, ob, m, ii, j);
   1006         }
   1007       }
   1008     }
   1009   }
   1010 }
   1011 
   1012 static void gc_mark_script_extern_roots(Linker* l, LinkImage* img, GcLive* g,
   1013                                         GcQueue* q, Heap* h) {
   1014   u32 i;
   1015   if (!l->script) return;
   1016   for (i = 0; i < l->script->nexterns; ++i) {
   1017     Sym name = pool_intern_slice(l->c->global, l->script->externs[i]);
   1018     LinkSymId id = symhash_get(&img->globals, name);
   1019     u32 tii;
   1020     ObjSecId tsid;
   1021     ObjAtomId taid;
   1022     if (gc_def_site(img, l, id, &tii, &tsid, &taid)) {
   1023       if (taid != OBJ_ATOM_NONE)
   1024         gc_mark_atom(g, q, h, tii, taid);
   1025       else
   1026         gc_mark(g, q, h, tii, tsid);
   1027     }
   1028   }
   1029 }
   1030 
   1031 void link_gc_compute(Linker* l, LinkImage* img, GcLive* g) {
   1032   u32 ii, j, k;
   1033   GcQueue q;
   1034   Heap* h = img->heap;
   1035 
   1036   if (!l->gc_sections) {
   1037     for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
   1038       ObjBuilder* ob = LinkInputs_at(&l->inputs, ii)->obj;
   1039       InputMap* m = &img->input_maps[ii];
   1040       u32 nsec = obj_section_count(ob);
   1041       for (j = 1; j < nsec; ++j) {
   1042         const Section* s = obj_section_get(ob, j);
   1043         if (s && link_section_kept(s) && !m->comdat_discarded[j])
   1044           gc_mark_section_or_atoms(g, NULL, h, ob, m, ii, j);
   1045       }
   1046     }
   1047     return;
   1048   }
   1049 
   1050   memset(&q, 0, sizeof(q));
   1051 
   1052   for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
   1053     ObjBuilder* ob = LinkInputs_at(&l->inputs, ii)->obj;
   1054     InputMap* m = &img->input_maps[ii];
   1055     u32 nsec = obj_section_count(ob);
   1056     for (j = 1; j < nsec; ++j) {
   1057       const Section* s = obj_section_get(ob, j);
   1058       int root;
   1059       if (!s || !link_section_kept(s)) continue;
   1060       if (m->comdat_discarded[j]) continue;
   1061       root = (s->flags & SF_RETAIN) || s->sem == SSEM_INIT_ARRAY ||
   1062              s->sem == SSEM_FINI_ARRAY || s->sem == SSEM_PREINIT_ARRAY;
   1063       if (root) gc_mark_section_or_atoms(g, &q, h, ob, m, ii, j);
   1064       if (link_input_section_has_atoms(m, j)) {
   1065         u32 first, count, ai;
   1066         link_input_section_atoms(m, j, &first, &count);
   1067         for (ai = 0; ai < count; ++ai) {
   1068           ObjAtomId aid = m->section_atom_ids[first + ai];
   1069           const ObjAtom* a = obj_atom_get(ob, aid);
   1070           if (!a || a->removed) continue;
   1071           if (a->flags & OBJ_ATOM_RETAIN) gc_mark_atom(g, &q, h, ii, aid);
   1072         }
   1073       }
   1074     }
   1075   }
   1076 
   1077   if (l->entry_name != 0) {
   1078     LinkSymId id = symhash_get(&img->globals, l->entry_name);
   1079     u32 tii;
   1080     ObjSecId tsid;
   1081     ObjAtomId taid;
   1082     if (gc_def_site(img, l, id, &tii, &tsid, &taid)) {
   1083       if (taid != OBJ_ATOM_NONE)
   1084         gc_mark_atom(g, &q, h, tii, taid);
   1085       else
   1086         gc_mark(g, &q, h, tii, tsid);
   1087     }
   1088   }
   1089 
   1090   gc_mark_script_extern_roots(l, img, g, &q, h);
   1091   gc_mark_script_keep_roots(l, img, g, &q, h);
   1092 
   1093   /* Keep executable definitions that a linked shared library references but
   1094    * nothing in the executable does (e.g. FreeBSD libc.so.7's back-references
   1095    * to crt-defined `environ` / `__progname`). Without rooting these, GC drops
   1096    * the defining section and the resulting dynamic exe fails to load
   1097    * ("Undefined symbol"). read_elf_dso records each DSO's undef names. */
   1098   for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
   1099     LinkInput* in = LinkInputs_at(&l->inputs, ii);
   1100     const ObjImage* dim;
   1101     u32 u, nu;
   1102     if (in->kind != LINK_INPUT_DSO_BYTES || !in->obj) continue;
   1103     dim = obj_image(in->obj);
   1104     nu = obj_image_nundefs(dim);
   1105     for (u = 0; u < nu; ++u) {
   1106       LinkSymId id = symhash_get(&img->globals, obj_image_undef(dim, u));
   1107       u32 tii;
   1108       ObjSecId tsid;
   1109       ObjAtomId taid;
   1110       if (id == LINK_SYM_NONE) continue;
   1111       if (gc_def_site(img, l, id, &tii, &tsid, &taid)) {
   1112         if (taid != OBJ_ATOM_NONE)
   1113           gc_mark_atom(g, &q, h, tii, taid);
   1114         else
   1115           gc_mark(g, &q, h, tii, tsid);
   1116       }
   1117     }
   1118   }
   1119 
   1120   while (q.n > 0) {
   1121     u64 v = q.items[--q.n];
   1122     u32 cii = GC_II(v);
   1123     int c_is_atom = GC_IS_ATOM(v);
   1124     ObjSecId cj = (ObjSecId)GC_J(v);
   1125     ObjBuilder* ob = LinkInputs_at(&l->inputs, cii)->obj;
   1126     InputMap* m = &img->input_maps[cii];
   1127     const ObjAtom* src_atom =
   1128         c_is_atom ? obj_atom_get(ob, (ObjAtomId)cj) : NULL;
   1129     ObjSecId src_sec = src_atom ? src_atom->section_id : cj;
   1130     u32 total = obj_reloc_total(ob);
   1131     (void)obj_section_count;
   1132     if (!total) continue;
   1133     for (k = 0; k < total; ++k) {
   1134       const Reloc* r = obj_reloc_at(ob, k);
   1135       LinkSymId target;
   1136       const LinkSymbol* tsym;
   1137       u32 tii;
   1138       ObjSecId tsid;
   1139       ObjAtomId taid;
   1140       if (r->section_id != src_sec) continue;
   1141       if (src_atom) {
   1142         u64 begin = src_atom->offset;
   1143         u64 end = begin + src_atom->size;
   1144         if ((u64)r->offset < begin || (u64)r->offset >= end) continue;
   1145       }
   1146       if (r->sym == OBJ_SYM_NONE || r->sym >= m->nsym) continue;
   1147       target = m->sym[r->sym];
   1148       if (target == LINK_SYM_NONE) continue;
   1149       tsym = LinkSyms_at(&img->syms, target - 1);
   1150 
   1151       if (tsym->name != 0) {
   1152         size_t off, ilen;
   1153         Slice nm_s = pool_slice(l->c->global, tsym->name);
   1154         const char* nm = nm_s.s;
   1155         size_t namelen = nm_s.len;
   1156         if (link_gc_split_start_stop(nm, namelen, &off, &ilen, NULL)) {
   1157           Sym secname = pool_intern_slice(l->c->global,
   1158                                           (Slice){.s = nm + off, .len = ilen});
   1159           gc_promote_by_section_name(l, img, g, &q, h, secname);
   1160         }
   1161       }
   1162 
   1163       if (gc_def_site(img, l, target, &tii, &tsid, &taid)) {
   1164         if (taid != OBJ_ATOM_NONE)
   1165           gc_mark_atom(g, &q, h, tii, taid);
   1166         else
   1167           gc_mark(g, &q, h, tii, tsid);
   1168       }
   1169     }
   1170   }
   1171 
   1172   if (q.items) h->free(h, q.items, sizeof(*q.items) * q.cap);
   1173 }
   1174 
   1175 void link_gc_drop_dead_globals(Linker* l, LinkImage* img, const GcLive* g) {
   1176   u32 i;
   1177   if (!l->gc_sections) return;
   1178   for (i = 0; i < LinkSyms_count(&img->syms); ++i) {
   1179     LinkSymbol* s = LinkSyms_at(&img->syms, i);
   1180     ObjBuilder* ob;
   1181     const ObjSym* osym;
   1182     ObjSecId osid;
   1183     ObjAtomId aid;
   1184     if (!s->defined) continue;
   1185     if (s->kind == SK_ABS || s->kind == SK_COMMON) continue;
   1186     if (s->input_id == LINK_INPUT_NONE) continue;
   1187     ob = LinkInputs_at(&l->inputs, s->input_id - 1)->obj;
   1188     osym = obj_symbol_get(ob, s->obj_sym);
   1189     if (!osym) continue;
   1190     osid = osym->section_id;
   1191     if (osid == OBJ_SEC_NONE) continue;
   1192     aid = link_input_sym_atom(&img->input_maps[s->input_id - 1u], s->obj_sym);
   1193     if (aid != OBJ_ATOM_NONE) {
   1194       if (link_gc_atom_live_get(g, (u32)(s->input_id - 1u), aid)) continue;
   1195       s->defined = 0;
   1196       s->vaddr = 0;
   1197       s->section_id = LINK_SEC_NONE;
   1198       continue;
   1199     }
   1200     if (link_gc_live_get(g, (u32)(s->input_id - 1u), osid)) continue;
   1201     s->defined = 0;
   1202     s->vaddr = 0;
   1203     s->section_id = LINK_SEC_NONE;
   1204   }
   1205 }
   1206 
   1207 /* ---- archive ingestion ---- */
   1208 
   1209 static void include_archive_member(Linker* l, const LinkArchive* ar,
   1210                                    LinkArchiveMember* mem) {
   1211   LinkInput* in;
   1212   LinkInputId id;
   1213   u32 idx;
   1214   Sym coff_dll = 0;
   1215   if (mem->included) return;
   1216   if (mem->obj)
   1217     link_merge_elf_e_flags(
   1218         l, mem->obj,
   1219         mem->name ? pool_slice(l->c->global, mem->name)
   1220                   : SLICE_LIT("<unnamed archive member>"));
   1221   in = LinkInputs_push(&l->inputs, &idx);
   1222   if (!in)
   1223     compiler_panic(l->c, SRCLOC_NONE,
   1224                    "link: oom growing inputs (archive member)");
   1225   id = (LinkInputId)(idx + 1u);
   1226   in->id = id;
   1227   /* PE/COFF short-import shim: read_coff_short_import stashes the
   1228    * providing DLL name on the ObjBuilder. Such members behave like
   1229    * DSO inputs — symbols are exports, not local definitions — so route
   1230    * through LINK_INPUT_DSO_BYTES with the DLL name as the soname. */
   1231   if (mem->obj && obj_get_coff_import_dll(mem->obj, &coff_dll) && coff_dll) {
   1232     in->kind = LINK_INPUT_DSO_BYTES;
   1233     in->soname = coff_dll;
   1234     /* Short-import NameType may make the DLL export name differ from the
   1235      * local symbol name (EXPORTAS etc.); carry it for import-table synthesis.
   1236      */
   1237     {
   1238       Sym coff_imp_name = 0;
   1239       if (obj_get_coff_import_name(mem->obj, &coff_imp_name))
   1240         in->coff_import_name = coff_imp_name;
   1241     }
   1242   } else {
   1243     in->kind = LINK_INPUT_OBJ_BYTES;
   1244   }
   1245   in->order = ar->order;
   1246   in->obj = mem->obj;
   1247   in->name = mem->name;
   1248   mem->included = 1;
   1249   mem->obj = NULL;
   1250 }
   1251 
   1252 static void scan_presence_before(Linker* l, u32 max_order, SymHash* defined,
   1253                                  SymHash* undefs) {
   1254   u32 ii;
   1255   ObjSymIter* it;
   1256   ObjSymEntry e;
   1257   for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
   1258     LinkInput* in = LinkInputs_at(&l->inputs, ii);
   1259     ObjBuilder* ob = in->obj;
   1260     int is_dso = (in->kind == LINK_INPUT_DSO_BYTES);
   1261     if (!ob || in->order > max_order) continue;
   1262     it = obj_symiter_new(ob);
   1263     while (obj_symiter_next(it, &e)) {
   1264       const ObjSym* s = e.sym;
   1265       if (s->name == 0) continue;
   1266       if (s->bind == SB_LOCAL) continue;
   1267       if (is_dso) {
   1268         /* A DSO's exported symbols satisfy undefined references, so a later
   1269          * static-archive member must NOT be pulled to redefine them. kit
   1270          * records DSO exports as OBJ_SEC_NONE globals (the importer's view),
   1271          * which obj_sym_is_logical_undef would otherwise misclassify as
   1272          * undefined — leaving e.g. a real libc's atoi looking unsatisfied and
   1273          * letting the freestanding rt's atoi shadow it. The DSO's own undefs
   1274          * (SK_UNDEF) are not exports and stay out of `defined`. */
   1275         if (s->kind != SK_UNDEF) symhash_set(defined, s->name, 1u);
   1276         continue;
   1277       }
   1278       /* An unreferenced global/weak extern declaration is a header
   1279        * artifact, not a real demand to pull from an archive.  Without
   1280        * this prune the C frontend's per-extern undef synthesis (e.g.
   1281        * every prototype in <math.h>) drags in matching archive members
   1282        * even when the user's source never references them. Matches the
   1283        * spurious-UNDEF prune in link_resolve_symbols and obj_sweep_dead
   1284        * at .o emit (obj.c). */
   1285       if (link_sym_is_spurious_undef(s)) continue;
   1286       if (obj_sym_is_logical_undef(s))
   1287         symhash_set(undefs, s->name, 1u);
   1288       else
   1289         symhash_set(defined, s->name, 1u);
   1290     }
   1291     obj_symiter_free(it);
   1292   }
   1293 }
   1294 
   1295 static int inputs_have_defined_ifunc_before(Linker* l, u32 max_order) {
   1296   u32 ii;
   1297   ObjSymIter* it;
   1298   ObjSymEntry e;
   1299   for (ii = 0; ii < LinkInputs_count(&l->inputs); ++ii) {
   1300     LinkInput* in = LinkInputs_at(&l->inputs, ii);
   1301     ObjBuilder* ob = in->obj;
   1302     if (!ob || in->order > max_order) continue;
   1303     it = obj_symiter_new(ob);
   1304     while (obj_symiter_next(it, &e)) {
   1305       const ObjSym* s = e.sym;
   1306       if (s->kind == SK_IFUNC) {
   1307         obj_symiter_free(it);
   1308         return 1;
   1309       }
   1310     }
   1311     obj_symiter_free(it);
   1312   }
   1313   return 0;
   1314 }
   1315 
   1316 static int member_satisfies(LinkArchiveMember* mem, const SymHash* defined,
   1317                             const SymHash* wanted, int weak_undef_pulls) {
   1318   ObjSymIter* it;
   1319   ObjSymEntry e;
   1320   int hit = 0;
   1321   it = obj_symiter_new(mem->obj);
   1322   while (obj_symiter_next(it, &e)) {
   1323     const ObjSym* s = e.sym;
   1324     if (s->name == 0) continue;
   1325     /* In COFF archives, WEAK_EXTERNAL alias declarations are read as
   1326      * SB_WEAK + SK_UNDEF (kit has no native alias model — see
   1327      * coff_read.c step "WEAK_EXTERNAL primary").  The archive's symbol
   1328      * map still lists the member as the canonical provider of that
   1329      * name, so treat such weak undefs as defining for the archive-pull
   1330      * decision (formats whose COMDAT semantics pull on a weak undef —
   1331      * obj_format_weak_undef_pulls_archive_member). The actual
   1332      * alias-to-target resolution happens later in link_resolve_undefs. */
   1333     if (s->kind == SK_UNDEF) {
   1334       if (!(weak_undef_pulls && s->bind == SB_WEAK)) continue;
   1335     }
   1336     if (s->bind != SB_GLOBAL && s->bind != SB_WEAK) continue;
   1337     if (symhash_get(wanted, s->name) == LINK_SYM_NONE) continue;
   1338     if (symhash_get(defined, s->name) != LINK_SYM_NONE) continue;
   1339     hit = 1;
   1340     break;
   1341   }
   1342   obj_symiter_free(it);
   1343   return hit;
   1344 }
   1345 
   1346 static int scan_archive_once(Linker* l, LinkArchive* ar, u32 max_order,
   1347                              Sym want_ifunc_init, int weak_undef_pulls) {
   1348   SymHash defined, undefs;
   1349   int changed = 0;
   1350   u32 m;
   1351   symhash_init(&defined, l->heap);
   1352   symhash_init(&undefs, l->heap);
   1353   scan_presence_before(l, max_order, &defined, &undefs);
   1354   if (want_ifunc_init != 0 &&
   1355       symhash_get(&defined, want_ifunc_init) == LINK_SYM_NONE)
   1356     symhash_set(&undefs, want_ifunc_init, 1u);
   1357   if (l->script && l->script->externs) {
   1358     u32 ei;
   1359     for (ei = 0; ei < l->script->nexterns; ++ei) {
   1360       Sym ex = pool_intern_slice(l->c->global, l->script->externs[ei]);
   1361       if (symhash_get(&defined, ex) == LINK_SYM_NONE)
   1362         symhash_set(&undefs, ex, 1u);
   1363     }
   1364   }
   1365 
   1366   for (m = 0; m < ar->nmembers; ++m) {
   1367     LinkArchiveMember* mem = &ar->members[m];
   1368     if (mem->included) continue;
   1369     if (!mem->obj) continue; /* long-form skip (head/trailer) */
   1370     if (!member_satisfies(mem, &defined, &undefs, weak_undef_pulls)) continue;
   1371     include_archive_member(l, ar, mem);
   1372     changed = 1;
   1373   }
   1374   symhash_fini(&defined);
   1375   symhash_fini(&undefs);
   1376   return changed;
   1377 }
   1378 
   1379 /* Synthesize an ObjBuilder providing the mingw CRT ctor/dtor list
   1380  * boundary symbols (`__CTOR_LIST__`, `__CTOR_END__`, `__DTOR_LIST__`,
   1381  * `__DTOR_END__`) backed by a 16-byte zero blob.  mingw's gccmain.o
   1382  * references these and walks them at program startup; lld/binutils
   1383  * generate them via the linker script's `.ctors` / `.dtors` rules.
   1384  * kit has no script for PE, so we inject an equivalent here.
   1385  *
   1386  * Zero contents are intentional for the empty-list case:
   1387  *   - __do_global_ctors loads `*(u32*)__CTOR_LIST__`; sees 0; cbz
   1388  *     short-circuit returns without iterating.
   1389  *   - __do_global_dtors loads `*(u64*)__DTOR_LIST__`; sees 0; cbz
   1390  *     short-circuit returns.
   1391  *
   1392  * For programs that emit real ctor/dtor sections this synth would
   1393  * need to coordinate with .ctors/.dtors layout; v1 covers the empty
   1394  * case (hello-world through mingw CRT). */
   1395 /* Registered as the COFF format's synth_inputs hook (src/obj/registry.c), so
   1396  * it is only ever invoked for COFF targets — no obj==COFF guard needed. */
   1397 void link_synth_coff_ctor_dtor_list(Linker* l) {
   1398   ObjBuilder* ob;
   1399   ObjSecId sid;
   1400   static const u8 kZeros[16] = {0};
   1401   LinkInput* in;
   1402   u32 idx;
   1403   if (!l) return;
   1404   ob = obj_new(l->c);
   1405   if (!ob) return;
   1406   sid = obj_section_ex(
   1407       ob, pool_intern_slice(l->c->global, SLICE_LIT(".rdata$ctors")),
   1408       SEC_RODATA, SSEM_PROGBITS, SF_ALLOC | SF_RETAIN, 16, 0u, 0u, 0u);
   1409   obj_section_replace_bytes(ob, sid, kZeros, sizeof(kZeros));
   1410   obj_symbol_ex(ob, pool_intern_slice(l->c->global, SLICE_LIT("__CTOR_LIST__")),
   1411                 SB_GLOBAL, SV_DEFAULT, SK_OBJ, sid, 0, 0, 0);
   1412   obj_symbol_ex(ob, pool_intern_slice(l->c->global, SLICE_LIT("__CTOR_END__")),
   1413                 SB_GLOBAL, SV_DEFAULT, SK_OBJ, sid, 0, 0, 0);
   1414   obj_symbol_ex(ob, pool_intern_slice(l->c->global, SLICE_LIT("__DTOR_LIST__")),
   1415                 SB_GLOBAL, SV_DEFAULT, SK_OBJ, sid, 0, 0, 0);
   1416   obj_symbol_ex(ob, pool_intern_slice(l->c->global, SLICE_LIT("__DTOR_END__")),
   1417                 SB_GLOBAL, SV_DEFAULT, SK_OBJ, sid, 0, 0, 0);
   1418   /* __chkstk: synthesized only for arches whose link descriptor carries the
   1419    * stub bytes (aarch64). x64 needs none — its codegen emits inline probes (or
   1420    * links libmingwex's plain-object __chkstk). Driven by the descriptor, so no
   1421    * arch identity is consulted here. */
   1422   {
   1423     const LinkArchDesc* la = link_arch_desc_for(l->c);
   1424     if (la && la->coff_chkstk_bytes && la->coff_chkstk_len) {
   1425       ObjSecId tsid = obj_section_ex(
   1426           ob, pool_intern_slice(l->c->global, SLICE_LIT(".text$chkstk")),
   1427           SEC_TEXT, SSEM_PROGBITS, SF_ALLOC | SF_EXEC | SF_RETAIN, 4, 0u, 0u,
   1428           0u);
   1429       obj_section_replace_bytes(ob, tsid, la->coff_chkstk_bytes,
   1430                                 la->coff_chkstk_len);
   1431       obj_symbol_ex(ob, pool_intern_slice(l->c->global, SLICE_LIT("__chkstk")),
   1432                     SB_GLOBAL, SV_DEFAULT, SK_FUNC, tsid, 0,
   1433                     la->coff_chkstk_len, 0);
   1434     }
   1435   }
   1436   obj_finalize(ob);
   1437   in = LinkInputs_push(&l->inputs, &idx);
   1438   if (!in)
   1439     compiler_panic(l->c, SRCLOC_NONE, "link: oom growing inputs (synth)");
   1440   in->id = (LinkInputId)(idx + 1u);
   1441   in->kind = LINK_INPUT_OBJ_BYTES;
   1442   in->order = l->next_input_order++;
   1443   in->obj = ob;
   1444   in->name =
   1445       pool_intern_slice(l->c->global, SLICE_LIT("<kit-synth-coff-runtime>"));
   1446   in->soname = 0;
   1447 }
   1448 
   1449 void link_ingest_archives(Linker* l) {
   1450   u32 a, m;
   1451   int weak_undef_pulls;
   1452   if (LinkArchives_count(&l->archives) == 0) return;
   1453   weak_undef_pulls = obj_format_weak_undef_pulls_archive_member(l->c);
   1454 
   1455   for (a = 0; a < LinkArchives_count(&l->archives); ++a) {
   1456     LinkArchive* ar = LinkArchives_at(&l->archives, a);
   1457     if (!ar->whole_archive) continue;
   1458     for (m = 0; m < ar->nmembers; ++m) {
   1459       /* obj==NULL is the long-form COFF head/trailer skip path
   1460        * (set by link_add_archive_bytes). Drop them silently. */
   1461       if (!ar->members[m].obj) continue;
   1462       include_archive_member(l, ar, &ar->members[m]);
   1463     }
   1464   }
   1465 
   1466   if (obj_format_global_archive_fixpoint(l->c)) {
   1467     for (;;) {
   1468       int changed = 0;
   1469       for (a = 0; a < LinkArchives_count(&l->archives); ++a) {
   1470         LinkArchive* ar = LinkArchives_at(&l->archives, a);
   1471         if (ar->whole_archive) continue;
   1472         changed |= scan_archive_once(l, ar, ~0u, 0, weak_undef_pulls);
   1473       }
   1474       if (!changed) break;
   1475     }
   1476     return;
   1477   }
   1478 
   1479   for (a = 0; a < LinkArchives_count(&l->archives); ++a) {
   1480     LinkArchive* ar = LinkArchives_at(&l->archives, a);
   1481     Sym want_ifunc_init = 0;
   1482     if (ar->whole_archive) continue;
   1483     if (l->emit_static_exe && inputs_have_defined_ifunc_before(l, ar->order))
   1484       want_ifunc_init =
   1485           pool_intern_slice(l->c->global, SLICE_LIT("__kit_ifunc_init"));
   1486     for (;;) {
   1487       if (!scan_archive_once(l, ar, ar->order, want_ifunc_init,
   1488                              weak_undef_pulls))
   1489         break;
   1490     }
   1491   }
   1492 }