commit 3e228bdf457d8be0fcbb8e44a8fa7a0b09677972 (tree)
parent 340a45683ca8e0b23e95f5fb86bd9c827970e6e8
Author: Jakub Konka <kubkon@jakubkonka.com>
Date: Wed, 16 Aug 2023 12:31:22 +0200
Merge pull request #16832 from kcbanner/coff_dwarf_and_pdb
Windows: Support loading debug symbols from both the PDB file and embedded DWARF information
Diffstat:
2 files changed, 74 insertions(+), 48 deletions(-)
diff --git a/lib/std/coff.zig b/lib/std/coff.zig
@@ -1059,6 +1059,8 @@ pub const CoffError = error{
// Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
pub const Coff = struct {
data: []const u8,
+ // Set if `data` is backed by the image as loaded by the loader
+ is_loaded: bool,
is_image: bool,
coff_header_offset: usize,
@@ -1066,7 +1068,7 @@ pub const Coff = struct {
age: u32 = undefined,
// The lifetime of `data` must be longer than the lifetime of the returned Coff
- pub fn init(data: []const u8) !Coff {
+ pub fn init(data: []const u8, is_loaded: bool) !Coff {
const pe_pointer_offset = 0x3C;
const pe_magic = "PE\x00\x00";
@@ -1082,6 +1084,7 @@ pub const Coff = struct {
var coff = @This(){
.data = data,
.is_image = is_image,
+ .is_loaded = is_loaded,
.coff_header_offset = coff_header_offset,
};
@@ -1098,27 +1101,40 @@ 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();
- try stream.seekTo(debug_dir.virtual_address);
+
+ if (self.is_loaded) {
+ try stream.seekTo(debug_dir.virtual_address);
+ } else {
+ // Find what section the debug_dir is in, in order to convert the RVA to a file offset
+ for (self.getSectionHeaders()) |*sect| {
+ if (debug_dir.virtual_address >= sect.virtual_address and debug_dir.virtual_address < sect.virtual_address + sect.virtual_size) {
+ try stream.seekTo(sect.pointer_to_raw_data + (debug_dir.virtual_address - sect.virtual_address));
+ break;
+ }
+ } else return error.InvalidDebugDirectory;
+ }
// Find the correct DebugDirectoryEntry, and where its data is stored.
// 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) {
- try stream.seekTo(debug_dir_entry.address_of_raw_data);
- break :blk;
+ 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;
}
- }
+ } else return null;
var cv_signature: [4]u8 = undefined; // CodeView signature
try reader.readNoEof(cv_signature[0..]);
@@ -1256,7 +1272,8 @@ pub const Coff = struct {
}
pub fn getSectionData(self: *const Coff, sec: *align(1) const SectionHeader) []const u8 {
- return self.data[sec.pointer_to_raw_data..][0..sec.virtual_size];
+ const offset = if (self.is_loaded) sec.virtual_address else sec.pointer_to_raw_data;
+ return self.data[offset..][0..sec.virtual_size];
}
pub fn getSectionDataAlloc(self: *const Coff, sec: *align(1) const SectionHeader, allocator: mem.Allocator) ![]u8 {
diff --git 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,32 +1021,33 @@ 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
- 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});
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;
+ 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;
+ // Only used by the pdb path
+ di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(allocator);
+ errdefer allocator.free(di.coff_section_headers);
+
return di;
}
}
@@ -1695,7 +1695,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 +1753,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 +2141,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 +2173,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 +2196,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;