diff options
| author | mlugg <mlugg@mlugg.co.uk> | 2025-09-03 15:42:33 +0100 |
|---|---|---|
| committer | mlugg <mlugg@mlugg.co.uk> | 2025-09-30 13:44:50 +0100 |
| commit | c895aa7a35b178576b89e600a20af9d16da36dea (patch) | |
| tree | 0816ce721cd4343b5abd3c86187ca064b8826420 /lib/std/debug/SelfInfo/DarwinModule.zig | |
| parent | dd9cb1beead2d0b9a22decf089aadf51cfe90da8 (diff) | |
| download | zig-c895aa7a35b178576b89e600a20af9d16da36dea.tar.gz zig-c895aa7a35b178576b89e600a20af9d16da36dea.zip | |
std.debug.SelfInfo: concrete error sets
The downside of this commit is that more precise errors are no longer
propagated up. However, these errors were pretty useless in isolation
due to them having no context; and regardless, we intentionally swallow
most of them in `std.debug` anyway. Therefore, this is better in
practice, because it allows `std.debug` to give slightly more useful
warnings when handling errors. This commit does that for unwind errors,
for instance, which differentiate between the unwind info being corrupt
vs missing vs inaccessible vs unsupported.
A better solution would be to also include more detailed information via
the diagnostics pattern, but this commit is an incremental improvement.
Diffstat (limited to 'lib/std/debug/SelfInfo/DarwinModule.zig')
| -rw-r--r-- | lib/std/debug/SelfInfo/DarwinModule.zig | 100 |
1 files changed, 56 insertions, 44 deletions
diff --git a/lib/std/debug/SelfInfo/DarwinModule.zig b/lib/std/debug/SelfInfo/DarwinModule.zig index e1fd387473..1a38bdd284 100644 --- a/lib/std/debug/SelfInfo/DarwinModule.zig +++ b/lib/std/debug/SelfInfo/DarwinModule.zig @@ -7,7 +7,9 @@ pub fn key(m: *const DarwinModule) usize { return m.text_base; } -pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !DarwinModule { +/// No cache needed, because `_dyld_get_image_header` etc are already fast. +pub const LookupCache = void; +pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) Error!DarwinModule { _ = cache; _ = gpa; const image_count = std.c._dyld_image_count(); @@ -186,8 +188,11 @@ fn loadFullInfo(module: *const DarwinModule, gpa: Allocator) !DebugInfo.Full { .ofiles = .empty, }; } -pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol { - if (di.full == null) di.full = try module.loadFullInfo(gpa); +pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, address: usize) Error!std.debug.Symbol { + if (di.full == null) di.full = module.loadFullInfo(gpa) catch |err| switch (err) { + error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory, error.Unexpected => |e| return e, + else => return error.ReadFailed, + }; const full = &di.full.?; const vaddr = address - module.load_offset; @@ -215,14 +220,9 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu const gop = try full.ofiles.getOrPut(gpa, symbol.ofile); if (!gop.found_existing) { const o_file_path = mem.sliceTo(full.strings[symbol.ofile..], 0); - gop.value_ptr.* = DebugInfo.loadOFile(gpa, o_file_path) catch |err| { - defer _ = full.ofiles.pop().?; - switch (err) { - error.MissingDebugInfo, - error.InvalidDebugInfo, - => return sym_only_result, - else => |e| return e, - } + gop.value_ptr.* = DebugInfo.loadOFile(gpa, o_file_path) catch { + _ = full.ofiles.pop().?; + return sym_only_result; }; } break :of gop.value_ptr; @@ -234,10 +234,7 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu ) orelse return sym_only_result; const symbol_ofile_vaddr = o_file.symtab[symbol_index].n_value; - const compile_unit = o_file.dwarf.findCompileUnit(native_endian, symbol_ofile_vaddr) catch |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => return sym_only_result, - else => |e| return e, - }; + const compile_unit = o_file.dwarf.findCompileUnit(native_endian, symbol_ofile_vaddr) catch return sym_only_result; return .{ .name = o_file.dwarf.getSymbolName(symbol_ofile_vaddr) orelse stab_symbol, @@ -255,28 +252,44 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu native_endian, compile_unit, symbol_ofile_vaddr + address_symbol_offset, - ) catch |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => null, - else => return err, - }, + ) catch null, }; } /// Unwind a frame using MachO compact unwind info (from __unwind_info). /// If the compact encoding can't encode a way to unwind a frame, it will /// defer unwinding to DWARF, in which case `.eh_frame` will be used if available. -pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize { +pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!usize { + return unwindFrameInner(module, gpa, di, context) catch |err| switch (err) { + error.InvalidDebugInfo, + error.MissingDebugInfo, + error.UnsupportedDebugInfo, + error.ReadFailed, + error.OutOfMemory, + error.Unexpected, + => |e| return e, + error.UnimplementedArch, + error.UnimplementedOs, + error.ThreadContextNotSupported, + => return error.UnsupportedDebugInfo, + error.InvalidRegister, + error.RegisterContextRequired, + error.IncompatibleRegisterSize, + => return error.InvalidDebugInfo, + }; +} +fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize { _ = gpa; if (di.unwind == null) di.unwind = module.loadUnwindInfo(); const unwind = &di.unwind.?; - const unwind_info = unwind.unwind_info orelse return error.MissingUnwindInfo; - if (unwind_info.len < @sizeOf(macho.unwind_info_section_header)) return error.InvalidUnwindInfo; + const unwind_info = unwind.unwind_info orelse return error.MissingDebugInfo; + if (unwind_info.len < @sizeOf(macho.unwind_info_section_header)) return error.InvalidDebugInfo; const header: *align(1) const macho.unwind_info_section_header = @ptrCast(unwind_info); const index_byte_count = header.indexCount * @sizeOf(macho.unwind_info_section_header_index_entry); - if (unwind_info.len < header.indexSectionOffset + index_byte_count) return error.InvalidUnwindInfo; + if (unwind_info.len < header.indexSectionOffset + index_byte_count) return error.InvalidDebugInfo; const indices: []align(1) const macho.unwind_info_section_header_index_entry = @ptrCast(unwind_info[header.indexSectionOffset..][0..index_byte_count]); - if (indices.len == 0) return error.MissingUnwindInfo; + if (indices.len == 0) return error.MissingDebugInfo; // offset of the PC into the `__TEXT` segment const pc_text_offset = context.pc - module.text_base; @@ -296,15 +309,15 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, break :index .{ indices[left].secondLevelPagesSectionOffset, indices[left].functionOffset }; }; // An offset of 0 is a sentinel indicating a range does not have unwind info. - if (start_offset == 0) return error.MissingUnwindInfo; + if (start_offset == 0) return error.MissingDebugInfo; const common_encodings_byte_count = header.commonEncodingsArrayCount * @sizeOf(macho.compact_unwind_encoding_t); - if (unwind_info.len < header.commonEncodingsArraySectionOffset + common_encodings_byte_count) return error.InvalidUnwindInfo; + if (unwind_info.len < header.commonEncodingsArraySectionOffset + common_encodings_byte_count) return error.InvalidDebugInfo; const common_encodings: []align(1) const macho.compact_unwind_encoding_t = @ptrCast( unwind_info[header.commonEncodingsArraySectionOffset..][0..common_encodings_byte_count], ); - if (unwind_info.len < start_offset + @sizeOf(macho.UNWIND_SECOND_LEVEL)) return error.InvalidUnwindInfo; + if (unwind_info.len < start_offset + @sizeOf(macho.UNWIND_SECOND_LEVEL)) return error.InvalidDebugInfo; const kind: *align(1) const macho.UNWIND_SECOND_LEVEL = @ptrCast(unwind_info[start_offset..]); const entry: struct { @@ -312,15 +325,15 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, raw_encoding: u32, } = switch (kind.*) { .REGULAR => entry: { - if (unwind_info.len < start_offset + @sizeOf(macho.unwind_info_regular_second_level_page_header)) return error.InvalidUnwindInfo; + if (unwind_info.len < start_offset + @sizeOf(macho.unwind_info_regular_second_level_page_header)) return error.InvalidDebugInfo; const page_header: *align(1) const macho.unwind_info_regular_second_level_page_header = @ptrCast(unwind_info[start_offset..]); const entries_byte_count = page_header.entryCount * @sizeOf(macho.unwind_info_regular_second_level_entry); - if (unwind_info.len < start_offset + entries_byte_count) return error.InvalidUnwindInfo; + if (unwind_info.len < start_offset + entries_byte_count) return error.InvalidDebugInfo; const entries: []align(1) const macho.unwind_info_regular_second_level_entry = @ptrCast( unwind_info[start_offset + page_header.entryPageOffset ..][0..entries_byte_count], ); - if (entries.len == 0) return error.InvalidUnwindInfo; + if (entries.len == 0) return error.InvalidDebugInfo; var left: usize = 0; var len: usize = entries.len; @@ -339,15 +352,15 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, }; }, .COMPRESSED => entry: { - if (unwind_info.len < start_offset + @sizeOf(macho.unwind_info_compressed_second_level_page_header)) return error.InvalidUnwindInfo; + if (unwind_info.len < start_offset + @sizeOf(macho.unwind_info_compressed_second_level_page_header)) return error.InvalidDebugInfo; const page_header: *align(1) const macho.unwind_info_compressed_second_level_page_header = @ptrCast(unwind_info[start_offset..]); const entries_byte_count = page_header.entryCount * @sizeOf(macho.UnwindInfoCompressedEntry); - if (unwind_info.len < start_offset + entries_byte_count) return error.InvalidUnwindInfo; + if (unwind_info.len < start_offset + entries_byte_count) return error.InvalidDebugInfo; const entries: []align(1) const macho.UnwindInfoCompressedEntry = @ptrCast( unwind_info[start_offset + page_header.entryPageOffset ..][0..entries_byte_count], ); - if (entries.len == 0) return error.InvalidUnwindInfo; + if (entries.len == 0) return error.InvalidDebugInfo; var left: usize = 0; var len: usize = entries.len; @@ -372,26 +385,26 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, const local_index = entry.encodingIndex - common_encodings.len; const local_encodings_byte_count = page_header.encodingsCount * @sizeOf(macho.compact_unwind_encoding_t); - if (unwind_info.len < start_offset + page_header.encodingsPageOffset + local_encodings_byte_count) return error.InvalidUnwindInfo; + if (unwind_info.len < start_offset + page_header.encodingsPageOffset + local_encodings_byte_count) return error.InvalidDebugInfo; const local_encodings: []align(1) const macho.compact_unwind_encoding_t = @ptrCast( unwind_info[start_offset + page_header.encodingsPageOffset ..][0..local_encodings_byte_count], ); - if (local_index >= local_encodings.len) return error.InvalidUnwindInfo; + if (local_index >= local_encodings.len) return error.InvalidDebugInfo; break :entry .{ .function_offset = function_offset, .raw_encoding = local_encodings[local_index], }; }, - else => return error.InvalidUnwindInfo, + else => return error.InvalidDebugInfo, }; - if (entry.raw_encoding == 0) return error.NoUnwindInfo; + if (entry.raw_encoding == 0) return error.MissingDebugInfo; const reg_context: Dwarf.abi.RegisterContext = .{ .eh_frame = false, .is_macho = true }; const encoding: macho.CompactUnwindEncoding = @bitCast(entry.raw_encoding); const new_ip = switch (builtin.cpu.arch) { .x86_64 => switch (encoding.mode.x86_64) { - .OLD => return error.UnimplementedUnwindEncoding, + .OLD => return error.UnsupportedDebugInfo, .RBP_FRAME => ip: { const frame = encoding.value.x86_64.frame; @@ -493,7 +506,7 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, break :ip new_ip; }, .DWARF => { - const eh_frame = unwind.eh_frame orelse return error.MissingEhFrame; + const eh_frame = unwind.eh_frame orelse return error.MissingDebugInfo; const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - module.load_offset; return context.unwindFrameDwarf( &.initSection(.eh_frame, eh_frame_vaddr, eh_frame), @@ -503,7 +516,7 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, }, }, .aarch64, .aarch64_be => switch (encoding.mode.arm64) { - .OLD => return error.UnimplementedUnwindEncoding, + .OLD => return error.UnsupportedDebugInfo, .FRAMELESS => ip: { const sp = (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).*; const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16; @@ -512,7 +525,7 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, break :ip new_ip; }, .DWARF => { - const eh_frame = unwind.eh_frame orelse return error.MissingEhFrame; + const eh_frame = unwind.eh_frame orelse return error.MissingDebugInfo; const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - module.load_offset; return context.unwindFrameDwarf( &.initSection(.eh_frame, eh_frame_vaddr, eh_frame), @@ -568,8 +581,6 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, if (context.pc > 0) context.pc -= 1; return new_ip; } -/// No cache needed, because `_dyld_get_image_header` etc are already fast. -pub const LookupCache = void; pub const DebugInfo = struct { unwind: ?Unwind, // MLUGG TODO: awful field name @@ -785,7 +796,7 @@ const ip_reg_num = Dwarf.abi.ipRegNum(builtin.target.cpu.arch).?; fn mapDebugInfoFile(path: []const u8) ![]align(std.heap.page_size_min) const u8 { const file = std.fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => return error.MissingDebugInfo, - else => |e| return e, + else => return error.ReadFailed, }; defer file.close(); @@ -812,6 +823,7 @@ const mem = std.mem; const posix = std.posix; const testing = std.testing; const UnwindContext = std.debug.SelfInfo.UnwindContext; +const Error = std.debug.SelfInfo.Error; const regBytes = Dwarf.abi.regBytes; const regValueNative = Dwarf.abi.regValueNative; |
