authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-06 22:02:47-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:15-04:00
log5f72c6508d78291d8f0358d06d407a8d6b4a28c9
treea5f2b6043ee8ef90a64679cc5299e7d55981d384
parent8547c42ba542f77c55ce50253446fd2bf4f3592b

debug: rename StackTraceContext to ThreadContext

dwarf: use ThreadContext instead of os.ucontext_t dwarf: add regBytes impl for windows dwarf: fixup expression types for non-native

8 files changed, 157 insertions(+), 77 deletions(-)

lib/std/debug.zig+6-6
...@@ -133,7 +133,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {...@@ -133,7 +133,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
133 }133 }
134}134}
135135
136pub const StackTraceContext = blk: {136pub const ThreadContext = blk: {
137 if (native_os == .windows) {137 if (native_os == .windows) {
138 break :blk std.os.windows.CONTEXT;138 break :blk std.os.windows.CONTEXT;
139 } else if (have_ucontext) {139 } else if (have_ucontext) {
...@@ -146,7 +146,7 @@ pub const StackTraceContext = blk: {...@@ -146,7 +146,7 @@ pub const StackTraceContext = blk: {
146/// Tries to print the stack trace starting from the supplied base pointer to stderr,146/// Tries to print the stack trace starting from the supplied base pointer to stderr,
147/// unbuffered, and ignores any error returned.147/// unbuffered, and ignores any error returned.
148/// TODO multithreaded awareness148/// TODO multithreaded awareness
149pub fn dumpStackTraceFromBase(context: *const StackTraceContext) void {149pub fn dumpStackTraceFromBase(context: *const ThreadContext) void {
150 nosuspend {150 nosuspend {
151 if (comptime builtin.target.isWasm()) {151 if (comptime builtin.target.isWasm()) {
152 if (native_os == .wasi) {152 if (native_os == .wasi) {
...@@ -437,7 +437,7 @@ pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and...@@ -437,7 +437,7 @@ pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and
437 else => true,437 else => true,
438});438});
439439
440pub inline fn getContext(context: *StackTraceContext) bool {440pub inline fn getContext(context: *ThreadContext) bool {
441 if (native_os == .windows) {441 if (native_os == .windows) {
442 context.* = std.mem.zeroes(windows.CONTEXT);442 context.* = std.mem.zeroes(windows.CONTEXT);
443 windows.ntdll.RtlCaptureContext(context);443 windows.ntdll.RtlCaptureContext(context);
...@@ -606,8 +606,8 @@ pub const StackIterator = struct {...@@ -606,8 +606,8 @@ pub const StackIterator = struct {
606 fn next_dwarf(self: *StackIterator) !usize {606 fn next_dwarf(self: *StackIterator) !usize {
607 const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc);607 const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc);
608 if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| {608 if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| {
609 self.dwarf_context.reg_ctx.eh_frame = true;609 self.dwarf_context.reg_context.eh_frame = true;
610 self.dwarf_context.reg_ctx.is_macho = di.is_macho;610 self.dwarf_context.reg_context.is_macho = di.is_macho;
611 return di.unwindFrame(&self.dwarf_context, module.base_address);611 return di.unwindFrame(&self.dwarf_context, module.base_address);
612 } else return error.MissingDebugInfo;612 } else return error.MissingDebugInfo;
613 }613 }
...@@ -663,7 +663,7 @@ pub fn writeCurrentStackTrace(...@@ -663,7 +663,7 @@ pub fn writeCurrentStackTrace(
663 tty_config: io.tty.Config,663 tty_config: io.tty.Config,
664 start_addr: ?usize,664 start_addr: ?usize,
665) !void {665) !void {
666 var context: StackTraceContext = undefined;666 var context: ThreadContext = undefined;
667 const has_context = getContext(&context);667 const has_context = getContext(&context);
668 if (native_os == .windows) {668 if (native_os == .windows) {
669 return writeStackTraceWindows(out_stream, debug_info, tty_config, &context, start_addr);669 return writeStackTraceWindows(out_stream, debug_info, tty_config, &context, start_addr);
lib/std/dwarf.zig+27-19
...@@ -146,11 +146,11 @@ pub const CC = enum(u8) {...@@ -146,11 +146,11 @@ pub const CC = enum(u8) {
146 pass_by_reference = 0x4,146 pass_by_reference = 0x4,
147 pass_by_value = 0x5,147 pass_by_value = 0x5,
148148
149 lo_user = 0x40,
150 hi_user = 0xff,
151
152 GNU_renesas_sh = 0x40,149 GNU_renesas_sh = 0x40,
153 GNU_borland_fastcall_i386 = 0x41,150 GNU_borland_fastcall_i386 = 0x41,
151
152 pub const lo_user = 0x40;
153 pub const hi_user = 0xff;
154};154};
155155
156pub const Format = enum { @"32", @"64" };156pub const Format = enum { @"32", @"64" };
...@@ -1676,13 +1676,13 @@ pub const DwarfInfo = struct {...@@ -1676,13 +1676,13 @@ pub const DwarfInfo = struct {
1676 var expression_context = .{1676 var expression_context = .{
1677 .isValidMemory = context.isValidMemory,1677 .isValidMemory = context.isValidMemory,
1678 .compile_unit = di.findCompileUnit(fde.pc_begin) catch null,1678 .compile_unit = di.findCompileUnit(fde.pc_begin) catch null,
1679 .ucontext = &context.ucontext,1679 .thread_context = &context.thread_context,
1680 .reg_ctx = context.reg_ctx,1680 .reg_context = context.reg_context,
1681 .cfa = context.cfa,1681 .cfa = context.cfa,
1682 };1682 };
16831683
1684 context.vm.reset();1684 context.vm.reset();
1685 context.reg_ctx.eh_frame = cie.version != 4;1685 context.reg_context.eh_frame = cie.version != 4;
16861686
1687 _ = try context.vm.runToNative(context.allocator, mapped_pc, cie, fde);1687 _ = try context.vm.runToNative(context.allocator, mapped_pc, cie, fde);
1688 const row = &context.vm.current_row;1688 const row = &context.vm.current_row;
...@@ -1690,7 +1690,7 @@ pub const DwarfInfo = struct {...@@ -1690,7 +1690,7 @@ pub const DwarfInfo = struct {
1690 context.cfa = switch (row.cfa.rule) {1690 context.cfa = switch (row.cfa.rule) {
1691 .val_offset => |offset| blk: {1691 .val_offset => |offset| blk: {
1692 const register = row.cfa.register orelse return error.InvalidCFARule;1692 const register = row.cfa.register orelse return error.InvalidCFARule;
1693 const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, register, context.reg_ctx));1693 const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.thread_context, register, context.reg_context));
1694 break :blk try call_frame.applyOffset(value, offset);1694 break :blk try call_frame.applyOffset(value, offset);
1695 },1695 },
1696 .expression => |expression| blk: {1696 .expression => |expression| blk: {
...@@ -1711,14 +1711,14 @@ pub const DwarfInfo = struct {...@@ -1711,14 +1711,14 @@ pub const DwarfInfo = struct {
1711 if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA;1711 if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA;
1712 expression_context.cfa = context.cfa;1712 expression_context.cfa = context.cfa;
17131713
1714 // Buffering the modifications is done because copying the ucontext is not portable,1714 // Buffering the modifications is done because copying the thread context is not portable,
1715 // some implementations (ie. darwin) use internal pointers to the mcontext.1715 // some implementations (ie. darwin) use internal pointers to the mcontext.
1716 var arena = std.heap.ArenaAllocator.init(context.allocator);1716 var arena = std.heap.ArenaAllocator.init(context.allocator);
1717 defer arena.deinit();1717 defer arena.deinit();
1718 const update_allocator = arena.allocator();1718 const update_allocator = arena.allocator();
17191719
1720 const RegisterUpdate = struct {1720 const RegisterUpdate = struct {
1721 // Backed by ucontext1721 // Backed by thread_context
1722 old_value: []u8,1722 old_value: []u8,
1723 // Backed by arena1723 // Backed by arena
1724 new_value: []const u8,1724 new_value: []const u8,
...@@ -1733,7 +1733,7 @@ pub const DwarfInfo = struct {...@@ -1733,7 +1733,7 @@ pub const DwarfInfo = struct {
1733 has_next_ip = column.rule != .undefined;1733 has_next_ip = column.rule != .undefined;
1734 }1734 }
17351735
1736 const old_value = try abi.regBytes(&context.ucontext, register, context.reg_ctx);1736 const old_value = try abi.regBytes(&context.thread_context, register, context.reg_context);
1737 const new_value = try update_allocator.alloc(u8, old_value.len);1737 const new_value = try update_allocator.alloc(u8, old_value.len);
17381738
1739 const prev = update_tail;1739 const prev = update_tail;
...@@ -1758,12 +1758,12 @@ pub const DwarfInfo = struct {...@@ -1758,12 +1758,12 @@ pub const DwarfInfo = struct {
1758 }1758 }
17591759
1760 if (has_next_ip) {1760 if (has_next_ip) {
1761 context.pc = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, comptime abi.ipRegNum(), context.reg_ctx));1761 context.pc = mem.readIntSliceNative(usize, try abi.regBytes(&context.thread_context, comptime abi.ipRegNum(), context.reg_context));
1762 } else {1762 } else {
1763 context.pc = 0;1763 context.pc = 0;
1764 }1764 }
17651765
1766 mem.writeIntSliceNative(usize, try abi.regBytes(&context.ucontext, abi.spRegNum(context.reg_ctx), context.reg_ctx), context.cfa.?);1766 mem.writeIntSliceNative(usize, try abi.regBytes(&context.thread_context, abi.spRegNum(context.reg_context), context.reg_context), context.cfa.?);
17671767
1768 // The call instruction will have pushed the address of the instruction that follows the call as the return address1768 // The call instruction will have pushed the address of the instruction that follows the call as the return address
1769 // However, this return address may be past the end of the function if the caller was `noreturn`.1769 // However, this return address may be past the end of the function if the caller was `noreturn`.
...@@ -1779,20 +1779,24 @@ pub const UnwindContext = struct {...@@ -1779,20 +1779,24 @@ pub const UnwindContext = struct {
1779 allocator: mem.Allocator,1779 allocator: mem.Allocator,
1780 cfa: ?usize,1780 cfa: ?usize,
1781 pc: usize,1781 pc: usize,
1782 ucontext: os.ucontext_t,1782 thread_context: debug.ThreadContext,
1783 reg_ctx: abi.RegisterContext,1783 reg_context: abi.RegisterContext,
1784 isValidMemory: *const fn (address: usize) bool,1784 isValidMemory: *const fn (address: usize) bool,
1785 vm: call_frame.VirtualMachine = .{},1785 vm: call_frame.VirtualMachine = .{},
1786 stack_machine: expressions.StackMachine(.{ .call_frame_context = true }) = .{},1786 stack_machine: expressions.StackMachine(.{ .call_frame_context = true }) = .{},
17871787
1788 pub fn init(allocator: mem.Allocator, ucontext: *const os.ucontext_t, isValidMemory: *const fn (address: usize) bool) !UnwindContext {1788 pub fn init(allocator: mem.Allocator, thread_context: *const debug.ThreadContext, isValidMemory: *const fn (address: usize) bool) !UnwindContext {
1789 const pc = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, abi.ipRegNum(), null));1789 const pc = mem.readIntSliceNative(usize, try abi.regBytes(thread_context, abi.ipRegNum(), null));
1790
1791 if (builtin.os.tag == .macos) @compileError("Fix below TODO");
1792
1790 return .{1793 return .{
1791 .allocator = allocator,1794 .allocator = allocator,
1792 .cfa = null,1795 .cfa = null,
1793 .pc = pc,1796 .pc = pc,
1794 .ucontext = ucontext.*,1797 // TODO: This is broken on macos, need a function that knows how to copy the OSs mcontext properly
1795 .reg_ctx = undefined,1798 .thread_context = thread_context.*,
1799 .reg_context = undefined,
1796 .isValidMemory = isValidMemory,1800 .isValidMemory = isValidMemory,
1797 };1801 };
1798 }1802 }
...@@ -1803,7 +1807,7 @@ pub const UnwindContext = struct {...@@ -1803,7 +1807,7 @@ pub const UnwindContext = struct {
1803 }1807 }
18041808
1805 pub fn getFp(self: *const UnwindContext) !usize {1809 pub fn getFp(self: *const UnwindContext) !usize {
1806 return mem.readIntSliceNative(usize, try abi.regBytes(&self.ucontext, abi.fpRegNum(self.reg_ctx), self.reg_ctx));1810 return mem.readIntSliceNative(usize, try abi.regBytes(&self.thread_context, abi.fpRegNum(self.reg_context), self.reg_context));
1807 }1811 }
1808};1812};
18091813
...@@ -2388,3 +2392,7 @@ fn pcRelBase(field_ptr: usize, pc_rel_offset: i64) !usize {...@@ -2388,3 +2392,7 @@ fn pcRelBase(field_ptr: usize, pc_rel_offset: i64) !usize {
2388 return math.add(usize, field_ptr, @as(usize, @intCast(pc_rel_offset)));2392 return math.add(usize, field_ptr, @as(usize, @intCast(pc_rel_offset)));
2389 }2393 }
2390}2394}
2395
2396test {
2397 std.testing.refAllDecls(@This());
2398}
lib/std/dwarf/abi.zig+65-16
...@@ -3,11 +3,6 @@ const std = @import("../std.zig");...@@ -3,11 +3,6 @@ const std = @import("../std.zig");
3const os = std.os;3const os = std.os;
4const mem = std.mem;4const mem = std.mem;
55
6pub const RegisterContext = struct {
7 eh_frame: bool,
8 is_macho: bool,
9};
10
11pub fn isSupportedArch(arch: std.Target.Cpu.Arch) bool {6pub fn isSupportedArch(arch: std.Target.Cpu.Arch) bool {
12 return switch (arch) {7 return switch (arch) {
13 .x86,8 .x86,
...@@ -29,10 +24,10 @@ pub fn ipRegNum() u8 {...@@ -29,10 +24,10 @@ pub fn ipRegNum() u8 {
29 };24 };
30}25}
3126
32pub fn fpRegNum(reg_ctx: RegisterContext) u8 {27pub fn fpRegNum(reg_context: RegisterContext) u8 {
33 return switch (builtin.cpu.arch) {28 return switch (builtin.cpu.arch) {
34 // GCC on OS X did the opposite of ELF for these registers (only in .eh_frame), and that is now the convention for MachO29 // GCC on OS X did the opposite of ELF for these registers (only in .eh_frame), and that is now the convention for MachO
35 .x86 => if (reg_ctx.eh_frame and reg_ctx.is_macho) 4 else 5,30 .x86 => if (reg_context.eh_frame and reg_context.is_macho) 4 else 5,
36 .x86_64 => 6,31 .x86_64 => 6,
37 .arm => 11,32 .arm => 11,
38 .aarch64 => 29,33 .aarch64 => 29,
...@@ -40,9 +35,9 @@ pub fn fpRegNum(reg_ctx: RegisterContext) u8 {...@@ -40,9 +35,9 @@ pub fn fpRegNum(reg_ctx: RegisterContext) u8 {
40 };35 };
41}36}
4237
43pub fn spRegNum(reg_ctx: RegisterContext) u8 {38pub fn spRegNum(reg_context: RegisterContext) u8 {
44 return switch (builtin.cpu.arch) {39 return switch (builtin.cpu.arch) {
45 .x86 => if (reg_ctx.eh_frame and reg_ctx.is_macho) 5 else 4,40 .x86 => if (reg_context.eh_frame and reg_context.is_macho) 5 else 4,
46 .x86_64 => 7,41 .x86_64 => 7,
47 .arm => 13,42 .arm => 13,
48 .aarch64 => 31,43 .aarch64 => 31,
...@@ -52,21 +47,76 @@ pub fn spRegNum(reg_ctx: RegisterContext) u8 {...@@ -52,21 +47,76 @@ pub fn spRegNum(reg_ctx: RegisterContext) u8 {
5247
53fn RegBytesReturnType(comptime ContextPtrType: type) type {48fn RegBytesReturnType(comptime ContextPtrType: type) type {
54 const info = @typeInfo(ContextPtrType);49 const info = @typeInfo(ContextPtrType);
55 if (info != .Pointer or info.Pointer.child != os.ucontext_t) {50 if (info != .Pointer or info.Pointer.child != std.debug.ThreadContext) {
56 @compileError("Expected a pointer to ucontext_t, got " ++ @typeName(@TypeOf(ContextPtrType)));51 @compileError("Expected a pointer to std.debug.ThreadContext, got " ++ @typeName(@TypeOf(ContextPtrType)));
57 }52 }
5853
59 return if (info.Pointer.is_const) return []const u8 else []u8;54 return if (info.Pointer.is_const) return []const u8 else []u8;
60}55}
6156
57pub const RegisterContext = struct {
58 eh_frame: bool,
59 is_macho: bool,
60};
61
62/// Returns a slice containing the backing storage for `reg_number`.62/// Returns a slice containing the backing storage for `reg_number`.
63///63///
64/// `reg_ctx` describes in what context the register number is used, as it can have different64/// `reg_context` describes in what context the register number is used, as it can have different
65/// meanings depending on the DWARF container. It is only required when getting the stack or65/// meanings depending on the DWARF container. It is only required when getting the stack or
66/// frame pointer register on some architectures.66/// frame pointer register on some architectures.
67pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext) !RegBytesReturnType(@TypeOf(ucontext_ptr)) {67pub fn regBytes(thread_context_ptr: anytype, reg_number: u8, reg_context: ?RegisterContext) !RegBytesReturnType(@TypeOf(thread_context_ptr)) {
68 var m = &ucontext_ptr.mcontext;68 if (builtin.os.tag == .windows) {
69 return switch (builtin.cpu.arch) {
70 .x86 => switch (reg_number) {
71 0 => mem.asBytes(&thread_context_ptr.Eax),
72 1 => mem.asBytes(&thread_context_ptr.Ecx),
73 2 => mem.asBytes(&thread_context_ptr.Edx),
74 3 => mem.asBytes(&thread_context_ptr.Ebx),
75 4 => mem.asBytes(&thread_context_ptr.Esp),
76 5 => mem.asBytes(&thread_context_ptr.Ebp),
77 6 => mem.asBytes(&thread_context_ptr.Esi),
78 7 => mem.asBytes(&thread_context_ptr.Edi),
79 8 => mem.asBytes(&thread_context_ptr.Eip),
80 9 => mem.asBytes(&thread_context_ptr.EFlags),
81 10 => mem.asBytes(&thread_context_ptr.SegCs),
82 11 => mem.asBytes(&thread_context_ptr.SegSs),
83 12 => mem.asBytes(&thread_context_ptr.SegDs),
84 13 => mem.asBytes(&thread_context_ptr.SegEs),
85 14 => mem.asBytes(&thread_context_ptr.SegFs),
86 15 => mem.asBytes(&thread_context_ptr.SegGs),
87 else => error.InvalidRegister,
88 },
89 .x86_64 => switch (reg_number) {
90 0 => mem.asBytes(&thread_context_ptr.Rax),
91 1 => mem.asBytes(&thread_context_ptr.Rdx),
92 2 => mem.asBytes(&thread_context_ptr.Rcx),
93 3 => mem.asBytes(&thread_context_ptr.Rbx),
94 4 => mem.asBytes(&thread_context_ptr.Rsi),
95 5 => mem.asBytes(&thread_context_ptr.Rdi),
96 6 => mem.asBytes(&thread_context_ptr.Rbp),
97 7 => mem.asBytes(&thread_context_ptr.Rsp),
98 8 => mem.asBytes(&thread_context_ptr.R8),
99 9 => mem.asBytes(&thread_context_ptr.R9),
100 10 => mem.asBytes(&thread_context_ptr.R10),
101 11 => mem.asBytes(&thread_context_ptr.R11),
102 12 => mem.asBytes(&thread_context_ptr.R12),
103 13 => mem.asBytes(&thread_context_ptr.R13),
104 14 => mem.asBytes(&thread_context_ptr.R14),
105 15 => mem.asBytes(&thread_context_ptr.R15),
106 16 => mem.asBytes(&thread_context_ptr.Rip),
107 else => error.InvalidRegister,
108 },
109 .aarch64 => switch (reg_number) {
110 0...30 => mem.asBytes(&thread_context_ptr.DUMMYUNIONNAME.X[reg_number]),
111 31 => mem.asBytes(&thread_context_ptr.Sp),
112 32 => mem.asBytes(&thread_context_ptr.Pc),
113 },
114 else => error.UnimplementedArch,
115 };
116 }
69117
118 const ucontext_ptr = thread_context_ptr;
119 var m = &ucontext_ptr.mcontext;
70 return switch (builtin.cpu.arch) {120 return switch (builtin.cpu.arch) {
71 .x86 => switch (builtin.os.tag) {121 .x86 => switch (builtin.os.tag) {
72 .linux, .netbsd, .solaris => switch (reg_number) {122 .linux, .netbsd, .solaris => switch (reg_number) {
...@@ -74,7 +124,7 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext...@@ -74,7 +124,7 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext
74 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ECX]),124 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ECX]),
75 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDX]),125 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDX]),
76 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBX]),126 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBX]),
77 4...5 => if (reg_ctx) |r| bytes: {127 4...5 => if (reg_context) |r| bytes: {
78 if (reg_number == 4) {128 if (reg_number == 4) {
79 break :bytes if (r.eh_frame and r.is_macho)129 break :bytes if (r.eh_frame and r.is_macho)
80 mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP])130 mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP])
...@@ -98,7 +148,6 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext...@@ -98,7 +148,6 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext
98 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.FS]),148 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.FS]),
99 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]),149 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]),
100 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs150 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs
101 // TODO: Map TRAPNO, ERR, UESP
102 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs151 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs
103 else => error.InvalidRegister,152 else => error.InvalidRegister,
104 },153 },
lib/std/dwarf/call_frame.zig+6-6
...@@ -2,10 +2,10 @@ const builtin = @import("builtin");...@@ -2,10 +2,10 @@ const builtin = @import("builtin");
2const std = @import("../std.zig");2const std = @import("../std.zig");
3const mem = std.mem;3const mem = std.mem;
4const debug = std.debug;4const debug = std.debug;
5const leb = @import("../leb128.zig");5const leb = std.leb;
6const abi = @import("abi.zig");6const dwarf = std.dwarf;
7const dwarf = @import("../dwarf.zig");7const abi = dwarf.abi;
8const expressions = @import("expressions.zig");8const expressions = dwarf.expressions;
9const assert = std.debug.assert;9const assert = std.debug.assert;
1010
11const Opcode = enum(u8) {11const Opcode = enum(u8) {
...@@ -315,9 +315,9 @@ pub const VirtualMachine = struct {...@@ -315,9 +315,9 @@ pub const VirtualMachine = struct {
315 } else return error.InvalidCFA;315 } else return error.InvalidCFA;
316 },316 },
317 .register => |register| {317 .register => |register| {
318 const src = try abi.regBytes(&context.ucontext, register, context.reg_ctx);318 const src = try abi.regBytes(&context.thread_context, register, context.reg_context);
319 if (src.len != out.len) return error.RegisterTypeMismatch;319 if (src.len != out.len) return error.RegisterTypeMismatch;
320 @memcpy(out, try abi.regBytes(&context.ucontext, register, context.reg_ctx));320 @memcpy(out, try abi.regBytes(&context.thread_context, register, context.reg_context));
321 },321 },
322 .expression => |expression| {322 .expression => |expression| {
323 context.stack_machine.reset();323 context.stack_machine.reset();
lib/std/dwarf/expressions.zig+50-27
...@@ -1,8 +1,8 @@...@@ -1,8 +1,8 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const OP = @import("OP.zig");3const OP = @import("OP.zig");
4const leb = @import("../leb128.zig");4const leb = std.leb;
5const dwarf = @import("../dwarf.zig");5const dwarf = std.dwarf;
6const abi = dwarf.abi;6const abi = dwarf.abi;
7const mem = std.mem;7const mem = std.mem;
8const assert = std.debug.assert;8const assert = std.debug.assert;
...@@ -17,12 +17,12 @@ pub const ExpressionContext = struct {...@@ -17,12 +17,12 @@ pub const ExpressionContext = struct {
17 /// The compilation unit this expression relates to, if any17 /// The compilation unit this expression relates to, if any
18 compile_unit: ?*const dwarf.CompileUnit = null,18 compile_unit: ?*const dwarf.CompileUnit = null,
1919
20 /// Register context20 /// Thread context
21 ucontext: ?*std.os.ucontext_t,21 thread_context: ?*std.debug.ThreadContext = null,
22 reg_ctx: ?abi.RegisterContext,22 reg_context: ?abi.RegisterContext = null,
2323
24 /// Call frame address, if in a CFI context24 /// Call frame address, if in a CFI context
25 cfa: ?usize,25 cfa: ?usize = null,
26};26};
2727
28pub const ExpressionOptions = struct {28pub const ExpressionOptions = struct {
...@@ -344,13 +344,13 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -344,13 +344,13 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
344 OP.breg0...OP.breg31,344 OP.breg0...OP.breg31,
345 OP.bregx,345 OP.bregx,
346 => {346 => {
347 if (context.ucontext == null) return error.IncompleteExpressionContext;347 if (context.thread_context == null) return error.IncompleteExpressionContext;
348348
349 const base_register = (try readOperand(stream, opcode)).?.base_register;349 const base_register = (try readOperand(stream, opcode)).?.base_register;
350 var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes(350 var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes(
351 context.ucontext.?,351 context.thread_context.?,
352 base_register.base_register,352 base_register.base_register,
353 context.reg_ctx,353 context.reg_context,
354 )));354 )));
355 value += base_register.offset;355 value += base_register.offset;
356 try self.stack.append(allocator, .{ .generic = @intCast(value) });356 try self.stack.append(allocator, .{ .generic = @intCast(value) });
...@@ -358,9 +358,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -358,9 +358,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
358 OP.regval_type => {358 OP.regval_type => {
359 const register_type = (try readOperand(stream, opcode)).?.register_type;359 const register_type = (try readOperand(stream, opcode)).?.register_type;
360 const value = mem.readIntSliceNative(usize, try abi.regBytes(360 const value = mem.readIntSliceNative(usize, try abi.regBytes(
361 context.ucontext.?,361 context.thread_context.?,
362 register_type.register,362 register_type.register,
363 context.reg_ctx,363 context.reg_context,
364 ));364 ));
365 try self.stack.append(allocator, .{365 try self.stack.append(allocator, .{
366 .regval_type = .{366 .regval_type = .{
...@@ -464,7 +464,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -464,7 +464,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
464 // 2.5.1.4: Arithmetic and Logical Operations464 // 2.5.1.4: Arithmetic and Logical Operations
465 OP.abs => {465 OP.abs => {
466 if (self.stack.items.len == 0) return error.InvalidExpression;466 if (self.stack.items.len == 0) return error.InvalidExpression;
467 const value: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());467 const value: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
468 self.stack.items[self.stack.items.len - 1] = .{468 self.stack.items[self.stack.items.len - 1] = .{
469 .generic = std.math.absCast(value),469 .generic = std.math.absCast(value),
470 };470 };
...@@ -478,10 +478,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -478,10 +478,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
478 },478 },
479 OP.div => {479 OP.div => {
480 if (self.stack.items.len < 2) return error.InvalidExpression;480 if (self.stack.items.len < 2) return error.InvalidExpression;
481 const a: isize = @bitCast(try self.stack.pop().asIntegral());481 const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral());
482 const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());482 const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
483 self.stack.items[self.stack.items.len - 1] = .{483 self.stack.items[self.stack.items.len - 1] = .{
484 .generic = @bitCast(try std.math.divTrunc(isize, b, a)),484 .generic = @bitCast(try std.math.divTrunc(addr_type_signed, b, a)),
485 };485 };
486 },486 },
487 OP.minus => {487 OP.minus => {
...@@ -493,16 +493,16 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -493,16 +493,16 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
493 },493 },
494 OP.mod => {494 OP.mod => {
495 if (self.stack.items.len < 2) return error.InvalidExpression;495 if (self.stack.items.len < 2) return error.InvalidExpression;
496 const a: isize = @bitCast(try self.stack.pop().asIntegral());496 const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral());
497 const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());497 const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
498 self.stack.items[self.stack.items.len - 1] = .{498 self.stack.items[self.stack.items.len - 1] = .{
499 .generic = @bitCast(@mod(b, a)),499 .generic = @bitCast(@mod(b, a)),
500 };500 };
501 },501 },
502 OP.mul => {502 OP.mul => {
503 if (self.stack.items.len < 2) return error.InvalidExpression;503 if (self.stack.items.len < 2) return error.InvalidExpression;
504 const a: isize = @bitCast(try self.stack.pop().asIntegral());504 const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral());
505 const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());505 const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
506 self.stack.items[self.stack.items.len - 1] = .{506 self.stack.items[self.stack.items.len - 1] = .{
507 .generic = @bitCast(@mulWithOverflow(a, b)[0]),507 .generic = @bitCast(@mulWithOverflow(a, b)[0]),
508 };508 };
...@@ -512,7 +512,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -512,7 +512,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
512 self.stack.items[self.stack.items.len - 1] = .{512 self.stack.items[self.stack.items.len - 1] = .{
513 .generic = @bitCast(513 .generic = @bitCast(
514 try std.math.negate(514 try std.math.negate(
515 @as(isize, @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral())),515 @as(addr_type_signed, @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral())),
516 ),516 ),
517 ),517 ),
518 };518 };
...@@ -563,9 +563,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -563,9 +563,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
563 OP.shra => {563 OP.shra => {
564 if (self.stack.items.len < 2) return error.InvalidExpression;564 if (self.stack.items.len < 2) return error.InvalidExpression;
565 const a = try self.stack.pop().asIntegral();565 const a = try self.stack.pop().asIntegral();
566 const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());566 const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
567 self.stack.items[self.stack.items.len - 1] = .{567 self.stack.items[self.stack.items.len - 1] = .{
568 .generic = @bitCast(std.math.shr(isize, b, a)),568 .generic = @bitCast(std.math.shr(addr_type_signed, b, a)),
569 };569 };
570 },570 },
571 OP.xor => {571 OP.xor => {
...@@ -589,8 +589,8 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -589,8 +589,8 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
589 const b = self.stack.items[self.stack.items.len - 1];589 const b = self.stack.items[self.stack.items.len - 1];
590590
591 if (a == .generic and b == .generic) {591 if (a == .generic and b == .generic) {
592 const a_int: isize = @bitCast(a.asIntegral() catch unreachable);592 const a_int: addr_type_signed = @bitCast(a.asIntegral() catch unreachable);
593 const b_int: isize = @bitCast(b.asIntegral() catch unreachable);593 const b_int: addr_type_signed = @bitCast(b.asIntegral() catch unreachable);
594 const result = @intFromBool(switch (opcode) {594 const result = @intFromBool(switch (opcode) {
595 OP.le => b_int < a_int,595 OP.le => b_int < a_int,
596 OP.ge => b_int >= a_int,596 OP.ge => b_int >= a_int,
...@@ -617,7 +617,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -617,7 +617,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
617 if (condition) {617 if (condition) {
618 const new_pos = std.math.cast(618 const new_pos = std.math.cast(
619 usize,619 usize,
620 try std.math.add(isize, @as(isize, @intCast(stream.pos)), branch_offset),620 try std.math.add(addr_type_signed, @as(addr_type_signed, @intCast(stream.pos)), branch_offset),
621 ) orelse return error.InvalidExpression;621 ) orelse return error.InvalidExpression;
622622
623 if (new_pos < 0 or new_pos >= stream.buffer.len) return error.InvalidExpression;623 if (new_pos < 0 or new_pos >= stream.buffer.len) return error.InvalidExpression;
...@@ -710,7 +710,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -710,7 +710,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
710 };710 };
711}711}
712712
713pub fn Writer(options: ExpressionOptions) type {713pub fn Builder(comptime options: ExpressionOptions) type {
714 const addr_type = switch (options.addr_size) {714 const addr_type = switch (options.addr_size) {
715 2 => u16,715 2 => u16,
716 4 => u32,716 4 => u32,
...@@ -959,10 +959,33 @@ fn opcodeValidInCFA(opcode: u8) bool {...@@ -959,10 +959,33 @@ fn opcodeValidInCFA(opcode: u8) bool {
959 };959 };
960}960}
961961
962const testing = std.testing;
962test "DWARF expressions" {963test "DWARF expressions" {
963 const allocator = std.testing.allocator;964 const allocator = std.testing.allocator;
964965
965 const options = ExpressionOptions{};966 const options = ExpressionOptions{};
966 const stack_machine = StackMachine(options){};967 var stack_machine = StackMachine(options){};
967 defer stack_machine.deinit(allocator);968 defer stack_machine.deinit(allocator);
969
970 const b = Builder(options);
971
972 var program = std.ArrayList(u8).init(allocator);
973 defer program.deinit();
974
975 const writer = program.writer();
976
977 // Literals
978 {
979 const context = ExpressionContext{};
980 for (0..32) |i| {
981 try b.writeLiteral(writer, @intCast(i));
982 }
983
984 _ = try stack_machine.run(program.items, allocator, context, 0);
985
986 for (0..32) |i| {
987 const expected = 31 - i;
988 try testing.expectEqual(expected, stack_machine.stack.popOrNull().?.generic);
989 }
990 }
968}991}
src/crash_report.zig+1-1
...@@ -271,7 +271,7 @@ const StackContext = union(enum) {...@@ -271,7 +271,7 @@ const StackContext = union(enum) {
271 current: struct {271 current: struct {
272 ret_addr: ?usize,272 ret_addr: ?usize,
273 },273 },
274 exception: *const debug.StackTraceContext,274 exception: *const debug.ThreadContext,
275 not_supported: void,275 not_supported: void,
276276
277 pub fn dumpStackTrace(ctx: @This()) void {277 pub fn dumpStackTrace(ctx: @This()) void {
test/standalone/dwarf_unwinding/shared_lib_unwind.zig+1-1
...@@ -5,7 +5,7 @@ const testing = std.testing;...@@ -5,7 +5,7 @@ const testing = std.testing;
5noinline fn frame4(expected: *[4]usize, unwound: *[4]usize) void {5noinline fn frame4(expected: *[4]usize, unwound: *[4]usize) void {
6 expected[0] = @returnAddress();6 expected[0] = @returnAddress();
77
8 var context: debug.StackTraceContext = undefined;8 var context: debug.ThreadContext = undefined;
9 testing.expect(debug.getContext(&context)) catch @panic("failed to getContext");9 testing.expect(debug.getContext(&context)) catch @panic("failed to getContext");
1010
11 var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo");11 var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo");
test/standalone/dwarf_unwinding/zig_unwind.zig+1-1
...@@ -5,7 +5,7 @@ const testing = std.testing;...@@ -5,7 +5,7 @@ const testing = std.testing;
5noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void {5noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void {
6 expected[0] = @returnAddress();6 expected[0] = @returnAddress();
77
8 var context: debug.StackTraceContext = undefined;8 var context: debug.ThreadContext = undefined;
9 testing.expect(debug.getContext(&context)) catch @panic("failed to getContext");9 testing.expect(debug.getContext(&context)) catch @panic("failed to getContext");
1010
11 var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo");11 var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo");