authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-03 15:42:33+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-30 13:44:50+01:00
logc895aa7a35b178576b89e600a20af9d16da36dea
tree0816ce721cd4343b5abd3c86187ca064b8826420
parentdd9cb1beead2d0b9a22decf089aadf51cfe90da8
signaturelock-open Commit is signed but in an unrecognized format.

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.

6 files changed, 230 insertions(+), 103 deletions(-)

lib/std/debug.zig+32-20
......@@ -766,11 +766,6 @@ pub fn writeStackTrace(
766766 }
767767}
768768
769pub const UnwindError = if (have_ucontext)
770 @typeInfo(@typeInfo(@TypeOf(SelfInfo.unwindFrame)).@"fn".return_type.?).error_union.error_set
771else
772 void;
773
774769pub const StackIterator = struct {
775770 // Skip every frame before this address is found.
776771 first_address: ?usize,
......@@ -783,7 +778,7 @@ pub const StackIterator = struct {
783778 unwind_state: if (have_ucontext) ?struct {
784779 debug_info: *SelfInfo,
785780 dwarf_context: SelfInfo.UnwindContext,
786 last_error: ?UnwindError = null,
781 last_error: ?SelfInfo.Error = null,
787782 failed: bool = false,
788783 } else void = if (have_ucontext) null else {},
789784
......@@ -821,7 +816,7 @@ pub const StackIterator = struct {
821816 }
822817
823818 pub fn getLastError(it: *StackIterator) ?struct {
824 err: UnwindError,
819 err: SelfInfo.Error,
825820 address: usize,
826821 } {
827822 if (!have_ucontext) return null;
......@@ -1037,17 +1032,29 @@ fn printLastUnwindError(it: *StackIterator, debug_info: *SelfInfo, writer: *Writ
10371032 }
10381033}
10391034
1040fn printUnwindError(debug_info: *SelfInfo, writer: *Writer, address: usize, unwind_err: UnwindError, tty_config: tty.Config) !void {
1035fn printUnwindError(debug_info: *SelfInfo, writer: *Writer, address: usize, unwind_err: SelfInfo.Error, tty_config: tty.Config) !void {
10411036 const module_name = debug_info.getModuleNameForAddress(getDebugInfoAllocator(), address) catch |err| switch (err) {
1042 error.MissingDebugInfo => "???",
1037 error.InvalidDebugInfo, error.MissingDebugInfo, error.UnsupportedDebugInfo, error.ReadFailed => "???",
10431038 error.Unexpected, error.OutOfMemory => |e| return e,
10441039 };
10451040 try tty_config.setColor(writer, .dim);
1046 // MLUGG TODO this makes no sense given that MissingUnwindInfo exists?
1047 if (unwind_err == error.MissingDebugInfo) {
1048 try writer.print("Unwind information for `{s}:0x{x}` was not available, trace may be incomplete\n\n", .{ module_name, address });
1049 } else {
1050 try writer.print("Unwind error at address `{s}:0x{x}` ({}), trace may be incomplete\n\n", .{ module_name, address, unwind_err });
1041 switch (unwind_err) {
1042 error.Unexpected, error.OutOfMemory => |e| return e,
1043 error.MissingDebugInfo => {
1044 try writer.print("Unwind information for `{s}:0x{x}` was not available, trace may be incomplete\n\n", .{ module_name, address });
1045 },
1046 error.InvalidDebugInfo,
1047 error.UnsupportedDebugInfo,
1048 error.ReadFailed,
1049 => {
1050 const caption: []const u8 = switch (unwind_err) {
1051 error.InvalidDebugInfo => "invalid unwind info",
1052 error.UnsupportedDebugInfo => "unsupported unwind info",
1053 error.ReadFailed => "filesystem error",
1054 else => unreachable,
1055 };
1056 try writer.print("Unwind error at address `{s}:0x{x}` ({s}), trace may be incomplete\n\n", .{ module_name, address, caption });
1057 },
10511058 }
10521059 try tty_config.setColor(writer, .reset);
10531060}
......@@ -1055,12 +1062,17 @@ fn printUnwindError(debug_info: *SelfInfo, writer: *Writer, address: usize, unwi
10551062pub fn printSourceAtAddress(debug_info: *SelfInfo, writer: *Writer, address: usize, tty_config: tty.Config) !void {
10561063 const gpa = getDebugInfoAllocator();
10571064 const symbol: Symbol = debug_info.getSymbolAtAddress(gpa, address) catch |err| switch (err) {
1058 error.MissingDebugInfo, error.InvalidDebugInfo => .{
1059 .name = null,
1060 .compile_unit_name = null,
1061 .source_location = null,
1065 error.MissingDebugInfo,
1066 error.UnsupportedDebugInfo,
1067 error.InvalidDebugInfo,
1068 => .{ .name = null, .compile_unit_name = null, .source_location = null },
1069 error.ReadFailed => s: {
1070 try tty_config.setColor(writer, .dim);
1071 try writer.print("Failed to read debug info from filesystem, trace may be incomplete\n\n", .{});
1072 try tty_config.setColor(writer, .reset);
1073 break :s .{ .name = null, .compile_unit_name = null, .source_location = null };
10621074 },
1063 else => |e| return e,
1075 error.OutOfMemory, error.Unexpected => |e| return e,
10641076 };
10651077 defer if (symbol.source_location) |sl| gpa.free(sl.file_name);
10661078 return printLineInfo(
......@@ -1069,7 +1081,7 @@ pub fn printSourceAtAddress(debug_info: *SelfInfo, writer: *Writer, address: usi
10691081 address,
10701082 symbol.name orelse "???",
10711083 symbol.compile_unit_name orelse debug_info.getModuleNameForAddress(gpa, address) catch |err| switch (err) {
1072 error.MissingDebugInfo => "???",
1084 error.InvalidDebugInfo, error.MissingDebugInfo, error.UnsupportedDebugInfo, error.ReadFailed => "???",
10731085 error.Unexpected, error.OutOfMemory => |e| return e,
10741086 },
10751087 tty_config,
lib/std/debug/Dwarf.zig+1-1
......@@ -1418,7 +1418,7 @@ pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
14181418 4 => 14, // R14
14191419 5 => 15, // R15
14201420 6 => 6, // RBP
1421 else => error.InvalidUnwindRegisterNumber,
1421 else => error.InvalidRegister,
14221422 };
14231423}
14241424
lib/std/debug/SelfInfo.zig+66-6
......@@ -1,8 +1,6 @@
11//! Cross-platform abstraction for this binary's own debug information, with a
22//! goal of minimal code bloat and compilation speed penalty.
33
4// MLUGG TODO: audit use of errors in this file. ideally, introduce some concrete error sets
5
64const builtin = @import("builtin");
75const native_os = builtin.os.tag;
86const native_endian = native_arch.endian();
......@@ -21,6 +19,19 @@ const SelfInfo = @This();
2119modules: std.AutoArrayHashMapUnmanaged(usize, Module.DebugInfo),
2220lookup_cache: Module.LookupCache,
2321
22pub const Error = error{
23 /// The required debug info is invalid or corrupted.
24 InvalidDebugInfo,
25 /// The required debug info could not be found.
26 MissingDebugInfo,
27 /// The required debug info was found, and may be valid, but is not supported by this implementation.
28 UnsupportedDebugInfo,
29 /// The required debug info could not be read from disk due to some IO error.
30 ReadFailed,
31 OutOfMemory,
32 Unexpected,
33};
34
2435/// Indicates whether the `SelfInfo` implementation has support for this target.
2536pub const target_supported: bool = switch (native_os) {
2637 .linux,
......@@ -82,7 +93,7 @@ test {
8293 _ = &deinit;
8394}
8495
85pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) !usize {
96pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) Error!usize {
8697 comptime assert(supports_unwinding);
8798 const module: Module = try .lookup(&self.lookup_cache, gpa, context.pc);
8899 const gop = try self.modules.getOrPut(gpa, module.key());
......@@ -92,7 +103,7 @@ pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) !us
92103 return module.unwindFrame(gpa, gop.value_ptr, context);
93104}
94105
95pub fn getSymbolAtAddress(self: *SelfInfo, gpa: Allocator, address: usize) !std.debug.Symbol {
106pub fn getSymbolAtAddress(self: *SelfInfo, gpa: Allocator, address: usize) Error!std.debug.Symbol {
96107 comptime assert(target_supported);
97108 const module: Module = try .lookup(&self.lookup_cache, gpa, address);
98109 const gop = try self.modules.getOrPut(gpa, module.key());
......@@ -102,7 +113,7 @@ pub fn getSymbolAtAddress(self: *SelfInfo, gpa: Allocator, address: usize) !std.
102113 return module.getSymbolAtAddress(gpa, gop.value_ptr, address);
103114}
104115
105pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) error{ Unexpected, OutOfMemory, MissingDebugInfo }![]const u8 {
116pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) Error![]const u8 {
106117 comptime assert(target_supported);
107118 const module: Module = try .lookup(&self.lookup_cache, gpa, address);
108119 return module.name;
......@@ -271,12 +282,61 @@ pub const UnwindContext = struct {
271282 /// may require lazily loading the data in those sections.
272283 ///
273284 /// `explicit_fde_offset` is for cases where the FDE offset is known, such as when __unwind_info
274 /// defers unwinding to DWARF. This is an offset into the `.eh_frame` section.
275285 pub fn unwindFrameDwarf(
276286 context: *UnwindContext,
277287 unwind: *const Dwarf.Unwind,
278288 load_offset: usize,
279289 explicit_fde_offset: ?usize,
290 ) Error!usize {
291 return unwindFrameDwarfInner(context, unwind, load_offset, explicit_fde_offset) catch |err| switch (err) {
292 error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e,
293
294 error.UnimplementedArch,
295 error.UnimplementedOs,
296 error.ThreadContextNotSupported,
297 error.UnimplementedRegisterRule,
298 error.UnsupportedAddrSize,
299 error.UnsupportedDwarfVersion,
300 error.UnimplementedUserOpcode,
301 error.UnimplementedExpressionCall,
302 error.UnimplementedOpcode,
303 error.UnimplementedTypedComparison,
304 error.UnimplementedTypeConversion,
305 error.UnknownExpressionOpcode,
306 => return error.UnsupportedDebugInfo,
307
308 error.InvalidRegister,
309 error.RegisterContextRequired,
310 error.ReadFailed,
311 error.EndOfStream,
312 error.IncompatibleRegisterSize,
313 error.Overflow,
314 error.StreamTooLong,
315 error.InvalidOperand,
316 error.InvalidOpcode,
317 error.InvalidOperation,
318 error.InvalidCFARule,
319 error.IncompleteExpressionContext,
320 error.InvalidCFAOpcode,
321 error.InvalidExpression,
322 error.InvalidFrameBase,
323 error.InvalidIntegralTypeSize,
324 error.InvalidSubExpression,
325 error.InvalidTypeLength,
326 error.TruncatedIntegralType,
327 error.DivisionByZero,
328 error.InvalidExpressionValue,
329 error.NoExpressionValue,
330 error.RegisterSizeMismatch,
331 error.InvalidCFA,
332 => return error.InvalidDebugInfo,
333 };
334 }
335 fn unwindFrameDwarfInner(
336 context: *UnwindContext,
337 unwind: *const Dwarf.Unwind,
338 load_offset: usize,
339 explicit_fde_offset: ?usize,
280340 ) !usize {
281341 if (!supports_unwinding) return error.UnsupportedCpuArchitecture;
282342 if (context.pc == 0) return 0;
lib/std/debug/SelfInfo/DarwinModule.zig+56-44
......@@ -7,7 +7,9 @@ pub fn key(m: *const DarwinModule) usize {
77 return m.text_base;
88}
99
10pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !DarwinModule {
10/// No cache needed, because `_dyld_get_image_header` etc are already fast.
11pub const LookupCache = void;
12pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) Error!DarwinModule {
1113 _ = cache;
1214 _ = gpa;
1315 const image_count = std.c._dyld_image_count();
......@@ -186,8 +188,11 @@ fn loadFullInfo(module: *const DarwinModule, gpa: Allocator) !DebugInfo.Full {
186188 .ofiles = .empty,
187189 };
188190}
189pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {
190 if (di.full == null) di.full = try module.loadFullInfo(gpa);
191pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, address: usize) Error!std.debug.Symbol {
192 if (di.full == null) di.full = module.loadFullInfo(gpa) catch |err| switch (err) {
193 error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory, error.Unexpected => |e| return e,
194 else => return error.ReadFailed,
195 };
191196 const full = &di.full.?;
192197
193198 const vaddr = address - module.load_offset;
......@@ -215,14 +220,9 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu
215220 const gop = try full.ofiles.getOrPut(gpa, symbol.ofile);
216221 if (!gop.found_existing) {
217222 const o_file_path = mem.sliceTo(full.strings[symbol.ofile..], 0);
218 gop.value_ptr.* = DebugInfo.loadOFile(gpa, o_file_path) catch |err| {
219 defer _ = full.ofiles.pop().?;
220 switch (err) {
221 error.MissingDebugInfo,
222 error.InvalidDebugInfo,
223 => return sym_only_result,
224 else => |e| return e,
225 }
223 gop.value_ptr.* = DebugInfo.loadOFile(gpa, o_file_path) catch {
224 _ = full.ofiles.pop().?;
225 return sym_only_result;
226226 };
227227 }
228228 break :of gop.value_ptr;
......@@ -234,10 +234,7 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu
234234 ) orelse return sym_only_result;
235235 const symbol_ofile_vaddr = o_file.symtab[symbol_index].n_value;
236236
237 const compile_unit = o_file.dwarf.findCompileUnit(native_endian, symbol_ofile_vaddr) catch |err| switch (err) {
238 error.MissingDebugInfo, error.InvalidDebugInfo => return sym_only_result,
239 else => |e| return e,
240 };
237 const compile_unit = o_file.dwarf.findCompileUnit(native_endian, symbol_ofile_vaddr) catch return sym_only_result;
241238
242239 return .{
243240 .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
255252 native_endian,
256253 compile_unit,
257254 symbol_ofile_vaddr + address_symbol_offset,
258 ) catch |err| switch (err) {
259 error.MissingDebugInfo, error.InvalidDebugInfo => null,
260 else => return err,
261 },
255 ) catch null,
262256 };
263257}
264258/// Unwind a frame using MachO compact unwind info (from __unwind_info).
265259/// If the compact encoding can't encode a way to unwind a frame, it will
266260/// defer unwinding to DWARF, in which case `.eh_frame` will be used if available.
267pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize {
261pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!usize {
262 return unwindFrameInner(module, gpa, di, context) catch |err| switch (err) {
263 error.InvalidDebugInfo,
264 error.MissingDebugInfo,
265 error.UnsupportedDebugInfo,
266 error.ReadFailed,
267 error.OutOfMemory,
268 error.Unexpected,
269 => |e| return e,
270 error.UnimplementedArch,
271 error.UnimplementedOs,
272 error.ThreadContextNotSupported,
273 => return error.UnsupportedDebugInfo,
274 error.InvalidRegister,
275 error.RegisterContextRequired,
276 error.IncompatibleRegisterSize,
277 => return error.InvalidDebugInfo,
278 };
279}
280fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize {
268281 _ = gpa;
269282 if (di.unwind == null) di.unwind = module.loadUnwindInfo();
270283 const unwind = &di.unwind.?;
271284
272 const unwind_info = unwind.unwind_info orelse return error.MissingUnwindInfo;
273 if (unwind_info.len < @sizeOf(macho.unwind_info_section_header)) return error.InvalidUnwindInfo;
285 const unwind_info = unwind.unwind_info orelse return error.MissingDebugInfo;
286 if (unwind_info.len < @sizeOf(macho.unwind_info_section_header)) return error.InvalidDebugInfo;
274287 const header: *align(1) const macho.unwind_info_section_header = @ptrCast(unwind_info);
275288
276289 const index_byte_count = header.indexCount * @sizeOf(macho.unwind_info_section_header_index_entry);
277 if (unwind_info.len < header.indexSectionOffset + index_byte_count) return error.InvalidUnwindInfo;
290 if (unwind_info.len < header.indexSectionOffset + index_byte_count) return error.InvalidDebugInfo;
278291 const indices: []align(1) const macho.unwind_info_section_header_index_entry = @ptrCast(unwind_info[header.indexSectionOffset..][0..index_byte_count]);
279 if (indices.len == 0) return error.MissingUnwindInfo;
292 if (indices.len == 0) return error.MissingDebugInfo;
280293
281294 // offset of the PC into the `__TEXT` segment
282295 const pc_text_offset = context.pc - module.text_base;
......@@ -296,15 +309,15 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
296309 break :index .{ indices[left].secondLevelPagesSectionOffset, indices[left].functionOffset };
297310 };
298311 // An offset of 0 is a sentinel indicating a range does not have unwind info.
299 if (start_offset == 0) return error.MissingUnwindInfo;
312 if (start_offset == 0) return error.MissingDebugInfo;
300313
301314 const common_encodings_byte_count = header.commonEncodingsArrayCount * @sizeOf(macho.compact_unwind_encoding_t);
302 if (unwind_info.len < header.commonEncodingsArraySectionOffset + common_encodings_byte_count) return error.InvalidUnwindInfo;
315 if (unwind_info.len < header.commonEncodingsArraySectionOffset + common_encodings_byte_count) return error.InvalidDebugInfo;
303316 const common_encodings: []align(1) const macho.compact_unwind_encoding_t = @ptrCast(
304317 unwind_info[header.commonEncodingsArraySectionOffset..][0..common_encodings_byte_count],
305318 );
306319
307 if (unwind_info.len < start_offset + @sizeOf(macho.UNWIND_SECOND_LEVEL)) return error.InvalidUnwindInfo;
320 if (unwind_info.len < start_offset + @sizeOf(macho.UNWIND_SECOND_LEVEL)) return error.InvalidDebugInfo;
308321 const kind: *align(1) const macho.UNWIND_SECOND_LEVEL = @ptrCast(unwind_info[start_offset..]);
309322
310323 const entry: struct {
......@@ -312,15 +325,15 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
312325 raw_encoding: u32,
313326 } = switch (kind.*) {
314327 .REGULAR => entry: {
315 if (unwind_info.len < start_offset + @sizeOf(macho.unwind_info_regular_second_level_page_header)) return error.InvalidUnwindInfo;
328 if (unwind_info.len < start_offset + @sizeOf(macho.unwind_info_regular_second_level_page_header)) return error.InvalidDebugInfo;
316329 const page_header: *align(1) const macho.unwind_info_regular_second_level_page_header = @ptrCast(unwind_info[start_offset..]);
317330
318331 const entries_byte_count = page_header.entryCount * @sizeOf(macho.unwind_info_regular_second_level_entry);
319 if (unwind_info.len < start_offset + entries_byte_count) return error.InvalidUnwindInfo;
332 if (unwind_info.len < start_offset + entries_byte_count) return error.InvalidDebugInfo;
320333 const entries: []align(1) const macho.unwind_info_regular_second_level_entry = @ptrCast(
321334 unwind_info[start_offset + page_header.entryPageOffset ..][0..entries_byte_count],
322335 );
323 if (entries.len == 0) return error.InvalidUnwindInfo;
336 if (entries.len == 0) return error.InvalidDebugInfo;
324337
325338 var left: usize = 0;
326339 var len: usize = entries.len;
......@@ -339,15 +352,15 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
339352 };
340353 },
341354 .COMPRESSED => entry: {
342 if (unwind_info.len < start_offset + @sizeOf(macho.unwind_info_compressed_second_level_page_header)) return error.InvalidUnwindInfo;
355 if (unwind_info.len < start_offset + @sizeOf(macho.unwind_info_compressed_second_level_page_header)) return error.InvalidDebugInfo;
343356 const page_header: *align(1) const macho.unwind_info_compressed_second_level_page_header = @ptrCast(unwind_info[start_offset..]);
344357
345358 const entries_byte_count = page_header.entryCount * @sizeOf(macho.UnwindInfoCompressedEntry);
346 if (unwind_info.len < start_offset + entries_byte_count) return error.InvalidUnwindInfo;
359 if (unwind_info.len < start_offset + entries_byte_count) return error.InvalidDebugInfo;
347360 const entries: []align(1) const macho.UnwindInfoCompressedEntry = @ptrCast(
348361 unwind_info[start_offset + page_header.entryPageOffset ..][0..entries_byte_count],
349362 );
350 if (entries.len == 0) return error.InvalidUnwindInfo;
363 if (entries.len == 0) return error.InvalidDebugInfo;
351364
352365 var left: usize = 0;
353366 var len: usize = entries.len;
......@@ -372,26 +385,26 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
372385
373386 const local_index = entry.encodingIndex - common_encodings.len;
374387 const local_encodings_byte_count = page_header.encodingsCount * @sizeOf(macho.compact_unwind_encoding_t);
375 if (unwind_info.len < start_offset + page_header.encodingsPageOffset + local_encodings_byte_count) return error.InvalidUnwindInfo;
388 if (unwind_info.len < start_offset + page_header.encodingsPageOffset + local_encodings_byte_count) return error.InvalidDebugInfo;
376389 const local_encodings: []align(1) const macho.compact_unwind_encoding_t = @ptrCast(
377390 unwind_info[start_offset + page_header.encodingsPageOffset ..][0..local_encodings_byte_count],
378391 );
379 if (local_index >= local_encodings.len) return error.InvalidUnwindInfo;
392 if (local_index >= local_encodings.len) return error.InvalidDebugInfo;
380393 break :entry .{
381394 .function_offset = function_offset,
382395 .raw_encoding = local_encodings[local_index],
383396 };
384397 },
385 else => return error.InvalidUnwindInfo,
398 else => return error.InvalidDebugInfo,
386399 };
387400
388 if (entry.raw_encoding == 0) return error.NoUnwindInfo;
401 if (entry.raw_encoding == 0) return error.MissingDebugInfo;
389402 const reg_context: Dwarf.abi.RegisterContext = .{ .eh_frame = false, .is_macho = true };
390403
391404 const encoding: macho.CompactUnwindEncoding = @bitCast(entry.raw_encoding);
392405 const new_ip = switch (builtin.cpu.arch) {
393406 .x86_64 => switch (encoding.mode.x86_64) {
394 .OLD => return error.UnimplementedUnwindEncoding,
407 .OLD => return error.UnsupportedDebugInfo,
395408 .RBP_FRAME => ip: {
396409 const frame = encoding.value.x86_64.frame;
397410
......@@ -493,7 +506,7 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
493506 break :ip new_ip;
494507 },
495508 .DWARF => {
496 const eh_frame = unwind.eh_frame orelse return error.MissingEhFrame;
509 const eh_frame = unwind.eh_frame orelse return error.MissingDebugInfo;
497510 const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - module.load_offset;
498511 return context.unwindFrameDwarf(
499512 &.initSection(.eh_frame, eh_frame_vaddr, eh_frame),
......@@ -503,7 +516,7 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
503516 },
504517 },
505518 .aarch64, .aarch64_be => switch (encoding.mode.arm64) {
506 .OLD => return error.UnimplementedUnwindEncoding,
519 .OLD => return error.UnsupportedDebugInfo,
507520 .FRAMELESS => ip: {
508521 const sp = (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).*;
509522 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,
512525 break :ip new_ip;
513526 },
514527 .DWARF => {
515 const eh_frame = unwind.eh_frame orelse return error.MissingEhFrame;
528 const eh_frame = unwind.eh_frame orelse return error.MissingDebugInfo;
516529 const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - module.load_offset;
517530 return context.unwindFrameDwarf(
518531 &.initSection(.eh_frame, eh_frame_vaddr, eh_frame),
......@@ -568,8 +581,6 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
568581 if (context.pc > 0) context.pc -= 1;
569582 return new_ip;
570583}
571/// No cache needed, because `_dyld_get_image_header` etc are already fast.
572pub const LookupCache = void;
573584pub const DebugInfo = struct {
574585 unwind: ?Unwind,
575586 // MLUGG TODO: awful field name
......@@ -785,7 +796,7 @@ const ip_reg_num = Dwarf.abi.ipRegNum(builtin.target.cpu.arch).?;
785796fn mapDebugInfoFile(path: []const u8) ![]align(std.heap.page_size_min) const u8 {
786797 const file = std.fs.cwd().openFile(path, .{}) catch |err| switch (err) {
787798 error.FileNotFound => return error.MissingDebugInfo,
788 else => |e| return e,
799 else => return error.ReadFailed,
789800 };
790801 defer file.close();
791802
......@@ -812,6 +823,7 @@ const mem = std.mem;
812823const posix = std.posix;
813824const testing = std.testing;
814825const UnwindContext = std.debug.SelfInfo.UnwindContext;
826const Error = std.debug.SelfInfo.Error;
815827const regBytes = Dwarf.abi.regBytes;
816828const regValueNative = Dwarf.abi.regValueNative;
817829
lib/std/debug/SelfInfo/ElfModule.zig+63-25
......@@ -21,7 +21,7 @@ pub const DebugInfo = struct {
2121pub fn key(m: ElfModule) usize {
2222 return m.load_offset;
2323}
24pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !ElfModule {
24pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) Error!ElfModule {
2525 _ = cache;
2626 _ = gpa;
2727 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 {
9292 };
9393 return error.MissingDebugInfo;
9494}
95fn loadDwarf(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) !void {
96 if (module.name.len > 0) {
97 di.loaded_elf = Dwarf.ElfModule.load(gpa, .{
95fn loadDwarf(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Error!void {
96 const load_result = if (module.name.len > 0) res: {
97 break :res Dwarf.ElfModule.load(gpa, .{
9898 .root_dir = .cwd(),
9999 .sub_path = module.name,
100 }, module.build_id, null, null, null) catch |err| switch (err) {
101 error.FileNotFound => return error.MissingDebugInfo,
102 error.Overflow => return error.InvalidDebugInfo,
103 else => |e| return e,
100 }, module.build_id, null, null, null);
101 } else res: {
102 const path = std.fs.selfExePathAlloc(gpa) catch |err| switch (err) {
103 error.OutOfMemory => |e| return e,
104 else => return error.ReadFailed,
104105 };
105 } else {
106 const path = try std.fs.selfExePathAlloc(gpa);
107106 defer gpa.free(path);
108 di.loaded_elf = Dwarf.ElfModule.load(gpa, .{
107 break :res Dwarf.ElfModule.load(gpa, .{
109108 .root_dir = .cwd(),
110109 .sub_path = path,
111 }, module.build_id, null, null, null) catch |err| switch (err) {
112 error.FileNotFound => return error.MissingDebugInfo,
113 error.Overflow => return error.InvalidDebugInfo,
114 else => |e| return e,
115 };
116 }
110 }, module.build_id, null, null, null);
111 };
112 di.loaded_elf = load_result catch |err| switch (err) {
113 error.FileNotFound => return error.MissingDebugInfo,
114
115 error.OutOfMemory,
116 error.InvalidDebugInfo,
117 error.MissingDebugInfo,
118 error.Unexpected,
119 => |e| return e,
120
121 error.InvalidElfEndian,
122 error.InvalidElfMagic,
123 error.InvalidElfVersion,
124 error.InvalidUtf8,
125 error.InvalidWtf8,
126 error.EndOfStream,
127 error.Overflow,
128 error.UnimplementedDwarfForeignEndian, // this should be impossible as we're looking at the debug info for this process
129 => return error.InvalidDebugInfo,
130
131 else => return error.ReadFailed,
132 };
117133}
118pub fn getSymbolAtAddress(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {
134pub fn getSymbolAtAddress(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, address: usize) Error!std.debug.Symbol {
119135 if (di.loaded_elf == null) try module.loadDwarf(gpa, di);
120136 const vaddr = address - module.load_offset;
121 return di.loaded_elf.?.dwarf.getSymbol(gpa, native_endian, vaddr);
137 return di.loaded_elf.?.dwarf.getSymbol(gpa, native_endian, vaddr) catch |err| switch (err) {
138 error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e,
139 error.ReadFailed,
140 error.EndOfStream,
141 error.Overflow,
142 error.StreamTooLong,
143 => return error.InvalidDebugInfo,
144 };
122145}
123fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) !void {
124 const section_bytes = module.gnu_eh_frame orelse return error.MissingUnwindInfo; // MLUGG TODO: load from file
146fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Error!void {
147 const section_bytes = module.gnu_eh_frame orelse return error.MissingDebugInfo; // MLUGG TODO: load from file
148
125149 const section_vaddr: u64 = @intFromPtr(section_bytes.ptr) - module.load_offset;
126 const header: Dwarf.Unwind.EhFrameHeader = try .parse(section_vaddr, section_bytes, @sizeOf(usize), native_endian);
127 di.unwind = .initEhFrameHdr(header, section_vaddr, @ptrFromInt(module.load_offset + header.eh_frame_vaddr));
128 try di.unwind.?.prepareLookup(gpa, @sizeOf(usize), native_endian);
150 const header = Dwarf.Unwind.EhFrameHeader.parse(section_vaddr, section_bytes, @sizeOf(usize), native_endian) catch |err| switch (err) {
151 error.ReadFailed => unreachable, // it's all fixed buffers
152 error.InvalidDebugInfo => |e| return e,
153 error.EndOfStream, error.Overflow => return error.InvalidDebugInfo,
154 error.UnsupportedAddrSize => return error.UnsupportedDebugInfo,
155 };
156
157 var unwind: Dwarf.Unwind = .initEhFrameHdr(header, section_vaddr, @ptrFromInt(module.load_offset + header.eh_frame_vaddr));
158 unwind.prepareLookup(gpa, @sizeOf(usize), native_endian) catch |err| switch (err) {
159 error.ReadFailed => unreachable, // it's all fixed buffers
160 error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e,
161 error.EndOfStream, error.Overflow, error.StreamTooLong => return error.InvalidDebugInfo,
162 error.UnsupportedAddrSize, error.UnsupportedDwarfVersion => return error.UnsupportedDebugInfo,
163 };
164
165 di.unwind = unwind;
129166}
130pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize {
167pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!usize {
131168 if (di.unwind == null) try module.loadUnwindInfo(gpa, di);
132169 return context.unwindFrameDwarf(&di.unwind.?, module.load_offset, null);
133170}
......@@ -140,6 +177,7 @@ const Dwarf = std.debug.Dwarf;
140177const elf = std.elf;
141178const mem = std.mem;
142179const UnwindContext = std.debug.SelfInfo.UnwindContext;
180const Error = std.debug.SelfInfo.Error;
143181
144182const builtin = @import("builtin");
145183const native_endian = builtin.target.cpu.arch.endian();
lib/std/debug/SelfInfo/WindowsModule.zig+12-7
......@@ -5,7 +5,7 @@ handle: windows.HMODULE,
55pub fn key(m: WindowsModule) usize {
66 return m.base_address;
77}
8pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !WindowsModule {
8pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) std.debug.SelfInfo.Error!WindowsModule {
99 if (lookupInCache(cache, address)) |m| return m;
1010 {
1111 // Check a new module hasn't been loaded
......@@ -29,18 +29,23 @@ pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) !WindowsModul
2929 if (lookupInCache(cache, address)) |m| return m;
3030 return error.MissingDebugInfo;
3131}
32pub fn getSymbolAtAddress(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {
33 if (!di.loaded) try module.loadLocationInfo(gpa, di);
32pub fn getSymbolAtAddress(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo, address: usize) std.debug.SelfInfo.Error!std.debug.Symbol {
33 if (!di.loaded) module.loadDebugInfo(gpa, di) catch |err| switch (err) {
34 error.OutOfMemory, error.InvalidDebugInfo, error.MissingDebugInfo, error.Unexpected => |e| return e,
35 error.FileNotFound => return error.MissingDebugInfo,
36 error.UnknownPDBVersion => return error.UnsupportedDebugInfo,
37 else => return error.ReadFailed,
38 };
3439 // Translate the runtime address into a virtual address into the module
3540 const vaddr = address - module.base_address;
3641
3742 if (di.pdb != null) {
38 if (try di.getSymbolFromPdb(vaddr)) |symbol| return symbol;
43 if (di.getSymbolFromPdb(vaddr) catch return error.InvalidDebugInfo) |symbol| return symbol;
3944 }
4045
4146 if (di.dwarf) |*dwarf| {
4247 const dwarf_address = vaddr + di.coff_image_base;
43 return dwarf.getSymbol(gpa, native_endian, dwarf_address);
48 return dwarf.getSymbol(gpa, native_endian, dwarf_address) catch return error.InvalidDebugInfo;
4449 }
4550
4651 return error.MissingDebugInfo;
......@@ -59,7 +64,7 @@ fn lookupInCache(cache: *const LookupCache, address: usize) ?WindowsModule {
5964 }
6065 return null;
6166}
62fn loadLocationInfo(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo) !void {
67fn loadDebugInfo(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo) !void {
6368 const mapped_ptr: [*]const u8 = @ptrFromInt(module.base_address);
6469 const mapped = mapped_ptr[0..module.size];
6570 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
151156
152157 di.pdb = Pdb.init(gpa, path) catch |err| switch (err) {
153158 error.FileNotFound, error.IsDir => break :pdb,
154 else => return err,
159 else => |e| return e,
155160 };
156161 try di.pdb.?.parseInfoStream();
157162 try di.pdb.?.parseDbiStream();