authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-31 23:22:35-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-02 12:02:43-07:00
logba6e5cbfd279583dcc724f6a74a5593fa26ad5df
tree8cf75e04b987873bf82f914eee4c39ef75c3fe3a
parentc0654d2db2cd66ee59f2147fbbb19d890e70bed1

stage2: add the .debug_line header and associated data types

* the .debug_line header is written properly * link.File.Elf gains: - SrcFn, which is now a field in Module.Fn - SrcFile, which is now a field in Module.Scope.File * link.File.Elf gets a whole *Package field rather than only root_src_dir_path. * the fields first_dbg_line_file and last_dbg_line_file tell where the Line Number Program begins and ends, which alows moving files when the header gets too big, and allows appending files to the end. * codegen is passed a buffer for emitting .debug_line Line Number Program opcodes for functions. See #5963 There is some work-in-progress code here, but I need to go make some experimental changes to changing how to represent source locations and I want to do that in a separate commit.

4 files changed, 284 insertions(+), 32 deletions(-)

lib/std/zig/ast.zig+6-2
...@@ -1299,6 +1299,10 @@ pub const Node = struct {...@@ -1299,6 +1299,10 @@ pub const Node = struct {
1299 });1299 });
1300 }1300 }
13011301
1302 pub fn body(self: *const FnProto) ?*Node {
1303 return self.getTrailer("body_node");
1304 }
1305
1302 pub fn getTrailer(self: *const FnProto, comptime name: []const u8) ?TrailerFlags.Field(name) {1306 pub fn getTrailer(self: *const FnProto, comptime name: []const u8) ?TrailerFlags.Field(name) {
1303 const trailers_start = @alignCast(1307 const trailers_start = @alignCast(
1304 @alignOf(ParamDecl),1308 @alignOf(ParamDecl),
...@@ -1381,7 +1385,7 @@ pub const Node = struct {...@@ -1381,7 +1385,7 @@ pub const Node = struct {
1381 .Invalid => {},1385 .Invalid => {},
1382 }1386 }
13831387
1384 if (self.getTrailer("body_node")) |body_node| {1388 if (self.body()) |body_node| {
1385 if (i < 1) return body_node;1389 if (i < 1) return body_node;
1386 i -= 1;1390 i -= 1;
1387 }1391 }
...@@ -1397,7 +1401,7 @@ pub const Node = struct {...@@ -1397,7 +1401,7 @@ pub const Node = struct {
1397 }1401 }
13981402
1399 pub fn lastToken(self: *const FnProto) TokenIndex {1403 pub fn lastToken(self: *const FnProto) TokenIndex {
1400 if (self.getTrailer("body_node")) |body_node| return body_node.lastToken();1404 if (self.body()) |body_node| return body_node.lastToken();
1401 switch (self.return_type) {1405 switch (self.return_type) {
1402 .Explicit, .InferErrorSet => |node| return node.lastToken(),1406 .Explicit, .InferErrorSet => |node| return node.lastToken(),
1403 .Invalid => |tok| return tok,1407 .Invalid => |tok| return tok,
src-self-hosted/Module.zig+8-1
...@@ -279,6 +279,9 @@ pub const Fn = struct {...@@ -279,6 +279,9 @@ pub const Fn = struct {
279 },279 },
280 owner_decl: *Decl,280 owner_decl: *Decl,
281281
282 /// Represents the function in the linked output file.
283 link: link.File.Elf.SrcFn = link.File.Elf.SrcFn.empty,
284
282 /// This memory is temporary and points to stack memory for the duration285 /// This memory is temporary and points to stack memory for the duration
283 /// of Fn analysis.286 /// of Fn analysis.
284 pub const Analysis = struct {287 pub const Analysis = struct {
...@@ -503,6 +506,10 @@ pub const Scope = struct {...@@ -503,6 +506,10 @@ pub const Scope = struct {
503 /// Direct children of the file.506 /// Direct children of the file.
504 decls: ArrayListUnmanaged(*Decl),507 decls: ArrayListUnmanaged(*Decl),
505508
509 /// Represents the file in the linker code. The linker code
510 /// uses this field to store data relevant to its purposes.
511 link: link.File.Elf.SrcFile = link.File.Elf.SrcFile.empty,
512
506 pub fn unload(self: *File, gpa: *Allocator) void {513 pub fn unload(self: *File, gpa: *Allocator) void {
507 switch (self.status) {514 switch (self.status) {
508 .never_loaded,515 .never_loaded,
...@@ -792,7 +799,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {...@@ -792,7 +799,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
792 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();799 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
793 const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{800 const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{
794 .root_name = root_name,801 .root_name = root_name,
795 .root_src_dir_path = options.root_pkg.root_src_dir_path,802 .root_pkg = options.root_pkg,
796 .target = options.target,803 .target = options.target,
797 .output_mode = options.output_mode,804 .output_mode = options.output_mode,
798 .link_mode = options.link_mode orelse .Static,805 .link_mode = options.link_mode orelse .Static,
src-self-hosted/codegen.zig+2-1
...@@ -44,6 +44,7 @@ pub fn generateSymbol(...@@ -44,6 +44,7 @@ pub fn generateSymbol(
44 src: usize,44 src: usize,
45 typed_value: TypedValue,45 typed_value: TypedValue,
46 code: *std.ArrayList(u8),46 code: *std.ArrayList(u8),
47 dbg_line: *std.ArrayList(u8),
47) GenerateSymbolError!Result {48) GenerateSymbolError!Result {
48 const tracy = trace(@src());49 const tracy = trace(@src());
49 defer tracy.end();50 defer tracy.end();
...@@ -114,7 +115,7 @@ pub fn generateSymbol(...@@ -114,7 +115,7 @@ pub fn generateSymbol(
114 switch (try generateSymbol(bin_file, src, .{115 switch (try generateSymbol(bin_file, src, .{
115 .ty = typed_value.ty.elemType(),116 .ty = typed_value.ty.elemType(),
116 .val = sentinel,117 .val = sentinel,
117 }, code)) {118 }, code, dbg_line)) {
118 .appended => return Result{ .appended = {} },119 .appended => return Result{ .appended = {} },
119 .externally_managed => |slice| {120 .externally_managed => |slice| {
120 code.appendSliceAssumeCapacity(slice);121 code.appendSliceAssumeCapacity(slice);
src-self-hosted/link.zig+268-28
...@@ -11,6 +11,9 @@ const c_codegen = @import("codegen/c.zig");...@@ -11,6 +11,9 @@ const c_codegen = @import("codegen/c.zig");
11const log = std.log;11const log = std.log;
12const DW = std.dwarf;12const DW = std.dwarf;
13const trace = @import("tracy.zig").trace;13const trace = @import("tracy.zig").trace;
14const leb128 = std.debug.leb;
15const Package = @import("Package.zig");
16const Value = @import("value.zig").Value;
1417
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: off19// 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 file31 /// 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,
294298
295 debug_abbrev_table_offset: ?u64 = null,299 debug_abbrev_table_offset: ?u64 = null,
296300
...@@ -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,
321326
322 error_flags: ErrorFlags = ErrorFlags{},327 error_flags: ErrorFlags = ErrorFlags{},
323328
...@@ -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,
341346
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 };
404412
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);
407454
...@@ -538,6 +585,14 @@ pub const File = struct {...@@ -538,6 +585,14 @@ pub const File = struct {
538 });585 });
539 }586 }
540587
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 }
613668
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 }
621677
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 };
913993
914 // Unfortunately these have to be buffered and done at the end because ELF does not allow994 // 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, // header1004 1, DW.TAG_compile_unit, DW.CHILDREN_no, // header
925 //DW.AT_stmt_list, DW.FORM_data4, TODO1005 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 version1054 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 simply1071 // 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;
9991076
1000 di_buf.appendAssumeCapacity(1); // abbrev tag, matching the value from the abbrev table header1077 di_buf.appendSliceAssumeCapacity(&[_]u8{
1001 //DW.AT_stmt_list, DW.FORM_data4, TODO line information1078 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); // version1140 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 {
11161192
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 }
11191288
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();
16371807
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 {
16481862
1649 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);1863 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
16501864
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 };
16551866
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);
17061917
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};
19922232