| ... | @@ -342,9 +342,10 @@ pub const File = struct { | ... | @@ -342,9 +342,10 @@ pub const File = struct { |
| 342 | shstrtab_dirty: bool = false, | 342 | shstrtab_dirty: bool = false, |
| 343 | debug_strtab_dirty: bool = false, | 343 | debug_strtab_dirty: bool = false, |
| 344 | offset_table_count_dirty: bool = false, | 344 | offset_table_count_dirty: bool = false, |
| 345 | debug_info_section_dirty: bool = false, | | |
| 346 | debug_abbrev_section_dirty: bool = false, | 345 | debug_abbrev_section_dirty: bool = false, |
| 347 | debug_aranges_section_dirty: bool = false, | 346 | debug_aranges_section_dirty: bool = false, |
| | 347 | |
| | 348 | debug_info_header_dirty: bool = false, |
| 348 | debug_line_header_dirty: bool = false, | 349 | debug_line_header_dirty: bool = false, |
| 349 | | 350 | |
| 350 | error_flags: ErrorFlags = ErrorFlags{}, | 351 | error_flags: ErrorFlags = ErrorFlags{}, |
| ... | @@ -364,7 +365,7 @@ pub const File = struct { | ... | @@ -364,7 +365,7 @@ pub const File = struct { |
| 364 | /// overcapacity can be negative. A simple way to have negative overcapacity is to | 365 | /// overcapacity can be negative. A simple way to have negative overcapacity is to |
| 365 | /// allocate a fresh text block, which will have ideal capacity, and then grow it | 366 | /// allocate a fresh text block, which will have ideal capacity, and then grow it |
| 366 | /// by 1 byte. It will then have -1 overcapacity. | 367 | /// by 1 byte. It will then have -1 overcapacity. |
| 367 | text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){}, | 368 | text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, |
| 368 | last_text_block: ?*TextBlock = null, | 369 | last_text_block: ?*TextBlock = null, |
| 369 | | 370 | |
| 370 | /// A list of `SrcFn` whose Line Number Programs have surplus capacity. | 371 | /// A list of `SrcFn` whose Line Number Programs have surplus capacity. |
| ... | @@ -373,6 +374,12 @@ pub const File = struct { | ... | @@ -373,6 +374,12 @@ pub const File = struct { |
| 373 | dbg_line_fn_first: ?*SrcFn = null, | 374 | dbg_line_fn_first: ?*SrcFn = null, |
| 374 | dbg_line_fn_last: ?*SrcFn = null, | 375 | dbg_line_fn_last: ?*SrcFn = null, |
| 375 | | 376 | |
| | 377 | /// A list of `TextBlock` whose corresponding .debug_info tags have surplus capacity. |
| | 378 | /// This is the same concept as `text_block_free_list`; see those doc comments. |
| | 379 | dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{}, |
| | 380 | dbg_info_decl_first: ?*TextBlock = null, |
| | 381 | dbg_info_decl_last: ?*TextBlock = null, |
| | 382 | |
| 376 | /// `alloc_num / alloc_den` is the factor of padding when allocating. | 383 | /// `alloc_num / alloc_den` is the factor of padding when allocating. |
| 377 | const alloc_num = 4; | 384 | const alloc_num = 4; |
| 378 | const alloc_den = 3; | 385 | const alloc_den = 3; |
| ... | @@ -398,11 +405,24 @@ pub const File = struct { | ... | @@ -398,11 +405,24 @@ pub const File = struct { |
| 398 | prev: ?*TextBlock, | 405 | prev: ?*TextBlock, |
| 399 | next: ?*TextBlock, | 406 | next: ?*TextBlock, |
| 400 | | 407 | |
| | 408 | /// Previous/next linked list pointers. This value is `next ^ prev`. |
| | 409 | /// This is the linked list node for this Decl's corresponding .debug_info tag. |
| | 410 | dbg_info_prev: ?*TextBlock, |
| | 411 | dbg_info_next: ?*TextBlock, |
| | 412 | /// Offset into .debug_info pointing to the tag for this Decl. |
| | 413 | dbg_info_off: u32, |
| | 414 | /// Size of the .debug_info tag for this Decl, not including padding. |
| | 415 | dbg_info_len: u32, |
| | 416 | |
| 401 | pub const empty = TextBlock{ | 417 | pub const empty = TextBlock{ |
| 402 | .local_sym_index = 0, | 418 | .local_sym_index = 0, |
| 403 | .offset_table_index = undefined, | 419 | .offset_table_index = undefined, |
| 404 | .prev = null, | 420 | .prev = null, |
| 405 | .next = null, | 421 | .next = null, |
| | 422 | .dbg_info_prev = null, |
| | 423 | .dbg_info_next = null, |
| | 424 | .dbg_info_off = undefined, |
| | 425 | .dbg_info_len = undefined, |
| 406 | }; | 426 | }; |
| 407 | | 427 | |
| 408 | /// Returns how much room there is to grow in virtual address space. | 428 | /// Returns how much room there is to grow in virtual address space. |
| ... | @@ -566,6 +586,7 @@ pub const File = struct { | ... | @@ -566,6 +586,7 @@ pub const File = struct { |
| 566 | self.offset_table_free_list.deinit(self.base.allocator); | 586 | self.offset_table_free_list.deinit(self.base.allocator); |
| 567 | self.text_block_free_list.deinit(self.base.allocator); | 587 | self.text_block_free_list.deinit(self.base.allocator); |
| 568 | self.dbg_line_fn_free_list.deinit(self.base.allocator); | 588 | self.dbg_line_fn_free_list.deinit(self.base.allocator); |
| | 589 | self.dbg_info_decl_free_list.deinit(self.base.allocator); |
| 569 | self.offset_table.deinit(self.base.allocator); | 590 | self.offset_table.deinit(self.base.allocator); |
| 570 | } | 591 | } |
| 571 | | 592 | |
| ... | @@ -854,7 +875,7 @@ pub const File = struct { | ... | @@ -854,7 +875,7 @@ pub const File = struct { |
| 854 | .sh_entsize = 0, | 875 | .sh_entsize = 0, |
| 855 | }); | 876 | }); |
| 856 | self.shdr_table_dirty = true; | 877 | self.shdr_table_dirty = true; |
| 857 | self.debug_info_section_dirty = true; | 878 | self.debug_info_header_dirty = true; |
| 858 | } | 879 | } |
| 859 | if (self.debug_abbrev_section_index == null) { | 880 | if (self.debug_abbrev_section_index == null) { |
| 860 | self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len); | 881 | self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len); |
| ... | @@ -1019,21 +1040,36 @@ pub const File = struct { | ... | @@ -1019,21 +1040,36 @@ pub const File = struct { |
| 1019 | | 1040 | |
| 1020 | self.debug_abbrev_section_dirty = false; | 1041 | self.debug_abbrev_section_dirty = false; |
| 1021 | } | 1042 | } |
| 1022 | if (self.debug_info_section_dirty) { | 1043 | |
| | 1044 | if (self.debug_info_header_dirty) debug_info: { |
| | 1045 | // If this value is null it means there is an error in the module; |
| | 1046 | // leave debug_info_header_dirty=true. |
| | 1047 | const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info; |
| | 1048 | const last_dbg_info_decl = self.dbg_info_decl_last.?; |
| 1023 | const debug_info_sect = &self.sections.items[self.debug_info_section_index.?]; | 1049 | const debug_info_sect = &self.sections.items[self.debug_info_section_index.?]; |
| 1024 | | 1050 | |
| 1025 | var di_buf = std.ArrayList(u8).init(self.base.allocator); | 1051 | var di_buf = std.ArrayList(u8).init(self.base.allocator); |
| 1026 | defer di_buf.deinit(); | 1052 | defer di_buf.deinit(); |
| 1027 | | 1053 | |
| 1028 | // Enough for a 64-bit header and main compilation unit without resizing. | 1054 | // We have a function to compute the upper bound size, because it's needed |
| 1029 | try di_buf.ensureCapacity(100); | 1055 | // for determining where to put the offset of the first `LinkBlock`. |
| | 1056 | try di_buf.ensureCapacity(self.dbgInfoNeededHeaderBytes()); |
| 1030 | | 1057 | |
| 1031 | // initial length - length of the .debug_info contribution for this compilation unit, | 1058 | // initial length - length of the .debug_info contribution for this compilation unit, |
| 1032 | // not including the initial length itself. | 1059 | // not including the initial length itself. |
| 1033 | // We have to come back and write it later after we know the size. | 1060 | // We have to come back and write it later after we know the size. |
| 1034 | const init_len_index = di_buf.items.len; | 1061 | const after_init_len = di_buf.items.len + init_len_size; |
| 1035 | di_buf.items.len += init_len_size; | 1062 | const dbg_info_end = last_dbg_info_decl.dbg_info_off + last_dbg_info_decl.dbg_info_len; |
| 1036 | const after_init_len = di_buf.items.len; | 1063 | const init_len = dbg_info_end - after_init_len; |
| | 1064 | switch (self.ptr_width) { |
| | 1065 | .p32 => { |
| | 1066 | mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian); |
| | 1067 | }, |
| | 1068 | .p64 => { |
| | 1069 | di_buf.appendNTimesAssumeCapacity(0xff, 4); |
| | 1070 | mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian); |
| | 1071 | }, |
| | 1072 | } |
| 1037 | mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version | 1073 | mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version |
| 1038 | const abbrev_offset = self.debug_abbrev_table_offset.?; | 1074 | const abbrev_offset = self.debug_abbrev_table_offset.?; |
| 1039 | switch (self.ptr_width) { | 1075 | switch (self.ptr_width) { |
| ... | @@ -1068,39 +1104,15 @@ pub const File = struct { | ... | @@ -1068,39 +1104,15 @@ pub const File = struct { |
| 1068 | // Until then we say it is C99. | 1104 | // Until then we say it is C99. |
| 1069 | mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99, target_endian); | 1105 | mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99, target_endian); |
| 1070 | | 1106 | |
| 1071 | const init_len = di_buf.items.len - after_init_len; | 1107 | if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) { |
| 1072 | switch (self.ptr_width) { | 1108 | // Move the first N decls to the end to make more padding for the header. |
| 1073 | .p32 => { | 1109 | @panic("TODO: handle .debug_info header exceeding its padding"); |
| 1074 | mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len), target_endian); | | |
| 1075 | }, | | |
| 1076 | .p64 => { | | |
| 1077 | // initial length - length of the .debug_info contribution for this compilation unit, | | |
| 1078 | // not including the initial length itself. | | |
| 1079 | di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff }; | | |
| 1080 | mem.writeInt(u64, di_buf.items[init_len_index + 4..][0..8], init_len, target_endian); | | |
| 1081 | }, | | |
| 1082 | } | | |
| 1083 | | | |
| 1084 | const needed_size = di_buf.items.len; | | |
| 1085 | const allocated_size = self.allocatedSize(debug_info_sect.sh_offset); | | |
| 1086 | if (needed_size > allocated_size) { | | |
| 1087 | debug_info_sect.sh_size = 0; // free the space | | |
| 1088 | debug_info_sect.sh_offset = self.findFreeSpace(needed_size, 1); | | |
| 1089 | } | | |
| 1090 | debug_info_sect.sh_size = needed_size; | | |
| 1091 | log.debug(.link, ".debug_info start=0x{x} end=0x{x}\n", .{ | | |
| 1092 | debug_info_sect.sh_offset, | | |
| 1093 | debug_info_sect.sh_offset + needed_size, | | |
| 1094 | }); | | |
| 1095 | | | |
| 1096 | try self.base.file.?.pwriteAll(di_buf.items, debug_info_sect.sh_offset); | | |
| 1097 | if (!self.shdr_table_dirty) { | | |
| 1098 | // Then it won't get written with the others and we need to do it. | | |
| 1099 | try self.writeSectHeader(self.debug_info_section_index.?); | | |
| 1100 | } | 1110 | } |
| 1101 | | 1111 | const jmp_amt = first_dbg_info_decl.dbg_info_off - di_buf.items.len; |
| 1102 | self.debug_info_section_dirty = false; | 1112 | try self.pwriteDbgInfoNops(0, di_buf.items, jmp_amt, debug_info_sect.sh_offset); |
| | 1113 | self.debug_info_header_dirty = false; |
| 1103 | } | 1114 | } |
| | 1115 | |
| 1104 | if (self.debug_aranges_section_dirty) { | 1116 | if (self.debug_aranges_section_dirty) { |
| 1105 | const debug_aranges_sect = &self.sections.items[self.debug_aranges_section_index.?]; | 1117 | const debug_aranges_sect = &self.sections.items[self.debug_aranges_section_index.?]; |
| 1106 | | 1118 | |
| ... | @@ -1266,7 +1278,7 @@ pub const File = struct { | ... | @@ -1266,7 +1278,7 @@ pub const File = struct { |
| 1266 | @panic("TODO: handle .debug_line header exceeding its padding"); | 1278 | @panic("TODO: handle .debug_line header exceeding its padding"); |
| 1267 | } | 1279 | } |
| 1268 | const jmp_amt = dbg_line_prg_off - di_buf.items.len; | 1280 | const jmp_amt = dbg_line_prg_off - di_buf.items.len; |
| 1269 | try self.pwriteWithNops(0, di_buf.items, jmp_amt, debug_line_sect.sh_offset); | 1281 | try self.pwriteDbgLineNops(0, di_buf.items, jmp_amt, debug_line_sect.sh_offset); |
| 1270 | self.debug_line_header_dirty = false; | 1282 | self.debug_line_header_dirty = false; |
| 1271 | } | 1283 | } |
| 1272 | | 1284 | |
| ... | @@ -1417,8 +1429,7 @@ pub const File = struct { | ... | @@ -1417,8 +1429,7 @@ pub const File = struct { |
| 1417 | // The point of flush() is to commit changes, so in theory, nothing should | 1429 | // The point of flush() is to commit changes, so in theory, nothing should |
| 1418 | // be dirty after this. However, it is possible for some things to remain | 1430 | // be dirty after this. However, it is possible for some things to remain |
| 1419 | // dirty because they fail to be written in the event of compile errors, | 1431 | // dirty because they fail to be written in the event of compile errors, |
| 1420 | // such as debug_line_header_dirty. | 1432 | // such as debug_line_header_dirty and debug_info_header_dirty. |
| 1421 | assert(!self.debug_info_section_dirty); | | |
| 1422 | assert(!self.debug_abbrev_section_dirty); | 1433 | assert(!self.debug_abbrev_section_dirty); |
| 1423 | assert(!self.debug_aranges_section_dirty); | 1434 | assert(!self.debug_aranges_section_dirty); |
| 1424 | assert(!self.phdr_table_dirty); | 1435 | assert(!self.phdr_table_dirty); |
| ... | @@ -1700,8 +1711,8 @@ pub const File = struct { | ... | @@ -1700,8 +1711,8 @@ pub const File = struct { |
| 1700 | | 1711 | |
| 1701 | // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address | 1712 | // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address |
| 1702 | // range of the compilation unit. When we expand the text section, this range changes, | 1713 | // range of the compilation unit. When we expand the text section, this range changes, |
| 1703 | // so the .debug_info section becomes dirty. | 1714 | // so the DW_TAG_compile_unit tag of the .debug_info section becomes dirty. |
| 1704 | self.debug_info_section_dirty = true; | 1715 | self.debug_info_header_dirty = true; |
| 1705 | // This becomes dirty for the same reason. We could potentially make this more | 1716 | // This becomes dirty for the same reason. We could potentially make this more |
| 1706 | // fine-grained with the addition of support for more compilation units. It is planned to | 1717 | // fine-grained with the addition of support for more compilation units. It is planned to |
| 1707 | // model each package as a different compilation unit. | 1718 | // model each package as a different compilation unit. |
| ... | @@ -1815,6 +1826,9 @@ pub const File = struct { | ... | @@ -1815,6 +1826,9 @@ pub const File = struct { |
| 1815 | var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator); | 1826 | var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 1816 | defer dbg_line_buffer.deinit(); | 1827 | defer dbg_line_buffer.deinit(); |
| 1817 | | 1828 | |
| | 1829 | var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator); |
| | 1830 | defer dbg_info_buffer.deinit(); |
| | 1831 | |
| 1818 | const typed_value = decl.typed_value.most_recent.typed_value; | 1832 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 1819 | const is_fn: bool = switch (typed_value.ty.zigTypeTag()) { | 1833 | const is_fn: bool = switch (typed_value.ty.zigTypeTag()) { |
| 1820 | .Fn => true, | 1834 | .Fn => true, |
| ... | @@ -1871,7 +1885,7 @@ pub const File = struct { | ... | @@ -1871,7 +1885,7 @@ pub const File = struct { |
| 1871 | // do the work of setting prologue_end=true and epilogue_begin=true. | 1885 | // do the work of setting prologue_end=true and epilogue_begin=true. |
| 1872 | dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy); | 1886 | dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy); |
| 1873 | } | 1887 | } |
| 1874 | const res = try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer, &dbg_line_buffer); | 1888 | const res = try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer, &dbg_line_buffer, &dbg_info_buffer); |
| 1875 | const code = switch (res) { | 1889 | const code = switch (res) { |
| 1876 | .externally_managed => |x| x, | 1890 | .externally_managed => |x| x, |
| 1877 | .appended => code_buffer.items, | 1891 | .appended => code_buffer.items, |
| ... | @@ -1937,6 +1951,8 @@ pub const File = struct { | ... | @@ -1937,6 +1951,8 @@ pub const File = struct { |
| 1937 | const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset; | 1951 | const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset; |
| 1938 | try self.base.file.?.pwriteAll(code, file_offset); | 1952 | try self.base.file.?.pwriteAll(code, file_offset); |
| 1939 | | 1953 | |
| | 1954 | try self.updateDeclDebugInfo(module, decl, dbg_info_buffer.items); |
| | 1955 | |
| 1940 | // If the Decl is a function, we need to update the .debug_line program. | 1956 | // If the Decl is a function, we need to update the .debug_line program. |
| 1941 | if (is_fn) { | 1957 | if (is_fn) { |
| 1942 | // Perform the relocation based on vaddr. | 1958 | // Perform the relocation based on vaddr. |
| ... | @@ -1956,6 +1972,10 @@ pub const File = struct { | ... | @@ -1956,6 +1972,10 @@ pub const File = struct { |
| 1956 | | 1972 | |
| 1957 | // Now we have the full contents and may allocate a region to store it. | 1973 | // Now we have the full contents and may allocate a region to store it. |
| 1958 | | 1974 | |
| | 1975 | // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for |
| | 1976 | // `TextBlock` and the .debug_info. If you are editing this logic, you |
| | 1977 | // probably need to edit that logic too. |
| | 1978 | |
| 1959 | const debug_line_sect = &self.sections.items[self.debug_line_section_index.?]; | 1979 | const debug_line_sect = &self.sections.items[self.debug_line_section_index.?]; |
| 1960 | const src_fn = &decl.fn_link.elf; | 1980 | const src_fn = &decl.fn_link.elf; |
| 1961 | src_fn.len = @intCast(u32, dbg_line_buffer.items.len); | 1981 | src_fn.len = @intCast(u32, dbg_line_buffer.items.len); |
| ... | @@ -1972,7 +1992,7 @@ pub const File = struct { | ... | @@ -1972,7 +1992,7 @@ pub const File = struct { |
| 1972 | src_fn.next = null; | 1992 | src_fn.next = null; |
| 1973 | // Populate where it used to be with NOPs. | 1993 | // Populate where it used to be with NOPs. |
| 1974 | const file_pos = debug_line_sect.sh_offset + src_fn.off; | 1994 | const file_pos = debug_line_sect.sh_offset + src_fn.off; |
| 1975 | try self.pwriteWithNops(0, &[0]u8{}, src_fn.len, file_pos); | 1995 | try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos); |
| 1976 | // TODO Look at the free list before appending at the end. | 1996 | // TODO Look at the free list before appending at the end. |
| 1977 | src_fn.prev = last; | 1997 | src_fn.prev = last; |
| 1978 | last.next = src_fn; | 1998 | last.next = src_fn; |
| ... | @@ -2022,7 +2042,7 @@ pub const File = struct { | ... | @@ -2022,7 +2042,7 @@ pub const File = struct { |
| 2022 | // We only have support for one compilation unit so far, so the offsets are directly | 2042 | // We only have support for one compilation unit so far, so the offsets are directly |
| 2023 | // from the .debug_line section. | 2043 | // from the .debug_line section. |
| 2024 | const file_pos = debug_line_sect.sh_offset + src_fn.off; | 2044 | const file_pos = debug_line_sect.sh_offset + src_fn.off; |
| 2025 | try self.pwriteWithNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos); | 2045 | try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos); |
| 2026 | } | 2046 | } |
| 2027 | | 2047 | |
| 2028 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | 2048 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. |
| ... | @@ -2030,6 +2050,88 @@ pub const File = struct { | ... | @@ -2030,6 +2050,88 @@ pub const File = struct { |
| 2030 | return self.updateDeclExports(module, decl, decl_exports); | 2050 | return self.updateDeclExports(module, decl, decl_exports); |
| 2031 | } | 2051 | } |
| 2032 | | 2052 | |
| | 2053 | pub fn updateDeclDebugInfo(self: *Elf, module: *Module, decl: *Module.Decl, dbg_info_buf: []const u8) !void { |
| | 2054 | const tracy = trace(@src()); |
| | 2055 | defer tracy.end(); |
| | 2056 | |
| | 2057 | // This logic is nearly identical to the logic above in `updateDecl` for |
| | 2058 | // `SrcFn` and the line number programs. If you are editing this logic, you |
| | 2059 | // probably need to edit that logic too. |
| | 2060 | |
| | 2061 | const debug_info_sect = &self.sections.items[self.debug_info_section_index.?]; |
| | 2062 | const text_block = &decl.link.elf; |
| | 2063 | if (self.dbg_info_decl_last) |last| { |
| | 2064 | if (text_block.dbg_info_next) |next| { |
| | 2065 | // Update existing Decl - non-last item. |
| | 2066 | if (text_block.dbg_info_off + text_block.dbg_info_len + min_nop_size > next.dbg_info_off) { |
| | 2067 | // It grew too big, so we move it to a new location. |
| | 2068 | if (text_block.dbg_info_prev) |prev| { |
| | 2069 | _ = self.dbg_info_decl_free_list.put(self.base.allocator, prev, {}) catch {}; |
| | 2070 | prev.dbg_info_next = text_block.dbg_info_next; |
| | 2071 | } |
| | 2072 | next.dbg_info_prev = text_block.dbg_info_prev; |
| | 2073 | text_block.dbg_info_next = null; |
| | 2074 | // Populate where it used to be with NOPs. |
| | 2075 | const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off; |
| | 2076 | try self.pwriteDbgInfoNops(0, &[0]u8{}, text_block.dbg_info_len, file_pos); |
| | 2077 | // TODO Look at the free list before appending at the end. |
| | 2078 | text_block.dbg_info_prev = last; |
| | 2079 | last.dbg_info_next = text_block; |
| | 2080 | self.dbg_info_decl_last = text_block; |
| | 2081 | |
| | 2082 | text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den); |
| | 2083 | } |
| | 2084 | } else if (text_block.dbg_info_prev == null) { |
| | 2085 | // Append new Decl. |
| | 2086 | // TODO Look at the free list before appending at the end. |
| | 2087 | text_block.dbg_info_prev = last; |
| | 2088 | last.dbg_info_next = text_block; |
| | 2089 | self.dbg_info_decl_last = text_block; |
| | 2090 | |
| | 2091 | text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den); |
| | 2092 | } |
| | 2093 | } else { |
| | 2094 | // This is the first Decl of the .debug_info |
| | 2095 | self.dbg_info_decl_first = text_block; |
| | 2096 | self.dbg_info_decl_last = text_block; |
| | 2097 | |
| | 2098 | text_block.dbg_info_off = self.dbgInfoNeededHeaderBytes() * alloc_num / alloc_den; |
| | 2099 | } |
| | 2100 | |
| | 2101 | const last_decl = self.dbg_info_decl_last.?; |
| | 2102 | const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len; |
| | 2103 | if (needed_size != debug_info_sect.sh_size) { |
| | 2104 | if (needed_size > self.allocatedSize(debug_info_sect.sh_offset)) { |
| | 2105 | const new_offset = self.findFreeSpace(needed_size, 1); |
| | 2106 | const existing_size = last_decl.dbg_info_off; |
| | 2107 | log.debug(.link, "moving .debug_info section: {} bytes from 0x{x} to 0x{x}\n", .{ |
| | 2108 | existing_size, |
| | 2109 | debug_info_sect.sh_offset, |
| | 2110 | new_offset, |
| | 2111 | }); |
| | 2112 | const amt = try self.base.file.?.copyRangeAll(debug_info_sect.sh_offset, self.base.file.?, new_offset, existing_size); |
| | 2113 | if (amt != existing_size) return error.InputOutput; |
| | 2114 | debug_info_sect.sh_offset = new_offset; |
| | 2115 | } |
| | 2116 | debug_info_sect.sh_size = needed_size; |
| | 2117 | self.shdr_table_dirty = true; // TODO look into making only the one section dirty |
| | 2118 | self.debug_info_header_dirty = true; |
| | 2119 | } |
| | 2120 | const prev_padding_size: u32 = if (text_block.dbg_info_prev) |prev| |
| | 2121 | text_block.dbg_info_off - (prev.dbg_info_off + prev.dbg_info_len) |
| | 2122 | else |
| | 2123 | 0; |
| | 2124 | const next_padding_size: u32 = if (text_block.dbg_info_next) |next| |
| | 2125 | next.dbg_info_off - (text_block.dbg_info_off + text_block.dbg_info_len) |
| | 2126 | else |
| | 2127 | 0; |
| | 2128 | |
| | 2129 | // We only have support for one compilation unit so far, so the offsets are directly |
| | 2130 | // from the .debug_info section. |
| | 2131 | const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off; |
| | 2132 | try self.pwriteDbgInfoNops(prev_padding_size, dbg_info_buf, next_padding_size, file_pos); |
| | 2133 | } |
| | 2134 | |
| 2033 | /// Must be called only after a successful call to `updateDecl`. | 2135 | /// Must be called only after a successful call to `updateDecl`. |
| 2034 | pub fn updateDeclExports( | 2136 | pub fn updateDeclExports( |
| 2035 | self: *Elf, | 2137 | self: *Elf, |
| ... | @@ -2221,6 +2323,9 @@ pub const File = struct { | ... | @@ -2221,6 +2323,9 @@ pub const File = struct { |
| 2221 | } | 2323 | } |
| 2222 | | 2324 | |
| 2223 | fn writeSymbol(self: *Elf, index: usize) !void { | 2325 | fn writeSymbol(self: *Elf, index: usize) !void { |
| | 2326 | const tracy = trace(@src()); |
| | 2327 | defer tracy.end(); |
| | 2328 | |
| 2224 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; | 2329 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; |
| 2225 | // Make sure we are not pointlessly writing symbol data that will have to get relocated | 2330 | // Make sure we are not pointlessly writing symbol data that will have to get relocated |
| 2226 | // due to running out of space. | 2331 | // due to running out of space. |
| ... | @@ -2361,18 +2466,27 @@ pub const File = struct { | ... | @@ -2361,18 +2466,27 @@ pub const File = struct { |
| 2361 | | 2466 | |
| 2362 | } | 2467 | } |
| 2363 | | 2468 | |
| | 2469 | fn dbgInfoNeededHeaderBytes(self: Elf) u32 { |
| | 2470 | return 120; |
| | 2471 | } |
| | 2472 | |
| | 2473 | const min_nop_size = 2; |
| | 2474 | |
| 2364 | /// Writes to the file a buffer, prefixed and suffixed by the specified number of | 2475 | /// Writes to the file a buffer, prefixed and suffixed by the specified number of |
| 2365 | /// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes | 2476 | /// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes |
| 2366 | /// are less than 126,976 bytes (if this limit is ever reached, this function can be | 2477 | /// are less than 126,976 bytes (if this limit is ever reached, this function can be |
| 2367 | /// improved to make more than one pwritev call, or the limit can be raised by a fixed | 2478 | /// improved to make more than one pwritev call, or the limit can be raised by a fixed |
| 2368 | /// amount by increasing the length of `vecs`). | 2479 | /// amount by increasing the length of `vecs`). |
| 2369 | fn pwriteWithNops( | 2480 | fn pwriteDbgLineNops( |
| 2370 | self: *Elf, | 2481 | self: *Elf, |
| 2371 | prev_padding_size: usize, | 2482 | prev_padding_size: usize, |
| 2372 | buf: []const u8, | 2483 | buf: []const u8, |
| 2373 | next_padding_size: usize, | 2484 | next_padding_size: usize, |
| 2374 | offset: usize, | 2485 | offset: usize, |
| 2375 | ) !void { | 2486 | ) !void { |
| | 2487 | const tracy = trace(@src()); |
| | 2488 | defer tracy.end(); |
| | 2489 | |
| 2376 | const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096; | 2490 | const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096; |
| 2377 | const three_byte_nop = [3]u8{DW.LNS_advance_pc, 0b1000_0000, 0}; | 2491 | const three_byte_nop = [3]u8{DW.LNS_advance_pc, 0b1000_0000, 0}; |
| 2378 | var vecs: [32]std.os.iovec_const = undefined; | 2492 | var vecs: [32]std.os.iovec_const = undefined; |
| ... | @@ -2439,7 +2553,66 @@ pub const File = struct { | ... | @@ -2439,7 +2553,66 @@ pub const File = struct { |
| 2439 | try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); | 2553 | try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); |
| 2440 | } | 2554 | } |
| 2441 | | 2555 | |
| 2442 | const min_nop_size = 2; | 2556 | /// Writes to the file a buffer, prefixed and suffixed by the specified number of |
| | 2557 | /// bytes of padding. |
| | 2558 | fn pwriteDbgInfoNops( |
| | 2559 | self: *Elf, |
| | 2560 | prev_padding_size: usize, |
| | 2561 | buf: []const u8, |
| | 2562 | next_padding_size: usize, |
| | 2563 | offset: usize, |
| | 2564 | ) !void { |
| | 2565 | const tracy = trace(@src()); |
| | 2566 | defer tracy.end(); |
| | 2567 | |
| | 2568 | const page_of_nops = [1]u8{0} ** 4096; |
| | 2569 | var vecs: [32]std.os.iovec_const = undefined; |
| | 2570 | var vec_index: usize = 0; |
| | 2571 | { |
| | 2572 | var padding_left = prev_padding_size; |
| | 2573 | while (padding_left > page_of_nops.len) { |
| | 2574 | vecs[vec_index] = .{ |
| | 2575 | .iov_base = &page_of_nops, |
| | 2576 | .iov_len = page_of_nops.len, |
| | 2577 | }; |
| | 2578 | vec_index += 1; |
| | 2579 | padding_left -= page_of_nops.len; |
| | 2580 | } |
| | 2581 | if (padding_left > 0) { |
| | 2582 | vecs[vec_index] = .{ |
| | 2583 | .iov_base = &page_of_nops, |
| | 2584 | .iov_len = padding_left, |
| | 2585 | }; |
| | 2586 | vec_index += 1; |
| | 2587 | } |
| | 2588 | } |
| | 2589 | |
| | 2590 | vecs[vec_index] = .{ |
| | 2591 | .iov_base = buf.ptr, |
| | 2592 | .iov_len = buf.len, |
| | 2593 | }; |
| | 2594 | vec_index += 1; |
| | 2595 | |
| | 2596 | { |
| | 2597 | var padding_left = next_padding_size; |
| | 2598 | while (padding_left > page_of_nops.len) { |
| | 2599 | vecs[vec_index] = .{ |
| | 2600 | .iov_base = &page_of_nops, |
| | 2601 | .iov_len = page_of_nops.len, |
| | 2602 | }; |
| | 2603 | vec_index += 1; |
| | 2604 | padding_left -= page_of_nops.len; |
| | 2605 | } |
| | 2606 | if (padding_left > 0) { |
| | 2607 | vecs[vec_index] = .{ |
| | 2608 | .iov_base = &page_of_nops, |
| | 2609 | .iov_len = padding_left, |
| | 2610 | }; |
| | 2611 | vec_index += 1; |
| | 2612 | } |
| | 2613 | } |
| | 2614 | try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); |
| | 2615 | } |
| 2443 | | 2616 | |
| 2444 | }; | 2617 | }; |
| 2445 | }; | 2618 | }; |