kit

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

object_file.c (29612B)


      1 /* Public KitObjFile reader. Wraps the internal read_/obj_ surface
      2  * and exposes format-neutral object inspection. */
      3 
      4 #include <kit/object.h>
      5 #include <setjmp.h>
      6 #include <string.h>
      7 
      8 #include "core/buf.h"
      9 #include "core/core.h"
     10 #include "core/heap.h"
     11 #include "core/pool.h"
     12 #include "core/slice.h"
     13 #include "core/vec.h"
     14 #include "obj/format.h"
     15 #include "obj/obj.h"
     16 
     17 struct KitObjFile {
     18   Compiler compiler;
     19   const KitContext* ctx;
     20   KitTarget* resolved_target;
     21   ObjBuilder* ob;
     22   ObjFmt fmt;
     23   KitTargetSpec target;
     24   const u8** sec_data_cache;
     25   u32* sec_data_size;
     26   u32 sec_data_n;
     27 };
     28 
     29 KitStatus kit_obj_open(const KitContext* ctx, KitSlice name,
     30                        const KitSlice* input, KitObjFile** out) {
     31   Heap* h;
     32   KitObjFile* f;
     33   const ObjFormatImpl* impl;
     34   KitTargetSpec target;
     35   KitStatus st;
     36 
     37   if (!out) return KIT_INVALID;
     38   *out = NULL;
     39   if (!ctx || !ctx->heap || !input) return KIT_INVALID;
     40   if (!input->data && input->len > 0) return KIT_INVALID;
     41 
     42   st = kit_detect_target(input->data, input->len, &target);
     43   if (st != KIT_OK) return st;
     44   impl = obj_format_lookup(target.obj);
     45   if (!impl || !impl->read) return KIT_UNSUPPORTED;
     46 
     47   h = ctx->heap;
     48   f = (KitObjFile*)h->alloc(h, sizeof(*f), _Alignof(KitObjFile));
     49   if (!f) return KIT_NOMEM;
     50   memset(f, 0, sizeof(*f));
     51   f->ctx = ctx;
     52   f->fmt = target.obj;
     53   f->target = target;
     54 
     55   {
     56     KitTargetOptions topts;
     57     memset(&topts, 0, sizeof topts);
     58     topts.spec = target;
     59     st = kit_target_new(ctx, &topts, &f->resolved_target);
     60     if (st != KIT_OK) {
     61       h->free(h, f, sizeof(*f));
     62       return st;
     63     }
     64   }
     65   st = compiler_init(&f->compiler, f->resolved_target, ctx);
     66   if (st != KIT_OK) {
     67     kit_target_free(f->resolved_target);
     68     h->free(h, f, sizeof(*f));
     69     return st;
     70   }
     71   {
     72     PanicFrame panic;
     73     compiler_panic_push(&f->compiler, &panic);
     74     if (setjmp(panic.env)) {
     75       compiler_run_cleanups(&f->compiler);
     76       compiler_panic_pop(&f->compiler, &panic);
     77       compiler_fini(&f->compiler);
     78       kit_target_free(f->resolved_target);
     79       h->free(h, f, sizeof(*f));
     80       return KIT_MALFORMED;
     81     }
     82     f->ob = impl->read(&f->compiler, name.s, input->data, input->len);
     83     compiler_panic_pop(&f->compiler, &panic);
     84   }
     85   if (!f->ob) {
     86     compiler_fini(&f->compiler);
     87     kit_target_free(f->resolved_target);
     88     h->free(h, f, sizeof(*f));
     89     return KIT_MALFORMED;
     90   }
     91   *out = f;
     92   return KIT_OK;
     93 }
     94 
     95 void kit_obj_free(KitObjFile* f) {
     96   Heap* h;
     97   if (!f) return;
     98   h = f->ctx->heap;
     99   if (f->sec_data_cache) {
    100     u32 i;
    101     for (i = 0; i < f->sec_data_n; ++i) {
    102       if (f->sec_data_cache[i]) {
    103         h->free(h, (void*)f->sec_data_cache[i], f->sec_data_size[i]);
    104       }
    105     }
    106     h->free(h, f->sec_data_cache, sizeof(*f->sec_data_cache) * f->sec_data_n);
    107     h->free(h, f->sec_data_size, sizeof(*f->sec_data_size) * f->sec_data_n);
    108   }
    109   if (f->ob) obj_free(f->ob);
    110   compiler_fini(&f->compiler);
    111   kit_target_free(f->resolved_target);
    112   h->free(h, f, sizeof(*f));
    113 }
    114 
    115 KitObjFmt kit_obj_fmt(const KitObjFile* f) { return f->fmt; }
    116 
    117 /* Public object-format name <-> KitObjFmt mapping (objcopy/objdump bfdname
    118  * spellings). Thin wrappers over the internal obj_format name registry. */
    119 KitStatus kit_obj_fmt_from_name(const char* name, KitObjFmt* out) {
    120   return obj_format_fmt_from_name(name, out) ? KIT_OK : KIT_NOT_FOUND;
    121 }
    122 
    123 const char* kit_obj_fmt_name(KitObjFmt fmt) { return obj_format_fmt_name(fmt); }
    124 
    125 KitTargetSpec kit_obj_target(const KitObjFile* f) { return f->target; }
    126 
    127 uint32_t kit_obj_nsections(const KitObjFile* f) {
    128   return obj_section_count(f->ob);
    129 }
    130 
    131 KitStatus kit_obj_section(const KitObjFile* f, KitObjSection idx,
    132                           KitObjSecInfo* out) {
    133   const Section* sec;
    134   if (!f || !out) return KIT_INVALID;
    135   if (idx >= obj_section_count(f->ob)) return KIT_NOT_FOUND;
    136   sec = obj_section_get(f->ob, (ObjSecId)(idx + 1));
    137   if (!sec) return KIT_NOT_FOUND;
    138   out->name =
    139       sec->name ? pool_slice(f->compiler.global, sec->name) : SLICE_LIT("");
    140   out->kind = (KitSecKind)sec->kind;
    141   out->flags = (uint32_t)sec->flags;
    142   out->size = sec->bss_size ? sec->bss_size : sec->bytes.total;
    143   out->addr = sec->addr;
    144   out->align = sec->align > 1u ? sec->align : 1u;
    145   out->entsize = sec->entsize;
    146   return KIT_OK;
    147 }
    148 
    149 KitStatus kit_obj_section_data(const KitObjFile* cf, KitObjSection idx,
    150                                const uint8_t** data_out, size_t* len_out) {
    151   KitObjFile* f = (KitObjFile*)cf;
    152   const Section* sec;
    153   Heap* h;
    154   u32 n;
    155   u8* buf;
    156 
    157   if (!f || !data_out || !len_out) return KIT_INVALID;
    158   *data_out = NULL;
    159   *len_out = 0;
    160 
    161   n = obj_section_count(f->ob);
    162   if (idx >= n) return KIT_NOT_FOUND;
    163 
    164   sec = obj_section_get(f->ob, (ObjSecId)(idx + 1));
    165   if (!sec) return KIT_NOT_FOUND;
    166   if (sec->bss_size || sec->bytes.total == 0) return KIT_OK;
    167 
    168   h = f->ctx->heap;
    169 
    170   if (!f->sec_data_cache) {
    171     f->sec_data_cache = (const u8**)h->alloc(h, sizeof(*f->sec_data_cache) * n,
    172                                              _Alignof(const u8*));
    173     if (!f->sec_data_cache) return KIT_NOMEM;
    174     f->sec_data_size =
    175         (u32*)h->alloc(h, sizeof(*f->sec_data_size) * n, _Alignof(u32));
    176     if (!f->sec_data_size) {
    177       h->free(h, f->sec_data_cache, sizeof(*f->sec_data_cache) * n);
    178       f->sec_data_cache = NULL;
    179       return KIT_NOMEM;
    180     }
    181     {
    182       u32 i;
    183       for (i = 0; i < n; ++i) {
    184         f->sec_data_cache[i] = NULL;
    185         f->sec_data_size[i] = 0;
    186       }
    187     }
    188     f->sec_data_n = n;
    189   }
    190 
    191   if (f->sec_data_cache[idx]) {
    192     *data_out = f->sec_data_cache[idx];
    193     *len_out = f->sec_data_size[idx];
    194     return KIT_OK;
    195   }
    196 
    197   buf = (u8*)h->alloc(h, sec->bytes.total, 1);
    198   if (!buf) return KIT_NOMEM;
    199   buf_flatten(&sec->bytes, buf);
    200   f->sec_data_cache[idx] = buf;
    201   f->sec_data_size[idx] = sec->bytes.total;
    202   *data_out = buf;
    203   *len_out = sec->bytes.total;
    204   return KIT_OK;
    205 }
    206 
    207 KitStatus kit_obj_section_format_flags(const KitObjFile* f, KitObjSection idx,
    208                                        uint32_t* raw_type_out,
    209                                        uint32_t* raw_flags_out) {
    210   const Section* sec;
    211   if (!f) return KIT_INVALID;
    212   if (idx >= obj_section_count(f->ob)) return KIT_NOT_FOUND;
    213   sec = obj_section_get(f->ob, (ObjSecId)(idx + 1));
    214   if (!sec) return KIT_NOT_FOUND;
    215   if (raw_type_out) *raw_type_out = sec->ext_type;
    216   if (raw_flags_out) *raw_flags_out = sec->ext_flags;
    217   return KIT_OK;
    218 }
    219 
    220 KitStatus kit_obj_section_by_name(const KitObjFile* f, KitSlice name,
    221                                   KitObjSection* out) {
    222   u32 n, i;
    223   if (!f || !out) return KIT_INVALID;
    224   n = obj_section_count(f->ob);
    225   for (i = 0; i < n; ++i) {
    226     const Section* sec = obj_section_get(f->ob, (ObjSecId)(i + 1));
    227     if (!sec || !sec->name) continue;
    228     if (slice_eq(pool_slice(f->compiler.global, sec->name), name)) {
    229       *out = i;
    230       return KIT_OK;
    231     }
    232   }
    233   return KIT_NOT_FOUND;
    234 }
    235 
    236 int kit_obj_section_size_class(const KitObjSecInfo* sec) {
    237   int exec, write;
    238   if (!sec) return KIT_SEC_SIZE_NONE;
    239   exec = (sec->flags & KIT_SF_EXEC) != 0;
    240   write = (sec->flags & KIT_SF_WRITE) != 0;
    241   if (!(sec->flags & KIT_SF_ALLOC)) return KIT_SEC_SIZE_NONE;
    242   if (sec->kind == KIT_SEC_DEBUG) return KIT_SEC_SIZE_NONE;
    243   if (sec->kind == KIT_SEC_TEXT || exec) return KIT_SEC_SIZE_TEXT;
    244   if (sec->kind == KIT_SEC_BSS) return KIT_SEC_SIZE_BSS;
    245   if (sec->kind == KIT_SEC_RODATA) return KIT_SEC_SIZE_DATA;
    246   if (sec->kind == KIT_SEC_DATA || write) return KIT_SEC_SIZE_DATA;
    247   return KIT_SEC_SIZE_DATA;
    248 }
    249 
    250 KitStatus kit_obj_size_totals(KitObjFile* f, bool include_common,
    251                               KitObjSizeTotals* out) {
    252   u32 ns, i;
    253   if (!f || !out) return KIT_INVALID;
    254   out->text = 0;
    255   out->data = 0;
    256   out->bss = 0;
    257   out->total = 0;
    258   ns = obj_section_count(f->ob);
    259   for (i = 0; i < ns; ++i) {
    260     KitObjSecInfo sec;
    261     if (kit_obj_section(f, i, &sec) != KIT_OK) continue;
    262     switch (kit_obj_section_size_class(&sec)) {
    263       case KIT_SEC_SIZE_TEXT:
    264         out->text += sec.size;
    265         break;
    266       case KIT_SEC_SIZE_DATA:
    267         out->data += sec.size;
    268         break;
    269       case KIT_SEC_SIZE_BSS:
    270         out->bss += sec.size;
    271         break;
    272       default:
    273         break;
    274     }
    275   }
    276   if (include_common) {
    277     KitObjSymIter* it = NULL;
    278     if (kit_obj_symiter_new(f, &it) == KIT_OK) {
    279       KitObjSymInfo si;
    280       while (kit_obj_symiter_next(it, &si) == KIT_ITER_ITEM) {
    281         if (si.kind == KIT_SK_COMMON) out->bss += si.size;
    282       }
    283       kit_obj_symiter_free(it);
    284     }
    285   }
    286   out->total = out->text + out->data + out->bss;
    287   return KIT_OK;
    288 }
    289 
    290 static void fill_syminfo(const KitObjFile* f, ObjSymId id, const ObjSym* sym,
    291                          KitObjSymInfo* out) {
    292   out->name =
    293       sym->name ? pool_slice(f->compiler.global, sym->name) : SLICE_LIT("");
    294   out->id = (id != OBJ_SYM_NONE) ? (KitObjSymbol)id : KIT_OBJ_SYMBOL_NONE;
    295   out->bind = (KitSymBind)sym->bind;
    296   out->kind = (KitSymKind)sym->kind;
    297   out->section = sym->section_id != OBJ_SEC_NONE
    298                      ? (KitObjSection)(sym->section_id - 1)
    299                      : KIT_SECTION_NONE;
    300   out->value = sym->value;
    301   out->size = sym->size;
    302 }
    303 
    304 KitStatus kit_obj_symbol_by_name(const KitObjFile* f, KitSlice name,
    305                                  KitObjSymInfo* out) {
    306   ObjSymIter* it;
    307   ObjSymEntry e;
    308   if (!f || !out) return KIT_INVALID;
    309   it = obj_symiter_new(f->ob);
    310   if (!it) return KIT_NOMEM;
    311   while (obj_symiter_next(it, &e)) {
    312     if (!e.sym || !e.sym->name) continue;
    313     if (slice_eq(pool_slice(f->compiler.global, e.sym->name), name)) {
    314       fill_syminfo(f, e.id, e.sym, out);
    315       obj_symiter_free(it);
    316       return KIT_OK;
    317     }
    318   }
    319   obj_symiter_free(it);
    320   return KIT_NOT_FOUND;
    321 }
    322 
    323 struct KitObjSymIter {
    324   const KitObjFile* file;
    325   ObjSymIter* inner; /* .symtab walk; NULL when iterating the dynamic table */
    326   u32 dyn_idx;       /* next index into obj_image dynsyms (dynamic mode) */
    327   int dynamic;
    328 };
    329 
    330 /* Shared by kit_obj_symiter_new (.symtab) and kit_obj_dynsymiter_new
    331  * (.dynsym). When dynamic, the inner ObjSymIter is unused and we walk the
    332  * image's dynamic symbol table by index. */
    333 static KitStatus symiter_make(const KitObjFile* f, int dynamic,
    334                               KitObjSymIter** out) {
    335   Heap* h;
    336   KitObjSymIter* it;
    337   if (!f || !out) return KIT_INVALID;
    338   h = f->ctx->heap;
    339   it = (KitObjSymIter*)h->alloc(h, sizeof(*it), _Alignof(KitObjSymIter));
    340   if (!it) return KIT_NOMEM;
    341   it->file = f;
    342   it->inner = NULL;
    343   it->dyn_idx = 0;
    344   it->dynamic = dynamic;
    345   if (!dynamic) {
    346     it->inner = obj_symiter_new(f->ob);
    347     if (!it->inner) {
    348       h->free(h, it, sizeof(*it));
    349       return KIT_NOMEM;
    350     }
    351   }
    352   *out = it;
    353   return KIT_OK;
    354 }
    355 
    356 KitStatus kit_obj_symiter_new(const KitObjFile* f, KitObjSymIter** out) {
    357   return symiter_make(f, 0, out);
    358 }
    359 
    360 KitStatus kit_obj_dynsymiter_new(const KitObjFile* f, KitObjSymIter** out) {
    361   return symiter_make(f, 1, out);
    362 }
    363 
    364 KitIterResult kit_obj_symiter_next(KitObjSymIter* it, KitObjSymInfo* out) {
    365   ObjSymEntry entry;
    366   if (!it || !out) return KIT_ITER_ERROR;
    367   if (it->dynamic) {
    368     const ObjImage* im = obj_image(it->file->ob);
    369     const ObjImageSym* s;
    370     if (it->dyn_idx >= obj_image_ndynsyms(im)) return KIT_ITER_END;
    371     s = obj_image_dynsym(im, it->dyn_idx++);
    372     out->name = pool_slice(it->file->compiler.global, s->name);
    373     out->id = KIT_OBJ_SYMBOL_NONE;
    374     out->bind = (KitSymBind)s->bind;
    375     out->kind = (KitSymKind)s->kind;
    376     out->section = s->section != OBJ_SEC_NONE ? (KitObjSection)(s->section - 1)
    377                                               : KIT_SECTION_NONE;
    378     out->value = s->value;
    379     out->size = s->size;
    380     return KIT_ITER_ITEM;
    381   }
    382   if (!obj_symiter_next(it->inner, &entry)) return KIT_ITER_END;
    383   fill_syminfo(it->file, entry.id, entry.sym, out);
    384   return KIT_ITER_ITEM;
    385 }
    386 
    387 void kit_obj_symiter_free(KitObjSymIter* it) {
    388   Heap* h;
    389   if (!it) return;
    390   if (it->inner) obj_symiter_free(it->inner);
    391   h = it->file->ctx->heap;
    392   h->free(h, it, sizeof(*it));
    393 }
    394 
    395 struct KitObjRelocIter {
    396   KitObjFile* file;
    397   u32 idx;
    398   u32 total;
    399   int dynamic; /* iterate obj_image dynamic relocs instead of section relocs */
    400 };
    401 
    402 static KitStatus reliter_make(KitObjFile* f, int dynamic,
    403                               KitObjRelocIter** out) {
    404   Heap* h;
    405   KitObjRelocIter* it;
    406   if (!f || !out) return KIT_INVALID;
    407   h = f->ctx->heap;
    408   it = (KitObjRelocIter*)h->alloc(h, sizeof(*it), _Alignof(KitObjRelocIter));
    409   if (!it) return KIT_NOMEM;
    410   it->file = f;
    411   it->idx = 0;
    412   it->dynamic = dynamic;
    413   it->total =
    414       dynamic ? obj_image_ndynrelocs(obj_image(f->ob)) : obj_reloc_total(f->ob);
    415   *out = it;
    416   return KIT_OK;
    417 }
    418 
    419 KitStatus kit_obj_reliter_new(KitObjFile* f, KitObjRelocIter** out) {
    420   return reliter_make(f, 0, out);
    421 }
    422 
    423 KitStatus kit_obj_dynreliter_new(KitObjFile* f, KitObjRelocIter** out) {
    424   return reliter_make(f, 1, out);
    425 }
    426 
    427 /* Format-specific canonical spelling of a reloc kind (e.g. "R_X86_64_PLT32",
    428  * "R_AARCH64_CALL26", "R_RISCV_CALL"), or NULL when the format has no per-arch
    429  * name table (callers fall back to the arch-neutral reloc_kind_name).
    430  *
    431  * Consulted for every ELF arch whose ObjElfArchOps carries a reloc_name table
    432  * (x86_64 / aarch64 / riscv): the kit-canonical RelocKind is lowered to its
    433  * ELF wire type via reloc_to, then named — matching binutils objdump's
    434  * spelling. The Mach-O / COFF formats have no reloc_name table yet and keep
    435  * the arch-neutral spelling. reloc_to maps unsupported kinds to wire type 0;
    436  * only R_NONE legitimately names that slot, so anything else falling through
    437  * to 0 is reported as "no per-arch name" (NULL) rather than the format's NONE
    438  * spelling. */
    439 static const char* kit_obj_reloc_kind_name(KitArchKind arch, KitObjFmt fmt,
    440                                            u32 kind) {
    441   const ObjFormatImpl* impl;
    442   const ObjElfArchOps* ops;
    443   u32 wire;
    444   if (fmt != KIT_OBJ_ELF) return NULL;
    445   impl = obj_format_lookup(fmt);
    446   if (!impl || !impl->elf_arch) return NULL;
    447   ops = impl->elf_arch(arch);
    448   if (!ops || !ops->reloc_to || !ops->reloc_name) return NULL;
    449   wire = ops->reloc_to(kind);
    450   if (wire == 0u && (RelocKind)kind != R_NONE) return NULL;
    451   return ops->reloc_name(wire);
    452 }
    453 
    454 KitIterResult kit_obj_reliter_next(KitObjRelocIter* it, KitObjReloc* out) {
    455   const Reloc* r;
    456   const ObjSym* sym;
    457   if (!it || !out) return KIT_ITER_ERROR;
    458   if (it->idx >= it->total) return KIT_ITER_END;
    459 
    460   if (it->dynamic) {
    461     const ObjImageReloc* dr =
    462         obj_image_dynreloc(obj_image(it->file->ob), it->idx++);
    463     const char* kn;
    464     out->section =
    465         dr->section ? (KitObjSection)(dr->section - 1) : KIT_SECTION_NONE;
    466     out->offset = dr->offset;
    467     out->addend = dr->addend;
    468     out->kind.arch = it->file->target.arch;
    469     out->kind.obj_fmt = it->file->fmt;
    470     out->kind.code = (uint32_t)dr->kind;
    471     kn =
    472         kit_obj_reloc_kind_name(it->file->target.arch, it->file->fmt, dr->kind);
    473     if (!kn) kn = reloc_kind_name(dr->kind);
    474     out->kind_name = kn ? slice_from_cstr(kn) : SLICE_NULL;
    475     out->sym = KIT_OBJ_SYMBOL_NONE;
    476     out->sym_name = pool_slice(it->file->compiler.global, dr->sym_name);
    477     return KIT_ITER_ITEM;
    478   }
    479 
    480   r = obj_reloc_at(it->file->ob, it->idx++);
    481   out->section =
    482       r->section_id ? (KitObjSection)(r->section_id - 1) : KIT_SECTION_NONE;
    483   out->offset = r->offset;
    484   out->addend = r->addend;
    485   out->kind.arch = it->file->target.arch;
    486   out->kind.obj_fmt = it->file->fmt;
    487   out->kind.code = (uint32_t)r->kind;
    488   {
    489     /* Prefer the format-specific canonical spelling (e.g. "R_X86_64_PLT32");
    490      * fall back to the arch-neutral diagnostic spelling ("RV_CALL"). */
    491     const char* kn =
    492         kit_obj_reloc_kind_name(it->file->target.arch, it->file->fmt, r->kind);
    493     if (!kn) kn = reloc_kind_name(r->kind);
    494     out->kind_name = kn ? slice_from_cstr(kn) : SLICE_NULL;
    495   }
    496 
    497   if (r->sym == OBJ_SYM_NONE) {
    498     out->sym = KIT_OBJ_SYMBOL_NONE;
    499     out->sym_name = SLICE_LIT("");
    500   } else {
    501     out->sym = (KitObjSymbol)r->sym;
    502     sym = obj_symbol_get(it->file->ob, r->sym);
    503     out->sym_name = (sym && sym->name)
    504                         ? pool_slice(it->file->compiler.global, sym->name)
    505                         : SLICE_LIT("");
    506   }
    507   return KIT_ITER_ITEM;
    508 }
    509 
    510 void kit_obj_reliter_free(KitObjRelocIter* it) {
    511   Heap* h;
    512   if (!it) return;
    513   h = it->file->ctx->heap;
    514   h->free(h, it, sizeof(*it));
    515 }
    516 
    517 static int reloc_live_id_in_set(KitObjSymbol id, const KitObjSymbol* arr,
    518                                 u32 n) {
    519   u32 i;
    520   for (i = 0; i < n; ++i)
    521     if (arr[i] == id) return 1;
    522   return 0;
    523 }
    524 
    525 KitStatus kit_obj_reloc_live_symbols(const KitContext* ctx, KitObjFile* f,
    526                                      KitObjSymbol** out, uint32_t* n_out) {
    527   Heap* h;
    528   KitObjRelocIter* rit = NULL;
    529   KitObjSymbol* arr = NULL;
    530   u32 n = 0, cap = 0;
    531   KitStatus st;
    532 
    533   if (!out || !n_out) return KIT_INVALID;
    534   *out = NULL;
    535   *n_out = 0;
    536   if (!ctx || !ctx->heap || !f) return KIT_INVALID;
    537   h = ctx->heap;
    538 
    539   st = kit_obj_reliter_new(f, &rit);
    540   if (st != KIT_OK) return st;
    541   for (;;) {
    542     KitObjReloc r;
    543     KitIterResult ir = kit_obj_reliter_next(rit, &r);
    544     if (ir != KIT_ITER_ITEM) break;
    545     if (r.sym == KIT_OBJ_SYMBOL_NONE) continue;
    546     /* Skip relocs hosted in a debug section -- that section is being dropped,
    547      * so its relocs don't actually keep their targets alive. */
    548     if (r.section != KIT_SECTION_NONE) {
    549       KitObjSecInfo hi;
    550       if (kit_obj_section(f, r.section, &hi) == KIT_OK &&
    551           hi.kind == KIT_SEC_DEBUG)
    552         continue;
    553     }
    554     if (reloc_live_id_in_set(r.sym, arr, n)) continue;
    555     if (n >= cap) {
    556       u32 newcap = cap ? cap * 2u : 32u;
    557       KitObjSymbol* nb =
    558           (KitObjSymbol*)h->alloc(h, (size_t)newcap * sizeof(*nb),
    559                                   _Alignof(KitObjSymbol));
    560       if (!nb) {
    561         kit_obj_reliter_free(rit);
    562         if (arr) h->free(h, arr, (size_t)cap * sizeof(*arr));
    563         return KIT_NOMEM;
    564       }
    565       if (arr) {
    566         memcpy(nb, arr, (size_t)n * sizeof(*arr));
    567         h->free(h, arr, (size_t)cap * sizeof(*arr));
    568       }
    569       arr = nb;
    570       cap = newcap;
    571     }
    572     arr[n++] = r.sym;
    573   }
    574   kit_obj_reliter_free(rit);
    575 
    576   /* Hand back an exactly-sized array so the caller frees with n_out, not a
    577    * private capacity. */
    578   if (n == 0) {
    579     if (arr) h->free(h, arr, (size_t)cap * sizeof(*arr));
    580     return KIT_OK;
    581   }
    582   if (n != cap) {
    583     KitObjSymbol* shrunk =
    584         (KitObjSymbol*)h->alloc(h, (size_t)n * sizeof(*shrunk),
    585                                 _Alignof(KitObjSymbol));
    586     if (!shrunk) {
    587       /* Keep the over-sized buffer rather than fail; but the caller frees with
    588        * n, which would mismatch. Safer to report NOMEM and release. */
    589       h->free(h, arr, (size_t)cap * sizeof(*arr));
    590       return KIT_NOMEM;
    591     }
    592     memcpy(shrunk, arr, (size_t)n * sizeof(*shrunk));
    593     h->free(h, arr, (size_t)cap * sizeof(*arr));
    594     arr = shrunk;
    595   }
    596   *out = arr;
    597   *n_out = n;
    598   return KIT_OK;
    599 }
    600 
    601 struct KitObjGroupIter {
    602   KitObjFile* file;
    603   ObjGroupIter* inner;
    604   /* Translation scratch for the borrowed `sections` slice handed back to
    605    * the caller. Lazily grown to the largest group's nsections. */
    606   KitObjSection* secbuf;
    607   u32 seccap;
    608 };
    609 
    610 KitStatus kit_obj_groupiter_new(KitObjFile* f, KitObjGroupIter** out) {
    611   Heap* h;
    612   KitObjGroupIter* it;
    613   if (!f || !out) return KIT_INVALID;
    614   h = f->ctx->heap;
    615   it = (KitObjGroupIter*)h->alloc(h, sizeof(*it), _Alignof(KitObjGroupIter));
    616   if (!it) return KIT_NOMEM;
    617   memset(it, 0, sizeof(*it));
    618   it->file = f;
    619   it->inner = obj_groupiter_new(f->ob);
    620   if (!it->inner) {
    621     h->free(h, it, sizeof(*it));
    622     return KIT_NOMEM;
    623   }
    624   *out = it;
    625   return KIT_OK;
    626 }
    627 
    628 KitIterResult kit_obj_groupiter_next(KitObjGroupIter* it,
    629                                      KitObjGroupInfo* out) {
    630   ObjGroupEntry entry;
    631   Heap* h;
    632   u32 i;
    633   if (!it || !out) return KIT_ITER_ERROR;
    634   if (!obj_groupiter_next(it->inner, &entry)) return KIT_ITER_END;
    635   h = it->file->ctx->heap;
    636   if (entry.group->nsections > it->seccap) {
    637     KitObjSection* nb;
    638     nb = (KitObjSection*)h->alloc(h, sizeof(*nb) * entry.group->nsections,
    639                                   _Alignof(KitObjSection));
    640     if (!nb) return KIT_ITER_ERROR;
    641     if (it->secbuf) h->free(h, it->secbuf, sizeof(*it->secbuf) * it->seccap);
    642     it->secbuf = nb;
    643     it->seccap = entry.group->nsections;
    644   }
    645   for (i = 0; i < entry.group->nsections; ++i) {
    646     ObjSecId sid = entry.group->sections[i];
    647     it->secbuf[i] =
    648         (sid != OBJ_SEC_NONE) ? (KitObjSection)(sid - 1) : KIT_SECTION_NONE;
    649   }
    650   out->name = entry.group->name
    651                   ? pool_slice(it->file->compiler.global, entry.group->name)
    652                   : KIT_SLICE_NULL;
    653   out->signature = (entry.group->signature != OBJ_SYM_NONE)
    654                        ? (KitObjSymbol)entry.group->signature
    655                        : KIT_OBJ_SYMBOL_NONE;
    656   out->flags = entry.group->flags;
    657   out->nsections = entry.group->nsections;
    658   out->sections = it->secbuf;
    659   return KIT_ITER_ITEM;
    660 }
    661 
    662 void kit_obj_groupiter_free(KitObjGroupIter* it) {
    663   Heap* h;
    664   if (!it) return;
    665   h = it->file->ctx->heap;
    666   if (it->secbuf) h->free(h, it->secbuf, sizeof(*it->secbuf) * it->seccap);
    667   obj_groupiter_free(it->inner);
    668   h->free(h, it, sizeof(*it));
    669 }
    670 
    671 /* Accessor for disasm/jit to access the underlying ObjBuilder when both
    672  * are inside libkit. Internal name kept stable for existing callers
    673  * (src/link/link_jit.c, src/api/disasm.c). */
    674 ObjBuilder* kit_objfile_builder(const KitObjFile* f) {
    675   return f ? f->ob : NULL;
    676 }
    677 
    678 /* Public alias of kit_objfile_builder. Promoted to the public API so the
    679  * driver (and other libkit consumers) can take an opened object and feed
    680  * it into kit_obj_builder_emit for a byte-equivalent roundtrip without
    681  * re-implementing the read-then-replay loop. */
    682 KitObjBuilder* kit_obj_file_builder(const KitObjFile* f) {
    683   return kit_objfile_builder(f);
    684 }
    685 
    686 /* Allocate an empty KitObjFile wrapping a private Compiler and a fresh
    687  * ObjBuilder. Used by the JIT debug-view builder (src/link/link_jit.c)
    688  * to assemble a synthetic object file from merged input debug sections.
    689  * The handle is freed via kit_objfile_internal_free below — the public
    690  * kit_obj_free path is keyed off obj_read_bytes, so the view path uses
    691  * its own teardown that does not depend on the cached section data
    692  * tables. */
    693 KitObjFile* kit_objfile_internal_new(const KitContext* ctx,
    694                                      KitTargetSpec target, KitObjFmt fmt) {
    695   Heap* h;
    696   KitObjFile* f;
    697   if (!ctx || !ctx->heap) return NULL;
    698   h = ctx->heap;
    699   f = (KitObjFile*)h->alloc(h, sizeof(*f), _Alignof(KitObjFile));
    700   if (!f) return NULL;
    701   memset(f, 0, sizeof(*f));
    702   f->ctx = ctx;
    703   f->fmt = fmt;
    704   f->target = target;
    705   {
    706     KitTargetOptions topts;
    707     KitStatus st;
    708     memset(&topts, 0, sizeof topts);
    709     topts.spec = target;
    710     st = kit_target_new(ctx, &topts, &f->resolved_target);
    711     if (st != KIT_OK) {
    712       h->free(h, f, sizeof(*f));
    713       return NULL;
    714     }
    715     st = compiler_init(&f->compiler, f->resolved_target, ctx);
    716     if (st != KIT_OK) {
    717       kit_target_free(f->resolved_target);
    718       h->free(h, f, sizeof(*f));
    719       return NULL;
    720     }
    721   }
    722   {
    723     PanicFrame panic;
    724     compiler_panic_push(&f->compiler, &panic);
    725     if (setjmp(panic.env)) {
    726       compiler_run_cleanups(&f->compiler);
    727       compiler_panic_pop(&f->compiler, &panic);
    728       compiler_fini(&f->compiler);
    729       kit_target_free(f->resolved_target);
    730       h->free(h, f, sizeof(*f));
    731       return NULL;
    732     }
    733     f->ob = obj_new(&f->compiler);
    734     compiler_panic_pop(&f->compiler, &panic);
    735   }
    736   if (!f->ob) {
    737     compiler_fini(&f->compiler);
    738     kit_target_free(f->resolved_target);
    739     h->free(h, f, sizeof(*f));
    740     return NULL;
    741   }
    742   return f;
    743 }
    744 
    745 void kit_objfile_internal_free(KitObjFile* f) {
    746   /* Same teardown contract as kit_obj_free: caller may have cached
    747    * section data, so we route through the same path. */
    748   kit_obj_free(f);
    749 }
    750 
    751 /* ============================================================
    752  * Linked-image view
    753  * ============================================================ */
    754 
    755 KitObjKind kit_obj_kind(const KitObjFile* f) {
    756   const ObjImage* im;
    757   if (!f) return KIT_OBJ_KIND_REL;
    758   im = obj_image(f->ob);
    759   if (!im) return KIT_OBJ_KIND_REL;
    760   switch (obj_image_kind(im)) {
    761     case OBJ_KIND_EXEC:
    762       return KIT_OBJ_KIND_EXEC;
    763     case OBJ_KIND_DYN:
    764       return KIT_OBJ_KIND_DYN;
    765     case OBJ_KIND_CORE:
    766       return KIT_OBJ_KIND_CORE;
    767     case OBJ_KIND_REL:
    768     default:
    769       return KIT_OBJ_KIND_REL;
    770   }
    771 }
    772 
    773 KitStatus kit_obj_image_info(const KitObjFile* f, KitObjImageInfo* out) {
    774   const ObjImage* im;
    775   if (!f || !out) return KIT_INVALID;
    776   im = obj_image(f->ob);
    777   if (!im) return KIT_NOT_FOUND;
    778   out->entry = obj_image_entry(im);
    779   out->image_base = obj_image_base(im);
    780   out->interp = pool_slice(f->compiler.global, obj_image_interp(im));
    781   out->soname = pool_slice(f->compiler.global, obj_image_soname(im));
    782   return KIT_OK;
    783 }
    784 
    785 struct KitObjSegIter {
    786   KitObjFile* file;
    787   u32 idx;
    788 };
    789 
    790 KitStatus kit_obj_segiter_new(KitObjFile* f, KitObjSegIter** out) {
    791   Heap* h;
    792   KitObjSegIter* it;
    793   if (!f || !out) return KIT_INVALID;
    794   h = f->ctx->heap;
    795   it = (KitObjSegIter*)h->alloc(h, sizeof(*it), _Alignof(KitObjSegIter));
    796   if (!it) return KIT_NOMEM;
    797   it->file = f;
    798   it->idx = 0;
    799   *out = it;
    800   return KIT_OK;
    801 }
    802 
    803 KitIterResult kit_obj_segiter_next(KitObjSegIter* it, KitObjSegInfo* out) {
    804   const ObjImage* im;
    805   const ObjSegment* s;
    806   if (!it || !out) return KIT_ITER_ERROR;
    807   im = obj_image(it->file->ob);
    808   if (it->idx >= obj_image_nsegments(im)) return KIT_ITER_END;
    809   s = obj_image_segment(im, it->idx++);
    810   out->name = pool_slice(it->file->compiler.global, s->name);
    811   out->vaddr = s->vaddr;
    812   out->paddr = s->paddr;
    813   out->vsize = s->vsize;
    814   out->file_off = s->file_off;
    815   out->file_size = s->file_size;
    816   out->perms = s->perms; /* OBJ_SEG_* and KIT_SEG_* share bit values */
    817   out->align = s->align;
    818   return KIT_ITER_ITEM;
    819 }
    820 
    821 void kit_obj_segiter_free(KitObjSegIter* it) {
    822   Heap* h;
    823   if (!it) return;
    824   h = it->file->ctx->heap;
    825   h->free(h, it, sizeof(*it));
    826 }
    827 
    828 struct KitObjDepIter {
    829   KitObjFile* file;
    830   u32 idx;
    831   KitSlice* import_buf; /* scratch for the current dep's import names */
    832   u32 import_cap;
    833 };
    834 
    835 KitStatus kit_obj_depiter_new(KitObjFile* f, KitObjDepIter** out) {
    836   Heap* h;
    837   KitObjDepIter* it;
    838   if (!f || !out) return KIT_INVALID;
    839   h = f->ctx->heap;
    840   it = (KitObjDepIter*)h->alloc(h, sizeof(*it), _Alignof(KitObjDepIter));
    841   if (!it) return KIT_NOMEM;
    842   it->file = f;
    843   it->idx = 0;
    844   it->import_buf = NULL;
    845   it->import_cap = 0;
    846   *out = it;
    847   return KIT_OK;
    848 }
    849 
    850 KitIterResult kit_obj_depiter_next(KitObjDepIter* it, KitObjDepInfo* out) {
    851   const ObjImage* im;
    852   const ObjImageDep* d;
    853   Heap* h;
    854   u32 i;
    855   if (!it || !out) return KIT_ITER_ERROR;
    856   im = obj_image(it->file->ob);
    857   if (it->idx >= obj_image_ndeps(im)) return KIT_ITER_END;
    858   d = obj_image_dep(im, it->idx++);
    859   out->name = pool_slice(it->file->compiler.global, d->name);
    860   out->imports = NULL;
    861   out->nimports = d->nimports;
    862   if (d->nimports) {
    863     h = it->file->ctx->heap;
    864     if (it->import_cap < d->nimports) {
    865       if (VEC_GROW(h, it->import_buf, it->import_cap, d->nimports))
    866         return KIT_ITER_ERROR;
    867     }
    868     for (i = 0; i < d->nimports; ++i)
    869       it->import_buf[i] = pool_slice(it->file->compiler.global, d->imports[i]);
    870     out->imports = it->import_buf;
    871   }
    872   return KIT_ITER_ITEM;
    873 }
    874 
    875 void kit_obj_depiter_free(KitObjDepIter* it) {
    876   Heap* h;
    877   if (!it) return;
    878   h = it->file->ctx->heap;
    879   if (it->import_buf)
    880     h->free(h, it->import_buf, sizeof(*it->import_buf) * it->import_cap);
    881   h->free(h, it, sizeof(*it));
    882 }
    883 
    884 struct KitObjRpathIter {
    885   KitObjFile* file;
    886   u32 idx;
    887 };
    888 
    889 KitStatus kit_obj_rpathiter_new(KitObjFile* f, KitObjRpathIter** out) {
    890   Heap* h;
    891   KitObjRpathIter* it;
    892   if (!f || !out) return KIT_INVALID;
    893   h = f->ctx->heap;
    894   it = (KitObjRpathIter*)h->alloc(h, sizeof(*it), _Alignof(KitObjRpathIter));
    895   if (!it) return KIT_NOMEM;
    896   it->file = f;
    897   it->idx = 0;
    898   *out = it;
    899   return KIT_OK;
    900 }
    901 
    902 KitIterResult kit_obj_rpathiter_next(KitObjRpathIter* it, KitSlice* out) {
    903   const ObjImage* im;
    904   if (!it || !out) return KIT_ITER_ERROR;
    905   im = obj_image(it->file->ob);
    906   if (it->idx >= obj_image_nrpaths(im)) return KIT_ITER_END;
    907   *out = pool_slice(it->file->compiler.global, obj_image_rpath(im, it->idx++));
    908   return KIT_ITER_ITEM;
    909 }
    910 
    911 void kit_obj_rpathiter_free(KitObjRpathIter* it) {
    912   Heap* h;
    913   if (!it) return;
    914   h = it->file->ctx->heap;
    915   h->free(h, it, sizeof(*it));
    916 }
    917 
    918 struct KitObjImageRawIter {
    919   KitObjFile* file;
    920   u32 idx;
    921 };
    922 
    923 KitStatus kit_obj_image_rawiter_new(KitObjFile* f, KitObjImageRawIter** out) {
    924   Heap* h;
    925   KitObjImageRawIter* it;
    926   if (!f || !out) return KIT_INVALID;
    927   if (!obj_image(f->ob)) return KIT_NOT_FOUND; /* relocatable: no image */
    928   h = f->ctx->heap;
    929   it = (KitObjImageRawIter*)h->alloc(h, sizeof(*it),
    930                                      _Alignof(KitObjImageRawIter));
    931   if (!it) return KIT_NOMEM;
    932   it->file = f;
    933   it->idx = 0;
    934   *out = it;
    935   return KIT_OK;
    936 }
    937 
    938 KitIterResult kit_obj_image_rawiter_next(KitObjImageRawIter* it,
    939                                          KitObjImageRaw* out) {
    940   const ObjImage* im;
    941   const ObjImageRaw* r;
    942   if (!it || !out) return KIT_ITER_ERROR;
    943   im = obj_image(it->file->ob);
    944   if (it->idx >= obj_image_nraws(im)) return KIT_ITER_END;
    945   r = obj_image_raw(im, it->idx++);
    946   out->tag = r->tag;
    947   out->value = r->value;
    948   out->extra = r->extra;
    949   return KIT_ITER_ITEM;
    950 }
    951 
    952 void kit_obj_image_rawiter_free(KitObjImageRawIter* it) {
    953   Heap* h;
    954   if (!it) return;
    955   h = it->file->ctx->heap;
    956   h->free(h, it, sizeof(*it));
    957 }