Commit a6c2e44ae7

Matt Chudleigh <matt.chudleigh@gmail.com>
2021-06-18 20:40:40
Add support for reading DWARF debug information from COFF files
1 parent 2dcdaa7
Changed files (2)
lib/std/coff.zig
@@ -184,18 +184,26 @@ pub const Coff = struct {
 
     fn loadOptionalHeader(self: *Coff) !void {
         const in = self.in_file.reader();
+        const opt_header_pos = try self.in_file.getPos();
+
         self.pe_header.magic = try in.readIntLittle(u16);
-        // For now we're only interested in finding the reference to the .pdb,
-        // so we'll skip most of this header, which size is different in 32
-        // 64 bits by the way.
-        var skip_size: u16 = undefined;
+        // All we care about is the image base value and PDB info
+        // The header structure is different for 32 or 64 bit
+        var num_rva_pos: u64 = undefined;
         if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
-            skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 18 * @sizeOf(u32);
+            num_rva_pos = opt_header_pos + 92;
+
+            try self.in_file.seekTo(opt_header_pos + 28);
+            const image_base32 = try in.readIntLittle(u32);
+            self.pe_header.image_base = image_base32;
         } else if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
-            skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 12 * @sizeOf(u32) + 5 * @sizeOf(u64);
+            num_rva_pos = opt_header_pos + 108;
+
+            try self.in_file.seekTo(opt_header_pos + 24);
+            self.pe_header.image_base = try in.readIntLittle(u64);
         } else return error.InvalidPEMagic;
 
-        try self.in_file.seekBy(skip_size);
+        try self.in_file.seekTo(num_rva_pos);
 
         const number_of_rva_and_sizes = try in.readIntLittle(u32);
         if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
@@ -320,6 +328,22 @@ pub const Coff = struct {
         }
         return null;
     }
+
+    // Return an owned slice full of the section data
+    pub fn getSectionData(self: *Coff, comptime name: []const u8, allocator: *mem.Allocator) ![]u8 {
+        const sec = for (self.sections.items) |*sec| {
+            if (mem.eql(u8, sec.header.name[0..name.len], name)) {
+                break sec;
+            }
+        } else {
+            return error.MissingCoffSection;
+        };
+        const in = self.in_file.reader();
+        try self.in_file.seekTo(sec.header.pointer_to_raw_data);
+        const out_buff = try allocator.alloc(u8, sec.header.misc.virtual_size);
+        try in.readNoEof(out_buff);
+        return out_buff;
+    }
 };
 
 const CoffHeader = struct {
@@ -340,6 +364,7 @@ const OptionalHeader = struct {
 
     magic: u16,
     data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory,
+    image_base: u64,
 };
 
 const DebugDirectoryEntry = packed struct {
lib/std/debug.zig
@@ -53,6 +53,10 @@ pub const SymbolInfo = struct {
         }
     }
 };
+const PdbOrDwarf = union(enum) {
+    pdb: pdb.Pdb,
+    dwarf: DW.DwarfInfo,
+};
 
 var stderr_mutex = std.Thread.Mutex{};
 
@@ -669,10 +673,32 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
         var di = ModuleDebugInfo{
             .base_address = undefined,
             .coff = coff_obj,
-            .pdb = undefined,
+            .debug_data = undefined,
         };
 
         try di.coff.loadHeader();
+        try di.coff.loadSections();
+        if (di.coff.getSection(".debug_info")) |sec| {
+            // This coff file has embedded DWARF debug info
+            // TODO: free the section data slices
+            const debug_info_data = di.coff.getSectionData(".debug_info", allocator) catch null;
+            const debug_abbrev_data = di.coff.getSectionData(".debug_abbrev", allocator) catch null;
+            const debug_str_data = di.coff.getSectionData(".debug_str", allocator) catch null;
+            const debug_line_data = di.coff.getSectionData(".debug_line", allocator) catch null;
+            const debug_ranges_data = di.coff.getSectionData(".debug_ranges", allocator) catch null;
+
+            var dwarf = DW.DwarfInfo{
+                .endian = native_endian,
+                .debug_info = debug_info_data orelse return error.MissingDebugInfo,
+                .debug_abbrev = debug_abbrev_data orelse return error.MissingDebugInfo,
+                .debug_str = debug_str_data orelse return error.MissingDebugInfo,
+                .debug_line = debug_line_data orelse return error.MissingDebugInfo,
+                .debug_ranges = debug_ranges_data,
+            };
+            try DW.openDwarfDebugInfo(&dwarf, allocator);
+            di.debug_data = PdbOrDwarf{ .dwarf = dwarf };
+            return di;
+        }
 
         var path_buf: [windows.MAX_PATH]u8 = undefined;
         const len = try di.coff.getPdbPath(path_buf[0..]);
@@ -681,11 +707,12 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
         const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path});
         defer allocator.free(path);
 
-        di.pdb = try pdb.Pdb.init(allocator, path);
-        try di.pdb.parseInfoStream();
-        try di.pdb.parseDbiStream();
+        di.debug_data = PdbOrDwarf{ .pdb = undefined };
+        di.debug_data.pdb = try pdb.Pdb.init(allocator, path);
+        try di.debug_data.pdb.parseInfoStream();
+        try di.debug_data.pdb.parseDbiStream();
 
-        if (!mem.eql(u8, &di.coff.guid, &di.pdb.guid) or di.coff.age != di.pdb.age)
+        if (!mem.eql(u8, &di.coff.guid, &di.debug_data.pdb.guid) or di.coff.age != di.debug_data.pdb.age)
             return error.InvalidDebugInfo;
 
         return di;
@@ -1331,7 +1358,7 @@ pub const ModuleDebugInfo = switch (native_os) {
     },
     .uefi, .windows => struct {
         base_address: usize,
-        pdb: pdb.Pdb,
+        debug_data: PdbOrDwarf,
         coff: *coff.Coff,
 
         pub fn allocator(self: @This()) *mem.Allocator {
@@ -1342,8 +1369,18 @@ pub const ModuleDebugInfo = switch (native_os) {
             // 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.pe_header.image_base;
+                    return getSymbolFromDwarf(dwarf_address, dwarf);
+                },
+                .pdb => {
+                    // fallthrough to pdb handling
+                },
+            }
+
             var coff_section: *coff.Section = undefined;
-            const mod_index = for (self.pdb.sect_contribs) |sect_contrib| {
+            const mod_index = for (self.debug_data.pdb.sect_contribs) |sect_contrib| {
                 if (sect_contrib.Section > self.coff.sections.items.len) continue;
                 // Remember that SectionContribEntry.Section is 1-based.
                 coff_section = &self.coff.sections.items[sect_contrib.Section - 1];
@@ -1358,15 +1395,15 @@ pub const ModuleDebugInfo = switch (native_os) {
                 return SymbolInfo{};
             };
 
-            const module = (try self.pdb.getModule(mod_index)) orelse
+            const module = (try self.debug_data.pdb.getModule(mod_index)) orelse
                 return error.InvalidDebugInfo;
             const obj_basename = fs.path.basename(module.obj_file_name);
 
-            const symbol_name = self.pdb.getSymbolName(
+            const symbol_name = self.debug_data.pdb.getSymbolName(
                 module,
                 relocated_address - coff_section.header.virtual_address,
             ) orelse "???";
-            const opt_line_info = try self.pdb.getLineNumberInfo(
+            const opt_line_info = try self.debug_data.pdb.getLineNumberInfo(
                 module,
                 relocated_address - coff_section.header.virtual_address,
             );
@@ -1386,32 +1423,33 @@ pub const ModuleDebugInfo = switch (native_os) {
         pub fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {
             // Translate the VA into an address into this object
             const relocated_address = address - self.base_address;
-
-            if (nosuspend self.dwarf.findCompileUnit(relocated_address)) |compile_unit| {
-                return SymbolInfo{
-                    .symbol_name = nosuspend self.dwarf.getSymbolName(relocated_address) orelse "???",
-                    .compile_unit_name = compile_unit.die.getAttrString(&self.dwarf, DW.AT_name) catch |err| switch (err) {
-                        error.MissingDebugInfo, error.InvalidDebugInfo => "???",
-                        else => return err,
-                    },
-                    .line_info = nosuspend self.dwarf.getLineNumberInfo(compile_unit.*, relocated_address) catch |err| switch (err) {
-                        error.MissingDebugInfo, error.InvalidDebugInfo => null,
-                        else => return err,
-                    },
-                };
-            } else |err| switch (err) {
-                error.MissingDebugInfo, error.InvalidDebugInfo => {
-                    return SymbolInfo{};
-                },
-                else => return err,
-            }
-
-            unreachable;
+            return getSymbolFromDwarf(relocated_address, &self.dwarf);
         }
     },
     else => DW.DwarfInfo,
 };
 
+fn getSymbolFromDwarf(address: u64, di: *DW.DwarfInfo) !SymbolInfo {
+    if (nosuspend di.findCompileUnit(address)) |compile_unit| {
+        return SymbolInfo{
+            .symbol_name = nosuspend di.getSymbolName(address) orelse "???",
+            .compile_unit_name = compile_unit.die.getAttrString(di, DW.AT_name) catch |err| switch (err) {
+                error.MissingDebugInfo, error.InvalidDebugInfo => "???",
+                else => return err,
+            },
+            .line_info = nosuspend di.getLineNumberInfo(compile_unit.*, address) catch |err| switch (err) {
+                error.MissingDebugInfo, error.InvalidDebugInfo => null,
+                else => return err,
+            },
+        };
+    } else |err| switch (err) {
+        error.MissingDebugInfo, error.InvalidDebugInfo => {
+            return SymbolInfo{};
+        },
+        else => return err,
+    }
+}
+
 /// TODO multithreaded awareness
 var debug_info_allocator: ?*mem.Allocator = null;
 var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;