kit

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

dwarf_open.c (24948B)


      1 /* dwarf_open.c — open/close, section lookup, primitives, abbrev cache.
      2  *
      3  * Per doc/DWARF.md §4.1: read .debug_abbrev / .debug_info / .debug_line /
      4  * .debug_str / .debug_line_str by section name from the KitObjFile.
      5  * Return NULL if any of those mandatory five are missing.
      6  */
      7 
      8 #include <kit/arch.h>
      9 #include <kit/dwarf.h>
     10 #include <kit/object.h>
     11 #include <stdint.h>
     12 #include <string.h>
     13 
     14 #include "core/core.h"
     15 #include "core/heap.h"
     16 #include "core/pool.h"
     17 #include "core/slice.h"
     18 #include "core/util.h"
     19 #include "core/vec.h"
     20 #include "debug/dwarf_internal.h"
     21 #include "obj/obj.h"
     22 
     23 /* ---- section lookup --------------------------------------------------- */
     24 
     25 void dw_find_section(KitDebugInfo* d, const char* name, DwSection* out) {
     26   uint32_t i, n;
     27   /* On Mach-O the obj layer reports DWARF sections as "__DWARF,__debug_*"
     28    * (16-char-truncated) and .eh_frame as "__TEXT,__eh_frame", not the
     29    * ELF ".debug_*"/".eh_frame" spelling.  Precompute the Mach-O candidate
     30    * for the requested section (via the shared name translator) so one lookup
     31    * spans both formats. */
     32   char macho_full[40];
     33   int have_macho = 0;
     34   out->data = NULL;
     35   out->size = 0;
     36   out->sec_idx = UINT32_MAX;
     37   if (!d->obj) return;
     38   {
     39     size_t nl = (size_t)slice_from_cstr(name).len;
     40     have_macho = obj_macho_native_secname(name, nl, macho_full);
     41   }
     42   n = kit_obj_nsections(d->obj);
     43   for (i = 0; i < n; ++i) {
     44     KitObjSecInfo info;
     45     if (kit_obj_section(d->obj, i, &info) != KIT_OK) continue;
     46     if (info.name.len &&
     47         (kit_slice_eq_cstr(info.name, name) ||
     48          (have_macho && kit_slice_eq_cstr(info.name, macho_full)))) {
     49       size_t len = 0;
     50       const uint8_t* p = NULL;
     51       if (kit_obj_section_data(d->obj, i, &p, &len) != KIT_OK) continue;
     52       out->data = p;
     53       out->size = (u32)len;
     54       out->sec_idx = i;
     55       return;
     56     }
     57   }
     58 }
     59 
     60 /* ---- byte-stream primitives ------------------------------------------- */
     61 
     62 /* On EOF we return zero / empty. The decoder will detect malformed input
     63  * via length checks elsewhere; for the consumer we just want to not
     64  * crash on truncated bytes. */
     65 
     66 u8 dw_u8(const u8* base, u32 size, u32* off) {
     67   if (*off >= size) return 0;
     68   return base[(*off)++];
     69 }
     70 u16 dw_u16(const u8* base, u32 size, u32* off) {
     71   u16 v;
     72   if (*off + 2 > size) {
     73     *off = size;
     74     return 0;
     75   }
     76   v = (u16)base[*off] | ((u16)base[*off + 1] << 8);
     77   *off += 2;
     78   return v;
     79 }
     80 u32 dw_u24(const u8* base, u32 size, u32* off) {
     81   u32 v;
     82   if (*off + 3 > size) {
     83     *off = size;
     84     return 0;
     85   }
     86   v = (u32)base[*off] | ((u32)base[*off + 1] << 8) |
     87       ((u32)base[*off + 2] << 16);
     88   *off += 3;
     89   return v;
     90 }
     91 u32 dw_u32(const u8* base, u32 size, u32* off) {
     92   u32 v;
     93   if (*off + 4 > size) {
     94     *off = size;
     95     return 0;
     96   }
     97   v = (u32)base[*off] | ((u32)base[*off + 1] << 8) |
     98       ((u32)base[*off + 2] << 16) | ((u32)base[*off + 3] << 24);
     99   *off += 4;
    100   return v;
    101 }
    102 u64 dw_u64(const u8* base, u32 size, u32* off) {
    103   u64 v;
    104   if (*off + 8 > size) {
    105     *off = size;
    106     return 0;
    107   }
    108   v = (u64)base[*off] | ((u64)base[*off + 1] << 8) |
    109       ((u64)base[*off + 2] << 16) | ((u64)base[*off + 3] << 24) |
    110       ((u64)base[*off + 4] << 32) | ((u64)base[*off + 5] << 40) |
    111       ((u64)base[*off + 6] << 48) | ((u64)base[*off + 7] << 56);
    112   *off += 8;
    113   return v;
    114 }
    115 u64 dw_uleb(const u8* base, u32 size, u32* off) {
    116   u64 v = 0;
    117   int shift = 0;
    118   while (*off < size) {
    119     u8 b = base[(*off)++];
    120     v |= ((u64)(b & 0x7f)) << shift;
    121     if (!(b & 0x80)) break;
    122     shift += 7;
    123     if (shift > 63) break;
    124   }
    125   return v;
    126 }
    127 i64 dw_sleb(const u8* base, u32 size, u32* off) {
    128   i64 v = 0;
    129   int shift = 0;
    130   u8 b = 0;
    131   while (*off < size) {
    132     b = base[(*off)++];
    133     v |= ((i64)(b & 0x7f)) << shift;
    134     shift += 7;
    135     if (!(b & 0x80)) break;
    136     if (shift > 63) break;
    137   }
    138   if (shift < 64 && (b & 0x40)) {
    139     v |= -((i64)1 << shift);
    140   }
    141   return v;
    142 }
    143 const char* dw_cstr(const u8* base, u32 size, u32* off) {
    144   const char* s = (const char*)base + *off;
    145   while (*off < size && base[*off] != 0) (*off)++;
    146   if (*off < size) (*off)++; /* consume terminator */
    147   return s;
    148 }
    149 
    150 /* ---- string interning ------------------------------------------------- */
    151 
    152 const char* dw_intern(KitDebugInfo* d, const char* s, size_t len) {
    153   Sym sym = pool_intern_slice(d->strs, (Slice){.s = s, .len = len});
    154   return pool_slice(d->strs, sym).s;
    155 }
    156 
    157 /* Resolve a .debug_str offset. */
    158 const char* dw_str(KitDebugInfo* d, u32 offset) {
    159   if (offset >= d->str.size) return "";
    160   return (const char*)(d->str.data + offset);
    161 }
    162 
    163 /* Resolve a .debug_line_str offset. */
    164 const char* dw_line_str(KitDebugInfo* d, u32 offset) {
    165   if (offset >= d->line_str.size) return "";
    166   return (const char*)(d->line_str.data + offset);
    167 }
    168 
    169 /* Resolve a strx index via .debug_str_offsets + cu->str_offsets_base. */
    170 const char* dw_strx(KitDebugInfo* d, const DwCu* cu, u64 idx) {
    171   /* DW5 .debug_str_offsets has a header per contribution:
    172    *   unit_length (4 or 12), version (2), padding (2), then entries.
    173    * cu->str_offsets_base points past the header to the first entry.
    174    * If the base attribute is absent we fall back to base=0+8 (assume 32-bit
    175    * header at start). */
    176   u32 base = cu->str_offsets_base;
    177   u32 ent_size = 4;
    178   u32 entry_off = base + (u32)idx * ent_size;
    179   u32 str_off;
    180   if (entry_off + ent_size > d->str_offsets.size) return "";
    181   {
    182     u32 tmp = entry_off;
    183     str_off = dw_u32(d->str_offsets.data, d->str_offsets.size, &tmp);
    184   }
    185   return dw_str(d, str_off);
    186 }
    187 
    188 /* ---- abbrev parsing --------------------------------------------------- */
    189 
    190 static void abbrev_parse_table(KitDebugInfo* d, u32 offset, DwAbbrevTable* t) {
    191   u32 off = offset;
    192   t->cu_abbrev_offset = offset;
    193   t->abbrevs = NULL;
    194   t->nabbrevs = 0;
    195   t->cap = 0;
    196   for (;;) {
    197     u64 code;
    198     DwAbbrev a;
    199     DwAbbrevAttr* attrs = NULL;
    200     u32 nattrs = 0, attrs_cap = 0;
    201     if (off >= d->abbrev.size) break;
    202     code = dw_uleb(d->abbrev.data, d->abbrev.size, &off);
    203     if (code == 0) break; /* end-of-table marker */
    204     a.code = code;
    205     a.tag = (u32)dw_uleb(d->abbrev.data, d->abbrev.size, &off);
    206     a.has_children = dw_u8(d->abbrev.data, d->abbrev.size, &off);
    207     a.attrs = NULL;
    208     a.nattrs = 0;
    209     /* Read (attr, form) pairs until (0,0). */
    210     for (;;) {
    211       u32 at = (u32)dw_uleb(d->abbrev.data, d->abbrev.size, &off);
    212       u32 fm = (u32)dw_uleb(d->abbrev.data, d->abbrev.size, &off);
    213       i64 ic = 0;
    214       if (at == 0 && fm == 0) break;
    215       if (fm == DW_FORM_implicit_const) {
    216         ic = dw_sleb(d->abbrev.data, d->abbrev.size, &off);
    217       }
    218       if (nattrs == attrs_cap) {
    219         u32 ncap = attrs_cap ? attrs_cap * 2 : 4;
    220         DwAbbrevAttr* na = (DwAbbrevAttr*)d->h->realloc(
    221             d->h, attrs, attrs_cap * sizeof(*attrs), ncap * sizeof(*attrs),
    222             _Alignof(DwAbbrevAttr));
    223         if (!na) {
    224           if (attrs) d->h->free(d->h, attrs, attrs_cap * sizeof(*attrs));
    225           attrs = NULL;
    226           attrs_cap = 0;
    227           nattrs = 0;
    228           break;
    229         }
    230         attrs = na;
    231         attrs_cap = ncap;
    232       }
    233       attrs[nattrs].attr = at;
    234       attrs[nattrs].form = fm;
    235       attrs[nattrs].implicit_const = ic;
    236       nattrs++;
    237     }
    238     a.attrs = attrs;
    239     a.nattrs = nattrs;
    240     if (t->nabbrevs == t->cap) {
    241       u32 ncap = t->cap ? t->cap * 2 : 8;
    242       DwAbbrev* na = (DwAbbrev*)d->h->realloc(
    243           d->h, t->abbrevs, t->cap * sizeof(*t->abbrevs),
    244           ncap * sizeof(*t->abbrevs), _Alignof(DwAbbrev));
    245       if (!na) break;
    246       t->abbrevs = na;
    247       t->cap = ncap;
    248     }
    249     t->abbrevs[t->nabbrevs++] = a;
    250   }
    251 }
    252 
    253 DwAbbrevTable* dw_abbrev_get(KitDebugInfo* d, u32 offset) {
    254   u32 i;
    255   DwAbbrevTable* t;
    256   for (i = 0; i < d->nabbrevs; ++i) {
    257     if (d->abbrevs[i].cu_abbrev_offset == offset) return &d->abbrevs[i];
    258   }
    259   if (d->nabbrevs == d->abbrevs_cap) {
    260     u32 ncap = d->abbrevs_cap ? d->abbrevs_cap * 2 : 4;
    261     DwAbbrevTable* na = (DwAbbrevTable*)d->h->realloc(
    262         d->h, d->abbrevs, d->abbrevs_cap * sizeof(*d->abbrevs),
    263         ncap * sizeof(*d->abbrevs), _Alignof(DwAbbrevTable));
    264     if (!na) return NULL;
    265     d->abbrevs = na;
    266     d->abbrevs_cap = ncap;
    267   }
    268   t = &d->abbrevs[d->nabbrevs++];
    269   abbrev_parse_table(d, offset, t);
    270   return t;
    271 }
    272 
    273 DwAbbrev* dw_abbrev_lookup(DwAbbrevTable* t, u64 code) {
    274   u32 i;
    275   if (!t) return NULL;
    276   for (i = 0; i < t->nabbrevs; ++i) {
    277     if (t->abbrevs[i].code == code) return &t->abbrevs[i];
    278   }
    279   return NULL;
    280 }
    281 
    282 /* ---- CU header parsing ----------------------------------------------- */
    283 
    284 u32 dw_cu_parse_header(KitDebugInfo* d, u32 off, DwCu* cu) {
    285   u32 start = off;
    286   u32 unit_length;
    287   u32 hdr_after_len_off;
    288   cu->hdr_offset = start;
    289   cu->is_64bit = 0;
    290   unit_length = dw_u32(d->info.data, d->info.size, &off);
    291   if (unit_length == 0xffffffffu) {
    292     /* DWARF64 — initial length followed by 8-byte length. We don't
    293      * fully support DWARF64 ourselves, but skip the unit. */
    294     cu->is_64bit = 1;
    295     cu->hdr_length = 0;
    296     cu->unit_total_size = 0;
    297     /* Skip past CU. */
    298     {
    299       u64 ulen = dw_u64(d->info.data, d->info.size, &off);
    300       cu->unit_total_size = 12 + (u32)ulen;
    301     }
    302     return start + cu->unit_total_size;
    303   }
    304   cu->hdr_length = unit_length;
    305   cu->unit_total_size = 4 + unit_length;
    306   hdr_after_len_off = off; /* points just past unit_length */
    307   cu->version = (u8)dw_u16(d->info.data, d->info.size, &off);
    308   if (cu->version >= 5) {
    309     cu->unit_type = dw_u8(d->info.data, d->info.size, &off);
    310     cu->address_size = dw_u8(d->info.data, d->info.size, &off);
    311     cu->abbrev_offset = dw_u32(d->info.data, d->info.size, &off);
    312   } else {
    313     /* DW4 layout: abbrev_offset, address_size. */
    314     cu->unit_type = 0;
    315     cu->abbrev_offset = dw_u32(d->info.data, d->info.size, &off);
    316     cu->address_size = dw_u8(d->info.data, d->info.size, &off);
    317   }
    318   cu->die_start_off = off;
    319   cu->str_offsets_base = 0;
    320   cu->addr_base = 0;
    321   cu->loclists_base = 0;
    322   cu->rnglists_base = 0;
    323   cu->stmt_list = 0;
    324   cu->has_stmt_list = 0;
    325   cu->comp_dir = "";
    326   cu->name = "";
    327   /* Resolve abbrev table now (cheap & idempotent). */
    328   {
    329     DwAbbrevTable* t = dw_abbrev_get(d, cu->abbrev_offset);
    330     cu->abbrev_table_idx = (u32)(t ? (t - d->abbrevs) : 0);
    331   }
    332   (void)hdr_after_len_off;
    333   return start + cu->unit_total_size;
    334 }
    335 
    336 /* Read the CU root DIE to capture base attributes (str_offsets_base,
    337  * addr_base, stmt_list, name, comp_dir). Restores no state — leaves the
    338  * CU in its parsed-header form. */
    339 static void cu_read_root_attrs(KitDebugInfo* d, DwCu* cu) {
    340   u32 off = cu->die_start_off;
    341   u64 code;
    342   DwAbbrev* ab;
    343   DwAttrValue v;
    344   u32 i;
    345   DwAbbrevTable* t = &d->abbrevs[cu->abbrev_table_idx];
    346   if (off >= d->info.size) return;
    347   code = dw_uleb(d->info.data, d->info.size, &off);
    348   if (code == 0) return;
    349   ab = dw_abbrev_lookup(t, code);
    350   if (!ab) return;
    351   /* Two-pass scan: do skipping reads, but capture base attrs. We must
    352    * be careful: dw_read_form for strx forms uses cu->str_offsets_base,
    353    * so we read in two passes. */
    354   off = cu->die_start_off;
    355   (void)dw_uleb(d->info.data, d->info.size, &off); /* re-skip code */
    356   /* Pass 1: only read str_offsets_base / addr_base (forms that don't
    357    * themselves need those bases). */
    358   for (i = 0; i < ab->nattrs; ++i) {
    359     DwAbbrevAttr* aa = &ab->attrs[i];
    360     if (aa->attr == DW_AT_str_offsets_base || aa->attr == DW_AT_addr_base ||
    361         aa->attr == DW_AT_loclists_base || aa->attr == DW_AT_rnglists_base) {
    362       dw_read_form(d, cu, aa->form, aa->implicit_const, &off, &v);
    363       if (aa->attr == DW_AT_str_offsets_base)
    364         cu->str_offsets_base = (u32)v.u;
    365       else if (aa->attr == DW_AT_addr_base)
    366         cu->addr_base = (u32)v.u;
    367       else if (aa->attr == DW_AT_loclists_base)
    368         cu->loclists_base = (u32)v.u;
    369       else if (aa->attr == DW_AT_rnglists_base)
    370         cu->rnglists_base = (u32)v.u;
    371     } else {
    372       dw_skip_form(d, cu, aa->form, aa->implicit_const, &off);
    373     }
    374   }
    375   /* Pass 2: read remaining attrs (stmt_list, name, comp_dir). */
    376   off = cu->die_start_off;
    377   (void)dw_uleb(d->info.data, d->info.size, &off);
    378   for (i = 0; i < ab->nattrs; ++i) {
    379     DwAbbrevAttr* aa = &ab->attrs[i];
    380     if (aa->attr == DW_AT_stmt_list) {
    381       dw_read_form(d, cu, aa->form, aa->implicit_const, &off, &v);
    382       cu->stmt_list = (u32)v.u;
    383       cu->has_stmt_list = 1;
    384     } else if (aa->attr == DW_AT_name) {
    385       dw_read_form(d, cu, aa->form, aa->implicit_const, &off, &v);
    386       cu->name = v.str ? v.str : "";
    387     } else if (aa->attr == DW_AT_comp_dir) {
    388       dw_read_form(d, cu, aa->form, aa->implicit_const, &off, &v);
    389       cu->comp_dir = v.str ? v.str : "";
    390     } else {
    391       dw_skip_form(d, cu, aa->form, aa->implicit_const, &off);
    392     }
    393   }
    394 }
    395 
    396 void dw_parse_all_cus(KitDebugInfo* d) {
    397   u32 off = 0;
    398   /* Idempotent: a successful kit_dwarf_open already populates d->cus, and
    399    * the structural-enumeration iterators call this again. Re-parsing would
    400    * append duplicate CUs, so bail once the table is built. */
    401   if (d->ncus) return;
    402   while (off < d->info.size) {
    403     DwCu cu;
    404     u32 next = dw_cu_parse_header(d, off, &cu);
    405     if (next <= off) break;
    406     if (cu.is_64bit) {
    407       off = next;
    408       continue;
    409     }
    410     if (cu.version < 2 || cu.version > 5) {
    411       off = next;
    412       continue;
    413     }
    414     if (d->ncus == d->cus_cap) {
    415       u32 ncap = d->cus_cap ? d->cus_cap * 2 : 4;
    416       DwCu* na =
    417           (DwCu*)d->h->realloc(d->h, d->cus, d->cus_cap * sizeof(*d->cus),
    418                                ncap * sizeof(*d->cus), _Alignof(DwCu));
    419       if (!na) break;
    420       d->cus = na;
    421       d->cus_cap = ncap;
    422     }
    423     d->cus[d->ncus++] = cu;
    424     /* Capture root attrs now. */
    425     cu_read_root_attrs(d, &d->cus[d->ncus - 1]);
    426     off = next;
    427   }
    428 }
    429 
    430 DwCu* dw_cu_at_die_offset(KitDebugInfo* d, u32 die_offset) {
    431   u32 i;
    432   for (i = 0; i < d->ncus; ++i) {
    433     DwCu* cu = &d->cus[i];
    434     if (die_offset >= cu->hdr_offset &&
    435         die_offset < cu->hdr_offset + cu->unit_total_size) {
    436       return cu;
    437     }
    438   }
    439   return NULL;
    440 }
    441 
    442 /* ---- form decoding ---------------------------------------------------- */
    443 
    444 /* Section-parameterized form decoder. Inline form bytes are pulled from
    445  * `sec` (.debug_info for DIE attributes, .debug_line for line-program
    446  * file/dir entry-format values); strp/line_strp/strx still resolve into the
    447  * shared string sections via the CU. This is the single source of truth —
    448  * dw_read_form wires it to .debug_info; the line decoder passes &d->line. */
    449 void dw_read_form_in(KitDebugInfo* d, const DwCu* cu, const DwSection* sec,
    450                      u32 form, i64 implicit_const, u32* off, DwAttrValue* out) {
    451   out->form = form;
    452   out->u = 0;
    453   out->s = 0;
    454   out->str = "";
    455   out->block = NULL;
    456   out->block_len = 0;
    457   switch (form) {
    458     case DW_FORM_addr:
    459       if (cu->address_size == 8)
    460         out->u = dw_u64(sec->data, sec->size, off);
    461       else
    462         out->u = dw_u32(sec->data, sec->size, off);
    463       break;
    464     case DW_FORM_data1:
    465     case DW_FORM_ref1:
    466     case DW_FORM_flag:
    467     case DW_FORM_strx1:
    468     case DW_FORM_addrx1:
    469       out->u = dw_u8(sec->data, sec->size, off);
    470       out->s = (i64)(i8)out->u;
    471       if (form == DW_FORM_strx1) out->str = dw_strx(d, cu, out->u);
    472       break;
    473     case DW_FORM_data2:
    474     case DW_FORM_ref2:
    475     case DW_FORM_strx2:
    476     case DW_FORM_addrx2:
    477       out->u = dw_u16(sec->data, sec->size, off);
    478       out->s = (i64)(i16)out->u;
    479       if (form == DW_FORM_strx2) out->str = dw_strx(d, cu, out->u);
    480       break;
    481     case DW_FORM_strx3:
    482     case DW_FORM_addrx3:
    483       out->u = dw_u24(sec->data, sec->size, off);
    484       if (form == DW_FORM_strx3) out->str = dw_strx(d, cu, out->u);
    485       break;
    486     case DW_FORM_data4:
    487     case DW_FORM_ref4:
    488     case DW_FORM_strx4:
    489     case DW_FORM_addrx4:
    490       out->u = dw_u32(sec->data, sec->size, off);
    491       out->s = (i64)(i32)out->u;
    492       if (form == DW_FORM_strx4) out->str = dw_strx(d, cu, out->u);
    493       break;
    494     case DW_FORM_data8:
    495     case DW_FORM_ref8:
    496     case DW_FORM_ref_sig8:
    497     case DW_FORM_ref_sup8:
    498       out->u = dw_u64(sec->data, sec->size, off);
    499       out->s = (i64)out->u;
    500       break;
    501     case DW_FORM_data16:
    502       /* Skip 16 bytes; not commonly needed. */
    503       *off += 16;
    504       break;
    505     case DW_FORM_sdata:
    506       out->s = dw_sleb(sec->data, sec->size, off);
    507       out->u = (u64)out->s;
    508       break;
    509     case DW_FORM_udata:
    510     case DW_FORM_ref_udata:
    511     case DW_FORM_strx:
    512     case DW_FORM_addrx:
    513     case DW_FORM_loclistx:
    514     case DW_FORM_rnglistx:
    515       out->u = dw_uleb(sec->data, sec->size, off);
    516       if (form == DW_FORM_strx) out->str = dw_strx(d, cu, out->u);
    517       break;
    518     case DW_FORM_string:
    519       out->str = dw_cstr(sec->data, sec->size, off);
    520       break;
    521     case DW_FORM_strp:
    522       out->u = dw_u32(sec->data, sec->size, off);
    523       out->str = dw_str(d, (u32)out->u);
    524       break;
    525     case DW_FORM_line_strp:
    526       out->u = dw_u32(sec->data, sec->size, off);
    527       out->str = dw_line_str(d, (u32)out->u);
    528       break;
    529     case DW_FORM_strp_sup:
    530     case DW_FORM_ref_sup4:
    531       out->u = dw_u32(sec->data, sec->size, off);
    532       break;
    533     case DW_FORM_sec_offset:
    534       out->u = dw_u32(sec->data, sec->size, off);
    535       break;
    536     case DW_FORM_ref_addr:
    537       /* DWARF 5: 4 bytes for 32-bit DWARF (we don't support DWARF64). */
    538       out->u = dw_u32(sec->data, sec->size, off);
    539       break;
    540     case DW_FORM_flag_present:
    541       out->u = 1;
    542       break;
    543     case DW_FORM_implicit_const:
    544       out->s = implicit_const;
    545       out->u = (u64)implicit_const;
    546       break;
    547     case DW_FORM_block1: {
    548       u32 n = dw_u8(sec->data, sec->size, off);
    549       out->block = sec->data + *off;
    550       out->block_len = n;
    551       out->u = n;
    552       *off += n;
    553     } break;
    554     case DW_FORM_block2: {
    555       u32 n = dw_u16(sec->data, sec->size, off);
    556       out->block = sec->data + *off;
    557       out->block_len = n;
    558       out->u = n;
    559       *off += n;
    560     } break;
    561     case DW_FORM_block4: {
    562       u32 n = dw_u32(sec->data, sec->size, off);
    563       out->block = sec->data + *off;
    564       out->block_len = n;
    565       out->u = n;
    566       *off += n;
    567     } break;
    568     case DW_FORM_block:
    569     case DW_FORM_exprloc: {
    570       u32 n = (u32)dw_uleb(sec->data, sec->size, off);
    571       out->block = sec->data + *off;
    572       out->block_len = n;
    573       out->u = n;
    574       *off += n;
    575     } break;
    576     case DW_FORM_indirect: {
    577       u32 ifrm = (u32)dw_uleb(sec->data, sec->size, off);
    578       dw_read_form_in(d, cu, sec, ifrm, 0, off, out);
    579     } break;
    580     default:
    581       /* Unknown form — best effort: skip nothing. */
    582       break;
    583   }
    584 }
    585 
    586 void dw_read_form(KitDebugInfo* d, const DwCu* cu, u32 form, i64 implicit_const,
    587                   u32* off, DwAttrValue* out) {
    588   dw_read_form_in(d, cu, &d->info, form, implicit_const, off, out);
    589 }
    590 
    591 void dw_skip_form(KitDebugInfo* d, const DwCu* cu, u32 form, i64 implicit_const,
    592                   u32* off) {
    593   DwAttrValue tmp;
    594   dw_read_form(d, cu, form, implicit_const, off, &tmp);
    595 }
    596 
    597 /* ---- DIE iteration ---------------------------------------------------- */
    598 
    599 int dw_read_die(KitDebugInfo* d, const DwCu* cu, u32* off, DwDie* out) {
    600   u64 code;
    601   out->die_off = *off;
    602   if (*off >= d->info.size || *off >= cu->hdr_offset + cu->unit_total_size) {
    603     out->abbrev_code = 0;
    604     out->abbrev = NULL;
    605     out->attrs_off = *off;
    606     return 0;
    607   }
    608   code = dw_uleb(d->info.data, d->info.size, off);
    609   out->abbrev_code = code;
    610   out->attrs_off = *off;
    611   out->next_sibling_off = 0;
    612   if (code == 0) {
    613     out->abbrev = NULL;
    614     return 0;
    615   }
    616   out->abbrev = dw_abbrev_lookup(&d->abbrevs[cu->abbrev_table_idx], code);
    617   return 1;
    618 }
    619 
    620 void dw_skip_die_attrs(KitDebugInfo* d, const DwCu* cu, DwDie* die, u32* off) {
    621   u32 i;
    622   if (!die->abbrev) return;
    623   for (i = 0; i < die->abbrev->nattrs; ++i) {
    624     DwAbbrevAttr* aa = &die->abbrev->attrs[i];
    625     dw_skip_form(d, cu, aa->form, aa->implicit_const, off);
    626   }
    627 }
    628 
    629 void dw_skip_die_subtree(KitDebugInfo* d, const DwCu* cu, DwDie* die,
    630                          u32* off) {
    631   if (!die->abbrev) return;
    632   dw_skip_die_attrs(d, cu, die, off);
    633   if (die->abbrev->has_children) {
    634     for (;;) {
    635       DwDie child;
    636       if (!dw_read_die(d, cu, off, &child)) break;
    637       dw_skip_die_subtree(d, cu, &child, off);
    638     }
    639   }
    640 }
    641 
    642 int dw_die_attr(KitDebugInfo* d, const DwCu* cu, DwDie* die, u32 attr,
    643                 DwAttrValue* out) {
    644   u32 off = die->attrs_off;
    645   u32 i;
    646   if (!die->abbrev) return 0;
    647   for (i = 0; i < die->abbrev->nattrs; ++i) {
    648     DwAbbrevAttr* aa = &die->abbrev->attrs[i];
    649     if (aa->attr == attr) {
    650       dw_read_form(d, cu, aa->form, aa->implicit_const, &off, out);
    651       return 1;
    652     }
    653     dw_skip_form(d, cu, aa->form, aa->implicit_const, &off);
    654   }
    655   return 0;
    656 }
    657 
    658 /* ---- public open/close ----------------------------------------------- */
    659 
    660 KitStatus kit_dwarf_open(const KitContext* ctx, const KitObjFile* obj,
    661                          KitDebugInfo** out) {
    662   Heap* h;
    663   KitDebugInfo* d;
    664   if (!out) return KIT_INVALID;
    665   *out = NULL;
    666   if (!ctx || !ctx->heap || !obj) return KIT_INVALID;
    667   h = ctx->heap;
    668   d = (KitDebugInfo*)h->alloc(h, sizeof(*d), _Alignof(KitDebugInfo));
    669   if (!d) return KIT_NOMEM;
    670   memset(d, 0, sizeof(*d));
    671   d->ctx = ctx;
    672   d->h = h;
    673   d->obj = obj;
    674   d->strs = (Pool*)h->alloc(h, sizeof(*d->strs), _Alignof(Pool));
    675   if (!d->strs) {
    676     kit_dwarf_free(d);
    677     return KIT_NOMEM;
    678   }
    679   pool_init(d->strs, h);
    680 
    681   dw_find_section(d, ".debug_abbrev", &d->abbrev);
    682   dw_find_section(d, ".debug_info", &d->info);
    683   dw_find_section(d, ".debug_line", &d->line);
    684   dw_find_section(d, ".debug_str", &d->str);
    685   dw_find_section(d, ".debug_line_str", &d->line_str);
    686   dw_find_section(d, ".debug_str_offsets", &d->str_offsets);
    687   dw_find_section(d, ".debug_addr", &d->addr);
    688   dw_find_section(d, ".debug_loclists", &d->loclists);
    689   dw_find_section(d, ".debug_rnglists", &d->rnglists);
    690   dw_find_section(d, ".eh_frame", &d->eh_frame);
    691   dw_find_section(d, ".debug_aranges", &d->aranges);
    692 
    693   if (d->abbrev.sec_idx == UINT32_MAX || d->info.sec_idx == UINT32_MAX ||
    694       d->line.sec_idx == UINT32_MAX || d->str.sec_idx == UINT32_MAX ||
    695       d->line_str.sec_idx == UINT32_MAX) {
    696     kit_dwarf_free(d);
    697     return KIT_NOT_FOUND;
    698   }
    699 
    700   /* str_offsets_base default: in the absence of DW_AT_str_offsets_base, the
    701    * offsets section starts with an 8-byte header (uniform for DW5). */
    702   dw_parse_all_cus(d);
    703   if (d->ncus == 0) {
    704     kit_dwarf_free(d);
    705     return KIT_MALFORMED;
    706   }
    707 
    708   /* Allocate per-CU lazy line-program state. */
    709   if (d->ncus) {
    710     d->lines_by_cu = (DwLineProgram*)h->alloc(
    711         h, d->ncus * sizeof(DwLineProgram), _Alignof(DwLineProgram));
    712     d->lines_built = (u8*)h->alloc(h, d->ncus, 1);
    713     if (!d->lines_by_cu || !d->lines_built) {
    714       kit_dwarf_free(d);
    715       return KIT_NOMEM;
    716     }
    717     memset(d->lines_by_cu, 0, d->ncus * sizeof(DwLineProgram));
    718     memset(d->lines_built, 0, d->ncus);
    719   }
    720 
    721   *out = d;
    722   return KIT_OK;
    723 }
    724 
    725 static void free_subprog(Heap* h, DwSubprog* sp) {
    726   if (sp->params) h->free(h, sp->params, sp->nparams * sizeof(DwLocal));
    727   if (sp->locals) h->free(h, sp->locals, sp->nlocals * sizeof(DwLocal));
    728 }
    729 
    730 void kit_dwarf_free(KitDebugInfo* d) {
    731   Heap* h;
    732   u32 i;
    733   if (!d) return;
    734   h = d->h;
    735   for (i = 0; i < d->nabbrevs; ++i) {
    736     u32 j;
    737     DwAbbrevTable* t = &d->abbrevs[i];
    738     for (j = 0; j < t->nabbrevs; ++j) {
    739       if (t->abbrevs[j].attrs)
    740         h->free(h, t->abbrevs[j].attrs,
    741                 t->abbrevs[j].nattrs * sizeof(DwAbbrevAttr));
    742     }
    743     if (t->abbrevs) h->free(h, t->abbrevs, t->cap * sizeof(DwAbbrev));
    744   }
    745   if (d->abbrevs)
    746     h->free(h, d->abbrevs, d->abbrevs_cap * sizeof(DwAbbrevTable));
    747   if (d->cus) h->free(h, d->cus, d->cus_cap * sizeof(DwCu));
    748 
    749   if (d->lines_by_cu) {
    750     for (i = 0; i < d->ncus; ++i) {
    751       DwLineProgram* lp = &d->lines_by_cu[i];
    752       if (lp->rows) h->free(h, lp->rows, lp->cap * sizeof(DwLineRow));
    753       if (lp->files) h->free(h, lp->files, lp->nfiles * sizeof(DwLineFile));
    754       if (lp->dirs) h->free(h, lp->dirs, lp->ndirs * sizeof(const char*));
    755       if (lp->file_norm)
    756         h->free(h, lp->file_norm, lp->nfile_norm * sizeof(const char*));
    757     }
    758     h->free(h, d->lines_by_cu, d->ncus * sizeof(DwLineProgram));
    759   }
    760   if (d->lines_built) h->free(h, d->lines_built, d->ncus);
    761 
    762   for (i = 0; i < d->nsubs; ++i) free_subprog(h, &d->subs[i]);
    763   if (d->subs) h->free(h, d->subs, d->subs_cap * sizeof(DwSubprog));
    764 
    765   for (i = 0; i < d->ntypes; ++i) {
    766     KitDwarfType* t = d->types_by_off[i];
    767     if (!t) continue;
    768     if (t->fields) h->free(h, t->fields, t->nfields * sizeof(DwField));
    769     if (t->evals) h->free(h, t->evals, t->nevals * sizeof(DwEnumVal));
    770     h->free(h, t, sizeof(*t));
    771   }
    772   if (d->types_by_off)
    773     h->free(h, d->types_by_off, d->types_cap * sizeof(KitDwarfType*));
    774   if (d->types_off) h->free(h, d->types_off, d->types_cap * sizeof(u32));
    775 
    776   if (d->globals) h->free(h, d->globals, d->globals_cap * sizeof(DwLocal));
    777 
    778   if (d->strs) {
    779     pool_fini(d->strs);
    780     h->free(h, d->strs, sizeof(*d->strs));
    781   }
    782 
    783   h->free(h, d, sizeof(*d));
    784 }