| ... | ... | @@ -41,6 +41,8 @@ abbrev_table_offset: ?u64 = null, |
| 41 | 41 | /// Table of debug symbol names. |
| 42 | 42 | strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 43 | 43 | |
| 44 | deferred_error_sets_relocs: std.ArrayListUnmanaged(u32) = .{}, |
| 45 | |
| 44 | 46 | pub const DebugInfoAtom = struct { |
| 45 | 47 | /// Previous/next linked list pointers. |
| 46 | 48 | /// This is the linked list node for this Decl's corresponding .debug_info tag. |
| ... | ... | @@ -119,6 +121,7 @@ pub fn deinit(self: *Dwarf) void { |
| 119 | 121 | self.dbg_line_fn_free_list.deinit(gpa); |
| 120 | 122 | self.dbg_info_decl_free_list.deinit(gpa); |
| 121 | 123 | self.strtab.deinit(gpa); |
| 124 | self.deferred_error_sets_relocs.deinit(gpa); |
| 122 | 125 | } |
| 123 | 126 | |
| 124 | 127 | pub const DeclDebugBuffers = struct { |
| ... | ... | @@ -462,6 +465,18 @@ pub fn commitDeclDebugInfo( |
| 462 | 465 | var it: usize = 0; |
| 463 | 466 | while (it < dbg_info_type_relocs.count()) : (it += 1) { |
| 464 | 467 | const ty = dbg_info_type_relocs.keys()[it]; |
| 468 | const deferred: bool = blk: { |
| 469 | if (ty.isAnyError()) break :blk true; |
| 470 | switch (ty.tag()) { |
| 471 | .error_set_inferred => { |
| 472 | if (!ty.castTag(.error_set_inferred).?.data.is_resolved) break :blk true; |
| 473 | }, |
| 474 | else => {}, |
| 475 | } |
| 476 | break :blk false; |
| 477 | }; |
| 478 | if (deferred) continue; |
| 479 | |
| 465 | 480 | const value_ptr = dbg_info_type_relocs.getPtrContext(ty, .{ |
| 466 | 481 | .target = self.target, |
| 467 | 482 | }).?; |
| ... | ... | @@ -486,14 +501,32 @@ pub fn commitDeclDebugInfo( |
| 486 | 501 | |
| 487 | 502 | { |
| 488 | 503 | // Now that we have the offset assigned we can finally perform type relocations. |
| 489 | | for (dbg_info_type_relocs.values()) |value| { |
| 504 | for (dbg_info_type_relocs.keys()) |ty| { |
| 505 | const value = dbg_info_type_relocs.getContext(ty, .{ |
| 506 | .target = self.target, |
| 507 | }).?; |
| 490 | 508 | for (value.relocs.items) |off| { |
| 491 | | mem.writeInt( |
| 492 | | u32, |
| 493 | | dbg_info_buffer.items[off..][0..4], |
| 494 | | atom.off + value.off, |
| 495 | | target_endian, |
| 496 | | ); |
| 509 | const deferred: bool = blk: { |
| 510 | if (ty.isAnyError()) break :blk true; |
| 511 | switch (ty.tag()) { |
| 512 | .error_set_inferred => { |
| 513 | if (!ty.castTag(.error_set_inferred).?.data.is_resolved) break :blk true; |
| 514 | }, |
| 515 | else => {}, |
| 516 | } |
| 517 | break :blk false; |
| 518 | }; |
| 519 | if (deferred) { |
| 520 | // Defer until later |
| 521 | try self.deferred_error_sets_relocs.append(self.allocator, atom.off + off); |
| 522 | } else { |
| 523 | mem.writeInt( |
| 524 | u32, |
| 525 | dbg_info_buffer.items[off..][0..4], |
| 526 | atom.off + value.off, |
| 527 | target_endian, |
| 528 | ); |
| 529 | } |
| 497 | 530 | } |
| 498 | 531 | } |
| 499 | 532 | // Offsets to positions with known a priori relative displacement values. |
| ... | ... | @@ -514,6 +547,79 @@ pub fn commitDeclDebugInfo( |
| 514 | 547 | try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); |
| 515 | 548 | } |
| 516 | 549 | |
| 550 | pub fn commitErrorSetDebugInfo(self: *Dwarf, file: *File, module: *Module) !void { |
| 551 | if (self.deferred_error_sets_relocs.items.len == 0) return; // Nothing to do |
| 552 | |
| 553 | const gpa = self.allocator; |
| 554 | var arena_alloc = std.heap.ArenaAllocator.init(gpa); |
| 555 | defer arena_alloc.deinit(); |
| 556 | const arena = arena_alloc.allocator(); |
| 557 | |
| 558 | const error_set = try arena.create(Module.ErrorSet); |
| 559 | const ty = try Type.Tag.error_set.create(arena, error_set); |
| 560 | var names = Module.ErrorSet.NameMap{}; |
| 561 | try names.ensureUnusedCapacity(arena, module.global_error_set.count()); |
| 562 | var it = module.global_error_set.keyIterator(); |
| 563 | while (it.next()) |key| { |
| 564 | names.putAssumeCapacityNoClobber(key.*, {}); |
| 565 | } |
| 566 | error_set.names = names; |
| 567 | |
| 568 | var dbg_info_buffer = std.ArrayList(u8).init(arena); |
| 569 | try self.addDbgInfoErrorSet(arena, module, ty, &dbg_info_buffer); |
| 570 | |
| 571 | // TODO seems like we need to store DebugInfoAtoms in Dwarf object |
| 572 | // In other words, I have turned Dwarf into a linker... |
| 573 | // FIXME memory leak!!! |
| 574 | const atom = try gpa.create(DebugInfoAtom); |
| 575 | errdefer gpa.destroy(atom); |
| 576 | atom.* = .{ |
| 577 | .prev = null, |
| 578 | .next = null, |
| 579 | .off = 0, |
| 580 | .len = 0, |
| 581 | }; |
| 582 | try self.updateDeclDebugInfoAllocation(file, atom, @intCast(u32, dbg_info_buffer.items.len)); |
| 583 | try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); |
| 584 | |
| 585 | const file_pos = blk: { |
| 586 | switch (self.tag) { |
| 587 | .elf => { |
| 588 | const elf_file = file.cast(File.Elf).?; |
| 589 | const debug_info_sect = &elf_file.sections.items[elf_file.debug_info_section_index.?]; |
| 590 | break :blk debug_info_sect.sh_offset; |
| 591 | }, |
| 592 | .macho => { |
| 593 | const macho_file = file.cast(File.MachO).?; |
| 594 | const d_sym = &macho_file.d_sym.?; |
| 595 | const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment; |
| 596 | const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?]; |
| 597 | break :blk debug_info_sect.offset; |
| 598 | }, |
| 599 | else => unreachable, |
| 600 | } |
| 601 | }; |
| 602 | |
| 603 | const target_endian = self.target.cpu.arch.endian(); |
| 604 | var buf: [@sizeOf(u32)]u8 = undefined; |
| 605 | while (self.deferred_error_sets_relocs.popOrNull()) |reloc| { |
| 606 | mem.writeInt(u32, &buf, atom.off, target_endian); |
| 607 | |
| 608 | switch (self.tag) { |
| 609 | .elf => { |
| 610 | const elf_file = file.cast(File.Elf).?; |
| 611 | try elf_file.base.file.?.pwriteAll(&buf, file_pos + reloc); |
| 612 | }, |
| 613 | .macho => { |
| 614 | const macho_file = file.cast(File.MachO).?; |
| 615 | const d_sym = &macho_file.d_sym.?; |
| 616 | try d_sym.file.pwriteAll(&buf, file_pos + reloc); |
| 617 | }, |
| 618 | else => unreachable, |
| 619 | } |
| 620 | } |
| 621 | } |
| 622 | |
| 517 | 623 | fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *DebugInfoAtom, len: u32) !void { |
| 518 | 624 | const tracy = trace(@src()); |
| 519 | 625 | defer tracy.end(); |
| ... | ... | @@ -1098,51 +1204,7 @@ fn addDbgInfoType( |
| 1098 | 1204 | } |
| 1099 | 1205 | }, |
| 1100 | 1206 | .ErrorSet => { |
| 1101 | | // DW.AT.enumeration_type |
| 1102 | | try dbg_info_buffer.append(abbrev_enum_type); |
| 1103 | | // DW.AT.byte_size, DW.FORM.sdata |
| 1104 | | const abi_size = ty.abiSize(target); |
| 1105 | | try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); |
| 1106 | | // DW.AT.name, DW.FORM.string |
| 1107 | | const name = try ty.nameAllocArena(arena, target); |
| 1108 | | try dbg_info_buffer.writer().print("{s}\x00", .{name}); |
| 1109 | | |
| 1110 | | // DW.AT.enumerator |
| 1111 | | const no_error = "(no error)"; |
| 1112 | | try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64)); |
| 1113 | | dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); |
| 1114 | | // DW.AT.name, DW.FORM.string |
| 1115 | | dbg_info_buffer.appendSliceAssumeCapacity(no_error); |
| 1116 | | dbg_info_buffer.appendAssumeCapacity(0); |
| 1117 | | // DW.AT.const_value, DW.FORM.data8 |
| 1118 | | mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian); |
| 1119 | | |
| 1120 | | const error_names = blk: { |
| 1121 | | if (ty.isAnyError()) |
| 1122 | | break :blk module.error_name_list.items; |
| 1123 | | // TODO not quite sure about the next one, but if I don't do this, I risk |
| 1124 | | // tripping an assert in `Type.errorSetNames` in case the inferred error set |
| 1125 | | // was not yet fully resolved. This so far only surfaced when this code would |
| 1126 | | // schedule analysis of an error set part of some error union. |
| 1127 | | if (ty.tag() == .error_set_inferred) |
| 1128 | | break :blk ty.castTag(.error_set_inferred).?.data.errors.keys(); |
| 1129 | | break :blk ty.errorSetNames(); |
| 1130 | | }; |
| 1131 | | |
| 1132 | | for (error_names) |error_name| { |
| 1133 | | const kv = module.getErrorValue(error_name) catch unreachable; |
| 1134 | | // DW.AT.enumerator |
| 1135 | | try dbg_info_buffer.ensureUnusedCapacity(error_name.len + 2 + @sizeOf(u64)); |
| 1136 | | dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); |
| 1137 | | // DW.AT.name, DW.FORM.string |
| 1138 | | dbg_info_buffer.appendSliceAssumeCapacity(error_name); |
| 1139 | | dbg_info_buffer.appendAssumeCapacity(0); |
| 1140 | | // DW.AT.const_value, DW.FORM.data8 |
| 1141 | | mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), kv.value, target_endian); |
| 1142 | | } |
| 1143 | | |
| 1144 | | // DW.AT.enumeration_type delimit children |
| 1145 | | try dbg_info_buffer.append(0); |
| 1207 | try self.addDbgInfoErrorSet(arena, module, ty, dbg_info_buffer); |
| 1146 | 1208 | }, |
| 1147 | 1209 | .ErrorUnion => { |
| 1148 | 1210 | const error_ty = ty.errorUnionSet(); |
| ... | ... | @@ -1208,6 +1270,52 @@ fn addDbgInfoType( |
| 1208 | 1270 | } |
| 1209 | 1271 | } |
| 1210 | 1272 | |
| 1273 | fn addDbgInfoErrorSet( |
| 1274 | self: *Dwarf, |
| 1275 | arena: Allocator, |
| 1276 | module: *Module, |
| 1277 | ty: Type, |
| 1278 | dbg_info_buffer: *std.ArrayList(u8), |
| 1279 | ) error{OutOfMemory}!void { |
| 1280 | const target = self.target; |
| 1281 | const target_endian = self.target.cpu.arch.endian(); |
| 1282 | |
| 1283 | // DW.AT.enumeration_type |
| 1284 | try dbg_info_buffer.append(abbrev_enum_type); |
| 1285 | // DW.AT.byte_size, DW.FORM.sdata |
| 1286 | const abi_size = ty.abiSize(target); |
| 1287 | try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); |
| 1288 | // DW.AT.name, DW.FORM.string |
| 1289 | const name = try ty.nameAllocArena(arena, target); |
| 1290 | try dbg_info_buffer.writer().print("{s}\x00", .{name}); |
| 1291 | |
| 1292 | // DW.AT.enumerator |
| 1293 | const no_error = "(no error)"; |
| 1294 | try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64)); |
| 1295 | dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); |
| 1296 | // DW.AT.name, DW.FORM.string |
| 1297 | dbg_info_buffer.appendSliceAssumeCapacity(no_error); |
| 1298 | dbg_info_buffer.appendAssumeCapacity(0); |
| 1299 | // DW.AT.const_value, DW.FORM.data8 |
| 1300 | mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian); |
| 1301 | |
| 1302 | const error_names = ty.errorSetNames(); |
| 1303 | for (error_names) |error_name| { |
| 1304 | const kv = module.getErrorValue(error_name) catch unreachable; |
| 1305 | // DW.AT.enumerator |
| 1306 | try dbg_info_buffer.ensureUnusedCapacity(error_name.len + 2 + @sizeOf(u64)); |
| 1307 | dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); |
| 1308 | // DW.AT.name, DW.FORM.string |
| 1309 | dbg_info_buffer.appendSliceAssumeCapacity(error_name); |
| 1310 | dbg_info_buffer.appendAssumeCapacity(0); |
| 1311 | // DW.AT.const_value, DW.FORM.data8 |
| 1312 | mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), kv.value, target_endian); |
| 1313 | } |
| 1314 | |
| 1315 | // DW.AT.enumeration_type delimit children |
| 1316 | try dbg_info_buffer.append(0); |
| 1317 | } |
| 1318 | |
| 1211 | 1319 | pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { |
| 1212 | 1320 | // These are LEB encoded but since the values are all less than 127 |
| 1213 | 1321 | // we can simply append these bytes. |