mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 06:15:21 +00:00
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
This commit is contained in:
parent
caa334712f
commit
f991b9dc05
@ -1214,6 +1214,11 @@ pub const Coff = struct {
|
||||
return Strtab{ .buffer = self.data[offset..][0..size] };
|
||||
}
|
||||
|
||||
pub fn strtabRequired(self: *const Coff) bool {
|
||||
for (self.getSectionHeaders()) |*sect_hdr| if (sect_hdr.getName() == null) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn getSectionHeaders(self: *const Coff) []align(1) const SectionHeader {
|
||||
const coff_header = self.getCoffHeader();
|
||||
const offset = self.coff_header_offset + @sizeOf(CoffHeader) + coff_header.size_of_optional_header;
|
||||
|
||||
@ -887,12 +887,8 @@ pub fn openSelfDebugInfo(allocator: mem.Allocator) OpenSelfDebugInfoError!DebugI
|
||||
}
|
||||
}
|
||||
|
||||
fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDebugInfo {
|
||||
fn readCoffDebugInfo(allocator: mem.Allocator, coff_obj: *coff.Coff) !ModuleDebugInfo {
|
||||
nosuspend {
|
||||
const coff_obj = try allocator.create(coff.Coff);
|
||||
defer allocator.destroy(coff_obj);
|
||||
coff_obj.* = try coff.Coff.init(coff_bytes);
|
||||
|
||||
var di = ModuleDebugInfo{
|
||||
.base_address = undefined,
|
||||
.coff_image_base = coff_obj.getImageBase(),
|
||||
@ -908,9 +904,14 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDe
|
||||
errdefer for (sections) |section| if (section) |s| if (s.owned) allocator.free(s.data);
|
||||
|
||||
inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| {
|
||||
sections[i] = .{
|
||||
.data = try coff_obj.getSectionDataAlloc("." ++ section.name, allocator),
|
||||
.owned = true,
|
||||
sections[i] = if (coff_obj.getSectionDataAlloc("." ++ section.name, allocator)) |data| blk: {
|
||||
break :blk .{
|
||||
.data = data,
|
||||
.owned = true,
|
||||
};
|
||||
} else |err| blk: {
|
||||
if (err == error.MissingCoffSection) break :blk null;
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
||||
@ -920,7 +921,7 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDe
|
||||
.is_macho = false,
|
||||
};
|
||||
|
||||
try DW.openDwarfDebugInfo(&dwarf, allocator, coff_bytes);
|
||||
try DW.openDwarfDebugInfo(&dwarf, allocator, coff_obj.data);
|
||||
di.debug_data = PdbOrDwarf{ .dwarf = dwarf };
|
||||
return di;
|
||||
}
|
||||
@ -1358,6 +1359,21 @@ pub const WindowsModuleInfo = struct {
|
||||
base_address: usize,
|
||||
size: u32,
|
||||
name: []const u8,
|
||||
handle: windows.HMODULE,
|
||||
|
||||
// Set when the image file needed to be mapped from disk
|
||||
mapped_file: ?struct {
|
||||
file: File,
|
||||
section_handle: windows.HANDLE,
|
||||
section_view: []const u8,
|
||||
|
||||
pub fn deinit(self: @This()) void {
|
||||
const process_handle = windows.kernel32.GetCurrentProcess();
|
||||
assert(windows.ntdll.NtUnmapViewOfSection(process_handle, @constCast(@ptrCast(self.section_view.ptr))) == .SUCCESS);
|
||||
windows.CloseHandle(self.section_handle);
|
||||
self.file.close();
|
||||
}
|
||||
} = null,
|
||||
};
|
||||
|
||||
pub const DebugInfo = struct {
|
||||
@ -1373,6 +1389,8 @@ pub const DebugInfo = struct {
|
||||
};
|
||||
|
||||
if (native_os == .windows) {
|
||||
errdefer debug_info.modules.deinit(allocator);
|
||||
|
||||
const handle = windows.kernel32.CreateToolhelp32Snapshot(windows.TH32CS_SNAPMODULE | windows.TH32CS_SNAPMODULE32, 0);
|
||||
if (handle == windows.INVALID_HANDLE_VALUE) {
|
||||
switch (windows.kernel32.GetLastError()) {
|
||||
@ -1390,9 +1408,16 @@ pub const DebugInfo = struct {
|
||||
var module_valid = true;
|
||||
while (module_valid) {
|
||||
const module_info = try debug_info.modules.addOne(allocator);
|
||||
module_info.base_address = @intFromPtr(module_entry.modBaseAddr);
|
||||
module_info.size = module_entry.modBaseSize;
|
||||
module_info.name = allocator.dupe(u8, mem.sliceTo(&module_entry.szModule, 0)) catch &.{};
|
||||
const name = allocator.dupe(u8, mem.sliceTo(&module_entry.szModule, 0)) catch &.{};
|
||||
errdefer allocator.free(name);
|
||||
|
||||
module_info.* = .{
|
||||
.base_address = @intFromPtr(module_entry.modBaseAddr),
|
||||
.size = module_entry.modBaseSize,
|
||||
.name = name,
|
||||
.handle = module_entry.hModule,
|
||||
};
|
||||
|
||||
module_valid = windows.kernel32.Module32Next(handle, &module_entry) == 1;
|
||||
}
|
||||
}
|
||||
@ -1411,6 +1436,7 @@ pub const DebugInfo = struct {
|
||||
if (native_os == .windows) {
|
||||
for (self.modules.items) |module| {
|
||||
self.allocator.free(module.name);
|
||||
if (module.mapped_file) |mapped_file| mapped_file.deinit();
|
||||
}
|
||||
self.modules.deinit(self.allocator);
|
||||
}
|
||||
@ -1500,17 +1526,85 @@ pub const DebugInfo = struct {
|
||||
}
|
||||
|
||||
fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
|
||||
for (self.modules.items) |module| {
|
||||
for (self.modules.items) |*module| {
|
||||
if (address >= module.base_address and address < module.base_address + module.size) {
|
||||
if (self.address_map.get(module.base_address)) |obj_di| {
|
||||
return obj_di;
|
||||
}
|
||||
|
||||
const mapped_module = @as([*]const u8, @ptrFromInt(module.base_address))[0..module.size];
|
||||
const obj_di = try self.allocator.create(ModuleDebugInfo);
|
||||
errdefer self.allocator.destroy(obj_di);
|
||||
|
||||
obj_di.* = try readCoffDebugInfo(self.allocator, mapped_module);
|
||||
const mapped_module = @as([*]const u8, @ptrFromInt(module.base_address))[0..module.size];
|
||||
var coff_obj = try coff.Coff.init(mapped_module);
|
||||
|
||||
// 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
|
||||
// a binary is produced with -gdwarf, since the section names are longer than 8 bytes.
|
||||
if (coff_obj.strtabRequired()) {
|
||||
var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined;
|
||||
// openFileAbsoluteW requires the prefix to be present
|
||||
mem.copy(u16, name_buffer[0..4], &[_]u16{ '\\', '?', '?', '\\' });
|
||||
|
||||
const process_handle = windows.kernel32.GetCurrentProcess();
|
||||
const len = windows.kernel32.K32GetModuleFileNameExW(
|
||||
process_handle,
|
||||
module.handle,
|
||||
@ptrCast(&name_buffer[4]),
|
||||
windows.PATH_MAX_WIDE,
|
||||
);
|
||||
|
||||
if (len == 0) return error.MissingDebugInfo;
|
||||
const coff_file = fs.openFileAbsoluteW(name_buffer[0 .. len + 4 :0], .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => return error.MissingDebugInfo,
|
||||
else => return err,
|
||||
};
|
||||
errdefer coff_file.close();
|
||||
|
||||
var section_handle: windows.HANDLE = undefined;
|
||||
const create_section_rc = windows.ntdll.NtCreateSection(
|
||||
§ion_handle,
|
||||
windows.STANDARD_RIGHTS_REQUIRED | windows.SECTION_QUERY | windows.SECTION_MAP_READ,
|
||||
null,
|
||||
null,
|
||||
windows.PAGE_READONLY,
|
||||
// The documentation states that if no AllocationAttribute is specified, then SEC_COMMIT is the default.
|
||||
// In practice, this isn't the case and specifying 0 will result in INVALID_PARAMETER_6.
|
||||
windows.SEC_COMMIT,
|
||||
coff_file.handle,
|
||||
);
|
||||
if (create_section_rc != .SUCCESS) return error.MissingDebugInfo;
|
||||
errdefer windows.CloseHandle(section_handle);
|
||||
|
||||
var coff_len: usize = 0;
|
||||
var base_ptr: usize = 0;
|
||||
const map_section_rc = windows.ntdll.NtMapViewOfSection(
|
||||
section_handle,
|
||||
process_handle,
|
||||
@ptrCast(&base_ptr),
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
&coff_len,
|
||||
.ViewUnmap,
|
||||
0,
|
||||
windows.PAGE_READONLY,
|
||||
);
|
||||
if (map_section_rc != .SUCCESS) return error.MissingDebugInfo;
|
||||
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);
|
||||
|
||||
module.mapped_file = .{
|
||||
.file = coff_file,
|
||||
.section_handle = section_handle,
|
||||
.section_view = section_view,
|
||||
};
|
||||
}
|
||||
errdefer if (module.mapped_file) |mapped_file| mapped_file.deinit();
|
||||
|
||||
obj_di.* = try readCoffDebugInfo(self.allocator, &coff_obj);
|
||||
obj_di.base_address = module.base_address;
|
||||
|
||||
try self.address_map.putNoClobber(module.base_address, obj_di);
|
||||
|
||||
@ -3301,6 +3301,35 @@ pub const REGSAM = ACCESS_MASK;
|
||||
pub const ACCESS_MASK = DWORD;
|
||||
pub const LSTATUS = LONG;
|
||||
|
||||
pub const SECTION_INHERIT = enum(c_int) {
|
||||
ViewShare = 0,
|
||||
ViewUnmap = 1,
|
||||
};
|
||||
|
||||
pub const SECTION_QUERY = 0x0001;
|
||||
pub const SECTION_MAP_WRITE = 0x0002;
|
||||
pub const SECTION_MAP_READ = 0x0004;
|
||||
pub const SECTION_MAP_EXECUTE = 0x0008;
|
||||
pub const SECTION_EXTEND_SIZE = 0x0010;
|
||||
pub const SECTION_ALL_ACCESS =
|
||||
STANDARD_RIGHTS_REQUIRED |
|
||||
SECTION_QUERY |
|
||||
SECTION_MAP_WRITE |
|
||||
SECTION_MAP_READ |
|
||||
SECTION_MAP_EXECUTE |
|
||||
SECTION_EXTEND_SIZE;
|
||||
|
||||
pub const SEC_64K_PAGES = 0x80000;
|
||||
pub const SEC_FILE = 0x800000;
|
||||
pub const SEC_IMAGE = 0x1000000;
|
||||
pub const SEC_PROTECTED_IMAGE = 0x2000000;
|
||||
pub const SEC_RESERVE = 0x4000000;
|
||||
pub const SEC_COMMIT = 0x8000000;
|
||||
pub const SEC_IMAGE_NO_EXECUTE = SEC_IMAGE | SEC_NOCACHE;
|
||||
pub const SEC_NOCACHE = 0x10000000;
|
||||
pub const SEC_WRITECOMBINE = 0x40000000;
|
||||
pub const SEC_LARGE_PAGES = 0x80000000;
|
||||
|
||||
pub const HKEY = *opaque {};
|
||||
|
||||
pub const HKEY_LOCAL_MACHINE: HKEY = @as(HKEY, @ptrFromInt(0x80000002));
|
||||
|
||||
@ -36,6 +36,7 @@ const THREADINFOCLASS = windows.THREADINFOCLASS;
|
||||
const PROCESSINFOCLASS = windows.PROCESSINFOCLASS;
|
||||
const LPVOID = windows.LPVOID;
|
||||
const LPCVOID = windows.LPCVOID;
|
||||
const SECTION_INHERIT = windows.SECTION_INHERIT;
|
||||
|
||||
pub extern "ntdll" fn NtQueryInformationProcess(
|
||||
ProcessHandle: HANDLE,
|
||||
@ -125,6 +126,31 @@ pub extern "ntdll" fn NtCreateFile(
|
||||
EaBuffer: ?*anyopaque,
|
||||
EaLength: ULONG,
|
||||
) callconv(WINAPI) NTSTATUS;
|
||||
pub extern "ntdll" fn NtCreateSection(
|
||||
SectionHandle: *HANDLE,
|
||||
DesiredAccess: ACCESS_MASK,
|
||||
ObjectAttributes: ?*OBJECT_ATTRIBUTES,
|
||||
MaximumSize: ?*LARGE_INTEGER,
|
||||
SectionPageProtection: ULONG,
|
||||
AllocationAttributes: ULONG,
|
||||
FileHandle: ?HANDLE,
|
||||
) callconv(WINAPI) NTSTATUS;
|
||||
pub extern "ntdll" fn NtMapViewOfSection(
|
||||
SectionHandle: HANDLE,
|
||||
ProcessHandle: HANDLE,
|
||||
BaseAddress: *PVOID,
|
||||
ZeroBits: ?*ULONG,
|
||||
CommitSize: SIZE_T,
|
||||
SectionOffset: ?*LARGE_INTEGER,
|
||||
ViewSize: *SIZE_T,
|
||||
InheritDispostion: SECTION_INHERIT,
|
||||
AllocationType: ULONG,
|
||||
Win32Protect: ULONG,
|
||||
) callconv(WINAPI) NTSTATUS;
|
||||
pub extern "ntdll" fn NtUnmapViewOfSection(
|
||||
ProcessHandle: HANDLE,
|
||||
BaseAddress: PVOID,
|
||||
) callconv(WINAPI) NTSTATUS;
|
||||
pub extern "ntdll" fn NtDeviceIoControlFile(
|
||||
FileHandle: HANDLE,
|
||||
Event: ?HANDLE,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user