Commit 1f15e265fe
Changed files (1)
lib
std
debug
lib/std/debug/cpu_context.zig
@@ -212,129 +212,63 @@ pub fn fromWindowsContext(ctx: *const std.os.windows.CONTEXT) Native {
};
}
-const X86 = struct {
- /// The first 8 registers here intentionally match the order of registers in the x86 instruction
- /// encoding. This order is inherited by the PUSHA instruction and the DWARF register mappings,
- /// among other things.
- 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, // eflags
- 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...100 => return error.UnsupportedRegister, // k0 - k7 (AVX-512)
-
- else => return error.InvalidRegister,
- }
- }
-};
-
-const X86_64 = struct {
- /// The order here intentionally matches the order of the DWARF register mappings. It's unclear
- /// where those mappings actually originated from---the ordering of the first 4 registers seems
- /// quite unusual---but it is currently convenient for us to match DWARF.
- 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),
+/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
+const Aarch64 = extern struct {
+ /// The numbered general-purpose registers X0 - X30.
+ x: [31]u64,
+ sp: u64,
+ pc: u64,
- pub inline fn current() X86_64 {
- var ctx: X86_64 = undefined;
+ pub inline fn current() Aarch64 {
+ var ctx: Aarch64 = 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
+ \\ 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]
:
- : [gprs] "{rdi}" (&ctx.gprs.values),
+ : [ctx] "{x0}" (&ctx),
: .{ .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"
+ 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...16 => return @ptrCast(&ctx.gprs.values[register_num]),
+ 0...30 => return @ptrCast(&ctx.x[register_num]),
+ 31 => return @ptrCast(&ctx.sp),
+ 32 => return @ptrCast(&ctx.pc),
- 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
- 67...82 => return error.UnsupportedRegister, // xmm16 - xmm31 (AVX-512)
- 118...125 => return error.UnsupportedRegister, // k0 - k7 (AVX-512)
- 130...145 => return error.UnsupportedRegister, // r16 - r31 (APX)
+ 33 => return error.UnsupportedRegister, // ELR_mode
+ 34 => return error.UnsupportedRegister, // RA_SIGN_STATE
+ 35 => return error.UnsupportedRegister, // TPIDRRO_ELO
+ 36 => return error.UnsupportedRegister, // TPIDR_ELO
+ 37 => return error.UnsupportedRegister, // TPIDR_EL1
+ 38 => return error.UnsupportedRegister, // TPIDR_EL2
+ 39 => return error.UnsupportedRegister, // TPIDR_EL3
+ 40...45 => return error.UnsupportedRegister, // Reserved
+ 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,
}
@@ -410,11 +344,11 @@ const Arm = struct {
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]
+ \\ // 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 });
@@ -461,69 +395,6 @@ const Arm = struct {
}
};
-/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
-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]
- :
- : [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) {
- 0...30 => return @ptrCast(&ctx.x[register_num]),
- 31 => return @ptrCast(&ctx.sp),
- 32 => return @ptrCast(&ctx.pc),
-
- 33 => return error.UnsupportedRegister, // ELR_mode
- 34 => return error.UnsupportedRegister, // RA_SIGN_STATE
- 35 => return error.UnsupportedRegister, // TPIDRRO_ELO
- 36 => return error.UnsupportedRegister, // TPIDR_ELO
- 37 => return error.UnsupportedRegister, // TPIDR_EL1
- 38 => return error.UnsupportedRegister, // TPIDR_EL2
- 39 => return error.UnsupportedRegister, // TPIDR_EL3
- 40...45 => return error.UnsupportedRegister, // Reserved
- 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,
- }
- }
-};
-
/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
const Csky = extern struct {
/// The numbered general-purpose registers r0 - r31.
@@ -600,7 +471,7 @@ const Hexagon = extern struct {
\\ memw(r0 + #128) = r1
\\ r1 = memw(r0 + #4)
:
- : [gprs] "{r0}" (&ctx),
+ : [ctx] "{r0}" (&ctx),
: .{ .memory = true });
return ctx;
}
@@ -762,7 +633,7 @@ const LoongArch = extern struct {
\\ st.w $ra, $t0, 128
\\ ld.w $ra, $t0, 4
:
- : [gprs] "{$r12}" (&ctx),
+ : [ctx] "{$r12}" (&ctx),
: .{ .memory = true });
return ctx;
}
@@ -908,7 +779,7 @@ const Mips = extern struct {
\\ lw $ra, 124($t4)
\\ .set pop
:
- : [gprs] "{$12}" (&ctx),
+ : [ctx] "{$12}" (&ctx),
: .{ .memory = true });
return ctx;
}
@@ -1087,7 +958,7 @@ const Powerpc = extern struct {
\\ stw 8, 128(10)
\\ lwz 8, 32(10)
:
- : [gprs] "{r10}" (&ctx),
+ : [ctx] "{r10}" (&ctx),
: .{ .lr = true, .memory = true });
return ctx;
}
@@ -1151,104 +1022,6 @@ const Powerpc = extern struct {
}
};
-/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
-const Sparc = extern struct {
- g: [8]Gpr,
- o: [8]Gpr,
- l: [8]Gpr,
- i: [8]Gpr,
- pc: Gpr,
-
- pub const Gpr = if (native_arch == .sparc64) u64 else u32;
-
- pub inline fn current() Sparc {
- flushWindows();
-
- var ctx: Sparc = undefined;
- asm volatile (if (Gpr == u64)
- \\ stx %g0, [%l0 + 0]
- \\ stx %g1, [%l0 + 8]
- \\ stx %g2, [%l0 + 16]
- \\ stx %g3, [%l0 + 24]
- \\ stx %g4, [%l0 + 32]
- \\ stx %g5, [%l0 + 40]
- \\ stx %g6, [%l0 + 48]
- \\ stx %g7, [%l0 + 56]
- \\ stx %o0, [%l0 + 64]
- \\ stx %o1, [%l0 + 72]
- \\ stx %o2, [%l0 + 80]
- \\ stx %o3, [%l0 + 88]
- \\ stx %o4, [%l0 + 96]
- \\ stx %o5, [%l0 + 104]
- \\ stx %o6, [%l0 + 112]
- \\ stx %o7, [%l0 + 120]
- \\ stx %l0, [%l0 + 128]
- \\ stx %l1, [%l0 + 136]
- \\ stx %l2, [%l0 + 144]
- \\ stx %l3, [%l0 + 152]
- \\ stx %l4, [%l0 + 160]
- \\ stx %l5, [%l0 + 168]
- \\ stx %l6, [%l0 + 176]
- \\ stx %l7, [%l0 + 184]
- \\ stx %i0, [%l0 + 192]
- \\ stx %i1, [%l0 + 200]
- \\ stx %i2, [%l0 + 208]
- \\ stx %i3, [%l0 + 216]
- \\ stx %i4, [%l0 + 224]
- \\ stx %i5, [%l0 + 232]
- \\ stx %i6, [%l0 + 240]
- \\ stx %i7, [%l0 + 248]
- \\ call 1f
- \\ stx %o7, [%l0 + 256]
- \\1:
- else
- \\ std %g0, [%l0 + 0]
- \\ std %g2, [%l0 + 8]
- \\ std %g4, [%l0 + 16]
- \\ std %g6, [%l0 + 24]
- \\ std %o0, [%l0 + 32]
- \\ std %o2, [%l0 + 40]
- \\ std %o4, [%l0 + 48]
- \\ std %o6, [%l0 + 56]
- \\ std %l0, [%l0 + 64]
- \\ std %l2, [%l0 + 72]
- \\ std %l4, [%l0 + 80]
- \\ std %l6, [%l0 + 88]
- \\ std %i0, [%l0 + 96]
- \\ std %i2, [%l0 + 104]
- \\ std %i4, [%l0 + 112]
- \\ std %i6, [%l0 + 120]
- \\ call 1f
- \\ st %o7, [%l0 + 128]
- \\1:
- :
- : [gprs] "{l0}" (&ctx),
- : .{ .o7 = true, .memory = true });
- return ctx;
- }
-
- noinline fn flushWindows() void {
- // Flush all register windows except the current one (hence `noinline`). This ensures that
- // we actually see meaningful data on the stack when we walk the frame chain.
- if (comptime builtin.target.cpu.has(.sparc, .v9))
- asm volatile ("flushw" ::: .{ .memory = true })
- else
- asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS
- }
-
- pub fn dwarfRegisterBytes(ctx: *Sparc, register_num: u16) DwarfRegisterError![]u8 {
- switch (register_num) {
- 0...7 => return @ptrCast(&ctx.g[register_num]),
- 8...15 => return @ptrCast(&ctx.o[register_num - 8]),
- 16...23 => return @ptrCast(&ctx.l[register_num - 16]),
- 24...31 => return @ptrCast(&ctx.i[register_num - 24]),
- 32 => return @ptrCast(&ctx.pc),
-
- else => return error.InvalidRegister,
- }
- }
-};
-
/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
const Riscv = extern struct {
/// The numbered general-purpose registers r0 - r31. r0 must be zero.
@@ -1334,7 +1107,7 @@ const Riscv = extern struct {
\\ sw ra, 128(t0)
\\ lw ra, 4(t0)
:
- : [gprs] "{t0}" (&ctx),
+ : [ctx] "{t0}" (&ctx),
: .{ .memory = true });
return ctx;
}
@@ -1376,7 +1149,7 @@ const S390x = extern struct {
\\ lg %%r0, 0(%%r2)
\\ lg %%r1, 8(%%r2)
:
- : [gprs] "{r2}" (&ctx),
+ : [ctx] "{r2}" (&ctx),
: .{ .memory = true });
return ctx;
}
@@ -1398,6 +1171,104 @@ const S390x = extern struct {
}
};
+/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
+const Sparc = extern struct {
+ g: [8]Gpr,
+ o: [8]Gpr,
+ l: [8]Gpr,
+ i: [8]Gpr,
+ pc: Gpr,
+
+ pub const Gpr = if (native_arch == .sparc64) u64 else u32;
+
+ pub inline fn current() Sparc {
+ flushWindows();
+
+ var ctx: Sparc = undefined;
+ asm volatile (if (Gpr == u64)
+ \\ stx %g0, [%l0 + 0]
+ \\ stx %g1, [%l0 + 8]
+ \\ stx %g2, [%l0 + 16]
+ \\ stx %g3, [%l0 + 24]
+ \\ stx %g4, [%l0 + 32]
+ \\ stx %g5, [%l0 + 40]
+ \\ stx %g6, [%l0 + 48]
+ \\ stx %g7, [%l0 + 56]
+ \\ stx %o0, [%l0 + 64]
+ \\ stx %o1, [%l0 + 72]
+ \\ stx %o2, [%l0 + 80]
+ \\ stx %o3, [%l0 + 88]
+ \\ stx %o4, [%l0 + 96]
+ \\ stx %o5, [%l0 + 104]
+ \\ stx %o6, [%l0 + 112]
+ \\ stx %o7, [%l0 + 120]
+ \\ stx %l0, [%l0 + 128]
+ \\ stx %l1, [%l0 + 136]
+ \\ stx %l2, [%l0 + 144]
+ \\ stx %l3, [%l0 + 152]
+ \\ stx %l4, [%l0 + 160]
+ \\ stx %l5, [%l0 + 168]
+ \\ stx %l6, [%l0 + 176]
+ \\ stx %l7, [%l0 + 184]
+ \\ stx %i0, [%l0 + 192]
+ \\ stx %i1, [%l0 + 200]
+ \\ stx %i2, [%l0 + 208]
+ \\ stx %i3, [%l0 + 216]
+ \\ stx %i4, [%l0 + 224]
+ \\ stx %i5, [%l0 + 232]
+ \\ stx %i6, [%l0 + 240]
+ \\ stx %i7, [%l0 + 248]
+ \\ call 1f
+ \\ stx %o7, [%l0 + 256]
+ \\1:
+ else
+ \\ std %g0, [%l0 + 0]
+ \\ std %g2, [%l0 + 8]
+ \\ std %g4, [%l0 + 16]
+ \\ std %g6, [%l0 + 24]
+ \\ std %o0, [%l0 + 32]
+ \\ std %o2, [%l0 + 40]
+ \\ std %o4, [%l0 + 48]
+ \\ std %o6, [%l0 + 56]
+ \\ std %l0, [%l0 + 64]
+ \\ std %l2, [%l0 + 72]
+ \\ std %l4, [%l0 + 80]
+ \\ std %l6, [%l0 + 88]
+ \\ std %i0, [%l0 + 96]
+ \\ std %i2, [%l0 + 104]
+ \\ std %i4, [%l0 + 112]
+ \\ std %i6, [%l0 + 120]
+ \\ call 1f
+ \\ st %o7, [%l0 + 128]
+ \\1:
+ :
+ : [ctx] "{l0}" (&ctx),
+ : .{ .o7 = true, .memory = true });
+ return ctx;
+ }
+
+ noinline fn flushWindows() void {
+ // Flush all register windows except the current one (hence `noinline`). This ensures that
+ // we actually see meaningful data on the stack when we walk the frame chain.
+ if (comptime builtin.target.cpu.has(.sparc, .v9))
+ asm volatile ("flushw" ::: .{ .memory = true })
+ else
+ asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS
+ }
+
+ pub fn dwarfRegisterBytes(ctx: *Sparc, register_num: u16) DwarfRegisterError![]u8 {
+ switch (register_num) {
+ 0...7 => return @ptrCast(&ctx.g[register_num]),
+ 8...15 => return @ptrCast(&ctx.o[register_num - 8]),
+ 16...23 => return @ptrCast(&ctx.l[register_num - 16]),
+ 24...31 => return @ptrCast(&ctx.i[register_num - 24]),
+ 32 => return @ptrCast(&ctx.pc),
+
+ else => return error.InvalidRegister,
+ }
+ }
+};
+
/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
const Ve = extern struct {
s: [64]u64,
@@ -1492,6 +1363,135 @@ const Ve = extern struct {
}
};
+const X86 = struct {
+ /// The first 8 registers here intentionally match the order of registers in the x86 instruction
+ /// encoding. This order is inherited by the PUSHA instruction and the DWARF register mappings,
+ /// among other things.
+ 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, // eflags
+ 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...100 => return error.UnsupportedRegister, // k0 - k7 (AVX-512)
+
+ else => return error.InvalidRegister,
+ }
+ }
+};
+
+const X86_64 = struct {
+ /// The order here intentionally matches the order of the DWARF register mappings. It's unclear
+ /// where those mappings actually originated from---the ordering of the first 4 registers seems
+ /// quite unusual---but it is currently convenient for us to match DWARF.
+ 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
+ :
+ : [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
+ 67...82 => return error.UnsupportedRegister, // xmm16 - xmm31 (AVX-512)
+ 118...125 => return error.UnsupportedRegister, // k0 - k7 (AVX-512)
+ 130...145 => return error.UnsupportedRegister, // r16 - r31 (APX)
+
+ else => return error.InvalidRegister,
+ }
+ }
+};
+
/// The native operating system's `ucontext_t` as seen in the third argument to signal handlers.
///
/// These are dramatically simplified since we only need general-purpose registers and don't care