diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2022-03-26 12:04:18 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2022-03-27 20:53:06 +0200 |
| commit | b4815b31310a36e3c1fabd83d010b44b693c9782 (patch) | |
| tree | bf1bd996212a148ba595332c37cc16dfe943827c /src | |
| parent | e444e69dc4f9e6a1f25f6081bd0cd4d45f4c9c93 (diff) | |
| download | zig-b4815b31310a36e3c1fabd83d010b44b693c9782.tar.gz zig-b4815b31310a36e3c1fabd83d010b44b693c9782.zip | |
dwarf: draft poc of deferred resolution of error sets debug info
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/Dwarf.zig | 212 | ||||
| -rw-r--r-- | src/link/Elf.zig | 13 |
2 files changed, 172 insertions, 53 deletions
diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index f00b1f6479..5e46811900 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -41,6 +41,8 @@ abbrev_table_offset: ?u64 = null, /// Table of debug symbol names. strtab: std.ArrayListUnmanaged(u8) = .{}, +deferred_error_sets_relocs: std.ArrayListUnmanaged(u32) = .{}, + pub const DebugInfoAtom = struct { /// Previous/next linked list pointers. /// This is the linked list node for this Decl's corresponding .debug_info tag. @@ -119,6 +121,7 @@ pub fn deinit(self: *Dwarf) void { self.dbg_line_fn_free_list.deinit(gpa); self.dbg_info_decl_free_list.deinit(gpa); self.strtab.deinit(gpa); + self.deferred_error_sets_relocs.deinit(gpa); } pub const DeclDebugBuffers = struct { @@ -462,6 +465,18 @@ pub fn commitDeclDebugInfo( var it: usize = 0; while (it < dbg_info_type_relocs.count()) : (it += 1) { const ty = dbg_info_type_relocs.keys()[it]; + const deferred: bool = blk: { + if (ty.isAnyError()) break :blk true; + switch (ty.tag()) { + .error_set_inferred => { + if (!ty.castTag(.error_set_inferred).?.data.is_resolved) break :blk true; + }, + else => {}, + } + break :blk false; + }; + if (deferred) continue; + const value_ptr = dbg_info_type_relocs.getPtrContext(ty, .{ .target = self.target, }).?; @@ -486,14 +501,32 @@ pub fn commitDeclDebugInfo( { // Now that we have the offset assigned we can finally perform type relocations. - for (dbg_info_type_relocs.values()) |value| { + for (dbg_info_type_relocs.keys()) |ty| { + const value = dbg_info_type_relocs.getContext(ty, .{ + .target = self.target, + }).?; for (value.relocs.items) |off| { - mem.writeInt( - u32, - dbg_info_buffer.items[off..][0..4], - atom.off + value.off, - target_endian, - ); + const deferred: bool = blk: { + if (ty.isAnyError()) break :blk true; + switch (ty.tag()) { + .error_set_inferred => { + if (!ty.castTag(.error_set_inferred).?.data.is_resolved) break :blk true; + }, + else => {}, + } + break :blk false; + }; + if (deferred) { + // Defer until later + try self.deferred_error_sets_relocs.append(self.allocator, atom.off + off); + } else { + mem.writeInt( + u32, + dbg_info_buffer.items[off..][0..4], + atom.off + value.off, + target_endian, + ); + } } } // Offsets to positions with known a priori relative displacement values. @@ -514,6 +547,79 @@ pub fn commitDeclDebugInfo( try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); } +pub fn commitErrorSetDebugInfo(self: *Dwarf, file: *File, module: *Module) !void { + if (self.deferred_error_sets_relocs.items.len == 0) return; // Nothing to do + + const gpa = self.allocator; + var arena_alloc = std.heap.ArenaAllocator.init(gpa); + defer arena_alloc.deinit(); + const arena = arena_alloc.allocator(); + + const error_set = try arena.create(Module.ErrorSet); + const ty = try Type.Tag.error_set.create(arena, error_set); + var names = Module.ErrorSet.NameMap{}; + try names.ensureUnusedCapacity(arena, module.global_error_set.count()); + var it = module.global_error_set.keyIterator(); + while (it.next()) |key| { + names.putAssumeCapacityNoClobber(key.*, {}); + } + error_set.names = names; + + var dbg_info_buffer = std.ArrayList(u8).init(arena); + try self.addDbgInfoErrorSet(arena, module, ty, &dbg_info_buffer); + + // TODO seems like we need to store DebugInfoAtoms in Dwarf object + // In other words, I have turned Dwarf into a linker... + // FIXME memory leak!!! + const atom = try gpa.create(DebugInfoAtom); + errdefer gpa.destroy(atom); + atom.* = .{ + .prev = null, + .next = null, + .off = 0, + .len = 0, + }; + try self.updateDeclDebugInfoAllocation(file, atom, @intCast(u32, dbg_info_buffer.items.len)); + try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); + + const file_pos = blk: { + switch (self.tag) { + .elf => { + const elf_file = file.cast(File.Elf).?; + const debug_info_sect = &elf_file.sections.items[elf_file.debug_info_section_index.?]; + break :blk debug_info_sect.sh_offset; + }, + .macho => { + const macho_file = file.cast(File.MachO).?; + const d_sym = &macho_file.d_sym.?; + const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment; + const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?]; + break :blk debug_info_sect.offset; + }, + else => unreachable, + } + }; + + const target_endian = self.target.cpu.arch.endian(); + var buf: [@sizeOf(u32)]u8 = undefined; + while (self.deferred_error_sets_relocs.popOrNull()) |reloc| { + mem.writeInt(u32, &buf, atom.off, target_endian); + + switch (self.tag) { + .elf => { + const elf_file = file.cast(File.Elf).?; + try elf_file.base.file.?.pwriteAll(&buf, file_pos + reloc); + }, + .macho => { + const macho_file = file.cast(File.MachO).?; + const d_sym = &macho_file.d_sym.?; + try d_sym.file.pwriteAll(&buf, file_pos + reloc); + }, + else => unreachable, + } + } +} + fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *DebugInfoAtom, len: u32) !void { const tracy = trace(@src()); defer tracy.end(); @@ -1098,51 +1204,7 @@ fn addDbgInfoType( } }, .ErrorSet => { - // DW.AT.enumeration_type - try dbg_info_buffer.append(abbrev_enum_type); - // DW.AT.byte_size, DW.FORM.sdata - const abi_size = ty.abiSize(target); - try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); - // DW.AT.name, DW.FORM.string - const name = try ty.nameAllocArena(arena, target); - try dbg_info_buffer.writer().print("{s}\x00", .{name}); - - // DW.AT.enumerator - const no_error = "(no error)"; - try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity(no_error); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.const_value, DW.FORM.data8 - mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian); - - const error_names = blk: { - if (ty.isAnyError()) - break :blk module.error_name_list.items; - // TODO not quite sure about the next one, but if I don't do this, I risk - // tripping an assert in `Type.errorSetNames` in case the inferred error set - // was not yet fully resolved. This so far only surfaced when this code would - // schedule analysis of an error set part of some error union. - if (ty.tag() == .error_set_inferred) - break :blk ty.castTag(.error_set_inferred).?.data.errors.keys(); - break :blk ty.errorSetNames(); - }; - - for (error_names) |error_name| { - const kv = module.getErrorValue(error_name) catch unreachable; - // DW.AT.enumerator - try dbg_info_buffer.ensureUnusedCapacity(error_name.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity(error_name); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.const_value, DW.FORM.data8 - mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), kv.value, target_endian); - } - - // DW.AT.enumeration_type delimit children - try dbg_info_buffer.append(0); + try self.addDbgInfoErrorSet(arena, module, ty, dbg_info_buffer); }, .ErrorUnion => { const error_ty = ty.errorUnionSet(); @@ -1208,6 +1270,52 @@ fn addDbgInfoType( } } +fn addDbgInfoErrorSet( + self: *Dwarf, + arena: Allocator, + module: *Module, + ty: Type, + dbg_info_buffer: *std.ArrayList(u8), +) error{OutOfMemory}!void { + const target = self.target; + const target_endian = self.target.cpu.arch.endian(); + + // DW.AT.enumeration_type + try dbg_info_buffer.append(abbrev_enum_type); + // DW.AT.byte_size, DW.FORM.sdata + const abi_size = ty.abiSize(target); + try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); + // DW.AT.name, DW.FORM.string + const name = try ty.nameAllocArena(arena, target); + try dbg_info_buffer.writer().print("{s}\x00", .{name}); + + // DW.AT.enumerator + const no_error = "(no error)"; + try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64)); + dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity(no_error); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.const_value, DW.FORM.data8 + mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian); + + const error_names = ty.errorSetNames(); + for (error_names) |error_name| { + const kv = module.getErrorValue(error_name) catch unreachable; + // DW.AT.enumerator + try dbg_info_buffer.ensureUnusedCapacity(error_name.len + 2 + @sizeOf(u64)); + dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity(error_name); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.const_value, DW.FORM.data8 + mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), kv.value, target_endian); + } + + // DW.AT.enumeration_type delimit children + try dbg_info_buffer.append(0); +} + pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { // These are LEB encoded but since the values are all less than 127 // we can simply append these bytes. diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 64d6df6756..c81c15f597 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -958,6 +958,10 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { const target_endian = self.base.options.target.cpu.arch.endian(); const foreign_endian = target_endian != builtin.cpu.arch.endian(); + if (self.dwarf) |*dwarf| { + try dwarf.commitErrorSetDebugInfo(&self.base, module); + } + { var it = self.relocs.iterator(); while (it.next()) |entry| { @@ -2376,7 +2380,14 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven }; const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC); if (debug_buffers) |dbg| { - try self.dwarf.?.commitDeclDebugInfo(&self.base, module, decl, local_sym.st_value, local_sym.st_size, dbg); + try self.dwarf.?.commitDeclDebugInfo( + &self.base, + module, + decl, + local_sym.st_value, + local_sym.st_size, + dbg, + ); } // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. |
