authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-06-28 20:37:45-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:14-04:00
logf991b9dc05706613839743f970a32d516085f182
treecd78cc290863b47a88c5e2892f78695be9d97010
parentcaa334712fc8f540349e54fa3008aa3fdef64e13

debug: fix reading -gdwarf generated debug sections in COFF files

I had accidentally regressed support for -gdwarf in 461fb499f3cff9038a427eae120fb34defc9ab38 when I changed the logic to use the already-mapped exe/dll image instead of loading it from disk. The string table is mapped as all zeroes by the loader, so if a section header's name is longer than 8 bytes (like the ones generated by -gdwarf), then the name can't be read. Now, if any section headers require the string table, the file is mapped from disk. windows: Add NtCreateSection/NtMapViewOfSection/NtUnmapViewOfSection

4 files changed, 169 insertions(+), 15 deletions(-)

lib/std/coff.zig+5
......@@ -1214,6 +1214,11 @@ pub const Coff = struct {
12141214 return Strtab{ .buffer = self.data[offset..][0..size] };
12151215 }
12161216
1217 pub fn strtabRequired(self: *const Coff) bool {
1218 for (self.getSectionHeaders()) |*sect_hdr| if (sect_hdr.getName() == null) return true;
1219 return false;
1220 }
1221
12171222 pub fn getSectionHeaders(self: *const Coff) []align(1) const SectionHeader {
12181223 const coff_header = self.getCoffHeader();
12191224 const offset = self.coff_header_offset + @sizeOf(CoffHeader) + coff_header.size_of_optional_header;
lib/std/debug.zig+109-15
......@@ -887,12 +887,8 @@ pub fn openSelfDebugInfo(allocator: mem.Allocator) OpenSelfDebugInfoError!DebugI
887887 }
888888}
889889
890fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDebugInfo {
890fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebugInfo {
891891 nosuspend {
892 const coff_obj = try allocator.create(coff.Coff);
893 defer allocator.destroy(coff_obj);
894 coff_obj.* = try coff.Coff.init(coff_bytes);
895
896892 var di = ModuleDebugInfo{
897893 .base_address = undefined,
898894 .coff_image_base = coff_obj.getImageBase(),
......@@ -908,9 +904,14 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDe
908904 errdefer for (sections) |section| if (section) |s| if (s.owned) allocator.free(s.data);
909905
910906 inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| {
911 sections[i] = .{
912 .data = try coff_obj.getSectionDataAlloc("." ++ section.name, allocator),
913 .owned = true,
907 sections[i] = if (coff_obj.getSectionDataAlloc("." ++ section.name, allocator)) |data| blk: {
908 break :blk .{
909 .data = data,
910 .owned = true,
911 };
912 } else |err| blk: {
913 if (err == error.MissingCoffSection) break :blk null;
914 return err;
914915 };
915916 }
916917
......@@ -920,7 +921,7 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDe
920921 .is_macho = false,
921922 };
922923
923 try DW.openDwarfDebugInfo(&dwarf, allocator, coff_bytes);
924 try DW.openDwarfDebugInfo(&dwarf, allocator, coff_obj.data);
924925 di.debug_data = PdbOrDwarf{ .dwarf = dwarf };
925926 return di;
926927 }
......@@ -1358,6 +1359,21 @@ pub const WindowsModuleInfo = struct {
13581359 base_address: usize,
13591360 size: u32,
13601361 name: []const u8,
1362 handle: windows.HMODULE,
1363
1364 // Set when the image file needed to be mapped from disk
1365 mapped_file: ?struct {
1366 file: File,
1367 section_handle: windows.HANDLE,
1368 section_view: []const u8,
1369
1370 pub fn deinit(self: @This()) void {
1371 const process_handle = windows.kernel32.GetCurrentProcess();
1372 assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @constCast(@ptrCast(self.section_view.ptr))) == .SUCCESS);
1373 windows.CloseHandle(self.section_handle);
1374 self.file.close();
1375 }
1376 } = null,
13611377};
13621378
13631379pub const DebugInfo = struct {
......@@ -1373,6 +1389,8 @@ pub const DebugInfo = struct {
13731389 };
13741390
13751391 if (native_os == .windows) {
1392 errdefer debug_info.modules.deinit(allocator);
1393
13761394 const handle = windows.kernel32.CreateToolhelp32Snapshot(windows.TH32CS_SNAPMODULE | windows.TH32CS_SNAPMODULE32, 0);
13771395 if (handle == windows.INVALID_HANDLE_VALUE) {
13781396 switch (windows.kernel32.GetLastError()) {
......@@ -1390,9 +1408,16 @@ pub const DebugInfo = struct {
13901408 var module_valid = true;
13911409 while (module_valid) {
13921410 const module_info = try debug_info.modules.addOne(allocator);
1393 module_info.base_address = @intFromPtr(module_entry.modBaseAddr);
1394 module_info.size = module_entry.modBaseSize;
1395 module_info.name = allocator.dupe(u8, mem.sliceTo(&module_entry.szModule, 0)) catch &.{};
1411 const name = allocator.dupe(u8, mem.sliceTo(&module_entry.szModule, 0)) catch &.{};
1412 errdefer allocator.free(name);
1413
1414 module_info.* = .{
1415 .base_address = @intFromPtr(module_entry.modBaseAddr),
1416 .size = module_entry.modBaseSize,
1417 .name = name,
1418 .handle = module_entry.hModule,
1419 };
1420
13961421 module_valid = windows.kernel32.Module32Next(handle, &module_entry) == 1;
13971422 }
13981423 }
......@@ -1411,6 +1436,7 @@ pub const DebugInfo = struct {
14111436 if (native_os == .windows) {
14121437 for (self.modules.items) |module| {
14131438 self.allocator.free(module.name);
1439 if (module.mapped_file) |mapped_file| mapped_file.deinit();
14141440 }
14151441 self.modules.deinit(self.allocator);
14161442 }
......@@ -1500,17 +1526,85 @@ pub const DebugInfo = struct {
15001526 }
15011527
15021528 fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
1503 for (self.modules.items) |module| {
1529 for (self.modules.items) |*module| {
15041530 if (address >= module.base_address and address < module.base_address + module.size) {
15051531 if (self.address_map.get(module.base_address)) |obj_di| {
15061532 return obj_di;
15071533 }
15081534
1509 const mapped_module = @as([*]const u8, @ptrFromInt(module.base_address))[0..module.size];
15101535 const obj_di = try self.allocator.create(ModuleDebugInfo);
15111536 errdefer self.allocator.destroy(obj_di);
15121537
1513 obj_di.* = try readCoffDebugInfo(self.allocator, mapped_module);
1538 const mapped_module = @as([*]const u8, @ptrFromInt(module.base_address))[0..module.size];
1539 var coff_obj = try coff.Coff.init(mapped_module);
1540
1541 // The string table is not mapped into memory by the loader, so if a section name is in the
1542 // string table then we have to map the full image file from disk. This can happen when
1543 // a binary is produced with -gdwarf, since the section names are longer than 8 bytes.
1544 if (coff_obj.strtabRequired()) {
1545 var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined;
1546 // openFileAbsoluteW requires the prefix to be present
1547 mem.copy(u16, name_buffer[0..4], &[_]u16{ '\\', '?', '?', '\\' });
1548
1549 const process_handle = windows.kernel32.GetCurrentProcess();
1550 const len = windows.kernel32.K32GetModuleFileNameExW(
1551 process_handle,
1552 module.handle,
1553 @ptrCast(&name_buffer[4]),
1554 windows.PATH_MAX_WIDE,
1555 );
1556
1557 if (len == 0) return error.MissingDebugInfo;
1558 const coff_file = fs.openFileAbsoluteW(name_buffer[0 .. len + 4 :0], .{}) catch |err| switch (err) {
1559 error.FileNotFound => return error.MissingDebugInfo,
1560 else => return err,
1561 };
1562 errdefer coff_file.close();
1563
1564 var section_handle: windows.HANDLE = undefined;
1565 const create_section_rc = windows.ntdll.NtCreateSection(
1566 &section_handle,
1567 windows.STANDARD_RIGHTS_REQUIRED | windows.SECTION_QUERY | windows.SECTION_MAP_READ,
1568 null,
1569 null,
1570 windows.PAGE_READONLY,
1571 // The documentation states that if no AllocationAttribute is specified, then SEC_COMMIT is the default.
1572 // In practice, this isn't the case and specifying 0 will result in INVALID_PARAMETER_6.
1573 windows.SEC_COMMIT,
1574 coff_file.handle,
1575 );
1576 if (create_section_rc != .SUCCESS) return error.MissingDebugInfo;
1577 errdefer windows.CloseHandle(section_handle);
1578
1579 var coff_len: usize = 0;
1580 var base_ptr: usize = 0;
1581 const map_section_rc = windows.ntdll.NtMapViewOfSection(
1582 section_handle,
1583 process_handle,
1584 @ptrCast(&base_ptr),
1585 null,
1586 0,
1587 null,
1588 &coff_len,
1589 .ViewUnmap,
1590 0,
1591 windows.PAGE_READONLY,
1592 );
1593 if (map_section_rc != .SUCCESS) return error.MissingDebugInfo;
1594 errdefer assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @ptrFromInt(base_ptr)) == .SUCCESS);
1595
1596 const section_view = @as([*]const u8, @ptrFromInt(base_ptr))[0..coff_len];
1597 coff_obj = try coff.Coff.init(section_view);
1598
1599 module.mapped_file = .{
1600 .file = coff_file,
1601 .section_handle = section_handle,
1602 .section_view = section_view,
1603 };
1604 }
1605 errdefer if (module.mapped_file) |mapped_file| mapped_file.deinit();
1606
1607 obj_di.* = try readCoffDebugInfo(self.allocator, &coff_obj);
15141608 obj_di.base_address = module.base_address;
15151609
15161610 try self.address_map.putNoClobber(module.base_address, obj_di);
lib/std/os/windows.zig+29
......@@ -3301,6 +3301,35 @@ pub const REGSAM = ACCESS_MASK;
33013301pub const ACCESS_MASK = DWORD;
33023302pub const LSTATUS = LONG;
33033303
3304pub const SECTION_INHERIT = enum(c_int) {
3305 ViewShare = 0,
3306 ViewUnmap = 1,
3307};
3308
3309pub const SECTION_QUERY = 0x0001;
3310pub const SECTION_MAP_WRITE = 0x0002;
3311pub const SECTION_MAP_READ = 0x0004;
3312pub const SECTION_MAP_EXECUTE = 0x0008;
3313pub const SECTION_EXTEND_SIZE = 0x0010;
3314pub const SECTION_ALL_ACCESS =
3315 STANDARD_RIGHTS_REQUIRED |
3316 SECTION_QUERY |
3317 SECTION_MAP_WRITE |
3318 SECTION_MAP_READ |
3319 SECTION_MAP_EXECUTE |
3320 SECTION_EXTEND_SIZE;
3321
3322pub const SEC_64K_PAGES = 0x80000;
3323pub const SEC_FILE = 0x800000;
3324pub const SEC_IMAGE = 0x1000000;
3325pub const SEC_PROTECTED_IMAGE = 0x2000000;
3326pub const SEC_RESERVE = 0x4000000;
3327pub const SEC_COMMIT = 0x8000000;
3328pub const SEC_IMAGE_NO_EXECUTE = SEC_IMAGE | SEC_NOCACHE;
3329pub const SEC_NOCACHE = 0x10000000;
3330pub const SEC_WRITECOMBINE = 0x40000000;
3331pub const SEC_LARGE_PAGES = 0x80000000;
3332
33043333pub const HKEY = *opaque {};
33053334
33063335pub const HKEY_LOCAL_MACHINE: HKEY = @as(HKEY, @ptrFromInt(0x80000002));
lib/std/os/windows/ntdll.zig+26
......@@ -36,6 +36,7 @@ const THREADINFOCLASS = windows.THREADINFOCLASS;
3636const PROCESSINFOCLASS = windows.PROCESSINFOCLASS;
3737const LPVOID = windows.LPVOID;
3838const LPCVOID = windows.LPCVOID;
39const SECTION_INHERIT = windows.SECTION_INHERIT;
3940
4041pub extern "ntdll" fn NtQueryInformationProcess(
4142 ProcessHandle: HANDLE,
......@@ -125,6 +126,31 @@ pub extern "ntdll" fn NtCreateFile(
125126 EaBuffer: ?*anyopaque,
126127 EaLength: ULONG,
127128) callconv(WINAPI) NTSTATUS;
129pub extern "ntdll" fn NtCreateSection(
130 SectionHandle: *HANDLE,
131 DesiredAccess: ACCESS_MASK,
132 ObjectAttributes: ?*OBJECT_ATTRIBUTES,
133 MaximumSize: ?*LARGE_INTEGER,
134 SectionPageProtection: ULONG,
135 AllocationAttributes: ULONG,
136 FileHandle: ?HANDLE,
137) callconv(WINAPI) NTSTATUS;
138pub extern "ntdll" fn NtMapViewOfSection(
139 SectionHandle: HANDLE,
140 ProcessHandle: HANDLE,
141 BaseAddress: *PVOID,
142 ZeroBits: ?*ULONG,
143 CommitSize: SIZE_T,
144 SectionOffset: ?*LARGE_INTEGER,
145 ViewSize: *SIZE_T,
146 InheritDispostion: SECTION_INHERIT,
147 AllocationType: ULONG,
148 Win32Protect: ULONG,
149) callconv(WINAPI) NTSTATUS;
150pub extern "ntdll" fn NtUnmapViewOfSection(
151 ProcessHandle: HANDLE,
152 BaseAddress: PVOID,
153) callconv(WINAPI) NTSTATUS;
128154pub extern "ntdll" fn NtDeviceIoControlFile(
129155 FileHandle: HANDLE,
130156 Event: ?HANDLE,