| author | |
| committer | |
| log | a18fd41064493e742eacebc88e2afeadd54ff6f0 |
| tree | 1081fbd6d3c64cf1f583ae3188ab05e0320f03d9 |
| parent | b578cca022f4c9ce94439e2ee795639b3a23c8f5 |
| signature |
Our usage of `ucontext_t` in the standard library was kind of
problematic. We unnecessarily mimiced libc-specific structures, and our
`getcontext` implementation was overkill for our use case of stack
tracing.
This commit introduces a new namespace, `std.debug.cpu_context`, which
contains "context" types for various architectures (currently x86,
x86_64, ARM, and AARCH64) containing the general-purpose CPU registers;
the ones needed in practice for stack unwinding. Each implementation has
a function `current` which populates the structure using inline
assembly. The structure is user-overrideable, though that should only be
necessary if the standard library does not have an implementation for
the *architecture*: that is to say, none of this is OS-dependent.
Of course, in POSIX signal handlers, we get a `ucontext_t` from the
kernel. The function `std.debug.cpu_context.fromPosixSignalContext`
converts this to a `std.debug.cpu_context.Native` with a big ol' target
switch.
This functionality is not exposed from `std.c` or `std.posix`, and
neither are `ucontext_t`, `mcontext_t`, or `getcontext`. The rationale
is that these types and functions do not conform to a specific ABI, and
in fact tend to get updated over time based on CPU features and
extensions; in addition, different libcs use different structures which
are "partially compatible" with the kernel structure. Overall, it's a
mess, but all we need is the kernel context, so we can just define a
kernel-compatible structure as long as we don't claim C compatibility by
putting it in `std.c` or `std.posix`.
This change resulted in a few nice `std.debug` simplifications, but
nothing too noteworthy. However, the main benefit of this change is that
DWARF unwinding---sometimes necessary for collecting stack traces
reliably---now requires far less target-specific integration.
Also fix a bug I noticed in `PageAllocator` (I found this due to a bug
in my distro's QEMU distribution; thanks, broken QEMU patch!) and I
think a couple of minor bugs in `std.debug`.
Resolves: #23801
Resolves: #2380233 files changed, 1416 insertions(+), 1523 deletions(-)
lib/std/c.zig-207| ... | @@ -7035,205 +7035,6 @@ pub const timezone = switch (native_os) { | ... | @@ -7035,205 +7035,6 @@ pub const timezone = switch (native_os) { |
| 7035 | else => void, | 7035 | else => void, |
| 7036 | }; | 7036 | }; |
| 7037 | 7037 | ||
| 7038 | pub const ucontext_t = switch (native_os) { | ||
| 7039 | .linux => linux.ucontext_t, // std.os.linux.ucontext_t is currently glibc-compatible, but it should probably not be. | ||
| 7040 | .emscripten => emscripten.ucontext_t, | ||
| 7041 | .macos, .ios, .tvos, .watchos, .visionos => extern struct { | ||
| 7042 | onstack: c_int, | ||
| 7043 | sigmask: sigset_t, | ||
| 7044 | stack: stack_t, | ||
| 7045 | link: ?*ucontext_t, | ||
| 7046 | mcsize: u64, | ||
| 7047 | mcontext: *mcontext_t, | ||
| 7048 | __mcontext_data: mcontext_t, | ||
| 7049 | }, | ||
| 7050 | .freebsd => extern struct { | ||
| 7051 | sigmask: sigset_t, | ||
| 7052 | mcontext: mcontext_t, | ||
| 7053 | link: ?*ucontext_t, | ||
| 7054 | stack: stack_t, | ||
| 7055 | flags: c_int, | ||
| 7056 | __spare__: [4]c_int, | ||
| 7057 | }, | ||
| 7058 | .solaris, .illumos => extern struct { | ||
| 7059 | flags: u64, | ||
| 7060 | link: ?*ucontext_t, | ||
| 7061 | sigmask: sigset_t, | ||
| 7062 | stack: stack_t, | ||
| 7063 | mcontext: mcontext_t, | ||
| 7064 | brand_data: [3]?*anyopaque, | ||
| 7065 | filler: [2]i64, | ||
| 7066 | }, | ||
| 7067 | .netbsd => extern struct { | ||
| 7068 | flags: u32, | ||
| 7069 | link: ?*ucontext_t, | ||
| 7070 | sigmask: sigset_t, | ||
| 7071 | stack: stack_t, | ||
| 7072 | mcontext: mcontext_t, | ||
| 7073 | __pad: [ | ||
| 7074 | switch (builtin.cpu.arch) { | ||
| 7075 | .x86 => 4, | ||
| 7076 | .mips, .mipsel, .mips64, .mips64el => 14, | ||
| 7077 | .arm, .armeb, .thumb, .thumbeb => 1, | ||
| 7078 | .sparc, .sparc64 => if (@sizeOf(usize) == 4) 43 else 8, | ||
| 7079 | else => 0, | ||
| 7080 | } | ||
| 7081 | ]u32, | ||
| 7082 | }, | ||
| 7083 | .dragonfly => extern struct { | ||
| 7084 | sigmask: sigset_t, | ||
| 7085 | mcontext: mcontext_t, | ||
| 7086 | link: ?*ucontext_t, | ||
| 7087 | stack: stack_t, | ||
| 7088 | cofunc: ?*fn (?*ucontext_t, ?*anyopaque) void, | ||
| 7089 | arg: ?*void, | ||
| 7090 | _spare: [4]c_int, | ||
| 7091 | }, | ||
| 7092 | // https://github.com/SerenityOS/serenity/blob/87eac0e424cff4a1f941fb704b9362a08654c24d/Kernel/API/POSIX/ucontext.h#L19-L24 | ||
| 7093 | .haiku, .serenity => extern struct { | ||
| 7094 | link: ?*ucontext_t, | ||
| 7095 | sigmask: sigset_t, | ||
| 7096 | stack: stack_t, | ||
| 7097 | mcontext: mcontext_t, | ||
| 7098 | }, | ||
| 7099 | .openbsd => openbsd.ucontext_t, | ||
| 7100 | else => void, | ||
| 7101 | }; | ||
| 7102 | pub const mcontext_t = switch (native_os) { | ||
| 7103 | .linux => linux.mcontext_t, | ||
| 7104 | .emscripten => emscripten.mcontext_t, | ||
| 7105 | .macos, .ios, .tvos, .watchos, .visionos => darwin.mcontext_t, | ||
| 7106 | .freebsd => switch (builtin.cpu.arch) { | ||
| 7107 | .x86_64 => extern struct { | ||
| 7108 | onstack: u64, | ||
| 7109 | rdi: u64, | ||
| 7110 | rsi: u64, | ||
| 7111 | rdx: u64, | ||
| 7112 | rcx: u64, | ||
| 7113 | r8: u64, | ||
| 7114 | r9: u64, | ||
| 7115 | rax: u64, | ||
| 7116 | rbx: u64, | ||
| 7117 | rbp: u64, | ||
| 7118 | r10: u64, | ||
| 7119 | r11: u64, | ||
| 7120 | r12: u64, | ||
| 7121 | r13: u64, | ||
| 7122 | r14: u64, | ||
| 7123 | r15: u64, | ||
| 7124 | trapno: u32, | ||
| 7125 | fs: u16, | ||
| 7126 | gs: u16, | ||
| 7127 | addr: u64, | ||
| 7128 | flags: u32, | ||
| 7129 | es: u16, | ||
| 7130 | ds: u16, | ||
| 7131 | err: u64, | ||
| 7132 | rip: u64, | ||
| 7133 | cs: u64, | ||
| 7134 | rflags: u64, | ||
| 7135 | rsp: u64, | ||
| 7136 | ss: u64, | ||
| 7137 | len: u64, | ||
| 7138 | fpformat: u64, | ||
| 7139 | ownedfp: u64, | ||
| 7140 | fpstate: [64]u64 align(16), | ||
| 7141 | fsbase: u64, | ||
| 7142 | gsbase: u64, | ||
| 7143 | xfpustate: u64, | ||
| 7144 | xfpustate_len: u64, | ||
| 7145 | spare: [4]u64, | ||
| 7146 | }, | ||
| 7147 | .aarch64 => extern struct { | ||
| 7148 | gpregs: extern struct { | ||
| 7149 | x: [30]u64, | ||
| 7150 | lr: u64, | ||
| 7151 | sp: u64, | ||
| 7152 | elr: u64, | ||
| 7153 | spsr: u32, | ||
| 7154 | _pad: u32, | ||
| 7155 | }, | ||
| 7156 | fpregs: extern struct { | ||
| 7157 | q: [32]u128, | ||
| 7158 | sr: u32, | ||
| 7159 | cr: u32, | ||
| 7160 | flags: u32, | ||
| 7161 | _pad: u32, | ||
| 7162 | }, | ||
| 7163 | flags: u32, | ||
| 7164 | _pad: u32, | ||
| 7165 | _spare: [8]u64, | ||
| 7166 | }, | ||
| 7167 | else => struct {}, | ||
| 7168 | }, | ||
| 7169 | .solaris, .illumos => extern struct { | ||
| 7170 | gregs: [28]u64, | ||
| 7171 | fpregs: solaris.fpregset_t, | ||
| 7172 | }, | ||
| 7173 | .netbsd => switch (builtin.cpu.arch) { | ||
| 7174 | .aarch64, .aarch64_be => extern struct { | ||
| 7175 | gregs: [35]u64, | ||
| 7176 | fregs: [528]u8 align(16), | ||
| 7177 | spare: [8]u64, | ||
| 7178 | }, | ||
| 7179 | .x86 => extern struct { | ||
| 7180 | gregs: [19]u32, | ||
| 7181 | fpregs: [161]u32, | ||
| 7182 | mc_tlsbase: u32, | ||
| 7183 | }, | ||
| 7184 | .x86_64 => extern struct { | ||
| 7185 | gregs: [26]u64, | ||
| 7186 | mc_tlsbase: u64, | ||
| 7187 | fpregs: [512]u8 align(8), | ||
| 7188 | }, | ||
| 7189 | else => struct {}, | ||
| 7190 | }, | ||
| 7191 | .dragonfly => dragonfly.mcontext_t, | ||
| 7192 | .haiku => haiku.mcontext_t, | ||
| 7193 | .serenity => switch (native_arch) { | ||
| 7194 | // https://github.com/SerenityOS/serenity/blob/200e91cd7f1ec5453799a2720d4dc114a59cc289/Kernel/Arch/aarch64/mcontext.h#L15-L19 | ||
| 7195 | .aarch64 => extern struct { | ||
| 7196 | x: [31]u64, | ||
| 7197 | sp: u64, | ||
| 7198 | pc: u64, | ||
| 7199 | }, | ||
| 7200 | // https://github.com/SerenityOS/serenity/blob/66f8d0f031ef25c409dbb4fecaa454800fecae0f/Kernel/Arch/riscv64/mcontext.h#L15-L18 | ||
| 7201 | .riscv64 => extern struct { | ||
| 7202 | x: [31]u64, | ||
| 7203 | pc: u64, | ||
| 7204 | }, | ||
| 7205 | // https://github.com/SerenityOS/serenity/blob/7b9ea3efdec9f86a1042893e8107d0b23aad8727/Kernel/Arch/x86_64/mcontext.h#L15-L40 | ||
| 7206 | .x86_64 => extern struct { | ||
| 7207 | rax: u64, | ||
| 7208 | rcx: u64, | ||
| 7209 | rdx: u64, | ||
| 7210 | rbx: u64, | ||
| 7211 | rsp: u64, | ||
| 7212 | rbp: u64, | ||
| 7213 | rsi: u64, | ||
| 7214 | rdi: u64, | ||
| 7215 | rip: u64, | ||
| 7216 | r8: u64, | ||
| 7217 | r9: u64, | ||
| 7218 | r10: u64, | ||
| 7219 | r11: u64, | ||
| 7220 | r12: u64, | ||
| 7221 | r13: u64, | ||
| 7222 | r14: u64, | ||
| 7223 | r15: u64, | ||
| 7224 | rflags: u64, | ||
| 7225 | cs: u32, | ||
| 7226 | ss: u32, | ||
| 7227 | ds: u32, | ||
| 7228 | es: u32, | ||
| 7229 | fs: u32, | ||
| 7230 | gs: u32, | ||
| 7231 | }, | ||
| 7232 | else => struct {}, | ||
| 7233 | }, | ||
| 7234 | else => void, | ||
| 7235 | }; | ||
| 7236 | |||
| 7237 | pub const user_desc = switch (native_os) { | 7038 | pub const user_desc = switch (native_os) { |
| 7238 | .linux => linux.user_desc, | 7039 | .linux => linux.user_desc, |
| 7239 | else => void, | 7040 | else => void, |
| ... | @@ -11238,13 +11039,6 @@ pub const LC = enum(c_int) { | ... | @@ -11238,13 +11039,6 @@ pub const LC = enum(c_int) { |
| 11238 | 11039 | ||
| 11239 | pub extern "c" fn setlocale(category: LC, locale: ?[*:0]const u8) ?[*:0]const u8; | 11040 | pub extern "c" fn setlocale(category: LC, locale: ?[*:0]const u8) ?[*:0]const u8; |
| 11240 | 11041 | ||
| 11241 | pub const getcontext = if (builtin.target.abi.isAndroid() or builtin.target.os.tag == .openbsd or builtin.target.os.tag == .haiku) | ||
| 11242 | {} // libc does not implement getcontext | ||
| 11243 | else if (native_os == .linux and builtin.target.abi.isMusl()) | ||
| 11244 | linux.getcontext | ||
| 11245 | else | ||
| 11246 | private.getcontext; | ||
| 11247 | |||
| 11248 | pub const max_align_t = if (native_abi == .msvc or native_abi == .itanium) | 11042 | pub const max_align_t = if (native_abi == .msvc or native_abi == .itanium) |
| 11249 | f64 | 11043 | f64 |
| 11250 | else if (native_os.isDarwin()) | 11044 | else if (native_os.isDarwin()) |
| ... | @@ -11668,7 +11462,6 @@ const private = struct { | ... | @@ -11668,7 +11462,6 @@ const private = struct { |
| 11668 | extern "c" fn shm_open(name: [*:0]const u8, flag: c_int, mode: mode_t) c_int; | 11462 | extern "c" fn shm_open(name: [*:0]const u8, flag: c_int, mode: mode_t) c_int; |
| 11669 | 11463 | ||
| 11670 | extern "c" fn pthread_setname_np(thread: pthread_t, name: [*:0]const u8) c_int; | 11464 | extern "c" fn pthread_setname_np(thread: pthread_t, name: [*:0]const u8) c_int; |
| 11671 | extern "c" fn getcontext(ucp: *ucontext_t) c_int; | ||
| 11672 | 11465 | ||
| 11673 | extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize; | 11466 | extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize; |
| 11674 | extern "c" fn getentropy(buffer: [*]u8, size: usize) c_int; | 11467 | extern "c" fn getentropy(buffer: [*]u8, size: usize) c_int; |
lib/std/c/darwin.zig-101| ... | @@ -348,107 +348,6 @@ pub const VM = struct { | ... | @@ -348,107 +348,6 @@ pub const VM = struct { |
| 348 | 348 | ||
| 349 | pub const exception_type_t = c_int; | 349 | pub const exception_type_t = c_int; |
| 350 | 350 | ||
| 351 | pub const mcontext_t = switch (native_arch) { | ||
| 352 | .aarch64 => extern struct { | ||
| 353 | es: exception_state, | ||
| 354 | ss: thread_state, | ||
| 355 | ns: neon_state, | ||
| 356 | }, | ||
| 357 | .x86_64 => extern struct { | ||
| 358 | es: exception_state, | ||
| 359 | ss: thread_state, | ||
| 360 | fs: float_state, | ||
| 361 | }, | ||
| 362 | else => @compileError("unsupported arch"), | ||
| 363 | }; | ||
| 364 | |||
| 365 | pub const exception_state = switch (native_arch) { | ||
| 366 | .aarch64 => extern struct { | ||
| 367 | far: u64, // Virtual Fault Address | ||
| 368 | esr: u32, // Exception syndrome | ||
| 369 | exception: u32, // Number of arm exception taken | ||
| 370 | }, | ||
| 371 | .x86_64 => extern struct { | ||
| 372 | trapno: u16, | ||
| 373 | cpu: u16, | ||
| 374 | err: u32, | ||
| 375 | faultvaddr: u64, | ||
| 376 | }, | ||
| 377 | else => @compileError("unsupported arch"), | ||
| 378 | }; | ||
| 379 | |||
| 380 | pub const thread_state = switch (native_arch) { | ||
| 381 | .aarch64 => extern struct { | ||
| 382 | /// General purpose registers | ||
| 383 | regs: [29]u64, | ||
| 384 | /// Frame pointer x29 | ||
| 385 | fp: u64, | ||
| 386 | /// Link register x30 | ||
| 387 | lr: u64, | ||
| 388 | /// Stack pointer x31 | ||
| 389 | sp: u64, | ||
| 390 | /// Program counter | ||
| 391 | pc: u64, | ||
| 392 | /// Current program status register | ||
| 393 | cpsr: u32, | ||
| 394 | __pad: u32, | ||
| 395 | }, | ||
| 396 | .x86_64 => extern struct { | ||
| 397 | rax: u64, | ||
| 398 | rbx: u64, | ||
| 399 | rcx: u64, | ||
| 400 | rdx: u64, | ||
| 401 | rdi: u64, | ||
| 402 | rsi: u64, | ||
| 403 | rbp: u64, | ||
| 404 | rsp: u64, | ||
| 405 | r8: u64, | ||
| 406 | r9: u64, | ||
| 407 | r10: u64, | ||
| 408 | r11: u64, | ||
| 409 | r12: u64, | ||
| 410 | r13: u64, | ||
| 411 | r14: u64, | ||
| 412 | r15: u64, | ||
| 413 | rip: u64, | ||
| 414 | rflags: u64, | ||
| 415 | cs: u64, | ||
| 416 | fs: u64, | ||
| 417 | gs: u64, | ||
| 418 | }, | ||
| 419 | else => @compileError("unsupported arch"), | ||
| 420 | }; | ||
| 421 | |||
| 422 | pub const neon_state = extern struct { | ||
| 423 | q: [32]u128, | ||
| 424 | fpsr: u32, | ||
| 425 | fpcr: u32, | ||
| 426 | }; | ||
| 427 | |||
| 428 | pub const float_state = extern struct { | ||
| 429 | reserved: [2]c_int, | ||
| 430 | fcw: u16, | ||
| 431 | fsw: u16, | ||
| 432 | ftw: u8, | ||
| 433 | rsrv1: u8, | ||
| 434 | fop: u16, | ||
| 435 | ip: u32, | ||
| 436 | cs: u16, | ||
| 437 | rsrv2: u16, | ||
| 438 | dp: u32, | ||
| 439 | ds: u16, | ||
| 440 | rsrv3: u16, | ||
| 441 | mxcsr: u32, | ||
| 442 | mxcsrmask: u32, | ||
| 443 | stmm: [8]stmm_reg, | ||
| 444 | xmm: [16]xmm_reg, | ||
| 445 | rsrv4: [96]u8, | ||
| 446 | reserved1: c_int, | ||
| 447 | }; | ||
| 448 | |||
| 449 | pub const stmm_reg = [16]u8; | ||
| 450 | pub const xmm_reg = [16]u8; | ||
| 451 | |||
| 452 | pub extern "c" fn NSVersionOfRunTimeLibrary(library_name: [*:0]const u8) u32; | 351 | pub extern "c" fn NSVersionOfRunTimeLibrary(library_name: [*:0]const u8) u32; |
| 453 | pub extern "c" fn _NSGetExecutablePath(buf: [*:0]u8, bufsize: *u32) c_int; | 352 | pub extern "c" fn _NSGetExecutablePath(buf: [*:0]u8, bufsize: *u32) c_int; |
| 454 | pub extern "c" fn _dyld_image_count() u32; | 353 | pub extern "c" fn _dyld_image_count() u32; |
lib/std/c/dragonfly.zig-40| ... | @@ -13,46 +13,6 @@ pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) | ... | @@ -13,46 +13,6 @@ pub extern "c" fn ptrace(request: c_int, pid: pid_t, addr: caddr_t, data: c_int) |
| 13 | pub extern "c" fn umtx_sleep(ptr: *const volatile c_int, value: c_int, timeout: c_int) c_int; | 13 | pub extern "c" fn umtx_sleep(ptr: *const volatile c_int, value: c_int, timeout: c_int) c_int; |
| 14 | pub extern "c" fn umtx_wakeup(ptr: *const volatile c_int, count: c_int) c_int; | 14 | pub extern "c" fn umtx_wakeup(ptr: *const volatile c_int, count: c_int) c_int; |
| 15 | 15 | ||
| 16 | pub const mcontext_t = extern struct { | ||
| 17 | onstack: register_t, // XXX - sigcontext compat. | ||
| 18 | rdi: register_t, | ||
| 19 | rsi: register_t, | ||
| 20 | rdx: register_t, | ||
| 21 | rcx: register_t, | ||
| 22 | r8: register_t, | ||
| 23 | r9: register_t, | ||
| 24 | rax: register_t, | ||
| 25 | rbx: register_t, | ||
| 26 | rbp: register_t, | ||
| 27 | r10: register_t, | ||
| 28 | r11: register_t, | ||
| 29 | r12: register_t, | ||
| 30 | r13: register_t, | ||
| 31 | r14: register_t, | ||
| 32 | r15: register_t, | ||
| 33 | xflags: register_t, | ||
| 34 | trapno: register_t, | ||
| 35 | addr: register_t, | ||
| 36 | flags: register_t, | ||
| 37 | err: register_t, | ||
| 38 | rip: register_t, | ||
| 39 | cs: register_t, | ||
| 40 | rflags: register_t, | ||
| 41 | rsp: register_t, // machine state | ||
| 42 | ss: register_t, | ||
| 43 | |||
| 44 | len: c_uint, // sizeof(mcontext_t) | ||
| 45 | fpformat: c_uint, | ||
| 46 | ownedfp: c_uint, | ||
| 47 | reserved: c_uint, | ||
| 48 | unused: [8]c_uint, | ||
| 49 | |||
| 50 | // NOTE! 64-byte aligned as of here. Also must match savefpu structure. | ||
| 51 | fpregs: [256]c_int align(64), | ||
| 52 | }; | ||
| 53 | |||
| 54 | pub const register_t = isize; | ||
| 55 | |||
| 56 | pub const E = enum(u16) { | 16 | pub const E = enum(u16) { |
| 57 | /// No error occurred. | 17 | /// No error occurred. |
| 58 | SUCCESS = 0, | 18 | SUCCESS = 0, |
lib/std/c/haiku.zig-263| ... | @@ -273,269 +273,6 @@ pub const E = enum(i32) { | ... | @@ -273,269 +273,6 @@ pub const E = enum(i32) { |
| 273 | 273 | ||
| 274 | pub const status_t = i32; | 274 | pub const status_t = i32; |
| 275 | 275 | ||
| 276 | pub const mcontext_t = switch (builtin.cpu.arch) { | ||
| 277 | .arm, .thumb => extern struct { | ||
| 278 | r0: u32, | ||
| 279 | r1: u32, | ||
| 280 | r2: u32, | ||
| 281 | r3: u32, | ||
| 282 | r4: u32, | ||
| 283 | r5: u32, | ||
| 284 | r6: u32, | ||
| 285 | r7: u32, | ||
| 286 | r8: u32, | ||
| 287 | r9: u32, | ||
| 288 | r10: u32, | ||
| 289 | r11: u32, | ||
| 290 | r12: u32, | ||
| 291 | r13: u32, | ||
| 292 | r14: u32, | ||
| 293 | r15: u32, | ||
| 294 | cpsr: u32, | ||
| 295 | }, | ||
| 296 | .aarch64 => extern struct { | ||
| 297 | x: [10]u64, | ||
| 298 | lr: u64, | ||
| 299 | sp: u64, | ||
| 300 | elr: u64, | ||
| 301 | spsr: u64, | ||
| 302 | fp_q: [32]u128, | ||
| 303 | fpsr: u32, | ||
| 304 | fpcr: u32, | ||
| 305 | }, | ||
| 306 | .m68k => extern struct { | ||
| 307 | pc: u32, | ||
| 308 | d0: u32, | ||
| 309 | d1: u32, | ||
| 310 | d2: u32, | ||
| 311 | d3: u32, | ||
| 312 | d4: u32, | ||
| 313 | d5: u32, | ||
| 314 | d6: u32, | ||
| 315 | d7: u32, | ||
| 316 | a0: u32, | ||
| 317 | a1: u32, | ||
| 318 | a2: u32, | ||
| 319 | a3: u32, | ||
| 320 | a4: u32, | ||
| 321 | a5: u32, | ||
| 322 | a6: u32, | ||
| 323 | a7: u32, | ||
| 324 | ccr: u8, | ||
| 325 | f0: f64, | ||
| 326 | f1: f64, | ||
| 327 | f2: f64, | ||
| 328 | f3: f64, | ||
| 329 | f4: f64, | ||
| 330 | f5: f64, | ||
| 331 | f6: f64, | ||
| 332 | f7: f64, | ||
| 333 | f8: f64, | ||
| 334 | f9: f64, | ||
| 335 | f10: f64, | ||
| 336 | f11: f64, | ||
| 337 | f12: f64, | ||
| 338 | f13: f64, | ||
| 339 | }, | ||
| 340 | .mipsel => extern struct { | ||
| 341 | r0: u32, | ||
| 342 | }, | ||
| 343 | .powerpc => extern struct { | ||
| 344 | pc: u32, | ||
| 345 | r0: u32, | ||
| 346 | r1: u32, | ||
| 347 | r2: u32, | ||
| 348 | r3: u32, | ||
| 349 | r4: u32, | ||
| 350 | r5: u32, | ||
| 351 | r6: u32, | ||
| 352 | r7: u32, | ||
| 353 | r8: u32, | ||
| 354 | r9: u32, | ||
| 355 | r10: u32, | ||
| 356 | r11: u32, | ||
| 357 | r12: u32, | ||
| 358 | f0: f64, | ||
| 359 | f1: f64, | ||
| 360 | f2: f64, | ||
| 361 | f3: f64, | ||
| 362 | f4: f64, | ||
| 363 | f5: f64, | ||
| 364 | f6: f64, | ||
| 365 | f7: f64, | ||
| 366 | f8: f64, | ||
| 367 | f9: f64, | ||
| 368 | f10: f64, | ||
| 369 | f11: f64, | ||
| 370 | f12: f64, | ||
| 371 | f13: f64, | ||
| 372 | reserved: u32, | ||
| 373 | fpscr: u32, | ||
| 374 | ctr: u32, | ||
| 375 | xer: u32, | ||
| 376 | cr: u32, | ||
| 377 | msr: u32, | ||
| 378 | lr: u32, | ||
| 379 | }, | ||
| 380 | .riscv64 => extern struct { | ||
| 381 | x: [31]u64, | ||
| 382 | pc: u64, | ||
| 383 | f: [32]f64, | ||
| 384 | fcsr: u64, | ||
| 385 | }, | ||
| 386 | .sparc64 => extern struct { | ||
| 387 | g1: u64, | ||
| 388 | g2: u64, | ||
| 389 | g3: u64, | ||
| 390 | g4: u64, | ||
| 391 | g5: u64, | ||
| 392 | g6: u64, | ||
| 393 | g7: u64, | ||
| 394 | o0: u64, | ||
| 395 | o1: u64, | ||
| 396 | o2: u64, | ||
| 397 | o3: u64, | ||
| 398 | o4: u64, | ||
| 399 | o5: u64, | ||
| 400 | sp: u64, | ||
| 401 | o7: u64, | ||
| 402 | l0: u64, | ||
| 403 | l1: u64, | ||
| 404 | l2: u64, | ||
| 405 | l3: u64, | ||
| 406 | l4: u64, | ||
| 407 | l5: u64, | ||
| 408 | l6: u64, | ||
| 409 | l7: u64, | ||
| 410 | i0: u64, | ||
| 411 | i1: u64, | ||
| 412 | i2: u64, | ||
| 413 | i3: u64, | ||
| 414 | i4: u64, | ||
| 415 | i5: u64, | ||
| 416 | fp: u64, | ||
| 417 | i7: u64, | ||
| 418 | }, | ||
| 419 | .x86 => extern struct { | ||
| 420 | pub const old_extended_regs = extern struct { | ||
| 421 | control: u16, | ||
| 422 | reserved1: u16, | ||
| 423 | status: u16, | ||
| 424 | reserved2: u16, | ||
| 425 | tag: u16, | ||
| 426 | reserved3: u16, | ||
| 427 | eip: u32, | ||
| 428 | cs: u16, | ||
| 429 | opcode: u16, | ||
| 430 | datap: u32, | ||
| 431 | ds: u16, | ||
| 432 | reserved4: u16, | ||
| 433 | fp_mmx: [8][10]u8, | ||
| 434 | }; | ||
| 435 | |||
| 436 | pub const fp_register = extern struct { value: [10]u8, reserved: [6]u8 }; | ||
| 437 | |||
| 438 | pub const xmm_register = extern struct { value: [16]u8 }; | ||
| 439 | |||
| 440 | pub const new_extended_regs = extern struct { | ||
| 441 | control: u16, | ||
| 442 | status: u16, | ||
| 443 | tag: u16, | ||
| 444 | opcode: u16, | ||
| 445 | eip: u32, | ||
| 446 | cs: u16, | ||
| 447 | reserved1: u16, | ||
| 448 | datap: u32, | ||
| 449 | ds: u16, | ||
| 450 | reserved2: u16, | ||
| 451 | mxcsr: u32, | ||
| 452 | reserved3: u32, | ||
| 453 | fp_mmx: [8]fp_register, | ||
| 454 | xmmx: [8]xmm_register, | ||
| 455 | reserved4: [224]u8, | ||
| 456 | }; | ||
| 457 | |||
| 458 | pub const extended_regs = extern struct { | ||
| 459 | state: extern union { | ||
| 460 | old_format: old_extended_regs, | ||
| 461 | new_format: new_extended_regs, | ||
| 462 | }, | ||
| 463 | format: u32, | ||
| 464 | }; | ||
| 465 | |||
| 466 | eip: u32, | ||
| 467 | eflags: u32, | ||
| 468 | eax: u32, | ||
| 469 | ecx: u32, | ||
| 470 | edx: u32, | ||
| 471 | esp: u32, | ||
| 472 | ebp: u32, | ||
| 473 | reserved: u32, | ||
| 474 | xregs: extended_regs, | ||
| 475 | edi: u32, | ||
| 476 | esi: u32, | ||
| 477 | ebx: u32, | ||
| 478 | }, | ||
| 479 | .x86_64 => extern struct { | ||
| 480 | pub const fp_register = extern struct { | ||
| 481 | value: [10]u8, | ||
| 482 | reserved: [6]u8, | ||
| 483 | }; | ||
| 484 | |||
| 485 | pub const xmm_register = extern struct { | ||
| 486 | value: [16]u8, | ||
| 487 | }; | ||
| 488 | |||
| 489 | pub const fpu_state = extern struct { | ||
| 490 | control: u16, | ||
| 491 | status: u16, | ||
| 492 | tag: u16, | ||
| 493 | opcode: u16, | ||
| 494 | rip: u64, | ||
| 495 | rdp: u64, | ||
| 496 | mxcsr: u32, | ||
| 497 | mscsr_mask: u32, | ||
| 498 | |||
| 499 | fp_mmx: [8]fp_register, | ||
| 500 | xmm: [16]xmm_register, | ||
| 501 | reserved: [96]u8, | ||
| 502 | }; | ||
| 503 | |||
| 504 | pub const xstate_hdr = extern struct { | ||
| 505 | bv: u64, | ||
| 506 | xcomp_bv: u64, | ||
| 507 | reserved: [48]u8, | ||
| 508 | }; | ||
| 509 | |||
| 510 | pub const savefpu = extern struct { | ||
| 511 | fxsave: fpu_state, | ||
| 512 | xstate: xstate_hdr, | ||
| 513 | ymm: [16]xmm_register, | ||
| 514 | }; | ||
| 515 | |||
| 516 | rax: u64, | ||
| 517 | rbx: u64, | ||
| 518 | rcx: u64, | ||
| 519 | rdx: u64, | ||
| 520 | rdi: u64, | ||
| 521 | rsi: u64, | ||
| 522 | rbp: u64, | ||
| 523 | r8: u64, | ||
| 524 | r9: u64, | ||
| 525 | r10: u64, | ||
| 526 | r11: u64, | ||
| 527 | r12: u64, | ||
| 528 | r13: u64, | ||
| 529 | r14: u64, | ||
| 530 | r15: u64, | ||
| 531 | rsp: u64, | ||
| 532 | rip: u64, | ||
| 533 | rflags: u64, | ||
| 534 | fpu: savefpu, | ||
| 535 | }, | ||
| 536 | else => void, | ||
| 537 | }; | ||
| 538 | |||
| 539 | pub const DirEnt = extern struct { | 276 | pub const DirEnt = extern struct { |
| 540 | /// device | 277 | /// device |
| 541 | dev: dev_t, | 278 | dev: dev_t, |
lib/std/c/openbsd.zig-47| ... | @@ -144,53 +144,6 @@ pub const TCIO = enum(u32) { | ... | @@ -144,53 +144,6 @@ pub const TCIO = enum(u32) { |
| 144 | ION = 4, | 144 | ION = 4, |
| 145 | }; | 145 | }; |
| 146 | 146 | ||
| 147 | pub const ucontext_t = switch (builtin.cpu.arch) { | ||
| 148 | .x86_64 => extern struct { | ||
| 149 | sc_rdi: c_long, | ||
| 150 | sc_rsi: c_long, | ||
| 151 | sc_rdx: c_long, | ||
| 152 | sc_rcx: c_long, | ||
| 153 | sc_r8: c_long, | ||
| 154 | sc_r9: c_long, | ||
| 155 | sc_r10: c_long, | ||
| 156 | sc_r11: c_long, | ||
| 157 | sc_r12: c_long, | ||
| 158 | sc_r13: c_long, | ||
| 159 | sc_r14: c_long, | ||
| 160 | sc_r15: c_long, | ||
| 161 | sc_rbp: c_long, | ||
| 162 | sc_rbx: c_long, | ||
| 163 | sc_rax: c_long, | ||
| 164 | sc_gs: c_long, | ||
| 165 | sc_fs: c_long, | ||
| 166 | sc_es: c_long, | ||
| 167 | sc_ds: c_long, | ||
| 168 | sc_trapno: c_long, | ||
| 169 | sc_err: c_long, | ||
| 170 | sc_rip: c_long, | ||
| 171 | sc_cs: c_long, | ||
| 172 | sc_rflags: c_long, | ||
| 173 | sc_rsp: c_long, | ||
| 174 | sc_ss: c_long, | ||
| 175 | |||
| 176 | sc_fpstate: *anyopaque, // struct fxsave64 * | ||
| 177 | __sc_unused: c_int, | ||
| 178 | sc_mask: c_int, | ||
| 179 | sc_cookie: c_long, | ||
| 180 | }, | ||
| 181 | .aarch64 => extern struct { | ||
| 182 | __sc_unused: c_int, | ||
| 183 | sc_mask: c_int, | ||
| 184 | sc_sp: c_ulong, | ||
| 185 | sc_lr: c_ulong, | ||
| 186 | sc_elr: c_ulong, | ||
| 187 | sc_spsr: c_ulong, | ||
| 188 | sc_x: [30]c_ulong, | ||
| 189 | sc_cookie: c_long, | ||
| 190 | }, | ||
| 191 | else => @compileError("missing ucontext_t type definition"), | ||
| 192 | }; | ||
| 193 | |||
| 194 | pub const E = enum(u16) { | 147 | pub const E = enum(u16) { |
| 195 | /// No error occurred. | 148 | /// No error occurred. |
| 196 | SUCCESS = 0, | 149 | SUCCESS = 0, |
lib/std/debug.zig+22-114| ... | @@ -22,6 +22,7 @@ pub const ElfFile = @import("debug/ElfFile.zig"); | ... | @@ -22,6 +22,7 @@ pub const ElfFile = @import("debug/ElfFile.zig"); |
| 22 | pub const SelfInfo = @import("debug/SelfInfo.zig"); | 22 | pub const SelfInfo = @import("debug/SelfInfo.zig"); |
| 23 | pub const Info = @import("debug/Info.zig"); | 23 | pub const Info = @import("debug/Info.zig"); |
| 24 | pub const Coverage = @import("debug/Coverage.zig"); | 24 | pub const Coverage = @import("debug/Coverage.zig"); |
| 25 | pub const cpu_context = @import("debug/cpu_context.zig"); | ||
| 25 | 26 | ||
| 26 | pub const simple_panic = @import("debug/simple_panic.zig"); | 27 | pub const simple_panic = @import("debug/simple_panic.zig"); |
| 27 | pub const no_panic = @import("debug/no_panic.zig"); | 28 | pub const no_panic = @import("debug/no_panic.zig"); |
| ... | @@ -331,66 +332,8 @@ test dumpHexFallible { | ... | @@ -331,66 +332,8 @@ test dumpHexFallible { |
| 331 | try std.testing.expectEqualStrings(expected, aw.written()); | 332 | try std.testing.expectEqualStrings(expected, aw.written()); |
| 332 | } | 333 | } |
| 333 | 334 | ||
| 334 | /// Platform-specific thread state. This contains register state, and on some platforms | 335 | /// The pointer through which a `cpu_context.Native` is received from callers of stack tracing logic. |
| 335 | /// information about the stack. This is not safe to trivially copy, because some platforms | 336 | pub const CpuContextPtr = if (cpu_context.Native == noreturn) noreturn else *const cpu_context.Native; |
| 336 | /// use internal pointers within this structure. After copying, call `relocateContext`. | ||
| 337 | pub const ThreadContext = ThreadContext: { | ||
| 338 | // Allow overriding the target's `ThreadContext` by exposing `root.debug.ThreadContext`. | ||
| 339 | if (@hasDecl(root, "debug") and @hasDecl(root.debug, "ThreadContext")) { | ||
| 340 | break :ThreadContext root.debug.ThreadContext; | ||
| 341 | } | ||
| 342 | |||
| 343 | if (native_os == .windows) break :ThreadContext windows.CONTEXT; | ||
| 344 | if (posix.ucontext_t != void) break :ThreadContext posix.ucontext_t; | ||
| 345 | |||
| 346 | break :ThreadContext noreturn; | ||
| 347 | }; | ||
| 348 | /// Updates any internal pointers of a `ThreadContext` after the caller copies it. | ||
| 349 | pub fn relocateContext(dest: *ThreadContext) void { | ||
| 350 | switch (native_os) { | ||
| 351 | .macos => dest.mcontext = &dest.__mcontext_data, | ||
| 352 | else => {}, | ||
| 353 | } | ||
| 354 | } | ||
| 355 | /// The value which is placed on the stack to make a copy of a `ThreadContext`. | ||
| 356 | const ThreadContextBuf = if (ThreadContext == noreturn) void else ThreadContext; | ||
| 357 | /// The pointer through which a `ThreadContext` is received from callers of stack tracing logic. | ||
| 358 | pub const ThreadContextPtr = if (ThreadContext == noreturn) noreturn else *const ThreadContext; | ||
| 359 | |||
| 360 | /// Capture the current context. The register values in the context will reflect the | ||
| 361 | /// state after the platform `getcontext` function returns. | ||
| 362 | /// | ||
| 363 | /// It is valid to call this if the platform doesn't have context capturing support, | ||
| 364 | /// in that case `false` will be returned. This function is `inline` so that the `false` | ||
| 365 | /// is comptime-known at the call site in that case. | ||
| 366 | pub inline fn getContext(context: *ThreadContextBuf) bool { | ||
| 367 | // Allow overriding the target's `getContext` by exposing `root.debug.getContext`. | ||
| 368 | if (@hasDecl(root, "debug") and @hasDecl(root.debug, "getContext")) { | ||
| 369 | return root.debug.getContext(context); | ||
| 370 | } | ||
| 371 | |||
| 372 | if (native_os == .windows) { | ||
| 373 | context.* = std.mem.zeroes(windows.CONTEXT); | ||
| 374 | windows.ntdll.RtlCaptureContext(context); | ||
| 375 | return true; | ||
| 376 | } | ||
| 377 | |||
| 378 | if (@TypeOf(posix.system.getcontext) != void) { | ||
| 379 | if (posix.system.getcontext(context) != 0) return false; | ||
| 380 | if (native_os == .macos) { | ||
| 381 | assert(context.mcsize == @sizeOf(std.c.mcontext_t)); | ||
| 382 | |||
| 383 | // On aarch64-macos, the system getcontext doesn't write anything into the pc | ||
| 384 | // register slot, it only writes lr. This makes the context consistent with | ||
| 385 | // other aarch64 getcontext implementations which write the current lr | ||
| 386 | // (where getcontext will return to) into both the lr and pc slot of the context. | ||
| 387 | if (native_arch == .aarch64) context.mcontext.ss.pc = context.mcontext.ss.lr; | ||
| 388 | } | ||
| 389 | return true; | ||
| 390 | } | ||
| 391 | |||
| 392 | return false; | ||
| 393 | } | ||
| 394 | 337 | ||
| 395 | /// Invokes detectable illegal behavior when `ok` is `false`. | 338 | /// Invokes detectable illegal behavior when `ok` is `false`. |
| 396 | /// | 339 | /// |
| ... | @@ -616,10 +559,10 @@ pub const StackUnwindOptions = struct { | ... | @@ -616,10 +559,10 @@ pub const StackUnwindOptions = struct { |
| 616 | /// used to omit intermediate handling code (for instance, a panic handler and its machinery) | 559 | /// used to omit intermediate handling code (for instance, a panic handler and its machinery) |
| 617 | /// from stack traces. | 560 | /// from stack traces. |
| 618 | first_address: ?usize = null, | 561 | first_address: ?usize = null, |
| 619 | /// If not `null`, we will unwind from this `ThreadContext` instead of the current top of the | 562 | /// If not `null`, we will unwind from this `cpu_context.Native` instead of the current top of |
| 620 | /// stack. The main use case here is printing stack traces from signal handlers, where the | 563 | /// the stack. The main use case here is printing stack traces from signal handlers, where the |
| 621 | /// kernel provides a `*const ThreadContext` of the state before the signal. | 564 | /// kernel provides a `*const cpu_context.Native` of the state before the signal. |
| 622 | context: ?ThreadContextPtr = null, | 565 | context: ?CpuContextPtr = null, |
| 623 | /// If `true`, stack unwinding strategies which may cause crashes are used as a last resort. | 566 | /// If `true`, stack unwinding strategies which may cause crashes are used as a last resort. |
| 624 | /// If `false`, only known-safe mechanisms will be attempted. | 567 | /// If `false`, only known-safe mechanisms will be attempted. |
| 625 | allow_unsafe_unwind: bool = false, | 568 | allow_unsafe_unwind: bool = false, |
| ... | @@ -630,8 +573,7 @@ pub const StackUnwindOptions = struct { | ... | @@ -630,8 +573,7 @@ pub const StackUnwindOptions = struct { |
| 630 | /// | 573 | /// |
| 631 | /// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it. | 574 | /// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it. |
| 632 | pub fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) std.builtin.StackTrace { | 575 | pub fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) std.builtin.StackTrace { |
| 633 | var context_buf: ThreadContextBuf = undefined; | 576 | var it = StackIterator.init(options.context) catch { |
| 634 | var it = StackIterator.init(options.context, &context_buf) catch { | ||
| 635 | return .{ .index = 0, .instruction_addresses = &.{} }; | 577 | return .{ .index = 0, .instruction_addresses = &.{} }; |
| 636 | }; | 578 | }; |
| 637 | defer it.deinit(); | 579 | defer it.deinit(); |
| ... | @@ -670,14 +612,7 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_ | ... | @@ -670,14 +612,7 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_ |
| 670 | return; | 612 | return; |
| 671 | }, | 613 | }, |
| 672 | }; | 614 | }; |
| 673 | var context_buf: ThreadContextBuf = undefined; | 615 | var it = StackIterator.init(options.context) catch |err| switch (err) { |
| 674 | var it = StackIterator.init(options.context, &context_buf) catch |err| switch (err) { | ||
| 675 | error.OutOfMemory => { | ||
| 676 | tty_config.setColor(writer, .dim) catch {}; | ||
| 677 | try writer.print("Cannot print stack trace: out of memory\n", .{}); | ||
| 678 | tty_config.setColor(writer, .reset) catch {}; | ||
| 679 | return; | ||
| 680 | }, | ||
| 681 | error.CannotUnwindFromContext => { | 616 | error.CannotUnwindFromContext => { |
| 682 | tty_config.setColor(writer, .dim) catch {}; | 617 | tty_config.setColor(writer, .dim) catch {}; |
| 683 | try writer.print("Cannot print stack trace: context unwind unavailable for target\n", .{}); | 618 | try writer.print("Cannot print stack trace: context unwind unavailable for target\n", .{}); |
| ... | @@ -794,9 +729,9 @@ const StackIterator = union(enum) { | ... | @@ -794,9 +729,9 @@ const StackIterator = union(enum) { |
| 794 | fp: usize, | 729 | fp: usize, |
| 795 | 730 | ||
| 796 | /// It is important that this function is marked `inline` so that it can safely use | 731 | /// It is important that this function is marked `inline` so that it can safely use |
| 797 | /// `@frameAddress` and `getContext` as the caller's stack frame and our own are one | 732 | /// `@frameAddress` and `cpu_context.Native.current` as the caller's stack frame and |
| 798 | /// and the same. | 733 | /// our own are one and the same. |
| 799 | inline fn init(context_opt: ?ThreadContextPtr, context_buf: *ThreadContextBuf) error{ OutOfMemory, CannotUnwindFromContext }!StackIterator { | 734 | inline fn init(opt_context_ptr: ?CpuContextPtr) error{CannotUnwindFromContext}!StackIterator { |
| 800 | if (builtin.cpu.arch.isSPARC()) { | 735 | if (builtin.cpu.arch.isSPARC()) { |
| 801 | // Flush all the register windows on stack. | 736 | // Flush all the register windows on stack. |
| 802 | if (builtin.cpu.has(.sparc, .v9)) { | 737 | if (builtin.cpu.has(.sparc, .v9)) { |
| ... | @@ -805,14 +740,12 @@ const StackIterator = union(enum) { | ... | @@ -805,14 +740,12 @@ const StackIterator = union(enum) { |
| 805 | asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS | 740 | asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS |
| 806 | } | 741 | } |
| 807 | } | 742 | } |
| 808 | if (context_opt) |context| { | 743 | if (opt_context_ptr) |context_ptr| { |
| 809 | if (!SelfInfo.supports_unwinding) return error.CannotUnwindFromContext; | 744 | if (!SelfInfo.supports_unwinding) return error.CannotUnwindFromContext; |
| 810 | context_buf.* = context.*; | 745 | return .{ .di = .init(context_ptr) }; |
| 811 | relocateContext(context_buf); | ||
| 812 | return .{ .di = try .init(context_buf, getDebugInfoAllocator()) }; | ||
| 813 | } | 746 | } |
| 814 | if (SelfInfo.supports_unwinding and getContext(context_buf)) { | 747 | if (SelfInfo.supports_unwinding and cpu_context.Native != noreturn) { |
| 815 | return .{ .di = try .init(context_buf, getDebugInfoAllocator()) }; | 748 | return .{ .di = .init(&.current()) }; |
| 816 | } | 749 | } |
| 817 | return .{ .fp = @frameAddress() }; | 750 | return .{ .fp = @frameAddress() }; |
| 818 | } | 751 | } |
| ... | @@ -1212,7 +1145,7 @@ pub const have_segfault_handling_support = switch (native_os) { | ... | @@ -1212,7 +1145,7 @@ pub const have_segfault_handling_support = switch (native_os) { |
| 1212 | .windows, | 1145 | .windows, |
| 1213 | => true, | 1146 | => true, |
| 1214 | 1147 | ||
| 1215 | .freebsd, .openbsd => ThreadContext != noreturn, | 1148 | .freebsd, .openbsd => cpu_context.Native != noreturn, |
| 1216 | else => false, | 1149 | else => false, |
| 1217 | }; | 1150 | }; |
| 1218 | 1151 | ||
| ... | @@ -1309,33 +1242,8 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa | ... | @@ -1309,33 +1242,8 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa |
| 1309 | }; | 1242 | }; |
| 1310 | break :info .{ addr, name }; | 1243 | break :info .{ addr, name }; |
| 1311 | }; | 1244 | }; |
| 1312 | 1245 | const opt_cpu_context: ?cpu_context.Native = cpu_context.fromPosixSignalContext(ctx_ptr); | |
| 1313 | if (ThreadContext == noreturn) return handleSegfault(addr, name, null); | 1246 | handleSegfault(addr, name, if (opt_cpu_context) |*ctx| ctx else null); |
| 1314 | |||
| 1315 | // Some kernels don't align `ctx_ptr` properly, so we'll copy it into a local buffer. | ||
| 1316 | var copied_ctx: posix.ucontext_t = undefined; | ||
| 1317 | const orig_ctx: *align(1) posix.ucontext_t = @ptrCast(ctx_ptr); | ||
| 1318 | copied_ctx = orig_ctx.*; | ||
| 1319 | if (builtin.os.tag.isDarwin() and builtin.cpu.arch == .aarch64) { | ||
| 1320 | // The kernel incorrectly writes the contents of `__mcontext_data` right after `mcontext`, | ||
| 1321 | // rather than after the 8 bytes of padding that are supposed to sit between the two. Copy the | ||
| 1322 | // contents to the right place so that the `mcontext` pointer will be correct after the | ||
| 1323 | // `relocateContext` call below. | ||
| 1324 | const WrittenContext = extern struct { | ||
| 1325 | onstack: c_int, | ||
| 1326 | sigmask: std.c.sigset_t, | ||
| 1327 | stack: std.c.stack_t, | ||
| 1328 | link: ?*std.c.ucontext_t, | ||
| 1329 | mcsize: u64, | ||
| 1330 | mcontext: *std.c.mcontext_t, | ||
| 1331 | __mcontext_data: std.c.mcontext_t align(@sizeOf(usize)), // Disable padding after `mcontext`. | ||
| 1332 | }; | ||
| 1333 | const written_ctx: *align(1) WrittenContext = @ptrCast(ctx_ptr); | ||
| 1334 | copied_ctx.__mcontext_data = written_ctx.__mcontext_data; | ||
| 1335 | } | ||
| 1336 | relocateContext(&copied_ctx); | ||
| 1337 | |||
| 1338 | handleSegfault(addr, name, &copied_ctx); | ||
| 1339 | } | 1247 | } |
| 1340 | 1248 | ||
| 1341 | fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(.winapi) c_long { | 1249 | fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(.winapi) c_long { |
| ... | @@ -1347,10 +1255,10 @@ fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(.winapi) c_ | ... | @@ -1347,10 +1255,10 @@ fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(.winapi) c_ |
| 1347 | windows.EXCEPTION_STACK_OVERFLOW => .{ "Stack overflow", null }, | 1255 | windows.EXCEPTION_STACK_OVERFLOW => .{ "Stack overflow", null }, |
| 1348 | else => return windows.EXCEPTION_CONTINUE_SEARCH, | 1256 | else => return windows.EXCEPTION_CONTINUE_SEARCH, |
| 1349 | }; | 1257 | }; |
| 1350 | handleSegfault(addr, name, info.ContextRecord); | 1258 | handleSegfault(addr, name, &cpu_context.fromWindowsContext(info.ContextRecord)); |
| 1351 | } | 1259 | } |
| 1352 | 1260 | ||
| 1353 | fn handleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?ThreadContextPtr) noreturn { | 1261 | fn handleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContextPtr) noreturn { |
| 1354 | // Allow overriding the target-agnostic segfault handler by exposing `root.debug.handleSegfault`. | 1262 | // Allow overriding the target-agnostic segfault handler by exposing `root.debug.handleSegfault`. |
| 1355 | if (@hasDecl(root, "debug") and @hasDecl(root.debug, "handleSegfault")) { | 1263 | if (@hasDecl(root, "debug") and @hasDecl(root.debug, "handleSegfault")) { |
| 1356 | return root.debug.handleSegfault(addr, name, opt_ctx); | 1264 | return root.debug.handleSegfault(addr, name, opt_ctx); |
| ... | @@ -1358,7 +1266,7 @@ fn handleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?ThreadContextPtr) no | ... | @@ -1358,7 +1266,7 @@ fn handleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?ThreadContextPtr) no |
| 1358 | return defaultHandleSegfault(addr, name, opt_ctx); | 1266 | return defaultHandleSegfault(addr, name, opt_ctx); |
| 1359 | } | 1267 | } |
| 1360 | 1268 | ||
| 1361 | pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?ThreadContextPtr) noreturn { | 1269 | pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContextPtr) noreturn { |
| 1362 | // There is very similar logic to the following in `defaultPanic`. | 1270 | // There is very similar logic to the following in `defaultPanic`. |
| 1363 | switch (panic_stage) { | 1271 | switch (panic_stage) { |
| 1364 | 0 => { | 1272 | 0 => { |
lib/std/debug/Dwarf.zig+55-2| ... | @@ -27,7 +27,6 @@ const Reader = std.Io.Reader; | ... | @@ -27,7 +27,6 @@ const Reader = std.Io.Reader; |
| 27 | const Dwarf = @This(); | 27 | const Dwarf = @This(); |
| 28 | 28 | ||
| 29 | pub const expression = @import("Dwarf/expression.zig"); | 29 | pub const expression = @import("Dwarf/expression.zig"); |
| 30 | pub const abi = @import("Dwarf/abi.zig"); | ||
| 31 | pub const call_frame = @import("Dwarf/call_frame.zig"); | 30 | pub const call_frame = @import("Dwarf/call_frame.zig"); |
| 32 | pub const Unwind = @import("Dwarf/Unwind.zig"); | 31 | pub const Unwind = @import("Dwarf/Unwind.zig"); |
| 33 | 32 | ||
| ... | @@ -1415,7 +1414,7 @@ pub fn readUnitHeader(r: *Reader, endian: Endian) ScanError!UnitHeader { | ... | @@ -1415,7 +1414,7 @@ pub fn readUnitHeader(r: *Reader, endian: Endian) ScanError!UnitHeader { |
| 1415 | } | 1414 | } |
| 1416 | 1415 | ||
| 1417 | /// Returns the DWARF register number for an x86_64 register number found in compact unwind info | 1416 | /// Returns the DWARF register number for an x86_64 register number found in compact unwind info |
| 1418 | pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 { | 1417 | pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u16 { |
| 1419 | return switch (unwind_reg_number) { | 1418 | return switch (unwind_reg_number) { |
| 1420 | 1 => 3, // RBX | 1419 | 1 => 3, // RBX |
| 1421 | 2 => 12, // R12 | 1420 | 2 => 12, // R12 |
| ... | @@ -1427,6 +1426,60 @@ pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 { | ... | @@ -1427,6 +1426,60 @@ pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 { |
| 1427 | }; | 1426 | }; |
| 1428 | } | 1427 | } |
| 1429 | 1428 | ||
| 1429 | /// Returns `null` for CPU architectures without an instruction pointer register. | ||
| 1430 | pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 { | ||
| 1431 | return switch (arch) { | ||
| 1432 | .x86 => 8, | ||
| 1433 | .x86_64 => 16, | ||
| 1434 | .arm, .armeb, .thumb, .thumbeb => 15, | ||
| 1435 | .aarch64, .aarch64_be => 32, | ||
| 1436 | else => null, | ||
| 1437 | }; | ||
| 1438 | } | ||
| 1439 | |||
| 1440 | pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 { | ||
| 1441 | return switch (arch) { | ||
| 1442 | .x86 => 5, | ||
| 1443 | .x86_64 => 6, | ||
| 1444 | .arm, .armeb, .thumb, .thumbeb => 11, | ||
| 1445 | .aarch64, .aarch64_be => 29, | ||
| 1446 | else => unreachable, | ||
| 1447 | }; | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 { | ||
| 1451 | return switch (arch) { | ||
| 1452 | .x86 => 4, | ||
| 1453 | .x86_64 => 7, | ||
| 1454 | .arm, .armeb, .thumb, .thumbeb => 13, | ||
| 1455 | .aarch64, .aarch64_be => 31, | ||
| 1456 | else => unreachable, | ||
| 1457 | }; | ||
| 1458 | } | ||
| 1459 | |||
| 1460 | /// Tells whether unwinding for this target is supported by the Dwarf standard. | ||
| 1461 | /// | ||
| 1462 | /// See also `std.debug.SelfInfo.supports_unwinding` which tells whether the Zig | ||
| 1463 | /// standard library has a working implementation of unwinding for this target. | ||
| 1464 | pub fn supportsUnwinding(target: *const std.Target) bool { | ||
| 1465 | return switch (target.cpu.arch) { | ||
| 1466 | .amdgcn, | ||
| 1467 | .nvptx, | ||
| 1468 | .nvptx64, | ||
| 1469 | .spirv32, | ||
| 1470 | .spirv64, | ||
| 1471 | => false, | ||
| 1472 | |||
| 1473 | // Enabling this causes relocation errors such as: | ||
| 1474 | // error: invalid relocation type R_RISCV_SUB32 at offset 0x20 | ||
| 1475 | .riscv64, .riscv64be, .riscv32, .riscv32be => false, | ||
| 1476 | |||
| 1477 | // Conservative guess. Feel free to update this logic with any targets | ||
| 1478 | // that are known to not support Dwarf unwinding. | ||
| 1479 | else => true, | ||
| 1480 | }; | ||
| 1481 | } | ||
| 1482 | |||
| 1430 | /// This function is to make it handy to comment out the return and make it | 1483 | /// This function is to make it handy to comment out the return and make it |
| 1431 | /// into a crash when working on this file. | 1484 | /// into a crash when working on this file. |
| 1432 | pub fn bad() error{InvalidDebugInfo} { | 1485 | pub fn bad() error{InvalidDebugInfo} { |
lib/std/debug/Dwarf/abi.zig deleted-351| ... | @@ -1,351 +0,0 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | |||
| 3 | const std = @import("../../std.zig"); | ||
| 4 | const mem = std.mem; | ||
| 5 | const posix = std.posix; | ||
| 6 | const Arch = std.Target.Cpu.Arch; | ||
| 7 | |||
| 8 | /// Tells whether unwinding for this target is supported by the Dwarf standard. | ||
| 9 | /// | ||
| 10 | /// See also `std.debug.SelfInfo.supports_unwinding` which tells whether the Zig | ||
| 11 | /// standard library has a working implementation of unwinding for this target. | ||
| 12 | pub fn supportsUnwinding(target: *const std.Target) bool { | ||
| 13 | return switch (target.cpu.arch) { | ||
| 14 | .amdgcn, | ||
| 15 | .nvptx, | ||
| 16 | .nvptx64, | ||
| 17 | .spirv32, | ||
| 18 | .spirv64, | ||
| 19 | => false, | ||
| 20 | |||
| 21 | // Enabling this causes relocation errors such as: | ||
| 22 | // error: invalid relocation type R_RISCV_SUB32 at offset 0x20 | ||
| 23 | .riscv64, .riscv64be, .riscv32, .riscv32be => false, | ||
| 24 | |||
| 25 | // Conservative guess. Feel free to update this logic with any targets | ||
| 26 | // that are known to not support Dwarf unwinding. | ||
| 27 | else => true, | ||
| 28 | }; | ||
| 29 | } | ||
| 30 | |||
| 31 | /// Returns `null` for CPU architectures without an instruction pointer register. | ||
| 32 | pub fn ipRegNum(arch: Arch) ?u8 { | ||
| 33 | return switch (arch) { | ||
| 34 | .x86 => 8, | ||
| 35 | .x86_64 => 16, | ||
| 36 | .arm, .armeb, .thumb, .thumbeb => 15, | ||
| 37 | .aarch64, .aarch64_be => 32, | ||
| 38 | else => null, | ||
| 39 | }; | ||
| 40 | } | ||
| 41 | |||
| 42 | pub fn fpRegNum(arch: Arch, reg_context: RegisterContext) u8 { | ||
| 43 | return switch (arch) { | ||
| 44 | // GCC on OS X historically did the opposite of ELF for these registers | ||
| 45 | // (only in .eh_frame), and that is now the convention for MachO | ||
| 46 | .x86 => if (reg_context.eh_frame and reg_context.is_macho) 4 else 5, | ||
| 47 | .x86_64 => 6, | ||
| 48 | .arm, .armeb, .thumb, .thumbeb => 11, | ||
| 49 | .aarch64, .aarch64_be => 29, | ||
| 50 | else => unreachable, | ||
| 51 | }; | ||
| 52 | } | ||
| 53 | |||
| 54 | pub fn spRegNum(arch: Arch, reg_context: RegisterContext) u8 { | ||
| 55 | return switch (arch) { | ||
| 56 | .x86 => if (reg_context.eh_frame and reg_context.is_macho) 5 else 4, | ||
| 57 | .x86_64 => 7, | ||
| 58 | .arm, .armeb, .thumb, .thumbeb => 13, | ||
| 59 | .aarch64, .aarch64_be => 31, | ||
| 60 | else => unreachable, | ||
| 61 | }; | ||
| 62 | } | ||
| 63 | |||
| 64 | pub const RegisterContext = struct { | ||
| 65 | eh_frame: bool, | ||
| 66 | is_macho: bool, | ||
| 67 | }; | ||
| 68 | |||
| 69 | pub const RegBytesError = error{ | ||
| 70 | InvalidRegister, | ||
| 71 | UnimplementedArch, | ||
| 72 | UnimplementedOs, | ||
| 73 | RegisterContextRequired, | ||
| 74 | ThreadContextNotSupported, | ||
| 75 | }; | ||
| 76 | |||
| 77 | /// Returns a slice containing the backing storage for `reg_number`. | ||
| 78 | /// | ||
| 79 | /// This function assumes the Dwarf information corresponds not necessarily to | ||
| 80 | /// the current executable, but at least with a matching CPU architecture and | ||
| 81 | /// OS. It is planned to lift this limitation with a future enhancement. | ||
| 82 | /// | ||
| 83 | /// `reg_context` describes in what context the register number is used, as it can have different | ||
| 84 | /// meanings depending on the DWARF container. It is only required when getting the stack or | ||
| 85 | /// frame pointer register on some architectures. | ||
| 86 | pub fn regBytes( | ||
| 87 | thread_context_ptr: *std.debug.ThreadContext, | ||
| 88 | reg_number: u8, | ||
| 89 | reg_context: ?RegisterContext, | ||
| 90 | ) RegBytesError![]u8 { | ||
| 91 | if (builtin.os.tag == .windows) { | ||
| 92 | return switch (builtin.cpu.arch) { | ||
| 93 | .x86 => switch (reg_number) { | ||
| 94 | 0 => mem.asBytes(&thread_context_ptr.Eax), | ||
| 95 | 1 => mem.asBytes(&thread_context_ptr.Ecx), | ||
| 96 | 2 => mem.asBytes(&thread_context_ptr.Edx), | ||
| 97 | 3 => mem.asBytes(&thread_context_ptr.Ebx), | ||
| 98 | 4 => mem.asBytes(&thread_context_ptr.Esp), | ||
| 99 | 5 => mem.asBytes(&thread_context_ptr.Ebp), | ||
| 100 | 6 => mem.asBytes(&thread_context_ptr.Esi), | ||
| 101 | 7 => mem.asBytes(&thread_context_ptr.Edi), | ||
| 102 | 8 => mem.asBytes(&thread_context_ptr.Eip), | ||
| 103 | 9 => mem.asBytes(&thread_context_ptr.EFlags), | ||
| 104 | 10 => mem.asBytes(&thread_context_ptr.SegCs), | ||
| 105 | 11 => mem.asBytes(&thread_context_ptr.SegSs), | ||
| 106 | 12 => mem.asBytes(&thread_context_ptr.SegDs), | ||
| 107 | 13 => mem.asBytes(&thread_context_ptr.SegEs), | ||
| 108 | 14 => mem.asBytes(&thread_context_ptr.SegFs), | ||
| 109 | 15 => mem.asBytes(&thread_context_ptr.SegGs), | ||
| 110 | else => error.InvalidRegister, | ||
| 111 | }, | ||
| 112 | .x86_64 => switch (reg_number) { | ||
| 113 | 0 => mem.asBytes(&thread_context_ptr.Rax), | ||
| 114 | 1 => mem.asBytes(&thread_context_ptr.Rdx), | ||
| 115 | 2 => mem.asBytes(&thread_context_ptr.Rcx), | ||
| 116 | 3 => mem.asBytes(&thread_context_ptr.Rbx), | ||
| 117 | 4 => mem.asBytes(&thread_context_ptr.Rsi), | ||
| 118 | 5 => mem.asBytes(&thread_context_ptr.Rdi), | ||
| 119 | 6 => mem.asBytes(&thread_context_ptr.Rbp), | ||
| 120 | 7 => mem.asBytes(&thread_context_ptr.Rsp), | ||
| 121 | 8 => mem.asBytes(&thread_context_ptr.R8), | ||
| 122 | 9 => mem.asBytes(&thread_context_ptr.R9), | ||
| 123 | 10 => mem.asBytes(&thread_context_ptr.R10), | ||
| 124 | 11 => mem.asBytes(&thread_context_ptr.R11), | ||
| 125 | 12 => mem.asBytes(&thread_context_ptr.R12), | ||
| 126 | 13 => mem.asBytes(&thread_context_ptr.R13), | ||
| 127 | 14 => mem.asBytes(&thread_context_ptr.R14), | ||
| 128 | 15 => mem.asBytes(&thread_context_ptr.R15), | ||
| 129 | 16 => mem.asBytes(&thread_context_ptr.Rip), | ||
| 130 | else => error.InvalidRegister, | ||
| 131 | }, | ||
| 132 | .aarch64, .aarch64_be => switch (reg_number) { | ||
| 133 | 0...30 => mem.asBytes(&thread_context_ptr.DUMMYUNIONNAME.X[reg_number]), | ||
| 134 | 31 => mem.asBytes(&thread_context_ptr.Sp), | ||
| 135 | 32 => mem.asBytes(&thread_context_ptr.Pc), | ||
| 136 | else => error.InvalidRegister, | ||
| 137 | }, | ||
| 138 | else => error.UnimplementedArch, | ||
| 139 | }; | ||
| 140 | } | ||
| 141 | |||
| 142 | if (posix.ucontext_t == void) return error.ThreadContextNotSupported; | ||
| 143 | |||
| 144 | const ucontext_ptr = thread_context_ptr; | ||
| 145 | return switch (builtin.cpu.arch) { | ||
| 146 | .x86 => switch (builtin.os.tag) { | ||
| 147 | .linux, .netbsd, .solaris, .illumos => switch (reg_number) { | ||
| 148 | 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EAX]), | ||
| 149 | 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ECX]), | ||
| 150 | 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EDX]), | ||
| 151 | 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBX]), | ||
| 152 | 4...5 => if (reg_context) |r| bytes: { | ||
| 153 | if (reg_number == 4) { | ||
| 154 | break :bytes if (r.eh_frame and r.is_macho) | ||
| 155 | mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBP]) | ||
| 156 | else | ||
| 157 | mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESP]); | ||
| 158 | } else { | ||
| 159 | break :bytes if (r.eh_frame and r.is_macho) | ||
| 160 | mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESP]) | ||
| 161 | else | ||
| 162 | mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBP]); | ||
| 163 | } | ||
| 164 | } else error.RegisterContextRequired, | ||
| 165 | 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESI]), | ||
| 166 | 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EDI]), | ||
| 167 | 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EIP]), | ||
| 168 | 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EFL]), | ||
| 169 | 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.CS]), | ||
| 170 | 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.SS]), | ||
| 171 | 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.DS]), | ||
| 172 | 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ES]), | ||
| 173 | 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.FS]), | ||
| 174 | 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.GS]), | ||
| 175 | 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs | ||
| 176 | 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs | ||
| 177 | else => error.InvalidRegister, | ||
| 178 | }, | ||
| 179 | else => error.UnimplementedOs, | ||
| 180 | }, | ||
| 181 | .x86_64 => switch (builtin.os.tag) { | ||
| 182 | .linux, .solaris, .illumos => switch (reg_number) { | ||
| 183 | 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RAX]), | ||
| 184 | 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDX]), | ||
| 185 | 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RCX]), | ||
| 186 | 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RBX]), | ||
| 187 | 4 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RSI]), | ||
| 188 | 5 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDI]), | ||
| 189 | 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RBP]), | ||
| 190 | 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RSP]), | ||
| 191 | 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R8]), | ||
| 192 | 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R9]), | ||
| 193 | 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R10]), | ||
| 194 | 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R11]), | ||
| 195 | 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R12]), | ||
| 196 | 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R13]), | ||
| 197 | 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R14]), | ||
| 198 | 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R15]), | ||
| 199 | 16 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RIP]), | ||
| 200 | 17...32 => |i| if (builtin.os.tag.isSolarish()) | ||
| 201 | mem.asBytes(&ucontext_ptr.mcontext.fpregs.chip_state.xmm[i - 17]) | ||
| 202 | else | ||
| 203 | mem.asBytes(&ucontext_ptr.mcontext.fpregs.xmm[i - 17]), | ||
| 204 | else => error.InvalidRegister, | ||
| 205 | }, | ||
| 206 | .freebsd => switch (reg_number) { | ||
| 207 | 0 => mem.asBytes(&ucontext_ptr.mcontext.rax), | ||
| 208 | 1 => mem.asBytes(&ucontext_ptr.mcontext.rdx), | ||
| 209 | 2 => mem.asBytes(&ucontext_ptr.mcontext.rcx), | ||
| 210 | 3 => mem.asBytes(&ucontext_ptr.mcontext.rbx), | ||
| 211 | 4 => mem.asBytes(&ucontext_ptr.mcontext.rsi), | ||
| 212 | 5 => mem.asBytes(&ucontext_ptr.mcontext.rdi), | ||
| 213 | 6 => mem.asBytes(&ucontext_ptr.mcontext.rbp), | ||
| 214 | 7 => mem.asBytes(&ucontext_ptr.mcontext.rsp), | ||
| 215 | 8 => mem.asBytes(&ucontext_ptr.mcontext.r8), | ||
| 216 | 9 => mem.asBytes(&ucontext_ptr.mcontext.r9), | ||
| 217 | 10 => mem.asBytes(&ucontext_ptr.mcontext.r10), | ||
| 218 | 11 => mem.asBytes(&ucontext_ptr.mcontext.r11), | ||
| 219 | 12 => mem.asBytes(&ucontext_ptr.mcontext.r12), | ||
| 220 | 13 => mem.asBytes(&ucontext_ptr.mcontext.r13), | ||
| 221 | 14 => mem.asBytes(&ucontext_ptr.mcontext.r14), | ||
| 222 | 15 => mem.asBytes(&ucontext_ptr.mcontext.r15), | ||
| 223 | 16 => mem.asBytes(&ucontext_ptr.mcontext.rip), | ||
| 224 | // TODO: Extract xmm state from mcontext.fpstate? | ||
| 225 | else => error.InvalidRegister, | ||
| 226 | }, | ||
| 227 | .openbsd => switch (reg_number) { | ||
| 228 | 0 => mem.asBytes(&ucontext_ptr.sc_rax), | ||
| 229 | 1 => mem.asBytes(&ucontext_ptr.sc_rdx), | ||
| 230 | 2 => mem.asBytes(&ucontext_ptr.sc_rcx), | ||
| 231 | 3 => mem.asBytes(&ucontext_ptr.sc_rbx), | ||
| 232 | 4 => mem.asBytes(&ucontext_ptr.sc_rsi), | ||
| 233 | 5 => mem.asBytes(&ucontext_ptr.sc_rdi), | ||
| 234 | 6 => mem.asBytes(&ucontext_ptr.sc_rbp), | ||
| 235 | 7 => mem.asBytes(&ucontext_ptr.sc_rsp), | ||
| 236 | 8 => mem.asBytes(&ucontext_ptr.sc_r8), | ||
| 237 | 9 => mem.asBytes(&ucontext_ptr.sc_r9), | ||
| 238 | 10 => mem.asBytes(&ucontext_ptr.sc_r10), | ||
| 239 | 11 => mem.asBytes(&ucontext_ptr.sc_r11), | ||
| 240 | 12 => mem.asBytes(&ucontext_ptr.sc_r12), | ||
| 241 | 13 => mem.asBytes(&ucontext_ptr.sc_r13), | ||
| 242 | 14 => mem.asBytes(&ucontext_ptr.sc_r14), | ||
| 243 | 15 => mem.asBytes(&ucontext_ptr.sc_r15), | ||
| 244 | 16 => mem.asBytes(&ucontext_ptr.sc_rip), | ||
| 245 | // TODO: Extract xmm state from sc_fpstate? | ||
| 246 | else => error.InvalidRegister, | ||
| 247 | }, | ||
| 248 | .macos, .ios => switch (reg_number) { | ||
| 249 | 0 => mem.asBytes(&ucontext_ptr.mcontext.ss.rax), | ||
| 250 | 1 => mem.asBytes(&ucontext_ptr.mcontext.ss.rdx), | ||
| 251 | 2 => mem.asBytes(&ucontext_ptr.mcontext.ss.rcx), | ||
| 252 | 3 => mem.asBytes(&ucontext_ptr.mcontext.ss.rbx), | ||
| 253 | 4 => mem.asBytes(&ucontext_ptr.mcontext.ss.rsi), | ||
| 254 | 5 => mem.asBytes(&ucontext_ptr.mcontext.ss.rdi), | ||
| 255 | 6 => mem.asBytes(&ucontext_ptr.mcontext.ss.rbp), | ||
| 256 | 7 => mem.asBytes(&ucontext_ptr.mcontext.ss.rsp), | ||
| 257 | 8 => mem.asBytes(&ucontext_ptr.mcontext.ss.r8), | ||
| 258 | 9 => mem.asBytes(&ucontext_ptr.mcontext.ss.r9), | ||
| 259 | 10 => mem.asBytes(&ucontext_ptr.mcontext.ss.r10), | ||
| 260 | 11 => mem.asBytes(&ucontext_ptr.mcontext.ss.r11), | ||
| 261 | 12 => mem.asBytes(&ucontext_ptr.mcontext.ss.r12), | ||
| 262 | 13 => mem.asBytes(&ucontext_ptr.mcontext.ss.r13), | ||
| 263 | 14 => mem.asBytes(&ucontext_ptr.mcontext.ss.r14), | ||
| 264 | 15 => mem.asBytes(&ucontext_ptr.mcontext.ss.r15), | ||
| 265 | 16 => mem.asBytes(&ucontext_ptr.mcontext.ss.rip), | ||
| 266 | else => error.InvalidRegister, | ||
| 267 | }, | ||
| 268 | else => error.UnimplementedOs, | ||
| 269 | }, | ||
| 270 | .arm, .armeb, .thumb, .thumbeb => switch (builtin.os.tag) { | ||
| 271 | .linux => switch (reg_number) { | ||
| 272 | 0 => mem.asBytes(&ucontext_ptr.mcontext.arm_r0), | ||
| 273 | 1 => mem.asBytes(&ucontext_ptr.mcontext.arm_r1), | ||
| 274 | 2 => mem.asBytes(&ucontext_ptr.mcontext.arm_r2), | ||
| 275 | 3 => mem.asBytes(&ucontext_ptr.mcontext.arm_r3), | ||
| 276 | 4 => mem.asBytes(&ucontext_ptr.mcontext.arm_r4), | ||
| 277 | 5 => mem.asBytes(&ucontext_ptr.mcontext.arm_r5), | ||
| 278 | 6 => mem.asBytes(&ucontext_ptr.mcontext.arm_r6), | ||
| 279 | 7 => mem.asBytes(&ucontext_ptr.mcontext.arm_r7), | ||
| 280 | 8 => mem.asBytes(&ucontext_ptr.mcontext.arm_r8), | ||
| 281 | 9 => mem.asBytes(&ucontext_ptr.mcontext.arm_r9), | ||
| 282 | 10 => mem.asBytes(&ucontext_ptr.mcontext.arm_r10), | ||
| 283 | 11 => mem.asBytes(&ucontext_ptr.mcontext.arm_fp), | ||
| 284 | 12 => mem.asBytes(&ucontext_ptr.mcontext.arm_ip), | ||
| 285 | 13 => mem.asBytes(&ucontext_ptr.mcontext.arm_sp), | ||
| 286 | 14 => mem.asBytes(&ucontext_ptr.mcontext.arm_lr), | ||
| 287 | 15 => mem.asBytes(&ucontext_ptr.mcontext.arm_pc), | ||
| 288 | // CPSR is not allocated a register number (See: https://github.com/ARM-software/abi-aa/blob/main/aadwarf32/aadwarf32.rst, Section 4.1) | ||
| 289 | else => error.InvalidRegister, | ||
| 290 | }, | ||
| 291 | else => error.UnimplementedOs, | ||
| 292 | }, | ||
| 293 | .aarch64, .aarch64_be => switch (builtin.os.tag) { | ||
| 294 | .macos, .ios, .watchos => switch (reg_number) { | ||
| 295 | 0...28 => mem.asBytes(&ucontext_ptr.mcontext.ss.regs[reg_number]), | ||
| 296 | 29 => mem.asBytes(&ucontext_ptr.mcontext.ss.fp), | ||
| 297 | 30 => mem.asBytes(&ucontext_ptr.mcontext.ss.lr), | ||
| 298 | 31 => mem.asBytes(&ucontext_ptr.mcontext.ss.sp), | ||
| 299 | 32 => mem.asBytes(&ucontext_ptr.mcontext.ss.pc), | ||
| 300 | |||
| 301 | // TODO: Find storage for this state | ||
| 302 | //34 => mem.asBytes(&ucontext_ptr.ra_sign_state), | ||
| 303 | |||
| 304 | // V0-V31 | ||
| 305 | 64...95 => mem.asBytes(&ucontext_ptr.mcontext.ns.q[reg_number - 64]), | ||
| 306 | else => error.InvalidRegister, | ||
| 307 | }, | ||
| 308 | .netbsd => switch (reg_number) { | ||
| 309 | 0...34 => mem.asBytes(&ucontext_ptr.mcontext.gregs[reg_number]), | ||
| 310 | else => error.InvalidRegister, | ||
| 311 | }, | ||
| 312 | .freebsd => switch (reg_number) { | ||
| 313 | 0...29 => mem.asBytes(&ucontext_ptr.mcontext.gpregs.x[reg_number]), | ||
| 314 | 30 => mem.asBytes(&ucontext_ptr.mcontext.gpregs.lr), | ||
| 315 | 31 => mem.asBytes(&ucontext_ptr.mcontext.gpregs.sp), | ||
| 316 | |||
| 317 | // TODO: This seems wrong, but it was in the previous debug.zig code for mapping PC, check this | ||
| 318 | 32 => mem.asBytes(&ucontext_ptr.mcontext.gpregs.elr), | ||
| 319 | |||
| 320 | else => error.InvalidRegister, | ||
| 321 | }, | ||
| 322 | .openbsd => switch (reg_number) { | ||
| 323 | 0...30 => mem.asBytes(&ucontext_ptr.sc_x[reg_number]), | ||
| 324 | 31 => mem.asBytes(&ucontext_ptr.sc_sp), | ||
| 325 | 32 => mem.asBytes(&ucontext_ptr.sc_lr), | ||
| 326 | 33 => mem.asBytes(&ucontext_ptr.sc_elr), | ||
| 327 | 34 => mem.asBytes(&ucontext_ptr.sc_spsr), | ||
| 328 | else => error.InvalidRegister, | ||
| 329 | }, | ||
| 330 | else => switch (reg_number) { | ||
| 331 | 0...30 => mem.asBytes(&ucontext_ptr.mcontext.regs[reg_number]), | ||
| 332 | 31 => mem.asBytes(&ucontext_ptr.mcontext.sp), | ||
| 333 | 32 => mem.asBytes(&ucontext_ptr.mcontext.pc), | ||
| 334 | else => error.InvalidRegister, | ||
| 335 | }, | ||
| 336 | }, | ||
| 337 | else => error.UnimplementedArch, | ||
| 338 | }; | ||
| 339 | } | ||
| 340 | |||
| 341 | /// Returns a pointer to a register stored in a ThreadContext, preserving the | ||
| 342 | /// pointer attributes of the context. | ||
| 343 | pub fn regValueNative( | ||
| 344 | thread_context_ptr: *std.debug.ThreadContext, | ||
| 345 | reg_number: u8, | ||
| 346 | reg_context: ?RegisterContext, | ||
| 347 | ) !*align(1) usize { | ||
| 348 | const reg_bytes = try regBytes(thread_context_ptr, reg_number, reg_context); | ||
| 349 | if (@sizeOf(usize) != reg_bytes.len) return error.IncompatibleRegisterSize; | ||
| 350 | return @ptrCast(reg_bytes); | ||
| 351 | } | ||
lib/std/debug/Dwarf/expression.zig+63-97| ... | @@ -5,12 +5,17 @@ const native_endian = native_arch.endian(); | ... | @@ -5,12 +5,17 @@ const native_endian = native_arch.endian(); |
| 5 | const std = @import("std"); | 5 | const std = @import("std"); |
| 6 | const leb = std.leb; | 6 | const leb = std.leb; |
| 7 | const OP = std.dwarf.OP; | 7 | const OP = std.dwarf.OP; |
| 8 | const abi = std.debug.Dwarf.abi; | ||
| 9 | const mem = std.mem; | 8 | const mem = std.mem; |
| 10 | const assert = std.debug.assert; | 9 | const assert = std.debug.assert; |
| 11 | const testing = std.testing; | 10 | const testing = std.testing; |
| 12 | const Writer = std.Io.Writer; | 11 | const Writer = std.Io.Writer; |
| 13 | 12 | ||
| 13 | const regNative = std.debug.SelfInfo.DwarfUnwindContext.regNative; | ||
| 14 | |||
| 15 | const ip_reg_num = std.debug.Dwarf.ipRegNum(native_arch).?; | ||
| 16 | const fp_reg_num = std.debug.Dwarf.fpRegNum(native_arch); | ||
| 17 | const sp_reg_num = std.debug.Dwarf.spRegNum(native_arch); | ||
| 18 | |||
| 14 | /// Expressions can be evaluated in different contexts, each requiring its own set of inputs. | 19 | /// Expressions can be evaluated in different contexts, each requiring its own set of inputs. |
| 15 | /// Callers should specify all the fields relevant to their context. If a field is required | 20 | /// Callers should specify all the fields relevant to their context. If a field is required |
| 16 | /// by the expression and it isn't in the context, error.IncompleteExpressionContext is returned. | 21 | /// by the expression and it isn't in the context, error.IncompleteExpressionContext is returned. |
| ... | @@ -23,9 +28,7 @@ pub const Context = struct { | ... | @@ -23,9 +28,7 @@ pub const Context = struct { |
| 23 | object_address: ?*const anyopaque = null, | 28 | object_address: ?*const anyopaque = null, |
| 24 | /// .debug_addr section | 29 | /// .debug_addr section |
| 25 | debug_addr: ?[]const u8 = null, | 30 | debug_addr: ?[]const u8 = null, |
| 26 | /// Thread context | 31 | cpu_context: ?*std.debug.cpu_context.Native = null, |
| 27 | thread_context: ?*std.debug.ThreadContext = null, | ||
| 28 | reg_context: ?abi.RegisterContext = null, | ||
| 29 | /// Call frame address, if in a CFI context | 32 | /// Call frame address, if in a CFI context |
| 30 | cfa: ?usize = null, | 33 | cfa: ?usize = null, |
| 31 | /// This expression is a sub-expression from an OP.entry_value instruction | 34 | /// This expression is a sub-expression from an OP.entry_value instruction |
| ... | @@ -62,7 +65,9 @@ pub const Error = error{ | ... | @@ -62,7 +65,9 @@ pub const Error = error{ |
| 62 | InvalidTypeLength, | 65 | InvalidTypeLength, |
| 63 | 66 | ||
| 64 | TruncatedIntegralType, | 67 | TruncatedIntegralType, |
| 65 | } || abi.RegBytesError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero, ReadFailed }; | 68 | |
| 69 | IncompatibleRegisterSize, | ||
| 70 | } || std.debug.cpu_context.DwarfRegisterError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero, ReadFailed }; | ||
| 66 | 71 | ||
| 67 | /// A stack machine that can decode and run DWARF expressions. | 72 | /// A stack machine that can decode and run DWARF expressions. |
| 68 | /// Expressions can be decoded for non-native address size and endianness, | 73 | /// Expressions can be decoded for non-native address size and endianness, |
| ... | @@ -369,29 +374,20 @@ pub fn StackMachine(comptime options: Options) type { | ... | @@ -369,29 +374,20 @@ pub fn StackMachine(comptime options: Options) type { |
| 369 | OP.breg0...OP.breg31, | 374 | OP.breg0...OP.breg31, |
| 370 | OP.bregx, | 375 | OP.bregx, |
| 371 | => { | 376 | => { |
| 372 | if (context.thread_context == null) return error.IncompleteExpressionContext; | 377 | const cpu_context = context.cpu_context orelse return error.IncompleteExpressionContext; |
| 373 | 378 | ||
| 374 | const base_register = operand.?.base_register; | 379 | const br = operand.?.base_register; |
| 375 | var value: i64 = @intCast(mem.readInt(usize, (try abi.regBytes( | 380 | const value: i64 = @intCast((try regNative(cpu_context, br.base_register)).*); |
| 376 | context.thread_context.?, | 381 | try self.stack.append(allocator, .{ .generic = @intCast(value + br.offset) }); |
| 377 | base_register.base_register, | ||
| 378 | context.reg_context, | ||
| 379 | ))[0..@sizeOf(usize)], native_endian)); | ||
| 380 | value += base_register.offset; | ||
| 381 | try self.stack.append(allocator, .{ .generic = @intCast(value) }); | ||
| 382 | }, | 382 | }, |
| 383 | OP.regval_type => { | 383 | OP.regval_type => { |
| 384 | const register_type = operand.?.register_type; | 384 | const cpu_context = context.cpu_context orelse return error.IncompleteExpressionContext; |
| 385 | const value = mem.readInt(usize, (try abi.regBytes( | 385 | const rt = operand.?.register_type; |
| 386 | context.thread_context.?, | ||
| 387 | register_type.register, | ||
| 388 | context.reg_context, | ||
| 389 | ))[0..@sizeOf(usize)], native_endian); | ||
| 390 | try self.stack.append(allocator, .{ | 386 | try self.stack.append(allocator, .{ |
| 391 | .regval_type = .{ | 387 | .regval_type = .{ |
| 392 | .type_offset = register_type.type_offset, | 388 | .type_offset = rt.type_offset, |
| 393 | .type_size = @sizeOf(addr_type), | 389 | .type_size = @sizeOf(addr_type), |
| 394 | .value = value, | 390 | .value = (try regNative(cpu_context, rt.register)).*, |
| 395 | }, | 391 | }, |
| 396 | }); | 392 | }); |
| 397 | }, | 393 | }, |
| ... | @@ -734,14 +730,14 @@ pub fn StackMachine(comptime options: Options) type { | ... | @@ -734,14 +730,14 @@ pub fn StackMachine(comptime options: Options) type { |
| 734 | 730 | ||
| 735 | // TODO: The spec states that this sub-expression needs to observe the state (ie. registers) | 731 | // TODO: The spec states that this sub-expression needs to observe the state (ie. registers) |
| 736 | // as it was upon entering the current subprogram. If this isn't being called at the | 732 | // as it was upon entering the current subprogram. If this isn't being called at the |
| 737 | // end of a frame unwind operation, an additional ThreadContext with this state will be needed. | 733 | // end of a frame unwind operation, an additional cpu_context.Native with this state will be needed. |
| 738 | 734 | ||
| 739 | if (isOpcodeRegisterLocation(block[0])) { | 735 | if (isOpcodeRegisterLocation(block[0])) { |
| 740 | if (context.thread_context == null) return error.IncompleteExpressionContext; | 736 | const cpu_context = context.cpu_context orelse return error.IncompleteExpressionContext; |
| 741 | 737 | ||
| 742 | var block_stream: std.Io.Reader = .fixed(block); | 738 | var block_stream: std.Io.Reader = .fixed(block); |
| 743 | const register = (try readOperand(&block_stream, block[0], context)).?.register; | 739 | const register = (try readOperand(&block_stream, block[0], context)).?.register; |
| 744 | const value = mem.readInt(usize, (try abi.regBytes(context.thread_context.?, register, context.reg_context))[0..@sizeOf(usize)], native_endian); | 740 | const value = (try regNative(cpu_context, register)).*; |
| 745 | try self.stack.append(allocator, .{ .generic = value }); | 741 | try self.stack.append(allocator, .{ .generic = value }); |
| 746 | } else { | 742 | } else { |
| 747 | var stack_machine: Self = .{}; | 743 | var stack_machine: Self = .{}; |
| ... | @@ -1149,55 +1145,39 @@ test "basics" { | ... | @@ -1149,55 +1145,39 @@ test "basics" { |
| 1149 | } | 1145 | } |
| 1150 | 1146 | ||
| 1151 | // Register values | 1147 | // Register values |
| 1152 | if (@sizeOf(std.debug.ThreadContext) != 0) { | 1148 | if (std.debug.cpu_context.Native != noreturn) { |
| 1153 | stack_machine.reset(); | 1149 | stack_machine.reset(); |
| 1154 | program.clearRetainingCapacity(); | 1150 | program.clearRetainingCapacity(); |
| 1155 | 1151 | ||
| 1156 | const reg_context = abi.RegisterContext{ | 1152 | var cpu_context: std.debug.cpu_context.Native = undefined; |
| 1157 | .eh_frame = true, | ||
| 1158 | .is_macho = builtin.os.tag == .macos, | ||
| 1159 | }; | ||
| 1160 | var thread_context: std.debug.ThreadContext = undefined; | ||
| 1161 | std.debug.relocateContext(&thread_context); | ||
| 1162 | const context = Context{ | 1153 | const context = Context{ |
| 1163 | .thread_context = &thread_context, | 1154 | .cpu_context = &cpu_context, |
| 1164 | .reg_context = reg_context, | ||
| 1165 | }; | 1155 | }; |
| 1166 | 1156 | ||
| 1167 | // Only test register operations on arch / os that have them implemented | 1157 | const reg_bytes = try cpu_context.dwarfRegisterBytes(0); |
| 1168 | if (abi.regBytes(&thread_context, 0, reg_context)) |reg_bytes| { | 1158 | |
| 1169 | 1159 | // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it | |
| 1170 | // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it | 1160 | |
| 1171 | 1161 | mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian); | |
| 1172 | mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian); | 1162 | (try regNative(&cpu_context, fp_reg_num)).* = 1; |
| 1173 | (try abi.regValueNative(&thread_context, abi.fpRegNum(native_arch, reg_context), reg_context)).* = 1; | 1163 | (try regNative(&cpu_context, sp_reg_num)).* = 2; |
| 1174 | (try abi.regValueNative(&thread_context, abi.spRegNum(native_arch, reg_context), reg_context)).* = 2; | 1164 | (try regNative(&cpu_context, ip_reg_num)).* = 3; |
| 1175 | (try abi.regValueNative(&thread_context, abi.ipRegNum(native_arch).?, reg_context)).* = 3; | 1165 | |
| 1176 | 1166 | try b.writeBreg(writer, fp_reg_num, @as(usize, 100)); | |
| 1177 | try b.writeBreg(writer, abi.fpRegNum(native_arch, reg_context), @as(usize, 100)); | 1167 | try b.writeBreg(writer, sp_reg_num, @as(usize, 200)); |
| 1178 | try b.writeBreg(writer, abi.spRegNum(native_arch, reg_context), @as(usize, 200)); | 1168 | try b.writeBregx(writer, ip_reg_num, @as(usize, 300)); |
| 1179 | try b.writeBregx(writer, abi.ipRegNum(native_arch).?, @as(usize, 300)); | 1169 | try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400)); |
| 1180 | try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400)); | 1170 | |
| 1181 | 1171 | _ = try stack_machine.run(program.written(), allocator, context, 0); | |
| 1182 | _ = try stack_machine.run(program.written(), allocator, context, 0); | 1172 | |
| 1183 | 1173 | const regval_type = stack_machine.stack.pop().?.regval_type; | |
| 1184 | const regval_type = stack_machine.stack.pop().?.regval_type; | 1174 | try testing.expectEqual(@as(usize, 400), regval_type.type_offset); |
| 1185 | try testing.expectEqual(@as(usize, 400), regval_type.type_offset); | 1175 | try testing.expectEqual(@as(u8, @sizeOf(usize)), regval_type.type_size); |
| 1186 | try testing.expectEqual(@as(u8, @sizeOf(usize)), regval_type.type_size); | 1176 | try testing.expectEqual(@as(usize, 0xee), regval_type.value); |
| 1187 | try testing.expectEqual(@as(usize, 0xee), regval_type.value); | 1177 | |
| 1188 | 1178 | try testing.expectEqual(@as(usize, 303), stack_machine.stack.pop().?.generic); | |
| 1189 | try testing.expectEqual(@as(usize, 303), stack_machine.stack.pop().?.generic); | 1179 | try testing.expectEqual(@as(usize, 202), stack_machine.stack.pop().?.generic); |
| 1190 | try testing.expectEqual(@as(usize, 202), stack_machine.stack.pop().?.generic); | 1180 | try testing.expectEqual(@as(usize, 101), stack_machine.stack.pop().?.generic); |
| 1191 | try testing.expectEqual(@as(usize, 101), stack_machine.stack.pop().?.generic); | ||
| 1192 | } else |err| { | ||
| 1193 | switch (err) { | ||
| 1194 | error.UnimplementedArch, | ||
| 1195 | error.UnimplementedOs, | ||
| 1196 | error.ThreadContextNotSupported, | ||
| 1197 | => {}, | ||
| 1198 | else => return err, | ||
| 1199 | } | ||
| 1200 | } | ||
| 1201 | } | 1181 | } |
| 1202 | 1182 | ||
| 1203 | // Stack operations | 1183 | // Stack operations |
| ... | @@ -1585,38 +1565,24 @@ test "basics" { | ... | @@ -1585,38 +1565,24 @@ test "basics" { |
| 1585 | } | 1565 | } |
| 1586 | 1566 | ||
| 1587 | // Register location description | 1567 | // Register location description |
| 1588 | const reg_context = abi.RegisterContext{ | 1568 | var cpu_context: std.debug.cpu_context.Native = undefined; |
| 1589 | .eh_frame = true, | 1569 | std.debug.relocateContext(&cpu_context); |
| 1590 | .is_macho = builtin.os.tag == .macos, | ||
| 1591 | }; | ||
| 1592 | var thread_context: std.debug.ThreadContext = undefined; | ||
| 1593 | std.debug.relocateContext(&thread_context); | ||
| 1594 | context = Context{ | 1570 | context = Context{ |
| 1595 | .thread_context = &thread_context, | 1571 | .cpu_context = &cpu_context, |
| 1596 | .reg_context = reg_context, | ||
| 1597 | }; | 1572 | }; |
| 1598 | 1573 | ||
| 1599 | if (abi.regBytes(&thread_context, 0, reg_context)) |reg_bytes| { | 1574 | const reg_bytes = try cpu_context.dwarfRegisterBytes(0); |
| 1600 | mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian); | 1575 | mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian); |
| 1601 | 1576 | ||
| 1602 | var sub_program: std.Io.Writer.Allocating = .init(allocator); | 1577 | var sub_program: std.Io.Writer.Allocating = .init(allocator); |
| 1603 | defer sub_program.deinit(); | 1578 | defer sub_program.deinit(); |
| 1604 | const sub_writer = &sub_program.writer; | 1579 | const sub_writer = &sub_program.writer; |
| 1605 | try b.writeReg(sub_writer, 0); | 1580 | try b.writeReg(sub_writer, 0); |
| 1606 | 1581 | ||
| 1607 | stack_machine.reset(); | 1582 | stack_machine.reset(); |
| 1608 | program.clearRetainingCapacity(); | 1583 | program.clearRetainingCapacity(); |
| 1609 | try b.writeEntryValue(writer, sub_program.written()); | 1584 | try b.writeEntryValue(writer, sub_program.written()); |
| 1610 | _ = try stack_machine.run(program.written(), allocator, context, null); | 1585 | _ = try stack_machine.run(program.written(), allocator, context, null); |
| 1611 | try testing.expectEqual(@as(usize, 0xee), stack_machine.stack.pop().?.generic); | 1586 | try testing.expectEqual(@as(usize, 0xee), stack_machine.stack.pop().?.generic); |
| 1612 | } else |err| { | ||
| 1613 | switch (err) { | ||
| 1614 | error.UnimplementedArch, | ||
| 1615 | error.UnimplementedOs, | ||
| 1616 | error.ThreadContextNotSupported, | ||
| 1617 | => {}, | ||
| 1618 | else => return err, | ||
| 1619 | } | ||
| 1620 | } | ||
| 1621 | } | 1587 | } |
| 1622 | } | 1588 | } |
lib/std/debug/SelfInfo.zig+121-108| ... | @@ -11,8 +11,7 @@ const mem = std.mem; | ... | @@ -11,8 +11,7 @@ const mem = std.mem; |
| 11 | const Allocator = std.mem.Allocator; | 11 | const Allocator = std.mem.Allocator; |
| 12 | const assert = std.debug.assert; | 12 | const assert = std.debug.assert; |
| 13 | const Dwarf = std.debug.Dwarf; | 13 | const Dwarf = std.debug.Dwarf; |
| 14 | const regBytes = Dwarf.abi.regBytes; | 14 | const CpuContext = std.debug.cpu_context.Native; |
| 15 | const regValueNative = Dwarf.abi.regValueNative; | ||
| 16 | 15 | ||
| 17 | const root = @import("root"); | 16 | const root = @import("root"); |
| 18 | 17 | ||
| ... | @@ -38,8 +37,6 @@ pub const Error = error{ | ... | @@ -38,8 +37,6 @@ pub const Error = error{ |
| 38 | pub const target_supported: bool = Module != void; | 37 | pub const target_supported: bool = Module != void; |
| 39 | 38 | ||
| 40 | /// Indicates whether the `SelfInfo` implementation has support for unwinding on this target. | 39 | /// Indicates whether the `SelfInfo` implementation has support for unwinding on this target. |
| 41 | /// | ||
| 42 | /// For whether DWARF unwinding is *theoretically* possible, see `Dwarf.abi.supportsUnwinding`. | ||
| 43 | pub const supports_unwinding: bool = target_supported and Module.supports_unwinding; | 40 | pub const supports_unwinding: bool = target_supported and Module.supports_unwinding; |
| 44 | 41 | ||
| 45 | pub const UnwindContext = if (supports_unwinding) Module.UnwindContext; | 42 | pub const UnwindContext = if (supports_unwinding) Module.UnwindContext; |
| ... | @@ -120,7 +117,7 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) | ... | @@ -120,7 +117,7 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) |
| 120 | /// pub const UnwindContext = struct { | 117 | /// pub const UnwindContext = struct { |
| 121 | /// /// A PC value inside the function of the last unwound frame. | 118 | /// /// A PC value inside the function of the last unwound frame. |
| 122 | /// pc: usize, | 119 | /// pc: usize, |
| 123 | /// pub fn init(tc: *std.debug.ThreadContext, gpa: Allocator) Allocator.Error!UnwindContext; | 120 | /// pub fn init(ctx: *std.debug.cpu_context.Native, gpa: Allocator) Allocator.Error!UnwindContext; |
| 124 | /// pub fn deinit(uc: *UnwindContext, gpa: Allocator) void; | 121 | /// pub fn deinit(uc: *UnwindContext, gpa: Allocator) void; |
| 125 | /// /// Returns the frame pointer associated with the last unwound stack frame. If the frame | 122 | /// /// Returns the frame pointer associated with the last unwound stack frame. If the frame |
| 126 | /// /// pointer is unknown, 0 may be returned instead. | 123 | /// /// pointer is unknown, 0 may be returned instead. |
| ... | @@ -141,9 +138,26 @@ const Module: type = Module: { | ... | @@ -141,9 +138,26 @@ const Module: type = Module: { |
| 141 | break :Module root.debug.Module; | 138 | break :Module root.debug.Module; |
| 142 | } | 139 | } |
| 143 | break :Module switch (native_os) { | 140 | break :Module switch (native_os) { |
| 144 | .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris, .illumos => @import("SelfInfo/ElfModule.zig"), | 141 | .linux, |
| 145 | .macos, .ios, .watchos, .tvos, .visionos => @import("SelfInfo/DarwinModule.zig"), | 142 | .netbsd, |
| 146 | .uefi, .windows => @import("SelfInfo/WindowsModule.zig"), | 143 | .freebsd, |
| 144 | .dragonfly, | ||
| 145 | .openbsd, | ||
| 146 | .solaris, | ||
| 147 | .illumos, | ||
| 148 | => @import("SelfInfo/ElfModule.zig"), | ||
| 149 | |||
| 150 | .macos, | ||
| 151 | .ios, | ||
| 152 | .watchos, | ||
| 153 | .tvos, | ||
| 154 | .visionos, | ||
| 155 | => @import("SelfInfo/DarwinModule.zig"), | ||
| 156 | |||
| 157 | .uefi, | ||
| 158 | .windows, | ||
| 159 | => @import("SelfInfo/WindowsModule.zig"), | ||
| 160 | |||
| 147 | else => void, | 161 | else => void, |
| 148 | }; | 162 | }; |
| 149 | }; | 163 | }; |
| ... | @@ -153,26 +167,25 @@ const Module: type = Module: { | ... | @@ -153,26 +167,25 @@ const Module: type = Module: { |
| 153 | pub const DwarfUnwindContext = struct { | 167 | pub const DwarfUnwindContext = struct { |
| 154 | cfa: ?usize, | 168 | cfa: ?usize, |
| 155 | pc: usize, | 169 | pc: usize, |
| 156 | thread_context: *std.debug.ThreadContext, | 170 | cpu_context: CpuContext, |
| 157 | reg_context: Dwarf.abi.RegisterContext, | ||
| 158 | vm: Dwarf.Unwind.VirtualMachine, | 171 | vm: Dwarf.Unwind.VirtualMachine, |
| 159 | stack_machine: Dwarf.expression.StackMachine(.{ .call_frame_context = true }), | 172 | stack_machine: Dwarf.expression.StackMachine(.{ .call_frame_context = true }), |
| 160 | 173 | ||
| 161 | pub fn init(thread_context: *std.debug.ThreadContext, gpa: Allocator) error{}!DwarfUnwindContext { | 174 | pub fn init(cpu_context: *const CpuContext) DwarfUnwindContext { |
| 162 | comptime assert(supports_unwinding); | 175 | comptime assert(supports_unwinding); |
| 163 | _ = gpa; | ||
| 164 | 176 | ||
| 165 | const ip_reg_num = Dwarf.abi.ipRegNum(native_arch).?; | 177 | // `@constCast` is safe because we aren't going to store to the resulting pointer. |
| 166 | const raw_pc_ptr = regValueNative(thread_context, ip_reg_num, null) catch { | 178 | const raw_pc_ptr = regNative(@constCast(cpu_context), ip_reg_num) catch |err| switch (err) { |
| 167 | unreachable; // error means unsupported, in which case `supports_unwinding` should have been `false` | 179 | error.InvalidRegister => unreachable, // `ip_reg_num` is definitely valid |
| 180 | error.UnsupportedRegister => unreachable, // the implementation needs to support ip | ||
| 181 | error.IncompatibleRegisterSize => unreachable, // ip is definitely `usize`-sized | ||
| 168 | }; | 182 | }; |
| 169 | const pc = stripInstructionPtrAuthCode(raw_pc_ptr.*); | 183 | const pc = stripInstructionPtrAuthCode(raw_pc_ptr.*); |
| 170 | 184 | ||
| 171 | return .{ | 185 | return .{ |
| 172 | .cfa = null, | 186 | .cfa = null, |
| 173 | .pc = pc, | 187 | .pc = pc, |
| 174 | .thread_context = thread_context, | 188 | .cpu_context = cpu_context.*, |
| 175 | .reg_context = undefined, | ||
| 176 | .vm = .{}, | 189 | .vm = .{}, |
| 177 | .stack_machine = .{}, | 190 | .stack_machine = .{}, |
| 178 | }; | 191 | }; |
| ... | @@ -185,17 +198,25 @@ pub const DwarfUnwindContext = struct { | ... | @@ -185,17 +198,25 @@ pub const DwarfUnwindContext = struct { |
| 185 | } | 198 | } |
| 186 | 199 | ||
| 187 | pub fn getFp(self: *const DwarfUnwindContext) usize { | 200 | pub fn getFp(self: *const DwarfUnwindContext) usize { |
| 188 | return (regValueNative(self.thread_context, Dwarf.abi.fpRegNum(native_arch, self.reg_context), self.reg_context) catch return 0).*; | 201 | // `@constCast` is safe because we aren't going to store to the resulting pointer. |
| 202 | const ptr = regNative(@constCast(&self.cpu_context), fp_reg_num) catch |err| switch (err) { | ||
| 203 | error.InvalidRegister => unreachable, // `fp_reg_num` is definitely valid | ||
| 204 | error.UnsupportedRegister => unreachable, // the implementation needs to support fp | ||
| 205 | error.IncompatibleRegisterSize => unreachable, // fp is a pointer so is `usize`-sized | ||
| 206 | }; | ||
| 207 | return ptr.*; | ||
| 189 | } | 208 | } |
| 190 | 209 | ||
| 191 | /// Resolves the register rule and places the result into `out` (see regBytes) | 210 | /// Resolves the register rule and places the result into `out` (see regBytes). Returns `true` |
| 211 | /// iff the rule was undefined. This is *not* the same as `col.rule == .undefined`, because the | ||
| 212 | /// default rule may be undefined. | ||
| 192 | pub fn resolveRegisterRule( | 213 | pub fn resolveRegisterRule( |
| 193 | context: *DwarfUnwindContext, | 214 | context: *DwarfUnwindContext, |
| 194 | gpa: Allocator, | 215 | gpa: Allocator, |
| 195 | col: Dwarf.Unwind.VirtualMachine.Column, | 216 | col: Dwarf.Unwind.VirtualMachine.Column, |
| 196 | expression_context: std.debug.Dwarf.expression.Context, | 217 | expression_context: std.debug.Dwarf.expression.Context, |
| 197 | out: []u8, | 218 | out: []u8, |
| 198 | ) !void { | 219 | ) !bool { |
| 199 | switch (col.rule) { | 220 | switch (col.rule) { |
| 200 | .default => { | 221 | .default => { |
| 201 | const register = col.register orelse return error.InvalidRegister; | 222 | const register = col.register orelse return error.InvalidRegister; |
| ... | @@ -203,58 +224,74 @@ pub const DwarfUnwindContext = struct { | ... | @@ -203,58 +224,74 @@ pub const DwarfUnwindContext = struct { |
| 203 | // See the doc comment on `Dwarf.Unwind.VirtualMachine.RegisterRule.default`. | 224 | // See the doc comment on `Dwarf.Unwind.VirtualMachine.RegisterRule.default`. |
| 204 | if (builtin.cpu.arch.isAARCH64() and register >= 19 and register <= 18) { | 225 | if (builtin.cpu.arch.isAARCH64() and register >= 19 and register <= 18) { |
| 205 | // Callee-saved registers are initialized as if they had the .same_value rule | 226 | // Callee-saved registers are initialized as if they had the .same_value rule |
| 206 | const src = try regBytes(context.thread_context, register, context.reg_context); | 227 | const src = try context.cpu_context.dwarfRegisterBytes(register); |
| 207 | if (src.len != out.len) return error.RegisterSizeMismatch; | 228 | if (src.len != out.len) return error.RegisterSizeMismatch; |
| 208 | @memcpy(out, src); | 229 | @memcpy(out, src); |
| 209 | return; | 230 | return false; |
| 210 | } | 231 | } |
| 211 | @memset(out, undefined); | 232 | @memset(out, undefined); |
| 233 | return true; | ||
| 212 | }, | 234 | }, |
| 213 | .undefined => { | 235 | .undefined => { |
| 214 | @memset(out, undefined); | 236 | @memset(out, undefined); |
| 237 | return true; | ||
| 215 | }, | 238 | }, |
| 216 | .same_value => { | 239 | .same_value => { |
| 217 | // TODO: This copy could be eliminated if callers always copy the state then call this function to update it | 240 | // TODO: This copy could be eliminated if callers always copy the state then call this function to update it |
| 218 | const register = col.register orelse return error.InvalidRegister; | 241 | const register = col.register orelse return error.InvalidRegister; |
| 219 | const src = try regBytes(context.thread_context, register, context.reg_context); | 242 | const src = try context.cpu_context.dwarfRegisterBytes(register); |
| 220 | if (src.len != out.len) return error.RegisterSizeMismatch; | 243 | if (src.len != out.len) return error.RegisterSizeMismatch; |
| 221 | @memcpy(out, src); | 244 | @memcpy(out, src); |
| 245 | return false; | ||
| 222 | }, | 246 | }, |
| 223 | .offset => |offset| { | 247 | .offset => |offset| { |
| 224 | if (context.cfa) |cfa| { | 248 | const cfa = context.cfa orelse return error.InvalidCFA; |
| 225 | const addr = try applyOffset(cfa, offset); | 249 | const addr = try applyOffset(cfa, offset); |
| 226 | const ptr: *const usize = @ptrFromInt(addr); | 250 | const ptr: *const usize = @ptrFromInt(addr); |
| 227 | mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian); | 251 | mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian); |
| 228 | } else return error.InvalidCFA; | 252 | return false; |
| 229 | }, | 253 | }, |
| 230 | .val_offset => |offset| { | 254 | .val_offset => |offset| { |
| 231 | if (context.cfa) |cfa| { | 255 | const cfa = context.cfa orelse return error.InvalidCFA; |
| 232 | mem.writeInt(usize, out[0..@sizeOf(usize)], try applyOffset(cfa, offset), native_endian); | 256 | mem.writeInt(usize, out[0..@sizeOf(usize)], try applyOffset(cfa, offset), native_endian); |
| 233 | } else return error.InvalidCFA; | 257 | return false; |
| 234 | }, | 258 | }, |
| 235 | .register => |register| { | 259 | .register => |register| { |
| 236 | const src = try regBytes(context.thread_context, register, context.reg_context); | 260 | const src = try context.cpu_context.dwarfRegisterBytes(register); |
| 237 | if (src.len != out.len) return error.RegisterSizeMismatch; | 261 | if (src.len != out.len) return error.RegisterSizeMismatch; |
| 238 | @memcpy(out, src); | 262 | @memcpy(out, src); |
| 263 | return false; | ||
| 239 | }, | 264 | }, |
| 240 | .expression => |expression| { | 265 | .expression => |expression| { |
| 241 | context.stack_machine.reset(); | 266 | context.stack_machine.reset(); |
| 242 | const value = try context.stack_machine.run(expression, gpa, expression_context, context.cfa.?); | 267 | const value = try context.stack_machine.run( |
| 243 | const addr = if (value) |v| blk: { | 268 | expression, |
| 244 | if (v != .generic) return error.InvalidExpressionValue; | 269 | gpa, |
| 245 | break :blk v.generic; | 270 | expression_context, |
| 246 | } else return error.NoExpressionValue; | 271 | context.cfa.?, |
| 247 | 272 | ) orelse return error.NoExpressionValue; | |
| 273 | const addr = switch (value) { | ||
| 274 | .generic => |addr| addr, | ||
| 275 | else => return error.InvalidExpressionValue, | ||
| 276 | }; | ||
| 248 | const ptr: *usize = @ptrFromInt(addr); | 277 | const ptr: *usize = @ptrFromInt(addr); |
| 249 | mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian); | 278 | mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian); |
| 279 | return false; | ||
| 250 | }, | 280 | }, |
| 251 | .val_expression => |expression| { | 281 | .val_expression => |expression| { |
| 252 | context.stack_machine.reset(); | 282 | context.stack_machine.reset(); |
| 253 | const value = try context.stack_machine.run(expression, gpa, expression_context, context.cfa.?); | 283 | const value = try context.stack_machine.run( |
| 254 | if (value) |v| { | 284 | expression, |
| 255 | if (v != .generic) return error.InvalidExpressionValue; | 285 | gpa, |
| 256 | mem.writeInt(usize, out[0..@sizeOf(usize)], v.generic, native_endian); | 286 | expression_context, |
| 257 | } else return error.NoExpressionValue; | 287 | context.cfa.?, |
| 288 | ) orelse return error.NoExpressionValue; | ||
| 289 | const val_raw = switch (value) { | ||
| 290 | .generic => |raw| raw, | ||
| 291 | else => return error.InvalidExpressionValue, | ||
| 292 | }; | ||
| 293 | mem.writeInt(usize, out[0..@sizeOf(usize)], val_raw, native_endian); | ||
| 294 | return false; | ||
| 258 | }, | 295 | }, |
| 259 | .architectural => return error.UnimplementedRegisterRule, | 296 | .architectural => return error.UnimplementedRegisterRule, |
| 260 | } | 297 | } |
| ... | @@ -277,9 +314,6 @@ pub const DwarfUnwindContext = struct { | ... | @@ -277,9 +314,6 @@ pub const DwarfUnwindContext = struct { |
| 277 | return unwindFrameInner(context, gpa, unwind, load_offset, explicit_fde_offset) catch |err| switch (err) { | 314 | return unwindFrameInner(context, gpa, unwind, load_offset, explicit_fde_offset) catch |err| switch (err) { |
| 278 | error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e, | 315 | error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e, |
| 279 | 316 | ||
| 280 | error.UnimplementedArch, | ||
| 281 | error.UnimplementedOs, | ||
| 282 | error.ThreadContextNotSupported, | ||
| 283 | error.UnimplementedRegisterRule, | 317 | error.UnimplementedRegisterRule, |
| 284 | error.UnsupportedAddrSize, | 318 | error.UnsupportedAddrSize, |
| 285 | error.UnsupportedDwarfVersion, | 319 | error.UnsupportedDwarfVersion, |
| ... | @@ -289,10 +323,10 @@ pub const DwarfUnwindContext = struct { | ... | @@ -289,10 +323,10 @@ pub const DwarfUnwindContext = struct { |
| 289 | error.UnimplementedTypedComparison, | 323 | error.UnimplementedTypedComparison, |
| 290 | error.UnimplementedTypeConversion, | 324 | error.UnimplementedTypeConversion, |
| 291 | error.UnknownExpressionOpcode, | 325 | error.UnknownExpressionOpcode, |
| 326 | error.UnsupportedRegister, | ||
| 292 | => return error.UnsupportedDebugInfo, | 327 | => return error.UnsupportedDebugInfo, |
| 293 | 328 | ||
| 294 | error.InvalidRegister, | 329 | error.InvalidRegister, |
| 295 | error.RegisterContextRequired, | ||
| 296 | error.ReadFailed, | 330 | error.ReadFailed, |
| 297 | error.EndOfStream, | 331 | error.EndOfStream, |
| 298 | error.IncompatibleRegisterSize, | 332 | error.IncompatibleRegisterSize, |
| ... | @@ -346,20 +380,17 @@ pub const DwarfUnwindContext = struct { | ... | @@ -346,20 +380,17 @@ pub const DwarfUnwindContext = struct { |
| 346 | // may not reference other debug sections anyway. | 380 | // may not reference other debug sections anyway. |
| 347 | var expression_context: Dwarf.expression.Context = .{ | 381 | var expression_context: Dwarf.expression.Context = .{ |
| 348 | .format = format, | 382 | .format = format, |
| 349 | .thread_context = context.thread_context, | 383 | .cpu_context = &context.cpu_context, |
| 350 | .reg_context = context.reg_context, | ||
| 351 | .cfa = context.cfa, | 384 | .cfa = context.cfa, |
| 352 | }; | 385 | }; |
| 353 | 386 | ||
| 354 | context.vm.reset(); | 387 | context.vm.reset(); |
| 355 | context.reg_context.eh_frame = cie.version != 4; | ||
| 356 | context.reg_context.is_macho = native_os.isDarwin(); | ||
| 357 | 388 | ||
| 358 | const row = try context.vm.runTo(gpa, pc_vaddr, cie, fde, @sizeOf(usize), native_endian); | 389 | const row = try context.vm.runTo(gpa, pc_vaddr, cie, fde, @sizeOf(usize), native_endian); |
| 359 | context.cfa = switch (row.cfa.rule) { | 390 | context.cfa = switch (row.cfa.rule) { |
| 360 | .val_offset => |offset| blk: { | 391 | .val_offset => |offset| blk: { |
| 361 | const register = row.cfa.register orelse return error.InvalidCFARule; | 392 | const register = row.cfa.register orelse return error.InvalidCFARule; |
| 362 | const value = (try regValueNative(context.thread_context, register, context.reg_context)).*; | 393 | const value = (try regNative(&context.cpu_context, register)).*; |
| 363 | break :blk try applyOffset(value, offset); | 394 | break :blk try applyOffset(value, offset); |
| 364 | }, | 395 | }, |
| 365 | .expression => |expr| blk: { | 396 | .expression => |expr| blk: { |
| ... | @@ -381,73 +412,41 @@ pub const DwarfUnwindContext = struct { | ... | @@ -381,73 +412,41 @@ pub const DwarfUnwindContext = struct { |
| 381 | 412 | ||
| 382 | expression_context.cfa = context.cfa; | 413 | expression_context.cfa = context.cfa; |
| 383 | 414 | ||
| 384 | // Buffering the modifications is done because copying the thread context is not portable, | ||
| 385 | // some implementations (ie. darwin) use internal pointers to the mcontext. | ||
| 386 | var arena: std.heap.ArenaAllocator = .init(gpa); | ||
| 387 | defer arena.deinit(); | ||
| 388 | const update_arena = arena.allocator(); | ||
| 389 | |||
| 390 | const RegisterUpdate = struct { | ||
| 391 | // Backed by thread_context | ||
| 392 | dest: []u8, | ||
| 393 | // Backed by arena | ||
| 394 | src: []const u8, | ||
| 395 | prev: ?*@This(), | ||
| 396 | }; | ||
| 397 | |||
| 398 | var update_tail: ?*RegisterUpdate = null; | ||
| 399 | var has_return_address = true; | 415 | var has_return_address = true; |
| 416 | |||
| 417 | // Create a copy of the CPU context, to which we will apply the new rules. | ||
| 418 | var new_cpu_context = context.cpu_context; | ||
| 419 | |||
| 420 | // On all implemented architectures, the CFA is defined as being the previous frame's SP | ||
| 421 | (try regNative(&new_cpu_context, sp_reg_num)).* = context.cfa.?; | ||
| 422 | |||
| 400 | for (context.vm.rowColumns(row)) |column| { | 423 | for (context.vm.rowColumns(row)) |column| { |
| 401 | if (column.register) |register| { | 424 | if (column.register) |register| { |
| 425 | const dest = try new_cpu_context.dwarfRegisterBytes(register); | ||
| 426 | const rule_undef = try context.resolveRegisterRule(gpa, column, expression_context, dest); | ||
| 402 | if (register == cie.return_address_register) { | 427 | if (register == cie.return_address_register) { |
| 403 | has_return_address = column.rule != .undefined; | 428 | has_return_address = !rule_undef; |
| 404 | } | 429 | } |
| 405 | |||
| 406 | const dest = try regBytes(context.thread_context, register, context.reg_context); | ||
| 407 | const src = try update_arena.alloc(u8, dest.len); | ||
| 408 | try context.resolveRegisterRule(gpa, column, expression_context, src); | ||
| 409 | |||
| 410 | const new_update = try update_arena.create(RegisterUpdate); | ||
| 411 | new_update.* = .{ | ||
| 412 | .dest = dest, | ||
| 413 | .src = src, | ||
| 414 | .prev = update_tail, | ||
| 415 | }; | ||
| 416 | update_tail = new_update; | ||
| 417 | } | 430 | } |
| 418 | } | 431 | } |
| 419 | 432 | ||
| 420 | // On all implemented architectures, the CFA is defined as being the previous frame's SP | 433 | const return_address: u64 = if (has_return_address) pc: { |
| 421 | (try regValueNative(context.thread_context, Dwarf.abi.spRegNum(native_arch, context.reg_context), context.reg_context)).* = context.cfa.?; | 434 | const raw_ptr = try regNative(&new_cpu_context, cie.return_address_register); |
| 422 | 435 | break :pc stripInstructionPtrAuthCode(raw_ptr.*); | |
| 423 | while (update_tail) |tail| { | 436 | } else 0; |
| 424 | @memcpy(tail.dest, tail.src); | ||
| 425 | update_tail = tail.prev; | ||
| 426 | } | ||
| 427 | 437 | ||
| 428 | if (has_return_address) { | 438 | (try regNative(new_cpu_context, ip_reg_num)).* = return_address; |
| 429 | context.pc = stripInstructionPtrAuthCode((try regValueNative( | ||
| 430 | context.thread_context, | ||
| 431 | cie.return_address_register, | ||
| 432 | context.reg_context, | ||
| 433 | )).*); | ||
| 434 | } else { | ||
| 435 | context.pc = 0; | ||
| 436 | } | ||
| 437 | 439 | ||
| 438 | const ip_reg_num = Dwarf.abi.ipRegNum(native_arch).?; | 440 | // The new CPU context is complete; flush changes. |
| 439 | (try regValueNative(context.thread_context, ip_reg_num, context.reg_context)).* = context.pc; | 441 | context.cpu_context = new_cpu_context; |
| 440 | 442 | ||
| 441 | // The call instruction will have pushed the address of the instruction that follows the call as the return address. | 443 | // Also update the stored pc. However, because `return_address` points to the instruction |
| 442 | // This next instruction may be past the end of the function if the caller was `noreturn` (ie. the last instruction in | 444 | // *after* the call, it could (in the case of noreturn functions) actually point outside of |
| 443 | // the function was the call). If we were to look up an FDE entry using the return address directly, it could end up | 445 | // the caller's address range, meaning an FDE lookup would fail. We can handle this by |
| 444 | // either not finding an FDE at all, or using the next FDE in the program, producing incorrect results. To prevent this, | 446 | // subtracting 1 from `return_address` so that the next lookup is guaranteed to land inside |
| 445 | // we subtract one so that the next lookup is guaranteed to land inside the | 447 | // the `call` instruction`. The exception to this rule is signal frames, where the return |
| 446 | // | 448 | // address is the same instruction that triggered the handler. |
| 447 | // The exception to this rule is signal frames, where we return execution would be returned to the instruction | 449 | context.pc = if (cie.is_signal_frame) return_address else return_address -| 1; |
| 448 | // that triggered the handler. | ||
| 449 | const return_address = context.pc; | ||
| 450 | if (context.pc > 0 and !cie.is_signal_frame) context.pc -= 1; | ||
| 451 | 450 | ||
| 452 | return return_address; | 451 | return return_address; |
| 453 | } | 452 | } |
| ... | @@ -479,4 +478,18 @@ pub const DwarfUnwindContext = struct { | ... | @@ -479,4 +478,18 @@ pub const DwarfUnwindContext = struct { |
| 479 | 478 | ||
| 480 | return ptr; | 479 | return ptr; |
| 481 | } | 480 | } |
| 481 | |||
| 482 | pub fn regNative(ctx: *CpuContext, num: u16) error{ | ||
| 483 | InvalidRegister, | ||
| 484 | UnsupportedRegister, | ||
| 485 | IncompatibleRegisterSize, | ||
| 486 | }!*align(1) usize { | ||
| 487 | const bytes = try ctx.dwarfRegisterBytes(num); | ||
| 488 | if (bytes.len != @sizeOf(usize)) return error.IncompatibleRegisterSize; | ||
| 489 | return @ptrCast(bytes); | ||
| 490 | } | ||
| 491 | |||
| 492 | const ip_reg_num = Dwarf.ipRegNum(native_arch).?; | ||
| 493 | const fp_reg_num = Dwarf.fpRegNum(native_arch); | ||
| 494 | const sp_reg_num = Dwarf.spRegNum(native_arch); | ||
| 482 | }; | 495 | }; |
lib/std/debug/SelfInfo/DarwinModule.zig+24-33| ... | @@ -265,12 +265,9 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -265,12 +265,9 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 265 | error.OutOfMemory, | 265 | error.OutOfMemory, |
| 266 | error.Unexpected, | 266 | error.Unexpected, |
| 267 | => |e| return e, | 267 | => |e| return e, |
| 268 | error.UnimplementedArch, | 268 | error.UnsupportedRegister, |
| 269 | error.UnimplementedOs, | ||
| 270 | error.ThreadContextNotSupported, | ||
| 271 | => return error.UnsupportedDebugInfo, | 269 | => return error.UnsupportedDebugInfo, |
| 272 | error.InvalidRegister, | 270 | error.InvalidRegister, |
| 273 | error.RegisterContextRequired, | ||
| 274 | error.IncompatibleRegisterSize, | 271 | error.IncompatibleRegisterSize, |
| 275 | => return error.InvalidDebugInfo, | 272 | => return error.InvalidDebugInfo, |
| 276 | }; | 273 | }; |
| ... | @@ -396,7 +393,6 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -396,7 +393,6 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 396 | }; | 393 | }; |
| 397 | 394 | ||
| 398 | if (entry.raw_encoding == 0) return error.MissingDebugInfo; | 395 | if (entry.raw_encoding == 0) return error.MissingDebugInfo; |
| 399 | const reg_context: Dwarf.abi.RegisterContext = .{ .eh_frame = false, .is_macho = true }; | ||
| 400 | 396 | ||
| 401 | const encoding: macho.CompactUnwindEncoding = @bitCast(entry.raw_encoding); | 397 | const encoding: macho.CompactUnwindEncoding = @bitCast(entry.raw_encoding); |
| 402 | const new_ip = switch (builtin.cpu.arch) { | 398 | const new_ip = switch (builtin.cpu.arch) { |
| ... | @@ -405,16 +401,16 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -405,16 +401,16 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 405 | .RBP_FRAME => ip: { | 401 | .RBP_FRAME => ip: { |
| 406 | const frame = encoding.value.x86_64.frame; | 402 | const frame = encoding.value.x86_64.frame; |
| 407 | 403 | ||
| 408 | const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*; | 404 | const fp = (try dwarfRegNative(&context.cpu_context, fp_reg_num)).*; |
| 409 | const new_sp = fp + 2 * @sizeOf(usize); | 405 | const new_sp = fp + 2 * @sizeOf(usize); |
| 410 | 406 | ||
| 411 | const ip_ptr = fp + @sizeOf(usize); | 407 | const ip_ptr = fp + @sizeOf(usize); |
| 412 | const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; | 408 | const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; |
| 413 | const new_fp = @as(*const usize, @ptrFromInt(fp)).*; | 409 | const new_fp = @as(*const usize, @ptrFromInt(fp)).*; |
| 414 | 410 | ||
| 415 | (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).* = new_fp; | 411 | (try dwarfRegNative(&context.cpu_context, fp_reg_num)).* = new_fp; |
| 416 | (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp; | 412 | (try dwarfRegNative(&context.cpu_context, sp_reg_num)).* = new_sp; |
| 417 | (try regValueNative(context.thread_context, ip_reg_num, reg_context)).* = new_ip; | 413 | (try dwarfRegNative(&context.cpu_context, ip_reg_num)).* = new_ip; |
| 418 | 414 | ||
| 419 | const regs: [5]u3 = .{ | 415 | const regs: [5]u3 = .{ |
| 420 | frame.reg0, | 416 | frame.reg0, |
| ... | @@ -427,7 +423,7 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -427,7 +423,7 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 427 | if (reg == 0) continue; | 423 | if (reg == 0) continue; |
| 428 | const addr = fp - frame.frame_offset * @sizeOf(usize) + i * @sizeOf(usize); | 424 | const addr = fp - frame.frame_offset * @sizeOf(usize) + i * @sizeOf(usize); |
| 429 | const reg_number = try Dwarf.compactUnwindToDwarfRegNumber(reg); | 425 | const reg_number = try Dwarf.compactUnwindToDwarfRegNumber(reg); |
| 430 | (try regValueNative(context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(addr)).*; | 426 | (try dwarfRegNative(&context.cpu_context, reg_number)).* = @as(*const usize, @ptrFromInt(addr)).*; |
| 431 | } | 427 | } |
| 432 | 428 | ||
| 433 | break :ip new_ip; | 429 | break :ip new_ip; |
| ... | @@ -437,7 +433,7 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -437,7 +433,7 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 437 | => ip: { | 433 | => ip: { |
| 438 | const frameless = encoding.value.x86_64.frameless; | 434 | const frameless = encoding.value.x86_64.frameless; |
| 439 | 435 | ||
| 440 | const sp = (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).*; | 436 | const sp = (try dwarfRegNative(&context.cpu_context, sp_reg_num)).*; |
| 441 | const stack_size: usize = stack_size: { | 437 | const stack_size: usize = stack_size: { |
| 442 | if (encoding.mode.x86_64 == .STACK_IMMD) { | 438 | if (encoding.mode.x86_64 == .STACK_IMMD) { |
| 443 | break :stack_size @as(usize, frameless.stack.direct.stack_size) * @sizeOf(usize); | 439 | 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, | ... | @@ -487,7 +483,7 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 487 | var reg_addr = sp + stack_size - @sizeOf(usize) * @as(usize, reg_count + 1); | 483 | var reg_addr = sp + stack_size - @sizeOf(usize) * @as(usize, reg_count + 1); |
| 488 | for (0..reg_count) |i| { | 484 | for (0..reg_count) |i| { |
| 489 | const reg_number = try Dwarf.compactUnwindToDwarfRegNumber(registers[i]); | 485 | const reg_number = try Dwarf.compactUnwindToDwarfRegNumber(registers[i]); |
| 490 | (try regValueNative(context.thread_context, reg_number, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*; | 486 | (try dwarfRegNative(&context.cpu_context, reg_number)).* = @as(*const usize, @ptrFromInt(reg_addr)).*; |
| 491 | reg_addr += @sizeOf(usize); | 487 | reg_addr += @sizeOf(usize); |
| 492 | } | 488 | } |
| 493 | 489 | ||
| ... | @@ -497,8 +493,8 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -497,8 +493,8 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 497 | const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; | 493 | const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; |
| 498 | const new_sp = ip_ptr + @sizeOf(usize); | 494 | const new_sp = ip_ptr + @sizeOf(usize); |
| 499 | 495 | ||
| 500 | (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp; | 496 | (try dwarfRegNative(&context.cpu_context, sp_reg_num)).* = new_sp; |
| 501 | (try regValueNative(context.thread_context, ip_reg_num, reg_context)).* = new_ip; | 497 | (try dwarfRegNative(&context.cpu_context, ip_reg_num)).* = new_ip; |
| 502 | 498 | ||
| 503 | break :ip new_ip; | 499 | break :ip new_ip; |
| 504 | }, | 500 | }, |
| ... | @@ -516,10 +512,10 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -516,10 +512,10 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 516 | .aarch64, .aarch64_be => switch (encoding.mode.arm64) { | 512 | .aarch64, .aarch64_be => switch (encoding.mode.arm64) { |
| 517 | .OLD => return error.UnsupportedDebugInfo, | 513 | .OLD => return error.UnsupportedDebugInfo, |
| 518 | .FRAMELESS => ip: { | 514 | .FRAMELESS => ip: { |
| 519 | const sp = (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).*; | 515 | const sp = (try dwarfRegNative(&context.cpu_context, sp_reg_num)).*; |
| 520 | const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16; | 516 | const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16; |
| 521 | const new_ip = (try regValueNative(context.thread_context, 30, reg_context)).*; | 517 | const new_ip = (try dwarfRegNative(&context.cpu_context, 30)).*; |
| 522 | (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp; | 518 | (try dwarfRegNative(&context.cpu_context, sp_reg_num)).* = new_sp; |
| 523 | break :ip new_ip; | 519 | break :ip new_ip; |
| 524 | }, | 520 | }, |
| 525 | .DWARF => { | 521 | .DWARF => { |
| ... | @@ -535,15 +531,15 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -535,15 +531,15 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 535 | .FRAME => ip: { | 531 | .FRAME => ip: { |
| 536 | const frame = encoding.value.arm64.frame; | 532 | const frame = encoding.value.arm64.frame; |
| 537 | 533 | ||
| 538 | const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*; | 534 | const fp = (try dwarfRegNative(&context.cpu_context, fp_reg_num)).*; |
| 539 | const ip_ptr = fp + @sizeOf(usize); | 535 | const ip_ptr = fp + @sizeOf(usize); |
| 540 | 536 | ||
| 541 | var reg_addr = fp - @sizeOf(usize); | 537 | var reg_addr = fp - @sizeOf(usize); |
| 542 | inline for (@typeInfo(@TypeOf(frame.x_reg_pairs)).@"struct".fields, 0..) |field, i| { | 538 | inline for (@typeInfo(@TypeOf(frame.x_reg_pairs)).@"struct".fields, 0..) |field, i| { |
| 543 | if (@field(frame.x_reg_pairs, field.name) != 0) { | 539 | if (@field(frame.x_reg_pairs, field.name) != 0) { |
| 544 | (try regValueNative(context.thread_context, 19 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*; | 540 | (try dwarfRegNative(&context.cpu_context, 19 + i)).* = @as(*const usize, @ptrFromInt(reg_addr)).*; |
| 545 | reg_addr += @sizeOf(usize); | 541 | reg_addr += @sizeOf(usize); |
| 546 | (try regValueNative(context.thread_context, 20 + i, reg_context)).* = @as(*const usize, @ptrFromInt(reg_addr)).*; | 542 | (try dwarfRegNative(&context.cpu_context, 20 + i)).* = @as(*const usize, @ptrFromInt(reg_addr)).*; |
| 547 | reg_addr += @sizeOf(usize); | 543 | reg_addr += @sizeOf(usize); |
| 548 | } | 544 | } |
| 549 | } | 545 | } |
| ... | @@ -552,12 +548,12 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -552,12 +548,12 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 552 | if (@field(frame.d_reg_pairs, field.name) != 0) { | 548 | if (@field(frame.d_reg_pairs, field.name) != 0) { |
| 553 | // Only the lower half of the 128-bit V registers are restored during unwinding | 549 | // Only the lower half of the 128-bit V registers are restored during unwinding |
| 554 | { | 550 | { |
| 555 | const dest: *align(1) usize = @ptrCast(try regBytes(context.thread_context, 64 + 8 + i, context.reg_context)); | 551 | const dest: *align(1) usize = @ptrCast(try context.cpu_context.dwarfRegisterBytes(64 + 8 + i)); |
| 556 | dest.* = @as(*const usize, @ptrFromInt(reg_addr)).*; | 552 | dest.* = @as(*const usize, @ptrFromInt(reg_addr)).*; |
| 557 | } | 553 | } |
| 558 | reg_addr += @sizeOf(usize); | 554 | reg_addr += @sizeOf(usize); |
| 559 | { | 555 | { |
| 560 | const dest: *align(1) usize = @ptrCast(try regBytes(context.thread_context, 64 + 9 + i, context.reg_context)); | 556 | const dest: *align(1) usize = @ptrCast(try context.cpu_context.dwarfRegisterBytes(64 + 9 + i)); |
| 561 | dest.* = @as(*const usize, @ptrFromInt(reg_addr)).*; | 557 | dest.* = @as(*const usize, @ptrFromInt(reg_addr)).*; |
| 562 | } | 558 | } |
| 563 | reg_addr += @sizeOf(usize); | 559 | reg_addr += @sizeOf(usize); |
| ... | @@ -567,8 +563,8 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, | ... | @@ -567,8 +563,8 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 567 | const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; | 563 | const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; |
| 568 | const new_fp = @as(*const usize, @ptrFromInt(fp)).*; | 564 | const new_fp = @as(*const usize, @ptrFromInt(fp)).*; |
| 569 | 565 | ||
| 570 | (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).* = new_fp; | 566 | (try dwarfRegNative(&context.cpu_context, fp_reg_num)).* = new_fp; |
| 571 | (try regValueNative(context.thread_context, ip_reg_num, reg_context)).* = new_ip; | 567 | (try dwarfRegNative(&context.cpu_context, ip_reg_num)).* = new_ip; |
| 572 | 568 | ||
| 573 | break :ip new_ip; | 569 | break :ip new_ip; |
| 574 | }, | 570 | }, |
| ... | @@ -782,13 +778,9 @@ test { | ... | @@ -782,13 +778,9 @@ test { |
| 782 | _ = MachoSymbol; | 778 | _ = MachoSymbol; |
| 783 | } | 779 | } |
| 784 | 780 | ||
| 785 | fn fpRegNum(reg_context: Dwarf.abi.RegisterContext) u8 { | 781 | const ip_reg_num = Dwarf.ipRegNum(builtin.target.cpu.arch).?; |
| 786 | return Dwarf.abi.fpRegNum(builtin.target.cpu.arch, reg_context); | 782 | const fp_reg_num = Dwarf.fpRegNum(builtin.target.cpu.arch); |
| 787 | } | 783 | const sp_reg_num = Dwarf.spRegNum(builtin.target.cpu.arch); |
| 788 | fn spRegNum(reg_context: Dwarf.abi.RegisterContext) u8 { | ||
| 789 | return Dwarf.abi.spRegNum(builtin.target.cpu.arch, reg_context); | ||
| 790 | } | ||
| 791 | const ip_reg_num = Dwarf.abi.ipRegNum(builtin.target.cpu.arch).?; | ||
| 792 | 784 | ||
| 793 | /// Uses `mmap` to map the file at `path` into memory. | 785 | /// Uses `mmap` to map the file at `path` into memory. |
| 794 | fn mapDebugInfoFile(path: []const u8) ![]align(std.heap.page_size_min) const u8 { | 786 | fn mapDebugInfoFile(path: []const u8) ![]align(std.heap.page_size_min) const u8 { |
| ... | @@ -821,8 +813,7 @@ const mem = std.mem; | ... | @@ -821,8 +813,7 @@ const mem = std.mem; |
| 821 | const posix = std.posix; | 813 | const posix = std.posix; |
| 822 | const testing = std.testing; | 814 | const testing = std.testing; |
| 823 | const Error = std.debug.SelfInfo.Error; | 815 | const Error = std.debug.SelfInfo.Error; |
| 824 | const regBytes = Dwarf.abi.regBytes; | 816 | const dwarfRegNative = std.debug.SelfInfo.DwarfUnwindContext.regNative; |
| 825 | const regValueNative = Dwarf.abi.regValueNative; | ||
| 826 | 817 | ||
| 827 | const builtin = @import("builtin"); | 818 | const builtin = @import("builtin"); |
| 828 | const native_endian = builtin.target.cpu.arch.endian(); | 819 | const native_endian = builtin.target.cpu.arch.endian(); |
lib/std/debug/SelfInfo/ElfModule.zig+1-2| ... | @@ -26,7 +26,6 @@ pub fn key(m: ElfModule) usize { | ... | @@ -26,7 +26,6 @@ pub fn key(m: ElfModule) usize { |
| 26 | pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) Error!ElfModule { | 26 | pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) Error!ElfModule { |
| 27 | _ = cache; | 27 | _ = cache; |
| 28 | _ = gpa; | 28 | _ = gpa; |
| 29 | if (builtin.target.os.tag == .haiku) @panic("TODO implement lookup module for Haiku"); | ||
| 30 | const DlIterContext = struct { | 29 | const DlIterContext = struct { |
| 31 | /// input | 30 | /// input |
| 32 | address: usize, | 31 | address: usize, |
| ... | @@ -261,7 +260,7 @@ pub const supports_unwinding: bool = s: { | ... | @@ -261,7 +260,7 @@ pub const supports_unwinding: bool = s: { |
| 261 | }; | 260 | }; |
| 262 | comptime { | 261 | comptime { |
| 263 | if (supports_unwinding) { | 262 | if (supports_unwinding) { |
| 264 | std.debug.assert(Dwarf.abi.supportsUnwinding(&builtin.target)); | 263 | std.debug.assert(Dwarf.supportsUnwinding(&builtin.target)); |
| 265 | } | 264 | } |
| 266 | } | 265 | } |
| 267 | 266 |
lib/std/debug/SelfInfo/WindowsModule.zig+37-3| ... | @@ -295,11 +295,45 @@ pub const UnwindContext = struct { | ... | @@ -295,11 +295,45 @@ pub const UnwindContext = struct { |
| 295 | pc: usize, | 295 | pc: usize, |
| 296 | cur: windows.CONTEXT, | 296 | cur: windows.CONTEXT, |
| 297 | history_table: windows.UNWIND_HISTORY_TABLE, | 297 | history_table: windows.UNWIND_HISTORY_TABLE, |
| 298 | pub fn init(ctx: *const windows.CONTEXT, gpa: Allocator) Allocator.Error!UnwindContext { | 298 | pub fn init(ctx: *const std.debug.cpu_context.Native) UnwindContext { |
| 299 | _ = gpa; | ||
| 300 | return .{ | 299 | return .{ |
| 301 | .pc = @returnAddress(), | 300 | .pc = @returnAddress(), |
| 302 | .cur = ctx.*, | 301 | .cur = switch (builtin.cpu.arch) { |
| 302 | .x86_64 => std.mem.zeroInit(windows.CONTEXT, .{ | ||
| 303 | .Rax = ctx.gprs.get(.rax), | ||
| 304 | .Rcx = ctx.gprs.get(.rcx), | ||
| 305 | .Rdx = ctx.gprs.get(.rdx), | ||
| 306 | .Rbx = ctx.gprs.get(.rbx), | ||
| 307 | .Rsp = ctx.gprs.get(.rsp), | ||
| 308 | .Rbp = ctx.gprs.get(.rbp), | ||
| 309 | .Rsi = ctx.gprs.get(.rsi), | ||
| 310 | .Rdi = ctx.gprs.get(.rdi), | ||
| 311 | .R8 = ctx.gprs.get(.r8), | ||
| 312 | .R9 = ctx.gprs.get(.r9), | ||
| 313 | .R10 = ctx.gprs.get(.r10), | ||
| 314 | .R11 = ctx.gprs.get(.r11), | ||
| 315 | .R12 = ctx.gprs.get(.r12), | ||
| 316 | .R13 = ctx.gprs.get(.r13), | ||
| 317 | .R14 = ctx.gprs.get(.r14), | ||
| 318 | .R15 = ctx.gprs.get(.r15), | ||
| 319 | .Rip = ctx.gprs.get(.rip), | ||
| 320 | }), | ||
| 321 | .aarch64, .aarch64_be => .{ | ||
| 322 | .ContextFlags = 0, | ||
| 323 | .Cpsr = 0, | ||
| 324 | .DUMMYUNIONNAME = .{ .X = ctx.x }, | ||
| 325 | .Sp = ctx.sp, | ||
| 326 | .Pc = ctx.pc, | ||
| 327 | .V = @splat(.{ .B = @splat(0) }), | ||
| 328 | .Fpcr = 0, | ||
| 329 | .Fpsr = 0, | ||
| 330 | .Bcr = @splat(0), | ||
| 331 | .Bvr = @splat(0), | ||
| 332 | .Wcr = @splat(0), | ||
| 333 | .Wvr = @splat(0), | ||
| 334 | }, | ||
| 335 | else => comptime unreachable, | ||
| 336 | }, | ||
| 303 | .history_table = std.mem.zeroes(windows.UNWIND_HISTORY_TABLE), | 337 | .history_table = std.mem.zeroes(windows.UNWIND_HISTORY_TABLE), |
| 304 | }; | 338 | }; |
| 305 | } | 339 | } |
lib/std/debug/cpu_context.zig created+1019| ... | @@ -0,0 +1,1019 @@ | ||
| 1 | /// Register state for the native architecture, used by `std.debug` for stack unwinding. | ||
| 2 | /// `noreturn` if there is no implementation for the native architecture. | ||
| 3 | /// This can be overriden by exposing a declaration `root.debug.CpuContext`. | ||
| 4 | pub const Native = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "CpuContext")) | ||
| 5 | root.debug.CpuContext | ||
| 6 | else switch (native_arch) { | ||
| 7 | .x86 => X86, | ||
| 8 | .x86_64 => X86_64, | ||
| 9 | .arm, .armeb, .thumb, .thumbeb => Arm, | ||
| 10 | .aarch64, .aarch64_be => Aarch64, | ||
| 11 | else => noreturn, | ||
| 12 | }; | ||
| 13 | |||
| 14 | pub const DwarfRegisterError = error{ | ||
| 15 | InvalidRegister, | ||
| 16 | UnsupportedRegister, | ||
| 17 | }; | ||
| 18 | |||
| 19 | pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native { | ||
| 20 | if (signal_ucontext_t == void) return null; | ||
| 21 | const uc: *const signal_ucontext_t = @ptrCast(@alignCast(ctx_ptr)); | ||
| 22 | return switch (native_arch) { | ||
| 23 | .x86 => switch (native_os) { | ||
| 24 | .linux, .netbsd, .solaris, .illumos => .{ .gprs = .init(.{ | ||
| 25 | .eax = uc.mcontext.gregs[std.posix.REG.EAX], | ||
| 26 | .ecx = uc.mcontext.gregs[std.posix.REG.ECX], | ||
| 27 | .edx = uc.mcontext.gregs[std.posix.REG.EDX], | ||
| 28 | .ebx = uc.mcontext.gregs[std.posix.REG.EBX], | ||
| 29 | .esp = uc.mcontext.gregs[std.posix.REG.ESP], | ||
| 30 | .ebp = uc.mcontext.gregs[std.posix.REG.EBP], | ||
| 31 | .esi = uc.mcontext.gregs[std.posix.REG.ESI], | ||
| 32 | .edi = uc.mcontext.gregs[std.posix.REG.EDI], | ||
| 33 | .eip = uc.mcontext.gregs[std.posix.REG.EIP], | ||
| 34 | }) }, | ||
| 35 | else => null, | ||
| 36 | }, | ||
| 37 | .x86_64 => switch (native_os) { | ||
| 38 | .linux, .solaris, .illumos => .{ .gprs = .init(.{ | ||
| 39 | .rax = uc.mcontext.gregs[std.posix.REG.RAX], | ||
| 40 | .rdx = uc.mcontext.gregs[std.posix.REG.RDX], | ||
| 41 | .rcx = uc.mcontext.gregs[std.posix.REG.RCX], | ||
| 42 | .rbx = uc.mcontext.gregs[std.posix.REG.RBX], | ||
| 43 | .rsi = uc.mcontext.gregs[std.posix.REG.RSI], | ||
| 44 | .rdi = uc.mcontext.gregs[std.posix.REG.RDI], | ||
| 45 | .rbp = uc.mcontext.gregs[std.posix.REG.RBP], | ||
| 46 | .rsp = uc.mcontext.gregs[std.posix.REG.RSP], | ||
| 47 | .r8 = uc.mcontext.gregs[std.posix.REG.R8], | ||
| 48 | .r9 = uc.mcontext.gregs[std.posix.REG.R9], | ||
| 49 | .r10 = uc.mcontext.gregs[std.posix.REG.R10], | ||
| 50 | .r11 = uc.mcontext.gregs[std.posix.REG.R11], | ||
| 51 | .r12 = uc.mcontext.gregs[std.posix.REG.R12], | ||
| 52 | .r13 = uc.mcontext.gregs[std.posix.REG.R13], | ||
| 53 | .r14 = uc.mcontext.gregs[std.posix.REG.R14], | ||
| 54 | .r15 = uc.mcontext.gregs[std.posix.REG.R15], | ||
| 55 | .rip = uc.mcontext.gregs[std.posix.REG.RIP], | ||
| 56 | }) }, | ||
| 57 | .freebsd => .{ .gprs = .init(.{ | ||
| 58 | .rax = uc.mcontext.rax, | ||
| 59 | .rdx = uc.mcontext.rdx, | ||
| 60 | .rcx = uc.mcontext.rcx, | ||
| 61 | .rbx = uc.mcontext.rbx, | ||
| 62 | .rsi = uc.mcontext.rsi, | ||
| 63 | .rdi = uc.mcontext.rdi, | ||
| 64 | .rbp = uc.mcontext.rbp, | ||
| 65 | .rsp = uc.mcontext.rsp, | ||
| 66 | .r8 = uc.mcontext.r8, | ||
| 67 | .r9 = uc.mcontext.r9, | ||
| 68 | .r10 = uc.mcontext.r10, | ||
| 69 | .r11 = uc.mcontext.r11, | ||
| 70 | .r12 = uc.mcontext.r12, | ||
| 71 | .r13 = uc.mcontext.r13, | ||
| 72 | .r14 = uc.mcontext.r14, | ||
| 73 | .r15 = uc.mcontext.r15, | ||
| 74 | .rip = uc.mcontext.rip, | ||
| 75 | }) }, | ||
| 76 | .openbsd => .{ .gprs = .init(.{ | ||
| 77 | .rax = @bitCast(uc.sc_rax), | ||
| 78 | .rdx = @bitCast(uc.sc_rdx), | ||
| 79 | .rcx = @bitCast(uc.sc_rcx), | ||
| 80 | .rbx = @bitCast(uc.sc_rbx), | ||
| 81 | .rsi = @bitCast(uc.sc_rsi), | ||
| 82 | .rdi = @bitCast(uc.sc_rdi), | ||
| 83 | .rbp = @bitCast(uc.sc_rbp), | ||
| 84 | .rsp = @bitCast(uc.sc_rsp), | ||
| 85 | .r8 = @bitCast(uc.sc_r8), | ||
| 86 | .r9 = @bitCast(uc.sc_r9), | ||
| 87 | .r10 = @bitCast(uc.sc_r10), | ||
| 88 | .r11 = @bitCast(uc.sc_r11), | ||
| 89 | .r12 = @bitCast(uc.sc_r12), | ||
| 90 | .r13 = @bitCast(uc.sc_r13), | ||
| 91 | .r14 = @bitCast(uc.sc_r14), | ||
| 92 | .r15 = @bitCast(uc.sc_r15), | ||
| 93 | .rip = @bitCast(uc.sc_rip), | ||
| 94 | }) }, | ||
| 95 | .macos, .ios => .{ .gprs = .init(.{ | ||
| 96 | .rax = uc.mcontext.ss.rax, | ||
| 97 | .rdx = uc.mcontext.ss.rdx, | ||
| 98 | .rcx = uc.mcontext.ss.rcx, | ||
| 99 | .rbx = uc.mcontext.ss.rbx, | ||
| 100 | .rsi = uc.mcontext.ss.rsi, | ||
| 101 | .rdi = uc.mcontext.ss.rdi, | ||
| 102 | .rbp = uc.mcontext.ss.rbp, | ||
| 103 | .rsp = uc.mcontext.ss.rsp, | ||
| 104 | .r8 = uc.mcontext.ss.r8, | ||
| 105 | .r9 = uc.mcontext.ss.r9, | ||
| 106 | .r10 = uc.mcontext.ss.r10, | ||
| 107 | .r11 = uc.mcontext.ss.r11, | ||
| 108 | .r12 = uc.mcontext.ss.r12, | ||
| 109 | .r13 = uc.mcontext.ss.r13, | ||
| 110 | .r14 = uc.mcontext.ss.r14, | ||
| 111 | .r15 = uc.mcontext.ss.r15, | ||
| 112 | .rip = uc.mcontext.ss.rip, | ||
| 113 | }) }, | ||
| 114 | else => null, | ||
| 115 | }, | ||
| 116 | .arm, .armeb, .thumb, .thumbeb => switch (builtin.os.tag) { | ||
| 117 | .linux => .{ | ||
| 118 | .r = .{ | ||
| 119 | uc.mcontext.arm_r0, | ||
| 120 | uc.mcontext.arm_r1, | ||
| 121 | uc.mcontext.arm_r2, | ||
| 122 | uc.mcontext.arm_r3, | ||
| 123 | uc.mcontext.arm_r4, | ||
| 124 | uc.mcontext.arm_r5, | ||
| 125 | uc.mcontext.arm_r6, | ||
| 126 | uc.mcontext.arm_r7, | ||
| 127 | uc.mcontext.arm_r8, | ||
| 128 | uc.mcontext.arm_r9, | ||
| 129 | uc.mcontext.arm_r10, | ||
| 130 | uc.mcontext.arm_fp, // r11 = fp | ||
| 131 | uc.mcontext.arm_ip, // r12 = ip | ||
| 132 | uc.mcontext.arm_sp, // r13 = sp | ||
| 133 | uc.mcontext.arm_lr, // r14 = lr | ||
| 134 | uc.mcontext.arm_pc, // r15 = pc | ||
| 135 | }, | ||
| 136 | }, | ||
| 137 | else => null, | ||
| 138 | }, | ||
| 139 | .aarch64, .aarch64_be => switch (builtin.os.tag) { | ||
| 140 | .macos, .ios, .tvos, .watchos, .visionos => .{ | ||
| 141 | .x = uc.mcontext.ss.regs ++ @as([2]u64, .{ | ||
| 142 | uc.mcontext.ss.fp, // x29 = fp | ||
| 143 | uc.mcontext.ss.lr, // x30 = lr | ||
| 144 | }), | ||
| 145 | .sp = uc.mcontext.ss.sp, | ||
| 146 | .pc = uc.mcontext.ss.pc, | ||
| 147 | }, | ||
| 148 | .netbsd => .{ | ||
| 149 | .x = uc.mcontext.gregs[0..31], | ||
| 150 | .sp = uc.mcontext.gregs[31], | ||
| 151 | .pc = uc.mcontext.gregs[32], | ||
| 152 | }, | ||
| 153 | .freebsd => .{ | ||
| 154 | .x = uc.mcontext.gpregs.x ++ @as([1]u64, .{ | ||
| 155 | uc.mcontext.gpregs.lr, // x30 = lr | ||
| 156 | }), | ||
| 157 | .sp = uc.mcontext.gpregs.sp, | ||
| 158 | // On aarch64, the register ELR_LR1 defines the address to return to after handling | ||
| 159 | // a CPU exception (ELR is "Exception Link Register"). FreeBSD's ucontext_t uses | ||
| 160 | // this as the field name, but it's the same thing as the context's PC. | ||
| 161 | .pc = uc.mcontext.gpregs.elr, | ||
| 162 | }, | ||
| 163 | .openbsd => .{ | ||
| 164 | .x = uc.sc_x ++ .{uc.sc_lr}, | ||
| 165 | .sp = uc.sc_sp, | ||
| 166 | // Not a bug; see freebsd above for explanation. | ||
| 167 | .pc = uc.sc_elr, | ||
| 168 | }, | ||
| 169 | .linux => .{ | ||
| 170 | .x = uc.mcontext.regs, | ||
| 171 | .sp = uc.mcontext.sp, | ||
| 172 | .pc = uc.mcontext.pc, | ||
| 173 | }, | ||
| 174 | else => null, | ||
| 175 | }, | ||
| 176 | else => null, | ||
| 177 | }; | ||
| 178 | } | ||
| 179 | |||
| 180 | pub fn fromWindowsContext(ctx: *const std.os.windows.CONTEXT) Native { | ||
| 181 | return switch (native_arch) { | ||
| 182 | .x86 => .{ .gprs = .init(.{ | ||
| 183 | .eax = ctx.Eax, | ||
| 184 | .ecx = ctx.Ecx, | ||
| 185 | .edx = ctx.Edx, | ||
| 186 | .ebx = ctx.Ebx, | ||
| 187 | .esp = ctx.Esp, | ||
| 188 | .ebp = ctx.Ebp, | ||
| 189 | .esi = ctx.Esi, | ||
| 190 | .edi = ctx.Edi, | ||
| 191 | .eip = ctx.Eip, | ||
| 192 | }) }, | ||
| 193 | .x86_64 => .{ .gprs = .init(.{ | ||
| 194 | .rax = ctx.Rax, | ||
| 195 | .rdx = ctx.Rdx, | ||
| 196 | .rcx = ctx.Rcx, | ||
| 197 | .rbx = ctx.Rbx, | ||
| 198 | .rsi = ctx.Rsi, | ||
| 199 | .rdi = ctx.Rdi, | ||
| 200 | .rbp = ctx.Rbp, | ||
| 201 | .rsp = ctx.Rsp, | ||
| 202 | .r8 = ctx.R8, | ||
| 203 | .r9 = ctx.R9, | ||
| 204 | .r10 = ctx.R10, | ||
| 205 | .r11 = ctx.R11, | ||
| 206 | .r12 = ctx.R12, | ||
| 207 | .r13 = ctx.R13, | ||
| 208 | .r14 = ctx.R14, | ||
| 209 | .r15 = ctx.R15, | ||
| 210 | .rip = ctx.Rip, | ||
| 211 | }) }, | ||
| 212 | .aarch64, .aarch64_be => .{ | ||
| 213 | .x = ctx.DUMMYUNIONNAME.X[0..31].*, | ||
| 214 | .sp = ctx.Sp, | ||
| 215 | .pc = ctx.Pc, | ||
| 216 | }, | ||
| 217 | else => comptime unreachable, | ||
| 218 | }; | ||
| 219 | } | ||
| 220 | |||
| 221 | pub const X86 = struct { | ||
| 222 | /// The first 8 registers here intentionally match the order of registers pushed | ||
| 223 | /// by PUSHA, which is also the order used by the DWARF register mappings. | ||
| 224 | pub const Gpr = enum { | ||
| 225 | // zig fmt: off | ||
| 226 | eax, ecx, edx, ebx, | ||
| 227 | esp, ebp, esi, edi, | ||
| 228 | eip, | ||
| 229 | // zig fmt: on | ||
| 230 | }; | ||
| 231 | gprs: std.enums.EnumArray(Gpr, u32), | ||
| 232 | |||
| 233 | pub inline fn current() X86 { | ||
| 234 | var ctx: X86 = undefined; | ||
| 235 | asm volatile ( | ||
| 236 | \\movl %%eax, 0x00(%%edi) | ||
| 237 | \\movl %%ecx, 0x04(%%edi) | ||
| 238 | \\movl %%edx, 0x08(%%edi) | ||
| 239 | \\movl %%ebx, 0x0c(%%edi) | ||
| 240 | \\movl %%esp, 0x10(%%edi) | ||
| 241 | \\movl %%ebp, 0x14(%%edi) | ||
| 242 | \\movl %%esi, 0x18(%%edi) | ||
| 243 | \\movl %%edi, 0x1c(%%edi) | ||
| 244 | \\call 1f | ||
| 245 | \\1: | ||
| 246 | \\popl 0x20(%%edi) | ||
| 247 | : | ||
| 248 | : [gprs] "{edi}" (&ctx.gprs.values), | ||
| 249 | : .{ .memory = true }); | ||
| 250 | return ctx; | ||
| 251 | } | ||
| 252 | |||
| 253 | pub fn dwarfRegisterBytes(ctx: *X86, register_num: u16) DwarfRegisterError![]u8 { | ||
| 254 | // System V Application Binary Interface Intel386 Architecture Processor Supplement Version 1.1 | ||
| 255 | // § 2.4.2 "DWARF Register Number Mapping" | ||
| 256 | switch (register_num) { | ||
| 257 | // The order of `Gpr` intentionally matches DWARF's mappings. | ||
| 258 | // | ||
| 259 | // x86-macos sometimes uses different mappings (ebp and esp are reversed when the unwind | ||
| 260 | // information is from `__eh_frame`). This deviation is not considered here, because | ||
| 261 | // x86-macos is a deprecated target which is not supported by the Zig Standard Library. | ||
| 262 | 0...8 => return @ptrCast(&ctx.gprs.values[register_num]), | ||
| 263 | |||
| 264 | 9 => return error.UnsupportedRegister, // rflags | ||
| 265 | 11...18 => return error.UnsupportedRegister, // st0 - st7 | ||
| 266 | 21...28 => return error.UnsupportedRegister, // xmm0 - xmm7 | ||
| 267 | 29...36 => return error.UnsupportedRegister, // mm0 - mm7 | ||
| 268 | 39 => return error.UnsupportedRegister, // mxcsr | ||
| 269 | 40...45 => return error.UnsupportedRegister, // es, cs, ss, ds, fs, gs | ||
| 270 | 48 => return error.UnsupportedRegister, // tr | ||
| 271 | 49 => return error.UnsupportedRegister, // ldtr | ||
| 272 | 93...94 => return error.UnsupportedRegister, // fs.base, gs.base | ||
| 273 | |||
| 274 | else => return error.InvalidRegister, | ||
| 275 | } | ||
| 276 | } | ||
| 277 | }; | ||
| 278 | |||
| 279 | pub const X86_64 = struct { | ||
| 280 | /// MLUGG TODO: explain this order. why does DWARF have this? | ||
| 281 | pub const Gpr = enum { | ||
| 282 | // zig fmt: off | ||
| 283 | rax, rdx, rcx, rbx, | ||
| 284 | rsi, rdi, rbp, rsp, | ||
| 285 | r8, r9, r10, r11, | ||
| 286 | r12, r13, r14, r15, | ||
| 287 | rip, | ||
| 288 | // zig fmt: on | ||
| 289 | }; | ||
| 290 | gprs: std.enums.EnumArray(Gpr, u64), | ||
| 291 | |||
| 292 | pub inline fn current() X86_64 { | ||
| 293 | var ctx: X86_64 = undefined; | ||
| 294 | asm volatile ( | ||
| 295 | \\movq %%rax, 0x00(%%rdi) | ||
| 296 | \\movq %%rdx, 0x08(%%rdi) | ||
| 297 | \\movq %%rcx, 0x10(%%rdi) | ||
| 298 | \\movq %%rbx, 0x18(%%rdi) | ||
| 299 | \\movq %%rsi, 0x20(%%rdi) | ||
| 300 | \\movq %%rdi, 0x28(%%rdi) | ||
| 301 | \\movq %%rbp, 0x30(%%rdi) | ||
| 302 | \\movq %%rsp, 0x38(%%rdi) | ||
| 303 | \\movq %%r8, 0x40(%%rdi) | ||
| 304 | \\movq %%r9, 0x48(%%rdi) | ||
| 305 | \\movq %%r10, 0x50(%%rdi) | ||
| 306 | \\movq %%r11, 0x58(%%rdi) | ||
| 307 | \\movq %%r12, 0x60(%%rdi) | ||
| 308 | \\movq %%r13, 0x68(%%rdi) | ||
| 309 | \\movq %%r14, 0x70(%%rdi) | ||
| 310 | \\movq %%r15, 0x78(%%rdi) | ||
| 311 | \\leaq (%%rip), %%rax | ||
| 312 | \\movq %%rax, 0x80(%%rdi) | ||
| 313 | \\movq 0x00(%%rdi), %%rax // restore saved rax | ||
| 314 | : | ||
| 315 | : [gprs] "{rdi}" (&ctx.gprs.values), | ||
| 316 | : .{ .memory = true }); | ||
| 317 | return ctx; | ||
| 318 | } | ||
| 319 | |||
| 320 | pub fn dwarfRegisterBytes(ctx: *X86_64, register_num: u16) DwarfRegisterError![]u8 { | ||
| 321 | // System V Application Binary Interface AMD64 Architecture Processor Supplement | ||
| 322 | // § 3.6.2 "DWARF Register Number Mapping" | ||
| 323 | switch (register_num) { | ||
| 324 | // The order of `Gpr` intentionally matches DWARF's mappings. | ||
| 325 | 0...16 => return @ptrCast(&ctx.gprs.values[register_num]), | ||
| 326 | |||
| 327 | 17...32 => return error.UnsupportedRegister, // xmm0 - xmm15 | ||
| 328 | 33...40 => return error.UnsupportedRegister, // st0 - st7 | ||
| 329 | 41...48 => return error.UnsupportedRegister, // mm0 - mm7 | ||
| 330 | 49 => return error.UnsupportedRegister, // rflags | ||
| 331 | 50...55 => return error.UnsupportedRegister, // es, cs, ss, ds, fs, gs | ||
| 332 | 58...59 => return error.UnsupportedRegister, // fs.base, gs.base | ||
| 333 | 62 => return error.UnsupportedRegister, // tr | ||
| 334 | 63 => return error.UnsupportedRegister, // ldtr | ||
| 335 | 64 => return error.UnsupportedRegister, // mxcsr | ||
| 336 | 65 => return error.UnsupportedRegister, // fcw | ||
| 337 | 66 => return error.UnsupportedRegister, // fsw | ||
| 338 | |||
| 339 | else => return error.InvalidRegister, | ||
| 340 | } | ||
| 341 | } | ||
| 342 | }; | ||
| 343 | |||
| 344 | pub const Arm = struct { | ||
| 345 | /// The numbered general-purpose registers R0 - R15. | ||
| 346 | r: [16]u32, | ||
| 347 | |||
| 348 | pub inline fn current() Arm { | ||
| 349 | var ctx: Arm = undefined; | ||
| 350 | asm volatile ( | ||
| 351 | \\// For compatibility with Thumb, we can't write r13 (sp) or r15 (pc) with stm. | ||
| 352 | \\stm r0, {r0-r12} | ||
| 353 | \\str r13, [r0, #0x34] | ||
| 354 | \\str r14, [r0, #0x38] | ||
| 355 | \\str r15, [r0, #0x3c] | ||
| 356 | : | ||
| 357 | : [r] "{r0}" (&ctx.r), | ||
| 358 | : .{ .memory = true }); | ||
| 359 | return ctx; | ||
| 360 | } | ||
| 361 | |||
| 362 | pub fn dwarfRegisterBytes(ctx: *Arm, register_num: u16) DwarfRegisterError![]u8 { | ||
| 363 | // DWARF for the Arm(r) Architecture § 4.1 "DWARF register names" | ||
| 364 | switch (register_num) { | ||
| 365 | // The order of `Gpr` intentionally matches DWARF's mappings. | ||
| 366 | 0...15 => return @ptrCast(&ctx.r[register_num]), | ||
| 367 | |||
| 368 | 64...95 => return error.UnsupportedRegister, // S0 - S31 | ||
| 369 | 96...103 => return error.UnsupportedRegister, // F0 - F7 | ||
| 370 | 104...111 => return error.UnsupportedRegister, // wCGR0 - wCGR7, or ACC0 - ACC7 | ||
| 371 | 112...127 => return error.UnsupportedRegister, // wR0 - wR15 | ||
| 372 | 128 => return error.UnsupportedRegister, // SPSR | ||
| 373 | 129 => return error.UnsupportedRegister, // SPSR_FIQ | ||
| 374 | 130 => return error.UnsupportedRegister, // SPSR_IRQ | ||
| 375 | 131 => return error.UnsupportedRegister, // SPSR_ABT | ||
| 376 | 132 => return error.UnsupportedRegister, // SPSR_UND | ||
| 377 | 133 => return error.UnsupportedRegister, // SPSR_SVC | ||
| 378 | 143 => return error.UnsupportedRegister, // RA_AUTH_CODE | ||
| 379 | 144...150 => return error.UnsupportedRegister, // R8_USR - R14_USR | ||
| 380 | 151...157 => return error.UnsupportedRegister, // R8_FIQ - R14_FIQ | ||
| 381 | 158...159 => return error.UnsupportedRegister, // R13_IRQ - R14_IRQ | ||
| 382 | 160...161 => return error.UnsupportedRegister, // R13_ABT - R14_ABT | ||
| 383 | 162...163 => return error.UnsupportedRegister, // R13_UND - R14_UND | ||
| 384 | 164...165 => return error.UnsupportedRegister, // R13_SVC - R14_SVC | ||
| 385 | 192...199 => return error.UnsupportedRegister, // wC0 - wC7 | ||
| 386 | 256...287 => return error.UnsupportedRegister, // D0 - D31 | ||
| 387 | 320 => return error.UnsupportedRegister, // TPIDRURO | ||
| 388 | 321 => return error.UnsupportedRegister, // TPIDRURW | ||
| 389 | 322 => return error.UnsupportedRegister, // TPIDPR | ||
| 390 | 323 => return error.UnsupportedRegister, // HTPIDPR | ||
| 391 | 8192...16383 => return error.UnsupportedRegister, // Unspecified vendor co-processor register | ||
| 392 | |||
| 393 | else => return error.InvalidRegister, | ||
| 394 | } | ||
| 395 | } | ||
| 396 | }; | ||
| 397 | |||
| 398 | /// This is an `extern struct` so that inline assembly in `current` can use field offsets. | ||
| 399 | pub const Aarch64 = extern struct { | ||
| 400 | /// The numbered general-purpose registers X0 - X30. | ||
| 401 | x: [31]u64, | ||
| 402 | sp: u64, | ||
| 403 | pc: u64, | ||
| 404 | |||
| 405 | pub inline fn current() Aarch64 { | ||
| 406 | var ctx: Aarch64 = undefined; | ||
| 407 | asm volatile ( | ||
| 408 | \\stp x0, x1, [x0, #0x000] | ||
| 409 | \\stp x2, x3, [x0, #0x010] | ||
| 410 | \\stp x4, x5, [x0, #0x020] | ||
| 411 | \\stp x6, x7, [x0, #0x030] | ||
| 412 | \\stp x8, x9, [x0, #0x040] | ||
| 413 | \\stp x10, x11, [x0, #0x050] | ||
| 414 | \\stp x12, x13, [x0, #0x060] | ||
| 415 | \\stp x14, x15, [x0, #0x070] | ||
| 416 | \\stp x16, x17, [x0, #0x080] | ||
| 417 | \\stp x18, x19, [x0, #0x090] | ||
| 418 | \\stp x20, x21, [x0, #0x0a0] | ||
| 419 | \\stp x22, x23, [x0, #0x0b0] | ||
| 420 | \\stp x24, x25, [x0, #0x0c0] | ||
| 421 | \\stp x26, x27, [x0, #0x0d0] | ||
| 422 | \\stp x28, x29, [x0, #0x0e0] | ||
| 423 | \\str x30, [x0, #0x0f0] | ||
| 424 | \\mov x1, sp | ||
| 425 | \\str x1, [x0, #0x0f8] | ||
| 426 | \\adr x1, . | ||
| 427 | \\str x1, [x0, #0x100] | ||
| 428 | \\ldr x1, [x0, #0x008] // restore saved x1 | ||
| 429 | : | ||
| 430 | : [gprs] "{x0}" (&ctx), | ||
| 431 | : .{ .memory = true }); | ||
| 432 | return ctx; | ||
| 433 | } | ||
| 434 | |||
| 435 | pub fn dwarfRegisterBytes(ctx: *Aarch64, register_num: u16) DwarfRegisterError![]u8 { | ||
| 436 | // DWARF for the Arm(r) 64-bit Architecture (AArch64) § 4.1 "DWARF register names" | ||
| 437 | switch (register_num) { | ||
| 438 | // The order of `Gpr` intentionally matches DWARF's mappings. | ||
| 439 | 0...30 => return @ptrCast(&ctx.x[register_num]), | ||
| 440 | 31 => return @ptrCast(&ctx.sp), | ||
| 441 | 32 => return @ptrCast(&ctx.pc), | ||
| 442 | |||
| 443 | 33 => return error.UnsupportedRegister, // ELF_mode | ||
| 444 | 34 => return error.UnsupportedRegister, // RA_SIGN_STATE | ||
| 445 | 35 => return error.UnsupportedRegister, // TPIDRRO_ELO | ||
| 446 | 36 => return error.UnsupportedRegister, // RPIDR_ELO | ||
| 447 | 37 => return error.UnsupportedRegister, // RPIDR_EL1 | ||
| 448 | 38 => return error.UnsupportedRegister, // RPIDR_EL2 | ||
| 449 | 39 => return error.UnsupportedRegister, // RPIDR_EL3 | ||
| 450 | 46 => return error.UnsupportedRegister, // VG | ||
| 451 | 47 => return error.UnsupportedRegister, // FFR | ||
| 452 | 48...63 => return error.UnsupportedRegister, // P0 - P15 | ||
| 453 | 64...95 => return error.UnsupportedRegister, // V0 - V31 | ||
| 454 | 96...127 => return error.UnsupportedRegister, // Z0 - Z31 | ||
| 455 | |||
| 456 | else => return error.InvalidRegister, | ||
| 457 | } | ||
| 458 | } | ||
| 459 | }; | ||
| 460 | |||
| 461 | const signal_ucontext_t = switch (native_os) { | ||
| 462 | .linux => std.os.linux.ucontext_t, | ||
| 463 | .emscripten => std.os.emscripten.ucontext_t, | ||
| 464 | .freebsd => std.os.freebsd.ucontext_t, | ||
| 465 | .macos, .ios, .tvos, .watchos, .visionos => extern struct { | ||
| 466 | onstack: c_int, | ||
| 467 | sigmask: std.c.sigset_t, | ||
| 468 | stack: std.c.stack_t, | ||
| 469 | link: ?*signal_ucontext_t, | ||
| 470 | mcsize: u64, | ||
| 471 | mcontext: *mcontext_t, | ||
| 472 | const mcontext_t = switch (native_arch) { | ||
| 473 | .aarch64 => extern struct { | ||
| 474 | es: extern struct { | ||
| 475 | far: u64, // Virtual Fault Address | ||
| 476 | esr: u32, // Exception syndrome | ||
| 477 | exception: u32, // Number of arm exception taken | ||
| 478 | }, | ||
| 479 | ss: extern struct { | ||
| 480 | /// General purpose registers | ||
| 481 | regs: [29]u64, | ||
| 482 | /// Frame pointer x29 | ||
| 483 | fp: u64, | ||
| 484 | /// Link register x30 | ||
| 485 | lr: u64, | ||
| 486 | /// Stack pointer x31 | ||
| 487 | sp: u64, | ||
| 488 | /// Program counter | ||
| 489 | pc: u64, | ||
| 490 | /// Current program status register | ||
| 491 | cpsr: u32, | ||
| 492 | __pad: u32, | ||
| 493 | }, | ||
| 494 | ns: extern struct { | ||
| 495 | q: [32]u128, | ||
| 496 | fpsr: u32, | ||
| 497 | fpcr: u32, | ||
| 498 | }, | ||
| 499 | }, | ||
| 500 | .x86_64 => extern struct { | ||
| 501 | es: extern struct { | ||
| 502 | trapno: u16, | ||
| 503 | cpu: u16, | ||
| 504 | err: u32, | ||
| 505 | faultvaddr: u64, | ||
| 506 | }, | ||
| 507 | ss: extern struct { | ||
| 508 | rax: u64, | ||
| 509 | rbx: u64, | ||
| 510 | rcx: u64, | ||
| 511 | rdx: u64, | ||
| 512 | rdi: u64, | ||
| 513 | rsi: u64, | ||
| 514 | rbp: u64, | ||
| 515 | rsp: u64, | ||
| 516 | r8: u64, | ||
| 517 | r9: u64, | ||
| 518 | r10: u64, | ||
| 519 | r11: u64, | ||
| 520 | r12: u64, | ||
| 521 | r13: u64, | ||
| 522 | r14: u64, | ||
| 523 | r15: u64, | ||
| 524 | rip: u64, | ||
| 525 | rflags: u64, | ||
| 526 | cs: u64, | ||
| 527 | fs: u64, | ||
| 528 | gs: u64, | ||
| 529 | }, | ||
| 530 | fs: extern struct { | ||
| 531 | reserved: [2]c_int, | ||
| 532 | fcw: u16, | ||
| 533 | fsw: u16, | ||
| 534 | ftw: u8, | ||
| 535 | rsrv1: u8, | ||
| 536 | fop: u16, | ||
| 537 | ip: u32, | ||
| 538 | cs: u16, | ||
| 539 | rsrv2: u16, | ||
| 540 | dp: u32, | ||
| 541 | ds: u16, | ||
| 542 | rsrv3: u16, | ||
| 543 | mxcsr: u32, | ||
| 544 | mxcsrmask: u32, | ||
| 545 | stmm: [8]stmm_reg, | ||
| 546 | xmm: [16]xmm_reg, | ||
| 547 | rsrv4: [96]u8, | ||
| 548 | reserved1: c_int, | ||
| 549 | |||
| 550 | const stmm_reg = [16]u8; | ||
| 551 | const xmm_reg = [16]u8; | ||
| 552 | }, | ||
| 553 | }, | ||
| 554 | else => void, | ||
| 555 | }; | ||
| 556 | }, | ||
| 557 | .solaris, .illumos => extern struct { | ||
| 558 | flags: u64, | ||
| 559 | link: ?*signal_ucontext_t, | ||
| 560 | sigmask: std.c.sigset_t, | ||
| 561 | stack: std.c.stack_t, | ||
| 562 | mcontext: mcontext_t, | ||
| 563 | brand_data: [3]?*anyopaque, | ||
| 564 | filler: [2]i64, | ||
| 565 | const mcontext_t = extern struct { | ||
| 566 | gregs: [28]u64, | ||
| 567 | fpregs: std.c.fpregset_t, | ||
| 568 | }; | ||
| 569 | }, | ||
| 570 | .openbsd => switch (builtin.cpu.arch) { | ||
| 571 | .x86_64 => extern struct { | ||
| 572 | sc_rdi: c_long, | ||
| 573 | sc_rsi: c_long, | ||
| 574 | sc_rdx: c_long, | ||
| 575 | sc_rcx: c_long, | ||
| 576 | sc_r8: c_long, | ||
| 577 | sc_r9: c_long, | ||
| 578 | sc_r10: c_long, | ||
| 579 | sc_r11: c_long, | ||
| 580 | sc_r12: c_long, | ||
| 581 | sc_r13: c_long, | ||
| 582 | sc_r14: c_long, | ||
| 583 | sc_r15: c_long, | ||
| 584 | sc_rbp: c_long, | ||
| 585 | sc_rbx: c_long, | ||
| 586 | sc_rax: c_long, | ||
| 587 | sc_gs: c_long, | ||
| 588 | sc_fs: c_long, | ||
| 589 | sc_es: c_long, | ||
| 590 | sc_ds: c_long, | ||
| 591 | sc_trapno: c_long, | ||
| 592 | sc_err: c_long, | ||
| 593 | sc_rip: c_long, | ||
| 594 | sc_cs: c_long, | ||
| 595 | sc_rflags: c_long, | ||
| 596 | sc_rsp: c_long, | ||
| 597 | sc_ss: c_long, | ||
| 598 | |||
| 599 | sc_fpstate: *anyopaque, // struct fxsave64 * | ||
| 600 | __sc_unused: c_int, | ||
| 601 | sc_mask: c_int, | ||
| 602 | sc_cookie: c_long, | ||
| 603 | }, | ||
| 604 | .aarch64 => extern struct { | ||
| 605 | __sc_unused: c_int, | ||
| 606 | sc_mask: c_int, | ||
| 607 | sc_sp: c_ulong, | ||
| 608 | sc_lr: c_ulong, | ||
| 609 | sc_elr: c_ulong, | ||
| 610 | sc_spsr: c_ulong, | ||
| 611 | sc_x: [30]c_ulong, | ||
| 612 | sc_cookie: c_long, | ||
| 613 | }, | ||
| 614 | else => void, | ||
| 615 | }, | ||
| 616 | .netbsd => extern struct { | ||
| 617 | flags: u32, | ||
| 618 | link: ?*signal_ucontext_t, | ||
| 619 | sigmask: std.c.sigset_t, | ||
| 620 | stack: std.c.stack_t, | ||
| 621 | mcontext: mcontext_t, | ||
| 622 | __pad: [ | ||
| 623 | switch (builtin.cpu.arch) { | ||
| 624 | .x86 => 4, | ||
| 625 | .mips, .mipsel, .mips64, .mips64el => 14, | ||
| 626 | .arm, .armeb, .thumb, .thumbeb => 1, | ||
| 627 | .sparc, .sparc64 => if (@sizeOf(usize) == 4) 43 else 8, | ||
| 628 | else => 0, | ||
| 629 | } | ||
| 630 | ]u32, | ||
| 631 | const mcontext_t = switch (builtin.cpu.arch) { | ||
| 632 | .aarch64, .aarch64_be => extern struct { | ||
| 633 | gregs: [35]u64, | ||
| 634 | fregs: [528]u8 align(16), | ||
| 635 | spare: [8]u64, | ||
| 636 | }, | ||
| 637 | .x86 => extern struct { | ||
| 638 | gregs: [19]u32, | ||
| 639 | fpregs: [161]u32, | ||
| 640 | mc_tlsbase: u32, | ||
| 641 | }, | ||
| 642 | .x86_64 => extern struct { | ||
| 643 | gregs: [26]u64, | ||
| 644 | mc_tlsbase: u64, | ||
| 645 | fpregs: [512]u8 align(8), | ||
| 646 | }, | ||
| 647 | else => void, | ||
| 648 | }; | ||
| 649 | }, | ||
| 650 | .dragonfly => extern struct { | ||
| 651 | sigmask: std.c.sigset_t, | ||
| 652 | mcontext: mcontext_t, | ||
| 653 | link: ?*signal_ucontext_t, | ||
| 654 | stack: std.c.stack_t, | ||
| 655 | cofunc: ?*fn (?*signal_ucontext_t, ?*anyopaque) void, | ||
| 656 | arg: ?*void, | ||
| 657 | _spare: [4]c_int, | ||
| 658 | const mcontext_t = extern struct { | ||
| 659 | const register_t = isize; | ||
| 660 | onstack: register_t, // XXX - sigcontext compat. | ||
| 661 | rdi: register_t, | ||
| 662 | rsi: register_t, | ||
| 663 | rdx: register_t, | ||
| 664 | rcx: register_t, | ||
| 665 | r8: register_t, | ||
| 666 | r9: register_t, | ||
| 667 | rax: register_t, | ||
| 668 | rbx: register_t, | ||
| 669 | rbp: register_t, | ||
| 670 | r10: register_t, | ||
| 671 | r11: register_t, | ||
| 672 | r12: register_t, | ||
| 673 | r13: register_t, | ||
| 674 | r14: register_t, | ||
| 675 | r15: register_t, | ||
| 676 | xflags: register_t, | ||
| 677 | trapno: register_t, | ||
| 678 | addr: register_t, | ||
| 679 | flags: register_t, | ||
| 680 | err: register_t, | ||
| 681 | rip: register_t, | ||
| 682 | cs: register_t, | ||
| 683 | rflags: register_t, | ||
| 684 | rsp: register_t, // machine state | ||
| 685 | ss: register_t, | ||
| 686 | |||
| 687 | len: c_uint, // sizeof(mcontext_t) | ||
| 688 | fpformat: c_uint, | ||
| 689 | ownedfp: c_uint, | ||
| 690 | reserved: c_uint, | ||
| 691 | unused: [8]c_uint, | ||
| 692 | |||
| 693 | // NOTE! 64-byte aligned as of here. Also must match savefpu structure. | ||
| 694 | fpregs: [256]c_int align(64), | ||
| 695 | }; | ||
| 696 | }, | ||
| 697 | .serenity => extern struct { | ||
| 698 | link: ?*signal_ucontext_t, | ||
| 699 | sigmask: std.c.sigset_t, | ||
| 700 | stack: std.c.stack_t, | ||
| 701 | mcontext: mcontext_t, | ||
| 702 | const mcontext_t = switch (builtin.cpu.arch) { | ||
| 703 | // https://github.com/SerenityOS/serenity/blob/200e91cd7f1ec5453799a2720d4dc114a59cc289/Kernel/Arch/aarch64/mcontext.h#L15-L19 | ||
| 704 | .aarch64 => extern struct { | ||
| 705 | x: [31]u64, | ||
| 706 | sp: u64, | ||
| 707 | pc: u64, | ||
| 708 | }, | ||
| 709 | // https://github.com/SerenityOS/serenity/blob/66f8d0f031ef25c409dbb4fecaa454800fecae0f/Kernel/Arch/riscv64/mcontext.h#L15-L18 | ||
| 710 | .riscv64 => extern struct { | ||
| 711 | x: [31]u64, | ||
| 712 | pc: u64, | ||
| 713 | }, | ||
| 714 | // https://github.com/SerenityOS/serenity/blob/7b9ea3efdec9f86a1042893e8107d0b23aad8727/Kernel/Arch/x86_64/mcontext.h#L15-L40 | ||
| 715 | .x86_64 => extern struct { | ||
| 716 | rax: u64, | ||
| 717 | rcx: u64, | ||
| 718 | rdx: u64, | ||
| 719 | rbx: u64, | ||
| 720 | rsp: u64, | ||
| 721 | rbp: u64, | ||
| 722 | rsi: u64, | ||
| 723 | rdi: u64, | ||
| 724 | rip: u64, | ||
| 725 | r8: u64, | ||
| 726 | r9: u64, | ||
| 727 | r10: u64, | ||
| 728 | r11: u64, | ||
| 729 | r12: u64, | ||
| 730 | r13: u64, | ||
| 731 | r14: u64, | ||
| 732 | r15: u64, | ||
| 733 | rflags: u64, | ||
| 734 | cs: u32, | ||
| 735 | ss: u32, | ||
| 736 | ds: u32, | ||
| 737 | es: u32, | ||
| 738 | fs: u32, | ||
| 739 | gs: u32, | ||
| 740 | }, | ||
| 741 | else => void, | ||
| 742 | }; | ||
| 743 | }, | ||
| 744 | .haiku => extern struct { | ||
| 745 | link: ?*signal_ucontext_t, | ||
| 746 | sigmask: std.c.sigset_t, | ||
| 747 | stack: std.c.stack_t, | ||
| 748 | mcontext: mcontext_t, | ||
| 749 | const mcontext_t = switch (builtin.cpu.arch) { | ||
| 750 | .arm, .thumb => extern struct { | ||
| 751 | r0: u32, | ||
| 752 | r1: u32, | ||
| 753 | r2: u32, | ||
| 754 | r3: u32, | ||
| 755 | r4: u32, | ||
| 756 | r5: u32, | ||
| 757 | r6: u32, | ||
| 758 | r7: u32, | ||
| 759 | r8: u32, | ||
| 760 | r9: u32, | ||
| 761 | r10: u32, | ||
| 762 | r11: u32, | ||
| 763 | r12: u32, | ||
| 764 | r13: u32, | ||
| 765 | r14: u32, | ||
| 766 | r15: u32, | ||
| 767 | cpsr: u32, | ||
| 768 | }, | ||
| 769 | .aarch64 => extern struct { | ||
| 770 | x: [10]u64, | ||
| 771 | lr: u64, | ||
| 772 | sp: u64, | ||
| 773 | elr: u64, | ||
| 774 | spsr: u64, | ||
| 775 | fp_q: [32]u128, | ||
| 776 | fpsr: u32, | ||
| 777 | fpcr: u32, | ||
| 778 | }, | ||
| 779 | .m68k => extern struct { | ||
| 780 | pc: u32, | ||
| 781 | d0: u32, | ||
| 782 | d1: u32, | ||
| 783 | d2: u32, | ||
| 784 | d3: u32, | ||
| 785 | d4: u32, | ||
| 786 | d5: u32, | ||
| 787 | d6: u32, | ||
| 788 | d7: u32, | ||
| 789 | a0: u32, | ||
| 790 | a1: u32, | ||
| 791 | a2: u32, | ||
| 792 | a3: u32, | ||
| 793 | a4: u32, | ||
| 794 | a5: u32, | ||
| 795 | a6: u32, | ||
| 796 | a7: u32, | ||
| 797 | ccr: u8, | ||
| 798 | f0: f64, | ||
| 799 | f1: f64, | ||
| 800 | f2: f64, | ||
| 801 | f3: f64, | ||
| 802 | f4: f64, | ||
| 803 | f5: f64, | ||
| 804 | f6: f64, | ||
| 805 | f7: f64, | ||
| 806 | f8: f64, | ||
| 807 | f9: f64, | ||
| 808 | f10: f64, | ||
| 809 | f11: f64, | ||
| 810 | f12: f64, | ||
| 811 | f13: f64, | ||
| 812 | }, | ||
| 813 | .mipsel => extern struct { | ||
| 814 | r0: u32, | ||
| 815 | }, | ||
| 816 | .powerpc => extern struct { | ||
| 817 | pc: u32, | ||
| 818 | r0: u32, | ||
| 819 | r1: u32, | ||
| 820 | r2: u32, | ||
| 821 | r3: u32, | ||
| 822 | r4: u32, | ||
| 823 | r5: u32, | ||
| 824 | r6: u32, | ||
| 825 | r7: u32, | ||
| 826 | r8: u32, | ||
| 827 | r9: u32, | ||
| 828 | r10: u32, | ||
| 829 | r11: u32, | ||
| 830 | r12: u32, | ||
| 831 | f0: f64, | ||
| 832 | f1: f64, | ||
| 833 | f2: f64, | ||
| 834 | f3: f64, | ||
| 835 | f4: f64, | ||
| 836 | f5: f64, | ||
| 837 | f6: f64, | ||
| 838 | f7: f64, | ||
| 839 | f8: f64, | ||
| 840 | f9: f64, | ||
| 841 | f10: f64, | ||
| 842 | f11: f64, | ||
| 843 | f12: f64, | ||
| 844 | f13: f64, | ||
| 845 | reserved: u32, | ||
| 846 | fpscr: u32, | ||
| 847 | ctr: u32, | ||
| 848 | xer: u32, | ||
| 849 | cr: u32, | ||
| 850 | msr: u32, | ||
| 851 | lr: u32, | ||
| 852 | }, | ||
| 853 | .riscv64 => extern struct { | ||
| 854 | x: [31]u64, | ||
| 855 | pc: u64, | ||
| 856 | f: [32]f64, | ||
| 857 | fcsr: u64, | ||
| 858 | }, | ||
| 859 | .sparc64 => extern struct { | ||
| 860 | g1: u64, | ||
| 861 | g2: u64, | ||
| 862 | g3: u64, | ||
| 863 | g4: u64, | ||
| 864 | g5: u64, | ||
| 865 | g6: u64, | ||
| 866 | g7: u64, | ||
| 867 | o0: u64, | ||
| 868 | o1: u64, | ||
| 869 | o2: u64, | ||
| 870 | o3: u64, | ||
| 871 | o4: u64, | ||
| 872 | o5: u64, | ||
| 873 | sp: u64, | ||
| 874 | o7: u64, | ||
| 875 | l0: u64, | ||
| 876 | l1: u64, | ||
| 877 | l2: u64, | ||
| 878 | l3: u64, | ||
| 879 | l4: u64, | ||
| 880 | l5: u64, | ||
| 881 | l6: u64, | ||
| 882 | l7: u64, | ||
| 883 | i0: u64, | ||
| 884 | i1: u64, | ||
| 885 | i2: u64, | ||
| 886 | i3: u64, | ||
| 887 | i4: u64, | ||
| 888 | i5: u64, | ||
| 889 | fp: u64, | ||
| 890 | i7: u64, | ||
| 891 | }, | ||
| 892 | .x86 => extern struct { | ||
| 893 | pub const old_extended_regs = extern struct { | ||
| 894 | control: u16, | ||
| 895 | reserved1: u16, | ||
| 896 | status: u16, | ||
| 897 | reserved2: u16, | ||
| 898 | tag: u16, | ||
| 899 | reserved3: u16, | ||
| 900 | eip: u32, | ||
| 901 | cs: u16, | ||
| 902 | opcode: u16, | ||
| 903 | datap: u32, | ||
| 904 | ds: u16, | ||
| 905 | reserved4: u16, | ||
| 906 | fp_mmx: [8][10]u8, | ||
| 907 | }; | ||
| 908 | |||
| 909 | pub const fp_register = extern struct { value: [10]u8, reserved: [6]u8 }; | ||
| 910 | |||
| 911 | pub const xmm_register = extern struct { value: [16]u8 }; | ||
| 912 | |||
| 913 | pub const new_extended_regs = extern struct { | ||
| 914 | control: u16, | ||
| 915 | status: u16, | ||
| 916 | tag: u16, | ||
| 917 | opcode: u16, | ||
| 918 | eip: u32, | ||
| 919 | cs: u16, | ||
| 920 | reserved1: u16, | ||
| 921 | datap: u32, | ||
| 922 | ds: u16, | ||
| 923 | reserved2: u16, | ||
| 924 | mxcsr: u32, | ||
| 925 | reserved3: u32, | ||
| 926 | fp_mmx: [8]fp_register, | ||
| 927 | xmmx: [8]xmm_register, | ||
| 928 | reserved4: [224]u8, | ||
| 929 | }; | ||
| 930 | |||
| 931 | pub const extended_regs = extern struct { | ||
| 932 | state: extern union { | ||
| 933 | old_format: old_extended_regs, | ||
| 934 | new_format: new_extended_regs, | ||
| 935 | }, | ||
| 936 | format: u32, | ||
| 937 | }; | ||
| 938 | |||
| 939 | eip: u32, | ||
| 940 | eflags: u32, | ||
| 941 | eax: u32, | ||
| 942 | ecx: u32, | ||
| 943 | edx: u32, | ||
| 944 | esp: u32, | ||
| 945 | ebp: u32, | ||
| 946 | reserved: u32, | ||
| 947 | xregs: extended_regs, | ||
| 948 | edi: u32, | ||
| 949 | esi: u32, | ||
| 950 | ebx: u32, | ||
| 951 | }, | ||
| 952 | .x86_64 => extern struct { | ||
| 953 | pub const fp_register = extern struct { | ||
| 954 | value: [10]u8, | ||
| 955 | reserved: [6]u8, | ||
| 956 | }; | ||
| 957 | |||
| 958 | pub const xmm_register = extern struct { | ||
| 959 | value: [16]u8, | ||
| 960 | }; | ||
| 961 | |||
| 962 | pub const fpu_state = extern struct { | ||
| 963 | control: u16, | ||
| 964 | status: u16, | ||
| 965 | tag: u16, | ||
| 966 | opcode: u16, | ||
| 967 | rip: u64, | ||
| 968 | rdp: u64, | ||
| 969 | mxcsr: u32, | ||
| 970 | mscsr_mask: u32, | ||
| 971 | |||
| 972 | fp_mmx: [8]fp_register, | ||
| 973 | xmm: [16]xmm_register, | ||
| 974 | reserved: [96]u8, | ||
| 975 | }; | ||
| 976 | |||
| 977 | pub const xstate_hdr = extern struct { | ||
| 978 | bv: u64, | ||
| 979 | xcomp_bv: u64, | ||
| 980 | reserved: [48]u8, | ||
| 981 | }; | ||
| 982 | |||
| 983 | pub const savefpu = extern struct { | ||
| 984 | fxsave: fpu_state, | ||
| 985 | xstate: xstate_hdr, | ||
| 986 | ymm: [16]xmm_register, | ||
| 987 | }; | ||
| 988 | |||
| 989 | rax: u64, | ||
| 990 | rbx: u64, | ||
| 991 | rcx: u64, | ||
| 992 | rdx: u64, | ||
| 993 | rdi: u64, | ||
| 994 | rsi: u64, | ||
| 995 | rbp: u64, | ||
| 996 | r8: u64, | ||
| 997 | r9: u64, | ||
| 998 | r10: u64, | ||
| 999 | r11: u64, | ||
| 1000 | r12: u64, | ||
| 1001 | r13: u64, | ||
| 1002 | r14: u64, | ||
| 1003 | r15: u64, | ||
| 1004 | rsp: u64, | ||
| 1005 | rip: u64, | ||
| 1006 | rflags: u64, | ||
| 1007 | fpu: savefpu, | ||
| 1008 | }, | ||
| 1009 | else => void, | ||
| 1010 | }; | ||
| 1011 | }, | ||
| 1012 | else => void, | ||
| 1013 | }; | ||
| 1014 | |||
| 1015 | const std = @import("../std.zig"); | ||
| 1016 | const root = @import("root"); | ||
| 1017 | const builtin = @import("builtin"); | ||
| 1018 | const native_arch = @import("builtin").target.cpu.arch; | ||
| 1019 | const native_os = @import("builtin").target.os.tag; | ||
lib/std/heap/PageAllocator.zig+1-1| ... | @@ -183,7 +183,7 @@ pub fn realloc(uncasted_memory: []u8, new_len: usize, may_move: bool) ?[*]u8 { | ... | @@ -183,7 +183,7 @@ pub fn realloc(uncasted_memory: []u8, new_len: usize, may_move: bool) ?[*]u8 { |
| 183 | 183 | ||
| 184 | if (posix.MREMAP != void) { | 184 | if (posix.MREMAP != void) { |
| 185 | // TODO: if the next_mmap_addr_hint is within the remapped range, update it | 185 | // TODO: if the next_mmap_addr_hint is within the remapped range, update it |
| 186 | const new_memory = posix.mremap(memory.ptr, memory.len, new_len, .{ .MAYMOVE = may_move }, null) catch return null; | 186 | const new_memory = posix.mremap(memory.ptr, page_aligned_len, new_size_aligned, .{ .MAYMOVE = may_move }, null) catch return null; |
| 187 | return new_memory.ptr; | 187 | return new_memory.ptr; |
| 188 | } | 188 | } |
| 189 | 189 |
lib/std/os/freebsd.zig+73| ... | @@ -3,6 +3,7 @@ const fd_t = std.c.fd_t; | ... | @@ -3,6 +3,7 @@ const fd_t = std.c.fd_t; |
| 3 | const off_t = std.c.off_t; | 3 | const off_t = std.c.off_t; |
| 4 | const unexpectedErrno = std.posix.unexpectedErrno; | 4 | const unexpectedErrno = std.posix.unexpectedErrno; |
| 5 | const errno = std.posix.errno; | 5 | const errno = std.posix.errno; |
| 6 | const builtin = @import("builtin"); | ||
| 6 | 7 | ||
| 7 | pub const CopyFileRangeError = std.posix.UnexpectedError || error{ | 8 | pub const CopyFileRangeError = std.posix.UnexpectedError || error{ |
| 8 | /// If infd is not open for reading or outfd is not open for writing, or | 9 | /// If infd is not open for reading or outfd is not open for writing, or |
| ... | @@ -47,3 +48,75 @@ pub fn copy_file_range(fd_in: fd_t, off_in: ?*i64, fd_out: fd_t, off_out: ?*i64, | ... | @@ -47,3 +48,75 @@ pub fn copy_file_range(fd_in: fd_t, off_in: ?*i64, fd_out: fd_t, off_out: ?*i64, |
| 47 | else => |err| return unexpectedErrno(err), | 48 | else => |err| return unexpectedErrno(err), |
| 48 | } | 49 | } |
| 49 | } | 50 | } |
| 51 | |||
| 52 | pub const ucontext_t = extern struct { | ||
| 53 | sigmask: std.c.sigset_t, | ||
| 54 | mcontext: mcontext_t, | ||
| 55 | link: ?*ucontext_t, | ||
| 56 | stack: std.c.stack_t, | ||
| 57 | flags: c_int, | ||
| 58 | __spare__: [4]c_int, | ||
| 59 | const mcontext_t = switch (builtin.cpu.arch) { | ||
| 60 | .x86_64 => extern struct { | ||
| 61 | onstack: u64, | ||
| 62 | rdi: u64, | ||
| 63 | rsi: u64, | ||
| 64 | rdx: u64, | ||
| 65 | rcx: u64, | ||
| 66 | r8: u64, | ||
| 67 | r9: u64, | ||
| 68 | rax: u64, | ||
| 69 | rbx: u64, | ||
| 70 | rbp: u64, | ||
| 71 | r10: u64, | ||
| 72 | r11: u64, | ||
| 73 | r12: u64, | ||
| 74 | r13: u64, | ||
| 75 | r14: u64, | ||
| 76 | r15: u64, | ||
| 77 | trapno: u32, | ||
| 78 | fs: u16, | ||
| 79 | gs: u16, | ||
| 80 | addr: u64, | ||
| 81 | flags: u32, | ||
| 82 | es: u16, | ||
| 83 | ds: u16, | ||
| 84 | err: u64, | ||
| 85 | rip: u64, | ||
| 86 | cs: u64, | ||
| 87 | rflags: u64, | ||
| 88 | rsp: u64, | ||
| 89 | ss: u64, | ||
| 90 | len: u64, | ||
| 91 | fpformat: u64, | ||
| 92 | ownedfp: u64, | ||
| 93 | fpstate: [64]u64 align(16), | ||
| 94 | fsbase: u64, | ||
| 95 | gsbase: u64, | ||
| 96 | xfpustate: u64, | ||
| 97 | xfpustate_len: u64, | ||
| 98 | spare: [4]u64, | ||
| 99 | }, | ||
| 100 | .aarch64 => extern struct { | ||
| 101 | gpregs: extern struct { | ||
| 102 | x: [30]u64, | ||
| 103 | lr: u64, | ||
| 104 | sp: u64, | ||
| 105 | elr: u64, | ||
| 106 | spsr: u32, | ||
| 107 | _pad: u32, | ||
| 108 | }, | ||
| 109 | fpregs: extern struct { | ||
| 110 | q: [32]u128, | ||
| 111 | sr: u32, | ||
| 112 | cr: u32, | ||
| 113 | flags: u32, | ||
| 114 | _pad: u32, | ||
| 115 | }, | ||
| 116 | flags: u32, | ||
| 117 | _pad: u32, | ||
| 118 | _spare: [8]u64, | ||
| 119 | }, | ||
| 120 | else => void, | ||
| 121 | }; | ||
| 122 | }; |
lib/std/os/linux.zig-2| ... | @@ -49,7 +49,6 @@ const arch_bits = switch (native_arch) { | ... | @@ -49,7 +49,6 @@ const arch_bits = switch (native_arch) { |
| 49 | .s390x => @import("linux/s390x.zig"), | 49 | .s390x => @import("linux/s390x.zig"), |
| 50 | else => struct { | 50 | else => struct { |
| 51 | pub const ucontext_t = void; | 51 | pub const ucontext_t = void; |
| 52 | pub const getcontext = {}; | ||
| 53 | }, | 52 | }, |
| 54 | }; | 53 | }; |
| 55 | 54 | ||
| ... | @@ -112,7 +111,6 @@ pub const timeval = arch_bits.timeval; | ... | @@ -112,7 +111,6 @@ pub const timeval = arch_bits.timeval; |
| 112 | pub const timezone = arch_bits.timezone; | 111 | pub const timezone = arch_bits.timezone; |
| 113 | pub const ucontext_t = arch_bits.ucontext_t; | 112 | pub const ucontext_t = arch_bits.ucontext_t; |
| 114 | pub const user_desc = arch_bits.user_desc; | 113 | pub const user_desc = arch_bits.user_desc; |
| 115 | pub const getcontext = arch_bits.getcontext; | ||
| 116 | 114 | ||
| 117 | pub const tls = @import("linux/tls.zig"); | 115 | pub const tls = @import("linux/tls.zig"); |
| 118 | pub const BPF = @import("linux/bpf.zig"); | 116 | pub const BPF = @import("linux/bpf.zig"); |
lib/std/os/linux/aarch64.zig-3| ... | @@ -260,7 +260,4 @@ pub const ucontext_t = extern struct { | ... | @@ -260,7 +260,4 @@ pub const ucontext_t = extern struct { |
| 260 | mcontext: mcontext_t, | 260 | mcontext: mcontext_t, |
| 261 | }; | 261 | }; |
| 262 | 262 | ||
| 263 | /// TODO | ||
| 264 | pub const getcontext = {}; | ||
| 265 | |||
| 266 | pub const Elf_Symndx = u32; | 263 | pub const Elf_Symndx = u32; |
lib/std/os/linux/arm.zig-3| ... | @@ -310,7 +310,4 @@ pub const ucontext_t = extern struct { | ... | @@ -310,7 +310,4 @@ pub const ucontext_t = extern struct { |
| 310 | regspace: [64]u64, | 310 | regspace: [64]u64, |
| 311 | }; | 311 | }; |
| 312 | 312 | ||
| 313 | /// TODO | ||
| 314 | pub const getcontext = {}; | ||
| 315 | |||
| 316 | pub const Elf_Symndx = u32; | 313 | pub const Elf_Symndx = u32; |
lib/std/os/linux/hexagon.zig-3| ... | @@ -237,6 +237,3 @@ pub const VDSO = void; | ... | @@ -237,6 +237,3 @@ pub const VDSO = void; |
| 237 | 237 | ||
| 238 | /// TODO | 238 | /// TODO |
| 239 | pub const ucontext_t = void; | 239 | pub const ucontext_t = void; |
| 240 | |||
| 241 | /// TODO | ||
| 242 | pub const getcontext = {}; |
lib/std/os/linux/loongarch64.zig-3| ... | @@ -250,6 +250,3 @@ pub const ucontext_t = extern struct { | ... | @@ -250,6 +250,3 @@ pub const ucontext_t = extern struct { |
| 250 | }; | 250 | }; |
| 251 | 251 | ||
| 252 | pub const Elf_Symndx = u32; | 252 | pub const Elf_Symndx = u32; |
| 253 | |||
| 254 | /// TODO | ||
| 255 | pub const getcontext = {}; |
lib/std/os/linux/m68k.zig-3| ... | @@ -258,6 +258,3 @@ pub const VDSO = void; | ... | @@ -258,6 +258,3 @@ pub const VDSO = void; |
| 258 | 258 | ||
| 259 | /// TODO | 259 | /// TODO |
| 260 | pub const ucontext_t = void; | 260 | pub const ucontext_t = void; |
| 261 | |||
| 262 | /// TODO | ||
| 263 | pub const getcontext = {}; |
lib/std/os/linux/mips.zig-3| ... | @@ -349,6 +349,3 @@ pub const Elf_Symndx = u32; | ... | @@ -349,6 +349,3 @@ pub const Elf_Symndx = u32; |
| 349 | 349 | ||
| 350 | /// TODO | 350 | /// TODO |
| 351 | pub const ucontext_t = void; | 351 | pub const ucontext_t = void; |
| 352 | |||
| 353 | /// TODO | ||
| 354 | pub const getcontext = {}; |
lib/std/os/linux/mips64.zig-3| ... | @@ -328,6 +328,3 @@ pub const Elf_Symndx = u32; | ... | @@ -328,6 +328,3 @@ pub const Elf_Symndx = u32; |
| 328 | 328 | ||
| 329 | /// TODO | 329 | /// TODO |
| 330 | pub const ucontext_t = void; | 330 | pub const ucontext_t = void; |
| 331 | |||
| 332 | /// TODO | ||
| 333 | pub const getcontext = {}; |
lib/std/os/linux/powerpc.zig-3| ... | @@ -381,6 +381,3 @@ pub const ucontext_t = extern struct { | ... | @@ -381,6 +381,3 @@ pub const ucontext_t = extern struct { |
| 381 | }; | 381 | }; |
| 382 | 382 | ||
| 383 | pub const Elf_Symndx = u32; | 383 | pub const Elf_Symndx = u32; |
| 384 | |||
| 385 | /// TODO | ||
| 386 | pub const getcontext = {}; |
lib/std/os/linux/powerpc64.zig-3| ... | @@ -376,6 +376,3 @@ pub const ucontext_t = extern struct { | ... | @@ -376,6 +376,3 @@ pub const ucontext_t = extern struct { |
| 376 | }; | 376 | }; |
| 377 | 377 | ||
| 378 | pub const Elf_Symndx = u32; | 378 | pub const Elf_Symndx = u32; |
| 379 | |||
| 380 | /// TODO | ||
| 381 | pub const getcontext = {}; |
lib/std/os/linux/riscv32.zig-3| ... | @@ -255,6 +255,3 @@ pub const ucontext_t = extern struct { | ... | @@ -255,6 +255,3 @@ pub const ucontext_t = extern struct { |
| 255 | sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask | 255 | sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask |
| 256 | mcontext: mcontext_t, | 256 | mcontext: mcontext_t, |
| 257 | }; | 257 | }; |
| 258 | |||
| 259 | /// TODO | ||
| 260 | pub const getcontext = {}; |
lib/std/os/linux/riscv64.zig-3| ... | @@ -255,6 +255,3 @@ pub const ucontext_t = extern struct { | ... | @@ -255,6 +255,3 @@ pub const ucontext_t = extern struct { |
| 255 | sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask | 255 | sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask |
| 256 | mcontext: mcontext_t, | 256 | mcontext: mcontext_t, |
| 257 | }; | 257 | }; |
| 258 | |||
| 259 | /// TODO | ||
| 260 | pub const getcontext = {}; |
lib/std/os/linux/s390x.zig-3| ... | @@ -273,6 +273,3 @@ pub const mcontext_t = extern struct { | ... | @@ -273,6 +273,3 @@ pub const mcontext_t = extern struct { |
| 273 | __regs2: [18]u32, | 273 | __regs2: [18]u32, |
| 274 | __regs3: [16]f64, | 274 | __regs3: [16]f64, |
| 275 | }; | 275 | }; |
| 276 | |||
| 277 | /// TODO | ||
| 278 | pub const getcontext = {}; |
lib/std/os/linux/sparc64.zig-3| ... | @@ -426,6 +426,3 @@ pub const ucontext_t = extern struct { | ... | @@ -426,6 +426,3 @@ pub const ucontext_t = extern struct { |
| 426 | stack: stack_t, | 426 | stack: stack_t, |
| 427 | sigset: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask | 427 | sigset: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask |
| 428 | }; | 428 | }; |
| 429 | |||
| 430 | /// TODO | ||
| 431 | pub const getcontext = {}; |
lib/std/os/linux/x86.zig-14| ... | @@ -436,17 +436,3 @@ pub fn getContextInternal() callconv(.naked) usize { | ... | @@ -436,17 +436,3 @@ pub fn getContextInternal() callconv(.naked) usize { |
| 436 | [sigset_size] "i" (linux.NSIG / 8), | 436 | [sigset_size] "i" (linux.NSIG / 8), |
| 437 | : .{ .cc = true, .memory = true, .eax = true, .ecx = true, .edx = true }); | 437 | : .{ .cc = true, .memory = true, .eax = true, .ecx = true, .edx = true }); |
| 438 | } | 438 | } |
| 439 | |||
| 440 | pub inline fn getcontext(context: *ucontext_t) usize { | ||
| 441 | // This method is used so that getContextInternal can control | ||
| 442 | // its prologue in order to read ESP from a constant offset. | ||
| 443 | // An aligned stack is not needed for getContextInternal. | ||
| 444 | var clobber_edx: usize = undefined; | ||
| 445 | return asm volatile ( | ||
| 446 | \\ calll %[getContextInternal:P] | ||
| 447 | : [_] "={eax}" (-> usize), | ||
| 448 | [_] "={edx}" (clobber_edx), | ||
| 449 | : [_] "{edx}" (context), | ||
| 450 | [getContextInternal] "X" (&getContextInternal), | ||
| 451 | : .{ .cc = true, .memory = true, .ecx = true }); | ||
| 452 | } |
lib/std/os/linux/x86_64.zig-95| ... | @@ -352,98 +352,3 @@ pub const ucontext_t = extern struct { | ... | @@ -352,98 +352,3 @@ pub const ucontext_t = extern struct { |
| 352 | sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a glibc-compatible (1024-bit) sigmask. | 352 | sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a glibc-compatible (1024-bit) sigmask. |
| 353 | fpregs_mem: [64]usize, // Not part of kernel ABI, only part of glibc ucontext_t | 353 | fpregs_mem: [64]usize, // Not part of kernel ABI, only part of glibc ucontext_t |
| 354 | }; | 354 | }; |
| 355 | |||
| 356 | fn gpRegisterOffset(comptime reg_index: comptime_int) usize { | ||
| 357 | return @offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "gregs") + @sizeOf(usize) * reg_index; | ||
| 358 | } | ||
| 359 | |||
| 360 | fn getContextInternal() callconv(.naked) usize { | ||
| 361 | // TODO: Read GS/FS registers? | ||
| 362 | asm volatile ( | ||
| 363 | \\ movq $0, %[flags_offset:c](%%rdi) | ||
| 364 | \\ movq $0, %[link_offset:c](%%rdi) | ||
| 365 | \\ movq %%r8, %[r8_offset:c](%%rdi) | ||
| 366 | \\ movq %%r9, %[r9_offset:c](%%rdi) | ||
| 367 | \\ movq %%r10, %[r10_offset:c](%%rdi) | ||
| 368 | \\ movq %%r11, %[r11_offset:c](%%rdi) | ||
| 369 | \\ movq %%r12, %[r12_offset:c](%%rdi) | ||
| 370 | \\ movq %%r13, %[r13_offset:c](%%rdi) | ||
| 371 | \\ movq %%r14, %[r14_offset:c](%%rdi) | ||
| 372 | \\ movq %%r15, %[r15_offset:c](%%rdi) | ||
| 373 | \\ movq %%rdi, %[rdi_offset:c](%%rdi) | ||
| 374 | \\ movq %%rsi, %[rsi_offset:c](%%rdi) | ||
| 375 | \\ movq %%rbp, %[rbp_offset:c](%%rdi) | ||
| 376 | \\ movq %%rbx, %[rbx_offset:c](%%rdi) | ||
| 377 | \\ movq %%rdx, %[rdx_offset:c](%%rdi) | ||
| 378 | \\ movq %%rax, %[rax_offset:c](%%rdi) | ||
| 379 | \\ movq %%rcx, %[rcx_offset:c](%%rdi) | ||
| 380 | \\ movq (%%rsp), %%rcx | ||
| 381 | \\ movq %%rcx, %[rip_offset:c](%%rdi) | ||
| 382 | \\ leaq 8(%%rsp), %%rcx | ||
| 383 | \\ movq %%rcx, %[rsp_offset:c](%%rdi) | ||
| 384 | \\ pushfq | ||
| 385 | \\ popq %[efl_offset:c](%%rdi) | ||
| 386 | \\ leaq %[fpmem_offset:c](%%rdi), %%rcx | ||
| 387 | \\ movq %%rcx, %[fpstate_offset:c](%%rdi) | ||
| 388 | \\ fnstenv (%%rcx) | ||
| 389 | \\ fldenv (%%rcx) | ||
| 390 | \\ stmxcsr %[mxcsr_offset:c](%%rdi) | ||
| 391 | \\ leaq %[stack_offset:c](%%rdi), %%rsi | ||
| 392 | \\ movq %%rdi, %%r8 | ||
| 393 | \\ xorl %%edi, %%edi | ||
| 394 | \\ movl %[sigaltstack], %%eax | ||
| 395 | \\ syscall | ||
| 396 | \\ testq %%rax, %%rax | ||
| 397 | \\ jnz 0f | ||
| 398 | \\ movl %[sigprocmask], %%eax | ||
| 399 | \\ xorl %%esi, %%esi | ||
| 400 | \\ leaq %[sigmask_offset:c](%%r8), %%rdx | ||
| 401 | \\ movl %[sigset_size], %%r10d | ||
| 402 | \\ syscall | ||
| 403 | \\0: | ||
| 404 | \\ retq | ||
| 405 | : | ||
| 406 | : [flags_offset] "i" (@offsetOf(ucontext_t, "flags")), | ||
| 407 | [link_offset] "i" (@offsetOf(ucontext_t, "link")), | ||
| 408 | [r8_offset] "i" (comptime gpRegisterOffset(REG.R8)), | ||
| 409 | [r9_offset] "i" (comptime gpRegisterOffset(REG.R9)), | ||
| 410 | [r10_offset] "i" (comptime gpRegisterOffset(REG.R10)), | ||
| 411 | [r11_offset] "i" (comptime gpRegisterOffset(REG.R11)), | ||
| 412 | [r12_offset] "i" (comptime gpRegisterOffset(REG.R12)), | ||
| 413 | [r13_offset] "i" (comptime gpRegisterOffset(REG.R13)), | ||
| 414 | [r14_offset] "i" (comptime gpRegisterOffset(REG.R14)), | ||
| 415 | [r15_offset] "i" (comptime gpRegisterOffset(REG.R15)), | ||
| 416 | [rdi_offset] "i" (comptime gpRegisterOffset(REG.RDI)), | ||
| 417 | [rsi_offset] "i" (comptime gpRegisterOffset(REG.RSI)), | ||
| 418 | [rbp_offset] "i" (comptime gpRegisterOffset(REG.RBP)), | ||
| 419 | [rbx_offset] "i" (comptime gpRegisterOffset(REG.RBX)), | ||
| 420 | [rdx_offset] "i" (comptime gpRegisterOffset(REG.RDX)), | ||
| 421 | [rax_offset] "i" (comptime gpRegisterOffset(REG.RAX)), | ||
| 422 | [rcx_offset] "i" (comptime gpRegisterOffset(REG.RCX)), | ||
| 423 | [rsp_offset] "i" (comptime gpRegisterOffset(REG.RSP)), | ||
| 424 | [rip_offset] "i" (comptime gpRegisterOffset(REG.RIP)), | ||
| 425 | [efl_offset] "i" (comptime gpRegisterOffset(REG.EFL)), | ||
| 426 | [fpstate_offset] "i" (@offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "fpregs")), | ||
| 427 | [fpmem_offset] "i" (@offsetOf(ucontext_t, "fpregs_mem")), | ||
| 428 | [mxcsr_offset] "i" (@offsetOf(ucontext_t, "fpregs_mem") + @offsetOf(fpstate, "mxcsr")), | ||
| 429 | [sigaltstack] "i" (@intFromEnum(linux.SYS.sigaltstack)), | ||
| 430 | [stack_offset] "i" (@offsetOf(ucontext_t, "stack")), | ||
| 431 | [sigprocmask] "i" (@intFromEnum(linux.SYS.rt_sigprocmask)), | ||
| 432 | [sigmask_offset] "i" (@offsetOf(ucontext_t, "sigmask")), | ||
| 433 | [sigset_size] "i" (@sizeOf(sigset_t)), | ||
| 434 | : .{ .cc = true, .memory = true, .rax = true, .rcx = true, .rdx = true, .rdi = true, .rsi = true, .r8 = true, .r10 = true, .r11 = true }); | ||
| 435 | } | ||
| 436 | |||
| 437 | pub inline fn getcontext(context: *ucontext_t) usize { | ||
| 438 | // This method is used so that getContextInternal can control | ||
| 439 | // its prologue in order to read RSP from a constant offset | ||
| 440 | // An aligned stack is not needed for getContextInternal. | ||
| 441 | var clobber_rdi: usize = undefined; | ||
| 442 | return asm volatile ( | ||
| 443 | \\ callq %[getContextInternal:P] | ||
| 444 | : [_] "={rax}" (-> usize), | ||
| 445 | [_] "={rdi}" (clobber_rdi), | ||
| 446 | : [_] "{rdi}" (context), | ||
| 447 | [getContextInternal] "X" (&getContextInternal), | ||
| 448 | : .{ .cc = true, .memory = true, .rcx = true, .rdx = true, .rsi = true, .r8 = true, .r10 = true, .r11 = true }); | ||
| 449 | } |
lib/std/posix.zig-4| ... | @@ -47,8 +47,6 @@ else switch (native_os) { | ... | @@ -47,8 +47,6 @@ else switch (native_os) { |
| 47 | .linux => linux, | 47 | .linux => linux, |
| 48 | .plan9 => std.os.plan9, | 48 | .plan9 => std.os.plan9, |
| 49 | else => struct { | 49 | else => struct { |
| 50 | pub const getcontext = {}; | ||
| 51 | pub const ucontext_t = void; | ||
| 52 | pub const pid_t = void; | 50 | pub const pid_t = void; |
| 53 | pub const pollfd = void; | 51 | pub const pollfd = void; |
| 54 | pub const fd_t = void; | 52 | pub const fd_t = void; |
| ... | @@ -142,7 +140,6 @@ pub const in_pktinfo = system.in_pktinfo; | ... | @@ -142,7 +140,6 @@ pub const in_pktinfo = system.in_pktinfo; |
| 142 | pub const in6_pktinfo = system.in6_pktinfo; | 140 | pub const in6_pktinfo = system.in6_pktinfo; |
| 143 | pub const ino_t = system.ino_t; | 141 | pub const ino_t = system.ino_t; |
| 144 | pub const linger = system.linger; | 142 | pub const linger = system.linger; |
| 145 | pub const mcontext_t = system.mcontext_t; | ||
| 146 | pub const mode_t = system.mode_t; | 143 | pub const mode_t = system.mode_t; |
| 147 | pub const msghdr = system.msghdr; | 144 | pub const msghdr = system.msghdr; |
| 148 | pub const msghdr_const = system.msghdr_const; | 145 | pub const msghdr_const = system.msghdr_const; |
| ... | @@ -171,7 +168,6 @@ pub const timespec = system.timespec; | ... | @@ -171,7 +168,6 @@ pub const timespec = system.timespec; |
| 171 | pub const timestamp_t = system.timestamp_t; | 168 | pub const timestamp_t = system.timestamp_t; |
| 172 | pub const timeval = system.timeval; | 169 | pub const timeval = system.timeval; |
| 173 | pub const timezone = system.timezone; | 170 | pub const timezone = system.timezone; |
| 174 | pub const ucontext_t = system.ucontext_t; | ||
| 175 | pub const uid_t = system.uid_t; | 171 | pub const uid_t = system.uid_t; |
| 176 | pub const user_desc = system.user_desc; | 172 | pub const user_desc = system.user_desc; |
| 177 | pub const utsname = system.utsname; | 173 | pub const utsname = system.utsname; |