| ... | @@ -11,6 +11,9 @@ const c_codegen = @import("codegen/c.zig"); | ... | @@ -11,6 +11,9 @@ const c_codegen = @import("codegen/c.zig"); |
| 11 | const log = std.log; | 11 | const log = std.log; |
| 12 | const DW = std.dwarf; | 12 | const DW = std.dwarf; |
| 13 | const trace = @import("tracy.zig").trace; | 13 | const trace = @import("tracy.zig").trace; |
| | 14 | const leb128 = std.debug.leb; |
| | 15 | const Package = @import("Package.zig"); |
| | 16 | const Value = @import("value.zig").Value; |
| 14 | | 17 | |
| 15 | // TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented. | 18 | // TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented. |
| 16 | // zig fmt: off | 19 | // zig fmt: off |
| ... | @@ -24,7 +27,7 @@ pub const Options = struct { | ... | @@ -24,7 +27,7 @@ pub const Options = struct { |
| 24 | object_format: std.builtin.ObjectFormat, | 27 | object_format: std.builtin.ObjectFormat, |
| 25 | optimize_mode: std.builtin.Mode, | 28 | optimize_mode: std.builtin.Mode, |
| 26 | root_name: []const u8, | 29 | root_name: []const u8, |
| 27 | root_src_dir_path: []const u8, | 30 | root_pkg: *const Package, |
| 28 | /// Used for calculating how much space to reserve for symbols in case the binary file | 31 | /// Used for calculating how much space to reserve for symbols in case the binary file |
| 29 | /// does not already have a symbol table. | 32 | /// does not already have a symbol table. |
| 30 | symbol_count_hint: u64 = 32, | 33 | symbol_count_hint: u64 = 32, |
| ... | @@ -291,6 +294,7 @@ pub const File = struct { | ... | @@ -291,6 +294,7 @@ pub const File = struct { |
| 291 | debug_abbrev_section_index: ?u16 = null, | 294 | debug_abbrev_section_index: ?u16 = null, |
| 292 | debug_str_section_index: ?u16 = null, | 295 | debug_str_section_index: ?u16 = null, |
| 293 | debug_aranges_section_index: ?u16 = null, | 296 | debug_aranges_section_index: ?u16 = null, |
| | 297 | debug_line_section_index: ?u16 = null, |
| 294 | | 298 | |
| 295 | debug_abbrev_table_offset: ?u64 = null, | 299 | debug_abbrev_table_offset: ?u64 = null, |
| 296 | | 300 | |
| ... | @@ -318,6 +322,7 @@ pub const File = struct { | ... | @@ -318,6 +322,7 @@ pub const File = struct { |
| 318 | debug_info_section_dirty: bool = false, | 322 | debug_info_section_dirty: bool = false, |
| 319 | debug_abbrev_section_dirty: bool = false, | 323 | debug_abbrev_section_dirty: bool = false, |
| 320 | debug_aranges_section_dirty: bool = false, | 324 | debug_aranges_section_dirty: bool = false, |
| | 325 | debug_line_header_dirty: bool = false, |
| 321 | | 326 | |
| 322 | error_flags: ErrorFlags = ErrorFlags{}, | 327 | error_flags: ErrorFlags = ErrorFlags{}, |
| 323 | | 328 | |
| ... | @@ -339,6 +344,9 @@ pub const File = struct { | ... | @@ -339,6 +344,9 @@ pub const File = struct { |
| 339 | text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){}, | 344 | text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){}, |
| 340 | last_text_block: ?*TextBlock = null, | 345 | last_text_block: ?*TextBlock = null, |
| 341 | | 346 | |
| | 347 | first_dbg_line_file: ?*SrcFile = null, |
| | 348 | last_dbg_line_file: ?*SrcFile = null, |
| | 349 | |
| 342 | /// `alloc_num / alloc_den` is the factor of padding when allocating. | 350 | /// `alloc_num / alloc_den` is the factor of padding when allocating. |
| 343 | const alloc_num = 4; | 351 | const alloc_num = 4; |
| 344 | const alloc_den = 3; | 352 | const alloc_den = 3; |
| ... | @@ -402,6 +410,45 @@ pub const File = struct { | ... | @@ -402,6 +410,45 @@ pub const File = struct { |
| 402 | sym_index: ?u32 = null, | 410 | sym_index: ?u32 = null, |
| 403 | }; | 411 | }; |
| 404 | | 412 | |
| | 413 | pub const SrcFn = struct { |
| | 414 | /// Offset from the `SrcFile` that contains this function. |
| | 415 | dbg_line_off: u32, |
| | 416 | /// Size of the line number program component belonging to this function, not |
| | 417 | /// including padding. |
| | 418 | dbg_line_len: u32, |
| | 419 | |
| | 420 | pub const empty: SrcFn = .{ |
| | 421 | .dbg_line_off = 0, |
| | 422 | .dbg_line_len = 0, |
| | 423 | }; |
| | 424 | }; |
| | 425 | |
| | 426 | pub const SrcFile = struct { |
| | 427 | /// Byte offset from the start of the Line Number Program that contains this file. |
| | 428 | off: u32, |
| | 429 | /// Length in bytes, not including padding, of this file component within the |
| | 430 | /// Line Number Program that contains it. |
| | 431 | len: u32, |
| | 432 | |
| | 433 | /// A list of `SrcFn` that have surplus capacity. |
| | 434 | /// This is the same concept as `text_block_free_list` (see the doc comments there) |
| | 435 | /// but it's for the function's component of the Line Number program. |
| | 436 | free_list: std.ArrayListUnmanaged(*SrcFn), |
| | 437 | |
| | 438 | /// Points to the previous and next neighbors, based on the offset from .debug_line. |
| | 439 | /// This can be used to find, for example, the capacity of this `SrcFile`. |
| | 440 | prev: ?*SrcFile, |
| | 441 | next: ?*SrcFile, |
| | 442 | |
| | 443 | pub const empty: SrcFile = .{ |
| | 444 | .off = 0, |
| | 445 | .len = 0, |
| | 446 | .free_list = .{}, |
| | 447 | .prev = null, |
| | 448 | .next = null, |
| | 449 | }; |
| | 450 | }; |
| | 451 | |
| 405 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File { | 452 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File { |
| 406 | assert(options.object_format == .elf); | 453 | assert(options.object_format == .elf); |
| 407 | | 454 | |
| ... | @@ -538,6 +585,14 @@ pub const File = struct { | ... | @@ -538,6 +585,14 @@ pub const File = struct { |
| 538 | }); | 585 | }); |
| 539 | } | 586 | } |
| 540 | | 587 | |
| | 588 | fn getDebugLineProgramOff(self: Elf) u32 { |
| | 589 | return self.first_dbg_line_file.?.off; |
| | 590 | } |
| | 591 | |
| | 592 | fn getDebugLineProgramLen(self: Elf) u32 { |
| | 593 | return self.last_dbg_line_file.?.off + self.last_dbg_line_file.?.len; |
| | 594 | } |
| | 595 | |
| 541 | /// Returns end pos of collision, if any. | 596 | /// Returns end pos of collision, if any. |
| 542 | fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { | 597 | fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { |
| 543 | const small_ptr = self.base.options.target.cpu.arch.ptrBitWidth() == 32; | 598 | const small_ptr = self.base.options.target.cpu.arch.ptrBitWidth() == 32; |
| ... | @@ -611,6 +666,7 @@ pub const File = struct { | ... | @@ -611,6 +666,7 @@ pub const File = struct { |
| 611 | return start; | 666 | return start; |
| 612 | } | 667 | } |
| 613 | | 668 | |
| | 669 | /// TODO Improve this to use a table. |
| 614 | fn makeString(self: *Elf, bytes: []const u8) !u32 { | 670 | fn makeString(self: *Elf, bytes: []const u8) !u32 { |
| 615 | try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1); | 671 | try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1); |
| 616 | const result = self.shstrtab.items.len; | 672 | const result = self.shstrtab.items.len; |
| ... | @@ -619,6 +675,7 @@ pub const File = struct { | ... | @@ -619,6 +675,7 @@ pub const File = struct { |
| 619 | return @intCast(u32, result); | 675 | return @intCast(u32, result); |
| 620 | } | 676 | } |
| 621 | | 677 | |
| | 678 | /// TODO Improve this to use a table. |
| 622 | fn makeDebugString(self: *Elf, bytes: []const u8) !u32 { | 679 | fn makeDebugString(self: *Elf, bytes: []const u8) !u32 { |
| 623 | try self.debug_strtab.ensureCapacity(self.allocator, self.debug_strtab.items.len + bytes.len + 1); | 680 | try self.debug_strtab.ensureCapacity(self.allocator, self.debug_strtab.items.len + bytes.len + 1); |
| 624 | const result = self.debug_strtab.items.len; | 681 | const result = self.debug_strtab.items.len; |
| ... | @@ -645,10 +702,7 @@ pub const File = struct { | ... | @@ -645,10 +702,7 @@ pub const File = struct { |
| 645 | .p32 => true, | 702 | .p32 => true, |
| 646 | .p64 => false, | 703 | .p64 => false, |
| 647 | }; | 704 | }; |
| 648 | const ptr_size: u8 = switch (self.ptr_width) { | 705 | const ptr_size: u8 = self.ptrWidthBytes(); |
| 649 | .p32 => 4, | | |
| 650 | .p64 => 8, | | |
| 651 | }; | | |
| 652 | if (self.phdr_load_re_index == null) { | 706 | if (self.phdr_load_re_index == null) { |
| 653 | self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len); | 707 | self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len); |
| 654 | const file_size = self.base.options.program_code_size_hint; | 708 | const file_size = self.base.options.program_code_size_hint; |
| ... | @@ -869,6 +923,31 @@ pub const File = struct { | ... | @@ -869,6 +923,31 @@ pub const File = struct { |
| 869 | self.shdr_table_dirty = true; | 923 | self.shdr_table_dirty = true; |
| 870 | self.debug_aranges_section_dirty = true; | 924 | self.debug_aranges_section_dirty = true; |
| 871 | } | 925 | } |
| | 926 | if (self.debug_line_section_index == null) { |
| | 927 | self.debug_line_section_index = @intCast(u16, self.sections.items.len); |
| | 928 | |
| | 929 | const file_size_hint = 250; |
| | 930 | const p_align = 1; |
| | 931 | const off = self.findFreeSpace(file_size_hint, p_align); |
| | 932 | log.debug(.link, "found .debug_line free space 0x{x} to 0x{x}\n", .{ |
| | 933 | off, |
| | 934 | off + file_size_hint, |
| | 935 | }); |
| | 936 | try self.sections.append(self.allocator, .{ |
| | 937 | .sh_name = try self.makeString(".debug_line"), |
| | 938 | .sh_type = elf.SHT_PROGBITS, |
| | 939 | .sh_flags = 0, |
| | 940 | .sh_addr = 0, |
| | 941 | .sh_offset = off, |
| | 942 | .sh_size = file_size_hint, |
| | 943 | .sh_link = 0, |
| | 944 | .sh_info = 0, |
| | 945 | .sh_addralign = p_align, |
| | 946 | .sh_entsize = 0, |
| | 947 | }); |
| | 948 | self.shdr_table_dirty = true; |
| | 949 | self.debug_line_header_dirty = true; |
| | 950 | } |
| 872 | const shsize: u64 = switch (self.ptr_width) { | 951 | const shsize: u64 = switch (self.ptr_width) { |
| 873 | .p32 => @sizeOf(elf.Elf32_Shdr), | 952 | .p32 => @sizeOf(elf.Elf32_Shdr), |
| 874 | .p64 => @sizeOf(elf.Elf64_Shdr), | 953 | .p64 => @sizeOf(elf.Elf64_Shdr), |
| ... | @@ -906,9 +985,10 @@ pub const File = struct { | ... | @@ -906,9 +985,10 @@ pub const File = struct { |
| 906 | pub fn flush(self: *Elf) !void { | 985 | pub fn flush(self: *Elf) !void { |
| 907 | const target_endian = self.base.options.target.cpu.arch.endian(); | 986 | const target_endian = self.base.options.target.cpu.arch.endian(); |
| 908 | const foreign_endian = target_endian != std.Target.current.cpu.arch.endian(); | 987 | const foreign_endian = target_endian != std.Target.current.cpu.arch.endian(); |
| 909 | const ptr_width_bytes: u8 = switch (self.ptr_width) { | 988 | const ptr_width_bytes: u8 = self.ptrWidthBytes(); |
| | 989 | const init_len_size: usize = switch (self.ptr_width) { |
| 910 | .p32 => 4, | 990 | .p32 => 4, |
| 911 | .p64 => 8, | 991 | .p64 => 12, |
| 912 | }; | 992 | }; |
| 913 | | 993 | |
| 914 | // Unfortunately these have to be buffered and done at the end because ELF does not allow | 994 | // Unfortunately these have to be buffered and done at the end because ELF does not allow |
| ... | @@ -922,7 +1002,7 @@ pub const File = struct { | ... | @@ -922,7 +1002,7 @@ pub const File = struct { |
| 922 | // we can simply append these bytes. | 1002 | // we can simply append these bytes. |
| 923 | const abbrev_buf = [_]u8{ | 1003 | const abbrev_buf = [_]u8{ |
| 924 | 1, DW.TAG_compile_unit, DW.CHILDREN_no, // header | 1004 | 1, DW.TAG_compile_unit, DW.CHILDREN_no, // header |
| 925 | //DW.AT_stmt_list, DW.FORM_data4, TODO | 1005 | DW.AT_stmt_list, DW.FORM_data1, |
| 926 | DW.AT_low_pc , DW.FORM_addr, | 1006 | DW.AT_low_pc , DW.FORM_addr, |
| 927 | DW.AT_high_pc , DW.FORM_addr, | 1007 | DW.AT_high_pc , DW.FORM_addr, |
| 928 | DW.AT_name , DW.FORM_strp, | 1008 | DW.AT_name , DW.FORM_strp, |
| ... | @@ -969,10 +1049,7 @@ pub const File = struct { | ... | @@ -969,10 +1049,7 @@ pub const File = struct { |
| 969 | // not including the initial length itself. | 1049 | // not including the initial length itself. |
| 970 | // We have to come back and write it later after we know the size. | 1050 | // We have to come back and write it later after we know the size. |
| 971 | const init_len_index = di_buf.items.len; | 1051 | const init_len_index = di_buf.items.len; |
| 972 | switch (self.ptr_width) { | 1052 | di_buf.items.len += init_len_size; |
| 973 | .p32 => di_buf.items.len += 4, | | |
| 974 | .p64 => di_buf.items.len += 12, | | |
| 975 | } | | |
| 976 | const after_init_len = di_buf.items.len; | 1053 | const after_init_len = di_buf.items.len; |
| 977 | mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 5, target_endian); // DWARF version | 1054 | mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 5, target_endian); // DWARF version |
| 978 | di_buf.appendAssumeCapacity(DW.UT_compile); | 1055 | di_buf.appendAssumeCapacity(DW.UT_compile); |
| ... | @@ -989,7 +1066,7 @@ pub const File = struct { | ... | @@ -989,7 +1066,7 @@ pub const File = struct { |
| 989 | } | 1066 | } |
| 990 | // Write the form for the compile unit, which must match the abbrev table above. | 1067 | // Write the form for the compile unit, which must match the abbrev table above. |
| 991 | const name_strp = try self.makeDebugString(self.base.options.root_name); | 1068 | const name_strp = try self.makeDebugString(self.base.options.root_name); |
| 992 | const comp_dir_strp = try self.makeDebugString(self.base.options.root_src_dir_path); | 1069 | const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path); |
| 993 | const producer_strp = try self.makeDebugString("zig (TODO version here)"); | 1070 | const producer_strp = try self.makeDebugString("zig (TODO version here)"); |
| 994 | // Currently only one compilation unit is supported, so the address range is simply | 1071 | // Currently only one compilation unit is supported, so the address range is simply |
| 995 | // identical to the main program header virtual address and memory size. | 1072 | // identical to the main program header virtual address and memory size. |
| ... | @@ -997,8 +1074,10 @@ pub const File = struct { | ... | @@ -997,8 +1074,10 @@ pub const File = struct { |
| 997 | const low_pc = text_phdr.p_vaddr; | 1074 | const low_pc = text_phdr.p_vaddr; |
| 998 | const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; | 1075 | const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; |
| 999 | | 1076 | |
| 1000 | di_buf.appendAssumeCapacity(1); // abbrev tag, matching the value from the abbrev table header | 1077 | di_buf.appendSliceAssumeCapacity(&[_]u8{ |
| 1001 | //DW.AT_stmt_list, DW.FORM_data4, TODO line information | 1078 | 1, // abbrev tag, matching the value from the abbrev table header |
| | 1079 | 0, // DW.AT_stmt_list, DW.FORM_data1: offset to corresponding .debug_line header |
| | 1080 | }); |
| 1002 | self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc); | 1081 | self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc); |
| 1003 | self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc); | 1082 | self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc); |
| 1004 | self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp); | 1083 | self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp); |
| ... | @@ -1056,10 +1135,7 @@ pub const File = struct { | ... | @@ -1056,10 +1135,7 @@ pub const File = struct { |
| 1056 | // not including the initial length itself. | 1135 | // not including the initial length itself. |
| 1057 | // We have to come back and write it later after we know the size. | 1136 | // We have to come back and write it later after we know the size. |
| 1058 | const init_len_index = di_buf.items.len; | 1137 | const init_len_index = di_buf.items.len; |
| 1059 | switch (self.ptr_width) { | 1138 | di_buf.items.len += init_len_size; |
| 1060 | .p32 => di_buf.items.len += 4, | | |
| 1061 | .p64 => di_buf.items.len += 12, | | |
| 1062 | } | | |
| 1063 | const after_init_len = di_buf.items.len; | 1139 | const after_init_len = di_buf.items.len; |
| 1064 | mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version | 1140 | mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version |
| 1065 | // When more than one compilation unit is supported, this will be the offset to it. | 1141 | // When more than one compilation unit is supported, this will be the offset to it. |
| ... | @@ -1116,6 +1192,99 @@ pub const File = struct { | ... | @@ -1116,6 +1192,99 @@ pub const File = struct { |
| 1116 | | 1192 | |
| 1117 | self.debug_aranges_section_dirty = false; | 1193 | self.debug_aranges_section_dirty = false; |
| 1118 | } | 1194 | } |
| | 1195 | if (self.debug_line_header_dirty) { |
| | 1196 | const dbg_line_prg_off = self.getDebugLineProgramOff(); |
| | 1197 | const dbg_line_prg_len = self.getDebugLineProgramLen(); |
| | 1198 | assert(dbg_line_prg_len != 0); |
| | 1199 | |
| | 1200 | const debug_line_sect = &self.sections.items[self.debug_line_section_index.?]; |
| | 1201 | |
| | 1202 | var di_buf = std.ArrayList(u8).init(self.allocator); |
| | 1203 | defer di_buf.deinit(); |
| | 1204 | |
| | 1205 | // This is a heuristic. The size of this header is variable, depending on |
| | 1206 | // the number of directories, files, and padding. |
| | 1207 | try di_buf.ensureCapacity(100); |
| | 1208 | |
| | 1209 | // initial length - length of the .debug_line contribution for this compilation unit, |
| | 1210 | // not including the initial length itself. |
| | 1211 | const after_init_len = di_buf.items.len + init_len_size; |
| | 1212 | const init_len = (dbg_line_prg_off + dbg_line_prg_len) - after_init_len; |
| | 1213 | switch (self.ptr_width) { |
| | 1214 | .p32 => { |
| | 1215 | mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian); |
| | 1216 | }, |
| | 1217 | .p64 => { |
| | 1218 | di_buf.appendNTimesAssumeCapacity(0xff, 4); |
| | 1219 | mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian); |
| | 1220 | }, |
| | 1221 | } |
| | 1222 | |
| | 1223 | mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 5, target_endian); // version |
| | 1224 | di_buf.appendSliceAssumeCapacity(&[_]u8{ |
| | 1225 | ptr_width_bytes, // address_size |
| | 1226 | 0, // segment_selector_size |
| | 1227 | }); |
| | 1228 | |
| | 1229 | const header_length = dbg_line_prg_off - (di_buf.items.len + ptr_width_bytes); |
| | 1230 | self.writeDwarfAddrAssumeCapacity(&di_buf, header_length); |
| | 1231 | |
| | 1232 | const opcode_base = DW.LNS_set_isa + 1; |
| | 1233 | di_buf.appendSliceAssumeCapacity(&[_]u8{ |
| | 1234 | 1, // minimum_instruction_length |
| | 1235 | 1, // maximum_operations_per_instruction |
| | 1236 | 1, // default_is_stmt |
| | 1237 | 1, // line_base (signed) |
| | 1238 | 1, // line_range |
| | 1239 | opcode_base, |
| | 1240 | |
| | 1241 | // Standard opcode lengths. The number of items here is based on `opcode_base`. |
| | 1242 | // The value is the number of LEB128 operands the instruction takes. |
| | 1243 | 0, // `DW.LNS_copy` |
| | 1244 | 1, // `DW.LNS_advance_pc` |
| | 1245 | 1, // `DW.LNS_advance_line` |
| | 1246 | 1, // `DW.LNS_set_file` |
| | 1247 | 1, // `DW.LNS_set_column` |
| | 1248 | 0, // `DW.LNS_negate_stmt` |
| | 1249 | 0, // `DW.LNS_set_basic_block` |
| | 1250 | 0, // `DW.LNS_const_add_pc` |
| | 1251 | 0, // `DW.LNS_fixed_advance_pc` |
| | 1252 | 0, // `DW.LNS_set_prologue_end` |
| | 1253 | 0, // `DW.LNS_set_epilogue_begin` |
| | 1254 | 1, // `DW.LNS_set_isa` |
| | 1255 | |
| | 1256 | 1, // directory_entry_format_count |
| | 1257 | DW.LNCT_path, DW.FORM_strp, // directory_entry_format |
| | 1258 | |
| | 1259 | // For now we only support one compilation unit, which has one directory. |
| | 1260 | 1, // directories_count (this is a ULEB128) |
| | 1261 | }); |
| | 1262 | const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path); |
| | 1263 | self.writeDwarfAddrAssumeCapacity(&di_buf, comp_dir_strp); |
| | 1264 | |
| | 1265 | di_buf.appendSliceAssumeCapacity(&[_]u8{ |
| | 1266 | 2, // file_name_entry_format_count |
| | 1267 | DW.LNCT_path, DW.FORM_strp, // file_name_entry_format[0] |
| | 1268 | DW.LNCT_directory_index, DW.FORM_data1, // file_name_entry_format[1] |
| | 1269 | // TODO Look into adding the file size here. Maybe even the mtime and MD5. |
| | 1270 | //DW.LNCT_size, DW.FORM_udata, // file_name_entry_format[2] |
| | 1271 | |
| | 1272 | // For now we only put the root file name here. Once more source files |
| | 1273 | // are supported, this will need to be improved. |
| | 1274 | 1, // file_names_count (this is a ULEB128) |
| | 1275 | }); |
| | 1276 | const root_src_file_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path); |
| | 1277 | self.writeDwarfAddrAssumeCapacity(&di_buf, root_src_file_strp); // DW.LNCT_path, DW.FORM_strp |
| | 1278 | di_buf.appendAssumeCapacity(0); // LNCT_directory_index, FORM_data1 |
| | 1279 | |
| | 1280 | if (di_buf.items.len > dbg_line_prg_off) { |
| | 1281 | // Move the first N files to the end to make more padding for the header. |
| | 1282 | @panic("TODO: handle .debug_line header exceeding its padding"); |
| | 1283 | } |
| | 1284 | |
| | 1285 | try self.file.?.pwriteAll(di_buf.items, debug_line_sect.sh_offset); |
| | 1286 | self.debug_line_header_dirty = false; |
| | 1287 | } |
| 1119 | | 1288 | |
| 1120 | if (self.phdr_table_dirty) { | 1289 | if (self.phdr_table_dirty) { |
| 1121 | const phsize: u64 = switch (self.ptr_width) { | 1290 | const phsize: u64 = switch (self.ptr_width) { |
| ... | @@ -1263,6 +1432,7 @@ pub const File = struct { | ... | @@ -1263,6 +1432,7 @@ pub const File = struct { |
| 1263 | assert(!self.debug_info_section_dirty); | 1432 | assert(!self.debug_info_section_dirty); |
| 1264 | assert(!self.debug_abbrev_section_dirty); | 1433 | assert(!self.debug_abbrev_section_dirty); |
| 1265 | assert(!self.debug_aranges_section_dirty); | 1434 | assert(!self.debug_aranges_section_dirty); |
| | 1435 | assert(!self.debug_line_header_dirty); |
| 1266 | assert(!self.phdr_table_dirty); | 1436 | assert(!self.phdr_table_dirty); |
| 1267 | assert(!self.shdr_table_dirty); | 1437 | assert(!self.shdr_table_dirty); |
| 1268 | assert(!self.shstrtab_dirty); | 1438 | assert(!self.shstrtab_dirty); |
| ... | @@ -1635,8 +1805,52 @@ pub const File = struct { | ... | @@ -1635,8 +1805,52 @@ pub const File = struct { |
| 1635 | var code_buffer = std.ArrayList(u8).init(self.allocator); | 1805 | var code_buffer = std.ArrayList(u8).init(self.allocator); |
| 1636 | defer code_buffer.deinit(); | 1806 | defer code_buffer.deinit(); |
| 1637 | | 1807 | |
| | 1808 | var dbg_line_buffer = std.ArrayList(u8).init(self.allocator); |
| | 1809 | defer dbg_line_buffer.deinit(); |
| | 1810 | |
| 1638 | const typed_value = decl.typed_value.most_recent.typed_value; | 1811 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 1639 | const code = switch (try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer)) { | 1812 | const is_fn: bool = switch (typed_value.ty.zigTypeTag()) { |
| | 1813 | .Fn => true, |
| | 1814 | else => false, |
| | 1815 | }; |
| | 1816 | const dbg_line_vaddr_reloc_index = 1; |
| | 1817 | if (is_fn) { |
| | 1818 | const scope_file = decl.scope.cast(Module.Scope.File).?; |
| | 1819 | const line_off: u28 = blk: { |
| | 1820 | const file_ast_decls = scope_file.contents.tree.root_node.decls(); |
| | 1821 | if (decl.src_index == 0) { |
| | 1822 | // Then it's the line number of the open curly. |
| | 1823 | const block = file_ast_decls[decl.src_index].castTag(.Block).?; |
| | 1824 | @panic("TODO implement this"); |
| | 1825 | } else { |
| | 1826 | const prev_decl = file_ast_decls[decl.src_index - 1]; |
| | 1827 | // Find the difference between prev decl end curly and this decl begin curly. |
| | 1828 | @panic("TODO implement this"); |
| | 1829 | } |
| | 1830 | }; |
| | 1831 | |
| | 1832 | // For functions we need to add a prologue to the debug line program. |
| | 1833 | try dbg_line_buffer.ensureCapacity(24); |
| | 1834 | |
| | 1835 | dbg_line_buffer.appendAssumeCapacity(DW.LNE_set_address); |
| | 1836 | // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`. |
| | 1837 | assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len); |
| | 1838 | dbg_line_buffer.items.len += self.ptrWidthBytes(); |
| | 1839 | |
| | 1840 | dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line); |
| | 1841 | // This is the "relocatable" relative line offset from the previous function's end curly |
| | 1842 | // to this function's begin curly. |
| | 1843 | assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len); |
| | 1844 | // Here we use a ULEB128 but we write 4 bytes regardless (possibly wasting space) |
| | 1845 | // so that we can patch this later as a fixed width field. |
| | 1846 | leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off); |
| | 1847 | |
| | 1848 | // Emit a line for the begin curly with prologue_end=false. The codegen will |
| | 1849 | // do the work of setting prologue_end=true and epilogue_begin=true. |
| | 1850 | dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy); |
| | 1851 | } |
| | 1852 | const res = try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer, &dbg_line_buffer); |
| | 1853 | const code = switch (res) { |
| 1640 | .externally_managed => |x| x, | 1854 | .externally_managed => |x| x, |
| 1641 | .appended => code_buffer.items, | 1855 | .appended => code_buffer.items, |
| 1642 | .fail => |em| { | 1856 | .fail => |em| { |
| ... | @@ -1648,10 +1862,7 @@ pub const File = struct { | ... | @@ -1648,10 +1862,7 @@ pub const File = struct { |
| 1648 | | 1862 | |
| 1649 | const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); | 1863 | const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); |
| 1650 | | 1864 | |
| 1651 | const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) { | 1865 | const stt_bits: u8 = if (is_fn) elf.STT_FUNC else elf.STT_OBJECT; |
| 1652 | .Fn => elf.STT_FUNC, | | |
| 1653 | else => elf.STT_OBJECT, | | |
| 1654 | }; | | |
| 1655 | | 1866 | |
| 1656 | assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes() | 1867 | assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes() |
| 1657 | const local_sym = &self.local_symbols.items[decl.link.local_sym_index]; | 1868 | const local_sym = &self.local_symbols.items[decl.link.local_sym_index]; |
| ... | @@ -1704,6 +1915,30 @@ pub const File = struct { | ... | @@ -1704,6 +1915,30 @@ pub const File = struct { |
| 1704 | const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset; | 1915 | const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset; |
| 1705 | try self.file.?.pwriteAll(code, file_offset); | 1916 | try self.file.?.pwriteAll(code, file_offset); |
| 1706 | | 1917 | |
| | 1918 | // If the Decl is a function, we need to update the .debug_line program. |
| | 1919 | if (is_fn) { |
| | 1920 | // Perform the relocation based on vaddr. |
| | 1921 | const target_endian = self.base.options.target.cpu.arch.endian(); |
| | 1922 | switch (self.ptr_width) { |
| | 1923 | .p32 => { |
| | 1924 | const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4]; |
| | 1925 | mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian); |
| | 1926 | }, |
| | 1927 | .p64 => { |
| | 1928 | const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8]; |
| | 1929 | mem.writeInt(u64, ptr, local_sym.st_value, target_endian); |
| | 1930 | }, |
| | 1931 | } |
| | 1932 | |
| | 1933 | const src_file = &decl.scope.cast(Module.Scope.File).?.link; |
| | 1934 | const src_fn = &typed_value.val.cast(Value.Payload.Function).?.func.link; |
| | 1935 | if (src_file.next == null and src_file.prev == null) { |
| | 1936 | @panic("TODO updateDecl for .debug_line: add new SrcFile"); |
| | 1937 | } else { |
| | 1938 | @panic("TODO updateDecl for .debug_line: update existing SrcFile"); |
| | 1939 | } |
| | 1940 | } |
| | 1941 | |
| 1707 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | 1942 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. |
| 1708 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; | 1943 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; |
| 1709 | return self.updateDeclExports(module, decl, decl_exports); | 1944 | return self.updateDeclExports(module, decl, decl_exports); |
| ... | @@ -1841,10 +2076,7 @@ pub const File = struct { | ... | @@ -1841,10 +2076,7 @@ pub const File = struct { |
| 1841 | fn writeOffsetTableEntry(self: *Elf, index: usize) !void { | 2076 | fn writeOffsetTableEntry(self: *Elf, index: usize) !void { |
| 1842 | const shdr = &self.sections.items[self.got_section_index.?]; | 2077 | const shdr = &self.sections.items[self.got_section_index.?]; |
| 1843 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; | 2078 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; |
| 1844 | const entry_size: u16 = switch (self.ptr_width) { | 2079 | const entry_size: u16 = self.ptrWidthBytes(); |
| 1845 | .p32 => 4, | | |
| 1846 | .p64 => 8, | | |
| 1847 | }; | | |
| 1848 | if (self.offset_table_count_dirty) { | 2080 | if (self.offset_table_count_dirty) { |
| 1849 | // TODO Also detect virtual address collisions. | 2081 | // TODO Also detect virtual address collisions. |
| 1850 | const allocated_size = self.allocatedSize(shdr.sh_offset); | 2082 | const allocated_size = self.allocatedSize(shdr.sh_offset); |
| ... | @@ -1987,6 +2219,14 @@ pub const File = struct { | ... | @@ -1987,6 +2219,14 @@ pub const File = struct { |
| 1987 | }, | 2219 | }, |
| 1988 | } | 2220 | } |
| 1989 | } | 2221 | } |
| | 2222 | |
| | 2223 | fn ptrWidthBytes(self: Elf) u8 { |
| | 2224 | return switch (self.ptr_width) { |
| | 2225 | .p32 => 4, |
| | 2226 | .p64 => 8, |
| | 2227 | }; |
| | 2228 | } |
| | 2229 | |
| 1990 | }; | 2230 | }; |
| 1991 | }; | 2231 | }; |
| 1992 | | 2232 | |