authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-10 18:08:12-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-01-10 18:08:12-05:00
log9b807f9c171e9e5998447ab3846d29de921cf8dd
treee52212b34832435f3f7118eec44540c509da4b34
parentbb4cb342048a9feee7e5408c4f444439197a96af
parent58e558822a2980bcaf29ce2a07474093702cabc6
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #14247 from kcbanner/windows_improve_module_lookup

Windows debug info lookup improvements

4 files changed, 208 insertions(+), 159 deletions(-)

lib/std/coff.zig+39-44
...@@ -1061,65 +1061,55 @@ pub const CoffError = error{...@@ -1061,65 +1061,55 @@ pub const CoffError = error{
10611061
1062// Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format1062// Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
1063pub const Coff = struct {1063pub const Coff = struct {
1064 allocator: mem.Allocator,1064 data: []const u8,
1065 data: []const u8 = undefined,1065 is_image: bool,
1066 is_image: bool = false,1066 coff_header_offset: usize,
1067 coff_header_offset: usize = 0,
10681067
1069 guid: [16]u8 = undefined,1068 guid: [16]u8 = undefined,
1070 age: u32 = undefined,1069 age: u32 = undefined,
10711070
1072 pub fn deinit(self: *Coff) void {1071 // The lifetime of `data` must be longer than the lifetime of the returned Coff
1073 self.allocator.free(self.data);1072 pub fn init(data: []const u8) !Coff {
1074 }
1075
1076 /// Takes ownership of `data`.
1077 pub fn parse(self: *Coff, data: []const u8) !void {
1078 self.data = data;
1079
1080 const pe_pointer_offset = 0x3C;1073 const pe_pointer_offset = 0x3C;
1081 const pe_magic = "PE\x00\x00";1074 const pe_magic = "PE\x00\x00";
10821075
1083 var stream = std.io.fixedBufferStream(self.data);1076 var stream = std.io.fixedBufferStream(data);
1084 const reader = stream.reader();1077 const reader = stream.reader();
1085 try stream.seekTo(pe_pointer_offset);1078 try stream.seekTo(pe_pointer_offset);
1086 const coff_header_offset = try reader.readIntLittle(u32);1079 var coff_header_offset = try reader.readIntLittle(u32);
1087 try stream.seekTo(coff_header_offset);1080 try stream.seekTo(coff_header_offset);
1088 var buf: [4]u8 = undefined;1081 var buf: [4]u8 = undefined;
1089 try reader.readNoEof(&buf);1082 try reader.readNoEof(&buf);
1090 self.is_image = mem.eql(u8, pe_magic, &buf);1083 const is_image = mem.eql(u8, pe_magic, &buf);
1084
1085 var coff = @This(){
1086 .data = data,
1087 .is_image = is_image,
1088 .coff_header_offset = coff_header_offset,
1089 };
10911090
1092 // Do some basic validation upfront1091 // Do some basic validation upfront
1093 if (self.is_image) {1092 if (is_image) {
1094 self.coff_header_offset = coff_header_offset + 4;1093 coff.coff_header_offset = coff.coff_header_offset + 4;
1095 const coff_header = self.getCoffHeader();1094 const coff_header = coff.getCoffHeader();
1096 if (coff_header.size_of_optional_header == 0) return error.MissingPEHeader;1095 if (coff_header.size_of_optional_header == 0) return error.MissingPEHeader;
1097 }1096 }
10981097
1099 // JK: we used to check for architecture here and throw an error if not x86 or derivative.1098 // JK: we used to check for architecture here and throw an error if not x86 or derivative.
1100 // However I am willing to take a leap of faith and let aarch64 have a shot also.1099 // However I am willing to take a leap of faith and let aarch64 have a shot also.
1100
1101 return coff;
1101 }1102 }
11021103
1103 pub fn getPdbPath(self: *Coff, buffer: []u8) !usize {1104 pub fn getPdbPath(self: *Coff, buffer: []u8) !usize {
1104 assert(self.is_image);1105 assert(self.is_image);
11051106
1106 const header = blk: {
1107 if (self.getSectionByName(".buildid")) |hdr| {
1108 break :blk hdr;
1109 } else if (self.getSectionByName(".rdata")) |hdr| {
1110 break :blk hdr;
1111 } else {
1112 return error.MissingCoffSection;
1113 }
1114 };
1115
1116 const data_dirs = self.getDataDirectories();1107 const data_dirs = self.getDataDirectories();
1117 const debug_dir = data_dirs[@enumToInt(DirectoryEntry.DEBUG)];1108 const debug_dir = data_dirs[@enumToInt(DirectoryEntry.DEBUG)];
1118 const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data;
11191109
1120 var stream = std.io.fixedBufferStream(self.data);1110 var stream = std.io.fixedBufferStream(self.data);
1121 const reader = stream.reader();1111 const reader = stream.reader();
1122 try stream.seekTo(file_offset);1112 try stream.seekTo(debug_dir.virtual_address);
11231113
1124 // Find the correct DebugDirectoryEntry, and where its data is stored.1114 // Find the correct DebugDirectoryEntry, and where its data is stored.
1125 // It can be in any section.1115 // It can be in any section.
...@@ -1128,16 +1118,8 @@ pub const Coff = struct {...@@ -1128,16 +1118,8 @@ pub const Coff = struct {
1128 blk: while (i < debug_dir_entry_count) : (i += 1) {1118 blk: while (i < debug_dir_entry_count) : (i += 1) {
1129 const debug_dir_entry = try reader.readStruct(DebugDirectoryEntry);1119 const debug_dir_entry = try reader.readStruct(DebugDirectoryEntry);
1130 if (debug_dir_entry.type == .CODEVIEW) {1120 if (debug_dir_entry.type == .CODEVIEW) {
1131 for (self.getSectionHeaders()) |*section| {1121 try stream.seekTo(debug_dir_entry.address_of_raw_data);
1132 const section_start = section.virtual_address;1122 break :blk;
1133 const section_size = section.virtual_size;
1134 const rva = debug_dir_entry.address_of_raw_data;
1135 const offset = rva - section_start;
1136 if (section_start <= rva and offset < section_size and debug_dir_entry.size_of_data <= section_size - offset) {
1137 try stream.seekTo(section.pointer_to_raw_data + offset);
1138 break :blk;
1139 }
1140 }
1141 }1123 }
1142 }1124 }
11431125
...@@ -1238,6 +1220,16 @@ pub const Coff = struct {...@@ -1238,6 +1220,16 @@ pub const Coff = struct {
1238 return @ptrCast([*]align(1) const SectionHeader, self.data.ptr + offset)[0..coff_header.number_of_sections];1220 return @ptrCast([*]align(1) const SectionHeader, self.data.ptr + offset)[0..coff_header.number_of_sections];
1239 }1221 }
12401222
1223 pub fn getSectionHeadersAlloc(self: *const Coff, allocator: mem.Allocator) ![]SectionHeader {
1224 const section_headers = self.getSectionHeaders();
1225 const out_buff = try allocator.alloc(SectionHeader, section_headers.len);
1226 for (out_buff) |*section_header, i| {
1227 section_header.* = section_headers[i];
1228 }
1229
1230 return out_buff;
1231 }
1232
1241 pub fn getSectionName(self: *const Coff, sect_hdr: *align(1) const SectionHeader) []const u8 {1233 pub fn getSectionName(self: *const Coff, sect_hdr: *align(1) const SectionHeader) []const u8 {
1242 const name = sect_hdr.getName() orelse blk: {1234 const name = sect_hdr.getName() orelse blk: {
1243 const strtab = self.getStrtab().?;1235 const strtab = self.getStrtab().?;
...@@ -1256,12 +1248,15 @@ pub const Coff = struct {...@@ -1256,12 +1248,15 @@ pub const Coff = struct {
1256 return null;1248 return null;
1257 }1249 }
12581250
1251 pub fn getSectionData(self: *const Coff, comptime name: []const u8) ![]const u8 {
1252 const sec = self.getSectionByName(name) orelse return error.MissingCoffSection;
1253 return self.data[sec.pointer_to_raw_data..][0..sec.virtual_size];
1254 }
1255
1259 // Return an owned slice full of the section data1256 // Return an owned slice full of the section data
1260 pub fn getSectionDataAlloc(self: *const Coff, comptime name: []const u8, allocator: mem.Allocator) ![]u8 {1257 pub fn getSectionDataAlloc(self: *const Coff, comptime name: []const u8, allocator: mem.Allocator) ![]u8 {
1261 const sec = self.getSectionByName(name) orelse return error.MissingCoffSection;1258 const section_data = try self.getSectionData(name);
1262 const out_buff = try allocator.alloc(u8, sec.virtual_size);1259 return allocator.dupe(u8, section_data);
1263 mem.copy(u8, out_buff, self.data[sec.pointer_to_raw_data..][0..sec.virtual_size]);
1264 return out_buff;
1265 }1260 }
1266};1261};
12671262
lib/std/debug.zig+118-113
...@@ -811,7 +811,7 @@ fn printLineInfo(...@@ -811,7 +811,7 @@ fn printLineInfo(
811pub const OpenSelfDebugInfoError = error{811pub const OpenSelfDebugInfoError = error{
812 MissingDebugInfo,812 MissingDebugInfo,
813 UnsupportedOperatingSystem,813 UnsupportedOperatingSystem,
814};814} || @typeInfo(@typeInfo(@TypeOf(DebugInfo.init)).Fn.return_type.?).ErrorUnion.error_set;
815815
816pub fn openSelfDebugInfo(allocator: mem.Allocator) OpenSelfDebugInfoError!DebugInfo {816pub fn openSelfDebugInfo(allocator: mem.Allocator) OpenSelfDebugInfoError!DebugInfo {
817 nosuspend {817 nosuspend {
...@@ -827,60 +827,56 @@ pub fn openSelfDebugInfo(allocator: mem.Allocator) OpenSelfDebugInfoError!DebugI...@@ -827,60 +827,56 @@ pub fn openSelfDebugInfo(allocator: mem.Allocator) OpenSelfDebugInfoError!DebugI
827 .dragonfly,827 .dragonfly,
828 .openbsd,828 .openbsd,
829 .macos,829 .macos,
830 .windows,
831 .solaris,830 .solaris,
832 => return DebugInfo.init(allocator),831 .windows,
832 => return try DebugInfo.init(allocator),
833 else => return error.UnsupportedOperatingSystem,833 else => return error.UnsupportedOperatingSystem,
834 }834 }
835 }835 }
836}836}
837837
838/// This takes ownership of coff_file: users of this function should not close838fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDebugInfo {
839/// it themselves, even on error.
840/// TODO it's weird to take ownership even on error, rework this code.
841fn readCoffDebugInfo(allocator: mem.Allocator, coff_file: File) !ModuleDebugInfo {
842 nosuspend {839 nosuspend {
843 defer coff_file.close();
844
845 const coff_obj = try allocator.create(coff.Coff);840 const coff_obj = try allocator.create(coff.Coff);
846 errdefer allocator.destroy(coff_obj);841 defer allocator.destroy(coff_obj);
847 coff_obj.* = .{ .allocator = allocator };842 coff_obj.* = try coff.Coff.init(coff_bytes);
848843
849 var di = ModuleDebugInfo{844 var di = ModuleDebugInfo{
850 .base_address = undefined,845 .base_address = undefined,
851 .coff = coff_obj,846 .coff_image_base = coff_obj.getImageBase(),
847 .coff_section_headers = undefined,
852 .debug_data = undefined,848 .debug_data = undefined,
853 };849 };
854850
855 // TODO convert to Windows' memory-mapped file API851 if (coff_obj.getSectionByName(".debug_info")) |sec| {
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| {
861 // This coff file has embedded DWARF debug info852 // This coff file has embedded DWARF debug info
862 _ = sec;853 _ = sec;
863 // TODO: free the section data slices854
864 const debug_info = di.coff.getSectionDataAlloc(".debug_info", allocator) catch null;855 const debug_info = coff_obj.getSectionDataAlloc(".debug_info", allocator) catch return error.MissingDebugInfo;
865 const debug_abbrev = di.coff.getSectionDataAlloc(".debug_abbrev", allocator) catch null;856 errdefer allocator.free(debug_info);
866 const debug_str = di.coff.getSectionDataAlloc(".debug_str", allocator) catch null;857 const debug_abbrev = coff_obj.getSectionDataAlloc(".debug_abbrev", allocator) catch return error.MissingDebugInfo;
867 const debug_str_offsets = di.coff.getSectionDataAlloc(".debug_str_offsets", allocator) catch null;858 errdefer allocator.free(debug_abbrev);
868 const debug_line = di.coff.getSectionDataAlloc(".debug_line", allocator) catch null;859 const debug_str = coff_obj.getSectionDataAlloc(".debug_str", allocator) catch return error.MissingDebugInfo;
869 const debug_line_str = di.coff.getSectionDataAlloc(".debug_line_str", allocator) catch null;860 errdefer allocator.free(debug_str);
870 const debug_ranges = di.coff.getSectionDataAlloc(".debug_ranges", allocator) catch null;861 const debug_line = coff_obj.getSectionDataAlloc(".debug_line", allocator) catch return error.MissingDebugInfo;
871 const debug_loclists = di.coff.getSectionDataAlloc(".debug_loclists", allocator) catch null;862 errdefer allocator.free(debug_line);
872 const debug_rnglists = di.coff.getSectionDataAlloc(".debug_rnglists", allocator) catch null;863
873 const debug_addr = di.coff.getSectionDataAlloc(".debug_addr", allocator) catch null;864 const debug_str_offsets = coff_obj.getSectionDataAlloc(".debug_str_offsets", allocator) catch null;
874 const debug_names = di.coff.getSectionDataAlloc(".debug_names", allocator) catch null;865 const debug_line_str = coff_obj.getSectionDataAlloc(".debug_line_str", allocator) catch null;
875 const debug_frame = di.coff.getSectionDataAlloc(".debug_frame", 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;
876872
877 var dwarf = DW.DwarfInfo{873 var dwarf = DW.DwarfInfo{
878 .endian = native_endian,874 .endian = native_endian,
879 .debug_info = debug_info orelse return error.MissingDebugInfo,875 .debug_info = debug_info,
880 .debug_abbrev = debug_abbrev orelse return error.MissingDebugInfo,876 .debug_abbrev = debug_abbrev,
881 .debug_str = debug_str orelse return error.MissingDebugInfo,877 .debug_str = debug_str,
882 .debug_str_offsets = debug_str_offsets,878 .debug_str_offsets = debug_str_offsets,
883 .debug_line = debug_line orelse return error.MissingDebugInfo,879 .debug_line = debug_line,
884 .debug_line_str = debug_line_str,880 .debug_line_str = debug_line_str,
885 .debug_ranges = debug_ranges,881 .debug_ranges = debug_ranges,
886 .debug_loclists = debug_loclists,882 .debug_loclists = debug_loclists,
...@@ -889,13 +885,28 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_file: File) !ModuleDebugInfo...@@ -889,13 +885,28 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_file: File) !ModuleDebugInfo
889 .debug_names = debug_names,885 .debug_names = debug_names,
890 .debug_frame = debug_frame,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 di.debug_data = PdbOrDwarf{ .dwarf = dwarf };901 di.debug_data = PdbOrDwarf{ .dwarf = dwarf };
894 return di;902 return di;
895 }903 }
896904
905 // Only used by pdb path
906 di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(allocator);
907
897 var path_buf: [windows.MAX_PATH]u8 = undefined;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 const raw_path = path_buf[0..len];910 const raw_path = path_buf[0..len];
900911
901 const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path});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,7 +920,7 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_file: File) !ModuleDebugInfo
909 try di.debug_data.pdb.parseInfoStream();920 try di.debug_data.pdb.parseInfoStream();
910 try di.debug_data.pdb.parseDbiStream();921 try di.debug_data.pdb.parseDbiStream();
911922
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 return error.InvalidDebugInfo;924 return error.InvalidDebugInfo;
914925
915 return di;926 return di;
...@@ -1225,15 +1236,49 @@ fn mapWholeFile(file: File) ![]align(mem.page_size) const u8 {...@@ -1225,15 +1236,49 @@ fn mapWholeFile(file: File) ![]align(mem.page_size) const u8 {
1225 }1236 }
1226}1237}
12271238
1239pub const ModuleInfo = struct {
1240 base_address: usize,
1241 size: u32,
1242};
1243
1228pub const DebugInfo = struct {1244pub const DebugInfo = struct {
1229 allocator: mem.Allocator,1245 allocator: mem.Allocator,
1230 address_map: std.AutoHashMap(usize, *ModuleDebugInfo),1246 address_map: std.AutoHashMap(usize, *ModuleDebugInfo),
1247 modules: if (native_os == .windows) std.ArrayListUnmanaged(ModuleInfo) else void,
12311248
1232 pub fn init(allocator: mem.Allocator) DebugInfo {1249 pub fn init(allocator: mem.Allocator) !DebugInfo {
1233 return DebugInfo{1250 var debug_info = DebugInfo{
1234 .allocator = allocator,1251 .allocator = allocator,
1235 .address_map = std.AutoHashMap(usize, *ModuleDebugInfo).init(allocator),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 }
12381283
1239 pub fn deinit(self: *DebugInfo) void {1284 pub fn deinit(self: *DebugInfo) void {
...@@ -1244,6 +1289,7 @@ pub const DebugInfo = struct {...@@ -1244,6 +1289,7 @@ pub const DebugInfo = struct {
1244 self.allocator.destroy(mdi);1289 self.allocator.destroy(mdi);
1245 }1290 }
1246 self.address_map.deinit();1291 self.address_map.deinit();
1292 if (native_os == .windows) self.modules.deinit(self.allocator);
1247 }1293 }
12481294
1249 pub fn getModuleForAddress(self: *DebugInfo, address: usize) !*ModuleDebugInfo {1295 pub fn getModuleForAddress(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
...@@ -1322,79 +1368,20 @@ pub const DebugInfo = struct {...@@ -1322,79 +1368,20 @@ pub const DebugInfo = struct {
1322 }1368 }
13231369
1324 fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ModuleDebugInfo {1370 fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
1325 const process_handle = windows.kernel32.GetCurrentProcess();1371 for (self.modules.items) |module| {
13261372 if (address >= module.base_address and address < module.base_address + module.size) {
1327 // Find how many modules are actually loaded1373 if (self.address_map.get(module.base_address)) |obj_di| {
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| {
1372 return obj_di;1374 return obj_di;
1373 }1375 }
13741376
1375 var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined;1377 const mapped_module = @intToPtr([*]const u8, module.base_address)[0..module.size];
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
1386 const obj_di = try self.allocator.create(ModuleDebugInfo);1378 const obj_di = try self.allocator.create(ModuleDebugInfo);
1387 errdefer self.allocator.destroy(obj_di);1379 errdefer self.allocator.destroy(obj_di);
13881380
1389 const coff_file = fs.openFileAbsoluteW(name_buffer[0 .. len + 4 :0], .{}) catch |err| switch (err) {1381 obj_di.* = try readCoffDebugInfo(self.allocator, mapped_module);
1390 error.FileNotFound => return error.MissingDebugInfo,1382 obj_di.base_address = module.base_address;
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);
13971383
1384 try self.address_map.putNoClobber(module.base_address, obj_di);
1398 return obj_di;1385 return obj_di;
1399 }1386 }
1400 }1387 }
...@@ -1727,12 +1714,31 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1727,12 +1714,31 @@ pub const ModuleDebugInfo = switch (native_os) {
1727 .uefi, .windows => struct {1714 .uefi, .windows => struct {
1728 base_address: usize,1715 base_address: usize,
1729 debug_data: PdbOrDwarf,1716 debug_data: PdbOrDwarf,
1730 coff: *coff.Coff,1717 coff_image_base: u64,
1718 coff_section_headers: []coff.SectionHeader,
17311719
1732 fn deinit(self: *@This(), allocator: mem.Allocator) void {1720 fn deinit(self: *@This(), allocator: mem.Allocator) void {
1721 switch (self.debug_data) {
1722 .dwarf => |*dwarf| {
1723 allocator.free(dwarf.debug_info);
1724 allocator.free(dwarf.debug_abbrev);
1725 allocator.free(dwarf.debug_str);
1726 allocator.free(dwarf.debug_line);
1727 if (dwarf.debug_str_offsets) |d| allocator.free(d);
1728 if (dwarf.debug_line_str) |d| allocator.free(d);
1729 if (dwarf.debug_ranges) |d| allocator.free(d);
1730 if (dwarf.debug_loclists) |d| allocator.free(d);
1731 if (dwarf.debug_rnglists) |d| allocator.free(d);
1732 if (dwarf.debug_addr) |d| allocator.free(d);
1733 if (dwarf.debug_names) |d| allocator.free(d);
1734 if (dwarf.debug_frame) |d| allocator.free(d);
1735 },
1736 .pdb => {
1737 allocator.free(self.coff_section_headers);
1738 },
1739 }
1740
1733 self.debug_data.deinit(allocator);1741 self.debug_data.deinit(allocator);
1734 self.coff.deinit();
1735 allocator.destroy(self.coff);
1736 }1742 }
17371743
1738 pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo {1744 pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo {
...@@ -1741,7 +1747,7 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1741,7 +1747,7 @@ pub const ModuleDebugInfo = switch (native_os) {
17411747
1742 switch (self.debug_data) {1748 switch (self.debug_data) {
1743 .dwarf => |*dwarf| {1749 .dwarf => |*dwarf| {
1744 const dwarf_address = relocated_address + self.coff.getImageBase();1750 const dwarf_address = relocated_address + self.coff_image_base;
1745 return getSymbolFromDwarf(allocator, dwarf_address, dwarf);1751 return getSymbolFromDwarf(allocator, dwarf_address, dwarf);
1746 },1752 },
1747 .pdb => {1753 .pdb => {
...@@ -1751,10 +1757,9 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1751,10 +1757,9 @@ pub const ModuleDebugInfo = switch (native_os) {
17511757
1752 var coff_section: *align(1) const coff.SectionHeader = undefined;1758 var coff_section: *align(1) const coff.SectionHeader = undefined;
1753 const mod_index = for (self.debug_data.pdb.sect_contribs) |sect_contrib| {1759 const mod_index = for (self.debug_data.pdb.sect_contribs) |sect_contrib| {
1754 const sections = self.coff.getSectionHeaders();1760 if (sect_contrib.Section > self.coff_section_headers.len) continue;
1755 if (sect_contrib.Section > sections.len) continue;
1756 // Remember that SectionContribEntry.Section is 1-based.1761 // Remember that SectionContribEntry.Section is 1-based.
1757 coff_section = &sections[sect_contrib.Section - 1];1762 coff_section = &self.coff_section_headers[sect_contrib.Section - 1];
17581763
1759 const vaddr_start = coff_section.virtual_address + sect_contrib.Offset;1764 const vaddr_start = coff_section.virtual_address + sect_contrib.Offset;
1760 const vaddr_end = vaddr_start + sect_contrib.Size;1765 const vaddr_end = vaddr_start + sect_contrib.Size;
lib/std/os/windows.zig+44-2
...@@ -2077,7 +2077,7 @@ pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError {...@@ -2077,7 +2077,7 @@ pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError {
2077 );2077 );
2078 _ = std.unicode.utf16leToUtf8(&buf_utf8, buf_wstr[0..len]) catch unreachable;2078 _ = std.unicode.utf16leToUtf8(&buf_utf8, buf_wstr[0..len]) catch unreachable;
2079 std.debug.print("error.Unexpected: GetLastError({}): {s}\n", .{ @enumToInt(err), buf_utf8[0..len] });2079 std.debug.print("error.Unexpected: GetLastError({}): {s}\n", .{ @enumToInt(err), buf_utf8[0..len] });
2080 std.debug.dumpCurrentStackTrace(null);2080 std.debug.dumpCurrentStackTrace(@returnAddress());
2081 }2081 }
2082 return error.Unexpected;2082 return error.Unexpected;
2083}2083}
...@@ -2091,7 +2091,7 @@ pub fn unexpectedWSAError(err: ws2_32.WinsockError) std.os.UnexpectedError {...@@ -2091,7 +2091,7 @@ pub fn unexpectedWSAError(err: ws2_32.WinsockError) std.os.UnexpectedError {
2091pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError {2091pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError {
2092 if (std.os.unexpected_error_tracing) {2092 if (std.os.unexpected_error_tracing) {
2093 std.debug.print("error.Unexpected NTSTATUS=0x{x}\n", .{@enumToInt(status)});2093 std.debug.print("error.Unexpected NTSTATUS=0x{x}\n", .{@enumToInt(status)});
2094 std.debug.dumpCurrentStackTrace(null);2094 std.debug.dumpCurrentStackTrace(@returnAddress());
2095 }2095 }
2096 return error.Unexpected;2096 return error.Unexpected;
2097}2097}
...@@ -3801,6 +3801,26 @@ pub const PEB_LDR_DATA = extern struct {...@@ -3801,6 +3801,26 @@ pub const PEB_LDR_DATA = extern struct {
3801 ShutdownThreadId: HANDLE,3801 ShutdownThreadId: HANDLE,
3802};3802};
38033803
3804/// Microsoft documentation of this is incomplete, the fields here are taken from various resources including:
3805/// - https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data
3806/// - https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntldr/ldr_data_table_entry.htm
3807pub const LDR_DATA_TABLE_ENTRY = extern struct {
3808 Reserved1: [2]PVOID,
3809 InMemoryOrderLinks: LIST_ENTRY,
3810 Reserved2: [2]PVOID,
3811 DllBase: PVOID,
3812 EntryPoint: PVOID,
3813 SizeOfImage: ULONG,
3814 FullDllName: UNICODE_STRING,
3815 Reserved4: [8]BYTE,
3816 Reserved5: [3]PVOID,
3817 DUMMYUNIONNAME: extern union {
3818 CheckSum: ULONG,
3819 Reserved6: PVOID,
3820 },
3821 TimeDateStamp: ULONG,
3822};
3823
3804pub const RTL_USER_PROCESS_PARAMETERS = extern struct {3824pub const RTL_USER_PROCESS_PARAMETERS = extern struct {
3805 AllocationSize: ULONG,3825 AllocationSize: ULONG,
3806 Size: ULONG,3826 Size: ULONG,
...@@ -4349,3 +4369,25 @@ pub fn IsProcessorFeaturePresent(feature: PF) bool {...@@ -4349,3 +4369,25 @@ pub fn IsProcessorFeaturePresent(feature: PF) bool {
4349 if (@enumToInt(feature) >= PROCESSOR_FEATURE_MAX) return false;4369 if (@enumToInt(feature) >= PROCESSOR_FEATURE_MAX) return false;
4350 return SharedUserData.ProcessorFeatures[@enumToInt(feature)] == 1;4370 return SharedUserData.ProcessorFeatures[@enumToInt(feature)] == 1;
4351}4371}
4372
4373pub const TH32CS_SNAPHEAPLIST = 0x00000001;
4374pub const TH32CS_SNAPPROCESS = 0x00000002;
4375pub const TH32CS_SNAPTHREAD = 0x00000004;
4376pub const TH32CS_SNAPMODULE = 0x00000008;
4377pub const TH32CS_SNAPMODULE32 = 0x00000010;
4378pub const TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE;
4379pub const TH32CS_INHERIT = 0x80000000;
4380
4381pub const MAX_MODULE_NAME32 = 255;
4382pub const MODULEENTRY32 = extern struct {
4383 dwSize: DWORD,
4384 th32ModuleID: DWORD,
4385 th32ProcessID: DWORD,
4386 GlblcntUsage: DWORD,
4387 ProccntUsage: DWORD,
4388 modBaseAddr: *BYTE,
4389 modBaseSize: DWORD,
4390 hModule: HMODULE,
4391 szModule: [MAX_MODULE_NAME32 + 1]CHAR,
4392 szExePath: [MAX_PATH]CHAR,
4393};
lib/std/os/windows/kernel32.zig+7
...@@ -66,6 +66,7 @@ const UNWIND_HISTORY_TABLE = windows.UNWIND_HISTORY_TABLE;...@@ -66,6 +66,7 @@ const UNWIND_HISTORY_TABLE = windows.UNWIND_HISTORY_TABLE;
66const RUNTIME_FUNCTION = windows.RUNTIME_FUNCTION;66const RUNTIME_FUNCTION = windows.RUNTIME_FUNCTION;
67const KNONVOLATILE_CONTEXT_POINTERS = windows.KNONVOLATILE_CONTEXT_POINTERS;67const KNONVOLATILE_CONTEXT_POINTERS = windows.KNONVOLATILE_CONTEXT_POINTERS;
68const EXCEPTION_ROUTINE = windows.EXCEPTION_ROUTINE;68const EXCEPTION_ROUTINE = windows.EXCEPTION_ROUTINE;
69const MODULEENTRY32 = windows.MODULEENTRY32;
6970
70pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(WINAPI) ?*anyopaque;71pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(WINAPI) ?*anyopaque;
71pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(WINAPI) c_ulong;72pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(WINAPI) c_ulong;
...@@ -132,6 +133,8 @@ pub extern "kernel32" fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingComp...@@ -132,6 +133,8 @@ pub extern "kernel32" fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingComp
132133
133pub extern "kernel32" fn CreateThread(lpThreadAttributes: ?*SECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?*DWORD) callconv(WINAPI) ?HANDLE;134pub extern "kernel32" fn CreateThread(lpThreadAttributes: ?*SECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?*DWORD) callconv(WINAPI) ?HANDLE;
134135
136pub extern "kernel32" fn CreateToolhelp32Snapshot(dwFlags: DWORD, th32ProcessID: DWORD) callconv(WINAPI) HANDLE;
137
135pub extern "kernel32" fn DeviceIoControl(138pub extern "kernel32" fn DeviceIoControl(
136 h: HANDLE,139 h: HANDLE,
137 dwIoControlCode: DWORD,140 dwIoControlCode: DWORD,
...@@ -265,6 +268,10 @@ pub extern "kernel32" fn VirtualQuery(lpAddress: ?LPVOID, lpBuffer: PMEMORY_BASI...@@ -265,6 +268,10 @@ pub extern "kernel32" fn VirtualQuery(lpAddress: ?LPVOID, lpBuffer: PMEMORY_BASI
265268
266pub extern "kernel32" fn LocalFree(hMem: HLOCAL) callconv(WINAPI) ?HLOCAL;269pub extern "kernel32" fn LocalFree(hMem: HLOCAL) callconv(WINAPI) ?HLOCAL;
267270
271pub extern "kernel32" fn Module32First(hSnapshot: HANDLE, lpme: *MODULEENTRY32) callconv(WINAPI) BOOL;
272
273pub extern "kernel32" fn Module32Next(hSnapshot: HANDLE, lpme: *MODULEENTRY32) callconv(WINAPI) BOOL;
274
268pub extern "kernel32" fn MoveFileExW(275pub extern "kernel32" fn MoveFileExW(
269 lpExistingFileName: [*:0]const u16,276 lpExistingFileName: [*:0]const u16,
270 lpNewFileName: [*:0]const u16,277 lpNewFileName: [*:0]const u16,