| author | |
| committer | |
| log | dd8d59686a069fdb72d9f0753e3482ff99cce98c |
| tree | 0a90991d41626bb9400a1d6cf956d10b99ed3a6c |
| parent | a18fd41064493e742eacebc88e2afeadd54ff6f0 |
| signature |
Mostly on macOS, since Loris showed me a not-great stack trace, and I
spent 8 hours trying to make it better. The dyld shared cache is
designed in a way which makes this really hard to do right, and
documentation is non-existent, but this *seems* to work pretty well.
I'll leave the ruling on whether I did a good job to CI and our users.8 files changed, 276 insertions(+), 132 deletions(-)
lib/std/c.zig+3| ... | @@ -10994,6 +10994,9 @@ pub extern "c" fn dlclose(handle: *anyopaque) c_int; | ... | @@ -10994,6 +10994,9 @@ pub extern "c" fn dlclose(handle: *anyopaque) c_int; |
| 10994 | pub extern "c" fn dlsym(handle: ?*anyopaque, symbol: [*:0]const u8) ?*anyopaque; | 10994 | pub extern "c" fn dlsym(handle: ?*anyopaque, symbol: [*:0]const u8) ?*anyopaque; |
| 10995 | pub extern "c" fn dlerror() ?[*:0]u8; | 10995 | pub extern "c" fn dlerror() ?[*:0]u8; |
| 10996 | 10996 | ||
| 10997 | pub const dladdr = if (native_os.isDarwin()) darwin.dladdr else {}; | ||
| 10998 | pub const dl_info = if (native_os.isDarwin()) darwin.dl_info else {}; | ||
| 10999 | |||
| 10997 | pub extern "c" fn sync() void; | 11000 | pub extern "c" fn sync() void; |
| 10998 | pub extern "c" fn syncfs(fd: c_int) c_int; | 11001 | pub extern "c" fn syncfs(fd: c_int) c_int; |
| 10999 | pub extern "c" fn fsync(fd: c_int) c_int; | 11002 | pub extern "c" fn fsync(fd: c_int) c_int; |
lib/std/c/darwin.zig+8| ... | @@ -354,6 +354,14 @@ pub extern "c" fn _dyld_image_count() u32; | ... | @@ -354,6 +354,14 @@ pub extern "c" fn _dyld_image_count() u32; |
| 354 | pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header; | 354 | pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header; |
| 355 | pub extern "c" fn _dyld_get_image_vmaddr_slide(image_index: u32) usize; | 355 | pub extern "c" fn _dyld_get_image_vmaddr_slide(image_index: u32) usize; |
| 356 | pub extern "c" fn _dyld_get_image_name(image_index: u32) [*:0]const u8; | 356 | pub extern "c" fn _dyld_get_image_name(image_index: u32) [*:0]const u8; |
| 357 | pub extern "c" fn dladdr(addr: *const anyopaque, info: *dl_info) c_int; | ||
| 358 | |||
| 359 | pub const dl_info = extern struct { | ||
| 360 | fname: [*:0]const u8, | ||
| 361 | fbase: *anyopaque, | ||
| 362 | sname: ?[*:0]const u8, | ||
| 363 | saddr: ?*anyopaque, | ||
| 364 | }; | ||
| 357 | 365 | ||
| 358 | pub const COPYFILE = packed struct(u32) { | 366 | pub const COPYFILE = packed struct(u32) { |
| 359 | ACL: bool = false, | 367 | ACL: bool = false, |
lib/std/debug.zig+55-20| ... | @@ -585,12 +585,14 @@ pub fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) | ... | @@ -585,12 +585,14 @@ pub fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) |
| 585 | while (true) switch (it.next()) { | 585 | while (true) switch (it.next()) { |
| 586 | .switch_to_fp => if (!it.stratOk(options.allow_unsafe_unwind)) break, | 586 | .switch_to_fp => if (!it.stratOk(options.allow_unsafe_unwind)) break, |
| 587 | .end => break, | 587 | .end => break, |
| 588 | .frame => |return_address| { | 588 | .frame => |pc_addr| { |
| 589 | if (wait_for) |target| { | 589 | if (wait_for) |target| { |
| 590 | if (return_address != target) continue; | 590 | // Possible off-by-one error: `pc_addr` might be one less than the return address (so |
| 591 | // that it falls *inside* the function call), while `target` *is* a return address. | ||
| 592 | if (pc_addr != target and pc_addr + 1 != target) continue; | ||
| 591 | wait_for = null; | 593 | wait_for = null; |
| 592 | } | 594 | } |
| 593 | if (frame_idx < addr_buf.len) addr_buf[frame_idx] = return_address; | 595 | if (frame_idx < addr_buf.len) addr_buf[frame_idx] = pc_addr; |
| 594 | frame_idx += 1; | 596 | frame_idx += 1; |
| 595 | }, | 597 | }, |
| 596 | }; | 598 | }; |
| ... | @@ -631,6 +633,7 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_ | ... | @@ -631,6 +633,7 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_ |
| 631 | var printed_any_frame = false; | 633 | var printed_any_frame = false; |
| 632 | while (true) switch (it.next()) { | 634 | while (true) switch (it.next()) { |
| 633 | .switch_to_fp => |unwind_error| { | 635 | .switch_to_fp => |unwind_error| { |
| 636 | if (StackIterator.fp_unwind_is_safe) continue; // no need to even warn | ||
| 634 | const module_name = di.getModuleNameForAddress(di_gpa, unwind_error.address) catch "???"; | 637 | const module_name = di.getModuleNameForAddress(di_gpa, unwind_error.address) catch "???"; |
| 635 | const caption: []const u8 = switch (unwind_error.err) { | 638 | const caption: []const u8 = switch (unwind_error.err) { |
| 636 | error.MissingDebugInfo => "unwind info unavailable", | 639 | error.MissingDebugInfo => "unwind info unavailable", |
| ... | @@ -658,12 +661,14 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_ | ... | @@ -658,12 +661,14 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_ |
| 658 | } | 661 | } |
| 659 | }, | 662 | }, |
| 660 | .end => break, | 663 | .end => break, |
| 661 | .frame => |return_address| { | 664 | .frame => |pc_addr| { |
| 662 | if (wait_for) |target| { | 665 | if (wait_for) |target| { |
| 663 | if (return_address != target) continue; | 666 | // Possible off-by-one error: `pc_addr` might be one less than the return address (so |
| 667 | // that it falls *inside* the function call), while `target` *is* a return address. | ||
| 668 | if (pc_addr != target and pc_addr + 1 != target) continue; | ||
| 664 | wait_for = null; | 669 | wait_for = null; |
| 665 | } | 670 | } |
| 666 | try printSourceAtAddress(di_gpa, di, writer, return_address -| 1, tty_config); | 671 | try printSourceAtAddress(di_gpa, di, writer, pc_addr, tty_config); |
| 667 | printed_any_frame = true; | 672 | printed_any_frame = true; |
| 668 | }, | 673 | }, |
| 669 | }; | 674 | }; |
| ... | @@ -703,8 +708,8 @@ pub fn writeStackTrace(st: *const std.builtin.StackTrace, writer: *Writer, tty_c | ... | @@ -703,8 +708,8 @@ pub fn writeStackTrace(st: *const std.builtin.StackTrace, writer: *Writer, tty_c |
| 703 | }, | 708 | }, |
| 704 | }; | 709 | }; |
| 705 | const captured_frames = @min(n_frames, st.instruction_addresses.len); | 710 | const captured_frames = @min(n_frames, st.instruction_addresses.len); |
| 706 | for (st.instruction_addresses[0..captured_frames]) |return_address| { | 711 | for (st.instruction_addresses[0..captured_frames]) |pc_addr| { |
| 707 | try printSourceAtAddress(di_gpa, di, writer, return_address -| 1, tty_config); | 712 | try printSourceAtAddress(di_gpa, di, writer, pc_addr, tty_config); |
| 708 | } | 713 | } |
| 709 | if (n_frames > captured_frames) { | 714 | if (n_frames > captured_frames) { |
| 710 | tty_config.setColor(writer, .bold) catch {}; | 715 | tty_config.setColor(writer, .bold) catch {}; |
| ... | @@ -725,6 +730,8 @@ pub fn dumpStackTrace(st: *const std.builtin.StackTrace) void { | ... | @@ -725,6 +730,8 @@ pub fn dumpStackTrace(st: *const std.builtin.StackTrace) void { |
| 725 | const StackIterator = union(enum) { | 730 | const StackIterator = union(enum) { |
| 726 | /// Unwinding using debug info (e.g. DWARF CFI). | 731 | /// Unwinding using debug info (e.g. DWARF CFI). |
| 727 | di: if (SelfInfo.supports_unwinding) SelfInfo.UnwindContext else noreturn, | 732 | di: if (SelfInfo.supports_unwinding) SelfInfo.UnwindContext else noreturn, |
| 733 | /// We will first report the *current* PC of this `UnwindContext`, then we will switch to `di`. | ||
| 734 | di_first: if (SelfInfo.supports_unwinding) SelfInfo.UnwindContext else noreturn, | ||
| 728 | /// Naive frame-pointer-based unwinding. Very simple, but typically unreliable. | 735 | /// Naive frame-pointer-based unwinding. Very simple, but typically unreliable. |
| 729 | fp: usize, | 736 | fp: usize, |
| 730 | 737 | ||
| ... | @@ -742,9 +749,12 @@ const StackIterator = union(enum) { | ... | @@ -742,9 +749,12 @@ const StackIterator = union(enum) { |
| 742 | } | 749 | } |
| 743 | if (opt_context_ptr) |context_ptr| { | 750 | if (opt_context_ptr) |context_ptr| { |
| 744 | if (!SelfInfo.supports_unwinding) return error.CannotUnwindFromContext; | 751 | if (!SelfInfo.supports_unwinding) return error.CannotUnwindFromContext; |
| 745 | return .{ .di = .init(context_ptr) }; | 752 | // Use `di_first` here so we report the PC in the context before unwinding any further. |
| 753 | return .{ .di_first = .init(context_ptr) }; | ||
| 746 | } | 754 | } |
| 747 | if (SelfInfo.supports_unwinding and cpu_context.Native != noreturn) { | 755 | if (SelfInfo.supports_unwinding and cpu_context.Native != noreturn) { |
| 756 | // We don't need `di_first` here, because our PC is in `std.debug`; we're only interested | ||
| 757 | // in our caller's frame and above. | ||
| 748 | return .{ .di = .init(&.current()) }; | 758 | return .{ .di = .init(&.current()) }; |
| 749 | } | 759 | } |
| 750 | return .{ .fp = @frameAddress() }; | 760 | return .{ .fp = @frameAddress() }; |
| ... | @@ -752,7 +762,7 @@ const StackIterator = union(enum) { | ... | @@ -752,7 +762,7 @@ const StackIterator = union(enum) { |
| 752 | fn deinit(si: *StackIterator) void { | 762 | fn deinit(si: *StackIterator) void { |
| 753 | switch (si.*) { | 763 | switch (si.*) { |
| 754 | .fp => {}, | 764 | .fp => {}, |
| 755 | .di => |*unwind_context| unwind_context.deinit(getDebugInfoAllocator()), | 765 | .di, .di_first => |*unwind_context| unwind_context.deinit(getDebugInfoAllocator()), |
| 756 | } | 766 | } |
| 757 | } | 767 | } |
| 758 | 768 | ||
| ... | @@ -763,7 +773,7 @@ const StackIterator = union(enum) { | ... | @@ -763,7 +773,7 @@ const StackIterator = union(enum) { |
| 763 | /// Whether the current unwind strategy is allowed given `allow_unsafe`. | 773 | /// Whether the current unwind strategy is allowed given `allow_unsafe`. |
| 764 | fn stratOk(it: *const StackIterator, allow_unsafe: bool) bool { | 774 | fn stratOk(it: *const StackIterator, allow_unsafe: bool) bool { |
| 765 | return switch (it.*) { | 775 | return switch (it.*) { |
| 766 | .di => true, | 776 | .di, .di_first => true, |
| 767 | // If we omitted frame pointers from *this* compilation, FP unwinding would crash | 777 | // If we omitted frame pointers from *this* compilation, FP unwinding would crash |
| 768 | // immediately regardless of anything. But FPs could also be omitted from a different | 778 | // immediately regardless of anything. But FPs could also be omitted from a different |
| 769 | // linked object, so it's not guaranteed to be safe, unless the target specifically | 779 | // linked object, so it's not guaranteed to be safe, unless the target specifically |
| ... | @@ -773,11 +783,11 @@ const StackIterator = union(enum) { | ... | @@ -773,11 +783,11 @@ const StackIterator = union(enum) { |
| 773 | } | 783 | } |
| 774 | 784 | ||
| 775 | const Result = union(enum) { | 785 | const Result = union(enum) { |
| 776 | /// A stack frame has been found; this is the corresponding return address. | 786 | /// A stack frame has been found; this is the corresponding program counter address. |
| 777 | frame: usize, | 787 | frame: usize, |
| 778 | /// The end of the stack has been reached. | 788 | /// The end of the stack has been reached. |
| 779 | end, | 789 | end, |
| 780 | /// We were using the `.di` strategy, but are now switching to `.fp` due to this error. | 790 | /// We were using `SelfInfo.UnwindInfo`, but are now switching to FP unwinding due to this error. |
| 781 | switch_to_fp: struct { | 791 | switch_to_fp: struct { |
| 782 | address: usize, | 792 | address: usize, |
| 783 | err: SelfInfo.Error, | 793 | err: SelfInfo.Error, |
| ... | @@ -785,20 +795,25 @@ const StackIterator = union(enum) { | ... | @@ -785,20 +795,25 @@ const StackIterator = union(enum) { |
| 785 | }; | 795 | }; |
| 786 | fn next(it: *StackIterator) Result { | 796 | fn next(it: *StackIterator) Result { |
| 787 | switch (it.*) { | 797 | switch (it.*) { |
| 798 | .di_first => |unwind_context| { | ||
| 799 | const first_pc = unwind_context.pc; | ||
| 800 | if (first_pc == 0) return .end; | ||
| 801 | it.* = .{ .di = unwind_context }; | ||
| 802 | return .{ .frame = first_pc }; | ||
| 803 | }, | ||
| 788 | .di => |*unwind_context| { | 804 | .di => |*unwind_context| { |
| 789 | const di = getSelfDebugInfo() catch unreachable; | 805 | const di = getSelfDebugInfo() catch unreachable; |
| 790 | const di_gpa = getDebugInfoAllocator(); | 806 | const di_gpa = getDebugInfoAllocator(); |
| 791 | if (di.unwindFrame(di_gpa, unwind_context)) |ra| { | 807 | di.unwindFrame(di_gpa, unwind_context) catch |err| { |
| 792 | if (ra <= 1) return .end; | ||
| 793 | return .{ .frame = ra }; | ||
| 794 | } else |err| { | ||
| 795 | const pc = unwind_context.pc; | 808 | const pc = unwind_context.pc; |
| 796 | it.* = .{ .fp = unwind_context.getFp() }; | 809 | it.* = .{ .fp = unwind_context.getFp() }; |
| 797 | return .{ .switch_to_fp = .{ | 810 | return .{ .switch_to_fp = .{ |
| 798 | .address = pc, | 811 | .address = pc, |
| 799 | .err = err, | 812 | .err = err, |
| 800 | } }; | 813 | } }; |
| 801 | } | 814 | }; |
| 815 | const pc = unwind_context.pc; | ||
| 816 | return if (pc == 0) .end else .{ .frame = pc }; | ||
| 802 | }, | 817 | }, |
| 803 | .fp => |fp| { | 818 | .fp => |fp| { |
| 804 | if (fp == 0) return .end; // we reached the "sentinel" base pointer | 819 | if (fp == 0) return .end; // we reached the "sentinel" base pointer |
| ... | @@ -824,9 +839,9 @@ const StackIterator = union(enum) { | ... | @@ -824,9 +839,9 @@ const StackIterator = union(enum) { |
| 824 | if (bp != 0 and bp <= fp) return .end; | 839 | if (bp != 0 and bp <= fp) return .end; |
| 825 | 840 | ||
| 826 | it.fp = bp; | 841 | it.fp = bp; |
| 827 | const ra = ra_ptr.*; | 842 | const ra = stripInstructionPtrAuthCode(ra_ptr.*); |
| 828 | if (ra <= 1) return .end; | 843 | if (ra <= 1) return .end; |
| 829 | return .{ .frame = ra }; | 844 | return .{ .frame = ra - 1 }; |
| 830 | }, | 845 | }, |
| 831 | } | 846 | } |
| 832 | } | 847 | } |
| ... | @@ -860,6 +875,26 @@ const StackIterator = union(enum) { | ... | @@ -860,6 +875,26 @@ const StackIterator = union(enum) { |
| 860 | } | 875 | } |
| 861 | }; | 876 | }; |
| 862 | 877 | ||
| 878 | /// Some platforms use pointer authentication: the upper bits of instruction pointers contain a | ||
| 879 | /// signature. This function clears those signature bits to make the pointer directly usable. | ||
| 880 | pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize { | ||
| 881 | if (native_arch.isAARCH64()) { | ||
| 882 | // `hint 0x07` maps to `xpaclri` (or `nop` if the hardware doesn't support it) | ||
| 883 | // The save / restore is because `xpaclri` operates on x30 (LR) | ||
| 884 | return asm ( | ||
| 885 | \\mov x16, x30 | ||
| 886 | \\mov x30, x15 | ||
| 887 | \\hint 0x07 | ||
| 888 | \\mov x15, x30 | ||
| 889 | \\mov x30, x16 | ||
| 890 | : [ret] "={x15}" (-> usize), | ||
| 891 | : [ptr] "{x15}" (ptr), | ||
| 892 | : .{ .x16 = true }); | ||
| 893 | } | ||
| 894 | |||
| 895 | return ptr; | ||
| 896 | } | ||
| 897 | |||
| 863 | fn printSourceAtAddress(gpa: Allocator, debug_info: *SelfInfo, writer: *Writer, address: usize, tty_config: tty.Config) Writer.Error!void { | 898 | fn printSourceAtAddress(gpa: Allocator, debug_info: *SelfInfo, writer: *Writer, address: usize, tty_config: tty.Config) Writer.Error!void { |
| 864 | const symbol: Symbol = debug_info.getSymbolAtAddress(gpa, address) catch |err| switch (err) { | 899 | const symbol: Symbol = debug_info.getSymbolAtAddress(gpa, address) catch |err| switch (err) { |
| 865 | error.MissingDebugInfo, | 900 | error.MissingDebugInfo, |
lib/std/debug/SelfInfo.zig+18-36| ... | @@ -2,7 +2,6 @@ | ... | @@ -2,7 +2,6 @@ |
| 2 | //! goal of minimal code bloat and compilation speed penalty. | 2 | //! goal of minimal code bloat and compilation speed penalty. |
| 3 | 3 | ||
| 4 | const builtin = @import("builtin"); | 4 | const builtin = @import("builtin"); |
| 5 | const native_os = builtin.os.tag; | ||
| 6 | const native_endian = native_arch.endian(); | 5 | const native_endian = native_arch.endian(); |
| 7 | const native_arch = builtin.cpu.arch; | 6 | const native_arch = builtin.cpu.arch; |
| 8 | 7 | ||
| ... | @@ -13,6 +12,8 @@ const assert = std.debug.assert; | ... | @@ -13,6 +12,8 @@ const assert = std.debug.assert; |
| 13 | const Dwarf = std.debug.Dwarf; | 12 | const Dwarf = std.debug.Dwarf; |
| 14 | const CpuContext = std.debug.cpu_context.Native; | 13 | const CpuContext = std.debug.cpu_context.Native; |
| 15 | 14 | ||
| 15 | const stripInstructionPtrAuthCode = std.debug.stripInstructionPtrAuthCode; | ||
| 16 | |||
| 16 | const root = @import("root"); | 17 | const root = @import("root"); |
| 17 | 18 | ||
| 18 | const SelfInfo = @This(); | 19 | const SelfInfo = @This(); |
| ... | @@ -52,7 +53,7 @@ pub fn deinit(self: *SelfInfo, gpa: Allocator) void { | ... | @@ -52,7 +53,7 @@ pub fn deinit(self: *SelfInfo, gpa: Allocator) void { |
| 52 | if (Module.LookupCache != void) self.lookup_cache.deinit(gpa); | 53 | if (Module.LookupCache != void) self.lookup_cache.deinit(gpa); |
| 53 | } | 54 | } |
| 54 | 55 | ||
| 55 | pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) Error!usize { | 56 | pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) Error!void { |
| 56 | comptime assert(supports_unwinding); | 57 | comptime assert(supports_unwinding); |
| 57 | const module: Module = try .lookup(&self.lookup_cache, gpa, context.pc); | 58 | const module: Module = try .lookup(&self.lookup_cache, gpa, context.pc); |
| 58 | const gop = try self.modules.getOrPut(gpa, module.key()); | 59 | const gop = try self.modules.getOrPut(gpa, module.key()); |
| ... | @@ -115,7 +116,7 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) | ... | @@ -115,7 +116,7 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) |
| 115 | /// pub const supports_unwinding: bool; | 116 | /// pub const supports_unwinding: bool; |
| 116 | /// /// Only required if `supports_unwinding == true`. | 117 | /// /// Only required if `supports_unwinding == true`. |
| 117 | /// pub const UnwindContext = struct { | 118 | /// pub const UnwindContext = struct { |
| 118 | /// /// A PC value inside the function of the last unwound frame. | 119 | /// /// A PC value representing the location in the last frame. |
| 119 | /// pc: usize, | 120 | /// pc: usize, |
| 120 | /// pub fn init(ctx: *std.debug.cpu_context.Native, gpa: Allocator) Allocator.Error!UnwindContext; | 121 | /// pub fn init(ctx: *std.debug.cpu_context.Native, gpa: Allocator) Allocator.Error!UnwindContext; |
| 121 | /// pub fn deinit(uc: *UnwindContext, gpa: Allocator) void; | 122 | /// pub fn deinit(uc: *UnwindContext, gpa: Allocator) void; |
| ... | @@ -123,21 +124,22 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) | ... | @@ -123,21 +124,22 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) |
| 123 | /// /// pointer is unknown, 0 may be returned instead. | 124 | /// /// pointer is unknown, 0 may be returned instead. |
| 124 | /// pub fn getFp(uc: *UnwindContext) usize; | 125 | /// pub fn getFp(uc: *UnwindContext) usize; |
| 125 | /// }; | 126 | /// }; |
| 126 | /// /// Only required if `supports_unwinding == true`. Unwinds a single stack frame and returns | 127 | /// /// Only required if `supports_unwinding == true`. Unwinds a single stack frame. |
| 127 | /// /// the next return address (which may be 0 indicating end of stack). | 128 | /// /// The caller will read the new instruction poiter from the `pc` field. |
| 129 | /// /// `pc = 0` indicates end of stack / no more frames. | ||
| 128 | /// pub fn unwindFrame( | 130 | /// pub fn unwindFrame( |
| 129 | /// mod: *const Module, | 131 | /// mod: *const Module, |
| 130 | /// gpa: Allocator, | 132 | /// gpa: Allocator, |
| 131 | /// di: *DebugInfo, | 133 | /// di: *DebugInfo, |
| 132 | /// ctx: *UnwindContext, | 134 | /// ctx: *UnwindContext, |
| 133 | /// ) SelfInfo.Error!usize; | 135 | /// ) SelfInfo.Error!void; |
| 134 | /// ``` | 136 | /// ``` |
| 135 | const Module: type = Module: { | 137 | const Module: type = Module: { |
| 136 | // Allow overriding the target-specific `SelfInfo` implementation by exposing `root.debug.Module`. | 138 | // Allow overriding the target-specific `SelfInfo` implementation by exposing `root.debug.Module`. |
| 137 | if (@hasDecl(root, "debug") and @hasDecl(root.debug, "Module")) { | 139 | if (@hasDecl(root, "debug") and @hasDecl(root.debug, "Module")) { |
| 138 | break :Module root.debug.Module; | 140 | break :Module root.debug.Module; |
| 139 | } | 141 | } |
| 140 | break :Module switch (native_os) { | 142 | break :Module switch (builtin.os.tag) { |
| 141 | .linux, | 143 | .linux, |
| 142 | .netbsd, | 144 | .netbsd, |
| 143 | .freebsd, | 145 | .freebsd, |
| ... | @@ -222,7 +224,7 @@ pub const DwarfUnwindContext = struct { | ... | @@ -222,7 +224,7 @@ pub const DwarfUnwindContext = struct { |
| 222 | const register = col.register orelse return error.InvalidRegister; | 224 | const register = col.register orelse return error.InvalidRegister; |
| 223 | // The default type is usually undefined, but can be overriden by ABI authors. | 225 | // The default type is usually undefined, but can be overriden by ABI authors. |
| 224 | // See the doc comment on `Dwarf.Unwind.VirtualMachine.RegisterRule.default`. | 226 | // See the doc comment on `Dwarf.Unwind.VirtualMachine.RegisterRule.default`. |
| 225 | if (builtin.cpu.arch.isAARCH64() and register >= 19 and register <= 18) { | 227 | if (builtin.cpu.arch.isAARCH64() and register >= 19 and register <= 28) { |
| 226 | // Callee-saved registers are initialized as if they had the .same_value rule | 228 | // Callee-saved registers are initialized as if they had the .same_value rule |
| 227 | const src = try context.cpu_context.dwarfRegisterBytes(register); | 229 | const src = try context.cpu_context.dwarfRegisterBytes(register); |
| 228 | if (src.len != out.len) return error.RegisterSizeMismatch; | 230 | if (src.len != out.len) return error.RegisterSizeMismatch; |
| ... | @@ -310,7 +312,7 @@ pub const DwarfUnwindContext = struct { | ... | @@ -310,7 +312,7 @@ pub const DwarfUnwindContext = struct { |
| 310 | unwind: *const Dwarf.Unwind, | 312 | unwind: *const Dwarf.Unwind, |
| 311 | load_offset: usize, | 313 | load_offset: usize, |
| 312 | explicit_fde_offset: ?usize, | 314 | explicit_fde_offset: ?usize, |
| 313 | ) Error!usize { | 315 | ) Error!void { |
| 314 | return unwindFrameInner(context, gpa, unwind, load_offset, explicit_fde_offset) catch |err| switch (err) { | 316 | return unwindFrameInner(context, gpa, unwind, load_offset, explicit_fde_offset) catch |err| switch (err) { |
| 315 | error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e, | 317 | error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e, |
| 316 | 318 | ||
| ... | @@ -358,9 +360,10 @@ pub const DwarfUnwindContext = struct { | ... | @@ -358,9 +360,10 @@ pub const DwarfUnwindContext = struct { |
| 358 | unwind: *const Dwarf.Unwind, | 360 | unwind: *const Dwarf.Unwind, |
| 359 | load_offset: usize, | 361 | load_offset: usize, |
| 360 | explicit_fde_offset: ?usize, | 362 | explicit_fde_offset: ?usize, |
| 361 | ) !usize { | 363 | ) !void { |
| 362 | if (!supports_unwinding) return error.UnsupportedCpuArchitecture; | 364 | comptime assert(supports_unwinding); |
| 363 | if (context.pc == 0) return 0; | 365 | |
| 366 | if (context.pc == 0) return; | ||
| 364 | 367 | ||
| 365 | const pc_vaddr = context.pc - load_offset; | 368 | const pc_vaddr = context.pc - load_offset; |
| 366 | 369 | ||
| ... | @@ -430,12 +433,12 @@ pub const DwarfUnwindContext = struct { | ... | @@ -430,12 +433,12 @@ pub const DwarfUnwindContext = struct { |
| 430 | } | 433 | } |
| 431 | } | 434 | } |
| 432 | 435 | ||
| 433 | const return_address: u64 = if (has_return_address) pc: { | 436 | const return_address: usize = if (has_return_address) pc: { |
| 434 | const raw_ptr = try regNative(&new_cpu_context, cie.return_address_register); | 437 | const raw_ptr = try regNative(&new_cpu_context, cie.return_address_register); |
| 435 | break :pc stripInstructionPtrAuthCode(raw_ptr.*); | 438 | break :pc stripInstructionPtrAuthCode(raw_ptr.*); |
| 436 | } else 0; | 439 | } else 0; |
| 437 | 440 | ||
| 438 | (try regNative(new_cpu_context, ip_reg_num)).* = return_address; | 441 | (try regNative(&new_cpu_context, ip_reg_num)).* = return_address; |
| 439 | 442 | ||
| 440 | // The new CPU context is complete; flush changes. | 443 | // The new CPU context is complete; flush changes. |
| 441 | context.cpu_context = new_cpu_context; | 444 | context.cpu_context = new_cpu_context; |
| ... | @@ -444,11 +447,9 @@ pub const DwarfUnwindContext = struct { | ... | @@ -444,11 +447,9 @@ pub const DwarfUnwindContext = struct { |
| 444 | // *after* the call, it could (in the case of noreturn functions) actually point outside of | 447 | // *after* the call, it could (in the case of noreturn functions) actually point outside of |
| 445 | // the caller's address range, meaning an FDE lookup would fail. We can handle this by | 448 | // the caller's address range, meaning an FDE lookup would fail. We can handle this by |
| 446 | // subtracting 1 from `return_address` so that the next lookup is guaranteed to land inside | 449 | // subtracting 1 from `return_address` so that the next lookup is guaranteed to land inside |
| 447 | // the `call` instruction`. The exception to this rule is signal frames, where the return | 450 | // the `call` instruction. The exception to this rule is signal frames, where the return |
| 448 | // address is the same instruction that triggered the handler. | 451 | // address is the same instruction that triggered the handler. |
| 449 | context.pc = if (cie.is_signal_frame) return_address else return_address -| 1; | 452 | context.pc = if (cie.is_signal_frame) return_address else return_address -| 1; |
| 450 | |||
| 451 | return return_address; | ||
| 452 | } | 453 | } |
| 453 | /// Since register rules are applied (usually) during a panic, | 454 | /// Since register rules are applied (usually) during a panic, |
| 454 | /// checked addition / subtraction is used so that we can return | 455 | /// checked addition / subtraction is used so that we can return |
| ... | @@ -459,25 +460,6 @@ pub const DwarfUnwindContext = struct { | ... | @@ -459,25 +460,6 @@ pub const DwarfUnwindContext = struct { |
| 459 | else | 460 | else |
| 460 | try std.math.sub(usize, base, @as(usize, @intCast(-offset))); | 461 | try std.math.sub(usize, base, @as(usize, @intCast(-offset))); |
| 461 | } | 462 | } |
| 462 | /// Some platforms use pointer authentication - the upper bits of instruction pointers contain a signature. | ||
| 463 | /// This function clears these signature bits to make the pointer usable. | ||
| 464 | pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize { | ||
| 465 | if (native_arch.isAARCH64()) { | ||
| 466 | // `hint 0x07` maps to `xpaclri` (or `nop` if the hardware doesn't support it) | ||
| 467 | // The save / restore is because `xpaclri` operates on x30 (LR) | ||
| 468 | return asm ( | ||
| 469 | \\mov x16, x30 | ||
| 470 | \\mov x30, x15 | ||
| 471 | \\hint 0x07 | ||
| 472 | \\mov x15, x30 | ||
| 473 | \\mov x30, x16 | ||
| 474 | : [ret] "={x15}" (-> usize), | ||
| 475 | : [ptr] "{x15}" (ptr), | ||
| 476 | : .{ .x16 = true }); | ||
| 477 | } | ||
| 478 | |||
| 479 | return ptr; | ||
| 480 | } | ||
| 481 | 463 | ||
| 482 | pub fn regNative(ctx: *CpuContext, num: u16) error{ | 464 | pub fn regNative(ctx: *CpuContext, num: u16) error{ |
| 483 | InvalidRegister, | 465 | InvalidRegister, |
lib/std/debug/SelfInfo/DarwinModule.zig+153-71| ... | @@ -1,6 +1,5 @@ | ... | @@ -1,6 +1,5 @@ |
| 1 | /// The runtime address where __TEXT is loaded. | 1 | /// The runtime address where __TEXT is loaded. |
| 2 | text_base: usize, | 2 | text_base: usize, |
| 3 | load_offset: usize, | ||
| 4 | name: []const u8, | 3 | name: []const u8, |
| 5 | 4 | ||
| 6 | pub fn key(m: *const DarwinModule) usize { | 5 | pub fn key(m: *const DarwinModule) usize { |
| ... | @@ -12,38 +11,14 @@ pub const LookupCache = void; | ... | @@ -12,38 +11,14 @@ pub const LookupCache = void; |
| 12 | pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) Error!DarwinModule { | 11 | pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) Error!DarwinModule { |
| 13 | _ = cache; | 12 | _ = cache; |
| 14 | _ = gpa; | 13 | _ = gpa; |
| 15 | const image_count = std.c._dyld_image_count(); | 14 | var info: std.c.dl_info = undefined; |
| 16 | for (0..image_count) |image_idx| { | 15 | switch (std.c.dladdr(@ptrFromInt(address), &info)) { |
| 17 | const header = std.c._dyld_get_image_header(@intCast(image_idx)) orelse continue; | 16 | 0 => return error.MissingDebugInfo, |
| 18 | const text_base = @intFromPtr(header); | 17 | else => return .{ |
| 19 | if (address < text_base) continue; | 18 | .name = std.mem.span(info.fname), |
| 20 | const load_offset = std.c._dyld_get_image_vmaddr_slide(@intCast(image_idx)); | 19 | .text_base = @intFromPtr(info.fbase), |
| 21 | 20 | }, | |
| 22 | // Find the __TEXT segment | ||
| 23 | var it: macho.LoadCommandIterator = .{ | ||
| 24 | .ncmds = header.ncmds, | ||
| 25 | .buffer = @as([*]u8, @ptrCast(header))[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds], | ||
| 26 | }; | ||
| 27 | const text_segment_cmd = while (it.next()) |load_cmd| { | ||
| 28 | if (load_cmd.cmd() != .SEGMENT_64) continue; | ||
| 29 | const segment_cmd = load_cmd.cast(macho.segment_command_64).?; | ||
| 30 | if (!mem.eql(u8, segment_cmd.segName(), "__TEXT")) continue; | ||
| 31 | break segment_cmd; | ||
| 32 | } else continue; | ||
| 33 | |||
| 34 | const seg_start = load_offset + text_segment_cmd.vmaddr; | ||
| 35 | assert(seg_start == text_base); | ||
| 36 | const seg_end = seg_start + text_segment_cmd.vmsize; | ||
| 37 | if (address < seg_start or address >= seg_end) continue; | ||
| 38 | |||
| 39 | // We've found the matching __TEXT segment. This is the image we need. | ||
| 40 | return .{ | ||
| 41 | .text_base = text_base, | ||
| 42 | .load_offset = load_offset, | ||
| 43 | .name = mem.span(std.c._dyld_get_image_name(@intCast(image_idx))), | ||
| 44 | }; | ||
| 45 | } | 21 | } |
| 46 | return error.MissingDebugInfo; | ||
| 47 | } | 22 | } |
| 48 | fn loadUnwindInfo(module: *const DarwinModule) DebugInfo.Unwind { | 23 | fn loadUnwindInfo(module: *const DarwinModule) DebugInfo.Unwind { |
| 49 | const header: *std.macho.mach_header = @ptrFromInt(module.text_base); | 24 | const header: *std.macho.mach_header = @ptrFromInt(module.text_base); |
| ... | @@ -52,56 +27,115 @@ fn loadUnwindInfo(module: *const DarwinModule) DebugInfo.Unwind { | ... | @@ -52,56 +27,115 @@ fn loadUnwindInfo(module: *const DarwinModule) DebugInfo.Unwind { |
| 52 | .ncmds = header.ncmds, | 27 | .ncmds = header.ncmds, |
| 53 | .buffer = @as([*]u8, @ptrCast(header))[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds], | 28 | .buffer = @as([*]u8, @ptrCast(header))[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds], |
| 54 | }; | 29 | }; |
| 55 | const sections = while (it.next()) |load_cmd| { | 30 | const sections, const text_vmaddr = while (it.next()) |load_cmd| { |
| 56 | if (load_cmd.cmd() != .SEGMENT_64) continue; | 31 | if (load_cmd.cmd() != .SEGMENT_64) continue; |
| 57 | const segment_cmd = load_cmd.cast(macho.segment_command_64).?; | 32 | const segment_cmd = load_cmd.cast(macho.segment_command_64).?; |
| 58 | if (!mem.eql(u8, segment_cmd.segName(), "__TEXT")) continue; | 33 | if (!mem.eql(u8, segment_cmd.segName(), "__TEXT")) continue; |
| 59 | break load_cmd.getSections(); | 34 | break .{ load_cmd.getSections(), segment_cmd.vmaddr }; |
| 60 | } else unreachable; | 35 | } else unreachable; |
| 61 | 36 | ||
| 37 | const vmaddr_slide = module.text_base - text_vmaddr; | ||
| 38 | |||
| 62 | var unwind_info: ?[]const u8 = null; | 39 | var unwind_info: ?[]const u8 = null; |
| 63 | var eh_frame: ?[]const u8 = null; | 40 | var eh_frame: ?[]const u8 = null; |
| 64 | for (sections) |sect| { | 41 | for (sections) |sect| { |
| 65 | if (mem.eql(u8, sect.sectName(), "__unwind_info")) { | 42 | if (mem.eql(u8, sect.sectName(), "__unwind_info")) { |
| 66 | const sect_ptr: [*]u8 = @ptrFromInt(@as(usize, @intCast(module.load_offset + sect.addr))); | 43 | const sect_ptr: [*]u8 = @ptrFromInt(@as(usize, @intCast(vmaddr_slide + sect.addr))); |
| 67 | unwind_info = sect_ptr[0..@intCast(sect.size)]; | 44 | unwind_info = sect_ptr[0..@intCast(sect.size)]; |
| 68 | } else if (mem.eql(u8, sect.sectName(), "__eh_frame")) { | 45 | } else if (mem.eql(u8, sect.sectName(), "__eh_frame")) { |
| 69 | const sect_ptr: [*]u8 = @ptrFromInt(@as(usize, @intCast(module.load_offset + sect.addr))); | 46 | const sect_ptr: [*]u8 = @ptrFromInt(@as(usize, @intCast(vmaddr_slide + sect.addr))); |
| 70 | eh_frame = sect_ptr[0..@intCast(sect.size)]; | 47 | eh_frame = sect_ptr[0..@intCast(sect.size)]; |
| 71 | } | 48 | } |
| 72 | } | 49 | } |
| 73 | return .{ | 50 | return .{ |
| 51 | .vmaddr_slide = vmaddr_slide, | ||
| 74 | .unwind_info = unwind_info, | 52 | .unwind_info = unwind_info, |
| 75 | .eh_frame = eh_frame, | 53 | .eh_frame = eh_frame, |
| 76 | }; | 54 | }; |
| 77 | } | 55 | } |
| 78 | fn loadMachO(module: *const DarwinModule, gpa: Allocator) !DebugInfo.LoadedMachO { | 56 | fn loadMachO(module: *const DarwinModule, gpa: Allocator) !DebugInfo.LoadedMachO { |
| 79 | const mapped_mem = try mapDebugInfoFile(module.name); | 57 | const all_mapped_memory = try mapDebugInfoFile(module.name); |
| 80 | errdefer posix.munmap(mapped_mem); | 58 | errdefer posix.munmap(all_mapped_memory); |
| 59 | |||
| 60 | // In most cases, the file we just mapped is a Mach-O binary. However, it could be a "universal | ||
| 61 | // binary": a simple file format which contains Mach-O binaries for multiple targets. For | ||
| 62 | // instance, `/usr/lib/dyld` is currently distributed as a universal binary containing images | ||
| 63 | // for both ARM64 Macs and x86_64 Macs. | ||
| 64 | if (all_mapped_memory.len < 4) return error.InvalidDebugInfo; | ||
| 65 | const magic = @as(*const u32, @ptrCast(all_mapped_memory.ptr)).*; | ||
| 66 | // The contents of a Mach-O file, which may or may not be the whole of `all_mapped_memory`. | ||
| 67 | const mapped_macho = switch (magic) { | ||
| 68 | macho.MH_MAGIC_64 => all_mapped_memory, | ||
| 69 | |||
| 70 | macho.FAT_CIGAM => mapped_macho: { | ||
| 71 | // This is the universal binary format (aka a "fat binary"). Annoyingly, the whole thing | ||
| 72 | // is big-endian, so we'll be swapping some bytes. | ||
| 73 | if (all_mapped_memory.len < @sizeOf(macho.fat_header)) return error.InvalidDebugInfo; | ||
| 74 | const hdr: *const macho.fat_header = @ptrCast(all_mapped_memory.ptr); | ||
| 75 | const archs_ptr: [*]const macho.fat_arch = @ptrCast(all_mapped_memory.ptr + @sizeOf(macho.fat_header)); | ||
| 76 | const archs: []const macho.fat_arch = archs_ptr[0..@byteSwap(hdr.nfat_arch)]; | ||
| 77 | const native_cpu_type = switch (builtin.cpu.arch) { | ||
| 78 | .x86_64 => macho.CPU_TYPE_X86_64, | ||
| 79 | .aarch64 => macho.CPU_TYPE_ARM64, | ||
| 80 | else => comptime unreachable, | ||
| 81 | }; | ||
| 82 | for (archs) |*arch| { | ||
| 83 | if (@byteSwap(arch.cputype) != native_cpu_type) continue; | ||
| 84 | const offset = @byteSwap(arch.offset); | ||
| 85 | const size = @byteSwap(arch.size); | ||
| 86 | break :mapped_macho all_mapped_memory[offset..][0..size]; | ||
| 87 | } | ||
| 88 | // Our native architecture was not present in the fat binary. | ||
| 89 | return error.MissingDebugInfo; | ||
| 90 | }, | ||
| 91 | |||
| 92 | // Even on modern 64-bit targets, this format doesn't seem to be too extensively used. It | ||
| 93 | // will be fairly easy to add support here if necessary; it's very similar to above. | ||
| 94 | macho.FAT_CIGAM_64 => return error.UnsupportedDebugInfo, | ||
| 81 | 95 | ||
| 82 | const hdr: *const macho.mach_header_64 = @ptrCast(@alignCast(mapped_mem.ptr)); | 96 | else => return error.InvalidDebugInfo, |
| 97 | }; | ||
| 98 | |||
| 99 | const hdr: *const macho.mach_header_64 = @ptrCast(@alignCast(mapped_macho.ptr)); | ||
| 83 | if (hdr.magic != macho.MH_MAGIC_64) | 100 | if (hdr.magic != macho.MH_MAGIC_64) |
| 84 | return error.InvalidDebugInfo; | 101 | return error.InvalidDebugInfo; |
| 85 | 102 | ||
| 86 | const symtab: macho.symtab_command = symtab: { | 103 | const symtab: macho.symtab_command, const text_vmaddr: u64 = lc_iter: { |
| 87 | var it: macho.LoadCommandIterator = .{ | 104 | var it: macho.LoadCommandIterator = .{ |
| 88 | .ncmds = hdr.ncmds, | 105 | .ncmds = hdr.ncmds, |
| 89 | .buffer = mapped_mem[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds], | 106 | .buffer = mapped_macho[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds], |
| 90 | }; | 107 | }; |
| 108 | var symtab: ?macho.symtab_command = null; | ||
| 109 | var text_vmaddr: ?u64 = null; | ||
| 91 | while (it.next()) |cmd| switch (cmd.cmd()) { | 110 | while (it.next()) |cmd| switch (cmd.cmd()) { |
| 92 | .SYMTAB => break :symtab cmd.cast(macho.symtab_command) orelse return error.InvalidDebugInfo, | 111 | .SYMTAB => symtab = cmd.cast(macho.symtab_command) orelse return error.InvalidDebugInfo, |
| 112 | .SEGMENT_64 => if (cmd.cast(macho.segment_command_64)) |seg_cmd| { | ||
| 113 | if (!mem.eql(u8, seg_cmd.segName(), "__TEXT")) continue; | ||
| 114 | text_vmaddr = seg_cmd.vmaddr; | ||
| 115 | }, | ||
| 93 | else => {}, | 116 | else => {}, |
| 94 | }; | 117 | }; |
| 95 | return error.MissingDebugInfo; | 118 | break :lc_iter .{ |
| 119 | symtab orelse return error.MissingDebugInfo, | ||
| 120 | text_vmaddr orelse return error.MissingDebugInfo, | ||
| 121 | }; | ||
| 96 | }; | 122 | }; |
| 97 | 123 | ||
| 98 | const syms_ptr: [*]align(1) const macho.nlist_64 = @ptrCast(mapped_mem[symtab.symoff..]); | 124 | const syms_ptr: [*]align(1) const macho.nlist_64 = @ptrCast(mapped_macho[symtab.symoff..]); |
| 99 | const syms = syms_ptr[0..symtab.nsyms]; | 125 | const syms = syms_ptr[0..symtab.nsyms]; |
| 100 | const strings = mapped_mem[symtab.stroff..][0 .. symtab.strsize - 1 :0]; | 126 | const strings = mapped_macho[symtab.stroff..][0 .. symtab.strsize - 1]; |
| 101 | 127 | ||
| 102 | var symbols: std.ArrayList(MachoSymbol) = try .initCapacity(gpa, syms.len); | 128 | var symbols: std.ArrayList(MachoSymbol) = try .initCapacity(gpa, syms.len); |
| 103 | defer symbols.deinit(gpa); | 129 | defer symbols.deinit(gpa); |
| 104 | 130 | ||
| 131 | // This map is temporary; it is used only to detect duplicates here. This is | ||
| 132 | // necessary because we prefer to use STAB ("symbolic debugging table") symbols, | ||
| 133 | // but they might not be present, so we track normal symbols too. | ||
| 134 | // Indices match 1-1 with those of `symbols`. | ||
| 135 | var symbol_names: std.StringArrayHashMapUnmanaged(void) = .empty; | ||
| 136 | defer symbol_names.deinit(gpa); | ||
| 137 | try symbol_names.ensureUnusedCapacity(gpa, syms.len); | ||
| 138 | |||
| 105 | var ofile: u32 = undefined; | 139 | var ofile: u32 = undefined; |
| 106 | var last_sym: MachoSymbol = undefined; | 140 | var last_sym: MachoSymbol = undefined; |
| 107 | var state: enum { | 141 | var state: enum { |
| ... | @@ -115,7 +149,25 @@ fn loadMachO(module: *const DarwinModule, gpa: Allocator) !DebugInfo.LoadedMachO | ... | @@ -115,7 +149,25 @@ fn loadMachO(module: *const DarwinModule, gpa: Allocator) !DebugInfo.LoadedMachO |
| 115 | } = .init; | 149 | } = .init; |
| 116 | 150 | ||
| 117 | for (syms) |*sym| { | 151 | for (syms) |*sym| { |
| 118 | if (sym.n_type.bits.is_stab == 0) continue; | 152 | if (sym.n_type.bits.is_stab == 0) { |
| 153 | if (sym.n_strx == 0) continue; | ||
| 154 | switch (sym.n_type.bits.type) { | ||
| 155 | .undf, .pbud, .indr, .abs, _ => continue, | ||
| 156 | .sect => { | ||
| 157 | const name = std.mem.sliceTo(strings[sym.n_strx..], 0); | ||
| 158 | const gop = symbol_names.getOrPutAssumeCapacity(name); | ||
| 159 | if (!gop.found_existing) { | ||
| 160 | assert(gop.index == symbols.items.len); | ||
| 161 | symbols.appendAssumeCapacity(.{ | ||
| 162 | .strx = sym.n_strx, | ||
| 163 | .addr = sym.n_value, | ||
| 164 | .ofile = MachoSymbol.unknown_ofile, | ||
| 165 | }); | ||
| 166 | } | ||
| 167 | }, | ||
| 168 | } | ||
| 169 | continue; | ||
| 170 | } | ||
| 119 | 171 | ||
| 120 | // TODO handle globals N_GSYM, and statics N_STSYM | 172 | // TODO handle globals N_GSYM, and statics N_STSYM |
| 121 | switch (sym.n_type.stab) { | 173 | switch (sym.n_type.stab) { |
| ... | @@ -132,7 +184,6 @@ fn loadMachO(module: *const DarwinModule, gpa: Allocator) !DebugInfo.LoadedMachO | ... | @@ -132,7 +184,6 @@ fn loadMachO(module: *const DarwinModule, gpa: Allocator) !DebugInfo.LoadedMachO |
| 132 | last_sym = .{ | 184 | last_sym = .{ |
| 133 | .strx = 0, | 185 | .strx = 0, |
| 134 | .addr = sym.n_value, | 186 | .addr = sym.n_value, |
| 135 | .size = 0, | ||
| 136 | .ofile = ofile, | 187 | .ofile = ofile, |
| 137 | }; | 188 | }; |
| 138 | }, | 189 | }, |
| ... | @@ -145,14 +196,22 @@ fn loadMachO(module: *const DarwinModule, gpa: Allocator) !DebugInfo.LoadedMachO | ... | @@ -145,14 +196,22 @@ fn loadMachO(module: *const DarwinModule, gpa: Allocator) !DebugInfo.LoadedMachO |
| 145 | }, | 196 | }, |
| 146 | .fun_strx => { | 197 | .fun_strx => { |
| 147 | state = .fun_size; | 198 | state = .fun_size; |
| 148 | last_sym.size = @intCast(sym.n_value); | ||
| 149 | }, | 199 | }, |
| 150 | else => return error.InvalidDebugInfo, | 200 | else => return error.InvalidDebugInfo, |
| 151 | }, | 201 | }, |
| 152 | .ensym => switch (state) { | 202 | .ensym => switch (state) { |
| 153 | .fun_size => { | 203 | .fun_size => { |
| 154 | state = .ensym; | 204 | state = .ensym; |
| 155 | symbols.appendAssumeCapacity(last_sym); | 205 | if (last_sym.strx != 0) { |
| 206 | const name = std.mem.sliceTo(strings[sym.n_strx..], 0); | ||
| 207 | const gop = symbol_names.getOrPutAssumeCapacity(name); | ||
| 208 | if (!gop.found_existing) { | ||
| 209 | assert(gop.index == symbols.items.len); | ||
| 210 | symbols.appendAssumeCapacity(last_sym); | ||
| 211 | } else { | ||
| 212 | symbols.items[gop.index] = last_sym; | ||
| 213 | } | ||
| 214 | } | ||
| 156 | }, | 215 | }, |
| 157 | else => return error.InvalidDebugInfo, | 216 | else => return error.InvalidDebugInfo, |
| 158 | }, | 217 | }, |
| ... | @@ -168,9 +227,12 @@ fn loadMachO(module: *const DarwinModule, gpa: Allocator) !DebugInfo.LoadedMachO | ... | @@ -168,9 +227,12 @@ fn loadMachO(module: *const DarwinModule, gpa: Allocator) !DebugInfo.LoadedMachO |
| 168 | } | 227 | } |
| 169 | 228 | ||
| 170 | switch (state) { | 229 | switch (state) { |
| 171 | .init => return error.MissingDebugInfo, | 230 | .init => { |
| 231 | // Missing STAB symtab entries is still okay, unless there were also no normal symbols. | ||
| 232 | if (symbols.items.len == 0) return error.MissingDebugInfo; | ||
| 233 | }, | ||
| 172 | .oso_close => {}, | 234 | .oso_close => {}, |
| 173 | else => return error.InvalidDebugInfo, | 235 | else => return error.InvalidDebugInfo, // corrupted STAB entries in symtab |
| 174 | } | 236 | } |
| 175 | 237 | ||
| 176 | const symbols_slice = try symbols.toOwnedSlice(gpa); | 238 | const symbols_slice = try symbols.toOwnedSlice(gpa); |
| ... | @@ -182,10 +244,11 @@ fn loadMachO(module: *const DarwinModule, gpa: Allocator) !DebugInfo.LoadedMachO | ... | @@ -182,10 +244,11 @@ fn loadMachO(module: *const DarwinModule, gpa: Allocator) !DebugInfo.LoadedMachO |
| 182 | mem.sort(MachoSymbol, symbols_slice, {}, MachoSymbol.addressLessThan); | 244 | mem.sort(MachoSymbol, symbols_slice, {}, MachoSymbol.addressLessThan); |
| 183 | 245 | ||
| 184 | return .{ | 246 | return .{ |
| 185 | .mapped_memory = mapped_mem, | 247 | .mapped_memory = all_mapped_memory, |
| 186 | .symbols = symbols_slice, | 248 | .symbols = symbols_slice, |
| 187 | .strings = strings, | 249 | .strings = strings, |
| 188 | .ofiles = .empty, | 250 | .ofiles = .empty, |
| 251 | .vaddr_offset = module.text_base - text_vmaddr, | ||
| 189 | }; | 252 | }; |
| 190 | } | 253 | } |
| 191 | pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, address: usize) Error!std.debug.Symbol { | 254 | pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, address: usize) Error!std.debug.Symbol { |
| ... | @@ -195,7 +258,7 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu | ... | @@ -195,7 +258,7 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu |
| 195 | }; | 258 | }; |
| 196 | const loaded_macho = &di.loaded_macho.?; | 259 | const loaded_macho = &di.loaded_macho.?; |
| 197 | 260 | ||
| 198 | const vaddr = address - module.load_offset; | 261 | const vaddr = address - loaded_macho.vaddr_offset; |
| 199 | const symbol = MachoSymbol.find(loaded_macho.symbols, vaddr) orelse return .unknown; | 262 | const symbol = MachoSymbol.find(loaded_macho.symbols, vaddr) orelse return .unknown; |
| 200 | 263 | ||
| 201 | // offset of `address` from start of `symbol` | 264 | // offset of `address` from start of `symbol` |
| ... | @@ -212,6 +275,11 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu | ... | @@ -212,6 +275,11 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu |
| 212 | .source_location = null, | 275 | .source_location = null, |
| 213 | }; | 276 | }; |
| 214 | 277 | ||
| 278 | if (symbol.ofile == MachoSymbol.unknown_ofile) { | ||
| 279 | // We don't have STAB info, so can't track down the object file; all we can do is the symbol name. | ||
| 280 | return sym_only_result; | ||
| 281 | } | ||
| 282 | |||
| 215 | const o_file: *DebugInfo.OFile = of: { | 283 | const o_file: *DebugInfo.OFile = of: { |
| 216 | const gop = try loaded_macho.ofiles.getOrPut(gpa, symbol.ofile); | 284 | const gop = try loaded_macho.ofiles.getOrPut(gpa, symbol.ofile); |
| 217 | if (!gop.found_existing) { | 285 | if (!gop.found_existing) { |
| ... | @@ -233,7 +301,7 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu | ... | @@ -233,7 +301,7 @@ pub fn getSymbolAtAddress(module: *const DarwinModule, gpa: Allocator, di: *Debu |
| 233 | const compile_unit = o_file.dwarf.findCompileUnit(native_endian, symbol_ofile_vaddr) catch return sym_only_result; | 301 | const compile_unit = o_file.dwarf.findCompileUnit(native_endian, symbol_ofile_vaddr) catch return sym_only_result; |
| 234 | 302 | ||
| 235 | return .{ | 303 | return .{ |
| 236 | .name = o_file.dwarf.getSymbolName(symbol_ofile_vaddr) orelse stab_symbol, | 304 | .name = o_file.dwarf.getSymbolName(symbol_ofile_vaddr + address_symbol_offset) orelse stab_symbol, |
| 237 | .compile_unit_name = compile_unit.die.getAttrString( | 305 | .compile_unit_name = compile_unit.die.getAttrString( |
| 238 | &o_file.dwarf, | 306 | &o_file.dwarf, |
| 239 | native_endian, | 307 | native_endian, |
| ... | @@ -256,7 +324,7 @@ pub const UnwindContext = std.debug.SelfInfo.DwarfUnwindContext; | ... | @@ -256,7 +324,7 @@ pub const UnwindContext = std.debug.SelfInfo.DwarfUnwindContext; |
| 256 | /// Unwind a frame using MachO compact unwind info (from __unwind_info). | 324 | /// Unwind a frame using MachO compact unwind info (from __unwind_info). |
| 257 | /// If the compact encoding can't encode a way to unwind a frame, it will | 325 | /// If the compact encoding can't encode a way to unwind a frame, it will |
| 258 | /// defer unwinding to DWARF, in which case `.eh_frame` will be used if available. | 326 | /// defer unwinding to DWARF, in which case `.eh_frame` will be used if available. |
| 259 | pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!usize { | 327 | pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!void { |
| 260 | return unwindFrameInner(module, gpa, di, context) catch |err| switch (err) { | 328 | return unwindFrameInner(module, gpa, di, context) catch |err| switch (err) { |
| 261 | error.InvalidDebugInfo, | 329 | error.InvalidDebugInfo, |
| 262 | error.MissingDebugInfo, | 330 | error.MissingDebugInfo, |
| ... | @@ -272,7 +340,7 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -272,7 +340,7 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 272 | => return error.InvalidDebugInfo, | 340 | => return error.InvalidDebugInfo, |
| 273 | }; | 341 | }; |
| 274 | } | 342 | } |
| 275 | fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize { | 343 | fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !void { |
| 276 | if (di.unwind == null) di.unwind = module.loadUnwindInfo(); | 344 | if (di.unwind == null) di.unwind = module.loadUnwindInfo(); |
| 277 | const unwind = &di.unwind.?; | 345 | const unwind = &di.unwind.?; |
| 278 | 346 | ||
| ... | @@ -500,11 +568,11 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -500,11 +568,11 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 500 | }, | 568 | }, |
| 501 | .DWARF => { | 569 | .DWARF => { |
| 502 | const eh_frame = unwind.eh_frame orelse return error.MissingDebugInfo; | 570 | const eh_frame = unwind.eh_frame orelse return error.MissingDebugInfo; |
| 503 | const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - module.load_offset; | 571 | const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - unwind.vmaddr_slide; |
| 504 | return context.unwindFrame( | 572 | return context.unwindFrame( |
| 505 | gpa, | 573 | gpa, |
| 506 | &.initSection(.eh_frame, eh_frame_vaddr, eh_frame), | 574 | &.initSection(.eh_frame, eh_frame_vaddr, eh_frame), |
| 507 | module.load_offset, | 575 | unwind.vmaddr_slide, |
| 508 | @intCast(encoding.value.x86_64.dwarf), | 576 | @intCast(encoding.value.x86_64.dwarf), |
| 509 | ); | 577 | ); |
| 510 | }, | 578 | }, |
| ... | @@ -520,11 +588,11 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -520,11 +588,11 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 520 | }, | 588 | }, |
| 521 | .DWARF => { | 589 | .DWARF => { |
| 522 | const eh_frame = unwind.eh_frame orelse return error.MissingDebugInfo; | 590 | const eh_frame = unwind.eh_frame orelse return error.MissingDebugInfo; |
| 523 | const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - module.load_offset; | 591 | const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - unwind.vmaddr_slide; |
| 524 | return context.unwindFrame( | 592 | return context.unwindFrame( |
| 525 | gpa, | 593 | gpa, |
| 526 | &.initSection(.eh_frame, eh_frame_vaddr, eh_frame), | 594 | &.initSection(.eh_frame, eh_frame_vaddr, eh_frame), |
| 527 | module.load_offset, | 595 | unwind.vmaddr_slide, |
| 528 | @intCast(encoding.value.x86_64.dwarf), | 596 | @intCast(encoding.value.x86_64.dwarf), |
| 529 | ); | 597 | ); |
| 530 | }, | 598 | }, |
| ... | @@ -572,9 +640,7 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -572,9 +640,7 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 572 | else => comptime unreachable, // unimplemented | 640 | else => comptime unreachable, // unimplemented |
| 573 | }; | 641 | }; |
| 574 | 642 | ||
| 575 | context.pc = UnwindContext.stripInstructionPtrAuthCode(new_ip); | 643 | context.pc = std.debug.stripInstructionPtrAuthCode(new_ip) -| 1; |
| 576 | if (context.pc > 0) context.pc -= 1; | ||
| 577 | return new_ip; | ||
| 578 | } | 644 | } |
| 579 | pub const DebugInfo = struct { | 645 | pub const DebugInfo = struct { |
| 580 | unwind: ?Unwind, | 646 | unwind: ?Unwind, |
| ... | @@ -590,6 +656,7 @@ pub const DebugInfo = struct { | ... | @@ -590,6 +656,7 @@ pub const DebugInfo = struct { |
| 590 | for (loaded_macho.ofiles.values()) |*ofile| { | 656 | for (loaded_macho.ofiles.values()) |*ofile| { |
| 591 | ofile.dwarf.deinit(gpa); | 657 | ofile.dwarf.deinit(gpa); |
| 592 | ofile.symbols_by_name.deinit(gpa); | 658 | ofile.symbols_by_name.deinit(gpa); |
| 659 | posix.munmap(ofile.mapped_memory); | ||
| 593 | } | 660 | } |
| 594 | loaded_macho.ofiles.deinit(gpa); | 661 | loaded_macho.ofiles.deinit(gpa); |
| 595 | gpa.free(loaded_macho.symbols); | 662 | gpa.free(loaded_macho.symbols); |
| ... | @@ -598,6 +665,9 @@ pub const DebugInfo = struct { | ... | @@ -598,6 +665,9 @@ pub const DebugInfo = struct { |
| 598 | } | 665 | } |
| 599 | 666 | ||
| 600 | const Unwind = struct { | 667 | const Unwind = struct { |
| 668 | /// The slide applied to the following sections. So, `unwind_info.ptr` is this many bytes | ||
| 669 | /// higher than the vmaddr of `__unwind_info`, and likewise for `__eh_frame`. | ||
| 670 | vmaddr_slide: u64, | ||
| 601 | // Backed by the in-memory sections mapped by the loader | 671 | // Backed by the in-memory sections mapped by the loader |
| 602 | unwind_info: ?[]const u8, | 672 | unwind_info: ?[]const u8, |
| 603 | eh_frame: ?[]const u8, | 673 | eh_frame: ?[]const u8, |
| ... | @@ -606,21 +676,31 @@ pub const DebugInfo = struct { | ... | @@ -606,21 +676,31 @@ pub const DebugInfo = struct { |
| 606 | const LoadedMachO = struct { | 676 | const LoadedMachO = struct { |
| 607 | mapped_memory: []align(std.heap.page_size_min) const u8, | 677 | mapped_memory: []align(std.heap.page_size_min) const u8, |
| 608 | symbols: []const MachoSymbol, | 678 | symbols: []const MachoSymbol, |
| 609 | strings: [:0]const u8, | 679 | strings: []const u8, |
| 610 | /// Key is index into `strings` of the file path. | 680 | /// Key is index into `strings` of the file path. |
| 611 | ofiles: std.AutoArrayHashMapUnmanaged(u32, OFile), | 681 | ofiles: std.AutoArrayHashMapUnmanaged(u32, OFile), |
| 682 | /// This is not necessarily the same as the vmaddr_slide that dyld would report. This is | ||
| 683 | /// because the segments in the file on disk might differ from the ones in memory. Normally | ||
| 684 | /// we wouldn't necessarily expect that to work, but /usr/lib/dyld is incredibly annoying: | ||
| 685 | /// it exists on disk (necessarily, because the kernel needs to load it!), but is also in | ||
| 686 | /// the dyld cache (dyld actually restart itself from cache after loading it), and the two | ||
| 687 | /// versions have (very) different segment base addresses. It's sort of like a large slide | ||
| 688 | /// has been applied to all addresses in memory. For an optimal experience, we consider the | ||
| 689 | /// on-disk vmaddr instead of the in-memory one. | ||
| 690 | vaddr_offset: usize, | ||
| 612 | }; | 691 | }; |
| 613 | 692 | ||
| 614 | const OFile = struct { | 693 | const OFile = struct { |
| 694 | mapped_memory: []align(std.heap.page_size_min) const u8, | ||
| 615 | dwarf: Dwarf, | 695 | dwarf: Dwarf, |
| 616 | strtab: [:0]const u8, | 696 | strtab: []const u8, |
| 617 | symtab: []align(1) const macho.nlist_64, | 697 | symtab: []align(1) const macho.nlist_64, |
| 618 | /// All named symbols in `symtab`. Stored `u32` key is the index into `symtab`. Accessed | 698 | /// All named symbols in `symtab`. Stored `u32` key is the index into `symtab`. Accessed |
| 619 | /// through `SymbolAdapter`, so that the symbol name is used as the logical key. | 699 | /// through `SymbolAdapter`, so that the symbol name is used as the logical key. |
| 620 | symbols_by_name: std.ArrayHashMapUnmanaged(u32, void, void, true), | 700 | symbols_by_name: std.ArrayHashMapUnmanaged(u32, void, void, true), |
| 621 | 701 | ||
| 622 | const SymbolAdapter = struct { | 702 | const SymbolAdapter = struct { |
| 623 | strtab: [:0]const u8, | 703 | strtab: []const u8, |
| 624 | symtab: []align(1) const macho.nlist_64, | 704 | symtab: []align(1) const macho.nlist_64, |
| 625 | pub fn hash(ctx: SymbolAdapter, sym_name: []const u8) u32 { | 705 | pub fn hash(ctx: SymbolAdapter, sym_name: []const u8) u32 { |
| 626 | _ = ctx; | 706 | _ = ctx; |
| ... | @@ -663,7 +743,7 @@ pub const DebugInfo = struct { | ... | @@ -663,7 +743,7 @@ pub const DebugInfo = struct { |
| 663 | 743 | ||
| 664 | if (mapped_mem.len < symtab_cmd.stroff + symtab_cmd.strsize) return error.InvalidDebugInfo; | 744 | if (mapped_mem.len < symtab_cmd.stroff + symtab_cmd.strsize) return error.InvalidDebugInfo; |
| 665 | if (mapped_mem[symtab_cmd.stroff + symtab_cmd.strsize - 1] != 0) return error.InvalidDebugInfo; | 745 | if (mapped_mem[symtab_cmd.stroff + symtab_cmd.strsize - 1] != 0) return error.InvalidDebugInfo; |
| 666 | const strtab = mapped_mem[symtab_cmd.stroff..][0 .. symtab_cmd.strsize - 1 :0]; | 746 | const strtab = mapped_mem[symtab_cmd.stroff..][0 .. symtab_cmd.strsize - 1]; |
| 667 | 747 | ||
| 668 | const n_sym_bytes = symtab_cmd.nsyms * @sizeOf(macho.nlist_64); | 748 | const n_sym_bytes = symtab_cmd.nsyms * @sizeOf(macho.nlist_64); |
| 669 | if (mapped_mem.len < symtab_cmd.symoff + n_sym_bytes) return error.InvalidDebugInfo; | 749 | if (mapped_mem.len < symtab_cmd.symoff + n_sym_bytes) return error.InvalidDebugInfo; |
| ... | @@ -717,6 +797,7 @@ pub const DebugInfo = struct { | ... | @@ -717,6 +797,7 @@ pub const DebugInfo = struct { |
| 717 | try dwarf.open(gpa, native_endian); | 797 | try dwarf.open(gpa, native_endian); |
| 718 | 798 | ||
| 719 | return .{ | 799 | return .{ |
| 800 | .mapped_memory = mapped_mem, | ||
| 720 | .dwarf = dwarf, | 801 | .dwarf = dwarf, |
| 721 | .strtab = strtab, | 802 | .strtab = strtab, |
| 722 | .symtab = symtab, | 803 | .symtab = symtab, |
| ... | @@ -728,8 +809,9 @@ pub const DebugInfo = struct { | ... | @@ -728,8 +809,9 @@ pub const DebugInfo = struct { |
| 728 | const MachoSymbol = struct { | 809 | const MachoSymbol = struct { |
| 729 | strx: u32, | 810 | strx: u32, |
| 730 | addr: u64, | 811 | addr: u64, |
| 731 | size: u32, | 812 | /// Value may be `unknown_ofile`. |
| 732 | ofile: u32, | 813 | ofile: u32, |
| 814 | const unknown_ofile = std.math.maxInt(u32); | ||
| 733 | fn addressLessThan(context: void, lhs: MachoSymbol, rhs: MachoSymbol) bool { | 815 | fn addressLessThan(context: void, lhs: MachoSymbol, rhs: MachoSymbol) bool { |
| 734 | _ = context; | 816 | _ = context; |
| 735 | return lhs.addr < rhs.addr; | 817 | return lhs.addr < rhs.addr; |
| ... | @@ -754,9 +836,9 @@ const MachoSymbol = struct { | ... | @@ -754,9 +836,9 @@ const MachoSymbol = struct { |
| 754 | 836 | ||
| 755 | test find { | 837 | test find { |
| 756 | const symbols: []const MachoSymbol = &.{ | 838 | const symbols: []const MachoSymbol = &.{ |
| 757 | .{ .addr = 100, .strx = undefined, .size = undefined, .ofile = undefined }, | 839 | .{ .addr = 100, .strx = undefined, .ofile = undefined }, |
| 758 | .{ .addr = 200, .strx = undefined, .size = undefined, .ofile = undefined }, | 840 | .{ .addr = 200, .strx = undefined, .ofile = undefined }, |
| 759 | .{ .addr = 300, .strx = undefined, .size = undefined, .ofile = undefined }, | 841 | .{ .addr = 300, .strx = undefined, .ofile = undefined }, |
| 760 | }; | 842 | }; |
| 761 | 843 | ||
| 762 | try testing.expectEqual(null, find(symbols, 0)); | 844 | try testing.expectEqual(null, find(symbols, 0)); |
lib/std/debug/SelfInfo/ElfModule.zig+1-1| ... | @@ -230,7 +230,7 @@ fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Erro | ... | @@ -230,7 +230,7 @@ fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Erro |
| 230 | else => unreachable, | 230 | else => unreachable, |
| 231 | } | 231 | } |
| 232 | } | 232 | } |
| 233 | pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!usize { | 233 | pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!void { |
| 234 | if (di.unwind[0] == null) try module.loadUnwindInfo(gpa, di); | 234 | if (di.unwind[0] == null) try module.loadUnwindInfo(gpa, di); |
| 235 | std.debug.assert(di.unwind[0] != null); | 235 | std.debug.assert(di.unwind[0] != null); |
| 236 | for (&di.unwind) |*opt_unwind| { | 236 | for (&di.unwind) |*opt_unwind| { |
lib/std/debug/SelfInfo/WindowsModule.zig+32-4| ... | @@ -332,6 +332,34 @@ pub const UnwindContext = struct { | ... | @@ -332,6 +332,34 @@ pub const UnwindContext = struct { |
| 332 | .Wcr = @splat(0), | 332 | .Wcr = @splat(0), |
| 333 | .Wvr = @splat(0), | 333 | .Wvr = @splat(0), |
| 334 | }, | 334 | }, |
| 335 | .thumb => .{ | ||
| 336 | .ContextFlags = 0, | ||
| 337 | .R0 = ctx.r[0], | ||
| 338 | .R1 = ctx.r[1], | ||
| 339 | .R2 = ctx.r[2], | ||
| 340 | .R3 = ctx.r[3], | ||
| 341 | .R4 = ctx.r[4], | ||
| 342 | .R5 = ctx.r[5], | ||
| 343 | .R6 = ctx.r[6], | ||
| 344 | .R7 = ctx.r[7], | ||
| 345 | .R8 = ctx.r[8], | ||
| 346 | .R9 = ctx.r[9], | ||
| 347 | .R10 = ctx.r[10], | ||
| 348 | .R11 = ctx.r[11], | ||
| 349 | .R12 = ctx.r[12], | ||
| 350 | .Sp = ctx.r[13], | ||
| 351 | .Lr = ctx.r[14], | ||
| 352 | .Pc = ctx.r[15], | ||
| 353 | .Cpsr = 0, | ||
| 354 | .Fpcsr = 0, | ||
| 355 | .Padding = 0, | ||
| 356 | .DUMMYUNIONNAME = .{ .S = @splat(0) }, | ||
| 357 | .Bvr = @splat(0), | ||
| 358 | .Bcr = @splat(0), | ||
| 359 | .Wvr = @splat(0), | ||
| 360 | .Wcr = @splat(0), | ||
| 361 | .Padding2 = @splat(0), | ||
| 362 | }, | ||
| 335 | else => comptime unreachable, | 363 | else => comptime unreachable, |
| 336 | }, | 364 | }, |
| 337 | .history_table = std.mem.zeroes(windows.UNWIND_HISTORY_TABLE), | 365 | .history_table = std.mem.zeroes(windows.UNWIND_HISTORY_TABLE), |
| ... | @@ -345,7 +373,7 @@ pub const UnwindContext = struct { | ... | @@ -345,7 +373,7 @@ pub const UnwindContext = struct { |
| 345 | return ctx.cur.getRegs().bp; | 373 | return ctx.cur.getRegs().bp; |
| 346 | } | 374 | } |
| 347 | }; | 375 | }; |
| 348 | pub fn unwindFrame(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize { | 376 | pub fn unwindFrame(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !void { |
| 349 | _ = module; | 377 | _ = module; |
| 350 | _ = gpa; | 378 | _ = gpa; |
| 351 | _ = di; | 379 | _ = di; |
| ... | @@ -374,10 +402,10 @@ pub fn unwindFrame(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -374,10 +402,10 @@ pub fn unwindFrame(module: *const WindowsModule, gpa: Allocator, di: *DebugInfo, |
| 374 | const next_regs = context.cur.getRegs(); | 402 | const next_regs = context.cur.getRegs(); |
| 375 | const tib = &windows.teb().NtTib; | 403 | const tib = &windows.teb().NtTib; |
| 376 | if (next_regs.sp < @intFromPtr(tib.StackLimit) or next_regs.sp > @intFromPtr(tib.StackBase)) { | 404 | if (next_regs.sp < @intFromPtr(tib.StackLimit) or next_regs.sp > @intFromPtr(tib.StackBase)) { |
| 377 | return 0; | 405 | context.pc = 0; |
| 406 | } else { | ||
| 407 | context.pc = next_regs.ip -| 1; | ||
| 378 | } | 408 | } |
| 379 | context.pc = next_regs.ip -| 1; | ||
| 380 | return next_regs.ip; | ||
| 381 | } | 409 | } |
| 382 | 410 | ||
| 383 | const WindowsModule = @This(); | 411 | const WindowsModule = @This(); |
lib/std/debug/cpu_context.zig+6| ... | @@ -214,6 +214,12 @@ pub fn fromWindowsContext(ctx: *const std.os.windows.CONTEXT) Native { | ... | @@ -214,6 +214,12 @@ pub fn fromWindowsContext(ctx: *const std.os.windows.CONTEXT) Native { |
| 214 | .sp = ctx.Sp, | 214 | .sp = ctx.Sp, |
| 215 | .pc = ctx.Pc, | 215 | .pc = ctx.Pc, |
| 216 | }, | 216 | }, |
| 217 | .thumb => .{ .r = .{ | ||
| 218 | ctx.R0, ctx.R1, ctx.R2, ctx.R3, | ||
| 219 | ctx.R4, ctx.R5, ctx.R6, ctx.R7, | ||
| 220 | ctx.R8, ctx.R9, ctx.R10, ctx.R11, | ||
| 221 | ctx.R12, ctx.Sp, ctx.Lr, ctx.Pc, | ||
| 222 | } }, | ||
| 217 | else => comptime unreachable, | 223 | else => comptime unreachable, |
| 218 | }; | 224 | }; |
| 219 | } | 225 | } |