authorgravatar for matt.chudleigh@gmail.comMatt Chudleigh <matt.chudleigh@gmail.com> 2021-06-18 11:40:40-07:00
committergravatar for matt.chudleigh@gmail.comMatt Chudleigh <matt.chudleigh@gmail.com> 2021-06-18 16:19:20-07:00
loga6c2e44ae7ab3009da8e079148cba7182d627fc4
tree0bb532c6c2e07c9da375fb5b2969f0e4dcb4b27b
parent2dcdaa76688cfb287432905fce6a097a53c6704d

Add support for reading DWARF debug information from COFF files


2 files changed, 101 insertions(+), 38 deletions(-)

lib/std/coff.zig+32-7
...@@ -184,18 +184,26 @@ pub const Coff = struct {...@@ -184,18 +184,26 @@ pub const Coff = struct {
184184
185 fn loadOptionalHeader(self: *Coff) !void {185 fn loadOptionalHeader(self: *Coff) !void {
186 const in = self.in_file.reader();186 const in = self.in_file.reader();
187 const opt_header_pos = try self.in_file.getPos();
188
187 self.pe_header.magic = try in.readIntLittle(u16);189 self.pe_header.magic = try in.readIntLittle(u16);
188 // For now we're only interested in finding the reference to the .pdb,190 // All we care about is the image base value and PDB info
189 // so we'll skip most of this header, which size is different in 32191 // The header structure is different for 32 or 64 bit
190 // 64 bits by the way.192 var num_rva_pos: u64 = undefined;
191 var skip_size: u16 = undefined;
192 if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {193 if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
193 skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 18 * @sizeOf(u32);194 num_rva_pos = opt_header_pos + 92;
195
196 try self.in_file.seekTo(opt_header_pos + 28);
197 const image_base32 = try in.readIntLittle(u32);
198 self.pe_header.image_base = image_base32;
194 } else if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {199 } else if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
195 skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 12 * @sizeOf(u32) + 5 * @sizeOf(u64);200 num_rva_pos = opt_header_pos + 108;
201
202 try self.in_file.seekTo(opt_header_pos + 24);
203 self.pe_header.image_base = try in.readIntLittle(u64);
196 } else return error.InvalidPEMagic;204 } else return error.InvalidPEMagic;
197205
198 try self.in_file.seekBy(skip_size);206 try self.in_file.seekTo(num_rva_pos);
199207
200 const number_of_rva_and_sizes = try in.readIntLittle(u32);208 const number_of_rva_and_sizes = try in.readIntLittle(u32);
201 if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)209 if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
...@@ -320,6 +328,22 @@ pub const Coff = struct {...@@ -320,6 +328,22 @@ pub const Coff = struct {
320 }328 }
321 return null;329 return null;
322 }330 }
331
332 // Return an owned slice full of the section data
333 pub fn getSectionData(self: *Coff, comptime name: []const u8, allocator: *mem.Allocator) ![]u8 {
334 const sec = for (self.sections.items) |*sec| {
335 if (mem.eql(u8, sec.header.name[0..name.len], name)) {
336 break sec;
337 }
338 } else {
339 return error.MissingCoffSection;
340 };
341 const in = self.in_file.reader();
342 try self.in_file.seekTo(sec.header.pointer_to_raw_data);
343 const out_buff = try allocator.alloc(u8, sec.header.misc.virtual_size);
344 try in.readNoEof(out_buff);
345 return out_buff;
346 }
323};347};
324348
325const CoffHeader = struct {349const CoffHeader = struct {
...@@ -340,6 +364,7 @@ const OptionalHeader = struct {...@@ -340,6 +364,7 @@ const OptionalHeader = struct {
340364
341 magic: u16,365 magic: u16,
342 data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory,366 data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory,
367 image_base: u64,
343};368};
344369
345const DebugDirectoryEntry = packed struct {370const DebugDirectoryEntry = packed struct {
lib/std/debug.zig+69-31
...@@ -53,6 +53,10 @@ pub const SymbolInfo = struct {...@@ -53,6 +53,10 @@ pub const SymbolInfo = struct {
53 }53 }
54 }54 }
55};55};
56const PdbOrDwarf = union(enum) {
57 pdb: pdb.Pdb,
58 dwarf: DW.DwarfInfo,
59};
5660
57var stderr_mutex = std.Thread.Mutex{};61var stderr_mutex = std.Thread.Mutex{};
5862
...@@ -669,10 +673,32 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf...@@ -669,10 +673,32 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
669 var di = ModuleDebugInfo{673 var di = ModuleDebugInfo{
670 .base_address = undefined,674 .base_address = undefined,
671 .coff = coff_obj,675 .coff = coff_obj,
672 .pdb = undefined,676 .debug_data = undefined,
673 };677 };
674678
675 try di.coff.loadHeader();679 try di.coff.loadHeader();
680 try di.coff.loadSections();
681 if (di.coff.getSection(".debug_info")) |sec| {
682 // This coff file has embedded DWARF debug info
683 // TODO: free the section data slices
684 const debug_info_data = di.coff.getSectionData(".debug_info", allocator) catch null;
685 const debug_abbrev_data = di.coff.getSectionData(".debug_abbrev", allocator) catch null;
686 const debug_str_data = di.coff.getSectionData(".debug_str", allocator) catch null;
687 const debug_line_data = di.coff.getSectionData(".debug_line", allocator) catch null;
688 const debug_ranges_data = di.coff.getSectionData(".debug_ranges", allocator) catch null;
689
690 var dwarf = DW.DwarfInfo{
691 .endian = native_endian,
692 .debug_info = debug_info_data orelse return error.MissingDebugInfo,
693 .debug_abbrev = debug_abbrev_data orelse return error.MissingDebugInfo,
694 .debug_str = debug_str_data orelse return error.MissingDebugInfo,
695 .debug_line = debug_line_data orelse return error.MissingDebugInfo,
696 .debug_ranges = debug_ranges_data,
697 };
698 try DW.openDwarfDebugInfo(&dwarf, allocator);
699 di.debug_data = PdbOrDwarf{ .dwarf = dwarf };
700 return di;
701 }
676702
677 var path_buf: [windows.MAX_PATH]u8 = undefined;703 var path_buf: [windows.MAX_PATH]u8 = undefined;
678 const len = try di.coff.getPdbPath(path_buf[0..]);704 const len = try di.coff.getPdbPath(path_buf[0..]);
...@@ -681,11 +707,12 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf...@@ -681,11 +707,12 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
681 const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path});707 const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path});
682 defer allocator.free(path);708 defer allocator.free(path);
683709
684 di.pdb = try pdb.Pdb.init(allocator, path);710 di.debug_data = PdbOrDwarf{ .pdb = undefined };
685 try di.pdb.parseInfoStream();711 di.debug_data.pdb = try pdb.Pdb.init(allocator, path);
686 try di.pdb.parseDbiStream();712 try di.debug_data.pdb.parseInfoStream();
713 try di.debug_data.pdb.parseDbiStream();
687714
688 if (!mem.eql(u8, &di.coff.guid, &di.pdb.guid) or di.coff.age != di.pdb.age)715 if (!mem.eql(u8, &di.coff.guid, &di.debug_data.pdb.guid) or di.coff.age != di.debug_data.pdb.age)
689 return error.InvalidDebugInfo;716 return error.InvalidDebugInfo;
690717
691 return di;718 return di;
...@@ -1331,7 +1358,7 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1331,7 +1358,7 @@ pub const ModuleDebugInfo = switch (native_os) {
1331 },1358 },
1332 .uefi, .windows => struct {1359 .uefi, .windows => struct {
1333 base_address: usize,1360 base_address: usize,
1334 pdb: pdb.Pdb,1361 debug_data: PdbOrDwarf,
1335 coff: *coff.Coff,1362 coff: *coff.Coff,
13361363
1337 pub fn allocator(self: @This()) *mem.Allocator {1364 pub fn allocator(self: @This()) *mem.Allocator {
...@@ -1342,8 +1369,18 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1342,8 +1369,18 @@ pub const ModuleDebugInfo = switch (native_os) {
1342 // Translate the VA into an address into this object1369 // Translate the VA into an address into this object
1343 const relocated_address = address - self.base_address;1370 const relocated_address = address - self.base_address;
13441371
1372 switch (self.debug_data) {
1373 .dwarf => |*dwarf| {
1374 const dwarf_address = relocated_address + self.coff.pe_header.image_base;
1375 return getSymbolFromDwarf(dwarf_address, dwarf);
1376 },
1377 .pdb => {
1378 // fallthrough to pdb handling
1379 },
1380 }
1381
1345 var coff_section: *coff.Section = undefined;1382 var coff_section: *coff.Section = undefined;
1346 const mod_index = for (self.pdb.sect_contribs) |sect_contrib| {1383 const mod_index = for (self.debug_data.pdb.sect_contribs) |sect_contrib| {
1347 if (sect_contrib.Section > self.coff.sections.items.len) continue;1384 if (sect_contrib.Section > self.coff.sections.items.len) continue;
1348 // Remember that SectionContribEntry.Section is 1-based.1385 // Remember that SectionContribEntry.Section is 1-based.
1349 coff_section = &self.coff.sections.items[sect_contrib.Section - 1];1386 coff_section = &self.coff.sections.items[sect_contrib.Section - 1];
...@@ -1358,15 +1395,15 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1358,15 +1395,15 @@ pub const ModuleDebugInfo = switch (native_os) {
1358 return SymbolInfo{};1395 return SymbolInfo{};
1359 };1396 };
13601397
1361 const module = (try self.pdb.getModule(mod_index)) orelse1398 const module = (try self.debug_data.pdb.getModule(mod_index)) orelse
1362 return error.InvalidDebugInfo;1399 return error.InvalidDebugInfo;
1363 const obj_basename = fs.path.basename(module.obj_file_name);1400 const obj_basename = fs.path.basename(module.obj_file_name);
13641401
1365 const symbol_name = self.pdb.getSymbolName(1402 const symbol_name = self.debug_data.pdb.getSymbolName(
1366 module,1403 module,
1367 relocated_address - coff_section.header.virtual_address,1404 relocated_address - coff_section.header.virtual_address,
1368 ) orelse "???";1405 ) orelse "???";
1369 const opt_line_info = try self.pdb.getLineNumberInfo(1406 const opt_line_info = try self.debug_data.pdb.getLineNumberInfo(
1370 module,1407 module,
1371 relocated_address - coff_section.header.virtual_address,1408 relocated_address - coff_section.header.virtual_address,
1372 );1409 );
...@@ -1386,32 +1423,33 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1386,32 +1423,33 @@ pub const ModuleDebugInfo = switch (native_os) {
1386 pub fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {1423 pub fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {
1387 // Translate the VA into an address into this object1424 // Translate the VA into an address into this object
1388 const relocated_address = address - self.base_address;1425 const relocated_address = address - self.base_address;
13891426 return getSymbolFromDwarf(relocated_address, &self.dwarf);
1390 if (nosuspend self.dwarf.findCompileUnit(relocated_address)) |compile_unit| {
1391 return SymbolInfo{
1392 .symbol_name = nosuspend self.dwarf.getSymbolName(relocated_address) orelse "???",
1393 .compile_unit_name = compile_unit.die.getAttrString(&self.dwarf, DW.AT_name) catch |err| switch (err) {
1394 error.MissingDebugInfo, error.InvalidDebugInfo => "???",
1395 else => return err,
1396 },
1397 .line_info = nosuspend self.dwarf.getLineNumberInfo(compile_unit.*, relocated_address) catch |err| switch (err) {
1398 error.MissingDebugInfo, error.InvalidDebugInfo => null,
1399 else => return err,
1400 },
1401 };
1402 } else |err| switch (err) {
1403 error.MissingDebugInfo, error.InvalidDebugInfo => {
1404 return SymbolInfo{};
1405 },
1406 else => return err,
1407 }
1408
1409 unreachable;
1410 }1427 }
1411 },1428 },
1412 else => DW.DwarfInfo,1429 else => DW.DwarfInfo,
1413};1430};
14141431
1432fn getSymbolFromDwarf(address: u64, di: *DW.DwarfInfo) !SymbolInfo {
1433 if (nosuspend di.findCompileUnit(address)) |compile_unit| {
1434 return SymbolInfo{
1435 .symbol_name = nosuspend di.getSymbolName(address) orelse "???",
1436 .compile_unit_name = compile_unit.die.getAttrString(di, DW.AT_name) catch |err| switch (err) {
1437 error.MissingDebugInfo, error.InvalidDebugInfo => "???",
1438 else => return err,
1439 },
1440 .line_info = nosuspend di.getLineNumberInfo(compile_unit.*, address) catch |err| switch (err) {
1441 error.MissingDebugInfo, error.InvalidDebugInfo => null,
1442 else => return err,
1443 },
1444 };
1445 } else |err| switch (err) {
1446 error.MissingDebugInfo, error.InvalidDebugInfo => {
1447 return SymbolInfo{};
1448 },
1449 else => return err,
1450 }
1451}
1452
1415/// TODO multithreaded awareness1453/// TODO multithreaded awareness
1416var debug_info_allocator: ?*mem.Allocator = null;1454var debug_info_allocator: ?*mem.Allocator = null;
1417var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;1455var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;