dwarf_internal.h (13693B)
1 #ifndef KIT_DWARF_INTERNAL_H 2 #define KIT_DWARF_INTERNAL_H 3 4 /* DWARF 5 consumer — internal types. 5 * 6 * This module reads DWARF bytes out of a KitObjFile and answers the 7 * kit_dwarf_* queries. It is colocated with the producer implementation 8 * but does not include debug/debug.h or share producer state; the public 9 * DWARF wire format is the only contract between producer and consumer. 10 */ 11 12 #include <kit/arch.h> 13 #include <kit/dwarf.h> 14 #include <kit/object.h> 15 16 #include "core/core.h" 17 #include "core/heap.h" 18 #include "debug/dwarf_defs.h" 19 20 /* ---- Section & byte slice helpers ------------------------------------- */ 21 22 typedef struct DwSection { 23 const u8* data; 24 u32 size; 25 u32 sec_idx; /* 0-based section index, or UINT32_MAX if missing */ 26 } DwSection; 27 28 /* ---- Abbrev table ---- */ 29 30 typedef struct DwAbbrevAttr { 31 u32 attr; /* DW_AT_* */ 32 u32 form; /* DW_FORM_* */ 33 i64 implicit_const; /* for DW_FORM_implicit_const */ 34 } DwAbbrevAttr; 35 36 typedef struct DwAbbrev { 37 u64 code; /* abbrev code; 0 if unused slot */ 38 u32 tag; /* DW_TAG_* */ 39 u8 has_children; 40 u32 nattrs; 41 DwAbbrevAttr* attrs; /* heap-allocated */ 42 } DwAbbrev; 43 44 typedef struct DwAbbrevTable { 45 u32 cu_abbrev_offset; /* offset into .debug_abbrev */ 46 /* Dense map: code → index (or 0 if absent). For typical small tables we 47 * keep them in a sorted array searched linearly. */ 48 DwAbbrev* abbrevs; 49 u32 nabbrevs; 50 u32 cap; 51 } DwAbbrevTable; 52 53 /* ---- Compilation unit ---- */ 54 55 typedef struct DwCu { 56 u32 hdr_offset; /* offset of CU header in .debug_info */ 57 u32 hdr_length; /* length of unit_length bytes (after the size field itself) 58 */ 59 u32 unit_total_size; /* hdr_length + length-field size (4 for 32-bit init) */ 60 u32 die_start_off; /* offset where the first DIE starts (in .debug_info) */ 61 u8 version; 62 u8 address_size; 63 u8 unit_type; 64 u8 is_64bit; /* DWARF64? */ 65 u32 abbrev_offset; /* into .debug_abbrev */ 66 u32 str_offsets_base; 67 u32 addr_base; 68 u32 loclists_base; 69 u32 rnglists_base; 70 u32 stmt_list; /* DW_AT_stmt_list value (offset into .debug_line) */ 71 u8 has_stmt_list; 72 const char* comp_dir; 73 const char* name; 74 /* Index of abbrev table in dbg->abbrevs */ 75 u32 abbrev_table_idx; 76 } DwCu; 77 78 /* ---- Materialized DIEs (we cache only what we need) ---- */ 79 80 /* A reference into .debug_info (compilation-unit relative). We store CU 81 * index plus offset-from-CU-header so we can resolve cross-CU later. */ 82 typedef struct DwDieRef { 83 u32 cu_idx; 84 u32 die_offset; /* absolute offset into .debug_info bytes */ 85 } DwDieRef; 86 87 /* ---- Type cache ---- */ 88 89 typedef enum DwTypeKind { 90 DTK_VOID, 91 DTK_BASE, /* maps to SINT/UINT/BOOL/FLOAT/CHAR by encoding */ 92 DTK_PTR, 93 DTK_ARRAY, 94 DTK_STRUCT, 95 DTK_UNION, 96 DTK_ENUM, 97 DTK_TYPEDEF, 98 DTK_FUNC, 99 DTK_CONST, /* alias to inner */ 100 DTK_VOLATILE, 101 DTK_RESTRICT, 102 } DwTypeKind; 103 104 typedef struct DwField { 105 const char* name; 106 u32 byte_offset; 107 u32 bit_offset; 108 u32 bit_size; 109 struct KitDwarfType* type; 110 } DwField; 111 112 typedef struct DwEnumVal { 113 const char* name; 114 i64 value; 115 } DwEnumVal; 116 117 struct KitDwarfType { 118 DwTypeKind kind; 119 u32 byte_size; 120 const char* name; 121 u32 element_count; 122 u32 die_offset; /* origin DIE for cycle-detection / dedup */ 123 /* DT_PTR/ARRAY/TYPEDEF/CONST/VOLATILE/RESTRICT/FUNC: inner type */ 124 struct KitDwarfType* inner; 125 /* Base type encoding (DW_ATE_*) — used to derive SINT/UINT/CHAR/BOOL/FLOAT */ 126 u32 base_encoding; 127 /* STRUCT/UNION fields */ 128 DwField* fields; 129 u32 nfields; 130 /* ENUM values */ 131 DwEnumVal* evals; 132 u32 nevals; 133 }; 134 135 /* ---- Line program decoded matrix ---- */ 136 137 typedef struct DwLineRow { 138 u64 address; 139 u32 file_index; 140 u32 line; 141 u32 column; 142 u8 is_stmt; 143 u8 end_sequence; 144 } DwLineRow; 145 146 typedef struct DwLineFile { 147 const char* path; /* interned in our string table */ 148 u32 dir_index; 149 } DwLineFile; 150 151 typedef struct DwLineProgram { 152 /* Per-CU line program decoding state. We materialize all rows into a 153 * single rows array for fast lookup. */ 154 DwLineRow* rows; 155 u32 nrows; 156 u32 cap; 157 /* File table (file_index 0 is the CU primary in DW5). */ 158 DwLineFile* files; 159 u32 nfiles; 160 const char** dirs; 161 u32 ndirs; 162 /* Cached fully-qualified path per file, lazily built. */ 163 const char** file_norm; 164 u32 nfile_norm; 165 } DwLineProgram; 166 167 /* ---- Subprogram descriptor (cached) ---- */ 168 169 typedef struct DwLocal { 170 const char* name; 171 u32 die_offset; 172 u32 type_die_offset; 173 u64 scope_lo; /* PCs at which the var is in scope. */ 174 u64 scope_hi; /* (low_pc, high_pc) of nearest enclosing block. */ 175 u32 scope_offset; /* offset of the lexical_block DIE; 0 = subprog scope */ 176 u8 has_scope; 177 /* Location form: either an exprloc or a loclistx index. */ 178 const u8* loc; 179 u32 loc_len; 180 u8 has_loclist; 181 u64 loclist_index; 182 /* Role: ARG vs LOCAL. */ 183 u8 is_param; 184 /* For globals only: the global variable role. */ 185 u8 is_global; 186 } DwLocal; 187 188 typedef struct DwSubprog { 189 const char* name; 190 u64 low_pc; 191 u64 high_pc; 192 const char* decl_file; 193 u32 decl_line; 194 u32 cu_idx; 195 u32 die_offset; /* offset of the subprogram DIE */ 196 u32 type_die_offset; 197 /* Frame base — DW_AT_frame_base exprloc bytes (or NULL). */ 198 const u8* frame_base; 199 u32 frame_base_len; 200 /* Cached params and locals (lazily). */ 201 DwLocal* params; 202 u32 nparams; 203 DwLocal* locals; 204 u32 nlocals; 205 u8 inlined; 206 u8 cached_locals; 207 } DwSubprog; 208 209 /* ---- The main consumer state ---- */ 210 211 struct KitDebugInfo { 212 const KitContext* ctx; 213 Heap* h; 214 /* Local string pool for interned strings (file paths, etc). The 215 * consumer used to borrow the compiler's global pool, but the new 216 * kit_dwarf_open takes only a KitContext — no compiler. */ 217 struct Pool* strs; 218 const KitObjFile* obj; 219 220 /* Sections */ 221 DwSection abbrev; 222 DwSection info; 223 DwSection line; 224 DwSection str; 225 DwSection line_str; 226 DwSection str_offsets; 227 DwSection addr; 228 DwSection loclists; 229 DwSection rnglists; 230 DwSection eh_frame; 231 DwSection aranges; 232 233 /* Abbrev tables (one per unique abbrev_offset we've seen). */ 234 DwAbbrevTable* abbrevs; 235 u32 nabbrevs; 236 u32 abbrevs_cap; 237 238 /* CUs */ 239 DwCu* cus; 240 u32 ncus; 241 u32 cus_cap; 242 243 /* Line programs by CU index (parallel to cus). Each lazily built. */ 244 DwLineProgram* lines_by_cu; 245 u8* lines_built; /* parallel; 0 = not yet decoded */ 246 247 /* Subprograms (sorted by low_pc on first build). */ 248 DwSubprog* subs; 249 u32 nsubs; 250 u32 subs_cap; 251 u8 subs_built; 252 253 /* Type cache: DIE-offset → KitDwarfType*. */ 254 KitDwarfType** types_by_off; /* parallel arrays */ 255 u32* types_off; 256 u32 ntypes; 257 u32 types_cap; 258 259 /* Globals (top-level DW_TAG_variable in any CU). */ 260 DwLocal* globals; 261 u32 nglobals; 262 u32 globals_cap; 263 u8 globals_built; 264 }; 265 266 /* ---- API between the dwarf_*.c files ---------------------------------- */ 267 268 /* Section lookup by name. Sets out->data/size; sec_idx = UINT32_MAX if missing. 269 */ 270 void dw_find_section(KitDebugInfo* d, const char* name, DwSection* out); 271 272 /* Read primitives. Each returns the new offset on success and panics on EOF. */ 273 u8 dw_u8(const u8* base, u32 size, u32* off); 274 u16 dw_u16(const u8* base, u32 size, u32* off); 275 u32 dw_u24(const u8* base, u32 size, u32* off); 276 u32 dw_u32(const u8* base, u32 size, u32* off); 277 u64 dw_u64(const u8* base, u32 size, u32* off); 278 u64 dw_uleb(const u8* base, u32 size, u32* off); 279 i64 dw_sleb(const u8* base, u32 size, u32* off); 280 const char* dw_cstr(const u8* base, u32 size, u32* off); 281 282 /* Abbrev parsing: ensure (and return) the abbrev table for `offset`. */ 283 DwAbbrevTable* dw_abbrev_get(KitDebugInfo* d, u32 offset); 284 DwAbbrev* dw_abbrev_lookup(DwAbbrevTable* t, u64 code); 285 286 /* Parse the CU header at offset `off` in .debug_info into `cu`. 287 * Returns the offset of the next CU header. */ 288 u32 dw_cu_parse_header(KitDebugInfo* d, u32 off, DwCu* cu); 289 290 /* Skim every CU and populate dbg->cus. */ 291 void dw_parse_all_cus(KitDebugInfo* d); 292 293 /* Open the .debug_str_offsets table indexed by str_offsets_base. */ 294 const char* dw_str(KitDebugInfo* d, u32 offset); 295 const char* dw_line_str(KitDebugInfo* d, u32 offset); 296 const char* dw_strx(KitDebugInfo* d, const DwCu* cu, u64 idx); 297 298 /* Skip one attribute value of `form` size. *off is updated. */ 299 void dw_skip_form(KitDebugInfo* d, const DwCu* cu, u32 form, i64 implicit_const, 300 u32* off); 301 302 /* Read attribute value into a typed accumulator. Caller picks which getter. */ 303 typedef struct DwAttrValue { 304 u32 form; 305 /* Values for various forms — only one slot is meaningful per form. */ 306 u64 u; /* udata, addr, ref (CU-relative offset for local refs) */ 307 i64 s; /* sdata */ 308 const char* str; /* strp/string/strx/line_strp resolved cstring */ 309 const u8* block; /* exprloc/block bytes */ 310 u32 block_len; 311 } DwAttrValue; 312 313 /* Read attr value at *off using `form`. Updates *off. 314 * 315 * Reads inline form bytes from .debug_info (the DIE stream). The 316 * section-parameterized core dw_read_form_in lets other consumers (the 317 * line-number program in dwarf_line.c) decode the same forms out of a 318 * different section while resolving strp/line_strp/strx into the shared 319 * string sections exactly as the DIE reader does. */ 320 void dw_read_form(KitDebugInfo* d, const DwCu* cu, u32 form, i64 implicit_const, 321 u32* off, DwAttrValue* out); 322 void dw_read_form_in(KitDebugInfo* d, const DwCu* cu, const DwSection* sec, 323 u32 form, i64 implicit_const, u32* off, DwAttrValue* out); 324 325 /* DIE iteration helpers. */ 326 typedef struct DwDie { 327 u64 abbrev_code; 328 DwAbbrev* abbrev; /* NULL if abbrev_code==0 (null entry) */ 329 u32 die_off; /* offset of this DIE itself in .debug_info */ 330 u32 attrs_off; /* where attribute encodings start */ 331 u32 next_sibling_off; /* lazily computed */ 332 } DwDie; 333 334 /* Read one DIE header at *off. Updates *off to point past the abbrev code, 335 * to the start of the attribute area. Returns 1 on success, 0 if this is a 336 * null-entry (terminates a sibling chain). */ 337 int dw_read_die(KitDebugInfo* d, const DwCu* cu, u32* off, DwDie* out); 338 339 /* Skip a DIE's attribute area, advancing *off past it. */ 340 void dw_skip_die_attrs(KitDebugInfo* d, const DwCu* cu, DwDie* die, u32* off); 341 342 /* Skip an entire DIE subtree (including children), starting at attrs_off. 343 * On entry, *off == die->attrs_off. On exit, *off is past the children 344 * terminator (if has_children) or just past the attrs (if no children). */ 345 void dw_skip_die_subtree(KitDebugInfo* d, const DwCu* cu, DwDie* die, u32* off); 346 347 /* Lookup an attribute on `die` by attr code. Returns 1 if found and fills 348 * *out; 0 otherwise. Restartable (rewinds the cursor). */ 349 int dw_die_attr(KitDebugInfo* d, const DwCu* cu, DwDie* die, u32 attr, 350 DwAttrValue* out); 351 352 /* String interning into the compiler's global pool. */ 353 const char* dw_intern(KitDebugInfo* d, const char* s, size_t len); 354 355 /* Inline strcmp/strlen — libkit avoids a runtime libc dep beyond the 356 * tightly-controlled allowlist (test/lib_deps.allowlist). */ 357 static inline int dw_streq(const char* a, const char* b) { 358 if (!a || !b) return 0; 359 while (*a && *b && *a == *b) { 360 a++; 361 b++; 362 } 363 return *a == 0 && *b == 0; 364 } 365 static inline size_t dw_strlen(const char* s) { 366 size_t n = 0; 367 if (!s) return 0; 368 while (s[n]) n++; 369 return n; 370 } 371 372 /* DIE attribute pack — shared between dwarf_die.c and dwarf_type.c. */ 373 typedef struct DieAttrPack { 374 const char* name; 375 u64 low_pc; 376 u64 high_pc_value; 377 u32 high_pc_form; 378 u8 has_low_pc; 379 u8 has_high_pc; 380 u32 type_die_offset; 381 u8 has_type; 382 u32 decl_file; 383 u32 decl_line; 384 const u8* loc_block; 385 u32 loc_block_len; 386 u8 has_loclist; 387 u64 loclist_index; 388 const u8* fb_block; 389 u32 fb_block_len; 390 i64 const_value; 391 u8 has_const_value; 392 u32 byte_offset; 393 u8 has_byte_offset; 394 u32 byte_size; 395 u8 has_byte_size; 396 u32 bit_size; 397 u8 has_bit_size; 398 u32 bit_offset; 399 u8 has_bit_offset; 400 u32 base_encoding; 401 u8 has_encoding; 402 u32 array_count; 403 u8 has_array_count; 404 u8 inlined; 405 } DieAttrPack; 406 407 void dw_die_pack(KitDebugInfo* d, const DwCu* cu, DwDie* die, DieAttrPack* p, 408 u32* off); 409 410 /* Subprograms */ 411 void dw_build_subs(KitDebugInfo* d); 412 DwSubprog* dw_find_subprog(KitDebugInfo* d, u64 pc); 413 void dw_build_locals(KitDebugInfo* d, DwSubprog* sp); 414 415 /* Globals */ 416 void dw_build_globals(KitDebugInfo* d); 417 418 /* Line program */ 419 void dw_build_line(KitDebugInfo* d, u32 cu_idx); 420 421 /* Type DIE → KitDwarfType*. die_offset is absolute offset in .debug_info. */ 422 KitDwarfType* dw_type_from_die(KitDebugInfo* d, u32 cu_idx, u32 die_offset); 423 KitDwarfType* dw_void_type(KitDebugInfo* d); 424 425 /* Loc-expr evaluator. Evaluates `expr` of length `len` in the context of 426 * `frame` (regs, cfa) and `frame_base_expr` (the subprog's DW_AT_frame_base 427 * expression — typically just DW_OP_call_frame_cfa). Returns 0 on success; 428 * fills *result with the location kind plus value. */ 429 typedef struct DwExprResult { 430 /* result_kind: 0 = address (memory), 1 = value-on-stack (DW_OP_stack_value), 431 * 2 = register, 3 = unsupported. */ 432 int kind; 433 u64 value; /* address if kind=0; literal if kind=1; reg# if kind=2 */ 434 } DwExprResult; 435 436 int dw_eval_expr(KitDebugInfo* d, const u8* expr, u32 len, const u8* fb_expr, 437 u32 fb_len, const KitUnwindFrame* frame, DwExprResult* out); 438 439 /* CU lookup helpers. */ 440 DwCu* dw_cu_at_die_offset(KitDebugInfo* d, u32 die_offset); 441 442 /* Resolve a DW_FORM_loclistx into the matching location list entry for 443 * `pc`. Returns 1 and fills bytes/len on success; 0 if the section is 444 * absent, the index is bad, or no entry covers `pc`. */ 445 int dw_loclist_resolve(KitDebugInfo* d, const DwCu* cu, u64 idx, u64 pc, 446 const u8** bytes, u32* len); 447 448 #endif