authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-31 01:16:34-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-31 01:16:34-07:00
log0962cc5a32a9d48e408ee0cfc95d2b2a533b8c1d
treebaae569eba506997df8c8def73eba2776e556b61
parent9d0872a625f511cee0628c92e5d4a0797641ad24

stage2: implement .debug_aranges DWARF


1 files changed, 110 insertions(+), 0 deletions(-)

src-self-hosted/link.zig+110
......@@ -340,6 +340,7 @@ pub const File = struct {
340340 debug_info_section_index: ?u16 = null,
341341 debug_abbrev_section_index: ?u16 = null,
342342 debug_str_section_index: ?u16 = null,
343 debug_aranges_section_index: ?u16 = null,
343344
344345 debug_abbrev_table_offset: ?u64 = null,
345346
......@@ -366,6 +367,7 @@ pub const File = struct {
366367 offset_table_count_dirty: bool = false,
367368 debug_info_section_dirty: bool = false,
368369 debug_abbrev_section_dirty: bool = false,
370 debug_aranges_section_dirty: bool = false,
369371
370372 error_flags: ErrorFlags = ErrorFlags{},
371373
......@@ -791,6 +793,31 @@ pub const File = struct {
791793 self.shdr_table_dirty = true;
792794 self.debug_abbrev_section_dirty = true;
793795 }
796 if (self.debug_aranges_section_index == null) {
797 self.debug_aranges_section_index = @intCast(u16, self.sections.items.len);
798
799 const file_size_hint = 160;
800 const p_align = 16;
801 const off = self.findFreeSpace(file_size_hint, p_align);
802 log.debug(.link, "found .debug_aranges free space 0x{x} to 0x{x}\n", .{
803 off,
804 off + file_size_hint,
805 });
806 try self.sections.append(self.allocator, .{
807 .sh_name = try self.makeString(".debug_aranges"),
808 .sh_type = elf.SHT_PROGBITS,
809 .sh_flags = 0,
810 .sh_addr = 0,
811 .sh_offset = off,
812 .sh_size = file_size_hint,
813 .sh_link = 0,
814 .sh_info = 0,
815 .sh_addralign = p_align,
816 .sh_entsize = 0,
817 });
818 self.shdr_table_dirty = true;
819 self.debug_aranges_section_dirty = true;
820 }
794821 const shsize: u64 = switch (self.ptr_width) {
795822 .p32 => @sizeOf(elf.Elf32_Shdr),
796823 .p64 => @sizeOf(elf.Elf64_Shdr),
......@@ -828,6 +855,10 @@ pub const File = struct {
828855 pub fn flush(self: *Elf) !void {
829856 const target_endian = self.base.options.target.cpu.arch.endian();
830857 const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();
858 const ptr_width_bytes: u8 = switch (self.ptr_width) {
859 .p32 => 4,
860 .p64 => 8,
861 };
831862
832863 // Unfortunately these have to be buffered and done at the end because ELF does not allow
833864 // mixing local and global symbols within a symbol table.
......@@ -960,6 +991,80 @@ pub const File = struct {
960991
961992 self.debug_info_section_dirty = false;
962993 }
994 if (self.debug_aranges_section_dirty) {
995 const debug_aranges_sect = &self.sections.items[self.debug_aranges_section_index.?];
996
997 var di_buf = std.ArrayList(u8).init(self.allocator);
998 defer di_buf.deinit();
999
1000 // Enough for all the data without resizing. When support for more compilation units
1001 // is added, the size of this section will become more variable.
1002 try di_buf.ensureCapacity(100);
1003
1004 // initial length - length of the .debug_aranges contribution for this compilation unit,
1005 // not including the initial length itself.
1006 // We have to come back and write it later after we know the size.
1007 const init_len_index = di_buf.items.len;
1008 switch (self.ptr_width) {
1009 .p32 => di_buf.items.len += 4,
1010 .p64 => di_buf.items.len += 12,
1011 }
1012 const after_init_len = di_buf.items.len;
1013 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version
1014 // When more than one compilation unit is supported, this will be the offset to it.
1015 // For now it is always at offset 0 in .debug_info.
1016 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // .debug_info offset
1017 di_buf.appendAssumeCapacity(ptr_width_bytes); // address_size
1018 di_buf.appendAssumeCapacity(0); // segment_selector_size
1019
1020 const end_header_offset = di_buf.items.len;
1021 const begin_entries_offset = mem.alignForward(end_header_offset, ptr_width_bytes * 2);
1022 di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset);
1023
1024 // Currently only one compilation unit is supported, so the address range is simply
1025 // identical to the main program header virtual address and memory size.
1026 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1027 self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_vaddr);
1028 self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_memsz);
1029
1030 // Sentinel.
1031 self.writeDwarfAddrAssumeCapacity(&di_buf, 0);
1032 self.writeDwarfAddrAssumeCapacity(&di_buf, 0);
1033
1034 // Go back and populate the initial length.
1035 const init_len = di_buf.items.len - after_init_len;
1036 switch (self.ptr_width) {
1037 .p32 => {
1038 mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len), target_endian);
1039 },
1040 .p64 => {
1041 // initial length - length of the .debug_aranges contribution for this compilation unit,
1042 // not including the initial length itself.
1043 di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff };
1044 mem.writeInt(u64, di_buf.items[init_len_index + 4..][0..8], init_len, target_endian);
1045 },
1046 }
1047
1048 const needed_size = di_buf.items.len;
1049 const allocated_size = self.allocatedSize(debug_aranges_sect.sh_offset);
1050 if (needed_size > allocated_size) {
1051 debug_aranges_sect.sh_size = 0; // free the space
1052 debug_aranges_sect.sh_offset = self.findFreeSpace(needed_size, 16);
1053 }
1054 debug_aranges_sect.sh_size = needed_size;
1055 log.debug(.link, ".debug_aranges start=0x{x} end=0x{x}\n", .{
1056 debug_aranges_sect.sh_offset,
1057 debug_aranges_sect.sh_offset + needed_size,
1058 });
1059
1060 try self.file.?.pwriteAll(di_buf.items, debug_aranges_sect.sh_offset);
1061 if (!self.shdr_table_dirty) {
1062 // Then it won't get written with the others and we need to do it.
1063 try self.writeSectHeader(self.debug_aranges_section_index.?);
1064 }
1065
1066 self.debug_aranges_section_dirty = false;
1067 }
9631068
9641069 if (self.phdr_table_dirty) {
9651070 const phsize: u64 = switch (self.ptr_width) {
......@@ -1106,6 +1211,7 @@ pub const File = struct {
11061211 // The point of flush() is to commit changes, so nothing should be dirty after this.
11071212 assert(!self.debug_info_section_dirty);
11081213 assert(!self.debug_abbrev_section_dirty);
1214 assert(!self.debug_aranges_section_dirty);
11091215 assert(!self.phdr_table_dirty);
11101216 assert(!self.shdr_table_dirty);
11111217 assert(!self.shstrtab_dirty);
......@@ -1387,6 +1493,10 @@ pub const File = struct {
13871493 // range of the compilation unit. When we expand the text section, this range changes,
13881494 // so the .debug_info section becomes dirty.
13891495 self.debug_info_section_dirty = true;
1496 // This becomes dirty for the same reason. We could potentially make this more
1497 // fine-grained with the addition of support for more compilation units. It is planned to
1498 // model each package as a different compilation unit.
1499 self.debug_aranges_section_dirty = true;
13901500
13911501 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
13921502 self.shdr_table_dirty = true; // TODO look into making only the one section dirty