dwarf_test.c (40053B)
1 /* test/dwarf/dwarf_test.c — round-trip tests for the DWARF consumer. 2 * 3 * Builds an in-memory ELF object containing hand-crafted .debug_* 4 * sections, then re-opens it with kit_obj_open + kit_dwarf_open 5 * and exercises the public consumer surface: 6 * 7 * - kit_dwarf_open finds the mandatory five sections 8 * - kit_dwarf_addr_to_line / line_to_addr round-trip 9 * - kit_dwarf_subprogram_at returns a non-empty range with a name 10 * - kit_dwarf_var_at + kit_dwarf_loc_read fast-path 11 * - kit_dwarf_type_info / field_iter / enum_iter 12 * 13 * Only depends on libkit.a (the public surface) plus libc. 14 */ 15 16 #include <kit/core.h> 17 #include <kit/dwarf.h> 18 #include <kit/object.h> 19 #include <stdarg.h> 20 #include <stdint.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 25 #include "lib/kit_unit.h" 26 27 /* This test reaches into the internal obj/ surface to construct a 28 * DWARF-bearing ELF without going through the parser/codegen path. 29 * That's deliberate: we want to test the *consumer* in isolation against 30 * known-good hand-crafted DWARF byte streams. */ 31 #include "core/core.h" 32 #include "core/pool.h" 33 #include "obj/obj.h" 34 35 /* Shared test context replaces the per-file heap/diag/counter globals; 36 * EXPECT aliases CU_EXPECT so the call sites are unchanged. */ 37 static KitUnit g_u; 38 #define EXPECT(cond, ...) CU_EXPECT(&g_u, cond, __VA_ARGS__) 39 40 /* ---- byte builders -------------------------------------------------- */ 41 42 typedef struct ByteBuf { 43 uint8_t* data; 44 size_t len; 45 size_t cap; 46 } ByteBuf; 47 48 static void bb_init(ByteBuf* b) { 49 b->data = NULL; 50 b->len = 0; 51 b->cap = 0; 52 } 53 static void bb_put(ByteBuf* b, const void* src, size_t n) { 54 if (b->len + n > b->cap) { 55 size_t nc = b->cap ? b->cap * 2 : 64; 56 while (nc < b->len + n) nc *= 2; 57 b->data = (uint8_t*)realloc(b->data, nc); 58 b->cap = nc; 59 } 60 memcpy(b->data + b->len, src, n); 61 b->len += n; 62 } 63 static void bb_u8(ByteBuf* b, uint8_t v) { bb_put(b, &v, 1); } 64 static void bb_u16(ByteBuf* b, uint16_t v) { 65 uint8_t buf[2] = {(uint8_t)v, (uint8_t)(v >> 8)}; 66 bb_put(b, buf, 2); 67 } 68 static void bb_u32(ByteBuf* b, uint32_t v) { 69 uint8_t buf[4] = {(uint8_t)v, (uint8_t)(v >> 8), (uint8_t)(v >> 16), 70 (uint8_t)(v >> 24)}; 71 bb_put(b, buf, 4); 72 } 73 static void bb_u64(ByteBuf* b, uint64_t v) { 74 uint8_t buf[8] = {(uint8_t)v, (uint8_t)(v >> 8), (uint8_t)(v >> 16), 75 (uint8_t)(v >> 24), (uint8_t)(v >> 32), (uint8_t)(v >> 40), 76 (uint8_t)(v >> 48), (uint8_t)(v >> 56)}; 77 bb_put(b, buf, 8); 78 } 79 static void bb_uleb(ByteBuf* b, uint64_t v) { 80 for (;;) { 81 uint8_t byte = v & 0x7f; 82 v >>= 7; 83 if (v) byte |= 0x80; 84 bb_u8(b, byte); 85 if (!v) break; 86 } 87 } 88 static void bb_sleb(ByteBuf* b, int64_t v) { 89 int more = 1; 90 while (more) { 91 uint8_t byte = v & 0x7f; 92 v >>= 7; 93 if ((v == 0 && !(byte & 0x40)) || (v == -1 && (byte & 0x40))) 94 more = 0; 95 else 96 byte |= 0x80; 97 bb_u8(b, byte); 98 } 99 } 100 static void bb_str(ByteBuf* b, const char* s) { bb_put(b, s, strlen(s) + 1); } 101 102 /* ---- DWARF constants (subset) --------------------------------------- */ 103 #define DW_TAG_compile_unit 0x11 104 #define DW_TAG_subprogram 0x2e 105 #define DW_TAG_base_type 0x24 106 #define DW_TAG_pointer_type 0x0f 107 #define DW_TAG_typedef 0x16 108 #define DW_TAG_array_type 0x01 109 #define DW_TAG_subrange_type 0x21 110 #define DW_TAG_structure_type 0x13 111 #define DW_TAG_member 0x0d 112 #define DW_TAG_enumeration_type 0x04 113 #define DW_TAG_enumerator 0x28 114 #define DW_TAG_variable 0x34 115 #define DW_TAG_formal_parameter 0x05 116 117 #define DW_AT_name 0x03 118 #define DW_AT_stmt_list 0x10 119 #define DW_AT_low_pc 0x11 120 #define DW_AT_high_pc 0x12 121 #define DW_AT_language 0x13 122 #define DW_AT_comp_dir 0x1b 123 #define DW_AT_const_value 0x1c 124 #define DW_AT_byte_size 0x0b 125 #define DW_AT_encoding 0x3e 126 #define DW_AT_type 0x49 127 #define DW_AT_data_member_location 0x38 128 #define DW_AT_count 0x37 129 #define DW_AT_location 0x02 130 #define DW_AT_frame_base 0x40 131 #define DW_AT_decl_file 0x3a 132 #define DW_AT_decl_line 0x3b 133 #define DW_AT_str_offsets_base 0x72 134 135 #define DW_FORM_addr 0x01 136 #define DW_FORM_data1 0x0b 137 #define DW_FORM_data2 0x05 138 #define DW_FORM_data4 0x06 139 #define DW_FORM_data8 0x07 140 #define DW_FORM_strx1 0x26 141 #define DW_FORM_strp 0x0e 142 #define DW_FORM_line_strp 0x1f 143 #define DW_FORM_sec_offset 0x17 144 #define DW_FORM_udata 0x0f 145 #define DW_FORM_flag_present 0x19 146 #define DW_FORM_ref4 0x13 147 #define DW_FORM_exprloc 0x18 148 #define DW_FORM_string 0x08 149 150 #define DW_LNCT_path 0x01 151 #define DW_LNCT_directory_index 0x02 152 153 #define DW_ATE_signed 0x05 154 #define DW_ATE_unsigned 0x07 155 156 #define DW_OP_reg0 0x50 157 #define DW_OP_fbreg 0x91 158 #define DW_OP_call_frame_cfa 0x9c 159 160 #define DW_LNS_copy 0x01 161 #define DW_LNS_advance_pc 0x02 162 #define DW_LNS_advance_line 0x03 163 #define DW_LNE_end_sequence 0x01 164 #define DW_LNE_set_address 0x02 165 166 #define DW_LANG_C11 0x1d 167 168 #define DW_CHILDREN_no 0 169 #define DW_CHILDREN_yes 1 170 171 /* ---- build the .debug_* sections ------------------------------------ */ 172 173 /* Plan: 174 * .debug_line_str: paths. 175 * .debug_str: cu name, subprog name, type names, var names. 176 * .debug_abbrev: 5 abbrevs: 177 * 1: compile_unit (children) — name(strp), comp_dir(strp), language(udata), 178 * stmt_list(sec_offset), low_pc(addr), high_pc(data8) 179 * 2: subprogram (children) — name(strp), low_pc(addr), high_pc(data8), 180 * frame_base(exprloc), decl_file(udata), decl_line(udata) 181 * 3: base_type (no children) — name(strp), byte_size(data1), 182 * encoding(data1) 4: variable (no children) — name(strp), type(ref4), 183 * location(exprloc) 5: formal_parameter (no children) — same shape as variable 184 * 185 * .debug_info: one CU; one subprogram with two locals + one param + 186 * one base type child. 187 * .debug_line: header for one file plus a small program emitting two rows: 188 * (file=0, line=10, addr=0x100), (file=0, line=11, addr=0x104). 189 */ 190 191 typedef struct DieOffsets { 192 uint32_t int_off; 193 uint32_t ptr_off; 194 uint32_t typedef_off; 195 uint32_t array_off; 196 uint32_t struct_off; 197 } DieOffsets; 198 199 static void build_debug_sections(ByteBuf* abbrev, ByteBuf* info, ByteBuf* line, 200 ByteBuf* str, ByteBuf* line_str, 201 uint64_t func_low, uint64_t func_size, 202 DieOffsets* off_out) { 203 /* str pool: collect offsets first by appending. */ 204 size_t s_cu_name = str->len; 205 bb_str(str, "test.c"); 206 size_t s_cu_dir = str->len; 207 bb_str(str, "/proj"); 208 size_t s_func = str->len; 209 bb_str(str, "test_main"); 210 size_t s_int = str->len; 211 bb_str(str, "int"); 212 size_t s_x = str->len; 213 bb_str(str, "x"); 214 size_t s_y = str->len; 215 bb_str(str, "y"); 216 size_t s_arg = str->len; 217 bb_str(str, "arg"); 218 size_t s_my_int = str->len; 219 bb_str(str, "my_int"); 220 size_t s_pt = str->len; 221 bb_str(str, "Point"); 222 size_t s_x_field = str->len; 223 bb_str(str, "x_field"); 224 size_t s_y_field = str->len; 225 bb_str(str, "y_field"); 226 227 /* line_str: dir, file. */ 228 size_t ls_dir = line_str->len; 229 bb_str(line_str, "/proj"); 230 size_t ls_file = line_str->len; 231 bb_str(line_str, "test.c"); 232 233 /* abbrev table */ 234 /* Abbrev 1: compile_unit, has children. */ 235 bb_uleb(abbrev, 1); 236 bb_uleb(abbrev, DW_TAG_compile_unit); 237 bb_u8(abbrev, DW_CHILDREN_yes); 238 bb_uleb(abbrev, DW_AT_name); 239 bb_uleb(abbrev, DW_FORM_strp); 240 bb_uleb(abbrev, DW_AT_comp_dir); 241 bb_uleb(abbrev, DW_FORM_strp); 242 bb_uleb(abbrev, DW_AT_language); 243 bb_uleb(abbrev, DW_FORM_udata); 244 bb_uleb(abbrev, DW_AT_stmt_list); 245 bb_uleb(abbrev, DW_FORM_sec_offset); 246 bb_uleb(abbrev, DW_AT_low_pc); 247 bb_uleb(abbrev, DW_FORM_addr); 248 bb_uleb(abbrev, DW_AT_high_pc); 249 bb_uleb(abbrev, DW_FORM_data8); 250 bb_uleb(abbrev, 0); 251 bb_uleb(abbrev, 0); 252 /* Abbrev 2: subprogram, has children. */ 253 bb_uleb(abbrev, 2); 254 bb_uleb(abbrev, DW_TAG_subprogram); 255 bb_u8(abbrev, DW_CHILDREN_yes); 256 bb_uleb(abbrev, DW_AT_name); 257 bb_uleb(abbrev, DW_FORM_strp); 258 bb_uleb(abbrev, DW_AT_low_pc); 259 bb_uleb(abbrev, DW_FORM_addr); 260 bb_uleb(abbrev, DW_AT_high_pc); 261 bb_uleb(abbrev, DW_FORM_data8); 262 bb_uleb(abbrev, DW_AT_frame_base); 263 bb_uleb(abbrev, DW_FORM_exprloc); 264 bb_uleb(abbrev, DW_AT_decl_file); 265 bb_uleb(abbrev, DW_FORM_udata); 266 bb_uleb(abbrev, DW_AT_decl_line); 267 bb_uleb(abbrev, DW_FORM_udata); 268 bb_uleb(abbrev, 0); 269 bb_uleb(abbrev, 0); 270 /* Abbrev 3: base_type, no children. */ 271 bb_uleb(abbrev, 3); 272 bb_uleb(abbrev, DW_TAG_base_type); 273 bb_u8(abbrev, DW_CHILDREN_no); 274 bb_uleb(abbrev, DW_AT_name); 275 bb_uleb(abbrev, DW_FORM_strp); 276 bb_uleb(abbrev, DW_AT_byte_size); 277 bb_uleb(abbrev, DW_FORM_data1); 278 bb_uleb(abbrev, DW_AT_encoding); 279 bb_uleb(abbrev, DW_FORM_data1); 280 bb_uleb(abbrev, 0); 281 bb_uleb(abbrev, 0); 282 /* Abbrev 4: variable, no children. */ 283 bb_uleb(abbrev, 4); 284 bb_uleb(abbrev, DW_TAG_variable); 285 bb_u8(abbrev, DW_CHILDREN_no); 286 bb_uleb(abbrev, DW_AT_name); 287 bb_uleb(abbrev, DW_FORM_strp); 288 bb_uleb(abbrev, DW_AT_type); 289 bb_uleb(abbrev, DW_FORM_ref4); 290 bb_uleb(abbrev, DW_AT_location); 291 bb_uleb(abbrev, DW_FORM_exprloc); 292 bb_uleb(abbrev, 0); 293 bb_uleb(abbrev, 0); 294 /* Abbrev 5: formal_parameter, no children. */ 295 bb_uleb(abbrev, 5); 296 bb_uleb(abbrev, DW_TAG_formal_parameter); 297 bb_u8(abbrev, DW_CHILDREN_no); 298 bb_uleb(abbrev, DW_AT_name); 299 bb_uleb(abbrev, DW_FORM_strp); 300 bb_uleb(abbrev, DW_AT_type); 301 bb_uleb(abbrev, DW_FORM_ref4); 302 bb_uleb(abbrev, DW_AT_location); 303 bb_uleb(abbrev, DW_FORM_exprloc); 304 bb_uleb(abbrev, 0); 305 bb_uleb(abbrev, 0); 306 /* Abbrev 6: pointer_type, no children — byte_size, type. */ 307 bb_uleb(abbrev, 6); 308 bb_uleb(abbrev, DW_TAG_pointer_type); 309 bb_u8(abbrev, DW_CHILDREN_no); 310 bb_uleb(abbrev, DW_AT_byte_size); 311 bb_uleb(abbrev, DW_FORM_data1); 312 bb_uleb(abbrev, DW_AT_type); 313 bb_uleb(abbrev, DW_FORM_ref4); 314 bb_uleb(abbrev, 0); 315 bb_uleb(abbrev, 0); 316 /* Abbrev 7: typedef, no children — name, type. */ 317 bb_uleb(abbrev, 7); 318 bb_uleb(abbrev, DW_TAG_typedef); 319 bb_u8(abbrev, DW_CHILDREN_no); 320 bb_uleb(abbrev, DW_AT_name); 321 bb_uleb(abbrev, DW_FORM_strp); 322 bb_uleb(abbrev, DW_AT_type); 323 bb_uleb(abbrev, DW_FORM_ref4); 324 bb_uleb(abbrev, 0); 325 bb_uleb(abbrev, 0); 326 /* Abbrev 8: array_type, has children — type. */ 327 bb_uleb(abbrev, 8); 328 bb_uleb(abbrev, DW_TAG_array_type); 329 bb_u8(abbrev, DW_CHILDREN_yes); 330 bb_uleb(abbrev, DW_AT_type); 331 bb_uleb(abbrev, DW_FORM_ref4); 332 bb_uleb(abbrev, 0); 333 bb_uleb(abbrev, 0); 334 /* Abbrev 9: subrange_type, no children — count. */ 335 bb_uleb(abbrev, 9); 336 bb_uleb(abbrev, DW_TAG_subrange_type); 337 bb_u8(abbrev, DW_CHILDREN_no); 338 bb_uleb(abbrev, DW_AT_count); 339 bb_uleb(abbrev, DW_FORM_data1); 340 bb_uleb(abbrev, 0); 341 bb_uleb(abbrev, 0); 342 /* Abbrev 10: structure_type, has children — name, byte_size. */ 343 bb_uleb(abbrev, 10); 344 bb_uleb(abbrev, DW_TAG_structure_type); 345 bb_u8(abbrev, DW_CHILDREN_yes); 346 bb_uleb(abbrev, DW_AT_name); 347 bb_uleb(abbrev, DW_FORM_strp); 348 bb_uleb(abbrev, DW_AT_byte_size); 349 bb_uleb(abbrev, DW_FORM_data1); 350 bb_uleb(abbrev, 0); 351 bb_uleb(abbrev, 0); 352 /* Abbrev 11: member, no children — name, type, data_member_location(udata). 353 */ 354 bb_uleb(abbrev, 11); 355 bb_uleb(abbrev, DW_TAG_member); 356 bb_u8(abbrev, DW_CHILDREN_no); 357 bb_uleb(abbrev, DW_AT_name); 358 bb_uleb(abbrev, DW_FORM_strp); 359 bb_uleb(abbrev, DW_AT_type); 360 bb_uleb(abbrev, DW_FORM_ref4); 361 bb_uleb(abbrev, DW_AT_data_member_location); 362 bb_uleb(abbrev, DW_FORM_udata); 363 bb_uleb(abbrev, 0); 364 bb_uleb(abbrev, 0); 365 /* End-of-table */ 366 bb_uleb(abbrev, 0); 367 368 /* .debug_info CU header (32-bit DWARF, version 5) */ 369 /* unit_length placeholder */ 370 size_t cu_len_pos = info->len; 371 bb_u32(info, 0); /* unit_length */ 372 size_t cu_body_start = info->len; 373 bb_u16(info, 5); /* version */ 374 bb_u8(info, 0x01); /* unit_type = DW_UT_compile */ 375 bb_u8(info, 8); /* address_size */ 376 bb_u32(info, 0); /* debug_abbrev_offset */ 377 /* CU root DIE — abbrev 1 */ 378 size_t cu_die_off = info->len; 379 bb_uleb(info, 1); /* abbrev code */ 380 bb_u32(info, (uint32_t)s_cu_name); 381 bb_u32(info, (uint32_t)s_cu_dir); 382 bb_uleb(info, DW_LANG_C11); 383 bb_u32(info, 0); /* stmt_list -> .debug_line offset 0 */ 384 bb_u64(info, func_low); /* low_pc */ 385 bb_u64(info, func_size); /* high_pc (offset) */ 386 387 /* Children: int (base_type), then sibling type DIEs, then subprogram. */ 388 size_t int_die_off = info->len; 389 bb_uleb(info, 3); /* base_type abbrev */ 390 bb_u32(info, (uint32_t)s_int); 391 bb_u8(info, 4); /* byte_size */ 392 bb_u8(info, DW_ATE_signed); 393 394 /* pointer_type → int (8-byte pointer). */ 395 size_t ptr_die_off = info->len; 396 bb_uleb(info, 6); 397 bb_u8(info, 8); /* byte_size */ 398 bb_u32(info, (uint32_t)(int_die_off - cu_len_pos)); 399 400 /* typedef my_int → int. */ 401 size_t td_die_off = info->len; 402 bb_uleb(info, 7); 403 bb_u32(info, (uint32_t)s_my_int); 404 bb_u32(info, (uint32_t)(int_die_off - cu_len_pos)); 405 406 /* array_type → int [4]. */ 407 size_t arr_die_off = info->len; 408 bb_uleb(info, 8); 409 bb_u32(info, (uint32_t)(int_die_off - cu_len_pos)); 410 /* subrange child: count=4 */ 411 bb_uleb(info, 9); 412 bb_u8(info, 4); 413 /* end-of-children for array */ 414 bb_uleb(info, 0); 415 416 /* struct Point { int x_field; int y_field; }, byte_size=8. */ 417 size_t st_die_off = info->len; 418 bb_uleb(info, 10); 419 bb_u32(info, (uint32_t)s_pt); 420 bb_u8(info, 8); 421 /* member x_field */ 422 bb_uleb(info, 11); 423 bb_u32(info, (uint32_t)s_x_field); 424 bb_u32(info, (uint32_t)(int_die_off - cu_len_pos)); 425 bb_uleb(info, 0); 426 /* member y_field */ 427 bb_uleb(info, 11); 428 bb_u32(info, (uint32_t)s_y_field); 429 bb_u32(info, (uint32_t)(int_die_off - cu_len_pos)); 430 bb_uleb(info, 4); 431 /* end-of-children for struct */ 432 bb_uleb(info, 0); 433 434 if (off_out) { 435 off_out->int_off = (uint32_t)int_die_off; 436 off_out->ptr_off = (uint32_t)ptr_die_off; 437 off_out->typedef_off = (uint32_t)td_die_off; 438 off_out->array_off = (uint32_t)arr_die_off; 439 off_out->struct_off = (uint32_t)st_die_off; 440 } 441 442 /* subprogram */ 443 size_t sub_die_off = info->len; 444 bb_uleb(info, 2); /* subprogram abbrev */ 445 bb_u32(info, (uint32_t)s_func); 446 bb_u64(info, func_low); 447 bb_u64(info, func_size); 448 bb_uleb(info, 1); /* frame_base exprloc len */ 449 bb_u8(info, DW_OP_call_frame_cfa); 450 bb_uleb(info, 1); /* decl_file = 1 (the cu primary) */ 451 bb_uleb(info, 9); /* decl_line */ 452 453 /* Children: x (variable, fbreg -16), y (variable, fbreg -8), 454 * arg (formal_parameter, reg0). */ 455 bb_uleb(info, 4); /* var abbrev */ 456 bb_u32(info, (uint32_t)s_x); 457 /* type ref: CU-relative offset of int_die_off. */ 458 bb_u32(info, (uint32_t)(int_die_off - cu_body_start + 4)); 459 /* Wait — ref4 is CU-relative, offset starting from CU header start. */ 460 /* CU header starts at cu_len_pos. The CU offset reference base is 461 * cu_len_pos (since DWARF 5 ref* are relative to the start of the CU 462 * header). */ 463 /* Re-patch: the previous bb_u32 wrote a wrong value. Patch in place. */ 464 { 465 uint32_t want = (uint32_t)(int_die_off - cu_len_pos); 466 info->data[info->len - 4] = (uint8_t)want; 467 info->data[info->len - 3] = (uint8_t)(want >> 8); 468 info->data[info->len - 2] = (uint8_t)(want >> 16); 469 info->data[info->len - 1] = (uint8_t)(want >> 24); 470 } 471 /* location: DW_OP_fbreg -16 */ 472 { 473 ByteBuf e; 474 bb_init(&e); 475 bb_u8(&e, DW_OP_fbreg); 476 bb_sleb(&e, -16); 477 bb_uleb(info, e.len); 478 bb_put(info, e.data, e.len); 479 free(e.data); 480 } 481 482 /* y */ 483 bb_uleb(info, 4); 484 bb_u32(info, (uint32_t)(int_die_off - cu_len_pos)); 485 bb_u32(info, (uint32_t)s_y); 486 /* The two writes above are out of order — fix: name first, then type. */ 487 /* Actually our abbrev was: name(strp), type(ref4), location(exprloc). 488 * So we should write: name strp, then type ref4. Let's revert. */ 489 { 490 /* Undo: we wrote 8 bytes for u32(type) then u32(s_y), but in the 491 * wrong order. Rewind by 8 bytes and redo. */ 492 info->len -= 8; 493 bb_u32(info, (uint32_t)s_y); 494 bb_u32(info, (uint32_t)(int_die_off - cu_len_pos)); 495 } 496 { 497 ByteBuf e; 498 bb_init(&e); 499 bb_u8(&e, DW_OP_fbreg); 500 bb_sleb(&e, -8); 501 bb_uleb(info, e.len); 502 bb_put(info, e.data, e.len); 503 free(e.data); 504 } 505 506 /* arg formal_parameter */ 507 bb_uleb(info, 5); 508 bb_u32(info, (uint32_t)s_arg); 509 bb_u32(info, (uint32_t)(int_die_off - cu_len_pos)); 510 { 511 ByteBuf e; 512 bb_init(&e); 513 bb_u8(&e, DW_OP_reg0); 514 bb_uleb(info, e.len); 515 bb_put(info, e.data, e.len); 516 free(e.data); 517 } 518 519 /* Locals for the extra type DIEs — give each a distinct frame offset. 520 * Names are reused: we re-use the "x"/"y" string slots to keep the 521 * existing test cases stable, but bind via the local fbreg position. */ 522 /* p (pointer to int) at fbreg -24. We re-purpose s_my_int's name. */ 523 bb_uleb(info, 4); 524 bb_u32(info, (uint32_t)s_my_int); /* name "my_int" — used as local var name */ 525 bb_u32(info, (uint32_t)(ptr_die_off - cu_len_pos)); 526 { 527 ByteBuf e; 528 bb_init(&e); 529 bb_u8(&e, DW_OP_fbreg); 530 bb_sleb(&e, -24); 531 bb_uleb(info, e.len); 532 bb_put(info, e.data, e.len); 533 free(e.data); 534 } 535 /* td (typedef alias) at fbreg -32 — name uses s_pt ("Point"). */ 536 bb_uleb(info, 4); 537 bb_u32(info, (uint32_t)s_pt); 538 bb_u32(info, (uint32_t)(td_die_off - cu_len_pos)); 539 { 540 ByteBuf e; 541 bb_init(&e); 542 bb_u8(&e, DW_OP_fbreg); 543 bb_sleb(&e, -32); 544 bb_uleb(info, e.len); 545 bb_put(info, e.data, e.len); 546 free(e.data); 547 } 548 /* arr (array of int) at fbreg -64 — name uses s_x_field ("x_field"). */ 549 bb_uleb(info, 4); 550 bb_u32(info, (uint32_t)s_x_field); 551 bb_u32(info, (uint32_t)(arr_die_off - cu_len_pos)); 552 { 553 ByteBuf e; 554 bb_init(&e); 555 bb_u8(&e, DW_OP_fbreg); 556 bb_sleb(&e, -64); 557 bb_uleb(info, e.len); 558 bb_put(info, e.data, e.len); 559 free(e.data); 560 } 561 /* st (struct Point) at fbreg -72 — name uses s_y_field ("y_field"). */ 562 bb_uleb(info, 4); 563 bb_u32(info, (uint32_t)s_y_field); 564 bb_u32(info, (uint32_t)(st_die_off - cu_len_pos)); 565 { 566 ByteBuf e; 567 bb_init(&e); 568 bb_u8(&e, DW_OP_fbreg); 569 bb_sleb(&e, -72); 570 bb_uleb(info, e.len); 571 bb_put(info, e.data, e.len); 572 free(e.data); 573 } 574 575 /* end-of-children for subprogram */ 576 bb_uleb(info, 0); 577 /* end-of-children for compile_unit */ 578 bb_uleb(info, 0); 579 580 /* Patch CU unit_length */ 581 { 582 uint32_t total = (uint32_t)(info->len - cu_body_start); 583 info->data[cu_len_pos + 0] = (uint8_t)total; 584 info->data[cu_len_pos + 1] = (uint8_t)(total >> 8); 585 info->data[cu_len_pos + 2] = (uint8_t)(total >> 16); 586 info->data[cu_len_pos + 3] = (uint8_t)(total >> 24); 587 } 588 (void)cu_die_off; 589 (void)sub_die_off; 590 591 /* .debug_line header (DWARF 5) */ 592 size_t line_len_pos = line->len; 593 bb_u32(line, 0); /* unit_length */ 594 size_t line_body_start = line->len; 595 bb_u16(line, 5); /* version */ 596 bb_u8(line, 8); /* address_size */ 597 bb_u8(line, 0); /* segment_selector_size */ 598 size_t hdr_len_pos = line->len; 599 bb_u32(line, 0); /* header_length */ 600 size_t header_len_start = line->len; 601 bb_u8(line, 4); /* min_inst_len */ 602 bb_u8(line, 1); /* max_ops_per_inst */ 603 bb_u8(line, 1); /* default_is_stmt */ 604 bb_u8(line, (uint8_t)(int8_t)-5); /* line_base */ 605 bb_u8(line, 14); /* line_range */ 606 bb_u8(line, 13); /* opcode_base */ 607 /* standard_opcode_lengths: 12 entries (opcode_base - 1) */ 608 uint8_t op_lens[] = {0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1}; 609 bb_put(line, op_lens, sizeof(op_lens)); 610 /* directory_entry_format: 1 pair (DW_LNCT_path, DW_FORM_line_strp) */ 611 bb_u8(line, 1); 612 bb_uleb(line, DW_LNCT_path); 613 bb_uleb(line, DW_FORM_line_strp); 614 /* directories_count = 1 */ 615 bb_uleb(line, 1); 616 bb_u32(line, (uint32_t)ls_dir); 617 /* file_name_entry_format: 2 pairs (path, dir_index) */ 618 bb_u8(line, 2); 619 bb_uleb(line, DW_LNCT_path); 620 bb_uleb(line, DW_FORM_line_strp); 621 bb_uleb(line, DW_LNCT_directory_index); 622 bb_uleb(line, DW_FORM_udata); 623 /* file_names_count = 1 */ 624 bb_uleb(line, 1); 625 bb_u32(line, (uint32_t)ls_file); 626 bb_uleb(line, 0); /* dir_index = 0 (the only dir) */ 627 /* Patch header_length = bytes from after header_length field to start 628 * of program. Program starts now. */ 629 { 630 uint32_t hl = (uint32_t)(line->len - header_len_start); 631 line->data[hdr_len_pos + 0] = (uint8_t)hl; 632 line->data[hdr_len_pos + 1] = (uint8_t)(hl >> 8); 633 line->data[hdr_len_pos + 2] = (uint8_t)(hl >> 16); 634 line->data[hdr_len_pos + 3] = (uint8_t)(hl >> 24); 635 } 636 /* Program */ 637 /* DW_LNE_set_address func_low */ 638 bb_u8(line, 0); 639 bb_uleb(line, 9); /* length: opcode + 8 addr bytes */ 640 bb_u8(line, DW_LNE_set_address); 641 bb_u64(line, func_low); 642 /* DW_LNS_set_file 0 — DW5 file 0 is the CU primary (we only have one 643 * file in the table, indexed at 0). */ 644 bb_u8(line, 4 /* DW_LNS_set_file */); 645 bb_uleb(line, 0); 646 /* DW_LNS_advance_line +9 (default line is 1 → 10) */ 647 bb_u8(line, DW_LNS_advance_line); 648 bb_sleb(line, 9); 649 /* DW_LNS_copy → row at (file=0/1?, line=10, addr=func_low). DW5 file 0 650 * is the CU primary; default file = 1 in standard, but DW5 line program 651 * starts with file=1. We'll match either since file_norm[1] equals 652 * file_norm[0] in our setup if we have one file. With nfiles_count=1, 653 * file 1 maps to the 0th entry. */ 654 bb_u8(line, DW_LNS_copy); 655 /* DW_LNS_advance_pc 1 (* min_inst_len 4 = 4 bytes) */ 656 bb_u8(line, DW_LNS_advance_pc); 657 bb_uleb(line, 1); 658 /* advance_line +1 → line 11 */ 659 bb_u8(line, DW_LNS_advance_line); 660 bb_sleb(line, 1); 661 bb_u8(line, DW_LNS_copy); 662 /* end_sequence */ 663 bb_u8(line, 0); 664 bb_uleb(line, 1); 665 bb_u8(line, DW_LNE_end_sequence); 666 /* Patch unit_length */ 667 { 668 uint32_t total = (uint32_t)(line->len - line_body_start); 669 line->data[line_len_pos + 0] = (uint8_t)total; 670 line->data[line_len_pos + 1] = (uint8_t)(total >> 8); 671 line->data[line_len_pos + 2] = (uint8_t)(total >> 16); 672 line->data[line_len_pos + 3] = (uint8_t)(total >> 24); 673 } 674 } 675 676 /* ---- structural enumeration (objdump --dwarf) ----------------------- */ 677 678 static void run_dump_tests(KitDebugInfo* di) { 679 /* CU iterator: the fixture has exactly one DWARF5 CU at offset 0. */ 680 uint32_t root_off = 0; 681 int have_root = 0; 682 { 683 KitDwarfCuIter* it = NULL; 684 KitDwarfCu cu; 685 int ncu = 0; 686 EXPECT(kit_dwarf_cu_iter_new(di, &it) == KIT_OK, "cu_iter_new failed"); 687 while (kit_dwarf_cu_iter_next(it, &cu) == KIT_ITER_ITEM) { 688 if (ncu == 0) { 689 EXPECT(cu.offset == 0, "cu offset expected 0, got 0x%x", cu.offset); 690 EXPECT(cu.version == 5, "cu version expected 5, got %u", 691 (unsigned)cu.version); 692 EXPECT(cu.address_size == 8, "cu addr_size expected 8, got %u", 693 (unsigned)cu.address_size); 694 } 695 ncu++; 696 } 697 EXPECT(ncu == 1, "expected 1 CU, got %d", ncu); 698 kit_dwarf_cu_iter_free(it); 699 } 700 701 /* DIE iterator: first DIE is the CU root (depth 0, compile_unit). We 702 * should also encounter a subprogram and a base_type along the way. */ 703 { 704 KitDwarfDieIter* it = NULL; 705 KitDwarfDie die; 706 int ndie = 0, saw_subprog = 0, saw_base = 0; 707 EXPECT(kit_dwarf_die_iter_new(di, &it) == KIT_OK, "die_iter_new failed"); 708 while (kit_dwarf_die_iter_next(it, &die) == KIT_ITER_ITEM) { 709 if (ndie == 0) { 710 EXPECT(die.depth == 0, "root die depth expected 0, got %u", die.depth); 711 EXPECT(die.tag == DW_TAG_compile_unit, 712 "root die tag expected compile_unit (0x11), got 0x%x", die.tag); 713 root_off = die.offset; 714 have_root = 1; 715 } 716 if (die.tag == DW_TAG_subprogram) saw_subprog = 1; 717 if (die.tag == DW_TAG_base_type) saw_base = 1; 718 ndie++; 719 } 720 EXPECT(ndie >= 4, "expected several DIEs, got %d", ndie); 721 EXPECT(saw_subprog, "expected a DW_TAG_subprogram DIE"); 722 EXPECT(saw_base, "expected a DW_TAG_base_type DIE"); 723 kit_dwarf_die_iter_free(it); 724 } 725 726 /* Attribute iterator on the CU root: the fixture's compile_unit carries 727 * DW_AT_low_pc == 0x1000 and a string-valued DW_AT_name. */ 728 if (have_root) { 729 KitDwarfAttrIter* it = NULL; 730 KitDwarfAttr a; 731 int saw_low_pc = 0, saw_string = 0; 732 EXPECT(kit_dwarf_attr_iter_new(di, root_off, &it) == KIT_OK, 733 "attr_iter_new failed"); 734 while (kit_dwarf_attr_iter_next(it, &a) == KIT_ITER_ITEM) { 735 if (a.attr == DW_AT_low_pc) { 736 saw_low_pc = 1; 737 EXPECT(a.u == 0x1000, "root low_pc expected 0x1000, got 0x%llx", 738 (unsigned long long)a.u); 739 } 740 if (a.form_class == KIT_DWARF_FC_STRING && a.str.len) saw_string = 1; 741 } 742 EXPECT(saw_low_pc, "root DIE missing DW_AT_low_pc"); 743 EXPECT(saw_string, "root DIE missing a string-valued attribute"); 744 kit_dwarf_attr_iter_free(it); 745 } 746 747 /* Abbrev iterator + per-abbrev attribute specs. */ 748 { 749 KitDwarfAbbrevIter* it = NULL; 750 KitDwarfAbbrev ab; 751 int nabbrev = 0, saw_cu_abbrev = 0; 752 uint32_t cu_code_tbl = 0; 753 uint64_t cu_code = 0; 754 EXPECT(kit_dwarf_abbrev_iter_new(di, &it) == KIT_OK, 755 "abbrev_iter_new failed"); 756 while (kit_dwarf_abbrev_iter_next(it, &ab) == KIT_ITER_ITEM) { 757 if (ab.tag == DW_TAG_compile_unit) { 758 saw_cu_abbrev = 1; 759 cu_code_tbl = ab.table_offset; 760 cu_code = ab.code; 761 EXPECT(ab.has_children, "compile_unit abbrev should have children"); 762 } 763 nabbrev++; 764 } 765 EXPECT(nabbrev >= 5, "expected several abbrevs, got %d", nabbrev); 766 EXPECT(saw_cu_abbrev, "expected a compile_unit abbrev"); 767 kit_dwarf_abbrev_iter_free(it); 768 769 if (saw_cu_abbrev) { 770 KitDwarfAbbrevAttrIter* ait = NULL; 771 KitDwarfAbbrevAttr aa; 772 int saw_name_spec = 0; 773 EXPECT(kit_dwarf_abbrev_attr_iter_new(di, cu_code_tbl, cu_code, &ait) == 774 KIT_OK, 775 "abbrev_attr_iter_new failed"); 776 while (kit_dwarf_abbrev_attr_iter_next(ait, &aa) == KIT_ITER_ITEM) { 777 if (aa.attr == DW_AT_name) saw_name_spec = 1; 778 } 779 EXPECT(saw_name_spec, "compile_unit abbrev missing DW_AT_name spec"); 780 kit_dwarf_abbrev_attr_iter_free(ait); 781 } 782 } 783 784 /* Line iterator: a row at address 0x1000 (line 10), and its file_index 785 * resolves to a path containing test.c. */ 786 { 787 KitDwarfLineIter* it = NULL; 788 KitDwarfLineRow row; 789 int saw_1000 = 0; 790 uint32_t file_at_1000 = 0; 791 EXPECT(kit_dwarf_line_iter_new(di, 0, &it) == KIT_OK, 792 "line_iter_new failed"); 793 while (kit_dwarf_line_iter_next(it, &row) == KIT_ITER_ITEM) { 794 if (row.address == 0x1000 && !row.end_sequence) { 795 saw_1000 = 1; 796 file_at_1000 = row.file_index; 797 EXPECT(row.line == 10, "row at 0x1000 expected line 10, got %u", 798 row.line); 799 } 800 } 801 EXPECT(saw_1000, "expected a line row at address 0x1000"); 802 kit_dwarf_line_iter_free(it); 803 804 if (saw_1000) { 805 KitSlice path = KIT_SLICE_NULL; 806 EXPECT(kit_dwarf_line_file(di, 0, file_at_1000, &path) == KIT_OK, 807 "line_file lookup failed"); 808 EXPECT(path.s && strstr(path.s, "test.c") != NULL, 809 "line file should contain test.c, got %.*s", KIT_SLICE_ARG(path)); 810 } 811 } 812 813 /* .debug_str iterator: the fixture interns these known strings. */ 814 { 815 KitDwarfStrIter* it = NULL; 816 KitDwarfStr s; 817 int saw_int = 0, saw_point = 0, saw_testc = 0; 818 EXPECT(kit_dwarf_str_iter_new(di, &it) == KIT_OK, "str_iter_new failed"); 819 while (kit_dwarf_str_iter_next(it, &s) == KIT_ITER_ITEM) { 820 if (kit_slice_eq_cstr(s.str, "int")) saw_int = 1; 821 if (kit_slice_eq_cstr(s.str, "Point")) saw_point = 1; 822 if (kit_slice_eq_cstr(s.str, "test.c")) saw_testc = 1; 823 } 824 EXPECT(saw_int && saw_point && saw_testc, 825 "expected int/Point/test.c in .debug_str (%d/%d/%d)", saw_int, 826 saw_point, saw_testc); 827 kit_dwarf_str_iter_free(it); 828 } 829 } 830 831 /* ---- main ----------------------------------------------------------- */ 832 833 static void run_tests(KitDebugInfo* di) { 834 /* 1. addr_to_line at func_low. */ 835 KitSlice file = KIT_SLICE_NULL; 836 uint32_t line = 0, col = 0; 837 if (kit_dwarf_addr_to_line(di, 0x1000, &file, &line, &col) == 0) { 838 EXPECT(line == 10, "expected line 10 at 0x1000, got %u (file=%.*s)", line, 839 KIT_SLICE_ARG(file)); 840 EXPECT(file.s && strstr(file.s, "test.c") != NULL, 841 "file should contain test.c, got %.*s", KIT_SLICE_ARG(file)); 842 } else { 843 g_u.fails++; 844 fprintf(stderr, "FAIL: addr_to_line(0x1000) returned no entry\n"); 845 } 846 /* 2. line_to_addr round trip. */ 847 uint64_t pc = 0; 848 if (kit_dwarf_line_to_addr(di, KIT_SLICE_LIT("/proj/test.c"), 10, &pc) == 0) { 849 EXPECT(pc == 0x1000, "expected pc 0x1000 for /proj/test.c:10, got 0x%llx", 850 (unsigned long long)pc); 851 } else { 852 fprintf(stderr, 853 "NOTE: line_to_addr looked up by absolute path failed; " 854 "trying relative\n"); 855 if (kit_dwarf_line_to_addr(di, KIT_SLICE_LIT("test.c"), 10, &pc) == 0) { 856 EXPECT(pc == 0x1000, "expected pc 0x1000 for test.c:10, got 0x%llx", 857 (unsigned long long)pc); 858 } else { 859 g_u.fails++; 860 fprintf(stderr, "FAIL: line_to_addr could not find any test.c:10\n"); 861 } 862 } 863 /* 3. subprogram_at. */ 864 KitDwarfSubprogram sp; 865 EXPECT(kit_dwarf_subprogram_at(di, 0x1000, &sp) == 0, 866 "subprogram_at(0x1000) should succeed"); 867 if (sp.name.s) { 868 EXPECT(kit_slice_eq_cstr(sp.name, "test_main"), 869 "subprogram name '%.*s' != test_main", KIT_SLICE_ARG(sp.name)); 870 } 871 EXPECT(sp.high_pc > sp.low_pc, "subprogram pc range empty"); 872 873 /* 4. var_at "x" should be FRAME_OFS. */ 874 KitDwarfVarLoc loc; 875 EXPECT(kit_dwarf_var_at(di, 0x1000, KIT_SLICE_LIT("x"), &loc) == 0, 876 "var_at(0x1000, x) failed"); 877 if (g_u.fails == 0) { 878 EXPECT(loc.kind == KIT_DLOC_FRAME_OFS, "expected x.kind=FRAME_OFS, got %d", 879 (int)loc.kind); 880 if (loc.kind == KIT_DLOC_FRAME_OFS) { 881 EXPECT(loc.v.frame_ofs == -16, "expected frame_ofs=-16, got %d", 882 loc.v.frame_ofs); 883 } 884 EXPECT(loc.byte_size == 4, "expected byte_size=4, got %u", loc.byte_size); 885 } 886 887 /* 5. var_at "arg" (param) should be REG. */ 888 EXPECT(kit_dwarf_var_at(di, 0x1000, KIT_SLICE_LIT("arg"), &loc) == 0, 889 "var_at(0x1000, arg) failed"); 890 EXPECT(loc.kind == KIT_DLOC_REG, "expected arg.kind=REG, got %d", 891 (int)loc.kind); 892 if (loc.kind == KIT_DLOC_REG) { 893 EXPECT(loc.v.reg == 0, "expected reg=0, got %u", loc.v.reg); 894 } 895 896 /* 6. type_info on int. */ 897 if (loc.type) { 898 KitDwarfTypeInfo ti = kit_dwarf_type_info(loc.type); 899 EXPECT(ti.kind == KIT_DT_SINT, "expected SINT, got kind=%d", (int)ti.kind); 900 EXPECT(ti.byte_size == 4, "expected byte_size=4, got %u", ti.byte_size); 901 EXPECT(kit_slice_eq_cstr(ti.name, "int"), "expected name=int, got %.*s", 902 KIT_SLICE_ARG(ti.name)); 903 } 904 905 /* 7. param_iter — should yield arg. */ 906 KitDwarfParamIter* pi = NULL; 907 EXPECT(kit_dwarf_param_iter_new(di, 0x1000, &pi) == KIT_OK && pi != NULL, 908 "param_iter_new failed"); 909 if (pi) { 910 KitDwarfVar v; 911 int n = 0; 912 for (;;) { 913 KitIterResult r = kit_dwarf_param_iter_next(pi, &v); 914 if (r != KIT_ITER_ITEM) break; 915 n++; 916 EXPECT(kit_slice_eq_cstr(v.name, "arg"), "param name %.*s != arg", 917 KIT_SLICE_ARG(v.name)); 918 } 919 EXPECT(n == 1, "expected 1 param, got %d", n); 920 kit_dwarf_param_iter_free(pi); 921 } 922 923 /* 8. addr_to_line at second row (0x1004) → line 11. */ 924 { 925 KitSlice f2 = KIT_SLICE_NULL; 926 uint32_t l2 = 0, c2 = 0; 927 if (kit_dwarf_addr_to_line(di, 0x1004, &f2, &l2, &c2) == 0) { 928 EXPECT(l2 == 11, "expected line 11 at 0x1004, got %u", l2); 929 } else { 930 g_u.fails++; 931 fprintf(stderr, "FAIL: addr_to_line(0x1004) failed\n"); 932 } 933 } 934 935 /* 9. vars_at_new — yields x, y as locals plus arg as ARG. */ 936 { 937 uint32_t mask = (1u << KIT_DVR_LOCAL) | (1u << KIT_DVR_ARG); 938 KitDwarfVarIter* vi = NULL; 939 int n_local = 0, n_arg = 0, saw_x = 0, saw_y = 0, saw_arg = 0; 940 EXPECT(kit_dwarf_vars_at_new(di, 0x1000, mask, &vi) == KIT_OK && vi != NULL, 941 "vars_at_new failed"); 942 if (vi) { 943 KitDwarfVar v; 944 for (;;) { 945 KitIterResult r = kit_dwarf_vars_at_next(vi, &v); 946 if (r != KIT_ITER_ITEM) break; 947 if (v.role == KIT_DVR_LOCAL) { 948 n_local++; 949 if (kit_slice_eq_cstr(v.name, "x")) saw_x = 1; 950 if (kit_slice_eq_cstr(v.name, "y")) saw_y = 1; 951 } else if (v.role == KIT_DVR_ARG) { 952 n_arg++; 953 if (kit_slice_eq_cstr(v.name, "arg")) saw_arg = 1; 954 } 955 } 956 /* The fixture has 6 locals total (x, y, my_int, Point, x_field, 957 * y_field). We only assert that x and y are among them. */ 958 EXPECT(n_local >= 2 && saw_x && saw_y, 959 "expected >=2 locals incl x,y, got %d", n_local); 960 EXPECT(n_arg == 1 && saw_arg, "expected 1 arg (arg), got %d", n_arg); 961 kit_dwarf_vars_at_free(vi); 962 } 963 } 964 965 /* 10. loc_read REG fast path: pull arg via a fake unwind frame. */ 966 { 967 KitDwarfVarLoc varg; 968 if (kit_dwarf_var_at(di, 0x1000, KIT_SLICE_LIT("arg"), &varg) == KIT_OK) { 969 KitUnwindFrame fr; 970 uint32_t v32 = 0; 971 size_t got = 0; 972 memset(&fr, 0, sizeof fr); 973 fr.regs[0] = 0xdeadbeefULL; 974 fr.cfa = 0x7000; 975 fr.pc = 0x1000; 976 EXPECT(kit_dwarf_loc_read(di, &varg, &fr, NULL, NULL, &v32, sizeof v32, 977 &got) == KIT_OK, 978 "loc_read REG failed"); 979 EXPECT(got >= sizeof v32 && v32 == 0xdeadbeefU, 980 "REG read got %u bytes, val 0x%x", (unsigned)got, v32); 981 } 982 } 983 984 /* 11. type_info: pointer (var "my_int" carries pointer_type → int). */ 985 { 986 KitDwarfVarLoc lp; 987 if (kit_dwarf_var_at(di, 0x1000, KIT_SLICE_LIT("my_int"), &lp) == 0) { 988 KitDwarfTypeInfo ti = kit_dwarf_type_info(lp.type); 989 EXPECT(ti.kind == KIT_DT_PTR, "expected PTR, got kind=%d", (int)ti.kind); 990 EXPECT(ti.byte_size == 8, "expected ptr byte_size=8, got %u", 991 ti.byte_size); 992 if (ti.inner) { 993 KitDwarfTypeInfo it = kit_dwarf_type_info(ti.inner); 994 EXPECT(it.kind == KIT_DT_SINT, "ptr inner kind != SINT (%d)", 995 (int)it.kind); 996 } 997 } else { 998 g_u.fails++; 999 fprintf(stderr, "FAIL: var_at(my_int) returned nothing\n"); 1000 } 1001 } 1002 1003 /* 12. type_info: typedef (var "Point" carries typedef → int). */ 1004 { 1005 KitDwarfVarLoc lp; 1006 if (kit_dwarf_var_at(di, 0x1000, KIT_SLICE_LIT("Point"), &lp) == 0) { 1007 KitDwarfTypeInfo ti = kit_dwarf_type_info(lp.type); 1008 EXPECT(ti.kind == KIT_DT_TYPEDEF, "expected TYPEDEF, got kind=%d", 1009 (int)ti.kind); 1010 EXPECT(kit_slice_eq_cstr(ti.name, "my_int"), 1011 "typedef name=%.*s != my_int", KIT_SLICE_ARG(ti.name)); 1012 EXPECT(ti.inner != NULL, "typedef inner missing"); 1013 } 1014 } 1015 1016 /* 13. type_info: array of int [4]. */ 1017 { 1018 KitDwarfVarLoc lp; 1019 if (kit_dwarf_var_at(di, 0x1000, KIT_SLICE_LIT("x_field"), &lp) == 0) { 1020 KitDwarfTypeInfo ti = kit_dwarf_type_info(lp.type); 1021 EXPECT(ti.kind == KIT_DT_ARRAY, "expected ARRAY, got kind=%d", 1022 (int)ti.kind); 1023 EXPECT(ti.element_count == 4, "expected ec=4, got %u", ti.element_count); 1024 } 1025 } 1026 1027 /* 14. type_info: struct Point with two int fields. */ 1028 { 1029 KitDwarfVarLoc lp; 1030 if (kit_dwarf_var_at(di, 0x1000, KIT_SLICE_LIT("y_field"), &lp) == 0) { 1031 KitDwarfTypeInfo ti = kit_dwarf_type_info(lp.type); 1032 EXPECT(ti.kind == KIT_DT_STRUCT, "expected STRUCT, got kind=%d", 1033 (int)ti.kind); 1034 EXPECT(ti.byte_size == 8, "struct byte_size=%u", ti.byte_size); 1035 EXPECT(kit_slice_eq_cstr(ti.name, "Point"), "struct name=%.*s", 1036 KIT_SLICE_ARG(ti.name)); 1037 KitDwarfFieldIter* fi = NULL; 1038 EXPECT(kit_dwarf_field_iter_new(di, lp.type, &fi) == KIT_OK && fi != NULL, 1039 "field_iter_new failed"); 1040 if (fi) { 1041 KitDwarfField f; 1042 int count = 0; 1043 int saw_x = 0, saw_y = 0; 1044 for (;;) { 1045 KitIterResult r = kit_dwarf_field_iter_next(fi, &f); 1046 if (r != KIT_ITER_ITEM) break; 1047 count++; 1048 if (kit_slice_eq_cstr(f.name, "x_field") && f.byte_offset == 0) 1049 saw_x = 1; 1050 if (kit_slice_eq_cstr(f.name, "y_field") && f.byte_offset == 4) 1051 saw_y = 1; 1052 } 1053 EXPECT(count == 2, "expected 2 fields, got %d", count); 1054 EXPECT(saw_x && saw_y, "missing x_field or y_field"); 1055 kit_dwarf_field_iter_free(fi); 1056 } 1057 } 1058 } 1059 } 1060 1061 int main(void) { 1062 KitTargetSpec target; 1063 kit_unit_init(&g_u); 1064 g_u.ctx.now = -1; 1065 target = kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF); 1066 1067 KitCompiler* cc = NULL; 1068 if (kit_unit_compiler_new(&g_u, target, &cc) != KIT_OK || !cc) { 1069 fprintf(stderr, "compiler_new failed\n"); 1070 return 1; 1071 } 1072 1073 /* Build .debug_* byte buffers. */ 1074 ByteBuf abbrev, info, line, str, line_str; 1075 bb_init(&abbrev); 1076 bb_init(&info); 1077 bb_init(&line); 1078 bb_init(&str); 1079 bb_init(&line_str); 1080 /* Reserve initial 0 in str/line_str so offset 0 is a valid empty 1081 * string. */ 1082 bb_u8(&str, 0); 1083 bb_u8(&line_str, 0); 1084 DieOffsets die_offs = {0}; 1085 build_debug_sections(&abbrev, &info, &line, &str, &line_str, 0x1000, 8, 1086 &die_offs); 1087 (void)die_offs; 1088 1089 /* Build an ObjBuilder via internal API. */ 1090 ObjBuilder* ob = obj_new(cc); 1091 Sym text_name = pool_intern_slice(cc->global, SLICE_LIT(".text")); 1092 Sym func_name = pool_intern_slice(cc->global, SLICE_LIT("test_main")); 1093 ObjSecId text_sec = 1094 obj_section(ob, text_name, SEC_TEXT, SF_EXEC | SF_ALLOC, 4); 1095 /* 8 bytes of nop-like text. */ 1096 uint8_t text_bytes[8] = {0}; 1097 obj_write(ob, text_sec, text_bytes, 8); 1098 obj_symbol(ob, func_name, SB_GLOBAL, SK_FUNC, text_sec, 0, 8); 1099 1100 Sym n_abbrev = pool_intern_slice(cc->global, SLICE_LIT(".debug_abbrev")); 1101 Sym n_info = pool_intern_slice(cc->global, SLICE_LIT(".debug_info")); 1102 Sym n_line = pool_intern_slice(cc->global, SLICE_LIT(".debug_line")); 1103 Sym n_str = pool_intern_slice(cc->global, SLICE_LIT(".debug_str")); 1104 Sym n_line_str = pool_intern_slice(cc->global, SLICE_LIT(".debug_line_str")); 1105 ObjSecId s_abbrev = obj_section(ob, n_abbrev, SEC_DEBUG, 0, 1); 1106 ObjSecId s_info = obj_section(ob, n_info, SEC_DEBUG, 0, 1); 1107 ObjSecId s_line = obj_section(ob, n_line, SEC_DEBUG, 0, 1); 1108 ObjSecId s_str = obj_section(ob, n_str, SEC_DEBUG, 0, 1); 1109 ObjSecId s_line_str = obj_section(ob, n_line_str, SEC_DEBUG, 0, 1); 1110 obj_write(ob, s_abbrev, abbrev.data, abbrev.len); 1111 obj_write(ob, s_info, info.data, info.len); 1112 obj_write(ob, s_line, line.data, line.len); 1113 obj_write(ob, s_str, str.data, str.len); 1114 obj_write(ob, s_line_str, line_str.data, line_str.len); 1115 obj_finalize(ob); 1116 1117 /* Emit ELF to memory. */ 1118 KitWriter* w = NULL; 1119 (void)kit_writer_mem(&g_u.heap, &w); 1120 emit_elf(cc, ob, w); 1121 size_t obj_len = 0; 1122 const uint8_t* obj_bytes = kit_writer_mem_bytes(w, &obj_len); 1123 fprintf(stderr, "built obj: %zu bytes\n", obj_len); 1124 1125 /* Optional: dump the synthesized DWARF-bearing ELF to a file so the 1126 * objdump driver test has a real fixture (cc -g is the usual source, but 1127 * this keeps the fixture reproducible from a known byte layout). */ 1128 { 1129 const char* dump = getenv("KIT_DWARF_WRITE_FIXTURE"); 1130 if (dump && obj_bytes) { 1131 FILE* fp = fopen(dump, "wb"); 1132 if (fp) { 1133 fwrite(obj_bytes, 1, obj_len, fp); 1134 fclose(fp); 1135 fprintf(stderr, "wrote fixture: %s\n", dump); 1136 } 1137 } 1138 } 1139 1140 /* Re-open via the public API. */ 1141 KitSlice in; 1142 memset(&in, 0, sizeof in); 1143 in.data = obj_bytes; 1144 in.len = obj_len; 1145 KitObjFile* obj = NULL; 1146 EXPECT(kit_obj_open(&g_u.ctx, KIT_SLICE_LIT("test.o"), &in, &obj) == KIT_OK && 1147 obj != NULL, 1148 "kit_obj_open failed"); 1149 if (obj) { 1150 KitDebugInfo* di = NULL; 1151 EXPECT(kit_dwarf_open(&g_u.ctx, obj, &di) == KIT_OK && di != NULL, 1152 "kit_dwarf_open failed"); 1153 if (di) { 1154 run_tests(di); 1155 run_dump_tests(di); 1156 kit_dwarf_free(di); 1157 } 1158 kit_obj_free(obj); 1159 } 1160 1161 if (w->close) w->close(w); 1162 obj_free(ob); 1163 1164 free(abbrev.data); 1165 free(info.data); 1166 free(line.data); 1167 free(str.data); 1168 free(line_str.data); 1169 1170 kit_compiler_free(cc); 1171 1172 if (g_u.fails) { 1173 fprintf(stderr, "%d failure(s)\n", g_u.fails); 1174 return 1; 1175 } 1176 printf("OK\n"); 1177 return 0; 1178 }