| ... | @@ -887,12 +887,8 @@ pub fn openSelfDebugInfo(allocator: mem.Allocator) OpenSelfDebugInfoError!DebugI | ... | @@ -887,12 +887,8 @@ pub fn openSelfDebugInfo(allocator: mem.Allocator) OpenSelfDebugInfoError!DebugI |
| 887 | } | 887 | } |
| 888 | } | 888 | } |
| 889 | | 889 | |
| 890 | fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDebugInfo { | 890 | fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebugInfo { |
| 891 | nosuspend { | 891 | 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 | | | |
| 896 | var di = ModuleDebugInfo{ | 892 | var di = ModuleDebugInfo{ |
| 897 | .base_address = undefined, | 893 | .base_address = undefined, |
| 898 | .coff_image_base = coff_obj.getImageBase(), | 894 | .coff_image_base = coff_obj.getImageBase(), |
| ... | @@ -908,9 +904,14 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDe | ... | @@ -908,9 +904,14 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDe |
| 908 | errdefer for (sections) |section| if (section) |s| if (s.owned) allocator.free(s.data); | 904 | errdefer for (sections) |section| if (section) |s| if (s.owned) allocator.free(s.data); |
| 909 | | 905 | |
| 910 | inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| { | 906 | inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| { |
| 911 | sections[i] = .{ | 907 | sections[i] = if (coff_obj.getSectionDataAlloc("." ++ section.name, allocator)) |data| blk: { |
| 912 | .data = try coff_obj.getSectionDataAlloc("." ++ section.name, allocator), | 908 | break :blk .{ |
| 913 | .owned = true, | 909 | .data = data, |
| | 910 | .owned = true, |
| | 911 | }; |
| | 912 | } else |err| blk: { |
| | 913 | if (err == error.MissingCoffSection) break :blk null; |
| | 914 | return err; |
| 914 | }; | 915 | }; |
| 915 | } | 916 | } |
| 916 | | 917 | |
| ... | @@ -920,7 +921,7 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDe | ... | @@ -920,7 +921,7 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDe |
| 920 | .is_macho = false, | 921 | .is_macho = false, |
| 921 | }; | 922 | }; |
| 922 | | 923 | |
| 923 | try DW.openDwarfDebugInfo(&dwarf, allocator, coff_bytes); | 924 | try DW.openDwarfDebugInfo(&dwarf, allocator, coff_obj.data); |
| 924 | di.debug_data = PdbOrDwarf{ .dwarf = dwarf }; | 925 | di.debug_data = PdbOrDwarf{ .dwarf = dwarf }; |
| 925 | return di; | 926 | return di; |
| 926 | } | 927 | } |
| ... | @@ -1358,6 +1359,21 @@ pub const WindowsModuleInfo = struct { | ... | @@ -1358,6 +1359,21 @@ pub const WindowsModuleInfo = struct { |
| 1358 | base_address: usize, | 1359 | base_address: usize, |
| 1359 | size: u32, | 1360 | size: u32, |
| 1360 | name: []const u8, | 1361 | 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, |
| 1361 | }; | 1377 | }; |
| 1362 | | 1378 | |
| 1363 | pub const DebugInfo = struct { | 1379 | pub const DebugInfo = struct { |
| ... | @@ -1373,6 +1389,8 @@ pub const DebugInfo = struct { | ... | @@ -1373,6 +1389,8 @@ pub const DebugInfo = struct { |
| 1373 | }; | 1389 | }; |
| 1374 | | 1390 | |
| 1375 | if (native_os == .windows) { | 1391 | if (native_os == .windows) { |
| | 1392 | errdefer debug_info.modules.deinit(allocator); |
| | 1393 | |
| 1376 | const handle = windows.kernel32.CreateToolhelp32Snapshot(windows.TH32CS_SNAPMODULE | windows.TH32CS_SNAPMODULE32, 0); | 1394 | const handle = windows.kernel32.CreateToolhelp32Snapshot(windows.TH32CS_SNAPMODULE | windows.TH32CS_SNAPMODULE32, 0); |
| 1377 | if (handle == windows.INVALID_HANDLE_VALUE) { | 1395 | if (handle == windows.INVALID_HANDLE_VALUE) { |
| 1378 | switch (windows.kernel32.GetLastError()) { | 1396 | switch (windows.kernel32.GetLastError()) { |
| ... | @@ -1390,9 +1408,16 @@ pub const DebugInfo = struct { | ... | @@ -1390,9 +1408,16 @@ pub const DebugInfo = struct { |
| 1390 | var module_valid = true; | 1408 | var module_valid = true; |
| 1391 | while (module_valid) { | 1409 | while (module_valid) { |
| 1392 | const module_info = try debug_info.modules.addOne(allocator); | 1410 | const module_info = try debug_info.modules.addOne(allocator); |
| 1393 | module_info.base_address = @intFromPtr(module_entry.modBaseAddr); | 1411 | const name = allocator.dupe(u8, mem.sliceTo(&module_entry.szModule, 0)) catch &.{}; |
| 1394 | module_info.size = module_entry.modBaseSize; | 1412 | errdefer allocator.free(name); |
| 1395 | module_info.name = allocator.dupe(u8, mem.sliceTo(&module_entry.szModule, 0)) catch &.{}; | 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 | |
| 1396 | module_valid = windows.kernel32.Module32Next(handle, &module_entry) == 1; | 1421 | module_valid = windows.kernel32.Module32Next(handle, &module_entry) == 1; |
| 1397 | } | 1422 | } |
| 1398 | } | 1423 | } |
| ... | @@ -1411,6 +1436,7 @@ pub const DebugInfo = struct { | ... | @@ -1411,6 +1436,7 @@ pub const DebugInfo = struct { |
| 1411 | if (native_os == .windows) { | 1436 | if (native_os == .windows) { |
| 1412 | for (self.modules.items) |module| { | 1437 | for (self.modules.items) |module| { |
| 1413 | self.allocator.free(module.name); | 1438 | self.allocator.free(module.name); |
| | 1439 | if (module.mapped_file) |mapped_file| mapped_file.deinit(); |
| 1414 | } | 1440 | } |
| 1415 | self.modules.deinit(self.allocator); | 1441 | self.modules.deinit(self.allocator); |
| 1416 | } | 1442 | } |
| ... | @@ -1500,17 +1526,85 @@ pub const DebugInfo = struct { | ... | @@ -1500,17 +1526,85 @@ pub const DebugInfo = struct { |
| 1500 | } | 1526 | } |
| 1501 | | 1527 | |
| 1502 | fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ModuleDebugInfo { | 1528 | fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ModuleDebugInfo { |
| 1503 | for (self.modules.items) |module| { | 1529 | for (self.modules.items) |*module| { |
| 1504 | if (address >= module.base_address and address < module.base_address + module.size) { | 1530 | if (address >= module.base_address and address < module.base_address + module.size) { |
| 1505 | if (self.address_map.get(module.base_address)) |obj_di| { | 1531 | if (self.address_map.get(module.base_address)) |obj_di| { |
| 1506 | return obj_di; | 1532 | return obj_di; |
| 1507 | } | 1533 | } |
| 1508 | | 1534 | |
| 1509 | const mapped_module = @as([*]const u8, @ptrFromInt(module.base_address))[0..module.size]; | | |
| 1510 | const obj_di = try self.allocator.create(ModuleDebugInfo); | 1535 | const obj_di = try self.allocator.create(ModuleDebugInfo); |
| 1511 | errdefer self.allocator.destroy(obj_di); | 1536 | errdefer self.allocator.destroy(obj_di); |
| 1512 | | 1537 | |
| 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); |
| 1514 | obj_di.base_address = module.base_address; | 1608 | obj_di.base_address = module.base_address; |
| 1515 | | 1609 | |
| 1516 | try self.address_map.putNoClobber(module.base_address, obj_di); | 1610 | try self.address_map.putNoClobber(module.base_address, obj_di); |