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 }