authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-08-01 22:04:40-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-08-01 23:11:59-07:00
log48d584e3a33a76ef4ea643905a11d311e9ed8bbf
treec6dbeb4913b297c28fcd57add186d444279862d2
parent290966c2497dc9d212bf9d4bd0fecee4988091a5

std.debug: reorg and clarify API goals

After this commit: `std.debug.SelfInfo` is a cross-platform abstraction for the current executable's own debug information, with a goal of minimal code bloat and compilation speed penalty. `std.debug.Dwarf` does not assume the current executable is itself the thing being debugged, however, it does assume the debug info has the same CPU architecture and OS as the current executable. It is planned to remove this limitation.

8 files changed, 1434 insertions(+), 1419 deletions(-)

lib/std/debug.zig+109-124
......@@ -13,6 +13,7 @@ const native_arch = builtin.cpu.arch;
1313const native_os = builtin.os.tag;
1414const native_endian = native_arch.endian();
1515
16pub const MemoryAccessor = @import("debug/MemoryAccessor.zig");
1617pub const Dwarf = @import("debug/Dwarf.zig");
1718pub const Pdb = @import("debug/Pdb.zig");
1819pub const SelfInfo = @import("debug/SelfInfo.zig");
......@@ -243,7 +244,7 @@ pub inline fn getContext(context: *ThreadContext) bool {
243244/// Tries to print the stack trace starting from the supplied base pointer to stderr,
244245/// unbuffered, and ignores any error returned.
245246/// TODO multithreaded awareness
246pub fn dumpStackTraceFromBase(context: *const ThreadContext) void {
247pub fn dumpStackTraceFromBase(context: *ThreadContext) void {
247248 nosuspend {
248249 if (comptime builtin.target.isWasm()) {
249250 if (native_os == .wasi) {
......@@ -545,7 +546,7 @@ pub const StackIterator = struct {
545546 // using DWARF and MachO unwind info.
546547 unwind_state: if (have_ucontext) ?struct {
547548 debug_info: *SelfInfo,
548 dwarf_context: Dwarf.UnwindContext,
549 dwarf_context: SelfInfo.UnwindContext,
549550 last_error: ?UnwindError = null,
550551 failed: bool = false,
551552 } else void = if (have_ucontext) null else {},
......@@ -569,16 +570,16 @@ pub const StackIterator = struct {
569570 };
570571 }
571572
572 pub fn initWithContext(first_address: ?usize, debug_info: *SelfInfo, context: *const posix.ucontext_t) !StackIterator {
573 pub fn initWithContext(first_address: ?usize, debug_info: *SelfInfo, context: *posix.ucontext_t) !StackIterator {
573574 // The implementation of DWARF unwinding on aarch64-macos is not complete. However, Apple mandates that
574575 // the frame pointer register is always used, so on this platform we can safely use the FP-based unwinder.
575 if (comptime builtin.target.isDarwin() and native_arch == .aarch64) {
576 if (builtin.target.isDarwin() and native_arch == .aarch64) {
576577 return init(first_address, context.mcontext.ss.fp);
577578 } else {
578579 var iterator = init(first_address, null);
579580 iterator.unwind_state = .{
580581 .debug_info = debug_info,
581 .dwarf_context = try Dwarf.UnwindContext.init(debug_info.allocator, context),
582 .dwarf_context = try SelfInfo.UnwindContext.init(debug_info.allocator, context),
582583 };
583584
584585 return iterator;
......@@ -644,116 +645,6 @@ pub const StackIterator = struct {
644645 return address;
645646 }
646647
647 fn isValidMemory(address: usize) bool {
648 // We are unable to determine validity of memory for freestanding targets
649 if (native_os == .freestanding or native_os == .uefi) return true;
650
651 const aligned_address = address & ~@as(usize, @intCast((mem.page_size - 1)));
652 if (aligned_address == 0) return false;
653 const aligned_memory = @as([*]align(mem.page_size) u8, @ptrFromInt(aligned_address))[0..mem.page_size];
654
655 if (native_os == .windows) {
656 var memory_info: windows.MEMORY_BASIC_INFORMATION = undefined;
657
658 // The only error this function can throw is ERROR_INVALID_PARAMETER.
659 // supply an address that invalid i'll be thrown.
660 const rc = windows.VirtualQuery(aligned_memory, &memory_info, aligned_memory.len) catch {
661 return false;
662 };
663
664 // Result code has to be bigger than zero (number of bytes written)
665 if (rc == 0) {
666 return false;
667 }
668
669 // Free pages cannot be read, they are unmapped
670 if (memory_info.State == windows.MEM_FREE) {
671 return false;
672 }
673
674 return true;
675 } else if (have_msync) {
676 posix.msync(aligned_memory, posix.MSF.ASYNC) catch |err| {
677 switch (err) {
678 error.UnmappedMemory => return false,
679 else => unreachable,
680 }
681 };
682
683 return true;
684 } else {
685 // We are unable to determine validity of memory on this target.
686 return true;
687 }
688 }
689
690 pub const MemoryAccessor = struct {
691 var cached_pid: posix.pid_t = -1;
692
693 mem: switch (native_os) {
694 .linux => File,
695 else => void,
696 },
697
698 pub const init: MemoryAccessor = .{
699 .mem = switch (native_os) {
700 .linux => .{ .handle = -1 },
701 else => {},
702 },
703 };
704
705 fn read(ma: *MemoryAccessor, address: usize, buf: []u8) bool {
706 switch (native_os) {
707 .linux => while (true) switch (ma.mem.handle) {
708 -2 => break,
709 -1 => {
710 const linux = std.os.linux;
711 const pid = switch (@atomicLoad(posix.pid_t, &cached_pid, .monotonic)) {
712 -1 => pid: {
713 const pid = linux.getpid();
714 @atomicStore(posix.pid_t, &cached_pid, pid, .monotonic);
715 break :pid pid;
716 },
717 else => |pid| pid,
718 };
719 const bytes_read = linux.process_vm_readv(
720 pid,
721 &.{.{ .base = buf.ptr, .len = buf.len }},
722 &.{.{ .base = @ptrFromInt(address), .len = buf.len }},
723 0,
724 );
725 switch (linux.E.init(bytes_read)) {
726 .SUCCESS => return bytes_read == buf.len,
727 .FAULT => return false,
728 .INVAL, .PERM, .SRCH => unreachable, // own pid is always valid
729 .NOMEM => {},
730 .NOSYS => {}, // QEMU is known not to implement this syscall.
731 else => unreachable, // unexpected
732 }
733 var path_buf: [
734 std.fmt.count("/proc/{d}/mem", .{math.minInt(posix.pid_t)})
735 ]u8 = undefined;
736 const path = std.fmt.bufPrint(&path_buf, "/proc/{d}/mem", .{pid}) catch
737 unreachable;
738 ma.mem = std.fs.openFileAbsolute(path, .{}) catch {
739 ma.mem.handle = -2;
740 break;
741 };
742 },
743 else => return (ma.mem.pread(buf, address) catch return false) == buf.len,
744 },
745 else => {},
746 }
747 if (!isValidMemory(address)) return false;
748 @memcpy(buf, @as([*]const u8, @ptrFromInt(address)));
749 return true;
750 }
751 pub fn load(ma: *MemoryAccessor, comptime Type: type, address: usize) ?Type {
752 var result: Type = undefined;
753 return if (ma.read(address, std.mem.asBytes(&result))) result else null;
754 }
755 };
756
757648 fn next_unwind(it: *StackIterator) !usize {
758649 const unwind_state = &it.unwind_state.?;
759650 const module = try unwind_state.debug_info.getModuleForAddress(unwind_state.dwarf_context.pc);
......@@ -762,7 +653,13 @@ pub const StackIterator = struct {
762653 // __unwind_info is a requirement for unwinding on Darwin. It may fall back to DWARF, but unwinding
763654 // via DWARF before attempting to use the compact unwind info will produce incorrect results.
764655 if (module.unwind_info) |unwind_info| {
765 if (Dwarf.unwindFrameMachO(&unwind_state.dwarf_context, &it.ma, unwind_info, module.eh_frame, module.base_address)) |return_address| {
656 if (SelfInfo.unwindFrameMachO(
657 &unwind_state.dwarf_context,
658 &it.ma,
659 unwind_info,
660 module.eh_frame,
661 module.base_address,
662 )) |return_address| {
766663 return return_address;
767664 } else |err| {
768665 if (err != error.RequiresDWARFUnwind) return err;
......@@ -773,7 +670,7 @@ pub const StackIterator = struct {
773670 }
774671
775672 if (try module.getDwarfInfoForAddress(unwind_state.debug_info.allocator, unwind_state.dwarf_context.pc)) |di| {
776 return di.unwindFrame(&unwind_state.dwarf_context, &it.ma, null);
673 return SelfInfo.unwindFrameDwarf(di, &unwind_state.dwarf_context, &it.ma, null);
777674 } else return error.MissingDebugInfo;
778675 }
779676
......@@ -822,11 +719,6 @@ pub const StackIterator = struct {
822719 }
823720};
824721
825const have_msync = switch (native_os) {
826 .wasi, .emscripten, .windows => false,
827 else => true,
828};
829
830722pub fn writeCurrentStackTrace(
831723 out_stream: anytype,
832724 debug_info: *SelfInfo,
......@@ -1333,7 +1225,7 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa
13331225 posix.abort();
13341226}
13351227
1336fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*const anyopaque) void {
1228fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*anyopaque) void {
13371229 const stderr = io.getStdErr().writer();
13381230 _ = switch (sig) {
13391231 posix.SIG.SEGV => if (native_arch == .x86_64 and native_os == .linux and code == 128) // SI_KERNEL
......@@ -1359,7 +1251,7 @@ fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*const anyo
13591251 .arm,
13601252 .aarch64,
13611253 => {
1362 const ctx: *const posix.ucontext_t = @ptrCast(@alignCast(ctx_ptr));
1254 const ctx: *posix.ucontext_t = @ptrCast(@alignCast(ctx_ptr));
13631255 dumpStackTraceFromBase(ctx);
13641256 },
13651257 else => {},
......@@ -1585,6 +1477,99 @@ pub const SafetyLock = struct {
15851477 }
15861478};
15871479
1480/// Deprecated. Don't use this, just read from your memory directly.
1481///
1482/// This only exists because someone was too lazy to rework logic that used to
1483/// operate on an open file to operate on a memory buffer instead.
1484pub const DeprecatedFixedBufferReader = struct {
1485 buf: []const u8,
1486 pos: usize = 0,
1487 endian: std.builtin.Endian,
1488
1489 pub const Error = error{ EndOfBuffer, Overflow, InvalidBuffer };
1490
1491 pub fn seekTo(fbr: *DeprecatedFixedBufferReader, pos: u64) Error!void {
1492 if (pos > fbr.buf.len) return error.EndOfBuffer;
1493 fbr.pos = @intCast(pos);
1494 }
1495
1496 pub fn seekForward(fbr: *DeprecatedFixedBufferReader, amount: u64) Error!void {
1497 if (fbr.buf.len - fbr.pos < amount) return error.EndOfBuffer;
1498 fbr.pos += @intCast(amount);
1499 }
1500
1501 pub inline fn readByte(fbr: *DeprecatedFixedBufferReader) Error!u8 {
1502 if (fbr.pos >= fbr.buf.len) return error.EndOfBuffer;
1503 defer fbr.pos += 1;
1504 return fbr.buf[fbr.pos];
1505 }
1506
1507 pub fn readByteSigned(fbr: *DeprecatedFixedBufferReader) Error!i8 {
1508 return @bitCast(try fbr.readByte());
1509 }
1510
1511 pub fn readInt(fbr: *DeprecatedFixedBufferReader, comptime T: type) Error!T {
1512 const size = @divExact(@typeInfo(T).Int.bits, 8);
1513 if (fbr.buf.len - fbr.pos < size) return error.EndOfBuffer;
1514 defer fbr.pos += size;
1515 return std.mem.readInt(T, fbr.buf[fbr.pos..][0..size], fbr.endian);
1516 }
1517
1518 pub fn readIntChecked(
1519 fbr: *DeprecatedFixedBufferReader,
1520 comptime T: type,
1521 ma: *MemoryAccessor,
1522 ) Error!T {
1523 if (ma.load(T, @intFromPtr(fbr.buf[fbr.pos..].ptr)) == null)
1524 return error.InvalidBuffer;
1525
1526 return fbr.readInt(T);
1527 }
1528
1529 pub fn readUleb128(fbr: *DeprecatedFixedBufferReader, comptime T: type) Error!T {
1530 return std.leb.readUleb128(T, fbr);
1531 }
1532
1533 pub fn readIleb128(fbr: *DeprecatedFixedBufferReader, comptime T: type) Error!T {
1534 return std.leb.readIleb128(T, fbr);
1535 }
1536
1537 pub fn readAddress(fbr: *DeprecatedFixedBufferReader, format: std.dwarf.Format) Error!u64 {
1538 return switch (format) {
1539 .@"32" => try fbr.readInt(u32),
1540 .@"64" => try fbr.readInt(u64),
1541 };
1542 }
1543
1544 pub fn readAddressChecked(
1545 fbr: *DeprecatedFixedBufferReader,
1546 format: std.dwarf.Format,
1547 ma: *MemoryAccessor,
1548 ) Error!u64 {
1549 return switch (format) {
1550 .@"32" => try fbr.readIntChecked(u32, ma),
1551 .@"64" => try fbr.readIntChecked(u64, ma),
1552 };
1553 }
1554
1555 pub fn readBytes(fbr: *DeprecatedFixedBufferReader, len: usize) Error![]const u8 {
1556 if (fbr.buf.len - fbr.pos < len) return error.EndOfBuffer;
1557 defer fbr.pos += len;
1558 return fbr.buf[fbr.pos..][0..len];
1559 }
1560
1561 pub fn readBytesTo(fbr: *DeprecatedFixedBufferReader, comptime sentinel: u8) Error![:sentinel]const u8 {
1562 const end = @call(.always_inline, std.mem.indexOfScalarPos, .{
1563 u8,
1564 fbr.buf,
1565 fbr.pos,
1566 sentinel,
1567 }) orelse return error.EndOfBuffer;
1568 defer fbr.pos = end + 1;
1569 return fbr.buf[fbr.pos..end :sentinel];
1570 }
1571};
1572
15881573/// Detect whether the program is being executed in the Valgrind virtual machine.
15891574///
15901575/// When Valgrind integrations are disabled, this returns comptime-known false.
lib/std/debug/Dwarf.zig+116-800
......@@ -1,23 +1,32 @@
11//! Implements parsing, decoding, and caching of DWARF information.
22//!
3//! This API does not assume the current executable is itself the thing being
4//! debugged, however, it does assume the debug info has the same CPU
5//! architecture and OS as the current executable. It is planned to remove this
6//! limitation.
7//!
38//! For unopinionated types and bits, see `std.dwarf`.
49
510const builtin = @import("builtin");
11const native_endian = builtin.cpu.arch.endian();
12
613const std = @import("../std.zig");
7const AT = DW.AT;
814const Allocator = std.mem.Allocator;
915const DW = std.dwarf;
16const AT = DW.AT;
1017const EH = DW.EH;
1118const FORM = DW.FORM;
1219const Format = DW.Format;
1320const RLE = DW.RLE;
14const StackIterator = std.debug.StackIterator;
1521const UT = DW.UT;
1622const assert = std.debug.assert;
1723const cast = std.math.cast;
1824const maxInt = std.math.maxInt;
19const native_endian = builtin.cpu.arch.endian();
2025const readInt = std.mem.readInt;
26const MemoryAccessor = std.debug.MemoryAccessor;
27
28/// Did I mention this is deprecated?
29const DeprecatedFixedBufferReader = std.debug.DeprecatedFixedBufferReader;
2130
2231const Dwarf = @This();
2332
......@@ -153,7 +162,7 @@ pub const FormValue = union(enum) {
153162 .string => |s| return s,
154163 .strp => |off| return di.getString(off),
155164 .line_strp => |off| return di.getLineString(off),
156 else => return badDwarf(),
165 else => return bad(),
157166 }
158167 }
159168
......@@ -162,8 +171,8 @@ pub const FormValue = union(enum) {
162171 inline .udata,
163172 .sdata,
164173 .sec_offset,
165 => |c| cast(U, c) orelse badDwarf(),
166 else => badDwarf(),
174 => |c| cast(U, c) orelse bad(),
175 else => bad(),
167176 };
168177 }
169178};
......@@ -237,25 +246,25 @@ pub const Die = struct {
237246 .string => |value| return value,
238247 .strp => |offset| return di.getString(offset),
239248 .strx => |index| {
240 const debug_str_offsets = di.section(.debug_str_offsets) orelse return badDwarf();
241 if (compile_unit.str_offsets_base == 0) return badDwarf();
249 const debug_str_offsets = di.section(.debug_str_offsets) orelse return bad();
250 if (compile_unit.str_offsets_base == 0) return bad();
242251 switch (compile_unit.format) {
243252 .@"32" => {
244253 const byte_offset = compile_unit.str_offsets_base + 4 * index;
245 if (byte_offset + 4 > debug_str_offsets.len) return badDwarf();
254 if (byte_offset + 4 > debug_str_offsets.len) return bad();
246255 const offset = readInt(u32, debug_str_offsets[byte_offset..][0..4], di.endian);
247256 return getStringGeneric(opt_str, offset);
248257 },
249258 .@"64" => {
250259 const byte_offset = compile_unit.str_offsets_base + 8 * index;
251 if (byte_offset + 8 > debug_str_offsets.len) return badDwarf();
260 if (byte_offset + 8 > debug_str_offsets.len) return bad();
252261 const offset = readInt(u64, debug_str_offsets[byte_offset..][0..8], di.endian);
253262 return getStringGeneric(opt_str, offset);
254263 },
255264 }
256265 },
257266 .line_strp => |offset| return di.getLineString(offset),
258 else => return badDwarf(),
267 else => return bad(),
259268 }
260269 }
261270};
......@@ -279,7 +288,7 @@ pub const ExceptionFrameHeader = struct {
279288 EH.PE.sdata8,
280289 => 16,
281290 // This is a binary search table, so all entries must be the same length
282 else => return badDwarf(),
291 else => return bad(),
283292 };
284293 }
285294
......@@ -287,7 +296,7 @@ pub const ExceptionFrameHeader = struct {
287296 self: ExceptionFrameHeader,
288297 comptime T: type,
289298 ptr: usize,
290 ma: *StackIterator.MemoryAccessor,
299 ma: *MemoryAccessor,
291300 eh_frame_len: ?usize,
292301 ) bool {
293302 if (eh_frame_len) |len| {
......@@ -304,7 +313,7 @@ pub const ExceptionFrameHeader = struct {
304313 /// If `eh_frame_len` is provided, then these checks can be skipped.
305314 pub fn findEntry(
306315 self: ExceptionFrameHeader,
307 ma: *StackIterator.MemoryAccessor,
316 ma: *MemoryAccessor,
308317 eh_frame_len: ?usize,
309318 eh_frame_hdr_ptr: usize,
310319 pc: usize,
......@@ -316,7 +325,7 @@ pub const ExceptionFrameHeader = struct {
316325 var left: usize = 0;
317326 var len: usize = self.fde_count;
318327
319 var fbr: FixedBufferReader = .{ .buf = self.entries, .endian = native_endian };
328 var fbr: DeprecatedFixedBufferReader = .{ .buf = self.entries, .endian = native_endian };
320329
321330 while (len > 1) {
322331 const mid = left + len / 2;
......@@ -326,7 +335,7 @@ pub const ExceptionFrameHeader = struct {
326335 .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),
327336 .follow_indirect = true,
328337 .data_rel_base = eh_frame_hdr_ptr,
329 }) orelse return badDwarf();
338 }) orelse return bad();
330339
331340 if (pc < pc_begin) {
332341 len /= 2;
......@@ -337,7 +346,7 @@ pub const ExceptionFrameHeader = struct {
337346 }
338347 }
339348
340 if (len == 0) return badDwarf();
349 if (len == 0) return bad();
341350 fbr.pos = left * entry_size;
342351
343352 // Read past the pc_begin field of the entry
......@@ -345,36 +354,36 @@ pub const ExceptionFrameHeader = struct {
345354 .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),
346355 .follow_indirect = true,
347356 .data_rel_base = eh_frame_hdr_ptr,
348 }) orelse return badDwarf();
357 }) orelse return bad();
349358
350359 const fde_ptr = cast(usize, try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{
351360 .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),
352361 .follow_indirect = true,
353362 .data_rel_base = eh_frame_hdr_ptr,
354 }) orelse return badDwarf()) orelse return badDwarf();
363 }) orelse return bad()) orelse return bad();
355364
356 if (fde_ptr < self.eh_frame_ptr) return badDwarf();
365 if (fde_ptr < self.eh_frame_ptr) return bad();
357366
358367 // Even if eh_frame_len is not specified, all ranges accssed are checked via MemoryAccessor
359368 const eh_frame = @as([*]const u8, @ptrFromInt(self.eh_frame_ptr))[0 .. eh_frame_len orelse maxInt(u32)];
360369
361370 const fde_offset = fde_ptr - self.eh_frame_ptr;
362 var eh_frame_fbr: FixedBufferReader = .{
371 var eh_frame_fbr: DeprecatedFixedBufferReader = .{
363372 .buf = eh_frame,
364373 .pos = fde_offset,
365374 .endian = native_endian,
366375 };
367376
368377 const fde_entry_header = try EntryHeader.read(&eh_frame_fbr, if (eh_frame_len == null) ma else null, .eh_frame);
369 if (!self.isValidPtr(u8, @intFromPtr(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]), ma, eh_frame_len)) return badDwarf();
370 if (fde_entry_header.type != .fde) return badDwarf();
378 if (!self.isValidPtr(u8, @intFromPtr(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]), ma, eh_frame_len)) return bad();
379 if (fde_entry_header.type != .fde) return bad();
371380
372381 // CIEs always come before FDEs (the offset is a subtraction), so we can assume this memory is readable
373382 const cie_offset = fde_entry_header.type.fde;
374383 try eh_frame_fbr.seekTo(cie_offset);
375384 const cie_entry_header = try EntryHeader.read(&eh_frame_fbr, if (eh_frame_len == null) ma else null, .eh_frame);
376 if (!self.isValidPtr(u8, @intFromPtr(&cie_entry_header.entry_bytes[cie_entry_header.entry_bytes.len - 1]), ma, eh_frame_len)) return badDwarf();
377 if (cie_entry_header.type != .cie) return badDwarf();
385 if (!self.isValidPtr(u8, @intFromPtr(&cie_entry_header.entry_bytes[cie_entry_header.entry_bytes.len - 1]), ma, eh_frame_len)) return bad();
386 if (cie_entry_header.type != .cie) return bad();
378387
379388 cie.* = try CommonInformationEntry.parse(
380389 cie_entry_header.entry_bytes,
......@@ -417,17 +426,17 @@ pub const EntryHeader = struct {
417426 }
418427
419428 /// Reads a header for either an FDE or a CIE, then advances the fbr to the position after the trailing structure.
420 /// `fbr` must be a FixedBufferReader backed by either the .eh_frame or .debug_frame sections.
429 /// `fbr` must be a DeprecatedFixedBufferReader backed by either the .eh_frame or .debug_frame sections.
421430 pub fn read(
422 fbr: *FixedBufferReader,
423 opt_ma: ?*StackIterator.MemoryAccessor,
431 fbr: *DeprecatedFixedBufferReader,
432 opt_ma: ?*MemoryAccessor,
424433 dwarf_section: Section.Id,
425434 ) !EntryHeader {
426435 assert(dwarf_section == .eh_frame or dwarf_section == .debug_frame);
427436
428437 const length_offset = fbr.pos;
429438 const unit_header = try readUnitHeader(fbr, opt_ma);
430 const unit_length = cast(usize, unit_header.unit_length) orelse return badDwarf();
439 const unit_length = cast(usize, unit_header.unit_length) orelse return bad();
431440 if (unit_length == 0) return .{
432441 .length_offset = length_offset,
433442 .format = unit_header.format,
......@@ -532,7 +541,7 @@ pub const CommonInformationEntry = struct {
532541 ) !CommonInformationEntry {
533542 if (addr_size_bytes > 8) return error.UnsupportedAddrSize;
534543
535 var fbr: FixedBufferReader = .{ .buf = cie_bytes, .endian = endian };
544 var fbr: DeprecatedFixedBufferReader = .{ .buf = cie_bytes, .endian = endian };
536545
537546 const version = try fbr.readByte();
538547 switch (dwarf_section) {
......@@ -550,15 +559,15 @@ pub const CommonInformationEntry = struct {
550559 while (aug_byte != 0) : (aug_byte = try fbr.readByte()) {
551560 switch (aug_byte) {
552561 'z' => {
553 if (aug_str_len != 0) return badDwarf();
562 if (aug_str_len != 0) return bad();
554563 has_aug_data = true;
555564 },
556565 'e' => {
557 if (has_aug_data or aug_str_len != 0) return badDwarf();
558 if (try fbr.readByte() != 'h') return badDwarf();
566 if (has_aug_data or aug_str_len != 0) return bad();
567 if (try fbr.readByte() != 'h') return bad();
559568 has_eh_data = true;
560569 },
561 else => if (has_eh_data) return badDwarf(),
570 else => if (has_eh_data) return bad(),
562571 }
563572
564573 aug_str_len += 1;
......@@ -604,7 +613,7 @@ pub const CommonInformationEntry = struct {
604613 fde_pointer_enc = try fbr.readByte();
605614 },
606615 'S', 'B', 'G' => {},
607 else => return badDwarf(),
616 else => return bad(),
608617 }
609618 }
610619
......@@ -666,17 +675,17 @@ pub const FrameDescriptionEntry = struct {
666675 ) !FrameDescriptionEntry {
667676 if (addr_size_bytes > 8) return error.InvalidAddrSize;
668677
669 var fbr: FixedBufferReader = .{ .buf = fde_bytes, .endian = endian };
678 var fbr: DeprecatedFixedBufferReader = .{ .buf = fde_bytes, .endian = endian };
670679
671680 const pc_begin = try readEhPointer(&fbr, cie.fde_pointer_enc, addr_size_bytes, .{
672681 .pc_rel_base = try pcRelBase(@intFromPtr(&fde_bytes[fbr.pos]), pc_rel_offset),
673682 .follow_indirect = is_runtime,
674 }) orelse return badDwarf();
683 }) orelse return bad();
675684
676685 const pc_range = try readEhPointer(&fbr, cie.fde_pointer_enc, addr_size_bytes, .{
677686 .pc_rel_base = 0,
678687 .follow_indirect = false,
679 }) orelse return badDwarf();
688 }) orelse return bad();
680689
681690 var aug_data: []const u8 = &[_]u8{};
682691 const lsda_pointer = if (cie.aug_str.len > 0) blk: {
......@@ -708,54 +717,6 @@ pub const FrameDescriptionEntry = struct {
708717 }
709718};
710719
711pub const UnwindContext = struct {
712 allocator: Allocator,
713 cfa: ?usize,
714 pc: usize,
715 thread_context: *std.debug.ThreadContext,
716 reg_context: abi.RegisterContext,
717 vm: call_frame.VirtualMachine,
718 stack_machine: expression.StackMachine(.{ .call_frame_context = true }),
719
720 pub fn init(
721 allocator: Allocator,
722 thread_context: *const std.debug.ThreadContext,
723 ) !UnwindContext {
724 const pc = abi.stripInstructionPtrAuthCode(
725 (try abi.regValueNative(
726 usize,
727 thread_context,
728 abi.ipRegNum(),
729 null,
730 )).*,
731 );
732
733 const context_copy = try allocator.create(std.debug.ThreadContext);
734 std.debug.copyContext(thread_context, context_copy);
735
736 return .{
737 .allocator = allocator,
738 .cfa = null,
739 .pc = pc,
740 .thread_context = context_copy,
741 .reg_context = undefined,
742 .vm = .{},
743 .stack_machine = .{},
744 };
745 }
746
747 pub fn deinit(self: *UnwindContext) void {
748 self.vm.deinit(self.allocator);
749 self.stack_machine.deinit(self.allocator);
750 self.allocator.destroy(self.thread_context);
751 self.* = undefined;
752 }
753
754 pub fn getFp(self: *const UnwindContext) !usize {
755 return (try abi.regValueNative(usize, self.thread_context, abi.fpRegNum(self.reg_context), self.reg_context)).*;
756 }
757};
758
759720const num_sections = std.enums.directEnumArrayLen(Section.Id, 0);
760721pub const SectionArray = [num_sections]?Section;
761722pub const null_section_array = [_]?Section{null} ** num_sections;
......@@ -817,7 +778,7 @@ pub fn getSymbolName(di: *Dwarf, address: u64) ?[]const u8 {
817778}
818779
819780fn scanAllFunctions(di: *Dwarf, allocator: Allocator) !void {
820 var fbr: FixedBufferReader = .{ .buf = di.section(.debug_info).?, .endian = di.endian };
781 var fbr: DeprecatedFixedBufferReader = .{ .buf = di.section(.debug_info).?, .endian = di.endian };
821782 var this_unit_offset: u64 = 0;
822783
823784 while (this_unit_offset < fbr.buf.len) {
......@@ -828,20 +789,20 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) !void {
828789 const next_offset = unit_header.header_length + unit_header.unit_length;
829790
830791 const version = try fbr.readInt(u16);
831 if (version < 2 or version > 5) return badDwarf();
792 if (version < 2 or version > 5) return bad();
832793
833794 var address_size: u8 = undefined;
834795 var debug_abbrev_offset: u64 = undefined;
835796 if (version >= 5) {
836797 const unit_type = try fbr.readInt(u8);
837 if (unit_type != DW.UT.compile) return badDwarf();
798 if (unit_type != DW.UT.compile) return bad();
838799 address_size = try fbr.readByte();
839800 debug_abbrev_offset = try fbr.readAddress(unit_header.format);
840801 } else {
841802 debug_abbrev_offset = try fbr.readAddress(unit_header.format);
842803 address_size = try fbr.readByte();
843804 }
844 if (address_size != @sizeOf(usize)) return badDwarf();
805 if (address_size != @sizeOf(usize)) return bad();
845806
846807 const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);
847808
......@@ -915,28 +876,28 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) !void {
915876
916877 // Follow the DIE it points to and repeat
917878 const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin);
918 if (ref_offset > next_offset) return badDwarf();
879 if (ref_offset > next_offset) return bad();
919880 try fbr.seekTo(this_unit_offset + ref_offset);
920881 this_die_obj = (try parseDie(
921882 &fbr,
922883 attrs_bufs[2],
923884 abbrev_table,
924885 unit_header.format,
925 )) orelse return badDwarf();
886 )) orelse return bad();
926887 } else if (this_die_obj.getAttr(AT.specification)) |_| {
927888 const after_die_offset = fbr.pos;
928889 defer fbr.pos = after_die_offset;
929890
930891 // Follow the DIE it points to and repeat
931892 const ref_offset = try this_die_obj.getAttrRef(AT.specification);
932 if (ref_offset > next_offset) return badDwarf();
893 if (ref_offset > next_offset) return bad();
933894 try fbr.seekTo(this_unit_offset + ref_offset);
934895 this_die_obj = (try parseDie(
935896 &fbr,
936897 attrs_bufs[2],
937898 abbrev_table,
938899 unit_header.format,
939 )) orelse return badDwarf();
900 )) orelse return bad();
940901 } else {
941902 break :x null;
942903 }
......@@ -950,7 +911,7 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) !void {
950911 const pc_end = switch (high_pc_value.*) {
951912 .addr => |value| value,
952913 .udata => |offset| low_pc + offset,
953 else => return badDwarf(),
914 else => return bad(),
954915 };
955916
956917 try di.func_list.append(allocator, .{
......@@ -1004,7 +965,7 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) !void {
1004965}
1005966
1006967fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) !void {
1007 var fbr: FixedBufferReader = .{ .buf = di.section(.debug_info).?, .endian = di.endian };
968 var fbr: DeprecatedFixedBufferReader = .{ .buf = di.section(.debug_info).?, .endian = di.endian };
1008969 var this_unit_offset: u64 = 0;
1009970
1010971 var attrs_buf = std.ArrayList(Die.Attr).init(allocator);
......@@ -1018,20 +979,20 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) !void {
1018979 const next_offset = unit_header.header_length + unit_header.unit_length;
1019980
1020981 const version = try fbr.readInt(u16);
1021 if (version < 2 or version > 5) return badDwarf();
982 if (version < 2 or version > 5) return bad();
1022983
1023984 var address_size: u8 = undefined;
1024985 var debug_abbrev_offset: u64 = undefined;
1025986 if (version >= 5) {
1026987 const unit_type = try fbr.readInt(u8);
1027 if (unit_type != UT.compile) return badDwarf();
988 if (unit_type != UT.compile) return bad();
1028989 address_size = try fbr.readByte();
1029990 debug_abbrev_offset = try fbr.readAddress(unit_header.format);
1030991 } else {
1031992 debug_abbrev_offset = try fbr.readAddress(unit_header.format);
1032993 address_size = try fbr.readByte();
1033994 }
1034 if (address_size != @sizeOf(usize)) return badDwarf();
995 if (address_size != @sizeOf(usize)) return bad();
1035996
1036997 const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);
1037998
......@@ -1046,9 +1007,9 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) !void {
10461007 attrs_buf.items,
10471008 abbrev_table,
10481009 unit_header.format,
1049 )) orelse return badDwarf();
1010 )) orelse return bad();
10501011
1051 if (compile_unit_die.tag_id != DW.TAG.compile_unit) return badDwarf();
1012 if (compile_unit_die.tag_id != DW.TAG.compile_unit) return bad();
10521013
10531014 compile_unit_die.attrs = try allocator.dupe(Die.Attr, compile_unit_die.attrs);
10541015
......@@ -1070,7 +1031,7 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) !void {
10701031 const pc_end = switch (high_pc_value.*) {
10711032 .addr => |value| value,
10721033 .udata => |offset| low_pc + offset,
1073 else => return badDwarf(),
1034 else => return bad(),
10741035 };
10751036 break :x PcRange{
10761037 .start = low_pc,
......@@ -1096,7 +1057,7 @@ const DebugRangeIterator = struct {
10961057 section_type: Section.Id,
10971058 di: *const Dwarf,
10981059 compile_unit: *const CompileUnit,
1099 fbr: FixedBufferReader,
1060 fbr: DeprecatedFixedBufferReader,
11001061
11011062 pub fn init(ranges_value: *const FormValue, di: *const Dwarf, compile_unit: *const CompileUnit) !@This() {
11021063 const section_type = if (compile_unit.version >= 5) Section.Id.debug_rnglists else Section.Id.debug_ranges;
......@@ -1108,19 +1069,19 @@ const DebugRangeIterator = struct {
11081069 switch (compile_unit.format) {
11091070 .@"32" => {
11101071 const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 4 * idx));
1111 if (offset_loc + 4 > debug_ranges.len) return badDwarf();
1072 if (offset_loc + 4 > debug_ranges.len) return bad();
11121073 const offset = readInt(u32, debug_ranges[offset_loc..][0..4], di.endian);
11131074 break :off compile_unit.rnglists_base + offset;
11141075 },
11151076 .@"64" => {
11161077 const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 8 * idx));
1117 if (offset_loc + 8 > debug_ranges.len) return badDwarf();
1078 if (offset_loc + 8 > debug_ranges.len) return bad();
11181079 const offset = readInt(u64, debug_ranges[offset_loc..][0..8], di.endian);
11191080 break :off compile_unit.rnglists_base + offset;
11201081 },
11211082 }
11221083 },
1123 else => return badDwarf(),
1084 else => return bad(),
11241085 };
11251086
11261087 // All the addresses in the list are relative to the value
......@@ -1139,7 +1100,7 @@ const DebugRangeIterator = struct {
11391100 .compile_unit = compile_unit,
11401101 .fbr = .{
11411102 .buf = debug_ranges,
1142 .pos = cast(usize, ranges_offset) orelse return badDwarf(),
1103 .pos = cast(usize, ranges_offset) orelse return bad(),
11431104 .endian = di.endian,
11441105 },
11451106 };
......@@ -1214,7 +1175,7 @@ const DebugRangeIterator = struct {
12141175 .end_addr = end_addr,
12151176 };
12161177 },
1217 else => return badDwarf(),
1178 else => return bad(),
12181179 }
12191180 },
12201181 .debug_ranges => {
......@@ -1251,7 +1212,7 @@ pub fn findCompileUnit(di: *const Dwarf, target_address: u64) !*const CompileUni
12511212 }
12521213 }
12531214
1254 return missingDwarf();
1215 return missing();
12551216}
12561217
12571218/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
......@@ -1270,9 +1231,9 @@ fn getAbbrevTable(di: *Dwarf, allocator: Allocator, abbrev_offset: u64) !*const
12701231}
12711232
12721233fn parseAbbrevTable(di: *Dwarf, allocator: Allocator, offset: u64) !Abbrev.Table {
1273 var fbr: FixedBufferReader = .{
1234 var fbr: DeprecatedFixedBufferReader = .{
12741235 .buf = di.section(.debug_abbrev).?,
1275 .pos = cast(usize, offset) orelse return badDwarf(),
1236 .pos = cast(usize, offset) orelse return bad(),
12761237 .endian = di.endian,
12771238 };
12781239
......@@ -1322,14 +1283,14 @@ fn parseAbbrevTable(di: *Dwarf, allocator: Allocator, offset: u64) !Abbrev.Table
13221283}
13231284
13241285fn parseDie(
1325 fbr: *FixedBufferReader,
1286 fbr: *DeprecatedFixedBufferReader,
13261287 attrs_buf: []Die.Attr,
13271288 abbrev_table: *const Abbrev.Table,
13281289 format: Format,
13291290) !?Die {
13301291 const abbrev_code = try fbr.readUleb128(u64);
13311292 if (abbrev_code == 0) return null;
1332 const table_entry = abbrev_table.get(abbrev_code) orelse return badDwarf();
1293 const table_entry = abbrev_table.get(abbrev_code) orelse return bad();
13331294
13341295 const attrs = attrs_buf[0..table_entry.attrs.len];
13351296 for (attrs, table_entry.attrs) |*result_attr, attr| result_attr.* = Die.Attr{
......@@ -1357,15 +1318,15 @@ pub fn getLineNumberInfo(
13571318 const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT.comp_dir, di.section(.debug_line_str), compile_unit);
13581319 const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);
13591320
1360 var fbr: FixedBufferReader = .{ .buf = di.section(.debug_line).?, .endian = di.endian };
1321 var fbr: DeprecatedFixedBufferReader = .{ .buf = di.section(.debug_line).?, .endian = di.endian };
13611322 try fbr.seekTo(line_info_offset);
13621323
13631324 const unit_header = try readUnitHeader(&fbr, null);
1364 if (unit_header.unit_length == 0) return missingDwarf();
1325 if (unit_header.unit_length == 0) return missing();
13651326 const next_offset = unit_header.header_length + unit_header.unit_length;
13661327
13671328 const version = try fbr.readInt(u16);
1368 if (version < 2) return badDwarf();
1329 if (version < 2) return bad();
13691330
13701331 var addr_size: u8 = switch (unit_header.format) {
13711332 .@"32" => 4,
......@@ -1381,7 +1342,7 @@ pub fn getLineNumberInfo(
13811342 const prog_start_offset = fbr.pos + prologue_length;
13821343
13831344 const minimum_instruction_length = try fbr.readByte();
1384 if (minimum_instruction_length == 0) return badDwarf();
1345 if (minimum_instruction_length == 0) return bad();
13851346
13861347 if (version >= 4) {
13871348 // maximum_operations_per_instruction
......@@ -1392,7 +1353,7 @@ pub fn getLineNumberInfo(
13921353 const line_base = try fbr.readByteSigned();
13931354
13941355 const line_range = try fbr.readByte();
1395 if (line_range == 0) return badDwarf();
1356 if (line_range == 0) return bad();
13961357
13971358 const opcode_base = try fbr.readByte();
13981359
......@@ -1433,7 +1394,7 @@ pub fn getLineNumberInfo(
14331394 {
14341395 var dir_ent_fmt_buf: [10]FileEntFmt = undefined;
14351396 const directory_entry_format_count = try fbr.readByte();
1436 if (directory_entry_format_count > dir_ent_fmt_buf.len) return badDwarf();
1397 if (directory_entry_format_count > dir_ent_fmt_buf.len) return bad();
14371398 for (dir_ent_fmt_buf[0..directory_entry_format_count]) |*ent_fmt| {
14381399 ent_fmt.* = .{
14391400 .content_type_code = try fbr.readUleb128(u8),
......@@ -1461,7 +1422,7 @@ pub fn getLineNumberInfo(
14611422 DW.LNCT.size => e.size = try form_value.getUInt(u64),
14621423 DW.LNCT.MD5 => e.md5 = switch (form_value) {
14631424 .data16 => |data16| data16.*,
1464 else => return badDwarf(),
1425 else => return bad(),
14651426 },
14661427 else => continue,
14671428 }
......@@ -1473,7 +1434,7 @@ pub fn getLineNumberInfo(
14731434
14741435 var file_ent_fmt_buf: [10]FileEntFmt = undefined;
14751436 const file_name_entry_format_count = try fbr.readByte();
1476 if (file_name_entry_format_count > file_ent_fmt_buf.len) return badDwarf();
1437 if (file_name_entry_format_count > file_ent_fmt_buf.len) return bad();
14771438 for (file_ent_fmt_buf[0..file_name_entry_format_count]) |*ent_fmt| {
14781439 ent_fmt.* = .{
14791440 .content_type_code = try fbr.readUleb128(u8),
......@@ -1501,7 +1462,7 @@ pub fn getLineNumberInfo(
15011462 DW.LNCT.size => e.size = try form_value.getUInt(u64),
15021463 DW.LNCT.MD5 => e.md5 = switch (form_value) {
15031464 .data16 => |data16| data16.*,
1504 else => return badDwarf(),
1465 else => return bad(),
15051466 },
15061467 else => continue,
15071468 }
......@@ -1527,7 +1488,7 @@ pub fn getLineNumberInfo(
15271488
15281489 if (opcode == DW.LNS.extended_op) {
15291490 const op_size = try fbr.readUleb128(u64);
1530 if (op_size < 1) return badDwarf();
1491 if (op_size < 1) return bad();
15311492 const sub_op = try fbr.readByte();
15321493 switch (sub_op) {
15331494 DW.LNE.end_sequence => {
......@@ -1600,14 +1561,14 @@ pub fn getLineNumberInfo(
16001561 },
16011562 DW.LNS.set_prologue_end => {},
16021563 else => {
1603 if (opcode - 1 >= standard_opcode_lengths.len) return badDwarf();
1564 if (opcode - 1 >= standard_opcode_lengths.len) return bad();
16041565 try fbr.seekForward(standard_opcode_lengths[opcode - 1]);
16051566 },
16061567 }
16071568 }
16081569 }
16091570
1610 return missingDwarf();
1571 return missing();
16111572}
16121573
16131574fn getString(di: Dwarf, offset: u64) ![:0]const u8 {
......@@ -1619,28 +1580,28 @@ fn getLineString(di: Dwarf, offset: u64) ![:0]const u8 {
16191580}
16201581
16211582fn readDebugAddr(di: Dwarf, compile_unit: CompileUnit, index: u64) !u64 {
1622 const debug_addr = di.section(.debug_addr) orelse return badDwarf();
1583 const debug_addr = di.section(.debug_addr) orelse return bad();
16231584
16241585 // addr_base points to the first item after the header, however we
16251586 // need to read the header to know the size of each item. Empirically,
16261587 // it may disagree with is_64 on the compile unit.
16271588 // The header is 8 or 12 bytes depending on is_64.
1628 if (compile_unit.addr_base < 8) return badDwarf();
1589 if (compile_unit.addr_base < 8) return bad();
16291590
16301591 const version = readInt(u16, debug_addr[compile_unit.addr_base - 4 ..][0..2], di.endian);
1631 if (version != 5) return badDwarf();
1592 if (version != 5) return bad();
16321593
16331594 const addr_size = debug_addr[compile_unit.addr_base - 2];
16341595 const seg_size = debug_addr[compile_unit.addr_base - 1];
16351596
16361597 const byte_offset = @as(usize, @intCast(compile_unit.addr_base + (addr_size + seg_size) * index));
1637 if (byte_offset + addr_size > debug_addr.len) return badDwarf();
1598 if (byte_offset + addr_size > debug_addr.len) return bad();
16381599 return switch (addr_size) {
16391600 1 => debug_addr[byte_offset],
16401601 2 => readInt(u16, debug_addr[byte_offset..][0..2], di.endian),
16411602 4 => readInt(u32, debug_addr[byte_offset..][0..4], di.endian),
16421603 8 => readInt(u64, debug_addr[byte_offset..][0..8], di.endian),
1643 else => badDwarf(),
1604 else => bad(),
16441605 };
16451606}
16461607
......@@ -1650,7 +1611,7 @@ fn readDebugAddr(di: Dwarf, compile_unit: CompileUnit, index: u64) !u64 {
16501611/// of FDEs is built for binary searching during unwinding.
16511612pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize) !void {
16521613 if (di.section(.eh_frame_hdr)) |eh_frame_hdr| blk: {
1653 var fbr: FixedBufferReader = .{ .buf = eh_frame_hdr, .endian = native_endian };
1614 var fbr: DeprecatedFixedBufferReader = .{ .buf = eh_frame_hdr, .endian = native_endian };
16541615
16551616 const version = try fbr.readByte();
16561617 if (version != 1) break :blk;
......@@ -1665,16 +1626,16 @@ pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize)
16651626 const eh_frame_ptr = cast(usize, try readEhPointer(&fbr, eh_frame_ptr_enc, @sizeOf(usize), .{
16661627 .pc_rel_base = @intFromPtr(&eh_frame_hdr[fbr.pos]),
16671628 .follow_indirect = true,
1668 }) orelse return badDwarf()) orelse return badDwarf();
1629 }) orelse return bad()) orelse return bad();
16691630
16701631 const fde_count = cast(usize, try readEhPointer(&fbr, fde_count_enc, @sizeOf(usize), .{
16711632 .pc_rel_base = @intFromPtr(&eh_frame_hdr[fbr.pos]),
16721633 .follow_indirect = true,
1673 }) orelse return badDwarf()) orelse return badDwarf();
1634 }) orelse return bad()) orelse return bad();
16741635
16751636 const entry_size = try ExceptionFrameHeader.entrySize(table_enc);
16761637 const entries_len = fde_count * entry_size;
1677 if (entries_len > eh_frame_hdr.len - fbr.pos) return badDwarf();
1638 if (entries_len > eh_frame_hdr.len - fbr.pos) return bad();
16781639
16791640 di.eh_frame_hdr = .{
16801641 .eh_frame_ptr = eh_frame_ptr,
......@@ -1690,7 +1651,7 @@ pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize)
16901651 const frame_sections = [2]Section.Id{ .eh_frame, .debug_frame };
16911652 for (frame_sections) |frame_section| {
16921653 if (di.section(frame_section)) |section_data| {
1693 var fbr: FixedBufferReader = .{ .buf = section_data, .endian = di.endian };
1654 var fbr: DeprecatedFixedBufferReader = .{ .buf = section_data, .endian = di.endian };
16941655 while (fbr.pos < fbr.buf.len) {
16951656 const entry_header = try EntryHeader.read(&fbr, null, frame_section);
16961657 switch (entry_header.type) {
......@@ -1708,7 +1669,7 @@ pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize)
17081669 try di.cie_map.put(allocator, entry_header.length_offset, cie);
17091670 },
17101671 .fde => |cie_offset| {
1711 const cie = di.cie_map.get(cie_offset) orelse return badDwarf();
1672 const cie = di.cie_map.get(cie_offset) orelse return bad();
17121673 const fde = try FrameDescriptionEntry.parse(
17131674 entry_header.entry_bytes,
17141675 di.sectionVirtualOffset(frame_section, base_address).?,
......@@ -1733,205 +1694,8 @@ pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize)
17331694 }
17341695}
17351696
1736/// Unwind a stack frame using DWARF unwinding info, updating the register context.
1737///
1738/// If `.eh_frame_hdr` is available, it will be used to binary search for the FDE.
1739/// Otherwise, a linear scan of `.eh_frame` and `.debug_frame` is done to find the FDE.
1740///
1741/// `explicit_fde_offset` is for cases where the FDE offset is known, such as when __unwind_info
1742/// defers unwinding to DWARF. This is an offset into the `.eh_frame` section.
1743pub fn unwindFrame(di: *const Dwarf, context: *UnwindContext, ma: *StackIterator.MemoryAccessor, explicit_fde_offset: ?usize) !usize {
1744 if (!comptime abi.supportsUnwinding(builtin.target)) return error.UnsupportedCpuArchitecture;
1745 if (context.pc == 0) return 0;
1746
1747 // Find the FDE and CIE
1748 var cie: CommonInformationEntry = undefined;
1749 var fde: FrameDescriptionEntry = undefined;
1750
1751 if (explicit_fde_offset) |fde_offset| {
1752 const dwarf_section: Section.Id = .eh_frame;
1753 const frame_section = di.section(dwarf_section) orelse return error.MissingFDE;
1754 if (fde_offset >= frame_section.len) return error.MissingFDE;
1755
1756 var fbr: FixedBufferReader = .{
1757 .buf = frame_section,
1758 .pos = fde_offset,
1759 .endian = di.endian,
1760 };
1761
1762 const fde_entry_header = try EntryHeader.read(&fbr, null, dwarf_section);
1763 if (fde_entry_header.type != .fde) return error.MissingFDE;
1764
1765 const cie_offset = fde_entry_header.type.fde;
1766 try fbr.seekTo(cie_offset);
1767
1768 fbr.endian = native_endian;
1769 const cie_entry_header = try EntryHeader.read(&fbr, null, dwarf_section);
1770 if (cie_entry_header.type != .cie) return badDwarf();
1771
1772 cie = try CommonInformationEntry.parse(
1773 cie_entry_header.entry_bytes,
1774 0,
1775 true,
1776 cie_entry_header.format,
1777 dwarf_section,
1778 cie_entry_header.length_offset,
1779 @sizeOf(usize),
1780 native_endian,
1781 );
1782
1783 fde = try FrameDescriptionEntry.parse(
1784 fde_entry_header.entry_bytes,
1785 0,
1786 true,
1787 cie,
1788 @sizeOf(usize),
1789 native_endian,
1790 );
1791 } else if (di.eh_frame_hdr) |header| {
1792 const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null;
1793 try header.findEntry(
1794 ma,
1795 eh_frame_len,
1796 @intFromPtr(di.section(.eh_frame_hdr).?.ptr),
1797 context.pc,
1798 &cie,
1799 &fde,
1800 );
1801 } else {
1802 const index = std.sort.binarySearch(FrameDescriptionEntry, context.pc, di.fde_list.items, {}, struct {
1803 pub fn compareFn(_: void, pc: usize, mid_item: FrameDescriptionEntry) std.math.Order {
1804 if (pc < mid_item.pc_begin) return .lt;
1805
1806 const range_end = mid_item.pc_begin + mid_item.pc_range;
1807 if (pc < range_end) return .eq;
1808
1809 return .gt;
1810 }
1811 }.compareFn);
1812
1813 fde = if (index) |i| di.fde_list.items[i] else return error.MissingFDE;
1814 cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE;
1815 }
1816
1817 var expression_context: expression.Context = .{
1818 .format = cie.format,
1819 .memory_accessor = ma,
1820 .compile_unit = di.findCompileUnit(fde.pc_begin) catch null,
1821 .thread_context = context.thread_context,
1822 .reg_context = context.reg_context,
1823 .cfa = context.cfa,
1824 };
1825
1826 context.vm.reset();
1827 context.reg_context.eh_frame = cie.version != 4;
1828 context.reg_context.is_macho = di.is_macho;
1829
1830 const row = try context.vm.runToNative(context.allocator, context.pc, cie, fde);
1831 context.cfa = switch (row.cfa.rule) {
1832 .val_offset => |offset| blk: {
1833 const register = row.cfa.register orelse return error.InvalidCFARule;
1834 const value = readInt(usize, (try abi.regBytes(context.thread_context, register, context.reg_context))[0..@sizeOf(usize)], native_endian);
1835 break :blk try call_frame.applyOffset(value, offset);
1836 },
1837 .expression => |expr| blk: {
1838 context.stack_machine.reset();
1839 const value = try context.stack_machine.run(
1840 expr,
1841 context.allocator,
1842 expression_context,
1843 context.cfa,
1844 );
1845
1846 if (value) |v| {
1847 if (v != .generic) return error.InvalidExpressionValue;
1848 break :blk v.generic;
1849 } else return error.NoExpressionValue;
1850 },
1851 else => return error.InvalidCFARule,
1852 };
1853
1854 if (ma.load(usize, context.cfa.?) == null) return error.InvalidCFA;
1855 expression_context.cfa = context.cfa;
1856
1857 // Buffering the modifications is done because copying the thread context is not portable,
1858 // some implementations (ie. darwin) use internal pointers to the mcontext.
1859 var arena = std.heap.ArenaAllocator.init(context.allocator);
1860 defer arena.deinit();
1861 const update_allocator = arena.allocator();
1862
1863 const RegisterUpdate = struct {
1864 // Backed by thread_context
1865 dest: []u8,
1866 // Backed by arena
1867 src: []const u8,
1868 prev: ?*@This(),
1869 };
1870
1871 var update_tail: ?*RegisterUpdate = null;
1872 var has_return_address = true;
1873 for (context.vm.rowColumns(row)) |column| {
1874 if (column.register) |register| {
1875 if (register == cie.return_address_register) {
1876 has_return_address = column.rule != .undefined;
1877 }
1878
1879 const dest = try abi.regBytes(context.thread_context, register, context.reg_context);
1880 const src = try update_allocator.alloc(u8, dest.len);
1881
1882 const prev = update_tail;
1883 update_tail = try update_allocator.create(RegisterUpdate);
1884 update_tail.?.* = .{
1885 .dest = dest,
1886 .src = src,
1887 .prev = prev,
1888 };
1889
1890 try column.resolveValue(
1891 context,
1892 expression_context,
1893 ma,
1894 src,
1895 );
1896 }
1897 }
1898
1899 // On all implemented architectures, the CFA is defined as being the previous frame's SP
1900 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(context.reg_context), context.reg_context)).* = context.cfa.?;
1901
1902 while (update_tail) |tail| {
1903 @memcpy(tail.dest, tail.src);
1904 update_tail = tail.prev;
1905 }
1906
1907 if (has_return_address) {
1908 context.pc = abi.stripInstructionPtrAuthCode(readInt(usize, (try abi.regBytes(
1909 context.thread_context,
1910 cie.return_address_register,
1911 context.reg_context,
1912 ))[0..@sizeOf(usize)], native_endian));
1913 } else {
1914 context.pc = 0;
1915 }
1916
1917 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), context.reg_context)).* = context.pc;
1918
1919 // The call instruction will have pushed the address of the instruction that follows the call as the return address.
1920 // This next instruction may be past the end of the function if the caller was `noreturn` (ie. the last instruction in
1921 // the function was the call). If we were to look up an FDE entry using the return address directly, it could end up
1922 // either not finding an FDE at all, or using the next FDE in the program, producing incorrect results. To prevent this,
1923 // we subtract one so that the next lookup is guaranteed to land inside the
1924 //
1925 // The exception to this rule is signal frames, where we return execution would be returned to the instruction
1926 // that triggered the handler.
1927 const return_address = context.pc;
1928 if (context.pc > 0 and !cie.isSignalFrame()) context.pc -= 1;
1929
1930 return return_address;
1931}
1932
19331697fn parseFormValue(
1934 fbr: *FixedBufferReader,
1698 fbr: *DeprecatedFixedBufferReader,
19351699 form_id: u64,
19361700 format: Format,
19371701 implicit_const: ?i64,
......@@ -1990,12 +1754,12 @@ fn parseFormValue(
19901754 FORM.strx => .{ .strx = try fbr.readUleb128(usize) },
19911755 FORM.line_strp => .{ .line_strp = try fbr.readAddress(format) },
19921756 FORM.indirect => parseFormValue(fbr, try fbr.readUleb128(u64), format, implicit_const),
1993 FORM.implicit_const => .{ .sdata = implicit_const orelse return badDwarf() },
1757 FORM.implicit_const => .{ .sdata = implicit_const orelse return bad() },
19941758 FORM.loclistx => .{ .loclistx = try fbr.readUleb128(u64) },
19951759 FORM.rnglistx => .{ .rnglistx = try fbr.readUleb128(u64) },
19961760 else => {
19971761 //debug.print("unrecognized form id: {x}\n", .{form_id});
1998 return badDwarf();
1762 return bad();
19991763 },
20001764 };
20011765}
......@@ -2090,14 +1854,14 @@ const LineNumberProgram = struct {
20901854 self.target_address < self.address)
20911855 {
20921856 const file_index = if (self.version >= 5) self.prev_file else i: {
2093 if (self.prev_file == 0) return missingDwarf();
1857 if (self.prev_file == 0) return missing();
20941858 break :i self.prev_file - 1;
20951859 };
20961860
2097 if (file_index >= file_entries.len) return badDwarf();
1861 if (file_index >= file_entries.len) return bad();
20981862 const file_entry = &file_entries[file_index];
20991863
2100 if (file_entry.dir_index >= self.include_dirs.len) return badDwarf();
1864 if (file_entry.dir_index >= self.include_dirs.len) return bad();
21011865 const dir_name = self.include_dirs[file_entry.dir_index].path;
21021866
21031867 const file_name = try std.fs.path.join(allocator, &[_][]const u8{
......@@ -2128,14 +1892,14 @@ const UnitHeader = struct {
21281892 header_length: u4,
21291893 unit_length: u64,
21301894};
2131fn readUnitHeader(fbr: *FixedBufferReader, opt_ma: ?*StackIterator.MemoryAccessor) !UnitHeader {
1895fn readUnitHeader(fbr: *DeprecatedFixedBufferReader, opt_ma: ?*MemoryAccessor) !UnitHeader {
21321896 return switch (try if (opt_ma) |ma| fbr.readIntChecked(u32, ma) else fbr.readInt(u32)) {
21331897 0...0xfffffff0 - 1 => |unit_length| .{
21341898 .format = .@"32",
21351899 .header_length = 4,
21361900 .unit_length = unit_length,
21371901 },
2138 0xfffffff0...0xffffffff - 1 => badDwarf(),
1902 0xfffffff0...0xffffffff - 1 => bad(),
21391903 0xffffffff => .{
21401904 .format = .@"64",
21411905 .header_length = 12,
......@@ -2145,7 +1909,7 @@ fn readUnitHeader(fbr: *FixedBufferReader, opt_ma: ?*StackIterator.MemoryAccesso
21451909}
21461910
21471911/// Returns the DWARF register number for an x86_64 register number found in compact unwind info
2148fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
1912pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
21491913 return switch (unwind_reg_number) {
21501914 1 => 3, // RBX
21511915 2 => 12, // R12
......@@ -2159,473 +1923,25 @@ fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
21591923
21601924/// This function is to make it handy to comment out the return and make it
21611925/// into a crash when working on this file.
2162fn badDwarf() error{InvalidDebugInfo} {
2163 //if (true) @panic("badDwarf"); // can be handy to uncomment when working on this file
1926pub fn bad() error{InvalidDebugInfo} {
1927 //if (true) @panic("bad dwarf"); // can be handy to uncomment when working on this file
21641928 return error.InvalidDebugInfo;
21651929}
21661930
2167fn missingDwarf() error{MissingDebugInfo} {
2168 //if (true) @panic("missingDwarf"); // can be handy to uncomment when working on this file
1931fn missing() error{MissingDebugInfo} {
1932 //if (true) @panic("missing dwarf"); // can be handy to uncomment when working on this file
21691933 return error.MissingDebugInfo;
21701934}
21711935
21721936fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {
2173 const str = opt_str orelse return badDwarf();
2174 if (offset > str.len) return badDwarf();
2175 const casted_offset = cast(usize, offset) orelse return badDwarf();
1937 const str = opt_str orelse return bad();
1938 if (offset > str.len) return bad();
1939 const casted_offset = cast(usize, offset) orelse return bad();
21761940 // Valid strings always have a terminating zero byte
2177 const last = std.mem.indexOfScalarPos(u8, str, casted_offset, 0) orelse return badDwarf();
1941 const last = std.mem.indexOfScalarPos(u8, str, casted_offset, 0) orelse return bad();
21781942 return str[casted_offset..last :0];
21791943}
21801944
2181// Reading debug info needs to be fast, even when compiled in debug mode,
2182// so avoid using a `std.io.FixedBufferStream` which is too slow.
2183pub const FixedBufferReader = struct {
2184 buf: []const u8,
2185 pos: usize = 0,
2186 endian: std.builtin.Endian,
2187
2188 pub const Error = error{ EndOfBuffer, Overflow, InvalidBuffer };
2189
2190 fn seekTo(fbr: *FixedBufferReader, pos: u64) Error!void {
2191 if (pos > fbr.buf.len) return error.EndOfBuffer;
2192 fbr.pos = @intCast(pos);
2193 }
2194
2195 fn seekForward(fbr: *FixedBufferReader, amount: u64) Error!void {
2196 if (fbr.buf.len - fbr.pos < amount) return error.EndOfBuffer;
2197 fbr.pos += @intCast(amount);
2198 }
2199
2200 pub inline fn readByte(fbr: *FixedBufferReader) Error!u8 {
2201 if (fbr.pos >= fbr.buf.len) return error.EndOfBuffer;
2202 defer fbr.pos += 1;
2203 return fbr.buf[fbr.pos];
2204 }
2205
2206 fn readByteSigned(fbr: *FixedBufferReader) Error!i8 {
2207 return @bitCast(try fbr.readByte());
2208 }
2209
2210 fn readInt(fbr: *FixedBufferReader, comptime T: type) Error!T {
2211 const size = @divExact(@typeInfo(T).Int.bits, 8);
2212 if (fbr.buf.len - fbr.pos < size) return error.EndOfBuffer;
2213 defer fbr.pos += size;
2214 return std.mem.readInt(T, fbr.buf[fbr.pos..][0..size], fbr.endian);
2215 }
2216
2217 fn readIntChecked(
2218 fbr: *FixedBufferReader,
2219 comptime T: type,
2220 ma: *std.debug.StackIterator.MemoryAccessor,
2221 ) Error!T {
2222 if (ma.load(T, @intFromPtr(fbr.buf[fbr.pos..].ptr)) == null)
2223 return error.InvalidBuffer;
2224
2225 return fbr.readInt(T);
2226 }
2227
2228 fn readUleb128(fbr: *FixedBufferReader, comptime T: type) Error!T {
2229 return std.leb.readUleb128(T, fbr);
2230 }
2231
2232 fn readIleb128(fbr: *FixedBufferReader, comptime T: type) Error!T {
2233 return std.leb.readIleb128(T, fbr);
2234 }
2235
2236 fn readAddress(fbr: *FixedBufferReader, format: Format) Error!u64 {
2237 return switch (format) {
2238 .@"32" => try fbr.readInt(u32),
2239 .@"64" => try fbr.readInt(u64),
2240 };
2241 }
2242
2243 fn readAddressChecked(
2244 fbr: *FixedBufferReader,
2245 format: Format,
2246 ma: *std.debug.StackIterator.MemoryAccessor,
2247 ) Error!u64 {
2248 return switch (format) {
2249 .@"32" => try fbr.readIntChecked(u32, ma),
2250 .@"64" => try fbr.readIntChecked(u64, ma),
2251 };
2252 }
2253
2254 fn readBytes(fbr: *FixedBufferReader, len: usize) Error![]const u8 {
2255 if (fbr.buf.len - fbr.pos < len) return error.EndOfBuffer;
2256 defer fbr.pos += len;
2257 return fbr.buf[fbr.pos..][0..len];
2258 }
2259
2260 fn readBytesTo(fbr: *FixedBufferReader, comptime sentinel: u8) Error![:sentinel]const u8 {
2261 const end = @call(.always_inline, std.mem.indexOfScalarPos, .{
2262 u8,
2263 fbr.buf,
2264 fbr.pos,
2265 sentinel,
2266 }) orelse return error.EndOfBuffer;
2267 defer fbr.pos = end + 1;
2268 return fbr.buf[fbr.pos..end :sentinel];
2269 }
2270};
2271
2272/// Unwind a frame using MachO compact unwind info (from __unwind_info).
2273/// If the compact encoding can't encode a way to unwind a frame, it will
2274/// defer unwinding to DWARF, in which case `.eh_frame` will be used if available.
2275pub fn unwindFrameMachO(
2276 context: *UnwindContext,
2277 ma: *StackIterator.MemoryAccessor,
2278 unwind_info: []const u8,
2279 eh_frame: ?[]const u8,
2280 module_base_address: usize,
2281) !usize {
2282 const macho = std.macho;
2283
2284 const header = std.mem.bytesAsValue(
2285 macho.unwind_info_section_header,
2286 unwind_info[0..@sizeOf(macho.unwind_info_section_header)],
2287 );
2288 const indices = std.mem.bytesAsSlice(
2289 macho.unwind_info_section_header_index_entry,
2290 unwind_info[header.indexSectionOffset..][0 .. header.indexCount * @sizeOf(macho.unwind_info_section_header_index_entry)],
2291 );
2292 if (indices.len == 0) return error.MissingUnwindInfo;
2293
2294 const mapped_pc = context.pc - module_base_address;
2295 const second_level_index = blk: {
2296 var left: usize = 0;
2297 var len: usize = indices.len;
2298
2299 while (len > 1) {
2300 const mid = left + len / 2;
2301 const offset = indices[mid].functionOffset;
2302 if (mapped_pc < offset) {
2303 len /= 2;
2304 } else {
2305 left = mid;
2306 if (mapped_pc == offset) break;
2307 len -= len / 2;
2308 }
2309 }
2310
2311 // Last index is a sentinel containing the highest address as its functionOffset
2312 if (indices[left].secondLevelPagesSectionOffset == 0) return error.MissingUnwindInfo;
2313 break :blk &indices[left];
2314 };
2315
2316 const common_encodings = std.mem.bytesAsSlice(
2317 macho.compact_unwind_encoding_t,
2318 unwind_info[header.commonEncodingsArraySectionOffset..][0 .. header.commonEncodingsArrayCount * @sizeOf(macho.compact_unwind_encoding_t)],
2319 );
2320
2321 const start_offset = second_level_index.secondLevelPagesSectionOffset;
2322 const kind = std.mem.bytesAsValue(
2323 macho.UNWIND_SECOND_LEVEL,
2324 unwind_info[start_offset..][0..@sizeOf(macho.UNWIND_SECOND_LEVEL)],
2325 );
2326
2327 const entry: struct {
2328 function_offset: usize,
2329 raw_encoding: u32,
2330 } = switch (kind.*) {
2331 .REGULAR => blk: {
2332 const page_header = std.mem.bytesAsValue(
2333 macho.unwind_info_regular_second_level_page_header,
2334 unwind_info[start_offset..][0..@sizeOf(macho.unwind_info_regular_second_level_page_header)],
2335 );
2336
2337 const entries = std.mem.bytesAsSlice(
2338 macho.unwind_info_regular_second_level_entry,
2339 unwind_info[start_offset + page_header.entryPageOffset ..][0 .. page_header.entryCount * @sizeOf(macho.unwind_info_regular_second_level_entry)],
2340 );
2341 if (entries.len == 0) return error.InvalidUnwindInfo;
2342
2343 var left: usize = 0;
2344 var len: usize = entries.len;
2345 while (len > 1) {
2346 const mid = left + len / 2;
2347 const offset = entries[mid].functionOffset;
2348 if (mapped_pc < offset) {
2349 len /= 2;
2350 } else {
2351 left = mid;
2352 if (mapped_pc == offset) break;
2353 len -= len / 2;
2354 }
2355 }
2356
2357 break :blk .{
2358 .function_offset = entries[left].functionOffset,
2359 .raw_encoding = entries[left].encoding,
2360 };
2361 },
2362 .COMPRESSED => blk: {
2363 const page_header = std.mem.bytesAsValue(
2364 macho.unwind_info_compressed_second_level_page_header,
2365 unwind_info[start_offset..][0..@sizeOf(macho.unwind_info_compressed_second_level_page_header)],
2366 );
2367
2368 const entries = std.mem.bytesAsSlice(
2369 macho.UnwindInfoCompressedEntry,
2370 unwind_info[start_offset + page_header.entryPageOffset ..][0 .. page_header.entryCount * @sizeOf(macho.UnwindInfoCompressedEntry)],
2371 );
2372 if (entries.len == 0) return error.InvalidUnwindInfo;
2373
2374 var left: usize = 0;
2375 var len: usize = entries.len;
2376 while (len > 1) {
2377 const mid = left + len / 2;
2378 const offset = second_level_index.functionOffset + entries[mid].funcOffset;
2379 if (mapped_pc < offset) {
2380 len /= 2;
2381 } else {
2382 left = mid;
2383 if (mapped_pc == offset) break;
2384 len -= len / 2;
2385 }
2386 }
2387
2388 const entry = entries[left];
2389 const function_offset = second_level_index.functionOffset + entry.funcOffset;
2390 if (entry.encodingIndex < header.commonEncodingsArrayCount) {
2391 if (entry.encodingIndex >= common_encodings.len) return error.InvalidUnwindInfo;
2392 break :blk .{
2393 .function_offset = function_offset,
2394 .raw_encoding = common_encodings[entry.encodingIndex],
2395 };
2396 } else {
2397 const local_index = try std.math.sub(
2398 u8,
2399 entry.encodingIndex,
2400 cast(u8, header.commonEncodingsArrayCount) orelse return error.InvalidUnwindInfo,
2401 );
2402 const local_encodings = std.mem.bytesAsSlice(
2403 macho.compact_unwind_encoding_t,
2404 unwind_info[start_offset + page_header.encodingsPageOffset ..][0 .. page_header.encodingsCount * @sizeOf(macho.compact_unwind_encoding_t)],
2405 );
2406 if (local_index >= local_encodings.len) return error.InvalidUnwindInfo;
2407 break :blk .{
2408 .function_offset = function_offset,
2409 .raw_encoding = local_encodings[local_index],
2410 };
2411 }
2412 },
2413 else => return error.InvalidUnwindInfo,
2414 };
2415
2416 if (entry.raw_encoding == 0) return error.NoUnwindInfo;
2417 const reg_context = abi.RegisterContext{
2418 .eh_frame = false,
2419 .is_macho = true,
2420 };
2421
2422 const encoding: macho.CompactUnwindEncoding = @bitCast(entry.raw_encoding);
2423 const new_ip = switch (builtin.cpu.arch) {
2424 .x86_64 => switch (encoding.mode.x86_64) {
2425 .OLD => return error.UnimplementedUnwindEncoding,
2426 .RBP_FRAME => blk: {
2427 const regs: [5]u3 = .{
2428 encoding.value.x86_64.frame.reg0,
2429 encoding.value.x86_64.frame.reg1,
2430 encoding.value.x86_64.frame.reg2,
2431 encoding.value.x86_64.frame.reg3,
2432 encoding.value.x86_64.frame.reg4,
2433 };
2434
2435 const frame_offset = encoding.value.x86_64.frame.frame_offset * @sizeOf(usize);
2436 var max_reg: usize = 0;
2437 inline for (regs, 0..) |reg, i| {
2438 if (reg > 0) max_reg = i;
2439 }
2440
2441 const fp = (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).*;
2442 const new_sp = fp + 2 * @sizeOf(usize);
2443
2444 // Verify the stack range we're about to read register values from
2445 if (ma.load(usize, new_sp) == null or ma.load(usize, fp - frame_offset + max_reg * @sizeOf(usize)) == null) return error.InvalidUnwindInfo;
2446
2447 const ip_ptr = fp + @sizeOf(usize);
2448 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2449 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
2450
2451 (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).* = new_fp;
2452 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp;
2453 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
2454
2455 for (regs, 0..) |reg, i| {
2456 if (reg == 0) continue;
2457 const addr = fp - frame_offset + i * @sizeOf(usize);
2458 const reg_number = try compactUnwindToDwarfRegNumber(reg);
2459 (try abi.regValueNative(usize, context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(addr)).*;
2460 }
2461
2462 break :blk new_ip;
2463 },
2464 .STACK_IMMD,
2465 .STACK_IND,
2466 => blk: {
2467 const sp = (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).*;
2468 const stack_size = if (encoding.mode.x86_64 == .STACK_IMMD)
2469 @as(usize, encoding.value.x86_64.frameless.stack.direct.stack_size) * @sizeOf(usize)
2470 else stack_size: {
2471 // In .STACK_IND, the stack size is inferred from the subq instruction at the beginning of the function.
2472 const sub_offset_addr =
2473 module_base_address +
2474 entry.function_offset +
2475 encoding.value.x86_64.frameless.stack.indirect.sub_offset;
2476 if (ma.load(usize, sub_offset_addr) == null) return error.InvalidUnwindInfo;
2477
2478 // `sub_offset_addr` points to the offset of the literal within the instruction
2479 const sub_operand = @as(*align(1) const u32, @ptrFromInt(sub_offset_addr)).*;
2480 break :stack_size sub_operand + @sizeOf(usize) * @as(usize, encoding.value.x86_64.frameless.stack.indirect.stack_adjust);
2481 };
2482
2483 // Decode the Lehmer-coded sequence of registers.
2484 // For a description of the encoding see lib/libc/include/any-macos.13-any/mach-o/compact_unwind_encoding.h
2485
2486 // Decode the variable-based permutation number into its digits. Each digit represents
2487 // an index into the list of register numbers that weren't yet used in the sequence at
2488 // the time the digit was added.
2489 const reg_count = encoding.value.x86_64.frameless.stack_reg_count;
2490 const ip_ptr = if (reg_count > 0) reg_blk: {
2491 var digits: [6]u3 = undefined;
2492 var accumulator: usize = encoding.value.x86_64.frameless.stack_reg_permutation;
2493 var base: usize = 2;
2494 for (0..reg_count) |i| {
2495 const div = accumulator / base;
2496 digits[digits.len - 1 - i] = @intCast(accumulator - base * div);
2497 accumulator = div;
2498 base += 1;
2499 }
2500
2501 const reg_numbers = [_]u3{ 1, 2, 3, 4, 5, 6 };
2502 var registers: [reg_numbers.len]u3 = undefined;
2503 var used_indices = [_]bool{false} ** reg_numbers.len;
2504 for (digits[digits.len - reg_count ..], 0..) |target_unused_index, i| {
2505 var unused_count: u8 = 0;
2506 const unused_index = for (used_indices, 0..) |used, index| {
2507 if (!used) {
2508 if (target_unused_index == unused_count) break index;
2509 unused_count += 1;
2510 }
2511 } else unreachable;
2512
2513 registers[i] = reg_numbers[unused_index];
2514 used_indices[unused_index] = true;
2515 }
2516
2517 var reg_addr = sp + stack_size - @sizeOf(usize) * @as(usize, reg_count + 1);
2518 if (ma.load(usize, reg_addr) == null) return error.InvalidUnwindInfo;
2519 for (0..reg_count) |i| {
2520 const reg_number = try compactUnwindToDwarfRegNumber(registers[i]);
2521 (try abi.regValueNative(usize, context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2522 reg_addr += @sizeOf(usize);
2523 }
2524
2525 break :reg_blk reg_addr;
2526 } else sp + stack_size - @sizeOf(usize);
2527
2528 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2529 const new_sp = ip_ptr + @sizeOf(usize);
2530 if (ma.load(usize, new_sp) == null) return error.InvalidUnwindInfo;
2531
2532 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp;
2533 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
2534
2535 break :blk new_ip;
2536 },
2537 .DWARF => {
2538 return unwindFrameMachODwarf(context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.x86_64.dwarf));
2539 },
2540 },
2541 .aarch64 => switch (encoding.mode.arm64) {
2542 .OLD => return error.UnimplementedUnwindEncoding,
2543 .FRAMELESS => blk: {
2544 const sp = (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).*;
2545 const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16;
2546 const new_ip = (try abi.regValueNative(usize, context.thread_context, 30, reg_context)).*;
2547 if (ma.load(usize, new_sp) == null) return error.InvalidUnwindInfo;
2548 (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(reg_context), reg_context)).* = new_sp;
2549 break :blk new_ip;
2550 },
2551 .DWARF => {
2552 return unwindFrameMachODwarf(context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.arm64.dwarf));
2553 },
2554 .FRAME => blk: {
2555 const fp = (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).*;
2556 const new_sp = fp + 16;
2557 const ip_ptr = fp + @sizeOf(usize);
2558
2559 const num_restored_pairs: usize =
2560 @popCount(@as(u5, @bitCast(encoding.value.arm64.frame.x_reg_pairs))) +
2561 @popCount(@as(u4, @bitCast(encoding.value.arm64.frame.d_reg_pairs)));
2562 const min_reg_addr = fp - num_restored_pairs * 2 * @sizeOf(usize);
2563
2564 if (ma.load(usize, new_sp) == null or ma.load(usize, min_reg_addr) == null) return error.InvalidUnwindInfo;
2565
2566 var reg_addr = fp - @sizeOf(usize);
2567 inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.x_reg_pairs)).Struct.fields, 0..) |field, i| {
2568 if (@field(encoding.value.arm64.frame.x_reg_pairs, field.name) != 0) {
2569 (try abi.regValueNative(usize, context.thread_context, 19 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2570 reg_addr += @sizeOf(usize);
2571 (try abi.regValueNative(usize, context.thread_context, 20 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
2572 reg_addr += @sizeOf(usize);
2573 }
2574 }
2575
2576 inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.d_reg_pairs)).Struct.fields, 0..) |field, i| {
2577 if (@field(encoding.value.arm64.frame.d_reg_pairs, field.name) != 0) {
2578 // Only the lower half of the 128-bit V registers are restored during unwinding
2579 @memcpy(
2580 try abi.regBytes(context.thread_context, 64 + 8 + i, context.reg_context),
2581 std.mem.asBytes(@as(*const usize, @ptrFromInt(reg_addr))),
2582 );
2583 reg_addr += @sizeOf(usize);
2584 @memcpy(
2585 try abi.regBytes(context.thread_context, 64 + 9 + i, context.reg_context),
2586 std.mem.asBytes(@as(*const usize, @ptrFromInt(reg_addr))),
2587 );
2588 reg_addr += @sizeOf(usize);
2589 }
2590 }
2591
2592 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
2593 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
2594
2595 (try abi.regValueNative(usize, context.thread_context, abi.fpRegNum(reg_context), reg_context)).* = new_fp;
2596 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), reg_context)).* = new_ip;
2597
2598 break :blk new_ip;
2599 },
2600 },
2601 else => return error.UnimplementedArch,
2602 };
2603
2604 context.pc = abi.stripInstructionPtrAuthCode(new_ip);
2605 if (context.pc > 0) context.pc -= 1;
2606 return new_ip;
2607}
2608
2609fn unwindFrameMachODwarf(
2610 context: *UnwindContext,
2611 ma: *std.debug.StackIterator.MemoryAccessor,
2612 eh_frame: []const u8,
2613 fde_offset: usize,
2614) !usize {
2615 var di = Dwarf{
2616 .endian = native_endian,
2617 .is_macho = true,
2618 };
2619 defer di.deinit(context.allocator);
2620
2621 di.sections[@intFromEnum(Section.Id.eh_frame)] = .{
2622 .data = eh_frame,
2623 .owned = false,
2624 };
2625
2626 return di.unwindFrame(context, ma, fde_offset);
2627}
2628
26291945const EhPointerContext = struct {
26301946 // The address of the pointer field itself
26311947 pc_rel_base: u64,
......@@ -2641,7 +1957,7 @@ const EhPointerContext = struct {
26411957 text_rel_base: ?u64 = null,
26421958 function_rel_base: ?u64 = null,
26431959};
2644fn readEhPointer(fbr: *FixedBufferReader, enc: u8, addr_size_bytes: u8, ctx: EhPointerContext) !?u64 {
1960fn readEhPointer(fbr: *DeprecatedFixedBufferReader, enc: u8, addr_size_bytes: u8, ctx: EhPointerContext) !?u64 {
26451961 if (enc == EH.PE.omit) return null;
26461962
26471963 const value: union(enum) {
......@@ -2664,7 +1980,7 @@ fn readEhPointer(fbr: *FixedBufferReader, enc: u8, addr_size_bytes: u8, ctx: EhP
26641980 EH.PE.sdata2 => .{ .signed = try fbr.readInt(i16) },
26651981 EH.PE.sdata4 => .{ .signed = try fbr.readInt(i32) },
26661982 EH.PE.sdata8 => .{ .signed = try fbr.readInt(i64) },
2667 else => return badDwarf(),
1983 else => return bad(),
26681984 };
26691985
26701986 const base = switch (enc & EH.PE.rel_mask) {
lib/std/debug/Dwarf/abi.zig+33-94
......@@ -1,8 +1,9 @@
11const builtin = @import("builtin");
2
23const std = @import("../../std.zig");
34const mem = std.mem;
4const native_os = builtin.os.tag;
55const posix = std.posix;
6const Arch = std.Target.Cpu.Arch;
67
78pub fn supportsUnwinding(target: std.Target) bool {
89 return switch (target.cpu.arch) {
......@@ -26,8 +27,8 @@ pub fn supportsUnwinding(target: std.Target) bool {
2627 };
2728}
2829
29pub fn ipRegNum() u8 {
30 return switch (builtin.cpu.arch) {
30pub fn ipRegNum(arch: Arch) u8 {
31 return switch (arch) {
3132 .x86 => 8,
3233 .x86_64 => 16,
3334 .arm => 15,
......@@ -36,9 +37,10 @@ pub fn ipRegNum() u8 {
3637 };
3738}
3839
39pub fn fpRegNum(reg_context: RegisterContext) u8 {
40 return switch (builtin.cpu.arch) {
41 // GCC on OS X historically did the opposite of ELF for these registers (only in .eh_frame), and that is now the convention for MachO
40pub fn fpRegNum(arch: Arch, reg_context: RegisterContext) u8 {
41 return switch (arch) {
42 // GCC on OS X historically did the opposite of ELF for these registers
43 // (only in .eh_frame), and that is now the convention for MachO
4244 .x86 => if (reg_context.eh_frame and reg_context.is_macho) 4 else 5,
4345 .x86_64 => 6,
4446 .arm => 11,
......@@ -47,8 +49,8 @@ pub fn fpRegNum(reg_context: RegisterContext) u8 {
4749 };
4850}
4951
50pub fn spRegNum(reg_context: RegisterContext) u8 {
51 return switch (builtin.cpu.arch) {
52pub fn spRegNum(arch: Arch, reg_context: RegisterContext) u8 {
53 return switch (arch) {
5254 .x86 => if (reg_context.eh_frame and reg_context.is_macho) 5 else 4,
5355 .x86_64 => 7,
5456 .arm => 13,
......@@ -57,33 +59,12 @@ pub fn spRegNum(reg_context: RegisterContext) u8 {
5759 };
5860}
5961
60/// Some platforms use pointer authentication - the upper bits of instruction pointers contain a signature.
61/// This function clears these signature bits to make the pointer usable.
62pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize {
63 if (builtin.cpu.arch == .aarch64) {
64 // `hint 0x07` maps to `xpaclri` (or `nop` if the hardware doesn't support it)
65 // The save / restore is because `xpaclri` operates on x30 (LR)
66 return asm (
67 \\mov x16, x30
68 \\mov x30, x15
69 \\hint 0x07
70 \\mov x15, x30
71 \\mov x30, x16
72 : [ret] "={x15}" (-> usize),
73 : [ptr] "{x15}" (ptr),
74 : "x16"
75 );
76 }
77
78 return ptr;
79}
80
8162pub const RegisterContext = struct {
8263 eh_frame: bool,
8364 is_macho: bool,
8465};
8566
86pub const AbiError = error{
67pub const RegBytesError = error{
8768 InvalidRegister,
8869 UnimplementedArch,
8970 UnimplementedOs,
......@@ -91,55 +72,21 @@ pub const AbiError = error{
9172 ThreadContextNotSupported,
9273};
9374
94fn RegValueReturnType(comptime ContextPtrType: type, comptime T: type) type {
95 const reg_bytes_type = comptime RegBytesReturnType(ContextPtrType);
96 const info = @typeInfo(reg_bytes_type).Pointer;
97 return @Type(.{
98 .Pointer = .{
99 .size = .One,
100 .is_const = info.is_const,
101 .is_volatile = info.is_volatile,
102 .is_allowzero = info.is_allowzero,
103 .alignment = info.alignment,
104 .address_space = info.address_space,
105 .child = T,
106 .sentinel = null,
107 },
108 });
109}
110
111/// Returns a pointer to a register stored in a ThreadContext, preserving the pointer attributes of the context.
112pub fn regValueNative(
113 comptime T: type,
114 thread_context_ptr: anytype,
115 reg_number: u8,
116 reg_context: ?RegisterContext,
117) !RegValueReturnType(@TypeOf(thread_context_ptr), T) {
118 const reg_bytes = try regBytes(thread_context_ptr, reg_number, reg_context);
119 if (@sizeOf(T) != reg_bytes.len) return error.IncompatibleRegisterSize;
120 return mem.bytesAsValue(T, reg_bytes[0..@sizeOf(T)]);
121}
122
123fn RegBytesReturnType(comptime ContextPtrType: type) type {
124 const info = @typeInfo(ContextPtrType);
125 if (info != .Pointer or info.Pointer.child != std.debug.ThreadContext) {
126 @compileError("Expected a pointer to std.debug.ThreadContext, got " ++ @typeName(@TypeOf(ContextPtrType)));
127 }
128
129 return if (info.Pointer.is_const) return []const u8 else []u8;
130}
131
13275/// Returns a slice containing the backing storage for `reg_number`.
13376///
77/// This function assumes the Dwarf information corresponds not necessarily to
78/// the current executable, but at least with a matching CPU architecture and
79/// OS. It is planned to lift this limitation with a future enhancement.
80///
13481/// `reg_context` describes in what context the register number is used, as it can have different
13582/// meanings depending on the DWARF container. It is only required when getting the stack or
13683/// frame pointer register on some architectures.
13784pub fn regBytes(
138 thread_context_ptr: anytype,
85 thread_context_ptr: *std.debug.ThreadContext,
13986 reg_number: u8,
14087 reg_context: ?RegisterContext,
141) AbiError!RegBytesReturnType(@TypeOf(thread_context_ptr)) {
142 if (native_os == .windows) {
88) RegBytesError![]u8 {
89 if (builtin.os.tag == .windows) {
14390 return switch (builtin.cpu.arch) {
14491 .x86 => switch (reg_number) {
14592 0 => mem.asBytes(&thread_context_ptr.Eax),
......@@ -194,7 +141,7 @@ pub fn regBytes(
194141
195142 const ucontext_ptr = thread_context_ptr;
196143 return switch (builtin.cpu.arch) {
197 .x86 => switch (native_os) {
144 .x86 => switch (builtin.os.tag) {
198145 .linux, .netbsd, .solaris, .illumos => switch (reg_number) {
199146 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EAX]),
200147 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ECX]),
......@@ -229,7 +176,7 @@ pub fn regBytes(
229176 },
230177 else => error.UnimplementedOs,
231178 },
232 .x86_64 => switch (native_os) {
179 .x86_64 => switch (builtin.os.tag) {
233180 .linux, .solaris, .illumos => switch (reg_number) {
234181 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RAX]),
235182 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDX]),
......@@ -248,7 +195,7 @@ pub fn regBytes(
248195 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R14]),
249196 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R15]),
250197 16 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RIP]),
251 17...32 => |i| if (native_os.isSolarish())
198 17...32 => |i| if (builtin.os.tag.isSolarish())
252199 mem.asBytes(&ucontext_ptr.mcontext.fpregs.chip_state.xmm[i - 17])
253200 else
254201 mem.asBytes(&ucontext_ptr.mcontext.fpregs.xmm[i - 17]),
......@@ -318,7 +265,7 @@ pub fn regBytes(
318265 },
319266 else => error.UnimplementedOs,
320267 },
321 .arm => switch (native_os) {
268 .arm => switch (builtin.os.tag) {
322269 .linux => switch (reg_number) {
323270 0 => mem.asBytes(&ucontext_ptr.mcontext.arm_r0),
324271 1 => mem.asBytes(&ucontext_ptr.mcontext.arm_r1),
......@@ -341,7 +288,7 @@ pub fn regBytes(
341288 },
342289 else => error.UnimplementedOs,
343290 },
344 .aarch64 => switch (native_os) {
291 .aarch64 => switch (builtin.os.tag) {
345292 .macos, .ios => switch (reg_number) {
346293 0...28 => mem.asBytes(&ucontext_ptr.mcontext.ss.regs[reg_number]),
347294 29 => mem.asBytes(&ucontext_ptr.mcontext.ss.fp),
......@@ -389,22 +336,14 @@ pub fn regBytes(
389336 };
390337}
391338
392/// Returns the ABI-defined default value this register has in the unwinding table
393/// before running any of the CIE instructions. The DWARF spec defines these as having
394/// the .undefined rule by default, but allows ABI authors to override that.
395pub fn getRegDefaultValue(reg_number: u8, context: *std.debug.Dwarf.UnwindContext, out: []u8) !void {
396 switch (builtin.cpu.arch) {
397 .aarch64 => {
398 // Callee-saved registers are initialized as if they had the .same_value rule
399 if (reg_number >= 19 and reg_number <= 28) {
400 const src = try regBytes(context.thread_context, reg_number, context.reg_context);
401 if (src.len != out.len) return error.RegisterSizeMismatch;
402 @memcpy(out, src);
403 return;
404 }
405 },
406 else => {},
407 }
408
409 @memset(out, undefined);
339/// Returns a pointer to a register stored in a ThreadContext, preserving the
340/// pointer attributes of the context.
341pub fn regValueNative(
342 thread_context_ptr: *std.debug.ThreadContext,
343 reg_number: u8,
344 reg_context: ?RegisterContext,
345) !*align(1) usize {
346 const reg_bytes = try regBytes(thread_context_ptr, reg_number, reg_context);
347 if (@sizeOf(usize) != reg_bytes.len) return error.IncompatibleRegisterSize;
348 return mem.bytesAsValue(usize, reg_bytes[0..@sizeOf(usize)]);
410349}
lib/std/debug/Dwarf/call_frame.zig-388
......@@ -297,391 +297,3 @@ pub const Instruction = union(Opcode) {
297297 }
298298 }
299299};
300
301/// Since register rules are applied (usually) during a panic,
302/// checked addition / subtraction is used so that we can return
303/// an error and fall back to FP-based unwinding.
304pub fn applyOffset(base: usize, offset: i64) !usize {
305 return if (offset >= 0)
306 try std.math.add(usize, base, @as(usize, @intCast(offset)))
307 else
308 try std.math.sub(usize, base, @as(usize, @intCast(-offset)));
309}
310
311/// This is a virtual machine that runs DWARF call frame instructions.
312pub const VirtualMachine = struct {
313 /// See section 6.4.1 of the DWARF5 specification for details on each
314 const RegisterRule = union(enum) {
315 // The spec says that the default rule for each column is the undefined rule.
316 // However, it also allows ABI / compiler authors to specify alternate defaults, so
317 // there is a distinction made here.
318 default: void,
319
320 undefined: void,
321 same_value: void,
322
323 // offset(N)
324 offset: i64,
325
326 // val_offset(N)
327 val_offset: i64,
328
329 // register(R)
330 register: u8,
331
332 // expression(E)
333 expression: []const u8,
334
335 // val_expression(E)
336 val_expression: []const u8,
337
338 // Augmenter-defined rule
339 architectural: void,
340 };
341
342 /// Each row contains unwinding rules for a set of registers.
343 pub const Row = struct {
344 /// Offset from `FrameDescriptionEntry.pc_begin`
345 offset: u64 = 0,
346
347 /// Special-case column that defines the CFA (Canonical Frame Address) rule.
348 /// The register field of this column defines the register that CFA is derived from.
349 cfa: Column = .{},
350
351 /// The register fields in these columns define the register the rule applies to.
352 columns: ColumnRange = .{},
353
354 /// Indicates that the next write to any column in this row needs to copy
355 /// the backing column storage first, as it may be referenced by previous rows.
356 copy_on_write: bool = false,
357 };
358
359 pub const Column = struct {
360 register: ?u8 = null,
361 rule: RegisterRule = .{ .default = {} },
362
363 /// Resolves the register rule and places the result into `out` (see dwarf.abi.regBytes)
364 pub fn resolveValue(
365 self: Column,
366 context: *std.debug.Dwarf.UnwindContext,
367 expression_context: std.debug.Dwarf.expression.Context,
368 ma: *debug.StackIterator.MemoryAccessor,
369 out: []u8,
370 ) !void {
371 switch (self.rule) {
372 .default => {
373 const register = self.register orelse return error.InvalidRegister;
374 try abi.getRegDefaultValue(register, context, out);
375 },
376 .undefined => {
377 @memset(out, undefined);
378 },
379 .same_value => {
380 // TODO: This copy could be eliminated if callers always copy the state then call this function to update it
381 const register = self.register orelse return error.InvalidRegister;
382 const src = try abi.regBytes(context.thread_context, register, context.reg_context);
383 if (src.len != out.len) return error.RegisterSizeMismatch;
384 @memcpy(out, src);
385 },
386 .offset => |offset| {
387 if (context.cfa) |cfa| {
388 const addr = try applyOffset(cfa, offset);
389 if (ma.load(usize, addr) == null) return error.InvalidAddress;
390 const ptr: *const usize = @ptrFromInt(addr);
391 mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);
392 } else return error.InvalidCFA;
393 },
394 .val_offset => |offset| {
395 if (context.cfa) |cfa| {
396 mem.writeInt(usize, out[0..@sizeOf(usize)], try applyOffset(cfa, offset), native_endian);
397 } else return error.InvalidCFA;
398 },
399 .register => |register| {
400 const src = try abi.regBytes(context.thread_context, register, context.reg_context);
401 if (src.len != out.len) return error.RegisterSizeMismatch;
402 @memcpy(out, try abi.regBytes(context.thread_context, register, context.reg_context));
403 },
404 .expression => |expression| {
405 context.stack_machine.reset();
406 const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);
407 const addr = if (value) |v| blk: {
408 if (v != .generic) return error.InvalidExpressionValue;
409 break :blk v.generic;
410 } else return error.NoExpressionValue;
411
412 if (ma.load(usize, addr) == null) return error.InvalidExpressionAddress;
413 const ptr: *usize = @ptrFromInt(addr);
414 mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);
415 },
416 .val_expression => |expression| {
417 context.stack_machine.reset();
418 const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);
419 if (value) |v| {
420 if (v != .generic) return error.InvalidExpressionValue;
421 mem.writeInt(usize, out[0..@sizeOf(usize)], v.generic, native_endian);
422 } else return error.NoExpressionValue;
423 },
424 .architectural => return error.UnimplementedRegisterRule,
425 }
426 }
427 };
428
429 const ColumnRange = struct {
430 /// Index into `columns` of the first column in this row.
431 start: usize = undefined,
432 len: u8 = 0,
433 };
434
435 columns: std.ArrayListUnmanaged(Column) = .{},
436 stack: std.ArrayListUnmanaged(ColumnRange) = .{},
437 current_row: Row = .{},
438
439 /// The result of executing the CIE's initial_instructions
440 cie_row: ?Row = null,
441
442 pub fn deinit(self: *VirtualMachine, allocator: std.mem.Allocator) void {
443 self.stack.deinit(allocator);
444 self.columns.deinit(allocator);
445 self.* = undefined;
446 }
447
448 pub fn reset(self: *VirtualMachine) void {
449 self.stack.clearRetainingCapacity();
450 self.columns.clearRetainingCapacity();
451 self.current_row = .{};
452 self.cie_row = null;
453 }
454
455 /// Return a slice backed by the row's non-CFA columns
456 pub fn rowColumns(self: VirtualMachine, row: Row) []Column {
457 if (row.columns.len == 0) return &.{};
458 return self.columns.items[row.columns.start..][0..row.columns.len];
459 }
460
461 /// Either retrieves or adds a column for `register` (non-CFA) in the current row.
462 fn getOrAddColumn(self: *VirtualMachine, allocator: std.mem.Allocator, register: u8) !*Column {
463 for (self.rowColumns(self.current_row)) |*c| {
464 if (c.register == register) return c;
465 }
466
467 if (self.current_row.columns.len == 0) {
468 self.current_row.columns.start = self.columns.items.len;
469 }
470 self.current_row.columns.len += 1;
471
472 const column = try self.columns.addOne(allocator);
473 column.* = .{
474 .register = register,
475 };
476
477 return column;
478 }
479
480 /// Runs the CIE instructions, then the FDE instructions. Execution halts
481 /// once the row that corresponds to `pc` is known, and the row is returned.
482 pub fn runTo(
483 self: *VirtualMachine,
484 allocator: std.mem.Allocator,
485 pc: u64,
486 cie: std.debug.Dwarf.CommonInformationEntry,
487 fde: std.debug.Dwarf.FrameDescriptionEntry,
488 addr_size_bytes: u8,
489 endian: std.builtin.Endian,
490 ) !Row {
491 assert(self.cie_row == null);
492 if (pc < fde.pc_begin or pc >= fde.pc_begin + fde.pc_range) return error.AddressOutOfRange;
493
494 var prev_row: Row = self.current_row;
495
496 var cie_stream = std.io.fixedBufferStream(cie.initial_instructions);
497 var fde_stream = std.io.fixedBufferStream(fde.instructions);
498 var streams = [_]*std.io.FixedBufferStream([]const u8){
499 &cie_stream,
500 &fde_stream,
501 };
502
503 for (&streams, 0..) |stream, i| {
504 while (stream.pos < stream.buffer.len) {
505 const instruction = try std.debug.Dwarf.call_frame.Instruction.read(stream, addr_size_bytes, endian);
506 prev_row = try self.step(allocator, cie, i == 0, instruction);
507 if (pc < fde.pc_begin + self.current_row.offset) return prev_row;
508 }
509 }
510
511 return self.current_row;
512 }
513
514 pub fn runToNative(
515 self: *VirtualMachine,
516 allocator: std.mem.Allocator,
517 pc: u64,
518 cie: std.debug.Dwarf.CommonInformationEntry,
519 fde: std.debug.Dwarf.FrameDescriptionEntry,
520 ) !Row {
521 return self.runTo(allocator, pc, cie, fde, @sizeOf(usize), builtin.target.cpu.arch.endian());
522 }
523
524 fn resolveCopyOnWrite(self: *VirtualMachine, allocator: std.mem.Allocator) !void {
525 if (!self.current_row.copy_on_write) return;
526
527 const new_start = self.columns.items.len;
528 if (self.current_row.columns.len > 0) {
529 try self.columns.ensureUnusedCapacity(allocator, self.current_row.columns.len);
530 self.columns.appendSliceAssumeCapacity(self.rowColumns(self.current_row));
531 self.current_row.columns.start = new_start;
532 }
533 }
534
535 /// Executes a single instruction.
536 /// If this instruction is from the CIE, `is_initial` should be set.
537 /// Returns the value of `current_row` before executing this instruction.
538 pub fn step(
539 self: *VirtualMachine,
540 allocator: std.mem.Allocator,
541 cie: std.debug.Dwarf.CommonInformationEntry,
542 is_initial: bool,
543 instruction: Instruction,
544 ) !Row {
545 // CIE instructions must be run before FDE instructions
546 assert(!is_initial or self.cie_row == null);
547 if (!is_initial and self.cie_row == null) {
548 self.cie_row = self.current_row;
549 self.current_row.copy_on_write = true;
550 }
551
552 const prev_row = self.current_row;
553 switch (instruction) {
554 .set_loc => |i| {
555 if (i.address <= self.current_row.offset) return error.InvalidOperation;
556 // TODO: Check cie.segment_selector_size != 0 for DWARFV4
557 self.current_row.offset = i.address;
558 },
559 inline .advance_loc,
560 .advance_loc1,
561 .advance_loc2,
562 .advance_loc4,
563 => |i| {
564 self.current_row.offset += i.delta * cie.code_alignment_factor;
565 self.current_row.copy_on_write = true;
566 },
567 inline .offset,
568 .offset_extended,
569 .offset_extended_sf,
570 => |i| {
571 try self.resolveCopyOnWrite(allocator);
572 const column = try self.getOrAddColumn(allocator, i.register);
573 column.rule = .{ .offset = @as(i64, @intCast(i.offset)) * cie.data_alignment_factor };
574 },
575 inline .restore,
576 .restore_extended,
577 => |i| {
578 try self.resolveCopyOnWrite(allocator);
579 if (self.cie_row) |cie_row| {
580 const column = try self.getOrAddColumn(allocator, i.register);
581 column.rule = for (self.rowColumns(cie_row)) |cie_column| {
582 if (cie_column.register == i.register) break cie_column.rule;
583 } else .{ .default = {} };
584 } else return error.InvalidOperation;
585 },
586 .nop => {},
587 .undefined => |i| {
588 try self.resolveCopyOnWrite(allocator);
589 const column = try self.getOrAddColumn(allocator, i.register);
590 column.rule = .{ .undefined = {} };
591 },
592 .same_value => |i| {
593 try self.resolveCopyOnWrite(allocator);
594 const column = try self.getOrAddColumn(allocator, i.register);
595 column.rule = .{ .same_value = {} };
596 },
597 .register => |i| {
598 try self.resolveCopyOnWrite(allocator);
599 const column = try self.getOrAddColumn(allocator, i.register);
600 column.rule = .{ .register = i.target_register };
601 },
602 .remember_state => {
603 try self.stack.append(allocator, self.current_row.columns);
604 self.current_row.copy_on_write = true;
605 },
606 .restore_state => {
607 const restored_columns = self.stack.popOrNull() orelse return error.InvalidOperation;
608 self.columns.shrinkRetainingCapacity(self.columns.items.len - self.current_row.columns.len);
609 try self.columns.ensureUnusedCapacity(allocator, restored_columns.len);
610
611 self.current_row.columns.start = self.columns.items.len;
612 self.current_row.columns.len = restored_columns.len;
613 self.columns.appendSliceAssumeCapacity(self.columns.items[restored_columns.start..][0..restored_columns.len]);
614 },
615 .def_cfa => |i| {
616 try self.resolveCopyOnWrite(allocator);
617 self.current_row.cfa = .{
618 .register = i.register,
619 .rule = .{ .val_offset = @intCast(i.offset) },
620 };
621 },
622 .def_cfa_sf => |i| {
623 try self.resolveCopyOnWrite(allocator);
624 self.current_row.cfa = .{
625 .register = i.register,
626 .rule = .{ .val_offset = i.offset * cie.data_alignment_factor },
627 };
628 },
629 .def_cfa_register => |i| {
630 try self.resolveCopyOnWrite(allocator);
631 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
632 self.current_row.cfa.register = i.register;
633 },
634 .def_cfa_offset => |i| {
635 try self.resolveCopyOnWrite(allocator);
636 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
637 self.current_row.cfa.rule = .{
638 .val_offset = @intCast(i.offset),
639 };
640 },
641 .def_cfa_offset_sf => |i| {
642 try self.resolveCopyOnWrite(allocator);
643 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
644 self.current_row.cfa.rule = .{
645 .val_offset = i.offset * cie.data_alignment_factor,
646 };
647 },
648 .def_cfa_expression => |i| {
649 try self.resolveCopyOnWrite(allocator);
650 self.current_row.cfa.register = undefined;
651 self.current_row.cfa.rule = .{
652 .expression = i.block,
653 };
654 },
655 .expression => |i| {
656 try self.resolveCopyOnWrite(allocator);
657 const column = try self.getOrAddColumn(allocator, i.register);
658 column.rule = .{
659 .expression = i.block,
660 };
661 },
662 .val_offset => |i| {
663 try self.resolveCopyOnWrite(allocator);
664 const column = try self.getOrAddColumn(allocator, i.register);
665 column.rule = .{
666 .val_offset = @as(i64, @intCast(i.offset)) * cie.data_alignment_factor,
667 };
668 },
669 .val_offset_sf => |i| {
670 try self.resolveCopyOnWrite(allocator);
671 const column = try self.getOrAddColumn(allocator, i.register);
672 column.rule = .{
673 .val_offset = i.offset * cie.data_alignment_factor,
674 };
675 },
676 .val_expression => |i| {
677 try self.resolveCopyOnWrite(allocator);
678 const column = try self.getOrAddColumn(allocator, i.register);
679 column.rule = .{
680 .val_expression = i.block,
681 };
682 },
683 }
684
685 return prev_row;
686 }
687};
lib/std/debug/Dwarf/expression.zig+14-12
......@@ -1,11 +1,13 @@
1const std = @import("std");
21const builtin = @import("builtin");
2const native_arch = builtin.cpu.arch;
3const native_endian = native_arch.endian();
4
5const std = @import("std");
36const leb = std.leb;
47const OP = std.dwarf.OP;
58const abi = std.debug.Dwarf.abi;
69const mem = std.mem;
710const assert = std.debug.assert;
8const native_endian = builtin.cpu.arch.endian();
911
1012/// Expressions can be evaluated in different contexts, each requiring its own set of inputs.
1113/// Callers should specify all the fields relevant to their context. If a field is required
......@@ -14,7 +16,7 @@ pub const Context = struct {
1416 /// The dwarf format of the section this expression is in
1517 format: std.dwarf.Format = .@"32",
1618 /// If specified, any addresses will pass through before being accessed
17 memory_accessor: ?*std.debug.StackIterator.MemoryAccessor = null,
19 memory_accessor: ?*std.debug.MemoryAccessor = null,
1820 /// The compilation unit this expression relates to, if any
1921 compile_unit: ?*const std.debug.Dwarf.CompileUnit = null,
2022 /// When evaluating a user-presented expression, this is the address of the object being evaluated
......@@ -34,7 +36,7 @@ pub const Options = struct {
3436 /// The address size of the target architecture
3537 addr_size: u8 = @sizeOf(usize),
3638 /// Endianness of the target architecture
37 endian: std.builtin.Endian = builtin.target.cpu.arch.endian(),
39 endian: std.builtin.Endian = native_endian,
3840 /// Restrict the stack machine to a subset of opcodes used in call frame instructions
3941 call_frame_context: bool = false,
4042};
......@@ -60,7 +62,7 @@ pub const Error = error{
6062 InvalidTypeLength,
6163
6264 TruncatedIntegralType,
63} || abi.AbiError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero };
65} || abi.RegBytesError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero };
6466
6567/// A stack machine that can decode and run DWARF expressions.
6668/// Expressions can be decoded for non-native address size and endianness,
......@@ -304,7 +306,7 @@ pub fn StackMachine(comptime options: Options) type {
304306 allocator: std.mem.Allocator,
305307 context: Context,
306308 ) Error!bool {
307 if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != comptime builtin.target.cpu.arch.endian())
309 if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != native_endian)
308310 @compileError("Execution of non-native address sizes / endianness is not supported");
309311
310312 const opcode = try stream.reader().readByte();
......@@ -1186,13 +1188,13 @@ test "DWARF expressions" {
11861188 // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it
11871189
11881190 mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian);
1189 (try abi.regValueNative(usize, &thread_context, abi.fpRegNum(reg_context), reg_context)).* = 1;
1190 (try abi.regValueNative(usize, &thread_context, abi.spRegNum(reg_context), reg_context)).* = 2;
1191 (try abi.regValueNative(usize, &thread_context, abi.ipRegNum(), reg_context)).* = 3;
1191 (try abi.regValueNative(&thread_context, abi.fpRegNum(native_arch, reg_context), reg_context)).* = 1;
1192 (try abi.regValueNative(&thread_context, abi.spRegNum(native_arch, reg_context), reg_context)).* = 2;
1193 (try abi.regValueNative(&thread_context, abi.ipRegNum(native_arch), reg_context)).* = 3;
11921194
1193 try b.writeBreg(writer, abi.fpRegNum(reg_context), @as(usize, 100));
1194 try b.writeBreg(writer, abi.spRegNum(reg_context), @as(usize, 200));
1195 try b.writeBregx(writer, abi.ipRegNum(), @as(usize, 300));
1195 try b.writeBreg(writer, abi.fpRegNum(native_arch, reg_context), @as(usize, 100));
1196 try b.writeBreg(writer, abi.spRegNum(native_arch, reg_context), @as(usize, 200));
1197 try b.writeBregx(writer, abi.ipRegNum(native_arch), @as(usize, 300));
11961198 try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400));
11971199
11981200 _ = try stack_machine.run(program.items, allocator, context, 0);
lib/std/debug/MemoryAccessor.zig created+128
......@@ -0,0 +1,128 @@
1//! Reads memory from any address of the current location using OS-specific
2//! syscalls, bypassing memory page protection. Useful for stack unwinding.
3
4const builtin = @import("builtin");
5const native_os = builtin.os.tag;
6
7const std = @import("../std.zig");
8const posix = std.posix;
9const File = std.fs.File;
10const page_size = std.mem.page_size;
11
12const MemoryAccessor = @This();
13
14var cached_pid: posix.pid_t = -1;
15
16mem: switch (native_os) {
17 .linux => File,
18 else => void,
19},
20
21pub const init: MemoryAccessor = .{
22 .mem = switch (native_os) {
23 .linux => .{ .handle = -1 },
24 else => {},
25 },
26};
27
28fn read(ma: *MemoryAccessor, address: usize, buf: []u8) bool {
29 switch (native_os) {
30 .linux => while (true) switch (ma.mem.handle) {
31 -2 => break,
32 -1 => {
33 const linux = std.os.linux;
34 const pid = switch (@atomicLoad(posix.pid_t, &cached_pid, .monotonic)) {
35 -1 => pid: {
36 const pid = linux.getpid();
37 @atomicStore(posix.pid_t, &cached_pid, pid, .monotonic);
38 break :pid pid;
39 },
40 else => |pid| pid,
41 };
42 const bytes_read = linux.process_vm_readv(
43 pid,
44 &.{.{ .base = buf.ptr, .len = buf.len }},
45 &.{.{ .base = @ptrFromInt(address), .len = buf.len }},
46 0,
47 );
48 switch (linux.E.init(bytes_read)) {
49 .SUCCESS => return bytes_read == buf.len,
50 .FAULT => return false,
51 .INVAL, .PERM, .SRCH => unreachable, // own pid is always valid
52 .NOMEM => {},
53 .NOSYS => {}, // QEMU is known not to implement this syscall.
54 else => unreachable, // unexpected
55 }
56 var path_buf: [
57 std.fmt.count("/proc/{d}/mem", .{std.math.minInt(posix.pid_t)})
58 ]u8 = undefined;
59 const path = std.fmt.bufPrint(&path_buf, "/proc/{d}/mem", .{pid}) catch
60 unreachable;
61 ma.mem = std.fs.openFileAbsolute(path, .{}) catch {
62 ma.mem.handle = -2;
63 break;
64 };
65 },
66 else => return (ma.mem.pread(buf, address) catch return false) == buf.len,
67 },
68 else => {},
69 }
70 if (!isValidMemory(address)) return false;
71 @memcpy(buf, @as([*]const u8, @ptrFromInt(address)));
72 return true;
73}
74
75pub fn load(ma: *MemoryAccessor, comptime Type: type, address: usize) ?Type {
76 var result: Type = undefined;
77 return if (ma.read(address, std.mem.asBytes(&result))) result else null;
78}
79
80pub fn isValidMemory(address: usize) bool {
81 // We are unable to determine validity of memory for freestanding targets
82 if (native_os == .freestanding or native_os == .uefi) return true;
83
84 const aligned_address = address & ~@as(usize, @intCast((page_size - 1)));
85 if (aligned_address == 0) return false;
86 const aligned_memory = @as([*]align(page_size) u8, @ptrFromInt(aligned_address))[0..page_size];
87
88 if (native_os == .windows) {
89 const windows = std.os.windows;
90
91 var memory_info: windows.MEMORY_BASIC_INFORMATION = undefined;
92
93 // The only error this function can throw is ERROR_INVALID_PARAMETER.
94 // supply an address that invalid i'll be thrown.
95 const rc = windows.VirtualQuery(aligned_memory, &memory_info, aligned_memory.len) catch {
96 return false;
97 };
98
99 // Result code has to be bigger than zero (number of bytes written)
100 if (rc == 0) {
101 return false;
102 }
103
104 // Free pages cannot be read, they are unmapped
105 if (memory_info.State == windows.MEM_FREE) {
106 return false;
107 }
108
109 return true;
110 } else if (have_msync) {
111 posix.msync(aligned_memory, posix.MSF.ASYNC) catch |err| {
112 switch (err) {
113 error.UnmappedMemory => return false,
114 else => unreachable,
115 }
116 };
117
118 return true;
119 } else {
120 // We are unable to determine validity of memory on this target.
121 return true;
122 }
123}
124
125const have_msync = switch (native_os) {
126 .wasi, .emscripten, .windows => false,
127 else => true,
128};
lib/std/debug/SelfInfo.zig+1033
......@@ -22,6 +22,9 @@ const Pdb = std.debug.Pdb;
2222const File = std.fs.File;
2323const math = std.math;
2424const testing = std.testing;
25const StackIterator = std.debug.StackIterator;
26const regBytes = Dwarf.abi.regBytes;
27const regValueNative = Dwarf.abi.regValueNative;
2528
2629const SelfInfo = @This();
2730
......@@ -1369,3 +1372,1033 @@ fn getSymbolFromDwarf(allocator: Allocator, address: u64, di: *Dwarf) !SymbolInf
13691372 else => return err,
13701373 }
13711374}
1375
1376/// Unwind a frame using MachO compact unwind info (from __unwind_info).
1377/// If the compact encoding can't encode a way to unwind a frame, it will
1378/// defer unwinding to DWARF, in which case `.eh_frame` will be used if available.
1379pub fn unwindFrameMachO(
1380 context: *UnwindContext,
1381 ma: *std.debug.MemoryAccessor,
1382 unwind_info: []const u8,
1383 eh_frame: ?[]const u8,
1384 module_base_address: usize,
1385) !usize {
1386 const header = std.mem.bytesAsValue(
1387 macho.unwind_info_section_header,
1388 unwind_info[0..@sizeOf(macho.unwind_info_section_header)],
1389 );
1390 const indices = std.mem.bytesAsSlice(
1391 macho.unwind_info_section_header_index_entry,
1392 unwind_info[header.indexSectionOffset..][0 .. header.indexCount * @sizeOf(macho.unwind_info_section_header_index_entry)],
1393 );
1394 if (indices.len == 0) return error.MissingUnwindInfo;
1395
1396 const mapped_pc = context.pc - module_base_address;
1397 const second_level_index = blk: {
1398 var left: usize = 0;
1399 var len: usize = indices.len;
1400
1401 while (len > 1) {
1402 const mid = left + len / 2;
1403 const offset = indices[mid].functionOffset;
1404 if (mapped_pc < offset) {
1405 len /= 2;
1406 } else {
1407 left = mid;
1408 if (mapped_pc == offset) break;
1409 len -= len / 2;
1410 }
1411 }
1412
1413 // Last index is a sentinel containing the highest address as its functionOffset
1414 if (indices[left].secondLevelPagesSectionOffset == 0) return error.MissingUnwindInfo;
1415 break :blk &indices[left];
1416 };
1417
1418 const common_encodings = std.mem.bytesAsSlice(
1419 macho.compact_unwind_encoding_t,
1420 unwind_info[header.commonEncodingsArraySectionOffset..][0 .. header.commonEncodingsArrayCount * @sizeOf(macho.compact_unwind_encoding_t)],
1421 );
1422
1423 const start_offset = second_level_index.secondLevelPagesSectionOffset;
1424 const kind = std.mem.bytesAsValue(
1425 macho.UNWIND_SECOND_LEVEL,
1426 unwind_info[start_offset..][0..@sizeOf(macho.UNWIND_SECOND_LEVEL)],
1427 );
1428
1429 const entry: struct {
1430 function_offset: usize,
1431 raw_encoding: u32,
1432 } = switch (kind.*) {
1433 .REGULAR => blk: {
1434 const page_header = std.mem.bytesAsValue(
1435 macho.unwind_info_regular_second_level_page_header,
1436 unwind_info[start_offset..][0..@sizeOf(macho.unwind_info_regular_second_level_page_header)],
1437 );
1438
1439 const entries = std.mem.bytesAsSlice(
1440 macho.unwind_info_regular_second_level_entry,
1441 unwind_info[start_offset + page_header.entryPageOffset ..][0 .. page_header.entryCount * @sizeOf(macho.unwind_info_regular_second_level_entry)],
1442 );
1443 if (entries.len == 0) return error.InvalidUnwindInfo;
1444
1445 var left: usize = 0;
1446 var len: usize = entries.len;
1447 while (len > 1) {
1448 const mid = left + len / 2;
1449 const offset = entries[mid].functionOffset;
1450 if (mapped_pc < offset) {
1451 len /= 2;
1452 } else {
1453 left = mid;
1454 if (mapped_pc == offset) break;
1455 len -= len / 2;
1456 }
1457 }
1458
1459 break :blk .{
1460 .function_offset = entries[left].functionOffset,
1461 .raw_encoding = entries[left].encoding,
1462 };
1463 },
1464 .COMPRESSED => blk: {
1465 const page_header = std.mem.bytesAsValue(
1466 macho.unwind_info_compressed_second_level_page_header,
1467 unwind_info[start_offset..][0..@sizeOf(macho.unwind_info_compressed_second_level_page_header)],
1468 );
1469
1470 const entries = std.mem.bytesAsSlice(
1471 macho.UnwindInfoCompressedEntry,
1472 unwind_info[start_offset + page_header.entryPageOffset ..][0 .. page_header.entryCount * @sizeOf(macho.UnwindInfoCompressedEntry)],
1473 );
1474 if (entries.len == 0) return error.InvalidUnwindInfo;
1475
1476 var left: usize = 0;
1477 var len: usize = entries.len;
1478 while (len > 1) {
1479 const mid = left + len / 2;
1480 const offset = second_level_index.functionOffset + entries[mid].funcOffset;
1481 if (mapped_pc < offset) {
1482 len /= 2;
1483 } else {
1484 left = mid;
1485 if (mapped_pc == offset) break;
1486 len -= len / 2;
1487 }
1488 }
1489
1490 const entry = entries[left];
1491 const function_offset = second_level_index.functionOffset + entry.funcOffset;
1492 if (entry.encodingIndex < header.commonEncodingsArrayCount) {
1493 if (entry.encodingIndex >= common_encodings.len) return error.InvalidUnwindInfo;
1494 break :blk .{
1495 .function_offset = function_offset,
1496 .raw_encoding = common_encodings[entry.encodingIndex],
1497 };
1498 } else {
1499 const local_index = try math.sub(
1500 u8,
1501 entry.encodingIndex,
1502 math.cast(u8, header.commonEncodingsArrayCount) orelse return error.InvalidUnwindInfo,
1503 );
1504 const local_encodings = std.mem.bytesAsSlice(
1505 macho.compact_unwind_encoding_t,
1506 unwind_info[start_offset + page_header.encodingsPageOffset ..][0 .. page_header.encodingsCount * @sizeOf(macho.compact_unwind_encoding_t)],
1507 );
1508 if (local_index >= local_encodings.len) return error.InvalidUnwindInfo;
1509 break :blk .{
1510 .function_offset = function_offset,
1511 .raw_encoding = local_encodings[local_index],
1512 };
1513 }
1514 },
1515 else => return error.InvalidUnwindInfo,
1516 };
1517
1518 if (entry.raw_encoding == 0) return error.NoUnwindInfo;
1519 const reg_context = Dwarf.abi.RegisterContext{
1520 .eh_frame = false,
1521 .is_macho = true,
1522 };
1523
1524 const encoding: macho.CompactUnwindEncoding = @bitCast(entry.raw_encoding);
1525 const new_ip = switch (builtin.cpu.arch) {
1526 .x86_64 => switch (encoding.mode.x86_64) {
1527 .OLD => return error.UnimplementedUnwindEncoding,
1528 .RBP_FRAME => blk: {
1529 const regs: [5]u3 = .{
1530 encoding.value.x86_64.frame.reg0,
1531 encoding.value.x86_64.frame.reg1,
1532 encoding.value.x86_64.frame.reg2,
1533 encoding.value.x86_64.frame.reg3,
1534 encoding.value.x86_64.frame.reg4,
1535 };
1536
1537 const frame_offset = encoding.value.x86_64.frame.frame_offset * @sizeOf(usize);
1538 var max_reg: usize = 0;
1539 inline for (regs, 0..) |reg, i| {
1540 if (reg > 0) max_reg = i;
1541 }
1542
1543 const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*;
1544 const new_sp = fp + 2 * @sizeOf(usize);
1545
1546 // Verify the stack range we're about to read register values from
1547 if (ma.load(usize, new_sp) == null or ma.load(usize, fp - frame_offset + max_reg * @sizeOf(usize)) == null) return error.InvalidUnwindInfo;
1548
1549 const ip_ptr = fp + @sizeOf(usize);
1550 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
1551 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
1552
1553 (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).* = new_fp;
1554 (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp;
1555 (try regValueNative(context.thread_context, ip_reg_num, reg_context)).* = new_ip;
1556
1557 for (regs, 0..) |reg, i| {
1558 if (reg == 0) continue;
1559 const addr = fp - frame_offset + i * @sizeOf(usize);
1560 const reg_number = try Dwarf.compactUnwindToDwarfRegNumber(reg);
1561 (try regValueNative(context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(addr)).*;
1562 }
1563
1564 break :blk new_ip;
1565 },
1566 .STACK_IMMD,
1567 .STACK_IND,
1568 => blk: {
1569 const sp = (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).*;
1570 const stack_size = if (encoding.mode.x86_64 == .STACK_IMMD)
1571 @as(usize, encoding.value.x86_64.frameless.stack.direct.stack_size) * @sizeOf(usize)
1572 else stack_size: {
1573 // In .STACK_IND, the stack size is inferred from the subq instruction at the beginning of the function.
1574 const sub_offset_addr =
1575 module_base_address +
1576 entry.function_offset +
1577 encoding.value.x86_64.frameless.stack.indirect.sub_offset;
1578 if (ma.load(usize, sub_offset_addr) == null) return error.InvalidUnwindInfo;
1579
1580 // `sub_offset_addr` points to the offset of the literal within the instruction
1581 const sub_operand = @as(*align(1) const u32, @ptrFromInt(sub_offset_addr)).*;
1582 break :stack_size sub_operand + @sizeOf(usize) * @as(usize, encoding.value.x86_64.frameless.stack.indirect.stack_adjust);
1583 };
1584
1585 // Decode the Lehmer-coded sequence of registers.
1586 // For a description of the encoding see lib/libc/include/any-macos.13-any/mach-o/compact_unwind_encoding.h
1587
1588 // Decode the variable-based permutation number into its digits. Each digit represents
1589 // an index into the list of register numbers that weren't yet used in the sequence at
1590 // the time the digit was added.
1591 const reg_count = encoding.value.x86_64.frameless.stack_reg_count;
1592 const ip_ptr = if (reg_count > 0) reg_blk: {
1593 var digits: [6]u3 = undefined;
1594 var accumulator: usize = encoding.value.x86_64.frameless.stack_reg_permutation;
1595 var base: usize = 2;
1596 for (0..reg_count) |i| {
1597 const div = accumulator / base;
1598 digits[digits.len - 1 - i] = @intCast(accumulator - base * div);
1599 accumulator = div;
1600 base += 1;
1601 }
1602
1603 const reg_numbers = [_]u3{ 1, 2, 3, 4, 5, 6 };
1604 var registers: [reg_numbers.len]u3 = undefined;
1605 var used_indices = [_]bool{false} ** reg_numbers.len;
1606 for (digits[digits.len - reg_count ..], 0..) |target_unused_index, i| {
1607 var unused_count: u8 = 0;
1608 const unused_index = for (used_indices, 0..) |used, index| {
1609 if (!used) {
1610 if (target_unused_index == unused_count) break index;
1611 unused_count += 1;
1612 }
1613 } else unreachable;
1614
1615 registers[i] = reg_numbers[unused_index];
1616 used_indices[unused_index] = true;
1617 }
1618
1619 var reg_addr = sp + stack_size - @sizeOf(usize) * @as(usize, reg_count + 1);
1620 if (ma.load(usize, reg_addr) == null) return error.InvalidUnwindInfo;
1621 for (0..reg_count) |i| {
1622 const reg_number = try Dwarf.compactUnwindToDwarfRegNumber(registers[i]);
1623 (try regValueNative(context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
1624 reg_addr += @sizeOf(usize);
1625 }
1626
1627 break :reg_blk reg_addr;
1628 } else sp + stack_size - @sizeOf(usize);
1629
1630 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
1631 const new_sp = ip_ptr + @sizeOf(usize);
1632 if (ma.load(usize, new_sp) == null) return error.InvalidUnwindInfo;
1633
1634 (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp;
1635 (try regValueNative(context.thread_context, ip_reg_num, reg_context)).* = new_ip;
1636
1637 break :blk new_ip;
1638 },
1639 .DWARF => {
1640 return unwindFrameMachODwarf(context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.x86_64.dwarf));
1641 },
1642 },
1643 .aarch64 => switch (encoding.mode.arm64) {
1644 .OLD => return error.UnimplementedUnwindEncoding,
1645 .FRAMELESS => blk: {
1646 const sp = (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).*;
1647 const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16;
1648 const new_ip = (try regValueNative(context.thread_context, 30, reg_context)).*;
1649 if (ma.load(usize, new_sp) == null) return error.InvalidUnwindInfo;
1650 (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp;
1651 break :blk new_ip;
1652 },
1653 .DWARF => {
1654 return unwindFrameMachODwarf(context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.arm64.dwarf));
1655 },
1656 .FRAME => blk: {
1657 const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*;
1658 const new_sp = fp + 16;
1659 const ip_ptr = fp + @sizeOf(usize);
1660
1661 const num_restored_pairs: usize =
1662 @popCount(@as(u5, @bitCast(encoding.value.arm64.frame.x_reg_pairs))) +
1663 @popCount(@as(u4, @bitCast(encoding.value.arm64.frame.d_reg_pairs)));
1664 const min_reg_addr = fp - num_restored_pairs * 2 * @sizeOf(usize);
1665
1666 if (ma.load(usize, new_sp) == null or ma.load(usize, min_reg_addr) == null) return error.InvalidUnwindInfo;
1667
1668 var reg_addr = fp - @sizeOf(usize);
1669 inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.x_reg_pairs)).Struct.fields, 0..) |field, i| {
1670 if (@field(encoding.value.arm64.frame.x_reg_pairs, field.name) != 0) {
1671 (try regValueNative(context.thread_context, 19 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
1672 reg_addr += @sizeOf(usize);
1673 (try regValueNative(context.thread_context, 20 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
1674 reg_addr += @sizeOf(usize);
1675 }
1676 }
1677
1678 inline for (@typeInfo(@TypeOf(encoding.value.arm64.frame.d_reg_pairs)).Struct.fields, 0..) |field, i| {
1679 if (@field(encoding.value.arm64.frame.d_reg_pairs, field.name) != 0) {
1680 // Only the lower half of the 128-bit V registers are restored during unwinding
1681 @memcpy(
1682 try regBytes(context.thread_context, 64 + 8 + i, context.reg_context),
1683 std.mem.asBytes(@as(*const usize, @ptrFromInt(reg_addr))),
1684 );
1685 reg_addr += @sizeOf(usize);
1686 @memcpy(
1687 try regBytes(context.thread_context, 64 + 9 + i, context.reg_context),
1688 std.mem.asBytes(@as(*const usize, @ptrFromInt(reg_addr))),
1689 );
1690 reg_addr += @sizeOf(usize);
1691 }
1692 }
1693
1694 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
1695 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
1696
1697 (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).* = new_fp;
1698 (try regValueNative(context.thread_context, ip_reg_num, reg_context)).* = new_ip;
1699
1700 break :blk new_ip;
1701 },
1702 },
1703 else => return error.UnimplementedArch,
1704 };
1705
1706 context.pc = stripInstructionPtrAuthCode(new_ip);
1707 if (context.pc > 0) context.pc -= 1;
1708 return new_ip;
1709}
1710
1711pub const UnwindContext = struct {
1712 allocator: Allocator,
1713 cfa: ?usize,
1714 pc: usize,
1715 thread_context: *std.debug.ThreadContext,
1716 reg_context: Dwarf.abi.RegisterContext,
1717 vm: VirtualMachine,
1718 stack_machine: Dwarf.expression.StackMachine(.{ .call_frame_context = true }),
1719
1720 pub fn init(
1721 allocator: Allocator,
1722 thread_context: *std.debug.ThreadContext,
1723 ) !UnwindContext {
1724 const pc = stripInstructionPtrAuthCode(
1725 (try regValueNative(thread_context, ip_reg_num, null)).*,
1726 );
1727
1728 const context_copy = try allocator.create(std.debug.ThreadContext);
1729 std.debug.copyContext(thread_context, context_copy);
1730
1731 return .{
1732 .allocator = allocator,
1733 .cfa = null,
1734 .pc = pc,
1735 .thread_context = context_copy,
1736 .reg_context = undefined,
1737 .vm = .{},
1738 .stack_machine = .{},
1739 };
1740 }
1741
1742 pub fn deinit(self: *UnwindContext) void {
1743 self.vm.deinit(self.allocator);
1744 self.stack_machine.deinit(self.allocator);
1745 self.allocator.destroy(self.thread_context);
1746 self.* = undefined;
1747 }
1748
1749 pub fn getFp(self: *const UnwindContext) !usize {
1750 return (try regValueNative(self.thread_context, fpRegNum(self.reg_context), self.reg_context)).*;
1751 }
1752};
1753
1754/// Some platforms use pointer authentication - the upper bits of instruction pointers contain a signature.
1755/// This function clears these signature bits to make the pointer usable.
1756pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize {
1757 if (native_arch == .aarch64) {
1758 // `hint 0x07` maps to `xpaclri` (or `nop` if the hardware doesn't support it)
1759 // The save / restore is because `xpaclri` operates on x30 (LR)
1760 return asm (
1761 \\mov x16, x30
1762 \\mov x30, x15
1763 \\hint 0x07
1764 \\mov x15, x30
1765 \\mov x30, x16
1766 : [ret] "={x15}" (-> usize),
1767 : [ptr] "{x15}" (ptr),
1768 : "x16"
1769 );
1770 }
1771
1772 return ptr;
1773}
1774
1775/// Unwind a stack frame using DWARF unwinding info, updating the register context.
1776///
1777/// If `.eh_frame_hdr` is available, it will be used to binary search for the FDE.
1778/// Otherwise, a linear scan of `.eh_frame` and `.debug_frame` is done to find the FDE.
1779///
1780/// `explicit_fde_offset` is for cases where the FDE offset is known, such as when __unwind_info
1781/// defers unwinding to DWARF. This is an offset into the `.eh_frame` section.
1782pub fn unwindFrameDwarf(
1783 di: *const Dwarf,
1784 context: *UnwindContext,
1785 ma: *std.debug.MemoryAccessor,
1786 explicit_fde_offset: ?usize,
1787) !usize {
1788 if (!supports_unwinding) return error.UnsupportedCpuArchitecture;
1789 if (context.pc == 0) return 0;
1790
1791 // Find the FDE and CIE
1792 var cie: Dwarf.CommonInformationEntry = undefined;
1793 var fde: Dwarf.FrameDescriptionEntry = undefined;
1794
1795 if (explicit_fde_offset) |fde_offset| {
1796 const dwarf_section: Dwarf.Section.Id = .eh_frame;
1797 const frame_section = di.section(dwarf_section) orelse return error.MissingFDE;
1798 if (fde_offset >= frame_section.len) return error.MissingFDE;
1799
1800 var fbr: std.debug.DeprecatedFixedBufferReader = .{
1801 .buf = frame_section,
1802 .pos = fde_offset,
1803 .endian = di.endian,
1804 };
1805
1806 const fde_entry_header = try Dwarf.EntryHeader.read(&fbr, null, dwarf_section);
1807 if (fde_entry_header.type != .fde) return error.MissingFDE;
1808
1809 const cie_offset = fde_entry_header.type.fde;
1810 try fbr.seekTo(cie_offset);
1811
1812 fbr.endian = native_endian;
1813 const cie_entry_header = try Dwarf.EntryHeader.read(&fbr, null, dwarf_section);
1814 if (cie_entry_header.type != .cie) return Dwarf.bad();
1815
1816 cie = try Dwarf.CommonInformationEntry.parse(
1817 cie_entry_header.entry_bytes,
1818 0,
1819 true,
1820 cie_entry_header.format,
1821 dwarf_section,
1822 cie_entry_header.length_offset,
1823 @sizeOf(usize),
1824 native_endian,
1825 );
1826
1827 fde = try Dwarf.FrameDescriptionEntry.parse(
1828 fde_entry_header.entry_bytes,
1829 0,
1830 true,
1831 cie,
1832 @sizeOf(usize),
1833 native_endian,
1834 );
1835 } else if (di.eh_frame_hdr) |header| {
1836 const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null;
1837 try header.findEntry(
1838 ma,
1839 eh_frame_len,
1840 @intFromPtr(di.section(.eh_frame_hdr).?.ptr),
1841 context.pc,
1842 &cie,
1843 &fde,
1844 );
1845 } else {
1846 const index = std.sort.binarySearch(Dwarf.FrameDescriptionEntry, context.pc, di.fde_list.items, {}, struct {
1847 pub fn compareFn(_: void, pc: usize, mid_item: Dwarf.FrameDescriptionEntry) std.math.Order {
1848 if (pc < mid_item.pc_begin) return .lt;
1849
1850 const range_end = mid_item.pc_begin + mid_item.pc_range;
1851 if (pc < range_end) return .eq;
1852
1853 return .gt;
1854 }
1855 }.compareFn);
1856
1857 fde = if (index) |i| di.fde_list.items[i] else return error.MissingFDE;
1858 cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE;
1859 }
1860
1861 var expression_context: Dwarf.expression.Context = .{
1862 .format = cie.format,
1863 .memory_accessor = ma,
1864 .compile_unit = di.findCompileUnit(fde.pc_begin) catch null,
1865 .thread_context = context.thread_context,
1866 .reg_context = context.reg_context,
1867 .cfa = context.cfa,
1868 };
1869
1870 context.vm.reset();
1871 context.reg_context.eh_frame = cie.version != 4;
1872 context.reg_context.is_macho = di.is_macho;
1873
1874 const row = try context.vm.runToNative(context.allocator, context.pc, cie, fde);
1875 context.cfa = switch (row.cfa.rule) {
1876 .val_offset => |offset| blk: {
1877 const register = row.cfa.register orelse return error.InvalidCFARule;
1878 const value = mem.readInt(usize, (try regBytes(context.thread_context, register, context.reg_context))[0..@sizeOf(usize)], native_endian);
1879 break :blk try applyOffset(value, offset);
1880 },
1881 .expression => |expr| blk: {
1882 context.stack_machine.reset();
1883 const value = try context.stack_machine.run(
1884 expr,
1885 context.allocator,
1886 expression_context,
1887 context.cfa,
1888 );
1889
1890 if (value) |v| {
1891 if (v != .generic) return error.InvalidExpressionValue;
1892 break :blk v.generic;
1893 } else return error.NoExpressionValue;
1894 },
1895 else => return error.InvalidCFARule,
1896 };
1897
1898 if (ma.load(usize, context.cfa.?) == null) return error.InvalidCFA;
1899 expression_context.cfa = context.cfa;
1900
1901 // Buffering the modifications is done because copying the thread context is not portable,
1902 // some implementations (ie. darwin) use internal pointers to the mcontext.
1903 var arena = std.heap.ArenaAllocator.init(context.allocator);
1904 defer arena.deinit();
1905 const update_allocator = arena.allocator();
1906
1907 const RegisterUpdate = struct {
1908 // Backed by thread_context
1909 dest: []u8,
1910 // Backed by arena
1911 src: []const u8,
1912 prev: ?*@This(),
1913 };
1914
1915 var update_tail: ?*RegisterUpdate = null;
1916 var has_return_address = true;
1917 for (context.vm.rowColumns(row)) |column| {
1918 if (column.register) |register| {
1919 if (register == cie.return_address_register) {
1920 has_return_address = column.rule != .undefined;
1921 }
1922
1923 const dest = try regBytes(context.thread_context, register, context.reg_context);
1924 const src = try update_allocator.alloc(u8, dest.len);
1925
1926 const prev = update_tail;
1927 update_tail = try update_allocator.create(RegisterUpdate);
1928 update_tail.?.* = .{
1929 .dest = dest,
1930 .src = src,
1931 .prev = prev,
1932 };
1933
1934 try column.resolveValue(
1935 context,
1936 expression_context,
1937 ma,
1938 src,
1939 );
1940 }
1941 }
1942
1943 // On all implemented architectures, the CFA is defined as being the previous frame's SP
1944 (try regValueNative(context.thread_context, spRegNum(context.reg_context), context.reg_context)).* = context.cfa.?;
1945
1946 while (update_tail) |tail| {
1947 @memcpy(tail.dest, tail.src);
1948 update_tail = tail.prev;
1949 }
1950
1951 if (has_return_address) {
1952 context.pc = stripInstructionPtrAuthCode(mem.readInt(usize, (try regBytes(
1953 context.thread_context,
1954 cie.return_address_register,
1955 context.reg_context,
1956 ))[0..@sizeOf(usize)], native_endian));
1957 } else {
1958 context.pc = 0;
1959 }
1960
1961 (try regValueNative(context.thread_context, ip_reg_num, context.reg_context)).* = context.pc;
1962
1963 // The call instruction will have pushed the address of the instruction that follows the call as the return address.
1964 // This next instruction may be past the end of the function if the caller was `noreturn` (ie. the last instruction in
1965 // the function was the call). If we were to look up an FDE entry using the return address directly, it could end up
1966 // either not finding an FDE at all, or using the next FDE in the program, producing incorrect results. To prevent this,
1967 // we subtract one so that the next lookup is guaranteed to land inside the
1968 //
1969 // The exception to this rule is signal frames, where we return execution would be returned to the instruction
1970 // that triggered the handler.
1971 const return_address = context.pc;
1972 if (context.pc > 0 and !cie.isSignalFrame()) context.pc -= 1;
1973
1974 return return_address;
1975}
1976
1977fn fpRegNum(reg_context: Dwarf.abi.RegisterContext) u8 {
1978 return Dwarf.abi.fpRegNum(native_arch, reg_context);
1979}
1980
1981fn spRegNum(reg_context: Dwarf.abi.RegisterContext) u8 {
1982 return Dwarf.abi.spRegNum(native_arch, reg_context);
1983}
1984
1985const ip_reg_num = Dwarf.abi.ipRegNum(native_arch);
1986const supports_unwinding = Dwarf.abi.supportsUnwinding(builtin.target);
1987
1988fn unwindFrameMachODwarf(
1989 context: *UnwindContext,
1990 ma: *std.debug.MemoryAccessor,
1991 eh_frame: []const u8,
1992 fde_offset: usize,
1993) !usize {
1994 var di: Dwarf = .{
1995 .endian = native_endian,
1996 .is_macho = true,
1997 };
1998 defer di.deinit(context.allocator);
1999
2000 di.sections[@intFromEnum(Dwarf.Section.Id.eh_frame)] = .{
2001 .data = eh_frame,
2002 .owned = false,
2003 };
2004
2005 return unwindFrameDwarf(&di, context, ma, fde_offset);
2006}
2007
2008/// This is a virtual machine that runs DWARF call frame instructions.
2009pub const VirtualMachine = struct {
2010 /// See section 6.4.1 of the DWARF5 specification for details on each
2011 const RegisterRule = union(enum) {
2012 // The spec says that the default rule for each column is the undefined rule.
2013 // However, it also allows ABI / compiler authors to specify alternate defaults, so
2014 // there is a distinction made here.
2015 default: void,
2016 undefined: void,
2017 same_value: void,
2018 // offset(N)
2019 offset: i64,
2020 // val_offset(N)
2021 val_offset: i64,
2022 // register(R)
2023 register: u8,
2024 // expression(E)
2025 expression: []const u8,
2026 // val_expression(E)
2027 val_expression: []const u8,
2028 // Augmenter-defined rule
2029 architectural: void,
2030 };
2031
2032 /// Each row contains unwinding rules for a set of registers.
2033 pub const Row = struct {
2034 /// Offset from `FrameDescriptionEntry.pc_begin`
2035 offset: u64 = 0,
2036 /// Special-case column that defines the CFA (Canonical Frame Address) rule.
2037 /// The register field of this column defines the register that CFA is derived from.
2038 cfa: Column = .{},
2039 /// The register fields in these columns define the register the rule applies to.
2040 columns: ColumnRange = .{},
2041 /// Indicates that the next write to any column in this row needs to copy
2042 /// the backing column storage first, as it may be referenced by previous rows.
2043 copy_on_write: bool = false,
2044 };
2045
2046 pub const Column = struct {
2047 register: ?u8 = null,
2048 rule: RegisterRule = .{ .default = {} },
2049
2050 /// Resolves the register rule and places the result into `out` (see regBytes)
2051 pub fn resolveValue(
2052 self: Column,
2053 context: *SelfInfo.UnwindContext,
2054 expression_context: std.debug.Dwarf.expression.Context,
2055 ma: *std.debug.MemoryAccessor,
2056 out: []u8,
2057 ) !void {
2058 switch (self.rule) {
2059 .default => {
2060 const register = self.register orelse return error.InvalidRegister;
2061 try getRegDefaultValue(register, context, out);
2062 },
2063 .undefined => {
2064 @memset(out, undefined);
2065 },
2066 .same_value => {
2067 // TODO: This copy could be eliminated if callers always copy the state then call this function to update it
2068 const register = self.register orelse return error.InvalidRegister;
2069 const src = try regBytes(context.thread_context, register, context.reg_context);
2070 if (src.len != out.len) return error.RegisterSizeMismatch;
2071 @memcpy(out, src);
2072 },
2073 .offset => |offset| {
2074 if (context.cfa) |cfa| {
2075 const addr = try applyOffset(cfa, offset);
2076 if (ma.load(usize, addr) == null) return error.InvalidAddress;
2077 const ptr: *const usize = @ptrFromInt(addr);
2078 mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);
2079 } else return error.InvalidCFA;
2080 },
2081 .val_offset => |offset| {
2082 if (context.cfa) |cfa| {
2083 mem.writeInt(usize, out[0..@sizeOf(usize)], try applyOffset(cfa, offset), native_endian);
2084 } else return error.InvalidCFA;
2085 },
2086 .register => |register| {
2087 const src = try regBytes(context.thread_context, register, context.reg_context);
2088 if (src.len != out.len) return error.RegisterSizeMismatch;
2089 @memcpy(out, try regBytes(context.thread_context, register, context.reg_context));
2090 },
2091 .expression => |expression| {
2092 context.stack_machine.reset();
2093 const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);
2094 const addr = if (value) |v| blk: {
2095 if (v != .generic) return error.InvalidExpressionValue;
2096 break :blk v.generic;
2097 } else return error.NoExpressionValue;
2098
2099 if (ma.load(usize, addr) == null) return error.InvalidExpressionAddress;
2100 const ptr: *usize = @ptrFromInt(addr);
2101 mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);
2102 },
2103 .val_expression => |expression| {
2104 context.stack_machine.reset();
2105 const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);
2106 if (value) |v| {
2107 if (v != .generic) return error.InvalidExpressionValue;
2108 mem.writeInt(usize, out[0..@sizeOf(usize)], v.generic, native_endian);
2109 } else return error.NoExpressionValue;
2110 },
2111 .architectural => return error.UnimplementedRegisterRule,
2112 }
2113 }
2114 };
2115
2116 const ColumnRange = struct {
2117 /// Index into `columns` of the first column in this row.
2118 start: usize = undefined,
2119 len: u8 = 0,
2120 };
2121
2122 columns: std.ArrayListUnmanaged(Column) = .{},
2123 stack: std.ArrayListUnmanaged(ColumnRange) = .{},
2124 current_row: Row = .{},
2125
2126 /// The result of executing the CIE's initial_instructions
2127 cie_row: ?Row = null,
2128
2129 pub fn deinit(self: *VirtualMachine, allocator: std.mem.Allocator) void {
2130 self.stack.deinit(allocator);
2131 self.columns.deinit(allocator);
2132 self.* = undefined;
2133 }
2134
2135 pub fn reset(self: *VirtualMachine) void {
2136 self.stack.clearRetainingCapacity();
2137 self.columns.clearRetainingCapacity();
2138 self.current_row = .{};
2139 self.cie_row = null;
2140 }
2141
2142 /// Return a slice backed by the row's non-CFA columns
2143 pub fn rowColumns(self: VirtualMachine, row: Row) []Column {
2144 if (row.columns.len == 0) return &.{};
2145 return self.columns.items[row.columns.start..][0..row.columns.len];
2146 }
2147
2148 /// Either retrieves or adds a column for `register` (non-CFA) in the current row.
2149 fn getOrAddColumn(self: *VirtualMachine, allocator: std.mem.Allocator, register: u8) !*Column {
2150 for (self.rowColumns(self.current_row)) |*c| {
2151 if (c.register == register) return c;
2152 }
2153
2154 if (self.current_row.columns.len == 0) {
2155 self.current_row.columns.start = self.columns.items.len;
2156 }
2157 self.current_row.columns.len += 1;
2158
2159 const column = try self.columns.addOne(allocator);
2160 column.* = .{
2161 .register = register,
2162 };
2163
2164 return column;
2165 }
2166
2167 /// Runs the CIE instructions, then the FDE instructions. Execution halts
2168 /// once the row that corresponds to `pc` is known, and the row is returned.
2169 pub fn runTo(
2170 self: *VirtualMachine,
2171 allocator: std.mem.Allocator,
2172 pc: u64,
2173 cie: std.debug.Dwarf.CommonInformationEntry,
2174 fde: std.debug.Dwarf.FrameDescriptionEntry,
2175 addr_size_bytes: u8,
2176 endian: std.builtin.Endian,
2177 ) !Row {
2178 assert(self.cie_row == null);
2179 if (pc < fde.pc_begin or pc >= fde.pc_begin + fde.pc_range) return error.AddressOutOfRange;
2180
2181 var prev_row: Row = self.current_row;
2182
2183 var cie_stream = std.io.fixedBufferStream(cie.initial_instructions);
2184 var fde_stream = std.io.fixedBufferStream(fde.instructions);
2185 var streams = [_]*std.io.FixedBufferStream([]const u8){
2186 &cie_stream,
2187 &fde_stream,
2188 };
2189
2190 for (&streams, 0..) |stream, i| {
2191 while (stream.pos < stream.buffer.len) {
2192 const instruction = try std.debug.Dwarf.call_frame.Instruction.read(stream, addr_size_bytes, endian);
2193 prev_row = try self.step(allocator, cie, i == 0, instruction);
2194 if (pc < fde.pc_begin + self.current_row.offset) return prev_row;
2195 }
2196 }
2197
2198 return self.current_row;
2199 }
2200
2201 pub fn runToNative(
2202 self: *VirtualMachine,
2203 allocator: std.mem.Allocator,
2204 pc: u64,
2205 cie: std.debug.Dwarf.CommonInformationEntry,
2206 fde: std.debug.Dwarf.FrameDescriptionEntry,
2207 ) !Row {
2208 return self.runTo(allocator, pc, cie, fde, @sizeOf(usize), native_endian);
2209 }
2210
2211 fn resolveCopyOnWrite(self: *VirtualMachine, allocator: std.mem.Allocator) !void {
2212 if (!self.current_row.copy_on_write) return;
2213
2214 const new_start = self.columns.items.len;
2215 if (self.current_row.columns.len > 0) {
2216 try self.columns.ensureUnusedCapacity(allocator, self.current_row.columns.len);
2217 self.columns.appendSliceAssumeCapacity(self.rowColumns(self.current_row));
2218 self.current_row.columns.start = new_start;
2219 }
2220 }
2221
2222 /// Executes a single instruction.
2223 /// If this instruction is from the CIE, `is_initial` should be set.
2224 /// Returns the value of `current_row` before executing this instruction.
2225 pub fn step(
2226 self: *VirtualMachine,
2227 allocator: std.mem.Allocator,
2228 cie: std.debug.Dwarf.CommonInformationEntry,
2229 is_initial: bool,
2230 instruction: Dwarf.call_frame.Instruction,
2231 ) !Row {
2232 // CIE instructions must be run before FDE instructions
2233 assert(!is_initial or self.cie_row == null);
2234 if (!is_initial and self.cie_row == null) {
2235 self.cie_row = self.current_row;
2236 self.current_row.copy_on_write = true;
2237 }
2238
2239 const prev_row = self.current_row;
2240 switch (instruction) {
2241 .set_loc => |i| {
2242 if (i.address <= self.current_row.offset) return error.InvalidOperation;
2243 // TODO: Check cie.segment_selector_size != 0 for DWARFV4
2244 self.current_row.offset = i.address;
2245 },
2246 inline .advance_loc,
2247 .advance_loc1,
2248 .advance_loc2,
2249 .advance_loc4,
2250 => |i| {
2251 self.current_row.offset += i.delta * cie.code_alignment_factor;
2252 self.current_row.copy_on_write = true;
2253 },
2254 inline .offset,
2255 .offset_extended,
2256 .offset_extended_sf,
2257 => |i| {
2258 try self.resolveCopyOnWrite(allocator);
2259 const column = try self.getOrAddColumn(allocator, i.register);
2260 column.rule = .{ .offset = @as(i64, @intCast(i.offset)) * cie.data_alignment_factor };
2261 },
2262 inline .restore,
2263 .restore_extended,
2264 => |i| {
2265 try self.resolveCopyOnWrite(allocator);
2266 if (self.cie_row) |cie_row| {
2267 const column = try self.getOrAddColumn(allocator, i.register);
2268 column.rule = for (self.rowColumns(cie_row)) |cie_column| {
2269 if (cie_column.register == i.register) break cie_column.rule;
2270 } else .{ .default = {} };
2271 } else return error.InvalidOperation;
2272 },
2273 .nop => {},
2274 .undefined => |i| {
2275 try self.resolveCopyOnWrite(allocator);
2276 const column = try self.getOrAddColumn(allocator, i.register);
2277 column.rule = .{ .undefined = {} };
2278 },
2279 .same_value => |i| {
2280 try self.resolveCopyOnWrite(allocator);
2281 const column = try self.getOrAddColumn(allocator, i.register);
2282 column.rule = .{ .same_value = {} };
2283 },
2284 .register => |i| {
2285 try self.resolveCopyOnWrite(allocator);
2286 const column = try self.getOrAddColumn(allocator, i.register);
2287 column.rule = .{ .register = i.target_register };
2288 },
2289 .remember_state => {
2290 try self.stack.append(allocator, self.current_row.columns);
2291 self.current_row.copy_on_write = true;
2292 },
2293 .restore_state => {
2294 const restored_columns = self.stack.popOrNull() orelse return error.InvalidOperation;
2295 self.columns.shrinkRetainingCapacity(self.columns.items.len - self.current_row.columns.len);
2296 try self.columns.ensureUnusedCapacity(allocator, restored_columns.len);
2297
2298 self.current_row.columns.start = self.columns.items.len;
2299 self.current_row.columns.len = restored_columns.len;
2300 self.columns.appendSliceAssumeCapacity(self.columns.items[restored_columns.start..][0..restored_columns.len]);
2301 },
2302 .def_cfa => |i| {
2303 try self.resolveCopyOnWrite(allocator);
2304 self.current_row.cfa = .{
2305 .register = i.register,
2306 .rule = .{ .val_offset = @intCast(i.offset) },
2307 };
2308 },
2309 .def_cfa_sf => |i| {
2310 try self.resolveCopyOnWrite(allocator);
2311 self.current_row.cfa = .{
2312 .register = i.register,
2313 .rule = .{ .val_offset = i.offset * cie.data_alignment_factor },
2314 };
2315 },
2316 .def_cfa_register => |i| {
2317 try self.resolveCopyOnWrite(allocator);
2318 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
2319 self.current_row.cfa.register = i.register;
2320 },
2321 .def_cfa_offset => |i| {
2322 try self.resolveCopyOnWrite(allocator);
2323 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
2324 self.current_row.cfa.rule = .{
2325 .val_offset = @intCast(i.offset),
2326 };
2327 },
2328 .def_cfa_offset_sf => |i| {
2329 try self.resolveCopyOnWrite(allocator);
2330 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
2331 self.current_row.cfa.rule = .{
2332 .val_offset = i.offset * cie.data_alignment_factor,
2333 };
2334 },
2335 .def_cfa_expression => |i| {
2336 try self.resolveCopyOnWrite(allocator);
2337 self.current_row.cfa.register = undefined;
2338 self.current_row.cfa.rule = .{
2339 .expression = i.block,
2340 };
2341 },
2342 .expression => |i| {
2343 try self.resolveCopyOnWrite(allocator);
2344 const column = try self.getOrAddColumn(allocator, i.register);
2345 column.rule = .{
2346 .expression = i.block,
2347 };
2348 },
2349 .val_offset => |i| {
2350 try self.resolveCopyOnWrite(allocator);
2351 const column = try self.getOrAddColumn(allocator, i.register);
2352 column.rule = .{
2353 .val_offset = @as(i64, @intCast(i.offset)) * cie.data_alignment_factor,
2354 };
2355 },
2356 .val_offset_sf => |i| {
2357 try self.resolveCopyOnWrite(allocator);
2358 const column = try self.getOrAddColumn(allocator, i.register);
2359 column.rule = .{
2360 .val_offset = i.offset * cie.data_alignment_factor,
2361 };
2362 },
2363 .val_expression => |i| {
2364 try self.resolveCopyOnWrite(allocator);
2365 const column = try self.getOrAddColumn(allocator, i.register);
2366 column.rule = .{
2367 .val_expression = i.block,
2368 };
2369 },
2370 }
2371
2372 return prev_row;
2373 }
2374};
2375
2376/// Returns the ABI-defined default value this register has in the unwinding table
2377/// before running any of the CIE instructions. The DWARF spec defines these as having
2378/// the .undefined rule by default, but allows ABI authors to override that.
2379fn getRegDefaultValue(reg_number: u8, context: *UnwindContext, out: []u8) !void {
2380 switch (builtin.cpu.arch) {
2381 .aarch64 => {
2382 // Callee-saved registers are initialized as if they had the .same_value rule
2383 if (reg_number >= 19 and reg_number <= 28) {
2384 const src = try regBytes(context.thread_context, reg_number, context.reg_context);
2385 if (src.len != out.len) return error.RegisterSizeMismatch;
2386 @memcpy(out, src);
2387 return;
2388 }
2389 },
2390 else => {},
2391 }
2392
2393 @memset(out, undefined);
2394}
2395
2396/// Since register rules are applied (usually) during a panic,
2397/// checked addition / subtraction is used so that we can return
2398/// an error and fall back to FP-based unwinding.
2399fn applyOffset(base: usize, offset: i64) !usize {
2400 return if (offset >= 0)
2401 try std.math.add(usize, base, @as(usize, @intCast(offset)))
2402 else
2403 try std.math.sub(usize, base, @as(usize, @intCast(-offset)));
2404}
src/crash_report.zig+1-1
......@@ -256,7 +256,7 @@ const StackContext = union(enum) {
256256 current: struct {
257257 ret_addr: ?usize,
258258 },
259 exception: *const debug.ThreadContext,
259 exception: *debug.ThreadContext,
260260 not_supported: void,
261261
262262 pub fn dumpStackTrace(ctx: @This()) void {