kit

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

dwarf_die.c (13205B)


      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       dw_skip_die_attrs(d, cu, &die, off);
    193       walk_for_subs(d, cu_idx, off);
    194     } else {
    195       dw_skip_die_attrs(d, cu, &die, off);
    196     }
    197   }
    198 }
    199 
    200 void dw_build_subs(KitDebugInfo* d) {
    201   u32 i;
    202   if (d->subs_built) return;
    203   d->subs_built = 1;
    204   for (i = 0; i < d->ncus; ++i) {
    205     DwCu* cu = &d->cus[i];
    206     u32 off = cu->die_start_off;
    207     /* The root DIE is the CU itself — recurse into it. */
    208     DwDie root;
    209     if (!dw_read_die(d, cu, &off, &root)) continue;
    210     /* Skip root attrs */
    211     dw_skip_die_attrs(d, cu, &root, &off);
    212     if (root.abbrev->has_children) walk_for_subs(d, i, &off);
    213   }
    214 }
    215 
    216 DwSubprog* dw_find_subprog(KitDebugInfo* d, u64 pc) {
    217   u32 i;
    218   dw_build_subs(d);
    219   for (i = 0; i < d->nsubs; ++i) {
    220     DwSubprog* sp = &d->subs[i];
    221     if (sp->low_pc <= pc && pc < sp->high_pc) return sp;
    222   }
    223   return NULL;
    224 }
    225 
    226 /* ---- locals + parameters --------------------------------------------- */
    227 
    228 typedef struct LocalCtx {
    229   KitDebugInfo* d;
    230   u32 cu_idx;
    231   DwLocal* params;
    232   u32 nparams, params_cap;
    233   DwLocal* locals;
    234   u32 nlocals, locals_cap;
    235 } LocalCtx;
    236 
    237 static void push_param(LocalCtx* x, DwLocal* v) {
    238   if (x->nparams == x->params_cap) {
    239     u32 ncap = x->params_cap ? x->params_cap * 2 : 4;
    240     DwLocal* na = (DwLocal*)x->d->h->realloc(
    241         x->d->h, x->params, x->params_cap * sizeof(*x->params),
    242         ncap * sizeof(*x->params), _Alignof(DwLocal));
    243     if (!na) return;
    244     x->params = na;
    245     x->params_cap = ncap;
    246   }
    247   x->params[x->nparams++] = *v;
    248 }
    249 static void push_local(LocalCtx* x, DwLocal* v) {
    250   if (x->nlocals == x->locals_cap) {
    251     u32 ncap = x->locals_cap ? x->locals_cap * 2 : 4;
    252     DwLocal* na = (DwLocal*)x->d->h->realloc(
    253         x->d->h, x->locals, x->locals_cap * sizeof(*x->locals),
    254         ncap * sizeof(*x->locals), _Alignof(DwLocal));
    255     if (!na) return;
    256     x->locals = na;
    257     x->locals_cap = ncap;
    258   }
    259   x->locals[x->nlocals++] = *v;
    260 }
    261 
    262 static void walk_subprog_body(LocalCtx* x, u32* off, u64 scope_lo, u64 scope_hi,
    263                               u32 scope_die_off, u8 has_scope) {
    264   KitDebugInfo* d = x->d;
    265   DwCu* cu = &d->cus[x->cu_idx];
    266   for (;;) {
    267     DwDie die;
    268     if (!dw_read_die(d, cu, off, &die)) return;
    269     if (die.abbrev->tag == DW_TAG_formal_parameter ||
    270         die.abbrev->tag == DW_TAG_variable) {
    271       DieAttrPack p;
    272       DwLocal v;
    273       pack_init(&p);
    274       read_pack(d, cu, &die, &p, off);
    275       memset(&v, 0, sizeof(v));
    276       v.name = p.name ? p.name : "";
    277       v.die_offset = die.die_off;
    278       v.type_die_offset = p.has_type ? p.type_die_offset : 0;
    279       v.scope_lo = scope_lo;
    280       v.scope_hi = scope_hi;
    281       v.scope_offset = scope_die_off;
    282       v.has_scope = has_scope;
    283       v.loc = p.loc_block;
    284       v.loc_len = p.loc_block_len;
    285       v.has_loclist = p.has_loclist;
    286       v.loclist_index = p.loclist_index;
    287       v.is_param = (die.abbrev->tag == DW_TAG_formal_parameter);
    288       v.is_global = 0;
    289       if (v.is_param)
    290         push_param(x, &v);
    291       else
    292         push_local(x, &v);
    293       if (die.abbrev->has_children)
    294         walk_subprog_body(x, off, scope_lo, scope_hi, scope_die_off, has_scope);
    295     } else if (die.abbrev->tag == DW_TAG_lexical_block) {
    296       DieAttrPack p;
    297       pack_init(&p);
    298       read_pack(d, cu, &die, &p, off);
    299       {
    300         u64 lo = p.has_low_pc ? p.low_pc : scope_lo;
    301         u64 hi = p.has_high_pc
    302                      ? (p.high_pc_form == DW_FORM_addr ? p.high_pc_value
    303                                                        : lo + p.high_pc_value)
    304                      : scope_hi;
    305         if (die.abbrev->has_children)
    306           walk_subprog_body(x, off, lo, hi, die.die_off, 1);
    307       }
    308     } else {
    309       dw_skip_die_attrs(d, cu, &die, off);
    310       if (die.abbrev->has_children)
    311         walk_subprog_body(x, off, scope_lo, scope_hi, scope_die_off, has_scope);
    312     }
    313   }
    314 }
    315 
    316 void dw_build_locals(KitDebugInfo* d, DwSubprog* sp) {
    317   LocalCtx x;
    318   DwCu* cu;
    319   u32 off;
    320   DwDie die;
    321   if (sp->cached_locals) return;
    322   sp->cached_locals = 1;
    323   cu = &d->cus[sp->cu_idx];
    324   off = sp->die_offset;
    325   if (!dw_read_die(d, cu, &off, &die)) return;
    326   if (!die.abbrev || !die.abbrev->has_children) return;
    327   /* Skip subprog attrs */
    328   dw_skip_die_attrs(d, cu, &die, &off);
    329   memset(&x, 0, sizeof(x));
    330   x.d = d;
    331   x.cu_idx = sp->cu_idx;
    332   walk_subprog_body(&x, &off, sp->low_pc, sp->high_pc, sp->die_offset, 1);
    333   sp->params = x.params;
    334   sp->nparams = x.nparams;
    335   sp->locals = x.locals;
    336   sp->nlocals = x.nlocals;
    337 }
    338 
    339 /* ---- globals --------------------------------------------------------- */
    340 
    341 void dw_build_globals(KitDebugInfo* d) {
    342   u32 i;
    343   if (d->globals_built) return;
    344   d->globals_built = 1;
    345   for (i = 0; i < d->ncus; ++i) {
    346     DwCu* cu = &d->cus[i];
    347     u32 off = cu->die_start_off;
    348     DwDie root;
    349     if (!dw_read_die(d, cu, &off, &root)) continue;
    350     dw_skip_die_attrs(d, cu, &root, &off);
    351     if (!root.abbrev->has_children) continue;
    352     /* Walk only top-level children of the CU; collect DW_TAG_variable. */
    353     for (;;) {
    354       DwDie die;
    355       if (!dw_read_die(d, cu, &off, &die)) break;
    356       if (die.abbrev->tag == DW_TAG_variable) {
    357         DieAttrPack p;
    358         DwLocal v;
    359         pack_init(&p);
    360         read_pack(d, cu, &die, &p, &off);
    361         memset(&v, 0, sizeof(v));
    362         v.name = p.name ? p.name : "";
    363         v.die_offset = die.die_off;
    364         v.type_die_offset = p.has_type ? p.type_die_offset : 0;
    365         v.loc = p.loc_block;
    366         v.loc_len = p.loc_block_len;
    367         v.has_loclist = p.has_loclist;
    368         v.loclist_index = p.loclist_index;
    369         v.is_param = 0;
    370         v.is_global = 1;
    371         if (d->nglobals == d->globals_cap) {
    372           u32 ncap = d->globals_cap ? d->globals_cap * 2 : 8;
    373           DwLocal* na = (DwLocal*)d->h->realloc(
    374               d->h, d->globals, d->globals_cap * sizeof(*d->globals),
    375               ncap * sizeof(*d->globals), _Alignof(DwLocal));
    376           if (!na) break;
    377           d->globals = na;
    378           d->globals_cap = ncap;
    379         }
    380         d->globals[d->nglobals++] = v;
    381         if (die.abbrev->has_children) {
    382           /* Skip children. */
    383           for (;;) {
    384             DwDie c;
    385             if (!dw_read_die(d, cu, &off, &c)) break;
    386             dw_skip_die_subtree(d, cu, &c, &off);
    387           }
    388         }
    389       } else {
    390         dw_skip_die_subtree(d, cu, &die, &off);
    391       }
    392     }
    393   }
    394 }
    395 
    396 /* Public accessor for the type module: decode a DIE's attrs into `p` and
    397  * advance *off past them (so the caller lands on the first child DIE). The
    398  * single read_pack pass both decodes and advances — callers no longer need a
    399  * second dw_skip_form loop to step over the attribute stream. */
    400 void dw_die_pack(KitDebugInfo* d, const DwCu* cu, DwDie* die, DieAttrPack* p,
    401                  u32* off) {
    402   pack_init(p);
    403   read_pack(d, cu, die, p, off);
    404 }