kit

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

dwarf_open.c (25370B)


      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   /* First pass: pull str_offsets_base if present (so subsequent strx
    352    * resolutions work). */
    353   for (i = 0; i < ab->nattrs; ++i) {
    354     DwAbbrevAttr* aa = &ab->attrs[i];
    355     if (aa->attr == DW_AT_str_offsets_base) {
    356       u32 tmp = off;
    357       /* Skip preceding attrs to locate this attr's payload — easier
    358        * to do a full pass and remember offsets. We re-scan instead. */
    359       (void)tmp;
    360       break;
    361     }
    362   }
    363   /* Two-pass scan: do skipping reads, but capture base attrs. We must
    364    * be careful: dw_read_form for strx forms uses cu->str_offsets_base,
    365    * so we read in two passes. */
    366   off = cu->die_start_off;
    367   (void)dw_uleb(d->info.data, d->info.size, &off); /* re-skip code */
    368   /* Pass 1: only read str_offsets_base / addr_base (forms that don't
    369    * themselves need those bases). */
    370   for (i = 0; i < ab->nattrs; ++i) {
    371     DwAbbrevAttr* aa = &ab->attrs[i];
    372     if (aa->attr == DW_AT_str_offsets_base || aa->attr == DW_AT_addr_base ||
    373         aa->attr == DW_AT_loclists_base || aa->attr == DW_AT_rnglists_base) {
    374       dw_read_form(d, cu, aa->form, aa->implicit_const, &off, &v);
    375       if (aa->attr == DW_AT_str_offsets_base)
    376         cu->str_offsets_base = (u32)v.u;
    377       else if (aa->attr == DW_AT_addr_base)
    378         cu->addr_base = (u32)v.u;
    379       else if (aa->attr == DW_AT_loclists_base)
    380         cu->loclists_base = (u32)v.u;
    381       else if (aa->attr == DW_AT_rnglists_base)
    382         cu->rnglists_base = (u32)v.u;
    383     } else {
    384       dw_skip_form(d, cu, aa->form, aa->implicit_const, &off);
    385     }
    386   }
    387   /* Pass 2: read remaining attrs (stmt_list, name, comp_dir). */
    388   off = cu->die_start_off;
    389   (void)dw_uleb(d->info.data, d->info.size, &off);
    390   for (i = 0; i < ab->nattrs; ++i) {
    391     DwAbbrevAttr* aa = &ab->attrs[i];
    392     if (aa->attr == DW_AT_stmt_list) {
    393       dw_read_form(d, cu, aa->form, aa->implicit_const, &off, &v);
    394       cu->stmt_list = (u32)v.u;
    395       cu->has_stmt_list = 1;
    396     } else if (aa->attr == DW_AT_name) {
    397       dw_read_form(d, cu, aa->form, aa->implicit_const, &off, &v);
    398       cu->name = v.str ? v.str : "";
    399     } else if (aa->attr == DW_AT_comp_dir) {
    400       dw_read_form(d, cu, aa->form, aa->implicit_const, &off, &v);
    401       cu->comp_dir = v.str ? v.str : "";
    402     } else {
    403       dw_skip_form(d, cu, aa->form, aa->implicit_const, &off);
    404     }
    405   }
    406 }
    407 
    408 void dw_parse_all_cus(KitDebugInfo* d) {
    409   u32 off = 0;
    410   /* Idempotent: a successful kit_dwarf_open already populates d->cus, and
    411    * the structural-enumeration iterators call this again. Re-parsing would
    412    * append duplicate CUs, so bail once the table is built. */
    413   if (d->ncus) return;
    414   while (off < d->info.size) {
    415     DwCu cu;
    416     u32 next = dw_cu_parse_header(d, off, &cu);
    417     if (next <= off) break;
    418     if (cu.is_64bit) {
    419       off = next;
    420       continue;
    421     }
    422     if (cu.version < 2 || cu.version > 5) {
    423       off = next;
    424       continue;
    425     }
    426     if (d->ncus == d->cus_cap) {
    427       u32 ncap = d->cus_cap ? d->cus_cap * 2 : 4;
    428       DwCu* na =
    429           (DwCu*)d->h->realloc(d->h, d->cus, d->cus_cap * sizeof(*d->cus),
    430                                ncap * sizeof(*d->cus), _Alignof(DwCu));
    431       if (!na) break;
    432       d->cus = na;
    433       d->cus_cap = ncap;
    434     }
    435     d->cus[d->ncus++] = cu;
    436     /* Capture root attrs now. */
    437     cu_read_root_attrs(d, &d->cus[d->ncus - 1]);
    438     off = next;
    439   }
    440 }
    441 
    442 DwCu* dw_cu_at_die_offset(KitDebugInfo* d, u32 die_offset) {
    443   u32 i;
    444   for (i = 0; i < d->ncus; ++i) {
    445     DwCu* cu = &d->cus[i];
    446     if (die_offset >= cu->hdr_offset &&
    447         die_offset < cu->hdr_offset + cu->unit_total_size) {
    448       return cu;
    449     }
    450   }
    451   return NULL;
    452 }
    453 
    454 /* ---- form decoding ---------------------------------------------------- */
    455 
    456 /* Section-parameterized form decoder. Inline form bytes are pulled from
    457  * `sec` (.debug_info for DIE attributes, .debug_line for line-program
    458  * file/dir entry-format values); strp/line_strp/strx still resolve into the
    459  * shared string sections via the CU. This is the single source of truth —
    460  * dw_read_form wires it to .debug_info; the line decoder passes &d->line. */
    461 void dw_read_form_in(KitDebugInfo* d, const DwCu* cu, const DwSection* sec,
    462                      u32 form, i64 implicit_const, u32* off, DwAttrValue* out) {
    463   out->form = form;
    464   out->u = 0;
    465   out->s = 0;
    466   out->str = "";
    467   out->block = NULL;
    468   out->block_len = 0;
    469   switch (form) {
    470     case DW_FORM_addr:
    471       if (cu->address_size == 8)
    472         out->u = dw_u64(sec->data, sec->size, off);
    473       else
    474         out->u = dw_u32(sec->data, sec->size, off);
    475       break;
    476     case DW_FORM_data1:
    477     case DW_FORM_ref1:
    478     case DW_FORM_flag:
    479     case DW_FORM_strx1:
    480     case DW_FORM_addrx1:
    481       out->u = dw_u8(sec->data, sec->size, off);
    482       out->s = (i64)(i8)out->u;
    483       if (form == DW_FORM_strx1) out->str = dw_strx(d, cu, out->u);
    484       break;
    485     case DW_FORM_data2:
    486     case DW_FORM_ref2:
    487     case DW_FORM_strx2:
    488     case DW_FORM_addrx2:
    489       out->u = dw_u16(sec->data, sec->size, off);
    490       out->s = (i64)(i16)out->u;
    491       if (form == DW_FORM_strx2) out->str = dw_strx(d, cu, out->u);
    492       break;
    493     case DW_FORM_strx3:
    494     case DW_FORM_addrx3:
    495       out->u = dw_u24(sec->data, sec->size, off);
    496       if (form == DW_FORM_strx3) out->str = dw_strx(d, cu, out->u);
    497       break;
    498     case DW_FORM_data4:
    499     case DW_FORM_ref4:
    500     case DW_FORM_strx4:
    501     case DW_FORM_addrx4:
    502       out->u = dw_u32(sec->data, sec->size, off);
    503       out->s = (i64)(i32)out->u;
    504       if (form == DW_FORM_strx4) out->str = dw_strx(d, cu, out->u);
    505       break;
    506     case DW_FORM_data8:
    507     case DW_FORM_ref8:
    508     case DW_FORM_ref_sig8:
    509     case DW_FORM_ref_sup8:
    510       out->u = dw_u64(sec->data, sec->size, off);
    511       out->s = (i64)out->u;
    512       break;
    513     case DW_FORM_data16:
    514       /* Skip 16 bytes; not commonly needed. */
    515       *off += 16;
    516       break;
    517     case DW_FORM_sdata:
    518       out->s = dw_sleb(sec->data, sec->size, off);
    519       out->u = (u64)out->s;
    520       break;
    521     case DW_FORM_udata:
    522     case DW_FORM_ref_udata:
    523     case DW_FORM_strx:
    524     case DW_FORM_addrx:
    525     case DW_FORM_loclistx:
    526     case DW_FORM_rnglistx:
    527       out->u = dw_uleb(sec->data, sec->size, off);
    528       if (form == DW_FORM_strx) out->str = dw_strx(d, cu, out->u);
    529       break;
    530     case DW_FORM_string:
    531       out->str = dw_cstr(sec->data, sec->size, off);
    532       break;
    533     case DW_FORM_strp:
    534       out->u = dw_u32(sec->data, sec->size, off);
    535       out->str = dw_str(d, (u32)out->u);
    536       break;
    537     case DW_FORM_line_strp:
    538       out->u = dw_u32(sec->data, sec->size, off);
    539       out->str = dw_line_str(d, (u32)out->u);
    540       break;
    541     case DW_FORM_strp_sup:
    542     case DW_FORM_ref_sup4:
    543       out->u = dw_u32(sec->data, sec->size, off);
    544       break;
    545     case DW_FORM_sec_offset:
    546       out->u = dw_u32(sec->data, sec->size, off);
    547       break;
    548     case DW_FORM_ref_addr:
    549       /* DWARF 5: 4 bytes for 32-bit DWARF (we don't support DWARF64). */
    550       out->u = dw_u32(sec->data, sec->size, off);
    551       break;
    552     case DW_FORM_flag_present:
    553       out->u = 1;
    554       break;
    555     case DW_FORM_implicit_const:
    556       out->s = implicit_const;
    557       out->u = (u64)implicit_const;
    558       break;
    559     case DW_FORM_block1: {
    560       u32 n = dw_u8(sec->data, sec->size, off);
    561       out->block = sec->data + *off;
    562       out->block_len = n;
    563       out->u = n;
    564       *off += n;
    565     } break;
    566     case DW_FORM_block2: {
    567       u32 n = dw_u16(sec->data, sec->size, off);
    568       out->block = sec->data + *off;
    569       out->block_len = n;
    570       out->u = n;
    571       *off += n;
    572     } break;
    573     case DW_FORM_block4: {
    574       u32 n = dw_u32(sec->data, sec->size, off);
    575       out->block = sec->data + *off;
    576       out->block_len = n;
    577       out->u = n;
    578       *off += n;
    579     } break;
    580     case DW_FORM_block:
    581     case DW_FORM_exprloc: {
    582       u32 n = (u32)dw_uleb(sec->data, sec->size, off);
    583       out->block = sec->data + *off;
    584       out->block_len = n;
    585       out->u = n;
    586       *off += n;
    587     } break;
    588     case DW_FORM_indirect: {
    589       u32 ifrm = (u32)dw_uleb(sec->data, sec->size, off);
    590       dw_read_form_in(d, cu, sec, ifrm, 0, off, out);
    591     } break;
    592     default:
    593       /* Unknown form — best effort: skip nothing. */
    594       break;
    595   }
    596 }
    597 
    598 void dw_read_form(KitDebugInfo* d, const DwCu* cu, u32 form, i64 implicit_const,
    599                   u32* off, DwAttrValue* out) {
    600   dw_read_form_in(d, cu, &d->info, form, implicit_const, off, out);
    601 }
    602 
    603 void dw_skip_form(KitDebugInfo* d, const DwCu* cu, u32 form, i64 implicit_const,
    604                   u32* off) {
    605   DwAttrValue tmp;
    606   dw_read_form(d, cu, form, implicit_const, off, &tmp);
    607 }
    608 
    609 /* ---- DIE iteration ---------------------------------------------------- */
    610 
    611 int dw_read_die(KitDebugInfo* d, const DwCu* cu, u32* off, DwDie* out) {
    612   u64 code;
    613   out->die_off = *off;
    614   if (*off >= d->info.size || *off >= cu->hdr_offset + cu->unit_total_size) {
    615     out->abbrev_code = 0;
    616     out->abbrev = NULL;
    617     out->attrs_off = *off;
    618     return 0;
    619   }
    620   code = dw_uleb(d->info.data, d->info.size, off);
    621   out->abbrev_code = code;
    622   out->attrs_off = *off;
    623   out->next_sibling_off = 0;
    624   if (code == 0) {
    625     out->abbrev = NULL;
    626     return 0;
    627   }
    628   out->abbrev = dw_abbrev_lookup(&d->abbrevs[cu->abbrev_table_idx], code);
    629   return 1;
    630 }
    631 
    632 void dw_skip_die_attrs(KitDebugInfo* d, const DwCu* cu, DwDie* die, u32* off) {
    633   u32 i;
    634   if (!die->abbrev) return;
    635   for (i = 0; i < die->abbrev->nattrs; ++i) {
    636     DwAbbrevAttr* aa = &die->abbrev->attrs[i];
    637     dw_skip_form(d, cu, aa->form, aa->implicit_const, off);
    638   }
    639 }
    640 
    641 void dw_skip_die_subtree(KitDebugInfo* d, const DwCu* cu, DwDie* die,
    642                          u32* off) {
    643   if (!die->abbrev) return;
    644   dw_skip_die_attrs(d, cu, die, off);
    645   if (die->abbrev->has_children) {
    646     for (;;) {
    647       DwDie child;
    648       if (!dw_read_die(d, cu, off, &child)) break;
    649       dw_skip_die_subtree(d, cu, &child, off);
    650     }
    651   }
    652 }
    653 
    654 int dw_die_attr(KitDebugInfo* d, const DwCu* cu, DwDie* die, u32 attr,
    655                 DwAttrValue* out) {
    656   u32 off = die->attrs_off;
    657   u32 i;
    658   if (!die->abbrev) return 0;
    659   for (i = 0; i < die->abbrev->nattrs; ++i) {
    660     DwAbbrevAttr* aa = &die->abbrev->attrs[i];
    661     if (aa->attr == attr) {
    662       dw_read_form(d, cu, aa->form, aa->implicit_const, &off, out);
    663       return 1;
    664     }
    665     dw_skip_form(d, cu, aa->form, aa->implicit_const, &off);
    666   }
    667   return 0;
    668 }
    669 
    670 /* ---- public open/close ----------------------------------------------- */
    671 
    672 KitStatus kit_dwarf_open(const KitContext* ctx, const KitObjFile* obj,
    673                          KitDebugInfo** out) {
    674   Heap* h;
    675   KitDebugInfo* d;
    676   if (!out) return KIT_INVALID;
    677   *out = NULL;
    678   if (!ctx || !ctx->heap || !obj) return KIT_INVALID;
    679   h = ctx->heap;
    680   d = (KitDebugInfo*)h->alloc(h, sizeof(*d), _Alignof(KitDebugInfo));
    681   if (!d) return KIT_NOMEM;
    682   memset(d, 0, sizeof(*d));
    683   d->ctx = ctx;
    684   d->h = h;
    685   d->obj = obj;
    686   d->strs = (Pool*)h->alloc(h, sizeof(*d->strs), _Alignof(Pool));
    687   if (!d->strs) {
    688     kit_dwarf_free(d);
    689     return KIT_NOMEM;
    690   }
    691   pool_init(d->strs, h);
    692 
    693   dw_find_section(d, ".debug_abbrev", &d->abbrev);
    694   dw_find_section(d, ".debug_info", &d->info);
    695   dw_find_section(d, ".debug_line", &d->line);
    696   dw_find_section(d, ".debug_str", &d->str);
    697   dw_find_section(d, ".debug_line_str", &d->line_str);
    698   dw_find_section(d, ".debug_str_offsets", &d->str_offsets);
    699   dw_find_section(d, ".debug_addr", &d->addr);
    700   dw_find_section(d, ".debug_loclists", &d->loclists);
    701   dw_find_section(d, ".debug_rnglists", &d->rnglists);
    702   dw_find_section(d, ".eh_frame", &d->eh_frame);
    703   dw_find_section(d, ".debug_aranges", &d->aranges);
    704 
    705   if (d->abbrev.sec_idx == UINT32_MAX || d->info.sec_idx == UINT32_MAX ||
    706       d->line.sec_idx == UINT32_MAX || d->str.sec_idx == UINT32_MAX ||
    707       d->line_str.sec_idx == UINT32_MAX) {
    708     kit_dwarf_free(d);
    709     return KIT_NOT_FOUND;
    710   }
    711 
    712   /* str_offsets_base default: in the absence of DW_AT_str_offsets_base, the
    713    * offsets section starts with an 8-byte header (uniform for DW5). */
    714   dw_parse_all_cus(d);
    715   if (d->ncus == 0) {
    716     kit_dwarf_free(d);
    717     return KIT_MALFORMED;
    718   }
    719 
    720   /* Allocate per-CU lazy line-program state. */
    721   if (d->ncus) {
    722     d->lines_by_cu = (DwLineProgram*)h->alloc(
    723         h, d->ncus * sizeof(DwLineProgram), _Alignof(DwLineProgram));
    724     d->lines_built = (u8*)h->alloc(h, d->ncus, 1);
    725     if (!d->lines_by_cu || !d->lines_built) {
    726       kit_dwarf_free(d);
    727       return KIT_NOMEM;
    728     }
    729     memset(d->lines_by_cu, 0, d->ncus * sizeof(DwLineProgram));
    730     memset(d->lines_built, 0, d->ncus);
    731   }
    732 
    733   *out = d;
    734   return KIT_OK;
    735 }
    736 
    737 static void free_subprog(Heap* h, DwSubprog* sp) {
    738   if (sp->params) h->free(h, sp->params, sp->nparams * sizeof(DwLocal));
    739   if (sp->locals) h->free(h, sp->locals, sp->nlocals * sizeof(DwLocal));
    740 }
    741 
    742 void kit_dwarf_free(KitDebugInfo* d) {
    743   Heap* h;
    744   u32 i;
    745   if (!d) return;
    746   h = d->h;
    747   for (i = 0; i < d->nabbrevs; ++i) {
    748     u32 j;
    749     DwAbbrevTable* t = &d->abbrevs[i];
    750     for (j = 0; j < t->nabbrevs; ++j) {
    751       if (t->abbrevs[j].attrs)
    752         h->free(h, t->abbrevs[j].attrs,
    753                 t->abbrevs[j].nattrs * sizeof(DwAbbrevAttr));
    754     }
    755     if (t->abbrevs) h->free(h, t->abbrevs, t->cap * sizeof(DwAbbrev));
    756   }
    757   if (d->abbrevs)
    758     h->free(h, d->abbrevs, d->abbrevs_cap * sizeof(DwAbbrevTable));
    759   if (d->cus) h->free(h, d->cus, d->cus_cap * sizeof(DwCu));
    760 
    761   if (d->lines_by_cu) {
    762     for (i = 0; i < d->ncus; ++i) {
    763       DwLineProgram* lp = &d->lines_by_cu[i];
    764       if (lp->rows) h->free(h, lp->rows, lp->cap * sizeof(DwLineRow));
    765       if (lp->files) h->free(h, lp->files, lp->nfiles * sizeof(DwLineFile));
    766       if (lp->dirs) h->free(h, lp->dirs, lp->ndirs * sizeof(const char*));
    767       if (lp->file_norm)
    768         h->free(h, lp->file_norm, lp->nfile_norm * sizeof(const char*));
    769     }
    770     h->free(h, d->lines_by_cu, d->ncus * sizeof(DwLineProgram));
    771   }
    772   if (d->lines_built) h->free(h, d->lines_built, d->ncus);
    773 
    774   for (i = 0; i < d->nsubs; ++i) free_subprog(h, &d->subs[i]);
    775   if (d->subs) h->free(h, d->subs, d->subs_cap * sizeof(DwSubprog));
    776 
    777   for (i = 0; i < d->ntypes; ++i) {
    778     KitDwarfType* t = d->types_by_off[i];
    779     if (!t) continue;
    780     if (t->fields) h->free(h, t->fields, t->nfields * sizeof(DwField));
    781     if (t->evals) h->free(h, t->evals, t->nevals * sizeof(DwEnumVal));
    782     h->free(h, t, sizeof(*t));
    783   }
    784   if (d->types_by_off)
    785     h->free(h, d->types_by_off, d->types_cap * sizeof(KitDwarfType*));
    786   if (d->types_off) h->free(h, d->types_off, d->types_cap * sizeof(u32));
    787 
    788   if (d->globals) h->free(h, d->globals, d->globals_cap * sizeof(DwLocal));
    789 
    790   if (d->strs) {
    791     pool_fini(d->strs);
    792     h->free(h, d->strs, sizeof(*d->strs));
    793   }
    794 
    795   h->free(h, d, sizeof(*d));
    796 }