authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-08-16 12:31:22+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-08-16 12:31:22+02:00
log3e228bdf457d8be0fcbb8e44a8fa7a0b09677972
tree197f5daa9b9cafadc5b4a047a29f23fea8e1fc6a
parent340a45683ca8e0b23e95f5fb86bd9c827970e6e8
parent8a5f331ec832201c6e4bdf211cdd37ca5eb4347b
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #16832 from kcbanner/coff_dwarf_and_pdb

Windows: Support loading debug symbols from both the PDB file and embedded DWARF information

2 files changed, 74 insertions(+), 48 deletions(-)

lib/std/coff.zig+26-9
...@@ -1059,6 +1059,8 @@ pub const CoffError = error{...@@ -1059,6 +1059,8 @@ pub const CoffError = error{
1059// Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format1059// Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
1060pub const Coff = struct {1060pub const Coff = struct {
1061 data: []const u8,1061 data: []const u8,
1062 // Set if `data` is backed by the image as loaded by the loader
1063 is_loaded: bool,
1062 is_image: bool,1064 is_image: bool,
1063 coff_header_offset: usize,1065 coff_header_offset: usize,
10641066
...@@ -1066,7 +1068,7 @@ pub const Coff = struct {...@@ -1066,7 +1068,7 @@ pub const Coff = struct {
1066 age: u32 = undefined,1068 age: u32 = undefined,
10671069
1068 // The lifetime of `data` must be longer than the lifetime of the returned Coff1070 // 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 {
1070 const pe_pointer_offset = 0x3C;1072 const pe_pointer_offset = 0x3C;
1071 const pe_magic = "PE\x00\x00";1073 const pe_magic = "PE\x00\x00";
10721074
...@@ -1082,6 +1084,7 @@ pub const Coff = struct {...@@ -1082,6 +1084,7 @@ pub const Coff = struct {
1082 var coff = @This(){1084 var coff = @This(){
1083 .data = data,1085 .data = data,
1084 .is_image = is_image,1086 .is_image = is_image,
1087 .is_loaded = is_loaded,
1085 .coff_header_offset = coff_header_offset,1088 .coff_header_offset = coff_header_offset,
1086 };1089 };
10871090
...@@ -1098,27 +1101,40 @@ pub const Coff = struct {...@@ -1098,27 +1101,40 @@ pub const Coff = struct {
1098 return coff;1101 return coff;
1099 }1102 }
11001103
1101 pub fn getPdbPath(self: *Coff, buffer: []u8) !usize {1104 pub fn getPdbPath(self: *Coff, buffer: []u8) !?usize {
1102 assert(self.is_image);1105 assert(self.is_image);
11031106
1104 const data_dirs = self.getDataDirectories();1107 const data_dirs = self.getDataDirectories();
1105 const debug_dir = data_dirs[@intFromEnum(DirectoryEntry.DEBUG)];1108 if (@intFromEnum(DirectoryEntry.DEBUG) >= data_dirs.len) return null;
11061109
1110 const debug_dir = data_dirs[@intFromEnum(DirectoryEntry.DEBUG)];
1107 var stream = std.io.fixedBufferStream(self.data);1111 var stream = std.io.fixedBufferStream(self.data);
1108 const reader = stream.reader();1112 const reader = stream.reader();
1109 try stream.seekTo(debug_dir.virtual_address);1113
1114 if (self.is_loaded) {
1115 try stream.seekTo(debug_dir.virtual_address);
1116 } else {
1117 // Find what section the debug_dir is in, in order to convert the RVA to a file offset
1118 for (self.getSectionHeaders()) |*sect| {
1119 if (debug_dir.virtual_address >= sect.virtual_address and debug_dir.virtual_address < sect.virtual_address + sect.virtual_size) {
1120 try stream.seekTo(sect.pointer_to_raw_data + (debug_dir.virtual_address - sect.virtual_address));
1121 break;
1122 }
1123 } else return error.InvalidDebugDirectory;
1124 }
11101125
1111 // Find the correct DebugDirectoryEntry, and where its data is stored.1126 // Find the correct DebugDirectoryEntry, and where its data is stored.
1112 // It can be in any section.1127 // It can be in any section.
1113 const debug_dir_entry_count = debug_dir.size / @sizeOf(DebugDirectoryEntry);1128 const debug_dir_entry_count = debug_dir.size / @sizeOf(DebugDirectoryEntry);
1114 var i: u32 = 0;1129 var i: u32 = 0;
1115 blk: while (i < debug_dir_entry_count) : (i += 1) {1130 while (i < debug_dir_entry_count) : (i += 1) {
1116 const debug_dir_entry = try reader.readStruct(DebugDirectoryEntry);1131 const debug_dir_entry = try reader.readStruct(DebugDirectoryEntry);
1117 if (debug_dir_entry.type == .CODEVIEW) {1132 if (debug_dir_entry.type == .CODEVIEW) {
1118 try stream.seekTo(debug_dir_entry.address_of_raw_data);1133 const dir_offset = if (self.is_loaded) debug_dir_entry.address_of_raw_data else debug_dir_entry.pointer_to_raw_data;
1119 break :blk;1134 try stream.seekTo(dir_offset);
1135 break;
1120 }1136 }
1121 }1137 } else return null;
11221138
1123 var cv_signature: [4]u8 = undefined; // CodeView signature1139 var cv_signature: [4]u8 = undefined; // CodeView signature
1124 try reader.readNoEof(cv_signature[0..]);1140 try reader.readNoEof(cv_signature[0..]);
...@@ -1256,7 +1272,8 @@ pub const Coff = struct {...@@ -1256,7 +1272,8 @@ pub const Coff = struct {
1256 }1272 }
12571273
1258 pub fn getSectionData(self: *const Coff, sec: *align(1) const SectionHeader) []const u8 {1274 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];1275 const offset = if (self.is_loaded) sec.virtual_address else sec.pointer_to_raw_data;
1276 return self.data[offset..][0..sec.virtual_size];
1260 }1277 }
12611278
1262 pub fn getSectionDataAlloc(self: *const Coff, sec: *align(1) const SectionHeader, allocator: mem.Allocator) ![]u8 {1279 pub fn getSectionDataAlloc(self: *const Coff, sec: *align(1) const SectionHeader, allocator: mem.Allocator) ![]u8 {
lib/std/debug.zig+48-39
...@@ -997,7 +997,6 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu...@@ -997,7 +997,6 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu
997 .base_address = undefined,997 .base_address = undefined,
998 .coff_image_base = coff_obj.getImageBase(),998 .coff_image_base = coff_obj.getImageBase(),
999 .coff_section_headers = undefined,999 .coff_section_headers = undefined,
1000 .debug_data = undefined,
1001 };1000 };
10021001
1003 if (coff_obj.getSectionByName(".debug_info")) |_| {1002 if (coff_obj.getSectionByName(".debug_info")) |_| {
...@@ -1022,32 +1021,33 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu...@@ -1022,32 +1021,33 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu
1022 };1021 };
10231022
1024 try DW.openDwarfDebugInfo(&dwarf, allocator);1023 try DW.openDwarfDebugInfo(&dwarf, allocator);
1025 di.debug_data = PdbOrDwarf{ .dwarf = dwarf };1024 di.dwarf = dwarf;
1026 return di;
1027 }1025 }
10281026
1029 // Only used by pdb path
1030 di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(allocator);
1031 errdefer allocator.free(di.coff_section_headers);
1032
1033 var path_buf: [windows.MAX_PATH]u8 = undefined;1027 var path_buf: [windows.MAX_PATH]u8 = undefined;
1034 const len = try coff_obj.getPdbPath(path_buf[0..]);1028 const len = try coff_obj.getPdbPath(path_buf[0..]) orelse return di;
1035 const raw_path = path_buf[0..len];1029 const raw_path = path_buf[0..len];
10361030
1037 const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path});1031 const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path});
1038 defer allocator.free(path);1032 defer allocator.free(path);
10391033
1040 di.debug_data = PdbOrDwarf{ .pdb = undefined };1034 di.pdb = pdb.Pdb.init(allocator, path) catch |err| switch (err) {
1041 di.debug_data.pdb = pdb.Pdb.init(allocator, path) catch |err| switch (err) {1035 error.FileNotFound, error.IsDir => {
1042 error.FileNotFound, error.IsDir => return error.MissingDebugInfo,1036 if (di.dwarf == null) return error.MissingDebugInfo;
1037 return di;
1038 },
1043 else => return err,1039 else => return err,
1044 };1040 };
1045 try di.debug_data.pdb.parseInfoStream();1041 try di.pdb.?.parseInfoStream();
1046 try di.debug_data.pdb.parseDbiStream();1042 try di.pdb.?.parseDbiStream();
10471043
1048 if (!mem.eql(u8, &coff_obj.guid, &di.debug_data.pdb.guid) or coff_obj.age != di.debug_data.pdb.age)1044 if (!mem.eql(u8, &coff_obj.guid, &di.pdb.?.guid) or coff_obj.age != di.pdb.?.age)
1049 return error.InvalidDebugInfo;1045 return error.InvalidDebugInfo;
10501046
1047 // Only used by the pdb path
1048 di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(allocator);
1049 errdefer allocator.free(di.coff_section_headers);
1050
1051 return di;1051 return di;
1052 }1052 }
1053}1053}
...@@ -1695,7 +1695,7 @@ pub const DebugInfo = struct {...@@ -1695,7 +1695,7 @@ pub const DebugInfo = struct {
1695 errdefer self.allocator.destroy(obj_di);1695 errdefer self.allocator.destroy(obj_di);
16961696
1697 const mapped_module = @as([*]const u8, @ptrFromInt(module.base_address))[0..module.size];1697 const mapped_module = @as([*]const u8, @ptrFromInt(module.base_address))[0..module.size];
1698 var coff_obj = try coff.Coff.init(mapped_module);1698 var coff_obj = try coff.Coff.init(mapped_module, true);
16991699
1700 // The string table is not mapped into memory by the loader, so if a section name is in the1700 // The string table is not mapped into memory by the loader, so if a section name is in the
1701 // string table then we have to map the full image file from disk. This can happen when1701 // 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 {...@@ -1753,7 +1753,7 @@ pub const DebugInfo = struct {
1753 errdefer assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @ptrFromInt(base_ptr)) == .SUCCESS);1753 errdefer assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @ptrFromInt(base_ptr)) == .SUCCESS);
17541754
1755 const section_view = @as([*]const u8, @ptrFromInt(base_ptr))[0..coff_len];1755 const section_view = @as([*]const u8, @ptrFromInt(base_ptr))[0..coff_len];
1756 coff_obj = try coff.Coff.init(section_view);1756 coff_obj = try coff.Coff.init(section_view, false);
17571757
1758 module.mapped_file = .{1758 module.mapped_file = .{
1759 .file = coff_file,1759 .file = coff_file,
...@@ -2141,34 +2141,27 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -2141,34 +2141,27 @@ pub const ModuleDebugInfo = switch (native_os) {
2141 },2141 },
2142 .uefi, .windows => struct {2142 .uefi, .windows => struct {
2143 base_address: usize,2143 base_address: usize,
2144 debug_data: PdbOrDwarf,2144 pdb: ?pdb.Pdb = null,
2145 dwarf: ?DW.DwarfInfo = null,
2145 coff_image_base: u64,2146 coff_image_base: u64,
2146 /// Only used if debug_data is .pdb2147
2148 /// Only used if pdb is non-null
2147 coff_section_headers: []coff.SectionHeader,2149 coff_section_headers: []coff.SectionHeader,
21482150
2149 pub fn deinit(self: *@This(), allocator: mem.Allocator) void {2151 pub fn deinit(self: *@This(), allocator: mem.Allocator) void {
2150 self.debug_data.deinit(allocator);2152 if (self.dwarf) |*dwarf| {
2151 if (self.debug_data == .pdb) {2153 dwarf.deinit(allocator);
2152 allocator.free(self.coff_section_headers);
2153 }2154 }
2154 }
21552155
2156 pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo {2156 if (self.pdb) |*p| {
2157 // Translate the VA into an address into this object2157 p.deinit();
2158 const relocated_address = address - self.base_address;2158 allocator.free(self.coff_section_headers);
2159
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 },
2168 }2159 }
2160 }
21692161
2162 fn getSymbolFromPdb(self: *@This(), relocated_address: usize) !?SymbolInfo {
2170 var coff_section: *align(1) const coff.SectionHeader = undefined;2163 var coff_section: *align(1) const coff.SectionHeader = undefined;
2171 const mod_index = for (self.debug_data.pdb.sect_contribs) |sect_contrib| {2164 const mod_index = for (self.pdb.?.sect_contribs) |sect_contrib| {
2172 if (sect_contrib.Section > self.coff_section_headers.len) continue;2165 if (sect_contrib.Section > self.coff_section_headers.len) continue;
2173 // Remember that SectionContribEntry.Section is 1-based.2166 // Remember that SectionContribEntry.Section is 1-based.
2174 coff_section = &self.coff_section_headers[sect_contrib.Section - 1];2167 coff_section = &self.coff_section_headers[sect_contrib.Section - 1];
...@@ -2180,18 +2173,18 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -2180,18 +2173,18 @@ pub const ModuleDebugInfo = switch (native_os) {
2180 }2173 }
2181 } else {2174 } else {
2182 // we have no information to add to the address2175 // we have no information to add to the address
2183 return SymbolInfo{};2176 return null;
2184 };2177 };
21852178
2186 const module = (try self.debug_data.pdb.getModule(mod_index)) orelse2179 const module = (try self.pdb.?.getModule(mod_index)) orelse
2187 return error.InvalidDebugInfo;2180 return error.InvalidDebugInfo;
2188 const obj_basename = fs.path.basename(module.obj_file_name);2181 const obj_basename = fs.path.basename(module.obj_file_name);
21892182
2190 const symbol_name = self.debug_data.pdb.getSymbolName(2183 const symbol_name = self.pdb.?.getSymbolName(
2191 module,2184 module,
2192 relocated_address - coff_section.virtual_address,2185 relocated_address - coff_section.virtual_address,
2193 ) orelse "???";2186 ) orelse "???";
2194 const opt_line_info = try self.debug_data.pdb.getLineNumberInfo(2187 const opt_line_info = try self.pdb.?.getLineNumberInfo(
2195 module,2188 module,
2196 relocated_address - coff_section.virtual_address,2189 relocated_address - coff_section.virtual_address,
2197 );2190 );
...@@ -2203,6 +2196,22 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -2203,6 +2196,22 @@ pub const ModuleDebugInfo = switch (native_os) {
2203 };2196 };
2204 }2197 }
22052198
2199 pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo {
2200 // Translate the VA into an address into this object
2201 const relocated_address = address - self.base_address;
2202
2203 if (self.pdb != null) {
2204 if (try self.getSymbolFromPdb(relocated_address)) |symbol| return symbol;
2205 }
2206
2207 if (self.dwarf) |*dwarf| {
2208 const dwarf_address = relocated_address + self.coff_image_base;
2209 return getSymbolFromDwarf(allocator, dwarf_address, dwarf);
2210 }
2211
2212 return SymbolInfo{};
2213 }
2214
2206 pub fn getDwarfInfoForAddress(self: *@This(), allocator: mem.Allocator, address: usize) !?*const DW.DwarfInfo {2215 pub fn getDwarfInfoForAddress(self: *@This(), allocator: mem.Allocator, address: usize) !?*const DW.DwarfInfo {
2207 _ = allocator;2216 _ = allocator;
2208 _ = address;2217 _ = address;