kit

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

link.c (50334B)


      1 /* Public link API entries.
      2  *
      3  * Thin orchestrators over the Linker primitives in link.c / link_resolve.c /
      4  * link_layout.c / link_jit.c. Each entry:
      5  *   - allocates a Linker against the caller's Compiler,
      6  *   - feeds in the KitLinkInputs,
      7  *   - configures lane-specific flags (pie, shared, jit),
      8  *   - calls link_resolve,
      9  *   - dispatches to the emit (writer) or JIT-map (kit_jit_from_image) tail.
     10  *
     11  * The driver's job ends at populating KitLinkInputs; everything below this
     12  * line is libkit-internal. */
     13 
     14 #include "link/link.h"
     15 
     16 #include <kit/core.h>
     17 #include <kit/jit.h>
     18 #include <kit/link.h>
     19 #include <kit/target.h>
     20 #include <setjmp.h>
     21 #include <stdarg.h>
     22 #include <stdio.h>
     23 #include <string.h>
     24 
     25 #include "cg/internal.h"
     26 #include "cg/ir_recorder.h"
     27 #include "core/core.h"
     28 #include "core/diag.h"
     29 #include "link/link_internal.h"
     30 
     31 KitJit* kit_jit_from_image(LinkImage*);
     32 
     33 static KitStatus link_session_guard(KitLinkSession* s,
     34                                     void (*fn)(KitLinkSession*, void*),
     35                                     void* arg) {
     36   PanicFrame panic;
     37   if (!s || !s->c || !s->linker || !fn) return KIT_INVALID;
     38   compiler_panic_push(s->c, &panic);
     39   if (setjmp(panic.env)) {
     40     compiler_run_cleanups(s->c);
     41     s->linker = NULL;
     42     s->image = NULL;
     43     compiler_panic_pop(s->c, &panic);
     44     return KIT_ERR;
     45   }
     46   fn(s, arg);
     47   compiler_panic_pop(s->c, &panic);
     48   return KIT_OK;
     49 }
     50 
     51 static KitStatus link_session_remember_publish_obj(KitLinkSession* s,
     52                                                    KitObjBuilder* ob) {
     53   Heap* h;
     54   KitObjBuilder** nb;
     55   u32 new_cap;
     56   if (!s || !ob) return KIT_INVALID;
     57   if (s->npublish_objs < s->publish_objs_cap) {
     58     s->publish_objs[s->npublish_objs++] = ob;
     59     return KIT_OK;
     60   }
     61   h = s->c->ctx->heap;
     62   new_cap = s->publish_objs_cap ? s->publish_objs_cap * 2u : 8u;
     63   nb = (KitObjBuilder**)h->realloc(
     64       h, s->publish_objs, sizeof(*s->publish_objs) * s->publish_objs_cap,
     65       sizeof(*s->publish_objs) * new_cap, _Alignof(KitObjBuilder*));
     66   if (!nb) return KIT_NOMEM;
     67   s->publish_objs = nb;
     68   s->publish_objs_cap = new_cap;
     69   s->publish_objs[s->npublish_objs++] = ob;
     70   return KIT_OK;
     71 }
     72 
     73 /* These KitLinkSessionOptions fields have no plumbing into the Linker yet:
     74  * the shared-library DT_* knobs (soname/rpaths/runpaths/exports) and the
     75  * build-id selector (build_id_mode/bytes/len; the
     76  * ELF writer currently emits a fixed image-hash note regardless of mode).
     77  * Until they are wired through, warn rather than silently honor the default
     78  * instead of the caller's request. */
     79 static void link_warn_ignored_opt(Compiler* c, const char* name) {
     80   DiagSink* diag = (c && c->ctx) ? c->ctx->diag : NULL;
     81   if (!diag) return;
     82   diag_emit(diag, DIAG_WARN, SRCLOC_NONE,
     83             "link: option '%s' is not yet supported and is ignored", name);
     84 }
     85 
     86 /* -l<name> suffix search order. libkit owns only this policy table + walk
     87  * order; the host probe does all path composition and filesystem access. */
     88 typedef struct LibVariant {
     89   const char* prefix;
     90   const char* suffix;
     91   uint8_t kind; /* KitLibResolveKind */
     92 } LibVariant;
     93 
     94 bool kit_lib_resolve(uint8_t os, uint8_t mode, const char* name,
     95                      const char* const* search_dirs, uint32_t nsearch_dirs,
     96                      KitLibResolveProbe probe, void* user) {
     97   /* POSIX dynamic: Apple .tbd/.dylib first (the macOS SDK ships .tbd stubs),
     98    * then .so, then the .a fallback. DYNAMIC_ONLY drops the trailing .a. */
     99   static const LibVariant posix_dyn[] = {
    100       {"lib", ".tbd", KIT_LIB_RESOLVE_KIND_TBD},
    101       {"lib", ".dylib", KIT_LIB_RESOLVE_KIND_SHARED},
    102       {"lib", ".so", KIT_LIB_RESOLVE_KIND_SHARED},
    103       {"lib", ".a", KIT_LIB_RESOLVE_KIND_ARCHIVE},
    104   };
    105   static const LibVariant posix_static[] = {
    106       {"lib", ".a", KIT_LIB_RESOLVE_KIND_ARCHIVE},
    107   };
    108   /* Windows / mingw: mingw-canonical names first (lib<n>.dll.a, lib<n>.a),
    109    * then the MSVC <n>.lib / <n>.dll.a variants. Every match feeds the linker
    110    * as an archive input (short-form import libs are AR archives). */
    111   static const LibVariant win_variants[] = {
    112       {"lib", ".dll.a", KIT_LIB_RESOLVE_KIND_ARCHIVE},
    113       {"lib", ".a", KIT_LIB_RESOLVE_KIND_ARCHIVE},
    114       {"", ".lib", KIT_LIB_RESOLVE_KIND_ARCHIVE},
    115       {"", ".dll.a", KIT_LIB_RESOLVE_KIND_ARCHIVE},
    116   };
    117   const LibVariant* variants;
    118   uint32_t nvariants, vi, di;
    119 
    120   if (!name || !probe) return false;
    121 
    122   if (os == KIT_LIB_RESOLVE_OS_WINDOWS) {
    123     variants = win_variants;
    124     nvariants = (uint32_t)(sizeof win_variants / sizeof win_variants[0]);
    125   } else if (mode == KIT_LIB_RESOLVE_STATIC_ONLY) {
    126     variants = posix_static;
    127     nvariants = (uint32_t)(sizeof posix_static / sizeof posix_static[0]);
    128   } else {
    129     variants = posix_dyn;
    130     nvariants = (uint32_t)(sizeof posix_dyn / sizeof posix_dyn[0]);
    131     if (mode == KIT_LIB_RESOLVE_DYNAMIC_ONLY) nvariants -= 1u; /* drop .a */
    132   }
    133 
    134   for (vi = 0; vi < nvariants; ++vi) {
    135     for (di = 0; di < nsearch_dirs; ++di) {
    136       if (probe(user, search_dirs[di], variants[vi].prefix, name,
    137                 variants[vi].suffix, variants[vi].kind))
    138         return true;
    139     }
    140   }
    141   return false;
    142 }
    143 
    144 KitStatus kit_link_session_new(KitCompiler* c,
    145                                const KitLinkSessionOptions* opts,
    146                                KitLinkSession** out) {
    147   Heap* h;
    148   KitLinkSession* s;
    149   Linker* l;
    150   if (!out) return KIT_INVALID;
    151   *out = NULL;
    152   if (!c || !opts) return KIT_INVALID;
    153   h = c->ctx->heap;
    154   l = link_new(c);
    155   if (!l) return KIT_NOMEM;
    156   s = (KitLinkSession*)h->alloc(h, sizeof(*s), _Alignof(KitLinkSession));
    157   if (!s) {
    158     link_free(l);
    159     return KIT_NOMEM;
    160   }
    161   memset(s, 0, sizeof(*s));
    162   s->c = (Compiler*)c;
    163   s->linker = l;
    164   s->opts = *opts;
    165   if (opts->output_kind > KIT_LINK_OUTPUT_JIT) {
    166     h->free(h, s, sizeof(*s));
    167     link_free(l);
    168     return KIT_INVALID;
    169   }
    170   if (opts->pe_subsystem != KIT_PE_SUBSYSTEM_DEFAULT &&
    171       opts->pe_subsystem != KIT_PE_SUBSYSTEM_WINDOWS_GUI &&
    172       opts->pe_subsystem != KIT_PE_SUBSYSTEM_WINDOWS_CUI) {
    173     h->free(h, s, sizeof(*s));
    174     link_free(l);
    175     return KIT_INVALID;
    176   }
    177   link_set_pe_subsystem(l, opts->pe_subsystem);
    178 
    179   switch ((KitLinkOutputKind)opts->output_kind) {
    180     case KIT_LINK_OUTPUT_EXE:
    181       link_set_emit_static_exe(l, 1);
    182       link_set_gc_sections(l, opts->gc_sections);
    183       link_set_strip_debug(l, opts->strip_debug);
    184       link_set_allow_undefined(l, opts->allow_undefined);
    185       link_set_pie(l, opts->pie);
    186       link_set_interp_path(l, opts->interp_path);
    187       break;
    188     case KIT_LINK_OUTPUT_SHARED:
    189       link_set_gc_sections(l, opts->gc_sections);
    190       link_set_strip_debug(l, opts->strip_debug);
    191       link_set_allow_undefined(l, 0);
    192       link_set_shared(l, 1);
    193       link_set_pie(l, 0);
    194       if (opts->exports && opts->nexports)
    195         link_warn_ignored_opt(s->c, "--export-symbol");
    196       break;
    197     case KIT_LINK_OUTPUT_RELOCATABLE:
    198       break;
    199     case KIT_LINK_OUTPUT_JIT:
    200       if (!opts->jit_host) {
    201         h->free(h, s, sizeof(*s));
    202         link_free(l);
    203         return KIT_INVALID;
    204       }
    205       link_set_jit_host(l, opts->jit_host);
    206       link_set_jit_mode(l, 1);
    207       link_set_gc_sections(l, opts->gc_sections);
    208       if (!opts->entry.s || opts->entry.len == 0) link_clear_entry(l);
    209       if (opts->extern_resolver) {
    210         link_set_extern_resolver(l, opts->extern_resolver,
    211                                  opts->extern_resolver_user);
    212       }
    213       break;
    214   }
    215   if (opts->linker_script) link_set_script(l, opts->linker_script);
    216   if (opts->text_base_set) link_set_text_base(l, opts->text_base);
    217   /* Linker-flags/policy plumbing (kernel-C work). The Linker struct lives in
    218    * link_internal.h; these fields have no dedicated setter, so the public API
    219    * composition layer assigns them directly. The arrays are borrowed from the
    220    * caller (the driver keeps them alive for the session's lifetime). */
    221   l->defsyms = opts->defsyms;
    222   l->ndefsyms = opts->ndefsyms;
    223   l->section_starts = opts->section_starts;
    224   l->nsection_starts = opts->nsection_starts;
    225   l->soname = opts->soname;
    226   l->rpaths = opts->rpaths;
    227   l->nrpaths = opts->nrpaths;
    228   l->runpaths = opts->runpaths;
    229   l->nrunpaths = opts->nrunpaths;
    230   l->orphan_handling = opts->orphan_handling;
    231   l->fatal_warnings = opts->fatal_warnings ? 1 : 0;
    232   l->freestanding_strict = opts->freestanding_strict ? 1 : 0;
    233   if (opts->entry.s && opts->entry.len) {
    234     link_set_entry(l, opts->entry);
    235   } else if (opts->pe_subsystem == KIT_PE_SUBSYSTEM_WINDOWS_GUI &&
    236              !(opts->linker_script && opts->linker_script->entry.s &&
    237                opts->linker_script->entry.len)) {
    238     link_set_entry(l, KIT_SLICE_LIT("WinMainCRTStartup"));
    239   }
    240   /* TODO(build-id): the ELF writer (src/obj/elf/link.c) emits a fixed
    241    * image-hash build-id note unconditionally for non-scripted layouts; the
    242    * caller's mode selection is not threaded through, so a note is emitted
    243    * even for KIT_BUILDID_NONE and uuid/user-bytes modes get the default hash.
    244    * Honoring the mode means carrying it into LinkImage + the ELF/Mach-O
    245    * writers (and is a no-op for the common default), so it is left as a known
    246    * gap rather than warned about on the default path. */
    247   (void)opts->build_id_mode;
    248   (void)opts->build_id_bytes;
    249   (void)opts->build_id_len;
    250   *out = s;
    251   return KIT_OK;
    252 }
    253 
    254 typedef struct LinkAddObjArg {
    255   KitObjBuilder* ob;
    256 } LinkAddObjArg;
    257 
    258 static void link_session_add_obj_inner(KitLinkSession* s, void* arg) {
    259   LinkAddObjArg* a = (LinkAddObjArg*)arg;
    260   link_add_obj(s->linker, (ObjBuilder*)a->ob);
    261 }
    262 
    263 KitStatus kit_link_session_add_obj(KitLinkSession* s, KitObjBuilder* ob) {
    264   LinkAddObjArg arg;
    265   KitStatus st;
    266   if (!s || !ob || s->resolved) return KIT_INVALID;
    267   arg.ob = ob;
    268   st = link_session_guard(s, link_session_add_obj_inner, &arg);
    269   if (st != KIT_OK) return st;
    270   return link_session_remember_publish_obj(s, ob);
    271 }
    272 
    273 typedef struct LinkAddBytesArg {
    274   KitSlice name;
    275   const KitSlice* bytes;
    276   const KitLinkArchiveInput* archive;
    277 } LinkAddBytesArg;
    278 
    279 static void link_session_add_obj_bytes_inner(KitLinkSession* s, void* arg) {
    280   LinkAddBytesArg* a = (LinkAddBytesArg*)arg;
    281   link_add_obj_bytes(s->linker, a->name.s, a->bytes->data, a->bytes->len);
    282 }
    283 
    284 KitStatus kit_link_session_add_obj_bytes(KitLinkSession* s, KitSlice name,
    285                                          const KitSlice* bytes) {
    286   LinkAddBytesArg arg;
    287   if (!s || !bytes || s->resolved) return KIT_INVALID;
    288   arg.name = name;
    289   arg.bytes = bytes;
    290   arg.archive = NULL;
    291   s->non_obj_inputs++;
    292   return link_session_guard(s, link_session_add_obj_bytes_inner, &arg);
    293 }
    294 
    295 static void link_session_add_archive_bytes_inner(KitLinkSession* s, void* arg) {
    296   const KitLinkArchiveInput* a = ((LinkAddBytesArg*)arg)->archive;
    297   link_add_archive_bytes(s->linker, a->name.s, a->bytes.data, a->bytes.len,
    298                          a->whole_archive, a->link_mode, a->group_id);
    299 }
    300 
    301 KitStatus kit_link_session_add_archive_bytes(
    302     KitLinkSession* s, const KitLinkArchiveInput* archive) {
    303   LinkAddBytesArg arg;
    304   if (!s || !archive || s->resolved) return KIT_INVALID;
    305   arg.bytes = NULL;
    306   arg.archive = archive;
    307   s->non_obj_inputs++;
    308   return link_session_guard(s, link_session_add_archive_bytes_inner, &arg);
    309 }
    310 
    311 static void link_session_add_dso_bytes_inner(KitLinkSession* s, void* arg) {
    312   LinkAddBytesArg* a = (LinkAddBytesArg*)arg;
    313   link_add_dso_bytes(s->linker, a->name.s, a->bytes->data, a->bytes->len);
    314 }
    315 
    316 KitStatus kit_link_session_add_dso_bytes(KitLinkSession* s, KitSlice name,
    317                                          const KitSlice* bytes) {
    318   LinkAddBytesArg arg;
    319   if (!s || !bytes || s->resolved) return KIT_INVALID;
    320   arg.name = name;
    321   arg.bytes = bytes;
    322   arg.archive = NULL;
    323   s->non_obj_inputs++;
    324   return link_session_guard(s, link_session_add_dso_bytes_inner, &arg);
    325 }
    326 
    327 typedef struct LinkLtoPreserveArg {
    328   KitObjBuilder* lto_obj;
    329   KitCg* lto_cg;
    330   KitLinkLtoPreservedCallback cb;
    331   void* user;
    332 } LinkLtoPreserveArg;
    333 
    334 typedef struct LinkLtoRefMark {
    335   ObjSymId sym;
    336   u8 referenced;
    337   u8 pad[3];
    338 } LinkLtoRefMark;
    339 
    340 typedef struct LinkLtoRefMarks {
    341   Compiler* c;
    342   ObjBuilder* ob;
    343   LinkLtoRefMark* marks;
    344   u32 nmarks;
    345   u32 cap;
    346 } LinkLtoRefMarks;
    347 
    348 static int link_lto_sym_is_logical_undef(const ObjSym* s) {
    349   return s && s->section_id == OBJ_SEC_NONE && s->kind != SK_ABS &&
    350          s->kind != SK_COMMON;
    351 }
    352 
    353 static int link_lto_sym_is_preservable_def(const ObjSym* s) {
    354   return s && !s->removed && s->name != 0 && s->bind != SB_LOCAL &&
    355          link_sym_is_def(s);
    356 }
    357 
    358 static void link_lto_preserve_name(LinkLtoPreserveArg* a, Sym name) {
    359   ObjSymIter* it;
    360   ObjSymEntry e;
    361   if (!a || !name) return;
    362   it = obj_symiter_new((ObjBuilder*)a->lto_obj);
    363   while (it && obj_symiter_next(it, &e)) {
    364     const ObjSym* s = e.sym;
    365     if (!s || s->name != name) continue;
    366     if (link_lto_sym_is_preservable_def(s)) a->cb(a->user, (KitCgSym)e.id);
    367   }
    368   if (it) obj_symiter_free(it);
    369 }
    370 
    371 static int link_lto_sym_in_preserved_section(ObjBuilder* ob, ObjSymId sym,
    372                                              const ObjSym* s) {
    373   const Section* sec;
    374   const ObjAtom* atom;
    375   ObjAtomId aid;
    376   if (!ob || !s) return 0;
    377   if (s->section_id == OBJ_SEC_NONE) return 0;
    378   sec = obj_section_get(ob, s->section_id);
    379   if (sec && ((sec->flags & SF_RETAIN) || sec->sem == SSEM_INIT_ARRAY ||
    380               sec->sem == SSEM_FINI_ARRAY || sec->sem == SSEM_PREINIT_ARRAY))
    381     return 1;
    382   aid = obj_atom_find_symbol(ob, sym);
    383   atom = obj_atom_get(ob, aid);
    384   return atom && (atom->flags & OBJ_ATOM_RETAIN);
    385 }
    386 
    387 static void link_lto_refmarks_add(LinkLtoRefMarks* marks, ObjSymId sym,
    388                                   const ObjSym* s) {
    389   Heap* h;
    390   LinkLtoRefMark* nm;
    391   u32 ncap;
    392   if (!marks || sym == OBJ_SYM_NONE || !s) return;
    393   for (u32 i = 0; i < marks->nmarks; ++i)
    394     if (marks->marks[i].sym == sym) return;
    395   if (marks->nmarks == marks->cap) {
    396     h = marks->c->ctx->heap;
    397     ncap = marks->cap ? marks->cap * 2u : 32u;
    398     nm = (LinkLtoRefMark*)h->realloc(
    399         h, marks->marks, sizeof(*marks->marks) * marks->cap,
    400         sizeof(*marks->marks) * ncap, _Alignof(LinkLtoRefMark));
    401     if (!nm)
    402       compiler_panic(marks->c, SRCLOC_NONE,
    403                      "link: oom on LTO semantic-ref marks");
    404     marks->marks = nm;
    405     marks->cap = ncap;
    406   }
    407   marks->marks[marks->nmarks].sym = sym;
    408   marks->marks[marks->nmarks].referenced = s->referenced ? 1u : 0u;
    409   marks->nmarks++;
    410 }
    411 
    412 static void link_lto_mark_refset(ObjBuilder* ob, const ObjSymSet* refs,
    413                                  LinkLtoRefMarks* marks) {
    414   if (!ob || !refs || !refs->cap) return;
    415   for (u32 i = 0; i < refs->cap; ++i) {
    416     ObjSymId sym = refs->slots[i].k;
    417     const ObjSym* s;
    418     if (sym == OBJ_SYM_NONE) continue;
    419     s = obj_symbol_get(ob, sym);
    420     if (link_lto_sym_is_logical_undef(s)) {
    421       link_lto_refmarks_add(marks, sym, s);
    422       obj_sym_mark_referenced(ob, sym);
    423     }
    424   }
    425 }
    426 
    427 static int link_lto_module_has_asm(const CgIrModule* module) {
    428   if (!module) return 0;
    429   if (module->nfile_scope_asms) return 1;
    430   for (u32 i = 0; i < module->nfuncs; ++i) {
    431     const CgIrFunc* f = module->funcs[i];
    432     if (!f || f->removed) continue;
    433     for (u32 k = 0; k < f->ninsts; ++k)
    434       if (f->insts[k].op == CG_IR_ASM_BLOCK) return 1;
    435   }
    436   return 0;
    437 }
    438 
    439 static void link_lto_mark_semantic_refs(LinkLtoPreserveArg* a,
    440                                         LinkLtoRefMarks* marks) {
    441   ObjBuilder* ob = (ObjBuilder*)a->lto_obj;
    442   const CgIrModule* module;
    443   if (!a->lto_cg || !a->lto_cg->target) return;
    444   module = cg_ir_recorder_module(a->lto_cg->target);
    445   if (!module) return;
    446   for (u32 i = 0; i < module->nfuncs; ++i) {
    447     const CgIrFunc* f = module->funcs[i];
    448     if (!f || f->removed) continue;
    449     link_lto_mark_refset(ob, &f->call_refs, marks);
    450     link_lto_mark_refset(ob, &f->global_refs, marks);
    451   }
    452 }
    453 
    454 static void link_lto_refmarks_restore(LinkLtoRefMarks* marks) {
    455   if (!marks || !marks->ob) return;
    456   for (u32 i = 0; i < marks->nmarks; ++i) {
    457     obj_sym_set_referenced(marks->ob, marks->marks[i].sym,
    458                            marks->marks[i].referenced);
    459   }
    460 }
    461 
    462 static void link_lto_refmarks_fini(LinkLtoRefMarks* marks) {
    463   Heap* h;
    464   if (!marks || !marks->marks) return;
    465   h = marks->c->ctx->heap;
    466   h->free(h, marks->marks, sizeof(*marks->marks) * marks->cap);
    467   memset(marks, 0, sizeof(*marks));
    468 }
    469 
    470 static void link_lto_preserve_intrinsic_roots(KitLinkSession* s,
    471                                               LinkLtoPreserveArg* a) {
    472   ObjBuilder* ob = (ObjBuilder*)a->lto_obj;
    473   const CgIrModule* module = NULL;
    474   int preserve_all_nonlocal = 0;
    475   ObjSymIter* it;
    476   ObjSymEntry e;
    477 
    478   if (a->lto_cg && a->lto_cg->target)
    479     module = cg_ir_recorder_module(a->lto_cg->target);
    480 
    481   preserve_all_nonlocal = s->opts.output_kind != KIT_LINK_OUTPUT_EXE ||
    482                           link_lto_module_has_asm(module);
    483   if (s->opts.output_kind == KIT_LINK_OUTPUT_SHARED) preserve_all_nonlocal = 1;
    484 
    485   it = obj_symiter_new(ob);
    486   while (it && obj_symiter_next(it, &e)) {
    487     const ObjSym* os = e.sym;
    488     if (!link_lto_sym_is_preservable_def(os)) continue;
    489     if (preserve_all_nonlocal || os->bind == SB_WEAK || os->kind == SK_IFUNC ||
    490         (os->flags & KIT_CG_SYM_USED) ||
    491         link_lto_sym_in_preserved_section(ob, e.id, os)) {
    492       a->cb(a->user, (KitCgSym)e.id);
    493     }
    494   }
    495   if (it) obj_symiter_free(it);
    496 
    497   if (s->linker->entry_name) link_lto_preserve_name(a, s->linker->entry_name);
    498   for (u32 i = 0; i < s->opts.nexports; ++i) {
    499     const KitSlice* ex = &s->opts.exports[i];
    500     if (ex->s && ex->len)
    501       link_lto_preserve_name(
    502           a,
    503           pool_intern_slice(s->c->global, (Slice){.s = ex->s, .len = ex->len}));
    504   }
    505 }
    506 
    507 static void link_lto_preserve_opaque_undef_refs(KitLinkSession* s,
    508                                                 LinkLtoPreserveArg* a) {
    509   u32 ninputs = LinkInputs_count(&s->linker->inputs);
    510   for (u32 ii = 0; ii < ninputs; ++ii) {
    511     LinkInput* in = LinkInputs_at(&s->linker->inputs, ii);
    512     ObjSymIter* it;
    513     ObjSymEntry e;
    514     if (!in || !in->obj || in->obj == (ObjBuilder*)a->lto_obj) continue;
    515     it = obj_symiter_new(in->obj);
    516     while (it && obj_symiter_next(it, &e)) {
    517       const ObjSym* os = e.sym;
    518       if (!os || os->name == 0 || os->bind == SB_LOCAL) continue;
    519       if (link_sym_is_spurious_undef(os)) continue;
    520       if (!link_lto_sym_is_logical_undef(os)) continue;
    521       link_lto_preserve_name(a, os->name);
    522     }
    523     if (it) obj_symiter_free(it);
    524   }
    525 }
    526 
    527 static void link_session_visit_lto_preserved_inner(KitLinkSession* s,
    528                                                    void* arg) {
    529   LinkLtoPreserveArg* a = (LinkLtoPreserveArg*)arg;
    530   LinkLtoRefMarks marks;
    531   memset(&marks, 0, sizeof marks);
    532   marks.c = s->c;
    533   marks.ob = (ObjBuilder*)a->lto_obj;
    534   if (s->opts.output_kind != KIT_LINK_OUTPUT_RELOCATABLE) {
    535     /* Archive selection needs pre-finish semantic refs, but those refs may
    536      * disappear after LTO internalization/DCE. Borrow ObjSym::referenced only
    537      * for archive ingestion, then restore it before CG finish. */
    538     link_lto_mark_semantic_refs(a, &marks);
    539     link_ingest_archives(s->linker);
    540     link_lto_refmarks_restore(&marks);
    541     link_lto_refmarks_fini(&marks);
    542   }
    543   link_lto_preserve_intrinsic_roots(s, a);
    544   link_lto_preserve_opaque_undef_refs(s, a);
    545 }
    546 
    547 KitStatus kit_link_session_visit_lto_preserved(KitLinkSession* s,
    548                                                KitObjBuilder* lto_obj,
    549                                                KitCg* lto_cg,
    550                                                KitLinkLtoPreservedCallback cb,
    551                                                void* user) {
    552   LinkLtoPreserveArg arg;
    553   if (!s || !lto_obj || !lto_cg || !cb || s->resolved) return KIT_INVALID;
    554   memset(&arg, 0, sizeof arg);
    555   arg.lto_obj = lto_obj;
    556   arg.lto_cg = lto_cg;
    557   arg.cb = cb;
    558   arg.user = user;
    559   return link_session_guard(s, link_session_visit_lto_preserved_inner, &arg);
    560 }
    561 
    562 static void link_session_resolve_inner(KitLinkSession* s, void* arg) {
    563   (void)arg;
    564   if ((KitLinkOutputKind)s->opts.output_kind == KIT_LINK_OUTPUT_RELOCATABLE) {
    565     s->resolved = 1;
    566     return;
    567   }
    568   s->image = link_resolve(s->linker);
    569   s->resolved = 1;
    570 }
    571 
    572 KitStatus kit_link_session_resolve(KitLinkSession* s) {
    573   if (!s || s->resolved) return KIT_INVALID;
    574   return link_session_guard(s, link_session_resolve_inner, NULL);
    575 }
    576 
    577 static KitStatus link_report_write(KitWriter* out, const char* s, size_t n) {
    578   return n ? kit_writer_write(out, s, n) : KIT_OK;
    579 }
    580 
    581 static KitStatus link_report_cstr(KitWriter* out, const char* s) {
    582   return link_report_write(out, s, s ? strlen(s) : 0);
    583 }
    584 
    585 static KitStatus link_report_slice(KitWriter* out, KitSlice s) {
    586   return link_report_write(out, s.s, s.s ? s.len : 0);
    587 }
    588 
    589 static KitStatus link_reportf(KitWriter* out, const char* fmt, ...) {
    590   char buf[512];
    591   va_list ap;
    592   int n;
    593   va_start(ap, fmt);
    594   n = vsnprintf(buf, sizeof(buf), fmt, ap);
    595   va_end(ap);
    596   if (n < 0) return KIT_ERR;
    597   if ((size_t)n >= sizeof(buf)) return KIT_UNSUPPORTED;
    598   return link_report_write(out, buf, (size_t)n);
    599 }
    600 
    601 static const char* link_report_output_kind(uint8_t kind) {
    602   switch ((KitLinkOutputKind)kind) {
    603     case KIT_LINK_OUTPUT_EXE:
    604       return "exe";
    605     case KIT_LINK_OUTPUT_SHARED:
    606       return "shared";
    607     case KIT_LINK_OUTPUT_RELOCATABLE:
    608       return "relocatable";
    609     case KIT_LINK_OUTPUT_JIT:
    610       return "jit";
    611   }
    612   return "unknown";
    613 }
    614 
    615 static KitSlice link_report_sym_name(LinkImage* img, Sym name) {
    616   return name ? pool_slice(img->c->global, name) : KIT_SLICE_NULL;
    617 }
    618 
    619 static int link_report_slice_cmp(KitSlice a, KitSlice b) {
    620   size_t n = a.len < b.len ? a.len : b.len;
    621   int c = n ? memcmp(a.s, b.s, n) : 0;
    622   if (c) return c;
    623   if (a.len < b.len) return -1;
    624   if (a.len > b.len) return 1;
    625   return 0;
    626 }
    627 
    628 static int link_report_symbol_include(const LinkImage* img,
    629                                       const LinkSymbol* s) {
    630   if (!s || !s->name || !s->defined || s->imported) return 0;
    631   if (s->kind == SK_FILE || s->kind == SK_SECTION) return 0;
    632   if (s->bind != SB_LOCAL && !link_symbol_is_canonical_global(img, s)) return 0;
    633   return 1;
    634 }
    635 
    636 typedef struct LinkReportSymbols {
    637   LinkSymId* ids;
    638   u32 n;
    639   u32 cap;
    640 } LinkReportSymbols;
    641 
    642 static int link_report_symbol_cmp(LinkImage* img, LinkSymId a, LinkSymId b) {
    643   const LinkSymbol* sa = LinkSyms_at(&img->syms, a - 1u);
    644   const LinkSymbol* sb = LinkSyms_at(&img->syms, b - 1u);
    645   KitSlice na;
    646   KitSlice nb;
    647   if (sa->vaddr < sb->vaddr) return -1;
    648   if (sa->vaddr > sb->vaddr) return 1;
    649   na = link_report_sym_name(img, sa->name);
    650   nb = link_report_sym_name(img, sb->name);
    651   return link_report_slice_cmp(na, nb);
    652 }
    653 
    654 static KitStatus link_report_collect_symbols(LinkImage* img,
    655                                              LinkReportSymbols* out) {
    656   Heap* h = img->heap;
    657   u32 i;
    658   memset(out, 0, sizeof(*out));
    659   out->cap = LinkSyms_count(&img->syms);
    660   if (!out->cap) return KIT_OK;
    661   out->ids =
    662       (LinkSymId*)h->alloc(h, sizeof(*out->ids) * out->cap, _Alignof(LinkSymId));
    663   if (!out->ids) return KIT_NOMEM;
    664   for (i = 0; i < out->cap; ++i) {
    665     const LinkSymbol* s = LinkSyms_at(&img->syms, i);
    666     if (link_report_symbol_include(img, s)) out->ids[out->n++] = s->id;
    667   }
    668   for (i = 1; i < out->n; ++i) {
    669     LinkSymId key = out->ids[i];
    670     u32 j = i;
    671     while (j > 0 && link_report_symbol_cmp(img, key, out->ids[j - 1u]) < 0) {
    672       out->ids[j] = out->ids[j - 1u];
    673       --j;
    674     }
    675     out->ids[j] = key;
    676   }
    677   return KIT_OK;
    678 }
    679 
    680 static void link_report_free_symbols(LinkImage* img, LinkReportSymbols* syms) {
    681   if (syms->ids)
    682     img->heap->free(img->heap, syms->ids,
    683                     sizeof(*syms->ids) * syms->cap);
    684   memset(syms, 0, sizeof(*syms));
    685 }
    686 
    687 static char link_report_symbol_type(const LinkImage* img,
    688                                     const LinkSymbol* s) {
    689   char t = 'N';
    690   const LinkSection* sec = NULL;
    691   if (!s->defined) {
    692     t = 'U';
    693   } else if (s->kind == SK_ABS) {
    694     t = 'A';
    695   } else if (s->kind == SK_COMMON) {
    696     t = 'C';
    697   } else if (s->kind == SK_FUNC || s->kind == SK_IFUNC) {
    698     t = 'T';
    699   } else {
    700     if (s->section_id != LINK_SEC_NONE && s->section_id <= img->nsections)
    701       sec = &img->sections[s->section_id - 1u];
    702     if (s->kind == SK_TLS) {
    703       t = 'D';
    704     } else if (sec && sec->sem == SSEM_NOBITS) {
    705       t = 'B';
    706     } else if (sec && (sec->flags & SF_EXEC)) {
    707       t = 'T';
    708     } else if (sec && (sec->flags & SF_WRITE)) {
    709       t = 'D';
    710     } else if (sec && (sec->flags & SF_ALLOC)) {
    711       t = 'R';
    712     }
    713   }
    714   if (s->bind == SB_WEAK) {
    715     if (t == 'D' || t == 'B' || t == 'R')
    716       t = 'V';
    717     else if (t != 'U')
    718       t = 'W';
    719   } else if (s->bind == SB_LOCAL && t >= 'A' && t <= 'Z') {
    720     t = (char)(t - 'A' + 'a');
    721   }
    722   return t;
    723 }
    724 
    725 /* Final runtime address of a symbol, mirroring the ELF emitter's st_value
    726  * rule: SK_FILE / undefined carry 0; SK_ABS carries its own value verbatim;
    727  * every other defined symbol is image-relative and gets img->load_base added
    728  * (0 for PIE/scripted, the static base or -Ttext override otherwise). Keeps
    729  * the side-files consistent with the produced binary's symbol table. */
    730 static u64 link_report_final_addr(const LinkImage* img, const LinkSymbol* s) {
    731   if (s->kind == SK_FILE || !s->defined) return 0;
    732   if (s->kind == SK_ABS) return s->vaddr;
    733   return img->load_base + s->vaddr;
    734 }
    735 
    736 static KitStatus link_report_write_symbol_line(LinkImage* img, KitWriter* out,
    737                                                const LinkSymbol* s) {
    738   KitSlice name = link_report_sym_name(img, s->name);
    739   KitStatus st = link_reportf(out, "%016llx %c ",
    740                               (unsigned long long)link_report_final_addr(img, s),
    741                               link_report_symbol_type(img, s));
    742   if (st != KIT_OK) return st;
    743   st = link_report_slice(out, name);
    744   if (st != KIT_OK) return st;
    745   return link_report_cstr(out, "\n");
    746 }
    747 
    748 static KitStatus link_report_write_symbols_nm(LinkImage* img, KitWriter* out) {
    749   LinkReportSymbols syms;
    750   KitStatus st;
    751   u32 i;
    752   st = link_report_collect_symbols(img, &syms);
    753   if (st != KIT_OK) return st;
    754   for (i = 0; i < syms.n; ++i) {
    755     const LinkSymbol* s = LinkSyms_at(&img->syms, syms.ids[i] - 1u);
    756     st = link_report_write_symbol_line(img, out, s);
    757     if (st != KIT_OK) break;
    758   }
    759   link_report_free_symbols(img, &syms);
    760   return st == KIT_OK ? kit_writer_status(out) : st;
    761 }
    762 
    763 /* Normalize a (possibly absolute) input path to its basename so map/side files
    764  * stay byte-identical regardless of where the inputs live on disk (an absolute
    765  * host path would leak the build directory and break determinism). */
    766 static KitSlice link_report_normalize_path(KitSlice raw) {
    767   size_t i;
    768   size_t base = 0;
    769   if (!raw.s || raw.len == 0) return raw;
    770   for (i = 0; i < raw.len; ++i)
    771     if (raw.s[i] == '/' || raw.s[i] == '\\') base = i + 1u;
    772   if (base >= raw.len) return raw; /* trailing separator: keep as-is */
    773   raw.s += base;
    774   raw.len -= base;
    775   return raw;
    776 }
    777 
    778 static KitSlice link_report_input_name(LinkImage* img, LinkInputId id) {
    779   LinkInput* in;
    780   KitSlice raw;
    781   if (!img || !img->linker || id == LINK_INPUT_NONE ||
    782       id > LinkInputs_count(&img->linker->inputs))
    783     return KIT_SLICE_NULL;
    784   in = LinkInputs_at(&img->linker->inputs, id - 1u);
    785   if (!in || !in->name) return KIT_SLICE_NULL;
    786   raw = pool_slice(img->c->global, in->name);
    787   return link_report_normalize_path(raw);
    788 }
    789 
    790 /* Discarded sections: input sections that are layout candidates
    791  * (link_section_kept) but did not land in the final image — dropped by
    792  * --gc-sections, retired as a COMDAT duplicate, or otherwise unplaced. Walks
    793  * the per-input maps in input/section order for determinism, and reports the
    794  * normalized input name so no absolute host path leaks. */
    795 static KitStatus link_report_write_discarded(LinkImage* img, KitWriter* out) {
    796   KitStatus st;
    797   u32 ii;
    798   if (!img || !img->linker) return KIT_OK;
    799   st = link_report_cstr(out, "\ndiscarded\n");
    800   if (st != KIT_OK) return st;
    801   for (ii = 0; ii < LinkInputs_count(&img->linker->inputs); ++ii) {
    802     LinkInput* in = LinkInputs_at(&img->linker->inputs, ii);
    803     InputMap* m = (ii < img->ninput_maps) ? &img->input_maps[ii] : NULL;
    804     ObjBuilder* ob = in ? in->obj : NULL;
    805     KitSlice input;
    806     u32 j, n;
    807     if (!ob || !m) continue;
    808     if (in->kind == LINK_INPUT_DSO_BYTES) continue;
    809     input = link_report_input_name(img, in->id);
    810     n = obj_section_count(ob);
    811     for (j = 1; j < n; ++j) {
    812       const Section* sec = obj_section_get(ob, j);
    813       int dropped;
    814       if (!sec || sec->removed || !link_section_kept(sec)) continue;
    815       dropped = (m->comdat_discarded && j < m->nsection && m->comdat_discarded[j]) ||
    816                 (m->section && j < m->nsection && m->section[j] == LINK_SEC_NONE);
    817       if (!dropped) continue;
    818       st = link_report_cstr(out, "  ");
    819       if (st != KIT_OK) return st;
    820       {
    821         KitSlice nm = sec->name ? link_report_sym_name(img, sec->name)
    822                                 : KIT_SLICE_NULL;
    823         st = nm.s ? link_report_slice(out, nm) : link_report_cstr(out, "-");
    824         if (st != KIT_OK) return st;
    825       }
    826       if (input.s && input.len) {
    827         st = link_report_cstr(out, " input=");
    828         if (st != KIT_OK) return st;
    829         st = link_report_slice(out, input);
    830         if (st != KIT_OK) return st;
    831       }
    832       st = link_report_cstr(out, "\n");
    833       if (st != KIT_OK) return st;
    834     }
    835   }
    836   return KIT_OK;
    837 }
    838 
    839 /* Unresolved symbols: image symbols that remain undefined and unimported
    840  * after resolution (only reachable when the link is permissive, e.g.
    841  * --allow-undefined; a strict link panics instead). Sorted by name for a
    842  * deterministic listing. */
    843 static KitStatus link_report_write_unresolved(LinkImage* img, KitWriter* out) {
    844   KitStatus st;
    845   Heap* h = img->heap;
    846   LinkSymId* ids = NULL;
    847   u32 n = 0, cap, i;
    848   if (!img) return KIT_OK;
    849   st = link_report_cstr(out, "\nunresolved\n");
    850   if (st != KIT_OK) return st;
    851   cap = LinkSyms_count(&img->syms);
    852   if (!cap) return KIT_OK;
    853   ids = (LinkSymId*)h->alloc(h, sizeof(*ids) * cap, _Alignof(LinkSymId));
    854   if (!ids) return KIT_NOMEM;
    855   for (i = 0; i < cap; ++i) {
    856     const LinkSymbol* s = LinkSyms_at(&img->syms, i);
    857     if (!s || s->name == 0) continue;
    858     if (s->defined || s->imported) continue;
    859     if (s->kind == SK_FILE || s->kind == SK_SECTION) continue;
    860     ids[n++] = s->id;
    861   }
    862   /* Insertion sort by name (small lists; deterministic). */
    863   for (i = 1; i < n; ++i) {
    864     LinkSymId key = ids[i];
    865     KitSlice kn = link_report_sym_name(
    866         img, LinkSyms_at(&img->syms, key - 1u)->name);
    867     u32 j = i;
    868     while (j > 0) {
    869       KitSlice pn = link_report_sym_name(
    870           img, LinkSyms_at(&img->syms, ids[j - 1u] - 1u)->name);
    871       if (link_report_slice_cmp(pn, kn) <= 0) break;
    872       ids[j] = ids[j - 1u];
    873       --j;
    874     }
    875     ids[j] = key;
    876   }
    877   for (i = 0; i < n; ++i) {
    878     const LinkSymbol* s = LinkSyms_at(&img->syms, ids[i] - 1u);
    879     KitSlice nm = link_report_sym_name(img, s->name);
    880     st = link_report_cstr(out, "  ");
    881     if (st == KIT_OK) st = link_report_slice(out, nm);
    882     if (st == KIT_OK) st = link_report_cstr(out, "\n");
    883     if (st != KIT_OK) break;
    884   }
    885   h->free(h, ids, sizeof(*ids) * cap);
    886   return st;
    887 }
    888 
    889 static KitStatus link_report_write_map_image(KitLinkSession* s,
    890                                              KitWriter* out) {
    891   LinkImage* img = s->image;
    892   char triple[96];
    893   KitStatus st;
    894   u32 i;
    895   if (!img) return KIT_INVALID;
    896   if (!kit_target_to_triple(img->c->target, triple, sizeof(triple)))
    897     strcpy(triple, "unknown");
    898   st = link_reportf(out,
    899                     "target %s\n"
    900                     "output %s\n",
    901                     triple, link_report_output_kind(s->opts.output_kind));
    902   if (st != KIT_OK) return st;
    903   if (img->entry_sym != LINK_SYM_NONE) {
    904     const LinkSymbol* es = LinkSyms_at(&img->syms, img->entry_sym - 1u);
    905     KitSlice name = link_report_sym_name(img, es->name);
    906     st = link_report_cstr(out, "entry ");
    907     if (st != KIT_OK) return st;
    908     st = link_report_slice(out, name);
    909     if (st != KIT_OK) return st;
    910     st = link_reportf(out, " 0x%llx\n",
    911                       (unsigned long long)link_report_final_addr(img, es));
    912     if (st != KIT_OK) return st;
    913   } else {
    914     st = link_report_cstr(out, "entry -\n");
    915     if (st != KIT_OK) return st;
    916   }
    917 
    918   st = link_report_cstr(out, "\nsegments\n");
    919   if (st != KIT_OK) return st;
    920   /* The synthesized read-only headers load segment (ehdr+phdrs) maps at the
    921    * image base ahead of the content segments; the format emitter records it
    922    * so the map shows where the image actually loads (e.g. a -Ttext base). */
    923   if (img->headers_present) {
    924     st = link_reportf(out,
    925                       "  [0] r-- off=0x0 vaddr=0x%llx "
    926                       "filesz=0x%llx memsz=0x%llx align=0x%x\n",
    927                       (unsigned long long)img->load_base,
    928                       (unsigned long long)img->headers_filesz,
    929                       (unsigned long long)img->headers_memsz,
    930                       img->headers_align);
    931     if (st != KIT_OK) return st;
    932   }
    933   for (i = 0; i < img->nsegments; ++i) {
    934     const LinkSegment* seg = &img->segments[i];
    935     char w = (seg->flags & SF_WRITE) ? 'w' : '-';
    936     char x = (seg->flags & SF_EXEC) ? 'x' : '-';
    937     st = link_reportf(out,
    938                       "  [%u] r%c%c off=0x%llx vaddr=0x%llx "
    939                       "filesz=0x%llx memsz=0x%llx align=0x%x\n",
    940                       seg->id, w, x, (unsigned long long)seg->file_offset,
    941                       (unsigned long long)(img->load_base + seg->vaddr),
    942                       (unsigned long long)seg->file_size,
    943                       (unsigned long long)seg->mem_size, seg->align);
    944     if (st != KIT_OK) return st;
    945   }
    946 
    947   st = link_report_cstr(out, "\nsections\n");
    948   if (st != KIT_OK) return st;
    949   for (i = 0; i < img->nsections; ++i) {
    950     const LinkSection* sec = &img->sections[i];
    951     KitSlice name = link_report_sym_name(img, sec->name);
    952     KitSlice input = link_report_input_name(img, sec->input_id);
    953     int loaded = !(sec->file_only || sec->segment_id == LINK_SEG_NONE);
    954     /* Loaded sections report their final runtime address; file-only debug
    955      * sections aren't mapped (sh_addr 0), so leave their image-relative
    956      * bookkeeping vaddr untouched rather than biasing it by the load base. */
    957     u64 sec_vaddr = loaded ? img->load_base + sec->vaddr : sec->vaddr;
    958     /* LMA (load-memory address): where the section's initialized bytes load.
    959      * Equals the VMA for the common case; differs only when the section's
    960      * segment declares a distinct load region (a script `AT>` clause, which
    961      * sets the segment paddr apart from its vaddr). */
    962     u64 sec_lma = sec_vaddr;
    963     if (loaded && sec->segment_id <= img->nsegments) {
    964       const LinkSegment* seg = &img->segments[sec->segment_id - 1u];
    965       if (seg->paddr != seg->vaddr)
    966         sec_lma = img->load_base + seg->paddr + (sec->vaddr - seg->vaddr);
    967     }
    968     st = link_reportf(out,
    969                       "  [%u] ", sec->id);
    970     if (st != KIT_OK) return st;
    971     st = name.s ? link_report_slice(out, name) : link_report_cstr(out, "-");
    972     if (st != KIT_OK) return st;
    973     st = link_reportf(out,
    974                       " seg=%u off=0x%llx vaddr=0x%llx lma=0x%llx size=0x%llx "
    975                       "align=0x%x",
    976                       sec->segment_id, (unsigned long long)sec->file_offset,
    977                       (unsigned long long)sec_vaddr,
    978                       (unsigned long long)sec_lma,
    979                       (unsigned long long)sec->size, sec->align);
    980     if (st != KIT_OK) return st;
    981     if (input.s && input.len) {
    982       st = link_report_cstr(out, " input=");
    983       if (st != KIT_OK) return st;
    984       st = link_report_slice(out, input);
    985       if (st != KIT_OK) return st;
    986     }
    987     st = link_report_cstr(out, "\n");
    988     if (st != KIT_OK) return st;
    989   }
    990 
    991   st = link_report_write_discarded(img, out);
    992   if (st != KIT_OK) return st;
    993   st = link_report_write_unresolved(img, out);
    994   if (st != KIT_OK) return st;
    995 
    996   st = link_report_cstr(out, "\nsymbols\n");
    997   if (st != KIT_OK) return st;
    998   return link_report_write_symbols_nm(img, out);
    999 }
   1000 
   1001 /* --cref predicate: a symbol participates in the cross-reference table when it
   1002  * is a named non-FILE/non-SECTION global that is either locally defined OR
   1003  * imported from a DSO. Imported (DSO-resolved) externals are exactly the
   1004  * cross-references a cref table exists to show, so — unlike the nm-style
   1005  * "symbols" listing's link_report_symbol_include — they are NOT dropped. */
   1006 static int link_report_cref_include(const LinkSymbol* s) {
   1007   if (!s || !s->name) return 0;
   1008   if (s->kind == SK_FILE || s->kind == SK_SECTION) return 0;
   1009   return (s->defined && !s->imported) || s->imported;
   1010 }
   1011 
   1012 /* --cref: cross-reference table. For each defined-or-imported global/weak
   1013  * symbol (deduped by name, sorted by name for determinism) emit the defining
   1014  * file — the providing DSO basename for an imported symbol, the defining object
   1015  * otherwise — then the set of distinct input files that reference it via a
   1016  * relocation. Input names are normalized to basenames. The symbol set is built
   1017  * INDEPENDENTLY of the nm-style "symbols" listing so imports are included. */
   1018 static KitStatus link_report_write_cref_image(KitLinkSession* s,
   1019                                               KitWriter* out) {
   1020   LinkImage* img = s->image;
   1021   Heap* h;
   1022   LinkSymId* ids = NULL;
   1023   u32 n = 0, cap, i;
   1024   KitStatus st;
   1025   if (!img) return KIT_INVALID;
   1026   h = img->heap;
   1027   st = link_report_cstr(out, "cross reference table\n");
   1028   if (st != KIT_OK) return st;
   1029   cap = LinkSyms_count(&img->syms);
   1030   if (!cap) return kit_writer_status(out);
   1031   ids = (LinkSymId*)h->alloc(h, sizeof(*ids) * cap, _Alignof(LinkSymId));
   1032   if (!ids) return KIT_NOMEM;
   1033   /* Collect one canonical slot per qualifying name, deduped by Sym (the
   1034    * interned name handle): a name's first qualifying slot wins. Prefer a
   1035    * locally-defined slot over an imported one when both exist (so a symbol that
   1036    * is both defined and referenced through a DSO reports its real def). */
   1037   for (i = 0; i < cap; ++i) {
   1038     const LinkSymbol* sy = LinkSyms_at(&img->syms, i);
   1039     u32 k;
   1040     int seen = 0;
   1041     if (!link_report_cref_include(sy)) continue;
   1042     for (k = 0; k < n; ++k) {
   1043       LinkSymbol* prev = LinkSyms_at(&img->syms, ids[k] - 1u);
   1044       if (prev->name != sy->name) continue;
   1045       seen = 1;
   1046       /* Upgrade a previously-recorded imported slot to a real definition. */
   1047       if (sy->defined && !sy->imported && (!prev->defined || prev->imported))
   1048         ids[k] = sy->id;
   1049       break;
   1050     }
   1051     if (!seen) ids[n++] = sy->id;
   1052   }
   1053   /* Insertion sort by name (small lists; deterministic). */
   1054   for (i = 1; i < n; ++i) {
   1055     LinkSymId key = ids[i];
   1056     KitSlice kn =
   1057         link_report_sym_name(img, LinkSyms_at(&img->syms, key - 1u)->name);
   1058     u32 j = i;
   1059     while (j > 0) {
   1060       KitSlice pn = link_report_sym_name(
   1061           img, LinkSyms_at(&img->syms, ids[j - 1u] - 1u)->name);
   1062       if (link_report_slice_cmp(pn, kn) <= 0) break;
   1063       ids[j] = ids[j - 1u];
   1064       --j;
   1065     }
   1066     ids[j] = key;
   1067   }
   1068   for (i = 0; i < n; ++i) {
   1069     const LinkSymbol* sym = LinkSyms_at(&img->syms, ids[i] - 1u);
   1070     KitSlice name = link_report_sym_name(img, sym->name);
   1071     KitSlice deffile;
   1072     LinkInputId defining_input = LINK_INPUT_NONE;
   1073     u32 r, k;
   1074     LinkInputId last = LINK_INPUT_NONE;
   1075     if (sym->imported) {
   1076       /* Imported symbol: the "definition" is the providing DSO input. */
   1077       defining_input = sym->dso_input_id;
   1078     } else {
   1079       /* Find the defining file: the canonical global slot (img->globals) is the
   1080        * real definition; resolution copies its value into per-input reference
   1081        * slots, so scanning slots by `defined` alone would mis-attribute. Fall
   1082        * back to any defined same-name slot for locals (never in globals). */
   1083       LinkSymId canon = symhash_get(&img->globals, sym->name);
   1084       if (canon != LINK_SYM_NONE) {
   1085         const LinkSymbol* c = LinkSyms_at(&img->syms, canon - 1u);
   1086         if (c->defined && !c->imported) defining_input = c->input_id;
   1087       }
   1088       if (defining_input == LINK_INPUT_NONE) {
   1089         for (k = 0; k < cap; ++k) {
   1090           const LinkSymbol* c = LinkSyms_at(&img->syms, k);
   1091           if (c->name != sym->name) continue;
   1092           if (c->defined && !c->imported && c->input_id != LINK_INPUT_NONE) {
   1093             defining_input = c->input_id;
   1094             break;
   1095           }
   1096         }
   1097       }
   1098     }
   1099     deffile = link_report_input_name(img, defining_input);
   1100     st = link_report_slice(out, name);
   1101     if (st == KIT_OK) st = link_report_cstr(out, "\n");
   1102     if (st != KIT_OK) break;
   1103     /* Defining file. */
   1104     st = link_report_cstr(out, "  def ");
   1105     if (st == KIT_OK)
   1106       st = (deffile.s && deffile.len) ? link_report_slice(out, deffile)
   1107                                       : link_report_cstr(out, "-");
   1108     if (st == KIT_OK) st = link_report_cstr(out, "\n");
   1109     if (st != KIT_OK) break;
   1110     /* Referencing files: scan relocs targeting any same-name slot. The reloc
   1111      * list is already in a deterministic input/section/offset order, so
   1112      * consecutive dedup of input ids gives a stable per-symbol reference set. */
   1113     for (r = 0; r < LinkRelocs_count(&img->relocs); ++r) {
   1114       const LinkRelocApply* ra = LinkRelocs_at(&img->relocs, r);
   1115       const LinkSymbol* tsym;
   1116       KitSlice reffile;
   1117       if (!ra || ra->target == LINK_SYM_NONE) continue;
   1118       tsym = LinkSyms_at(&img->syms, ra->target - 1u);
   1119       if (!tsym || tsym->name != sym->name) continue;
   1120       if (ra->input_id == last) continue;
   1121       last = ra->input_id;
   1122       reffile = link_report_input_name(img, ra->input_id);
   1123       st = link_report_cstr(out, "  ref ");
   1124       if (st == KIT_OK)
   1125         st = (reffile.s && reffile.len) ? link_report_slice(out, reffile)
   1126                                         : link_report_cstr(out, "-");
   1127       if (st == KIT_OK) st = link_report_cstr(out, "\n");
   1128       if (st != KIT_OK) break;
   1129     }
   1130     if (st != KIT_OK) break;
   1131   }
   1132   h->free(h, ids, sizeof(*ids) * cap);
   1133   return st == KIT_OK ? kit_writer_status(out) : st;
   1134 }
   1135 
   1136 /* --print-memory-usage: GNU-ld-style per-MEMORY-region used/free/total. The
   1137  * regions come from the linker script; usage is the total size of placed
   1138  * sections (and BSS zero-fill) whose runtime address falls inside a region's
   1139  * [origin, origin+length) window. */
   1140 static KitStatus link_report_write_memory_usage_image(KitLinkSession* s,
   1141                                                       KitWriter* out) {
   1142   LinkImage* img = s->image;
   1143   const KitLinkScript* script = s->opts.linker_script;
   1144   KitStatus st;
   1145   u32 ri;
   1146   if (!img) return KIT_INVALID;
   1147   st = link_report_cstr(out, "Memory region         Used Size  Region Size  "
   1148                              "%age Used\n");
   1149   if (st != KIT_OK) return st;
   1150   if (!script || script->nregions == 0) return kit_writer_status(out);
   1151   for (ri = 0; ri < script->nregions; ++ri) {
   1152     const KitLinkRegion* rg = &script->regions[ri];
   1153     u64 used = 0;
   1154     u64 free_bytes;
   1155     unsigned pct_int = 0, pct_frac = 0;
   1156     u32 j;
   1157     for (j = 0; j < img->nsegments; ++j) {
   1158       const LinkSegment* seg = &img->segments[j];
   1159       u64 base = img->load_base + seg->vaddr;
   1160       /* Alias / program-header-only segments (nsections==0) describe the SAME
   1161        * bytes as a primary segment under an additional :phdr; charging them
   1162        * would double-count a section placed into multiple PT_LOADs. Real
   1163        * primaries (incl. BSS/COMMON zero-fill) carry nsections>=1, so genuine
   1164        * mem_size stays counted exactly once. */
   1165       if (seg->nsections == 0) continue;
   1166       /* A segment belongs to a region when its load address sits within the
   1167        * [origin, origin+length) window. Count memsz (covers BSS). The end of
   1168        * the window is computed overflow-safe via a subtract — a region that
   1169        * touches the top of the address space (origin+length wraps to 0) would
   1170        * otherwise mis-test `base < 0` and attribute nothing. */
   1171       if (base >= rg->origin &&
   1172           (rg->length == 0 ? 0 : base - rg->origin < rg->length))
   1173         used += seg->mem_size;
   1174     }
   1175     free_bytes = (rg->length > used) ? rg->length - used : 0;
   1176     if (rg->length) {
   1177       /* Two-decimal percentage without floating point: used*10000/length. */
   1178       u64 scaled = (used * 10000ull) / rg->length;
   1179       pct_int = (unsigned)(scaled / 100ull);
   1180       pct_frac = (unsigned)(scaled % 100ull);
   1181     }
   1182     st = link_report_slice(out,
   1183                            rg->name.s ? rg->name : KIT_SLICE_LIT("<unnamed>"));
   1184     if (st == KIT_OK)
   1185       st = link_reportf(out, "  used=0x%llx free=0x%llx total=0x%llx %u.%02u%%\n",
   1186                         (unsigned long long)used,
   1187                         (unsigned long long)free_bytes,
   1188                         (unsigned long long)rg->length, pct_int, pct_frac);
   1189     if (st != KIT_OK) return st;
   1190   }
   1191   return kit_writer_status(out);
   1192 }
   1193 
   1194 typedef struct LinkReportArg {
   1195   KitWriter* out;
   1196   uint8_t format;
   1197   KitStatus st;
   1198 } LinkReportArg;
   1199 
   1200 static void link_session_map_inner(KitLinkSession* s, void* arg) {
   1201   LinkReportArg* a = (LinkReportArg*)arg;
   1202   a->st = link_report_write_map_image(s, a->out);
   1203 }
   1204 
   1205 static void link_session_symbols_inner(KitLinkSession* s, void* arg) {
   1206   LinkReportArg* a = (LinkReportArg*)arg;
   1207   if (a->format != KIT_LINK_SYMBOLS_NM) {
   1208     a->st = KIT_UNSUPPORTED;
   1209     return;
   1210   }
   1211   a->st = link_report_write_symbols_nm(s->image, a->out);
   1212 }
   1213 
   1214 KitStatus kit_link_session_write_map(KitLinkSession* s, KitWriter* out) {
   1215   LinkReportArg arg;
   1216   KitStatus st;
   1217   if (!s || !out) return KIT_INVALID;
   1218   if ((KitLinkOutputKind)s->opts.output_kind == KIT_LINK_OUTPUT_RELOCATABLE ||
   1219       (KitLinkOutputKind)s->opts.output_kind == KIT_LINK_OUTPUT_JIT)
   1220     return KIT_INVALID;
   1221   if (!s->resolved) {
   1222     st = kit_link_session_resolve(s);
   1223     if (st != KIT_OK) return st;
   1224   }
   1225   arg.out = out;
   1226   arg.format = KIT_LINK_SYMBOLS_NM;
   1227   arg.st = KIT_OK;
   1228   st = link_session_guard(s, link_session_map_inner, &arg);
   1229   if (st != KIT_OK) return st;
   1230   return arg.st == KIT_OK ? kit_writer_status(out) : arg.st;
   1231 }
   1232 
   1233 KitStatus kit_link_session_write_symbols(KitLinkSession* s, uint8_t format,
   1234                                          KitWriter* out) {
   1235   LinkReportArg arg;
   1236   KitStatus st;
   1237   if (!s || !out) return KIT_INVALID;
   1238   if ((KitLinkOutputKind)s->opts.output_kind == KIT_LINK_OUTPUT_RELOCATABLE ||
   1239       (KitLinkOutputKind)s->opts.output_kind == KIT_LINK_OUTPUT_JIT)
   1240     return KIT_INVALID;
   1241   if (!s->resolved) {
   1242     st = kit_link_session_resolve(s);
   1243     if (st != KIT_OK) return st;
   1244   }
   1245   arg.out = out;
   1246   arg.format = format;
   1247   arg.st = KIT_OK;
   1248   st = link_session_guard(s, link_session_symbols_inner, &arg);
   1249   if (st != KIT_OK) return st;
   1250   return arg.st == KIT_OK ? kit_writer_status(out) : arg.st;
   1251 }
   1252 
   1253 static void link_session_cref_inner(KitLinkSession* s, void* arg) {
   1254   LinkReportArg* a = (LinkReportArg*)arg;
   1255   a->st = link_report_write_cref_image(s, a->out);
   1256 }
   1257 
   1258 static void link_session_memusage_inner(KitLinkSession* s, void* arg) {
   1259   LinkReportArg* a = (LinkReportArg*)arg;
   1260   a->st = link_report_write_memory_usage_image(s, a->out);
   1261 }
   1262 
   1263 KitStatus kit_link_session_write_cref(KitLinkSession* s, KitWriter* out) {
   1264   LinkReportArg arg;
   1265   KitStatus st;
   1266   if (!s || !out) return KIT_INVALID;
   1267   if ((KitLinkOutputKind)s->opts.output_kind == KIT_LINK_OUTPUT_RELOCATABLE ||
   1268       (KitLinkOutputKind)s->opts.output_kind == KIT_LINK_OUTPUT_JIT)
   1269     return KIT_INVALID;
   1270   if (!s->resolved) {
   1271     st = kit_link_session_resolve(s);
   1272     if (st != KIT_OK) return st;
   1273   }
   1274   arg.out = out;
   1275   arg.format = KIT_LINK_SYMBOLS_NM;
   1276   arg.st = KIT_OK;
   1277   st = link_session_guard(s, link_session_cref_inner, &arg);
   1278   if (st != KIT_OK) return st;
   1279   return arg.st == KIT_OK ? kit_writer_status(out) : arg.st;
   1280 }
   1281 
   1282 KitStatus kit_link_session_write_memory_usage(KitLinkSession* s,
   1283                                               KitWriter* out) {
   1284   LinkReportArg arg;
   1285   KitStatus st;
   1286   if (!s || !out) return KIT_INVALID;
   1287   if ((KitLinkOutputKind)s->opts.output_kind == KIT_LINK_OUTPUT_RELOCATABLE ||
   1288       (KitLinkOutputKind)s->opts.output_kind == KIT_LINK_OUTPUT_JIT)
   1289     return KIT_INVALID;
   1290   if (!s->resolved) {
   1291     st = kit_link_session_resolve(s);
   1292     if (st != KIT_OK) return st;
   1293   }
   1294   arg.out = out;
   1295   arg.format = KIT_LINK_SYMBOLS_NM;
   1296   arg.st = KIT_OK;
   1297   st = link_session_guard(s, link_session_memusage_inner, &arg);
   1298   if (st != KIT_OK) return st;
   1299   return arg.st == KIT_OK ? kit_writer_status(out) : arg.st;
   1300 }
   1301 
   1302 typedef struct LinkEmitArg {
   1303   KitWriter* out;
   1304 } LinkEmitArg;
   1305 
   1306 static void link_session_emit_inner(KitLinkSession* s, void* arg) {
   1307   KitWriter* out = ((LinkEmitArg*)arg)->out;
   1308   if ((KitLinkOutputKind)s->opts.output_kind == KIT_LINK_OUTPUT_RELOCATABLE) {
   1309     link_emit_relocatable_writer(s->linker, out);
   1310   } else {
   1311     link_emit_image_writer(s->image, out);
   1312   }
   1313 }
   1314 
   1315 KitStatus kit_link_session_emit(KitLinkSession* s, KitWriter* out) {
   1316   LinkEmitArg arg;
   1317   KitStatus st;
   1318   if (!s || !out) return KIT_INVALID;
   1319   if (!s->resolved) {
   1320     st = kit_link_session_resolve(s);
   1321     if (st != KIT_OK) return st;
   1322   }
   1323   if ((KitLinkOutputKind)s->opts.output_kind == KIT_LINK_OUTPUT_JIT)
   1324     return KIT_INVALID;
   1325   arg.out = out;
   1326   return link_session_guard(s, link_session_emit_inner, &arg);
   1327 }
   1328 
   1329 typedef struct LinkJitArg {
   1330   KitJit* jit;
   1331 } LinkJitArg;
   1332 
   1333 static void link_session_jit_inner(KitLinkSession* s, void* arg) {
   1334   LinkJitArg* a = (LinkJitArg*)arg;
   1335   a->jit = kit_jit_from_image(s->image);
   1336   if (a->jit) {
   1337     s->image = NULL;
   1338     s->linker_transferred = 1;
   1339     s->linker = NULL;
   1340   }
   1341 }
   1342 
   1343 KitStatus kit_link_session_jit(KitLinkSession* s, KitJit** out_jit) {
   1344   LinkJitArg arg;
   1345   KitStatus st;
   1346   if (!out_jit) return KIT_INVALID;
   1347   *out_jit = NULL;
   1348   if (!s || (KitLinkOutputKind)s->opts.output_kind != KIT_LINK_OUTPUT_JIT)
   1349     return KIT_INVALID;
   1350   if (!s->resolved) {
   1351     st = kit_link_session_resolve(s);
   1352     if (st != KIT_OK) return st;
   1353   }
   1354   arg.jit = NULL;
   1355   st = link_session_guard(s, link_session_jit_inner, &arg);
   1356   if (st != KIT_OK) return st;
   1357   if (!arg.jit) return KIT_ERR;
   1358   *out_jit = arg.jit;
   1359   return KIT_OK;
   1360 }
   1361 
   1362 void kit_link_session_free(KitLinkSession* s) {
   1363   Heap* h;
   1364   if (!s) return;
   1365   h = s->c->ctx->heap;
   1366   if (s->image) link_image_free(s->image);
   1367   if (s->linker) link_free(s->linker);
   1368   if (s->publish_objs) {
   1369     h->free(h, s->publish_objs, sizeof(*s->publish_objs) * s->publish_objs_cap);
   1370   }
   1371   h->free(h, s, sizeof(*s));
   1372 }