| ... | ... | @@ -811,7 +811,7 @@ fn printLineInfo( |
| 811 | 811 | pub const OpenSelfDebugInfoError = error{ |
| 812 | 812 | MissingDebugInfo, |
| 813 | 813 | UnsupportedOperatingSystem, |
| 814 | | }; |
| 814 | } || @typeInfo(@typeInfo(@TypeOf(DebugInfo.init)).Fn.return_type.?).ErrorUnion.error_set; |
| 815 | 815 | |
| 816 | 816 | pub fn openSelfDebugInfo(allocator: mem.Allocator) OpenSelfDebugInfoError!DebugInfo { |
| 817 | 817 | nosuspend { |
| ... | ... | @@ -827,60 +827,56 @@ pub fn openSelfDebugInfo(allocator: mem.Allocator) OpenSelfDebugInfoError!DebugI |
| 827 | 827 | .dragonfly, |
| 828 | 828 | .openbsd, |
| 829 | 829 | .macos, |
| 830 | | .windows, |
| 831 | 830 | .solaris, |
| 832 | | => return DebugInfo.init(allocator), |
| 831 | .windows, |
| 832 | => return try DebugInfo.init(allocator), |
| 833 | 833 | else => return error.UnsupportedOperatingSystem, |
| 834 | 834 | } |
| 835 | 835 | } |
| 836 | 836 | } |
| 837 | 837 | |
| 838 | | /// This takes ownership of coff_file: users of this function should not close |
| 839 | | /// it themselves, even on error. |
| 840 | | /// TODO it's weird to take ownership even on error, rework this code. |
| 841 | | fn readCoffDebugInfo(allocator: mem.Allocator, coff_file: File) !ModuleDebugInfo { |
| 838 | fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDebugInfo { |
| 842 | 839 | nosuspend { |
| 843 | | defer coff_file.close(); |
| 844 | | |
| 845 | 840 | const coff_obj = try allocator.create(coff.Coff); |
| 846 | 841 | errdefer allocator.destroy(coff_obj); |
| 847 | | coff_obj.* = .{ .allocator = allocator }; |
| 842 | coff_obj.* = try coff.Coff.init(coff_bytes); |
| 848 | 843 | |
| 849 | 844 | var di = ModuleDebugInfo{ |
| 850 | 845 | .base_address = undefined, |
| 851 | | .coff = coff_obj, |
| 846 | .coff_image_base = coff_obj.getImageBase(), |
| 847 | .coff_section_headers = undefined, |
| 852 | 848 | .debug_data = undefined, |
| 853 | 849 | }; |
| 854 | 850 | |
| 855 | | // TODO convert to Windows' memory-mapped file API |
| 856 | | const file_len = math.cast(usize, try coff_file.getEndPos()) orelse math.maxInt(usize); |
| 857 | | const data = try coff_file.readToEndAlloc(allocator, file_len); |
| 858 | | try di.coff.parse(data); |
| 859 | | |
| 860 | | if (di.coff.getSectionByName(".debug_info")) |sec| { |
| 851 | if (coff_obj.getSectionByName(".debug_info")) |sec| { |
| 861 | 852 | // This coff file has embedded DWARF debug info |
| 862 | 853 | _ = sec; |
| 863 | | // TODO: free the section data slices |
| 864 | | const debug_info = di.coff.getSectionDataAlloc(".debug_info", allocator) catch null; |
| 865 | | const debug_abbrev = di.coff.getSectionDataAlloc(".debug_abbrev", allocator) catch null; |
| 866 | | const debug_str = di.coff.getSectionDataAlloc(".debug_str", allocator) catch null; |
| 867 | | const debug_str_offsets = di.coff.getSectionDataAlloc(".debug_str_offsets", allocator) catch null; |
| 868 | | const debug_line = di.coff.getSectionDataAlloc(".debug_line", allocator) catch null; |
| 869 | | const debug_line_str = di.coff.getSectionDataAlloc(".debug_line_str", allocator) catch null; |
| 870 | | const debug_ranges = di.coff.getSectionDataAlloc(".debug_ranges", allocator) catch null; |
| 871 | | const debug_loclists = di.coff.getSectionDataAlloc(".debug_loclists", allocator) catch null; |
| 872 | | const debug_rnglists = di.coff.getSectionDataAlloc(".debug_rnglists", allocator) catch null; |
| 873 | | const debug_addr = di.coff.getSectionDataAlloc(".debug_addr", allocator) catch null; |
| 874 | | const debug_names = di.coff.getSectionDataAlloc(".debug_names", allocator) catch null; |
| 875 | | const debug_frame = di.coff.getSectionDataAlloc(".debug_frame", allocator) catch null; |
| 854 | |
| 855 | const debug_info = coff_obj.getSectionDataAlloc(".debug_info", allocator) catch return error.MissingDebugInfo; |
| 856 | errdefer allocator.free(debug_info); |
| 857 | const debug_abbrev = coff_obj.getSectionDataAlloc(".debug_abbrev", allocator) catch return error.MissingDebugInfo; |
| 858 | errdefer allocator.free(debug_abbrev); |
| 859 | const debug_str = coff_obj.getSectionDataAlloc(".debug_str", allocator) catch return error.MissingDebugInfo; |
| 860 | errdefer allocator.free(debug_str); |
| 861 | const debug_line = coff_obj.getSectionDataAlloc(".debug_line", allocator) catch return error.MissingDebugInfo; |
| 862 | errdefer allocator.free(debug_line); |
| 863 | |
| 864 | const debug_str_offsets = coff_obj.getSectionDataAlloc(".debug_str_offsets", allocator) catch null; |
| 865 | const debug_line_str = coff_obj.getSectionDataAlloc(".debug_line_str", allocator) catch null; |
| 866 | const debug_ranges = coff_obj.getSectionDataAlloc(".debug_ranges", allocator) catch null; |
| 867 | const debug_loclists = coff_obj.getSectionDataAlloc(".debug_loclists", allocator) catch null; |
| 868 | const debug_rnglists = coff_obj.getSectionDataAlloc(".debug_rnglists", allocator) catch null; |
| 869 | const debug_addr = coff_obj.getSectionDataAlloc(".debug_addr", allocator) catch null; |
| 870 | const debug_names = coff_obj.getSectionDataAlloc(".debug_names", allocator) catch null; |
| 871 | const debug_frame = coff_obj.getSectionDataAlloc(".debug_frame", allocator) catch null; |
| 876 | 872 | |
| 877 | 873 | var dwarf = DW.DwarfInfo{ |
| 878 | 874 | .endian = native_endian, |
| 879 | | .debug_info = debug_info orelse return error.MissingDebugInfo, |
| 880 | | .debug_abbrev = debug_abbrev orelse return error.MissingDebugInfo, |
| 881 | | .debug_str = debug_str orelse return error.MissingDebugInfo, |
| 875 | .debug_info = debug_info, |
| 876 | .debug_abbrev = debug_abbrev, |
| 877 | .debug_str = debug_str, |
| 882 | 878 | .debug_str_offsets = debug_str_offsets, |
| 883 | | .debug_line = debug_line orelse return error.MissingDebugInfo, |
| 879 | .debug_line = debug_line, |
| 884 | 880 | .debug_line_str = debug_line_str, |
| 885 | 881 | .debug_ranges = debug_ranges, |
| 886 | 882 | .debug_loclists = debug_loclists, |
| ... | ... | @@ -889,13 +885,28 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_file: File) !ModuleDebugInfo |
| 889 | 885 | .debug_names = debug_names, |
| 890 | 886 | .debug_frame = debug_frame, |
| 891 | 887 | }; |
| 892 | | try DW.openDwarfDebugInfo(&dwarf, allocator); |
| 888 | |
| 889 | DW.openDwarfDebugInfo(&dwarf, allocator) catch |err| { |
| 890 | if (debug_str_offsets) |d| allocator.free(d); |
| 891 | if (debug_line_str) |d| allocator.free(d); |
| 892 | if (debug_ranges) |d| allocator.free(d); |
| 893 | if (debug_loclists) |d| allocator.free(d); |
| 894 | if (debug_rnglists) |d| allocator.free(d); |
| 895 | if (debug_addr) |d| allocator.free(d); |
| 896 | if (debug_names) |d| allocator.free(d); |
| 897 | if (debug_frame) |d| allocator.free(d); |
| 898 | return err; |
| 899 | }; |
| 900 | |
| 893 | 901 | di.debug_data = PdbOrDwarf{ .dwarf = dwarf }; |
| 894 | 902 | return di; |
| 895 | 903 | } |
| 896 | 904 | |
| 905 | // Only used by pdb path |
| 906 | di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(allocator); |
| 907 | |
| 897 | 908 | var path_buf: [windows.MAX_PATH]u8 = undefined; |
| 898 | | const len = try di.coff.getPdbPath(path_buf[0..]); |
| 909 | const len = try coff_obj.getPdbPath(path_buf[0..]); |
| 899 | 910 | const raw_path = path_buf[0..len]; |
| 900 | 911 | |
| 901 | 912 | const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path}); |
| ... | ... | @@ -909,7 +920,7 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_file: File) !ModuleDebugInfo |
| 909 | 920 | try di.debug_data.pdb.parseInfoStream(); |
| 910 | 921 | try di.debug_data.pdb.parseDbiStream(); |
| 911 | 922 | |
| 912 | | if (!mem.eql(u8, &di.coff.guid, &di.debug_data.pdb.guid) or di.coff.age != di.debug_data.pdb.age) |
| 923 | if (!mem.eql(u8, &coff_obj.guid, &di.debug_data.pdb.guid) or coff_obj.age != di.debug_data.pdb.age) |
| 913 | 924 | return error.InvalidDebugInfo; |
| 914 | 925 | |
| 915 | 926 | return di; |
| ... | ... | @@ -1225,15 +1236,49 @@ fn mapWholeFile(file: File) ![]align(mem.page_size) const u8 { |
| 1225 | 1236 | } |
| 1226 | 1237 | } |
| 1227 | 1238 | |
| 1239 | pub const ModuleInfo = struct { |
| 1240 | base_address: usize, |
| 1241 | size: u32, |
| 1242 | }; |
| 1243 | |
| 1228 | 1244 | pub const DebugInfo = struct { |
| 1229 | 1245 | allocator: mem.Allocator, |
| 1230 | 1246 | address_map: std.AutoHashMap(usize, *ModuleDebugInfo), |
| 1247 | modules: if (native_os == .windows) std.ArrayListUnmanaged(ModuleInfo) else void, |
| 1231 | 1248 | |
| 1232 | | pub fn init(allocator: mem.Allocator) DebugInfo { |
| 1233 | | return DebugInfo{ |
| 1249 | pub fn init(allocator: mem.Allocator) !DebugInfo { |
| 1250 | var debug_info = DebugInfo{ |
| 1234 | 1251 | .allocator = allocator, |
| 1235 | 1252 | .address_map = std.AutoHashMap(usize, *ModuleDebugInfo).init(allocator), |
| 1253 | .modules = if (native_os == .windows) .{} else {}, |
| 1236 | 1254 | }; |
| 1255 | |
| 1256 | if (native_os == .windows) { |
| 1257 | const handle = windows.kernel32.CreateToolhelp32Snapshot(windows.TH32CS_SNAPMODULE | windows.TH32CS_SNAPMODULE32, 0); |
| 1258 | if (handle == windows.INVALID_HANDLE_VALUE) { |
| 1259 | switch (windows.kernel32.GetLastError()) { |
| 1260 | else => |err| return windows.unexpectedError(err), |
| 1261 | } |
| 1262 | } |
| 1263 | |
| 1264 | defer windows.CloseHandle(handle); |
| 1265 | |
| 1266 | var module_entry: windows.MODULEENTRY32 = undefined; |
| 1267 | module_entry.dwSize = @sizeOf(windows.MODULEENTRY32); |
| 1268 | if (windows.kernel32.Module32First(handle, &module_entry) == 0) { |
| 1269 | return error.MissingDebugInfo; |
| 1270 | } |
| 1271 | |
| 1272 | var module_valid = true; |
| 1273 | while (module_valid) { |
| 1274 | const module_info = try debug_info.modules.addOne(allocator); |
| 1275 | module_info.base_address = @ptrToInt(module_entry.modBaseAddr); |
| 1276 | module_info.size = module_entry.modBaseSize; |
| 1277 | module_valid = windows.kernel32.Module32Next(handle, &module_entry) == 1; |
| 1278 | } |
| 1279 | } |
| 1280 | |
| 1281 | return debug_info; |
| 1237 | 1282 | } |
| 1238 | 1283 | |
| 1239 | 1284 | pub fn deinit(self: *DebugInfo) void { |
| ... | ... | @@ -1322,79 +1367,20 @@ pub const DebugInfo = struct { |
| 1322 | 1367 | } |
| 1323 | 1368 | |
| 1324 | 1369 | fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ModuleDebugInfo { |
| 1325 | | const process_handle = windows.kernel32.GetCurrentProcess(); |
| 1326 | | |
| 1327 | | // Find how many modules are actually loaded |
| 1328 | | var dummy: windows.HMODULE = undefined; |
| 1329 | | var bytes_needed: windows.DWORD = undefined; |
| 1330 | | if (windows.kernel32.K32EnumProcessModules( |
| 1331 | | process_handle, |
| 1332 | | @ptrCast([*]windows.HMODULE, &dummy), |
| 1333 | | 0, |
| 1334 | | &bytes_needed, |
| 1335 | | ) == 0) |
| 1336 | | return error.MissingDebugInfo; |
| 1337 | | |
| 1338 | | const needed_modules = bytes_needed / @sizeOf(windows.HMODULE); |
| 1339 | | |
| 1340 | | // Fetch the complete module list |
| 1341 | | var modules = try self.allocator.alloc(windows.HMODULE, needed_modules); |
| 1342 | | defer self.allocator.free(modules); |
| 1343 | | if (windows.kernel32.K32EnumProcessModules( |
| 1344 | | process_handle, |
| 1345 | | modules.ptr, |
| 1346 | | math.cast(windows.DWORD, modules.len * @sizeOf(windows.HMODULE)) orelse return error.Overflow, |
| 1347 | | &bytes_needed, |
| 1348 | | ) == 0) |
| 1349 | | return error.MissingDebugInfo; |
| 1350 | | |
| 1351 | | // There's an unavoidable TOCTOU problem here, the module list may have |
| 1352 | | // changed between the two EnumProcessModules call. |
| 1353 | | // Pick the smallest amount of elements to avoid processing garbage. |
| 1354 | | const needed_modules_after = bytes_needed / @sizeOf(windows.HMODULE); |
| 1355 | | const loaded_modules = math.min(needed_modules, needed_modules_after); |
| 1356 | | |
| 1357 | | for (modules[0..loaded_modules]) |module| { |
| 1358 | | var info: windows.MODULEINFO = undefined; |
| 1359 | | if (windows.kernel32.K32GetModuleInformation( |
| 1360 | | process_handle, |
| 1361 | | module, |
| 1362 | | &info, |
| 1363 | | @sizeOf(@TypeOf(info)), |
| 1364 | | ) == 0) |
| 1365 | | return error.MissingDebugInfo; |
| 1366 | | |
| 1367 | | const seg_start = @ptrToInt(info.lpBaseOfDll); |
| 1368 | | const seg_end = seg_start + info.SizeOfImage; |
| 1369 | | |
| 1370 | | if (address >= seg_start and address < seg_end) { |
| 1371 | | if (self.address_map.get(seg_start)) |obj_di| { |
| 1370 | for (self.modules.items) |module| { |
| 1371 | if (address >= module.base_address and address < module.base_address + module.size) { |
| 1372 | if (self.address_map.get(module.base_address)) |obj_di| { |
| 1372 | 1373 | return obj_di; |
| 1373 | 1374 | } |
| 1374 | 1375 | |
| 1375 | | var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined; |
| 1376 | | // openFileAbsoluteW requires the prefix to be present |
| 1377 | | mem.copy(u16, name_buffer[0..4], &[_]u16{ '\\', '?', '?', '\\' }); |
| 1378 | | const len = windows.kernel32.K32GetModuleFileNameExW( |
| 1379 | | process_handle, |
| 1380 | | module, |
| 1381 | | @ptrCast(windows.LPWSTR, &name_buffer[4]), |
| 1382 | | windows.PATH_MAX_WIDE, |
| 1383 | | ); |
| 1384 | | assert(len > 0); |
| 1385 | | |
| 1376 | const mapped_module = @intToPtr([*]const u8, module.base_address)[0..module.size]; |
| 1386 | 1377 | const obj_di = try self.allocator.create(ModuleDebugInfo); |
| 1387 | 1378 | errdefer self.allocator.destroy(obj_di); |
| 1388 | 1379 | |
| 1389 | | const coff_file = fs.openFileAbsoluteW(name_buffer[0 .. len + 4 :0], .{}) catch |err| switch (err) { |
| 1390 | | error.FileNotFound => return error.MissingDebugInfo, |
| 1391 | | else => return err, |
| 1392 | | }; |
| 1393 | | obj_di.* = try readCoffDebugInfo(self.allocator, coff_file); |
| 1394 | | obj_di.base_address = seg_start; |
| 1395 | | |
| 1396 | | try self.address_map.putNoClobber(seg_start, obj_di); |
| 1380 | obj_di.* = try readCoffDebugInfo(self.allocator, mapped_module); |
| 1381 | obj_di.base_address = module.base_address; |
| 1397 | 1382 | |
| 1383 | try self.address_map.putNoClobber(module.base_address, obj_di); |
| 1398 | 1384 | return obj_di; |
| 1399 | 1385 | } |
| 1400 | 1386 | } |
| ... | ... | @@ -1727,12 +1713,31 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1727 | 1713 | .uefi, .windows => struct { |
| 1728 | 1714 | base_address: usize, |
| 1729 | 1715 | debug_data: PdbOrDwarf, |
| 1730 | | coff: *coff.Coff, |
| 1716 | coff_image_base: u64, |
| 1717 | coff_section_headers: []coff.SectionHeader, |
| 1731 | 1718 | |
| 1732 | 1719 | fn deinit(self: *@This(), allocator: mem.Allocator) void { |
| 1720 | switch (self.debug_data) { |
| 1721 | .dwarf => |*dwarf| { |
| 1722 | allocator.free(dwarf.debug_info); |
| 1723 | allocator.free(dwarf.debug_abbrev); |
| 1724 | allocator.free(dwarf.debug_str); |
| 1725 | allocator.free(dwarf.debug_line); |
| 1726 | if (dwarf.debug_str_offsets) |d| allocator.free(d); |
| 1727 | if (dwarf.debug_line_str) |d| allocator.free(d); |
| 1728 | if (dwarf.debug_ranges) |d| allocator.free(d); |
| 1729 | if (dwarf.debug_loclists) |d| allocator.free(d); |
| 1730 | if (dwarf.debug_rnglists) |d| allocator.free(d); |
| 1731 | if (dwarf.debug_addr) |d| allocator.free(d); |
| 1732 | if (dwarf.debug_names) |d| allocator.free(d); |
| 1733 | if (dwarf.debug_frame) |d| allocator.free(d); |
| 1734 | }, |
| 1735 | .pdb => { |
| 1736 | allocator.free(self.coff_section_headers); |
| 1737 | }, |
| 1738 | } |
| 1739 | |
| 1733 | 1740 | self.debug_data.deinit(allocator); |
| 1734 | | self.coff.deinit(); |
| 1735 | | allocator.destroy(self.coff); |
| 1736 | 1741 | } |
| 1737 | 1742 | |
| 1738 | 1743 | pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo { |
| ... | ... | @@ -1741,7 +1746,7 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1741 | 1746 | |
| 1742 | 1747 | switch (self.debug_data) { |
| 1743 | 1748 | .dwarf => |*dwarf| { |
| 1744 | | const dwarf_address = relocated_address + self.coff.getImageBase(); |
| 1749 | const dwarf_address = relocated_address + self.coff_image_base; |
| 1745 | 1750 | return getSymbolFromDwarf(allocator, dwarf_address, dwarf); |
| 1746 | 1751 | }, |
| 1747 | 1752 | .pdb => { |
| ... | ... | @@ -1751,10 +1756,9 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1751 | 1756 | |
| 1752 | 1757 | var coff_section: *align(1) const coff.SectionHeader = undefined; |
| 1753 | 1758 | const mod_index = for (self.debug_data.pdb.sect_contribs) |sect_contrib| { |
| 1754 | | const sections = self.coff.getSectionHeaders(); |
| 1755 | | if (sect_contrib.Section > sections.len) continue; |
| 1759 | if (sect_contrib.Section > self.coff_section_headers.len) continue; |
| 1756 | 1760 | // Remember that SectionContribEntry.Section is 1-based. |
| 1757 | | coff_section = &sections[sect_contrib.Section - 1]; |
| 1761 | coff_section = &self.coff_section_headers[sect_contrib.Section - 1]; |
| 1758 | 1762 | |
| 1759 | 1763 | const vaddr_start = coff_section.virtual_address + sect_contrib.Offset; |
| 1760 | 1764 | const vaddr_end = vaddr_start + sect_contrib.Size; |