| ... | @@ -41,6 +41,8 @@ abbrev_table_offset: ?u64 = null, | ... | @@ -41,6 +41,8 @@ abbrev_table_offset: ?u64 = null, |
| 41 | /// Table of debug symbol names. | 41 | /// Table of debug symbol names. |
| 42 | strtab: std.ArrayListUnmanaged(u8) = .{}, | 42 | strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 43 | | 43 | |
| | 44 | deferred_error_sets_relocs: std.ArrayListUnmanaged(u32) = .{}, |
| | 45 | |
| 44 | pub const DebugInfoAtom = struct { | 46 | pub const DebugInfoAtom = struct { |
| 45 | /// Previous/next linked list pointers. | 47 | /// Previous/next linked list pointers. |
| 46 | /// This is the linked list node for this Decl's corresponding .debug_info tag. | 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,6 +121,7 @@ pub fn deinit(self: *Dwarf) void { |
| 119 | self.dbg_line_fn_free_list.deinit(gpa); | 121 | self.dbg_line_fn_free_list.deinit(gpa); |
| 120 | self.dbg_info_decl_free_list.deinit(gpa); | 122 | self.dbg_info_decl_free_list.deinit(gpa); |
| 121 | self.strtab.deinit(gpa); | 123 | self.strtab.deinit(gpa); |
| | 124 | self.deferred_error_sets_relocs.deinit(gpa); |
| 122 | } | 125 | } |
| 123 | | 126 | |
| 124 | pub const DeclDebugBuffers = struct { | 127 | pub const DeclDebugBuffers = struct { |
| ... | @@ -462,6 +465,18 @@ pub fn commitDeclDebugInfo( | ... | @@ -462,6 +465,18 @@ pub fn commitDeclDebugInfo( |
| 462 | var it: usize = 0; | 465 | var it: usize = 0; |
| 463 | while (it < dbg_info_type_relocs.count()) : (it += 1) { | 466 | while (it < dbg_info_type_relocs.count()) : (it += 1) { |
| 464 | const ty = dbg_info_type_relocs.keys()[it]; | 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 | const value_ptr = dbg_info_type_relocs.getPtrContext(ty, .{ | 480 | const value_ptr = dbg_info_type_relocs.getPtrContext(ty, .{ |
| 466 | .target = self.target, | 481 | .target = self.target, |
| 467 | }).?; | 482 | }).?; |
| ... | @@ -486,14 +501,32 @@ pub fn commitDeclDebugInfo( | ... | @@ -486,14 +501,32 @@ pub fn commitDeclDebugInfo( |
| 486 | | 501 | |
| 487 | { | 502 | { |
| 488 | // Now that we have the offset assigned we can finally perform type relocations. | 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 | for (value.relocs.items) |off| { | 508 | for (value.relocs.items) |off| { |
| 491 | mem.writeInt( | 509 | const deferred: bool = blk: { |
| 492 | u32, | 510 | if (ty.isAnyError()) break :blk true; |
| 493 | dbg_info_buffer.items[off..][0..4], | 511 | switch (ty.tag()) { |
| 494 | atom.off + value.off, | 512 | .error_set_inferred => { |
| 495 | target_endian, | 513 | if (!ty.castTag(.error_set_inferred).?.data.is_resolved) break :blk true; |
| 496 | ); | 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 | // Offsets to positions with known a priori relative displacement values. | 532 | // Offsets to positions with known a priori relative displacement values. |
| ... | @@ -514,6 +547,79 @@ pub fn commitDeclDebugInfo( | ... | @@ -514,6 +547,79 @@ pub fn commitDeclDebugInfo( |
| 514 | try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); | 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 | fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *DebugInfoAtom, len: u32) !void { | 623 | fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *DebugInfoAtom, len: u32) !void { |
| 518 | const tracy = trace(@src()); | 624 | const tracy = trace(@src()); |
| 519 | defer tracy.end(); | 625 | defer tracy.end(); |
| ... | @@ -1098,51 +1204,7 @@ fn addDbgInfoType( | ... | @@ -1098,51 +1204,7 @@ fn addDbgInfoType( |
| 1098 | } | 1204 | } |
| 1099 | }, | 1205 | }, |
| 1100 | .ErrorSet => { | 1206 | .ErrorSet => { |
| 1101 | // DW.AT.enumeration_type | 1207 | try self.addDbgInfoErrorSet(arena, module, ty, dbg_info_buffer); |
| 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); | | |
| 1146 | }, | 1208 | }, |
| 1147 | .ErrorUnion => { | 1209 | .ErrorUnion => { |
| 1148 | const error_ty = ty.errorUnionSet(); | 1210 | const error_ty = ty.errorUnionSet(); |
| ... | @@ -1208,6 +1270,52 @@ fn addDbgInfoType( | ... | @@ -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 | pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { | 1319 | pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { |
| 1212 | // These are LEB encoded but since the values are all less than 127 | 1320 | // These are LEB encoded but since the values are all less than 127 |
| 1213 | // we can simply append these bytes. | 1321 | // we can simply append these bytes. |