| 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 | 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 | 7038 | pub const user_desc = switch (native_os) { |
| 7238 | 7039 | .linux => linux.user_desc, |
| 7239 | 7040 | else => void, |
| ... | ... | @@ -11238,13 +11039,6 @@ pub const LC = enum(c_int) { |
| 11238 | 11039 | |
| 11239 | 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 | 11042 | pub const max_align_t = if (native_abi == .msvc or native_abi == .itanium) |
| 11249 | 11043 | f64 |
| 11250 | 11044 | else if (native_os.isDarwin()) |
| ... | ... | @@ -11668,7 +11462,6 @@ const private = struct { |
| 11668 | 11462 | extern "c" fn shm_open(name: [*:0]const u8, flag: c_int, mode: mode_t) c_int; |
| 11669 | 11463 | |
| 11670 | 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 | 11466 | extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize; |
| 11674 | 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 | 348 | |
| 349 | 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 | 351 | pub extern "c" fn NSVersionOfRunTimeLibrary(library_name: [*:0]const u8) u32; |
| 453 | 352 | pub extern "c" fn _NSGetExecutablePath(buf: [*:0]u8, bufsize: *u32) c_int; |
| 454 | 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 | 13 | pub extern "c" fn umtx_sleep(ptr: *const volatile c_int, value: c_int, timeout: c_int) c_int; |
| 14 | 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 | 16 | pub const E = enum(u16) { |
| 57 | 17 | /// No error occurred. |
| 58 | 18 | SUCCESS = 0, |
lib/std/c/haiku.zig-263| ... | ... | @@ -273,269 +273,6 @@ pub const E = enum(i32) { |
| 273 | 273 | |
| 274 | 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 | 276 | pub const DirEnt = extern struct { |
| 540 | 277 | /// device |
| 541 | 278 | dev: dev_t, |
lib/std/c/openbsd.zig-47| ... | ... | @@ -144,53 +144,6 @@ pub const TCIO = enum(u32) { |
| 144 | 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 | 147 | pub const E = enum(u16) { |
| 195 | 148 | /// No error occurred. |
| 196 | 149 | SUCCESS = 0, |
lib/std/debug.zig+22-114| ... | ... | @@ -22,6 +22,7 @@ pub const ElfFile = @import("debug/ElfFile.zig"); |
| 22 | 22 | pub const SelfInfo = @import("debug/SelfInfo.zig"); |
| 23 | 23 | pub const Info = @import("debug/Info.zig"); |
| 24 | 24 | pub const Coverage = @import("debug/Coverage.zig"); |
| 25 | pub const cpu_context = @import("debug/cpu_context.zig"); | |
| 25 | 26 | |
| 26 | 27 | pub const simple_panic = @import("debug/simple_panic.zig"); |
| 27 | 28 | pub const no_panic = @import("debug/no_panic.zig"); |
| ... | ... | @@ -331,66 +332,8 @@ test dumpHexFallible { |
| 331 | 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 | /// information about the stack. This is not safe to trivially copy, because some platforms | |
| 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 | } | |
| 335 | /// The pointer through which a `cpu_context.Native` is received from callers of stack tracing logic. | |
| 336 | pub const CpuContextPtr = if (cpu_context.Native == noreturn) noreturn else *const cpu_context.Native; | |
| 394 | 337 | |
| 395 | 338 | /// Invokes detectable illegal behavior when `ok` is `false`. |
| 396 | 339 | /// |
| ... | ... | @@ -616,10 +559,10 @@ pub const StackUnwindOptions = struct { |
| 616 | 559 | /// used to omit intermediate handling code (for instance, a panic handler and its machinery) |
| 617 | 560 | /// from stack traces. |
| 618 | 561 | first_address: ?usize = null, |
| 619 | /// If not `null`, we will unwind from this `ThreadContext` instead of the current top of the | |
| 620 | /// 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. | |
| 622 | context: ?ThreadContextPtr = null, | |
| 562 | /// If not `null`, we will unwind from this `cpu_context.Native` instead of the current top of | |
| 563 | /// the stack. The main use case here is printing stack traces from signal handlers, where the | |
| 564 | /// kernel provides a `*const cpu_context.Native` of the state before the signal. | |
| 565 | context: ?CpuContextPtr = null, | |
| 623 | 566 | /// If `true`, stack unwinding strategies which may cause crashes are used as a last resort. |
| 624 | 567 | /// If `false`, only known-safe mechanisms will be attempted. |
| 625 | 568 | allow_unsafe_unwind: bool = false, |
| ... | ... | @@ -630,8 +573,7 @@ pub const StackUnwindOptions = struct { |
| 630 | 573 | /// |
| 631 | 574 | /// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it. |
| 632 | 575 | pub fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) std.builtin.StackTrace { |
| 633 | var context_buf: ThreadContextBuf = undefined; | |
| 634 | var it = StackIterator.init(options.context, &context_buf) catch { | |
| 576 | var it = StackIterator.init(options.context) catch { | |
| 635 | 577 | return .{ .index = 0, .instruction_addresses = &.{} }; |
| 636 | 578 | }; |
| 637 | 579 | defer it.deinit(); |
| ... | ... | @@ -670,14 +612,7 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_ |
| 670 | 612 | return; |
| 671 | 613 | }, |
| 672 | 614 | }; |
| 673 | var context_buf: ThreadContextBuf = undefined; | |
| 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 | }, | |
| 615 | var it = StackIterator.init(options.context) catch |err| switch (err) { | |
| 681 | 616 | error.CannotUnwindFromContext => { |
| 682 | 617 | tty_config.setColor(writer, .dim) catch {}; |
| 683 | 618 | try writer.print("Cannot print stack trace: context unwind unavailable for target\n", .{}); |
| ... | ... | @@ -794,9 +729,9 @@ const StackIterator = union(enum) { |
| 794 | 729 | fp: usize, |
| 795 | 730 | |
| 796 | 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 | |
| 798 | /// and the same. | |
| 799 | inline fn init(context_opt: ?ThreadContextPtr, context_buf: *ThreadContextBuf) error{ OutOfMemory, CannotUnwindFromContext }!StackIterator { | |
| 732 | /// `@frameAddress` and `cpu_context.Native.current` as the caller's stack frame and | |
| 733 | /// our own are one and the same. | |
| 734 | inline fn init(opt_context_ptr: ?CpuContextPtr) error{CannotUnwindFromContext}!StackIterator { | |
| 800 | 735 | if (builtin.cpu.arch.isSPARC()) { |
| 801 | 736 | // Flush all the register windows on stack. |
| 802 | 737 | if (builtin.cpu.has(.sparc, .v9)) { |
| ... | ... | @@ -805,14 +740,12 @@ const StackIterator = union(enum) { |
| 805 | 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 | 744 | if (!SelfInfo.supports_unwinding) return error.CannotUnwindFromContext; |
| 810 | context_buf.* = context.*; | |
| 811 | relocateContext(context_buf); | |
| 812 | return .{ .di = try .init(context_buf, getDebugInfoAllocator()) }; | |
| 745 | return .{ .di = .init(context_ptr) }; | |
| 813 | 746 | } |
| 814 | if (SelfInfo.supports_unwinding and getContext(context_buf)) { | |
| 815 | return .{ .di = try .init(context_buf, getDebugInfoAllocator()) }; | |
| 747 | if (SelfInfo.supports_unwinding and cpu_context.Native != noreturn) { | |
| 748 | return .{ .di = .init(&.current()) }; | |
| 816 | 749 | } |
| 817 | 750 | return .{ .fp = @frameAddress() }; |
| 818 | 751 | } |
| ... | ... | @@ -1212,7 +1145,7 @@ pub const have_segfault_handling_support = switch (native_os) { |
| 1212 | 1145 | .windows, |
| 1213 | 1146 | => true, |
| 1214 | 1147 | |
| 1215 | .freebsd, .openbsd => ThreadContext != noreturn, | |
| 1148 | .freebsd, .openbsd => cpu_context.Native != noreturn, | |
| 1216 | 1149 | else => false, |
| 1217 | 1150 | }; |
| 1218 | 1151 | |
| ... | ... | @@ -1309,33 +1242,8 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa |
| 1309 | 1242 | }; |
| 1310 | 1243 | break :info .{ addr, name }; |
| 1311 | 1244 | }; |
| 1312 | ||
| 1313 | if (ThreadContext == noreturn) return handleSegfault(addr, name, 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); | |
| 1245 | const opt_cpu_context: ?cpu_context.Native = cpu_context.fromPosixSignalContext(ctx_ptr); | |
| 1246 | handleSegfault(addr, name, if (opt_cpu_context) |*ctx| ctx else null); | |
| 1339 | 1247 | } |
| 1340 | 1248 | |
| 1341 | 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 | 1255 | windows.EXCEPTION_STACK_OVERFLOW => .{ "Stack overflow", null }, |
| 1348 | 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 | 1262 | // Allow overriding the target-agnostic segfault handler by exposing `root.debug.handleSegfault`. |
| 1355 | 1263 | if (@hasDecl(root, "debug") and @hasDecl(root.debug, "handleSegfault")) { |
| 1356 | 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 | 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 | 1270 | // There is very similar logic to the following in `defaultPanic`. |
| 1363 | 1271 | switch (panic_stage) { |
| 1364 | 1272 | 0 => { |
lib/std/debug/Dwarf.zig+55-2| ... | ... | @@ -27,7 +27,6 @@ const Reader = std.Io.Reader; |
| 27 | 27 | const Dwarf = @This(); |
| 28 | 28 | |
| 29 | 29 | pub const expression = @import("Dwarf/expression.zig"); |
| 30 | pub const abi = @import("Dwarf/abi.zig"); | |
| 31 | 30 | pub const call_frame = @import("Dwarf/call_frame.zig"); |
| 32 | 31 | pub const Unwind = @import("Dwarf/Unwind.zig"); |
| 33 | 32 | |
| ... | ... | @@ -1415,7 +1414,7 @@ pub fn readUnitHeader(r: *Reader, endian: Endian) ScanError!UnitHeader { |
| 1415 | 1414 | } |
| 1416 | 1415 | |
| 1417 | 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 | 1418 | return switch (unwind_reg_number) { |
| 1420 | 1419 | 1 => 3, // RBX |
| 1421 | 1420 | 2 => 12, // R12 |
| ... | ... | @@ -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 | 1483 | /// This function is to make it handy to comment out the return and make it |
| 1431 | 1484 | /// into a crash when working on this file. |
| 1432 | 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 | 5 | const std = @import("std"); |
| 6 | 6 | const leb = std.leb; |
| 7 | 7 | const OP = std.dwarf.OP; |
| 8 | const abi = std.debug.Dwarf.abi; | |
| 9 | 8 | const mem = std.mem; |
| 10 | 9 | const assert = std.debug.assert; |
| 11 | 10 | const testing = std.testing; |
| 12 | 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 | 19 | /// Expressions can be evaluated in different contexts, each requiring its own set of inputs. |
| 15 | 20 | /// Callers should specify all the fields relevant to their context. If a field is required |
| 16 | 21 | /// by the expression and it isn't in the context, error.IncompleteExpressionContext is returned. |
| ... | ... | @@ -23,9 +28,7 @@ pub const Context = struct { |
| 23 | 28 | object_address: ?*const anyopaque = null, |
| 24 | 29 | /// .debug_addr section |
| 25 | 30 | debug_addr: ?[]const u8 = null, |
| 26 | /// Thread context | |
| 27 | thread_context: ?*std.debug.ThreadContext = null, | |
| 28 | reg_context: ?abi.RegisterContext = null, | |
| 31 | cpu_context: ?*std.debug.cpu_context.Native = null, | |
| 29 | 32 | /// Call frame address, if in a CFI context |
| 30 | 33 | cfa: ?usize = null, |
| 31 | 34 | /// This expression is a sub-expression from an OP.entry_value instruction |
| ... | ... | @@ -62,7 +65,9 @@ pub const Error = error{ |
| 62 | 65 | InvalidTypeLength, |
| 63 | 66 | |
| 64 | 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 | 72 | /// A stack machine that can decode and run DWARF expressions. |
| 68 | 73 | /// Expressions can be decoded for non-native address size and endianness, |
| ... | ... | @@ -369,29 +374,20 @@ pub fn StackMachine(comptime options: Options) type { |
| 369 | 374 | OP.breg0...OP.breg31, |
| 370 | 375 | OP.bregx, |
| 371 | 376 | => { |
| 372 | if (context.thread_context == null) return error.IncompleteExpressionContext; | |
| 373 | ||
| 374 | const base_register = operand.?.base_register; | |
| 375 | var value: i64 = @intCast(mem.readInt(usize, (try abi.regBytes( | |
| 376 | context.thread_context.?, | |
| 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) }); | |
| 377 | const cpu_context = context.cpu_context orelse return error.IncompleteExpressionContext; | |
| 378 | ||
| 379 | const br = operand.?.base_register; | |
| 380 | const value: i64 = @intCast((try regNative(cpu_context, br.base_register)).*); | |
| 381 | try self.stack.append(allocator, .{ .generic = @intCast(value + br.offset) }); | |
| 382 | 382 | }, |
| 383 | 383 | OP.regval_type => { |
| 384 | const register_type = operand.?.register_type; | |
| 385 | const value = mem.readInt(usize, (try abi.regBytes( | |
| 386 | context.thread_context.?, | |
| 387 | register_type.register, | |
| 388 | context.reg_context, | |
| 389 | ))[0..@sizeOf(usize)], native_endian); | |
| 384 | const cpu_context = context.cpu_context orelse return error.IncompleteExpressionContext; | |
| 385 | const rt = operand.?.register_type; | |
| 390 | 386 | try self.stack.append(allocator, .{ |
| 391 | 387 | .regval_type = .{ |
| 392 | .type_offset = register_type.type_offset, | |
| 388 | .type_offset = rt.type_offset, | |
| 393 | 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 | 730 | |
| 735 | 731 | // TODO: The spec states that this sub-expression needs to observe the state (ie. registers) |
| 736 | 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 | 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 | 738 | var block_stream: std.Io.Reader = .fixed(block); |
| 743 | 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 | 741 | try self.stack.append(allocator, .{ .generic = value }); |
| 746 | 742 | } else { |
| 747 | 743 | var stack_machine: Self = .{}; |
| ... | ... | @@ -1149,55 +1145,39 @@ test "basics" { |
| 1149 | 1145 | } |
| 1150 | 1146 | |
| 1151 | 1147 | // Register values |
| 1152 | if (@sizeOf(std.debug.ThreadContext) != 0) { | |
| 1148 | if (std.debug.cpu_context.Native != noreturn) { | |
| 1153 | 1149 | stack_machine.reset(); |
| 1154 | 1150 | program.clearRetainingCapacity(); |
| 1155 | 1151 | |
| 1156 | const reg_context = abi.RegisterContext{ | |
| 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); | |
| 1152 | var cpu_context: std.debug.cpu_context.Native = undefined; | |
| 1162 | 1153 | const context = Context{ |
| 1163 | .thread_context = &thread_context, | |
| 1164 | .reg_context = reg_context, | |
| 1154 | .cpu_context = &cpu_context, | |
| 1165 | 1155 | }; |
| 1166 | 1156 | |
| 1167 | // Only test register operations on arch / os that have them implemented | |
| 1168 | if (abi.regBytes(&thread_context, 0, reg_context)) |reg_bytes| { | |
| 1169 | ||
| 1170 | // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it | |
| 1171 | ||
| 1172 | mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian); | |
| 1173 | (try abi.regValueNative(&thread_context, abi.fpRegNum(native_arch, reg_context), reg_context)).* = 1; | |
| 1174 | (try abi.regValueNative(&thread_context, abi.spRegNum(native_arch, reg_context), reg_context)).* = 2; | |
| 1175 | (try abi.regValueNative(&thread_context, abi.ipRegNum(native_arch).?, reg_context)).* = 3; | |
| 1176 | ||
| 1177 | try b.writeBreg(writer, abi.fpRegNum(native_arch, reg_context), @as(usize, 100)); | |
| 1178 | try b.writeBreg(writer, abi.spRegNum(native_arch, reg_context), @as(usize, 200)); | |
| 1179 | try b.writeBregx(writer, abi.ipRegNum(native_arch).?, @as(usize, 300)); | |
| 1180 | try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400)); | |
| 1181 | ||
| 1182 | _ = try stack_machine.run(program.written(), allocator, context, 0); | |
| 1183 | ||
| 1184 | const regval_type = stack_machine.stack.pop().?.regval_type; | |
| 1185 | try testing.expectEqual(@as(usize, 400), regval_type.type_offset); | |
| 1186 | try testing.expectEqual(@as(u8, @sizeOf(usize)), regval_type.type_size); | |
| 1187 | try testing.expectEqual(@as(usize, 0xee), regval_type.value); | |
| 1188 | ||
| 1189 | try testing.expectEqual(@as(usize, 303), stack_machine.stack.pop().?.generic); | |
| 1190 | try testing.expectEqual(@as(usize, 202), 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 | } | |
| 1157 | const reg_bytes = try cpu_context.dwarfRegisterBytes(0); | |
| 1158 | ||
| 1159 | // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it | |
| 1160 | ||
| 1161 | mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian); | |
| 1162 | (try regNative(&cpu_context, fp_reg_num)).* = 1; | |
| 1163 | (try regNative(&cpu_context, sp_reg_num)).* = 2; | |
| 1164 | (try regNative(&cpu_context, ip_reg_num)).* = 3; | |
| 1165 | ||
| 1166 | try b.writeBreg(writer, fp_reg_num, @as(usize, 100)); | |
| 1167 | try b.writeBreg(writer, sp_reg_num, @as(usize, 200)); | |
| 1168 | try b.writeBregx(writer, ip_reg_num, @as(usize, 300)); | |
| 1169 | try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400)); | |
| 1170 | ||
| 1171 | _ = try stack_machine.run(program.written(), allocator, context, 0); | |
| 1172 | ||
| 1173 | const regval_type = stack_machine.stack.pop().?.regval_type; | |
| 1174 | try testing.expectEqual(@as(usize, 400), regval_type.type_offset); | |
| 1175 | try testing.expectEqual(@as(u8, @sizeOf(usize)), regval_type.type_size); | |
| 1176 | try testing.expectEqual(@as(usize, 0xee), regval_type.value); | |
| 1177 | ||
| 1178 | try testing.expectEqual(@as(usize, 303), stack_machine.stack.pop().?.generic); | |
| 1179 | try testing.expectEqual(@as(usize, 202), stack_machine.stack.pop().?.generic); | |
| 1180 | try testing.expectEqual(@as(usize, 101), stack_machine.stack.pop().?.generic); | |
| 1201 | 1181 | } |
| 1202 | 1182 | |
| 1203 | 1183 | // Stack operations |
| ... | ... | @@ -1585,38 +1565,24 @@ test "basics" { |
| 1585 | 1565 | } |
| 1586 | 1566 | |
| 1587 | 1567 | // Register location description |
| 1588 | const reg_context = abi.RegisterContext{ | |
| 1589 | .eh_frame = true, | |
| 1590 | .is_macho = builtin.os.tag == .macos, | |
| 1591 | }; | |
| 1592 | var thread_context: std.debug.ThreadContext = undefined; | |
| 1593 | std.debug.relocateContext(&thread_context); | |
| 1568 | var cpu_context: std.debug.cpu_context.Native = undefined; | |
| 1569 | std.debug.relocateContext(&cpu_context); | |
| 1594 | 1570 | context = Context{ |
| 1595 | .thread_context = &thread_context, | |
| 1596 | .reg_context = reg_context, | |
| 1571 | .cpu_context = &cpu_context, | |
| 1597 | 1572 | }; |
| 1598 | 1573 | |
| 1599 | if (abi.regBytes(&thread_context, 0, reg_context)) |reg_bytes| { | |
| 1600 | mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian); | |
| 1574 | const reg_bytes = try cpu_context.dwarfRegisterBytes(0); | |
| 1575 | mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian); | |
| 1601 | 1576 | |
| 1602 | var sub_program: std.Io.Writer.Allocating = .init(allocator); | |
| 1603 | defer sub_program.deinit(); | |
| 1604 | const sub_writer = &sub_program.writer; | |
| 1605 | try b.writeReg(sub_writer, 0); | |
| 1577 | var sub_program: std.Io.Writer.Allocating = .init(allocator); | |
| 1578 | defer sub_program.deinit(); | |
| 1579 | const sub_writer = &sub_program.writer; | |
| 1580 | try b.writeReg(sub_writer, 0); | |
| 1606 | 1581 | |
| 1607 | stack_machine.reset(); | |
| 1608 | program.clearRetainingCapacity(); | |
| 1609 | try b.writeEntryValue(writer, sub_program.written()); | |
| 1610 | _ = try stack_machine.run(program.written(), allocator, context, null); | |
| 1611 | 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 | } | |
| 1582 | stack_machine.reset(); | |
| 1583 | program.clearRetainingCapacity(); | |
| 1584 | try b.writeEntryValue(writer, sub_program.written()); | |
| 1585 | _ = try stack_machine.run(program.written(), allocator, context, null); | |
| 1586 | try testing.expectEqual(@as(usize, 0xee), stack_machine.stack.pop().?.generic); | |
| 1621 | 1587 | } |
| 1622 | 1588 | } |
lib/std/debug/SelfInfo.zig+121-108| ... | ... | @@ -11,8 +11,7 @@ const mem = std.mem; |
| 11 | 11 | const Allocator = std.mem.Allocator; |
| 12 | 12 | const assert = std.debug.assert; |
| 13 | 13 | const Dwarf = std.debug.Dwarf; |
| 14 | const regBytes = Dwarf.abi.regBytes; | |
| 15 | const regValueNative = Dwarf.abi.regValueNative; | |
| 14 | const CpuContext = std.debug.cpu_context.Native; | |
| 16 | 15 | |
| 17 | 16 | const root = @import("root"); |
| 18 | 17 | |
| ... | ... | @@ -38,8 +37,6 @@ pub const Error = error{ |
| 38 | 37 | pub const target_supported: bool = Module != void; |
| 39 | 38 | |
| 40 | 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 | 40 | pub const supports_unwinding: bool = target_supported and Module.supports_unwinding; |
| 44 | 41 | |
| 45 | 42 | pub const UnwindContext = if (supports_unwinding) Module.UnwindContext; |
| ... | ... | @@ -120,7 +117,7 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) |
| 120 | 117 | /// pub const UnwindContext = struct { |
| 121 | 118 | /// /// A PC value inside the function of the last unwound frame. |
| 122 | 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 | 121 | /// pub fn deinit(uc: *UnwindContext, gpa: Allocator) void; |
| 125 | 122 | /// /// Returns the frame pointer associated with the last unwound stack frame. If the frame |
| 126 | 123 | /// /// pointer is unknown, 0 may be returned instead. |
| ... | ... | @@ -141,9 +138,26 @@ const Module: type = Module: { |
| 141 | 138 | break :Module root.debug.Module; |
| 142 | 139 | } |
| 143 | 140 | break :Module switch (native_os) { |
| 144 | .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris, .illumos => @import("SelfInfo/ElfModule.zig"), | |
| 145 | .macos, .ios, .watchos, .tvos, .visionos => @import("SelfInfo/DarwinModule.zig"), | |
| 146 | .uefi, .windows => @import("SelfInfo/WindowsModule.zig"), | |
| 141 | .linux, | |
| 142 | .netbsd, | |
| 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 | 161 | else => void, |
| 148 | 162 | }; |
| 149 | 163 | }; |
| ... | ... | @@ -153,26 +167,25 @@ const Module: type = Module: { |
| 153 | 167 | pub const DwarfUnwindContext = struct { |
| 154 | 168 | cfa: ?usize, |
| 155 | 169 | pc: usize, |
| 156 | thread_context: *std.debug.ThreadContext, | |
| 157 | reg_context: Dwarf.abi.RegisterContext, | |
| 170 | cpu_context: CpuContext, | |
| 158 | 171 | vm: Dwarf.Unwind.VirtualMachine, |
| 159 | 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 | 175 | comptime assert(supports_unwinding); |
| 163 | _ = gpa; | |
| 164 | 176 | |
| 165 | const ip_reg_num = Dwarf.abi.ipRegNum(native_arch).?; | |
| 166 | const raw_pc_ptr = regValueNative(thread_context, ip_reg_num, null) catch { | |
| 167 | unreachable; // error means unsupported, in which case `supports_unwinding` should have been `false` | |
| 177 | // `@constCast` is safe because we aren't going to store to the resulting pointer. | |
| 178 | const raw_pc_ptr = regNative(@constCast(cpu_context), ip_reg_num) catch |err| switch (err) { | |
| 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 | 183 | const pc = stripInstructionPtrAuthCode(raw_pc_ptr.*); |
| 170 | 184 | |
| 171 | 185 | return .{ |
| 172 | 186 | .cfa = null, |
| 173 | 187 | .pc = pc, |
| 174 | .thread_context = thread_context, | |
| 175 | .reg_context = undefined, | |
| 188 | .cpu_context = cpu_context.*, | |
| 176 | 189 | .vm = .{}, |
| 177 | 190 | .stack_machine = .{}, |
| 178 | 191 | }; |
| ... | ... | @@ -185,17 +198,25 @@ pub const DwarfUnwindContext = struct { |
| 185 | 198 | } |
| 186 | 199 | |
| 187 | 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 | 213 | pub fn resolveRegisterRule( |
| 193 | 214 | context: *DwarfUnwindContext, |
| 194 | 215 | gpa: Allocator, |
| 195 | 216 | col: Dwarf.Unwind.VirtualMachine.Column, |
| 196 | 217 | expression_context: std.debug.Dwarf.expression.Context, |
| 197 | 218 | out: []u8, |
| 198 | ) !void { | |
| 219 | ) !bool { | |
| 199 | 220 | switch (col.rule) { |
| 200 | 221 | .default => { |
| 201 | 222 | const register = col.register orelse return error.InvalidRegister; |
| ... | ... | @@ -203,58 +224,74 @@ pub const DwarfUnwindContext = struct { |
| 203 | 224 | // See the doc comment on `Dwarf.Unwind.VirtualMachine.RegisterRule.default`. |
| 204 | 225 | if (builtin.cpu.arch.isAARCH64() and register >= 19 and register <= 18) { |
| 205 | 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 | 228 | if (src.len != out.len) return error.RegisterSizeMismatch; |
| 208 | 229 | @memcpy(out, src); |
| 209 | return; | |
| 230 | return false; | |
| 210 | 231 | } |
| 211 | 232 | @memset(out, undefined); |
| 233 | return true; | |
| 212 | 234 | }, |
| 213 | 235 | .undefined => { |
| 214 | 236 | @memset(out, undefined); |
| 237 | return true; | |
| 215 | 238 | }, |
| 216 | 239 | .same_value => { |
| 217 | 240 | // TODO: This copy could be eliminated if callers always copy the state then call this function to update it |
| 218 | 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 | 243 | if (src.len != out.len) return error.RegisterSizeMismatch; |
| 221 | 244 | @memcpy(out, src); |
| 245 | return false; | |
| 222 | 246 | }, |
| 223 | 247 | .offset => |offset| { |
| 224 | if (context.cfa) |cfa| { | |
| 225 | const addr = try applyOffset(cfa, offset); | |
| 226 | const ptr: *const usize = @ptrFromInt(addr); | |
| 227 | mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian); | |
| 228 | } else return error.InvalidCFA; | |
| 248 | const cfa = context.cfa orelse return error.InvalidCFA; | |
| 249 | const addr = try applyOffset(cfa, offset); | |
| 250 | const ptr: *const usize = @ptrFromInt(addr); | |
| 251 | mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian); | |
| 252 | return false; | |
| 229 | 253 | }, |
| 230 | 254 | .val_offset => |offset| { |
| 231 | if (context.cfa) |cfa| { | |
| 232 | mem.writeInt(usize, out[0..@sizeOf(usize)], try applyOffset(cfa, offset), native_endian); | |
| 233 | } else return error.InvalidCFA; | |
| 255 | const cfa = context.cfa orelse return error.InvalidCFA; | |
| 256 | mem.writeInt(usize, out[0..@sizeOf(usize)], try applyOffset(cfa, offset), native_endian); | |
| 257 | return false; | |
| 234 | 258 | }, |
| 235 | 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 | 261 | if (src.len != out.len) return error.RegisterSizeMismatch; |
| 238 | 262 | @memcpy(out, src); |
| 263 | return false; | |
| 239 | 264 | }, |
| 240 | 265 | .expression => |expression| { |
| 241 | 266 | context.stack_machine.reset(); |
| 242 | const value = try context.stack_machine.run(expression, gpa, expression_context, context.cfa.?); | |
| 243 | const addr = if (value) |v| blk: { | |
| 244 | if (v != .generic) return error.InvalidExpressionValue; | |
| 245 | break :blk v.generic; | |
| 246 | } else return error.NoExpressionValue; | |
| 247 | ||
| 267 | const value = try context.stack_machine.run( | |
| 268 | expression, | |
| 269 | gpa, | |
| 270 | expression_context, | |
| 271 | context.cfa.?, | |
| 272 | ) orelse return error.NoExpressionValue; | |
| 273 | const addr = switch (value) { | |
| 274 | .generic => |addr| addr, | |
| 275 | else => return error.InvalidExpressionValue, | |
| 276 | }; | |
| 248 | 277 | const ptr: *usize = @ptrFromInt(addr); |
| 249 | 278 | mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian); |
| 279 | return false; | |
| 250 | 280 | }, |
| 251 | 281 | .val_expression => |expression| { |
| 252 | 282 | context.stack_machine.reset(); |
| 253 | const value = try context.stack_machine.run(expression, gpa, expression_context, context.cfa.?); | |
| 254 | if (value) |v| { | |
| 255 | if (v != .generic) return error.InvalidExpressionValue; | |
| 256 | mem.writeInt(usize, out[0..@sizeOf(usize)], v.generic, native_endian); | |
| 257 | } else return error.NoExpressionValue; | |
| 283 | const value = try context.stack_machine.run( | |
| 284 | expression, | |
| 285 | gpa, | |
| 286 | expression_context, | |
| 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 | 296 | .architectural => return error.UnimplementedRegisterRule, |
| 260 | 297 | } |
| ... | ... | @@ -277,9 +314,6 @@ pub const DwarfUnwindContext = struct { |
| 277 | 314 | return unwindFrameInner(context, gpa, unwind, load_offset, explicit_fde_offset) catch |err| switch (err) { |
| 278 | 315 | error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e, |
| 279 | 316 | |
| 280 | error.UnimplementedArch, | |
| 281 | error.UnimplementedOs, | |
| 282 | error.ThreadContextNotSupported, | |
| 283 | 317 | error.UnimplementedRegisterRule, |
| 284 | 318 | error.UnsupportedAddrSize, |
| 285 | 319 | error.UnsupportedDwarfVersion, |
| ... | ... | @@ -289,10 +323,10 @@ pub const DwarfUnwindContext = struct { |
| 289 | 323 | error.UnimplementedTypedComparison, |
| 290 | 324 | error.UnimplementedTypeConversion, |
| 291 | 325 | error.UnknownExpressionOpcode, |
| 326 | error.UnsupportedRegister, | |
| 292 | 327 | => return error.UnsupportedDebugInfo, |
| 293 | 328 | |
| 294 | 329 | error.InvalidRegister, |
| 295 | error.RegisterContextRequired, | |
| 296 | 330 | error.ReadFailed, |
| 297 | 331 | error.EndOfStream, |
| 298 | 332 | error.IncompatibleRegisterSize, |
| ... | ... | @@ -346,20 +380,17 @@ pub const DwarfUnwindContext = struct { |
| 346 | 380 | // may not reference other debug sections anyway. |
| 347 | 381 | var expression_context: Dwarf.expression.Context = .{ |
| 348 | 382 | .format = format, |
| 349 | .thread_context = context.thread_context, | |
| 350 | .reg_context = context.reg_context, | |
| 383 | .cpu_context = &context.cpu_context, | |
| 351 | 384 | .cfa = context.cfa, |
| 352 | 385 | }; |
| 353 | 386 | |
| 354 | 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 | 389 | const row = try context.vm.runTo(gpa, pc_vaddr, cie, fde, @sizeOf(usize), native_endian); |
| 359 | 390 | context.cfa = switch (row.cfa.rule) { |
| 360 | 391 | .val_offset => |offset| blk: { |
| 361 | 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 | 394 | break :blk try applyOffset(value, offset); |
| 364 | 395 | }, |
| 365 | 396 | .expression => |expr| blk: { |
| ... | ... | @@ -381,73 +412,41 @@ pub const DwarfUnwindContext = struct { |
| 381 | 412 | |
| 382 | 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 | 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 | 423 | for (context.vm.rowColumns(row)) |column| { |
| 401 | 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 | 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 | |
| 421 | (try regValueNative(context.thread_context, Dwarf.abi.spRegNum(native_arch, context.reg_context), context.reg_context)).* = context.cfa.?; | |
| 422 | ||
| 423 | while (update_tail) |tail| { | |
| 424 | @memcpy(tail.dest, tail.src); | |
| 425 | update_tail = tail.prev; | |
| 426 | } | |
| 433 | const return_address: u64 = if (has_return_address) pc: { | |
| 434 | const raw_ptr = try regNative(&new_cpu_context, cie.return_address_register); | |
| 435 | break :pc stripInstructionPtrAuthCode(raw_ptr.*); | |
| 436 | } else 0; | |
| 427 | 437 | |
| 428 | if (has_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 | } | |
| 438 | (try regNative(new_cpu_context, ip_reg_num)).* = return_address; | |
| 437 | 439 | |
| 438 | const ip_reg_num = Dwarf.abi.ipRegNum(native_arch).?; | |
| 439 | (try regValueNative(context.thread_context, ip_reg_num, context.reg_context)).* = context.pc; | |
| 440 | // The new CPU context is complete; flush changes. | |
| 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. | |
| 442 | // This next instruction may be past the end of the function if the caller was `noreturn` (ie. the last instruction in | |
| 443 | // the function was the call). If we were to look up an FDE entry using the return address directly, it could end up | |
| 444 | // either not finding an FDE at all, or using the next FDE in the program, producing incorrect results. To prevent this, | |
| 445 | // we subtract one so that the next lookup is guaranteed to land inside the | |
| 446 | // | |
| 447 | // The exception to this rule is signal frames, where we return execution would be returned to the instruction | |
| 448 | // that triggered the handler. | |
| 449 | const return_address = context.pc; | |
| 450 | if (context.pc > 0 and !cie.is_signal_frame) context.pc -= 1; | |
| 443 | // Also update the stored pc. However, because `return_address` points to the instruction | |
| 444 | // *after* the call, it could (in the case of noreturn functions) actually point outside of | |
| 445 | // the caller's address range, meaning an FDE lookup would fail. We can handle this by | |
| 446 | // subtracting 1 from `return_address` so that the next lookup is guaranteed to land inside | |
| 447 | // the `call` instruction`. The exception to this rule is signal frames, where the return | |
| 448 | // address is the same instruction that triggered the handler. | |
| 449 | context.pc = if (cie.is_signal_frame) return_address else return_address -| 1; | |
| 451 | 450 | |
| 452 | 451 | return return_address; |
| 453 | 452 | } |
| ... | ... | @@ -479,4 +478,18 @@ pub const DwarfUnwindContext = struct { |
| 479 | 478 | |
| 480 | 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 | 265 | error.OutOfMemory, |
| 266 | 266 | error.Unexpected, |
| 267 | 267 | => |e| return e, |
| 268 | error.UnimplementedArch, | |
| 269 | error.UnimplementedOs, | |
| 270 | error.ThreadContextNotSupported, | |
| 268 | error.UnsupportedRegister, | |
| 271 | 269 | => return error.UnsupportedDebugInfo, |
| 272 | 270 | error.InvalidRegister, |
| 273 | error.RegisterContextRequired, | |
| 274 | 271 | error.IncompatibleRegisterSize, |
| 275 | 272 | => return error.InvalidDebugInfo, |
| 276 | 273 | }; |
| ... | ... | @@ -396,7 +393,6 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 396 | 393 | }; |
| 397 | 394 | |
| 398 | 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 | 397 | const encoding: macho.CompactUnwindEncoding = @bitCast(entry.raw_encoding); |
| 402 | 398 | const new_ip = switch (builtin.cpu.arch) { |
| ... | ... | @@ -405,16 +401,16 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 405 | 401 | .RBP_FRAME => ip: { |
| 406 | 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 | 405 | const new_sp = fp + 2 * @sizeOf(usize); |
| 410 | 406 | |
| 411 | 407 | const ip_ptr = fp + @sizeOf(usize); |
| 412 | 408 | const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; |
| 413 | 409 | const new_fp = @as(*const usize, @ptrFromInt(fp)).*; |
| 414 | 410 | |
| 415 | (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).* = new_fp; | |
| 416 | (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp; | |
| 417 | (try regValueNative(context.thread_context, ip_reg_num, reg_context)).* = new_ip; | |
| 411 | (try dwarfRegNative(&context.cpu_context, fp_reg_num)).* = new_fp; | |
| 412 | (try dwarfRegNative(&context.cpu_context, sp_reg_num)).* = new_sp; | |
| 413 | (try dwarfRegNative(&context.cpu_context, ip_reg_num)).* = new_ip; | |
| 418 | 414 | |
| 419 | 415 | const regs: [5]u3 = .{ |
| 420 | 416 | frame.reg0, |
| ... | ... | @@ -427,7 +423,7 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 427 | 423 | if (reg == 0) continue; |
| 428 | 424 | const addr = fp - frame.frame_offset * @sizeOf(usize) + i * @sizeOf(usize); |
| 429 | 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 | 429 | break :ip new_ip; |
| ... | ... | @@ -437,7 +433,7 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 437 | 433 | => ip: { |
| 438 | 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 | 437 | const stack_size: usize = stack_size: { |
| 442 | 438 | if (encoding.mode.x86_64 == .STACK_IMMD) { |
| 443 | 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 | 483 | var reg_addr = sp + stack_size - @sizeOf(usize) * @as(usize, reg_count + 1); |
| 488 | 484 | for (0..reg_count) |i| { |
| 489 | 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 | 487 | reg_addr += @sizeOf(usize); |
| 492 | 488 | } |
| 493 | 489 | |
| ... | ... | @@ -497,8 +493,8 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 497 | 493 | const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; |
| 498 | 494 | const new_sp = ip_ptr + @sizeOf(usize); |
| 499 | 495 | |
| 500 | (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp; | |
| 501 | (try regValueNative(context.thread_context, ip_reg_num, reg_context)).* = new_ip; | |
| 496 | (try dwarfRegNative(&context.cpu_context, sp_reg_num)).* = new_sp; | |
| 497 | (try dwarfRegNative(&context.cpu_context, ip_reg_num)).* = new_ip; | |
| 502 | 498 | |
| 503 | 499 | break :ip new_ip; |
| 504 | 500 | }, |
| ... | ... | @@ -516,10 +512,10 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 516 | 512 | .aarch64, .aarch64_be => switch (encoding.mode.arm64) { |
| 517 | 513 | .OLD => return error.UnsupportedDebugInfo, |
| 518 | 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 | 516 | const new_sp = sp + encoding.value.arm64.frameless.stack_size * 16; |
| 521 | const new_ip = (try regValueNative(context.thread_context, 30, reg_context)).*; | |
| 522 | (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).* = new_sp; | |
| 517 | const new_ip = (try dwarfRegNative(&context.cpu_context, 30)).*; | |
| 518 | (try dwarfRegNative(&context.cpu_context, sp_reg_num)).* = new_sp; | |
| 523 | 519 | break :ip new_ip; |
| 524 | 520 | }, |
| 525 | 521 | .DWARF => { |
| ... | ... | @@ -535,15 +531,15 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 535 | 531 | .FRAME => ip: { |
| 536 | 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 | 535 | const ip_ptr = fp + @sizeOf(usize); |
| 540 | 536 | |
| 541 | 537 | var reg_addr = fp - @sizeOf(usize); |
| 542 | 538 | inline for (@typeInfo(@TypeOf(frame.x_reg_pairs)).@"struct".fields, 0..) |field, i| { |
| 543 | 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 | 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 | 543 | reg_addr += @sizeOf(usize); |
| 548 | 544 | } |
| 549 | 545 | } |
| ... | ... | @@ -552,12 +548,12 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 552 | 548 | if (@field(frame.d_reg_pairs, field.name) != 0) { |
| 553 | 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 | 552 | dest.* = @as(*const usize, @ptrFromInt(reg_addr)).*; |
| 557 | 553 | } |
| 558 | 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 | 557 | dest.* = @as(*const usize, @ptrFromInt(reg_addr)).*; |
| 562 | 558 | } |
| 563 | 559 | reg_addr += @sizeOf(usize); |
| ... | ... | @@ -567,8 +563,8 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo, |
| 567 | 563 | const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; |
| 568 | 564 | const new_fp = @as(*const usize, @ptrFromInt(fp)).*; |
| 569 | 565 | |
| 570 | (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).* = new_fp; | |
| 571 | (try regValueNative(context.thread_context, ip_reg_num, reg_context)).* = new_ip; | |
| 566 | (try dwarfRegNative(&context.cpu_context, fp_reg_num)).* = new_fp; | |
| 567 | (try dwarfRegNative(&context.cpu_context, ip_reg_num)).* = new_ip; | |
| 572 | 568 | |
| 573 | 569 | break :ip new_ip; |
| 574 | 570 | }, |
| ... | ... | @@ -782,13 +778,9 @@ test { |
| 782 | 778 | _ = MachoSymbol; |
| 783 | 779 | } |
| 784 | 780 | |
| 785 | fn fpRegNum(reg_context: Dwarf.abi.RegisterContext) u8 { | |
| 786 | return Dwarf.abi.fpRegNum(builtin.target.cpu.arch, reg_context); | |
| 787 | } | |
| 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).?; | |
| 781 | const ip_reg_num = Dwarf.ipRegNum(builtin.target.cpu.arch).?; | |
| 782 | const fp_reg_num = Dwarf.fpRegNum(builtin.target.cpu.arch); | |
| 783 | const sp_reg_num = Dwarf.spRegNum(builtin.target.cpu.arch); | |
| 792 | 784 | |
| 793 | 785 | /// Uses `mmap` to map the file at `path` into memory. |
| 794 | 786 | fn mapDebugInfoFile(path: []const u8) ![]align(std.heap.page_size_min) const u8 { |
| ... | ... | @@ -821,8 +813,7 @@ const mem = std.mem; |
| 821 | 813 | const posix = std.posix; |
| 822 | 814 | const testing = std.testing; |
| 823 | 815 | const Error = std.debug.SelfInfo.Error; |
| 824 | const regBytes = Dwarf.abi.regBytes; | |
| 825 | const regValueNative = Dwarf.abi.regValueNative; | |
| 816 | const dwarfRegNative = std.debug.SelfInfo.DwarfUnwindContext.regNative; | |
| 826 | 817 | |
| 827 | 818 | const builtin = @import("builtin"); |
| 828 | 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 | 26 | pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) Error!ElfModule { |
| 27 | 27 | _ = cache; |
| 28 | 28 | _ = gpa; |
| 29 | if (builtin.target.os.tag == .haiku) @panic("TODO implement lookup module for Haiku"); | |
| 30 | 29 | const DlIterContext = struct { |
| 31 | 30 | /// input |
| 32 | 31 | address: usize, |
| ... | ... | @@ -261,7 +260,7 @@ pub const supports_unwinding: bool = s: { |
| 261 | 260 | }; |
| 262 | 261 | comptime { |
| 263 | 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 | 295 | pc: usize, |
| 296 | 296 | cur: windows.CONTEXT, |
| 297 | 297 | history_table: windows.UNWIND_HISTORY_TABLE, |
| 298 | pub fn init(ctx: *const windows.CONTEXT, gpa: Allocator) Allocator.Error!UnwindContext { | |
| 299 | _ = gpa; | |
| 298 | pub fn init(ctx: *const std.debug.cpu_context.Native) UnwindContext { | |
| 300 | 299 | return .{ |
| 301 | 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 | 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 | 183 | |
| 184 | 184 | if (posix.MREMAP != void) { |
| 185 | 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 | 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 | 3 | const off_t = std.c.off_t; |
| 4 | 4 | const unexpectedErrno = std.posix.unexpectedErrno; |
| 5 | 5 | const errno = std.posix.errno; |
| 6 | const builtin = @import("builtin"); | |
| 6 | 7 | |
| 7 | 8 | pub const CopyFileRangeError = std.posix.UnexpectedError || error{ |
| 8 | 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 | 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 | 49 | .s390x => @import("linux/s390x.zig"), |
| 50 | 50 | else => struct { |
| 51 | 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 | 111 | pub const timezone = arch_bits.timezone; |
| 113 | 112 | pub const ucontext_t = arch_bits.ucontext_t; |
| 114 | 113 | pub const user_desc = arch_bits.user_desc; |
| 115 | pub const getcontext = arch_bits.getcontext; | |
| 116 | 114 | |
| 117 | 115 | pub const tls = @import("linux/tls.zig"); |
| 118 | 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 | 260 | mcontext: mcontext_t, |
| 261 | 261 | }; |
| 262 | 262 | |
| 263 | /// TODO | |
| 264 | pub const getcontext = {}; | |
| 265 | ||
| 266 | 263 | pub const Elf_Symndx = u32; |
lib/std/os/linux/arm.zig-3| ... | ... | @@ -310,7 +310,4 @@ pub const ucontext_t = extern struct { |
| 310 | 310 | regspace: [64]u64, |
| 311 | 311 | }; |
| 312 | 312 | |
| 313 | /// TODO | |
| 314 | pub const getcontext = {}; | |
| 315 | ||
| 316 | 313 | pub const Elf_Symndx = u32; |
lib/std/os/linux/hexagon.zig-3| ... | ... | @@ -237,6 +237,3 @@ pub const VDSO = void; |
| 237 | 237 | |
| 238 | 238 | /// TODO |
| 239 | 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 | 250 | }; |
| 251 | 251 | |
| 252 | 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 | 258 | |
| 259 | 259 | /// TODO |
| 260 | 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 | 349 | |
| 350 | 350 | /// TODO |
| 351 | 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 | 328 | |
| 329 | 329 | /// TODO |
| 330 | 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 | 381 | }; |
| 382 | 382 | |
| 383 | 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 | 376 | }; |
| 377 | 377 | |
| 378 | 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 | 255 | sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask |
| 256 | 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 | 255 | sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask |
| 256 | 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 | 273 | __regs2: [18]u32, |
| 274 | 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 | 426 | stack: stack_t, |
| 427 | 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 | 436 | [sigset_size] "i" (linux.NSIG / 8), |
| 437 | 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 | 352 | sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a glibc-compatible (1024-bit) sigmask. |
| 353 | 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 | 47 | .linux => linux, |
| 48 | 48 | .plan9 => std.os.plan9, |
| 49 | 49 | else => struct { |
| 50 | pub const getcontext = {}; | |
| 51 | pub const ucontext_t = void; | |
| 52 | 50 | pub const pid_t = void; |
| 53 | 51 | pub const pollfd = void; |
| 54 | 52 | pub const fd_t = void; |
| ... | ... | @@ -142,7 +140,6 @@ pub const in_pktinfo = system.in_pktinfo; |
| 142 | 140 | pub const in6_pktinfo = system.in6_pktinfo; |
| 143 | 141 | pub const ino_t = system.ino_t; |
| 144 | 142 | pub const linger = system.linger; |
| 145 | pub const mcontext_t = system.mcontext_t; | |
| 146 | 143 | pub const mode_t = system.mode_t; |
| 147 | 144 | pub const msghdr = system.msghdr; |
| 148 | 145 | pub const msghdr_const = system.msghdr_const; |
| ... | ... | @@ -171,7 +168,6 @@ pub const timespec = system.timespec; |
| 171 | 168 | pub const timestamp_t = system.timestamp_t; |
| 172 | 169 | pub const timeval = system.timeval; |
| 173 | 170 | pub const timezone = system.timezone; |
| 174 | pub const ucontext_t = system.ucontext_t; | |
| 175 | 171 | pub const uid_t = system.uid_t; |
| 176 | 172 | pub const user_desc = system.user_desc; |
| 177 | 173 | pub const utsname = system.utsname; |