Commit 84b65860cf
Changed files (5)
lib
std
lib/std/debug/Dwarf/Unwind.zig
@@ -41,8 +41,6 @@ const SortedFdeEntry = struct {
const Section = enum { debug_frame, eh_frame };
-// MLUGG TODO deinit?
-
/// Initialize with unwind information from the contents of a `.debug_frame` or `.eh_frame` section.
///
/// If the `.eh_frame_hdr` section is available, consider instead using `initEhFrameHdr`. This
@@ -78,6 +76,13 @@ pub fn initEhFrameHdr(header: EhFrameHeader, section_vaddr: u64, section_bytes_p
};
}
+pub fn deinit(unwind: *Unwind, gpa: Allocator) void {
+ if (unwind.lookup) |lookup| switch (lookup) {
+ .eh_frame_hdr => {},
+ .sorted_fdes => |fdes| gpa.free(fdes),
+ };
+}
+
/// This represents the decoded .eh_frame_hdr header
pub const EhFrameHeader = struct {
eh_frame_vaddr: u64,
@@ -205,8 +210,6 @@ pub const EntryHeader = union(enum) {
const unit_header = try Dwarf.readUnitHeader(r, endian);
if (unit_header.unit_length == 0) return .terminator;
- // TODO MLUGG: seriously, just... check the formats of everything in BOTH LSB Core and DWARF. this is a fucking *mess*. maybe add spec references.
-
// Next is a value which will disambiguate CIEs and FDEs. Annoyingly, LSB Core makes this
// value always 4-byte, whereas DWARF makes it depend on the `dwarf.Format`.
const cie_ptr_or_id_size: u8 = switch (section) {
lib/std/debug/Dwarf.zig
@@ -1487,20 +1487,42 @@ pub const ElfModule = struct {
MemoryMappingNotSupported,
} || Allocator.Error || std.fs.File.OpenError || OpenError;
- /// Reads debug info from an already mapped ELF file.
+ /// Reads debug info from an ELF file given its path.
///
/// If the required sections aren't present but a reference to external debug
/// info is, then this this function will recurse to attempt to load the debug
/// sections from an external file.
pub fn load(
gpa: Allocator,
- mapped_mem: []align(std.heap.page_size_min) const u8,
+ elf_file_path: Path,
build_id: ?[]const u8,
expected_crc: ?u32,
parent_sections: ?*Dwarf.SectionArray,
parent_mapped_mem: ?[]align(std.heap.page_size_min) const u8,
- elf_filename: ?[]const u8,
) LoadError!ElfModule {
+ const mapped_mem: []align(std.heap.page_size_min) const u8 = mapped: {
+ const elf_file = try elf_file_path.root_dir.handle.openFile(elf_file_path.sub_path, .{});
+ defer elf_file.close();
+
+ const file_len = cast(
+ usize,
+ elf_file.getEndPos() catch return bad(),
+ ) orelse return error.Overflow;
+
+ break :mapped std.posix.mmap(
+ null,
+ file_len,
+ std.posix.PROT.READ,
+ .{ .TYPE = .SHARED },
+ elf_file.handle,
+ 0,
+ ) catch |err| switch (err) {
+ error.MappingAlreadyExists => unreachable,
+ else => |e| return e,
+ };
+ };
+ errdefer std.posix.munmap(mapped_mem);
+
if (expected_crc) |crc| if (crc != std.hash.crc.Crc32.hash(mapped_mem)) return error.InvalidDebugInfo;
const hdr: *const elf.Ehdr = @ptrCast(&mapped_mem[0]);
@@ -1606,39 +1628,36 @@ pub const ElfModule = struct {
// $XDG_CACHE_HOME/debuginfod_client/<buildid>/debuginfo
// This only opportunisticly tries to load from the debuginfod cache, but doesn't try to populate it.
// One can manually run `debuginfod-find debuginfo PATH` to download the symbols
- if (build_id) |id| blk: {
- var debuginfod_dir: std.fs.Dir = switch (builtin.os.tag) {
- .wasi, .windows => break :blk,
- else => dir: {
- if (std.posix.getenv("DEBUGINFOD_CACHE_PATH")) |path| {
- break :dir std.fs.openDirAbsolute(path, .{}) catch break :blk;
- }
- if (std.posix.getenv("XDG_CACHE_HOME")) |cache_path| {
- if (cache_path.len > 0) {
- const path = std.fs.path.join(gpa, &[_][]const u8{ cache_path, "debuginfod_client" }) catch break :blk;
- defer gpa.free(path);
- break :dir std.fs.openDirAbsolute(path, .{}) catch break :blk;
- }
- }
- if (std.posix.getenv("HOME")) |home_path| {
- const path = std.fs.path.join(gpa, &[_][]const u8{ home_path, ".cache", "debuginfod_client" }) catch break :blk;
- defer gpa.free(path);
- break :dir std.fs.openDirAbsolute(path, .{}) catch break :blk;
+ debuginfod: {
+ const id = build_id orelse break :debuginfod;
+ switch (builtin.os.tag) {
+ .wasi, .windows => break :debuginfod,
+ else => {},
+ }
+ const id_dir_path: []u8 = p: {
+ if (std.posix.getenv("DEBUGINFOD_CACHE_PATH")) |path| {
+ break :p try std.fmt.allocPrint(gpa, "{s}/{x}", .{ path, id });
+ }
+ if (std.posix.getenv("XDG_CACHE_HOME")) |cache_path| {
+ if (cache_path.len > 0) {
+ break :p try std.fmt.allocPrint(gpa, "{s}/debuginfod_client/{x}", .{ cache_path, id });
}
- break :blk;
- },
+ }
+ if (std.posix.getenv("HOME")) |home_path| {
+ break :p try std.fmt.allocPrint(gpa, "{s}/.cache/debuginfod_client/{x}", .{ home_path, id });
+ }
+ break :debuginfod;
};
- defer debuginfod_dir.close();
-
- const filename = std.fmt.allocPrint(gpa, "{x}/debuginfo", .{id}) catch break :blk;
- defer gpa.free(filename);
+ defer gpa.free(id_dir_path);
+ if (!std.fs.path.isAbsolute(id_dir_path)) break :debuginfod;
- const path: Path = .{
- .root_dir = .{ .path = null, .handle = debuginfod_dir },
- .sub_path = filename,
- };
+ var id_dir = std.fs.openDirAbsolute(id_dir_path, .{}) catch break :debuginfod;
+ defer id_dir.close();
- return loadPath(gpa, path, null, separate_debug_crc, §ions, mapped_mem) catch break :blk;
+ return load(gpa, .{
+ .root_dir = .{ .path = id_dir_path, .handle = id_dir },
+ .sub_path = "debuginfo",
+ }, null, separate_debug_crc, §ions, mapped_mem) catch break :debuginfod;
}
const global_debug_directories = [_][]const u8{
@@ -1659,33 +1678,37 @@ pub const ElfModule = struct {
for (global_debug_directories) |global_directory| {
const path: Path = .{
- .root_dir = std.Build.Cache.Directory.cwd(),
+ .root_dir = .cwd(),
.sub_path = try std.fs.path.join(gpa, &.{
global_directory, ".build-id", &id_prefix_buf, filename,
}),
};
defer gpa.free(path.sub_path);
- return loadPath(gpa, path, null, separate_debug_crc, §ions, mapped_mem) catch continue;
+ return load(gpa, path, null, separate_debug_crc, §ions, mapped_mem) catch continue;
}
}
// use the path from .gnu_debuglink, in the same search order as gdb
- if (separate_debug_filename) |separate_filename| blk: {
- if (elf_filename != null and mem.eql(u8, elf_filename.?, separate_filename))
+ separate: {
+ const separate_filename = separate_debug_filename orelse break :separate;
+ if (mem.eql(u8, std.fs.path.basename(elf_file_path.sub_path), separate_filename))
return error.MissingDebugInfo;
exe_dir: {
- var exe_dir_buf: [std.fs.max_path_bytes]u8 = undefined;
- const exe_dir_path = std.fs.selfExeDirPath(&exe_dir_buf) catch break :exe_dir;
+ const exe_dir_path = try std.fs.path.resolve(gpa, &.{
+ elf_file_path.root_dir.path orelse ".",
+ std.fs.path.dirname(elf_file_path.sub_path) orelse ".",
+ });
+ defer gpa.free(exe_dir_path);
var exe_dir = std.fs.openDirAbsolute(exe_dir_path, .{}) catch break :exe_dir;
defer exe_dir.close();
// <exe_dir>/<gnu_debuglink>
- if (loadPath(
+ if (load(
gpa,
.{
- .root_dir = .{ .path = null, .handle = exe_dir },
+ .root_dir = .{ .path = exe_dir_path, .handle = exe_dir },
.sub_path = separate_filename,
},
null,
@@ -1698,27 +1721,27 @@ pub const ElfModule = struct {
// <exe_dir>/.debug/<gnu_debuglink>
const path: Path = .{
- .root_dir = .{ .path = null, .handle = exe_dir },
+ .root_dir = .{ .path = exe_dir_path, .handle = exe_dir },
.sub_path = try std.fs.path.join(gpa, &.{ ".debug", separate_filename }),
};
defer gpa.free(path.sub_path);
- if (loadPath(gpa, path, null, separate_debug_crc, §ions, mapped_mem)) |em| {
+ if (load(gpa, path, null, separate_debug_crc, §ions, mapped_mem)) |em| {
return em;
} else |_| {}
}
var cwd_buf: [std.fs.max_path_bytes]u8 = undefined;
- const cwd_path = std.posix.realpath(".", &cwd_buf) catch break :blk;
+ const cwd_path = std.posix.realpath(".", &cwd_buf) catch break :separate;
// <global debug directory>/<absolute folder of current binary>/<gnu_debuglink>
for (global_debug_directories) |global_directory| {
const path: Path = .{
- .root_dir = std.Build.Cache.Directory.cwd(),
+ .root_dir = .cwd(),
.sub_path = try std.fs.path.join(gpa, &.{ global_directory, cwd_path, separate_filename }),
};
defer gpa.free(path.sub_path);
- if (loadPath(gpa, path, null, separate_debug_crc, §ions, mapped_mem)) |em| {
+ if (load(gpa, path, null, separate_debug_crc, §ions, mapped_mem)) |em| {
return em;
} else |_| {}
}
@@ -1735,47 +1758,6 @@ pub const ElfModule = struct {
.dwarf = dwarf,
};
}
-
- pub fn loadPath(
- gpa: Allocator,
- elf_file_path: Path,
- build_id: ?[]const u8,
- expected_crc: ?u32,
- parent_sections: *Dwarf.SectionArray,
- parent_mapped_mem: ?[]align(std.heap.page_size_min) const u8,
- ) LoadError!ElfModule {
- const elf_file = elf_file_path.root_dir.handle.openFile(elf_file_path.sub_path, .{}) catch |err| switch (err) {
- error.FileNotFound => return missing(),
- else => return err,
- };
- defer elf_file.close();
-
- const end_pos = elf_file.getEndPos() catch return bad();
- const file_len = cast(usize, end_pos) orelse return error.Overflow;
-
- const mapped_mem = std.posix.mmap(
- null,
- file_len,
- std.posix.PROT.READ,
- .{ .TYPE = .SHARED },
- elf_file.handle,
- 0,
- ) catch |err| switch (err) {
- error.MappingAlreadyExists => unreachable,
- else => |e| return e,
- };
- errdefer std.posix.munmap(mapped_mem);
-
- return load(
- gpa,
- mapped_mem,
- build_id,
- expected_crc,
- parent_sections,
- parent_mapped_mem,
- elf_file_path.sub_path,
- );
- }
};
pub fn getSymbol(di: *Dwarf, allocator: Allocator, endian: Endian, address: u64) !std.debug.Symbol {
lib/std/debug/Info.zig
@@ -25,7 +25,7 @@ pub const LoadError = Dwarf.ElfModule.LoadError;
pub fn load(gpa: Allocator, path: Path, coverage: *Coverage) LoadError!Info {
var sections: Dwarf.SectionArray = Dwarf.null_section_array;
- var elf_module = try Dwarf.ElfModule.loadPath(gpa, path, null, null, §ions, null);
+ var elf_module = try Dwarf.ElfModule.load(gpa, path, null, null, §ions, null);
try elf_module.dwarf.populateRanges(gpa);
var info: Info = .{
.address_map = .{},
lib/std/debug/SelfInfo.zig
@@ -156,11 +156,7 @@ const Module = switch (native_os) {
return error.MissingDebugInfo;
}
fn loadLocationInfo(module: *const Module, gpa: Allocator, di: *Module.DebugInfo) !void {
- const mapped_mem = mapFileOrSelfExe(module.name) catch |err| switch (err) {
- error.FileNotFound => return error.MissingDebugInfo,
- error.FileTooBig => return error.InvalidDebugInfo,
- else => |e| return e,
- };
+ const mapped_mem = try mapDebugInfoFile(module.name);
errdefer posix.munmap(mapped_mem);
const hdr: *const macho.mach_header_64 = @ptrCast(@alignCast(mapped_mem.ptr));
@@ -311,7 +307,6 @@ const Module = switch (native_os) {
gop.value_ptr.* = DebugInfo.loadOFile(gpa, o_file_path) catch |err| {
defer _ = di.full.?.ofiles.pop().?;
switch (err) {
- error.FileNotFound,
error.MissingDebugInfo,
error.InvalidDebugInfo,
=> return sym_only_result,
@@ -402,7 +397,7 @@ const Module = switch (native_os) {
}
fn loadOFile(gpa: Allocator, o_file_path: []const u8) !OFile {
- const mapped_mem = try mapFileOrSelfExe(o_file_path);
+ const mapped_mem = try mapDebugInfoFile(o_file_path);
errdefer posix.munmap(mapped_mem);
if (mapped_mem.len < @sizeOf(macho.mach_header_64)) return error.InvalidDebugInfo;
@@ -595,14 +590,27 @@ const Module = switch (native_os) {
return error.MissingDebugInfo;
}
fn loadLocationInfo(module: *const Module, gpa: Allocator, di: *Module.DebugInfo) !void {
- const filename: ?[]const u8 = if (module.name.len > 0) module.name else null;
- const mapped_mem = mapFileOrSelfExe(filename) catch |err| switch (err) {
- error.FileNotFound => return error.MissingDebugInfo,
- error.FileTooBig => return error.InvalidDebugInfo,
- else => |e| return e,
- };
- errdefer posix.munmap(mapped_mem);
- di.em = try .load(gpa, mapped_mem, module.build_id, null, null, null, filename);
+ if (module.name.len > 0) {
+ di.em = Dwarf.ElfModule.load(gpa, .{
+ .root_dir = .cwd(),
+ .sub_path = module.name,
+ }, module.build_id, null, null, null) catch |err| switch (err) {
+ error.FileNotFound => return error.MissingDebugInfo,
+ error.Overflow => return error.InvalidDebugInfo,
+ else => |e| return e,
+ };
+ } else {
+ const path = try std.fs.selfExePathAlloc(gpa);
+ defer gpa.free(path);
+ di.em = Dwarf.ElfModule.load(gpa, .{
+ .root_dir = .cwd(),
+ .sub_path = path,
+ }, module.build_id, null, null, null) catch |err| switch (err) {
+ error.FileNotFound => return error.MissingDebugInfo,
+ error.Overflow => return error.InvalidDebugInfo,
+ else => |e| return e,
+ };
+ }
}
fn getSymbolAtAddress(module: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {
if (di.em == null) try module.loadLocationInfo(gpa, di);
@@ -1247,14 +1255,18 @@ fn applyOffset(base: usize, offset: i64) !usize {
}
/// Uses `mmap` to map the file at `opt_path` (or, if `null`, the self executable image) into memory.
-fn mapFileOrSelfExe(opt_path: ?[]const u8) ![]align(std.heap.page_size_min) const u8 {
- const file = if (opt_path) |path|
- try fs.cwd().openFile(path, .{})
+fn mapDebugInfoFile(opt_path: ?[]const u8) ![]align(std.heap.page_size_min) const u8 {
+ const open_result = if (opt_path) |path|
+ fs.cwd().openFile(path, .{})
else
- try fs.openSelfExe(.{});
+ fs.openSelfExe(.{});
+ const file = open_result catch |err| switch (err) {
+ error.FileNotFound => return error.MissingDebugInfo,
+ else => |e| return e,
+ };
defer file.close();
- const file_len = math.cast(usize, try file.getEndPos()) orelse return error.FileTooBig;
+ const file_len = math.cast(usize, try file.getEndPos()) orelse return error.InvalidDebugInfo;
return posix.mmap(
null,
lib/std/debug.zig
@@ -153,10 +153,9 @@ pub const SourceLocation = struct {
};
pub const Symbol = struct {
- // MLUGG TODO: remove the defaults and audit everywhere. also grep for '???' across std
- name: []const u8 = "???",
- compile_unit_name: []const u8 = "???",
- source_location: ?SourceLocation = null,
+ name: ?[]const u8,
+ compile_unit_name: ?[]const u8,
+ source_location: ?SourceLocation,
};
/// Deprecated because it returns the optimization mode of the standard
@@ -1040,10 +1039,11 @@ fn printLastUnwindError(it: *StackIterator, debug_info: *SelfInfo, writer: *Writ
fn printUnwindError(debug_info: *SelfInfo, writer: *Writer, address: usize, unwind_err: UnwindError, tty_config: tty.Config) !void {
const module_name = debug_info.getModuleNameForAddress(getDebugInfoAllocator(), address) catch |err| switch (err) {
- error.Unexpected, error.OutOfMemory => |e| return e,
error.MissingDebugInfo => "???",
+ error.Unexpected, error.OutOfMemory => |e| return e,
};
try tty_config.setColor(writer, .dim);
+ // MLUGG TODO this makes no sense given that MissingUnwindInfo exists?
if (unwind_err == error.MissingDebugInfo) {
try writer.print("Unwind information for `{s}:0x{x}` was not available, trace may be incomplete\n\n", .{ module_name, address });
} else {
@@ -1054,35 +1054,27 @@ fn printUnwindError(debug_info: *SelfInfo, writer: *Writer, address: usize, unwi
pub fn printSourceAtAddress(debug_info: *SelfInfo, writer: *Writer, address: usize, tty_config: tty.Config) !void {
const gpa = getDebugInfoAllocator();
- if (debug_info.getSymbolAtAddress(gpa, address)) |symbol_info| {
- defer if (symbol_info.source_location) |sl| gpa.free(sl.file_name);
- return printLineInfo(
- writer,
- symbol_info.source_location,
- address,
- symbol_info.name,
- symbol_info.compile_unit_name,
- tty_config,
- );
- } else |err| switch (err) {
- error.MissingDebugInfo, error.InvalidDebugInfo => {},
+ const symbol: Symbol = debug_info.getSymbolAtAddress(gpa, address) catch |err| switch (err) {
+ error.MissingDebugInfo, error.InvalidDebugInfo => .{
+ .name = null,
+ .compile_unit_name = null,
+ .source_location = null,
+ },
else => |e| return e,
- }
- // Unknown source location, but perhaps we can at least get a module name
- const compile_unit_name = debug_info.getModuleNameForAddress(getDebugInfoAllocator(), address) catch |err| switch (err) {
- error.MissingDebugInfo => "???",
- error.Unexpected, error.OutOfMemory => |e| return e,
};
+ defer if (symbol.source_location) |sl| gpa.free(sl.file_name);
return printLineInfo(
writer,
- null,
+ symbol.source_location,
address,
- "???",
- compile_unit_name,
+ symbol.name orelse "???",
+ symbol.compile_unit_name orelse debug_info.getModuleNameForAddress(gpa, address) catch |err| switch (err) {
+ error.MissingDebugInfo => "???",
+ error.Unexpected, error.OutOfMemory => |e| return e,
+ },
tty_config,
);
}
-
fn printLineInfo(
writer: *Writer,
source_location: ?SourceLocation,