authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-03-26 12:04:18+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-03-27 20:53:06+02:00
logb4815b31310a36e3c1fabd83d010b44b693c9782
treebf1bd996212a148ba595332c37cc16dfe943827c
parente444e69dc4f9e6a1f25f6081bd0cd4d45f4c9c93

dwarf: draft poc of deferred resolution of error sets debug info


2 files changed, 172 insertions(+), 53 deletions(-)

src/link/Dwarf.zig+160-52
...@@ -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.
42strtab: std.ArrayListUnmanaged(u8) = .{},42strtab: std.ArrayListUnmanaged(u8) = .{},
4343
44deferred_error_sets_relocs: std.ArrayListUnmanaged(u32) = .{},
45
44pub const DebugInfoAtom = struct {46pub 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}
123126
124pub const DeclDebugBuffers = struct {127pub 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(
486501
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}
516549
550pub 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
517fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *DebugInfoAtom, len: u32) !void {623fn 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_type1207 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}
12101272
1273fn 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
1211pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {1319pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void {
1212 // These are LEB encoded but since the values are all less than 1271320 // 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.
src/link/Elf.zig+12-1
...@@ -958,6 +958,10 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {...@@ -958,6 +958,10 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
958 const target_endian = self.base.options.target.cpu.arch.endian();958 const target_endian = self.base.options.target.cpu.arch.endian();
959 const foreign_endian = target_endian != builtin.cpu.arch.endian();959 const foreign_endian = target_endian != builtin.cpu.arch.endian();
960960
961 if (self.dwarf) |*dwarf| {
962 try dwarf.commitErrorSetDebugInfo(&self.base, module);
963 }
964
961 {965 {
962 var it = self.relocs.iterator();966 var it = self.relocs.iterator();
963 while (it.next()) |entry| {967 while (it.next()) |entry| {
...@@ -2376,7 +2380,14 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven...@@ -2376,7 +2380,14 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
2376 };2380 };
2377 const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC);2381 const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC);
2378 if (debug_buffers) |dbg| {2382 if (debug_buffers) |dbg| {
2379 try self.dwarf.?.commitDeclDebugInfo(&self.base, module, decl, local_sym.st_value, local_sym.st_size, dbg);2383 try self.dwarf.?.commitDeclDebugInfo(
2384 &self.base,
2385 module,
2386 decl,
2387 local_sym.st_value,
2388 local_sym.st_size,
2389 dbg,
2390 );
2380 }2391 }
23812392
2382 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.2393 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.