From bc82e0f3d3aed55165902c37271af120fcd4f858 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 21 Jan 2020 20:51:57 +0100 Subject: Refactor some code in the debug output --- lib/std/debug.zig | 235 ++++++++++++++++++------------------------------------ 1 file changed, 77 insertions(+), 158 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/debug.zig b/lib/std/debug.zig index dfdaca6d3f..51fb384d75 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -379,16 +379,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres } } else { // we have no information to add to the address - if (tty_color) { - try out_stream.print("???:?:?: ", .{}); - setTtyColor(TtyColor.Dim); - try out_stream.print("0x{x} in ??? (???)", .{relocated_address}); - setTtyColor(TtyColor.Reset); - try out_stream.print("\n\n\n", .{}); - } else { - try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", .{relocated_address}); - } - return; + return printLineInfo(out_stream, null, relocated_address, "???", "???", tty_color, printLineFromFileAnyOs); }; const mod = &di.modules[mod_index]; @@ -510,66 +501,15 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres } }; - if (tty_color) { - setTtyColor(TtyColor.White); - if (opt_line_info) |li| { - try out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column }); - } else { - try out_stream.print("???:?:?", .{}); - } - setTtyColor(TtyColor.Reset); - try out_stream.print(": ", .{}); - setTtyColor(TtyColor.Dim); - try out_stream.print("0x{x} in {} ({})", .{ relocated_address, symbol_name, obj_basename }); - setTtyColor(TtyColor.Reset); - - if (opt_line_info) |line_info| { - try out_stream.print("\n", .{}); - if (printLineFromFileAnyOs(out_stream, line_info)) { - if (line_info.column == 0) { - try out_stream.write("\n"); - } else { - { - var col_i: usize = 1; - while (col_i < line_info.column) : (col_i += 1) { - try out_stream.writeByte(' '); - } - } - setTtyColor(TtyColor.Green); - try out_stream.write("^"); - setTtyColor(TtyColor.Reset); - try out_stream.write("\n"); - } - } else |err| switch (err) { - error.EndOfFile => {}, - error.FileNotFound => { - setTtyColor(TtyColor.Dim); - try out_stream.write("file not found\n\n"); - setTtyColor(TtyColor.White); - }, - else => return err, - } - } else { - try out_stream.print("\n\n\n", .{}); - } - } else { - if (opt_line_info) |li| { - try out_stream.print("{}:{}:{}: 0x{x} in {} ({})\n\n\n", .{ - li.file_name, - li.line, - li.column, - relocated_address, - symbol_name, - obj_basename, - }); - } else { - try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", .{ - relocated_address, - symbol_name, - obj_basename, - }); - } - } + try printLineInfo( + out_stream, + opt_line_info, + relocated_address, + symbol_name, + obj_basename, + tty_color, + printLineFromFileAnyOs, + ); } const TtyColor = enum { @@ -605,7 +545,11 @@ fn setTtyColor(tty_color: TtyColor) void { stderr_file.write(RESET) catch return; }, } - } else { + + return; + } + + if (builtin.os == .windows) { const S = struct { var attrs: windows.WORD = undefined; var init_attrs = false; @@ -711,12 +655,7 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt const adjusted_addr = 0x100000000 + (address - base_addr); const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse { - if (tty_color) { - try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", .{address}); - } else { - try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", .{address}); - } - return; + return printLineInfo(out_stream, null, address, "???", "???", tty_color, printLineFromFileAnyOs); }; const symbol_name = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + symbol.nlist.n_strx)); @@ -724,29 +663,22 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx)); break :blk fs.path.basename(ofile_path); } else "???"; - if (getLineNumberInfoMacOs(di, symbol.*, adjusted_addr)) |line_info| { - defer line_info.deinit(); - try printLineInfo( - out_stream, - line_info, - address, - symbol_name, - compile_unit_name, - tty_color, - printLineFromFileAnyOs, - ); - } else |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => { - if (tty_color) { - try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n\n\n", .{ - address, symbol_name, compile_unit_name, - }); - } else { - try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", .{ address, symbol_name, compile_unit_name }); - } - }, + + const line_info = getLineNumberInfoMacOs(di, symbol.*, adjusted_addr) catch |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => null, else => return err, - } + }; + defer if (line_info) |li| li.deinit(); + + try printLineInfo( + out_stream, + line_info, + address, + symbol_name, + compile_unit_name, + tty_color, + printLineFromFileAnyOs, + ); } pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { @@ -755,47 +687,46 @@ pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, addres fn printLineInfo( out_stream: var, - line_info: LineInfo, + line_info: ?LineInfo, address: usize, symbol_name: []const u8, compile_unit_name: []const u8, tty_color: bool, comptime printLineFromFile: var, ) !void { - if (tty_color) { - try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n", .{ - line_info.file_name, - line_info.line, - line_info.column, - address, - symbol_name, - compile_unit_name, - }); - if (printLineFromFile(out_stream, line_info)) { - if (line_info.column == 0) { - try out_stream.write("\n"); - } else { - { - var col_i: usize = 1; - while (col_i < line_info.column) : (col_i += 1) { - try out_stream.writeByte(' '); - } - } - try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); + if (tty_color) setTtyColor(.White); + + if (line_info) |*li| { + try out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column }); + } else { + try out_stream.print("???:?:?", .{}); + } + + if (tty_color) setTtyColor(.Reset); + try out_stream.write(": "); + if (tty_color) setTtyColor(.Dim); + try out_stream.print("0x{x} in {} ({})", .{ address, symbol_name, compile_unit_name }); + if (tty_color) setTtyColor(.Reset); + try out_stream.write("\n"); + + // Show the matching source code line if possible + if (line_info) |li| { + if (printLineFromFile(out_stream, li)) { + if (li.column > 0) { + // The caret already takes one char + const space_needed = @intCast(usize, li.column - 1); + + try out_stream.writeByteNTimes(' ', space_needed); + if (tty_color) setTtyColor(.Green); + try out_stream.write("^"); + if (tty_color) setTtyColor(.Reset); } + try out_stream.write("\n"); } else |err| switch (err) { error.EndOfFile, error.FileNotFound => {}, + error.BadPathName => {}, else => return err, } - } else { - try out_stream.print("{}:{}:{}: 0x{x} in {} ({})\n", .{ - line_info.file_name, - line_info.line, - line_info.column, - address, - symbol_name, - compile_unit_name, - }); } } @@ -1240,38 +1171,26 @@ pub const DwarfInfo = struct { comptime printLineFromFile: var, ) !void { const compile_unit = self.findCompileUnit(address) catch { - if (tty_color) { - try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", .{address}); - } else { - try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", .{address}); - } - return; + return printLineInfo(out_stream, null, address, "???", "???", tty_color, printLineFromFile); }; + const compile_unit_name = try compile_unit.die.getAttrString(self, DW.AT_name); - if (self.getLineNumberInfo(compile_unit.*, address)) |line_info| { - defer line_info.deinit(); - const symbol_name = self.getSymbolName(address) orelse "???"; - try printLineInfo( - out_stream, - line_info, - address, - symbol_name, - compile_unit_name, - tty_color, - printLineFromFile, - ); - } else |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => { - if (tty_color) { - try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n\n\n", .{ - address, compile_unit_name, - }); - } else { - try out_stream.print("???:?:?: 0x{x} in ??? ({})\n\n\n", .{ address, compile_unit_name }); - } - }, + const symbol_name = self.getSymbolName(address) orelse "???"; + const line_info = self.getLineNumberInfo(compile_unit.*, address) catch |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => null, else => return err, - } + }; + defer if (line_info) |li| li.deinit(); + + try printLineInfo( + out_stream, + line_info, + address, + symbol_name, + compile_unit_name, + tty_color, + printLineFromFile, + ); } fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 { -- cgit v1.2.3 From b8601e92524b9ea2e51b5dc8d816085b7399a08c Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 21 Jan 2020 22:58:51 +0100 Subject: Adjust tests & work around a nasty ICE --- lib/std/debug.zig | 2 +- test/stack_traces.zig | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 51fb384d75..dd162487b8 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -711,7 +711,7 @@ fn printLineInfo( // Show the matching source code line if possible if (line_info) |li| { - if (printLineFromFile(out_stream, li)) { + if (noasync printLineFromFile(out_stream, li)) { if (li.column > 0) { // The caret already takes one char const space_needed = @intCast(usize, li.column - 1); diff --git a/test/stack_traces.zig b/test/stack_traces.zig index ad4a34fe4e..81e074f01e 100644 --- a/test/stack_traces.zig +++ b/test/stack_traces.zig @@ -51,11 +51,15 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // debug \\error: TheSkyIsFalling \\source.zig:4:5: [address] in main (test) + \\ return error.TheSkyIsFalling; + \\ ^ \\ , // release-safe \\error: TheSkyIsFalling \\source.zig:4:5: [address] in std.start.main (test) + \\ return error.TheSkyIsFalling; + \\ ^ \\ , // release-fast @@ -74,13 +78,21 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // debug \\error: TheSkyIsFalling \\source.zig:4:5: [address] in foo (test) + \\ return error.TheSkyIsFalling; + \\ ^ \\source.zig:8:5: [address] in main (test) + \\ try foo(); + \\ ^ \\ , // release-safe \\error: TheSkyIsFalling \\source.zig:4:5: [address] in std.start.main (test) + \\ return error.TheSkyIsFalling; + \\ ^ \\source.zig:8:5: [address] in std.start.main (test) + \\ try foo(); + \\ ^ \\ , // release-fast @@ -99,17 +111,33 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // debug \\error: TheSkyIsFalling \\source.zig:12:5: [address] in make_error (test) + \\ return error.TheSkyIsFalling; + \\ ^ \\source.zig:8:5: [address] in bar (test) + \\ return make_error(); + \\ ^ \\source.zig:4:5: [address] in foo (test) + \\ try bar(); + \\ ^ \\source.zig:16:5: [address] in main (test) + \\ try foo(); + \\ ^ \\ , // release-safe \\error: TheSkyIsFalling \\source.zig:12:5: [address] in std.start.main (test) + \\ return error.TheSkyIsFalling; + \\ ^ \\source.zig:8:5: [address] in std.start.main (test) + \\ return make_error(); + \\ ^ \\source.zig:4:5: [address] in std.start.main (test) + \\ try bar(); + \\ ^ \\source.zig:16:5: [address] in std.start.main (test) + \\ try foo(); + \\ ^ \\ , // release-fast @@ -130,11 +158,15 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // debug \\error: TheSkyIsFalling \\source.zig:4:5: [address] in main (test) + \\ return error.TheSkyIsFalling; + \\ ^ \\ , // release-safe \\error: TheSkyIsFalling \\source.zig:4:5: [address] in std.start.posixCallMainAndExit (test) + \\ return error.TheSkyIsFalling; + \\ ^ \\ , // release-fast @@ -153,13 +185,21 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // debug \\error: TheSkyIsFalling \\source.zig:4:5: [address] in foo (test) + \\ return error.TheSkyIsFalling; + \\ ^ \\source.zig:8:5: [address] in main (test) + \\ try foo(); + \\ ^ \\ , // release-safe \\error: TheSkyIsFalling \\source.zig:4:5: [address] in std.start.posixCallMainAndExit (test) + \\ return error.TheSkyIsFalling; + \\ ^ \\source.zig:8:5: [address] in std.start.posixCallMainAndExit (test) + \\ try foo(); + \\ ^ \\ , // release-fast @@ -178,17 +218,33 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // debug \\error: TheSkyIsFalling \\source.zig:12:5: [address] in make_error (test) + \\ return error.TheSkyIsFalling; + \\ ^ \\source.zig:8:5: [address] in bar (test) + \\ return make_error(); + \\ ^ \\source.zig:4:5: [address] in foo (test) + \\ try bar(); + \\ ^ \\source.zig:16:5: [address] in main (test) + \\ try foo(); + \\ ^ \\ , // release-safe \\error: TheSkyIsFalling \\source.zig:12:5: [address] in std.start.posixCallMainAndExit (test) + \\ return error.TheSkyIsFalling; + \\ ^ \\source.zig:8:5: [address] in std.start.posixCallMainAndExit (test) + \\ return make_error(); + \\ ^ \\source.zig:4:5: [address] in std.start.posixCallMainAndExit (test) + \\ try bar(); + \\ ^ \\source.zig:16:5: [address] in std.start.posixCallMainAndExit (test) + \\ try foo(); + \\ ^ \\ , // release-fast @@ -209,11 +265,15 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // debug \\error: TheSkyIsFalling \\source.zig:4:5: [address] in _main.0 (test.o) + \\ return error.TheSkyIsFalling; + \\ ^ \\ , // release-safe \\error: TheSkyIsFalling \\source.zig:4:5: [address] in _main (test.o) + \\ return error.TheSkyIsFalling; + \\ ^ \\ , // release-fast @@ -232,13 +292,21 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // debug \\error: TheSkyIsFalling \\source.zig:4:5: [address] in _foo (test.o) + \\ return error.TheSkyIsFalling; + \\ ^ \\source.zig:8:5: [address] in _main.0 (test.o) + \\ try foo(); + \\ ^ \\ , // release-safe \\error: TheSkyIsFalling \\source.zig:4:5: [address] in _main (test.o) + \\ return error.TheSkyIsFalling; + \\ ^ \\source.zig:8:5: [address] in _main (test.o) + \\ try foo(); + \\ ^ \\ , // release-fast @@ -257,17 +325,33 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // debug \\error: TheSkyIsFalling \\source.zig:12:5: [address] in _make_error (test.o) + \\ return error.TheSkyIsFalling; + \\ ^ \\source.zig:8:5: [address] in _bar (test.o) + \\ return make_error(); + \\ ^ \\source.zig:4:5: [address] in _foo (test.o) + \\ try bar(); + \\ ^ \\source.zig:16:5: [address] in _main.0 (test.o) + \\ try foo(); + \\ ^ \\ , // release-safe \\error: TheSkyIsFalling \\source.zig:12:5: [address] in _main (test.o) + \\ return error.TheSkyIsFalling; + \\ ^ \\source.zig:8:5: [address] in _main (test.o) + \\ return make_error(); + \\ ^ \\source.zig:4:5: [address] in _main (test.o) + \\ try bar(); + \\ ^ \\source.zig:16:5: [address] in _main (test.o) + \\ try foo(); + \\ ^ \\ , // release-fast @@ -288,6 +372,8 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // debug \\error: TheSkyIsFalling \\source.zig:4:5: [address] in main (test.obj) + \\ return error.TheSkyIsFalling; + \\ ^ \\ , // release-safe @@ -309,7 +395,11 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // debug \\error: TheSkyIsFalling \\source.zig:4:5: [address] in foo (test.obj) + \\ return error.TheSkyIsFalling; + \\ ^ \\source.zig:8:5: [address] in main (test.obj) + \\ try foo(); + \\ ^ \\ , // release-safe @@ -331,9 +421,17 @@ pub fn addCases(cases: *tests.StackTracesContext) void { // debug \\error: TheSkyIsFalling \\source.zig:12:5: [address] in make_error (test.obj) + \\ return error.TheSkyIsFalling; + \\ ^ \\source.zig:8:5: [address] in bar (test.obj) + \\ return make_error(); + \\ ^ \\source.zig:4:5: [address] in foo (test.obj) + \\ try bar(); + \\ ^ \\source.zig:16:5: [address] in main (test.obj) + \\ try foo(); + \\ ^ \\ , // release-safe -- cgit v1.2.3 From 8516ee392c8e1e29b3968a7c9c8812013414004c Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 25 Jan 2020 14:06:46 +0100 Subject: Fix parsing of DW_AT_Ranges debug entry Follow the specification about what the base address is and how it can be changed by some entries in the list itself. --- lib/std/debug.zig | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/debug.zig b/lib/std/debug.zig index dd162487b8..e7c296d172 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1377,9 +1377,13 @@ pub const DwarfInfo = struct { if (compile_unit.pc_range) |range| { if (target_address >= range.start and target_address < range.end) return compile_unit; } - if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { - var base_address: usize = 0; - if (di.debug_ranges) |debug_ranges| { + if (di.debug_ranges) |debug_ranges| { + if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { + // All the addresses in the list are relative to the value + // specified by DW_AT_low_pc or to some other value encoded + // in the list itself + var base_address = try compile_unit.die.getAttrAddr(DW.AT_low_pc); + try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset); while (true) { const begin_addr = try di.dwarf_in_stream.readIntLittle(usize); @@ -1387,18 +1391,21 @@ pub const DwarfInfo = struct { if (begin_addr == 0 and end_addr == 0) { break; } + // This entry selects a new value for the base address if (begin_addr == maxInt(usize)) { - base_address = begin_addr; + base_address = end_addr; continue; } - if (target_address >= begin_addr and target_address < end_addr) { + if (target_address >= base_address + begin_addr and target_address < base_address + end_addr) { return compile_unit; } } + + return error.InvalidDebugInfo; + } else |err| { + if (err != error.MissingDebugInfo) return err; + continue; } - } else |err| { - if (err != error.MissingDebugInfo) return err; - continue; } } return error.MissingDebugInfo; -- cgit v1.2.3 From aaa2f9ab2febf9e8be9bb284cd8d5e3f6c2c54f1 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 25 Jan 2020 23:35:43 +0100 Subject: Fix handling of DW_LNE_end_sequence The DWARF specification states that LNE_end_sequence should just reset the state machine, it's not an error. --- lib/std/debug.zig | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/debug.zig b/lib/std/debug.zig index e7c296d172..33b062db23 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1478,7 +1478,8 @@ pub const DwarfInfo = struct { assert(line_info_offset < di.debug_line.size); - try di.dwarf_seekable_stream.seekTo(di.debug_line.offset + line_info_offset); + const this_unit_offset = di.debug_line.offset + line_info_offset; + try di.dwarf_seekable_stream.seekTo(this_unit_offset); var is_64: bool = undefined; const unit_length = try readInitialLength(@TypeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); @@ -1546,7 +1547,9 @@ pub const DwarfInfo = struct { try di.dwarf_seekable_stream.seekTo(prog_start_offset); - while (true) { + const next_unit_pos = this_unit_offset + next_offset; + + while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) { const opcode = try di.dwarf_in_stream.readByte(); if (opcode == DW.LNS_extended_op) { @@ -1557,7 +1560,7 @@ pub const DwarfInfo = struct { DW.LNE_end_sequence => { prog.end_sequence = true; if (try prog.checkLineMatch()) |info| return info; - return error.MissingDebugInfo; + prog.reset(); }, DW.LNE_set_address => { const addr = try di.dwarf_in_stream.readInt(usize, di.endian); @@ -1814,6 +1817,7 @@ const LineNumberProgram = struct { basic_block: bool, end_sequence: bool, + default_is_stmt: bool, target_address: usize, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), @@ -1826,6 +1830,25 @@ const LineNumberProgram = struct { prev_basic_block: bool, prev_end_sequence: bool, + // Reset the state machine following the DWARF specification + pub fn reset(self: *LineNumberProgram) void { + self.address = 0; + self.file = 1; + self.line = 1; + self.column = 0; + self.is_stmt = self.default_is_stmt; + self.basic_block = false; + self.end_sequence = false; + // Invalidate all the remaining fields + self.prev_address = 0; + self.prev_file = undefined; + self.prev_line = undefined; + self.prev_column = undefined; + self.prev_is_stmt = undefined; + self.prev_basic_block = undefined; + self.prev_end_sequence = undefined; + } + pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram { return LineNumberProgram{ .address = 0, @@ -1837,6 +1860,7 @@ const LineNumberProgram = struct { .end_sequence = false, .include_dirs = include_dirs, .file_entries = file_entries, + .default_is_stmt = is_stmt, .target_address = target_address, .prev_address = 0, .prev_file = undefined, -- cgit v1.2.3 From 3839ea89785856bbed0624a6a18eb6e5acfb46c3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 26 Jan 2020 09:55:04 -0500 Subject: fix debug info code not being freestanding compatible in stack tracing code, the idea was to detect the tty settings at the top of the stack and pass the information down. somewhere along the way this got changed so that setTtyColor was assuming the global stderr_file was related to the output stream the stack trace was being printed to. now, tty_color is changed to tty_config, and it is an enum rather than a bool, telling how tty colors are expected to be handled. windows is still incorrectly looking at stderr_file. --- lib/std/debug.zig | 230 +++++++++++++++++++++++++++++------------------------- 1 file changed, 123 insertions(+), 107 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 33b062db23..75d2afb8e9 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -81,10 +81,20 @@ pub fn getSelfDebugInfo() !*DebugInfo { } } -fn wantTtyColor() bool { +fn detectTTYConfig() TTY.Config { var bytes: [128]u8 = undefined; const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; - return if (process.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| true else |_| stderr_file.isTty(); + if (process.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| { + return .escape_codes; + } else |_| { + if (stderr_file.supportsAnsiEscapeCodes()) { + return .escape_codes; + } else if (builtin.os == .windows) { + return .windows_api; + } else { + return .no_color; + } + } } /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. @@ -99,7 +109,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; return; }; - writeCurrentStackTrace(stderr, debug_info, wantTtyColor(), start_addr) catch |err| { + writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(), start_addr) catch |err| { stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return; return; }; @@ -118,16 +128,16 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void { stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; return; }; - const tty_color = wantTtyColor(); - printSourceAtAddress(debug_info, stderr, ip, tty_color) catch return; + const tty_config = detectTTYConfig(); + printSourceAtAddress(debug_info, stderr, ip, tty_config) catch return; const first_return_address = @intToPtr(*const usize, bp + @sizeOf(usize)).*; - printSourceAtAddress(debug_info, stderr, first_return_address - 1, tty_color) catch return; + printSourceAtAddress(debug_info, stderr, first_return_address - 1, tty_config) catch return; var it = StackIterator{ .first_addr = null, .fp = bp, }; while (it.next()) |return_address| { - printSourceAtAddress(debug_info, stderr, return_address - 1, tty_color) catch return; + printSourceAtAddress(debug_info, stderr, return_address - 1, tty_config) catch return; } } @@ -191,7 +201,7 @@ pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void { stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; return; }; - writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, wantTtyColor()) catch |err| { + writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, detectTTYConfig()) catch |err| { stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return; return; }; @@ -264,7 +274,7 @@ pub fn writeStackTrace( out_stream: var, allocator: *mem.Allocator, debug_info: *DebugInfo, - tty_color: bool, + tty_config: TTY.Config, ) !void { if (builtin.strip_debug_info) return error.MissingDebugInfo; var frame_index: usize = 0; @@ -275,7 +285,7 @@ pub fn writeStackTrace( frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len; }) { const return_address = stack_trace.instruction_addresses[frame_index]; - try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_color); + try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_config); } } @@ -319,20 +329,25 @@ pub const StackIterator = struct { } }; -pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void { +pub fn writeCurrentStackTrace( + out_stream: var, + debug_info: *DebugInfo, + tty_config: TTY.Config, + start_addr: ?usize, +) !void { if (builtin.os == .windows) { - return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr); + return writeCurrentStackTraceWindows(out_stream, debug_info, tty_config, start_addr); } var it = StackIterator.init(start_addr); while (it.next()) |return_address| { - try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_color); + try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_config); } } pub fn writeCurrentStackTraceWindows( out_stream: var, debug_info: *DebugInfo, - tty_color: bool, + tty_config: TTY.Config, start_addr: ?usize, ) !void { var addr_buf: [1024]usize = undefined; @@ -345,23 +360,28 @@ pub fn writeCurrentStackTraceWindows( return; } else 0; for (addrs[start_i..]) |addr| { - try printSourceAtAddress(debug_info, out_stream, addr, tty_color); + try printSourceAtAddress(debug_info, out_stream, addr, tty_config); } } /// TODO once https://github.com/ziglang/zig/issues/3157 is fully implemented, /// make this `noasync fn` and remove the individual noasync calls. -pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { +pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { if (builtin.os == .windows) { - return noasync printSourceAtAddressWindows(debug_info, out_stream, address, tty_color); + return noasync printSourceAtAddressWindows(debug_info, out_stream, address, tty_config); } if (comptime std.Target.current.isDarwin()) { - return noasync printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color); + return noasync printSourceAtAddressMacOs(debug_info, out_stream, address, tty_config); } - return noasync printSourceAtAddressPosix(debug_info, out_stream, address, tty_color); + return noasync printSourceAtAddressPosix(debug_info, out_stream, address, tty_config); } -fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_address: usize, tty_color: bool) !void { +fn printSourceAtAddressWindows( + di: *DebugInfo, + out_stream: var, + relocated_address: usize, + tty_config: TTY.Config, +) !void { const allocator = getDebugInfoAllocator(); const base_address = process.getBaseAddress(); const relative_address = relocated_address - base_address; @@ -379,7 +399,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres } } else { // we have no information to add to the address - return printLineInfo(out_stream, null, relocated_address, "???", "???", tty_color, printLineFromFileAnyOs); + return printLineInfo(out_stream, null, relocated_address, "???", "???", tty_config, printLineFromFileAnyOs); }; const mod = &di.modules[mod_index]; @@ -507,84 +527,80 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres relocated_address, symbol_name, obj_basename, - tty_color, + tty_config, printLineFromFileAnyOs, ); } -const TtyColor = enum { - Red, - Green, - Cyan, - White, - Dim, - Bold, - Reset, -}; - -/// TODO this is a special case hack right now. clean it up and maybe make it part of std.fmt -fn setTtyColor(tty_color: TtyColor) void { - if (stderr_file.supportsAnsiEscapeCodes()) { - switch (tty_color) { - TtyColor.Red => { - stderr_file.write(RED) catch return; - }, - TtyColor.Green => { - stderr_file.write(GREEN) catch return; - }, - TtyColor.Cyan => { - stderr_file.write(CYAN) catch return; - }, - TtyColor.White, TtyColor.Bold => { - stderr_file.write(WHITE) catch return; - }, - TtyColor.Dim => { - stderr_file.write(DIM) catch return; - }, - TtyColor.Reset => { - stderr_file.write(RESET) catch return; - }, - } - - return; - } +pub const TTY = struct { + pub const Color = enum { + Red, + Green, + Cyan, + White, + Dim, + Bold, + Reset, + }; - if (builtin.os == .windows) { - const S = struct { - var attrs: windows.WORD = undefined; - var init_attrs = false; - }; - if (!S.init_attrs) { - S.init_attrs = true; - var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; - // TODO handle error - _ = windows.kernel32.GetConsoleScreenBufferInfo(stderr_file.handle, &info); - S.attrs = info.wAttributes; - } + pub const Config = enum { + no_color, + escape_codes, + // TODO give this a payload of file handle + windows_api, + + fn setColor(conf: Config, out_stream: var, color: Color) void { + switch (conf) { + .no_color => return, + .escape_codes => switch (color) { + .Red => out_stream.write(RED) catch return, + .Green => out_stream.write(GREEN) catch return, + .Cyan => out_stream.write(CYAN) catch return, + .White, .Bold => out_stream.write(WHITE) catch return, + .Dim => out_stream.write(DIM) catch return, + .Reset => out_stream.write(RESET) catch return, + }, + .windows_api => if (builtin.os == .windows) { + const S = struct { + var attrs: windows.WORD = undefined; + var init_attrs = false; + }; + if (!S.init_attrs) { + S.init_attrs = true; + var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; + // TODO handle error + _ = windows.kernel32.GetConsoleScreenBufferInfo(stderr_file.handle, &info); + S.attrs = info.wAttributes; + } - // TODO handle errors - switch (tty_color) { - TtyColor.Red => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY) catch {}; - }, - TtyColor.Green => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY) catch {}; - }, - TtyColor.Cyan => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY) catch {}; - }, - TtyColor.White, TtyColor.Bold => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY) catch {}; - }, - TtyColor.Dim => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_INTENSITY) catch {}; - }, - TtyColor.Reset => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs) catch {}; - }, + // TODO handle errors + switch (color) { + .Red => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY) catch {}; + }, + .Green => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY) catch {}; + }, + .Cyan => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY) catch {}; + }, + .White, .Bold => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY) catch {}; + }, + .Dim => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_INTENSITY) catch {}; + }, + .Reset => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs) catch {}; + }, + } + } else { + unreachable; + }, + } } - } -} + }; +}; fn populateModule(di: *DebugInfo, mod: *Module) !void { if (mod.populated) @@ -650,12 +666,12 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach return null; } -fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { +fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { const base_addr = process.getBaseAddress(); const adjusted_addr = 0x100000000 + (address - base_addr); const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse { - return printLineInfo(out_stream, null, address, "???", "???", tty_color, printLineFromFileAnyOs); + return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs); }; const symbol_name = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + symbol.nlist.n_strx)); @@ -676,13 +692,13 @@ fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tt address, symbol_name, compile_unit_name, - tty_color, + tty_config, printLineFromFileAnyOs, ); } -pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { - return debug_info.printSourceAtAddress(out_stream, address, tty_color, printLineFromFileAnyOs); +pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { + return debug_info.printSourceAtAddress(out_stream, address, tty_config, printLineFromFileAnyOs); } fn printLineInfo( @@ -691,10 +707,10 @@ fn printLineInfo( address: usize, symbol_name: []const u8, compile_unit_name: []const u8, - tty_color: bool, + tty_config: TTY.Config, comptime printLineFromFile: var, ) !void { - if (tty_color) setTtyColor(.White); + tty_config.setColor(out_stream, .White); if (line_info) |*li| { try out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column }); @@ -702,11 +718,11 @@ fn printLineInfo( try out_stream.print("???:?:?", .{}); } - if (tty_color) setTtyColor(.Reset); + tty_config.setColor(out_stream, .Reset); try out_stream.write(": "); - if (tty_color) setTtyColor(.Dim); + tty_config.setColor(out_stream, .Dim); try out_stream.print("0x{x} in {} ({})", .{ address, symbol_name, compile_unit_name }); - if (tty_color) setTtyColor(.Reset); + tty_config.setColor(out_stream, .Reset); try out_stream.write("\n"); // Show the matching source code line if possible @@ -717,9 +733,9 @@ fn printLineInfo( const space_needed = @intCast(usize, li.column - 1); try out_stream.writeByteNTimes(' ', space_needed); - if (tty_color) setTtyColor(.Green); + tty_config.setColor(out_stream, .Green); try out_stream.write("^"); - if (tty_color) setTtyColor(.Reset); + tty_config.setColor(out_stream, .Reset); } try out_stream.write("\n"); } else |err| switch (err) { @@ -1167,11 +1183,11 @@ pub const DwarfInfo = struct { self: *DwarfInfo, out_stream: var, address: usize, - tty_color: bool, + tty_config: TTY.Config, comptime printLineFromFile: var, ) !void { const compile_unit = self.findCompileUnit(address) catch { - return printLineInfo(out_stream, null, address, "???", "???", tty_color, printLineFromFile); + return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFile); }; const compile_unit_name = try compile_unit.die.getAttrString(self, DW.AT_name); @@ -1188,7 +1204,7 @@ pub const DwarfInfo = struct { address, symbol_name, compile_unit_name, - tty_color, + tty_config, printLineFromFile, ); } -- cgit v1.2.3 From 51ac8eb08e7542c25c7d3a0d1ebeac040544e4c0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 26 Jan 2020 18:28:52 -0500 Subject: fix regression in windows stack traces tty detection --- lib/std/debug.zig | 4 ++-- test/tests.zig | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 75d2afb8e9..857fe9223f 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -81,7 +81,7 @@ pub fn getSelfDebugInfo() !*DebugInfo { } } -fn detectTTYConfig() TTY.Config { +pub fn detectTTYConfig() TTY.Config { var bytes: [128]u8 = undefined; const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; if (process.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| { @@ -89,7 +89,7 @@ fn detectTTYConfig() TTY.Config { } else |_| { if (stderr_file.supportsAnsiEscapeCodes()) { return .escape_codes; - } else if (builtin.os == .windows) { + } else if (builtin.os == .windows and stderr_file.isTty()) { return .windows_api; } else { return .no_color; diff --git a/test/tests.zig b/test/tests.zig index 4d1f15fe29..f079b4664c 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -622,6 +622,9 @@ pub const StackTracesContext = struct { child.stderr_behavior = .Pipe; child.env_map = b.env_map; + if (b.verbose) { + printInvocation(args.toSliceConst()); + } child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", .{ full_exe_path, @errorName(err) }); var stdout = Buffer.initNull(b.allocator); -- cgit v1.2.3 From f8e015c85f2eaf4d35523d362a7be2b9614a162b Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 27 Jan 2020 13:10:35 +0100 Subject: Handle S_GPROC32 symbols in PDB files Fixes some incomplete stack traces on Windows. --- lib/std/debug.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 857fe9223f..442b5db210 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -412,7 +412,7 @@ fn printSourceAtAddressWindows( if (prefix.RecordLen < 2) return error.InvalidDebugInfo; switch (prefix.RecordKind) { - pdb.SymbolKind.S_LPROC32 => { + .S_LPROC32, .S_GPROC32 => { const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]); const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; const vaddr_end = vaddr_start + proc_sym.CodeSize; -- cgit v1.2.3 From 7336b750bd5e3c271a654e6d44d572cf0427a0b1 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 27 Jan 2020 13:12:01 +0100 Subject: Fix stack-trace address calculation on Windows Let's always subtract 1 from the return address so that we're sure to be inside the callee. Fixes some edge case where the stack trace skipped the first entry. --- lib/std/debug.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 442b5db210..97c20e5104 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -360,7 +360,7 @@ pub fn writeCurrentStackTraceWindows( return; } else 0; for (addrs[start_i..]) |addr| { - try printSourceAtAddress(debug_info, out_stream, addr, tty_config); + try printSourceAtAddress(debug_info, out_stream, addr - 1, tty_config); } } -- cgit v1.2.3 From d5c2a20d8e2a8c694127fffbe0d1a19f5eaaf92f Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 23 Jan 2020 18:07:05 +0100 Subject: Unify the two DWARF interpreters * Let's consolidate the special-cased DWARF interpreter for OSX with the general purpose one * Drop the assumption that all the debug data is contained in a single contiguous slice of memory. This is a good news for freestanding targets and paves the way for supporting compressed debug sections. --- lib/std/c/darwin.zig | 3 + lib/std/debug.zig | 593 ++++++++++++++++++++------------------------------- 2 files changed, 232 insertions(+), 364 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index bc58b1fba1..524c82211e 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -7,7 +7,10 @@ usingnamespace @import("../os/bits.zig"); extern "c" fn __error() *c_int; pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int; +pub extern "c" fn _dyld_image_count() u32; pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header; +pub extern "c" fn _dyld_get_image_vmaddr_slide(image_index: u32) usize; +pub extern "c" fn _dyld_get_image_name(image_index: u32) [*:0]const u8; pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) isize; diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 857fe9223f..0dad04fae7 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -963,59 +963,61 @@ pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { pub fn openElfDebugInfo( allocator: *mem.Allocator, - elf_seekable_stream: *DwarfSeekableStream, - elf_in_stream: *DwarfInStream, + data: []u8, ) !DwarfInfo { - var efile = try elf.Elf.openStream(allocator, elf_seekable_stream, elf_in_stream); - errdefer efile.close(); + var seekable_stream = io.SliceSeekableInStream.init(data); + var efile = try elf.Elf.openStream( + allocator, + @ptrCast(*DwarfSeekableStream, &seekable_stream.seekable_stream), + @ptrCast(*DwarfInStream, &seekable_stream.stream), + ); + defer efile.close(); + + const debug_info = (try efile.findSection(".debug_info")) orelse + return error.MissingDebugInfo; + const debug_abbrev = (try efile.findSection(".debug_abbrev")) orelse + return error.MissingDebugInfo; + const debug_str = (try efile.findSection(".debug_str")) orelse + return error.MissingDebugInfo; + const debug_line = (try efile.findSection(".debug_line")) orelse + return error.MissingDebugInfo; + const opt_debug_ranges = try efile.findSection(".debug_ranges"); var di = DwarfInfo{ - .dwarf_seekable_stream = elf_seekable_stream, - .dwarf_in_stream = elf_in_stream, .endian = efile.endian, - .debug_info = (try findDwarfSectionFromElf(&efile, ".debug_info")) orelse return error.MissingDebugInfo, - .debug_abbrev = (try findDwarfSectionFromElf(&efile, ".debug_abbrev")) orelse return error.MissingDebugInfo, - .debug_str = (try findDwarfSectionFromElf(&efile, ".debug_str")) orelse return error.MissingDebugInfo, - .debug_line = (try findDwarfSectionFromElf(&efile, ".debug_line")) orelse return error.MissingDebugInfo, - .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")), - .abbrev_table_list = undefined, - .compile_unit_list = undefined, - .func_list = undefined, + .debug_info = (data[@intCast(usize, debug_info.offset)..@intCast(usize, debug_info.offset + debug_info.size)]), + .debug_abbrev = (data[@intCast(usize, debug_abbrev.offset)..@intCast(usize, debug_abbrev.offset + debug_abbrev.size)]), + .debug_str = (data[@intCast(usize, debug_str.offset)..@intCast(usize, debug_str.offset + debug_str.size)]), + .debug_line = (data[@intCast(usize, debug_line.offset)..@intCast(usize, debug_line.offset + debug_line.size)]), + .debug_ranges = if (opt_debug_ranges) |debug_ranges| + data[@intCast(usize, debug_ranges.offset)..@intCast(usize, debug_ranges.offset + debug_ranges.size)] + else + null, }; + + efile.close(); + try openDwarfDebugInfo(&di, allocator); return di; } fn openSelfDebugInfoPosix(allocator: *mem.Allocator) !DwarfInfo { - const S = struct { - var self_exe_file: File = undefined; - var self_exe_mmap_seekable: io.SliceSeekableInStream = undefined; - }; - - S.self_exe_file = try fs.openSelfExe(); - errdefer S.self_exe_file.close(); + var exe_file = try fs.openSelfExe(); + errdefer exe_file.close(); - const self_exe_len = math.cast(usize, try S.self_exe_file.getEndPos()) catch return error.DebugInfoTooLarge; - const self_exe_mmap_len = mem.alignForward(self_exe_len, mem.page_size); - const self_exe_mmap = try os.mmap( + const exe_len = math.cast(usize, try exe_file.getEndPos()) catch + return error.DebugInfoTooLarge; + const exe_mmap = try os.mmap( null, - self_exe_mmap_len, + exe_len, os.PROT_READ, os.MAP_SHARED, - S.self_exe_file.handle, + exe_file.handle, 0, ); - errdefer os.munmap(self_exe_mmap); + errdefer os.munmap(exe_mmap); - S.self_exe_mmap_seekable = io.SliceSeekableInStream.init(self_exe_mmap); - - return openElfDebugInfo( - allocator, - // TODO https://github.com/ziglang/zig/issues/764 - @ptrCast(*DwarfSeekableStream, &S.self_exe_mmap_seekable.seekable_stream), - // TODO https://github.com/ziglang/zig/issues/764 - @ptrCast(*DwarfInStream, &S.self_exe_mmap_seekable.stream), - ); + return openElfDebugInfo(allocator, exe_mmap); } fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { @@ -1142,41 +1144,26 @@ const MachoSymbol = struct { } }; -const MachOFile = struct { - bytes: []align(@alignOf(macho.mach_header_64)) const u8, - sect_debug_info: ?*const macho.section_64, - sect_debug_line: ?*const macho.section_64, -}; - pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror); pub const DwarfInStream = io.InStream(anyerror); pub const DwarfInfo = struct { - dwarf_seekable_stream: *DwarfSeekableStream, - dwarf_in_stream: *DwarfInStream, endian: builtin.Endian, - debug_info: Section, - debug_abbrev: Section, - debug_str: Section, - debug_line: Section, - debug_ranges: ?Section, - abbrev_table_list: ArrayList(AbbrevTableHeader), - compile_unit_list: ArrayList(CompileUnit), - func_list: ArrayList(Func), - - pub const Section = struct { - offset: u64, - size: u64, - }; + // No memory is owned by the DwarfInfo + debug_info: []u8, + debug_abbrev: []u8, + debug_str: []u8, + debug_line: []u8, + debug_ranges: ?[]u8, + // Filled later by the initializer + abbrev_table_list: ArrayList(AbbrevTableHeader) = undefined, + compile_unit_list: ArrayList(CompileUnit) = undefined, + func_list: ArrayList(Func) = undefined, pub fn allocator(self: DwarfInfo) *mem.Allocator { return self.abbrev_table_list.allocator; } - pub fn readString(self: *DwarfInfo) ![]u8 { - return readStringRaw(self.allocator(), self.dwarf_in_stream); - } - /// This function works in freestanding mode. /// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void pub fn printSourceAtAddress( @@ -1222,35 +1209,38 @@ pub const DwarfInfo = struct { } fn scanAllFunctions(di: *DwarfInfo) !void { - const debug_info_end = di.debug_info.offset + di.debug_info.size; - var this_unit_offset = di.debug_info.offset; + var s = io.SliceSeekableInStream.init(di.debug_info); + var this_unit_offset: u64 = 0; - while (this_unit_offset < debug_info_end) { - try di.dwarf_seekable_stream.seekTo(this_unit_offset); + while (true) { + s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) { + error.EndOfStream => return, + else => return err, + }; var is_64: bool = undefined; - const unit_length = try readInitialLength(@TypeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); + const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64); if (unit_length == 0) return; const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); - const version = try di.dwarf_in_stream.readInt(u16, di.endian); + const version = try s.stream.readInt(u16, di.endian); if (version < 2 or version > 5) return error.InvalidDebugInfo; - const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian); + const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian); - const address_size = try di.dwarf_in_stream.readByte(); + const address_size = try s.stream.readByte(); if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; - const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); + const compile_unit_pos = try s.seekable_stream.getPos(); const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); - try di.dwarf_seekable_stream.seekTo(compile_unit_pos); + try s.seekable_stream.seekTo(compile_unit_pos); const next_unit_pos = this_unit_offset + next_offset; - while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) { - const die_obj = (try di.parseDie(abbrev_table, is_64)) orelse continue; - const after_die_offset = try di.dwarf_seekable_stream.getPos(); + while ((try s.seekable_stream.getPos()) < next_unit_pos) { + const die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse continue; + const after_die_offset = try s.seekable_stream.getPos(); switch (die_obj.tag_id) { DW.TAG_subprogram, DW.TAG_inlined_subroutine, DW.TAG_subroutine, DW.TAG_entry_point => { @@ -1266,14 +1256,14 @@ pub const DwarfInfo = struct { // Follow the DIE it points to and repeat const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin); if (ref_offset > next_offset) return error.InvalidDebugInfo; - try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset); - this_die_obj = (try di.parseDie(abbrev_table, is_64)) orelse return error.InvalidDebugInfo; + try s.seekable_stream.seekTo(this_unit_offset + ref_offset); + this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; } else if (this_die_obj.getAttr(DW.AT_specification)) |ref| { // Follow the DIE it points to and repeat const ref_offset = try this_die_obj.getAttrRef(DW.AT_specification); if (ref_offset > next_offset) return error.InvalidDebugInfo; - try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset); - this_die_obj = (try di.parseDie(abbrev_table, is_64)) orelse return error.InvalidDebugInfo; + try s.seekable_stream.seekTo(this_unit_offset + ref_offset); + this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; } else { break :x null; } @@ -1311,12 +1301,10 @@ pub const DwarfInfo = struct { .pc_range = pc_range, }); }, - else => { - continue; - }, + else => {}, } - try di.dwarf_seekable_stream.seekTo(after_die_offset); + try s.seekable_stream.seekTo(after_die_offset); } this_unit_offset += next_offset; @@ -1324,32 +1312,35 @@ pub const DwarfInfo = struct { } fn scanAllCompileUnits(di: *DwarfInfo) !void { - const debug_info_end = di.debug_info.offset + di.debug_info.size; - var this_unit_offset = di.debug_info.offset; + var s = io.SliceSeekableInStream.init(di.debug_info); + var this_unit_offset: u64 = 0; - while (this_unit_offset < debug_info_end) { - try di.dwarf_seekable_stream.seekTo(this_unit_offset); + while (true) { + s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) { + error.EndOfStream => return, + else => return err, + }; var is_64: bool = undefined; - const unit_length = try readInitialLength(@TypeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); + const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64); if (unit_length == 0) return; const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); - const version = try di.dwarf_in_stream.readInt(u16, di.endian); + const version = try s.stream.readInt(u16, di.endian); if (version < 2 or version > 5) return error.InvalidDebugInfo; - const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian); + const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian); - const address_size = try di.dwarf_in_stream.readByte(); + const address_size = try s.stream.readByte(); if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; - const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); + const compile_unit_pos = try s.seekable_stream.getPos(); const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); - try di.dwarf_seekable_stream.seekTo(compile_unit_pos); + try s.seekable_stream.seekTo(compile_unit_pos); const compile_unit_die = try di.allocator().create(Die); - compile_unit_die.* = (try di.parseDie(abbrev_table, is_64)) orelse return error.InvalidDebugInfo; + compile_unit_die.* = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; @@ -1395,15 +1386,18 @@ pub const DwarfInfo = struct { } if (di.debug_ranges) |debug_ranges| { if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { + var s = io.SliceSeekableInStream.init(debug_ranges); + // All the addresses in the list are relative to the value // specified by DW_AT_low_pc or to some other value encoded // in the list itself var base_address = try compile_unit.die.getAttrAddr(DW.AT_low_pc); - try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset); + try s.seekable_stream.seekTo(ranges_offset); + while (true) { - const begin_addr = try di.dwarf_in_stream.readIntLittle(usize); - const end_addr = try di.dwarf_in_stream.readIntLittle(usize); + const begin_addr = try s.stream.readIntLittle(usize); + const end_addr = try s.stream.readIntLittle(usize); if (begin_addr == 0 and end_addr == 0) { break; } @@ -1435,30 +1429,33 @@ pub const DwarfInfo = struct { return &header.table; } } - try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_offset); try di.abbrev_table_list.append(AbbrevTableHeader{ .offset = abbrev_offset, - .table = try di.parseAbbrevTable(), + .table = try di.parseAbbrevTable(abbrev_offset), }); return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table; } - fn parseAbbrevTable(di: *DwarfInfo) !AbbrevTable { + fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable { + var s = io.SliceSeekableInStream.init(di.debug_abbrev); + + try s.seekable_stream.seekTo(offset); var result = AbbrevTable.init(di.allocator()); + errdefer result.deinit(); while (true) { - const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream); + const abbrev_code = try leb.readULEB128(u64, &s.stream); if (abbrev_code == 0) return result; try result.append(AbbrevTableEntry{ .abbrev_code = abbrev_code, - .tag_id = try leb.readULEB128(u64, di.dwarf_in_stream), - .has_children = (try di.dwarf_in_stream.readByte()) == DW.CHILDREN_yes, + .tag_id = try leb.readULEB128(u64, &s.stream), + .has_children = (try s.stream.readByte()) == DW.CHILDREN_yes, .attrs = ArrayList(AbbrevAttr).init(di.allocator()), }); const attrs = &result.items[result.len - 1].attrs; while (true) { - const attr_id = try leb.readULEB128(u64, di.dwarf_in_stream); - const form_id = try leb.readULEB128(u64, di.dwarf_in_stream); + const attr_id = try leb.readULEB128(u64, &s.stream); + const form_id = try leb.readULEB128(u64, &s.stream); if (attr_id == 0 and form_id == 0) break; try attrs.append(AbbrevAttr{ .attr_id = attr_id, @@ -1468,8 +1465,8 @@ pub const DwarfInfo = struct { } } - fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !?Die { - const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream); + fn parseDie(di: *DwarfInfo, in_stream: var, abbrev_table: *const AbbrevTable, is_64: bool) !?Die { + const abbrev_code = try leb.readULEB128(u64, in_stream); if (abbrev_code == 0) return null; const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; @@ -1482,64 +1479,63 @@ pub const DwarfInfo = struct { for (table_entry.attrs.toSliceConst()) |attr, i| { result.attrs.items[i] = Die.Attr{ .id = attr.attr_id, - .value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64), + .value = try parseFormValue(di.allocator(), in_stream, attr.form_id, is_64), }; } return result; } fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo { + var s = io.SliceSeekableInStream.init(di.debug_line); + const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir); const line_info_offset = try compile_unit.die.getAttrSecOffset(DW.AT_stmt_list); - assert(line_info_offset < di.debug_line.size); - - const this_unit_offset = di.debug_line.offset + line_info_offset; - try di.dwarf_seekable_stream.seekTo(this_unit_offset); + try s.seekable_stream.seekTo(line_info_offset); var is_64: bool = undefined; - const unit_length = try readInitialLength(@TypeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); + const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64); if (unit_length == 0) { return error.MissingDebugInfo; } const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4)); - const version = try di.dwarf_in_stream.readInt(u16, di.endian); + const version = try s.stream.readInt(u16, di.endian); // TODO support 3 and 5 if (version != 2 and version != 4) return error.InvalidDebugInfo; - const prologue_length = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian); - const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length; + const prologue_length = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian); + const prog_start_offset = (try s.seekable_stream.getPos()) + prologue_length; - const minimum_instruction_length = try di.dwarf_in_stream.readByte(); + const minimum_instruction_length = try s.stream.readByte(); if (minimum_instruction_length == 0) return error.InvalidDebugInfo; if (version >= 4) { // maximum_operations_per_instruction - _ = try di.dwarf_in_stream.readByte(); + _ = try s.stream.readByte(); } - const default_is_stmt = (try di.dwarf_in_stream.readByte()) != 0; - const line_base = try di.dwarf_in_stream.readByteSigned(); + const default_is_stmt = (try s.stream.readByte()) != 0; + const line_base = try s.stream.readByteSigned(); - const line_range = try di.dwarf_in_stream.readByte(); + const line_range = try s.stream.readByte(); if (line_range == 0) return error.InvalidDebugInfo; - const opcode_base = try di.dwarf_in_stream.readByte(); + const opcode_base = try s.stream.readByte(); const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1); { var i: usize = 0; while (i < opcode_base - 1) : (i += 1) { - standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte(); + standard_opcode_lengths[i] = try s.stream.readByte(); } } var include_directories = ArrayList([]u8).init(di.allocator()); try include_directories.append(compile_unit_cwd); while (true) { - const dir = try di.readString(); + const dir = try readStringRaw(di.allocator(), &s.stream); if (dir.len == 0) break; try include_directories.append(dir); } @@ -1548,11 +1544,11 @@ pub const DwarfInfo = struct { var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); while (true) { - const file_name = try di.readString(); + const file_name = try readStringRaw(di.allocator(), &s.stream); if (file_name.len == 0) break; - const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream); - const mtime = try leb.readULEB128(usize, di.dwarf_in_stream); - const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream); + const dir_index = try leb.readULEB128(usize, &s.stream); + const mtime = try leb.readULEB128(usize, &s.stream); + const len_bytes = try leb.readULEB128(usize, &s.stream); try file_entries.append(FileEntry{ .file_name = file_name, .dir_index = dir_index, @@ -1561,17 +1557,17 @@ pub const DwarfInfo = struct { }); } - try di.dwarf_seekable_stream.seekTo(prog_start_offset); + try s.seekable_stream.seekTo(prog_start_offset); - const next_unit_pos = this_unit_offset + next_offset; + const next_unit_pos = line_info_offset + next_offset; - while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) { - const opcode = try di.dwarf_in_stream.readByte(); + while ((try s.seekable_stream.getPos()) < next_unit_pos) { + const opcode = try s.stream.readByte(); if (opcode == DW.LNS_extended_op) { - const op_size = try leb.readULEB128(u64, di.dwarf_in_stream); + const op_size = try leb.readULEB128(u64, &s.stream); if (op_size < 1) return error.InvalidDebugInfo; - var sub_op = try di.dwarf_in_stream.readByte(); + var sub_op = try s.stream.readByte(); switch (sub_op) { DW.LNE_end_sequence => { prog.end_sequence = true; @@ -1579,14 +1575,14 @@ pub const DwarfInfo = struct { prog.reset(); }, DW.LNE_set_address => { - const addr = try di.dwarf_in_stream.readInt(usize, di.endian); + const addr = try s.stream.readInt(usize, di.endian); prog.address = addr; }, DW.LNE_define_file => { - const file_name = try di.readString(); - const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream); - const mtime = try leb.readULEB128(usize, di.dwarf_in_stream); - const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream); + const file_name = try readStringRaw(di.allocator(), &s.stream); + const dir_index = try leb.readULEB128(usize, &s.stream); + const mtime = try leb.readULEB128(usize, &s.stream); + const len_bytes = try leb.readULEB128(usize, &s.stream); try file_entries.append(FileEntry{ .file_name = file_name, .dir_index = dir_index, @@ -1596,7 +1592,7 @@ pub const DwarfInfo = struct { }, else => { const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo; - try di.dwarf_seekable_stream.seekBy(fwd_amt); + try s.seekable_stream.seekBy(fwd_amt); }, } } else if (opcode >= opcode_base) { @@ -1615,19 +1611,19 @@ pub const DwarfInfo = struct { prog.basic_block = false; }, DW.LNS_advance_pc => { - const arg = try leb.readULEB128(usize, di.dwarf_in_stream); + const arg = try leb.readULEB128(usize, &s.stream); prog.address += arg * minimum_instruction_length; }, DW.LNS_advance_line => { - const arg = try leb.readILEB128(i64, di.dwarf_in_stream); + const arg = try leb.readILEB128(i64, &s.stream); prog.line += arg; }, DW.LNS_set_file => { - const arg = try leb.readULEB128(usize, di.dwarf_in_stream); + const arg = try leb.readULEB128(usize, &s.stream); prog.file = arg; }, DW.LNS_set_column => { - const arg = try leb.readULEB128(u64, di.dwarf_in_stream); + const arg = try leb.readULEB128(u64, &s.stream); prog.column = arg; }, DW.LNS_negate_stmt => { @@ -1641,14 +1637,14 @@ pub const DwarfInfo = struct { prog.address += inc_addr; }, DW.LNS_fixed_advance_pc => { - const arg = try di.dwarf_in_stream.readInt(u16, di.endian); + const arg = try s.stream.readInt(u16, di.endian); prog.address += arg; }, DW.LNS_set_prologue_end => {}, else => { if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; const len_bytes = standard_opcode_lengths[opcode - 1]; - try di.dwarf_seekable_stream.seekBy(len_bytes); + try s.seekable_stream.seekBy(len_bytes); }, } } @@ -1658,9 +1654,17 @@ pub const DwarfInfo = struct { } fn getString(di: *DwarfInfo, offset: u64) ![]u8 { - const pos = di.debug_str.offset + offset; - try di.dwarf_seekable_stream.seekTo(pos); - return di.readString(); + if (offset > di.debug_str.len) + return error.InvalidDebugInfo; + const casted_offset = math.cast(usize, offset) catch + return error.InvalidDebugInfo; + + // Valid strings always have a terminating zero byte + if (mem.indexOfScalarPos(u8, di.debug_str, casted_offset, 0)) |last| { + return di.debug_str[casted_offset..last]; + } + + return error.InvalidDebugInfo; } }; @@ -1672,7 +1676,7 @@ pub const DebugInfo = switch (builtin.os) { const OFileTable = std.HashMap( *macho.nlist_64, - MachOFile, + DwarfInfo, std.hash_map.getHashPtrAddrFn(*macho.nlist_64), std.hash_map.getTrivialEqlFn(*macho.nlist_64), ); @@ -2066,24 +2070,32 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con return null; } -fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: usize) !LineInfo { +fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, address: usize) !LineInfo { const ofile = symbol.ofile orelse return error.MissingDebugInfo; const gop = try di.ofiles.getOrPut(ofile); - const mach_o_file = if (gop.found_existing) &gop.kv.value else blk: { + const dwarf_info = if (gop.found_existing) &gop.kv.value else blk: { errdefer _ = di.ofiles.remove(ofile); const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx)); - gop.kv.value = MachOFile{ - .bytes = try std.fs.cwd().readFileAllocAligned( - di.ofiles.allocator, - ofile_path, - maxInt(usize), - @alignOf(macho.mach_header_64), - ), - .sect_debug_info = null, - .sect_debug_line = null, - }; - const hdr = @ptrCast(*const macho.mach_header_64, gop.kv.value.bytes.ptr); + var exe_file = try std.fs.openFileAbsoluteC(ofile_path, .{}); + errdefer exe_file.close(); + + const exe_len = math.cast(usize, try exe_file.getEndPos()) catch + return error.DebugInfoTooLarge; + const exe_mmap = try os.mmap( + null, + exe_len, + os.PROT_READ, + os.MAP_SHARED, + exe_file.handle, + 0, + ); + errdefer os.munmap(exe_mmap); + + const hdr = @ptrCast( + *const macho.mach_header_64, + @alignCast(@alignOf(macho.mach_header_64), exe_mmap.ptr), + ); if (hdr.magic != std.macho.MH_MAGIC_64) return error.InvalidDebugInfo; const hdr_base = @ptrCast([*]const u8, hdr); @@ -2092,181 +2104,75 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u const segcmd = while (ncmd != 0) : (ncmd -= 1) { const lc = @ptrCast(*const std.macho.load_command, ptr); switch (lc.cmd) { - std.macho.LC_SEGMENT_64 => break @ptrCast(*const std.macho.segment_command_64, @alignCast(@alignOf(std.macho.segment_command_64), ptr)), + std.macho.LC_SEGMENT_64 => { + break @ptrCast( + *const std.macho.segment_command_64, + @alignCast(@alignOf(std.macho.segment_command_64), ptr), + ); + }, else => {}, } ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize); } else { return error.MissingDebugInfo; }; + + var opt_debug_line: ?*const macho.section_64 = null; + var opt_debug_info: ?*const macho.section_64 = null; + var opt_debug_abbrev: ?*const macho.section_64 = null; + var opt_debug_str: ?*const macho.section_64 = null; + var opt_debug_ranges: ?*const macho.section_64 = null; + const sections = @ptrCast([*]const macho.section_64, @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects]; for (sections) |*sect| { - if (sect.flags & macho.SECTION_TYPE == macho.S_REGULAR and - (sect.flags & macho.SECTION_ATTRIBUTES) & macho.S_ATTR_DEBUG == macho.S_ATTR_DEBUG) - { - const sect_name = mem.toSliceConst(u8, @ptrCast([*:0]const u8, §.sectname)); - if (mem.eql(u8, sect_name, "__debug_line")) { - gop.kv.value.sect_debug_line = sect; - } else if (mem.eql(u8, sect_name, "__debug_info")) { - gop.kv.value.sect_debug_info = sect; - } + // The section name may not exceed 16 chars and a trailing null may + // not be present + const name = if (mem.indexOfScalar(u8, sect.sectname[0..], 0)) |last| + sect.sectname[0..last] + else + sect.sectname[0..]; + + if (mem.eql(u8, name, "__debug_line")) { + opt_debug_line = sect; + } else if (mem.eql(u8, name, "__debug_info")) { + opt_debug_info = sect; + } else if (mem.eql(u8, name, "__debug_abbrev")) { + opt_debug_abbrev = sect; + } else if (mem.eql(u8, name, "__debug_str")) { + opt_debug_str = sect; + } else if (mem.eql(u8, name, "__debug_ranges")) { + opt_debug_ranges = sect; } } - break :blk &gop.kv.value; - }; - - const sect_debug_line = mach_o_file.sect_debug_line orelse return error.MissingDebugInfo; - var ptr = mach_o_file.bytes.ptr + sect_debug_line.offset; - - var is_64: bool = undefined; - const unit_length = try readInitialLengthMem(&ptr, &is_64); - if (unit_length == 0) return error.MissingDebugInfo; - - const version = readIntMem(&ptr, u16, builtin.Endian.Little); - // TODO support 3 and 5 - if (version != 2 and version != 4) return error.InvalidDebugInfo; - - const prologue_length = if (is_64) - readIntMem(&ptr, u64, builtin.Endian.Little) - else - readIntMem(&ptr, u32, builtin.Endian.Little); - const prog_start = ptr + prologue_length; - - const minimum_instruction_length = readByteMem(&ptr); - if (minimum_instruction_length == 0) return error.InvalidDebugInfo; - - if (version >= 4) { - // maximum_operations_per_instruction - ptr += 1; - } - - const default_is_stmt = readByteMem(&ptr) != 0; - const line_base = readByteSignedMem(&ptr); - - const line_range = readByteMem(&ptr); - if (line_range == 0) return error.InvalidDebugInfo; - - const opcode_base = readByteMem(&ptr); - - const standard_opcode_lengths = ptr[0 .. opcode_base - 1]; - ptr += opcode_base - 1; - - var include_directories = ArrayList([]const u8).init(di.allocator()); - try include_directories.append(""); - while (true) { - const dir = readStringMem(&ptr); - if (dir.len == 0) break; - try include_directories.append(dir); - } - - var file_entries = ArrayList(FileEntry).init(di.allocator()); - var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); + var debug_line = opt_debug_line orelse + return error.MissingDebugInfo; + var debug_info = opt_debug_info orelse + return error.MissingDebugInfo; + var debug_str = opt_debug_str orelse + return error.MissingDebugInfo; + var debug_abbrev = opt_debug_abbrev orelse + return error.MissingDebugInfo; - while (true) { - const file_name = readStringMem(&ptr); - if (file_name.len == 0) break; - const dir_index = try leb.readULEB128Mem(usize, &ptr); - const mtime = try leb.readULEB128Mem(usize, &ptr); - const len_bytes = try leb.readULEB128Mem(usize, &ptr); - try file_entries.append(FileEntry{ - .file_name = file_name, - .dir_index = dir_index, - .mtime = mtime, - .len_bytes = len_bytes, - }); - } + gop.kv.value = DwarfInfo{ + .endian = .Little, + .debug_info = exe_mmap[@intCast(usize, debug_info.offset)..@intCast(usize, debug_info.offset + debug_info.size)], + .debug_abbrev = exe_mmap[@intCast(usize, debug_abbrev.offset)..@intCast(usize, debug_abbrev.offset + debug_abbrev.size)], + .debug_str = exe_mmap[@intCast(usize, debug_str.offset)..@intCast(usize, debug_str.offset + debug_str.size)], + .debug_line = exe_mmap[@intCast(usize, debug_line.offset)..@intCast(usize, debug_line.offset + debug_line.size)], + .debug_ranges = if (opt_debug_ranges) |debug_ranges| + exe_mmap[@intCast(usize, debug_ranges.offset)..@intCast(usize, debug_ranges.offset + debug_ranges.size)] + else + null, + }; + try openDwarfDebugInfo(&gop.kv.value, di.allocator()); - ptr = prog_start; - while (true) { - const opcode = readByteMem(&ptr); - - if (opcode == DW.LNS_extended_op) { - const op_size = try leb.readULEB128Mem(u64, &ptr); - if (op_size < 1) return error.InvalidDebugInfo; - var sub_op = readByteMem(&ptr); - switch (sub_op) { - DW.LNE_end_sequence => { - prog.end_sequence = true; - if (try prog.checkLineMatch()) |info| return info; - return error.MissingDebugInfo; - }, - DW.LNE_set_address => { - const addr = readIntMem(&ptr, usize, builtin.Endian.Little); - prog.address = symbol.reloc + addr; - }, - DW.LNE_define_file => { - const file_name = readStringMem(&ptr); - const dir_index = try leb.readULEB128Mem(usize, &ptr); - const mtime = try leb.readULEB128Mem(usize, &ptr); - const len_bytes = try leb.readULEB128Mem(usize, &ptr); - try file_entries.append(FileEntry{ - .file_name = file_name, - .dir_index = dir_index, - .mtime = mtime, - .len_bytes = len_bytes, - }); - }, - else => { - ptr += op_size - 1; - }, - } - } else if (opcode >= opcode_base) { - // special opcodes - const adjusted_opcode = opcode - opcode_base; - const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range); - const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range); - prog.line += inc_line; - prog.address += inc_addr; - if (try prog.checkLineMatch()) |info| return info; - prog.basic_block = false; - } else { - switch (opcode) { - DW.LNS_copy => { - if (try prog.checkLineMatch()) |info| return info; - prog.basic_block = false; - }, - DW.LNS_advance_pc => { - const arg = try leb.readULEB128Mem(usize, &ptr); - prog.address += arg * minimum_instruction_length; - }, - DW.LNS_advance_line => { - const arg = try leb.readILEB128Mem(i64, &ptr); - prog.line += arg; - }, - DW.LNS_set_file => { - const arg = try leb.readULEB128Mem(usize, &ptr); - prog.file = arg; - }, - DW.LNS_set_column => { - const arg = try leb.readULEB128Mem(u64, &ptr); - prog.column = arg; - }, - DW.LNS_negate_stmt => { - prog.is_stmt = !prog.is_stmt; - }, - DW.LNS_set_basic_block => { - prog.basic_block = true; - }, - DW.LNS_const_add_pc => { - const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range); - prog.address += inc_addr; - }, - DW.LNS_fixed_advance_pc => { - const arg = readIntMem(&ptr, u16, builtin.Endian.Little); - prog.address += arg; - }, - DW.LNS_set_prologue_end => {}, - else => { - if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; - const len_bytes = standard_opcode_lengths[opcode - 1]; - ptr += len_bytes; - }, - } - } - } + break :blk &gop.kv.value; + }; - return error.MissingDebugInfo; + const o_file_address = address - symbol.reloc; + const compile_unit = try dwarf_info.findCompileUnit(o_file_address); + return dwarf_info.getLineNumberInfo(compile_unit.*, o_file_address); } const Func = struct { @@ -2274,47 +2180,6 @@ const Func = struct { name: ?[]u8, }; -fn readIntMem(ptr: *[*]const u8, comptime T: type, endian: builtin.Endian) T { - // TODO https://github.com/ziglang/zig/issues/863 - const size = (T.bit_count + 7) / 8; - const result = mem.readIntSlice(T, ptr.*[0..size], endian); - ptr.* += size; - return result; -} - -fn readByteMem(ptr: *[*]const u8) u8 { - const result = ptr.*[0]; - ptr.* += 1; - return result; -} - -fn readByteSignedMem(ptr: *[*]const u8) i8 { - return @bitCast(i8, readByteMem(ptr)); -} - -fn readInitialLengthMem(ptr: *[*]const u8, is_64: *bool) !u64 { - // TODO this code can be improved with https://github.com/ziglang/zig/issues/863 - const first_32_bits = mem.readIntSliceLittle(u32, ptr.*[0..4]); - is_64.* = (first_32_bits == 0xffffffff); - if (is_64.*) { - ptr.* += 4; - const result = mem.readIntSliceLittle(u64, ptr.*[0..8]); - ptr.* += 8; - return result; - } else { - if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; - ptr.* += 4; - // TODO this cast should not be needed - return @as(u64, first_32_bits); - } -} - -fn readStringMem(ptr: *[*]const u8) [:0]const u8 { - const result = mem.toSliceConst(u8, @ptrCast([*:0]const u8, ptr.*)); - ptr.* += result.len + 1; - return result; -} - fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 { const first_32_bits = try in_stream.readIntLittle(u32); is_64.* = (first_32_bits == 0xffffffff); -- cgit v1.2.3