diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 059f6e73a78e..025f10ef0e6b 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -22,6 +22,10 @@ const Progress = @This(); /// not print on update() terminal: ?std.fs.File = undefined, +/// Is this a windows API terminal (note: this is not the same as being run on windows +/// because other terminals exist like MSYS/git-bash) +is_windows_terminal: bool = false, + /// Whether the terminal supports ANSI escape codes. supports_ansi_escape_codes: bool = false, @@ -143,6 +147,7 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !* self.terminal = stderr; self.supports_ansi_escape_codes = true; } else if (std.builtin.os.tag == .windows and stderr.isTty()) { + self.is_windows_terminal = true; self.terminal = stderr; } else if (std.builtin.os.tag != .windows) { // we are in a "dumb" terminal like in acme or writing to a file @@ -192,8 +197,9 @@ const DECRC = "\x1b8"; // supported by some terminals (eg. Terminal.app). fn refreshWithHeldLock(self: *Progress) void { - const is_dumb = !self.supports_ansi_escape_codes and !(std.builtin.os.tag == .windows); + const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal; if (is_dumb and self.dont_print_on_dumb) return; + const file = self.terminal orelse return; var end: usize = 0; @@ -206,6 +212,8 @@ fn refreshWithHeldLock(self: *Progress) void { std.mem.copy(u8, self.output_buffer[end..], seq_before); end += seq_before.len; } else if (std.builtin.os.tag == .windows) winapi: { + std.debug.assert(self.is_windows_terminal); + var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) unreachable; @@ -282,7 +290,7 @@ fn refreshWithHeldLock(self: *Progress) void { const seq_after = DECRC; std.mem.copy(u8, self.output_buffer[end..], seq_after); end += seq_after.len; - } else if (std.builtin.os.tag != .windows) { + } else if (!self.is_windows_terminal) { self.output_buffer[end] = '\n'; end += 1; } @@ -293,8 +301,10 @@ fn refreshWithHeldLock(self: *Progress) void { }; if (std.builtin.os.tag == .windows) { - if (windows.kernel32.SetConsoleCursorPosition(file.handle, saved_cursor_pos) != windows.TRUE) - unreachable; + if (self.is_windows_terminal) { + const res = windows.kernel32.SetConsoleCursorPosition(file.handle, saved_cursor_pos); + std.debug.assert(res == windows.TRUE); + } } self.prev_refresh_timestamp = self.timer.read(); @@ -318,7 +328,7 @@ fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: any end.* = self.output_buffer.len; }, } - const bytes_needed_for_esc_codes_at_end = if (std.builtin.os.tag == .windows) 0 else 11; + const bytes_needed_for_esc_codes_at_end: u8 = if (self.is_windows_terminal) 0 else 11; const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end; if (end.* > max_end) { const suffix = "... "; diff --git a/lib/std/coff.zig b/lib/std/coff.zig index 22ba7468fc5e..8b00a2429702 100644 --- a/lib/std/coff.zig +++ b/lib/std/coff.zig @@ -98,6 +98,7 @@ pub const CoffError = error{ InvalidPEHeader, InvalidMachine, MissingCoffSection, + MissingStringTable, }; // Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format @@ -162,20 +163,47 @@ pub const Coff = struct { try self.loadOptionalHeader(); } + fn readStringFromTable(self: *Coff, offset: usize, buf: []u8) ![]const u8 { + if (self.coff_header.pointer_to_symbol_table == 0) { + // No symbol table therefore no string table + return error.MissingStringTable; + } + // The string table is at the end of the symbol table and symbols are 18 bytes long + const string_table_offset = self.coff_header.pointer_to_symbol_table + (self.coff_header.number_of_symbols * 18) + offset; + const in = self.in_file.reader(); + const old_pos = try self.in_file.getPos(); + + try self.in_file.seekTo(string_table_offset); + defer { + self.in_file.seekTo(old_pos) catch unreachable; + } + + const str = try in.readUntilDelimiterOrEof(buf, 0); + return str orelse ""; + } + fn loadOptionalHeader(self: *Coff) !void { const in = self.in_file.reader(); + const opt_header_pos = try self.in_file.getPos(); + self.pe_header.magic = try in.readIntLittle(u16); - // For now we're only interested in finding the reference to the .pdb, - // so we'll skip most of this header, which size is different in 32 - // 64 bits by the way. - var skip_size: u16 = undefined; + // All we care about is the image base value and PDB info + // The header structure is different for 32 or 64 bit + var num_rva_pos: u64 = undefined; if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { - skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 18 * @sizeOf(u32); + num_rva_pos = opt_header_pos + 92; + + try self.in_file.seekTo(opt_header_pos + 28); + const image_base32 = try in.readIntLittle(u32); + self.pe_header.image_base = image_base32; } else if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { - skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 12 * @sizeOf(u32) + 5 * @sizeOf(u64); + num_rva_pos = opt_header_pos + 108; + + try self.in_file.seekTo(opt_header_pos + 24); + self.pe_header.image_base = try in.readIntLittle(u64); } else return error.InvalidPEMagic; - try self.in_file.seekBy(skip_size); + try self.in_file.seekTo(num_rva_pos); const number_of_rva_and_sizes = try in.readIntLittle(u32); if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES) @@ -258,11 +286,23 @@ pub const Coff = struct { const in = self.in_file.reader(); - var name: [8]u8 = undefined; + var name: [32]u8 = undefined; var i: u16 = 0; while (i < self.coff_header.number_of_sections) : (i += 1) { - try in.readNoEof(name[0..]); + try in.readNoEof(name[0..8]); + + if (name[0] == '/') { + // This is a long name and stored in the string table + const offset_len = mem.indexOfScalar(u8, name[1..], 0) orelse 7; + + const str_offset = try std.fmt.parseInt(u32, name[1 .. offset_len + 1], 10); + const str = try self.readStringFromTable(str_offset, &name); + std.mem.set(u8, name[str.len..], 0); + } else { + std.mem.set(u8, name[8..], 0); + } + try self.sections.append(Section{ .header = SectionHeader{ .name = name, @@ -288,6 +328,22 @@ pub const Coff = struct { } return null; } + + // Return an owned slice full of the section data + pub fn getSectionData(self: *Coff, comptime name: []const u8, allocator: *mem.Allocator) ![]u8 { + const sec = for (self.sections.items) |*sec| { + if (mem.eql(u8, sec.header.name[0..name.len], name)) { + break sec; + } + } else { + return error.MissingCoffSection; + }; + const in = self.in_file.reader(); + try self.in_file.seekTo(sec.header.pointer_to_raw_data); + const out_buff = try allocator.alloc(u8, sec.header.misc.virtual_size); + try in.readNoEof(out_buff); + return out_buff; + } }; const CoffHeader = struct { @@ -308,6 +364,7 @@ const OptionalHeader = struct { magic: u16, data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory, + image_base: u64, }; const DebugDirectoryEntry = packed struct { @@ -331,7 +388,7 @@ const SectionHeader = struct { virtual_size: u32, }; - name: [8]u8, + name: [32]u8, misc: Misc, virtual_address: u32, size_of_raw_data: u32, diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 23f4867e8194..926e518ca1c5 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -53,6 +53,10 @@ pub const SymbolInfo = struct { } } }; +const PdbOrDwarf = union(enum) { + pdb: pdb.Pdb, + dwarf: DW.DwarfInfo, +}; var stderr_mutex = std.Thread.Mutex{}; @@ -669,10 +673,32 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf var di = ModuleDebugInfo{ .base_address = undefined, .coff = coff_obj, - .pdb = undefined, + .debug_data = undefined, }; try di.coff.loadHeader(); + try di.coff.loadSections(); + if (di.coff.getSection(".debug_info")) |sec| { + // This coff file has embedded DWARF debug info + // TODO: free the section data slices + const debug_info_data = di.coff.getSectionData(".debug_info", allocator) catch null; + const debug_abbrev_data = di.coff.getSectionData(".debug_abbrev", allocator) catch null; + const debug_str_data = di.coff.getSectionData(".debug_str", allocator) catch null; + const debug_line_data = di.coff.getSectionData(".debug_line", allocator) catch null; + const debug_ranges_data = di.coff.getSectionData(".debug_ranges", allocator) catch null; + + var dwarf = DW.DwarfInfo{ + .endian = native_endian, + .debug_info = debug_info_data orelse return error.MissingDebugInfo, + .debug_abbrev = debug_abbrev_data orelse return error.MissingDebugInfo, + .debug_str = debug_str_data orelse return error.MissingDebugInfo, + .debug_line = debug_line_data orelse return error.MissingDebugInfo, + .debug_ranges = debug_ranges_data, + }; + try DW.openDwarfDebugInfo(&dwarf, allocator); + di.debug_data = PdbOrDwarf{ .dwarf = dwarf }; + return di; + } var path_buf: [windows.MAX_PATH]u8 = undefined; const len = try di.coff.getPdbPath(path_buf[0..]); @@ -681,11 +707,12 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path}); defer allocator.free(path); - di.pdb = try pdb.Pdb.init(allocator, path); - try di.pdb.parseInfoStream(); - try di.pdb.parseDbiStream(); + di.debug_data = PdbOrDwarf{ .pdb = undefined }; + di.debug_data.pdb = try pdb.Pdb.init(allocator, path); + try di.debug_data.pdb.parseInfoStream(); + try di.debug_data.pdb.parseDbiStream(); - if (!mem.eql(u8, &di.coff.guid, &di.pdb.guid) or di.coff.age != di.pdb.age) + if (!mem.eql(u8, &di.coff.guid, &di.debug_data.pdb.guid) or di.coff.age != di.debug_data.pdb.age) return error.InvalidDebugInfo; return di; @@ -1331,7 +1358,7 @@ pub const ModuleDebugInfo = switch (native_os) { }, .uefi, .windows => struct { base_address: usize, - pdb: pdb.Pdb, + debug_data: PdbOrDwarf, coff: *coff.Coff, pub fn allocator(self: @This()) *mem.Allocator { @@ -1342,8 +1369,18 @@ pub const ModuleDebugInfo = switch (native_os) { // Translate the VA into an address into this object const relocated_address = address - self.base_address; + switch (self.debug_data) { + .dwarf => |*dwarf| { + const dwarf_address = relocated_address + self.coff.pe_header.image_base; + return getSymbolFromDwarf(dwarf_address, dwarf); + }, + .pdb => { + // fallthrough to pdb handling + }, + } + var coff_section: *coff.Section = undefined; - const mod_index = for (self.pdb.sect_contribs) |sect_contrib| { + const mod_index = for (self.debug_data.pdb.sect_contribs) |sect_contrib| { if (sect_contrib.Section > self.coff.sections.items.len) continue; // Remember that SectionContribEntry.Section is 1-based. coff_section = &self.coff.sections.items[sect_contrib.Section - 1]; @@ -1358,15 +1395,15 @@ pub const ModuleDebugInfo = switch (native_os) { return SymbolInfo{}; }; - const module = (try self.pdb.getModule(mod_index)) orelse + const module = (try self.debug_data.pdb.getModule(mod_index)) orelse return error.InvalidDebugInfo; const obj_basename = fs.path.basename(module.obj_file_name); - const symbol_name = self.pdb.getSymbolName( + const symbol_name = self.debug_data.pdb.getSymbolName( module, relocated_address - coff_section.header.virtual_address, ) orelse "???"; - const opt_line_info = try self.pdb.getLineNumberInfo( + const opt_line_info = try self.debug_data.pdb.getLineNumberInfo( module, relocated_address - coff_section.header.virtual_address, ); @@ -1386,32 +1423,33 @@ pub const ModuleDebugInfo = switch (native_os) { pub fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { // Translate the VA into an address into this object const relocated_address = address - self.base_address; - - if (nosuspend self.dwarf.findCompileUnit(relocated_address)) |compile_unit| { - return SymbolInfo{ - .symbol_name = nosuspend self.dwarf.getSymbolName(relocated_address) orelse "???", - .compile_unit_name = compile_unit.die.getAttrString(&self.dwarf, DW.AT_name) catch |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => "???", - else => return err, - }, - .line_info = nosuspend self.dwarf.getLineNumberInfo(compile_unit.*, relocated_address) catch |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => null, - else => return err, - }, - }; - } else |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => { - return SymbolInfo{}; - }, - else => return err, - } - - unreachable; + return getSymbolFromDwarf(relocated_address, &self.dwarf); } }, else => DW.DwarfInfo, }; +fn getSymbolFromDwarf(address: u64, di: *DW.DwarfInfo) !SymbolInfo { + if (nosuspend di.findCompileUnit(address)) |compile_unit| { + return SymbolInfo{ + .symbol_name = nosuspend di.getSymbolName(address) orelse "???", + .compile_unit_name = compile_unit.die.getAttrString(di, DW.AT_name) catch |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => "???", + else => return err, + }, + .line_info = nosuspend di.getLineNumberInfo(compile_unit.*, address) catch |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => null, + else => return err, + }, + }; + } else |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => { + return SymbolInfo{}; + }, + else => return err, + } +} + /// TODO multithreaded awareness var debug_info_allocator: ?*mem.Allocator = null; var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined; diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index 93736f3d9c20..116f5b01ffb4 100644 --- a/lib/std/dwarf.zig +++ b/lib/std/dwarf.zig @@ -144,7 +144,7 @@ const FileEntry = struct { }; const LineNumberProgram = struct { - address: usize, + address: u64, file: usize, line: i64, column: u64, @@ -153,12 +153,12 @@ const LineNumberProgram = struct { end_sequence: bool, default_is_stmt: bool, - target_address: usize, + target_address: u64, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), prev_valid: bool, - prev_address: usize, + prev_address: u64, prev_file: usize, prev_line: i64, prev_column: u64, @@ -186,7 +186,7 @@ const LineNumberProgram = struct { self.prev_end_sequence = undefined; } - pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram { + pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: u64) LineNumberProgram { return LineNumberProgram{ .address = 0, .file = 1, @@ -691,7 +691,7 @@ pub const DwarfInfo = struct { return result; } - pub fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !debug.LineInfo { + pub fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: u64) !debug.LineInfo { var stream = io.fixedBufferStream(di.debug_line); const in = &stream.reader(); const seekable = &stream.seekableStream();