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 }