std.debug.cpu_context: sort context decls according to switch prongs (NFC)

This commit is contained in:
Alex Rønne Petersen 2025-10-16 18:51:09 +02:00
parent 4c81a496e7
commit 1f15e265fe
No known key found for this signature in database

View File

@ -212,129 +212,63 @@ pub fn fromWindowsContext(ctx: *const std.os.windows.CONTEXT) Native {
}; };
} }
const X86 = struct { /// This is an `extern struct` so that inline assembly in `current` can use field offsets.
/// The first 8 registers here intentionally match the order of registers in the x86 instruction const Aarch64 = extern struct {
/// encoding. This order is inherited by the PUSHA instruction and the DWARF register mappings, /// The numbered general-purpose registers X0 - X30.
/// among other things. x: [31]u64,
pub const Gpr = enum { sp: u64,
// zig fmt: off pc: u64,
eax, ecx, edx, ebx,
esp, ebp, esi, edi,
eip,
// zig fmt: on
};
gprs: std.enums.EnumArray(Gpr, u32),
pub inline fn current() X86 { pub inline fn current() Aarch64 {
var ctx: X86 = undefined; var ctx: Aarch64 = undefined;
asm volatile ( asm volatile (
\\movl %%eax, 0x00(%%edi) \\ stp x0, x1, [x0, #0x000]
\\movl %%ecx, 0x04(%%edi) \\ stp x2, x3, [x0, #0x010]
\\movl %%edx, 0x08(%%edi) \\ stp x4, x5, [x0, #0x020]
\\movl %%ebx, 0x0c(%%edi) \\ stp x6, x7, [x0, #0x030]
\\movl %%esp, 0x10(%%edi) \\ stp x8, x9, [x0, #0x040]
\\movl %%ebp, 0x14(%%edi) \\ stp x10, x11, [x0, #0x050]
\\movl %%esi, 0x18(%%edi) \\ stp x12, x13, [x0, #0x060]
\\movl %%edi, 0x1c(%%edi) \\ stp x14, x15, [x0, #0x070]
\\call 1f \\ stp x16, x17, [x0, #0x080]
\\1: \\ stp x18, x19, [x0, #0x090]
\\popl 0x20(%%edi) \\ 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] "{edi}" (&ctx.gprs.values), : [ctx] "{x0}" (&ctx),
: .{ .memory = true }); : .{ .memory = true });
return ctx; return ctx;
} }
pub fn dwarfRegisterBytes(ctx: *X86, register_num: u16) DwarfRegisterError![]u8 { pub fn dwarfRegisterBytes(ctx: *Aarch64, register_num: u16) DwarfRegisterError![]u8 {
// System V Application Binary Interface Intel386 Architecture Processor Supplement Version 1.1 // DWARF for the Arm(r) 64-bit Architecture (AArch64) § 4.1 "DWARF register names"
// § 2.4.2 "DWARF Register Number Mapping"
switch (register_num) { 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),
// x86-macos sometimes uses different mappings (ebp and esp are reversed when the unwind 32 => return @ptrCast(&ctx.pc),
// 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 33 => return error.UnsupportedRegister, // ELR_mode
11...18 => return error.UnsupportedRegister, // st0 - st7 34 => return error.UnsupportedRegister, // RA_SIGN_STATE
21...28 => return error.UnsupportedRegister, // xmm0 - xmm7 35 => return error.UnsupportedRegister, // TPIDRRO_ELO
29...36 => return error.UnsupportedRegister, // mm0 - mm7 36 => return error.UnsupportedRegister, // TPIDR_ELO
39 => return error.UnsupportedRegister, // mxcsr 37 => return error.UnsupportedRegister, // TPIDR_EL1
40...45 => return error.UnsupportedRegister, // es, cs, ss, ds, fs, gs 38 => return error.UnsupportedRegister, // TPIDR_EL2
48 => return error.UnsupportedRegister, // tr 39 => return error.UnsupportedRegister, // TPIDR_EL3
49 => return error.UnsupportedRegister, // ldtr 40...45 => return error.UnsupportedRegister, // Reserved
93...100 => return error.UnsupportedRegister, // k0 - k7 (AVX-512) 46 => return error.UnsupportedRegister, // VG
47 => return error.UnsupportedRegister, // FFR
else => return error.InvalidRegister, 48...63 => return error.UnsupportedRegister, // P0 - P15
} 64...95 => return error.UnsupportedRegister, // V0 - V31
} 96...127 => return error.UnsupportedRegister, // Z0 - Z31
};
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, else => return error.InvalidRegister,
} }
@ -410,11 +344,11 @@ const Arm = struct {
pub inline fn current() Arm { pub inline fn current() Arm {
var ctx: Arm = undefined; var ctx: Arm = undefined;
asm volatile ( asm volatile (
\\// For compatibility with Thumb, we can't write r13 (sp) or r15 (pc) with stm. \\ // For compatibility with Thumb, we can't write r13 (sp) or r15 (pc) with stm.
\\stm r0, {r0-r12} \\ stm r0, {r0-r12}
\\str r13, [r0, #0x34] \\ str r13, [r0, #0x34]
\\str r14, [r0, #0x38] \\ str r14, [r0, #0x38]
\\str r15, [r0, #0x3c] \\ str r15, [r0, #0x3c]
: :
: [r] "{r0}" (&ctx.r), : [r] "{r0}" (&ctx.r),
: .{ .memory = true }); : .{ .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. /// This is an `extern struct` so that inline assembly in `current` can use field offsets.
const Csky = extern struct { const Csky = extern struct {
/// The numbered general-purpose registers r0 - r31. /// The numbered general-purpose registers r0 - r31.
@ -600,7 +471,7 @@ const Hexagon = extern struct {
\\ memw(r0 + #128) = r1 \\ memw(r0 + #128) = r1
\\ r1 = memw(r0 + #4) \\ r1 = memw(r0 + #4)
: :
: [gprs] "{r0}" (&ctx), : [ctx] "{r0}" (&ctx),
: .{ .memory = true }); : .{ .memory = true });
return ctx; return ctx;
} }
@ -762,7 +633,7 @@ const LoongArch = extern struct {
\\ st.w $ra, $t0, 128 \\ st.w $ra, $t0, 128
\\ ld.w $ra, $t0, 4 \\ ld.w $ra, $t0, 4
: :
: [gprs] "{$r12}" (&ctx), : [ctx] "{$r12}" (&ctx),
: .{ .memory = true }); : .{ .memory = true });
return ctx; return ctx;
} }
@ -908,7 +779,7 @@ const Mips = extern struct {
\\ lw $ra, 124($t4) \\ lw $ra, 124($t4)
\\ .set pop \\ .set pop
: :
: [gprs] "{$12}" (&ctx), : [ctx] "{$12}" (&ctx),
: .{ .memory = true }); : .{ .memory = true });
return ctx; return ctx;
} }
@ -1087,7 +958,7 @@ const Powerpc = extern struct {
\\ stw 8, 128(10) \\ stw 8, 128(10)
\\ lwz 8, 32(10) \\ lwz 8, 32(10)
: :
: [gprs] "{r10}" (&ctx), : [ctx] "{r10}" (&ctx),
: .{ .lr = true, .memory = true }); : .{ .lr = true, .memory = true });
return ctx; 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. /// This is an `extern struct` so that inline assembly in `current` can use field offsets.
const Riscv = extern struct { const Riscv = extern struct {
/// The numbered general-purpose registers r0 - r31. r0 must be zero. /// The numbered general-purpose registers r0 - r31. r0 must be zero.
@ -1334,7 +1107,7 @@ const Riscv = extern struct {
\\ sw ra, 128(t0) \\ sw ra, 128(t0)
\\ lw ra, 4(t0) \\ lw ra, 4(t0)
: :
: [gprs] "{t0}" (&ctx), : [ctx] "{t0}" (&ctx),
: .{ .memory = true }); : .{ .memory = true });
return ctx; return ctx;
} }
@ -1376,7 +1149,7 @@ const S390x = extern struct {
\\ lg %%r0, 0(%%r2) \\ lg %%r0, 0(%%r2)
\\ lg %%r1, 8(%%r2) \\ lg %%r1, 8(%%r2)
: :
: [gprs] "{r2}" (&ctx), : [ctx] "{r2}" (&ctx),
: .{ .memory = true }); : .{ .memory = true });
return ctx; 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. /// This is an `extern struct` so that inline assembly in `current` can use field offsets.
const Ve = extern struct { const Ve = extern struct {
s: [64]u64, 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. /// 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 /// These are dramatically simplified since we only need general-purpose registers and don't care