diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2022-03-06 18:35:58 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2022-03-08 09:46:27 +0100 |
| commit | ba17552b4eb8def495053013eebbe39fc324c8ae (patch) | |
| tree | 33f871441f432bfdee7f6f555b95062be565ded8 /src/link/Elf.zig | |
| parent | 38c161afabe1dc7d0df7ad981d70b962ef87120a (diff) | |
| download | zig-ba17552b4eb8def495053013eebbe39fc324c8ae.tar.gz zig-ba17552b4eb8def495053013eebbe39fc324c8ae.zip | |
dwarf: move all dwarf into standalone module
Hook up Elf and MachO linkers to the new solution.
Diffstat (limited to 'src/link/Elf.zig')
| -rw-r--r-- | src/link/Elf.zig | 1342 |
1 files changed, 105 insertions, 1237 deletions
diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 810bef2f9b..64d6df6756 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -9,11 +9,10 @@ const Allocator = std.mem.Allocator; const fs = std.fs; const elf = std.elf; const log = std.log.scoped(.link); -const DW = std.dwarf; -const leb128 = std.leb; const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); +const Dwarf = @import("Dwarf.zig"); const codegen = @import("../codegen.zig"); const lldMain = @import("../main.zig").lldMain; const trace = @import("../tracy.zig").trace; @@ -37,6 +36,7 @@ const default_entry_addr = 0x8000000; pub const base_tag: File.Tag = .elf; base: File, +dwarf: ?Dwarf = null, ptr_width: PtrWidth, @@ -67,7 +67,6 @@ phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{}, entry_addr: ?u64 = null, page_size: u16, -debug_strtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, shstrtab_index: ?u16 = null, @@ -82,8 +81,6 @@ debug_str_section_index: ?u16 = null, debug_aranges_section_index: ?u16 = null, debug_line_section_index: ?u16 = null, -debug_abbrev_table_offset: ?u64 = null, - /// The same order as in the file. ELF requires global symbols to all be after the /// local symbols, they cannot be mixed. So we must buffer all the global symbols and /// write them at the end. These are only the local symbols. The length of this array @@ -168,18 +165,6 @@ atom_by_index_table: std.AutoHashMapUnmanaged(u32, *TextBlock) = .{}, /// with `Decl` `main`, and lives as long as that `Decl`. unnamed_const_atoms: UnnamedConstTable = .{}, -/// A list of `SrcFn` whose Line Number Programs have surplus capacity. -/// This is the same concept as `text_block_free_list`; see those doc comments. -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(*TextBlock, void) = .{}, -dbg_info_decl_first: ?*TextBlock = null, -dbg_info_decl_last: ?*TextBlock = null, - /// A table of relocations indexed by the owning them `TextBlock`. /// Note that once we refactor `TextBlock`'s lifetime and ownership rules, /// this will be a table indexed by index into the list of Atoms. @@ -222,24 +207,14 @@ pub const TextBlock = struct { prev: ?*TextBlock, next: ?*TextBlock, - /// Previous/next linked list pointers. - /// This is the linked list node for this Decl's corresponding .debug_info tag. - dbg_info_prev: ?*TextBlock, - dbg_info_next: ?*TextBlock, - /// Offset into .debug_info pointing to the tag for this Decl. - dbg_info_off: u32, - /// Size of the .debug_info tag for this Decl, not including padding. - dbg_info_len: u32, + dbg_info_atom: Dwarf.DebugInfoAtom, pub const empty = TextBlock{ .local_sym_index = 0, .offset_table_index = undefined, .prev = null, .next = null, - .dbg_info_prev = null, - .dbg_info_next = null, - .dbg_info_off = undefined, - .dbg_info_len = undefined, + .dbg_info_atom = undefined, }; /// Returns how much room there is to grow in virtual address space. @@ -273,26 +248,6 @@ pub const Export = struct { sym_index: ?u32 = null, }; -pub const SrcFn = struct { - /// Offset from the beginning of the Debug Line Program header that contains this function. - off: u32, - /// Size of the line number program component belonging to this function, not - /// including padding. - len: u32, - - /// Points to the previous and next neighbors, based on the offset from .debug_line. - /// This can be used to find, for example, the capacity of this `SrcFn`. - prev: ?*SrcFn, - next: ?*SrcFn, - - pub const empty: SrcFn = .{ - .off = 0, - .len = 0, - .prev = null, - .next = null, - }; -}; - pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Elf { assert(options.object_format == .elf); @@ -351,6 +306,11 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { errdefer gpa.destroy(self); const page_size: u16 = 0x1000; // TODO ppc64le requires 64KB + var dwarf: ?Dwarf = if (!options.strip and options.module != null) + Dwarf.init(gpa, .elf, options.target) + else + null; + self.* = .{ .base = .{ .tag = .elf, @@ -358,6 +318,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { .allocator = gpa, .file = null, }, + .dwarf = dwarf, .ptr_width = ptr_width, .page_size = page_size, }; @@ -377,14 +338,11 @@ pub fn deinit(self: *Elf) void { self.sections.deinit(self.base.allocator); self.program_headers.deinit(self.base.allocator); self.shstrtab.deinit(self.base.allocator); - self.debug_strtab.deinit(self.base.allocator); self.local_symbols.deinit(self.base.allocator); self.global_symbols.deinit(self.base.allocator); self.global_symbol_free_list.deinit(self.base.allocator); self.local_symbol_free_list.deinit(self.base.allocator); self.offset_table_free_list.deinit(self.base.allocator); - self.dbg_line_fn_free_list.deinit(self.base.allocator); - self.dbg_info_decl_free_list.deinit(self.base.allocator); self.offset_table.deinit(self.base.allocator); self.phdr_shdr_table.deinit(self.base.allocator); self.decls.deinit(self.base.allocator); @@ -420,6 +378,10 @@ pub fn deinit(self: *Elf) void { } self.atom_by_index_table.deinit(self.base.allocator); + + if (self.dwarf) |*dw| { + dw.deinit(); + } } pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl, reloc_info: File.RelocInfo) !u64 { @@ -443,14 +405,6 @@ pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl, reloc_info: File.Reloc return vaddr; } -fn getDebugLineProgramOff(self: Elf) u32 { - return self.dbg_line_fn_first.?.off; -} - -fn getDebugLineProgramEnd(self: Elf) u32 { - return self.dbg_line_fn_last.?.off + self.dbg_line_fn_last.?.len; -} - /// Returns end pos of collision, if any. fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { const small_ptr = self.ptr_width == .p32; @@ -497,7 +451,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { return null; } -fn allocatedSize(self: *Elf, start: u64) u64 { +pub fn allocatedSize(self: *Elf, start: u64) u64 { if (start == 0) return 0; var min_pos: u64 = std.math.maxInt(u64); @@ -518,7 +472,7 @@ fn allocatedSize(self: *Elf, start: u64) u64 { return min_pos - start; } -fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 { +pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 { var start: u64 = 0; while (self.detectAllocCollision(start, object_size)) |item_end| { start = mem.alignForwardGeneric(u64, item_end, min_alignment); @@ -535,15 +489,6 @@ fn makeString(self: *Elf, bytes: []const u8) !u32 { return @intCast(u32, result); } -/// TODO Improve this to use a table. -fn makeDebugString(self: *Elf, bytes: []const u8) !u32 { - try self.debug_strtab.ensureUnusedCapacity(self.base.allocator, bytes.len + 1); - const result = self.debug_strtab.items.len; - self.debug_strtab.appendSliceAssumeCapacity(bytes); - self.debug_strtab.appendAssumeCapacity(0); - return @intCast(u32, result); -} - fn getString(self: *Elf, str_off: u32) []const u8 { assert(str_off < self.shstrtab.items.len); return mem.sliceTo(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off), 0); @@ -806,14 +751,14 @@ pub fn populateMissingMetadata(self: *Elf) !void { if (self.debug_str_section_index == null) { self.debug_str_section_index = @intCast(u16, self.sections.items.len); - assert(self.debug_strtab.items.len == 0); + assert(self.dwarf.?.strtab.items.len == 0); try self.sections.append(self.base.allocator, .{ .sh_name = try self.makeString(".debug_str"), .sh_type = elf.SHT_PROGBITS, .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS, .sh_addr = 0, .sh_offset = 0, - .sh_size = self.debug_strtab.items.len, + .sh_size = 0, .sh_link = 0, .sh_info = 0, .sh_addralign = 1, @@ -977,16 +922,6 @@ pub fn populateMissingMetadata(self: *Elf) !void { } } -pub const abbrev_compile_unit = 1; -pub const abbrev_subprogram = 2; -pub const abbrev_subprogram_retvoid = 3; -pub const abbrev_base_type = 4; -pub const abbrev_ptr_type = 5; -pub const abbrev_struct_type = 6; -pub const abbrev_struct_member = 7; -pub const abbrev_pad1 = 8; -pub const abbrev_parameter = 9; - pub fn flush(self: *Elf, comp: *Compilation) !void { if (self.base.options.emit == null) { if (build_options.have_llvm) { @@ -1022,11 +957,6 @@ 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(); - const ptr_width_bytes: u8 = self.ptrWidthBytes(); - const init_len_size: usize = switch (self.ptr_width) { - .p32 => 4, - .p64 => 12, - }; { var it = self.relocs.iterator(); @@ -1068,349 +998,38 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { try self.writeAllGlobalSymbols(); if (self.debug_abbrev_section_dirty) { - const debug_abbrev_sect = &self.sections.items[self.debug_abbrev_section_index.?]; - - // These are LEB encoded but since the values are all less than 127 - // we can simply append these bytes. - const abbrev_buf = [_]u8{ - abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header - DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc, - DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr, - DW.AT.name, DW.FORM.strp, DW.AT.comp_dir, - DW.FORM.strp, DW.AT.producer, DW.FORM.strp, - DW.AT.language, DW.FORM.data2, 0, - 0, // table sentinel - abbrev_subprogram, - DW.TAG.subprogram, - DW.CHILDREN.yes, // header - DW.AT.low_pc, - DW.FORM.addr, - DW.AT.high_pc, - DW.FORM.data4, - DW.AT.type, - DW.FORM.ref4, - DW.AT.name, - DW.FORM.string, - 0, 0, // table sentinel - abbrev_subprogram_retvoid, - DW.TAG.subprogram, DW.CHILDREN.yes, // header - DW.AT.low_pc, DW.FORM.addr, - DW.AT.high_pc, DW.FORM.data4, - DW.AT.name, DW.FORM.string, - 0, - 0, // table sentinel - abbrev_base_type, - DW.TAG.base_type, - DW.CHILDREN.no, // header - DW.AT.encoding, - DW.FORM.data1, - DW.AT.byte_size, - DW.FORM.data1, - DW.AT.name, - DW.FORM.string, - 0, - 0, // table sentinel - abbrev_ptr_type, - DW.TAG.pointer_type, - DW.CHILDREN.no, // header - DW.AT.type, - DW.FORM.ref4, - 0, - 0, // table sentinel - abbrev_struct_type, - DW.TAG.structure_type, - DW.CHILDREN.yes, // header - DW.AT.byte_size, - DW.FORM.sdata, - DW.AT.name, - DW.FORM.string, - 0, - 0, // table sentinel - abbrev_struct_member, - DW.TAG.member, - DW.CHILDREN.no, // header - DW.AT.name, - DW.FORM.string, - DW.AT.type, - DW.FORM.ref4, - DW.AT.data_member_location, - DW.FORM.sdata, - 0, - 0, // table sentinel - abbrev_pad1, - DW.TAG.unspecified_type, - DW.CHILDREN.no, // header - 0, - 0, // table sentinel - abbrev_parameter, - DW.TAG.formal_parameter, DW.CHILDREN.no, // header - DW.AT.location, DW.FORM.exprloc, - DW.AT.type, DW.FORM.ref4, - DW.AT.name, DW.FORM.string, - 0, - 0, // table sentinel - 0, - 0, - 0, // section sentinel - }; - - const needed_size = abbrev_buf.len; - const allocated_size = self.allocatedSize(debug_abbrev_sect.sh_offset); - if (needed_size > allocated_size) { - debug_abbrev_sect.sh_size = 0; // free the space - debug_abbrev_sect.sh_offset = self.findFreeSpace(needed_size, 1); - } - debug_abbrev_sect.sh_size = needed_size; - log.debug(".debug_abbrev start=0x{x} end=0x{x}", .{ - debug_abbrev_sect.sh_offset, - debug_abbrev_sect.sh_offset + needed_size, - }); - - const abbrev_offset = 0; - self.debug_abbrev_table_offset = abbrev_offset; - try self.base.file.?.pwriteAll(&abbrev_buf, debug_abbrev_sect.sh_offset + abbrev_offset); + try self.dwarf.?.writeDbgAbbrev(&self.base); if (!self.shdr_table_dirty) { // Then it won't get written with the others and we need to do it. try self.writeSectHeader(self.debug_abbrev_section_index.?); } - self.debug_abbrev_section_dirty = false; } - if (self.debug_info_header_dirty) debug_info: { - // If this value is null it means there is an error in the module; - // leave debug_info_header_dirty=true. - const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info; - const last_dbg_info_decl = self.dbg_info_decl_last.?; - const debug_info_sect = &self.sections.items[self.debug_info_section_index.?]; - - // We have a function to compute the upper bound size, because it's needed - // for determining where to put the offset of the first `LinkBlock`. - const needed_bytes = self.dbgInfoNeededHeaderBytes(); - var di_buf = try std.ArrayList(u8).initCapacity(self.base.allocator, needed_bytes); - defer di_buf.deinit(); - - // initial length - length of the .debug_info contribution for this compilation unit, - // not including the initial length itself. - // We have to come back and write it later after we know the size. - const after_init_len = di_buf.items.len + init_len_size; - // +1 for the final 0 that ends the compilation unit children. - const dbg_info_end = last_dbg_info_decl.dbg_info_off + last_dbg_info_decl.dbg_info_len + 1; - const init_len = dbg_info_end - after_init_len; - switch (self.ptr_width) { - .p32 => { - mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian); - }, - .p64 => { - di_buf.appendNTimesAssumeCapacity(0xff, 4); - mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian); - }, - } - mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version - const abbrev_offset = self.debug_abbrev_table_offset.?; - switch (self.ptr_width) { - .p32 => { - mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset), target_endian); - di_buf.appendAssumeCapacity(4); // address size - }, - .p64 => { - mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), abbrev_offset, target_endian); - di_buf.appendAssumeCapacity(8); // address size - }, - } - // Write the form for the compile unit, which must match the abbrev table above. - const name_strp = try self.makeDebugString(module.root_pkg.root_src_path); - const comp_dir_strp = try self.makeDebugString(module.root_pkg.root_src_directory.path orelse "."); - const producer_strp = try self.makeDebugString(link.producer_string); + if (self.debug_info_header_dirty) { // Currently only one compilation unit is supported, so the address range is simply // identical to the main program header virtual address and memory size. const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; const low_pc = text_phdr.p_vaddr; const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; - - di_buf.appendAssumeCapacity(abbrev_compile_unit); - self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT.stmt_list, DW.FORM.sec_offset - self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc); - self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc); - self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp); - self.writeDwarfAddrAssumeCapacity(&di_buf, comp_dir_strp); - self.writeDwarfAddrAssumeCapacity(&di_buf, producer_strp); - // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number: - // http://dwarfstd.org/ShowIssue.php?issue=171115.1 - // Until then we say it is C99. - mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG.C99, target_endian); - - if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) { - // Move the first N decls to the end to make more padding for the header. - @panic("TODO: handle .debug_info header exceeding its padding"); - } - const jmp_amt = first_dbg_info_decl.dbg_info_off - di_buf.items.len; - try self.pwriteDbgInfoNops(0, di_buf.items, jmp_amt, false, debug_info_sect.sh_offset); + try self.dwarf.?.writeDbgInfoHeader(&self.base, module, low_pc, high_pc); self.debug_info_header_dirty = false; } if (self.debug_aranges_section_dirty) { - const debug_aranges_sect = &self.sections.items[self.debug_aranges_section_index.?]; - - // Enough for all the data without resizing. When support for more compilation units - // is added, the size of this section will become more variable. - var di_buf = try std.ArrayList(u8).initCapacity(self.base.allocator, 100); - defer di_buf.deinit(); - - // initial length - length of the .debug_aranges contribution for this compilation unit, - // not including the initial length itself. - // We have to come back and write it later after we know the size. - const init_len_index = di_buf.items.len; - di_buf.items.len += init_len_size; - const after_init_len = di_buf.items.len; - mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version - // When more than one compilation unit is supported, this will be the offset to it. - // For now it is always at offset 0 in .debug_info. - self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // .debug_info offset - di_buf.appendAssumeCapacity(ptr_width_bytes); // address_size - di_buf.appendAssumeCapacity(0); // segment_selector_size - - const end_header_offset = di_buf.items.len; - const begin_entries_offset = mem.alignForward(end_header_offset, ptr_width_bytes * 2); - di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset); - // Currently only one compilation unit is supported, so the address range is simply // identical to the main program header virtual address and memory size. const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; - self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_vaddr); - self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_memsz); - - // Sentinel. - self.writeDwarfAddrAssumeCapacity(&di_buf, 0); - self.writeDwarfAddrAssumeCapacity(&di_buf, 0); - - // Go back and populate the initial length. - const init_len = di_buf.items.len - after_init_len; - switch (self.ptr_width) { - .p32 => { - mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len), target_endian); - }, - .p64 => { - // initial length - length of the .debug_aranges contribution for this compilation unit, - // not including the initial length itself. - di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff }; - mem.writeInt(u64, di_buf.items[init_len_index + 4 ..][0..8], init_len, target_endian); - }, - } - - const needed_size = di_buf.items.len; - const allocated_size = self.allocatedSize(debug_aranges_sect.sh_offset); - if (needed_size > allocated_size) { - debug_aranges_sect.sh_size = 0; // free the space - debug_aranges_sect.sh_offset = self.findFreeSpace(needed_size, 16); - } - debug_aranges_sect.sh_size = needed_size; - log.debug(".debug_aranges start=0x{x} end=0x{x}", .{ - debug_aranges_sect.sh_offset, - debug_aranges_sect.sh_offset + needed_size, - }); - - try self.base.file.?.pwriteAll(di_buf.items, debug_aranges_sect.sh_offset); + try self.dwarf.?.writeDbgAranges(&self.base, text_phdr.p_vaddr, text_phdr.p_memsz); if (!self.shdr_table_dirty) { // Then it won't get written with the others and we need to do it. try self.writeSectHeader(self.debug_aranges_section_index.?); } - self.debug_aranges_section_dirty = false; } - if (self.debug_line_header_dirty) debug_line: { - if (self.dbg_line_fn_first == null) { - break :debug_line; // Error in module; leave debug_line_header_dirty=true. - } - const dbg_line_prg_off = self.getDebugLineProgramOff(); - const dbg_line_prg_end = self.getDebugLineProgramEnd(); - assert(dbg_line_prg_end != 0); - - const debug_line_sect = &self.sections.items[self.debug_line_section_index.?]; - - // The size of this header is variable, depending on the number of directories, - // files, and padding. We have a function to compute the upper bound size, however, - // because it's needed for determining where to put the offset of the first `SrcFn`. - const needed_bytes = self.dbgLineNeededHeaderBytes(); - var di_buf = try std.ArrayList(u8).initCapacity(self.base.allocator, needed_bytes); - defer di_buf.deinit(); - - // initial length - length of the .debug_line contribution for this compilation unit, - // not including the initial length itself. - const after_init_len = di_buf.items.len + init_len_size; - const init_len = dbg_line_prg_end - after_init_len; - switch (self.ptr_width) { - .p32 => { - mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian); - }, - .p64 => { - di_buf.appendNTimesAssumeCapacity(0xff, 4); - mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian); - }, - } - - mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // version - - // Empirically, debug info consumers do not respect this field, or otherwise - // consider it to be an error when it does not point exactly to the end of the header. - // Therefore we rely on the NOP jump at the beginning of the Line Number Program for - // padding rather than this field. - const before_header_len = di_buf.items.len; - di_buf.items.len += ptr_width_bytes; // We will come back and write this. - const after_header_len = di_buf.items.len; - - const opcode_base = DW.LNS.set_isa + 1; - di_buf.appendSliceAssumeCapacity(&[_]u8{ - 1, // minimum_instruction_length - 1, // maximum_operations_per_instruction - 1, // default_is_stmt - 1, // line_base (signed) - 1, // line_range - opcode_base, - - // Standard opcode lengths. The number of items here is based on `opcode_base`. - // The value is the number of LEB128 operands the instruction takes. - 0, // `DW.LNS.copy` - 1, // `DW.LNS.advance_pc` - 1, // `DW.LNS.advance_line` - 1, // `DW.LNS.set_file` - 1, // `DW.LNS.set_column` - 0, // `DW.LNS.negate_stmt` - 0, // `DW.LNS.set_basic_block` - 0, // `DW.LNS.const_add_pc` - 1, // `DW.LNS.fixed_advance_pc` - 0, // `DW.LNS.set_prologue_end` - 0, // `DW.LNS.set_epilogue_begin` - 1, // `DW.LNS.set_isa` - 0, // include_directories (none except the compilation unit cwd) - }); - // file_names[0] - di_buf.appendSliceAssumeCapacity(module.root_pkg.root_src_path); // relative path name - di_buf.appendSliceAssumeCapacity(&[_]u8{ - 0, // null byte for the relative path name - 0, // directory_index - 0, // mtime (TODO supply this) - 0, // file size bytes (TODO supply this) - 0, // file_names sentinel - }); - const header_len = di_buf.items.len - after_header_len; - switch (self.ptr_width) { - .p32 => { - mem.writeInt(u32, di_buf.items[before_header_len..][0..4], @intCast(u32, header_len), target_endian); - }, - .p64 => { - mem.writeInt(u64, di_buf.items[before_header_len..][0..8], header_len, target_endian); - }, - } - - // We use NOPs because consumers empirically do not respect the header length field. - if (di_buf.items.len > dbg_line_prg_off) { - // Move the first N files to the end to make more padding for the header. - @panic("TODO: handle .debug_line header exceeding its padding"); - } - const jmp_amt = dbg_line_prg_off - di_buf.items.len; - try self.pwriteDbgLineNops(0, di_buf.items, jmp_amt, debug_line_sect.sh_offset); + if (self.debug_line_header_dirty) { + try self.dwarf.?.writeDbgLineHeader(&self.base, module); self.debug_line_header_dirty = false; } @@ -1481,11 +1100,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { self.shstrtab_dirty = false; } } + { const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?]; - if (self.debug_strtab_dirty or self.debug_strtab.items.len != debug_strtab_sect.sh_size) { + const dwarf = self.dwarf.?; + if (self.debug_strtab_dirty or dwarf.strtab.items.len != debug_strtab_sect.sh_size) { const allocated_size = self.allocatedSize(debug_strtab_sect.sh_offset); - const needed_size = self.debug_strtab.items.len; + const needed_size = dwarf.strtab.items.len; if (needed_size > allocated_size) { debug_strtab_sect.sh_size = 0; // free the space @@ -1494,7 +1115,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { debug_strtab_sect.sh_size = needed_size; log.debug("debug_strtab start=0x{x} end=0x{x}", .{ debug_strtab_sect.sh_offset, debug_strtab_sect.sh_offset + needed_size }); - try self.base.file.?.pwriteAll(self.debug_strtab.items, debug_strtab_sect.sh_offset); + try self.base.file.?.pwriteAll(dwarf.strtab.items, debug_strtab_sect.sh_offset); if (!self.shdr_table_dirty) { // Then it won't get written with the others and we need to do it. try self.writeSectHeader(self.debug_str_section_index.?); @@ -1502,6 +1123,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { self.debug_strtab_dirty = false; } } + if (self.shdr_table_dirty) { const shsize: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Shdr), @@ -2340,7 +1962,6 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void { i += 1; } } - // TODO process free list for dbg info just like we do above for vaddrs if (self.atoms.getPtr(phdr_index)) |last_block| { if (last_block.* == text_block) { @@ -2353,14 +1974,6 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void { } } - if (self.dbg_info_decl_first == text_block) { - self.dbg_info_decl_first = text_block.dbg_info_next; - } - if (self.dbg_info_decl_last == text_block) { - // TODO shrink the .debug_info section size here - self.dbg_info_decl_last = text_block.dbg_info_prev; - } - if (text_block.prev) |prev| { prev.next = text_block.next; @@ -2379,18 +1992,8 @@ fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void { text_block.next = null; } - if (text_block.dbg_info_prev) |prev| { - prev.dbg_info_next = text_block.dbg_info_next; - - // TODO the free list logic like we do for text blocks above - } else { - text_block.dbg_info_prev = null; - } - - if (text_block.dbg_info_next) |next| { - next.dbg_info_prev = text_block.dbg_info_prev; - } else { - text_block.dbg_info_next = null; + if (self.dwarf) |*dw| { + dw.freeAtom(&text_block.dbg_info_atom); } } @@ -2619,26 +2222,9 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {}; } - // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing - // is desired for both. - _ = self.dbg_line_fn_free_list.remove(&decl.fn_link.elf); - if (decl.fn_link.elf.prev) |prev| { - self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {}; - prev.next = decl.fn_link.elf.next; - if (decl.fn_link.elf.next) |next| { - next.prev = prev; - } else { - self.dbg_line_fn_last = prev; - } - } else if (decl.fn_link.elf.next) |next| { - self.dbg_line_fn_first = next; - next.prev = null; - } - if (self.dbg_line_fn_first == &decl.fn_link.elf) { - self.dbg_line_fn_first = decl.fn_link.elf.next; - } - if (self.dbg_line_fn_last == &decl.fn_link.elf) { - self.dbg_line_fn_last = decl.fn_link.elf.prev; + + if (self.dwarf) |*dw| { + dw.freeDecl(decl); } } @@ -2739,62 +2325,6 @@ fn updateDeclCode(self: *Elf, decl: *Module.Decl, code: []const u8, stt_bits: u8 return local_sym; } -fn finishUpdateDecl( - self: *Elf, - module: *Module, - decl: *Module.Decl, - dbg_info_type_relocs: *File.DbgInfoTypeRelocsTable, - dbg_info_buffer: *std.ArrayList(u8), -) !void { - // 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(self.base.allocator); - defer dbg_type_arena.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]; - const value_ptr = dbg_info_type_relocs.getPtr(ty).?; - value_ptr.off = @intCast(u32, dbg_info_buffer.items.len); - try self.addDbgInfoType(dbg_type_arena.allocator(), ty, dbg_info_buffer, dbg_info_type_relocs); - } - } - - const text_block = &decl.link.elf; - try self.updateDeclDebugInfoAllocation(text_block, @intCast(u32, dbg_info_buffer.items.len)); - - const target_endian = self.base.options.target.cpu.arch.endian(); - - { - // Now that we have the offset assigned we can finally perform type relocations. - for (dbg_info_type_relocs.values()) |value| { - for (value.relocs.items) |off| { - mem.writeInt( - u32, - dbg_info_buffer.items[off..][0..4], - text_block.dbg_info_off + value.off, - target_endian, - ); - } - } - } - - try self.writeDeclDebugInfo(text_block, dbg_info_buffer.items); - - // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. - const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; - return self.updateDeclExports(module, decl, decl_exports); -} - pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { if (build_options.skip_non_native and builtin.object_format != .elf) { @panic("Attempted to compile for object format that was disabled by build configuration"); @@ -2809,96 +2339,33 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - // For functions we need to add a prologue to the debug line program. - var dbg_line_buffer = try std.ArrayList(u8).initCapacity(self.base.allocator, 26); - defer dbg_line_buffer.deinit(); - - var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator); - defer dbg_info_buffer.deinit(); - - var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{}; - defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs); - const decl = func.owner_decl; self.freeUnnamedConsts(decl); - const decl_name = try decl.getFullyQualifiedName(self.base.allocator); - defer self.base.allocator.free(decl_name); - - log.debug("updateFunc {s}{*}", .{ decl_name, func.owner_decl }); - log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{ - decl.src_line, - func.lbrace_line, - func.rbrace_line, - }); - const line = @intCast(u28, decl.src_line + func.lbrace_line); - - const ptr_width_bytes = self.ptrWidthBytes(); - dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{ - DW.LNS.extended_op, - ptr_width_bytes + 1, - DW.LNE.set_address, - }); - // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`. - assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len); - dbg_line_buffer.items.len += ptr_width_bytes; - - dbg_line_buffer.appendAssumeCapacity(DW.LNS.advance_line); - // This is the "relocatable" relative line offset from the previous function's end curly - // to this function's begin curly. - assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len); - // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later. - leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line); - - dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file); - assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len); - // Once we support more than one source file, this will have the ability to be more - // than one possible value. - const file_index = 1; - leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index); - - // Emit a line for the begin curly with prologue_end=false. The codegen will - // do the work of setting prologue_end=true and epilogue_begin=true. - dbg_line_buffer.appendAssumeCapacity(DW.LNS.copy); - - // .debug_info subprogram - const decl_name_with_null = decl_name[0 .. decl_name.len + 1]; - try dbg_info_buffer.ensureUnusedCapacity(25 + decl_name_with_null.len); - - const fn_ret_type = decl.ty.fnReturnType(); - const fn_ret_has_bits = fn_ret_type.hasRuntimeBits(); - if (fn_ret_has_bits) { - dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram); - } else { - dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid); - } - // These get overwritten after generating the machine code. These values are - // "relocations" and have to be in this fixed place so that functions can be - // moved in virtual address space. - assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len); - 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.getOrPut(self.base.allocator, fn_ret_type); - if (!gop.found_existing) { - gop.value_ptr.* = .{ - .off = undefined, - .relocs = .{}, - }; + 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); } - try gop.value_ptr.relocs.append(self.base.allocator, @intCast(u32, dbg_info_buffer.items.len)); - 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 - const res = try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ - .dwarf = .{ - .dbg_line = &dbg_line_buffer, - .dbg_info = &dbg_info_buffer, - .dbg_info_type_relocs = &dbg_info_type_relocs, - }, - }); + const res = if (debug_buffers) |dbg| + 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, + }, + }) + else + try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); + const code = switch (res) { .appended => code_buffer.items, .fail => |em| { @@ -2907,128 +2374,14 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven return; }, }; - const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC); - - const target_endian = self.base.options.target.cpu.arch.endian(); - - // Since the Decl is a function, we need to update the .debug_line program. - // Perform the relocations based on vaddr. - switch (self.ptr_width) { - .p32 => { - { - const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4]; - mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian); - } - { - const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..4]; - mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian); - } - }, - .p64 => { - { - const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8]; - mem.writeInt(u64, ptr, local_sym.st_value, target_endian); - } - { - const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8]; - mem.writeInt(u64, ptr, local_sym.st_value, target_endian); - } - }, + if (debug_buffers) |dbg| { + try self.dwarf.?.commitDeclDebugInfo(&self.base, module, decl, local_sym.st_value, local_sym.st_size, dbg); } - { - const ptr = dbg_info_buffer.items[self.getRelocDbgInfoSubprogramHighPC()..][0..4]; - mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_size), target_endian); - } - - try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS.extended_op, 1, DW.LNE.end_sequence }); - - // Now we have the full contents and may allocate a region to store it. - - // This logic is nearly identical to the logic below in `updateDeclDebugInfoAllocation` for - // `TextBlock` and the .debug_info. If you are editing this logic, you - // probably need to edit that logic too. - - const debug_line_sect = &self.sections.items[self.debug_line_section_index.?]; - const src_fn = &decl.fn_link.elf; - src_fn.len = @intCast(u32, dbg_line_buffer.items.len); - if (self.dbg_line_fn_last) |last| not_first: { - if (src_fn.next) |next| { - // Update existing function - non-last item. - if (src_fn.off + src_fn.len + min_nop_size > next.off) { - // It grew too big, so we move it to a new location. - if (src_fn.prev) |prev| { - self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {}; - prev.next = src_fn.next; - } - assert(src_fn.prev != next); - next.prev = src_fn.prev; - src_fn.next = null; - // Populate where it used to be with NOPs. - const file_pos = debug_line_sect.sh_offset + src_fn.off; - try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos); - // TODO Look at the free list before appending at the end. - src_fn.prev = last; - last.next = src_fn; - self.dbg_line_fn_last = src_fn; - - src_fn.off = last.off + padToIdeal(last.len); - } - } else if (src_fn.prev == null) { - if (src_fn == last) { - // Special case: there is only 1 function and it is being updated. - // In this case there is nothing to do. The function's length has - // already been updated, and the logic below takes care of - // resizing the .debug_line section. - break :not_first; - } - // Append new function. - // TODO Look at the free list before appending at the end. - src_fn.prev = last; - last.next = src_fn; - self.dbg_line_fn_last = src_fn; - src_fn.off = last.off + padToIdeal(last.len); - } - } else { - // This is the first function of the Line Number Program. - self.dbg_line_fn_first = src_fn; - self.dbg_line_fn_last = src_fn; - - src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes()); - } - - const last_src_fn = self.dbg_line_fn_last.?; - const needed_size = last_src_fn.off + last_src_fn.len; - if (needed_size != debug_line_sect.sh_size) { - if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) { - const new_offset = self.findFreeSpace(needed_size, 1); - const existing_size = last_src_fn.off; - log.debug("moving .debug_line section: {d} bytes from 0x{x} to 0x{x}", .{ - existing_size, - debug_line_sect.sh_offset, - new_offset, - }); - const amt = try self.base.file.?.copyRangeAll(debug_line_sect.sh_offset, self.base.file.?, new_offset, existing_size); - if (amt != existing_size) return error.InputOutput; - debug_line_sect.sh_offset = new_offset; - } - debug_line_sect.sh_size = needed_size; - self.shdr_table_dirty = true; // TODO look into making only the one section dirty - self.debug_line_header_dirty = true; - } - const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0; - const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0; - - // We only have support for one compilation unit so far, so the offsets are directly - // from the .debug_line section. - const file_pos = debug_line_sect.sh_offset + src_fn.off; - try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos); - - // .debug_info - End the TAG.subprogram children. - try dbg_info_buffer.append(0); - - return self.finishUpdateDecl(module, decl, &dbg_info_type_relocs, &dbg_info_buffer); + // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. + const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; + return self.updateDeclExports(module, decl, decl_exports); } pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { @@ -3057,29 +2410,42 @@ 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 dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator); - defer dbg_line_buffer.deinit(); - - var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator); - defer dbg_info_buffer.deinit(); - - var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{}; - defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs); + 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); + } + } // TODO implement .debug_info for global variables const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; - const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ - .ty = decl.ty, - .val = decl_val, - }, &code_buffer, .{ - .dwarf = .{ - .dbg_line = &dbg_line_buffer, - .dbg_info = &dbg_info_buffer, - .dbg_info_type_relocs = &dbg_info_type_relocs, - }, - }, .{ - .parent_atom_index = decl.link.elf.local_sym_index, - }); + const res = if (debug_buffers) |dbg| + 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, + }, + }, .{ + .parent_atom_index = decl.link.elf.local_sym_index, + }) + else + try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ + .ty = decl.ty, + .val = decl_val, + }, &code_buffer, .none, .{ + .parent_atom_index = decl.link.elf.local_sym_index, + }); + const code = switch (res) { .externally_managed => |x| x, .appended => code_buffer.items, @@ -3090,8 +2456,14 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { }, }; - _ = try self.updateDeclCode(decl, code, elf.STT_OBJECT); - return self.finishUpdateDecl(module, decl, &dbg_info_type_relocs, &dbg_info_buffer); + 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); + } + + // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. + const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; + return self.updateDeclExports(module, decl, decl_exports); } pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl) !u32 { @@ -3170,304 +2542,6 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl return atom.local_sym_index; } -/// Asserts the type has codegen bits. -fn addDbgInfoType( - self: *Elf, - arena: Allocator, - ty: Type, - dbg_info_buffer: *std.ArrayList(u8), - dbg_info_type_relocs: *File.DbgInfoTypeRelocsTable, -) error{OutOfMemory}!void { - const target = self.base.options.target; - var relocs = std.ArrayList(struct { ty: Type, reloc: u32 }).init(arena); - - switch (ty.zigTypeTag()) { - .NoReturn => unreachable, - .Void => { - try dbg_info_buffer.append(abbrev_pad1); - }, - .Bool => { - try dbg_info_buffer.appendSlice(&[_]u8{ - abbrev_base_type, - DW.ATE.boolean, // DW.AT.encoding , DW.FORM.data1 - 1, // DW.AT.byte_size, DW.FORM.data1 - 'b', 'o', 'o', 'l', 0, // DW.AT.name, DW.FORM.string - }); - }, - .Int => { - const info = ty.intInfo(target); - try dbg_info_buffer.ensureUnusedCapacity(12); - dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); - // DW.AT.encoding, DW.FORM.data1 - dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) { - .signed => DW.ATE.signed, - .unsigned => DW.ATE.unsigned, - }); - // DW.AT.byte_size, DW.FORM.data1 - dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); - // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty}); - }, - .Optional => { - if (ty.isPtrLikeOptional()) { - try dbg_info_buffer.ensureUnusedCapacity(12); - dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); - // DW.AT.encoding, DW.FORM.data1 - dbg_info_buffer.appendAssumeCapacity(DW.ATE.address); - // DW.AT.byte_size, DW.FORM.data1 - dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); - // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty}); - } else { - // Non-pointer optionals are structs: struct { .maybe = *, .val = * } - var buf = try arena.create(Type.Payload.ElemType); - const payload_ty = ty.optionalChild(buf); - // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_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 - try dbg_info_buffer.writer().print("{}\x00", .{ty}); - // 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("maybe"); - 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 = Type.bool, .reloc = @intCast(u32, index) }); - // DW.AT.data_member_location, DW.FORM.sdata - try dbg_info_buffer.ensureUnusedCapacity(6); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.member - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity("val"); - 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 = payload_ty, .reloc = @intCast(u32, index) }); - // DW.AT.data_member_location, DW.FORM.sdata - const offset = abi_size - payload_ty.abiSize(target); - try leb128.writeULEB128(dbg_info_buffer.writer(), offset); - // DW.AT.structure_type delimit children - try dbg_info_buffer.append(0); - } - }, - .Pointer => { - if (ty.isSlice()) { - // Slices are structs: struct { .ptr = *, .len = N } - // DW.AT.structure_type - try dbg_info_buffer.ensureUnusedCapacity(2); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_type); - // DW.AT.byte_size, DW.FORM.sdata - dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize) * 2); - // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty}); - // 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("ptr"); - 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); - var buf = try arena.create(Type.SlicePtrFieldTypeBuffer); - const ptr_ty = ty.slicePtrFieldType(buf); - try relocs.append(.{ .ty = ptr_ty, .reloc = @intCast(u32, index) }); - // DW.AT.data_member_location, DW.FORM.sdata - try dbg_info_buffer.ensureUnusedCapacity(6); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.member - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity("len"); - 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 = Type.initTag(.usize), .reloc = @intCast(u32, index) }); - // DW.AT.data_member_location, DW.FORM.sdata - try dbg_info_buffer.ensureUnusedCapacity(2); - dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize)); - // DW.AT.structure_type delimit children - dbg_info_buffer.appendAssumeCapacity(0); - } else { - try dbg_info_buffer.ensureUnusedCapacity(5); - dbg_info_buffer.appendAssumeCapacity(abbrev_ptr_type); - // 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) }); - } - }, - .Struct => blk: { - // try dbg_info_buffer.ensureUnusedCapacity(23); - // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_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 struct_name = try ty.nameAlloc(arena); - try dbg_info_buffer.ensureUnusedCapacity(struct_name.len + 1); - dbg_info_buffer.appendSliceAssumeCapacity(struct_name); - dbg_info_buffer.appendAssumeCapacity(0); - - const struct_obj = ty.castTag(.@"struct").?.data; - if (struct_obj.layout == .Packed) { - log.debug("TODO implement .debug_info for packed structs", .{}); - break :blk; - } - - const fields = ty.structFields(); - for (fields.keys()) |field_name, field_index| { - const field = fields.get(field_name).?; - // DW.AT.member - try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity(field_name); - 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 = field.ty, .reloc = @intCast(u32, index) }); - // 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); - } - - // DW.AT.structure_type delimit children - try dbg_info_buffer.append(0); - }, - else => { - log.debug("TODO implement .debug_info for type '{}'", .{ty}); - try dbg_info_buffer.append(abbrev_pad1); - }, - } - - for (relocs.items) |rel| { - const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, rel.ty); - if (!gop.found_existing) { - gop.value_ptr.* = .{ - .off = undefined, - .relocs = .{}, - }; - } - try gop.value_ptr.relocs.append(self.base.allocator, rel.reloc); - } -} - -fn updateDeclDebugInfoAllocation(self: *Elf, text_block: *TextBlock, len: u32) !void { - const tracy = trace(@src()); - defer tracy.end(); - - // This logic is nearly identical to the logic above in `updateDecl` for - // `SrcFn` and the line number programs. If you are editing this logic, you - // probably need to edit that logic too. - - const debug_info_sect = &self.sections.items[self.debug_info_section_index.?]; - text_block.dbg_info_len = len; - if (self.dbg_info_decl_last) |last| not_first: { - if (text_block.dbg_info_next) |next| { - // Update existing Decl - non-last item. - if (text_block.dbg_info_off + text_block.dbg_info_len + min_nop_size > next.dbg_info_off) { - // It grew too big, so we move it to a new location. - if (text_block.dbg_info_prev) |prev| { - self.dbg_info_decl_free_list.put(self.base.allocator, prev, {}) catch {}; - prev.dbg_info_next = text_block.dbg_info_next; - } - next.dbg_info_prev = text_block.dbg_info_prev; - text_block.dbg_info_next = null; - // Populate where it used to be with NOPs. - const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off; - try self.pwriteDbgInfoNops(0, &[0]u8{}, text_block.dbg_info_len, false, file_pos); - // TODO Look at the free list before appending at the end. - text_block.dbg_info_prev = last; - last.dbg_info_next = text_block; - self.dbg_info_decl_last = text_block; - - text_block.dbg_info_off = last.dbg_info_off + padToIdeal(last.dbg_info_len); - } - } else if (text_block.dbg_info_prev == null) { - if (text_block == last) { - // Special case: there is only 1 .debug_info block and it is being updated. - // In this case there is nothing to do. The block's length has - // already been updated, and logic in writeDeclDebugInfo takes care of - // resizing the .debug_info section. - break :not_first; - } - // Append new Decl. - // TODO Look at the free list before appending at the end. - text_block.dbg_info_prev = last; - last.dbg_info_next = text_block; - self.dbg_info_decl_last = text_block; - - text_block.dbg_info_off = last.dbg_info_off + padToIdeal(last.dbg_info_len); - } - } else { - // This is the first Decl of the .debug_info - self.dbg_info_decl_first = text_block; - self.dbg_info_decl_last = text_block; - - text_block.dbg_info_off = padToIdeal(self.dbgInfoNeededHeaderBytes()); - } -} - -fn writeDeclDebugInfo(self: *Elf, text_block: *TextBlock, dbg_info_buf: []const u8) !void { - const tracy = trace(@src()); - defer tracy.end(); - - // This logic is nearly identical to the logic above in `updateDecl` for - // `SrcFn` and the line number programs. If you are editing this logic, you - // probably need to edit that logic too. - - const debug_info_sect = &self.sections.items[self.debug_info_section_index.?]; - - const last_decl = self.dbg_info_decl_last.?; - // +1 for a trailing zero to end the children of the decl tag. - const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len + 1; - if (needed_size != debug_info_sect.sh_size) { - if (needed_size > self.allocatedSize(debug_info_sect.sh_offset)) { - const new_offset = self.findFreeSpace(needed_size, 1); - const existing_size = last_decl.dbg_info_off; - log.debug("moving .debug_info section: {} bytes from 0x{x} to 0x{x}", .{ - existing_size, - debug_info_sect.sh_offset, - new_offset, - }); - const amt = try self.base.file.?.copyRangeAll(debug_info_sect.sh_offset, self.base.file.?, new_offset, existing_size); - if (amt != existing_size) return error.InputOutput; - debug_info_sect.sh_offset = new_offset; - } - debug_info_sect.sh_size = needed_size; - self.shdr_table_dirty = true; // TODO look into making only the one section dirty - self.debug_info_header_dirty = true; - } - const prev_padding_size: u32 = if (text_block.dbg_info_prev) |prev| - text_block.dbg_info_off - (prev.dbg_info_off + prev.dbg_info_len) - else - 0; - const next_padding_size: u32 = if (text_block.dbg_info_next) |next| - next.dbg_info_off - (text_block.dbg_info_off + text_block.dbg_info_len) - else - 0; - - // To end the children of the decl tag. - const trailing_zero = text_block.dbg_info_next == null; - - // We only have support for one compilation unit so far, so the offsets are directly - // from the .debug_info section. - const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off; - try self.pwriteDbgInfoNops(prev_padding_size, dbg_info_buf, next_padding_size, trailing_zero, file_pos); -} - pub fn updateDeclExports( self: *Elf, module: *Module, @@ -3568,20 +2642,9 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl }); if (self.llvm_object) |_| return; - - const func = decl.val.castTag(.function).?.data; - log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{ - decl.src_line, - func.lbrace_line, - func.rbrace_line, - }); - const line = @intCast(u28, decl.src_line + func.lbrace_line); - - const shdr = &self.sections.items[self.debug_line_section_index.?]; - const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff(); - var data: [4]u8 = undefined; - leb128.writeUnsignedFixed(4, &data, line); - try self.base.file.?.pwriteAll(&data, file_pos); + if (self.dwarf) |*dw| { + try dw.updateDeclLineNumber(&self.base, decl); + } } pub fn deleteExport(self: *Elf, exp: Export) void { @@ -3806,201 +2869,6 @@ fn archPtrWidthBytes(self: Elf) u8 { return @intCast(u8, self.base.options.target.cpu.arch.ptrBitWidth() / 8); } -/// The reloc offset for the virtual address of a function in its Line Number Program. -/// Size is a virtual address integer. -const dbg_line_vaddr_reloc_index = 3; -/// The reloc offset for the virtual address of a function in its .debug_info TAG.subprogram. -/// Size is a virtual address integer. -const dbg_info_low_pc_reloc_index = 1; - -/// The reloc offset for the line offset of a function from the previous function's line. -/// It's a fixed-size 4-byte ULEB128. -fn getRelocDbgLineOff(self: Elf) usize { - return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1; -} - -fn getRelocDbgFileIndex(self: Elf) usize { - return self.getRelocDbgLineOff() + 5; -} - -fn getRelocDbgInfoSubprogramHighPC(self: Elf) u32 { - return dbg_info_low_pc_reloc_index + self.ptrWidthBytes(); -} - -fn dbgLineNeededHeaderBytes(self: Elf) u32 { - const directory_entry_format_count = 1; - const file_name_entry_format_count = 1; - const directory_count = 1; - const file_name_count = 1; - const root_src_dir_path_len = if (self.base.options.module.?.root_pkg.root_src_directory.path) |p| p.len else 1; // "." - return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 + - directory_count * 8 + file_name_count * 8 + - // These are encoded as DW.FORM.string rather than DW.FORM.strp as we would like - // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. - root_src_dir_path_len + - self.base.options.module.?.root_pkg.root_src_path.len); -} - -fn dbgInfoNeededHeaderBytes(self: Elf) u32 { - _ = self; - return 120; -} - -const min_nop_size = 2; - -/// Writes to the file a buffer, prefixed and suffixed by the specified number of -/// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes -/// are less than 1044480 bytes (if this limit is ever reached, this function can be -/// improved to make more than one pwritev call, or the limit can be raised by a fixed -/// amount by increasing the length of `vecs`). -fn pwriteDbgLineNops( - self: *Elf, - prev_padding_size: usize, - buf: []const u8, - next_padding_size: usize, - offset: u64, -) !void { - const tracy = trace(@src()); - defer tracy.end(); - - const page_of_nops = [1]u8{DW.LNS.negate_stmt} ** 4096; - const three_byte_nop = [3]u8{ DW.LNS.advance_pc, 0b1000_0000, 0 }; - var vecs: [512]std.os.iovec_const = undefined; - var vec_index: usize = 0; - { - var padding_left = prev_padding_size; - if (padding_left % 2 != 0) { - vecs[vec_index] = .{ - .iov_base = &three_byte_nop, - .iov_len = three_byte_nop.len, - }; - vec_index += 1; - padding_left -= three_byte_nop.len; - } - while (padding_left > page_of_nops.len) { - vecs[vec_index] = .{ - .iov_base = &page_of_nops, - .iov_len = page_of_nops.len, - }; - vec_index += 1; - padding_left -= page_of_nops.len; - } - if (padding_left > 0) { - vecs[vec_index] = .{ - .iov_base = &page_of_nops, - .iov_len = padding_left, - }; - vec_index += 1; - } - } - - vecs[vec_index] = .{ - .iov_base = buf.ptr, - .iov_len = buf.len, - }; - vec_index += 1; - - { - var padding_left = next_padding_size; - if (padding_left % 2 != 0) { - vecs[vec_index] = .{ - .iov_base = &three_byte_nop, - .iov_len = three_byte_nop.len, - }; - vec_index += 1; - padding_left -= three_byte_nop.len; - } - while (padding_left > page_of_nops.len) { - vecs[vec_index] = .{ - .iov_base = &page_of_nops, - .iov_len = page_of_nops.len, - }; - vec_index += 1; - padding_left -= page_of_nops.len; - } - if (padding_left > 0) { - vecs[vec_index] = .{ - .iov_base = &page_of_nops, - .iov_len = padding_left, - }; - vec_index += 1; - } - } - try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); -} - -/// Writes to the file a buffer, prefixed and suffixed by the specified number of -/// bytes of padding. -fn pwriteDbgInfoNops( - self: *Elf, - prev_padding_size: usize, - buf: []const u8, - next_padding_size: usize, - trailing_zero: bool, - offset: u64, -) !void { - const tracy = trace(@src()); - defer tracy.end(); - - const page_of_nops = [1]u8{abbrev_pad1} ** 4096; - var vecs: [32]std.os.iovec_const = undefined; - var vec_index: usize = 0; - { - var padding_left = prev_padding_size; - while (padding_left > page_of_nops.len) { - vecs[vec_index] = .{ - .iov_base = &page_of_nops, - .iov_len = page_of_nops.len, - }; - vec_index += 1; - padding_left -= page_of_nops.len; - } - if (padding_left > 0) { - vecs[vec_index] = .{ - .iov_base = &page_of_nops, - .iov_len = padding_left, - }; - vec_index += 1; - } - } - - vecs[vec_index] = .{ - .iov_base = buf.ptr, - .iov_len = buf.len, - }; - vec_index += 1; - - { - var padding_left = next_padding_size; - while (padding_left > page_of_nops.len) { - vecs[vec_index] = .{ - .iov_base = &page_of_nops, - .iov_len = page_of_nops.len, - }; - vec_index += 1; - padding_left -= page_of_nops.len; - } - if (padding_left > 0) { - vecs[vec_index] = .{ - .iov_base = &page_of_nops, - .iov_len = padding_left, - }; - vec_index += 1; - } - } - - if (trailing_zero) { - var zbuf = [1]u8{0}; - vecs[vec_index] = .{ - .iov_base = &zbuf, - .iov_len = zbuf.len, - }; - vec_index += 1; - } - - try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); -} - fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr { return .{ .p_type = phdr.p_type, |
