| author | |
| committer | |
| log | 48f8133beaef31121caea82e39190291bdfdc633 |
| tree | bdef017a179e3ef0450b2cb6dd71e15543ffd8e9 |
| parent | 493ad58ff72ac79528cbd20e9506daca964ec37b |
| parent | e0f10da2703fa4a1390c9daf91c0997270920c4a |
| signature |
`std.debug`: implement `sparc*-linux` unwinding5 files changed, 235 insertions(+), 57 deletions(-)
lib/std/debug.zig+71-26| ... | @@ -728,7 +728,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri | ... | @@ -728,7 +728,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri |
| 728 | } | 728 | } |
| 729 | // `ret_addr` is the return address, which is *after* the function call. | 729 | // `ret_addr` is the return address, which is *after* the function call. |
| 730 | // Subtract 1 to get an address *in* the function call for a better source location. | 730 | // Subtract 1 to get an address *in* the function call for a better source location. |
| 731 | try printSourceAtAddress(di_gpa, di, writer, ret_addr -| 1, tty_config); | 731 | try printSourceAtAddress(di_gpa, di, writer, ret_addr -| StackIterator.ra_call_offset, tty_config); |
| 732 | printed_any_frame = true; | 732 | printed_any_frame = true; |
| 733 | }, | 733 | }, |
| 734 | }; | 734 | }; |
| ... | @@ -777,7 +777,7 @@ pub fn writeStackTrace(st: *const std.builtin.StackTrace, writer: *Writer, tty_c | ... | @@ -777,7 +777,7 @@ pub fn writeStackTrace(st: *const std.builtin.StackTrace, writer: *Writer, tty_c |
| 777 | for (st.instruction_addresses[0..captured_frames]) |ret_addr| { | 777 | for (st.instruction_addresses[0..captured_frames]) |ret_addr| { |
| 778 | // `ret_addr` is the return address, which is *after* the function call. | 778 | // `ret_addr` is the return address, which is *after* the function call. |
| 779 | // Subtract 1 to get an address *in* the function call for a better source location. | 779 | // Subtract 1 to get an address *in* the function call for a better source location. |
| 780 | try printSourceAtAddress(di_gpa, di, writer, ret_addr -| 1, tty_config); | 780 | try printSourceAtAddress(di_gpa, di, writer, ret_addr -| StackIterator.ra_call_offset, tty_config); |
| 781 | } | 781 | } |
| 782 | if (n_frames > captured_frames) { | 782 | if (n_frames > captured_frames) { |
| 783 | tty_config.setColor(writer, .bold) catch {}; | 783 | tty_config.setColor(writer, .bold) catch {}; |
| ... | @@ -807,14 +807,6 @@ const StackIterator = union(enum) { | ... | @@ -807,14 +807,6 @@ const StackIterator = union(enum) { |
| 807 | /// `@frameAddress` and `cpu_context.Native.current` as the caller's stack frame and | 807 | /// `@frameAddress` and `cpu_context.Native.current` as the caller's stack frame and |
| 808 | /// our own are one and the same. | 808 | /// our own are one and the same. |
| 809 | inline fn init(opt_context_ptr: ?CpuContextPtr) error{CannotUnwindFromContext}!StackIterator { | 809 | inline fn init(opt_context_ptr: ?CpuContextPtr) error{CannotUnwindFromContext}!StackIterator { |
| 810 | if (builtin.cpu.arch.isSPARC()) { | ||
| 811 | // Flush all the register windows on stack. | ||
| 812 | if (builtin.cpu.has(.sparc, .v9)) { | ||
| 813 | asm volatile ("flushw" ::: .{ .memory = true }); | ||
| 814 | } else { | ||
| 815 | asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS | ||
| 816 | } | ||
| 817 | } | ||
| 818 | if (opt_context_ptr) |context_ptr| { | 810 | if (opt_context_ptr) |context_ptr| { |
| 819 | if (SelfInfo == void or !SelfInfo.can_unwind) return error.CannotUnwindFromContext; | 811 | if (SelfInfo == void or !SelfInfo.can_unwind) return error.CannotUnwindFromContext; |
| 820 | // Use `di_first` here so we report the PC in the context before unwinding any further. | 812 | // Use `di_first` here so we report the PC in the context before unwinding any further. |
| ... | @@ -833,7 +825,19 @@ const StackIterator = union(enum) { | ... | @@ -833,7 +825,19 @@ const StackIterator = union(enum) { |
| 833 | // in our caller's frame and above. | 825 | // in our caller's frame and above. |
| 834 | return .{ .di = .init(&.current()) }; | 826 | return .{ .di = .init(&.current()) }; |
| 835 | } | 827 | } |
| 836 | return .{ .fp = @frameAddress() }; | 828 | return .{ |
| 829 | // On SPARC, the frame pointer will point to the previous frame's save area, | ||
| 830 | // meaning we will read the previous return address and thus miss a frame. | ||
| 831 | // Instead, start at the stack pointer so we get the return address from the | ||
| 832 | // current frame's save area. The addition of the stack bias cannot fail here | ||
| 833 | // since we know we have a valid stack pointer. | ||
| 834 | .fp = if (native_arch.isSPARC()) sp: { | ||
| 835 | flushSparcWindows(); | ||
| 836 | break :sp asm ("" | ||
| 837 | : [_] "={o6}" (-> usize), | ||
| 838 | ) + stack_bias; | ||
| 839 | } else @frameAddress(), | ||
| 840 | }; | ||
| 837 | } | 841 | } |
| 838 | fn deinit(si: *StackIterator) void { | 842 | fn deinit(si: *StackIterator) void { |
| 839 | switch (si.*) { | 843 | switch (si.*) { |
| ... | @@ -842,6 +846,15 @@ const StackIterator = union(enum) { | ... | @@ -842,6 +846,15 @@ const StackIterator = union(enum) { |
| 842 | } | 846 | } |
| 843 | } | 847 | } |
| 844 | 848 | ||
| 849 | noinline fn flushSparcWindows() void { | ||
| 850 | // Flush all register windows except the current one (hence `noinline`). This ensures that | ||
| 851 | // we actually see meaningful data on the stack when we walk the frame chain. | ||
| 852 | if (comptime builtin.target.cpu.has(.sparc, .v9)) | ||
| 853 | asm volatile ("flushw" ::: .{ .memory = true }) | ||
| 854 | else | ||
| 855 | asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS | ||
| 856 | } | ||
| 857 | |||
| 845 | const FpUsability = enum { | 858 | const FpUsability = enum { |
| 846 | /// FP unwinding is impractical on this target. For example, due to its very silly ABI | 859 | /// FP unwinding is impractical on this target. For example, due to its very silly ABI |
| 847 | /// design decisions, it's not possible to do generic FP unwinding on MIPS without a | 860 | /// design decisions, it's not possible to do generic FP unwinding on MIPS without a |
| ... | @@ -873,6 +886,8 @@ const StackIterator = union(enum) { | ... | @@ -873,6 +886,8 @@ const StackIterator = union(enum) { |
| 873 | .powerpcle, | 886 | .powerpcle, |
| 874 | .powerpc64, | 887 | .powerpc64, |
| 875 | .powerpc64le, | 888 | .powerpc64le, |
| 889 | .sparc, | ||
| 890 | .sparc64, | ||
| 876 | => .ideal, | 891 | => .ideal, |
| 877 | // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Respect-the-purpose-of-specific-CPU-registers | 892 | // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Respect-the-purpose-of-specific-CPU-registers |
| 878 | .aarch64 => if (builtin.target.os.tag.isDarwin()) .safe else .unsafe, | 893 | .aarch64 => if (builtin.target.os.tag.isDarwin()) .safe else .unsafe, |
| ... | @@ -923,7 +938,8 @@ const StackIterator = union(enum) { | ... | @@ -923,7 +938,8 @@ const StackIterator = union(enum) { |
| 923 | const di_gpa = getDebugInfoAllocator(); | 938 | const di_gpa = getDebugInfoAllocator(); |
| 924 | const ret_addr = di.unwindFrame(di_gpa, unwind_context) catch |err| { | 939 | const ret_addr = di.unwindFrame(di_gpa, unwind_context) catch |err| { |
| 925 | const pc = unwind_context.pc; | 940 | const pc = unwind_context.pc; |
| 926 | it.* = .{ .fp = unwind_context.getFp() }; | 941 | const fp = unwind_context.getFp(); |
| 942 | it.* = .{ .fp = fp }; | ||
| 927 | return .{ .switch_to_fp = .{ | 943 | return .{ .switch_to_fp = .{ |
| 928 | .address = pc, | 944 | .address = pc, |
| 929 | .err = err, | 945 | .err = err, |
| ... | @@ -935,8 +951,8 @@ const StackIterator = union(enum) { | ... | @@ -935,8 +951,8 @@ const StackIterator = union(enum) { |
| 935 | .fp => |fp| { | 951 | .fp => |fp| { |
| 936 | if (fp == 0) return .end; // we reached the "sentinel" base pointer | 952 | if (fp == 0) return .end; // we reached the "sentinel" base pointer |
| 937 | 953 | ||
| 938 | const bp_addr = applyOffset(fp, bp_offset) orelse return .end; | 954 | const bp_addr = applyOffset(fp, fp_to_bp_offset) orelse return .end; |
| 939 | const ra_addr = applyOffset(fp, ra_offset) orelse return .end; | 955 | const ra_addr = applyOffset(fp, fp_to_ra_offset) orelse return .end; |
| 940 | 956 | ||
| 941 | if (bp_addr == 0 or !mem.isAligned(bp_addr, @alignOf(usize)) or | 957 | if (bp_addr == 0 or !mem.isAligned(bp_addr, @alignOf(usize)) or |
| 942 | ra_addr == 0 or !mem.isAligned(ra_addr, @alignOf(usize))) | 958 | ra_addr == 0 or !mem.isAligned(ra_addr, @alignOf(usize))) |
| ... | @@ -947,7 +963,7 @@ const StackIterator = union(enum) { | ... | @@ -947,7 +963,7 @@ const StackIterator = union(enum) { |
| 947 | 963 | ||
| 948 | const bp_ptr: *const usize = @ptrFromInt(bp_addr); | 964 | const bp_ptr: *const usize = @ptrFromInt(bp_addr); |
| 949 | const ra_ptr: *const usize = @ptrFromInt(ra_addr); | 965 | const ra_ptr: *const usize = @ptrFromInt(ra_addr); |
| 950 | const bp = applyOffset(bp_ptr.*, bp_bias) orelse return .end; | 966 | const bp = applyOffset(bp_ptr.*, stack_bias) orelse return .end; |
| 951 | 967 | ||
| 952 | // The stack grows downards, so `bp > fp` should always hold. If it doesn't, this | 968 | // The stack grows downards, so `bp > fp` should always hold. If it doesn't, this |
| 953 | // frame is invalid, so we'll treat it as though it we reached end of stack. The | 969 | // frame is invalid, so we'll treat it as though it we reached end of stack. The |
| ... | @@ -964,33 +980,46 @@ const StackIterator = union(enum) { | ... | @@ -964,33 +980,46 @@ const StackIterator = union(enum) { |
| 964 | } | 980 | } |
| 965 | 981 | ||
| 966 | /// Offset of the saved base pointer (previous frame pointer) wrt the frame pointer. | 982 | /// Offset of the saved base pointer (previous frame pointer) wrt the frame pointer. |
| 967 | const bp_offset = off: { | 983 | const fp_to_bp_offset = off: { |
| 968 | // On RISC-V the frame pointer points to the top of the saved register | 984 | // On LoongArch and RISC-V, the frame pointer points to the top of the saved register area, |
| 969 | // area, on pretty much every other architecture it points to the stack | 985 | // in which the base pointer is the first word. |
| 970 | // slot where the previous frame pointer is saved. | ||
| 971 | if (native_arch.isLoongArch() or native_arch.isRISCV()) break :off -2 * @sizeOf(usize); | 986 | if (native_arch.isLoongArch() or native_arch.isRISCV()) break :off -2 * @sizeOf(usize); |
| 972 | // On SPARC the previous frame pointer is stored at 14 slots past %fp+BIAS. | 987 | // On SPARC, the frame pointer points to the save area which holds 16 slots for the local |
| 988 | // and incoming registers. The base pointer (i6) is stored in its customary save slot. | ||
| 973 | if (native_arch.isSPARC()) break :off 14 * @sizeOf(usize); | 989 | if (native_arch.isSPARC()) break :off 14 * @sizeOf(usize); |
| 990 | // Everywhere else, the frame pointer points directly to the location of the base pointer. | ||
| 974 | break :off 0; | 991 | break :off 0; |
| 975 | }; | 992 | }; |
| 976 | 993 | ||
| 977 | /// Offset of the saved return address wrt the frame pointer. | 994 | /// Offset of the saved return address wrt the frame pointer. |
| 978 | const ra_offset = off: { | 995 | const fp_to_ra_offset = off: { |
| 979 | if (native_arch.isLoongArch() or native_arch.isRISCV()) break :off -1 * @sizeOf(usize); | 996 | // On LoongArch and RISC-V, the frame pointer points to the top of the saved register area, |
| 980 | if (native_arch.isSPARC()) break :off 15 * @sizeOf(usize); | 997 | // in which the return address is the second word. |
| 998 | if (native_arch.isRISCV() or native_arch.isLoongArch()) break :off -1 * @sizeOf(usize); | ||
| 981 | if (native_arch.isPowerPC64()) break :off 2 * @sizeOf(usize); | 999 | if (native_arch.isPowerPC64()) break :off 2 * @sizeOf(usize); |
| 982 | // On s390x, r14 is the link register and we need to grab it from its customary slot in the | 1000 | // On s390x, r14 is the link register and we need to grab it from its customary slot in the |
| 983 | // register save area (ELF ABI s390x Supplement §1.2.2.2). | 1001 | // register save area (ELF ABI s390x Supplement §1.2.2.2). |
| 984 | if (native_arch == .s390x) break :off 14 * @sizeOf(usize); | 1002 | if (native_arch == .s390x) break :off 14 * @sizeOf(usize); |
| 1003 | // On SPARC, the frame pointer points to the save area which holds 16 slots for the local | ||
| 1004 | // and incoming registers. The return address (i7) is stored in its customary save slot. | ||
| 1005 | if (native_arch.isSPARC()) break :off 15 * @sizeOf(usize); | ||
| 985 | break :off @sizeOf(usize); | 1006 | break :off @sizeOf(usize); |
| 986 | }; | 1007 | }; |
| 987 | 1008 | ||
| 988 | /// Value to add to a base pointer after loading it from the stack. Yes, SPARC really does this. | 1009 | /// Value to add to the stack pointer and frame/base pointers to get the real location being |
| 989 | const bp_bias = bias: { | 1010 | /// pointed to. Yes, SPARC really does this. |
| 990 | if (native_arch.isSPARC()) break :bias 2047; | 1011 | const stack_bias = bias: { |
| 1012 | if (native_arch == .sparc64) break :bias 2047; | ||
| 991 | break :bias 0; | 1013 | break :bias 0; |
| 992 | }; | 1014 | }; |
| 993 | 1015 | ||
| 1016 | /// On some oddball architectures, a return address points to the call instruction rather than | ||
| 1017 | /// the instruction following it. | ||
| 1018 | const ra_call_offset = off: { | ||
| 1019 | if (native_arch.isSPARC()) break :off 0; | ||
| 1020 | break :off 1; | ||
| 1021 | }; | ||
| 1022 | |||
| 994 | fn applyOffset(addr: usize, comptime off: comptime_int) ?usize { | 1023 | fn applyOffset(addr: usize, comptime off: comptime_int) ?usize { |
| 995 | if (off >= 0) return math.add(usize, addr, off) catch return null; | 1024 | if (off >= 0) return math.add(usize, addr, off) catch return null; |
| 996 | return math.sub(usize, addr, -off) catch return null; | 1025 | return math.sub(usize, addr, -off) catch return null; |
| ... | @@ -1429,6 +1458,22 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa | ... | @@ -1429,6 +1458,22 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa |
| 1429 | break :info .{ addr, name }; | 1458 | break :info .{ addr, name }; |
| 1430 | }; | 1459 | }; |
| 1431 | const opt_cpu_context: ?cpu_context.Native = cpu_context.fromPosixSignalContext(ctx_ptr); | 1460 | const opt_cpu_context: ?cpu_context.Native = cpu_context.fromPosixSignalContext(ctx_ptr); |
| 1461 | |||
| 1462 | if (native_arch.isSPARC()) { | ||
| 1463 | // It's unclear to me whether this is a QEMU bug or also real kernel behavior, but in the | ||
| 1464 | // former, I observed that the most recent register window wasn't getting spilled on the | ||
| 1465 | // stack as expected when a signal arrived. A `flushw` from the signal handler does not | ||
| 1466 | // appear to be sufficient either. On the other hand, when doing a synchronous stack trace | ||
| 1467 | // and using `flushw`, this all appears to work as expected. So, *probably* a QEMU bug, but | ||
| 1468 | // someone with real SPARC hardware should verify. | ||
| 1469 | // | ||
| 1470 | // In any case, the register save area exists specifically so that register windows can be | ||
| 1471 | // spilled asynchronously. This means that it should be perfectly fine for us to manually do | ||
| 1472 | // so here. | ||
| 1473 | const ctx = opt_cpu_context.?; | ||
| 1474 | @as(*[16]usize, @ptrFromInt(ctx.o[6] + StackIterator.stack_bias)).* = ctx.l ++ ctx.i; | ||
| 1475 | } | ||
| 1476 | |||
| 1432 | handleSegfault(addr, name, if (opt_cpu_context) |*ctx| ctx else null); | 1477 | handleSegfault(addr, name, if (opt_cpu_context) |*ctx| ctx else null); |
| 1433 | } | 1478 | } |
| 1434 | 1479 |
lib/std/debug/Dwarf.zig+3| ... | @@ -1437,6 +1437,7 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 { | ... | @@ -1437,6 +1437,7 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 { |
| 1437 | .powerpc, .powerpcle, .powerpc64, .powerpc64le => 67, | 1437 | .powerpc, .powerpcle, .powerpc64, .powerpc64le => 67, |
| 1438 | .riscv32, .riscv32be, .riscv64, .riscv64be => 65, | 1438 | .riscv32, .riscv32be, .riscv64, .riscv64be => 65, |
| 1439 | .s390x => 65, | 1439 | .s390x => 65, |
| 1440 | .sparc, .sparc64 => 32, | ||
| 1440 | .x86 => 8, | 1441 | .x86 => 8, |
| 1441 | .x86_64 => 16, | 1442 | .x86_64 => 16, |
| 1442 | else => null, | 1443 | else => null, |
| ... | @@ -1453,6 +1454,7 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 { | ... | @@ -1453,6 +1454,7 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 { |
| 1453 | .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1, | 1454 | .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1, |
| 1454 | .riscv32, .riscv32be, .riscv64, .riscv64be => 8, | 1455 | .riscv32, .riscv32be, .riscv64, .riscv64be => 8, |
| 1455 | .s390x => 11, | 1456 | .s390x => 11, |
| 1457 | .sparc, .sparc64 => 30, | ||
| 1456 | .x86 => 5, | 1458 | .x86 => 5, |
| 1457 | .x86_64 => 6, | 1459 | .x86_64 => 6, |
| 1458 | else => unreachable, | 1460 | else => unreachable, |
| ... | @@ -1469,6 +1471,7 @@ pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 { | ... | @@ -1469,6 +1471,7 @@ pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 { |
| 1469 | .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1, | 1471 | .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1, |
| 1470 | .riscv32, .riscv32be, .riscv64, .riscv64be => 2, | 1472 | .riscv32, .riscv32be, .riscv64, .riscv64be => 2, |
| 1471 | .s390x => 15, | 1473 | .s390x => 15, |
| 1474 | .sparc, .sparc64 => 14, | ||
| 1472 | .x86 => 4, | 1475 | .x86 => 4, |
| 1473 | .x86_64 => 7, | 1476 | .x86_64 => 7, |
| 1474 | else => unreachable, | 1477 | else => unreachable, |
lib/std/debug/SelfInfo/Elf.zig+4-16| ... | @@ -94,28 +94,22 @@ pub const can_unwind: bool = s: { | ... | @@ -94,28 +94,22 @@ pub const can_unwind: bool = s: { |
| 94 | // Notably, we are yet to support unwinding on ARM. There, unwinding is not done through | 94 | // Notably, we are yet to support unwinding on ARM. There, unwinding is not done through |
| 95 | // `.eh_frame`, but instead with the `.ARM.exidx` section, which has a different format. | 95 | // `.eh_frame`, but instead with the `.ARM.exidx` section, which has a different format. |
| 96 | const archs: []const std.Target.Cpu.Arch = switch (builtin.target.os.tag) { | 96 | const archs: []const std.Target.Cpu.Arch = switch (builtin.target.os.tag) { |
| 97 | // Not supported yet: arm, m68k, sparc64 | 97 | // Not supported yet: arm, m68k |
| 98 | .haiku => &.{ | 98 | .haiku => &.{ |
| 99 | .aarch64, | 99 | .aarch64, |
| 100 | .powerpc, | ||
| 101 | .riscv64, | 100 | .riscv64, |
| 102 | .x86, | 101 | .x86, |
| 103 | .x86_64, | 102 | .x86_64, |
| 104 | }, | 103 | }, |
| 105 | // Not supported yet: arc, arm/armeb/thumb/thumbeb, csky, m68k, or1k, sparc/sparc64, xtensa | 104 | // Not supported yet: arc, arm/armeb/thumb/thumbeb, csky, m68k, or1k, xtensa |
| 106 | .linux => &.{ | 105 | .linux => &.{ |
| 107 | .aarch64, | 106 | .aarch64, |
| 108 | .aarch64_be, | 107 | .aarch64_be, |
| 109 | .hexagon, | ||
| 110 | .loongarch64, | 108 | .loongarch64, |
| 111 | .mips, | 109 | .mips, |
| 112 | .mipsel, | 110 | .mipsel, |
| 113 | .mips64, | 111 | .mips64, |
| 114 | .mips64el, | 112 | .mips64el, |
| 115 | .powerpc, | ||
| 116 | .powerpcle, | ||
| 117 | .powerpc64, | ||
| 118 | .powerpc64le, | ||
| 119 | .riscv32, | 113 | .riscv32, |
| 120 | .riscv64, | 114 | .riscv64, |
| 121 | .s390x, | 115 | .s390x, |
| ... | @@ -134,28 +128,23 @@ pub const can_unwind: bool = s: { | ... | @@ -134,28 +128,23 @@ pub const can_unwind: bool = s: { |
| 134 | // Not supported yet: arm | 128 | // Not supported yet: arm |
| 135 | .freebsd => &.{ | 129 | .freebsd => &.{ |
| 136 | .aarch64, | 130 | .aarch64, |
| 137 | .powerpc64, | ||
| 138 | .powerpc64le, | ||
| 139 | .riscv64, | 131 | .riscv64, |
| 140 | .x86_64, | 132 | .x86_64, |
| 141 | }, | 133 | }, |
| 142 | // Not supported yet: arm/armeb, m68k, mips64/mips64el, sparc/sparc64 | 134 | // Not supported yet: arm/armeb, m68k, mips64/mips64el |
| 143 | .netbsd => &.{ | 135 | .netbsd => &.{ |
| 144 | .aarch64, | 136 | .aarch64, |
| 145 | .aarch64_be, | 137 | .aarch64_be, |
| 146 | .mips, | 138 | .mips, |
| 147 | .mipsel, | 139 | .mipsel, |
| 148 | .powerpc, | ||
| 149 | .x86, | 140 | .x86, |
| 150 | .x86_64, | 141 | .x86_64, |
| 151 | }, | 142 | }, |
| 152 | // Not supported yet: arm, sparc64 | 143 | // Not supported yet: arm |
| 153 | .openbsd => &.{ | 144 | .openbsd => &.{ |
| 154 | .aarch64, | 145 | .aarch64, |
| 155 | .mips64, | 146 | .mips64, |
| 156 | .mips64el, | 147 | .mips64el, |
| 157 | .powerpc, | ||
| 158 | .powerpc64, | ||
| 159 | .riscv64, | 148 | .riscv64, |
| 160 | .x86, | 149 | .x86, |
| 161 | .x86_64, | 150 | .x86_64, |
| ... | @@ -165,7 +154,6 @@ pub const can_unwind: bool = s: { | ... | @@ -165,7 +154,6 @@ pub const can_unwind: bool = s: { |
| 165 | .x86, | 154 | .x86, |
| 166 | .x86_64, | 155 | .x86_64, |
| 167 | }, | 156 | }, |
| 168 | // Not supported yet: sparc64 | ||
| 169 | .solaris => &.{ | 157 | .solaris => &.{ |
| 170 | .x86_64, | 158 | .x86_64, |
| 171 | }, | 159 | }, |
lib/std/debug/cpu_context.zig+155-13| ... | @@ -10,6 +10,7 @@ else switch (native_arch) { | ... | @@ -10,6 +10,7 @@ else switch (native_arch) { |
| 10 | .loongarch32, .loongarch64 => LoongArch, | 10 | .loongarch32, .loongarch64 => LoongArch, |
| 11 | .mips, .mipsel, .mips64, .mips64el => Mips, | 11 | .mips, .mipsel, .mips64, .mips64el => Mips, |
| 12 | .powerpc, .powerpcle, .powerpc64, .powerpc64le => Powerpc, | 12 | .powerpc, .powerpcle, .powerpc64, .powerpc64le => Powerpc, |
| 13 | .sparc, .sparc64 => Sparc, | ||
| 13 | .riscv32, .riscv32be, .riscv64, .riscv64be => Riscv, | 14 | .riscv32, .riscv32be, .riscv64, .riscv64be => Riscv, |
| 14 | .s390x => S390x, | 15 | .s390x => S390x, |
| 15 | .x86 => X86, | 16 | .x86 => X86, |
| ... | @@ -39,6 +40,26 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native { | ... | @@ -39,6 +40,26 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native { |
| 39 | }, | 40 | }, |
| 40 | .pc = @truncate(uc.mcontext.pc), | 41 | .pc = @truncate(uc.mcontext.pc), |
| 41 | }; | 42 | }; |
| 43 | } else if (native_arch.isSPARC() and native_os == .linux) { | ||
| 44 | const SparcStackFrame = extern struct { | ||
| 45 | l: [8]usize, | ||
| 46 | i: [8]usize, | ||
| 47 | _x: [8]usize, | ||
| 48 | }; | ||
| 49 | |||
| 50 | // When invoking a signal handler, the kernel builds an `rt_signal_frame` structure on the | ||
| 51 | // stack and passes a pointer to its `info` field to the signal handler. This implies that | ||
| 52 | // prior to said `info` field, we will find the `ss` field which, among other things, | ||
| 53 | // contains the incoming and local registers of the interrupted code. | ||
| 54 | const frame = @as(*const SparcStackFrame, @ptrFromInt(@as(usize, @intFromPtr(ctx_ptr)) - @sizeOf(SparcStackFrame))); | ||
| 55 | |||
| 56 | return .{ | ||
| 57 | .g = uc.mcontext.g, | ||
| 58 | .o = uc.mcontext.o, | ||
| 59 | .l = frame.l, | ||
| 60 | .i = frame.i, | ||
| 61 | .pc = uc.mcontext.pc, | ||
| 62 | }; | ||
| 42 | } | 63 | } |
| 43 | 64 | ||
| 44 | // Only unified conversions from here. | 65 | // Only unified conversions from here. |
| ... | @@ -858,6 +879,104 @@ const Powerpc = extern struct { | ... | @@ -858,6 +879,104 @@ const Powerpc = extern struct { |
| 858 | } | 879 | } |
| 859 | }; | 880 | }; |
| 860 | 881 | ||
| 882 | /// This is an `extern struct` so that inline assembly in `current` can use field offsets. | ||
| 883 | const Sparc = extern struct { | ||
| 884 | g: [8]Gpr, | ||
| 885 | o: [8]Gpr, | ||
| 886 | l: [8]Gpr, | ||
| 887 | i: [8]Gpr, | ||
| 888 | pc: Gpr, | ||
| 889 | |||
| 890 | pub const Gpr = if (native_arch == .sparc64) u64 else u32; | ||
| 891 | |||
| 892 | pub inline fn current() Sparc { | ||
| 893 | flushWindows(); | ||
| 894 | |||
| 895 | var ctx: Sparc = undefined; | ||
| 896 | asm volatile (if (Gpr == u64) | ||
| 897 | \\ stx %g0, [%l0 + 0] | ||
| 898 | \\ stx %g1, [%l0 + 8] | ||
| 899 | \\ stx %g2, [%l0 + 16] | ||
| 900 | \\ stx %g3, [%l0 + 24] | ||
| 901 | \\ stx %g4, [%l0 + 32] | ||
| 902 | \\ stx %g5, [%l0 + 40] | ||
| 903 | \\ stx %g6, [%l0 + 48] | ||
| 904 | \\ stx %g7, [%l0 + 56] | ||
| 905 | \\ stx %o0, [%l0 + 64] | ||
| 906 | \\ stx %o1, [%l0 + 72] | ||
| 907 | \\ stx %o2, [%l0 + 80] | ||
| 908 | \\ stx %o3, [%l0 + 88] | ||
| 909 | \\ stx %o4, [%l0 + 96] | ||
| 910 | \\ stx %o5, [%l0 + 104] | ||
| 911 | \\ stx %o6, [%l0 + 112] | ||
| 912 | \\ stx %o7, [%l0 + 120] | ||
| 913 | \\ stx %l0, [%l0 + 128] | ||
| 914 | \\ stx %l1, [%l0 + 136] | ||
| 915 | \\ stx %l2, [%l0 + 144] | ||
| 916 | \\ stx %l3, [%l0 + 152] | ||
| 917 | \\ stx %l4, [%l0 + 160] | ||
| 918 | \\ stx %l5, [%l0 + 168] | ||
| 919 | \\ stx %l6, [%l0 + 176] | ||
| 920 | \\ stx %l7, [%l0 + 184] | ||
| 921 | \\ stx %i0, [%l0 + 192] | ||
| 922 | \\ stx %i1, [%l0 + 200] | ||
| 923 | \\ stx %i2, [%l0 + 208] | ||
| 924 | \\ stx %i3, [%l0 + 216] | ||
| 925 | \\ stx %i4, [%l0 + 224] | ||
| 926 | \\ stx %i5, [%l0 + 232] | ||
| 927 | \\ stx %i6, [%l0 + 240] | ||
| 928 | \\ stx %i7, [%l0 + 248] | ||
| 929 | \\ call 1f | ||
| 930 | \\ stx %o7, [%l0 + 256] | ||
| 931 | \\1: | ||
| 932 | else | ||
| 933 | \\ std %g0, [%l0 + 0] | ||
| 934 | \\ std %g2, [%l0 + 8] | ||
| 935 | \\ std %g4, [%l0 + 16] | ||
| 936 | \\ std %g6, [%l0 + 24] | ||
| 937 | \\ std %o0, [%l0 + 32] | ||
| 938 | \\ std %o2, [%l0 + 40] | ||
| 939 | \\ std %o4, [%l0 + 48] | ||
| 940 | \\ std %o6, [%l0 + 56] | ||
| 941 | \\ std %l0, [%l0 + 64] | ||
| 942 | \\ std %l2, [%l0 + 72] | ||
| 943 | \\ std %l4, [%l0 + 80] | ||
| 944 | \\ std %l6, [%l0 + 88] | ||
| 945 | \\ std %i0, [%l0 + 96] | ||
| 946 | \\ std %i2, [%l0 + 104] | ||
| 947 | \\ std %i4, [%l0 + 112] | ||
| 948 | \\ std %i6, [%l0 + 120] | ||
| 949 | \\ call 1f | ||
| 950 | \\ st %o7, [%l0 + 128] | ||
| 951 | \\1: | ||
| 952 | : | ||
| 953 | : [gprs] "{l0}" (&ctx), | ||
| 954 | : .{ .o7 = true, .memory = true }); | ||
| 955 | return ctx; | ||
| 956 | } | ||
| 957 | |||
| 958 | noinline fn flushWindows() void { | ||
| 959 | // Flush all register windows except the current one (hence `noinline`). This ensures that | ||
| 960 | // we actually see meaningful data on the stack when we walk the frame chain. | ||
| 961 | if (comptime builtin.target.cpu.has(.sparc, .v9)) | ||
| 962 | asm volatile ("flushw" ::: .{ .memory = true }) | ||
| 963 | else | ||
| 964 | asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS | ||
| 965 | } | ||
| 966 | |||
| 967 | pub fn dwarfRegisterBytes(ctx: *Sparc, register_num: u16) DwarfRegisterError![]u8 { | ||
| 968 | switch (register_num) { | ||
| 969 | 0...7 => return @ptrCast(&ctx.g[register_num]), | ||
| 970 | 8...15 => return @ptrCast(&ctx.o[register_num - 8]), | ||
| 971 | 16...23 => return @ptrCast(&ctx.l[register_num - 16]), | ||
| 972 | 24...31 => return @ptrCast(&ctx.i[register_num - 24]), | ||
| 973 | 32 => return @ptrCast(&ctx.pc), | ||
| 974 | |||
| 975 | else => return error.InvalidRegister, | ||
| 976 | } | ||
| 977 | } | ||
| 978 | }; | ||
| 979 | |||
| 861 | /// This is an `extern struct` so that inline assembly in `current` can use field offsets. | 980 | /// This is an `extern struct` so that inline assembly in `current` can use field offsets. |
| 862 | const Riscv = extern struct { | 981 | const Riscv = extern struct { |
| 863 | /// The numbered general-purpose registers r0 - r31. r0 must be zero. | 982 | /// The numbered general-purpose registers r0 - r31. r0 must be zero. |
| ... | @@ -1271,9 +1390,32 @@ const signal_ucontext_t = switch (native_os) { | ... | @@ -1271,9 +1390,32 @@ const signal_ucontext_t = switch (native_os) { |
| 1271 | lr: u32, | 1390 | lr: u32, |
| 1272 | }, | 1391 | }, |
| 1273 | }, | 1392 | }, |
| 1274 | // https://github.com/torvalds/linux/blob/cd5a0afbdf8033dc83786315d63f8b325bdba2fd/arch/sparc/include/uapi/asm/uctx.h | 1393 | // https://github.com/torvalds/linux/blob/cd5a0afbdf8033dc83786315d63f8b325bdba2fd/arch/sparc/kernel/signal_32.c#L48-L49 |
| 1275 | .sparc => @compileError("sparc-linux ucontext_t missing"), | 1394 | .sparc => extern struct { |
| 1276 | .sparc64 => @compileError("sparc64-linux ucontext_t missing"), | 1395 | // Not actually a `ucontext_t` at all because, uh, reasons? |
| 1396 | |||
| 1397 | _info: std.os.linux.siginfo_t, | ||
| 1398 | mcontext: extern struct { | ||
| 1399 | _psr: u32, | ||
| 1400 | pc: u32, | ||
| 1401 | _npc: u32, | ||
| 1402 | _y: u32, | ||
| 1403 | g: [8]u32, | ||
| 1404 | o: [8]u32, | ||
| 1405 | }, | ||
| 1406 | }, | ||
| 1407 | // https://github.com/torvalds/linux/blob/cd5a0afbdf8033dc83786315d63f8b325bdba2fd/arch/sparc/kernel/signal_64.c#L247-L248 | ||
| 1408 | .sparc64 => extern struct { | ||
| 1409 | // Ditto... | ||
| 1410 | |||
| 1411 | _info: std.os.linux.siginfo_t, | ||
| 1412 | mcontext: extern struct { | ||
| 1413 | g: [8]u64, | ||
| 1414 | o: [8]u64, | ||
| 1415 | _tstate: u64, | ||
| 1416 | pc: u64, | ||
| 1417 | }, | ||
| 1418 | }, | ||
| 1277 | else => unreachable, | 1419 | else => unreachable, |
| 1278 | }, | 1420 | }, |
| 1279 | // https://github.com/freebsd/freebsd-src/blob/55c28005f544282b984ae0e15dacd0c108d8ab12/sys/sys/_ucontext.h | 1421 | // https://github.com/freebsd/freebsd-src/blob/55c28005f544282b984ae0e15dacd0c108d8ab12/sys/sys/_ucontext.h |
| ... | @@ -1398,14 +1540,14 @@ const signal_ucontext_t = switch (native_os) { | ... | @@ -1398,14 +1540,14 @@ const signal_ucontext_t = switch (native_os) { |
| 1398 | }, | 1540 | }, |
| 1399 | }, | 1541 | }, |
| 1400 | // This needs to be audited by someone with access to the Solaris headers. | 1542 | // This needs to be audited by someone with access to the Solaris headers. |
| 1401 | .solaris => extern struct { | 1543 | .solaris => switch (native_arch) { |
| 1402 | _flags: u64, | 1544 | .sparc64 => @compileError("sparc64-solaris ucontext_t missing"), |
| 1403 | _link: ?*signal_ucontext_t, | 1545 | .x86_64 => extern struct { |
| 1404 | _sigmask: std.c.sigset_t, | 1546 | _flags: u64, |
| 1405 | _stack: std.c.stack_t, | 1547 | _link: ?*signal_ucontext_t, |
| 1406 | mcontext: switch (native_arch) { | 1548 | _sigmask: std.c.sigset_t, |
| 1407 | .sparc64 => @compileError("sparc64-solaris mcontext_t missing"), | 1549 | _stack: std.c.stack_t, |
| 1408 | .x86_64 => extern struct { | 1550 | mcontext: extern struct { |
| 1409 | r15: u64, | 1551 | r15: u64, |
| 1410 | r14: u64, | 1552 | r14: u64, |
| 1411 | r13: u64, | 1553 | r13: u64, |
| ... | @@ -1425,8 +1567,8 @@ const signal_ucontext_t = switch (native_os) { | ... | @@ -1425,8 +1567,8 @@ const signal_ucontext_t = switch (native_os) { |
| 1425 | _err: i64, | 1567 | _err: i64, |
| 1426 | rip: u64, | 1568 | rip: u64, |
| 1427 | }, | 1569 | }, |
| 1428 | else => unreachable, | ||
| 1429 | }, | 1570 | }, |
| 1571 | else => unreachable, | ||
| 1430 | }, | 1572 | }, |
| 1431 | // https://github.com/illumos/illumos-gate/blob/d4ce137bba3bd16823db6374d9e9a643264ce245/usr/src/uts/intel/sys/ucontext.h | 1573 | // https://github.com/illumos/illumos-gate/blob/d4ce137bba3bd16823db6374d9e9a643264ce245/usr/src/uts/intel/sys/ucontext.h |
| 1432 | .illumos => extern struct { | 1574 | .illumos => extern struct { |
| ... | @@ -1538,7 +1680,7 @@ const signal_ucontext_t = switch (native_os) { | ... | @@ -1538,7 +1680,7 @@ const signal_ucontext_t = switch (native_os) { |
| 1538 | }, | 1680 | }, |
| 1539 | }, | 1681 | }, |
| 1540 | // https://github.com/openbsd/src/blob/42468faed8369d07ae49ae02dd71ec34f59b66cd/sys/arch/sparc64/include/signal.h | 1682 | // https://github.com/openbsd/src/blob/42468faed8369d07ae49ae02dd71ec34f59b66cd/sys/arch/sparc64/include/signal.h |
| 1541 | .sparc64 => @compileError("sparc64-openbsd mcontext_t missing"), | 1683 | .sparc64 => @compileError("sparc64-openbsd ucontext_t missing"), |
| 1542 | // https://github.com/openbsd/src/blob/42468faed8369d07ae49ae02dd71ec34f59b66cd/sys/arch/i386/include/signal.h | 1684 | // https://github.com/openbsd/src/blob/42468faed8369d07ae49ae02dd71ec34f59b66cd/sys/arch/i386/include/signal.h |
| 1543 | .x86 => extern struct { | 1685 | .x86 => extern struct { |
| 1544 | mcontext: extern struct { | 1686 | mcontext: extern struct { |
lib/std/os/windows.zig+2-2| ... | @@ -4229,8 +4229,8 @@ pub const CONTEXT = switch (native_arch) { | ... | @@ -4229,8 +4229,8 @@ pub const CONTEXT = switch (native_arch) { |
| 4229 | SegSs: DWORD, | 4229 | SegSs: DWORD, |
| 4230 | ExtendedRegisters: [512]BYTE, | 4230 | ExtendedRegisters: [512]BYTE, |
| 4231 | 4231 | ||
| 4232 | pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize } { | 4232 | pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize, sp: usize } { |
| 4233 | return .{ .bp = ctx.Ebp, .ip = ctx.Eip }; | 4233 | return .{ .bp = ctx.Ebp, .ip = ctx.Eip, .sp = ctx.Esp }; |
| 4234 | } | 4234 | } |
| 4235 | }, | 4235 | }, |
| 4236 | .x86_64 => extern struct { | 4236 | .x86_64 => extern struct { |