From a1040a105ab4dcab9e23d218d2cfa4e58f56be37 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 25 Mar 2022 13:55:12 +0100 Subject: [PATCH 1/7] dwarf: add debug info for error sets --- src/codegen.zig | 2 -- src/link/Dwarf.zig | 28 ++++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index cc7014a136..ca24f998e8 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -573,7 +573,6 @@ pub fn generateSymbol( return Result{ .appended = {} }; }, .Union => { - // TODO generate debug info for unions const union_obj = typed_value.val.castTag(.@"union").?.data; const layout = typed_value.ty.unionGetLayout(target); @@ -749,7 +748,6 @@ pub fn generateSymbol( return Result{ .appended = {} }; }, .ErrorSet => { - // TODO generate debug info for error sets switch (typed_value.val.tag()) { .@"error" => { const name = typed_value.val.getError().?; diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 370aac2734..353b40e201 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -468,6 +468,7 @@ pub fn commitDeclDebugInfo( value_ptr.off = @intCast(u32, dbg_info_buffer.items.len); try self.addDbgInfoType( dbg_type_arena.allocator(), + module, ty, dbg_info_buffer, dbg_info_type_relocs, @@ -771,6 +772,7 @@ pub fn freeDecl(self: *Dwarf, decl: *Module.Decl) void { fn addDbgInfoType( self: *Dwarf, arena: Allocator, + module: *Module, ty: Type, dbg_info_buffer: *std.ArrayList(u8), dbg_info_type_relocs: *File.DbgInfoTypeRelocsTable, @@ -1095,6 +1097,32 @@ fn addDbgInfoType( try dbg_info_buffer.append(0); } }, + .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}); + + const error_names = if (ty.isAnyError()) module.error_name_list.items else 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); + }, else => { log.debug("TODO implement .debug_info for type '{}'", .{ty.fmtDebug()}); try dbg_info_buffer.append(abbrev_pad1); From 4985abcc494cc545e34b164fe12de592cf1b50f4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 25 Mar 2022 15:50:42 +0100 Subject: [PATCH 2/7] dwarf: add debug info for error unions --- src/codegen.zig | 1 - src/link/Dwarf.zig | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/codegen.zig b/src/codegen.zig index ca24f998e8..8924f13e17 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -694,7 +694,6 @@ pub fn generateSymbol( return Result{ .appended = {} }; }, .ErrorUnion => { - // TODO generate debug info for error unions const error_ty = typed_value.ty.errorUnionSet(); const payload_ty = typed_value.ty.errorUnionPayload(); const is_payload = typed_value.val.errorUnionIsPayload(); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 353b40e201..639ef31be4 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -1107,6 +1107,16 @@ fn addDbgInfoType( 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 = if (ty.isAnyError()) module.error_name_list.items else ty.errorSetNames(); for (error_names) |error_name| { const kv = module.getErrorValue(error_name) catch unreachable; @@ -1123,6 +1133,50 @@ fn addDbgInfoType( // DW.AT.enumeration_type delimit children try dbg_info_buffer.append(0); }, + .ErrorUnion => { + const error_ty = ty.errorUnionSet(); + const payload_ty = ty.errorUnionPayload(); + const abi_size = ty.abiSize(target); + const abi_align = ty.abiAlignment(target); + const payload_off = mem.alignForwardGeneric(u64, error_ty.abiSize(target), abi_align); + + // DW.AT.structure_type + try dbg_info_buffer.append(abbrev_struct_type); + // DW.AT.byte_size, DW.FORM.sdata + 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.member + try dbg_info_buffer.ensureUnusedCapacity(7); + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity("value"); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + var index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try relocs.append(.{ .ty = payload_ty, .reloc = @intCast(u32, index) }); + // DW.AT.data_member_location, DW.FORM.sdata + try leb128.writeULEB128(dbg_info_buffer.writer(), payload_off); + + // DW.AT.member + try dbg_info_buffer.ensureUnusedCapacity(5); + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity("err"); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try relocs.append(.{ .ty = error_ty, .reloc = @intCast(u32, index) }); + // DW.AT.data_member_location, DW.FORM.sdata + try dbg_info_buffer.append(0); + + // DW.AT.structure_type delimit children + try dbg_info_buffer.append(0); + }, else => { log.debug("TODO implement .debug_info for type '{}'", .{ty.fmtDebug()}); try dbg_info_buffer.append(abbrev_pad1); From e444e69dc4f9e6a1f25f6081bd0cd4d45f4c9c93 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 25 Mar 2022 17:31:22 +0100 Subject: [PATCH 3/7] dwarf: do not use `Type.errorSetNames()` for inferred error sets Otherwise, we risk tripping an assertion in `Type.errorSetNames()` in case the inferred error set was not yet fully resolved. This so far only surfaced when Dwarf triggers recursive analysis of an error set as part of emitting debug info for an error union. --- src/link/Dwarf.zig | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 639ef31be4..f00b1f6479 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -1117,7 +1117,18 @@ fn addDbgInfoType( // DW.AT.const_value, DW.FORM.data8 mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian); - const error_names = if (ty.isAnyError()) module.error_name_list.items else ty.errorSetNames(); + 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 From b4815b31310a36e3c1fabd83d010b44b693c9782 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 26 Mar 2022 12:04:18 +0100 Subject: [PATCH 4/7] dwarf: draft poc of deferred resolution of error sets debug info --- src/link/Dwarf.zig | 212 ++++++++++++++++++++++++++++++++++----------- 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. From 1a80315836f77e38eee3e4c0a646b82febbc3604 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 26 Mar 2022 14:20:08 +0100 Subject: [PATCH 5/7] dwarf: rename DebugInfoAtom into Atom; free all allocated memory --- src/link/Dwarf.zig | 65 +++++++++++++++++++++++------------------ src/link/Elf.zig | 2 +- src/link/MachO/Atom.zig | 2 +- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 5e46811900..b2848c8e1e 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -31,10 +31,11 @@ dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{}, dbg_line_fn_first: ?*SrcFn = null, dbg_line_fn_last: ?*SrcFn = null, -/// A list of `TextBlock` whose corresponding .debug_info tags have surplus capacity. /// This is the same concept as `text_block_free_list`; see those doc comments. -dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*DebugInfoAtom, void) = .{}, -dbg_info_decl_first: ?*DebugInfoAtom = null, -dbg_info_decl_last: ?*DebugInfoAtom = null, +/// A list of `Atom`s whose corresponding .debug_info tags have surplus capacity. +/// This is the same concept as `text_block_free_list`; see those doc comments. +atom_free_list: std.AutoHashMapUnmanaged(*Atom, void) = .{}, +atom_first: ?*Atom = null, +atom_last: ?*Atom = null, abbrev_table_offset: ?u64 = null, @@ -43,11 +44,16 @@ strtab: std.ArrayListUnmanaged(u8) = .{}, deferred_error_sets_relocs: std.ArrayListUnmanaged(u32) = .{}, -pub const DebugInfoAtom = struct { +/// List of atoms that are owned directly by the DWARF module. +/// TODO convert links in DebugInfoAtom into indices and make +/// sure every atom is owned by this module. +managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, + +pub const Atom = struct { /// Previous/next linked list pointers. /// This is the linked list node for this Decl's corresponding .debug_info tag. - prev: ?*DebugInfoAtom, - next: ?*DebugInfoAtom, + prev: ?*Atom, + next: ?*Atom, /// Offset into .debug_info pointing to the tag for this Decl. off: u32, /// Size of the .debug_info tag for this Decl, not including padding. @@ -119,9 +125,14 @@ pub fn init(allocator: Allocator, tag: File.Tag, target: std.Target) Dwarf { pub fn deinit(self: *Dwarf) void { const gpa = self.allocator; self.dbg_line_fn_free_list.deinit(gpa); - self.dbg_info_decl_free_list.deinit(gpa); + self.atom_free_list.deinit(gpa); self.strtab.deinit(gpa); self.deferred_error_sets_relocs.deinit(gpa); + + for (self.managed_atoms.items) |atom| { + gpa.destroy(atom); + } + self.managed_atoms.deinit(gpa); } pub const DeclDebugBuffers = struct { @@ -568,10 +579,7 @@ pub fn commitErrorSetDebugInfo(self: *Dwarf, file: *File, module: *Module) !void 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); + const atom = try gpa.create(Atom); errdefer gpa.destroy(atom); atom.* = .{ .prev = null, @@ -579,6 +587,7 @@ pub fn commitErrorSetDebugInfo(self: *Dwarf, file: *File, module: *Module) !void .off = 0, .len = 0, }; + try self.managed_atoms.append(gpa, atom); try self.updateDeclDebugInfoAllocation(file, atom, @intCast(u32, dbg_info_buffer.items.len)); try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); @@ -620,7 +629,7 @@ pub fn commitErrorSetDebugInfo(self: *Dwarf, file: *File, module: *Module) !void } } -fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *DebugInfoAtom, len: u32) !void { +fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u32) !void { const tracy = trace(@src()); defer tracy.end(); @@ -630,14 +639,14 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *DebugInfoAtom const gpa = self.allocator; atom.len = len; - if (self.dbg_info_decl_last) |last| blk: { + if (self.atom_last) |last| blk: { if (atom == last) break :blk; if (atom.next) |next| { // Update existing Decl - non-last item. if (atom.off + atom.len + min_nop_size > next.off) { // It grew too big, so we move it to a new location. if (atom.prev) |prev| { - self.dbg_info_decl_free_list.put(gpa, prev, {}) catch {}; + self.atom_free_list.put(gpa, prev, {}) catch {}; prev.next = atom.next; } next.prev = atom.prev; @@ -663,7 +672,7 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *DebugInfoAtom // TODO Look at the free list before appending at the end. atom.prev = last; last.next = atom; - self.dbg_info_decl_last = atom; + self.atom_last = atom; atom.off = last.off + padToIdeal(last.len); } @@ -672,20 +681,20 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *DebugInfoAtom // TODO Look at the free list before appending at the end. atom.prev = last; last.next = atom; - self.dbg_info_decl_last = atom; + self.atom_last = atom; atom.off = last.off + padToIdeal(last.len); } } else { // This is the first Decl of the .debug_info - self.dbg_info_decl_first = atom; - self.dbg_info_decl_last = atom; + self.atom_first = atom; + self.atom_last = atom; atom.off = @intCast(u32, padToIdeal(self.dbgInfoHeaderBytes())); } } -fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *DebugInfoAtom, dbg_info_buf: []const u8) !void { +fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []const u8) !void { const tracy = trace(@src()); defer tracy.end(); @@ -694,7 +703,7 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *DebugInfoAtom, dbg_info_ // probably need to edit that logic too. const gpa = self.allocator; - const last_decl = self.dbg_info_decl_last.?; + const last_decl = self.atom_last.?; // +1 for a trailing zero to end the children of the decl tag. const needed_size = last_decl.off + last_decl.len + 1; const prev_padding_size: u32 = if (atom.prev) |prev| atom.off - (prev.off + prev.len) else 0; @@ -819,13 +828,13 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl) } } -pub fn freeAtom(self: *Dwarf, atom: *DebugInfoAtom) void { - if (self.dbg_info_decl_first == atom) { - self.dbg_info_decl_first = atom.next; +pub fn freeAtom(self: *Dwarf, atom: *Atom) void { + if (self.atom_first == atom) { + self.atom_first = atom.next; } - if (self.dbg_info_decl_last == atom) { + if (self.atom_last == atom) { // TODO shrink the .debug_info section size here - self.dbg_info_decl_last = atom.prev; + self.atom_last = atom.prev; } if (atom.prev) |prev| { @@ -1964,12 +1973,12 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void { } fn getDebugInfoOff(self: Dwarf) ?u32 { - const first = self.dbg_info_decl_first orelse return null; + const first = self.atom_first orelse return null; return first.off; } fn getDebugInfoEnd(self: Dwarf) ?u32 { - const last = self.dbg_info_decl_last orelse return null; + const last = self.atom_last orelse return null; return last.off + last.len; } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index c81c15f597..676952ea24 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -207,7 +207,7 @@ pub const TextBlock = struct { prev: ?*TextBlock, next: ?*TextBlock, - dbg_info_atom: Dwarf.DebugInfoAtom, + dbg_info_atom: Dwarf.Atom, pub const empty = TextBlock{ .local_sym_index = 0, diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index 4e58946735..dd2dc3c1f1 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -72,7 +72,7 @@ stab: ?Stab = null, next: ?*Atom, prev: ?*Atom, -dbg_info_atom: Dwarf.DebugInfoAtom, +dbg_info_atom: Dwarf.Atom, dirty: bool = true, From 4ca9b4c44a4fbe2b64b11d2c8a951c2c3e961619 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 26 Mar 2022 14:33:31 +0100 Subject: [PATCH 6/7] dwarf: move DbgInfoTypeRelocsTable into Dwarf module --- src/codegen.zig | 31 +++++++++++++++---------------- src/link.zig | 44 +++++++++++++------------------------------- src/link/Dwarf.zig | 22 +++++++++++++++++++--- src/link/Elf.zig | 2 +- 4 files changed, 48 insertions(+), 51 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index 8924f13e17..f4803c77ea 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1,26 +1,25 @@ const std = @import("std"); +const build_options = @import("build_options"); const builtin = @import("builtin"); +const assert = std.debug.assert; +const leb128 = std.leb; +const link = @import("link.zig"); +const log = std.log.scoped(.codegen); const mem = std.mem; const math = std.math; -const assert = std.debug.assert; +const trace = @import("tracy.zig").trace; + const Air = @import("Air.zig"); -const Zir = @import("Zir.zig"); -const Liveness = @import("Liveness.zig"); -const Type = @import("type.zig").Type; -const Value = @import("value.zig").Value; -const TypedValue = @import("TypedValue.zig"); -const link = @import("link.zig"); -const Module = @import("Module.zig"); +const Allocator = mem.Allocator; const Compilation = @import("Compilation.zig"); const ErrorMsg = Module.ErrorMsg; +const Liveness = @import("Liveness.zig"); +const Module = @import("Module.zig"); const Target = std.Target; -const Allocator = mem.Allocator; -const trace = @import("tracy.zig").trace; -const DW = std.dwarf; -const leb128 = std.leb; -const log = std.log.scoped(.codegen); -const build_options = @import("build_options"); -const RegisterManager = @import("register_manager.zig").RegisterManager; +const Type = @import("type.zig").Type; +const TypedValue = @import("TypedValue.zig"); +const Value = @import("value.zig").Value; +const Zir = @import("Zir.zig"); pub const FnResult = union(enum) { /// The `code` parameter passed to `generateSymbol` has the value appended. @@ -46,7 +45,7 @@ pub const DebugInfoOutput = union(enum) { dwarf: struct { dbg_line: *std.ArrayList(u8), dbg_info: *std.ArrayList(u8), - dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable, + dbg_info_type_relocs: *link.File.Dwarf.DbgInfoTypeRelocsTable, }, /// the plan9 debuginfo output is a bytecode with 4 opcodes /// assume all numbers/variables are bytes diff --git a/src/link.zig b/src/link.zig index 0fd6797153..7c135a7405 100644 --- a/src/link.zig +++ b/src/link.zig @@ -1,22 +1,22 @@ const std = @import("std"); +const build_options = @import("build_options"); const builtin = @import("builtin"); -const mem = std.mem; -const Allocator = std.mem.Allocator; -const fs = std.fs; -const log = std.log.scoped(.link); const assert = std.debug.assert; - -const Compilation = @import("Compilation.zig"); -const Module = @import("Module.zig"); +const fs = std.fs; +const mem = std.mem; +const log = std.log.scoped(.link); const trace = @import("tracy.zig").trace; +const wasi_libc = @import("wasi_libc.zig"); + +const Air = @import("Air.zig"); +const Allocator = std.mem.Allocator; +const Cache = @import("Cache.zig"); +const Compilation = @import("Compilation.zig"); +const LibCInstallation = @import("libc_installation.zig").LibCInstallation; +const Liveness = @import("Liveness.zig"); +const Module = @import("Module.zig"); const Package = @import("Package.zig"); const Type = @import("type.zig").Type; -const Cache = @import("Cache.zig"); -const build_options = @import("build_options"); -const LibCInstallation = @import("libc_installation.zig").LibCInstallation; -const wasi_libc = @import("wasi_libc.zig"); -const Air = @import("Air.zig"); -const Liveness = @import("Liveness.zig"); const TypedValue = @import("TypedValue.zig"); pub const SystemLib = struct { @@ -245,24 +245,6 @@ pub const File = struct { nvptx: void, }; - /// For DWARF .debug_info. - pub const DbgInfoTypeRelocsTable = std.ArrayHashMapUnmanaged( - Type, - DbgInfoTypeReloc, - Type.HashContext32, - true, - ); - - /// For DWARF .debug_info. - pub const DbgInfoTypeReloc = struct { - /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). - /// This is where the .debug_info tag for the type is. - off: u32, - /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). - /// List of DW.AT.type / DW.FORM.ref4 that points to the type. - relocs: std.ArrayListUnmanaged(u32), - }; - /// Attempts incremental linking, if the file already exists. If /// incremental linking fails, falls back to truncating the file and /// rewriting it. A malicious file is detected as incremental link failure diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index b2848c8e1e..199bf9df61 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -82,6 +82,22 @@ pub const SrcFn = struct { pub const PtrWidth = enum { p32, p64 }; +pub const DbgInfoTypeRelocsTable = std.ArrayHashMapUnmanaged( + Type, + DbgInfoTypeReloc, + Type.HashContext32, + true, +); + +pub const DbgInfoTypeReloc = struct { + /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). + /// This is where the .debug_info tag for the type is. + off: u32, + /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). + /// List of DW.AT.type / DW.FORM.ref4 that points to the type. + relocs: std.ArrayListUnmanaged(u32), +}; + pub const abbrev_compile_unit = 1; pub const abbrev_subprogram = 2; pub const abbrev_subprogram_retvoid = 3; @@ -138,7 +154,7 @@ pub fn deinit(self: *Dwarf) void { pub const DeclDebugBuffers = struct { dbg_line_buffer: std.ArrayList(u8), dbg_info_buffer: std.ArrayList(u8), - dbg_info_type_relocs: File.DbgInfoTypeRelocsTable, + dbg_info_type_relocs: DbgInfoTypeRelocsTable, }; pub fn initDeclDebugInfo(self: *Dwarf, decl: *Module.Decl) !DeclDebugBuffers { @@ -153,7 +169,7 @@ pub fn initDeclDebugInfo(self: *Dwarf, decl: *Module.Decl) !DeclDebugBuffers { const gpa = self.allocator; var dbg_line_buffer = std.ArrayList(u8).init(gpa); var dbg_info_buffer = std.ArrayList(u8).init(gpa); - var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{}; + var dbg_info_type_relocs: DbgInfoTypeRelocsTable = .{}; assert(decl.has_tv); @@ -890,7 +906,7 @@ fn addDbgInfoType( module: *Module, ty: Type, dbg_info_buffer: *std.ArrayList(u8), - dbg_info_type_relocs: *File.DbgInfoTypeRelocsTable, + dbg_info_type_relocs: *DbgInfoTypeRelocsTable, nested_ref4_relocs: *std.ArrayList(u32), ) error{OutOfMemory}!void { const target = self.target; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 676952ea24..dda2841db8 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2232,7 +2232,7 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { } } -fn deinitRelocs(gpa: Allocator, table: *File.DbgInfoTypeRelocsTable) void { +fn deinitRelocs(gpa: Allocator, table: *link.File.Dwarf.DbgInfoTypeRelocsTable) void { for (table.values()) |*value| { value.relocs.deinit(gpa); } From 366ec2105249050fe88e5fa9f241e14f708db891 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 27 Mar 2022 17:04:20 +0200 Subject: [PATCH 7/7] dwarf: track type relocation state in Dwarf module --- src/arch/aarch64/Emit.zig | 23 +- src/arch/arm/Emit.zig | 73 ++--- src/arch/riscv64/CodeGen.zig | 37 +-- src/arch/riscv64/Emit.zig | 23 +- src/arch/x86_64/CodeGen.zig | 1 + src/arch/x86_64/Emit.zig | 81 ++--- src/codegen.zig | 6 +- src/link/Dwarf.zig | 546 ++++++++++++++++---------------- src/link/Elf.zig | 68 +--- src/link/MachO.zig | 72 ++--- src/link/MachO/DebugSymbols.zig | 22 -- 11 files changed, 435 insertions(+), 517 deletions(-) diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 3eb25dda7b..2a4e6cb985 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -386,18 +386,19 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line); const delta_pc: usize = self.code.items.len - self.prev_di_pc; switch (self.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { // TODO Look into using the DWARF special opcodes to compress this data. // It lets you emit single-byte opcodes that add different numbers to // both the PC and the line number at the same time. - try dbg_out.dbg_line.ensureUnusedCapacity(11); - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); - leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable; + const dbg_line = dw.getDeclDebugLineBuffer(); + try dbg_line.ensureUnusedCapacity(11); + dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); + leb128.writeULEB128(dbg_line.writer(), delta_pc) catch unreachable; if (delta_line != 0) { - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line); - leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; + dbg_line.appendAssumeCapacity(DW.LNS.advance_line); + leb128.writeILEB128(dbg_line.writer(), delta_line) catch unreachable; } - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); + dbg_line.appendAssumeCapacity(DW.LNS.copy); self.prev_di_pc = self.code.items.len; self.prev_di_line = line; self.prev_di_column = column; @@ -586,8 +587,8 @@ fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirDebugPrologueEnd(self: *Emit) !void { switch (self.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); + .dwarf => |dw| { + try dw.getDeclDebugLineBuffer().append(DW.LNS.set_prologue_end); try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); }, .plan9 => {}, @@ -597,8 +598,8 @@ fn mirDebugPrologueEnd(self: *Emit) !void { fn mirDebugEpilogueBegin(self: *Emit) !void { switch (self.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); + .dwarf => |dw| { + try dw.getDeclDebugLineBuffer().append(DW.LNS.set_epilogue_begin); try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); }, .plan9 => {}, diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index 1e03c4649b..62705651a6 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -328,18 +328,19 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line); const delta_pc: usize = self.code.items.len - self.prev_di_pc; switch (self.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { // TODO Look into using the DWARF special opcodes to compress this data. // It lets you emit single-byte opcodes that add different numbers to // both the PC and the line number at the same time. - try dbg_out.dbg_line.ensureUnusedCapacity(11); - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); - leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable; + const dbg_line = dw.getDeclDebugLineBuffer(); + try dbg_line.ensureUnusedCapacity(11); + dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); + leb128.writeULEB128(dbg_line.writer(), delta_pc) catch unreachable; if (delta_line != 0) { - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line); - leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; + dbg_line.appendAssumeCapacity(DW.LNS.advance_line); + leb128.writeILEB128(dbg_line.writer(), delta_line) catch unreachable; } - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); + dbg_line.appendAssumeCapacity(DW.LNS.copy); self.prev_di_pc = self.code.items.len; self.prev_di_line = line; self.prev_di_column = column; @@ -379,19 +380,17 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { /// after codegen for this symbol is done. fn addDbgInfoTypeReloc(self: *Emit, ty: Type) !void { switch (self.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { assert(ty.hasRuntimeBits()); - const index = dbg_out.dbg_info.items.len; - try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 - - const gop = try dbg_out.dbg_info_type_relocs.getOrPutContext(self.bin_file.allocator, ty, .{ .target = self.target.* }); - if (!gop.found_existing) { - gop.value_ptr.* = .{ - .off = undefined, - .relocs = .{}, - }; - } - try gop.value_ptr.relocs.append(self.bin_file.allocator, @intCast(u32, index)); + const dbg_info = dw.getDeclDebugInfoBuffer(); + const index = dbg_info.items.len; + try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 + const atom = switch (self.bin_file.tag) { + .elf => &self.function.mod_fn.owner_decl.link.elf.dbg_info_atom, + .macho => unreachable, + else => unreachable, + }; + try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); }, .plan9 => {}, .none => {}, @@ -409,16 +408,17 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { switch (mcv) { .register => |reg| { switch (self.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_info.ensureUnusedCapacity(3); - dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); - dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + .dwarf => |dw| { + const dbg_info = dw.getDeclDebugInfoBuffer(); + try dbg_info.ensureUnusedCapacity(3); + dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, // ULEB128 dwarf expression length reg.dwarfLocOp(), }); - try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string }, .plan9 => {}, .none => {}, @@ -428,7 +428,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { .stack_argument_offset, => { switch (self.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch { return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(target)}); }; @@ -442,7 +442,8 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { else => unreachable, }; - try dbg_out.dbg_info.append(link.File.Dwarf.abbrev_parameter); + const dbg_info = dw.getDeclDebugInfoBuffer(); + try dbg_info.append(link.File.Dwarf.abbrev_parameter); // Get length of the LEB128 stack offset var counting_writer = std.io.countingWriter(std.io.null_writer); @@ -450,13 +451,13 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { // DW.AT.location, DW.FORM.exprloc // ULEB128 dwarf expression length - try leb128.writeULEB128(dbg_out.dbg_info.writer(), counting_writer.bytes_written + 1); - try dbg_out.dbg_info.append(DW.OP.breg11); - try leb128.writeILEB128(dbg_out.dbg_info.writer(), adjusted_stack_offset); + try leb128.writeULEB128(dbg_info.writer(), counting_writer.bytes_written + 1); + try dbg_info.append(DW.OP.breg11); + try leb128.writeILEB128(dbg_info.writer(), adjusted_stack_offset); - try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string }, .plan9 => {}, .none => {}, @@ -558,8 +559,8 @@ fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirDebugPrologueEnd(emit: *Emit) !void { switch (emit.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); + .dwarf => |dw| { + try dw.getDeclDebugLineBuffer().append(DW.LNS.set_prologue_end); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, .plan9 => {}, @@ -569,8 +570,8 @@ fn mirDebugPrologueEnd(emit: *Emit) !void { fn mirDebugEpilogueBegin(emit: *Emit) !void { switch (emit.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); + .dwarf => |dw| { + try dw.getDeclDebugLineBuffer().append(DW.LNS.set_epilogue_begin); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, .plan9 => {}, diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 8b697677c1..fa243819cf 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -745,21 +745,17 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { /// after codegen for this symbol is done. fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { switch (self.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { assert(ty.hasRuntimeBits()); - const index = dbg_out.dbg_info.items.len; - try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 - - const gop = try dbg_out.dbg_info_type_relocs.getOrPutContext(self.gpa, ty, .{ - .target = self.target.*, - }); - if (!gop.found_existing) { - gop.value_ptr.* = .{ - .off = undefined, - .relocs = .{}, - }; - } - try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index)); + const dbg_info = dw.getDeclDebugInfoBuffer(); + const index = dbg_info.items.len; + try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 + const atom = switch (self.bin_file.tag) { + .elf => &self.mod_fn.owner_decl.link.elf.dbg_info_atom, + .macho => unreachable, + else => unreachable, + }; + try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); }, .plan9 => {}, .none => {}, @@ -1573,16 +1569,17 @@ fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32 switch (mcv) { .register => |reg| { switch (self.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_info.ensureUnusedCapacity(3); - dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); - dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + .dwarf => |dw| { + const dbg_info = dw.getDeclDebugInfoBuffer(); + try dbg_info.ensureUnusedCapacity(3); + dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, // ULEB128 dwarf expression length reg.dwarfLocOp(), }); - try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string }, .plan9 => {}, .none => {}, diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index c34d2f4ff4..bfa4e00d00 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -89,18 +89,19 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line); const delta_pc: usize = self.code.items.len - self.prev_di_pc; switch (self.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { // TODO Look into using the DWARF special opcodes to compress this data. // It lets you emit single-byte opcodes that add different numbers to // both the PC and the line number at the same time. - try dbg_out.dbg_line.ensureUnusedCapacity(11); - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); - leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable; + const dbg_line = dw.getDeclDebugLineBuffer(); + try dbg_line.ensureUnusedCapacity(11); + dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); + leb128.writeULEB128(dbg_line.writer(), delta_pc) catch unreachable; if (delta_line != 0) { - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line); - leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; + dbg_line.appendAssumeCapacity(DW.LNS.advance_line); + leb128.writeILEB128(dbg_line.writer(), delta_line) catch unreachable; } - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); + dbg_line.appendAssumeCapacity(DW.LNS.copy); self.prev_di_pc = self.code.items.len; self.prev_di_line = line; self.prev_di_column = column; @@ -182,8 +183,8 @@ fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirDebugPrologueEnd(self: *Emit) !void { switch (self.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); + .dwarf => |dw| { + try dw.getDeclDebugLineBuffer().append(DW.LNS.set_prologue_end); try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); }, .plan9 => {}, @@ -193,8 +194,8 @@ fn mirDebugPrologueEnd(self: *Emit) !void { fn mirDebugEpilogueBegin(self: *Emit) !void { switch (self.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); + .dwarf => |dw| { + try dw.getDeclDebugLineBuffer().append(DW.LNS.set_epilogue_begin); try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); }, .plan9 => {}, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5b252b6c0a..c95e86a424 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -350,6 +350,7 @@ pub fn generate( var emit = Emit{ .mir = mir, .bin_file = bin_file, + .function = &function, .debug_output = debug_output, .target = &bin_file.options.target, .src_loc = src_loc, diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 3ce77ffb73..4616d3ff3a 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -16,6 +16,7 @@ const testing = std.testing; const Air = @import("../../Air.zig"); const Allocator = mem.Allocator; +const CodeGen = @import("CodeGen.zig"); const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; const DW = std.dwarf; const Encoder = bits.Encoder; @@ -29,6 +30,7 @@ const Type = @import("../../type.zig").Type; mir: Mir, bin_file: *link.File, +function: *const CodeGen, debug_output: DebugInfoOutput, target: *const std.Target, err_msg: ?*ErrorMsg = null, @@ -963,18 +965,19 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void { const delta_pc: usize = emit.code.items.len - emit.prev_di_pc; log.debug(" (advance pc={d} and line={d})", .{ delta_line, delta_pc }); switch (emit.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { // TODO Look into using the DWARF special opcodes to compress this data. // It lets you emit single-byte opcodes that add different numbers to // both the PC and the line number at the same time. - try dbg_out.dbg_line.ensureUnusedCapacity(11); - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); - leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable; + const dbg_line = dw.getDeclDebugLineBuffer(); + try dbg_line.ensureUnusedCapacity(11); + dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); + leb128.writeULEB128(dbg_line.writer(), delta_pc) catch unreachable; if (delta_line != 0) { - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line); - leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; + dbg_line.appendAssumeCapacity(DW.LNS.advance_line); + leb128.writeILEB128(dbg_line.writer(), delta_line) catch unreachable; } - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); + dbg_line.appendAssumeCapacity(DW.LNS.copy); emit.prev_di_line = line; emit.prev_di_column = column; emit.prev_di_pc = emit.code.items.len; @@ -1022,8 +1025,8 @@ fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .dbg_prologue_end); switch (emit.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); + .dwarf => |dw| { + try dw.getDeclDebugLineBuffer().append(DW.LNS.set_prologue_end); log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{ emit.prev_di_line, emit.prev_di_column }); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, @@ -1036,8 +1039,8 @@ fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .dbg_epilogue_begin); switch (emit.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); + .dwarf => |dw| { + try dw.getDeclDebugLineBuffer().append(DW.LNS.set_epilogue_begin); log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{ emit.prev_di_line, emit.prev_di_column }); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, @@ -1063,16 +1066,17 @@ fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32 switch (mcv) { .register => |reg| { switch (emit.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_info.ensureUnusedCapacity(3); - dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); - dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + .dwarf => |dw| { + const dbg_info = dw.getDeclDebugInfoBuffer(); + try dbg_info.ensureUnusedCapacity(3); + dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, // ULEB128 dwarf expression length reg.dwarfLocOp(), }); - try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); try emit.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string }, .plan9 => {}, .none => {}, @@ -1080,25 +1084,26 @@ fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32 }, .stack_offset => |off| { switch (emit.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { // we add here +16 like we do in airArg in CodeGen since we refer directly to // rbp as the start of function frame minus 8 bytes for caller's rbp preserved in the // prologue, and 8 bytes for return address. // TODO we need to make this more generic if we don't use rbp as the frame pointer // for example when -fomit-frame-pointer is set. const disp = @intCast(i32, max_stack) - off + 16; - try dbg_out.dbg_info.ensureUnusedCapacity(8); - dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); - const fixup = dbg_out.dbg_info.items.len; - dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + const dbg_info = dw.getDeclDebugInfoBuffer(); + try dbg_info.ensureUnusedCapacity(8); + dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); + const fixup = dbg_info.items.len; + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, // we will backpatch it after we encode the displacement in LEB128 DW.OP.breg6, // .rbp TODO handle -fomit-frame-pointer }); - leb128.writeILEB128(dbg_out.dbg_info.writer(), disp) catch unreachable; - dbg_out.dbg_info.items[fixup] += @intCast(u8, dbg_out.dbg_info.items.len - fixup - 2); - try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + leb128.writeILEB128(dbg_info.writer(), disp) catch unreachable; + dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); try emit.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string }, .plan9 => {}, @@ -1113,21 +1118,17 @@ fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32 /// after codegen for this symbol is done. fn addDbgInfoTypeReloc(emit: *Emit, ty: Type) !void { switch (emit.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { assert(ty.hasRuntimeBits()); - const index = dbg_out.dbg_info.items.len; - try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 - - const gop = try dbg_out.dbg_info_type_relocs.getOrPutContext(emit.bin_file.allocator, ty, .{ - .target = emit.target.*, - }); - if (!gop.found_existing) { - gop.value_ptr.* = .{ - .off = undefined, - .relocs = .{}, - }; - } - try gop.value_ptr.relocs.append(emit.bin_file.allocator, @intCast(u32, index)); + const dbg_info = dw.getDeclDebugInfoBuffer(); + const index = dbg_info.items.len; + try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 + const atom = switch (emit.bin_file.tag) { + .elf => &emit.function.mod_fn.owner_decl.link.elf.dbg_info_atom, + .macho => &emit.function.mod_fn.owner_decl.link.macho.dbg_info_atom, + else => unreachable, + }; + try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); }, .plan9 => {}, .none => {}, diff --git a/src/codegen.zig b/src/codegen.zig index f4803c77ea..fa84ad0d52 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -42,11 +42,7 @@ pub const GenerateSymbolError = error{ }; pub const DebugInfoOutput = union(enum) { - dwarf: struct { - dbg_line: *std.ArrayList(u8), - dbg_info: *std.ArrayList(u8), - dbg_info_type_relocs: *link.File.Dwarf.DbgInfoTypeRelocsTable, - }, + dwarf: *link.File.Dwarf, /// the plan9 debuginfo output is a bytecode with 4 opcodes /// assume all numbers/variables are bytes /// 0 w x y z -> interpret w x y z as a big-endian i32, and add it to the line offset diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 199bf9df61..83222090c0 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -39,16 +39,22 @@ atom_last: ?*Atom = null, abbrev_table_offset: ?u64 = null, +/// TODO replace with InternArena /// Table of debug symbol names. strtab: std.ArrayListUnmanaged(u8) = .{}, -deferred_error_sets_relocs: std.ArrayListUnmanaged(u32) = .{}, +/// Lives only as long as the analysed Decl. +/// Allocated with `initDeclState`. +/// Freed with `commitDeclState`. +decl_state: ?DeclState = null, /// List of atoms that are owned directly by the DWARF module. /// TODO convert links in DebugInfoAtom into indices and make /// sure every atom is owned by this module. managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, +global_abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, + pub const Atom = struct { /// Previous/next linked list pointers. /// This is the linked list node for this Decl's corresponding .debug_info tag. @@ -60,6 +66,54 @@ pub const Atom = struct { len: u32, }; +/// Represents state of the analysed Decl. +/// Includes Decl's abbrev table of type Types, matching arena +/// and a set of relocations that will be resolved once this +/// Decl's inner Atom is assigned an offset within the DWARF section. +pub const DeclState = struct { + dbg_line: std.ArrayList(u8), + dbg_info: std.ArrayList(u8), + abbrev_type_arena: std.heap.ArenaAllocator, + abbrev_table: std.ArrayListUnmanaged(AbbrevEntry) = .{}, + abbrev_resolver: std.HashMapUnmanaged( + Type, + u32, + Type.HashContext64, + std.hash_map.default_max_load_percentage, + ) = .{}, + abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, + + fn init(gpa: Allocator) DeclState { + return .{ + .dbg_line = std.ArrayList(u8).init(gpa), + .dbg_info = std.ArrayList(u8).init(gpa), + .abbrev_type_arena = std.heap.ArenaAllocator.init(gpa), + }; + } + + fn deinit(self: *DeclState, gpa: Allocator) void { + self.dbg_line.deinit(); + self.dbg_info.deinit(); + self.abbrev_type_arena.deinit(); + self.abbrev_table.deinit(gpa); + self.abbrev_resolver.deinit(gpa); + self.abbrev_relocs.deinit(gpa); + } +}; + +pub const AbbrevEntry = struct { + atom: *const Atom, + @"type": Type, + offset: u32, +}; + +pub const AbbrevRelocation = struct { + target: u32, + atom: *const Atom, + offset: u32, + addend: u32, +}; + pub const SrcFn = struct { /// Offset from the beginning of the Debug Line Program header that contains this function. off: u32, @@ -82,22 +136,6 @@ pub const SrcFn = struct { pub const PtrWidth = enum { p32, p64 }; -pub const DbgInfoTypeRelocsTable = std.ArrayHashMapUnmanaged( - Type, - DbgInfoTypeReloc, - Type.HashContext32, - true, -); - -pub const DbgInfoTypeReloc = struct { - /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). - /// This is where the .debug_info tag for the type is. - off: u32, - /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). - /// List of DW.AT.type / DW.FORM.ref4 that points to the type. - relocs: std.ArrayListUnmanaged(u32), -}; - pub const abbrev_compile_unit = 1; pub const abbrev_subprogram = 2; pub const abbrev_subprogram_retvoid = 3; @@ -143,7 +181,7 @@ pub fn deinit(self: *Dwarf) void { self.dbg_line_fn_free_list.deinit(gpa); self.atom_free_list.deinit(gpa); self.strtab.deinit(gpa); - self.deferred_error_sets_relocs.deinit(gpa); + self.global_abbrev_relocs.deinit(gpa); for (self.managed_atoms.items) |atom| { gpa.destroy(atom); @@ -151,25 +189,22 @@ pub fn deinit(self: *Dwarf) void { self.managed_atoms.deinit(gpa); } -pub const DeclDebugBuffers = struct { - dbg_line_buffer: std.ArrayList(u8), - dbg_info_buffer: std.ArrayList(u8), - dbg_info_type_relocs: DbgInfoTypeRelocsTable, -}; - -pub fn initDeclDebugInfo(self: *Dwarf, decl: *Module.Decl) !DeclDebugBuffers { +/// Initializes Decl's state and its matching output buffers. +/// Call this before `commitDeclState`. +pub fn initDeclState(self: *Dwarf, decl: *Module.Decl) !void { const tracy = trace(@src()); defer tracy.end(); const decl_name = try decl.getFullyQualifiedName(self.allocator); defer self.allocator.free(decl_name); - log.debug("initDeclDebugInfo {s}{*}", .{ decl_name, decl }); + log.debug("initDeclState {s}{*}", .{ decl_name, decl }); const gpa = self.allocator; - var dbg_line_buffer = std.ArrayList(u8).init(gpa); - var dbg_info_buffer = std.ArrayList(u8).init(gpa); - var dbg_info_type_relocs: DbgInfoTypeRelocsTable = .{}; + assert(self.decl_state == null); + self.decl_state = DeclState.init(gpa); + const dbg_line_buffer = &self.decl_state.?.dbg_line; + const dbg_info_buffer = &self.decl_state.?.dbg_info; assert(decl.has_tv); @@ -232,19 +267,17 @@ pub fn initDeclDebugInfo(self: *Dwarf, decl: *Module.Decl) !DeclDebugBuffers { dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT.low_pc, DW.FORM.addr assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len); dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4 + // if (fn_ret_has_bits) { - const gop = try dbg_info_type_relocs.getOrPutContext(gpa, fn_ret_type, .{ - .target = self.target, - }); - if (!gop.found_existing) { - gop.value_ptr.* = .{ - .off = undefined, - .relocs = .{}, - }; - } - try gop.value_ptr.relocs.append(gpa, @intCast(u32, dbg_info_buffer.items.len)); + const atom = switch (self.tag) { + .elf => &decl.link.elf.dbg_info_atom, + .macho => &decl.link.macho.dbg_info_atom, + else => unreachable, + }; + try self.addTypeReloc(atom, fn_ret_type, @intCast(u32, dbg_info_buffer.items.len), null); dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4 } + dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT.name, DW.FORM.string }, @@ -252,30 +285,28 @@ pub fn initDeclDebugInfo(self: *Dwarf, decl: *Module.Decl) !DeclDebugBuffers { // TODO implement .debug_info for global variables }, } - - return DeclDebugBuffers{ - .dbg_info_buffer = dbg_info_buffer, - .dbg_line_buffer = dbg_line_buffer, - .dbg_info_type_relocs = dbg_info_type_relocs, - }; } -pub fn commitDeclDebugInfo( +pub fn commitDeclState( self: *Dwarf, file: *File, module: *Module, decl: *Module.Decl, sym_addr: u64, sym_size: u64, - debug_buffers: *DeclDebugBuffers, ) !void { const tracy = trace(@src()); defer tracy.end(); + assert(self.decl_state != null); // Caller forgot to call `initDeclState` + defer { + self.decl_state.?.deinit(self.allocator); + self.decl_state = null; + } + const gpa = self.allocator; - var dbg_line_buffer = &debug_buffers.dbg_line_buffer; - var dbg_info_buffer = &debug_buffers.dbg_info_buffer; - var dbg_info_type_relocs = &debug_buffers.dbg_info_type_relocs; + var dbg_line_buffer = &self.decl_state.?.dbg_line; + var dbg_info_buffer = &self.decl_state.?.dbg_info; const target_endian = self.target.cpu.arch.endian(); @@ -473,25 +504,21 @@ pub fn commitDeclDebugInfo( if (dbg_info_buffer.items.len == 0) return; - // We need this for the duration of this function only so that for composite - // types such as []const u32, if the type *u32 is non-existent, we create - // it synthetically and store the backing bytes in this arena. After we are - // done with the relocations, we can safely deinit the entire memory slab. - // TODO currently, we do not store the relocations for future use, however, - // if that is the case, we should move memory management to a higher scope, - // such as linker scope, or whatnot. - var dbg_type_arena = std.heap.ArenaAllocator.init(gpa); - defer dbg_type_arena.deinit(); + const atom = switch (self.tag) { + .elf => &decl.link.elf.dbg_info_atom, + .macho => &decl.link.macho.dbg_info_atom, + else => unreachable, + }; + const decl_state = &self.decl_state.?; - var nested_ref4_relocs = std.ArrayList(u32).init(gpa); - defer nested_ref4_relocs.deinit(); { // Now we emit the .debug_info types of the Decl. These will count towards the size of // the buffer, so we have to do it before computing the offset, and we can't perform the actual // relocations yet. - var it: usize = 0; - while (it < dbg_info_type_relocs.count()) : (it += 1) { - const ty = dbg_info_type_relocs.keys()[it]; + var sym_index: usize = 0; + while (sym_index < decl_state.abbrev_table.items.len) : (sym_index += 1) { + const symbol = &decl_state.abbrev_table.items[sym_index]; + const ty = symbol.@"type"; const deferred: bool = blk: { if (ty.isAnyError()) break :blk true; switch (ty.tag()) { @@ -504,68 +531,38 @@ pub fn commitDeclDebugInfo( }; if (deferred) continue; - const value_ptr = dbg_info_type_relocs.getPtrContext(ty, .{ - .target = self.target, - }).?; - value_ptr.off = @intCast(u32, dbg_info_buffer.items.len); - try self.addDbgInfoType( - dbg_type_arena.allocator(), - module, - ty, - dbg_info_buffer, - dbg_info_type_relocs, - &nested_ref4_relocs, - ); + symbol.offset = @intCast(u32, dbg_info_buffer.items.len); + try self.addDbgInfoType(decl_state.abbrev_type_arena.allocator(), module, atom, ty, dbg_info_buffer); } } - const atom = switch (self.tag) { - .elf => &decl.link.elf.dbg_info_atom, - .macho => &decl.link.macho.dbg_info_atom, - else => unreachable, - }; try self.updateDeclDebugInfoAllocation(file, atom, @intCast(u32, dbg_info_buffer.items.len)); - { - // Now that we have the offset assigned we can finally perform type relocations. - for (dbg_info_type_relocs.keys()) |ty| { - const value = dbg_info_type_relocs.getContext(ty, .{ - .target = self.target, - }).?; - for (value.relocs.items) |off| { - 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, - ); - } + while (decl_state.abbrev_relocs.popOrNull()) |reloc| { + const symbol = decl_state.abbrev_table.items[reloc.target]; + const ty = symbol.@"type"; + 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 => {}, } - } - // Offsets to positions with known a priori relative displacement values. - // Here, we just need to add the offset of the atom to the read value in the - // relocated cell. - // TODO Should probably generalise this with type relocs. - for (nested_ref4_relocs.items) |off| { - const addend = mem.readInt(u32, dbg_info_buffer.items[off..][0..4], target_endian); + break :blk false; + }; + if (deferred) { + try self.global_abbrev_relocs.append(gpa, .{ + .target = undefined, + .offset = reloc.offset, + .atom = reloc.atom, + .addend = reloc.addend, + }); + } else { mem.writeInt( u32, - dbg_info_buffer.items[off..][0..4], - atom.off + addend, + dbg_info_buffer.items[reloc.offset..][0..@sizeOf(u32)], + symbol.atom.off + symbol.offset + reloc.addend, target_endian, ); } @@ -574,77 +571,6 @@ 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); - - const atom = try gpa.create(Atom); - errdefer gpa.destroy(atom); - atom.* = .{ - .prev = null, - .next = null, - .off = 0, - .len = 0, - }; - try self.managed_atoms.append(gpa, atom); - 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: *Atom, len: u32) !void { const tracy = trace(@src()); defer tracy.end(); @@ -904,14 +830,12 @@ fn addDbgInfoType( self: *Dwarf, arena: Allocator, module: *Module, + atom: *Atom, ty: Type, dbg_info_buffer: *std.ArrayList(u8), - dbg_info_type_relocs: *DbgInfoTypeRelocsTable, - nested_ref4_relocs: *std.ArrayList(u32), ) error{OutOfMemory}!void { const target = self.target; const target_endian = self.target.cpu.arch.endian(); - var relocs = std.ArrayList(struct { ty: Type, reloc: u32 }).init(arena); switch (ty.zigTypeTag()) { .NoReturn => unreachable, @@ -970,7 +894,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 var index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = Type.bool, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, Type.bool, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata try dbg_info_buffer.ensureUnusedCapacity(6); dbg_info_buffer.appendAssumeCapacity(0); @@ -982,7 +906,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = payload_ty, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, payload_ty, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata const offset = abi_size - payload_ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), offset); @@ -1011,7 +935,7 @@ fn addDbgInfoType( try dbg_info_buffer.resize(index + 4); var buf = try arena.create(Type.SlicePtrFieldTypeBuffer); const ptr_ty = ty.slicePtrFieldType(buf); - try relocs.append(.{ .ty = ptr_ty, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, ptr_ty, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata try dbg_info_buffer.ensureUnusedCapacity(6); dbg_info_buffer.appendAssumeCapacity(0); @@ -1023,7 +947,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = Type.initTag(.usize), .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, Type.usize, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata try dbg_info_buffer.ensureUnusedCapacity(2); dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize)); @@ -1035,7 +959,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 const index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = ty.childType(), .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, ty.childType(), @intCast(u32, index), null); } }, .Struct => blk: { @@ -1059,7 +983,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 var index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = field, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, field, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata const field_off = ty.structFieldOffset(field_index, target); try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); @@ -1090,7 +1014,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 var index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = field.ty, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, field.ty, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata const field_off = ty.structFieldOffset(field_index, target); try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); @@ -1169,14 +1093,8 @@ fn addDbgInfoType( dbg_info_buffer.appendAssumeCapacity(0); // DW.AT.type, DW.FORM.ref4 const inner_union_index = dbg_info_buffer.items.len; - try dbg_info_buffer.ensureUnusedCapacity(4); - mem.writeInt( - u32, - dbg_info_buffer.addManyAsArrayAssumeCapacity(4), - @intCast(u32, inner_union_index + 5), - target_endian, - ); - try nested_ref4_relocs.append(@intCast(u32, inner_union_index)); + try dbg_info_buffer.resize(inner_union_index + 4); + try self.addTypeReloc(atom, ty, @intCast(u32, inner_union_index), 5); // DW.AT.data_member_location, DW.FORM.sdata try leb128.writeULEB128(dbg_info_buffer.writer(), payload_offset); } @@ -1203,7 +1121,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 const index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = field.ty, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, field.ty, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata try dbg_info_buffer.append(0); } @@ -1220,7 +1138,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 const index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = union_obj.tag_ty, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, union_obj.tag_ty, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata try leb128.writeULEB128(dbg_info_buffer.writer(), tag_offset); @@ -1229,7 +1147,40 @@ fn addDbgInfoType( } }, .ErrorSet => { - try self.addDbgInfoErrorSet(arena, module, ty, dbg_info_buffer); + // 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); }, .ErrorUnion => { const error_ty = ty.errorUnionSet(); @@ -1255,7 +1206,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 var index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = payload_ty, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, payload_ty, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata try leb128.writeULEB128(dbg_info_buffer.writer(), payload_off); @@ -1268,7 +1219,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = error_ty, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, error_ty, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata try dbg_info_buffer.append(0); @@ -1280,65 +1231,6 @@ fn addDbgInfoType( try dbg_info_buffer.append(abbrev_pad1); }, } - - for (relocs.items) |rel| { - const gop = try dbg_info_type_relocs.getOrPutContext(self.allocator, rel.ty, .{ - .target = self.target, - }); - if (!gop.found_existing) { - gop.value_ptr.* = .{ - .off = undefined, - .relocs = .{}, - }; - } - try gop.value_ptr.relocs.append(self.allocator, rel.reloc); - } -} - -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 { @@ -2059,3 +1951,115 @@ fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch std.math.maxInt(@TypeOf(actual_size)); } + +pub fn addTypeReloc(self: *Dwarf, atom: *const Atom, ty: Type, offset: u32, addend: ?u32) !void { + const decl_state = &self.decl_state.?; + const gpa = self.allocator; + const resolv = decl_state.abbrev_resolver.getContext(ty, .{ + .target = self.target, + }) orelse blk: { + const sym_index = @intCast(u32, decl_state.abbrev_table.items.len); + try decl_state.abbrev_table.append(gpa, .{ + .atom = atom, + .@"type" = ty, + .offset = undefined, + }); + log.debug("@{d}: {}", .{ sym_index, ty.fmtDebug() }); + try decl_state.abbrev_resolver.putNoClobberContext(gpa, ty, sym_index, .{ + .target = self.target, + }); + break :blk decl_state.abbrev_resolver.getContext(ty, .{ + .target = self.target, + }).?; + }; + const add: u32 = addend orelse 0; + + log.debug("{x}: @{d} + {x}", .{ offset, resolv, add }); + try decl_state.abbrev_relocs.append(gpa, .{ + .target = resolv, + .atom = atom, + .offset = offset, + .addend = add, + }); +} + +pub fn getDeclDebugLineBuffer(self: *Dwarf) *std.ArrayList(u8) { + return &self.decl_state.?.dbg_line; +} + +pub fn getDeclDebugInfoBuffer(self: *Dwarf) *std.ArrayList(u8) { + return &self.decl_state.?.dbg_info; +} + +pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void { + if (self.global_abbrev_relocs.items.len > 0) { + 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 error_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; + + const atom = try gpa.create(Atom); + errdefer gpa.destroy(atom); + atom.* = .{ + .prev = null, + .next = null, + .off = 0, + .len = 0, + }; + + var dbg_info_buffer = std.ArrayList(u8).init(arena); + try self.addDbgInfoType(arena, module, atom, error_ty, &dbg_info_buffer); + + try self.managed_atoms.append(gpa, atom); + 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, + } + }; + + var buf: [@sizeOf(u32)]u8 = undefined; + mem.writeInt(u32, &buf, atom.off, self.target.cpu.arch.endian()); + + while (self.global_abbrev_relocs.popOrNull()) |reloc| { + switch (self.tag) { + .elf => { + const elf_file = file.cast(File.Elf).?; + try elf_file.base.file.?.pwriteAll(&buf, file_pos + reloc.atom.off + reloc.offset); + }, + .macho => { + const macho_file = file.cast(File.MachO).?; + const d_sym = &macho_file.d_sym.?; + try d_sym.file.pwriteAll(&buf, file_pos + reloc.atom.off + reloc.offset); + }, + else => unreachable, + } + } + } + + assert(self.decl_state == null); +} diff --git a/src/link/Elf.zig b/src/link/Elf.zig index dda2841db8..771a57b26e 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -958,8 +958,8 @@ 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); + if (self.dwarf) |*dw| { + try dw.flushModule(&self.base, module); } { @@ -2232,13 +2232,6 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { } } -fn deinitRelocs(gpa: Allocator, table: *link.File.Dwarf.DbgInfoTypeRelocsTable) void { - for (table.values()) |*value| { - value.relocs.deinit(gpa); - } - table.deinit(gpa); -} - fn getDeclPhdrIndex(self: *Elf, decl: *Module.Decl) !u16 { const ty = decl.ty; const zig_ty = ty.zigTypeTag(); @@ -2346,26 +2339,13 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven const decl = func.owner_decl; self.freeUnnamedConsts(decl); - var debug_buffers_buf: Dwarf.DeclDebugBuffers = undefined; - const debug_buffers = if (self.dwarf) |*dw| blk: { - debug_buffers_buf = try dw.initDeclDebugInfo(decl); - break :blk &debug_buffers_buf; - } else null; - defer { - if (debug_buffers) |dbg| { - dbg.dbg_line_buffer.deinit(); - dbg.dbg_info_buffer.deinit(); - deinitRelocs(self.base.allocator, &dbg.dbg_info_type_relocs); - } + if (self.dwarf) |*dw| { + try dw.initDeclState(decl); } - const res = if (debug_buffers) |dbg| + const res = if (self.dwarf) |*dw| try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ - .dwarf = .{ - .dbg_line = &dbg.dbg_line_buffer, - .dbg_info = &dbg.dbg_info_buffer, - .dbg_info_type_relocs = &dbg.dbg_info_type_relocs, - }, + .dwarf = dw, }) else try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); @@ -2379,15 +2359,8 @@ 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, - ); + if (self.dwarf) |*dw| { + try dw.commitDeclState(&self.base, module, decl, local_sym.st_value, local_sym.st_size); } // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. @@ -2421,31 +2394,18 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - var debug_buffers_buf: Dwarf.DeclDebugBuffers = undefined; - const debug_buffers = if (self.dwarf) |*dw| blk: { - debug_buffers_buf = try dw.initDeclDebugInfo(decl); - break :blk &debug_buffers_buf; - } else null; - defer { - if (debug_buffers) |dbg| { - dbg.dbg_line_buffer.deinit(); - dbg.dbg_info_buffer.deinit(); - deinitRelocs(self.base.allocator, &dbg.dbg_info_type_relocs); - } + if (self.dwarf) |*dw| { + try dw.initDeclState(decl); } // TODO implement .debug_info for global variables const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; - const res = if (debug_buffers) |dbg| + const res = if (self.dwarf) |*dw| try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .{ - .dwarf = .{ - .dbg_line = &dbg.dbg_line_buffer, - .dbg_info = &dbg.dbg_info_buffer, - .dbg_info_type_relocs = &dbg.dbg_info_type_relocs, - }, + .dwarf = dw, }, .{ .parent_atom_index = decl.link.elf.local_sym_index, }) @@ -2468,8 +2428,8 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { }; const local_sym = try self.updateDeclCode(decl, code, elf.STT_OBJECT); - if (debug_buffers) |dbg| { - try self.dwarf.?.commitDeclDebugInfo(&self.base, module, decl, local_sym.st_value, local_sym.st_size, dbg); + if (self.dwarf) |*dw| { + try dw.commitDeclState(&self.base, module, decl, local_sym.st_value, local_sym.st_size); } // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. diff --git a/src/link/MachO.zig b/src/link/MachO.zig index e613772f22..31ada3b4ee 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -453,6 +453,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); + if (self.d_sym) |*d_sym| { + if (self.base.options.module) |module| { + try d_sym.dwarf.flushModule(&self.base, module); + } + } + // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { @@ -3670,32 +3676,17 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - var debug_buffers_buf: link.File.Dwarf.DeclDebugBuffers = undefined; - const debug_buffers = if (self.d_sym) |*d_sym| blk: { - debug_buffers_buf = try d_sym.initDeclDebugInfo(module, decl); - break :blk &debug_buffers_buf; - } else null; - defer { - if (debug_buffers) |dbg| { - dbg.dbg_line_buffer.deinit(); - dbg.dbg_info_buffer.deinit(); - for (dbg.dbg_info_type_relocs.values()) |*value| { - value.relocs.deinit(self.base.allocator); - } - dbg.dbg_info_type_relocs.deinit(self.base.allocator); - } + if (self.d_sym) |*d_sym| { + try d_sym.dwarf.initDeclState(decl); } - const res = if (debug_buffers) |dbg| + const res = if (self.d_sym) |*d_sym| try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ - .dwarf = .{ - .dbg_line = &dbg.dbg_line_buffer, - .dbg_info = &dbg.dbg_info_buffer, - .dbg_info_type_relocs = &dbg.dbg_info_type_relocs, - }, + .dwarf = &d_sym.dwarf, }) else try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); + switch (res) { .appended => { try decl.link.macho.code.appendSlice(self.base.allocator, code_buffer.items); @@ -3707,12 +3698,10 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv }, } - _ = try self.placeDecl(decl, decl.link.macho.code.items.len); + const symbol = try self.placeDecl(decl, decl.link.macho.code.items.len); - if (debug_buffers) |db| { - if (self.d_sym) |*d_sym| { - try d_sym.commitDeclDebugInfo(module, decl, db); - } + if (self.d_sym) |*d_sym| { + try d_sym.dwarf.commitDeclState(&self.base, module, decl, symbol.n_value, decl.link.macho.size); } // Since we updated the vaddr and the size, each corresponding export symbol also @@ -3812,33 +3801,17 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - var debug_buffers_buf: link.File.Dwarf.DeclDebugBuffers = undefined; - const debug_buffers = if (self.d_sym) |*d_sym| blk: { - debug_buffers_buf = try d_sym.initDeclDebugInfo(module, decl); - break :blk &debug_buffers_buf; - } else null; - defer { - if (debug_buffers) |dbg| { - dbg.dbg_line_buffer.deinit(); - dbg.dbg_info_buffer.deinit(); - for (dbg.dbg_info_type_relocs.values()) |*value| { - value.relocs.deinit(self.base.allocator); - } - dbg.dbg_info_type_relocs.deinit(self.base.allocator); - } + if (self.d_sym) |*d_sym| { + try d_sym.dwarf.initDeclState(decl); } const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; - const res = if (debug_buffers) |dbg| + const res = if (self.d_sym) |*d_sym| try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .{ - .dwarf = .{ - .dbg_line = &dbg.dbg_line_buffer, - .dbg_info = &dbg.dbg_info_buffer, - .dbg_info_type_relocs = &dbg.dbg_info_type_relocs, - }, + .dwarf = &d_sym.dwarf, }, .{ .parent_atom_index = decl.link.macho.local_sym_index, }) @@ -3870,7 +3843,11 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { }, } }; - _ = try self.placeDecl(decl, code.len); + const symbol = try self.placeDecl(decl, code.len); + + if (self.d_sym) |*d_sym| { + try d_sym.dwarf.commitDeclState(&self.base, module, decl, symbol.n_value, decl.link.macho.size); + } // Since we updated the vaddr and the size, each corresponding export symbol also // needs to be updated. @@ -4084,8 +4061,9 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 } pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void { + _ = module; if (self.d_sym) |*d_sym| { - try d_sym.updateDeclLineNumber(module, decl); + try d_sym.dwarf.updateDeclLineNumber(&self.base, decl); } } diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 9fc2828ea1..885f0ca6a8 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -645,25 +645,3 @@ fn writeStringTable(self: *DebugSymbols) !void { self.load_commands_dirty = true; } - -pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void { - _ = module; - return self.dwarf.updateDeclLineNumber(&self.base.base, decl); -} - -/// Caller owns the returned memory. -pub fn initDeclDebugInfo(self: *DebugSymbols, module: *Module, decl: *Module.Decl) !Dwarf.DeclDebugBuffers { - _ = module; - return self.dwarf.initDeclDebugInfo(decl); -} - -pub fn commitDeclDebugInfo( - self: *DebugSymbols, - module: *Module, - decl: *Module.Decl, - debug_buffers: *Dwarf.DeclDebugBuffers, -) !void { - const symbol = self.base.locals.items[decl.link.macho.local_sym_index]; - const atom = &decl.link.macho; - return self.dwarf.commitDeclDebugInfo(&self.base.base, module, decl, symbol.n_value, atom.size, debug_buffers); -}