| author | |
| committer | |
| log | 5f72c6508d78291d8f0358d06d407a8d6b4a28c9 |
| tree | a5f2b6043ee8ef90a64679cc5299e7d55981d384 |
| parent | 8547c42ba542f77c55ce50253446fd2bf4f3592b |
dwarf: use ThreadContext instead of os.ucontext_t
dwarf: add regBytes impl for windows
dwarf: fixup expression types for non-native8 files changed, 157 insertions(+), 77 deletions(-)
lib/std/debug.zig+6-6| ... | ... | @@ -133,7 +133,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { |
| 133 | 133 | } |
| 134 | 134 | } |
| 135 | 135 | |
| 136 | pub const StackTraceContext = blk: { | |
| 136 | pub const ThreadContext = blk: { | |
| 137 | 137 | if (native_os == .windows) { |
| 138 | 138 | break :blk std.os.windows.CONTEXT; |
| 139 | 139 | } else if (have_ucontext) { |
| ... | ... | @@ -146,7 +146,7 @@ pub const StackTraceContext = blk: { |
| 146 | 146 | /// Tries to print the stack trace starting from the supplied base pointer to stderr, |
| 147 | 147 | /// unbuffered, and ignores any error returned. |
| 148 | 148 | /// TODO multithreaded awareness |
| 149 | pub fn dumpStackTraceFromBase(context: *const StackTraceContext) void { | |
| 149 | pub fn dumpStackTraceFromBase(context: *const ThreadContext) void { | |
| 150 | 150 | nosuspend { |
| 151 | 151 | if (comptime builtin.target.isWasm()) { |
| 152 | 152 | if (native_os == .wasi) { |
| ... | ... | @@ -437,7 +437,7 @@ pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and |
| 437 | 437 | else => true, |
| 438 | 438 | }); |
| 439 | 439 | |
| 440 | pub inline fn getContext(context: *StackTraceContext) bool { | |
| 440 | pub inline fn getContext(context: *ThreadContext) bool { | |
| 441 | 441 | if (native_os == .windows) { |
| 442 | 442 | context.* = std.mem.zeroes(windows.CONTEXT); |
| 443 | 443 | windows.ntdll.RtlCaptureContext(context); |
| ... | ... | @@ -606,8 +606,8 @@ pub const StackIterator = struct { |
| 606 | 606 | fn next_dwarf(self: *StackIterator) !usize { |
| 607 | 607 | const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc); |
| 608 | 608 | if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| { |
| 609 | self.dwarf_context.reg_ctx.eh_frame = true; | |
| 610 | self.dwarf_context.reg_ctx.is_macho = di.is_macho; | |
| 609 | self.dwarf_context.reg_context.eh_frame = true; | |
| 610 | self.dwarf_context.reg_context.is_macho = di.is_macho; | |
| 611 | 611 | return di.unwindFrame(&self.dwarf_context, module.base_address); |
| 612 | 612 | } else return error.MissingDebugInfo; |
| 613 | 613 | } |
| ... | ... | @@ -663,7 +663,7 @@ pub fn writeCurrentStackTrace( |
| 663 | 663 | tty_config: io.tty.Config, |
| 664 | 664 | start_addr: ?usize, |
| 665 | 665 | ) !void { |
| 666 | var context: StackTraceContext = undefined; | |
| 666 | var context: ThreadContext = undefined; | |
| 667 | 667 | const has_context = getContext(&context); |
| 668 | 668 | if (native_os == .windows) { |
| 669 | 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 | 146 | pass_by_reference = 0x4, |
| 147 | 147 | pass_by_value = 0x5, |
| 148 | 148 | |
| 149 | lo_user = 0x40, | |
| 150 | hi_user = 0xff, | |
| 151 | ||
| 152 | 149 | GNU_renesas_sh = 0x40, |
| 153 | 150 | GNU_borland_fastcall_i386 = 0x41, |
| 151 | ||
| 152 | pub const lo_user = 0x40; | |
| 153 | pub const hi_user = 0xff; | |
| 154 | 154 | }; |
| 155 | 155 | |
| 156 | 156 | pub const Format = enum { @"32", @"64" }; |
| ... | ... | @@ -1676,13 +1676,13 @@ pub const DwarfInfo = struct { |
| 1676 | 1676 | var expression_context = .{ |
| 1677 | 1677 | .isValidMemory = context.isValidMemory, |
| 1678 | 1678 | .compile_unit = di.findCompileUnit(fde.pc_begin) catch null, |
| 1679 | .ucontext = &context.ucontext, | |
| 1680 | .reg_ctx = context.reg_ctx, | |
| 1679 | .thread_context = &context.thread_context, | |
| 1680 | .reg_context = context.reg_context, | |
| 1681 | 1681 | .cfa = context.cfa, |
| 1682 | 1682 | }; |
| 1683 | 1683 | |
| 1684 | 1684 | context.vm.reset(); |
| 1685 | context.reg_ctx.eh_frame = cie.version != 4; | |
| 1685 | context.reg_context.eh_frame = cie.version != 4; | |
| 1686 | 1686 | |
| 1687 | 1687 | _ = try context.vm.runToNative(context.allocator, mapped_pc, cie, fde); |
| 1688 | 1688 | const row = &context.vm.current_row; |
| ... | ... | @@ -1690,7 +1690,7 @@ pub const DwarfInfo = struct { |
| 1690 | 1690 | context.cfa = switch (row.cfa.rule) { |
| 1691 | 1691 | .val_offset => |offset| blk: { |
| 1692 | 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 | 1694 | break :blk try call_frame.applyOffset(value, offset); |
| 1695 | 1695 | }, |
| 1696 | 1696 | .expression => |expression| blk: { |
| ... | ... | @@ -1711,14 +1711,14 @@ pub const DwarfInfo = struct { |
| 1711 | 1711 | if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA; |
| 1712 | 1712 | expression_context.cfa = context.cfa; |
| 1713 | 1713 | |
| 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 | 1715 | // some implementations (ie. darwin) use internal pointers to the mcontext. |
| 1716 | 1716 | var arena = std.heap.ArenaAllocator.init(context.allocator); |
| 1717 | 1717 | defer arena.deinit(); |
| 1718 | 1718 | const update_allocator = arena.allocator(); |
| 1719 | 1719 | |
| 1720 | 1720 | const RegisterUpdate = struct { |
| 1721 | // Backed by ucontext | |
| 1721 | // Backed by thread_context | |
| 1722 | 1722 | old_value: []u8, |
| 1723 | 1723 | // Backed by arena |
| 1724 | 1724 | new_value: []const u8, |
| ... | ... | @@ -1733,7 +1733,7 @@ pub const DwarfInfo = struct { |
| 1733 | 1733 | has_next_ip = column.rule != .undefined; |
| 1734 | 1734 | } |
| 1735 | 1735 | |
| 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 | 1737 | const new_value = try update_allocator.alloc(u8, old_value.len); |
| 1738 | 1738 | |
| 1739 | 1739 | const prev = update_tail; |
| ... | ... | @@ -1758,12 +1758,12 @@ pub const DwarfInfo = struct { |
| 1758 | 1758 | } |
| 1759 | 1759 | |
| 1760 | 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 | 1762 | } else { |
| 1763 | 1763 | context.pc = 0; |
| 1764 | 1764 | } |
| 1765 | 1765 | |
| 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.?); | |
| 1767 | 1767 | |
| 1768 | 1768 | // The call instruction will have pushed the address of the instruction that follows the call as the return address |
| 1769 | 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 | 1779 | allocator: mem.Allocator, |
| 1780 | 1780 | cfa: ?usize, |
| 1781 | 1781 | pc: usize, |
| 1782 | ucontext: os.ucontext_t, | |
| 1783 | reg_ctx: abi.RegisterContext, | |
| 1782 | thread_context: debug.ThreadContext, | |
| 1783 | reg_context: abi.RegisterContext, | |
| 1784 | 1784 | isValidMemory: *const fn (address: usize) bool, |
| 1785 | 1785 | vm: call_frame.VirtualMachine = .{}, |
| 1786 | 1786 | stack_machine: expressions.StackMachine(.{ .call_frame_context = true }) = .{}, |
| 1787 | 1787 | |
| 1788 | pub fn init(allocator: mem.Allocator, ucontext: *const os.ucontext_t, isValidMemory: *const fn (address: usize) bool) !UnwindContext { | |
| 1789 | const pc = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, abi.ipRegNum(), null)); | |
| 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(thread_context, abi.ipRegNum(), null)); | |
| 1790 | ||
| 1791 | if (builtin.os.tag == .macos) @compileError("Fix below TODO"); | |
| 1792 | ||
| 1790 | 1793 | return .{ |
| 1791 | 1794 | .allocator = allocator, |
| 1792 | 1795 | .cfa = null, |
| 1793 | 1796 | .pc = pc, |
| 1794 | .ucontext = ucontext.*, | |
| 1795 | .reg_ctx = undefined, | |
| 1797 | // TODO: This is broken on macos, need a function that knows how to copy the OSs mcontext properly | |
| 1798 | .thread_context = thread_context.*, | |
| 1799 | .reg_context = undefined, | |
| 1796 | 1800 | .isValidMemory = isValidMemory, |
| 1797 | 1801 | }; |
| 1798 | 1802 | } |
| ... | ... | @@ -1803,7 +1807,7 @@ pub const UnwindContext = struct { |
| 1803 | 1807 | } |
| 1804 | 1808 | |
| 1805 | 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 | }; |
| 1809 | 1813 | |
| ... | ... | @@ -2388,3 +2392,7 @@ fn pcRelBase(field_ptr: usize, pc_rel_offset: i64) !usize { |
| 2388 | 2392 | return math.add(usize, field_ptr, @as(usize, @intCast(pc_rel_offset))); |
| 2389 | 2393 | } |
| 2390 | 2394 | } |
| 2395 | ||
| 2396 | test { | |
| 2397 | std.testing.refAllDecls(@This()); | |
| 2398 | } |
lib/std/dwarf/abi.zig+65-16| ... | ... | @@ -3,11 +3,6 @@ const std = @import("../std.zig"); |
| 3 | 3 | const os = std.os; |
| 4 | 4 | const mem = std.mem; |
| 5 | 5 | |
| 6 | pub const RegisterContext = struct { | |
| 7 | eh_frame: bool, | |
| 8 | is_macho: bool, | |
| 9 | }; | |
| 10 | ||
| 11 | 6 | pub fn isSupportedArch(arch: std.Target.Cpu.Arch) bool { |
| 12 | 7 | return switch (arch) { |
| 13 | 8 | .x86, |
| ... | ... | @@ -29,10 +24,10 @@ pub fn ipRegNum() u8 { |
| 29 | 24 | }; |
| 30 | 25 | } |
| 31 | 26 | |
| 32 | pub fn fpRegNum(reg_ctx: RegisterContext) u8 { | |
| 27 | pub fn fpRegNum(reg_context: RegisterContext) u8 { | |
| 33 | 28 | return switch (builtin.cpu.arch) { |
| 34 | 29 | // 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 | 31 | .x86_64 => 6, |
| 37 | 32 | .arm => 11, |
| 38 | 33 | .aarch64 => 29, |
| ... | ... | @@ -40,9 +35,9 @@ pub fn fpRegNum(reg_ctx: RegisterContext) u8 { |
| 40 | 35 | }; |
| 41 | 36 | } |
| 42 | 37 | |
| 43 | pub fn spRegNum(reg_ctx: RegisterContext) u8 { | |
| 38 | pub fn spRegNum(reg_context: RegisterContext) u8 { | |
| 44 | 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 | 41 | .x86_64 => 7, |
| 47 | 42 | .arm => 13, |
| 48 | 43 | .aarch64 => 31, |
| ... | ... | @@ -52,21 +47,76 @@ pub fn spRegNum(reg_ctx: RegisterContext) u8 { |
| 52 | 47 | |
| 53 | 48 | fn RegBytesReturnType(comptime ContextPtrType: type) type { |
| 54 | 49 | const info = @typeInfo(ContextPtrType); |
| 55 | if (info != .Pointer or info.Pointer.child != os.ucontext_t) { | |
| 56 | @compileError("Expected a pointer to ucontext_t, got " ++ @typeName(@TypeOf(ContextPtrType))); | |
| 50 | if (info != .Pointer or info.Pointer.child != std.debug.ThreadContext) { | |
| 51 | @compileError("Expected a pointer to std.debug.ThreadContext, got " ++ @typeName(@TypeOf(ContextPtrType))); | |
| 57 | 52 | } |
| 58 | 53 | |
| 59 | 54 | return if (info.Pointer.is_const) return []const u8 else []u8; |
| 60 | 55 | } |
| 61 | 56 | |
| 57 | pub const RegisterContext = struct { | |
| 58 | eh_frame: bool, | |
| 59 | is_macho: bool, | |
| 60 | }; | |
| 61 | ||
| 62 | 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 different | |
| 64 | /// `reg_context` describes in what context the register number is used, as it can have different | |
| 65 | 65 | /// meanings depending on the DWARF container. It is only required when getting the stack or |
| 66 | 66 | /// frame pointer register on some architectures. |
| 67 | pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext) !RegBytesReturnType(@TypeOf(ucontext_ptr)) { | |
| 68 | var m = &ucontext_ptr.mcontext; | |
| 67 | pub fn regBytes(thread_context_ptr: anytype, reg_number: u8, reg_context: ?RegisterContext) !RegBytesReturnType(@TypeOf(thread_context_ptr)) { | |
| 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 | } | |
| 69 | 117 | |
| 118 | const ucontext_ptr = thread_context_ptr; | |
| 119 | var m = &ucontext_ptr.mcontext; | |
| 70 | 120 | return switch (builtin.cpu.arch) { |
| 71 | 121 | .x86 => switch (builtin.os.tag) { |
| 72 | 122 | .linux, .netbsd, .solaris => switch (reg_number) { |
| ... | ... | @@ -74,7 +124,7 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext |
| 74 | 124 | 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ECX]), |
| 75 | 125 | 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDX]), |
| 76 | 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 | 128 | if (reg_number == 4) { |
| 79 | 129 | break :bytes if (r.eh_frame and r.is_macho) |
| 80 | 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 | 148 | 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.FS]), |
| 99 | 149 | 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]), |
| 100 | 150 | 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs |
| 101 | // TODO: Map TRAPNO, ERR, UESP | |
| 102 | 151 | 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs |
| 103 | 152 | else => error.InvalidRegister, |
| 104 | 153 | }, |
lib/std/dwarf/call_frame.zig+6-6| ... | ... | @@ -2,10 +2,10 @@ const builtin = @import("builtin"); |
| 2 | 2 | const std = @import("../std.zig"); |
| 3 | 3 | const mem = std.mem; |
| 4 | 4 | const debug = std.debug; |
| 5 | const leb = @import("../leb128.zig"); | |
| 6 | const abi = @import("abi.zig"); | |
| 7 | const dwarf = @import("../dwarf.zig"); | |
| 8 | const expressions = @import("expressions.zig"); | |
| 5 | const leb = std.leb; | |
| 6 | const dwarf = std.dwarf; | |
| 7 | const abi = dwarf.abi; | |
| 8 | const expressions = dwarf.expressions; | |
| 9 | 9 | const assert = std.debug.assert; |
| 10 | 10 | |
| 11 | 11 | const Opcode = enum(u8) { |
| ... | ... | @@ -315,9 +315,9 @@ pub const VirtualMachine = struct { |
| 315 | 315 | } else return error.InvalidCFA; |
| 316 | 316 | }, |
| 317 | 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 | 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 | 322 | .expression => |expression| { |
| 323 | 323 | context.stack_machine.reset(); |
lib/std/dwarf/expressions.zig+50-27| ... | ... | @@ -1,8 +1,8 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const OP = @import("OP.zig"); |
| 4 | const leb = @import("../leb128.zig"); | |
| 5 | const dwarf = @import("../dwarf.zig"); | |
| 4 | const leb = std.leb; | |
| 5 | const dwarf = std.dwarf; | |
| 6 | 6 | const abi = dwarf.abi; |
| 7 | 7 | const mem = std.mem; |
| 8 | 8 | const assert = std.debug.assert; |
| ... | ... | @@ -17,12 +17,12 @@ pub const ExpressionContext = struct { |
| 17 | 17 | /// The compilation unit this expression relates to, if any |
| 18 | 18 | compile_unit: ?*const dwarf.CompileUnit = null, |
| 19 | 19 | |
| 20 | /// Register context | |
| 21 | ucontext: ?*std.os.ucontext_t, | |
| 22 | reg_ctx: ?abi.RegisterContext, | |
| 20 | /// Thread context | |
| 21 | thread_context: ?*std.debug.ThreadContext = null, | |
| 22 | reg_context: ?abi.RegisterContext = null, | |
| 23 | 23 | |
| 24 | 24 | /// Call frame address, if in a CFI context |
| 25 | cfa: ?usize, | |
| 25 | cfa: ?usize = null, | |
| 26 | 26 | }; |
| 27 | 27 | |
| 28 | 28 | pub const ExpressionOptions = struct { |
| ... | ... | @@ -344,13 +344,13 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 344 | 344 | OP.breg0...OP.breg31, |
| 345 | 345 | OP.bregx, |
| 346 | 346 | => { |
| 347 | if (context.ucontext == null) return error.IncompleteExpressionContext; | |
| 347 | if (context.thread_context == null) return error.IncompleteExpressionContext; | |
| 348 | 348 | |
| 349 | 349 | const base_register = (try readOperand(stream, opcode)).?.base_register; |
| 350 | 350 | var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes( |
| 351 | context.ucontext.?, | |
| 351 | context.thread_context.?, | |
| 352 | 352 | base_register.base_register, |
| 353 | context.reg_ctx, | |
| 353 | context.reg_context, | |
| 354 | 354 | ))); |
| 355 | 355 | value += base_register.offset; |
| 356 | 356 | try self.stack.append(allocator, .{ .generic = @intCast(value) }); |
| ... | ... | @@ -358,9 +358,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 358 | 358 | OP.regval_type => { |
| 359 | 359 | const register_type = (try readOperand(stream, opcode)).?.register_type; |
| 360 | 360 | const value = mem.readIntSliceNative(usize, try abi.regBytes( |
| 361 | context.ucontext.?, | |
| 361 | context.thread_context.?, | |
| 362 | 362 | register_type.register, |
| 363 | context.reg_ctx, | |
| 363 | context.reg_context, | |
| 364 | 364 | )); |
| 365 | 365 | try self.stack.append(allocator, .{ |
| 366 | 366 | .regval_type = .{ |
| ... | ... | @@ -464,7 +464,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 464 | 464 | // 2.5.1.4: Arithmetic and Logical Operations |
| 465 | 465 | OP.abs => { |
| 466 | 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 | 468 | self.stack.items[self.stack.items.len - 1] = .{ |
| 469 | 469 | .generic = std.math.absCast(value), |
| 470 | 470 | }; |
| ... | ... | @@ -478,10 +478,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 478 | 478 | }, |
| 479 | 479 | OP.div => { |
| 480 | 480 | if (self.stack.items.len < 2) return error.InvalidExpression; |
| 481 | const a: isize = @bitCast(try self.stack.pop().asIntegral()); | |
| 482 | const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); | |
| 481 | const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral()); | |
| 482 | const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); | |
| 483 | 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 | 487 | OP.minus => { |
| ... | ... | @@ -493,16 +493,16 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 493 | 493 | }, |
| 494 | 494 | OP.mod => { |
| 495 | 495 | if (self.stack.items.len < 2) return error.InvalidExpression; |
| 496 | const a: isize = @bitCast(try self.stack.pop().asIntegral()); | |
| 497 | const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); | |
| 496 | const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral()); | |
| 497 | const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); | |
| 498 | 498 | self.stack.items[self.stack.items.len - 1] = .{ |
| 499 | 499 | .generic = @bitCast(@mod(b, a)), |
| 500 | 500 | }; |
| 501 | 501 | }, |
| 502 | 502 | OP.mul => { |
| 503 | 503 | if (self.stack.items.len < 2) return error.InvalidExpression; |
| 504 | const a: isize = @bitCast(try self.stack.pop().asIntegral()); | |
| 505 | const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); | |
| 504 | const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral()); | |
| 505 | const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); | |
| 506 | 506 | self.stack.items[self.stack.items.len - 1] = .{ |
| 507 | 507 | .generic = @bitCast(@mulWithOverflow(a, b)[0]), |
| 508 | 508 | }; |
| ... | ... | @@ -512,7 +512,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 512 | 512 | self.stack.items[self.stack.items.len - 1] = .{ |
| 513 | 513 | .generic = @bitCast( |
| 514 | 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 | 563 | OP.shra => { |
| 564 | 564 | if (self.stack.items.len < 2) return error.InvalidExpression; |
| 565 | 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 | 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 | 571 | OP.xor => { |
| ... | ... | @@ -589,8 +589,8 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 589 | 589 | const b = self.stack.items[self.stack.items.len - 1]; |
| 590 | 590 | |
| 591 | 591 | if (a == .generic and b == .generic) { |
| 592 | const a_int: isize = @bitCast(a.asIntegral() catch unreachable); | |
| 593 | const b_int: isize = @bitCast(b.asIntegral() catch unreachable); | |
| 592 | const a_int: addr_type_signed = @bitCast(a.asIntegral() catch unreachable); | |
| 593 | const b_int: addr_type_signed = @bitCast(b.asIntegral() catch unreachable); | |
| 594 | 594 | const result = @intFromBool(switch (opcode) { |
| 595 | 595 | OP.le => b_int < a_int, |
| 596 | 596 | OP.ge => b_int >= a_int, |
| ... | ... | @@ -617,7 +617,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { |
| 617 | 617 | if (condition) { |
| 618 | 618 | const new_pos = std.math.cast( |
| 619 | 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 | 621 | ) orelse return error.InvalidExpression; |
| 622 | 622 | |
| 623 | 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 | 710 | }; |
| 711 | 711 | } |
| 712 | 712 | |
| 713 | pub fn Writer(options: ExpressionOptions) type { | |
| 713 | pub fn Builder(comptime options: ExpressionOptions) type { | |
| 714 | 714 | const addr_type = switch (options.addr_size) { |
| 715 | 715 | 2 => u16, |
| 716 | 716 | 4 => u32, |
| ... | ... | @@ -959,10 +959,33 @@ fn opcodeValidInCFA(opcode: u8) bool { |
| 959 | 959 | }; |
| 960 | 960 | } |
| 961 | 961 | |
| 962 | const testing = std.testing; | |
| 962 | 963 | test "DWARF expressions" { |
| 963 | 964 | const allocator = std.testing.allocator; |
| 964 | 965 | |
| 965 | 966 | const options = ExpressionOptions{}; |
| 966 | const stack_machine = StackMachine(options){}; | |
| 967 | var stack_machine = StackMachine(options){}; | |
| 967 | 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 | 271 | current: struct { |
| 272 | 272 | ret_addr: ?usize, |
| 273 | 273 | }, |
| 274 | exception: *const debug.StackTraceContext, | |
| 274 | exception: *const debug.ThreadContext, | |
| 275 | 275 | not_supported: void, |
| 276 | 276 | |
| 277 | 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 | 5 | noinline fn frame4(expected: *[4]usize, unwound: *[4]usize) void { |
| 6 | 6 | expected[0] = @returnAddress(); |
| 7 | 7 | |
| 8 | var context: debug.StackTraceContext = undefined; | |
| 8 | var context: debug.ThreadContext = undefined; | |
| 9 | 9 | testing.expect(debug.getContext(&context)) catch @panic("failed to getContext"); |
| 10 | 10 | |
| 11 | 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 | 5 | noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void { |
| 6 | 6 | expected[0] = @returnAddress(); |
| 7 | 7 | |
| 8 | var context: debug.StackTraceContext = undefined; | |
| 8 | var context: debug.ThreadContext = undefined; | |
| 9 | 9 | testing.expect(debug.getContext(&context)) catch @panic("failed to getContext"); |
| 10 | 10 | |
| 11 | 11 | var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo"); |