aboutsummaryrefslogtreecommitdiff
path: root/lib/std/debug.zig
diff options
context:
space:
mode:
authorkcbanner <kcbanner@gmail.com>2022-09-24 21:50:15 -0400
committerkcbanner <kcbanner@gmail.com>2023-01-08 20:28:42 -0500
commit461fb499f3cff9038a427eae120fb34defc9ab38 (patch)
tree85ca72253c7382fb98597f82a1dddb0766c8fecc /lib/std/debug.zig
parentfcee1bf9930ad48300863d1b85b1f0a7ee924fbc (diff)
downloadzig-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.zig228
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 = &sections[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;