aboutsummaryrefslogtreecommitdiff
path: root/lib/std/debug
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std/debug')
-rw-r--r--lib/std/debug/Dwarf.zig57
-rw-r--r--lib/std/debug/Dwarf/abi.zig351
-rw-r--r--lib/std/debug/Dwarf/expression.zig160
-rw-r--r--lib/std/debug/SelfInfo.zig229
-rw-r--r--lib/std/debug/SelfInfo/DarwinModule.zig57
-rw-r--r--lib/std/debug/SelfInfo/ElfModule.zig3
-rw-r--r--lib/std/debug/SelfInfo/WindowsModule.zig40
-rw-r--r--lib/std/debug/cpu_context.zig1019
8 files changed, 1320 insertions, 596 deletions
diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig
index 9a70746b0a..655a65b709 100644
--- a/lib/std/debug/Dwarf.zig
+++ b/lib/std/debug/Dwarf.zig
@@ -27,7 +27,6 @@ const Reader = std.Io.Reader;
const Dwarf = @This();
pub const expression = @import("Dwarf/expression.zig");
-pub const abi = @import("Dwarf/abi.zig");
pub const call_frame = @import("Dwarf/call_frame.zig");
pub const Unwind = @import("Dwarf/Unwind.zig");
@@ -1415,7 +1414,7 @@ pub fn readUnitHeader(r: *Reader, endian: Endian) ScanError!UnitHeader {
}
/// Returns the DWARF register number for an x86_64 register number found in compact unwind info
-pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
+pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u16 {
return switch (unwind_reg_number) {
1 => 3, // RBX
2 => 12, // R12
@@ -1427,6 +1426,60 @@ pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
};
}
+/// Returns `null` for CPU architectures without an instruction pointer register.
+pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 {
+ return switch (arch) {
+ .x86 => 8,
+ .x86_64 => 16,
+ .arm, .armeb, .thumb, .thumbeb => 15,
+ .aarch64, .aarch64_be => 32,
+ else => null,
+ };
+}
+
+pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 {
+ return switch (arch) {
+ .x86 => 5,
+ .x86_64 => 6,
+ .arm, .armeb, .thumb, .thumbeb => 11,
+ .aarch64, .aarch64_be => 29,
+ else => unreachable,
+ };
+}
+
+pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 {
+ return switch (arch) {
+ .x86 => 4,
+ .x86_64 => 7,
+ .arm, .armeb, .thumb, .thumbeb => 13,
+ .aarch64, .aarch64_be => 31,
+ else => unreachable,
+ };
+}
+
+/// Tells whether unwinding for this target is supported by the Dwarf standard.
+///
+/// See also `std.debug.SelfInfo.supports_unwinding` which tells whether the Zig
+/// standard library has a working implementation of unwinding for this target.
+pub fn supportsUnwinding(target: *const std.Target) bool {
+ return switch (target.cpu.arch) {
+ .amdgcn,
+ .nvptx,
+ .nvptx64,
+ .spirv32,
+ .spirv64,
+ => false,
+
+ // Enabling this causes relocation errors such as:
+ // error: invalid relocation type R_RISCV_SUB32 at offset 0x20
+ .riscv64, .riscv64be, .riscv32, .riscv32be => false,
+
+ // Conservative guess. Feel free to update this logic with any targets
+ // that are known to not support Dwarf unwinding.
+ else => true,
+ };
+}
+
/// This function is to make it handy to comment out the return and make it
/// into a crash when working on this file.
pub fn bad() error{InvalidDebugInfo} {
diff --git a/lib/std/debug/Dwarf/abi.zig b/lib/std/debug/Dwarf/abi.zig
deleted file mode 100644
index 98a84392e6..0000000000
--- a/lib/std/debug/Dwarf/abi.zig
+++ /dev/null
@@ -1,351 +0,0 @@
-const builtin = @import("builtin");
-
-const std = @import("../../std.zig");
-const mem = std.mem;
-const posix = std.posix;
-const Arch = std.Target.Cpu.Arch;
-
-/// Tells whether unwinding for this target is supported by the Dwarf standard.
-///
-/// See also `std.debug.SelfInfo.supports_unwinding` which tells whether the Zig
-/// standard library has a working implementation of unwinding for this target.
-pub fn supportsUnwinding(target: *const std.Target) bool {
- return switch (target.cpu.arch) {
- .amdgcn,
- .nvptx,
- .nvptx64,
- .spirv32,
- .spirv64,
- => false,
-
- // Enabling this causes relocation errors such as:
- // error: invalid relocation type R_RISCV_SUB32 at offset 0x20
- .riscv64, .riscv64be, .riscv32, .riscv32be => false,
-
- // Conservative guess. Feel free to update this logic with any targets
- // that are known to not support Dwarf unwinding.
- else => true,
- };
-}
-
-/// Returns `null` for CPU architectures without an instruction pointer register.
-pub fn ipRegNum(arch: Arch) ?u8 {
- return switch (arch) {
- .x86 => 8,
- .x86_64 => 16,
- .arm, .armeb, .thumb, .thumbeb => 15,
- .aarch64, .aarch64_be => 32,
- else => null,
- };
-}
-
-pub fn fpRegNum(arch: Arch, reg_context: RegisterContext) u8 {
- return switch (arch) {
- // GCC on OS X historically did the opposite of ELF for these registers
- // (only in .eh_frame), and that is now the convention for MachO
- .x86 => if (reg_context.eh_frame and reg_context.is_macho) 4 else 5,
- .x86_64 => 6,
- .arm, .armeb, .thumb, .thumbeb => 11,
- .aarch64, .aarch64_be => 29,
- else => unreachable,
- };
-}
-
-pub fn spRegNum(arch: Arch, reg_context: RegisterContext) u8 {
- return switch (arch) {
- .x86 => if (reg_context.eh_frame and reg_context.is_macho) 5 else 4,
- .x86_64 => 7,
- .arm, .armeb, .thumb, .thumbeb => 13,
- .aarch64, .aarch64_be => 31,
- else => unreachable,
- };
-}
-
-pub const RegisterContext = struct {
- eh_frame: bool,
- is_macho: bool,
-};
-
-pub const RegBytesError = error{
- InvalidRegister,
- UnimplementedArch,
- UnimplementedOs,
- RegisterContextRequired,
- ThreadContextNotSupported,
-};
-
-/// Returns a slice containing the backing storage for `reg_number`.
-///
-/// This function assumes the Dwarf information corresponds not necessarily to
-/// the current executable, but at least with a matching CPU architecture and
-/// OS. It is planned to lift this limitation with a future enhancement.
-///
-/// `reg_context` describes in what context the register number is used, as it can have different
-/// meanings depending on the DWARF container. It is only required when getting the stack or
-/// frame pointer register on some architectures.
-pub fn regBytes(
- thread_context_ptr: *std.debug.ThreadContext,
- reg_number: u8,
- reg_context: ?RegisterContext,
-) RegBytesError![]u8 {
- if (builtin.os.tag == .windows) {
- return switch (builtin.cpu.arch) {
- .x86 => switch (reg_number) {
- 0 => mem.asBytes(&thread_context_ptr.Eax),
- 1 => mem.asBytes(&thread_context_ptr.Ecx),
- 2 => mem.asBytes(&thread_context_ptr.Edx),
- 3 => mem.asBytes(&thread_context_ptr.Ebx),
- 4 => mem.asBytes(&thread_context_ptr.Esp),
- 5 => mem.asBytes(&thread_context_ptr.Ebp),
- 6 => mem.asBytes(&thread_context_ptr.Esi),
- 7 => mem.asBytes(&thread_context_ptr.Edi),
- 8 => mem.asBytes(&thread_context_ptr.Eip),
- 9 => mem.asBytes(&thread_context_ptr.EFlags),
- 10 => mem.asBytes(&thread_context_ptr.SegCs),
- 11 => mem.asBytes(&thread_context_ptr.SegSs),
- 12 => mem.asBytes(&thread_context_ptr.SegDs),
- 13 => mem.asBytes(&thread_context_ptr.SegEs),
- 14 => mem.asBytes(&thread_context_ptr.SegFs),
- 15 => mem.asBytes(&thread_context_ptr.SegGs),
- else => error.InvalidRegister,
- },
- .x86_64 => switch (reg_number) {
- 0 => mem.asBytes(&thread_context_ptr.Rax),
- 1 => mem.asBytes(&thread_context_ptr.Rdx),
- 2 => mem.asBytes(&thread_context_ptr.Rcx),
- 3 => mem.asBytes(&thread_context_ptr.Rbx),
- 4 => mem.asBytes(&thread_context_ptr.Rsi),
- 5 => mem.asBytes(&thread_context_ptr.Rdi),
- 6 => mem.asBytes(&thread_context_ptr.Rbp),
- 7 => mem.asBytes(&thread_context_ptr.Rsp),
- 8 => mem.asBytes(&thread_context_ptr.R8),
- 9 => mem.asBytes(&thread_context_ptr.R9),
- 10 => mem.asBytes(&thread_context_ptr.R10),
- 11 => mem.asBytes(&thread_context_ptr.R11),
- 12 => mem.asBytes(&thread_context_ptr.R12),
- 13 => mem.asBytes(&thread_context_ptr.R13),
- 14 => mem.asBytes(&thread_context_ptr.R14),
- 15 => mem.asBytes(&thread_context_ptr.R15),
- 16 => mem.asBytes(&thread_context_ptr.Rip),
- else => error.InvalidRegister,
- },
- .aarch64, .aarch64_be => switch (reg_number) {
- 0...30 => mem.asBytes(&thread_context_ptr.DUMMYUNIONNAME.X[reg_number]),
- 31 => mem.asBytes(&thread_context_ptr.Sp),
- 32 => mem.asBytes(&thread_context_ptr.Pc),
- else => error.InvalidRegister,
- },
- else => error.UnimplementedArch,
- };
- }
-
- if (posix.ucontext_t == void) return error.ThreadContextNotSupported;
-
- const ucontext_ptr = thread_context_ptr;
- return switch (builtin.cpu.arch) {
- .x86 => switch (builtin.os.tag) {
- .linux, .netbsd, .solaris, .illumos => switch (reg_number) {
- 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EAX]),
- 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ECX]),
- 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EDX]),
- 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBX]),
- 4...5 => if (reg_context) |r| bytes: {
- if (reg_number == 4) {
- break :bytes if (r.eh_frame and r.is_macho)
- mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBP])
- else
- mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESP]);
- } else {
- break :bytes if (r.eh_frame and r.is_macho)
- mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESP])
- else
- mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBP]);
- }
- } else error.RegisterContextRequired,
- 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESI]),
- 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EDI]),
- 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EIP]),
- 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EFL]),
- 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.CS]),
- 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.SS]),
- 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.DS]),
- 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ES]),
- 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.FS]),
- 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.GS]),
- 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs
- 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs
- else => error.InvalidRegister,
- },
- else => error.UnimplementedOs,
- },
- .x86_64 => switch (builtin.os.tag) {
- .linux, .solaris, .illumos => switch (reg_number) {
- 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RAX]),
- 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDX]),
- 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RCX]),
- 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RBX]),
- 4 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RSI]),
- 5 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDI]),
- 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RBP]),
- 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RSP]),
- 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R8]),
- 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R9]),
- 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R10]),
- 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R11]),
- 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R12]),
- 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R13]),
- 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R14]),
- 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R15]),
- 16 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RIP]),
- 17...32 => |i| if (builtin.os.tag.isSolarish())
- mem.asBytes(&ucontext_ptr.mcontext.fpregs.chip_state.xmm[i - 17])
- else
- mem.asBytes(&ucontext_ptr.mcontext.fpregs.xmm[i - 17]),
- else => error.InvalidRegister,
- },
- .freebsd => switch (reg_number) {
- 0 => mem.asBytes(&ucontext_ptr.mcontext.rax),
- 1 => mem.asBytes(&ucontext_ptr.mcontext.rdx),
- 2 => mem.asBytes(&ucontext_ptr.mcontext.rcx),
- 3 => mem.asBytes(&ucontext_ptr.mcontext.rbx),
- 4 => mem.asBytes(&ucontext_ptr.mcontext.rsi),
- 5 => mem.asBytes(&ucontext_ptr.mcontext.rdi),
- 6 => mem.asBytes(&ucontext_ptr.mcontext.rbp),
- 7 => mem.asBytes(&ucontext_ptr.mcontext.rsp),
- 8 => mem.asBytes(&ucontext_ptr.mcontext.r8),
- 9 => mem.asBytes(&ucontext_ptr.mcontext.r9),
- 10 => mem.asBytes(&ucontext_ptr.mcontext.r10),
- 11 => mem.asBytes(&ucontext_ptr.mcontext.r11),
- 12 => mem.asBytes(&ucontext_ptr.mcontext.r12),
- 13 => mem.asBytes(&ucontext_ptr.mcontext.r13),
- 14 => mem.asBytes(&ucontext_ptr.mcontext.r14),
- 15 => mem.asBytes(&ucontext_ptr.mcontext.r15),
- 16 => mem.asBytes(&ucontext_ptr.mcontext.rip),
- // TODO: Extract xmm state from mcontext.fpstate?
- else => error.InvalidRegister,
- },
- .openbsd => switch (reg_number) {
- 0 => mem.asBytes(&ucontext_ptr.sc_rax),
- 1 => mem.asBytes(&ucontext_ptr.sc_rdx),
- 2 => mem.asBytes(&ucontext_ptr.sc_rcx),
- 3 => mem.asBytes(&ucontext_ptr.sc_rbx),
- 4 => mem.asBytes(&ucontext_ptr.sc_rsi),
- 5 => mem.asBytes(&ucontext_ptr.sc_rdi),
- 6 => mem.asBytes(&ucontext_ptr.sc_rbp),
- 7 => mem.asBytes(&ucontext_ptr.sc_rsp),
- 8 => mem.asBytes(&ucontext_ptr.sc_r8),
- 9 => mem.asBytes(&ucontext_ptr.sc_r9),
- 10 => mem.asBytes(&ucontext_ptr.sc_r10),
- 11 => mem.asBytes(&ucontext_ptr.sc_r11),
- 12 => mem.asBytes(&ucontext_ptr.sc_r12),
- 13 => mem.asBytes(&ucontext_ptr.sc_r13),
- 14 => mem.asBytes(&ucontext_ptr.sc_r14),
- 15 => mem.asBytes(&ucontext_ptr.sc_r15),
- 16 => mem.asBytes(&ucontext_ptr.sc_rip),
- // TODO: Extract xmm state from sc_fpstate?
- else => error.InvalidRegister,
- },
- .macos, .ios => switch (reg_number) {
- 0 => mem.asBytes(&ucontext_ptr.mcontext.ss.rax),
- 1 => mem.asBytes(&ucontext_ptr.mcontext.ss.rdx),
- 2 => mem.asBytes(&ucontext_ptr.mcontext.ss.rcx),
- 3 => mem.asBytes(&ucontext_ptr.mcontext.ss.rbx),
- 4 => mem.asBytes(&ucontext_ptr.mcontext.ss.rsi),
- 5 => mem.asBytes(&ucontext_ptr.mcontext.ss.rdi),
- 6 => mem.asBytes(&ucontext_ptr.mcontext.ss.rbp),
- 7 => mem.asBytes(&ucontext_ptr.mcontext.ss.rsp),
- 8 => mem.asBytes(&ucontext_ptr.mcontext.ss.r8),
- 9 => mem.asBytes(&ucontext_ptr.mcontext.ss.r9),
- 10 => mem.asBytes(&ucontext_ptr.mcontext.ss.r10),
- 11 => mem.asBytes(&ucontext_ptr.mcontext.ss.r11),
- 12 => mem.asBytes(&ucontext_ptr.mcontext.ss.r12),
- 13 => mem.asBytes(&ucontext_ptr.mcontext.ss.r13),
- 14 => mem.asBytes(&ucontext_ptr.mcontext.ss.r14),
- 15 => mem.asBytes(&ucontext_ptr.mcontext.ss.r15),
- 16 => mem.asBytes(&ucontext_ptr.mcontext.ss.rip),
- else => error.InvalidRegister,
- },
- else => error.UnimplementedOs,
- },
- .arm, .armeb, .thumb, .thumbeb => switch (builtin.os.tag) {
- .linux => switch (reg_number) {
- 0 => mem.asBytes(&ucontext_ptr.mcontext.arm_r0),
- 1 => mem.asBytes(&ucontext_ptr.mcontext.arm_r1),
- 2 => mem.asBytes(&ucontext_ptr.mcontext.arm_r2),
- 3 => mem.asBytes(&ucontext_ptr.mcontext.arm_r3),
- 4 => mem.asBytes(&ucontext_ptr.mcontext.arm_r4),
- 5 => mem.asBytes(&ucontext_ptr.mcontext.arm_r5),
- 6 => mem.asBytes(&ucontext_ptr.mcontext.arm_r6),
- 7 => mem.asBytes(&ucontext_ptr.mcontext.arm_r7),
- 8 => mem.asBytes(&ucontext_ptr.mcontext.arm_r8),
- 9 => mem.asBytes(&ucontext_ptr.mcontext.arm_r9),
- 10 => mem.asBytes(&ucontext_ptr.mcontext.arm_r10),
- 11 => mem.asBytes(&ucontext_ptr.mcontext.arm_fp),
- 12 => mem.asBytes(&ucontext_ptr.mcontext.arm_ip),
- 13 => mem.asBytes(&ucontext_ptr.mcontext.arm_sp),
- 14 => mem.asBytes(&ucontext_ptr.mcontext.arm_lr),
- 15 => mem.asBytes(&ucontext_ptr.mcontext.arm_pc),
- // CPSR is not allocated a register number (See: https://github.com/ARM-software/abi-aa/blob/main/aadwarf32/aadwarf32.rst, Section 4.1)
- else => error.InvalidRegister,
- },
- else => error.UnimplementedOs,
- },
- .aarch64, .aarch64_be => switch (builtin.os.tag) {
- .macos, .ios, .watchos => switch (reg_number) {
- 0...28 => mem.asBytes(&ucontext_ptr.mcontext.ss.regs[reg_number]),
- 29 => mem.asBytes(&ucontext_ptr.mcontext.ss.fp),
- 30 => mem.asBytes(&ucontext_ptr.mcontext.ss.lr),
- 31 => mem.asBytes(&ucontext_ptr.mcontext.ss.sp),
- 32 => mem.asBytes(&ucontext_ptr.mcontext.ss.pc),
-
- // TODO: Find storage for this state
- //34 => mem.asBytes(&ucontext_ptr.ra_sign_state),
-
- // V0-V31
- 64...95 => mem.asBytes(&ucontext_ptr.mcontext.ns.q[reg_number - 64]),
- else => error.InvalidRegister,
- },
- .netbsd => switch (reg_number) {
- 0...34 => mem.asBytes(&ucontext_ptr.mcontext.gregs[reg_number]),
- else => error.InvalidRegister,
- },
- .freebsd => switch (reg_number) {
- 0...29 => mem.asBytes(&ucontext_ptr.mcontext.gpregs.x[reg_number]),
- 30 => mem.asBytes(&ucontext_ptr.mcontext.gpregs.lr),
- 31 => mem.asBytes(&ucontext_ptr.mcontext.gpregs.sp),
-
- // TODO: This seems wrong, but it was in the previous debug.zig code for mapping PC, check this
- 32 => mem.asBytes(&ucontext_ptr.mcontext.gpregs.elr),
-
- else => error.InvalidRegister,
- },
- .openbsd => switch (reg_number) {
- 0...30 => mem.asBytes(&ucontext_ptr.sc_x[reg_number]),
- 31 => mem.asBytes(&ucontext_ptr.sc_sp),
- 32 => mem.asBytes(&ucontext_ptr.sc_lr),
- 33 => mem.asBytes(&ucontext_ptr.sc_elr),
- 34 => mem.asBytes(&ucontext_ptr.sc_spsr),
- else => error.InvalidRegister,
- },
- else => switch (reg_number) {
- 0...30 => mem.asBytes(&ucontext_ptr.mcontext.regs[reg_number]),
- 31 => mem.asBytes(&ucontext_ptr.mcontext.sp),
- 32 => mem.asBytes(&ucontext_ptr.mcontext.pc),
- else => error.InvalidRegister,
- },
- },
- else => error.UnimplementedArch,
- };
-}
-
-/// Returns a pointer to a register stored in a ThreadContext, preserving the
-/// pointer attributes of the context.
-pub fn regValueNative(
- thread_context_ptr: *std.debug.ThreadContext,
- reg_number: u8,
- reg_context: ?RegisterContext,
-) !*align(1) usize {
- const reg_bytes = try regBytes(thread_context_ptr, reg_number, reg_context);
- if (@sizeOf(usize) != reg_bytes.len) return error.IncompatibleRegisterSize;
- return @ptrCast(reg_bytes);
-}
diff --git a/lib/std/debug/Dwarf/expression.zig b/lib/std/debug/Dwarf/expression.zig
index 68b49587c2..b1751af749 100644
--- a/lib/std/debug/Dwarf/expression.zig
+++ b/lib/std/debug/Dwarf/expression.zig
@@ -5,12 +5,17 @@ const native_endian = native_arch.endian();
const std = @import("std");
const leb = std.leb;
const OP = std.dwarf.OP;
-const abi = std.debug.Dwarf.abi;
const mem = std.mem;
const assert = std.debug.assert;
const testing = std.testing;
const Writer = std.Io.Writer;
+const regNative = std.debug.SelfInfo.DwarfUnwindContext.regNative;
+
+const ip_reg_num = std.debug.Dwarf.ipRegNum(native_arch).?;
+const fp_reg_num = std.debug.Dwarf.fpRegNum(native_arch);
+const sp_reg_num = std.debug.Dwarf.spRegNum(native_arch);
+
/// Expressions can be evaluated in different contexts, each requiring its own set of inputs.
/// Callers should specify all the fields relevant to their context. If a field is required
/// by the expression and it isn't in the context, error.IncompleteExpressionContext is returned.
@@ -23,9 +28,7 @@ pub const Context = struct {
object_address: ?*const anyopaque = null,
/// .debug_addr section
debug_addr: ?[]const u8 = null,
- /// Thread context
- thread_context: ?*std.debug.ThreadContext = null,
- reg_context: ?abi.RegisterContext = null,
+ cpu_context: ?*std.debug.cpu_context.Native = null,
/// Call frame address, if in a CFI context
cfa: ?usize = null,
/// This expression is a sub-expression from an OP.entry_value instruction
@@ -62,7 +65,9 @@ pub const Error = error{
InvalidTypeLength,
TruncatedIntegralType,
-} || abi.RegBytesError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero, ReadFailed };
+
+ IncompatibleRegisterSize,
+} || std.debug.cpu_context.DwarfRegisterError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero, ReadFailed };
/// A stack machine that can decode and run DWARF expressions.
/// Expressions can be decoded for non-native address size and endianness,
@@ -369,29 +374,20 @@ pub fn StackMachine(comptime options: Options) type {
OP.breg0...OP.breg31,
OP.bregx,
=> {
- if (context.thread_context == null) return error.IncompleteExpressionContext;
-
- const base_register = operand.?.base_register;
- var value: i64 = @intCast(mem.readInt(usize, (try abi.regBytes(
- context.thread_context.?,
- base_register.base_register,
- context.reg_context,
- ))[0..@sizeOf(usize)], native_endian));
- value += base_register.offset;
- try self.stack.append(allocator, .{ .generic = @intCast(value) });
+ const cpu_context = context.cpu_context orelse return error.IncompleteExpressionContext;
+
+ const br = operand.?.base_register;
+ const value: i64 = @intCast((try regNative(cpu_context, br.base_register)).*);
+ try self.stack.append(allocator, .{ .generic = @intCast(value + br.offset) });
},
OP.regval_type => {
- const register_type = operand.?.register_type;
- const value = mem.readInt(usize, (try abi.regBytes(
- context.thread_context.?,
- register_type.register,
- context.reg_context,
- ))[0..@sizeOf(usize)], native_endian);
+ const cpu_context = context.cpu_context orelse return error.IncompleteExpressionContext;
+ const rt = operand.?.register_type;
try self.stack.append(allocator, .{
.regval_type = .{
- .type_offset = register_type.type_offset,
+ .type_offset = rt.type_offset,
.type_size = @sizeOf(addr_type),
- .value = value,
+ .value = (try regNative(cpu_context, rt.register)).*,
},
});
},
@@ -734,14 +730,14 @@ pub fn StackMachine(comptime options: Options) type {
// TODO: The spec states that this sub-expression needs to observe the state (ie. registers)
// as it was upon entering the current subprogram. If this isn't being called at the
- // end of a frame unwind operation, an additional ThreadContext with this state will be needed.
+ // end of a frame unwind operation, an additional cpu_context.Native with this state will be needed.
if (isOpcodeRegisterLocation(block[0])) {
- if (context.thread_context == null) return error.IncompleteExpressionContext;
+ const cpu_context = context.cpu_context orelse return error.IncompleteExpressionContext;
var block_stream: std.Io.Reader = .fixed(block);
const register = (try readOperand(&block_stream, block[0], context)).?.register;
- const value = mem.readInt(usize, (try abi.regBytes(context.thread_context.?, register, context.reg_context))[0..@sizeOf(usize)], native_endian);
+ const value = (try regNative(cpu_context, register)).*;
try self.stack.append(allocator, .{ .generic = value });
} else {
var stack_machine: Self = .{};
@@ -1149,55 +1145,39 @@ test "basics" {
}
// Register values
- if (@sizeOf(std.debug.ThreadContext) != 0) {
+ if (std.debug.cpu_context.Native != noreturn) {
stack_machine.reset();
program.clearRetainingCapacity();
- const reg_context = abi.RegisterContext{
- .eh_frame = true,
- .is_macho = builtin.os.tag == .macos,
- };
- var thread_context: std.debug.ThreadContext = undefined;
- std.debug.relocateContext(&thread_context);
+ var cpu_context: std.debug.cpu_context.Native = undefined;
const context = Context{
- .thread_context = &thread_context,
- .reg_context = reg_context,
+ .cpu_context = &cpu_context,
};
- // Only test register operations on arch / os that have them implemented
- if (abi.regBytes(&thread_context, 0, reg_context)) |reg_bytes| {
-
- // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it
-
- mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian);
- (try abi.regValueNative(&thread_context, abi.fpRegNum(native_arch, reg_context), reg_context)).* = 1;
- (try abi.regValueNative(&thread_context, abi.spRegNum(native_arch, reg_context), reg_context)).* = 2;
- (try abi.regValueNative(&thread_context, abi.ipRegNum(native_arch).?, reg_context)).* = 3;
-
- try b.writeBreg(writer, abi.fpRegNum(native_arch, reg_context), @as(usize, 100));
- try b.writeBreg(writer, abi.spRegNum(native_arch, reg_context), @as(usize, 200));
- try b.writeBregx(writer, abi.ipRegNum(native_arch).?, @as(usize, 300));
- try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400));
-
- _ = try stack_machine.run(program.written(), allocator, context, 0);
-
- const regval_type = stack_machine.stack.pop().?.regval_type;
- try testing.expectEqual(@as(usize, 400), regval_type.type_offset);
- try testing.expectEqual(@as(u8, @sizeOf(usize)), regval_type.type_size);
- try testing.expectEqual(@as(usize, 0xee), regval_type.value);
-
- try testing.expectEqual(@as(usize, 303), stack_machine.stack.pop().?.generic);
- try testing.expectEqual(@as(usize, 202), stack_machine.stack.pop().?.generic);
- try testing.expectEqual(@as(usize, 101), stack_machine.stack.pop().?.generic);
- } else |err| {
- switch (err) {
- error.UnimplementedArch,
- error.UnimplementedOs,
- error.ThreadContextNotSupported,
- => {},
- else => return err,
- }
- }
+ const reg_bytes = try cpu_context.dwarfRegisterBytes(0);
+
+ // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it
+
+ mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian);
+ (try regNative(&cpu_context, fp_reg_num)).* = 1;
+ (try regNative(&cpu_context, sp_reg_num)).* = 2;
+ (try regNative(&cpu_context, ip_reg_num)).* = 3;
+
+ try b.writeBreg(writer, fp_reg_num, @as(usize, 100));
+ try b.writeBreg(writer, sp_reg_num, @as(usize, 200));
+ try b.writeBregx(writer, ip_reg_num, @as(usize, 300));
+ try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400));
+
+ _ = try stack_machine.run(program.written(), allocator, context, 0);
+
+ const regval_type = stack_machine.stack.pop().?.regval_type;
+ try testing.expectEqual(@as(usize, 400), regval_type.type_offset);
+ try testing.expectEqual(@as(u8, @sizeOf(usize)), regval_type.type_size);
+ try testing.expectEqual(@as(usize, 0xee), regval_type.value);
+
+ try testing.expectEqual(@as(usize, 303), stack_machine.stack.pop().?.generic);
+ try testing.expectEqual(@as(usize, 202), stack_machine.stack.pop().?.generic);
+ try testing.expectEqual(@as(usize, 101), stack_machine.stack.pop().?.generic);
}
// Stack operations
@@ -1585,38 +1565,24 @@ test "basics" {
}
// Register location description
- const reg_context = abi.RegisterContext{
- .eh_frame = true,
- .is_macho = builtin.os.tag == .macos,
- };
- var thread_context: std.debug.ThreadContext = undefined;
- std.debug.relocateContext(&thread_context);
+ var cpu_context: std.debug.cpu_context.Native = undefined;
+ std.debug.relocateContext(&cpu_context);
context = Context{
- .thread_context = &thread_context,
- .reg_context = reg_context,
+ .cpu_context = &cpu_context,
};
- if (abi.regBytes(&thread_context, 0, reg_context)) |reg_bytes| {
- mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian);
+ const reg_bytes = try cpu_context.dwarfRegisterBytes(0);
+ mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian);
- var sub_program: std.Io.Writer.Allocating = .init(allocator);
- defer sub_program.deinit();
- const sub_writer = &sub_program.writer;
- try b.writeReg(sub_writer, 0);
+ var sub_program: std.Io.Writer.Allocating = .init(allocator);
+ defer sub_program.deinit();
+ const sub_writer = &sub_program.writer;
+ try b.writeReg(sub_writer, 0);
- stack_machine.reset();
- program.clearRetainingCapacity();
- try b.writeEntryValue(writer, sub_program.written());
- _ = try stack_machine.run(program.written(), allocator, context, null);
- try testing.expectEqual(@as(usize, 0xee), stack_machine.stack.pop().?.generic);
- } else |err| {
- switch (err) {
- error.UnimplementedArch,
- error.UnimplementedOs,
- error.ThreadContextNotSupported,
- => {},
- else => return err,
- }
- }
+ stack_machine.reset();
+ program.clearRetainingCapacity();
+ try b.writeEntryValue(writer, sub_program.written());
+ _ = try stack_machine.run(program.written(), allocator, context, null);
+ try testing.expectEqual(@as(usize, 0xee), stack_machine.stack.pop().?.generic);
}
}
diff --git a/lib/std/debug/SelfInfo.zig b/lib/std/debug/SelfInfo.zig
index c3243edeb9..321e67bb7c 100644
--- a/lib/std/debug/SelfInfo.zig
+++ b/lib/std/debug/SelfInfo.zig
@@ -11,8 +11,7 @@ const mem = std.mem;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const Dwarf = std.debug.Dwarf;
-const regBytes = Dwarf.abi.regBytes;
-const regValueNative = Dwarf.abi.regValueNative;
+const CpuContext = std.debug.cpu_context.Native;
const root = @import("root");
@@ -38,8 +37,6 @@ pub const Error = error{
pub const target_supported: bool = Module != void;
/// Indicates whether the `SelfInfo` implementation has support for unwinding on this target.
-///
-/// For whether DWARF unwinding is *theoretically* possible, see `Dwarf.abi.supportsUnwinding`.
pub const supports_unwinding: bool = target_supported and Module.supports_unwinding;
pub const UnwindContext = if (supports_unwinding) Module.UnwindContext;
@@ -120,7 +117,7 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize)
/// pub const UnwindContext = struct {
/// /// A PC value inside the function of the last unwound frame.
/// pc: usize,
-/// pub fn init(tc: *std.debug.ThreadContext, gpa: Allocator) Allocator.Error!UnwindContext;
+/// pub fn init(ctx: *std.debug.cpu_context.Native, gpa: Allocator) Allocator.Error!UnwindContext;
/// pub fn deinit(uc: *UnwindContext, gpa: Allocator) void;
/// /// Returns the frame pointer associated with the last unwound stack frame. If the frame
/// /// pointer is unknown, 0 may be returned instead.
@@ -141,9 +138,26 @@ const Module: type = Module: {
break :Module root.debug.Module;
}
break :Module switch (native_os) {
- .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris, .illumos => @import("SelfInfo/ElfModule.zig"),
- .macos, .ios, .watchos, .tvos, .visionos => @import("SelfInfo/DarwinModule.zig"),
- .uefi, .windows => @import("SelfInfo/WindowsModule.zig"),
+ .linux,
+ .netbsd,
+ .freebsd,
+ .dragonfly,
+ .openbsd,
+ .solaris,
+ .illumos,
+ => @import("SelfInfo/ElfModule.zig"),
+
+ .macos,
+ .ios,
+ .watchos,
+ .tvos,
+ .visionos,
+ => @import("SelfInfo/DarwinModule.zig"),
+
+ .uefi,
+ .windows,
+ => @import("SelfInfo/WindowsModule.zig"),
+
else => void,
};
};
@@ -153,26 +167,25 @@ const Module: type = Module: {
pub const DwarfUnwindContext = struct {
cfa: ?usize,
pc: usize,
- thread_context: *std.debug.ThreadContext,
- reg_context: Dwarf.abi.RegisterContext,
+ cpu_context: CpuContext,
vm: Dwarf.Unwind.VirtualMachine,
stack_machine: Dwarf.expression.StackMachine(.{ .call_frame_context = true }),
- pub fn init(thread_context: *std.debug.ThreadContext, gpa: Allocator) error{}!DwarfUnwindContext {
+ pub fn init(cpu_context: *const CpuContext) DwarfUnwindContext {
comptime assert(supports_unwinding);
- _ = gpa;
- const ip_reg_num = Dwarf.abi.ipRegNum(native_arch).?;
- const raw_pc_ptr = regValueNative(thread_context, ip_reg_num, null) catch {
- unreachable; // error means unsupported, in which case `supports_unwinding` should have been `false`
+ // `@constCast` is safe because we aren't going to store to the resulting pointer.
+ const raw_pc_ptr = regNative(@constCast(cpu_context), ip_reg_num) catch |err| switch (err) {
+ error.InvalidRegister => unreachable, // `ip_reg_num` is definitely valid
+ error.UnsupportedRegister => unreachable, // the implementation needs to support ip
+ error.IncompatibleRegisterSize => unreachable, // ip is definitely `usize`-sized
};
const pc = stripInstructionPtrAuthCode(raw_pc_ptr.*);
return .{
.cfa = null,
.pc = pc,
- .thread_context = thread_context,
- .reg_context = undefined,
+ .cpu_context = cpu_context.*,
.vm = .{},
.stack_machine = .{},
};
@@ -185,17 +198,25 @@ pub const DwarfUnwindContext = struct {
}
pub fn getFp(self: *const DwarfUnwindContext) usize {
- return (regValueNative(self.thread_context, Dwarf.abi.fpRegNum(native_arch, self.reg_context), self.reg_context) catch return 0).*;
+ // `@constCast` is safe because we aren't going to store to the resulting pointer.
+ const ptr = regNative(@constCast(&self.cpu_context), fp_reg_num) catch |err| switch (err) {
+ error.InvalidRegister => unreachable, // `fp_reg_num` is definitely valid
+ error.UnsupportedRegister => unreachable, // the implementation needs to support fp
+ error.IncompatibleRegisterSize => unreachable, // fp is a pointer so is `usize`-sized
+ };
+ return ptr.*;
}
- /// Resolves the register rule and places the result into `out` (see regBytes)
+ /// Resolves the register rule and places the result into `out` (see regBytes). Returns `true`
+ /// iff the rule was undefined. This is *not* the same as `col.rule == .undefined`, because the
+ /// default rule may be undefined.
pub fn resolveRegisterRule(
context: *DwarfUnwindContext,
gpa: Allocator,
col: Dwarf.Unwind.VirtualMachine.Column,
expression_context: std.debug.Dwarf.expression.Context,
out: []u8,
- ) !void {
+ ) !bool {
switch (col.rule) {
.default => {
const register = col.register orelse return error.InvalidRegister;
@@ -203,58 +224,74 @@ pub const DwarfUnwindContext = struct {
// See the doc comment on `Dwarf.Unwind.VirtualMachine.RegisterRule.default`.
if (builtin.cpu.arch.isAARCH64() and register >= 19 and register <= 18) {
// Callee-saved registers are initialized as if they had the .same_value rule
- const src = try regBytes(context.thread_context, register, context.reg_context);
+ const src = try context.cpu_context.dwarfRegisterBytes(register);
if (src.len != out.len) return error.RegisterSizeMismatch;
@memcpy(out, src);
- return;
+ return false;
}
@memset(out, undefined);
+ return true;
},
.undefined => {
@memset(out, undefined);
+ return true;
},
.same_value => {
// TODO: This copy could be eliminated if callers always copy the state then call this function to update it
const register = col.register orelse return error.InvalidRegister;
- const src = try regBytes(context.thread_context, register, context.reg_context);
+ const src = try context.cpu_context.dwarfRegisterBytes(register);
if (src.len != out.len) return error.RegisterSizeMismatch;
@memcpy(out, src);
+ return false;
},
.offset => |offset| {
- if (context.cfa) |cfa| {
- const addr = try applyOffset(cfa, offset);
- const ptr: *const usize = @ptrFromInt(addr);
- mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);
- } else return error.InvalidCFA;
+ const cfa = context.cfa orelse return error.InvalidCFA;
+ const addr = try applyOffset(cfa, offset);
+ const ptr: *const usize = @ptrFromInt(addr);
+ mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);
+ return false;
},
.val_offset => |offset| {
- if (context.cfa) |cfa| {
- mem.writeInt(usize, out[0..@sizeOf(usize)], try applyOffset(cfa, offset), native_endian);
- } else return error.InvalidCFA;
+ const cfa = context.cfa orelse return error.InvalidCFA;
+ mem.writeInt(usize, out[0..@sizeOf(usize)], try applyOffset(cfa, offset), native_endian);
+ return false;
},
.register => |register| {
- const src = try regBytes(context.thread_context, register, context.reg_context);
+ const src = try context.cpu_context.dwarfRegisterBytes(register);
if (src.len != out.len) return error.RegisterSizeMismatch;
@memcpy(out, src);
+ return false;
},
.expression => |expression| {
context.stack_machine.reset();
- const value = try context.stack_machine.run(expression, gpa, expression_context, context.cfa.?);
- const addr = if (value) |v| blk: {
- if (v != .generic) return error.InvalidExpressionValue;
- break :blk v.generic;
- } else return error.NoExpressionValue;
-
+ const value = try context.stack_machine.run(
+ expression,
+ gpa,
+ expression_context,
+ context.cfa.?,
+ ) orelse return error.NoExpressionValue;
+ const addr = switch (value) {
+ .generic => |addr| addr,
+ else => return error.InvalidExpressionValue,
+ };
const ptr: *usize = @ptrFromInt(addr);
mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);
+ return false;
},
.val_expression => |expression| {
context.stack_machine.reset();
- const value = try context.stack_machine.run(expression, gpa, expression_context, context.cfa.?);
- if (value) |v| {
- if (v != .generic) return error.InvalidExpressionValue;
- mem.writeInt(usize, out[0..@sizeOf(usize)], v.generic, native_endian);
- } else return error.NoExpressionValue;
+ const value = try context.stack_machine.run(
+ expression,
+ gpa,
+ expression_context,
+ context.cfa.?,
+ ) orelse return error.NoExpressionValue;
+ const val_raw = switch (value) {
+ .generic => |raw| raw,
+ else => return error.InvalidExpressionValue,
+ };
+ mem.writeInt(usize, out[0..@sizeOf(usize)], val_raw, native_endian);
+ return false;
},
.architectural => return error.UnimplementedRegisterRule,
}
@@ -277,9 +314,6 @@ pub const DwarfUnwindContext = struct {
return unwindFrameInner(context, gpa, unwind, load_offset, explicit_fde_offset) catch |err| switch (err) {
error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e,
- error.UnimplementedArch,
- error.UnimplementedOs,
- error.ThreadContextNotSupported,
error.UnimplementedRegisterRule,
error.UnsupportedAddrSize,
error.UnsupportedDwarfVersion,
@@ -289,10 +323,10 @@ pub const DwarfUnwindContext = struct {
error.UnimplementedTypedComparison,
error.UnimplementedTypeConversion,
error.UnknownExpressionOpcode,
+ error.UnsupportedRegister,
=> return error.UnsupportedDebugInfo,
error.InvalidRegister,
- error.RegisterContextRequired,
error.ReadFailed,
error.EndOfStream,
error.IncompatibleRegisterSize,
@@ -346,20 +380,17 @@ pub const DwarfUnwindContext = struct {
// may not reference other debug sections anyway.
var expression_context: Dwarf.expression.Context = .{
.format = format,
- .thread_context = context.thread_context,
- .reg_context = context.reg_context,
+ .cpu_context = &context.cpu_context,
.cfa = context.cfa,
};
context.vm.reset();
- context.reg_context.eh_frame = cie.version != 4;
- context.reg_context.is_macho = native_os.isDarwin();
const row = try context.vm.runTo(gpa, pc_vaddr, cie, fde, @sizeOf(usize), native_endian);
context.cfa = switch (row.cfa.rule) {
.val_offset => |offset| blk: {
const register = row.cfa.register orelse return error.InvalidCFARule;
- const value = (try regValueNative(context.thread_context, register, context.reg_context)).*;
+ const value = (try regNative(&context.cpu_context, register)).*;
break :blk try applyOffset(value, offset);
},
.expression => |expr| blk: {
@@ -381,73 +412,41 @@ pub const DwarfUnwindContext = struct {
expression_context.cfa = context.cfa;
- // Buffering the modifications is done because copying the thread context is not portable,
- // some implementations (ie. darwin) use internal pointers to the mcontext.
- var arena: std.heap.ArenaAllocator = .init(gpa);
- defer arena.deinit();
- const update_arena = arena.allocator();
-
- const RegisterUpdate = struct {
- // Backed by thread_context
- dest: []u8,
- // Backed by arena
- src: []const u8,
- prev: ?*@This(),
- };
-
- var update_tail: ?*RegisterUpdate = null;
var has_return_address = true;
+
+ // Create a copy of the CPU context, to which we will apply the new rules.
+ var new_cpu_context = context.cpu_context;
+
+ // On all implemented architectures, the CFA is defined as being the previous frame's SP
+ (try regNative(&new_cpu_context, sp_reg_num)).* = context.cfa.?;
+
for (context.vm.rowColumns(row)) |column| {
if (column.register) |register| {
+ const dest = try new_cpu_context.dwarfRegisterBytes(register);
+ const rule_undef = try context.resolveRegisterRule(gpa, column, expression_context, dest);
if (register == cie.return_address_register) {
- has_return_address = column.rule != .undefined;
+ has_return_address = !rule_undef;
}
-
- const dest = try regBytes(context.thread_context, register, context.reg_context);
- const src = try update_arena.alloc(u8, dest.len);
- try context.resolveRegisterRule(gpa, column, expression_context, src);
-
- const new_update = try update_arena.create(RegisterUpdate);
- new_update.* = .{
- .dest = dest,
- .src = src,
- .prev = update_tail,
- };
- update_tail = new_update;
}
}
- // On all implemented architectures, the CFA is defined as being the previous frame's SP
- (try regValueNative(context.thread_context, Dwarf.abi.spRegNum(native_arch, context.reg_context), context.reg_context)).* = context.cfa.?;
-
- while (update_tail) |tail| {
- @memcpy(tail.dest, tail.src);
- update_tail = tail.prev;
- }
+ const return_address: u64 = if (has_return_address) pc: {
+ const raw_ptr = try regNative(&new_cpu_context, cie.return_address_register);
+ break :pc stripInstructionPtrAuthCode(raw_ptr.*);
+ } else 0;
- if (has_return_address) {
- context.pc = stripInstructionPtrAuthCode((try regValueNative(
- context.thread_context,
- cie.return_address_register,
- context.reg_context,
- )).*);
- } else {
- context.pc = 0;
- }
+ (try regNative(new_cpu_context, ip_reg_num)).* = return_address;
- const ip_reg_num = Dwarf.abi.ipRegNum(native_arch).?;
- (try regValueNative(context.thread_context, ip_reg_num, context.reg_context)).* = context.pc;
+ // The new CPU context is complete; flush changes.
+ context.cpu_context = new_cpu_context;
- // The call instruction will have pushed the address of the instruction that follows the call as the return address.
- // This next instruction may be past the end of the function if the caller was `noreturn` (ie. the last instruction in
- // the function was the call). If we were to look up an FDE entry using the return address directly, it could end up
- // either not finding an FDE at all, or using the next FDE in the program, producing incorrect results. To prevent this,
- // we subtract one so that the next lookup is guaranteed to land inside the
- //
- // The exception to this rule is signal frames, where we return execution would be returned to the instruction
- // that triggered the handler.
- const return_address = context.pc;
- if (context.pc > 0 and !cie.is_signal_frame) context.pc -= 1;
+ // Also update the stored pc. However, because `return_address` points to the instruction
+ // *after* the call, it could (in the case of noreturn functions) actually point outside of
+ // the caller's address range, meaning an FDE lookup would fail. We can handle this by
+ // subtracting 1 from `return_address` so that the next lookup is guaranteed to land inside
+ // the `call` instruction`. The exception to this rule is signal frames, where the return
+ // address is the same instruction that triggered the handler.
+ context.pc = if (cie.is_signal_frame) return_address else return_address -| 1;
return return_address;
}
@@ -479,4 +478,18 @@ pub const DwarfUnwindContext = struct {
return ptr;
}
+
+ pub fn regNative(ctx: *CpuContext, num: u16) error{
+ InvalidRegister,
+ UnsupportedRegister,
+ IncompatibleRegisterSize,
+ }!*align(1) usize {
+ const bytes = try ctx.dwarfRegisterBytes(num);
+ if (bytes.len != @sizeOf(usize)) return error.IncompatibleRegisterSize;
+ return @ptrCast(bytes);
+ }
+
+ const ip_reg_num = Dwarf.ipRegNum(native_arch).?;
+ const fp_reg_num = Dwarf.fpRegNum(native_arch);
+ const sp_reg_num = Dwarf.spRegNum(native_arch);
};
diff --git a/lib/std/debug/SelfInfo/DarwinModule.zig b/lib/std/debug/SelfInfo/DarwinModule.zig
index 5bce65b89f..fc2f1c89bb 100644
--- a/lib/std/debug/SelfInfo/DarwinModule.zig
+++ b/lib/std/debug/SelfInfo/DarwinModule.zig
@@ -265,12 +265,9 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
error.OutOfMemory,
error.Unexpected,
=> |e| return e,
- error.UnimplementedArch,
- error.UnimplementedOs,
- error.ThreadContextNotSupported,
+ error.UnsupportedRegister,
=> return error.UnsupportedDebugInfo,
error.InvalidRegister,
- error.RegisterContextRequired,
error.IncompatibleRegisterSize,
=> return error.InvalidDebugInfo,
};
@@ -396,7 +393,6 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
};
if (entry.raw_encoding == 0) return error.MissingDebugInfo;
- const reg_context: Dwarf.abi.RegisterContext = .{ .eh_frame = false, .is_macho = true };
const encoding: macho.CompactUnwindEncoding = @bitCast(entry.raw_encoding);
const new_ip = switch (builtin.cpu.arch) {
@@ -405,16 +401,16 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
.RBP_FRAME => ip: {
const frame = encoding.value.x86_64.frame;
- const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*;
+ const fp = (try dwarfRegNative(&context.cpu_context, fp_reg_num)).*;
const new_sp = fp + 2 * @sizeOf(usize);
const ip_ptr = fp + @sizeOf(usize);
const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
- (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).* = new_fp;
- (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp;
- (try regValueNative(context.thread_context, ip_reg_num, reg_context)).* = new_ip;
+ (try dwarfRegNative(&context.cpu_context, fp_reg_num)).* = new_fp;
+ (try dwarfRegNative(&context.cpu_context, sp_reg_num)).* = new_sp;
+ (try dwarfRegNative(&context.cpu_context, ip_reg_num)).* = new_ip;
const regs: [5]u3 = .{
frame.reg0,
@@ -427,7 +423,7 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
if (reg == 0) continue;
const addr = fp - frame.frame_offset * @sizeOf(usize) + i * @sizeOf(usize);
const reg_number = try Dwarf.compactUnwindToDwarfRegNumber(reg);
- (try regValueNative(context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(addr)).*;
+ (try dwarfRegNative(&context.cpu_context, reg_number)).* = @as(*const usize, @ptrFromInt(addr)).*;
}
break :ip new_ip;
@@ -437,7 +433,7 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
=> ip: {
const frameless = encoding.value.x86_64.frameless;
- const sp = (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).*;
+ const sp = (try dwarfRegNative(&context.cpu_context, sp_reg_num)).*;
const stack_size: usize = stack_size: {
if (encoding.mode.x86_64 == .STACK_IMMD) {
break :stack_size @as(usize, frameless.stack.direct.stack_size) * @sizeOf(usize);
@@ -487,7 +483,7 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
var reg_addr = sp + stack_size - @sizeOf(usize) * @as(usize, reg_count + 1);
for (0..reg_count) |i| {
const reg_number = try Dwarf.compactUnwindToDwarfRegNumber(registers[i]);
- (try regValueNative(context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
+ (try dwarfRegNative(&context.cpu_context, reg_number)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
reg_addr += @sizeOf(usize);
}
@@ -497,8 +493,8 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
const new_sp = ip_ptr + @sizeOf(usize);
- (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp;
- (try regValueNative(context.thread_context, ip_reg_num, reg_context)).* = new_ip;
+ (try dwarfRegNative(&context.cpu_context, sp_reg_num)).* = new_sp;
+ (try dwarfRegNative(&context.cpu_context, ip_reg_num)).* = new_ip;
break :ip new_ip;
},
@@ -516,10 +512,10 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
.aarch64, .aarch64_be => switch (encoding.mode.arm64) {
.OLD => return error.UnsupportedDebugInfo,
.FRAMELESS => ip: {
- const sp = (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).*;
+ const sp = (try dwarfRegNative(&context.cpu_context, sp_reg_num)).*;
const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16;
- const new_ip = (try regValueNative(context.thread_context, 30, reg_context)).*;
- (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp;
+ const new_ip = (try dwarfRegNative(&context.cpu_context, 30)).*;
+ (try dwarfRegNative(&context.cpu_context, sp_reg_num)).* = new_sp;
break :ip new_ip;
},
.DWARF => {
@@ -535,15 +531,15 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
.FRAME => ip: {
const frame = encoding.value.arm64.frame;
- const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*;
+ const fp = (try dwarfRegNative(&context.cpu_context, fp_reg_num)).*;
const ip_ptr = fp + @sizeOf(usize);
var reg_addr = fp - @sizeOf(usize);
inline for (@typeInfo(@TypeOf(frame.x_reg_pairs)).@"struct".fields, 0..) |field, i| {
if (@field(frame.x_reg_pairs, field.name) != 0) {
- (try regValueNative(context.thread_context, 19 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
+ (try dwarfRegNative(&context.cpu_context, 19 + i)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
reg_addr += @sizeOf(usize);
- (try regValueNative(context.thread_context, 20 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
+ (try dwarfRegNative(&context.cpu_context, 20 + i)).* = @as(*const usize, @ptrFromInt(reg_addr)).*;
reg_addr += @sizeOf(usize);
}
}
@@ -552,12 +548,12 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
if (@field(frame.d_reg_pairs, field.name) != 0) {
// Only the lower half of the 128-bit V registers are restored during unwinding
{
- const dest: *align(1) usize = @ptrCast(try regBytes(context.thread_context, 64 + 8 + i, context.reg_context));
+ const dest: *align(1) usize = @ptrCast(try context.cpu_context.dwarfRegisterBytes(64 + 8 + i));
dest.* = @as(*const usize, @ptrFromInt(reg_addr)).*;
}
reg_addr += @sizeOf(usize);
{
- const dest: *align(1) usize = @ptrCast(try regBytes(context.thread_context, 64 + 9 + i, context.reg_context));
+ const dest: *align(1) usize = @ptrCast(try context.cpu_context.dwarfRegisterBytes(64 + 9 + i));
dest.* = @as(*const usize, @ptrFromInt(reg_addr)).*;
}
reg_addr += @sizeOf(usize);
@@ -567,8 +563,8 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
- (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).* = new_fp;
- (try regValueNative(context.thread_context, ip_reg_num, reg_context)).* = new_ip;
+ (try dwarfRegNative(&context.cpu_context, fp_reg_num)).* = new_fp;
+ (try dwarfRegNative(&context.cpu_context, ip_reg_num)).* = new_ip;
break :ip new_ip;
},
@@ -782,13 +778,9 @@ test {
_ = MachoSymbol;
}
-fn fpRegNum(reg_context: Dwarf.abi.RegisterContext) u8 {
- return Dwarf.abi.fpRegNum(builtin.target.cpu.arch, reg_context);
-}
-fn spRegNum(reg_context: Dwarf.abi.RegisterContext) u8 {
- return Dwarf.abi.spRegNum(builtin.target.cpu.arch, reg_context);
-}
-const ip_reg_num = Dwarf.abi.ipRegNum(builtin.target.cpu.arch).?;
+const ip_reg_num = Dwarf.ipRegNum(builtin.target.cpu.arch).?;
+const fp_reg_num = Dwarf.fpRegNum(builtin.target.cpu.arch);
+const sp_reg_num = Dwarf.spRegNum(builtin.target.cpu.arch);
/// Uses `mmap` to map the file at `path` into memory.
fn mapDebugInfoFile(path: []const u8) ![]align(std.heap.page_size_min) const u8 {
@@ -821,8 +813,7 @@ const mem = std.mem;
const posix = std.posix;
const testing = std.testing;
const Error = std.debug.SelfInfo.Error;
-const regBytes = Dwarf.abi.regBytes;
-const regValueNative = Dwarf.abi.regValueNative;
+const dwarfRegNative = std.debug.SelfInfo.DwarfUnwindContext.regNative;
const builtin = @import("builtin");
const native_endian = builtin.target.cpu.arch.endian();
diff --git a/lib/std/debug/SelfInfo/ElfModule.zig b/lib/std/debug/SelfInfo/ElfModule.zig
index 8a0acf8bb0..fde61d8140 100644
--- a/lib/std/debug/SelfInfo/ElfModule.zig
+++ b/lib/std/debug/SelfInfo/ElfModule.zig
@@ -26,7 +26,6 @@ pub fn key(m: ElfModule) usize {
pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) Error!ElfModule {
_ = cache;
_ = gpa;
- if (builtin.target.os.tag == .haiku) @panic("TODO implement lookup module for Haiku");
const DlIterContext = struct {
/// input
address: usize,
@@ -261,7 +260,7 @@ pub const supports_unwinding: bool = s: {
};
comptime {
if (supports_unwinding) {
- std.debug.assert(Dwarf.abi.supportsUnwinding(&builtin.target));
+ std.debug.assert(Dwarf.supportsUnwinding(&builtin.target));
}
}
diff --git a/lib/std/debug/SelfInfo/WindowsModule.zig b/lib/std/debug/SelfInfo/WindowsModule.zig
index 4bbc220c5b..8c88bd8b2f 100644
--- a/lib/std/debug/SelfInfo/WindowsModule.zig
+++ b/lib/std/debug/SelfInfo/WindowsModule.zig
@@ -295,11 +295,45 @@ pub const UnwindContext = struct {
pc: usize,
cur: windows.CONTEXT,
history_table: windows.UNWIND_HISTORY_TABLE,
- pub fn init(ctx: *const windows.CONTEXT, gpa: Allocator) Allocator.Error!UnwindContext {
- _ = gpa;
+ pub fn init(ctx: *const std.debug.cpu_context.Native) UnwindContext {
return .{
.pc = @returnAddress(),
- .cur = ctx.*,
+ .cur = switch (builtin.cpu.arch) {
+ .x86_64 => std.mem.zeroInit(windows.CONTEXT, .{
+ .Rax = ctx.gprs.get(.rax),
+ .Rcx = ctx.gprs.get(.rcx),
+ .Rdx = ctx.gprs.get(.rdx),
+ .Rbx = ctx.gprs.get(.rbx),
+ .Rsp = ctx.gprs.get(.rsp),
+ .Rbp = ctx.gprs.get(.rbp),
+ .Rsi = ctx.gprs.get(.rsi),
+ .Rdi = ctx.gprs.get(.rdi),
+ .R8 = ctx.gprs.get(.r8),
+ .R9 = ctx.gprs.get(.r9),
+ .R10 = ctx.gprs.get(.r10),
+ .R11 = ctx.gprs.get(.r11),
+ .R12 = ctx.gprs.get(.r12),
+ .R13 = ctx.gprs.get(.r13),
+ .R14 = ctx.gprs.get(.r14),
+ .R15 = ctx.gprs.get(.r15),
+ .Rip = ctx.gprs.get(.rip),
+ }),
+ .aarch64, .aarch64_be => .{
+ .ContextFlags = 0,
+ .Cpsr = 0,
+ .DUMMYUNIONNAME = .{ .X = ctx.x },
+ .Sp = ctx.sp,
+ .Pc = ctx.pc,
+ .V = @splat(.{ .B = @splat(0) }),
+ .Fpcr = 0,
+ .Fpsr = 0,
+ .Bcr = @splat(0),
+ .Bvr = @splat(0),
+ .Wcr = @splat(0),
+ .Wvr = @splat(0),
+ },
+ else => comptime unreachable,
+ },
.history_table = std.mem.zeroes(windows.UNWIND_HISTORY_TABLE),
};
}
diff --git a/lib/std/debug/cpu_context.zig b/lib/std/debug/cpu_context.zig
new file mode 100644
index 0000000000..9859575fa3
--- /dev/null
+++ b/lib/std/debug/cpu_context.zig
@@ -0,0 +1,1019 @@
+/// Register state for the native architecture, used by `std.debug` for stack unwinding.
+/// `noreturn` if there is no implementation for the native architecture.
+/// This can be overriden by exposing a declaration `root.debug.CpuContext`.
+pub const Native = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "CpuContext"))
+ root.debug.CpuContext
+else switch (native_arch) {
+ .x86 => X86,
+ .x86_64 => X86_64,
+ .arm, .armeb, .thumb, .thumbeb => Arm,
+ .aarch64, .aarch64_be => Aarch64,
+ else => noreturn,
+};
+
+pub const DwarfRegisterError = error{
+ InvalidRegister,
+ UnsupportedRegister,
+};
+
+pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native {
+ if (signal_ucontext_t == void) return null;
+ const uc: *const signal_ucontext_t = @ptrCast(@alignCast(ctx_ptr));
+ return switch (native_arch) {
+ .x86 => switch (native_os) {
+ .linux, .netbsd, .solaris, .illumos => .{ .gprs = .init(.{
+ .eax = uc.mcontext.gregs[std.posix.REG.EAX],
+ .ecx = uc.mcontext.gregs[std.posix.REG.ECX],
+ .edx = uc.mcontext.gregs[std.posix.REG.EDX],
+ .ebx = uc.mcontext.gregs[std.posix.REG.EBX],
+ .esp = uc.mcontext.gregs[std.posix.REG.ESP],
+ .ebp = uc.mcontext.gregs[std.posix.REG.EBP],
+ .esi = uc.mcontext.gregs[std.posix.REG.ESI],
+ .edi = uc.mcontext.gregs[std.posix.REG.EDI],
+ .eip = uc.mcontext.gregs[std.posix.REG.EIP],
+ }) },
+ else => null,
+ },
+ .x86_64 => switch (native_os) {
+ .linux, .solaris, .illumos => .{ .gprs = .init(.{
+ .rax = uc.mcontext.gregs[std.posix.REG.RAX],
+ .rdx = uc.mcontext.gregs[std.posix.REG.RDX],
+ .rcx = uc.mcontext.gregs[std.posix.REG.RCX],
+ .rbx = uc.mcontext.gregs[std.posix.REG.RBX],
+ .rsi = uc.mcontext.gregs[std.posix.REG.RSI],
+ .rdi = uc.mcontext.gregs[std.posix.REG.RDI],
+ .rbp = uc.mcontext.gregs[std.posix.REG.RBP],
+ .rsp = uc.mcontext.gregs[std.posix.REG.RSP],
+ .r8 = uc.mcontext.gregs[std.posix.REG.R8],
+ .r9 = uc.mcontext.gregs[std.posix.REG.R9],
+ .r10 = uc.mcontext.gregs[std.posix.REG.R10],
+ .r11 = uc.mcontext.gregs[std.posix.REG.R11],
+ .r12 = uc.mcontext.gregs[std.posix.REG.R12],
+ .r13 = uc.mcontext.gregs[std.posix.REG.R13],
+ .r14 = uc.mcontext.gregs[std.posix.REG.R14],
+ .r15 = uc.mcontext.gregs[std.posix.REG.R15],
+ .rip = uc.mcontext.gregs[std.posix.REG.RIP],
+ }) },
+ .freebsd => .{ .gprs = .init(.{
+ .rax = uc.mcontext.rax,
+ .rdx = uc.mcontext.rdx,
+ .rcx = uc.mcontext.rcx,
+ .rbx = uc.mcontext.rbx,
+ .rsi = uc.mcontext.rsi,
+ .rdi = uc.mcontext.rdi,
+ .rbp = uc.mcontext.rbp,
+ .rsp = uc.mcontext.rsp,
+ .r8 = uc.mcontext.r8,
+ .r9 = uc.mcontext.r9,
+ .r10 = uc.mcontext.r10,
+ .r11 = uc.mcontext.r11,
+ .r12 = uc.mcontext.r12,
+ .r13 = uc.mcontext.r13,
+ .r14 = uc.mcontext.r14,
+ .r15 = uc.mcontext.r15,
+ .rip = uc.mcontext.rip,
+ }) },
+ .openbsd => .{ .gprs = .init(.{
+ .rax = @bitCast(uc.sc_rax),
+ .rdx = @bitCast(uc.sc_rdx),
+ .rcx = @bitCast(uc.sc_rcx),
+ .rbx = @bitCast(uc.sc_rbx),
+ .rsi = @bitCast(uc.sc_rsi),
+ .rdi = @bitCast(uc.sc_rdi),
+ .rbp = @bitCast(uc.sc_rbp),
+ .rsp = @bitCast(uc.sc_rsp),
+ .r8 = @bitCast(uc.sc_r8),
+ .r9 = @bitCast(uc.sc_r9),
+ .r10 = @bitCast(uc.sc_r10),
+ .r11 = @bitCast(uc.sc_r11),
+ .r12 = @bitCast(uc.sc_r12),
+ .r13 = @bitCast(uc.sc_r13),
+ .r14 = @bitCast(uc.sc_r14),
+ .r15 = @bitCast(uc.sc_r15),
+ .rip = @bitCast(uc.sc_rip),
+ }) },
+ .macos, .ios => .{ .gprs = .init(.{
+ .rax = uc.mcontext.ss.rax,
+ .rdx = uc.mcontext.ss.rdx,
+ .rcx = uc.mcontext.ss.rcx,
+ .rbx = uc.mcontext.ss.rbx,
+ .rsi = uc.mcontext.ss.rsi,
+ .rdi = uc.mcontext.ss.rdi,
+ .rbp = uc.mcontext.ss.rbp,
+ .rsp = uc.mcontext.ss.rsp,
+ .r8 = uc.mcontext.ss.r8,
+ .r9 = uc.mcontext.ss.r9,
+ .r10 = uc.mcontext.ss.r10,
+ .r11 = uc.mcontext.ss.r11,
+ .r12 = uc.mcontext.ss.r12,
+ .r13 = uc.mcontext.ss.r13,
+ .r14 = uc.mcontext.ss.r14,
+ .r15 = uc.mcontext.ss.r15,
+ .rip = uc.mcontext.ss.rip,
+ }) },
+ else => null,
+ },
+ .arm, .armeb, .thumb, .thumbeb => switch (builtin.os.tag) {
+ .linux => .{
+ .r = .{
+ uc.mcontext.arm_r0,
+ uc.mcontext.arm_r1,
+ uc.mcontext.arm_r2,
+ uc.mcontext.arm_r3,
+ uc.mcontext.arm_r4,
+ uc.mcontext.arm_r5,
+ uc.mcontext.arm_r6,
+ uc.mcontext.arm_r7,
+ uc.mcontext.arm_r8,
+ uc.mcontext.arm_r9,
+ uc.mcontext.arm_r10,
+ uc.mcontext.arm_fp, // r11 = fp
+ uc.mcontext.arm_ip, // r12 = ip
+ uc.mcontext.arm_sp, // r13 = sp
+ uc.mcontext.arm_lr, // r14 = lr
+ uc.mcontext.arm_pc, // r15 = pc
+ },
+ },
+ else => null,
+ },
+ .aarch64, .aarch64_be => switch (builtin.os.tag) {
+ .macos, .ios, .tvos, .watchos, .visionos => .{
+ .x = uc.mcontext.ss.regs ++ @as([2]u64, .{
+ uc.mcontext.ss.fp, // x29 = fp
+ uc.mcontext.ss.lr, // x30 = lr
+ }),
+ .sp = uc.mcontext.ss.sp,
+ .pc = uc.mcontext.ss.pc,
+ },
+ .netbsd => .{
+ .x = uc.mcontext.gregs[0..31],
+ .sp = uc.mcontext.gregs[31],
+ .pc = uc.mcontext.gregs[32],
+ },
+ .freebsd => .{
+ .x = uc.mcontext.gpregs.x ++ @as([1]u64, .{
+ uc.mcontext.gpregs.lr, // x30 = lr
+ }),
+ .sp = uc.mcontext.gpregs.sp,
+ // On aarch64, the register ELR_LR1 defines the address to return to after handling
+ // a CPU exception (ELR is "Exception Link Register"). FreeBSD's ucontext_t uses
+ // this as the field name, but it's the same thing as the context's PC.
+ .pc = uc.mcontext.gpregs.elr,
+ },
+ .openbsd => .{
+ .x = uc.sc_x ++ .{uc.sc_lr},
+ .sp = uc.sc_sp,
+ // Not a bug; see freebsd above for explanation.
+ .pc = uc.sc_elr,
+ },
+ .linux => .{
+ .x = uc.mcontext.regs,
+ .sp = uc.mcontext.sp,
+ .pc = uc.mcontext.pc,
+ },
+ else => null,
+ },
+ else => null,
+ };
+}
+
+pub fn fromWindowsContext(ctx: *const std.os.windows.CONTEXT) Native {
+ return switch (native_arch) {
+ .x86 => .{ .gprs = .init(.{
+ .eax = ctx.Eax,
+ .ecx = ctx.Ecx,
+ .edx = ctx.Edx,
+ .ebx = ctx.Ebx,
+ .esp = ctx.Esp,
+ .ebp = ctx.Ebp,
+ .esi = ctx.Esi,
+ .edi = ctx.Edi,
+ .eip = ctx.Eip,
+ }) },
+ .x86_64 => .{ .gprs = .init(.{
+ .rax = ctx.Rax,
+ .rdx = ctx.Rdx,
+ .rcx = ctx.Rcx,
+ .rbx = ctx.Rbx,
+ .rsi = ctx.Rsi,
+ .rdi = ctx.Rdi,
+ .rbp = ctx.Rbp,
+ .rsp = ctx.Rsp,
+ .r8 = ctx.R8,
+ .r9 = ctx.R9,
+ .r10 = ctx.R10,
+ .r11 = ctx.R11,
+ .r12 = ctx.R12,
+ .r13 = ctx.R13,
+ .r14 = ctx.R14,
+ .r15 = ctx.R15,
+ .rip = ctx.Rip,
+ }) },
+ .aarch64, .aarch64_be => .{
+ .x = ctx.DUMMYUNIONNAME.X[0..31].*,
+ .sp = ctx.Sp,
+ .pc = ctx.Pc,
+ },
+ else => comptime unreachable,
+ };
+}
+
+pub const X86 = struct {
+ /// The first 8 registers here intentionally match the order of registers pushed
+ /// by PUSHA, which is also the order used by the DWARF register mappings.
+ pub const Gpr = enum {
+ // zig fmt: off
+ eax, ecx, edx, ebx,
+ esp, ebp, esi, edi,
+ eip,
+ // zig fmt: on
+ };
+ gprs: std.enums.EnumArray(Gpr, u32),
+
+ pub inline fn current() X86 {
+ var ctx: X86 = undefined;
+ asm volatile (
+ \\movl %%eax, 0x00(%%edi)
+ \\movl %%ecx, 0x04(%%edi)
+ \\movl %%edx, 0x08(%%edi)
+ \\movl %%ebx, 0x0c(%%edi)
+ \\movl %%esp, 0x10(%%edi)
+ \\movl %%ebp, 0x14(%%edi)
+ \\movl %%esi, 0x18(%%edi)
+ \\movl %%edi, 0x1c(%%edi)
+ \\call 1f
+ \\1:
+ \\popl 0x20(%%edi)
+ :
+ : [gprs] "{edi}" (&ctx.gprs.values),
+ : .{ .memory = true });
+ return ctx;
+ }
+
+ pub fn dwarfRegisterBytes(ctx: *X86, register_num: u16) DwarfRegisterError![]u8 {
+ // System V Application Binary Interface Intel386 Architecture Processor Supplement Version 1.1
+ // § 2.4.2 "DWARF Register Number Mapping"
+ switch (register_num) {
+ // The order of `Gpr` intentionally matches DWARF's mappings.
+ //
+ // x86-macos sometimes uses different mappings (ebp and esp are reversed when the unwind
+ // information is from `__eh_frame`). This deviation is not considered here, because
+ // x86-macos is a deprecated target which is not supported by the Zig Standard Library.
+ 0...8 => return @ptrCast(&ctx.gprs.values[register_num]),
+
+ 9 => return error.UnsupportedRegister, // rflags
+ 11...18 => return error.UnsupportedRegister, // st0 - st7
+ 21...28 => return error.UnsupportedRegister, // xmm0 - xmm7
+ 29...36 => return error.UnsupportedRegister, // mm0 - mm7
+ 39 => return error.UnsupportedRegister, // mxcsr
+ 40...45 => return error.UnsupportedRegister, // es, cs, ss, ds, fs, gs
+ 48 => return error.UnsupportedRegister, // tr
+ 49 => return error.UnsupportedRegister, // ldtr
+ 93...94 => return error.UnsupportedRegister, // fs.base, gs.base
+
+ else => return error.InvalidRegister,
+ }
+ }
+};
+
+pub const X86_64 = struct {
+ /// MLUGG TODO: explain this order. why does DWARF have this?
+ pub const Gpr = enum {
+ // zig fmt: off
+ rax, rdx, rcx, rbx,
+ rsi, rdi, rbp, rsp,
+ r8, r9, r10, r11,
+ r12, r13, r14, r15,
+ rip,
+ // zig fmt: on
+ };
+ gprs: std.enums.EnumArray(Gpr, u64),
+
+ pub inline fn current() X86_64 {
+ var ctx: X86_64 = undefined;
+ asm volatile (
+ \\movq %%rax, 0x00(%%rdi)
+ \\movq %%rdx, 0x08(%%rdi)
+ \\movq %%rcx, 0x10(%%rdi)
+ \\movq %%rbx, 0x18(%%rdi)
+ \\movq %%rsi, 0x20(%%rdi)
+ \\movq %%rdi, 0x28(%%rdi)
+ \\movq %%rbp, 0x30(%%rdi)
+ \\movq %%rsp, 0x38(%%rdi)
+ \\movq %%r8, 0x40(%%rdi)
+ \\movq %%r9, 0x48(%%rdi)
+ \\movq %%r10, 0x50(%%rdi)
+ \\movq %%r11, 0x58(%%rdi)
+ \\movq %%r12, 0x60(%%rdi)
+ \\movq %%r13, 0x68(%%rdi)
+ \\movq %%r14, 0x70(%%rdi)
+ \\movq %%r15, 0x78(%%rdi)
+ \\leaq (%%rip), %%rax
+ \\movq %%rax, 0x80(%%rdi)
+ \\movq 0x00(%%rdi), %%rax // restore saved rax
+ :
+ : [gprs] "{rdi}" (&ctx.gprs.values),
+ : .{ .memory = true });
+ return ctx;
+ }
+
+ pub fn dwarfRegisterBytes(ctx: *X86_64, register_num: u16) DwarfRegisterError![]u8 {
+ // System V Application Binary Interface AMD64 Architecture Processor Supplement
+ // § 3.6.2 "DWARF Register Number Mapping"
+ switch (register_num) {
+ // The order of `Gpr` intentionally matches DWARF's mappings.
+ 0...16 => return @ptrCast(&ctx.gprs.values[register_num]),
+
+ 17...32 => return error.UnsupportedRegister, // xmm0 - xmm15
+ 33...40 => return error.UnsupportedRegister, // st0 - st7
+ 41...48 => return error.UnsupportedRegister, // mm0 - mm7
+ 49 => return error.UnsupportedRegister, // rflags
+ 50...55 => return error.UnsupportedRegister, // es, cs, ss, ds, fs, gs
+ 58...59 => return error.UnsupportedRegister, // fs.base, gs.base
+ 62 => return error.UnsupportedRegister, // tr
+ 63 => return error.UnsupportedRegister, // ldtr
+ 64 => return error.UnsupportedRegister, // mxcsr
+ 65 => return error.UnsupportedRegister, // fcw
+ 66 => return error.UnsupportedRegister, // fsw
+
+ else => return error.InvalidRegister,
+ }
+ }
+};
+
+pub const Arm = struct {
+ /// The numbered general-purpose registers R0 - R15.
+ r: [16]u32,
+
+ pub inline fn current() Arm {
+ var ctx: Arm = undefined;
+ asm volatile (
+ \\// For compatibility with Thumb, we can't write r13 (sp) or r15 (pc) with stm.
+ \\stm r0, {r0-r12}
+ \\str r13, [r0, #0x34]
+ \\str r14, [r0, #0x38]
+ \\str r15, [r0, #0x3c]
+ :
+ : [r] "{r0}" (&ctx.r),
+ : .{ .memory = true });
+ return ctx;
+ }
+
+ pub fn dwarfRegisterBytes(ctx: *Arm, register_num: u16) DwarfRegisterError![]u8 {
+ // DWARF for the Arm(r) Architecture § 4.1 "DWARF register names"
+ switch (register_num) {
+ // The order of `Gpr` intentionally matches DWARF's mappings.
+ 0...15 => return @ptrCast(&ctx.r[register_num]),
+
+ 64...95 => return error.UnsupportedRegister, // S0 - S31
+ 96...103 => return error.UnsupportedRegister, // F0 - F7
+ 104...111 => return error.UnsupportedRegister, // wCGR0 - wCGR7, or ACC0 - ACC7
+ 112...127 => return error.UnsupportedRegister, // wR0 - wR15
+ 128 => return error.UnsupportedRegister, // SPSR
+ 129 => return error.UnsupportedRegister, // SPSR_FIQ
+ 130 => return error.UnsupportedRegister, // SPSR_IRQ
+ 131 => return error.UnsupportedRegister, // SPSR_ABT
+ 132 => return error.UnsupportedRegister, // SPSR_UND
+ 133 => return error.UnsupportedRegister, // SPSR_SVC
+ 143 => return error.UnsupportedRegister, // RA_AUTH_CODE
+ 144...150 => return error.UnsupportedRegister, // R8_USR - R14_USR
+ 151...157 => return error.UnsupportedRegister, // R8_FIQ - R14_FIQ
+ 158...159 => return error.UnsupportedRegister, // R13_IRQ - R14_IRQ
+ 160...161 => return error.UnsupportedRegister, // R13_ABT - R14_ABT
+ 162...163 => return error.UnsupportedRegister, // R13_UND - R14_UND
+ 164...165 => return error.UnsupportedRegister, // R13_SVC - R14_SVC
+ 192...199 => return error.UnsupportedRegister, // wC0 - wC7
+ 256...287 => return error.UnsupportedRegister, // D0 - D31
+ 320 => return error.UnsupportedRegister, // TPIDRURO
+ 321 => return error.UnsupportedRegister, // TPIDRURW
+ 322 => return error.UnsupportedRegister, // TPIDPR
+ 323 => return error.UnsupportedRegister, // HTPIDPR
+ 8192...16383 => return error.UnsupportedRegister, // Unspecified vendor co-processor register
+
+ else => return error.InvalidRegister,
+ }
+ }
+};
+
+/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
+pub const Aarch64 = extern struct {
+ /// The numbered general-purpose registers X0 - X30.
+ x: [31]u64,
+ sp: u64,
+ pc: u64,
+
+ pub inline fn current() Aarch64 {
+ var ctx: Aarch64 = undefined;
+ asm volatile (
+ \\stp x0, x1, [x0, #0x000]
+ \\stp x2, x3, [x0, #0x010]
+ \\stp x4, x5, [x0, #0x020]
+ \\stp x6, x7, [x0, #0x030]
+ \\stp x8, x9, [x0, #0x040]
+ \\stp x10, x11, [x0, #0x050]
+ \\stp x12, x13, [x0, #0x060]
+ \\stp x14, x15, [x0, #0x070]
+ \\stp x16, x17, [x0, #0x080]
+ \\stp x18, x19, [x0, #0x090]
+ \\stp x20, x21, [x0, #0x0a0]
+ \\stp x22, x23, [x0, #0x0b0]
+ \\stp x24, x25, [x0, #0x0c0]
+ \\stp x26, x27, [x0, #0x0d0]
+ \\stp x28, x29, [x0, #0x0e0]
+ \\str x30, [x0, #0x0f0]
+ \\mov x1, sp
+ \\str x1, [x0, #0x0f8]
+ \\adr x1, .
+ \\str x1, [x0, #0x100]
+ \\ldr x1, [x0, #0x008] // restore saved x1
+ :
+ : [gprs] "{x0}" (&ctx),
+ : .{ .memory = true });
+ return ctx;
+ }
+
+ pub fn dwarfRegisterBytes(ctx: *Aarch64, register_num: u16) DwarfRegisterError![]u8 {
+ // DWARF for the Arm(r) 64-bit Architecture (AArch64) § 4.1 "DWARF register names"
+ switch (register_num) {
+ // The order of `Gpr` intentionally matches DWARF's mappings.
+ 0...30 => return @ptrCast(&ctx.x[register_num]),
+ 31 => return @ptrCast(&ctx.sp),
+ 32 => return @ptrCast(&ctx.pc),
+
+ 33 => return error.UnsupportedRegister, // ELF_mode
+ 34 => return error.UnsupportedRegister, // RA_SIGN_STATE
+ 35 => return error.UnsupportedRegister, // TPIDRRO_ELO
+ 36 => return error.UnsupportedRegister, // RPIDR_ELO
+ 37 => return error.UnsupportedRegister, // RPIDR_EL1
+ 38 => return error.UnsupportedRegister, // RPIDR_EL2
+ 39 => return error.UnsupportedRegister, // RPIDR_EL3
+ 46 => return error.UnsupportedRegister, // VG
+ 47 => return error.UnsupportedRegister, // FFR
+ 48...63 => return error.UnsupportedRegister, // P0 - P15
+ 64...95 => return error.UnsupportedRegister, // V0 - V31
+ 96...127 => return error.UnsupportedRegister, // Z0 - Z31
+
+ else => return error.InvalidRegister,
+ }
+ }
+};
+
+const signal_ucontext_t = switch (native_os) {
+ .linux => std.os.linux.ucontext_t,
+ .emscripten => std.os.emscripten.ucontext_t,
+ .freebsd => std.os.freebsd.ucontext_t,
+ .macos, .ios, .tvos, .watchos, .visionos => extern struct {
+ onstack: c_int,
+ sigmask: std.c.sigset_t,
+ stack: std.c.stack_t,
+ link: ?*signal_ucontext_t,
+ mcsize: u64,
+ mcontext: *mcontext_t,
+ const mcontext_t = switch (native_arch) {
+ .aarch64 => extern struct {
+ es: extern struct {
+ far: u64, // Virtual Fault Address
+ esr: u32, // Exception syndrome
+ exception: u32, // Number of arm exception taken
+ },
+ ss: extern struct {
+ /// General purpose registers
+ regs: [29]u64,
+ /// Frame pointer x29
+ fp: u64,
+ /// Link register x30
+ lr: u64,
+ /// Stack pointer x31
+ sp: u64,
+ /// Program counter
+ pc: u64,
+ /// Current program status register
+ cpsr: u32,
+ __pad: u32,
+ },
+ ns: extern struct {
+ q: [32]u128,
+ fpsr: u32,
+ fpcr: u32,
+ },
+ },
+ .x86_64 => extern struct {
+ es: extern struct {
+ trapno: u16,
+ cpu: u16,
+ err: u32,
+ faultvaddr: u64,
+ },
+ ss: extern struct {
+ rax: u64,
+ rbx: u64,
+ rcx: u64,
+ rdx: u64,
+ rdi: u64,
+ rsi: u64,
+ rbp: u64,
+ rsp: u64,
+ r8: u64,
+ r9: u64,
+ r10: u64,
+ r11: u64,
+ r12: u64,
+ r13: u64,
+ r14: u64,
+ r15: u64,
+ rip: u64,
+ rflags: u64,
+ cs: u64,
+ fs: u64,
+ gs: u64,
+ },
+ fs: extern struct {
+ reserved: [2]c_int,
+ fcw: u16,
+ fsw: u16,
+ ftw: u8,
+ rsrv1: u8,
+ fop: u16,
+ ip: u32,
+ cs: u16,
+ rsrv2: u16,
+ dp: u32,
+ ds: u16,
+ rsrv3: u16,
+ mxcsr: u32,
+ mxcsrmask: u32,
+ stmm: [8]stmm_reg,
+ xmm: [16]xmm_reg,
+ rsrv4: [96]u8,
+ reserved1: c_int,
+
+ const stmm_reg = [16]u8;
+ const xmm_reg = [16]u8;
+ },
+ },
+ else => void,
+ };
+ },
+ .solaris, .illumos => extern struct {
+ flags: u64,
+ link: ?*signal_ucontext_t,
+ sigmask: std.c.sigset_t,
+ stack: std.c.stack_t,
+ mcontext: mcontext_t,
+ brand_data: [3]?*anyopaque,
+ filler: [2]i64,
+ const mcontext_t = extern struct {
+ gregs: [28]u64,
+ fpregs: std.c.fpregset_t,
+ };
+ },
+ .openbsd => switch (builtin.cpu.arch) {
+ .x86_64 => extern struct {
+ sc_rdi: c_long,
+ sc_rsi: c_long,
+ sc_rdx: c_long,
+ sc_rcx: c_long,
+ sc_r8: c_long,
+ sc_r9: c_long,
+ sc_r10: c_long,
+ sc_r11: c_long,
+ sc_r12: c_long,
+ sc_r13: c_long,
+ sc_r14: c_long,
+ sc_r15: c_long,
+ sc_rbp: c_long,
+ sc_rbx: c_long,
+ sc_rax: c_long,
+ sc_gs: c_long,
+ sc_fs: c_long,
+ sc_es: c_long,
+ sc_ds: c_long,
+ sc_trapno: c_long,
+ sc_err: c_long,
+ sc_rip: c_long,
+ sc_cs: c_long,
+ sc_rflags: c_long,
+ sc_rsp: c_long,
+ sc_ss: c_long,
+
+ sc_fpstate: *anyopaque, // struct fxsave64 *
+ __sc_unused: c_int,
+ sc_mask: c_int,
+ sc_cookie: c_long,
+ },
+ .aarch64 => extern struct {
+ __sc_unused: c_int,
+ sc_mask: c_int,
+ sc_sp: c_ulong,
+ sc_lr: c_ulong,
+ sc_elr: c_ulong,
+ sc_spsr: c_ulong,
+ sc_x: [30]c_ulong,
+ sc_cookie: c_long,
+ },
+ else => void,
+ },
+ .netbsd => extern struct {
+ flags: u32,
+ link: ?*signal_ucontext_t,
+ sigmask: std.c.sigset_t,
+ stack: std.c.stack_t,
+ mcontext: mcontext_t,
+ __pad: [
+ switch (builtin.cpu.arch) {
+ .x86 => 4,
+ .mips, .mipsel, .mips64, .mips64el => 14,
+ .arm, .armeb, .thumb, .thumbeb => 1,
+ .sparc, .sparc64 => if (@sizeOf(usize) == 4) 43 else 8,
+ else => 0,
+ }
+ ]u32,
+ const mcontext_t = switch (builtin.cpu.arch) {
+ .aarch64, .aarch64_be => extern struct {
+ gregs: [35]u64,
+ fregs: [528]u8 align(16),
+ spare: [8]u64,
+ },
+ .x86 => extern struct {
+ gregs: [19]u32,
+ fpregs: [161]u32,
+ mc_tlsbase: u32,
+ },
+ .x86_64 => extern struct {
+ gregs: [26]u64,
+ mc_tlsbase: u64,
+ fpregs: [512]u8 align(8),
+ },
+ else => void,
+ };
+ },
+ .dragonfly => extern struct {
+ sigmask: std.c.sigset_t,
+ mcontext: mcontext_t,
+ link: ?*signal_ucontext_t,
+ stack: std.c.stack_t,
+ cofunc: ?*fn (?*signal_ucontext_t, ?*anyopaque) void,
+ arg: ?*void,
+ _spare: [4]c_int,
+ const mcontext_t = extern struct {
+ const register_t = isize;
+ onstack: register_t, // XXX - sigcontext compat.
+ rdi: register_t,
+ rsi: register_t,
+ rdx: register_t,
+ rcx: register_t,
+ r8: register_t,
+ r9: register_t,
+ rax: register_t,
+ rbx: register_t,
+ rbp: register_t,
+ r10: register_t,
+ r11: register_t,
+ r12: register_t,
+ r13: register_t,
+ r14: register_t,
+ r15: register_t,
+ xflags: register_t,
+ trapno: register_t,
+ addr: register_t,
+ flags: register_t,
+ err: register_t,
+ rip: register_t,
+ cs: register_t,
+ rflags: register_t,
+ rsp: register_t, // machine state
+ ss: register_t,
+
+ len: c_uint, // sizeof(mcontext_t)
+ fpformat: c_uint,
+ ownedfp: c_uint,
+ reserved: c_uint,
+ unused: [8]c_uint,
+
+ // NOTE! 64-byte aligned as of here. Also must match savefpu structure.
+ fpregs: [256]c_int align(64),
+ };
+ },
+ .serenity => extern struct {
+ link: ?*signal_ucontext_t,
+ sigmask: std.c.sigset_t,
+ stack: std.c.stack_t,
+ mcontext: mcontext_t,
+ const mcontext_t = switch (builtin.cpu.arch) {
+ // https://github.com/SerenityOS/serenity/blob/200e91cd7f1ec5453799a2720d4dc114a59cc289/Kernel/Arch/aarch64/mcontext.h#L15-L19
+ .aarch64 => extern struct {
+ x: [31]u64,
+ sp: u64,
+ pc: u64,
+ },
+ // https://github.com/SerenityOS/serenity/blob/66f8d0f031ef25c409dbb4fecaa454800fecae0f/Kernel/Arch/riscv64/mcontext.h#L15-L18
+ .riscv64 => extern struct {
+ x: [31]u64,
+ pc: u64,
+ },
+ // https://github.com/SerenityOS/serenity/blob/7b9ea3efdec9f86a1042893e8107d0b23aad8727/Kernel/Arch/x86_64/mcontext.h#L15-L40
+ .x86_64 => extern struct {
+ rax: u64,
+ rcx: u64,
+ rdx: u64,
+ rbx: u64,
+ rsp: u64,
+ rbp: u64,
+ rsi: u64,
+ rdi: u64,
+ rip: u64,
+ r8: u64,
+ r9: u64,
+ r10: u64,
+ r11: u64,
+ r12: u64,
+ r13: u64,
+ r14: u64,
+ r15: u64,
+ rflags: u64,
+ cs: u32,
+ ss: u32,
+ ds: u32,
+ es: u32,
+ fs: u32,
+ gs: u32,
+ },
+ else => void,
+ };
+ },
+ .haiku => extern struct {
+ link: ?*signal_ucontext_t,
+ sigmask: std.c.sigset_t,
+ stack: std.c.stack_t,
+ mcontext: mcontext_t,
+ const mcontext_t = switch (builtin.cpu.arch) {
+ .arm, .thumb => extern struct {
+ r0: u32,
+ r1: u32,
+ r2: u32,
+ r3: u32,
+ r4: u32,
+ r5: u32,
+ r6: u32,
+ r7: u32,
+ r8: u32,
+ r9: u32,
+ r10: u32,
+ r11: u32,
+ r12: u32,
+ r13: u32,
+ r14: u32,
+ r15: u32,
+ cpsr: u32,
+ },
+ .aarch64 => extern struct {
+ x: [10]u64,
+ lr: u64,
+ sp: u64,
+ elr: u64,
+ spsr: u64,
+ fp_q: [32]u128,
+ fpsr: u32,
+ fpcr: u32,
+ },
+ .m68k => extern struct {
+ pc: u32,
+ d0: u32,
+ d1: u32,
+ d2: u32,
+ d3: u32,
+ d4: u32,
+ d5: u32,
+ d6: u32,
+ d7: u32,
+ a0: u32,
+ a1: u32,
+ a2: u32,
+ a3: u32,
+ a4: u32,
+ a5: u32,
+ a6: u32,
+ a7: u32,
+ ccr: u8,
+ f0: f64,
+ f1: f64,
+ f2: f64,
+ f3: f64,
+ f4: f64,
+ f5: f64,
+ f6: f64,
+ f7: f64,
+ f8: f64,
+ f9: f64,
+ f10: f64,
+ f11: f64,
+ f12: f64,
+ f13: f64,
+ },
+ .mipsel => extern struct {
+ r0: u32,
+ },
+ .powerpc => extern struct {
+ pc: u32,
+ r0: u32,
+ r1: u32,
+ r2: u32,
+ r3: u32,
+ r4: u32,
+ r5: u32,
+ r6: u32,
+ r7: u32,
+ r8: u32,
+ r9: u32,
+ r10: u32,
+ r11: u32,
+ r12: u32,
+ f0: f64,
+ f1: f64,
+ f2: f64,
+ f3: f64,
+ f4: f64,
+ f5: f64,
+ f6: f64,
+ f7: f64,
+ f8: f64,
+ f9: f64,
+ f10: f64,
+ f11: f64,
+ f12: f64,
+ f13: f64,
+ reserved: u32,
+ fpscr: u32,
+ ctr: u32,
+ xer: u32,
+ cr: u32,
+ msr: u32,
+ lr: u32,
+ },
+ .riscv64 => extern struct {
+ x: [31]u64,
+ pc: u64,
+ f: [32]f64,
+ fcsr: u64,
+ },
+ .sparc64 => extern struct {
+ g1: u64,
+ g2: u64,
+ g3: u64,
+ g4: u64,
+ g5: u64,
+ g6: u64,
+ g7: u64,
+ o0: u64,
+ o1: u64,
+ o2: u64,
+ o3: u64,
+ o4: u64,
+ o5: u64,
+ sp: u64,
+ o7: u64,
+ l0: u64,
+ l1: u64,
+ l2: u64,
+ l3: u64,
+ l4: u64,
+ l5: u64,
+ l6: u64,
+ l7: u64,
+ i0: u64,
+ i1: u64,
+ i2: u64,
+ i3: u64,
+ i4: u64,
+ i5: u64,
+ fp: u64,
+ i7: u64,
+ },
+ .x86 => extern struct {
+ pub const old_extended_regs = extern struct {
+ control: u16,
+ reserved1: u16,
+ status: u16,
+ reserved2: u16,
+ tag: u16,
+ reserved3: u16,
+ eip: u32,
+ cs: u16,
+ opcode: u16,
+ datap: u32,
+ ds: u16,
+ reserved4: u16,
+ fp_mmx: [8][10]u8,
+ };
+
+ pub const fp_register = extern struct { value: [10]u8, reserved: [6]u8 };
+
+ pub const xmm_register = extern struct { value: [16]u8 };
+
+ pub const new_extended_regs = extern struct {
+ control: u16,
+ status: u16,
+ tag: u16,
+ opcode: u16,
+ eip: u32,
+ cs: u16,
+ reserved1: u16,
+ datap: u32,
+ ds: u16,
+ reserved2: u16,
+ mxcsr: u32,
+ reserved3: u32,
+ fp_mmx: [8]fp_register,
+ xmmx: [8]xmm_register,
+ reserved4: [224]u8,
+ };
+
+ pub const extended_regs = extern struct {
+ state: extern union {
+ old_format: old_extended_regs,
+ new_format: new_extended_regs,
+ },
+ format: u32,
+ };
+
+ eip: u32,
+ eflags: u32,
+ eax: u32,
+ ecx: u32,
+ edx: u32,
+ esp: u32,
+ ebp: u32,
+ reserved: u32,
+ xregs: extended_regs,
+ edi: u32,
+ esi: u32,
+ ebx: u32,
+ },
+ .x86_64 => extern struct {
+ pub const fp_register = extern struct {
+ value: [10]u8,
+ reserved: [6]u8,
+ };
+
+ pub const xmm_register = extern struct {
+ value: [16]u8,
+ };
+
+ pub const fpu_state = extern struct {
+ control: u16,
+ status: u16,
+ tag: u16,
+ opcode: u16,
+ rip: u64,
+ rdp: u64,
+ mxcsr: u32,
+ mscsr_mask: u32,
+
+ fp_mmx: [8]fp_register,
+ xmm: [16]xmm_register,
+ reserved: [96]u8,
+ };
+
+ pub const xstate_hdr = extern struct {
+ bv: u64,
+ xcomp_bv: u64,
+ reserved: [48]u8,
+ };
+
+ pub const savefpu = extern struct {
+ fxsave: fpu_state,
+ xstate: xstate_hdr,
+ ymm: [16]xmm_register,
+ };
+
+ rax: u64,
+ rbx: u64,
+ rcx: u64,
+ rdx: u64,
+ rdi: u64,
+ rsi: u64,
+ rbp: u64,
+ r8: u64,
+ r9: u64,
+ r10: u64,
+ r11: u64,
+ r12: u64,
+ r13: u64,
+ r14: u64,
+ r15: u64,
+ rsp: u64,
+ rip: u64,
+ rflags: u64,
+ fpu: savefpu,
+ },
+ else => void,
+ };
+ },
+ else => void,
+};
+
+const std = @import("../std.zig");
+const root = @import("root");
+const builtin = @import("builtin");
+const native_arch = @import("builtin").target.cpu.arch;
+const native_os = @import("builtin").target.os.tag;