authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-08-15 02:22:22-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-08-15 10:20:11-04:00
log5b86180ae3b451288bbc1aed5cf8040d4fcb65fd
tree7452ba33129d590160779844e5bf7b497314a61c
parenta155e35850def120af90993e1b4309b80b97eb85

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

2 files changed, 66 insertions(+), 39 deletions(-)

lib/std/coff.zig+20-4
......@@ -1059,6 +1059,8 @@ pub const CoffError = error{
10591059// Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
10601060pub const Coff = struct {
10611061 data: []const u8,
1062 // Set if `data` is backed by the image as loaded by the loader
1063 is_loaded: bool,
10621064 is_image: bool,
10631065 coff_header_offset: usize,
10641066
......@@ -1066,7 +1068,7 @@ pub const Coff = struct {
10661068 age: u32 = undefined,
10671069
10681070 // The lifetime of `data` must be longer than the lifetime of the returned Coff
1069 pub fn init(data: []const u8) !Coff {
1071 pub fn init(data: []const u8, is_loaded: bool) !Coff {
10701072 const pe_pointer_offset = 0x3C;
10711073 const pe_magic = "PE\x00\x00";
10721074
......@@ -1082,6 +1084,7 @@ pub const Coff = struct {
10821084 var coff = @This(){
10831085 .data = data,
10841086 .is_image = is_image,
1087 .is_loaded = is_loaded,
10851088 .coff_header_offset = coff_header_offset,
10861089 };
10871090
......@@ -1106,7 +1109,18 @@ pub const Coff = struct {
11061109
11071110 var stream = std.io.fixedBufferStream(self.data);
11081111 const reader = stream.reader();
1109 try stream.seekTo(debug_dir.virtual_address);
1112
1113 if (self.is_loaded) {
1114 try stream.seekTo(debug_dir.virtual_address);
1115 } else {
1116 // Find what section the debug_dir is in, in order to convert the RVA to a file offset
1117 for (self.getSectionHeaders()) |*sect| {
1118 if (debug_dir.virtual_address >= sect.virtual_address and debug_dir.virtual_address < sect.virtual_address + sect.virtual_size) {
1119 try stream.seekTo(sect.pointer_to_raw_data + (debug_dir.virtual_address - sect.virtual_address));
1120 break;
1121 }
1122 } else return error.InvalidDebugDirectory;
1123 }
11101124
11111125 // Find the correct DebugDirectoryEntry, and where its data is stored.
11121126 // It can be in any section.
......@@ -1115,7 +1129,8 @@ pub const Coff = struct {
11151129 blk: while (i < debug_dir_entry_count) : (i += 1) {
11161130 const debug_dir_entry = try reader.readStruct(DebugDirectoryEntry);
11171131 if (debug_dir_entry.type == .CODEVIEW) {
1118 try stream.seekTo(debug_dir_entry.address_of_raw_data);
1132 const dir_offset = if (self.is_loaded) debug_dir_entry.address_of_raw_data else debug_dir_entry.pointer_to_raw_data;
1133 try stream.seekTo(dir_offset);
11191134 break :blk;
11201135 }
11211136 }
......@@ -1256,7 +1271,8 @@ pub const Coff = struct {
12561271 }
12571272
12581273 pub fn getSectionData(self: *const Coff, sec: *align(1) const SectionHeader) []const u8 {
1259 return self.data[sec.pointer_to_raw_data..][0..sec.virtual_size];
1274 const offset = if (self.is_loaded) sec.virtual_address else sec.pointer_to_raw_data;
1275 return self.data[offset..][0..sec.virtual_size];
12601276 }
12611277
12621278 pub fn getSectionDataAlloc(self: *const Coff, sec: *align(1) const SectionHeader, allocator: mem.Allocator) ![]u8 {
lib/std/debug.zig+46-35
......@@ -997,7 +997,6 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu
997997 .base_address = undefined,
998998 .coff_image_base = coff_obj.getImageBase(),
999999 .coff_section_headers = undefined,
1000 .debug_data = undefined,
10011000 };
10021001
10031002 if (coff_obj.getSectionByName(".debug_info")) |_| {
......@@ -1022,11 +1021,10 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu
10221021 };
10231022
10241023 try DW.openDwarfDebugInfo(&dwarf, allocator);
1025 di.debug_data = PdbOrDwarf{ .dwarf = dwarf };
1026 return di;
1024 di.dwarf = dwarf;
10271025 }
10281026
1029 // Only used by pdb path
1027 // Only used by the pdb path
10301028 di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(allocator);
10311029 errdefer allocator.free(di.coff_section_headers);
10321030
......@@ -1037,15 +1035,19 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu
10371035 const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path});
10381036 defer allocator.free(path);
10391037
1040 di.debug_data = PdbOrDwarf{ .pdb = undefined };
1041 di.debug_data.pdb = pdb.Pdb.init(allocator, path) catch |err| switch (err) {
1042 error.FileNotFound, error.IsDir => return error.MissingDebugInfo,
1038 di.pdb = pdb.Pdb.init(allocator, path) catch |err| switch (err) {
1039 error.FileNotFound, error.IsDir => {
1040 if (di.dwarf == null) return error.MissingDebugInfo;
1041 allocator.free(di.coff_section_headers);
1042 di.coff_section_headers = undefined;
1043 return di;
1044 },
10431045 else => return err,
10441046 };
1045 try di.debug_data.pdb.parseInfoStream();
1046 try di.debug_data.pdb.parseDbiStream();
1047 try di.pdb.?.parseInfoStream();
1048 try di.pdb.?.parseDbiStream();
10471049
1048 if (!mem.eql(u8, &coff_obj.guid, &di.debug_data.pdb.guid) or coff_obj.age != di.debug_data.pdb.age)
1050 if (!mem.eql(u8, &coff_obj.guid, &di.pdb.?.guid) or coff_obj.age != di.pdb.?.age)
10491051 return error.InvalidDebugInfo;
10501052
10511053 return di;
......@@ -1695,7 +1697,7 @@ pub const DebugInfo = struct {
16951697 errdefer self.allocator.destroy(obj_di);
16961698
16971699 const mapped_module = @as([*]const u8, @ptrFromInt(module.base_address))[0..module.size];
1698 var coff_obj = try coff.Coff.init(mapped_module);
1700 var coff_obj = try coff.Coff.init(mapped_module, true);
16991701
17001702 // The string table is not mapped into memory by the loader, so if a section name is in the
17011703 // 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 {
17531755 errdefer assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @ptrFromInt(base_ptr)) == .SUCCESS);
17541756
17551757 const section_view = @as([*]const u8, @ptrFromInt(base_ptr))[0..coff_len];
1756 coff_obj = try coff.Coff.init(section_view);
1758 coff_obj = try coff.Coff.init(section_view, false);
17571759
17581760 module.mapped_file = .{
17591761 .file = coff_file,
......@@ -2141,34 +2143,27 @@ pub const ModuleDebugInfo = switch (native_os) {
21412143 },
21422144 .uefi, .windows => struct {
21432145 base_address: usize,
2144 debug_data: PdbOrDwarf,
2146 pdb: ?pdb.Pdb = null,
2147 dwarf: ?DW.DwarfInfo = null,
21452148 coff_image_base: u64,
2146 /// Only used if debug_data is .pdb
2149
2150 /// Only used if pdb is non-null
21472151 coff_section_headers: []coff.SectionHeader,
21482152
21492153 pub fn deinit(self: *@This(), allocator: mem.Allocator) void {
2150 self.debug_data.deinit(allocator);
2151 if (self.debug_data == .pdb) {
2152 allocator.free(self.coff_section_headers);
2154 if (self.dwarf) |*dwarf| {
2155 dwarf.deinit(allocator);
21532156 }
2154 }
2155
2156 pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo {
2157 // Translate the VA into an address into this object
2158 const relocated_address = address - self.base_address;
21592157
2160 switch (self.debug_data) {
2161 .dwarf => |*dwarf| {
2162 const dwarf_address = relocated_address + self.coff_image_base;
2163 return getSymbolFromDwarf(allocator, dwarf_address, dwarf);
2164 },
2165 .pdb => {
2166 // fallthrough to pdb handling
2167 },
2158 if (self.pdb) |*p| {
2159 p.deinit();
2160 allocator.free(self.coff_section_headers);
21682161 }
2162 }
21692163
2164 fn getSymbolFromPdb(self: *@This(), relocated_address: usize) !?SymbolInfo {
21702165 var coff_section: *align(1) const coff.SectionHeader = undefined;
2171 const mod_index = for (self.debug_data.pdb.sect_contribs) |sect_contrib| {
2166 const mod_index = for (self.pdb.?.sect_contribs) |sect_contrib| {
21722167 if (sect_contrib.Section > self.coff_section_headers.len) continue;
21732168 // Remember that SectionContribEntry.Section is 1-based.
21742169 coff_section = &self.coff_section_headers[sect_contrib.Section - 1];
......@@ -2180,18 +2175,18 @@ pub const ModuleDebugInfo = switch (native_os) {
21802175 }
21812176 } else {
21822177 // we have no information to add to the address
2183 return SymbolInfo{};
2178 return null;
21842179 };
21852180
2186 const module = (try self.debug_data.pdb.getModule(mod_index)) orelse
2181 const module = (try self.pdb.?.getModule(mod_index)) orelse
21872182 return error.InvalidDebugInfo;
21882183 const obj_basename = fs.path.basename(module.obj_file_name);
21892184
2190 const symbol_name = self.debug_data.pdb.getSymbolName(
2185 const symbol_name = self.pdb.?.getSymbolName(
21912186 module,
21922187 relocated_address - coff_section.virtual_address,
21932188 ) orelse "???";
2194 const opt_line_info = try self.debug_data.pdb.getLineNumberInfo(
2189 const opt_line_info = try self.pdb.?.getLineNumberInfo(
21952190 module,
21962191 relocated_address - coff_section.virtual_address,
21972192 );
......@@ -2203,6 +2198,22 @@ pub const ModuleDebugInfo = switch (native_os) {
22032198 };
22042199 }
22052200
2201 pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo {
2202 // Translate the VA into an address into this object
2203 const relocated_address = address - self.base_address;
2204
2205 if (self.pdb != null) {
2206 if (try self.getSymbolFromPdb(relocated_address)) |symbol| return symbol;
2207 }
2208
2209 if (self.dwarf) |*dwarf| {
2210 const dwarf_address = relocated_address + self.coff_image_base;
2211 return getSymbolFromDwarf(allocator, dwarf_address, dwarf);
2212 }
2213
2214 return SymbolInfo{};
2215 }
2216
22062217 pub fn getDwarfInfoForAddress(self: *@This(), allocator: mem.Allocator, address: usize) !?*const DW.DwarfInfo {
22072218 _ = allocator;
22082219 _ = address;