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;...@@ -13,6 +13,7 @@ const native_arch = builtin.cpu.arch;
13const native_os = builtin.os.tag;13const native_os = builtin.os.tag;
14const native_endian = native_arch.endian();14const native_endian = native_arch.endian();
1515
16pub const MemoryAccessor = @import("debug/MemoryAccessor.zig");
16pub const Dwarf = @import("debug/Dwarf.zig");17pub const Dwarf = @import("debug/Dwarf.zig");
17pub const Pdb = @import("debug/Pdb.zig");18pub const Pdb = @import("debug/Pdb.zig");
18pub const SelfInfo = @import("debug/SelfInfo.zig");19pub const SelfInfo = @import("debug/SelfInfo.zig");
...@@ -243,7 +244,7 @@ pub inline fn getContext(context: *ThreadContext) bool {...@@ -243,7 +244,7 @@ pub inline fn getContext(context: *ThreadContext) bool {
243/// Tries to print the stack trace starting from the supplied base pointer to stderr,244/// Tries to print the stack trace starting from the supplied base pointer to stderr,
244/// unbuffered, and ignores any error returned.245/// unbuffered, and ignores any error returned.
245/// TODO multithreaded awareness246/// TODO multithreaded awareness
246pub fn dumpStackTraceFromBase(context: *const ThreadContext) void {247pub fn dumpStackTraceFromBase(context: *ThreadContext) void {
247 nosuspend {248 nosuspend {
248 if (comptime builtin.target.isWasm()) {249 if (comptime builtin.target.isWasm()) {
249 if (native_os == .wasi) {250 if (native_os == .wasi) {
...@@ -545,7 +546,7 @@ pub const StackIterator = struct {...@@ -545,7 +546,7 @@ pub const StackIterator = struct {
545 // using DWARF and MachO unwind info.546 // using DWARF and MachO unwind info.
546 unwind_state: if (have_ucontext) ?struct {547 unwind_state: if (have_ucontext) ?struct {
547 debug_info: *SelfInfo,548 debug_info: *SelfInfo,
548 dwarf_context: Dwarf.UnwindContext,549 dwarf_context: SelfInfo.UnwindContext,
549 last_error: ?UnwindError = null,550 last_error: ?UnwindError = null,
550 failed: bool = false,551 failed: bool = false,
551 } else void = if (have_ucontext) null else {},552 } else void = if (have_ucontext) null else {},
...@@ -569,16 +570,16 @@ pub const StackIterator = struct {...@@ -569,16 +570,16 @@ pub const StackIterator = struct {
569 };570 };
570 }571 }
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 {
573 // The implementation of DWARF unwinding on aarch64-macos is not complete. However, Apple mandates that574 // The implementation of DWARF unwinding on aarch64-macos is not complete. However, Apple mandates that
574 // the frame pointer register is always used, so on this platform we can safely use the FP-based unwinder.575 // 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) {
576 return init(first_address, context.mcontext.ss.fp);577 return init(first_address, context.mcontext.ss.fp);
577 } else {578 } else {
578 var iterator = init(first_address, null);579 var iterator = init(first_address, null);
579 iterator.unwind_state = .{580 iterator.unwind_state = .{
580 .debug_info = debug_info,581 .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),
582 };583 };
583584
584 return iterator;585 return iterator;
...@@ -644,116 +645,6 @@ pub const StackIterator = struct {...@@ -644,116 +645,6 @@ pub const StackIterator = struct {
644 return address;645 return address;
645 }646 }
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
757 fn next_unwind(it: *StackIterator) !usize {648 fn next_unwind(it: *StackIterator) !usize {
758 const unwind_state = &it.unwind_state.?;649 const unwind_state = &it.unwind_state.?;
759 const module = try unwind_state.debug_info.getModuleForAddress(unwind_state.dwarf_context.pc);650 const module = try unwind_state.debug_info.getModuleForAddress(unwind_state.dwarf_context.pc);
...@@ -762,7 +653,13 @@ pub const StackIterator = struct {...@@ -762,7 +653,13 @@ pub const StackIterator = struct {
762 // __unwind_info is a requirement for unwinding on Darwin. It may fall back to DWARF, but unwinding653 // __unwind_info is a requirement for unwinding on Darwin. It may fall back to DWARF, but unwinding
763 // via DWARF before attempting to use the compact unwind info will produce incorrect results.654 // via DWARF before attempting to use the compact unwind info will produce incorrect results.
764 if (module.unwind_info) |unwind_info| {655 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| {
766 return return_address;663 return return_address;
767 } else |err| {664 } else |err| {
768 if (err != error.RequiresDWARFUnwind) return err;665 if (err != error.RequiresDWARFUnwind) return err;
...@@ -773,7 +670,7 @@ pub const StackIterator = struct {...@@ -773,7 +670,7 @@ pub const StackIterator = struct {
773 }670 }
774671
775 if (try module.getDwarfInfoForAddress(unwind_state.debug_info.allocator, unwind_state.dwarf_context.pc)) |di| {672 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);
777 } else return error.MissingDebugInfo;674 } else return error.MissingDebugInfo;
778 }675 }
779676
...@@ -822,11 +719,6 @@ pub const StackIterator = struct {...@@ -822,11 +719,6 @@ pub const StackIterator = struct {
822 }719 }
823};720};
824721
825const have_msync = switch (native_os) {
826 .wasi, .emscripten, .windows => false,
827 else => true,
828};
829
830pub fn writeCurrentStackTrace(722pub fn writeCurrentStackTrace(
831 out_stream: anytype,723 out_stream: anytype,
832 debug_info: *SelfInfo,724 debug_info: *SelfInfo,
...@@ -1333,7 +1225,7 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa...@@ -1333,7 +1225,7 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa
1333 posix.abort();1225 posix.abort();
1334}1226}
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 {
1337 const stderr = io.getStdErr().writer();1229 const stderr = io.getStdErr().writer();
1338 _ = switch (sig) {1230 _ = switch (sig) {
1339 posix.SIG.SEGV => if (native_arch == .x86_64 and native_os == .linux and code == 128) // SI_KERNEL1231 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...@@ -1359,7 +1251,7 @@ fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*const anyo
1359 .arm,1251 .arm,
1360 .aarch64,1252 .aarch64,
1361 => {1253 => {
1362 const ctx: *const posix.ucontext_t = @ptrCast(@alignCast(ctx_ptr));1254 const ctx: *posix.ucontext_t = @ptrCast(@alignCast(ctx_ptr));
1363 dumpStackTraceFromBase(ctx);1255 dumpStackTraceFromBase(ctx);
1364 },1256 },
1365 else => {},1257 else => {},
...@@ -1585,6 +1477,99 @@ pub const SafetyLock = struct {...@@ -1585,6 +1477,99 @@ pub const SafetyLock = struct {
1585 }1477 }
1586};1478};
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
1588/// Detect whether the program is being executed in the Valgrind virtual machine.1573/// Detect whether the program is being executed in the Valgrind virtual machine.
1589///1574///
1590/// When Valgrind integrations are disabled, this returns comptime-known false.1575/// When Valgrind integrations are disabled, this returns comptime-known false.
lib/std/debug/Dwarf.zig+116-800
...@@ -1,23 +1,32 @@...@@ -1,23 +1,32 @@
1//! Implements parsing, decoding, and caching of DWARF information.1//! Implements parsing, decoding, and caching of DWARF information.
2//!2//!
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//!
3//! For unopinionated types and bits, see `std.dwarf`.8//! For unopinionated types and bits, see `std.dwarf`.
49
5const builtin = @import("builtin");10const builtin = @import("builtin");
11const native_endian = builtin.cpu.arch.endian();
12
6const std = @import("../std.zig");13const std = @import("../std.zig");
7const AT = DW.AT;
8const Allocator = std.mem.Allocator;14const Allocator = std.mem.Allocator;
9const DW = std.dwarf;15const DW = std.dwarf;
16const AT = DW.AT;
10const EH = DW.EH;17const EH = DW.EH;
11const FORM = DW.FORM;18const FORM = DW.FORM;
12const Format = DW.Format;19const Format = DW.Format;
13const RLE = DW.RLE;20const RLE = DW.RLE;
14const StackIterator = std.debug.StackIterator;
15const UT = DW.UT;21const UT = DW.UT;
16const assert = std.debug.assert;22const assert = std.debug.assert;
17const cast = std.math.cast;23const cast = std.math.cast;
18const maxInt = std.math.maxInt;24const maxInt = std.math.maxInt;
19const native_endian = builtin.cpu.arch.endian();
20const readInt = std.mem.readInt;25const readInt = std.mem.readInt;
26const MemoryAccessor = std.debug.MemoryAccessor;
27
28/// Did I mention this is deprecated?
29const DeprecatedFixedBufferReader = std.debug.DeprecatedFixedBufferReader;
2130
22const Dwarf = @This();31const Dwarf = @This();
2332
...@@ -153,7 +162,7 @@ pub const FormValue = union(enum) {...@@ -153,7 +162,7 @@ pub const FormValue = union(enum) {
153 .string => |s| return s,162 .string => |s| return s,
154 .strp => |off| return di.getString(off),163 .strp => |off| return di.getString(off),
155 .line_strp => |off| return di.getLineString(off),164 .line_strp => |off| return di.getLineString(off),
156 else => return badDwarf(),165 else => return bad(),
157 }166 }
158 }167 }
159168
...@@ -162,8 +171,8 @@ pub const FormValue = union(enum) {...@@ -162,8 +171,8 @@ pub const FormValue = union(enum) {
162 inline .udata,171 inline .udata,
163 .sdata,172 .sdata,
164 .sec_offset,173 .sec_offset,
165 => |c| cast(U, c) orelse badDwarf(),174 => |c| cast(U, c) orelse bad(),
166 else => badDwarf(),175 else => bad(),
167 };176 };
168 }177 }
169};178};
...@@ -237,25 +246,25 @@ pub const Die = struct {...@@ -237,25 +246,25 @@ pub const Die = struct {
237 .string => |value| return value,246 .string => |value| return value,
238 .strp => |offset| return di.getString(offset),247 .strp => |offset| return di.getString(offset),
239 .strx => |index| {248 .strx => |index| {
240 const debug_str_offsets = di.section(.debug_str_offsets) orelse return badDwarf();249 const debug_str_offsets = di.section(.debug_str_offsets) orelse return bad();
241 if (compile_unit.str_offsets_base == 0) return badDwarf();250 if (compile_unit.str_offsets_base == 0) return bad();
242 switch (compile_unit.format) {251 switch (compile_unit.format) {
243 .@"32" => {252 .@"32" => {
244 const byte_offset = compile_unit.str_offsets_base + 4 * index;253 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();
246 const offset = readInt(u32, debug_str_offsets[byte_offset..][0..4], di.endian);255 const offset = readInt(u32, debug_str_offsets[byte_offset..][0..4], di.endian);
247 return getStringGeneric(opt_str, offset);256 return getStringGeneric(opt_str, offset);
248 },257 },
249 .@"64" => {258 .@"64" => {
250 const byte_offset = compile_unit.str_offsets_base + 8 * index;259 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();
252 const offset = readInt(u64, debug_str_offsets[byte_offset..][0..8], di.endian);261 const offset = readInt(u64, debug_str_offsets[byte_offset..][0..8], di.endian);
253 return getStringGeneric(opt_str, offset);262 return getStringGeneric(opt_str, offset);
254 },263 },
255 }264 }
256 },265 },
257 .line_strp => |offset| return di.getLineString(offset),266 .line_strp => |offset| return di.getLineString(offset),
258 else => return badDwarf(),267 else => return bad(),
259 }268 }
260 }269 }
261};270};
...@@ -279,7 +288,7 @@ pub const ExceptionFrameHeader = struct {...@@ -279,7 +288,7 @@ pub const ExceptionFrameHeader = struct {
279 EH.PE.sdata8,288 EH.PE.sdata8,
280 => 16,289 => 16,
281 // This is a binary search table, so all entries must be the same length290 // This is a binary search table, so all entries must be the same length
282 else => return badDwarf(),291 else => return bad(),
283 };292 };
284 }293 }
285294
...@@ -287,7 +296,7 @@ pub const ExceptionFrameHeader = struct {...@@ -287,7 +296,7 @@ pub const ExceptionFrameHeader = struct {
287 self: ExceptionFrameHeader,296 self: ExceptionFrameHeader,
288 comptime T: type,297 comptime T: type,
289 ptr: usize,298 ptr: usize,
290 ma: *StackIterator.MemoryAccessor,299 ma: *MemoryAccessor,
291 eh_frame_len: ?usize,300 eh_frame_len: ?usize,
292 ) bool {301 ) bool {
293 if (eh_frame_len) |len| {302 if (eh_frame_len) |len| {
...@@ -304,7 +313,7 @@ pub const ExceptionFrameHeader = struct {...@@ -304,7 +313,7 @@ pub const ExceptionFrameHeader = struct {
304 /// If `eh_frame_len` is provided, then these checks can be skipped.313 /// If `eh_frame_len` is provided, then these checks can be skipped.
305 pub fn findEntry(314 pub fn findEntry(
306 self: ExceptionFrameHeader,315 self: ExceptionFrameHeader,
307 ma: *StackIterator.MemoryAccessor,316 ma: *MemoryAccessor,
308 eh_frame_len: ?usize,317 eh_frame_len: ?usize,
309 eh_frame_hdr_ptr: usize,318 eh_frame_hdr_ptr: usize,
310 pc: usize,319 pc: usize,
...@@ -316,7 +325,7 @@ pub const ExceptionFrameHeader = struct {...@@ -316,7 +325,7 @@ pub const ExceptionFrameHeader = struct {
316 var left: usize = 0;325 var left: usize = 0;
317 var len: usize = self.fde_count;326 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
321 while (len > 1) {330 while (len > 1) {
322 const mid = left + len / 2;331 const mid = left + len / 2;
...@@ -326,7 +335,7 @@ pub const ExceptionFrameHeader = struct {...@@ -326,7 +335,7 @@ pub const ExceptionFrameHeader = struct {
326 .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),335 .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),
327 .follow_indirect = true,336 .follow_indirect = true,
328 .data_rel_base = eh_frame_hdr_ptr,337 .data_rel_base = eh_frame_hdr_ptr,
329 }) orelse return badDwarf();338 }) orelse return bad();
330339
331 if (pc < pc_begin) {340 if (pc < pc_begin) {
332 len /= 2;341 len /= 2;
...@@ -337,7 +346,7 @@ pub const ExceptionFrameHeader = struct {...@@ -337,7 +346,7 @@ pub const ExceptionFrameHeader = struct {
337 }346 }
338 }347 }
339348
340 if (len == 0) return badDwarf();349 if (len == 0) return bad();
341 fbr.pos = left * entry_size;350 fbr.pos = left * entry_size;
342351
343 // Read past the pc_begin field of the entry352 // Read past the pc_begin field of the entry
...@@ -345,36 +354,36 @@ pub const ExceptionFrameHeader = struct {...@@ -345,36 +354,36 @@ pub const ExceptionFrameHeader = struct {
345 .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),354 .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),
346 .follow_indirect = true,355 .follow_indirect = true,
347 .data_rel_base = eh_frame_hdr_ptr,356 .data_rel_base = eh_frame_hdr_ptr,
348 }) orelse return badDwarf();357 }) orelse return bad();
349358
350 const fde_ptr = cast(usize, try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{359 const fde_ptr = cast(usize, try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{
351 .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),360 .pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),
352 .follow_indirect = true,361 .follow_indirect = true,
353 .data_rel_base = eh_frame_hdr_ptr,362 .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
358 // Even if eh_frame_len is not specified, all ranges accssed are checked via MemoryAccessor367 // Even if eh_frame_len is not specified, all ranges accssed are checked via MemoryAccessor
359 const eh_frame = @as([*]const u8, @ptrFromInt(self.eh_frame_ptr))[0 .. eh_frame_len orelse maxInt(u32)];368 const eh_frame = @as([*]const u8, @ptrFromInt(self.eh_frame_ptr))[0 .. eh_frame_len orelse maxInt(u32)];
360369
361 const fde_offset = fde_ptr - self.eh_frame_ptr;370 const fde_offset = fde_ptr - self.eh_frame_ptr;
362 var eh_frame_fbr: FixedBufferReader = .{371 var eh_frame_fbr: DeprecatedFixedBufferReader = .{
363 .buf = eh_frame,372 .buf = eh_frame,
364 .pos = fde_offset,373 .pos = fde_offset,
365 .endian = native_endian,374 .endian = native_endian,
366 };375 };
367376
368 const fde_entry_header = try EntryHeader.read(&eh_frame_fbr, if (eh_frame_len == null) ma else null, .eh_frame);377 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();378 if (!self.isValidPtr(u8, @intFromPtr(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]), ma, eh_frame_len)) return bad();
370 if (fde_entry_header.type != .fde) return badDwarf();379 if (fde_entry_header.type != .fde) return bad();
371380
372 // CIEs always come before FDEs (the offset is a subtraction), so we can assume this memory is readable381 // CIEs always come before FDEs (the offset is a subtraction), so we can assume this memory is readable
373 const cie_offset = fde_entry_header.type.fde;382 const cie_offset = fde_entry_header.type.fde;
374 try eh_frame_fbr.seekTo(cie_offset);383 try eh_frame_fbr.seekTo(cie_offset);
375 const cie_entry_header = try EntryHeader.read(&eh_frame_fbr, if (eh_frame_len == null) ma else null, .eh_frame);384 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();385 if (!self.isValidPtr(u8, @intFromPtr(&cie_entry_header.entry_bytes[cie_entry_header.entry_bytes.len - 1]), ma, eh_frame_len)) return bad();
377 if (cie_entry_header.type != .cie) return badDwarf();386 if (cie_entry_header.type != .cie) return bad();
378387
379 cie.* = try CommonInformationEntry.parse(388 cie.* = try CommonInformationEntry.parse(
380 cie_entry_header.entry_bytes,389 cie_entry_header.entry_bytes,
...@@ -417,17 +426,17 @@ pub const EntryHeader = struct {...@@ -417,17 +426,17 @@ pub const EntryHeader = struct {
417 }426 }
418427
419 /// Reads a header for either an FDE or a CIE, then advances the fbr to the position after the trailing structure.428 /// 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.
421 pub fn read(430 pub fn read(
422 fbr: *FixedBufferReader,431 fbr: *DeprecatedFixedBufferReader,
423 opt_ma: ?*StackIterator.MemoryAccessor,432 opt_ma: ?*MemoryAccessor,
424 dwarf_section: Section.Id,433 dwarf_section: Section.Id,
425 ) !EntryHeader {434 ) !EntryHeader {
426 assert(dwarf_section == .eh_frame or dwarf_section == .debug_frame);435 assert(dwarf_section == .eh_frame or dwarf_section == .debug_frame);
427436
428 const length_offset = fbr.pos;437 const length_offset = fbr.pos;
429 const unit_header = try readUnitHeader(fbr, opt_ma);438 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();
431 if (unit_length == 0) return .{440 if (unit_length == 0) return .{
432 .length_offset = length_offset,441 .length_offset = length_offset,
433 .format = unit_header.format,442 .format = unit_header.format,
...@@ -532,7 +541,7 @@ pub const CommonInformationEntry = struct {...@@ -532,7 +541,7 @@ pub const CommonInformationEntry = struct {
532 ) !CommonInformationEntry {541 ) !CommonInformationEntry {
533 if (addr_size_bytes > 8) return error.UnsupportedAddrSize;542 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
537 const version = try fbr.readByte();546 const version = try fbr.readByte();
538 switch (dwarf_section) {547 switch (dwarf_section) {
...@@ -550,15 +559,15 @@ pub const CommonInformationEntry = struct {...@@ -550,15 +559,15 @@ pub const CommonInformationEntry = struct {
550 while (aug_byte != 0) : (aug_byte = try fbr.readByte()) {559 while (aug_byte != 0) : (aug_byte = try fbr.readByte()) {
551 switch (aug_byte) {560 switch (aug_byte) {
552 'z' => {561 'z' => {
553 if (aug_str_len != 0) return badDwarf();562 if (aug_str_len != 0) return bad();
554 has_aug_data = true;563 has_aug_data = true;
555 },564 },
556 'e' => {565 'e' => {
557 if (has_aug_data or aug_str_len != 0) return badDwarf();566 if (has_aug_data or aug_str_len != 0) return bad();
558 if (try fbr.readByte() != 'h') return badDwarf();567 if (try fbr.readByte() != 'h') return bad();
559 has_eh_data = true;568 has_eh_data = true;
560 },569 },
561 else => if (has_eh_data) return badDwarf(),570 else => if (has_eh_data) return bad(),
562 }571 }
563572
564 aug_str_len += 1;573 aug_str_len += 1;
...@@ -604,7 +613,7 @@ pub const CommonInformationEntry = struct {...@@ -604,7 +613,7 @@ pub const CommonInformationEntry = struct {
604 fde_pointer_enc = try fbr.readByte();613 fde_pointer_enc = try fbr.readByte();
605 },614 },
606 'S', 'B', 'G' => {},615 'S', 'B', 'G' => {},
607 else => return badDwarf(),616 else => return bad(),
608 }617 }
609 }618 }
610619
...@@ -666,17 +675,17 @@ pub const FrameDescriptionEntry = struct {...@@ -666,17 +675,17 @@ pub const FrameDescriptionEntry = struct {
666 ) !FrameDescriptionEntry {675 ) !FrameDescriptionEntry {
667 if (addr_size_bytes > 8) return error.InvalidAddrSize;676 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
671 const pc_begin = try readEhPointer(&fbr, cie.fde_pointer_enc, addr_size_bytes, .{680 const pc_begin = try readEhPointer(&fbr, cie.fde_pointer_enc, addr_size_bytes, .{
672 .pc_rel_base = try pcRelBase(@intFromPtr(&fde_bytes[fbr.pos]), pc_rel_offset),681 .pc_rel_base = try pcRelBase(@intFromPtr(&fde_bytes[fbr.pos]), pc_rel_offset),
673 .follow_indirect = is_runtime,682 .follow_indirect = is_runtime,
674 }) orelse return badDwarf();683 }) orelse return bad();
675684
676 const pc_range = try readEhPointer(&fbr, cie.fde_pointer_enc, addr_size_bytes, .{685 const pc_range = try readEhPointer(&fbr, cie.fde_pointer_enc, addr_size_bytes, .{
677 .pc_rel_base = 0,686 .pc_rel_base = 0,
678 .follow_indirect = false,687 .follow_indirect = false,
679 }) orelse return badDwarf();688 }) orelse return bad();
680689
681 var aug_data: []const u8 = &[_]u8{};690 var aug_data: []const u8 = &[_]u8{};
682 const lsda_pointer = if (cie.aug_str.len > 0) blk: {691 const lsda_pointer = if (cie.aug_str.len > 0) blk: {
...@@ -708,54 +717,6 @@ pub const FrameDescriptionEntry = struct {...@@ -708,54 +717,6 @@ pub const FrameDescriptionEntry = struct {
708 }717 }
709};718};
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
759const num_sections = std.enums.directEnumArrayLen(Section.Id, 0);720const num_sections = std.enums.directEnumArrayLen(Section.Id, 0);
760pub const SectionArray = [num_sections]?Section;721pub const SectionArray = [num_sections]?Section;
761pub const null_section_array = [_]?Section{null} ** num_sections;722pub const null_section_array = [_]?Section{null} ** num_sections;
...@@ -817,7 +778,7 @@ pub fn getSymbolName(di: *Dwarf, address: u64) ?[]const u8 {...@@ -817,7 +778,7 @@ pub fn getSymbolName(di: *Dwarf, address: u64) ?[]const u8 {
817}778}
818779
819fn scanAllFunctions(di: *Dwarf, allocator: Allocator) !void {780fn 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 };
821 var this_unit_offset: u64 = 0;782 var this_unit_offset: u64 = 0;
822783
823 while (this_unit_offset < fbr.buf.len) {784 while (this_unit_offset < fbr.buf.len) {
...@@ -828,20 +789,20 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) !void {...@@ -828,20 +789,20 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) !void {
828 const next_offset = unit_header.header_length + unit_header.unit_length;789 const next_offset = unit_header.header_length + unit_header.unit_length;
829790
830 const version = try fbr.readInt(u16);791 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
833 var address_size: u8 = undefined;794 var address_size: u8 = undefined;
834 var debug_abbrev_offset: u64 = undefined;795 var debug_abbrev_offset: u64 = undefined;
835 if (version >= 5) {796 if (version >= 5) {
836 const unit_type = try fbr.readInt(u8);797 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();
838 address_size = try fbr.readByte();799 address_size = try fbr.readByte();
839 debug_abbrev_offset = try fbr.readAddress(unit_header.format);800 debug_abbrev_offset = try fbr.readAddress(unit_header.format);
840 } else {801 } else {
841 debug_abbrev_offset = try fbr.readAddress(unit_header.format);802 debug_abbrev_offset = try fbr.readAddress(unit_header.format);
842 address_size = try fbr.readByte();803 address_size = try fbr.readByte();
843 }804 }
844 if (address_size != @sizeOf(usize)) return badDwarf();805 if (address_size != @sizeOf(usize)) return bad();
845806
846 const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);807 const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);
847808
...@@ -915,28 +876,28 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) !void {...@@ -915,28 +876,28 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) !void {
915876
916 // Follow the DIE it points to and repeat877 // Follow the DIE it points to and repeat
917 const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin);878 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();
919 try fbr.seekTo(this_unit_offset + ref_offset);880 try fbr.seekTo(this_unit_offset + ref_offset);
920 this_die_obj = (try parseDie(881 this_die_obj = (try parseDie(
921 &fbr,882 &fbr,
922 attrs_bufs[2],883 attrs_bufs[2],
923 abbrev_table,884 abbrev_table,
924 unit_header.format,885 unit_header.format,
925 )) orelse return badDwarf();886 )) orelse return bad();
926 } else if (this_die_obj.getAttr(AT.specification)) |_| {887 } else if (this_die_obj.getAttr(AT.specification)) |_| {
927 const after_die_offset = fbr.pos;888 const after_die_offset = fbr.pos;
928 defer fbr.pos = after_die_offset;889 defer fbr.pos = after_die_offset;
929890
930 // Follow the DIE it points to and repeat891 // Follow the DIE it points to and repeat
931 const ref_offset = try this_die_obj.getAttrRef(AT.specification);892 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();
933 try fbr.seekTo(this_unit_offset + ref_offset);894 try fbr.seekTo(this_unit_offset + ref_offset);
934 this_die_obj = (try parseDie(895 this_die_obj = (try parseDie(
935 &fbr,896 &fbr,
936 attrs_bufs[2],897 attrs_bufs[2],
937 abbrev_table,898 abbrev_table,
938 unit_header.format,899 unit_header.format,
939 )) orelse return badDwarf();900 )) orelse return bad();
940 } else {901 } else {
941 break :x null;902 break :x null;
942 }903 }
...@@ -950,7 +911,7 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) !void {...@@ -950,7 +911,7 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) !void {
950 const pc_end = switch (high_pc_value.*) {911 const pc_end = switch (high_pc_value.*) {
951 .addr => |value| value,912 .addr => |value| value,
952 .udata => |offset| low_pc + offset,913 .udata => |offset| low_pc + offset,
953 else => return badDwarf(),914 else => return bad(),
954 };915 };
955916
956 try di.func_list.append(allocator, .{917 try di.func_list.append(allocator, .{
...@@ -1004,7 +965,7 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) !void {...@@ -1004,7 +965,7 @@ fn scanAllFunctions(di: *Dwarf, allocator: Allocator) !void {
1004}965}
1005966
1006fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) !void {967fn 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 };
1008 var this_unit_offset: u64 = 0;969 var this_unit_offset: u64 = 0;
1009970
1010 var attrs_buf = std.ArrayList(Die.Attr).init(allocator);971 var attrs_buf = std.ArrayList(Die.Attr).init(allocator);
...@@ -1018,20 +979,20 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) !void {...@@ -1018,20 +979,20 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) !void {
1018 const next_offset = unit_header.header_length + unit_header.unit_length;979 const next_offset = unit_header.header_length + unit_header.unit_length;
1019980
1020 const version = try fbr.readInt(u16);981 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
1023 var address_size: u8 = undefined;984 var address_size: u8 = undefined;
1024 var debug_abbrev_offset: u64 = undefined;985 var debug_abbrev_offset: u64 = undefined;
1025 if (version >= 5) {986 if (version >= 5) {
1026 const unit_type = try fbr.readInt(u8);987 const unit_type = try fbr.readInt(u8);
1027 if (unit_type != UT.compile) return badDwarf();988 if (unit_type != UT.compile) return bad();
1028 address_size = try fbr.readByte();989 address_size = try fbr.readByte();
1029 debug_abbrev_offset = try fbr.readAddress(unit_header.format);990 debug_abbrev_offset = try fbr.readAddress(unit_header.format);
1030 } else {991 } else {
1031 debug_abbrev_offset = try fbr.readAddress(unit_header.format);992 debug_abbrev_offset = try fbr.readAddress(unit_header.format);
1032 address_size = try fbr.readByte();993 address_size = try fbr.readByte();
1033 }994 }
1034 if (address_size != @sizeOf(usize)) return badDwarf();995 if (address_size != @sizeOf(usize)) return bad();
1035996
1036 const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);997 const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset);
1037998
...@@ -1046,9 +1007,9 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) !void {...@@ -1046,9 +1007,9 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) !void {
1046 attrs_buf.items,1007 attrs_buf.items,
1047 abbrev_table,1008 abbrev_table,
1048 unit_header.format,1009 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
1053 compile_unit_die.attrs = try allocator.dupe(Die.Attr, compile_unit_die.attrs);1014 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 {...@@ -1070,7 +1031,7 @@ fn scanAllCompileUnits(di: *Dwarf, allocator: Allocator) !void {
1070 const pc_end = switch (high_pc_value.*) {1031 const pc_end = switch (high_pc_value.*) {
1071 .addr => |value| value,1032 .addr => |value| value,
1072 .udata => |offset| low_pc + offset,1033 .udata => |offset| low_pc + offset,
1073 else => return badDwarf(),1034 else => return bad(),
1074 };1035 };
1075 break :x PcRange{1036 break :x PcRange{
1076 .start = low_pc,1037 .start = low_pc,
...@@ -1096,7 +1057,7 @@ const DebugRangeIterator = struct {...@@ -1096,7 +1057,7 @@ const DebugRangeIterator = struct {
1096 section_type: Section.Id,1057 section_type: Section.Id,
1097 di: *const Dwarf,1058 di: *const Dwarf,
1098 compile_unit: *const CompileUnit,1059 compile_unit: *const CompileUnit,
1099 fbr: FixedBufferReader,1060 fbr: DeprecatedFixedBufferReader,
11001061
1101 pub fn init(ranges_value: *const FormValue, di: *const Dwarf, compile_unit: *const CompileUnit) !@This() {1062 pub fn init(ranges_value: *const FormValue, di: *const Dwarf, compile_unit: *const CompileUnit) !@This() {
1102 const section_type = if (compile_unit.version >= 5) Section.Id.debug_rnglists else Section.Id.debug_ranges;1063 const section_type = if (compile_unit.version >= 5) Section.Id.debug_rnglists else Section.Id.debug_ranges;
...@@ -1108,19 +1069,19 @@ const DebugRangeIterator = struct {...@@ -1108,19 +1069,19 @@ const DebugRangeIterator = struct {
1108 switch (compile_unit.format) {1069 switch (compile_unit.format) {
1109 .@"32" => {1070 .@"32" => {
1110 const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 4 * idx));1071 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();
1112 const offset = readInt(u32, debug_ranges[offset_loc..][0..4], di.endian);1073 const offset = readInt(u32, debug_ranges[offset_loc..][0..4], di.endian);
1113 break :off compile_unit.rnglists_base + offset;1074 break :off compile_unit.rnglists_base + offset;
1114 },1075 },
1115 .@"64" => {1076 .@"64" => {
1116 const offset_loc = @as(usize, @intCast(compile_unit.rnglists_base + 8 * idx));1077 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();
1118 const offset = readInt(u64, debug_ranges[offset_loc..][0..8], di.endian);1079 const offset = readInt(u64, debug_ranges[offset_loc..][0..8], di.endian);
1119 break :off compile_unit.rnglists_base + offset;1080 break :off compile_unit.rnglists_base + offset;
1120 },1081 },
1121 }1082 }
1122 },1083 },
1123 else => return badDwarf(),1084 else => return bad(),
1124 };1085 };
11251086
1126 // All the addresses in the list are relative to the value1087 // All the addresses in the list are relative to the value
...@@ -1139,7 +1100,7 @@ const DebugRangeIterator = struct {...@@ -1139,7 +1100,7 @@ const DebugRangeIterator = struct {
1139 .compile_unit = compile_unit,1100 .compile_unit = compile_unit,
1140 .fbr = .{1101 .fbr = .{
1141 .buf = debug_ranges,1102 .buf = debug_ranges,
1142 .pos = cast(usize, ranges_offset) orelse return badDwarf(),1103 .pos = cast(usize, ranges_offset) orelse return bad(),
1143 .endian = di.endian,1104 .endian = di.endian,
1144 },1105 },
1145 };1106 };
...@@ -1214,7 +1175,7 @@ const DebugRangeIterator = struct {...@@ -1214,7 +1175,7 @@ const DebugRangeIterator = struct {
1214 .end_addr = end_addr,1175 .end_addr = end_addr,
1215 };1176 };
1216 },1177 },
1217 else => return badDwarf(),1178 else => return bad(),
1218 }1179 }
1219 },1180 },
1220 .debug_ranges => {1181 .debug_ranges => {
...@@ -1251,7 +1212,7 @@ pub fn findCompileUnit(di: *const Dwarf, target_address: u64) !*const CompileUni...@@ -1251,7 +1212,7 @@ pub fn findCompileUnit(di: *const Dwarf, target_address: u64) !*const CompileUni
1251 }1212 }
1252 }1213 }
12531214
1254 return missingDwarf();1215 return missing();
1255}1216}
12561217
1257/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,1218/// 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...@@ -1270,9 +1231,9 @@ fn getAbbrevTable(di: *Dwarf, allocator: Allocator, abbrev_offset: u64) !*const
1270}1231}
12711232
1272fn parseAbbrevTable(di: *Dwarf, allocator: Allocator, offset: u64) !Abbrev.Table {1233fn parseAbbrevTable(di: *Dwarf, allocator: Allocator, offset: u64) !Abbrev.Table {
1273 var fbr: FixedBufferReader = .{1234 var fbr: DeprecatedFixedBufferReader = .{
1274 .buf = di.section(.debug_abbrev).?,1235 .buf = di.section(.debug_abbrev).?,
1275 .pos = cast(usize, offset) orelse return badDwarf(),1236 .pos = cast(usize, offset) orelse return bad(),
1276 .endian = di.endian,1237 .endian = di.endian,
1277 };1238 };
12781239
...@@ -1322,14 +1283,14 @@ fn parseAbbrevTable(di: *Dwarf, allocator: Allocator, offset: u64) !Abbrev.Table...@@ -1322,14 +1283,14 @@ fn parseAbbrevTable(di: *Dwarf, allocator: Allocator, offset: u64) !Abbrev.Table
1322}1283}
13231284
1324fn parseDie(1285fn parseDie(
1325 fbr: *FixedBufferReader,1286 fbr: *DeprecatedFixedBufferReader,
1326 attrs_buf: []Die.Attr,1287 attrs_buf: []Die.Attr,
1327 abbrev_table: *const Abbrev.Table,1288 abbrev_table: *const Abbrev.Table,
1328 format: Format,1289 format: Format,
1329) !?Die {1290) !?Die {
1330 const abbrev_code = try fbr.readUleb128(u64);1291 const abbrev_code = try fbr.readUleb128(u64);
1331 if (abbrev_code == 0) return null;1292 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
1334 const attrs = attrs_buf[0..table_entry.attrs.len];1295 const attrs = attrs_buf[0..table_entry.attrs.len];
1335 for (attrs, table_entry.attrs) |*result_attr, attr| result_attr.* = Die.Attr{1296 for (attrs, table_entry.attrs) |*result_attr, attr| result_attr.* = Die.Attr{
...@@ -1357,15 +1318,15 @@ pub fn getLineNumberInfo(...@@ -1357,15 +1318,15 @@ pub fn getLineNumberInfo(
1357 const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT.comp_dir, di.section(.debug_line_str), compile_unit);1318 const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT.comp_dir, di.section(.debug_line_str), compile_unit);
1358 const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);1319 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 };
1361 try fbr.seekTo(line_info_offset);1322 try fbr.seekTo(line_info_offset);
13621323
1363 const unit_header = try readUnitHeader(&fbr, null);1324 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();
1365 const next_offset = unit_header.header_length + unit_header.unit_length;1326 const next_offset = unit_header.header_length + unit_header.unit_length;
13661327
1367 const version = try fbr.readInt(u16);1328 const version = try fbr.readInt(u16);
1368 if (version < 2) return badDwarf();1329 if (version < 2) return bad();
13691330
1370 var addr_size: u8 = switch (unit_header.format) {1331 var addr_size: u8 = switch (unit_header.format) {
1371 .@"32" => 4,1332 .@"32" => 4,
...@@ -1381,7 +1342,7 @@ pub fn getLineNumberInfo(...@@ -1381,7 +1342,7 @@ pub fn getLineNumberInfo(
1381 const prog_start_offset = fbr.pos + prologue_length;1342 const prog_start_offset = fbr.pos + prologue_length;
13821343
1383 const minimum_instruction_length = try fbr.readByte();1344 const minimum_instruction_length = try fbr.readByte();
1384 if (minimum_instruction_length == 0) return badDwarf();1345 if (minimum_instruction_length == 0) return bad();
13851346
1386 if (version >= 4) {1347 if (version >= 4) {
1387 // maximum_operations_per_instruction1348 // maximum_operations_per_instruction
...@@ -1392,7 +1353,7 @@ pub fn getLineNumberInfo(...@@ -1392,7 +1353,7 @@ pub fn getLineNumberInfo(
1392 const line_base = try fbr.readByteSigned();1353 const line_base = try fbr.readByteSigned();
13931354
1394 const line_range = try fbr.readByte();1355 const line_range = try fbr.readByte();
1395 if (line_range == 0) return badDwarf();1356 if (line_range == 0) return bad();
13961357
1397 const opcode_base = try fbr.readByte();1358 const opcode_base = try fbr.readByte();
13981359
...@@ -1433,7 +1394,7 @@ pub fn getLineNumberInfo(...@@ -1433,7 +1394,7 @@ pub fn getLineNumberInfo(
1433 {1394 {
1434 var dir_ent_fmt_buf: [10]FileEntFmt = undefined;1395 var dir_ent_fmt_buf: [10]FileEntFmt = undefined;
1435 const directory_entry_format_count = try fbr.readByte();1396 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();
1437 for (dir_ent_fmt_buf[0..directory_entry_format_count]) |*ent_fmt| {1398 for (dir_ent_fmt_buf[0..directory_entry_format_count]) |*ent_fmt| {
1438 ent_fmt.* = .{1399 ent_fmt.* = .{
1439 .content_type_code = try fbr.readUleb128(u8),1400 .content_type_code = try fbr.readUleb128(u8),
...@@ -1461,7 +1422,7 @@ pub fn getLineNumberInfo(...@@ -1461,7 +1422,7 @@ pub fn getLineNumberInfo(
1461 DW.LNCT.size => e.size = try form_value.getUInt(u64),1422 DW.LNCT.size => e.size = try form_value.getUInt(u64),
1462 DW.LNCT.MD5 => e.md5 = switch (form_value) {1423 DW.LNCT.MD5 => e.md5 = switch (form_value) {
1463 .data16 => |data16| data16.*,1424 .data16 => |data16| data16.*,
1464 else => return badDwarf(),1425 else => return bad(),
1465 },1426 },
1466 else => continue,1427 else => continue,
1467 }1428 }
...@@ -1473,7 +1434,7 @@ pub fn getLineNumberInfo(...@@ -1473,7 +1434,7 @@ pub fn getLineNumberInfo(
14731434
1474 var file_ent_fmt_buf: [10]FileEntFmt = undefined;1435 var file_ent_fmt_buf: [10]FileEntFmt = undefined;
1475 const file_name_entry_format_count = try fbr.readByte();1436 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();
1477 for (file_ent_fmt_buf[0..file_name_entry_format_count]) |*ent_fmt| {1438 for (file_ent_fmt_buf[0..file_name_entry_format_count]) |*ent_fmt| {
1478 ent_fmt.* = .{1439 ent_fmt.* = .{
1479 .content_type_code = try fbr.readUleb128(u8),1440 .content_type_code = try fbr.readUleb128(u8),
...@@ -1501,7 +1462,7 @@ pub fn getLineNumberInfo(...@@ -1501,7 +1462,7 @@ pub fn getLineNumberInfo(
1501 DW.LNCT.size => e.size = try form_value.getUInt(u64),1462 DW.LNCT.size => e.size = try form_value.getUInt(u64),
1502 DW.LNCT.MD5 => e.md5 = switch (form_value) {1463 DW.LNCT.MD5 => e.md5 = switch (form_value) {
1503 .data16 => |data16| data16.*,1464 .data16 => |data16| data16.*,
1504 else => return badDwarf(),1465 else => return bad(),
1505 },1466 },
1506 else => continue,1467 else => continue,
1507 }1468 }
...@@ -1527,7 +1488,7 @@ pub fn getLineNumberInfo(...@@ -1527,7 +1488,7 @@ pub fn getLineNumberInfo(
15271488
1528 if (opcode == DW.LNS.extended_op) {1489 if (opcode == DW.LNS.extended_op) {
1529 const op_size = try fbr.readUleb128(u64);1490 const op_size = try fbr.readUleb128(u64);
1530 if (op_size < 1) return badDwarf();1491 if (op_size < 1) return bad();
1531 const sub_op = try fbr.readByte();1492 const sub_op = try fbr.readByte();
1532 switch (sub_op) {1493 switch (sub_op) {
1533 DW.LNE.end_sequence => {1494 DW.LNE.end_sequence => {
...@@ -1600,14 +1561,14 @@ pub fn getLineNumberInfo(...@@ -1600,14 +1561,14 @@ pub fn getLineNumberInfo(
1600 },1561 },
1601 DW.LNS.set_prologue_end => {},1562 DW.LNS.set_prologue_end => {},
1602 else => {1563 else => {
1603 if (opcode - 1 >= standard_opcode_lengths.len) return badDwarf();1564 if (opcode - 1 >= standard_opcode_lengths.len) return bad();
1604 try fbr.seekForward(standard_opcode_lengths[opcode - 1]);1565 try fbr.seekForward(standard_opcode_lengths[opcode - 1]);
1605 },1566 },
1606 }1567 }
1607 }1568 }
1608 }1569 }
16091570
1610 return missingDwarf();1571 return missing();
1611}1572}
16121573
1613fn getString(di: Dwarf, offset: u64) ![:0]const u8 {1574fn getString(di: Dwarf, offset: u64) ![:0]const u8 {
...@@ -1619,28 +1580,28 @@ fn getLineString(di: Dwarf, offset: u64) ![:0]const u8 {...@@ -1619,28 +1580,28 @@ fn getLineString(di: Dwarf, offset: u64) ![:0]const u8 {
1619}1580}
16201581
1621fn readDebugAddr(di: Dwarf, compile_unit: CompileUnit, index: u64) !u64 {1582fn 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
1624 // addr_base points to the first item after the header, however we1585 // addr_base points to the first item after the header, however we
1625 // need to read the header to know the size of each item. Empirically,1586 // need to read the header to know the size of each item. Empirically,
1626 // it may disagree with is_64 on the compile unit.1587 // it may disagree with is_64 on the compile unit.
1627 // The header is 8 or 12 bytes depending on is_64.1588 // 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
1630 const version = readInt(u16, debug_addr[compile_unit.addr_base - 4 ..][0..2], di.endian);1591 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
1633 const addr_size = debug_addr[compile_unit.addr_base - 2];1594 const addr_size = debug_addr[compile_unit.addr_base - 2];
1634 const seg_size = debug_addr[compile_unit.addr_base - 1];1595 const seg_size = debug_addr[compile_unit.addr_base - 1];
16351596
1636 const byte_offset = @as(usize, @intCast(compile_unit.addr_base + (addr_size + seg_size) * index));1597 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();
1638 return switch (addr_size) {1599 return switch (addr_size) {
1639 1 => debug_addr[byte_offset],1600 1 => debug_addr[byte_offset],
1640 2 => readInt(u16, debug_addr[byte_offset..][0..2], di.endian),1601 2 => readInt(u16, debug_addr[byte_offset..][0..2], di.endian),
1641 4 => readInt(u32, debug_addr[byte_offset..][0..4], di.endian),1602 4 => readInt(u32, debug_addr[byte_offset..][0..4], di.endian),
1642 8 => readInt(u64, debug_addr[byte_offset..][0..8], di.endian),1603 8 => readInt(u64, debug_addr[byte_offset..][0..8], di.endian),
1643 else => badDwarf(),1604 else => bad(),
1644 };1605 };
1645}1606}
16461607
...@@ -1650,7 +1611,7 @@ fn readDebugAddr(di: Dwarf, compile_unit: CompileUnit, index: u64) !u64 {...@@ -1650,7 +1611,7 @@ fn readDebugAddr(di: Dwarf, compile_unit: CompileUnit, index: u64) !u64 {
1650/// of FDEs is built for binary searching during unwinding.1611/// of FDEs is built for binary searching during unwinding.
1651pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize) !void {1612pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize) !void {
1652 if (di.section(.eh_frame_hdr)) |eh_frame_hdr| blk: {1613 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
1655 const version = try fbr.readByte();1616 const version = try fbr.readByte();
1656 if (version != 1) break :blk;1617 if (version != 1) break :blk;
...@@ -1665,16 +1626,16 @@ pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize)...@@ -1665,16 +1626,16 @@ pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize)
1665 const eh_frame_ptr = cast(usize, try readEhPointer(&fbr, eh_frame_ptr_enc, @sizeOf(usize), .{1626 const eh_frame_ptr = cast(usize, try readEhPointer(&fbr, eh_frame_ptr_enc, @sizeOf(usize), .{
1666 .pc_rel_base = @intFromPtr(&eh_frame_hdr[fbr.pos]),1627 .pc_rel_base = @intFromPtr(&eh_frame_hdr[fbr.pos]),
1667 .follow_indirect = true,1628 .follow_indirect = true,
1668 }) orelse return badDwarf()) orelse return badDwarf();1629 }) orelse return bad()) orelse return bad();
16691630
1670 const fde_count = cast(usize, try readEhPointer(&fbr, fde_count_enc, @sizeOf(usize), .{1631 const fde_count = cast(usize, try readEhPointer(&fbr, fde_count_enc, @sizeOf(usize), .{
1671 .pc_rel_base = @intFromPtr(&eh_frame_hdr[fbr.pos]),1632 .pc_rel_base = @intFromPtr(&eh_frame_hdr[fbr.pos]),
1672 .follow_indirect = true,1633 .follow_indirect = true,
1673 }) orelse return badDwarf()) orelse return badDwarf();1634 }) orelse return bad()) orelse return bad();
16741635
1675 const entry_size = try ExceptionFrameHeader.entrySize(table_enc);1636 const entry_size = try ExceptionFrameHeader.entrySize(table_enc);
1676 const entries_len = fde_count * entry_size;1637 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
1679 di.eh_frame_hdr = .{1640 di.eh_frame_hdr = .{
1680 .eh_frame_ptr = eh_frame_ptr,1641 .eh_frame_ptr = eh_frame_ptr,
...@@ -1690,7 +1651,7 @@ pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize)...@@ -1690,7 +1651,7 @@ pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize)
1690 const frame_sections = [2]Section.Id{ .eh_frame, .debug_frame };1651 const frame_sections = [2]Section.Id{ .eh_frame, .debug_frame };
1691 for (frame_sections) |frame_section| {1652 for (frame_sections) |frame_section| {
1692 if (di.section(frame_section)) |section_data| {1653 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 };
1694 while (fbr.pos < fbr.buf.len) {1655 while (fbr.pos < fbr.buf.len) {
1695 const entry_header = try EntryHeader.read(&fbr, null, frame_section);1656 const entry_header = try EntryHeader.read(&fbr, null, frame_section);
1696 switch (entry_header.type) {1657 switch (entry_header.type) {
...@@ -1708,7 +1669,7 @@ pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize)...@@ -1708,7 +1669,7 @@ pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize)
1708 try di.cie_map.put(allocator, entry_header.length_offset, cie);1669 try di.cie_map.put(allocator, entry_header.length_offset, cie);
1709 },1670 },
1710 .fde => |cie_offset| {1671 .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();
1712 const fde = try FrameDescriptionEntry.parse(1673 const fde = try FrameDescriptionEntry.parse(
1713 entry_header.entry_bytes,1674 entry_header.entry_bytes,
1714 di.sectionVirtualOffset(frame_section, base_address).?,1675 di.sectionVirtualOffset(frame_section, base_address).?,
...@@ -1733,205 +1694,8 @@ pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize)...@@ -1733,205 +1694,8 @@ pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize)
1733 }1694 }
1734}1695}
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
1933fn parseFormValue(1697fn parseFormValue(
1934 fbr: *FixedBufferReader,1698 fbr: *DeprecatedFixedBufferReader,
1935 form_id: u64,1699 form_id: u64,
1936 format: Format,1700 format: Format,
1937 implicit_const: ?i64,1701 implicit_const: ?i64,
...@@ -1990,12 +1754,12 @@ fn parseFormValue(...@@ -1990,12 +1754,12 @@ fn parseFormValue(
1990 FORM.strx => .{ .strx = try fbr.readUleb128(usize) },1754 FORM.strx => .{ .strx = try fbr.readUleb128(usize) },
1991 FORM.line_strp => .{ .line_strp = try fbr.readAddress(format) },1755 FORM.line_strp => .{ .line_strp = try fbr.readAddress(format) },
1992 FORM.indirect => parseFormValue(fbr, try fbr.readUleb128(u64), format, implicit_const),1756 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() },
1994 FORM.loclistx => .{ .loclistx = try fbr.readUleb128(u64) },1758 FORM.loclistx => .{ .loclistx = try fbr.readUleb128(u64) },
1995 FORM.rnglistx => .{ .rnglistx = try fbr.readUleb128(u64) },1759 FORM.rnglistx => .{ .rnglistx = try fbr.readUleb128(u64) },
1996 else => {1760 else => {
1997 //debug.print("unrecognized form id: {x}\n", .{form_id});1761 //debug.print("unrecognized form id: {x}\n", .{form_id});
1998 return badDwarf();1762 return bad();
1999 },1763 },
2000 };1764 };
2001}1765}
...@@ -2090,14 +1854,14 @@ const LineNumberProgram = struct {...@@ -2090,14 +1854,14 @@ const LineNumberProgram = struct {
2090 self.target_address < self.address)1854 self.target_address < self.address)
2091 {1855 {
2092 const file_index = if (self.version >= 5) self.prev_file else i: {1856 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();
2094 break :i self.prev_file - 1;1858 break :i self.prev_file - 1;
2095 };1859 };
20961860
2097 if (file_index >= file_entries.len) return badDwarf();1861 if (file_index >= file_entries.len) return bad();
2098 const file_entry = &file_entries[file_index];1862 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();
2101 const dir_name = self.include_dirs[file_entry.dir_index].path;1865 const dir_name = self.include_dirs[file_entry.dir_index].path;
21021866
2103 const file_name = try std.fs.path.join(allocator, &[_][]const u8{1867 const file_name = try std.fs.path.join(allocator, &[_][]const u8{
...@@ -2128,14 +1892,14 @@ const UnitHeader = struct {...@@ -2128,14 +1892,14 @@ const UnitHeader = struct {
2128 header_length: u4,1892 header_length: u4,
2129 unit_length: u64,1893 unit_length: u64,
2130};1894};
2131fn readUnitHeader(fbr: *FixedBufferReader, opt_ma: ?*StackIterator.MemoryAccessor) !UnitHeader {1895fn readUnitHeader(fbr: *DeprecatedFixedBufferReader, opt_ma: ?*MemoryAccessor) !UnitHeader {
2132 return switch (try if (opt_ma) |ma| fbr.readIntChecked(u32, ma) else fbr.readInt(u32)) {1896 return switch (try if (opt_ma) |ma| fbr.readIntChecked(u32, ma) else fbr.readInt(u32)) {
2133 0...0xfffffff0 - 1 => |unit_length| .{1897 0...0xfffffff0 - 1 => |unit_length| .{
2134 .format = .@"32",1898 .format = .@"32",
2135 .header_length = 4,1899 .header_length = 4,
2136 .unit_length = unit_length,1900 .unit_length = unit_length,
2137 },1901 },
2138 0xfffffff0...0xffffffff - 1 => badDwarf(),1902 0xfffffff0...0xffffffff - 1 => bad(),
2139 0xffffffff => .{1903 0xffffffff => .{
2140 .format = .@"64",1904 .format = .@"64",
2141 .header_length = 12,1905 .header_length = 12,
...@@ -2145,7 +1909,7 @@ fn readUnitHeader(fbr: *FixedBufferReader, opt_ma: ?*StackIterator.MemoryAccesso...@@ -2145,7 +1909,7 @@ fn readUnitHeader(fbr: *FixedBufferReader, opt_ma: ?*StackIterator.MemoryAccesso
2145}1909}
21461910
2147/// Returns the DWARF register number for an x86_64 register number found in compact unwind info1911/// 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 {
2149 return switch (unwind_reg_number) {1913 return switch (unwind_reg_number) {
2150 1 => 3, // RBX1914 1 => 3, // RBX
2151 2 => 12, // R121915 2 => 12, // R12
...@@ -2159,473 +1923,25 @@ fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {...@@ -2159,473 +1923,25 @@ fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
21591923
2160/// This function is to make it handy to comment out the return and make it1924/// This function is to make it handy to comment out the return and make it
2161/// into a crash when working on this file.1925/// into a crash when working on this file.
2162fn badDwarf() error{InvalidDebugInfo} {1926pub fn bad() error{InvalidDebugInfo} {
2163 //if (true) @panic("badDwarf"); // can be handy to uncomment when working on this file1927 //if (true) @panic("bad dwarf"); // can be handy to uncomment when working on this file
2164 return error.InvalidDebugInfo;1928 return error.InvalidDebugInfo;
2165}1929}
21661930
2167fn missingDwarf() error{MissingDebugInfo} {1931fn missing() error{MissingDebugInfo} {
2168 //if (true) @panic("missingDwarf"); // can be handy to uncomment when working on this file1932 //if (true) @panic("missing dwarf"); // can be handy to uncomment when working on this file
2169 return error.MissingDebugInfo;1933 return error.MissingDebugInfo;
2170}1934}
21711935
2172fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {1936fn getStringGeneric(opt_str: ?[]const u8, offset: u64) ![:0]const u8 {
2173 const str = opt_str orelse return badDwarf();1937 const str = opt_str orelse return bad();
2174 if (offset > str.len) return badDwarf();1938 if (offset > str.len) return bad();
2175 const casted_offset = cast(usize, offset) orelse return badDwarf();1939 const casted_offset = cast(usize, offset) orelse return bad();
2176 // Valid strings always have a terminating zero byte1940 // 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();
2178 return str[casted_offset..last :0];1942 return str[casted_offset..last :0];
2179}1943}
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
2629const EhPointerContext = struct {1945const EhPointerContext = struct {
2630 // The address of the pointer field itself1946 // The address of the pointer field itself
2631 pc_rel_base: u64,1947 pc_rel_base: u64,
...@@ -2641,7 +1957,7 @@ const EhPointerContext = struct {...@@ -2641,7 +1957,7 @@ const EhPointerContext = struct {
2641 text_rel_base: ?u64 = null,1957 text_rel_base: ?u64 = null,
2642 function_rel_base: ?u64 = null,1958 function_rel_base: ?u64 = null,
2643};1959};
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 {
2645 if (enc == EH.PE.omit) return null;1961 if (enc == EH.PE.omit) return null;
26461962
2647 const value: union(enum) {1963 const value: union(enum) {
...@@ -2664,7 +1980,7 @@ fn readEhPointer(fbr: *FixedBufferReader, enc: u8, addr_size_bytes: u8, ctx: EhP...@@ -2664,7 +1980,7 @@ fn readEhPointer(fbr: *FixedBufferReader, enc: u8, addr_size_bytes: u8, ctx: EhP
2664 EH.PE.sdata2 => .{ .signed = try fbr.readInt(i16) },1980 EH.PE.sdata2 => .{ .signed = try fbr.readInt(i16) },
2665 EH.PE.sdata4 => .{ .signed = try fbr.readInt(i32) },1981 EH.PE.sdata4 => .{ .signed = try fbr.readInt(i32) },
2666 EH.PE.sdata8 => .{ .signed = try fbr.readInt(i64) },1982 EH.PE.sdata8 => .{ .signed = try fbr.readInt(i64) },
2667 else => return badDwarf(),1983 else => return bad(),
2668 };1984 };
26691985
2670 const base = switch (enc & EH.PE.rel_mask) {1986 const base = switch (enc & EH.PE.rel_mask) {
lib/std/debug/Dwarf/abi.zig+33-94
...@@ -1,8 +1,9 @@...@@ -1,8 +1,9 @@
1const builtin = @import("builtin");1const builtin = @import("builtin");
2
2const std = @import("../../std.zig");3const std = @import("../../std.zig");
3const mem = std.mem;4const mem = std.mem;
4const native_os = builtin.os.tag;
5const posix = std.posix;5const posix = std.posix;
6const Arch = std.Target.Cpu.Arch;
67
7pub fn supportsUnwinding(target: std.Target) bool {8pub fn supportsUnwinding(target: std.Target) bool {
8 return switch (target.cpu.arch) {9 return switch (target.cpu.arch) {
...@@ -26,8 +27,8 @@ pub fn supportsUnwinding(target: std.Target) bool {...@@ -26,8 +27,8 @@ pub fn supportsUnwinding(target: std.Target) bool {
26 };27 };
27}28}
2829
29pub fn ipRegNum() u8 {30pub fn ipRegNum(arch: Arch) u8 {
30 return switch (builtin.cpu.arch) {31 return switch (arch) {
31 .x86 => 8,32 .x86 => 8,
32 .x86_64 => 16,33 .x86_64 => 16,
33 .arm => 15,34 .arm => 15,
...@@ -36,9 +37,10 @@ pub fn ipRegNum() u8 {...@@ -36,9 +37,10 @@ pub fn ipRegNum() u8 {
36 };37 };
37}38}
3839
39pub fn fpRegNum(reg_context: RegisterContext) u8 {40pub fn fpRegNum(arch: Arch, reg_context: RegisterContext) u8 {
40 return switch (builtin.cpu.arch) {41 return switch (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 MachO42 // 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
42 .x86 => if (reg_context.eh_frame and reg_context.is_macho) 4 else 5,44 .x86 => if (reg_context.eh_frame and reg_context.is_macho) 4 else 5,
43 .x86_64 => 6,45 .x86_64 => 6,
44 .arm => 11,46 .arm => 11,
...@@ -47,8 +49,8 @@ pub fn fpRegNum(reg_context: RegisterContext) u8 {...@@ -47,8 +49,8 @@ pub fn fpRegNum(reg_context: RegisterContext) u8 {
47 };49 };
48}50}
4951
50pub fn spRegNum(reg_context: RegisterContext) u8 {52pub fn spRegNum(arch: Arch, reg_context: RegisterContext) u8 {
51 return switch (builtin.cpu.arch) {53 return switch (arch) {
52 .x86 => if (reg_context.eh_frame and reg_context.is_macho) 5 else 4,54 .x86 => if (reg_context.eh_frame and reg_context.is_macho) 5 else 4,
53 .x86_64 => 7,55 .x86_64 => 7,
54 .arm => 13,56 .arm => 13,
...@@ -57,33 +59,12 @@ pub fn spRegNum(reg_context: RegisterContext) u8 {...@@ -57,33 +59,12 @@ pub fn spRegNum(reg_context: RegisterContext) u8 {
57 };59 };
58}60}
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
81pub const RegisterContext = struct {62pub const RegisterContext = struct {
82 eh_frame: bool,63 eh_frame: bool,
83 is_macho: bool,64 is_macho: bool,
84};65};
8566
86pub const AbiError = error{67pub const RegBytesError = error{
87 InvalidRegister,68 InvalidRegister,
88 UnimplementedArch,69 UnimplementedArch,
89 UnimplementedOs,70 UnimplementedOs,
...@@ -91,55 +72,21 @@ pub const AbiError = error{...@@ -91,55 +72,21 @@ pub const AbiError = error{
91 ThreadContextNotSupported,72 ThreadContextNotSupported,
92};73};
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
132/// Returns a slice containing the backing storage for `reg_number`.75/// Returns a slice containing the backing storage for `reg_number`.
133///76///
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///
134/// `reg_context` describes in what context the register number is used, as it can have different81/// `reg_context` describes in what context the register number is used, as it can have different
135/// meanings depending on the DWARF container. It is only required when getting the stack or82/// meanings depending on the DWARF container. It is only required when getting the stack or
136/// frame pointer register on some architectures.83/// frame pointer register on some architectures.
137pub fn regBytes(84pub fn regBytes(
138 thread_context_ptr: anytype,85 thread_context_ptr: *std.debug.ThreadContext,
139 reg_number: u8,86 reg_number: u8,
140 reg_context: ?RegisterContext,87 reg_context: ?RegisterContext,
141) AbiError!RegBytesReturnType(@TypeOf(thread_context_ptr)) {88) RegBytesError![]u8 {
142 if (native_os == .windows) {89 if (builtin.os.tag == .windows) {
143 return switch (builtin.cpu.arch) {90 return switch (builtin.cpu.arch) {
144 .x86 => switch (reg_number) {91 .x86 => switch (reg_number) {
145 0 => mem.asBytes(&thread_context_ptr.Eax),92 0 => mem.asBytes(&thread_context_ptr.Eax),
...@@ -194,7 +141,7 @@ pub fn regBytes(...@@ -194,7 +141,7 @@ pub fn regBytes(
194141
195 const ucontext_ptr = thread_context_ptr;142 const ucontext_ptr = thread_context_ptr;
196 return switch (builtin.cpu.arch) {143 return switch (builtin.cpu.arch) {
197 .x86 => switch (native_os) {144 .x86 => switch (builtin.os.tag) {
198 .linux, .netbsd, .solaris, .illumos => switch (reg_number) {145 .linux, .netbsd, .solaris, .illumos => switch (reg_number) {
199 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EAX]),146 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EAX]),
200 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ECX]),147 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ECX]),
...@@ -229,7 +176,7 @@ pub fn regBytes(...@@ -229,7 +176,7 @@ pub fn regBytes(
229 },176 },
230 else => error.UnimplementedOs,177 else => error.UnimplementedOs,
231 },178 },
232 .x86_64 => switch (native_os) {179 .x86_64 => switch (builtin.os.tag) {
233 .linux, .solaris, .illumos => switch (reg_number) {180 .linux, .solaris, .illumos => switch (reg_number) {
234 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RAX]),181 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RAX]),
235 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDX]),182 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDX]),
...@@ -248,7 +195,7 @@ pub fn regBytes(...@@ -248,7 +195,7 @@ pub fn regBytes(
248 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R14]),195 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R14]),
249 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R15]),196 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R15]),
250 16 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RIP]),197 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())
252 mem.asBytes(&ucontext_ptr.mcontext.fpregs.chip_state.xmm[i - 17])199 mem.asBytes(&ucontext_ptr.mcontext.fpregs.chip_state.xmm[i - 17])
253 else200 else
254 mem.asBytes(&ucontext_ptr.mcontext.fpregs.xmm[i - 17]),201 mem.asBytes(&ucontext_ptr.mcontext.fpregs.xmm[i - 17]),
...@@ -318,7 +265,7 @@ pub fn regBytes(...@@ -318,7 +265,7 @@ pub fn regBytes(
318 },265 },
319 else => error.UnimplementedOs,266 else => error.UnimplementedOs,
320 },267 },
321 .arm => switch (native_os) {268 .arm => switch (builtin.os.tag) {
322 .linux => switch (reg_number) {269 .linux => switch (reg_number) {
323 0 => mem.asBytes(&ucontext_ptr.mcontext.arm_r0),270 0 => mem.asBytes(&ucontext_ptr.mcontext.arm_r0),
324 1 => mem.asBytes(&ucontext_ptr.mcontext.arm_r1),271 1 => mem.asBytes(&ucontext_ptr.mcontext.arm_r1),
...@@ -341,7 +288,7 @@ pub fn regBytes(...@@ -341,7 +288,7 @@ pub fn regBytes(
341 },288 },
342 else => error.UnimplementedOs,289 else => error.UnimplementedOs,
343 },290 },
344 .aarch64 => switch (native_os) {291 .aarch64 => switch (builtin.os.tag) {
345 .macos, .ios => switch (reg_number) {292 .macos, .ios => switch (reg_number) {
346 0...28 => mem.asBytes(&ucontext_ptr.mcontext.ss.regs[reg_number]),293 0...28 => mem.asBytes(&ucontext_ptr.mcontext.ss.regs[reg_number]),
347 29 => mem.asBytes(&ucontext_ptr.mcontext.ss.fp),294 29 => mem.asBytes(&ucontext_ptr.mcontext.ss.fp),
...@@ -389,22 +336,14 @@ pub fn regBytes(...@@ -389,22 +336,14 @@ pub fn regBytes(
389 };336 };
390}337}
391338
392/// Returns the ABI-defined default value this register has in the unwinding table339/// Returns a pointer to a register stored in a ThreadContext, preserving the
393/// before running any of the CIE instructions. The DWARF spec defines these as having340/// pointer attributes of the context.
394/// the .undefined rule by default, but allows ABI authors to override that.341pub fn regValueNative(
395pub fn getRegDefaultValue(reg_number: u8, context: *std.debug.Dwarf.UnwindContext, out: []u8) !void {342 thread_context_ptr: *std.debug.ThreadContext,
396 switch (builtin.cpu.arch) {343 reg_number: u8,
397 .aarch64 => {344 reg_context: ?RegisterContext,
398 // Callee-saved registers are initialized as if they had the .same_value rule345) !*align(1) usize {
399 if (reg_number >= 19 and reg_number <= 28) {346 const reg_bytes = try regBytes(thread_context_ptr, reg_number, reg_context);
400 const src = try regBytes(context.thread_context, reg_number, context.reg_context);347 if (@sizeOf(usize) != reg_bytes.len) return error.IncompatibleRegisterSize;
401 if (src.len != out.len) return error.RegisterSizeMismatch;348 return mem.bytesAsValue(usize, reg_bytes[0..@sizeOf(usize)]);
402 @memcpy(out, src);
403 return;
404 }
405 },
406 else => {},
407 }
408
409 @memset(out, undefined);
410}349}
lib/std/debug/Dwarf/call_frame.zig-388
...@@ -297,391 +297,3 @@ pub const Instruction = union(Opcode) {...@@ -297,391 +297,3 @@ pub const Instruction = union(Opcode) {
297 }297 }
298 }298 }
299};299};
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 @@...@@ -1,11 +1,13 @@
1const std = @import("std");
2const builtin = @import("builtin");1const builtin = @import("builtin");
2const native_arch = builtin.cpu.arch;
3const native_endian = native_arch.endian();
4
5const std = @import("std");
3const leb = std.leb;6const leb = std.leb;
4const OP = std.dwarf.OP;7const OP = std.dwarf.OP;
5const abi = std.debug.Dwarf.abi;8const abi = std.debug.Dwarf.abi;
6const mem = std.mem;9const mem = std.mem;
7const assert = std.debug.assert;10const assert = std.debug.assert;
8const native_endian = builtin.cpu.arch.endian();
911
10/// Expressions can be evaluated in different contexts, each requiring its own set of inputs.12/// Expressions can be evaluated in different contexts, each requiring its own set of inputs.
11/// Callers should specify all the fields relevant to their context. If a field is required13/// Callers should specify all the fields relevant to their context. If a field is required
...@@ -14,7 +16,7 @@ pub const Context = struct {...@@ -14,7 +16,7 @@ pub const Context = struct {
14 /// The dwarf format of the section this expression is in16 /// The dwarf format of the section this expression is in
15 format: std.dwarf.Format = .@"32",17 format: std.dwarf.Format = .@"32",
16 /// If specified, any addresses will pass through before being accessed18 /// 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,
18 /// The compilation unit this expression relates to, if any20 /// The compilation unit this expression relates to, if any
19 compile_unit: ?*const std.debug.Dwarf.CompileUnit = null,21 compile_unit: ?*const std.debug.Dwarf.CompileUnit = null,
20 /// When evaluating a user-presented expression, this is the address of the object being evaluated22 /// When evaluating a user-presented expression, this is the address of the object being evaluated
...@@ -34,7 +36,7 @@ pub const Options = struct {...@@ -34,7 +36,7 @@ pub const Options = struct {
34 /// The address size of the target architecture36 /// The address size of the target architecture
35 addr_size: u8 = @sizeOf(usize),37 addr_size: u8 = @sizeOf(usize),
36 /// Endianness of the target architecture38 /// Endianness of the target architecture
37 endian: std.builtin.Endian = builtin.target.cpu.arch.endian(),39 endian: std.builtin.Endian = native_endian,
38 /// Restrict the stack machine to a subset of opcodes used in call frame instructions40 /// Restrict the stack machine to a subset of opcodes used in call frame instructions
39 call_frame_context: bool = false,41 call_frame_context: bool = false,
40};42};
...@@ -60,7 +62,7 @@ pub const Error = error{...@@ -60,7 +62,7 @@ pub const Error = error{
60 InvalidTypeLength,62 InvalidTypeLength,
6163
62 TruncatedIntegralType,64 TruncatedIntegralType,
63} || abi.AbiError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero };65} || abi.RegBytesError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero };
6466
65/// A stack machine that can decode and run DWARF expressions.67/// A stack machine that can decode and run DWARF expressions.
66/// Expressions can be decoded for non-native address size and endianness,68/// Expressions can be decoded for non-native address size and endianness,
...@@ -304,7 +306,7 @@ pub fn StackMachine(comptime options: Options) type {...@@ -304,7 +306,7 @@ pub fn StackMachine(comptime options: Options) type {
304 allocator: std.mem.Allocator,306 allocator: std.mem.Allocator,
305 context: Context,307 context: Context,
306 ) Error!bool {308 ) 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)
308 @compileError("Execution of non-native address sizes / endianness is not supported");310 @compileError("Execution of non-native address sizes / endianness is not supported");
309311
310 const opcode = try stream.reader().readByte();312 const opcode = try stream.reader().readByte();
...@@ -1186,13 +1188,13 @@ test "DWARF expressions" {...@@ -1186,13 +1188,13 @@ test "DWARF expressions" {
1186 // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it1188 // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it
11871189
1188 mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian);1190 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;1191 (try abi.regValueNative(&thread_context, abi.fpRegNum(native_arch, reg_context), reg_context)).* = 1;
1190 (try abi.regValueNative(usize, &thread_context, abi.spRegNum(reg_context), reg_context)).* = 2;1192 (try abi.regValueNative(&thread_context, abi.spRegNum(native_arch, reg_context), reg_context)).* = 2;
1191 (try abi.regValueNative(usize, &thread_context, abi.ipRegNum(), reg_context)).* = 3;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));1195 try b.writeBreg(writer, abi.fpRegNum(native_arch, reg_context), @as(usize, 100));
1194 try b.writeBreg(writer, abi.spRegNum(reg_context), @as(usize, 200));1196 try b.writeBreg(writer, abi.spRegNum(native_arch, reg_context), @as(usize, 200));
1195 try b.writeBregx(writer, abi.ipRegNum(), @as(usize, 300));1197 try b.writeBregx(writer, abi.ipRegNum(native_arch), @as(usize, 300));
1196 try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400));1198 try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400));
11971199
1198 _ = try stack_machine.run(program.items, allocator, context, 0);1200 _ = 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;...@@ -22,6 +22,9 @@ const Pdb = std.debug.Pdb;
22const File = std.fs.File;22const File = std.fs.File;
23const math = std.math;23const math = std.math;
24const testing = std.testing;24const testing = std.testing;
25const StackIterator = std.debug.StackIterator;
26const regBytes = Dwarf.abi.regBytes;
27const regValueNative = Dwarf.abi.regValueNative;
2528
26const SelfInfo = @This();29const SelfInfo = @This();
2730
...@@ -1369,3 +1372,1033 @@ fn getSymbolFromDwarf(allocator: Allocator, address: u64, di: *Dwarf) !SymbolInf...@@ -1369,3 +1372,1033 @@ fn getSymbolFromDwarf(allocator: Allocator, address: u64, di: *Dwarf) !SymbolInf
1369 else => return err,1372 else => return err,
1370 }1373 }
1371}1374}
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) {...@@ -256,7 +256,7 @@ const StackContext = union(enum) {
256 current: struct {256 current: struct {
257 ret_addr: ?usize,257 ret_addr: ?usize,
258 },258 },
259 exception: *const debug.ThreadContext,259 exception: *debug.ThreadContext,
260 not_supported: void,260 not_supported: void,
261261
262 pub fn dumpStackTrace(ctx: @This()) void {262 pub fn dumpStackTrace(ctx: @This()) void {