diff options
| author | mlugg <mlugg@mlugg.co.uk> | 2025-09-02 15:54:36 +0100 |
|---|---|---|
| committer | mlugg <mlugg@mlugg.co.uk> | 2025-09-30 13:44:49 +0100 |
| commit | 84b65860cfa4b7e61cb98347778331d67137d7e8 (patch) | |
| tree | 164290d3b2e86f06e74e02f2ca9d315edaed4e5b /lib/std/debug | |
| parent | 55a7affea41a4a1f4e117d7ee55c1c0e8b869203 (diff) | |
| download | zig-84b65860cfa4b7e61cb98347778331d67137d7e8.tar.gz zig-84b65860cfa4b7e61cb98347778331d67137d7e8.zip | |
the world if ElfModule didn't suck:
Diffstat (limited to 'lib/std/debug')
| -rw-r--r-- | lib/std/debug/Dwarf.zig | 154 | ||||
| -rw-r--r-- | lib/std/debug/Dwarf/Unwind.zig | 11 | ||||
| -rw-r--r-- | lib/std/debug/Info.zig | 2 | ||||
| -rw-r--r-- | lib/std/debug/SelfInfo.zig | 52 |
4 files changed, 108 insertions, 111 deletions
diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig index bb4bee2fbf..5c8ac1b14a 100644 --- a/lib/std/debug/Dwarf.zig +++ b/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 { diff --git a/lib/std/debug/Dwarf/Unwind.zig b/lib/std/debug/Dwarf/Unwind.zig index 09cad0db23..323bf39874 100644 --- a/lib/std/debug/Dwarf/Unwind.zig +++ b/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) { diff --git a/lib/std/debug/Info.zig b/lib/std/debug/Info.zig index c809547f73..e38645e1f9 100644 --- a/lib/std/debug/Info.zig +++ b/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 = .{}, diff --git a/lib/std/debug/SelfInfo.zig b/lib/std/debug/SelfInfo.zig index a12152a210..e99a83ce75 100644 --- a/lib/std/debug/SelfInfo.zig +++ b/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, |
