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 cc7014a1361886d24ac21b9c866d2d3241b00643..ca24f998e8a9a98c03c5d63329e41ec4955a7c2a 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 370aac2734d18ddd74e36300378aa172778ef045..353b40e2014a81315c96ae7e06455f0f4825b6f5 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); -- 2.54.0 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 ca24f998e8a9a98c03c5d63329e41ec4955a7c2a..8924f13e17bbf8a3fe61e3e375e083f129803f27 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 353b40e2014a81315c96ae7e06455f0f4825b6f5..639ef31be4368539a75bf02d4364cb5c78f632f0 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); -- 2.54.0 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 639ef31be4368539a75bf02d4364cb5c78f632f0..f00b1f64791dbb6d392ea288d612808d5bda4110 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 -- 2.54.0 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 f00b1f64791dbb6d392ea288d612808d5bda4110..5e46811900c7c639eaf9b675f870d5193539efbd 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 64d6df67564c776dc5d648044f92689eb08cd867..c81c15f5978304c1d7bae8cc21a3e636ab01b554 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. -- 2.54.0 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 5e46811900c7c639eaf9b675f870d5193539efbd..b2848c8e1e70fb6e007679447106dadd2f990a1b 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 c81c15f5978304c1d7bae8cc21a3e636ab01b554..676952ea24df4acb95c20d06ee461262181f51f1 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 4e58946735f8dbbf80d24dc0e300a1639ecf1591..dd2dc3c1f1efd757eef191ccf8bb946b4742e3b9 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, -- 2.54.0 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 | 40 +++++++++++----------------------------- src/link/Dwarf.zig | 22 +++++++++++++++++++--- src/link/Elf.zig | 2 +- 4 files changed, 46 insertions(+), 49 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index 8924f13e17bbf8a3fe61e3e375e083f129803f27..f4803c77ea8ca65025d8790527f7b1158398eff8 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 0fd6797153e7e2284cb067657a4ea72938cd7c5b..7c135a7405b9f55a6865ebdf75e8164e5d1eff57 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 assert = std.debug.assert; const fs = std.fs; +const mem = std.mem; const log = std.log.scoped(.link); -const assert = std.debug.assert; - -const Compilation = @import("Compilation.zig"); -const Module = @import("Module.zig"); const trace = @import("tracy.zig").trace; -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 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 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 b2848c8e1e70fb6e007679447106dadd2f990a1b..199bf9df6144f808e9b6fc830396b0da3d7115bd 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 676952ea24df4acb95c20d06ee461262181f51f1..dda2841db8d377ea3c5af87c1e0db43621b688bf 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); } -- 2.54.0 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 3eb25dda7b19d0bdbaf7560f5a15028360e227d6..2a4e6cb98509ab2e0702aebd543f6ae0130fd122 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 1e03c4649b05c28ffad720044f54ea9d9d3df638..62705651a6ca161da0cbe12e53f50e6b6230d9c8 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 8b697677c1d3fc5c92c726a9e5532cc9de21d324..fa243819cf633aacba3a9dfaf3b4c1b5c3111087 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 c34d2f4ff406134db80dcaad4f7557243aace5a4..bfa4e00d003641a2456ee99b45c5cf2acee75838 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 5b252b6c0a84499de67adecece643cef972a9f99..c95e86a424889ab8e1271a5b89a3ddddcf486e57 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 3ce77ffb7358112819edaec4f59f441e59c9e416..4616d3ff3a8f23d7e97adcf7c72a5dbe551c4cf3 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 f4803c77ea8ca65025d8790527f7b1158398eff8..fa84ad0d525b662886cd87febb34c3ea3e6d78e6 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 199bf9df6144f808e9b6fc830396b0da3d7115bd..83222090c0a7289a61ff7c6835e4fec8a8fe1fb4 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 dda2841db8d377ea3c5af87c1e0db43621b688bf..771a57b26eb7c7211620be815925516f8a42b113 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 e613772f22ec30e752dbe84371720a9aba5bf909..31ada3b4ee819ff35e4d57c0a1545a308e45f101 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 9fc2828ea1309b6bdf1ee705356cbddf66f97349..885f0ca6a83abd0ee26253ca751a0b650d18f549 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); -} -- 2.54.0