kit

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

dwarf_die.c (13854B)


      1 /* dwarf_die.c โ€” DIE walker: subprogram collection, locals, globals.
      2  *
      3  * Per doc/DWARF.md ยง4.3: streaming walker over .debug_info keyed off the
      4  * abbrev table; collects subprograms, lexical_blocks, formal_parameters,
      5  * variables. Cross-CU refs land later when needed.
      6  */
      7 
      8 #include <kit/dwarf.h>
      9 #include <stddef.h>
     10 #include <stdint.h>
     11 #include <string.h>
     12 
     13 #include "core/core.h"
     14 #include "core/heap.h"
     15 #include "core/util.h"
     16 #include "debug/dwarf_internal.h"
     17 
     18 /* ---- subprogram + lexical_block walk --------------------------------- */
     19 
     20 static void pack_init(DieAttrPack* p) { memset(p, 0, sizeof(*p)); }
     21 
     22 /* Read all attributes of a DIE into pack `p`; updates *off to past attrs. */
     23 static void read_pack(KitDebugInfo* d, const DwCu* cu, DwDie* die,
     24                       DieAttrPack* p, u32* off) {
     25   u32 i;
     26   if (!die->abbrev) return;
     27   for (i = 0; i < die->abbrev->nattrs; ++i) {
     28     DwAbbrevAttr* aa = &die->abbrev->attrs[i];
     29     DwAttrValue v;
     30     dw_read_form(d, cu, aa->form, aa->implicit_const, off, &v);
     31     switch (aa->attr) {
     32       case DW_AT_name:
     33         p->name = v.str;
     34         break;
     35       case DW_AT_low_pc:
     36         p->low_pc = v.u;
     37         p->has_low_pc = 1;
     38         break;
     39       case DW_AT_high_pc:
     40         p->high_pc_value = v.u;
     41         p->high_pc_form = aa->form;
     42         p->has_high_pc = 1;
     43         break;
     44       case DW_AT_type:
     45         /* Local CU offset: ref* forms are CU-relative; ref_addr is
     46          * .debug_info-absolute. */
     47         if (aa->form == DW_FORM_ref_addr)
     48           p->type_die_offset = (u32)v.u;
     49         else
     50           p->type_die_offset = cu->hdr_offset + (u32)v.u;
     51         p->has_type = 1;
     52         break;
     53       case DW_AT_decl_file:
     54         p->decl_file = (u32)v.u;
     55         break;
     56       case DW_AT_decl_line:
     57         p->decl_line = (u32)v.u;
     58         break;
     59       case DW_AT_location:
     60         if (aa->form == DW_FORM_loclistx) {
     61           p->has_loclist = 1;
     62           p->loclist_index = v.u;
     63         } else if (aa->form == DW_FORM_exprloc || aa->form == DW_FORM_block ||
     64                    aa->form == DW_FORM_block1 || aa->form == DW_FORM_block2 ||
     65                    aa->form == DW_FORM_block4) {
     66           p->loc_block = v.block;
     67           p->loc_block_len = v.block_len;
     68         } else if (aa->form == DW_FORM_sec_offset) {
     69           /* Reference into .debug_loclists โ€” not supported in Phase 5
     70            * baseline. */
     71           p->has_loclist = 1;
     72           p->loclist_index = v.u;
     73         }
     74         break;
     75       case DW_AT_frame_base:
     76         p->fb_block = v.block;
     77         p->fb_block_len = v.block_len;
     78         break;
     79       case DW_AT_const_value:
     80         p->const_value = v.s;
     81         p->has_const_value = 1;
     82         break;
     83       case DW_AT_data_member_location:
     84         if (aa->form == DW_FORM_exprloc || aa->form == DW_FORM_block ||
     85             aa->form == DW_FORM_block1 || aa->form == DW_FORM_block2 ||
     86             aa->form == DW_FORM_block4) {
     87           /* Best effort: evaluate a single DW_OP_plus_uconst form by
     88            * peeking. */
     89           if (v.block && v.block_len > 0 && v.block[0] == DW_OP_plus_uconst) {
     90             u32 t = 1;
     91             p->byte_offset = (u32)dw_uleb(v.block, v.block_len, &t);
     92             p->has_byte_offset = 1;
     93           }
     94         } else {
     95           p->byte_offset = (u32)v.u;
     96           p->has_byte_offset = 1;
     97         }
     98         break;
     99       case DW_AT_byte_size:
    100         p->byte_size = (u32)v.u;
    101         p->has_byte_size = 1;
    102         break;
    103       case DW_AT_bit_size:
    104         p->bit_size = (u32)v.u;
    105         p->has_bit_size = 1;
    106         break;
    107       case DW_AT_bit_offset:
    108       case DW_AT_data_bit_offset:
    109         p->bit_offset = (u32)v.u;
    110         p->has_bit_offset = 1;
    111         break;
    112       case DW_AT_encoding:
    113         p->base_encoding = (u32)v.u;
    114         p->has_encoding = 1;
    115         break;
    116       case DW_AT_count:
    117       case DW_AT_upper_bound:
    118         p->array_count = (u32)v.u;
    119         if (aa->attr == DW_AT_upper_bound) p->array_count++;
    120         p->has_array_count = 1;
    121         break;
    122     }
    123   }
    124 }
    125 
    126 /* Append a subprogram (or skip if its bounds aren't useful). */
    127 static void push_subprog(KitDebugInfo* d, DwSubprog* sp) {
    128   if (d->nsubs == d->subs_cap) {
    129     u32 ncap = d->subs_cap ? d->subs_cap * 2 : 8;
    130     DwSubprog* na =
    131         (DwSubprog*)d->h->realloc(d->h, d->subs, d->subs_cap * sizeof(*d->subs),
    132                                   ncap * sizeof(*d->subs), _Alignof(DwSubprog));
    133     if (!na) return;
    134     d->subs = na;
    135     d->subs_cap = ncap;
    136   }
    137   d->subs[d->nsubs++] = *sp;
    138 }
    139 
    140 /* Walk a DIE subtree, collecting subprograms. */
    141 static void walk_for_subs(KitDebugInfo* d, u32 cu_idx, u32* off) {
    142   DwCu* cu = &d->cus[cu_idx];
    143   for (;;) {
    144     DwDie die;
    145     if (!dw_read_die(d, cu, off, &die)) return;
    146     if (die.abbrev->tag == DW_TAG_subprogram ||
    147         die.abbrev->tag == DW_TAG_inlined_subroutine) {
    148       DieAttrPack p;
    149       DwSubprog sp;
    150       u32 saved_off;
    151       pack_init(&p);
    152       saved_off = *off;
    153       read_pack(d, cu, &die, &p, off);
    154       memset(&sp, 0, sizeof(sp));
    155       sp.name = p.name ? p.name : "";
    156       sp.low_pc = p.low_pc;
    157       if (p.has_high_pc) {
    158         if (p.high_pc_form == DW_FORM_addr)
    159           sp.high_pc = p.high_pc_value;
    160         else
    161           sp.high_pc = p.low_pc + p.high_pc_value;
    162       } else {
    163         sp.high_pc = p.low_pc;
    164       }
    165       sp.decl_line = p.decl_line;
    166       sp.type_die_offset = p.has_type ? p.type_die_offset : 0;
    167       /* Resolve decl_file via the CU's line program. */
    168       sp.decl_file = "";
    169       if (p.decl_file != 0 && cu->has_stmt_list) {
    170         DwLineProgram* lp;
    171         if (!d->lines_built[cu_idx]) dw_build_line(d, cu_idx);
    172         lp = &d->lines_by_cu[cu_idx];
    173         if (lp->nfile_norm && p.decl_file < lp->nfile_norm)
    174           sp.decl_file = lp->file_norm[p.decl_file];
    175       }
    176       sp.cu_idx = cu_idx;
    177       sp.die_offset = die.die_off;
    178       sp.frame_base = p.fb_block;
    179       sp.frame_base_len = p.fb_block_len;
    180       sp.inlined = (die.abbrev->tag == DW_TAG_inlined_subroutine);
    181       if (p.has_low_pc && sp.high_pc > sp.low_pc)
    182         push_subprog(d, &sp);
    183       else if (die.abbrev->tag == DW_TAG_subprogram && p.name)
    184         push_subprog(d, &sp); /* declaration-only OK */
    185       (void)saved_off;
    186       /* Recurse into children for nested subprograms / inlines. */
    187       if (die.abbrev->has_children) {
    188         walk_for_subs(d, cu_idx, off);
    189       }
    190     } else if (die.abbrev->has_children) {
    191       /* Skip attrs, then descend. */
    192       u32 i;
    193       for (i = 0; i < die.abbrev->nattrs; ++i) {
    194         DwAbbrevAttr* aa = &die.abbrev->attrs[i];
    195         dw_skip_form(d, cu, aa->form, aa->implicit_const, off);
    196       }
    197       walk_for_subs(d, cu_idx, off);
    198     } else {
    199       u32 i;
    200       for (i = 0; i < die.abbrev->nattrs; ++i) {
    201         DwAbbrevAttr* aa = &die.abbrev->attrs[i];
    202         dw_skip_form(d, cu, aa->form, aa->implicit_const, off);
    203       }
    204     }
    205   }
    206 }
    207 
    208 void dw_build_subs(KitDebugInfo* d) {
    209   u32 i;
    210   if (d->subs_built) return;
    211   d->subs_built = 1;
    212   for (i = 0; i < d->ncus; ++i) {
    213     DwCu* cu = &d->cus[i];
    214     u32 off = cu->die_start_off;
    215     /* The root DIE is the CU itself โ€” recurse into it. */
    216     DwDie root;
    217     if (!dw_read_die(d, cu, &off, &root)) continue;
    218     /* Skip root attrs */
    219     {
    220       u32 j;
    221       for (j = 0; j < root.abbrev->nattrs; ++j) {
    222         DwAbbrevAttr* aa = &root.abbrev->attrs[j];
    223         dw_skip_form(d, cu, aa->form, aa->implicit_const, &off);
    224       }
    225     }
    226     if (root.abbrev->has_children) walk_for_subs(d, i, &off);
    227   }
    228 }
    229 
    230 DwSubprog* dw_find_subprog(KitDebugInfo* d, u64 pc) {
    231   u32 i;
    232   dw_build_subs(d);
    233   for (i = 0; i < d->nsubs; ++i) {
    234     DwSubprog* sp = &d->subs[i];
    235     if (sp->low_pc <= pc && pc < sp->high_pc) return sp;
    236   }
    237   return NULL;
    238 }
    239 
    240 /* ---- locals + parameters --------------------------------------------- */
    241 
    242 typedef struct LocalCtx {
    243   KitDebugInfo* d;
    244   u32 cu_idx;
    245   DwLocal* params;
    246   u32 nparams, params_cap;
    247   DwLocal* locals;
    248   u32 nlocals, locals_cap;
    249 } LocalCtx;
    250 
    251 static void push_param(LocalCtx* x, DwLocal* v) {
    252   if (x->nparams == x->params_cap) {
    253     u32 ncap = x->params_cap ? x->params_cap * 2 : 4;
    254     DwLocal* na = (DwLocal*)x->d->h->realloc(
    255         x->d->h, x->params, x->params_cap * sizeof(*x->params),
    256         ncap * sizeof(*x->params), _Alignof(DwLocal));
    257     if (!na) return;
    258     x->params = na;
    259     x->params_cap = ncap;
    260   }
    261   x->params[x->nparams++] = *v;
    262 }
    263 static void push_local(LocalCtx* x, DwLocal* v) {
    264   if (x->nlocals == x->locals_cap) {
    265     u32 ncap = x->locals_cap ? x->locals_cap * 2 : 4;
    266     DwLocal* na = (DwLocal*)x->d->h->realloc(
    267         x->d->h, x->locals, x->locals_cap * sizeof(*x->locals),
    268         ncap * sizeof(*x->locals), _Alignof(DwLocal));
    269     if (!na) return;
    270     x->locals = na;
    271     x->locals_cap = ncap;
    272   }
    273   x->locals[x->nlocals++] = *v;
    274 }
    275 
    276 static void walk_subprog_body(LocalCtx* x, u32* off, u64 scope_lo, u64 scope_hi,
    277                               u32 scope_die_off, u8 has_scope) {
    278   KitDebugInfo* d = x->d;
    279   DwCu* cu = &d->cus[x->cu_idx];
    280   for (;;) {
    281     DwDie die;
    282     if (!dw_read_die(d, cu, off, &die)) return;
    283     if (die.abbrev->tag == DW_TAG_formal_parameter ||
    284         die.abbrev->tag == DW_TAG_variable) {
    285       DieAttrPack p;
    286       DwLocal v;
    287       pack_init(&p);
    288       read_pack(d, cu, &die, &p, off);
    289       memset(&v, 0, sizeof(v));
    290       v.name = p.name ? p.name : "";
    291       v.die_offset = die.die_off;
    292       v.type_die_offset = p.has_type ? p.type_die_offset : 0;
    293       v.scope_lo = scope_lo;
    294       v.scope_hi = scope_hi;
    295       v.scope_offset = scope_die_off;
    296       v.has_scope = has_scope;
    297       v.loc = p.loc_block;
    298       v.loc_len = p.loc_block_len;
    299       v.has_loclist = p.has_loclist;
    300       v.loclist_index = p.loclist_index;
    301       v.is_param = (die.abbrev->tag == DW_TAG_formal_parameter);
    302       v.is_global = 0;
    303       if (v.is_param)
    304         push_param(x, &v);
    305       else
    306         push_local(x, &v);
    307       if (die.abbrev->has_children)
    308         walk_subprog_body(x, off, scope_lo, scope_hi, scope_die_off, has_scope);
    309     } else if (die.abbrev->tag == DW_TAG_lexical_block) {
    310       DieAttrPack p;
    311       pack_init(&p);
    312       read_pack(d, cu, &die, &p, off);
    313       {
    314         u64 lo = p.has_low_pc ? p.low_pc : scope_lo;
    315         u64 hi = p.has_high_pc
    316                      ? (p.high_pc_form == DW_FORM_addr ? p.high_pc_value
    317                                                        : lo + p.high_pc_value)
    318                      : scope_hi;
    319         if (die.abbrev->has_children)
    320           walk_subprog_body(x, off, lo, hi, die.die_off, 1);
    321       }
    322     } else {
    323       u32 i;
    324       for (i = 0; i < die.abbrev->nattrs; ++i) {
    325         DwAbbrevAttr* aa = &die.abbrev->attrs[i];
    326         dw_skip_form(d, cu, aa->form, aa->implicit_const, off);
    327       }
    328       if (die.abbrev->has_children)
    329         walk_subprog_body(x, off, scope_lo, scope_hi, scope_die_off, has_scope);
    330     }
    331   }
    332 }
    333 
    334 void dw_build_locals(KitDebugInfo* d, DwSubprog* sp) {
    335   LocalCtx x;
    336   DwCu* cu;
    337   u32 off;
    338   DwDie die;
    339   if (sp->cached_locals) return;
    340   sp->cached_locals = 1;
    341   cu = &d->cus[sp->cu_idx];
    342   off = sp->die_offset;
    343   if (!dw_read_die(d, cu, &off, &die)) return;
    344   if (!die.abbrev || !die.abbrev->has_children) return;
    345   /* Skip subprog attrs */
    346   {
    347     u32 i;
    348     for (i = 0; i < die.abbrev->nattrs; ++i) {
    349       DwAbbrevAttr* aa = &die.abbrev->attrs[i];
    350       dw_skip_form(d, cu, aa->form, aa->implicit_const, &off);
    351     }
    352   }
    353   memset(&x, 0, sizeof(x));
    354   x.d = d;
    355   x.cu_idx = sp->cu_idx;
    356   walk_subprog_body(&x, &off, sp->low_pc, sp->high_pc, sp->die_offset, 1);
    357   sp->params = x.params;
    358   sp->nparams = x.nparams;
    359   sp->locals = x.locals;
    360   sp->nlocals = x.nlocals;
    361 }
    362 
    363 /* ---- globals --------------------------------------------------------- */
    364 
    365 void dw_build_globals(KitDebugInfo* d) {
    366   u32 i;
    367   if (d->globals_built) return;
    368   d->globals_built = 1;
    369   for (i = 0; i < d->ncus; ++i) {
    370     DwCu* cu = &d->cus[i];
    371     u32 off = cu->die_start_off;
    372     DwDie root;
    373     if (!dw_read_die(d, cu, &off, &root)) continue;
    374     {
    375       u32 j;
    376       for (j = 0; j < root.abbrev->nattrs; ++j) {
    377         DwAbbrevAttr* aa = &root.abbrev->attrs[j];
    378         dw_skip_form(d, cu, aa->form, aa->implicit_const, &off);
    379       }
    380     }
    381     if (!root.abbrev->has_children) continue;
    382     /* Walk only top-level children of the CU; collect DW_TAG_variable. */
    383     for (;;) {
    384       DwDie die;
    385       if (!dw_read_die(d, cu, &off, &die)) break;
    386       if (die.abbrev->tag == DW_TAG_variable) {
    387         DieAttrPack p;
    388         DwLocal v;
    389         pack_init(&p);
    390         read_pack(d, cu, &die, &p, &off);
    391         memset(&v, 0, sizeof(v));
    392         v.name = p.name ? p.name : "";
    393         v.die_offset = die.die_off;
    394         v.type_die_offset = p.has_type ? p.type_die_offset : 0;
    395         v.loc = p.loc_block;
    396         v.loc_len = p.loc_block_len;
    397         v.has_loclist = p.has_loclist;
    398         v.loclist_index = p.loclist_index;
    399         v.is_param = 0;
    400         v.is_global = 1;
    401         if (d->nglobals == d->globals_cap) {
    402           u32 ncap = d->globals_cap ? d->globals_cap * 2 : 8;
    403           DwLocal* na = (DwLocal*)d->h->realloc(
    404               d->h, d->globals, d->globals_cap * sizeof(*d->globals),
    405               ncap * sizeof(*d->globals), _Alignof(DwLocal));
    406           if (!na) break;
    407           d->globals = na;
    408           d->globals_cap = ncap;
    409         }
    410         d->globals[d->nglobals++] = v;
    411         if (die.abbrev->has_children) {
    412           /* Skip children. */
    413           for (;;) {
    414             DwDie c;
    415             if (!dw_read_die(d, cu, &off, &c)) break;
    416             dw_skip_die_subtree(d, cu, &c, &off);
    417           }
    418         }
    419       } else {
    420         dw_skip_die_subtree(d, cu, &die, &off);
    421       }
    422     }
    423   }
    424 }
    425 
    426 /* Public accessor for the type module: read attrs given die. */
    427 void dw_die_pack(KitDebugInfo* d, const DwCu* cu, DwDie* die, DieAttrPack* p) {
    428   u32 off = die->attrs_off;
    429   pack_init(p);
    430   read_pack(d, cu, die, p, &off);
    431 }