From 5b86180ae3b451288bbc1aed5cf8040d4fcb65fd Mon Sep 17 00:00:00 2001 From: kcbanner Date: Tue, 15 Aug 2023 02:22:22 -0400 Subject: debug: support looking up debug symbols in both PDB and DWARF debug info, instead of only using DWARF if `.debug_info` is present coff: support reading from memory loaded by the loader, or a mapped file --- lib/std/debug.zig | 81 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 35 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/debug.zig b/lib/std/debug.zig index db9b93dff6..a59630385a 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -997,7 +997,6 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu .base_address = undefined, .coff_image_base = coff_obj.getImageBase(), .coff_section_headers = undefined, - .debug_data = undefined, }; if (coff_obj.getSectionByName(".debug_info")) |_| { @@ -1022,11 +1021,10 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu }; try DW.openDwarfDebugInfo(&dwarf, allocator); - di.debug_data = PdbOrDwarf{ .dwarf = dwarf }; - return di; + di.dwarf = dwarf; } - // Only used by pdb path + // Only used by the pdb path di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(allocator); errdefer allocator.free(di.coff_section_headers); @@ -1037,15 +1035,19 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path}); defer allocator.free(path); - di.debug_data = PdbOrDwarf{ .pdb = undefined }; - di.debug_data.pdb = pdb.Pdb.init(allocator, path) catch |err| switch (err) { - error.FileNotFound, error.IsDir => return error.MissingDebugInfo, + di.pdb = pdb.Pdb.init(allocator, path) catch |err| switch (err) { + error.FileNotFound, error.IsDir => { + if (di.dwarf == null) return error.MissingDebugInfo; + allocator.free(di.coff_section_headers); + di.coff_section_headers = undefined; + return di; + }, else => return err, }; - try di.debug_data.pdb.parseInfoStream(); - try di.debug_data.pdb.parseDbiStream(); + try di.pdb.?.parseInfoStream(); + try di.pdb.?.parseDbiStream(); - if (!mem.eql(u8, &coff_obj.guid, &di.debug_data.pdb.guid) or coff_obj.age != di.debug_data.pdb.age) + if (!mem.eql(u8, &coff_obj.guid, &di.pdb.?.guid) or coff_obj.age != di.pdb.?.age) return error.InvalidDebugInfo; return di; @@ -1695,7 +1697,7 @@ pub const DebugInfo = struct { errdefer self.allocator.destroy(obj_di); const mapped_module = @as([*]const u8, @ptrFromInt(module.base_address))[0..module.size]; - var coff_obj = try coff.Coff.init(mapped_module); + var coff_obj = try coff.Coff.init(mapped_module, true); // The string table is not mapped into memory by the loader, so if a section name is in the // string table then we have to map the full image file from disk. This can happen when @@ -1753,7 +1755,7 @@ pub const DebugInfo = struct { errdefer assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @ptrFromInt(base_ptr)) == .SUCCESS); const section_view = @as([*]const u8, @ptrFromInt(base_ptr))[0..coff_len]; - coff_obj = try coff.Coff.init(section_view); + coff_obj = try coff.Coff.init(section_view, false); module.mapped_file = .{ .file = coff_file, @@ -2141,34 +2143,27 @@ pub const ModuleDebugInfo = switch (native_os) { }, .uefi, .windows => struct { base_address: usize, - debug_data: PdbOrDwarf, + pdb: ?pdb.Pdb = null, + dwarf: ?DW.DwarfInfo = null, coff_image_base: u64, - /// Only used if debug_data is .pdb + + /// Only used if pdb is non-null coff_section_headers: []coff.SectionHeader, pub fn deinit(self: *@This(), allocator: mem.Allocator) void { - self.debug_data.deinit(allocator); - if (self.debug_data == .pdb) { - allocator.free(self.coff_section_headers); + if (self.dwarf) |*dwarf| { + dwarf.deinit(allocator); } - } - - pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo { - // 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_image_base; - return getSymbolFromDwarf(allocator, dwarf_address, dwarf); - }, - .pdb => { - // fallthrough to pdb handling - }, + if (self.pdb) |*p| { + p.deinit(); + allocator.free(self.coff_section_headers); } + } + fn getSymbolFromPdb(self: *@This(), relocated_address: usize) !?SymbolInfo { var coff_section: *align(1) const coff.SectionHeader = undefined; - const mod_index = for (self.debug_data.pdb.sect_contribs) |sect_contrib| { + const mod_index = for (self.pdb.?.sect_contribs) |sect_contrib| { if (sect_contrib.Section > self.coff_section_headers.len) continue; // Remember that SectionContribEntry.Section is 1-based. coff_section = &self.coff_section_headers[sect_contrib.Section - 1]; @@ -2180,18 +2175,18 @@ pub const ModuleDebugInfo = switch (native_os) { } } else { // we have no information to add to the address - return SymbolInfo{}; + return null; }; - const module = (try self.debug_data.pdb.getModule(mod_index)) orelse + const module = (try self.pdb.?.getModule(mod_index)) orelse return error.InvalidDebugInfo; const obj_basename = fs.path.basename(module.obj_file_name); - const symbol_name = self.debug_data.pdb.getSymbolName( + const symbol_name = self.pdb.?.getSymbolName( module, relocated_address - coff_section.virtual_address, ) orelse "???"; - const opt_line_info = try self.debug_data.pdb.getLineNumberInfo( + const opt_line_info = try self.pdb.?.getLineNumberInfo( module, relocated_address - coff_section.virtual_address, ); @@ -2203,6 +2198,22 @@ pub const ModuleDebugInfo = switch (native_os) { }; } + pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo { + // Translate the VA into an address into this object + const relocated_address = address - self.base_address; + + if (self.pdb != null) { + if (try self.getSymbolFromPdb(relocated_address)) |symbol| return symbol; + } + + if (self.dwarf) |*dwarf| { + const dwarf_address = relocated_address + self.coff_image_base; + return getSymbolFromDwarf(allocator, dwarf_address, dwarf); + } + + return SymbolInfo{}; + } + pub fn getDwarfInfoForAddress(self: *@This(), allocator: mem.Allocator, address: usize) !?*const DW.DwarfInfo { _ = allocator; _ = address; -- cgit v1.2.3 From 8a5f331ec832201c6e4bdf211cdd37ca5eb4347b Mon Sep 17 00:00:00 2001 From: kcbanner Date: Tue, 15 Aug 2023 02:44:34 -0400 Subject: coff: handle the case of there being no PDB path --- lib/std/coff.zig | 11 ++++++----- lib/std/debug.zig | 12 +++++------- 2 files changed, 11 insertions(+), 12 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/coff.zig b/lib/std/coff.zig index 8cd571e4b7..9db9f4d988 100644 --- a/lib/std/coff.zig +++ b/lib/std/coff.zig @@ -1101,12 +1101,13 @@ pub const Coff = struct { return coff; } - pub fn getPdbPath(self: *Coff, buffer: []u8) !usize { + pub fn getPdbPath(self: *Coff, buffer: []u8) !?usize { assert(self.is_image); const data_dirs = self.getDataDirectories(); - const debug_dir = data_dirs[@intFromEnum(DirectoryEntry.DEBUG)]; + if (@intFromEnum(DirectoryEntry.DEBUG) >= data_dirs.len) return null; + const debug_dir = data_dirs[@intFromEnum(DirectoryEntry.DEBUG)]; var stream = std.io.fixedBufferStream(self.data); const reader = stream.reader(); @@ -1126,14 +1127,14 @@ pub const Coff = struct { // It can be in any section. const debug_dir_entry_count = debug_dir.size / @sizeOf(DebugDirectoryEntry); var i: u32 = 0; - blk: while (i < debug_dir_entry_count) : (i += 1) { + while (i < debug_dir_entry_count) : (i += 1) { const debug_dir_entry = try reader.readStruct(DebugDirectoryEntry); if (debug_dir_entry.type == .CODEVIEW) { const dir_offset = if (self.is_loaded) debug_dir_entry.address_of_raw_data else debug_dir_entry.pointer_to_raw_data; try stream.seekTo(dir_offset); - break :blk; + break; } - } + } else return null; var cv_signature: [4]u8 = undefined; // CodeView signature try reader.readNoEof(cv_signature[0..]); diff --git a/lib/std/debug.zig b/lib/std/debug.zig index a59630385a..3296f8190a 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1024,12 +1024,8 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu di.dwarf = dwarf; } - // Only used by the pdb path - di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(allocator); - errdefer allocator.free(di.coff_section_headers); - var path_buf: [windows.MAX_PATH]u8 = undefined; - const len = try coff_obj.getPdbPath(path_buf[0..]); + const len = try coff_obj.getPdbPath(path_buf[0..]) orelse return di; const raw_path = path_buf[0..len]; const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path}); @@ -1038,8 +1034,6 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu di.pdb = pdb.Pdb.init(allocator, path) catch |err| switch (err) { error.FileNotFound, error.IsDir => { if (di.dwarf == null) return error.MissingDebugInfo; - allocator.free(di.coff_section_headers); - di.coff_section_headers = undefined; return di; }, else => return err, @@ -1050,6 +1044,10 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu if (!mem.eql(u8, &coff_obj.guid, &di.pdb.?.guid) or coff_obj.age != di.pdb.?.age) return error.InvalidDebugInfo; + // Only used by the pdb path + di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(allocator); + errdefer allocator.free(di.coff_section_headers); + return di; } } -- cgit v1.2.3