| ... | @@ -340,6 +340,7 @@ pub const File = struct { | ... | @@ -340,6 +340,7 @@ pub const File = struct { |
| 340 | debug_info_section_index: ?u16 = null, | 340 | debug_info_section_index: ?u16 = null, |
| 341 | debug_abbrev_section_index: ?u16 = null, | 341 | debug_abbrev_section_index: ?u16 = null, |
| 342 | debug_str_section_index: ?u16 = null, | 342 | debug_str_section_index: ?u16 = null, |
| | 343 | debug_aranges_section_index: ?u16 = null, |
| 343 | | 344 | |
| 344 | debug_abbrev_table_offset: ?u64 = null, | 345 | debug_abbrev_table_offset: ?u64 = null, |
| 345 | | 346 | |
| ... | @@ -366,6 +367,7 @@ pub const File = struct { | ... | @@ -366,6 +367,7 @@ pub const File = struct { |
| 366 | offset_table_count_dirty: bool = false, | 367 | offset_table_count_dirty: bool = false, |
| 367 | debug_info_section_dirty: bool = false, | 368 | debug_info_section_dirty: bool = false, |
| 368 | debug_abbrev_section_dirty: bool = false, | 369 | debug_abbrev_section_dirty: bool = false, |
| | 370 | debug_aranges_section_dirty: bool = false, |
| 369 | | 371 | |
| 370 | error_flags: ErrorFlags = ErrorFlags{}, | 372 | error_flags: ErrorFlags = ErrorFlags{}, |
| 371 | | 373 | |
| ... | @@ -791,6 +793,31 @@ pub const File = struct { | ... | @@ -791,6 +793,31 @@ pub const File = struct { |
| 791 | self.shdr_table_dirty = true; | 793 | self.shdr_table_dirty = true; |
| 792 | self.debug_abbrev_section_dirty = true; | 794 | self.debug_abbrev_section_dirty = true; |
| 793 | } | 795 | } |
| | 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 | } |
| 794 | const shsize: u64 = switch (self.ptr_width) { | 821 | const shsize: u64 = switch (self.ptr_width) { |
| 795 | .p32 => @sizeOf(elf.Elf32_Shdr), | 822 | .p32 => @sizeOf(elf.Elf32_Shdr), |
| 796 | .p64 => @sizeOf(elf.Elf64_Shdr), | 823 | .p64 => @sizeOf(elf.Elf64_Shdr), |
| ... | @@ -828,6 +855,10 @@ pub const File = struct { | ... | @@ -828,6 +855,10 @@ pub const File = struct { |
| 828 | pub fn flush(self: *Elf) !void { | 855 | pub fn flush(self: *Elf) !void { |
| 829 | const target_endian = self.base.options.target.cpu.arch.endian(); | 856 | const target_endian = self.base.options.target.cpu.arch.endian(); |
| 830 | const foreign_endian = target_endian != std.Target.current.cpu.arch.endian(); | 857 | 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 | }; |
| 831 | | 862 | |
| 832 | // Unfortunately these have to be buffered and done at the end because ELF does not allow | 863 | // Unfortunately these have to be buffered and done at the end because ELF does not allow |
| 833 | // mixing local and global symbols within a symbol table. | 864 | // mixing local and global symbols within a symbol table. |
| ... | @@ -960,6 +991,80 @@ pub const File = struct { | ... | @@ -960,6 +991,80 @@ pub const File = struct { |
| 960 | | 991 | |
| 961 | self.debug_info_section_dirty = false; | 992 | self.debug_info_section_dirty = false; |
| 962 | } | 993 | } |
| | 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 | } |
| 963 | | 1068 | |
| 964 | if (self.phdr_table_dirty) { | 1069 | if (self.phdr_table_dirty) { |
| 965 | const phsize: u64 = switch (self.ptr_width) { | 1070 | const phsize: u64 = switch (self.ptr_width) { |
| ... | @@ -1106,6 +1211,7 @@ pub const File = struct { | ... | @@ -1106,6 +1211,7 @@ pub const File = struct { |
| 1106 | // The point of flush() is to commit changes, so nothing should be dirty after this. | 1211 | // The point of flush() is to commit changes, so nothing should be dirty after this. |
| 1107 | assert(!self.debug_info_section_dirty); | 1212 | assert(!self.debug_info_section_dirty); |
| 1108 | assert(!self.debug_abbrev_section_dirty); | 1213 | assert(!self.debug_abbrev_section_dirty); |
| | 1214 | assert(!self.debug_aranges_section_dirty); |
| 1109 | assert(!self.phdr_table_dirty); | 1215 | assert(!self.phdr_table_dirty); |
| 1110 | assert(!self.shdr_table_dirty); | 1216 | assert(!self.shdr_table_dirty); |
| 1111 | assert(!self.shstrtab_dirty); | 1217 | assert(!self.shstrtab_dirty); |
| ... | @@ -1387,6 +1493,10 @@ pub const File = struct { | ... | @@ -1387,6 +1493,10 @@ pub const File = struct { |
| 1387 | // range of the compilation unit. When we expand the text section, this range changes, | 1493 | // range of the compilation unit. When we expand the text section, this range changes, |
| 1388 | // so the .debug_info section becomes dirty. | 1494 | // so the .debug_info section becomes dirty. |
| 1389 | self.debug_info_section_dirty = true; | 1495 | 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; |
| 1390 | | 1500 | |
| 1391 | self.phdr_table_dirty = true; // TODO look into making only the one program header dirty | 1501 | self.phdr_table_dirty = true; // TODO look into making only the one program header dirty |
| 1392 | self.shdr_table_dirty = true; // TODO look into making only the one section dirty | 1502 | self.shdr_table_dirty = true; // TODO look into making only the one section dirty |