kit

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

link_jit.c (64244B)


      1 /* JIT mapper. Takes a resolved LinkImage, reserves a fresh writable
      2  * region (via env->execmem), copies segments, applies relocations
      3  * against the runtime base, flips to final permissions, and returns
      4  * an owning KitJit handle.
      5  *
      6  * Lookup is by interned Sym name (object-local handles never escape).
      7  * Inspector entries (kit_jit_view, _addr_to_sym, _sym_iter_*) are
      8  * stubbed for now — the linker can land without dragging the inspector
      9  * surface up. */
     10 
     11 #include <kit/core.h>
     12 #include <kit/jit.h>
     13 #include <kit/object.h>
     14 #include <string.h>
     15 
     16 #include "core/buf.h"
     17 #include "core/bytes.h"
     18 #include "core/heap.h"
     19 #include "core/metrics.h"
     20 #include "core/pool.h"
     21 #include "core/slice.h"
     22 #include "core/util.h"
     23 #include "link/link.h"
     24 #include "link/link_arch.h"
     25 #include "link/link_internal.h"
     26 #include "link/link_reloc_desc.h"
     27 #include "obj/obj.h"
     28 
     29 /* Defined in src/api/objfile.c — exposes the underlying ObjBuilder of a
     30  * KitObjFile and the internal-only helpers for allocating an empty
     31  * KitObjFile (used by the JIT debug-view builder). */
     32 ObjBuilder* kit_objfile_builder(const KitObjFile*);
     33 KitObjFile* kit_objfile_internal_new(const KitContext* ctx,
     34                                      KitTargetSpec target, KitObjFmt fmt);
     35 void kit_objfile_internal_free(KitObjFile*);
     36 #define jit_view_objfile_free(f) kit_objfile_internal_free(f)
     37 
     38 static const KitJitHost* jit_host_from_linker(Linker* l, Compiler* c) {
     39   const KitJitHost* host = l ? l->jit_host : NULL;
     40   if (!host)
     41     compiler_panic(c, SRCLOC_NONE,
     42                    "kit_jit: link jit host is required for JIT");
     43   return host;
     44 }
     45 
     46 static const KitExecMem* require_execmem_h(const KitJitHost* host,
     47                                            Compiler* c) {
     48   const KitExecMem* m = host ? host->execmem : NULL;
     49   if (!m || !m->reserve || !m->protect || !m->release) {
     50     compiler_panic(c, SRCLOC_NONE,
     51                    "kit_jit: jit host execmem is required for JIT");
     52   }
     53   return m;
     54 }
     55 
     56 static u64 jit_page_size_h(const KitJitHost* host, Compiler* c) {
     57   const KitExecMem* m = require_execmem_h(host, c);
     58   return m->page_size ? (u64)m->page_size : 0x4000u;
     59 }
     60 
     61 /* Per-segment runtime placement. Each segment gets its own host
     62  * reservation. Code segments come back as a dual mapping (write alias /
     63  * runtime alias backing the same physical pages); data and rodata are
     64  * single-aliased. We populate via the write alias, encode runtime
     65  * addresses against the runtime alias, then protect the runtime alias to
     66  * its final perms. No virtual page is ever simultaneously writable and
     67  * executable. */
     68 struct KitJit {
     69   Compiler* c;
     70   LinkImage* image;
     71   /* Single contiguous reservation covering every segment.  All segments
     72    * are sub-ranges of this region — runtime/write aliases are derived
     73    * by offsetting against (image-vaddr - image_base).  Keeping them
     74    * inside one mapping guarantees inter-segment displacements stay in
     75    * range for ADRP (±4 GiB) and CALL26 (±128 MiB), which would
     76    * otherwise depend on the OS placing independent mmap'd segments
     77    * close together. */
     78   KitExecMemRegion master;
     79   u64 image_base;         /* page-aligned image vaddr that maps to master.* */
     80   KitExecMemRegion* segs; /* one per image->nsegments; views into master */
     81   u32 nsegs;
     82   /* DWARF view, lazily constructed on first kit_jit_view call.  Built
     83    * over a private Compiler so its string pools and the new ObjBuilder
     84    * are owned end-to-end by the view; freed in kit_jit_free.  NULL
     85    * means "not yet built"; view_built distinguishes "tried and gave up"
     86    * (multi-input v1, etc.) from "untried". */
     87   KitObjFile* view;
     88   Linker* linker; /* kept alive for append-time resolver/input context */
     89   u64 append_cursor[SEG_NBUCKETS];
     90   u64 append_limit[SEG_NBUCKETS];
     91   u64 generation;
     92   /* Borrowed JIT host: execmem vtable.  Mirrors the Linker's
     93    * jit_host so the JIT's lifetime accessors don't need to walk back to
     94    * the Linker (which is still live behind jit->linker but may be
     95    * decoupled in incremental flows). */
     96   const KitJitHost* jit_host;
     97   u8 view_built;
     98   u8 pad[7];
     99 };
    100 
    101 #define JIT_APPEND_RX_SLACK (64ull * 1024ull * 1024ull)
    102 #define JIT_APPEND_R_SLACK (16ull * 1024ull * 1024ull)
    103 #define JIT_APPEND_RW_SLACK (16ull * 1024ull * 1024ull)
    104 #define JIT_APPEND_TLS_SLACK (4ull * 1024ull * 1024ull)
    105 
    106 /* RISC-V PCREL_LO12_I/S target a local "anchor" symbol whose vaddr is
    107  * the address of the paired AUIPC's PCREL_HI20 (or GOT_HI20) site.
    108  * Defined below vaddr_to_runtime. */
    109 static i64 jit_rv_pcrel_lo12_disp(LinkImage* img, KitExecMemRegion* segs,
    110                                   u64 auipc_image_vaddr);
    111 
    112 static int perms_for(u32 secflags) {
    113   int p = KIT_PROT_READ;
    114   if (secflags & SF_EXEC) p |= KIT_PROT_EXEC;
    115   if (secflags & SF_WRITE) p |= KIT_PROT_WRITE;
    116   /* JIT TLS storage is the single live instance (the JIT is single-threaded),
    117    * accessed and mutated in place, so the TLS segment must be writable — even
    118    * though the AOT image treats .tdata/.tbss as a read-only init template that
    119    * each thread copies. */
    120   if (secflags & SF_TLS) p |= KIT_PROT_WRITE;
    121   return p;
    122 }
    123 
    124 /* A reference the single-threaded JIT drops because the format's TLS access
    125  * idiom that materializes its target is relaxed in-image. On Windows/COFF the
    126  * idiom reads TEB.ThreadLocalStoragePointer, indexes it by the module's
    127  * `_tls_index`, then `+ SECREL(var)` — there is no loaded PE for the OS to
    128  * assign a `_tls_index`, so jit_tls_le_relax rewrites that load away and the
    129  * `_tls_index` ADRP/PC32 relocs must not be applied. The format authority
    130  * (obj_format_jit_drops_symbol_ref) owns which symbol that is. */
    131 static int jit_tls_is_index_ref(Compiler* c, const LinkSymbol* tgt) {
    132   return tgt && obj_format_jit_drops_symbol_ref(c, tgt->name);
    133 }
    134 
    135 /* TLS Local-Exec classifier for the JIT reloc passes, entirely flag-driven:
    136  * the per-arch tp-relative idiom and the aa64 COFF SECREL12A pair carry
    137  * RELOC_IS_TLS_LE; x86-64's generic COFF SECREL (RELOC_IS_SECREL) is a TLS
    138  * access exactly when its target is a TLS symbol. */
    139 static int jit_reloc_is_tls_le(Compiler* c, RelocKind k, u8 tgt_kind) {
    140   return reloc_kind_is_tls_le(c, k) ||
    141          (reloc_kind_is_secrel(c, k) && tgt_kind == SK_TLS);
    142 }
    143 
    144 /* Find the segment that contains image-relative `vaddr` and return its
    145  * runtime address (the runtime alias, not the write alias). Up to 3
    146  * segments after layout, so a linear scan is fine.
    147  *
    148  * The two-pass shape lets a vaddr that lands exactly on a segment's
    149  * one-past-end boundary (e.g. `__fini_array_end` when .fini_array is
    150  * the last section in its segment) still resolve, while preferring an
    151  * exact start-of-next-segment match when segments happen to abut. */
    152 static uintptr_t vaddr_to_runtime(const LinkImage* img,
    153                                   const KitExecMemRegion* segs, u64 vaddr) {
    154   u32 i;
    155   for (i = 0; i < img->nsegments; ++i) {
    156     const LinkSegment* s = &img->segments[i];
    157     u64 lo = s->vaddr;
    158     u64 hi = lo + s->mem_size;
    159     if (vaddr >= lo && vaddr < hi)
    160       return (uintptr_t)segs[i].runtime + (uintptr_t)(vaddr - lo);
    161   }
    162   for (i = 0; i < img->nsegments; ++i) {
    163     const LinkSegment* s = &img->segments[i];
    164     u64 hi = s->vaddr + s->mem_size;
    165     if (vaddr == hi) return (uintptr_t)segs[i].runtime + (uintptr_t)s->mem_size;
    166   }
    167   return 0;
    168 }
    169 
    170 /* Same as above but returns the WRITE-alias address — used to determine
    171  * the byte position at which to apply a relocation. The PC-relative
    172  * arithmetic in link_reloc_apply uses the runtime address; the bytes
    173  * themselves get patched at the matching offset of the write alias. */
    174 static uintptr_t vaddr_to_write(const LinkImage* img,
    175                                 const KitExecMemRegion* segs, u64 vaddr) {
    176   u32 i;
    177   for (i = 0; i < img->nsegments; ++i) {
    178     const LinkSegment* s = &img->segments[i];
    179     u64 lo = s->vaddr;
    180     u64 hi = lo + s->mem_size;
    181     if (vaddr >= lo && vaddr < hi)
    182       return (uintptr_t)segs[i].write + (uintptr_t)(vaddr - lo);
    183   }
    184   for (i = 0; i < img->nsegments; ++i) {
    185     const LinkSegment* s = &img->segments[i];
    186     u64 hi = s->vaddr + s->mem_size;
    187     if (vaddr == hi) return (uintptr_t)segs[i].write + (uintptr_t)s->mem_size;
    188   }
    189   return 0;
    190 }
    191 
    192 /* See forward decl above.  Find the paired AUIPC PCREL_HI20/GOT_HI20
    193  * reloc whose write_vaddr matches the anchor target, recompute the
    194  * displacement using runtime addresses, and return it so the
    195  * link_reloc_apply LO12_I/S encoder produces matching low-12 bits.
    196  *
    197  * Linear scan; reloc counts are small even for full JIT images. */
    198 static i64 jit_rv_pcrel_lo12_disp(LinkImage* img, KitExecMemRegion* segs,
    199                                   u64 auipc_image_vaddr) {
    200   u32 n = LinkRelocs_count(&img->relocs);
    201   u32 i;
    202   for (i = 0; i < n; ++i) {
    203     const LinkRelocApply* hi = LinkRelocs_at(&img->relocs, i);
    204     const LinkSymbol* hi_tgt;
    205     u64 hi_S, hi_P;
    206     if (!reloc_kind_is_pcrel_anchor(img->c, hi->kind)) continue;
    207     if (hi->write_vaddr != auipc_image_vaddr) continue;
    208     hi_tgt = LinkSyms_at(&img->syms, hi->target - 1);
    209     if (!hi_tgt) continue;
    210     if (hi_tgt->kind == SK_ABS)
    211       hi_S = hi_tgt->vaddr;
    212     else
    213       hi_S = (u64)vaddr_to_runtime(img, segs, hi_tgt->vaddr);
    214     hi_P = (u64)vaddr_to_runtime(img, segs, hi->write_vaddr);
    215     return (i64)hi_S + hi->addend - (i64)hi_P;
    216   }
    217   compiler_panic(img->c, SRCLOC_NONE,
    218                  "kit_jit: RV PCREL_LO12 at 0x%llx has no paired PCREL_HI20",
    219                  (unsigned long long)auipc_image_vaddr);
    220   return 0;
    221 }
    222 
    223 static void jit_copy_input_section_bytes(LinkImage* img,
    224                                          const KitExecMemRegion* segs) {
    225   Compiler* c = img->c;
    226   Linker* l = img->linker;
    227   u32 i;
    228   for (i = 0; i < img->nsections; ++i) {
    229     const LinkSection* ls = &img->sections[i];
    230     const LinkSegment* seg;
    231     ObjBuilder* ob;
    232     const Section* s;
    233     u32 input_idx;
    234     u8* dst;
    235     if (ls->input_id == LINK_INPUT_NONE) continue;
    236     input_idx = ls->input_id - 1u;
    237     if (ls->segment_id == LINK_SEG_NONE || ls->segment_id > img->nsegments)
    238       compiler_panic(c, SRCLOC_NONE,
    239                      "kit_jit_from_image: section has invalid segment");
    240     seg = &img->segments[ls->segment_id - 1u];
    241     ob = (input_idx < img->dbg_objs_n) ? img->dbg_objs[input_idx] : NULL;
    242     if (!ob && l && input_idx < LinkInputs_count(&l->inputs))
    243       ob = LinkInputs_at(&l->inputs, input_idx)->obj;
    244     if (!ob)
    245       compiler_panic(c, SRCLOC_NONE,
    246                      "kit_jit_from_image: input section bytes unavailable");
    247     s = obj_section_get(ob, ls->obj_section_id);
    248     if (!s || s->sem == SSEM_NOBITS || s->kind == SEC_BSS) continue;
    249     if (ls->size == 0) continue;
    250     dst = (u8*)segs[seg->id - 1u].write + (size_t)(ls->vaddr - seg->vaddr);
    251     metrics_count(c, "jit.input_section_bytes", ls->size);
    252     buf_read(&s->bytes, (u32)ls->obj_offset, dst, (size_t)ls->size);
    253   }
    254 }
    255 
    256 /* Opaque user data for the JitRelaxCtx.resolve_pair callback: the arch hook
    257  * (RISC-V PCREL_LO12) gets the paired AUIPC's runtime displacement without
    258  * reaching into LinkImage itself. */
    259 typedef struct {
    260   LinkImage* img;
    261   KitExecMemRegion* segs;
    262 } JitPairResolve;
    263 
    264 static i64 jit_resolve_pcrel_pair(void* user, u64 anchor_vaddr) {
    265   JitPairResolve* p = (JitPairResolve*)user;
    266   return jit_rv_pcrel_lo12_disp(p->img, p->segs, anchor_vaddr);
    267 }
    268 
    269 /* Apply one relocation against the JIT runtime base.  Shared by the bulk
    270  * from-image pass and the incremental-append pass so their reloc handling can
    271  * never drift.  Patch-site bytes go through the write alias; PC-relative
    272  * arithmetic uses the runtime alias.
    273  *
    274  * Order mirrors the resolution chain: drop Windows `_tls_index` refs, relax
    275  * Local-Exec TLS via the arch idiom hook, then resolve S and offer the reloc
    276  * to the arch's JIT-relaxation hook (TLV / GOT / weak-undef / RISC-V pcrel
    277  * pairing) before the ordinary apply.  `got_relaxed` is 1 on the append path
    278  * (no real GOT was built) and 0 for the full link (where .got slots exist). */
    279 static void jit_apply_reloc(Compiler* c, LinkImage* img, KitExecMemRegion* segs,
    280                             const LinkRelocApply* r, int got_relaxed) {
    281   const LinkSymbol* tgt = LinkSyms_at(&img->syms, r->target - 1);
    282   const LinkArchDesc* d;
    283   JitPairResolve pair;
    284   JitRelaxCtx ctx;
    285   u64 S, P;
    286   u8* P_bytes;
    287 
    288   /* Windows TLS idiom `_tls_index` references are dropped: the access is
    289    * relaxed to in-image addressing, so the index load is rewritten away. */
    290   if (jit_tls_is_index_ref(c, tgt)) return;
    291 
    292   /* Local-Exec TLS -> in-image addressing (single-threaded JIT).  The per-arch
    293    * idiom rewrite lives behind LinkArchDesc.jit_tls_le_relax; this stays
    294    * arch-neutral, classifying ELF via RELOC_IS_TLS_LE and the COFF Windows
    295    * idiom via its terminal SECREL reloc(s). */
    296   if (jit_reloc_is_tls_le(c, r->kind, tgt->kind)) {
    297     u64 storage = (tgt->kind == SK_ABS)
    298                       ? tgt->vaddr + (u64)r->addend
    299                       : (u64)vaddr_to_runtime(img, segs, tgt->vaddr) +
    300                             (u64)r->addend;
    301     u8* bytes = (u8*)vaddr_to_write(img, segs, r->write_vaddr);
    302     u64 site_pc = (u64)vaddr_to_runtime(img, segs, r->write_vaddr);
    303     d = link_arch_desc_for(c);
    304     if (!d || !d->jit_tls_le_relax || !bytes)
    305       compiler_panic(c, SRCLOC_NONE,
    306                      "kit_jit: target has no TLS Local-Exec relaxation");
    307     d->jit_tls_le_relax(c, r->kind, bytes, storage, site_pc);
    308     return;
    309   }
    310 
    311   /* extern resolver result OR true absolute symbol — vaddr already holds the
    312    * runtime address; otherwise map the image vaddr to its runtime alias. */
    313   S = (tgt->kind == SK_ABS) ? tgt->vaddr
    314                             : (u64)vaddr_to_runtime(img, segs, tgt->vaddr);
    315   P = (u64)vaddr_to_runtime(img, segs, r->write_vaddr);
    316   P_bytes = (u8*)vaddr_to_write(img, segs, r->write_vaddr);
    317   if (!P_bytes)
    318     compiler_panic(c, SRCLOC_NONE, "kit_jit: relocation site is unmapped");
    319 
    320   /* Arch JIT relaxation: collapse the runtime indirection (Mach-O TLV
    321    * descriptor call, GOT load on the append path, weak-undef zeroing, RISC-V
    322    * PCREL_LO12 anchor recompute) to direct in-image addressing.  Returns 1 if
    323    * it owned the kind; otherwise fall through to the ordinary apply. */
    324   pair.img = img;
    325   pair.segs = segs;
    326   ctx.site = P_bytes;
    327   ctx.S = S;
    328   ctx.addend = r->addend;
    329   ctx.site_pc = P;
    330   ctx.weak_undef_zero =
    331       (tgt->bind == SB_WEAK && tgt->kind == SK_ABS && tgt->vaddr == 0);
    332   ctx.got_relaxed = got_relaxed;
    333   ctx.resolve_pair = jit_resolve_pcrel_pair;
    334   ctx.resolve_user = &pair;
    335   ctx.anchor_vaddr = tgt->vaddr;
    336   d = link_arch_desc_for(c);
    337   if (d && d->jit_reloc_relax && d->jit_reloc_relax(c, r->kind, &ctx)) return;
    338 
    339   link_reloc_apply(c, r->kind, P_bytes, S, r->addend, P);
    340 }
    341 
    342 KitJit* kit_jit_from_image(LinkImage* img) {
    343   Compiler* c;
    344   Heap* heap;
    345   const KitExecMem* mem;
    346   const KitJitHost* host;
    347   KitJit* jit;
    348   KitExecMemRegion* segs;
    349   KitExecMemRegion master;
    350   u64 page;
    351   u64 image_base = (u64)-1;
    352   u64 image_end = 0;
    353   u64 append_start;
    354   u64 append_total;
    355   u64 master_size;
    356   int needs_exec = 0;
    357   int needs_input_materialize = 0;
    358   u32 i;
    359 
    360   if (!img) return NULL;
    361   c = img->c;
    362   heap = img->heap;
    363   host = jit_host_from_linker(img->linker, c);
    364   mem = require_execmem_h(host, c);
    365   page = jit_page_size_h(host, c);
    366 
    367   /* Compute the span all segments must fit inside.  Layout guarantees
    368    * each segment's vaddr is page-aligned (layout_segments / layout_got
    369    * align via ALIGN_UP at the page size), so the offset within the
    370    * master mapping is (vaddr - image_base).  An empty JIT image has no
    371    * initial segments, but still owns append slack so the debugger can
    372    * start from an empty translation unit and append code later. */
    373   if (img->nsegments == 0) {
    374     image_base = page;
    375     image_end = page;
    376     needs_exec = 1;
    377   } else {
    378     for (i = 0; i < img->nsegments; ++i) {
    379       const LinkSegment* seg = &img->segments[i];
    380       u64 hi = ALIGN_UP(seg->vaddr + seg->mem_size, page);
    381       if (seg->vaddr < image_base) image_base = seg->vaddr;
    382       if (hi > image_end) image_end = hi;
    383       if (seg->flags & SF_EXEC) needs_exec = 1;
    384     }
    385   }
    386   if (image_base & (page - 1u))
    387     compiler_panic(c, SRCLOC_NONE,
    388                    "kit_jit_from_image: segment vaddr not page-aligned");
    389   append_start = image_end;
    390   append_total = JIT_APPEND_RX_SLACK + JIT_APPEND_R_SLACK +
    391                  JIT_APPEND_RW_SLACK + JIT_APPEND_TLS_SLACK;
    392   master_size = (image_end - image_base) + append_total;
    393   metrics_count(c, "jit.master_size", master_size);
    394   metrics_count(c, "jit.nsegments", img->nsegments);
    395 
    396   /* One reservation for the whole image.  Requesting EXEC if any segment
    397    * is exec triggers the dual-mapping path on Apple silicon; non-exec
    398    * regions just leave the alternate alias unused.  Per-segment final
    399    * perms are applied via mem->protect on sub-ranges below. */
    400   {
    401     int master_prot = KIT_PROT_READ | KIT_PROT_WRITE;
    402     if (needs_exec) master_prot |= KIT_PROT_EXEC;
    403     metrics_scope_begin(c, "jit.reserve");
    404     if (mem->reserve(mem->user, (size_t)master_size, master_prot, &master) !=
    405         0) {
    406       compiler_panic(c, SRCLOC_NONE,
    407                      "kit_jit_from_image: execmem.reserve failed");
    408     }
    409     metrics_scope_end(c, "jit.reserve");
    410   }
    411 
    412   segs = NULL;
    413   if (img->nsegments) {
    414     segs = (KitExecMemRegion*)heap->alloc(heap, sizeof(*segs) * img->nsegments,
    415                                           _Alignof(KitExecMemRegion));
    416     if (!segs) {
    417       mem->release(mem->user, &master);
    418       compiler_panic(c, SRCLOC_NONE,
    419                      "kit_jit_from_image: oom on segment table");
    420     }
    421     memset(segs, 0, sizeof(*segs) * img->nsegments);
    422   }
    423 
    424   /* Subdivide the master mapping.  segs[i].token stays NULL — the
    425    * master reservation owns the underlying mapping and is released in
    426    * kit_jit_free. */
    427   for (i = 0; i < img->nsegments; ++i) {
    428     const LinkSegment* seg = &img->segments[i];
    429     u64 off = seg->vaddr - image_base;
    430     size_t mlen = (size_t)ALIGN_UP(seg->mem_size, page);
    431     segs[i].write = (u8*)master.write + off;
    432     segs[i].runtime = (u8*)master.runtime + off;
    433     segs[i].size = mlen;
    434     segs[i].token = NULL;
    435   }
    436   /* Master reservation is zeroed; BSS is naturally zero. */
    437 
    438   /* Copy synthetic segment buffers to their write aliases.  In JIT mode,
    439    * ordinary input-section segments intentionally have NULL payload buffers
    440    * and are materialized directly from input Buf chunks below. */
    441   metrics_scope_begin(c, "jit.copy_segments");
    442   for (i = 0; i < img->nsegments; ++i) {
    443     const LinkSegment* seg = &img->segments[i];
    444     if (seg->file_size == 0) continue;
    445     if (!img->segment_bytes[i]) {
    446       needs_input_materialize = 1;
    447       continue;
    448     }
    449     metrics_count(c, "jit.segment_bytes", seg->file_size);
    450     memcpy(segs[i].write, img->segment_bytes[i], (size_t)seg->file_size);
    451   }
    452   if (needs_input_materialize) {
    453     if (!img->linker || !img->linker->jit_mode)
    454       compiler_panic(c, SRCLOC_NONE,
    455                      "kit_jit_from_image: segment bytes are not materialized");
    456     jit_copy_input_section_bytes(img, segs);
    457   }
    458   metrics_scope_end(c, "jit.copy_segments");
    459 
    460   /* Apply relocations. The patch site bytes go through the write
    461    * alias; PC-relative arithmetic uses the runtime alias address. */
    462   metrics_scope_begin(c, "jit.apply_relocs");
    463   for (i = 0; i < LinkRelocs_count(&img->relocs); ++i) {
    464     /* got_relaxed=0: the full from-image link ran GOT layout, so .got slots
    465      * exist and GOT loads apply normally (S points at the slot). */
    466     jit_apply_reloc(c, img, segs, LinkRelocs_at(&img->relocs, i), 0);
    467   }
    468   metrics_scope_end(c, "jit.apply_relocs");
    469 
    470   /* Flip the runtime alias of each segment to its final perms. The
    471    * write alias is unaffected (still RW for any segment we'd want to
    472    * write to from JITed code; for EXEC segments the write alias is
    473    * orphaned after this point — JITed code is not expected to write
    474    * to its own code).  Each segs[i] is a sub-range of master; protect
    475    * accepts arbitrary [addr,size) inside the reservation. */
    476   metrics_scope_begin(c, "jit.protect");
    477   for (i = 0; i < img->nsegments; ++i) {
    478     const LinkSegment* seg = &img->segments[i];
    479     if (mem->protect(mem->user, segs[i].runtime, segs[i].size,
    480                      perms_for(seg->flags)) != 0) {
    481       mem->release(mem->user, &master);
    482       if (segs) heap->free(heap, segs, sizeof(*segs) * img->nsegments);
    483       compiler_panic(c, SRCLOC_NONE,
    484                      "kit_jit_from_image: execmem.protect failed");
    485     }
    486   }
    487   if (append_total) {
    488     void* slack_rt =
    489         (u8*)master.runtime + (uintptr_t)(append_start - image_base);
    490     (void)mem->protect(mem->user, slack_rt, (size_t)append_total,
    491                        KIT_PROT_NONE);
    492   }
    493   metrics_scope_end(c, "jit.protect");
    494 
    495   /* Flush only the segments that will be executed, against the
    496    * runtime alias (the address from which the CPU will fetch). */
    497   if (mem->flush_icache) {
    498     metrics_scope_begin(c, "jit.flush_icache");
    499     for (i = 0; i < img->nsegments; ++i) {
    500       const LinkSegment* seg = &img->segments[i];
    501       if (seg->flags & SF_EXEC)
    502         mem->flush_icache(mem->user, segs[i].runtime, segs[i].size);
    503     }
    504     metrics_scope_end(c, "jit.flush_icache");
    505   }
    506 
    507   /* IFUNC pre-resolution: now that code is executable and slots are
    508    * writable, walk every (resolver_vaddr, slot_vaddr) pair, call the
    509    * resolver in-process, and store its return value into the slot's
    510    * runtime address.  The iplt stub then loads the slot and tail-
    511    * calls the chosen implementation on every invocation. */
    512   if (img->niplt) {
    513     typedef void* (*ResolverFn)(void);
    514     for (i = 0; i < img->niplt; ++i) {
    515       u64 resolver_vaddr = img->iplt_pairs[2u * i + 0];
    516       u64 slot_vaddr = img->iplt_pairs[2u * i + 1];
    517       uintptr_t resolver_rt = vaddr_to_runtime(img, segs, resolver_vaddr);
    518       uintptr_t slot_rt = vaddr_to_runtime(img, segs, slot_vaddr);
    519       void* impl;
    520       if (!resolver_rt || !slot_rt)
    521         compiler_panic(c, SRCLOC_NONE,
    522                        "kit_jit: iplt vaddr does not map to runtime");
    523       impl = ((ResolverFn)resolver_rt)();
    524       *(void**)(uintptr_t)slot_rt = impl;
    525     }
    526   }
    527 
    528   jit = (KitJit*)heap->alloc(heap, sizeof(*jit), _Alignof(KitJit));
    529   if (!jit) {
    530     mem->release(mem->user, &master);
    531     if (segs) heap->free(heap, segs, sizeof(*segs) * img->nsegments);
    532     compiler_panic(c, SRCLOC_NONE, "kit_jit_from_image: oom on jit handle");
    533   }
    534   jit->c = c;
    535   jit->image = img;
    536   jit->master = master;
    537   jit->image_base = image_base;
    538   jit->segs = segs;
    539   jit->nsegs = img->nsegments;
    540   jit->view = NULL;
    541   jit->linker = img->linker;
    542   jit->append_cursor[SEG_RX] = append_start;
    543   jit->append_limit[SEG_RX] = jit->append_cursor[SEG_RX] + JIT_APPEND_RX_SLACK;
    544   jit->append_cursor[SEG_R] = jit->append_limit[SEG_RX];
    545   jit->append_limit[SEG_R] = jit->append_cursor[SEG_R] + JIT_APPEND_R_SLACK;
    546   jit->append_cursor[SEG_RW] = jit->append_limit[SEG_R];
    547   jit->append_limit[SEG_RW] = jit->append_cursor[SEG_RW] + JIT_APPEND_RW_SLACK;
    548   jit->append_cursor[SEG_TLS] = jit->append_limit[SEG_RW];
    549   jit->append_limit[SEG_TLS] =
    550       jit->append_cursor[SEG_TLS] + JIT_APPEND_TLS_SLACK;
    551   jit->generation = 0;
    552   jit->view_built = 0u;
    553   jit->jit_host = host;
    554 
    555   /* Take ownership of the image: undefer it from the compiler so a
    556    * future panic doesn't reap something we still hold. */
    557   if (img->deferred) {
    558     compiler_undefer(c, img->deferred);
    559     img->deferred = NULL;
    560   }
    561   if (jit->linker && jit->linker->deferred) {
    562     compiler_undefer(c, jit->linker->deferred);
    563     jit->linker->deferred = NULL;
    564   }
    565 
    566   /* Run .init_array constructors in forward order. */
    567   {
    568     typedef void (*VoidFn)(void);
    569     void* p_start = kit_jit_lookup(jit, KIT_SLICE_LIT("__init_array_start"));
    570     void* p_end = kit_jit_lookup(jit, KIT_SLICE_LIT("__init_array_end"));
    571     if (p_start && p_end) {
    572       VoidFn* fn = (VoidFn*)p_start;
    573       VoidFn* end = (VoidFn*)p_end;
    574       metrics_scope_begin(c, "jit.ctors");
    575       for (; fn != end; ++fn)
    576         if (*fn) (*fn)();
    577       metrics_scope_end(c, "jit.ctors");
    578     }
    579   }
    580 
    581   return jit;
    582 }
    583 
    584 const KitExecMem* kit_jit_image_execmem(KitJit* jit) {
    585   if (!jit || !jit->jit_host) return NULL;
    586   return jit->jit_host->execmem;
    587 }
    588 
    589 void kit_jit_free(KitJit* jit) {
    590   Heap* heap;
    591   const KitExecMem* mem;
    592   if (!jit) return;
    593   heap = (Heap*)jit->c->ctx->heap;
    594   mem = jit->jit_host ? jit->jit_host->execmem : NULL;
    595   /* The debug view (if built) is closed first — it owns a private
    596    * Compiler whose pools must be released before the image's
    597    * referenced builders are freed in link_image_free. */
    598   if (jit->view) {
    599     jit_view_objfile_free(jit->view);
    600     jit->view = NULL;
    601   }
    602   /* segs[] are views into master — release master only. */
    603   if (mem && mem->release && jit->master.size) {
    604     mem->release(mem->user, &jit->master);
    605   }
    606   if (jit->segs) {
    607     heap->free(heap, jit->segs, sizeof(*jit->segs) * jit->nsegs);
    608   }
    609   if (jit->image) {
    610     /* link_image_free unfederes (no-op now) and releases storage. */
    611     link_image_free(jit->image);
    612   }
    613   if (jit->linker) {
    614     link_free(jit->linker);
    615     jit->linker = NULL;
    616   }
    617   heap->free(heap, jit, sizeof(*jit));
    618 }
    619 
    620 void* kit_jit_lookup(KitJit* jit, KitSlice name) {
    621   Sym sym;
    622   LinkSymId id;
    623   const LinkSymbol* s;
    624   if (!jit || !name.s) return NULL;
    625   /* C-symbol mangling lives in obj_format_c_mangle so JIT lookups by
    626    * source-level name find the symbol regardless of target format.
    627    * name.s is NUL-terminated (KIT_SLICE_LIT / kit_slice_cstr / interned).
    628    */
    629   sym = obj_format_c_mangle(jit->c, name.s);
    630   id = symhash_get(&jit->image->globals, sym);
    631   if (id == LINK_SYM_NONE) return NULL;
    632   s = LinkSyms_at(&jit->image->syms, id - 1);
    633   if (!s) return NULL;
    634   if (!s->defined) return NULL;
    635   if (s->kind == SK_ABS) return (void*)(uintptr_t)s->vaddr;
    636   return (void*)vaddr_to_runtime(jit->image, jit->segs, s->vaddr);
    637 }
    638 
    639 void* kit_jit_tls_addr(KitJit* jit, void* sym_addr) {
    640   /* Resolve a thread-local's in-image storage from the address its SYMBOL
    641    * resolves to (single-threaded JIT: the in-image .tdata/.tbss is the single
    642    * instance — see the TLVP relaxation in kit_jit_from_image).
    643    *
    644    *  - Mach-O: the symbol resolves to a 24-byte TLV descriptor; its +16 slot
    645    *    holds the variable's in-image storage address (the normal R_ABS64
    646    *    against the storage symbol, applied during the reloc pass).
    647    *  - ELF/COFF: the symbol already resolves to the in-image storage.
    648    *
    649    * Returns NULL for anything outside this image's reservation — e.g. a
    650    * foreign/extern thread-local resolved through the host (dlsym) — which we
    651    * must not dereference. The interpreter relies on this NULL to diagnose
    652    * cleanly rather than treat a foreign pointer as our storage. */
    653   u8* p = (u8*)sym_addr;
    654   u8* lo = (u8*)jit->master.runtime;
    655   u8* hi = lo + jit->master.size;
    656   if (!jit || !p) return NULL;
    657   if (!jit->master.runtime || p < lo || p >= hi) return NULL;
    658   if (obj_format_tls_via_descriptor(jit->c)) {
    659     void* storage;
    660     if (p + 24u > hi) return NULL;
    661     memcpy(&storage, p + 16u, sizeof storage);
    662     return storage;
    663   }
    664   return p;
    665 }
    666 
    667 uint64_t kit_jit_generation(KitJit* jit) { return jit ? jit->generation : 0; }
    668 
    669 typedef struct JitAppendSec {
    670   ObjSecId obj_sec;
    671   ObjAtomId obj_atom;
    672   LinkSectionId link_sec;
    673   LinkSegmentId link_seg;
    674   SegBucket bucket;
    675   u64 obj_offset;
    676   u64 vaddr;
    677   u64 size;
    678   u64 file_size;
    679   u32 align;
    680   u16 flags;
    681   u16 sem;
    682   Sym name;
    683 } JitAppendSec;
    684 
    685 SEGVEC_DEFINE(JitAppendSecs, JitAppendSec, 4);
    686 
    687 static InputMap jit_input_map_alloc(KitJit* jit, ObjBuilder* ob) {
    688   InputMap m;
    689   ObjSymIter* it;
    690   ObjSymEntry e;
    691   u32 nsyms = 0;
    692   it = obj_symiter_new(ob);
    693   while (obj_symiter_next(it, &e)) ++nsyms;
    694   obj_symiter_free(it);
    695   link_input_map_alloc(jit->image, &m, ob, nsyms + 1u);
    696   return m;
    697 }
    698 
    699 static void jit_invalidate_view(KitJit* jit) {
    700   if (jit->view) {
    701     jit_view_objfile_free(jit->view);
    702     jit->view = NULL;
    703   }
    704   jit->view_built = 0u;
    705 }
    706 
    707 /* Incremental-append reloc apply.  Delegates to the shared jit_apply_reloc
    708  * with got_relaxed=1: the append path does not re-run GOT layout, so GOT loads
    709  * in the appended object are relaxed to direct in-image addressing. */
    710 static void jit_apply_one_reloc(KitJit* jit, const LinkRelocApply* r) {
    711   jit_apply_reloc(jit->c, jit->image, jit->segs, r, 1);
    712 }
    713 
    714 static void jit_append_obj_inner(KitJit* jit, ObjBuilder* ob) {
    715   LinkImage* img = jit->image;
    716   Heap* h = img->heap;
    717   const KitExecMem* mem = require_execmem_h(jit->jit_host, jit->c);
    718   u64 page = jit_page_size_h(jit->jit_host, jit->c);
    719   u32 old_nsections = img->nsections;
    720   u32 old_nsegments = img->nsegments;
    721   u32 old_nsegs = jit->nsegs;
    722   u32 old_nmaps = img->ninput_maps;
    723   u32 old_dbg_n = img->dbg_objs_n;
    724   u32 old_nrelocs = LinkRelocs_count(&img->relocs);
    725   u64 old_cursor[SEG_NBUCKETS];
    726   InputMap m;
    727   JitAppendSecs secs;
    728   u32 nsecs = 0;
    729   u32 obj_sec_count;
    730   u64 cursor[SEG_NBUCKETS];
    731   LinkInputId new_input_id;
    732   u32 new_input_idx;
    733   ObjSymIter* it;
    734   ObjSymEntry e;
    735   u32 i;
    736 
    737   JitAppendSecs_init(&secs, h);
    738   for (i = 0; i < SEG_NBUCKETS; ++i) old_cursor[i] = jit->append_cursor[i];
    739 
    740   if (!jit || !ob || !jit->linker || obj_compiler(ob) != jit->c)
    741     compiler_panic(jit ? jit->c : NULL, SRCLOC_NONE,
    742                    "kit_jit_append_obj: object is not appendable");
    743 
    744   /* Preflight duplicate strong definitions and unresolved references before
    745    * mutating image-visible state. */
    746   it = obj_symiter_new(ob);
    747   while (obj_symiter_next(it, &e)) {
    748     const ObjSym* s = e.sym;
    749     if (link_sym_is_spurious_undef(s)) continue;
    750     if (link_sym_is_def(s) && s->name != 0 &&
    751         (s->bind == SB_GLOBAL || s->bind == SB_WEAK)) {
    752       LinkSymId existing = symhash_get(&img->globals, s->name);
    753       if (existing != LINK_SYM_NONE) {
    754         LinkSymbol* prev = LinkSyms_at(&img->syms, existing - 1);
    755         if (prev && prev->defined &&
    756             link_bind_strength((u8)s->bind) == link_bind_strength(SB_GLOBAL) &&
    757             link_bind_strength(prev->bind) == link_bind_strength(SB_GLOBAL)) {
    758           Slice nm_s = pool_slice(jit->c->global, s->name);
    759           const char* nm = nm_s.s;
    760           size_t namelen = nm_s.len;
    761           obj_format_demangle_c(jit->c, &nm, &namelen);
    762           obj_symiter_free(it);
    763           compiler_panic(jit->c, SRCLOC_NONE,
    764                          "kit_jit_append_obj: duplicate global '%.*s'",
    765                          (int)namelen, nm);
    766         }
    767       }
    768     } else if (!link_sym_is_def(s) && s->name != 0) {
    769       LinkSymId hit = symhash_get(&img->globals, s->name);
    770       int ok = 0;
    771       if (hit != LINK_SYM_NONE) {
    772         LinkSymbol* def = LinkSyms_at(&img->syms, hit - 1);
    773         if (def && def->defined) ok = 1;
    774       }
    775       if (!ok) {
    776         ObjSymIter* it2 = obj_symiter_new(ob);
    777         ObjSymEntry e2;
    778         while (obj_symiter_next(it2, &e2)) {
    779           const ObjSym* d = e2.sym;
    780           if (d && d->name == s->name && link_sym_is_def(d) &&
    781               (d->bind == SB_GLOBAL || d->bind == SB_WEAK)) {
    782             ok = 1;
    783             break;
    784           }
    785         }
    786         obj_symiter_free(it2);
    787       }
    788       if (!ok && jit->linker->resolver) {
    789         Slice nm_s = pool_slice(jit->c->global, s->name);
    790         if (jit->linker->resolver(jit->linker->resolver_user, nm_s)) ok = 1;
    791       }
    792       if (!ok && s->bind == SB_WEAK) ok = 1;
    793       if (!ok) {
    794         Slice nm_s = pool_slice(jit->c->global, s->name);
    795         const char* nm = nm_s.s;
    796         size_t nlen = nm_s.len;
    797         if (nm && nlen == 15u && memcmp(nm, "__tlv_bootstrap", 15u) == 0)
    798           ok = 1;
    799         /* Windows COFF TLS module-index symbol: the JIT relaxes every TLS
    800          * access to in-image addressing, so `_tls_index` is never read. */
    801         if (nm && nlen == 10u && memcmp(nm, "_tls_index", 10u) == 0)
    802           ok = 1;
    803         if (!ok) {
    804           obj_format_demangle_c(jit->c, &nm, &nlen);
    805           obj_symiter_free(it);
    806           compiler_panic(jit->c, SRCLOC_NONE,
    807                          "kit_jit_append_obj: undefined reference to '%.*s'",
    808                          (int)nlen, nm);
    809         }
    810       }
    811     }
    812   }
    813   obj_symiter_free(it);
    814 
    815   m = jit_input_map_alloc(jit, ob);
    816   obj_sec_count = obj_section_count(ob);
    817   for (i = 0; i < SEG_NBUCKETS; ++i) cursor[i] = jit->append_cursor[i];
    818 
    819   for (i = 1; i < obj_sec_count; ++i) {
    820     const Section* s = obj_section_get(ob, (ObjSecId)i);
    821     u32 first = 0, count = 1, ai;
    822     int has_atoms;
    823     if (!s || !link_section_kept(s)) continue;
    824     has_atoms = link_input_section_has_atoms(&m, (ObjSecId)i);
    825     if (has_atoms) link_input_section_atoms(&m, (ObjSecId)i, &first, &count);
    826     for (ai = 0; ai < count; ++ai) {
    827       ObjAtomId aid =
    828           has_atoms ? m.section_atom_ids[first + ai] : OBJ_ATOM_NONE;
    829       const ObjAtom* atom = has_atoms ? obj_atom_get(ob, aid) : NULL;
    830       SegBucket b;
    831       u64 obj_offset;
    832       u64 size;
    833       u64 vaddr;
    834       JitAppendSec* ps;
    835       u32 sec_idx;
    836       if (has_atoms) {
    837         if (!atom || atom->removed) continue;
    838         obj_offset = atom->offset;
    839         size = atom->size;
    840       } else {
    841         obj_offset = 0u;
    842         size = link_section_size_for_link(s);
    843       }
    844       if (size == 0) continue;
    845       b = link_bucket_for(s->flags);
    846       vaddr = ALIGN_UP(cursor[b], (u64)(s->align ? s->align : 1u));
    847       if (vaddr + size > jit->append_limit[b])
    848         compiler_panic(jit->c, SRCLOC_NONE,
    849                        "kit_jit_append_obj: append slack exhausted");
    850       ps = JitAppendSecs_push(&secs, &sec_idx);
    851       if (!ps)
    852         compiler_panic(jit->c, SRCLOC_NONE,
    853                        "kit_jit_append_obj: oom on section plan");
    854       ps->obj_sec = (ObjSecId)i;
    855       ps->obj_atom = has_atoms ? aid : OBJ_ATOM_NONE;
    856       ps->link_sec = (LinkSectionId)(old_nsections + sec_idx + 1u);
    857       ps->link_seg = (LinkSegmentId)(old_nsegments + sec_idx + 1u);
    858       ps->bucket = b;
    859       ps->obj_offset = obj_offset;
    860       ps->vaddr = vaddr;
    861       ps->size = size;
    862       ps->file_size = (s->sem == SSEM_NOBITS || s->kind == SEC_BSS) ? 0 : size;
    863       ps->align = s->align ? s->align : 1u;
    864       ps->flags = s->flags;
    865       ps->sem = (s->kind == SEC_BSS) ? SSEM_NOBITS : s->sem;
    866       ps->name = s->name;
    867       if (has_atoms) {
    868         m.atom[aid] = ps->link_sec;
    869         if (m.section[i] == LINK_SEC_NONE) m.section[i] = ps->link_sec;
    870       } else {
    871         m.section[i] = ps->link_sec;
    872       }
    873       cursor[b] = vaddr + size;
    874     }
    875   }
    876   nsecs = JitAppendSecs_count(&secs);
    877   for (i = 0; i < SEG_NBUCKETS; ++i) jit->append_cursor[i] = cursor[i];
    878 
    879   it = obj_symiter_new(ob);
    880   while (obj_symiter_next(it, &e)) {
    881     const ObjSym* s = e.sym;
    882     int is_def;
    883     if (e.id >= m.nsym || link_sym_is_spurious_undef(s)) continue;
    884     is_def = link_sym_is_def(s);
    885     if (is_def && (s->bind == SB_GLOBAL || s->bind == SB_WEAK) &&
    886         s->name != 0) {
    887       LinkSymId existing = symhash_get(&img->globals, s->name);
    888       if (existing != LINK_SYM_NONE) {
    889         LinkSymbol* prev = LinkSyms_at(&img->syms, existing - 1);
    890         if (prev && prev->defined &&
    891             link_bind_strength((u8)s->bind) == link_bind_strength(SB_GLOBAL) &&
    892             link_bind_strength(prev->bind) == link_bind_strength(SB_GLOBAL)) {
    893           Slice nm_s = pool_slice(jit->c->global, s->name);
    894           const char* nm = nm_s.s;
    895           size_t namelen = nm_s.len;
    896           obj_format_demangle_c(jit->c, &nm, &namelen);
    897           obj_symiter_free(it);
    898           compiler_panic(jit->c, SRCLOC_NONE,
    899                          "kit_jit_append_obj: duplicate global '%.*s'",
    900                          (int)namelen, nm);
    901         }
    902         if (prev && prev->defined) {
    903           m.sym[e.id] = existing;
    904           continue;
    905         }
    906       }
    907     }
    908     m.sym[e.id] = (LinkSymId)(LinkSyms_count(&img->syms) + 1u);
    909     {
    910       LinkSymbol rec;
    911       memset(&rec, 0, sizeof(rec));
    912       rec.name = s->name;
    913       rec.input_id = (LinkInputId)(LinkInputs_count(&jit->linker->inputs) + 1u);
    914       rec.obj_sym = e.id;
    915       rec.section_id = LINK_SEC_NONE;
    916       rec.atom_id = is_def ? link_input_sym_atom(&m, e.id) : OBJ_ATOM_NONE;
    917       rec.value = s->value;
    918       rec.size = s->size;
    919       rec.common_align = (s->kind == SK_COMMON) ? (u32)s->common_align : 0u;
    920       rec.bind = (u8)s->bind;
    921       rec.kind = (u8)s->kind;
    922       rec.defined = (u8)is_def;
    923       if (is_def && s->section_id != OBJ_SEC_NONE &&
    924           s->section_id < m.nsection) {
    925         rec.section_id = link_input_symbol_section(&m, s, e.id);
    926         if (rec.section_id != LINK_SEC_NONE) {
    927           u32 sj;
    928           for (sj = 0; sj < nsecs; ++sj) {
    929             JitAppendSec* ps = JitAppendSecs_at(&secs, sj);
    930             if (ps && ps->link_sec == rec.section_id) {
    931               rec.vaddr = ps->vaddr + (s->value - ps->obj_offset);
    932               break;
    933             }
    934           }
    935         }
    936       } else if (s->kind == SK_ABS) {
    937         rec.vaddr = s->value;
    938       }
    939       m.sym[e.id] = link_append_symbol(img, &rec);
    940       if (is_def && (s->bind == SB_GLOBAL || s->bind == SB_WEAK) &&
    941           s->name != 0) {
    942         LinkSymId existing;
    943         if (symhash_insert(&img->globals, s->name, m.sym[e.id], &existing)) {
    944         } else {
    945           m.sym[e.id] = existing;
    946         }
    947       }
    948     }
    949   }
    950   obj_symiter_free(it);
    951 
    952   /* Resolve newly appended undefs before any bytes become executable. */
    953   for (i = 0; i < LinkSyms_count(&img->syms); ++i) {
    954     LinkSymbol* s = LinkSyms_at(&img->syms, i);
    955     if (s->input_id !=
    956         (LinkInputId)(LinkInputs_count(&jit->linker->inputs) + 1u))
    957       continue;
    958     if (s->defined) continue;
    959     if (s->name != 0) {
    960       LinkSymId hit = symhash_get(&img->globals, s->name);
    961       if (hit != LINK_SYM_NONE && hit != s->id) {
    962         LinkSymbol* def = LinkSyms_at(&img->syms, hit - 1);
    963         if (def->defined) {
    964           s->section_id = def->section_id;
    965           s->atom_id = def->atom_id;
    966           s->value = def->value;
    967           s->vaddr = def->vaddr;
    968           s->kind = def->kind;
    969           s->bind = def->bind;
    970           s->defined = 1;
    971           continue;
    972         }
    973       }
    974     }
    975     if (jit->linker->resolver && s->name != 0) {
    976       Slice nm_s = pool_slice(jit->c->global, s->name);
    977       void* p = jit->linker->resolver(jit->linker->resolver_user, nm_s);
    978       if (p) {
    979         s->kind = SK_ABS;
    980         s->vaddr = (u64)(uintptr_t)p;
    981         s->defined = 1;
    982         continue;
    983       }
    984     }
    985     if (s->bind == SB_WEAK) {
    986       s->kind = SK_ABS;
    987       s->vaddr = 0;
    988       s->defined = 1;
    989       continue;
    990     }
    991     if (s->name != 0) {
    992       Slice nm_s = pool_slice(jit->c->global, s->name);
    993       const char* nm = nm_s.s;
    994       size_t nlen = nm_s.len;
    995       if (nm && nlen == 15u && memcmp(nm, "__tlv_bootstrap", 15u) == 0) {
    996         s->kind = SK_ABS;
    997         s->vaddr = 0;
    998         s->defined = 1;
    999         continue;
   1000       }
   1001       obj_format_demangle_c(jit->c, &nm, &nlen);
   1002       compiler_panic(jit->c, SRCLOC_NONE,
   1003                      "kit_jit_append_obj: undefined reference to '%.*s'",
   1004                      (int)nlen, nm);
   1005     }
   1006     compiler_panic(jit->c, SRCLOC_NONE,
   1007                    "kit_jit_append_obj: undefined anonymous symbol");
   1008   }
   1009 
   1010   new_input_id = link_add_obj(jit->linker, ob);
   1011   new_input_idx = new_input_id - 1u;
   1012 
   1013   {
   1014     InputMap* maps = (InputMap*)h->realloc(
   1015         h, img->input_maps, sizeof(*img->input_maps) * img->ninput_maps,
   1016         sizeof(*img->input_maps) * (new_input_idx + 1u), _Alignof(InputMap));
   1017     if (!maps)
   1018       compiler_panic(jit->c, SRCLOC_NONE,
   1019                      "kit_jit_append_obj: oom growing input maps");
   1020     img->input_maps = maps;
   1021     while (img->ninput_maps < new_input_idx) {
   1022       memset(&img->input_maps[img->ninput_maps], 0, sizeof(InputMap));
   1023       img->ninput_maps++;
   1024     }
   1025     img->input_maps[new_input_idx] = m;
   1026     img->ninput_maps = new_input_idx + 1u;
   1027   }
   1028 
   1029   if (nsecs) {
   1030     LinkSection* nsections = (LinkSection*)h->realloc(
   1031         h, img->sections, sizeof(*img->sections) * old_nsections,
   1032         sizeof(*img->sections) * (old_nsections + nsecs),
   1033         _Alignof(LinkSection));
   1034     LinkSegment* nsegments = (LinkSegment*)h->realloc(
   1035         h, img->segments, sizeof(*img->segments) * old_nsegments,
   1036         sizeof(*img->segments) * (old_nsegments + nsecs),
   1037         _Alignof(LinkSegment));
   1038     u8** nsegbytes = (u8**)h->realloc(
   1039         h, img->segment_bytes, sizeof(*img->segment_bytes) * old_nsegments,
   1040         sizeof(*img->segment_bytes) * (old_nsegments + nsecs), _Alignof(u8*));
   1041     size_t* nsegcaps = (size_t*)h->realloc(
   1042         h, img->segment_bytes_cap,
   1043         sizeof(*img->segment_bytes_cap) * old_nsegments,
   1044         sizeof(*img->segment_bytes_cap) * (old_nsegments + nsecs),
   1045         _Alignof(size_t));
   1046     KitExecMemRegion* njsegs = (KitExecMemRegion*)h->realloc(
   1047         h, jit->segs, sizeof(*jit->segs) * old_nsegs,
   1048         sizeof(*jit->segs) * (old_nsegs + nsecs), _Alignof(KitExecMemRegion));
   1049     if (!nsections || !nsegments || !nsegbytes || !nsegcaps || !njsegs)
   1050       compiler_panic(jit->c, SRCLOC_NONE,
   1051                      "kit_jit_append_obj: oom growing image tables");
   1052     img->sections = nsections;
   1053     img->segments = nsegments;
   1054     img->segment_bytes = nsegbytes;
   1055     img->segment_bytes_cap = nsegcaps;
   1056     jit->segs = njsegs;
   1057   }
   1058 
   1059   for (i = 0; i < nsecs; ++i) {
   1060     JitAppendSec* ps = JitAppendSecs_at(&secs, i);
   1061     LinkSection* ls = &img->sections[old_nsections + i];
   1062     LinkSegment* seg = &img->segments[old_nsegments + i];
   1063     KitExecMemRegion* js = &jit->segs[old_nsegs + i];
   1064     const Section* os = obj_section_get(ob, ps->obj_sec);
   1065     memset(ls, 0, sizeof(*ls));
   1066     ls->id = ps->link_sec;
   1067     ls->input_id = new_input_id;
   1068     ls->obj_section_id = ps->obj_sec;
   1069     ls->obj_atom_id = ps->obj_atom;
   1070     ls->segment_id = ps->link_seg;
   1071     ls->obj_offset = ps->obj_offset;
   1072     ls->input_offset = 0;
   1073     ls->file_offset = ps->vaddr;
   1074     ls->vaddr = ps->vaddr;
   1075     ls->size = ps->size;
   1076     ls->flags = ps->flags;
   1077     ls->align = ps->align;
   1078     ls->name = ps->name;
   1079     ls->sem = ps->sem;
   1080 
   1081     memset(seg, 0, sizeof(*seg));
   1082     seg->id = ps->link_seg;
   1083     seg->flags = SF_ALLOC;
   1084     if (ps->bucket == SEG_RX) seg->flags |= SF_EXEC;
   1085     if (ps->bucket == SEG_RW) seg->flags |= SF_WRITE;
   1086     if (ps->bucket == SEG_TLS) seg->flags |= SF_TLS;
   1087     seg->file_offset = ps->vaddr;
   1088     seg->vaddr = ps->vaddr;
   1089     seg->mem_size = ps->size;
   1090     seg->file_size = ps->file_size;
   1091     seg->align = (u32)page;
   1092     seg->nsections = 1;
   1093     img->segment_bytes[old_nsegments + i] = NULL;
   1094     img->segment_bytes_cap[old_nsegments + i] = 0;
   1095 
   1096     memset(js, 0, sizeof(*js));
   1097     js->write =
   1098         (u8*)jit->master.write + (uintptr_t)(ps->vaddr - jit->image_base);
   1099     js->runtime =
   1100         (u8*)jit->master.runtime + (uintptr_t)(ps->vaddr - jit->image_base);
   1101     js->size = (size_t)ps->size;
   1102     js->token = NULL;
   1103 
   1104     if (os && os->sem != SSEM_NOBITS && os->kind != SEC_BSS &&
   1105         os->bytes.total && ps->size)
   1106       buf_read(&os->bytes, (u32)ps->obj_offset, (u8*)js->write,
   1107                (size_t)ps->size);
   1108     img->nsections++;
   1109     img->nsegments++;
   1110     jit->nsegs++;
   1111   }
   1112 
   1113   {
   1114     u32 total = obj_reloc_total(ob);
   1115     for (i = 0; i < total; ++i) {
   1116       const Reloc* r = obj_reloc_at(ob, i);
   1117       LinkRelocApply rec;
   1118       LinkSectionId ls_id;
   1119       const LinkSection* ls;
   1120       if (!r || r->section_id == OBJ_SEC_NONE || r->section_id >= m.nsection)
   1121         continue;
   1122       ls_id = link_input_reloc_section(&m, r, i);
   1123       if (ls_id == LINK_SEC_NONE) continue;
   1124       if (r->sym == OBJ_SYM_NONE || r->sym >= m.nsym ||
   1125           m.sym[r->sym] == LINK_SYM_NONE)
   1126         continue;
   1127       ls = &img->sections[ls_id - 1];
   1128       memset(&rec, 0, sizeof(rec));
   1129       rec.input_id = new_input_id;
   1130       rec.section_id = r->section_id;
   1131       rec.link_section_id = ls_id;
   1132       rec.offset = r->offset - (u32)ls->obj_offset;
   1133       rec.width = reloc_kind_width(jit->c, (RelocKind)r->kind);
   1134       rec.write_vaddr = ls->vaddr + rec.offset;
   1135       rec.write_file_offset = rec.write_vaddr;
   1136       rec.kind = (RelocKind)r->kind;
   1137       rec.target = m.sym[r->sym];
   1138       rec.addend = r->addend;
   1139       *link_append_reloc_slot(img) = rec;
   1140     }
   1141   }
   1142 
   1143   for (i = old_nrelocs; i < LinkRelocs_count(&img->relocs); ++i) {
   1144     const LinkRelocApply* r = LinkRelocs_at(&img->relocs, i);
   1145     jit_apply_one_reloc(jit, r);
   1146   }
   1147 
   1148   for (i = old_nsegs; i < jit->nsegs; ++i) {
   1149     const LinkSegment* seg = &img->segments[i];
   1150     u64 page_lo = seg->vaddr & ~(page - 1u);
   1151     u64 page_hi = ALIGN_UP(seg->vaddr + seg->mem_size, page);
   1152     void* rt =
   1153         (u8*)jit->master.runtime + (uintptr_t)(page_lo - jit->image_base);
   1154     if (mem->protect(mem->user, rt, (size_t)(page_hi - page_lo),
   1155                      perms_for(seg->flags)) != 0) {
   1156       compiler_panic(jit->c, SRCLOC_NONE,
   1157                      "kit_jit_append_obj: execmem.protect failed");
   1158     }
   1159   }
   1160   if (mem->flush_icache) {
   1161     for (i = old_nsegs; i < jit->nsegs; ++i) {
   1162       const LinkSegment* seg = &img->segments[i];
   1163       if (seg->flags & SF_EXEC)
   1164         mem->flush_icache(mem->user, jit->segs[i].runtime, jit->segs[i].size);
   1165     }
   1166   }
   1167 
   1168   {
   1169     ObjBuilder** nd = (ObjBuilder**)h->realloc(
   1170         h, img->dbg_objs, sizeof(*img->dbg_objs) * old_dbg_n,
   1171         sizeof(*img->dbg_objs) * (new_input_idx + 1u), _Alignof(ObjBuilder*));
   1172     u8* no = (u8*)h->realloc(
   1173         h, img->dbg_objs_owned, sizeof(*img->dbg_objs_owned) * old_dbg_n,
   1174         sizeof(*img->dbg_objs_owned) * (new_input_idx + 1u), 1u);
   1175     if (!nd || !no)
   1176       compiler_panic(jit->c, SRCLOC_NONE,
   1177                      "kit_jit_append_obj: oom growing debug inputs");
   1178     img->dbg_objs = nd;
   1179     img->dbg_objs_owned = no;
   1180     while (img->dbg_objs_n < new_input_idx) {
   1181       img->dbg_objs[img->dbg_objs_n] = NULL;
   1182       img->dbg_objs_owned[img->dbg_objs_n] = 0u;
   1183       img->dbg_objs_n++;
   1184     }
   1185     img->dbg_objs[new_input_idx] = ob;
   1186     img->dbg_objs_owned[new_input_idx] = 0u;
   1187     img->dbg_objs_n = new_input_idx + 1u;
   1188   }
   1189 
   1190   jit_invalidate_view(jit);
   1191   jit->generation++;
   1192   JitAppendSecs_fini(&secs);
   1193   (void)old_cursor;
   1194   (void)old_nmaps;
   1195 }
   1196 
   1197 KitStatus kit_jit_publish(KitJit* jit, const KitJitPublishOptions* opts,
   1198                           KitJitPublishResult* result) {
   1199   PanicFrame panic;
   1200   Compiler* c;
   1201   KitLinkSession* link;
   1202   u32 i;
   1203 
   1204   if (result) memset(result, 0, sizeof(*result));
   1205   if (!jit || !opts || !opts->link) return KIT_INVALID;
   1206   if ((KitJitPublishKind)opts->kind != KIT_JIT_PUBLISH_APPEND_OBJECTS)
   1207     return KIT_UNSUPPORTED;
   1208   link = opts->link;
   1209   if (link->non_obj_inputs) return KIT_UNSUPPORTED;
   1210   c = jit->c;
   1211   compiler_panic_push(c, &panic);
   1212   if (setjmp(panic.env)) {
   1213     /* A failed append (e.g. duplicate global) panics from
   1214      * jit_append_obj_inner. compiler_run_cleanups fires the borrowed link
   1215      * session's deferred linker_cleanup, which frees its Linker; null the
   1216      * session's references so the caller's kit_link_session_free does not
   1217      * double-free. Mirrors link_session_guard's recovery. */
   1218     compiler_run_cleanups(c);
   1219     link->linker = NULL;
   1220     link->image = NULL;
   1221     compiler_panic_pop(c, &panic);
   1222     return KIT_ERR;
   1223   }
   1224   for (i = 0; i < link->npublish_objs; ++i)
   1225     jit_append_obj_inner(jit, link->publish_objs[i]);
   1226   compiler_panic_pop(c, &panic);
   1227   if (result) result->generation = jit->generation;
   1228   return KIT_OK;
   1229 }
   1230 
   1231 /* ---- inspector entries ---- */
   1232 
   1233 /* True if `name` (NUL-terminated) is a debug section the DWARF consumer
   1234  * (src/debug/dwarf_open.c) might read.  Everything else is skipped. */
   1235 static int jit_view_is_debug_name(const char* name) {
   1236   if (!name) return 0;
   1237   if (name[0] == '.' && name[1] == 'd' && name[2] == 'e' && name[3] == 'b' &&
   1238       name[4] == 'u' && name[5] == 'g' && name[6] == '_')
   1239     return 1; /* .debug_* */
   1240   /* .eh_frame is consulted by kit_dwarf for CFI unwinding when
   1241    * available; kit itself doesn't currently emit it, but inputs read
   1242    * from external .o files may carry it. */
   1243   if (name[0] == '.' && name[1] == 'e' && name[2] == 'h' && name[3] == '_' &&
   1244       name[4] == 'f' && name[5] == 'r' && name[6] == 'a' && name[7] == 'm' &&
   1245       name[8] == 'e' && name[9] == '\0')
   1246     return 1;
   1247   return 0;
   1248 }
   1249 
   1250 /* True if input `ii` carries any debug section that's worth surfacing.
   1251  * Cheap walk over the input's section table.  Sym values are pool-local,
   1252  * so name strings must be dereferenced through the input's *own*
   1253  * compiler pool — not the jit's pool. */
   1254 static int jit_view_input_has_debug(KitJit* jit, u32 ii) {
   1255   ObjBuilder* ob;
   1256   Pool* in_pool;
   1257   u32 nsec, k;
   1258   if (ii >= jit->image->dbg_objs_n) return 0;
   1259   ob = jit->image->dbg_objs[ii];
   1260   if (!ob) return 0;
   1261   in_pool = obj_compiler(ob)->global;
   1262   nsec = obj_section_count(ob);
   1263   for (k = 0; k < nsec; ++k) {
   1264     const Section* s = obj_section_get(ob, (ObjSecId)(k + 1));
   1265     const char* nm;
   1266     if (!s || !s->name) continue;
   1267     nm = pool_slice(in_pool, s->name).s;
   1268     if (jit_view_is_debug_name(nm)) return 1;
   1269   }
   1270   return 0;
   1271 }
   1272 
   1273 /* Resolve an input-local ObjSymId to the final image vaddr of the
   1274  * defining LinkSymbol, going through the per-input InputMap and the
   1275  * image's LinkSyms table.  Returns 0 if the symbol is undefined or the
   1276  * mapping is missing — debug relocations against unresolved symbols
   1277  * collapse to zero, which is the DWARF "absent" convention. */
   1278 static u64 jit_view_sym_vaddr(KitJit* jit, u32 ii, ObjSymId obj_sym) {
   1279   const InputMap* m;
   1280   LinkSymId lid;
   1281   const LinkSymbol* s;
   1282   if (obj_sym == OBJ_SYM_NONE) return 0;
   1283   if (ii >= jit->image->ninput_maps) return 0;
   1284   m = &jit->image->input_maps[ii];
   1285   if (!m->sym || obj_sym >= m->nsym) return 0;
   1286   lid = m->sym[obj_sym];
   1287   if (lid == LINK_SYM_NONE || lid > LinkSyms_count(&jit->image->syms)) return 0;
   1288   s = LinkSyms_at(&jit->image->syms, lid - 1);
   1289   if (!s || !s->defined) return 0;
   1290   return s->vaddr; /* image-relative — what DWARF was emitted in */
   1291 }
   1292 
   1293 /* Per-output-section state used while concatenating multi-input debug
   1294  * bytes.  Keyed by interned name in the view compiler's pool. */
   1295 typedef struct ViewSec {
   1296   Sym view_name; /* interned in view_ob's compiler pool */
   1297   ObjSecId view_id;
   1298   u32 cur_size; /* bytes currently in the view section */
   1299   /* Snapshot of cur_size taken at the start of processing the current
   1300    * input.  Reloc apply for SK_SECTION targets must use this value so
   1301    * that intra-input references resolve to *this* input's contribution,
   1302    * not bytes appended later from the same input. */
   1303   u32 snap;
   1304 } ViewSec;
   1305 
   1306 /* Find-or-create a ViewSec entry by debug-section name. */
   1307 static ViewSec* view_sec_for(KitJit* jit, ViewSec* tab, u32* ntab,
   1308                              u32* cap_inout, const char* name, u16 flags,
   1309                              u32 align, u32 entsize, ObjBuilder* view_ob,
   1310                              ViewSec** tab_out) {
   1311   Heap* h = (Heap*)jit->c->ctx->heap;
   1312   Pool* view_pool = obj_compiler(view_ob)->global;
   1313   Sym vn = pool_intern_slice(view_pool, slice_from_cstr(name));
   1314   u32 i;
   1315   for (i = 0; i < *ntab; ++i) {
   1316     if (tab[i].view_name == vn) {
   1317       if (tab_out) *tab_out = tab;
   1318       return &tab[i];
   1319     }
   1320   }
   1321   if (*ntab == *cap_inout) {
   1322     u32 ncap = *cap_inout ? *cap_inout * 2u : 4u;
   1323     ViewSec* na = (ViewSec*)h->realloc(h, tab, *cap_inout * sizeof(*tab),
   1324                                        ncap * sizeof(*tab), _Alignof(ViewSec));
   1325     if (!na) return NULL;
   1326     tab = na;
   1327     *cap_inout = ncap;
   1328     if (tab_out) *tab_out = tab;
   1329   } else if (tab_out) {
   1330     *tab_out = tab;
   1331   }
   1332   {
   1333     ViewSec* slot = &tab[(*ntab)++];
   1334     slot->view_name = vn;
   1335     slot->view_id = obj_section_ex(view_ob, vn, SEC_DEBUG, SSEM_PROGBITS, flags,
   1336                                    align ? align : 1u, entsize, 0, 0);
   1337     slot->cur_size = 0;
   1338     slot->snap = 0;
   1339     return slot;
   1340   }
   1341 }
   1342 
   1343 /* Find a ViewSec by view-pool name (no creation).  Returns NULL on miss. */
   1344 static ViewSec* view_sec_find(ViewSec* tab, u32 ntab, Sym view_name) {
   1345   u32 i;
   1346   for (i = 0; i < ntab; ++i)
   1347     if (tab[i].view_name == view_name) return &tab[i];
   1348   return NULL;
   1349 }
   1350 
   1351 /* Copy one debug section from input `ii` into the view, applying its
   1352  * relocations against either the view-relative section prefix (for
   1353  * SK_SECTION targets pointing at a debug section) or the final image
   1354  * vaddr (for code/data symbol targets like DW_AT_low_pc).
   1355  *
   1356  * `tab` is the find-or-create table of view sections, keyed by name;
   1357  * snapshots taken at the start of this input are read from it via
   1358  * view_sec_find. */
   1359 static void jit_view_copy_debug_section(KitJit* jit, u32 ii, ObjSecId in_sec_id,
   1360                                         ObjBuilder* view_ob, ViewSec* tab,
   1361                                         u32 ntab, ViewSec* out_vs) {
   1362   ObjBuilder* in_ob = jit->image->dbg_objs[ii];
   1363   const Section* in_sec = obj_section_get(in_ob, in_sec_id);
   1364   Pool* in_pool;
   1365   Pool* view_pool = obj_compiler(view_ob)->global;
   1366   Heap* h;
   1367   u32 nbytes, k, total_relocs;
   1368   u8* bytes;
   1369   if (!in_sec) return;
   1370   nbytes = in_sec->bytes.total;
   1371   if (nbytes == 0) return;
   1372   in_pool = obj_compiler(in_ob)->global;
   1373   h = (Heap*)jit->c->ctx->heap;
   1374   (void)in_pool;
   1375 
   1376   bytes = (u8*)h->alloc(h, nbytes, 1);
   1377   if (!bytes) return;
   1378   buf_flatten(&in_sec->bytes, bytes);
   1379 
   1380   /* Apply this section's relocations in place.  obj_reloc_at returns
   1381    * all relocations across the input; filter by section_id. */
   1382   total_relocs = obj_reloc_total(in_ob);
   1383   for (k = 0; k < total_relocs; ++k) {
   1384     const Reloc* r = obj_reloc_at(in_ob, k);
   1385     u64 S = 0;
   1386     int handled = 0;
   1387     if (!r || r->section_id != in_sec_id) continue;
   1388     if (r->offset >= nbytes) continue; /* malformed; skip */
   1389     /* SK_SECTION target → resolve against the per-input snapshot of the
   1390      * matching view section's prefix size.  This is what makes the
   1391      * concatenated multi-input view's cross-section offsets land in
   1392      * the right slot. */
   1393     if (r->sym != OBJ_SYM_NONE) {
   1394       const ObjSym* ts = obj_symbol_get(in_ob, r->sym);
   1395       if (ts && ts->kind == SK_SECTION && ts->section_id != OBJ_SEC_NONE) {
   1396         const Section* tsec = obj_section_get(in_ob, ts->section_id);
   1397         if (tsec && tsec->kind == SEC_DEBUG && tsec->name) {
   1398           Slice tnm_s = pool_slice(obj_compiler(in_ob)->global, tsec->name);
   1399           const char* tnm = tnm_s.s;
   1400           size_t tnlen = tnm_s.len;
   1401           if (tnm) {
   1402             Sym v_tn =
   1403                 pool_intern_slice(view_pool, (Slice){.s = tnm, .len = tnlen});
   1404             ViewSec* tgt = view_sec_find(tab, ntab, v_tn);
   1405             if (tgt) {
   1406               S = (u64)tgt->snap;
   1407               handled = 1;
   1408             }
   1409           }
   1410         }
   1411       }
   1412     }
   1413     if (!handled) S = jit_view_sym_vaddr(jit, ii, r->sym);
   1414     /* P is unused by ABS kinds; PC-relative debug-section relocs are
   1415      * not produced by kit's debug emitter, but if some external .o
   1416      * carried one against a debug section, P=0 would give a
   1417      * non-meaningful offset — acceptable for a viewer that only reads
   1418      * absolute address fields. */
   1419     link_reloc_apply(jit->c, (RelocKind)r->kind, bytes + r->offset, S,
   1420                      r->addend, 0);
   1421   }
   1422 
   1423   obj_write(view_ob, out_vs->view_id, bytes, nbytes);
   1424   out_vs->cur_size += nbytes;
   1425   h->free(h, bytes, nbytes);
   1426 }
   1427 
   1428 /* Build the view on first call.  Walks every input that carries a debug
   1429  * section and concatenates per-section bytes into the view's matching
   1430  * sections.  SK_SECTION relocations are resolved against the view-side
   1431  * prefix length snapshotted at the start of each input, so the merged
   1432  * CU2/CU3/... cross-section offsets land in the right slot. */
   1433 static KitObjFile* jit_view_build(KitJit* jit) {
   1434   KitObjFile* view;
   1435   ObjBuilder* view_ob;
   1436   ViewSec* tab = NULL;
   1437   u32 ntab = 0, cap = 0;
   1438   Heap* h;
   1439   u32 ii, k;
   1440   int any = 0;
   1441 
   1442   if (!jit->image || jit->image->dbg_objs_n == 0) return NULL;
   1443   for (ii = 0; ii < jit->image->dbg_objs_n; ++ii) {
   1444     if (jit_view_input_has_debug(jit, ii)) {
   1445       any = 1;
   1446       break;
   1447     }
   1448   }
   1449   if (!any) return NULL;
   1450 
   1451   view =
   1452       kit_objfile_internal_new(jit->c->ctx, jit->c->target, jit->c->target.obj);
   1453   if (!view) return NULL;
   1454   view_ob = kit_objfile_builder(view);
   1455   if (!view_ob) {
   1456     kit_objfile_internal_free(view);
   1457     return NULL;
   1458   }
   1459   h = (Heap*)jit->c->ctx->heap;
   1460 
   1461   for (ii = 0; ii < jit->image->dbg_objs_n; ++ii) {
   1462     ObjBuilder* in_ob;
   1463     u32 nsec;
   1464     if (!jit_view_input_has_debug(jit, ii)) continue;
   1465     in_ob = jit->image->dbg_objs[ii];
   1466 
   1467     /* Phase A: find-or-create every debug section this input contributes
   1468      * to, and snapshot cur_size as the per-input prefix. */
   1469     nsec = obj_section_count(in_ob);
   1470     for (k = 0; k < nsec; ++k) {
   1471       const Section* s = obj_section_get(in_ob, (ObjSecId)(k + 1));
   1472       const char* nm;
   1473       ViewSec* vs;
   1474       if (!s || !s->name) continue;
   1475       nm = pool_slice(obj_compiler(in_ob)->global, s->name).s;
   1476       if (!nm || !jit_view_is_debug_name(nm)) continue;
   1477       vs = view_sec_for(jit, tab, &ntab, &cap, nm, s->flags,
   1478                         s->align ? s->align : 1u, s->entsize, view_ob, &tab);
   1479       if (!vs) continue;
   1480     }
   1481     for (k = 0; k < ntab; ++k) tab[k].snap = tab[k].cur_size;
   1482 
   1483     /* Phase B: copy each debug section + apply relocs.  cur_size grows
   1484      * as bytes get appended; SK_SECTION resolution always reads `snap`
   1485      * (the start-of-input snapshot), so intra-input references inside
   1486      * any debug section still land in this input's slice. */
   1487     for (k = 0; k < nsec; ++k) {
   1488       const Section* s = obj_section_get(in_ob, (ObjSecId)(k + 1));
   1489       const char* nm;
   1490       size_t nlen = 0;
   1491       Sym v_nm;
   1492       ViewSec* vs;
   1493       if (!s || !s->name) continue;
   1494       {
   1495         Slice nm_s = pool_slice(obj_compiler(in_ob)->global, s->name);
   1496         nm = nm_s.s;
   1497         nlen = nm_s.len;
   1498       }
   1499       if (!nm || !jit_view_is_debug_name(nm)) continue;
   1500       v_nm = pool_intern_slice(obj_compiler(view_ob)->global,
   1501                                (Slice){.s = nm, .len = nlen});
   1502       vs = view_sec_find(tab, ntab, v_nm);
   1503       if (!vs) continue;
   1504       jit_view_copy_debug_section(jit, ii, (ObjSecId)(k + 1), view_ob, tab,
   1505                                   ntab, vs);
   1506     }
   1507   }
   1508 
   1509   if (tab) h->free(h, tab, sizeof(*tab) * cap);
   1510   obj_finalize(view_ob);
   1511   return view;
   1512 }
   1513 
   1514 const KitObjFile* kit_jit_view(KitJit* jit) {
   1515   if (!jit) return NULL;
   1516   if (jit->view_built) return jit->view;
   1517   jit->view = jit_view_build(jit);
   1518   jit->view_built = 1u;
   1519   return jit->view;
   1520 }
   1521 
   1522 /* True for symbol kinds the user-facing JIT inspector surfaces. Mapping
   1523  * symbols (SK_NOTYPE — aarch64 $x/$d), section/file/undef symbols are
   1524  * skipped. */
   1525 static int jit_sym_kind_visible(u8 k) {
   1526   return k == SK_FUNC || k == SK_OBJ || k == SK_COMMON || k == SK_TLS ||
   1527          k == SK_IFUNC || k == SK_ABS;
   1528 }
   1529 
   1530 /* Resolve a LinkSymbol's interned name to a stable C string, stripping
   1531  * the target format's C-mangling prefix (Mach-O's leading `_`) so the
   1532  * user sees the source-level name. */
   1533 static Slice jit_sym_name_slice(KitJit* jit, const LinkSymbol* s) {
   1534   Slice nm_s = pool_slice(jit->c->global, s->name);
   1535   const char* nm = nm_s.s;
   1536   size_t len = nm_s.len;
   1537   if (!nm) return KIT_SLICE_NULL;
   1538   obj_format_demangle_c(jit->c, &nm, &len);
   1539   nm_s.s = nm;
   1540   nm_s.len = len;
   1541   return nm_s;
   1542 }
   1543 
   1544 static uint64_t jit_sym_runtime_addr(KitJit* jit, const LinkSymbol* s) {
   1545   if (s->kind == SK_ABS) return s->vaddr;
   1546   return (uint64_t)vaddr_to_runtime(jit->image, jit->segs, s->vaddr);
   1547 }
   1548 
   1549 KitStatus kit_jit_addr_to_sym(KitJit* jit, uint64_t addr, KitSlice* name_out,
   1550                               uint64_t* off_out) {
   1551   u32 n;
   1552   u32 i;
   1553   const LinkSymbol* best = NULL;
   1554   uint64_t best_base = 0;
   1555   uint64_t best_off = (uint64_t)-1;
   1556   if (name_out) *name_out = KIT_SLICE_NULL;
   1557   if (off_out) *off_out = 0;
   1558   if (!jit) return KIT_INVALID;
   1559   n = LinkSyms_count(&jit->image->syms);
   1560   for (i = 0; i < n; ++i) {
   1561     LinkSymbol* s = LinkSyms_at(&jit->image->syms, i);
   1562     uint64_t base;
   1563     if (!s || !s->defined) continue;
   1564     if (!jit_sym_kind_visible(s->kind)) continue;
   1565     base = jit_sym_runtime_addr(jit, s);
   1566     if (!base) continue;
   1567     if (addr >= base && (s->size == 0 || addr < base + s->size)) {
   1568       uint64_t off = addr - base;
   1569       if (off < best_off ||
   1570           (off == best_off && best && best->size == 0 && s->size != 0)) {
   1571         best = s;
   1572         best_base = base;
   1573         best_off = off;
   1574       }
   1575     }
   1576   }
   1577   if (best) {
   1578     if (name_out) *name_out = jit_sym_name_slice(jit, best);
   1579     if (off_out) *off_out = addr - best_base;
   1580     return KIT_OK;
   1581   }
   1582   return KIT_NOT_FOUND;
   1583 }
   1584 
   1585 struct KitJitSymIter {
   1586   KitJit* jit;
   1587   u32 next;
   1588 };
   1589 
   1590 KitStatus kit_jit_sym_iter_new(KitJit* jit, KitJitSymIter** out) {
   1591   Heap* h;
   1592   KitJitSymIter* it;
   1593   if (!out) return KIT_INVALID;
   1594   *out = NULL;
   1595   if (!jit) return KIT_INVALID;
   1596   h = (Heap*)jit->c->ctx->heap;
   1597   it = (KitJitSymIter*)h->alloc(h, sizeof(*it), _Alignof(KitJitSymIter));
   1598   if (!it) return KIT_NOMEM;
   1599   it->jit = jit;
   1600   it->next = 0;
   1601   *out = it;
   1602   return KIT_OK;
   1603 }
   1604 
   1605 KitIterResult kit_jit_sym_iter_next(KitJitSymIter* it, KitJitSym* out) {
   1606   u32 n;
   1607   if (!it || !out) return KIT_ITER_ERROR;
   1608   n = LinkSyms_count(&it->jit->image->syms);
   1609   while (it->next < n) {
   1610     LinkSymbol* s = LinkSyms_at(&it->jit->image->syms, it->next++);
   1611     if (!s || !s->defined) continue;
   1612     if (!jit_sym_kind_visible(s->kind)) continue;
   1613     out->name = jit_sym_name_slice(it->jit, s);
   1614     out->addr = jit_sym_runtime_addr(it->jit, s);
   1615     out->size = s->size;
   1616     out->kind = (KitSymKind)s->kind;
   1617     return KIT_ITER_ITEM;
   1618   }
   1619   return KIT_ITER_END;
   1620 }
   1621 
   1622 void kit_jit_sym_iter_free(KitJitSymIter* it) {
   1623   Heap* h;
   1624   if (!it) return;
   1625   h = (Heap*)it->jit->c->ctx->heap;
   1626   h->free(h, it, sizeof(*it));
   1627 }
   1628 
   1629 /* ---- accessors for src/dbg/ ----
   1630  *
   1631  * The KitJit struct is private to this TU. The debugger session needs a
   1632  * way to validate that an address lies inside the JIT image (so it can
   1633  * reject breakpoint and read/write requests pointing outside the code
   1634  * region) and a way to read the image's target arch. These accessors give
   1635  * it just that, without exporting the segment table or the LinkImage. */
   1636 int kit_jit_image_contains(KitJit* jit, uint64_t runtime_addr) {
   1637   u32 i;
   1638   uintptr_t a;
   1639   if (!jit || !jit->segs) return 0;
   1640   a = (uintptr_t)runtime_addr;
   1641   for (i = 0; i < jit->nsegs; ++i) {
   1642     uintptr_t lo = (uintptr_t)jit->segs[i].runtime;
   1643     uintptr_t hi = lo + (uintptr_t)jit->segs[i].size;
   1644     if (a >= lo && a < hi) return 1;
   1645   }
   1646   return 0;
   1647 }
   1648 
   1649 /* runtime <-> image vaddr translation.  The pair is symmetric with
   1650  * vaddr_to_runtime above; exposed here so dwarf consumers and the
   1651  * driver can cross the boundary at every DWARF call.  Walks the
   1652  * segment table (at most a handful of entries) so callers can do this
   1653  * per stop without measurable cost. */
   1654 uint64_t kit_jit_runtime_to_image(KitJit* jit, uint64_t runtime_pc) {
   1655   u32 i;
   1656   uintptr_t a;
   1657   if (!jit || !jit->segs) return 0;
   1658   a = (uintptr_t)runtime_pc;
   1659   for (i = 0; i < jit->nsegs; ++i) {
   1660     uintptr_t lo = (uintptr_t)jit->segs[i].runtime;
   1661     uintptr_t hi = lo + (uintptr_t)jit->segs[i].size;
   1662     if (a >= lo && a < hi) {
   1663       const LinkSegment* s = &jit->image->segments[i];
   1664       return s->vaddr + (uint64_t)(a - lo);
   1665     }
   1666   }
   1667   /* One-past-end: lets a return-address that sits exactly at a
   1668    * segment's end-boundary still round-trip. */
   1669   for (i = 0; i < jit->nsegs; ++i) {
   1670     uintptr_t hi =
   1671         (uintptr_t)jit->segs[i].runtime + (uintptr_t)jit->segs[i].size;
   1672     if (a == hi) {
   1673       const LinkSegment* s = &jit->image->segments[i];
   1674       return s->vaddr + s->mem_size;
   1675     }
   1676   }
   1677   return 0;
   1678 }
   1679 
   1680 uint64_t kit_jit_image_to_runtime(KitJit* jit, uint64_t image_vaddr) {
   1681   uintptr_t rt;
   1682   if (!jit || !jit->segs) return 0;
   1683   rt = vaddr_to_runtime(jit->image, jit->segs, image_vaddr);
   1684   return (uint64_t)rt;
   1685 }
   1686 
   1687 KitArchKind kit_jit_image_arch(KitJit* jit) { return jit->c->target.arch; }
   1688 
   1689 Compiler* kit_jit_compiler(KitJit* jit) { return jit->c; }
   1690 
   1691 void kit_jit_run_dtors(KitJit* jit) {
   1692   typedef void (*VoidFn)(void);
   1693   void* p_start;
   1694   void* p_end;
   1695   if (!jit) return;
   1696   p_start = kit_jit_lookup(jit, KIT_SLICE_LIT("__fini_array_start"));
   1697   p_end = kit_jit_lookup(jit, KIT_SLICE_LIT("__fini_array_end"));
   1698   if (p_start && p_end) {
   1699     VoidFn* begin = (VoidFn*)p_start;
   1700     VoidFn* fn = (VoidFn*)p_end;
   1701     while (fn != begin) {
   1702       --fn;
   1703       if (*fn) (*fn)();
   1704     }
   1705   }
   1706 }