diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 505677dcb6..d005dd7841 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -766,11 +766,6 @@ pub fn writeStackTrace( } } -pub const UnwindError = if (have_ucontext) - @typeInfo(@typeInfo(@TypeOf(SelfInfo.unwindFrame)).@"fn".return_type.?).error_union.error_set -else - void; - pub const StackIterator = struct { // Skip every frame before this address is found. first_address: ?usize, @@ -783,7 +778,7 @@ pub const StackIterator = struct { unwind_state: if (have_ucontext) ?struct { debug_info: *SelfInfo, dwarf_context: SelfInfo.UnwindContext, - last_error: ?UnwindError = null, + last_error: ?SelfInfo.Error = null, failed: bool = false, } else void = if (have_ucontext) null else {}, @@ -821,7 +816,7 @@ pub const StackIterator = struct { } pub fn getLastError(it: *StackIterator) ?struct { - err: UnwindError, + err: SelfInfo.Error, address: usize, } { if (!have_ucontext) return null; @@ -1037,17 +1032,29 @@ 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 { +fn printUnwindError(debug_info: *SelfInfo, writer: *Writer, address: usize, unwind_err: SelfInfo.Error, tty_config: tty.Config) !void { const module_name = debug_info.getModuleNameForAddress(getDebugInfoAllocator(), address) catch |err| switch (err) { - error.MissingDebugInfo => "???", + error.InvalidDebugInfo, error.MissingDebugInfo, error.UnsupportedDebugInfo, error.ReadFailed => "???", 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 { - try writer.print("Unwind error at address `{s}:0x{x}` ({}), trace may be incomplete\n\n", .{ module_name, address, unwind_err }); + switch (unwind_err) { + error.Unexpected, error.OutOfMemory => |e| return e, + error.MissingDebugInfo => { + try writer.print("Unwind information for `{s}:0x{x}` was not available, trace may be incomplete\n\n", .{ module_name, address }); + }, + error.InvalidDebugInfo, + error.UnsupportedDebugInfo, + error.ReadFailed, + => { + const caption: []const u8 = switch (unwind_err) { + error.InvalidDebugInfo => "invalid unwind info", + error.UnsupportedDebugInfo => "unsupported unwind info", + error.ReadFailed => "filesystem error", + else => unreachable, + }; + try writer.print("Unwind error at address `{s}:0x{x}` ({s}), trace may be incomplete\n\n", .{ module_name, address, caption }); + }, } try tty_config.setColor(writer, .reset); } @@ -1055,12 +1062,17 @@ 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(); 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, + error.MissingDebugInfo, + error.UnsupportedDebugInfo, + error.InvalidDebugInfo, + => .{ .name = null, .compile_unit_name = null, .source_location = null }, + error.ReadFailed => s: { + try tty_config.setColor(writer, .dim); + try writer.print("Failed to read debug info from filesystem, trace may be incomplete\n\n", .{}); + try tty_config.setColor(writer, .reset); + break :s .{ .name = null, .compile_unit_name = null, .source_location = null }; }, - else => |e| return e, + error.OutOfMemory, error.Unexpected => |e| return e, }; defer if (symbol.source_location) |sl| gpa.free(sl.file_name); return printLineInfo( @@ -1069,7 +1081,7 @@ pub fn printSourceAtAddress(debug_info: *SelfInfo, writer: *Writer, address: usi address, symbol.name orelse "???", symbol.compile_unit_name orelse debug_info.getModuleNameForAddress(gpa, address) catch |err| switch (err) { - error.MissingDebugInfo => "???", + error.InvalidDebugInfo, error.MissingDebugInfo, error.UnsupportedDebugInfo, error.ReadFailed => "???", error.Unexpected, error.OutOfMemory => |e| return e, }, tty_config, diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig index 0ba4ab8048..f50b9ed163 100644 --- a/lib/std/debug/Dwarf.zig +++ b/lib/std/debug/Dwarf.zig @@ -1418,7 +1418,7 @@ pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 { 4 => 14, // R14 5 => 15, // R15 6 => 6, // RBP - else => error.InvalidUnwindRegisterNumber, + else => error.InvalidRegister, }; } diff --git a/lib/std/debug/SelfInfo.zig b/lib/std/debug/SelfInfo.zig index 9fa57208e1..1f913efeea 100644 --- a/lib/std/debug/SelfInfo.zig +++ b/lib/std/debug/SelfInfo.zig @@ -1,8 +1,6 @@ //! Cross-platform abstraction for this binary's own debug information, with a //! goal of minimal code bloat and compilation speed penalty. -// MLUGG TODO: audit use of errors in this file. ideally, introduce some concrete error sets - const builtin = @import("builtin"); const native_os = builtin.os.tag; const native_endian = native_arch.endian(); @@ -21,6 +19,19 @@ const SelfInfo = @This(); modules: std.AutoArrayHashMapUnmanaged(usize, Module.DebugInfo), lookup_cache: Module.LookupCache, +pub const Error = error{ + /// The required debug info is invalid or corrupted. + InvalidDebugInfo, + /// The required debug info could not be found. + MissingDebugInfo, + /// The required debug info was found, and may be valid, but is not supported by this implementation. + UnsupportedDebugInfo, + /// The required debug info could not be read from disk due to some IO error. + ReadFailed, + OutOfMemory, + Unexpected, +}; + /// Indicates whether the `SelfInfo` implementation has support for this target. pub const target_supported: bool = switch (native_os) { .linux, @@ -82,7 +93,7 @@ test { _ = &deinit; } -pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) !usize { +pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) Error!usize { comptime assert(supports_unwinding); const module: Module = try .lookup(&self.lookup_cache, gpa, context.pc); const gop = try self.modules.getOrPut(gpa, module.key()); @@ -92,7 +103,7 @@ pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) !us return module.unwindFrame(gpa, gop.value_ptr, context); } -pub fn getSymbolAtAddress(self: *SelfInfo, gpa: Allocator, address: usize) !std.debug.Symbol { +pub fn getSymbolAtAddress(self: *SelfInfo, gpa: Allocator, address: usize) Error!std.debug.Symbol { comptime assert(target_supported); const module: Module = try .lookup(&self.lookup_cache, gpa, address); const gop = try self.modules.getOrPut(gpa, module.key()); @@ -102,7 +113,7 @@ pub fn getSymbolAtAddress(self: *SelfInfo, gpa: Allocator, address: usize) !std. return module.getSymbolAtAddress(gpa, gop.value_ptr, address); } -pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) error{ Unexpected, OutOfMemory, MissingDebugInfo }![]const u8 { +pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) Error![]const u8 { comptime assert(target_supported); const module: Module = try .lookup(&self.lookup_cache, gpa, address); return module.name; @@ -271,12 +282,61 @@ pub const UnwindContext = struct { /// may require lazily loading the data in those sections. /// /// `explicit_fde_offset` is for cases where the FDE offset is known, such as when __unwind_info - /// defers unwinding to DWARF. This is an offset into the `.eh_frame` section. pub fn unwindFrameDwarf( context: *UnwindContext, unwind: *const Dwarf.Unwind, load_offset: usize, explicit_fde_offset: ?usize, + ) Error!usize { + return unwindFrameDwarfInner(context, unwind, load_offset, explicit_fde_offset) catch |err| switch (err) { + error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e, + + error.UnimplementedArch, + error.UnimplementedOs, + error.ThreadContextNotSupported, + error.UnimplementedRegisterRule, + error.UnsupportedAddrSize, + error.UnsupportedDwarfVersion, + error.UnimplementedUserOpcode, + error.UnimplementedExpressionCall, + error.UnimplementedOpcode, + error.UnimplementedTypedComparison, + error.UnimplementedTypeConversion, + error.UnknownExpressionOpcode, + => return error.UnsupportedDebugInfo, + + error.InvalidRegister, + error.RegisterContextRequired, + error.ReadFailed, + error.EndOfStream, + error.IncompatibleRegisterSize, + error.Overflow, + error.StreamTooLong, + error.InvalidOperand, + error.InvalidOpcode, + error.InvalidOperation, + error.InvalidCFARule, + error.IncompleteExpressionContext, + error.InvalidCFAOpcode, + error.InvalidExpression, + error.InvalidFrameBase, + error.InvalidIntegralTypeSize, + error.InvalidSubExpression, + error.InvalidTypeLength, + error.TruncatedIntegralType, + error.DivisionByZero, + error.InvalidExpressionValue, + error.NoExpressionValue, + error.RegisterSizeMismatch, + error.InvalidCFA, + => return error.InvalidDebugInfo, + }; + } + fn unwindFrameDwarfInner( + context: *UnwindContext, + unwind: *const Dwarf.Unwind, + load_offset: usize, + explicit_fde_offset: ?usize, ) !usize { if (!supports_unwinding) return error.UnsupportedCpuArchitecture; if (context.pc == 0) return 0; 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; diff --git a/lib/std/debug/SelfInfo/ElfModule.zig b/lib/std/debug/SelfInfo/ElfModule.zig index 6c1da9d8da..25ce1827b7 100644 --- a/lib/std/debug/SelfInfo/ElfModule.zig +++ b/lib/std/debug/SelfInfo/ElfModule.zig @@ -21,7 +21,7 @@ pub const DebugInfo = struct { pub fn key(m: ElfModule) usize { return m.load_offset; } -pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !ElfModule { +pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) Error!ElfModule { _ = cache; _ = gpa; if (builtin.target.os.tag == .haiku) @panic("TODO implement lookup module for Haiku"); @@ -92,42 +92,79 @@ pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !ElfModule { }; return error.MissingDebugInfo; } -fn loadDwarf(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) !void { - if (module.name.len > 0) { - di.loaded_elf = Dwarf.ElfModule.load(gpa, .{ +fn loadDwarf(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Error!void { + 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) catch |err| switch (err) { - error.FileNotFound => return error.MissingDebugInfo, - error.Overflow => return error.InvalidDebugInfo, - else => |e| return e, + }, module.build_id, null, null, null); + } else res: { + const path = std.fs.selfExePathAlloc(gpa) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + else => return error.ReadFailed, }; - } else { - const path = try std.fs.selfExePathAlloc(gpa); defer gpa.free(path); - di.loaded_elf = Dwarf.ElfModule.load(gpa, .{ + break :res 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, - }; - } + }, module.build_id, null, null, null); + }; + 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.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 + => return error.InvalidDebugInfo, + + else => return error.ReadFailed, + }; } -pub fn getSymbolAtAddress(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol { +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); const vaddr = address - module.load_offset; - return di.loaded_elf.?.dwarf.getSymbol(gpa, native_endian, vaddr); + 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, + }; } -fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) !void { - const section_bytes = module.gnu_eh_frame orelse return error.MissingUnwindInfo; // MLUGG TODO: load from file +fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Error!void { + const section_bytes = module.gnu_eh_frame orelse return error.MissingDebugInfo; // MLUGG TODO: load from file + const section_vaddr: u64 = @intFromPtr(section_bytes.ptr) - module.load_offset; - const header: Dwarf.Unwind.EhFrameHeader = try .parse(section_vaddr, section_bytes, @sizeOf(usize), native_endian); - di.unwind = .initEhFrameHdr(header, section_vaddr, @ptrFromInt(module.load_offset + header.eh_frame_vaddr)); - try di.unwind.?.prepareLookup(gpa, @sizeOf(usize), native_endian); + const header = Dwarf.Unwind.EhFrameHeader.parse(section_vaddr, section_bytes, @sizeOf(usize), native_endian) catch |err| switch (err) { + error.ReadFailed => unreachable, // it's all fixed buffers + error.InvalidDebugInfo => |e| return e, + error.EndOfStream, error.Overflow => return error.InvalidDebugInfo, + error.UnsupportedAddrSize => return error.UnsupportedDebugInfo, + }; + + var unwind: Dwarf.Unwind = .initEhFrameHdr(header, section_vaddr, @ptrFromInt(module.load_offset + header.eh_frame_vaddr)); + unwind.prepareLookup(gpa, @sizeOf(usize), native_endian) catch |err| switch (err) { + error.ReadFailed => unreachable, // it's all fixed buffers + error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e, + error.EndOfStream, error.Overflow, error.StreamTooLong => return error.InvalidDebugInfo, + error.UnsupportedAddrSize, error.UnsupportedDwarfVersion => return error.UnsupportedDebugInfo, + }; + + di.unwind = unwind; } -pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize { +pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!usize { if (di.unwind == null) try module.loadUnwindInfo(gpa, di); return context.unwindFrameDwarf(&di.unwind.?, module.load_offset, null); } @@ -140,6 +177,7 @@ const Dwarf = std.debug.Dwarf; const elf = std.elf; const mem = std.mem; const UnwindContext = std.debug.SelfInfo.UnwindContext; +const Error = std.debug.SelfInfo.Error; const builtin = @import("builtin"); const native_endian = builtin.target.cpu.arch.endian(); diff --git a/lib/std/debug/SelfInfo/WindowsModule.zig b/lib/std/debug/SelfInfo/WindowsModule.zig index 4f9d98353b..674c6adae5 100644 --- a/lib/std/debug/SelfInfo/WindowsModule.zig +++ b/lib/std/debug/SelfInfo/WindowsModule.zig @@ -5,7 +5,7 @@ handle: windows.HMODULE, pub fn key(m: WindowsModule) usize { return m.base_address; } -pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !WindowsModule { +pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) std.debug.SelfInfo.Error!WindowsModule { if (lookupInCache(cache, address)) |m| return m; { // Check a new module hasn't been loaded @@ -29,18 +29,23 @@ pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !WindowsModul if (lookupInCache(cache, address)) |m| return m; return error.MissingDebugInfo; } -pub fn getSymbolAtAddress(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol { - if (!di.loaded) try module.loadLocationInfo(gpa, di); +pub fn getSymbolAtAddress(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo, address: usize) std.debug.SelfInfo.Error!std.debug.Symbol { + if (!di.loaded) module.loadDebugInfo(gpa, di) catch |err| switch (err) { + error.OutOfMemory, error.InvalidDebugInfo, error.MissingDebugInfo, error.Unexpected => |e| return e, + error.FileNotFound => return error.MissingDebugInfo, + error.UnknownPDBVersion => return error.UnsupportedDebugInfo, + else => return error.ReadFailed, + }; // Translate the runtime address into a virtual address into the module const vaddr = address - module.base_address; if (di.pdb != null) { - if (try di.getSymbolFromPdb(vaddr)) |symbol| return symbol; + if (di.getSymbolFromPdb(vaddr) catch return error.InvalidDebugInfo) |symbol| return symbol; } if (di.dwarf) |*dwarf| { const dwarf_address = vaddr + di.coff_image_base; - return dwarf.getSymbol(gpa, native_endian, dwarf_address); + return dwarf.getSymbol(gpa, native_endian, dwarf_address) catch return error.InvalidDebugInfo; } return error.MissingDebugInfo; @@ -59,7 +64,7 @@ fn lookupInCache(cache: *const LookupCache, address: usize) ?WindowsModule { } return null; } -fn loadLocationInfo(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo) !void { +fn loadDebugInfo(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo) !void { const mapped_ptr: [*]const u8 = @ptrFromInt(module.base_address); const mapped = mapped_ptr[0..module.size]; var coff_obj = coff.Coff.init(mapped, true) catch return error.InvalidDebugInfo; @@ -151,7 +156,7 @@ fn loadLocationInfo(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo di.pdb = Pdb.init(gpa, path) catch |err| switch (err) { error.FileNotFound, error.IsDir => break :pdb, - else => return err, + else => |e| return e, }; try di.pdb.?.parseInfoStream(); try di.pdb.?.parseDbiStream();