authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-17 18:38:11+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-30 13:44:54+01:00
loga18fd41064493e742eacebc88e2afeadd54ff6f0
tree1081fbd6d3c64cf1f583ae3188ab05e0320f03d9
parentb578cca022f4c9ce94439e2ee795639b3a23c8f5
signaturelock-open Commit is signed but in an unrecognized format.

std: rework/remove ucontext_t

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: #23802

33 files changed, 1416 insertions(+), 1523 deletions(-)

lib/std/c.zig-207
......@@ -7035,205 +7035,6 @@ pub const timezone = switch (native_os) {
70357035 else => void,
70367036};
70377037
7038pub 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};
7102pub 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
72377038pub const user_desc = switch (native_os) {
72387039 .linux => linux.user_desc,
72397040 else => void,
......@@ -11238,13 +11039,6 @@ pub const LC = enum(c_int) {
1123811039
1123911040pub extern "c" fn setlocale(category: LC, locale: ?[*:0]const u8) ?[*:0]const u8;
1124011041
11241pub 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
1124811042pub const max_align_t = if (native_abi == .msvc or native_abi == .itanium)
1124911043 f64
1125011044else if (native_os.isDarwin())
......@@ -11668,7 +11462,6 @@ const private = struct {
1166811462 extern "c" fn shm_open(name: [*:0]const u8, flag: c_int, mode: mode_t) c_int;
1166911463
1167011464 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;
1167211465
1167311466 extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize;
1167411467 extern "c" fn getentropy(buffer: [*]u8, size: usize) c_int;
lib/std/c/darwin.zig-101
......@@ -348,107 +348,6 @@ pub const VM = struct {
348348
349349pub const exception_type_t = c_int;
350350
351pub 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
365pub 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
380pub 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
422pub const neon_state = extern struct {
423 q: [32]u128,
424 fpsr: u32,
425 fpcr: u32,
426};
427
428pub 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
449pub const stmm_reg = [16]u8;
450pub const xmm_reg = [16]u8;
451
452351pub extern "c" fn NSVersionOfRunTimeLibrary(library_name: [*:0]const u8) u32;
453352pub extern "c" fn _NSGetExecutablePath(buf: [*:0]u8, bufsize: *u32) c_int;
454353pub 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)
1313pub extern "c" fn umtx_sleep(ptr: *const volatile c_int, value: c_int, timeout: c_int) c_int;
1414pub extern "c" fn umtx_wakeup(ptr: *const volatile c_int, count: c_int) c_int;
1515
16pub 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
54pub const register_t = isize;
55
5616pub const E = enum(u16) {
5717 /// No error occurred.
5818 SUCCESS = 0,
lib/std/c/haiku.zig-263
......@@ -273,269 +273,6 @@ pub const E = enum(i32) {
273273
274274pub const status_t = i32;
275275
276pub 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
539276pub const DirEnt = extern struct {
540277 /// device
541278 dev: dev_t,
lib/std/c/openbsd.zig-47
......@@ -144,53 +144,6 @@ pub const TCIO = enum(u32) {
144144 ION = 4,
145145};
146146
147pub 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
194147pub const E = enum(u16) {
195148 /// No error occurred.
196149 SUCCESS = 0,
lib/std/debug.zig+22-114
......@@ -22,6 +22,7 @@ pub const ElfFile = @import("debug/ElfFile.zig");
2222pub const SelfInfo = @import("debug/SelfInfo.zig");
2323pub const Info = @import("debug/Info.zig");
2424pub const Coverage = @import("debug/Coverage.zig");
25pub const cpu_context = @import("debug/cpu_context.zig");
2526
2627pub const simple_panic = @import("debug/simple_panic.zig");
2728pub const no_panic = @import("debug/no_panic.zig");
......@@ -331,66 +332,8 @@ test dumpHexFallible {
331332 try std.testing.expectEqualStrings(expected, aw.written());
332333}
333334
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`.
337pub 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.
349pub 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`.
356const ThreadContextBuf = if (ThreadContext == noreturn) void else ThreadContext;
357/// The pointer through which a `ThreadContext` is received from callers of stack tracing logic.
358pub 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.
366pub 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.
336pub const CpuContextPtr = if (cpu_context.Native == noreturn) noreturn else *const cpu_context.Native;
394337
395338/// Invokes detectable illegal behavior when `ok` is `false`.
396339///
......@@ -616,10 +559,10 @@ pub const StackUnwindOptions = struct {
616559 /// used to omit intermediate handling code (for instance, a panic handler and its machinery)
617560 /// from stack traces.
618561 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,
623566 /// If `true`, stack unwinding strategies which may cause crashes are used as a last resort.
624567 /// If `false`, only known-safe mechanisms will be attempted.
625568 allow_unsafe_unwind: bool = false,
......@@ -630,8 +573,7 @@ pub const StackUnwindOptions = struct {
630573///
631574/// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it.
632575pub 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 {
635577 return .{ .index = 0, .instruction_addresses = &.{} };
636578 };
637579 defer it.deinit();
......@@ -670,14 +612,7 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_
670612 return;
671613 },
672614 };
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) {
681616 error.CannotUnwindFromContext => {
682617 tty_config.setColor(writer, .dim) catch {};
683618 try writer.print("Cannot print stack trace: context unwind unavailable for target\n", .{});
......@@ -794,9 +729,9 @@ const StackIterator = union(enum) {
794729 fp: usize,
795730
796731 /// 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 {
800735 if (builtin.cpu.arch.isSPARC()) {
801736 // Flush all the register windows on stack.
802737 if (builtin.cpu.has(.sparc, .v9)) {
......@@ -805,14 +740,12 @@ const StackIterator = union(enum) {
805740 asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS
806741 }
807742 }
808 if (context_opt) |context| {
743 if (opt_context_ptr) |context_ptr| {
809744 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) };
813746 }
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()) };
816749 }
817750 return .{ .fp = @frameAddress() };
818751 }
......@@ -1212,7 +1145,7 @@ pub const have_segfault_handling_support = switch (native_os) {
12121145 .windows,
12131146 => true,
12141147
1215 .freebsd, .openbsd => ThreadContext != noreturn,
1148 .freebsd, .openbsd => cpu_context.Native != noreturn,
12161149 else => false,
12171150};
12181151
......@@ -1309,33 +1242,8 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa
13091242 };
13101243 break :info .{ addr, name };
13111244 };
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);
13391247}
13401248
13411249fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(.winapi) c_long {
......@@ -1347,10 +1255,10 @@ fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(.winapi) c_
13471255 windows.EXCEPTION_STACK_OVERFLOW => .{ "Stack overflow", null },
13481256 else => return windows.EXCEPTION_CONTINUE_SEARCH,
13491257 };
1350 handleSegfault(addr, name, info.ContextRecord);
1258 handleSegfault(addr, name, &cpu_context.fromWindowsContext(info.ContextRecord));
13511259}
13521260
1353fn handleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?ThreadContextPtr) noreturn {
1261fn handleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContextPtr) noreturn {
13541262 // Allow overriding the target-agnostic segfault handler by exposing `root.debug.handleSegfault`.
13551263 if (@hasDecl(root, "debug") and @hasDecl(root.debug, "handleSegfault")) {
13561264 return root.debug.handleSegfault(addr, name, opt_ctx);
......@@ -1358,7 +1266,7 @@ fn handleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?ThreadContextPtr) no
13581266 return defaultHandleSegfault(addr, name, opt_ctx);
13591267}
13601268
1361pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?ThreadContextPtr) noreturn {
1269pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContextPtr) noreturn {
13621270 // There is very similar logic to the following in `defaultPanic`.
13631271 switch (panic_stage) {
13641272 0 => {
lib/std/debug/Dwarf.zig+55-2
......@@ -27,7 +27,6 @@ const Reader = std.Io.Reader;
2727const Dwarf = @This();
2828
2929pub const expression = @import("Dwarf/expression.zig");
30pub const abi = @import("Dwarf/abi.zig");
3130pub const call_frame = @import("Dwarf/call_frame.zig");
3231pub const Unwind = @import("Dwarf/Unwind.zig");
3332
......@@ -1415,7 +1414,7 @@ pub fn readUnitHeader(r: *Reader, endian: Endian) ScanError!UnitHeader {
14151414}
14161415
14171416/// Returns the DWARF register number for an x86_64 register number found in compact unwind info
1418pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
1417pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u16 {
14191418 return switch (unwind_reg_number) {
14201419 1 => 3, // RBX
14211420 2 => 12, // R12
......@@ -1427,6 +1426,60 @@ pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u8 {
14271426 };
14281427}
14291428
1429/// Returns `null` for CPU architectures without an instruction pointer register.
1430pub 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
1440pub 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
1450pub 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.
1464pub 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
14301483/// This function is to make it handy to comment out the return and make it
14311484/// into a crash when working on this file.
14321485pub fn bad() error{InvalidDebugInfo} {
lib/std/debug/Dwarf/abi.zig deleted-351
......@@ -1,351 +0,0 @@
1const builtin = @import("builtin");
2
3const std = @import("../../std.zig");
4const mem = std.mem;
5const posix = std.posix;
6const 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.
12pub 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.
32pub 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
42pub 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
54pub 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
64pub const RegisterContext = struct {
65 eh_frame: bool,
66 is_macho: bool,
67};
68
69pub 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.
86pub 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.
343pub 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();
55const std = @import("std");
66const leb = std.leb;
77const OP = std.dwarf.OP;
8const abi = std.debug.Dwarf.abi;
98const mem = std.mem;
109const assert = std.debug.assert;
1110const testing = std.testing;
1211const Writer = std.Io.Writer;
1312
13const regNative = std.debug.SelfInfo.DwarfUnwindContext.regNative;
14
15const ip_reg_num = std.debug.Dwarf.ipRegNum(native_arch).?;
16const fp_reg_num = std.debug.Dwarf.fpRegNum(native_arch);
17const sp_reg_num = std.debug.Dwarf.spRegNum(native_arch);
18
1419/// Expressions can be evaluated in different contexts, each requiring its own set of inputs.
1520/// Callers should specify all the fields relevant to their context. If a field is required
1621/// by the expression and it isn't in the context, error.IncompleteExpressionContext is returned.
......@@ -23,9 +28,7 @@ pub const Context = struct {
2328 object_address: ?*const anyopaque = null,
2429 /// .debug_addr section
2530 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,
2932 /// Call frame address, if in a CFI context
3033 cfa: ?usize = null,
3134 /// This expression is a sub-expression from an OP.entry_value instruction
......@@ -62,7 +65,9 @@ pub const Error = error{
6265 InvalidTypeLength,
6366
6467 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 };
6671
6772/// A stack machine that can decode and run DWARF expressions.
6873/// Expressions can be decoded for non-native address size and endianness,
......@@ -369,29 +374,20 @@ pub fn StackMachine(comptime options: Options) type {
369374 OP.breg0...OP.breg31,
370375 OP.bregx,
371376 => {
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) });
382382 },
383383 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;
390386 try self.stack.append(allocator, .{
391387 .regval_type = .{
392 .type_offset = register_type.type_offset,
388 .type_offset = rt.type_offset,
393389 .type_size = @sizeOf(addr_type),
394 .value = value,
390 .value = (try regNative(cpu_context, rt.register)).*,
395391 },
396392 });
397393 },
......@@ -734,14 +730,14 @@ pub fn StackMachine(comptime options: Options) type {
734730
735731 // TODO: The spec states that this sub-expression needs to observe the state (ie. registers)
736732 // 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.
738734
739735 if (isOpcodeRegisterLocation(block[0])) {
740 if (context.thread_context == null) return error.IncompleteExpressionContext;
736 const cpu_context = context.cpu_context orelse return error.IncompleteExpressionContext;
741737
742738 var block_stream: std.Io.Reader = .fixed(block);
743739 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)).*;
745741 try self.stack.append(allocator, .{ .generic = value });
746742 } else {
747743 var stack_machine: Self = .{};
......@@ -1149,55 +1145,39 @@ test "basics" {
11491145 }
11501146
11511147 // Register values
1152 if (@sizeOf(std.debug.ThreadContext) != 0) {
1148 if (std.debug.cpu_context.Native != noreturn) {
11531149 stack_machine.reset();
11541150 program.clearRetainingCapacity();
11551151
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;
11621153 const context = Context{
1163 .thread_context = &thread_context,
1164 .reg_context = reg_context,
1154 .cpu_context = &cpu_context,
11651155 };
11661156
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);
12011181 }
12021182
12031183 // Stack operations
......@@ -1585,38 +1565,24 @@ test "basics" {
15851565 }
15861566
15871567 // 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);
15941570 context = Context{
1595 .thread_context = &thread_context,
1596 .reg_context = reg_context,
1571 .cpu_context = &cpu_context,
15971572 };
15981573
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);
16011576
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);
16061581
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);
16211587 }
16221588}
lib/std/debug/SelfInfo.zig+121-108
......@@ -11,8 +11,7 @@ const mem = std.mem;
1111const Allocator = std.mem.Allocator;
1212const assert = std.debug.assert;
1313const Dwarf = std.debug.Dwarf;
14const regBytes = Dwarf.abi.regBytes;
15const regValueNative = Dwarf.abi.regValueNative;
14const CpuContext = std.debug.cpu_context.Native;
1615
1716const root = @import("root");
1817
......@@ -38,8 +37,6 @@ pub const Error = error{
3837pub const target_supported: bool = Module != void;
3938
4039/// 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`.
4340pub const supports_unwinding: bool = target_supported and Module.supports_unwinding;
4441
4542pub const UnwindContext = if (supports_unwinding) Module.UnwindContext;
......@@ -120,7 +117,7 @@ pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize)
120117/// pub const UnwindContext = struct {
121118/// /// A PC value inside the function of the last unwound frame.
122119/// 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;
124121/// pub fn deinit(uc: *UnwindContext, gpa: Allocator) void;
125122/// /// Returns the frame pointer associated with the last unwound stack frame. If the frame
126123/// /// pointer is unknown, 0 may be returned instead.
......@@ -141,9 +138,26 @@ const Module: type = Module: {
141138 break :Module root.debug.Module;
142139 }
143140 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
147161 else => void,
148162 };
149163};
......@@ -153,26 +167,25 @@ const Module: type = Module: {
153167pub const DwarfUnwindContext = struct {
154168 cfa: ?usize,
155169 pc: usize,
156 thread_context: *std.debug.ThreadContext,
157 reg_context: Dwarf.abi.RegisterContext,
170 cpu_context: CpuContext,
158171 vm: Dwarf.Unwind.VirtualMachine,
159172 stack_machine: Dwarf.expression.StackMachine(.{ .call_frame_context = true }),
160173
161 pub fn init(thread_context: *std.debug.ThreadContext, gpa: Allocator) error{}!DwarfUnwindContext {
174 pub fn init(cpu_context: *const CpuContext) DwarfUnwindContext {
162175 comptime assert(supports_unwinding);
163 _ = gpa;
164176
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
168182 };
169183 const pc = stripInstructionPtrAuthCode(raw_pc_ptr.*);
170184
171185 return .{
172186 .cfa = null,
173187 .pc = pc,
174 .thread_context = thread_context,
175 .reg_context = undefined,
188 .cpu_context = cpu_context.*,
176189 .vm = .{},
177190 .stack_machine = .{},
178191 };
......@@ -185,17 +198,25 @@ pub const DwarfUnwindContext = struct {
185198 }
186199
187200 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.*;
189208 }
190209
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.
192213 pub fn resolveRegisterRule(
193214 context: *DwarfUnwindContext,
194215 gpa: Allocator,
195216 col: Dwarf.Unwind.VirtualMachine.Column,
196217 expression_context: std.debug.Dwarf.expression.Context,
197218 out: []u8,
198 ) !void {
219 ) !bool {
199220 switch (col.rule) {
200221 .default => {
201222 const register = col.register orelse return error.InvalidRegister;
......@@ -203,58 +224,74 @@ pub const DwarfUnwindContext = struct {
203224 // See the doc comment on `Dwarf.Unwind.VirtualMachine.RegisterRule.default`.
204225 if (builtin.cpu.arch.isAARCH64() and register >= 19 and register <= 18) {
205226 // 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);
207228 if (src.len != out.len) return error.RegisterSizeMismatch;
208229 @memcpy(out, src);
209 return;
230 return false;
210231 }
211232 @memset(out, undefined);
233 return true;
212234 },
213235 .undefined => {
214236 @memset(out, undefined);
237 return true;
215238 },
216239 .same_value => {
217240 // TODO: This copy could be eliminated if callers always copy the state then call this function to update it
218241 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);
220243 if (src.len != out.len) return error.RegisterSizeMismatch;
221244 @memcpy(out, src);
245 return false;
222246 },
223247 .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;
229253 },
230254 .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;
234258 },
235259 .register => |register| {
236 const src = try regBytes(context.thread_context, register, context.reg_context);
260 const src = try context.cpu_context.dwarfRegisterBytes(register);
237261 if (src.len != out.len) return error.RegisterSizeMismatch;
238262 @memcpy(out, src);
263 return false;
239264 },
240265 .expression => |expression| {
241266 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 };
248277 const ptr: *usize = @ptrFromInt(addr);
249278 mem.writeInt(usize, out[0..@sizeOf(usize)], ptr.*, native_endian);
279 return false;
250280 },
251281 .val_expression => |expression| {
252282 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;
258295 },
259296 .architectural => return error.UnimplementedRegisterRule,
260297 }
......@@ -277,9 +314,6 @@ pub const DwarfUnwindContext = struct {
277314 return unwindFrameInner(context, gpa, unwind, load_offset, explicit_fde_offset) catch |err| switch (err) {
278315 error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e,
279316
280 error.UnimplementedArch,
281 error.UnimplementedOs,
282 error.ThreadContextNotSupported,
283317 error.UnimplementedRegisterRule,
284318 error.UnsupportedAddrSize,
285319 error.UnsupportedDwarfVersion,
......@@ -289,10 +323,10 @@ pub const DwarfUnwindContext = struct {
289323 error.UnimplementedTypedComparison,
290324 error.UnimplementedTypeConversion,
291325 error.UnknownExpressionOpcode,
326 error.UnsupportedRegister,
292327 => return error.UnsupportedDebugInfo,
293328
294329 error.InvalidRegister,
295 error.RegisterContextRequired,
296330 error.ReadFailed,
297331 error.EndOfStream,
298332 error.IncompatibleRegisterSize,
......@@ -346,20 +380,17 @@ pub const DwarfUnwindContext = struct {
346380 // may not reference other debug sections anyway.
347381 var expression_context: Dwarf.expression.Context = .{
348382 .format = format,
349 .thread_context = context.thread_context,
350 .reg_context = context.reg_context,
383 .cpu_context = &context.cpu_context,
351384 .cfa = context.cfa,
352385 };
353386
354387 context.vm.reset();
355 context.reg_context.eh_frame = cie.version != 4;
356 context.reg_context.is_macho = native_os.isDarwin();
357388
358389 const row = try context.vm.runTo(gpa, pc_vaddr, cie, fde, @sizeOf(usize), native_endian);
359390 context.cfa = switch (row.cfa.rule) {
360391 .val_offset => |offset| blk: {
361392 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)).*;
363394 break :blk try applyOffset(value, offset);
364395 },
365396 .expression => |expr| blk: {
......@@ -381,73 +412,41 @@ pub const DwarfUnwindContext = struct {
381412
382413 expression_context.cfa = context.cfa;
383414
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;
399415 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
400423 for (context.vm.rowColumns(row)) |column| {
401424 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);
402427 if (register == cie.return_address_register) {
403 has_return_address = column.rule != .undefined;
428 has_return_address = !rule_undef;
404429 }
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;
417430 }
418431 }
419432
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;
427437
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;
437439
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;
440442
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;
451450
452451 return return_address;
453452 }
......@@ -479,4 +478,18 @@ pub const DwarfUnwindContext = struct {
479478
480479 return ptr;
481480 }
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);
482495};
lib/std/debug/SelfInfo/DarwinModule.zig+24-33
......@@ -265,12 +265,9 @@ pub fn unwindFrame(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
265265 error.OutOfMemory,
266266 error.Unexpected,
267267 => |e| return e,
268 error.UnimplementedArch,
269 error.UnimplementedOs,
270 error.ThreadContextNotSupported,
268 error.UnsupportedRegister,
271269 => return error.UnsupportedDebugInfo,
272270 error.InvalidRegister,
273 error.RegisterContextRequired,
274271 error.IncompatibleRegisterSize,
275272 => return error.InvalidDebugInfo,
276273 };
......@@ -396,7 +393,6 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
396393 };
397394
398395 if (entry.raw_encoding == 0) return error.MissingDebugInfo;
399 const reg_context: Dwarf.abi.RegisterContext = .{ .eh_frame = false, .is_macho = true };
400396
401397 const encoding: macho.CompactUnwindEncoding = @bitCast(entry.raw_encoding);
402398 const new_ip = switch (builtin.cpu.arch) {
......@@ -405,16 +401,16 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
405401 .RBP_FRAME => ip: {
406402 const frame = encoding.value.x86_64.frame;
407403
408 const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*;
404 const fp = (try dwarfRegNative(&context.cpu_context, fp_reg_num)).*;
409405 const new_sp = fp + 2 * @sizeOf(usize);
410406
411407 const ip_ptr = fp + @sizeOf(usize);
412408 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
413409 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
414410
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;
418414
419415 const regs: [5]u3 = .{
420416 frame.reg0,
......@@ -427,7 +423,7 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
427423 if (reg == 0) continue;
428424 const addr = fp - frame.frame_offset * @sizeOf(usize) + i * @sizeOf(usize);
429425 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)).*;
431427 }
432428
433429 break :ip new_ip;
......@@ -437,7 +433,7 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
437433 => ip: {
438434 const frameless = encoding.value.x86_64.frameless;
439435
440 const sp = (try regValueNative(context.thread_context, spRegNum(reg_context), reg_context)).*;
436 const sp = (try dwarfRegNative(&context.cpu_context, sp_reg_num)).*;
441437 const stack_size: usize = stack_size: {
442438 if (encoding.mode.x86_64 == .STACK_IMMD) {
443439 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,
487483 var reg_addr = sp + stack_size - @sizeOf(usize) * @as(usize, reg_count + 1);
488484 for (0..reg_count) |i| {
489485 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)).*;
491487 reg_addr += @sizeOf(usize);
492488 }
493489
......@@ -497,8 +493,8 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
497493 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
498494 const new_sp = ip_ptr + @sizeOf(usize);
499495
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;
502498
503499 break :ip new_ip;
504500 },
......@@ -516,10 +512,10 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
516512 .aarch64, .aarch64_be => switch (encoding.mode.arm64) {
517513 .OLD => return error.UnsupportedDebugInfo,
518514 .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)).*;
520516 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;
523519 break :ip new_ip;
524520 },
525521 .DWARF => {
......@@ -535,15 +531,15 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
535531 .FRAME => ip: {
536532 const frame = encoding.value.arm64.frame;
537533
538 const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*;
534 const fp = (try dwarfRegNative(&context.cpu_context, fp_reg_num)).*;
539535 const ip_ptr = fp + @sizeOf(usize);
540536
541537 var reg_addr = fp - @sizeOf(usize);
542538 inline for (@typeInfo(@TypeOf(frame.x_reg_pairs)).@"struct".fields, 0..) |field, i| {
543539 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)).*;
545541 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)).*;
547543 reg_addr += @sizeOf(usize);
548544 }
549545 }
......@@ -552,12 +548,12 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
552548 if (@field(frame.d_reg_pairs, field.name) != 0) {
553549 // Only the lower half of the 128-bit V registers are restored during unwinding
554550 {
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));
556552 dest.* = @as(*const usize, @ptrFromInt(reg_addr)).*;
557553 }
558554 reg_addr += @sizeOf(usize);
559555 {
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));
561557 dest.* = @as(*const usize, @ptrFromInt(reg_addr)).*;
562558 }
563559 reg_addr += @sizeOf(usize);
......@@ -567,8 +563,8 @@ fn unwindFrameInner(module: *const DarwinModule, gpa: Allocator, di: *DebugInfo,
567563 const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*;
568564 const new_fp = @as(*const usize, @ptrFromInt(fp)).*;
569565
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;
572568
573569 break :ip new_ip;
574570 },
......@@ -782,13 +778,9 @@ test {
782778 _ = MachoSymbol;
783779}
784780
785fn fpRegNum(reg_context: Dwarf.abi.RegisterContext) u8 {
786 return Dwarf.abi.fpRegNum(builtin.target.cpu.arch, reg_context);
787}
788fn spRegNum(reg_context: Dwarf.abi.RegisterContext) u8 {
789 return Dwarf.abi.spRegNum(builtin.target.cpu.arch, reg_context);
790}
791const ip_reg_num = Dwarf.abi.ipRegNum(builtin.target.cpu.arch).?;
781const ip_reg_num = Dwarf.ipRegNum(builtin.target.cpu.arch).?;
782const fp_reg_num = Dwarf.fpRegNum(builtin.target.cpu.arch);
783const sp_reg_num = Dwarf.spRegNum(builtin.target.cpu.arch);
792784
793785/// Uses `mmap` to map the file at `path` into memory.
794786fn mapDebugInfoFile(path: []const u8) ![]align(std.heap.page_size_min) const u8 {
......@@ -821,8 +813,7 @@ const mem = std.mem;
821813const posix = std.posix;
822814const testing = std.testing;
823815const Error = std.debug.SelfInfo.Error;
824const regBytes = Dwarf.abi.regBytes;
825const regValueNative = Dwarf.abi.regValueNative;
816const dwarfRegNative = std.debug.SelfInfo.DwarfUnwindContext.regNative;
826817
827818const builtin = @import("builtin");
828819const 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 {
2626pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) Error!ElfModule {
2727 _ = cache;
2828 _ = gpa;
29 if (builtin.target.os.tag == .haiku) @panic("TODO implement lookup module for Haiku");
3029 const DlIterContext = struct {
3130 /// input
3231 address: usize,
......@@ -261,7 +260,7 @@ pub const supports_unwinding: bool = s: {
261260};
262261comptime {
263262 if (supports_unwinding) {
264 std.debug.assert(Dwarf.abi.supportsUnwinding(&builtin.target));
263 std.debug.assert(Dwarf.supportsUnwinding(&builtin.target));
265264 }
266265}
267266
lib/std/debug/SelfInfo/WindowsModule.zig+37-3
......@@ -295,11 +295,45 @@ pub const UnwindContext = struct {
295295 pc: usize,
296296 cur: windows.CONTEXT,
297297 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 {
300299 return .{
301300 .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 },
303337 .history_table = std.mem.zeroes(windows.UNWIND_HISTORY_TABLE),
304338 };
305339 }
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`.
4pub const Native = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "CpuContext"))
5 root.debug.CpuContext
6else 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
14pub const DwarfRegisterError = error{
15 InvalidRegister,
16 UnsupportedRegister,
17};
18
19pub 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
180pub 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
221pub 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
279pub 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
344pub 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.
399pub 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
461const 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
1015const std = @import("../std.zig");
1016const root = @import("root");
1017const builtin = @import("builtin");
1018const native_arch = @import("builtin").target.cpu.arch;
1019const 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 {
183183
184184 if (posix.MREMAP != void) {
185185 // 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;
187187 return new_memory.ptr;
188188 }
189189
lib/std/os/freebsd.zig+73
......@@ -3,6 +3,7 @@ const fd_t = std.c.fd_t;
33const off_t = std.c.off_t;
44const unexpectedErrno = std.posix.unexpectedErrno;
55const errno = std.posix.errno;
6const builtin = @import("builtin");
67
78pub const CopyFileRangeError = std.posix.UnexpectedError || error{
89 /// 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,
4748 else => |err| return unexpectedErrno(err),
4849 }
4950}
51
52pub 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) {
4949 .s390x => @import("linux/s390x.zig"),
5050 else => struct {
5151 pub const ucontext_t = void;
52 pub const getcontext = {};
5352 },
5453};
5554
......@@ -112,7 +111,6 @@ pub const timeval = arch_bits.timeval;
112111pub const timezone = arch_bits.timezone;
113112pub const ucontext_t = arch_bits.ucontext_t;
114113pub const user_desc = arch_bits.user_desc;
115pub const getcontext = arch_bits.getcontext;
116114
117115pub const tls = @import("linux/tls.zig");
118116pub const BPF = @import("linux/bpf.zig");
lib/std/os/linux/aarch64.zig-3
......@@ -260,7 +260,4 @@ pub const ucontext_t = extern struct {
260260 mcontext: mcontext_t,
261261};
262262
263/// TODO
264pub const getcontext = {};
265
266263pub const Elf_Symndx = u32;
lib/std/os/linux/arm.zig-3
......@@ -310,7 +310,4 @@ pub const ucontext_t = extern struct {
310310 regspace: [64]u64,
311311};
312312
313/// TODO
314pub const getcontext = {};
315
316313pub const Elf_Symndx = u32;
lib/std/os/linux/hexagon.zig-3
......@@ -237,6 +237,3 @@ pub const VDSO = void;
237237
238238/// TODO
239239pub const ucontext_t = void;
240
241/// TODO
242pub const getcontext = {};
lib/std/os/linux/loongarch64.zig-3
......@@ -250,6 +250,3 @@ pub const ucontext_t = extern struct {
250250};
251251
252252pub const Elf_Symndx = u32;
253
254/// TODO
255pub const getcontext = {};
lib/std/os/linux/m68k.zig-3
......@@ -258,6 +258,3 @@ pub const VDSO = void;
258258
259259/// TODO
260260pub const ucontext_t = void;
261
262/// TODO
263pub const getcontext = {};
lib/std/os/linux/mips.zig-3
......@@ -349,6 +349,3 @@ pub const Elf_Symndx = u32;
349349
350350/// TODO
351351pub const ucontext_t = void;
352
353/// TODO
354pub const getcontext = {};
lib/std/os/linux/mips64.zig-3
......@@ -328,6 +328,3 @@ pub const Elf_Symndx = u32;
328328
329329/// TODO
330330pub const ucontext_t = void;
331
332/// TODO
333pub const getcontext = {};
lib/std/os/linux/powerpc.zig-3
......@@ -381,6 +381,3 @@ pub const ucontext_t = extern struct {
381381};
382382
383383pub const Elf_Symndx = u32;
384
385/// TODO
386pub const getcontext = {};
lib/std/os/linux/powerpc64.zig-3
......@@ -376,6 +376,3 @@ pub const ucontext_t = extern struct {
376376};
377377
378378pub const Elf_Symndx = u32;
379
380/// TODO
381pub const getcontext = {};
lib/std/os/linux/riscv32.zig-3
......@@ -255,6 +255,3 @@ pub const ucontext_t = extern struct {
255255 sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
256256 mcontext: mcontext_t,
257257};
258
259/// TODO
260pub const getcontext = {};
lib/std/os/linux/riscv64.zig-3
......@@ -255,6 +255,3 @@ pub const ucontext_t = extern struct {
255255 sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
256256 mcontext: mcontext_t,
257257};
258
259/// TODO
260pub const getcontext = {};
lib/std/os/linux/s390x.zig-3
......@@ -273,6 +273,3 @@ pub const mcontext_t = extern struct {
273273 __regs2: [18]u32,
274274 __regs3: [16]f64,
275275};
276
277/// TODO
278pub const getcontext = {};
lib/std/os/linux/sparc64.zig-3
......@@ -426,6 +426,3 @@ pub const ucontext_t = extern struct {
426426 stack: stack_t,
427427 sigset: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
428428};
429
430/// TODO
431pub const getcontext = {};
lib/std/os/linux/x86.zig-14
......@@ -436,17 +436,3 @@ pub fn getContextInternal() callconv(.naked) usize {
436436 [sigset_size] "i" (linux.NSIG / 8),
437437 : .{ .cc = true, .memory = true, .eax = true, .ecx = true, .edx = true });
438438}
439
440pub 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 {
352352 sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a glibc-compatible (1024-bit) sigmask.
353353 fpregs_mem: [64]usize, // Not part of kernel ABI, only part of glibc ucontext_t
354354};
355
356fn gpRegisterOffset(comptime reg_index: comptime_int) usize {
357 return @offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "gregs") + @sizeOf(usize) * reg_index;
358}
359
360fn 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
437pub 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) {
4747 .linux => linux,
4848 .plan9 => std.os.plan9,
4949 else => struct {
50 pub const getcontext = {};
51 pub const ucontext_t = void;
5250 pub const pid_t = void;
5351 pub const pollfd = void;
5452 pub const fd_t = void;
......@@ -142,7 +140,6 @@ pub const in_pktinfo = system.in_pktinfo;
142140pub const in6_pktinfo = system.in6_pktinfo;
143141pub const ino_t = system.ino_t;
144142pub const linger = system.linger;
145pub const mcontext_t = system.mcontext_t;
146143pub const mode_t = system.mode_t;
147144pub const msghdr = system.msghdr;
148145pub const msghdr_const = system.msghdr_const;
......@@ -171,7 +168,6 @@ pub const timespec = system.timespec;
171168pub const timestamp_t = system.timestamp_t;
172169pub const timeval = system.timeval;
173170pub const timezone = system.timezone;
174pub const ucontext_t = system.ucontext_t;
175171pub const uid_t = system.uid_t;
176172pub const user_desc = system.user_desc;
177173pub const utsname = system.utsname;