Commit 5b86180ae3

kcbanner <kcbanner@gmail.com>
2023-08-15 08:22:22
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
1 parent a155e35
Changed files (2)
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,
         };
 
@@ -1106,7 +1109,18 @@ pub const Coff = struct {
 
         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.
@@ -1115,7 +1129,8 @@ pub const Coff = struct {
         blk: 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);
+                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 :blk;
             }
         }
@@ -1256,7 +1271,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 {
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,11 +1021,10 @@ 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
+        // Only used by the pdb path
         di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(allocator);
         errdefer allocator.free(di.coff_section_headers);
 
@@ -1037,15 +1035,19 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebu
         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;
+                allocator.free(di.coff_section_headers);
+                di.coff_section_headers = undefined;
+                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;
 
         return di;
@@ -1695,7 +1697,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 +1755,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 +2143,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 +2175,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 +2198,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;