diff options
| author | kcbanner <kcbanner@gmail.com> | 2022-09-24 21:50:15 -0400 |
|---|---|---|
| committer | kcbanner <kcbanner@gmail.com> | 2023-01-08 20:28:42 -0500 |
| commit | 461fb499f3cff9038a427eae120fb34defc9ab38 (patch) | |
| tree | 85ca72253c7382fb98597f82a1dddb0766c8fecc /lib/std/debug.zig | |
| parent | fcee1bf9930ad48300863d1b85b1f0a7ee924fbc (diff) | |
| download | zig-461fb499f3cff9038a427eae120fb34defc9ab38.tar.gz zig-461fb499f3cff9038a427eae120fb34defc9ab38.zip | |
windows: rework DebugInfo to use less file operations and fix some memory management issues
Diffstat (limited to 'lib/std/debug.zig')
| -rw-r--r-- | lib/std/debug.zig | 228 |
1 files changed, 116 insertions, 112 deletions
diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 983d088374..55072b6363 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -811,7 +811,7 @@ fn printLineInfo( pub const OpenSelfDebugInfoError = error{ MissingDebugInfo, UnsupportedOperatingSystem, -}; +} || @typeInfo(@typeInfo(@TypeOf(DebugInfo.init)).Fn.return_type.?).ErrorUnion.error_set; pub fn openSelfDebugInfo(allocator: mem.Allocator) OpenSelfDebugInfoError!DebugInfo { nosuspend { @@ -827,60 +827,56 @@ pub fn openSelfDebugInfo(allocator: mem.Allocator) OpenSelfDebugInfoError!DebugI .dragonfly, .openbsd, .macos, - .windows, .solaris, - => return DebugInfo.init(allocator), + .windows, + => return try DebugInfo.init(allocator), else => return error.UnsupportedOperatingSystem, } } } -/// This takes ownership of coff_file: users of this function should not close -/// it themselves, even on error. -/// TODO it's weird to take ownership even on error, rework this code. -fn readCoffDebugInfo(allocator: mem.Allocator, coff_file: File) !ModuleDebugInfo { +fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDebugInfo { nosuspend { - defer coff_file.close(); - const coff_obj = try allocator.create(coff.Coff); errdefer allocator.destroy(coff_obj); - coff_obj.* = .{ .allocator = allocator }; + coff_obj.* = try coff.Coff.init(coff_bytes); var di = ModuleDebugInfo{ .base_address = undefined, - .coff = coff_obj, + .coff_image_base = coff_obj.getImageBase(), + .coff_section_headers = undefined, .debug_data = undefined, }; - // TODO convert to Windows' memory-mapped file API - const file_len = math.cast(usize, try coff_file.getEndPos()) orelse math.maxInt(usize); - const data = try coff_file.readToEndAlloc(allocator, file_len); - try di.coff.parse(data); - - if (di.coff.getSectionByName(".debug_info")) |sec| { + if (coff_obj.getSectionByName(".debug_info")) |sec| { // This coff file has embedded DWARF debug info _ = sec; - // TODO: free the section data slices - const debug_info = di.coff.getSectionDataAlloc(".debug_info", allocator) catch null; - const debug_abbrev = di.coff.getSectionDataAlloc(".debug_abbrev", allocator) catch null; - const debug_str = di.coff.getSectionDataAlloc(".debug_str", allocator) catch null; - const debug_str_offsets = di.coff.getSectionDataAlloc(".debug_str_offsets", allocator) catch null; - const debug_line = di.coff.getSectionDataAlloc(".debug_line", allocator) catch null; - const debug_line_str = di.coff.getSectionDataAlloc(".debug_line_str", allocator) catch null; - const debug_ranges = di.coff.getSectionDataAlloc(".debug_ranges", allocator) catch null; - const debug_loclists = di.coff.getSectionDataAlloc(".debug_loclists", allocator) catch null; - const debug_rnglists = di.coff.getSectionDataAlloc(".debug_rnglists", allocator) catch null; - const debug_addr = di.coff.getSectionDataAlloc(".debug_addr", allocator) catch null; - const debug_names = di.coff.getSectionDataAlloc(".debug_names", allocator) catch null; - const debug_frame = di.coff.getSectionDataAlloc(".debug_frame", allocator) catch null; + + const debug_info = coff_obj.getSectionDataAlloc(".debug_info", allocator) catch return error.MissingDebugInfo; + errdefer allocator.free(debug_info); + const debug_abbrev = coff_obj.getSectionDataAlloc(".debug_abbrev", allocator) catch return error.MissingDebugInfo; + errdefer allocator.free(debug_abbrev); + const debug_str = coff_obj.getSectionDataAlloc(".debug_str", allocator) catch return error.MissingDebugInfo; + errdefer allocator.free(debug_str); + const debug_line = coff_obj.getSectionDataAlloc(".debug_line", allocator) catch return error.MissingDebugInfo; + errdefer allocator.free(debug_line); + + const debug_str_offsets = coff_obj.getSectionDataAlloc(".debug_str_offsets", allocator) catch null; + const debug_line_str = coff_obj.getSectionDataAlloc(".debug_line_str", allocator) catch null; + const debug_ranges = coff_obj.getSectionDataAlloc(".debug_ranges", allocator) catch null; + const debug_loclists = coff_obj.getSectionDataAlloc(".debug_loclists", allocator) catch null; + const debug_rnglists = coff_obj.getSectionDataAlloc(".debug_rnglists", allocator) catch null; + const debug_addr = coff_obj.getSectionDataAlloc(".debug_addr", allocator) catch null; + const debug_names = coff_obj.getSectionDataAlloc(".debug_names", allocator) catch null; + const debug_frame = coff_obj.getSectionDataAlloc(".debug_frame", allocator) catch null; var dwarf = DW.DwarfInfo{ .endian = native_endian, - .debug_info = debug_info orelse return error.MissingDebugInfo, - .debug_abbrev = debug_abbrev orelse return error.MissingDebugInfo, - .debug_str = debug_str orelse return error.MissingDebugInfo, + .debug_info = debug_info, + .debug_abbrev = debug_abbrev, + .debug_str = debug_str, .debug_str_offsets = debug_str_offsets, - .debug_line = debug_line orelse return error.MissingDebugInfo, + .debug_line = debug_line, .debug_line_str = debug_line_str, .debug_ranges = debug_ranges, .debug_loclists = debug_loclists, @@ -889,13 +885,28 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_file: File) !ModuleDebugInfo .debug_names = debug_names, .debug_frame = debug_frame, }; - try DW.openDwarfDebugInfo(&dwarf, allocator); + + DW.openDwarfDebugInfo(&dwarf, allocator) catch |err| { + if (debug_str_offsets) |d| allocator.free(d); + if (debug_line_str) |d| allocator.free(d); + if (debug_ranges) |d| allocator.free(d); + if (debug_loclists) |d| allocator.free(d); + if (debug_rnglists) |d| allocator.free(d); + if (debug_addr) |d| allocator.free(d); + if (debug_names) |d| allocator.free(d); + if (debug_frame) |d| allocator.free(d); + return err; + }; + di.debug_data = PdbOrDwarf{ .dwarf = dwarf }; return di; } + // Only used by pdb path + di.coff_section_headers = try coff_obj.getSectionHeadersAlloc(allocator); + var path_buf: [windows.MAX_PATH]u8 = undefined; - const len = try di.coff.getPdbPath(path_buf[0..]); + const len = try coff_obj.getPdbPath(path_buf[0..]); const raw_path = path_buf[0..len]; const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path}); @@ -909,7 +920,7 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_file: File) !ModuleDebugInfo try di.debug_data.pdb.parseInfoStream(); try di.debug_data.pdb.parseDbiStream(); - if (!mem.eql(u8, &di.coff.guid, &di.debug_data.pdb.guid) or di.coff.age != di.debug_data.pdb.age) + if (!mem.eql(u8, &coff_obj.guid, &di.debug_data.pdb.guid) or coff_obj.age != di.debug_data.pdb.age) return error.InvalidDebugInfo; return di; @@ -1225,15 +1236,49 @@ fn mapWholeFile(file: File) ![]align(mem.page_size) const u8 { } } +pub const ModuleInfo = struct { + base_address: usize, + size: u32, +}; + pub const DebugInfo = struct { allocator: mem.Allocator, address_map: std.AutoHashMap(usize, *ModuleDebugInfo), + modules: if (native_os == .windows) std.ArrayListUnmanaged(ModuleInfo) else void, - pub fn init(allocator: mem.Allocator) DebugInfo { - return DebugInfo{ + pub fn init(allocator: mem.Allocator) !DebugInfo { + var debug_info = DebugInfo{ .allocator = allocator, .address_map = std.AutoHashMap(usize, *ModuleDebugInfo).init(allocator), + .modules = if (native_os == .windows) .{} else {}, }; + + if (native_os == .windows) { + const handle = windows.kernel32.CreateToolhelp32Snapshot(windows.TH32CS_SNAPMODULE | windows.TH32CS_SNAPMODULE32, 0); + if (handle == windows.INVALID_HANDLE_VALUE) { + switch (windows.kernel32.GetLastError()) { + else => |err| return windows.unexpectedError(err), + } + } + + defer windows.CloseHandle(handle); + + var module_entry: windows.MODULEENTRY32 = undefined; + module_entry.dwSize = @sizeOf(windows.MODULEENTRY32); + if (windows.kernel32.Module32First(handle, &module_entry) == 0) { + return error.MissingDebugInfo; + } + + var module_valid = true; + while (module_valid) { + const module_info = try debug_info.modules.addOne(allocator); + module_info.base_address = @ptrToInt(module_entry.modBaseAddr); + module_info.size = module_entry.modBaseSize; + module_valid = windows.kernel32.Module32Next(handle, &module_entry) == 1; + } + } + + return debug_info; } pub fn deinit(self: *DebugInfo) void { @@ -1322,79 +1367,20 @@ pub const DebugInfo = struct { } fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ModuleDebugInfo { - const process_handle = windows.kernel32.GetCurrentProcess(); - - // Find how many modules are actually loaded - var dummy: windows.HMODULE = undefined; - var bytes_needed: windows.DWORD = undefined; - if (windows.kernel32.K32EnumProcessModules( - process_handle, - @ptrCast([*]windows.HMODULE, &dummy), - 0, - &bytes_needed, - ) == 0) - return error.MissingDebugInfo; - - const needed_modules = bytes_needed / @sizeOf(windows.HMODULE); - - // Fetch the complete module list - var modules = try self.allocator.alloc(windows.HMODULE, needed_modules); - defer self.allocator.free(modules); - if (windows.kernel32.K32EnumProcessModules( - process_handle, - modules.ptr, - math.cast(windows.DWORD, modules.len * @sizeOf(windows.HMODULE)) orelse return error.Overflow, - &bytes_needed, - ) == 0) - return error.MissingDebugInfo; - - // There's an unavoidable TOCTOU problem here, the module list may have - // changed between the two EnumProcessModules call. - // Pick the smallest amount of elements to avoid processing garbage. - const needed_modules_after = bytes_needed / @sizeOf(windows.HMODULE); - const loaded_modules = math.min(needed_modules, needed_modules_after); - - for (modules[0..loaded_modules]) |module| { - var info: windows.MODULEINFO = undefined; - if (windows.kernel32.K32GetModuleInformation( - process_handle, - module, - &info, - @sizeOf(@TypeOf(info)), - ) == 0) - return error.MissingDebugInfo; - - const seg_start = @ptrToInt(info.lpBaseOfDll); - const seg_end = seg_start + info.SizeOfImage; - - if (address >= seg_start and address < seg_end) { - if (self.address_map.get(seg_start)) |obj_di| { + 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; } - 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 len = windows.kernel32.K32GetModuleFileNameExW( - process_handle, - module, - @ptrCast(windows.LPWSTR, &name_buffer[4]), - windows.PATH_MAX_WIDE, - ); - assert(len > 0); - + const mapped_module = @intToPtr([*]const u8, module.base_address)[0..module.size]; const obj_di = try self.allocator.create(ModuleDebugInfo); errdefer self.allocator.destroy(obj_di); - const coff_file = fs.openFileAbsoluteW(name_buffer[0 .. len + 4 :0], .{}) catch |err| switch (err) { - error.FileNotFound => return error.MissingDebugInfo, - else => return err, - }; - obj_di.* = try readCoffDebugInfo(self.allocator, coff_file); - obj_di.base_address = seg_start; - - try self.address_map.putNoClobber(seg_start, obj_di); + obj_di.* = try readCoffDebugInfo(self.allocator, mapped_module); + obj_di.base_address = module.base_address; + try self.address_map.putNoClobber(module.base_address, obj_di); return obj_di; } } @@ -1727,12 +1713,31 @@ pub const ModuleDebugInfo = switch (native_os) { .uefi, .windows => struct { base_address: usize, debug_data: PdbOrDwarf, - coff: *coff.Coff, + coff_image_base: u64, + coff_section_headers: []coff.SectionHeader, fn deinit(self: *@This(), allocator: mem.Allocator) void { + switch (self.debug_data) { + .dwarf => |*dwarf| { + allocator.free(dwarf.debug_info); + allocator.free(dwarf.debug_abbrev); + allocator.free(dwarf.debug_str); + allocator.free(dwarf.debug_line); + if (dwarf.debug_str_offsets) |d| allocator.free(d); + if (dwarf.debug_line_str) |d| allocator.free(d); + if (dwarf.debug_ranges) |d| allocator.free(d); + if (dwarf.debug_loclists) |d| allocator.free(d); + if (dwarf.debug_rnglists) |d| allocator.free(d); + if (dwarf.debug_addr) |d| allocator.free(d); + if (dwarf.debug_names) |d| allocator.free(d); + if (dwarf.debug_frame) |d| allocator.free(d); + }, + .pdb => { + allocator.free(self.coff_section_headers); + }, + } + self.debug_data.deinit(allocator); - self.coff.deinit(); - allocator.destroy(self.coff); } pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo { @@ -1741,7 +1746,7 @@ pub const ModuleDebugInfo = switch (native_os) { switch (self.debug_data) { .dwarf => |*dwarf| { - const dwarf_address = relocated_address + self.coff.getImageBase(); + const dwarf_address = relocated_address + self.coff_image_base; return getSymbolFromDwarf(allocator, dwarf_address, dwarf); }, .pdb => { @@ -1751,10 +1756,9 @@ pub const ModuleDebugInfo = switch (native_os) { var coff_section: *align(1) const coff.SectionHeader = undefined; const mod_index = for (self.debug_data.pdb.sect_contribs) |sect_contrib| { - const sections = self.coff.getSectionHeaders(); - if (sect_contrib.Section > sections.len) continue; + if (sect_contrib.Section > self.coff_section_headers.len) continue; // Remember that SectionContribEntry.Section is 1-based. - coff_section = §ions[sect_contrib.Section - 1]; + coff_section = &self.coff_section_headers[sect_contrib.Section - 1]; const vaddr_start = coff_section.virtual_address + sect_contrib.Offset; const vaddr_end = vaddr_start + sect_contrib.Size; |
