diff options
| author | mlugg <mlugg@mlugg.co.uk> | 2025-09-09 14:20:49 +0100 |
|---|---|---|
| committer | mlugg <mlugg@mlugg.co.uk> | 2025-09-30 13:44:52 +0100 |
| commit | c1a30bd0d876330ce7a241fc297c66577ae7e6aa (patch) | |
| tree | fbc6e50c11746e259fb5366caf57fce40c4d6964 /lib/std/debug/SelfInfo/ElfModule.zig | |
| parent | f7980487395b660d5c568ba57891ab371a27102d (diff) | |
| download | zig-c1a30bd0d876330ce7a241fc297c66577ae7e6aa.tar.gz zig-c1a30bd0d876330ce7a241fc297c66577ae7e6aa.zip | |
std: replace debug.Dwarf.ElfModule with debug.ElfFile
This abstraction isn't really tied to DWARF at all! Really, we're just
loading some information from an ELF file which is useful for debugging.
That *includes* DWARF, but it also includes other information. For
instance, the other change here:
Now, if DWARF information is missing, `debug.SelfInfo.ElfModule` will
name symbols by finding a matching symtab entry. We actually already do
this on Mach-O, so it makes obvious sense to do the same on ELF! This
change is what motivated the restructuring to begin with.
The symtab work is derived from #22077.
Co-authored-by: geemili <opensource@geemili.xyz>
Diffstat (limited to 'lib/std/debug/SelfInfo/ElfModule.zig')
| -rw-r--r-- | lib/std/debug/SelfInfo/ElfModule.zig | 100 |
1 files changed, 70 insertions, 30 deletions
diff --git a/lib/std/debug/SelfInfo/ElfModule.zig b/lib/std/debug/SelfInfo/ElfModule.zig index 7a280c0d6e..7871f1012f 100644 --- a/lib/std/debug/SelfInfo/ElfModule.zig +++ b/lib/std/debug/SelfInfo/ElfModule.zig @@ -7,10 +7,12 @@ gnu_eh_frame: ?[]const u8, pub const LookupCache = void; pub const DebugInfo = struct { - loaded_elf: ?Dwarf.ElfModule, + loaded_elf: ?ElfFile, + scanned_dwarf: bool, unwind: [2]?Dwarf.Unwind, pub const init: DebugInfo = .{ .loaded_elf = null, + .scanned_dwarf = false, .unwind = @splat(null), }; pub fn deinit(di: *DebugInfo, gpa: Allocator) void { @@ -92,55 +94,92 @@ pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) Error!ElfModu }; return error.MissingDebugInfo; } -fn loadDwarf(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Error!void { +fn loadElf(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Error!void { + std.debug.assert(di.loaded_elf == null); + std.debug.assert(!di.scanned_dwarf); + const load_result = if (module.name.len > 0) res: { - break :res Dwarf.ElfModule.load(gpa, .{ - .root_dir = .cwd(), - .sub_path = module.name, - }, module.build_id, null, null, null); + var file = std.fs.cwd().openFile(module.name, .{}) catch return error.MissingDebugInfo; + defer file.close(); + break :res ElfFile.load(gpa, file, module.build_id, &.native(module.name)); } else res: { const path = std.fs.selfExePathAlloc(gpa) catch |err| switch (err) { error.OutOfMemory => |e| return e, else => return error.ReadFailed, }; defer gpa.free(path); - break :res Dwarf.ElfModule.load(gpa, .{ - .root_dir = .cwd(), - .sub_path = path, - }, module.build_id, null, null, null); + var file = std.fs.cwd().openFile(path, .{}) catch return error.MissingDebugInfo; + defer file.close(); + break :res ElfFile.load(gpa, file, module.build_id, &.native(path)); }; di.loaded_elf = load_result catch |err| switch (err) { - error.FileNotFound => return error.MissingDebugInfo, - error.OutOfMemory, - error.InvalidDebugInfo, - error.MissingDebugInfo, error.Unexpected, => |e| return e, - error.InvalidElfEndian, + error.Overflow, + error.TruncatedElfFile, + error.InvalidCompressedSection, error.InvalidElfMagic, error.InvalidElfVersion, - error.InvalidUtf8, - error.InvalidWtf8, - error.EndOfStream, - error.Overflow, - error.UnimplementedDwarfForeignEndian, // this should be impossible as we're looking at the debug info for this process + error.InvalidElfClass, + error.InvalidElfEndian, => return error.InvalidDebugInfo, - else => return error.ReadFailed, + error.SystemResources, + error.MemoryMappingNotSupported, + error.AccessDenied, + error.LockedMemoryLimitExceeded, + error.ProcessFdQuotaExceeded, + error.SystemFdQuotaExceeded, + => return error.ReadFailed, }; + + const matches_native = + di.loaded_elf.?.endian == native_endian and + di.loaded_elf.?.is_64 == (@sizeOf(usize) == 8); + + if (!matches_native) { + di.loaded_elf.?.deinit(gpa); + di.loaded_elf = null; + return error.InvalidDebugInfo; + } } pub fn getSymbolAtAddress(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, address: usize) Error!std.debug.Symbol { - if (di.loaded_elf == null) try module.loadDwarf(gpa, di); + if (di.loaded_elf == null) try module.loadElf(gpa, di); const vaddr = address - module.load_offset; - return di.loaded_elf.?.dwarf.getSymbol(gpa, native_endian, vaddr) catch |err| switch (err) { - error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e, - error.ReadFailed, - error.EndOfStream, - error.Overflow, - error.StreamTooLong, - => return error.InvalidDebugInfo, + if (di.loaded_elf.?.dwarf) |*dwarf| { + if (!di.scanned_dwarf) { + dwarf.open(gpa, native_endian) catch |err| switch (err) { + error.InvalidDebugInfo, + error.MissingDebugInfo, + error.OutOfMemory, + => |e| return e, + error.EndOfStream, + error.Overflow, + error.ReadFailed, + error.StreamTooLong, + => return error.InvalidDebugInfo, + }; + di.scanned_dwarf = true; + } + return dwarf.getSymbol(gpa, native_endian, vaddr) catch |err| switch (err) { + error.InvalidDebugInfo, + error.MissingDebugInfo, + error.OutOfMemory, + => |e| return e, + error.ReadFailed, + error.EndOfStream, + error.Overflow, + error.StreamTooLong, + => return error.InvalidDebugInfo, + }; + } + // When there's no DWARF available, fall back to searching the symtab. + return di.loaded_elf.?.searchSymtab(gpa, vaddr) catch |err| switch (err) { + error.NoSymtab, error.NoStrtab => return error.MissingDebugInfo, + error.BadSymtab => return error.InvalidDebugInfo, + error.OutOfMemory => |e| return e, }; } fn prepareUnwindLookup(unwind: *Dwarf.Unwind, gpa: Allocator) Error!void { @@ -166,7 +205,7 @@ fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Erro } else unwinds: { // There is no `.eh_frame_hdr` section. There may still be an `.eh_frame` or `.debug_frame` // section, but we'll have to load the binary to get at it. - try module.loadDwarf(gpa, di); + try module.loadElf(gpa, di); const opt_debug_frame = &di.loaded_elf.?.debug_frame; const opt_eh_frame = &di.loaded_elf.?.eh_frame; // If both are present, we can't just pick one -- the info could be split between them. @@ -232,6 +271,7 @@ const ElfModule = @This(); const std = @import("../../std.zig"); const Allocator = std.mem.Allocator; const Dwarf = std.debug.Dwarf; +const ElfFile = std.debug.ElfFile; const elf = std.elf; const mem = std.mem; const Error = std.debug.SelfInfo.Error; |
