summaryrefslogtreecommitdiffstats
path: root/third_party/rust/crash-context/src
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/crash-context/src')
-rw-r--r--third_party/rust/crash-context/src/lib.rs38
-rw-r--r--third_party/rust/crash-context/src/linux.rs273
-rw-r--r--third_party/rust/crash-context/src/linux/getcontext.rs24
-rw-r--r--third_party/rust/crash-context/src/linux/getcontext/aarch64.rs86
-rw-r--r--third_party/rust/crash-context/src/linux/getcontext/arm.rs53
-rw-r--r--third_party/rust/crash-context/src/linux/getcontext/x86.rs53
-rw-r--r--third_party/rust/crash-context/src/linux/getcontext/x86_64.rs105
-rw-r--r--third_party/rust/crash-context/src/mac.rs32
-rw-r--r--third_party/rust/crash-context/src/mac/guard.rs92
-rw-r--r--third_party/rust/crash-context/src/mac/ipc.rs553
-rw-r--r--third_party/rust/crash-context/src/mac/resource.rs516
-rw-r--r--third_party/rust/crash-context/src/windows.rs262
12 files changed, 2087 insertions, 0 deletions
diff --git a/third_party/rust/crash-context/src/lib.rs b/third_party/rust/crash-context/src/lib.rs
new file mode 100644
index 0000000000..4056de8e73
--- /dev/null
+++ b/third_party/rust/crash-context/src/lib.rs
@@ -0,0 +1,38 @@
+//! This crate exposes a platform specific [`CrashContext`] which contains the
+//! details for a crash (signal, exception, etc). This crate is fairly minimal
+//! since the intended use case is to more easily share these crash details
+//! between different crates without requiring lots of dependencies, and is
+//! currently only really made for the purposes of the crates in this repo, and
+//! [minidump-writer](https://github.com/rust-minidump/minidump-writer).
+//!
+//! ## Linux/Android
+//!
+//! This crate also contains a portable implementation of [`getcontext`](
+//! https://man7.org/linux/man-pages/man3/getcontext.3.html), as not all libc
+//! implementations (notably `musl`) implement it as it has been deprecated from
+//! POSIX.
+//!
+//! ## Macos
+//!
+//! One major difference on Macos is that the details in the [`CrashContext`]
+//! cannot be transferred to another process via normal methods (eg. sockets)
+//! and must be sent via the criminally undocumented mach ports. This crate
+//! provides a `Client` and `Server` that can be used to send and receive a
+//! [`CrashContext`] across processes so that you don't have to suffer like I
+//! did.
+
+// crate-specific exceptions:
+#![allow(unsafe_code, nonstandard_style)]
+
+cfg_if::cfg_if! {
+ if #[cfg(any(target_os = "linux", target_os = "android"))] {
+ mod linux;
+ pub use linux::*;
+ } else if #[cfg(target_os = "windows")] {
+ mod windows;
+ pub use windows::*;
+ } else if #[cfg(target_os = "macos")] {
+ mod mac;
+ pub use mac::*;
+ }
+}
diff --git a/third_party/rust/crash-context/src/linux.rs b/third_party/rust/crash-context/src/linux.rs
new file mode 100644
index 0000000000..f9952c12d9
--- /dev/null
+++ b/third_party/rust/crash-context/src/linux.rs
@@ -0,0 +1,273 @@
+mod getcontext;
+
+pub use getcontext::crash_context_getcontext;
+
+/// The full context for a Linux/Android crash
+#[repr(C)]
+#[derive(Clone)]
+pub struct CrashContext {
+ /// Crashing thread context.
+ ///
+ /// Note that we use [`crate::ucontext_t`] instead of [`libc::ucontext_t`]
+ /// as libc's differs between glibc and musl <https://github.com/rust-lang/libc/pull/1646>
+ /// even though the ucontext_t received from a signal will be the same
+ /// regardless of the libc implementation used as it is only arch specific
+ /// and not libc specific
+ ///
+ /// Note that we hide `ucontext_t::uc_link` as it is a pointer and thus can't
+ /// be accessed in a process other than the one the `CrashContext` was created
+ /// in. This is a just a self-reference so is not useful in practice.
+ ///
+ /// Note that the same applies to [`mcontext_t::fpregs`], but since that points
+ /// to floating point registers and _is_ interesting to read in another process,
+ /// those registers available as [`Self::float_state`], except on the `arm`
+ /// architecture since they aren't part of `mcontext_t` at all.
+ pub context: ucontext_t,
+ /// State of floating point registers.
+ ///
+ /// This isn't part of the user ABI for Linux arm
+ #[cfg(not(target_arch = "arm"))]
+ pub float_state: fpregset_t,
+ /// The signal info for the crash
+ pub siginfo: libc::signalfd_siginfo,
+ /// The id of the crashing process
+ pub pid: libc::pid_t,
+ /// The id of the crashing thread
+ pub tid: libc::pid_t,
+}
+
+unsafe impl Send for CrashContext {}
+
+impl CrashContext {
+ pub fn as_bytes(&self) -> &[u8] {
+ unsafe {
+ let size = std::mem::size_of_val(self);
+ let ptr = (self as *const Self).cast();
+ std::slice::from_raw_parts(ptr, size)
+ }
+ }
+
+ pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
+ if bytes.len() != std::mem::size_of::<Self>() {
+ return None;
+ }
+
+ unsafe { Some((*bytes.as_ptr().cast::<Self>()).clone()) }
+ }
+}
+
+#[repr(C)]
+#[derive(Clone)]
+#[doc(hidden)]
+pub struct sigset_t {
+ #[cfg(target_pointer_width = "32")]
+ __val: [u32; 32],
+ #[cfg(target_pointer_width = "64")]
+ __val: [u64; 16],
+}
+
+#[repr(C)]
+#[derive(Clone)]
+#[doc(hidden)]
+pub struct stack_t {
+ pub ss_sp: *mut std::ffi::c_void,
+ pub ss_flags: i32,
+ pub ss_size: usize,
+}
+
+cfg_if::cfg_if! {
+ if #[cfg(target_arch = "x86_64")] {
+ #[repr(C)]
+ #[derive(Clone)]
+ #[doc(hidden)]
+ pub struct ucontext_t {
+ pub uc_flags: u64,
+ uc_link: *mut ucontext_t,
+ pub uc_stack: stack_t,
+ pub uc_mcontext: mcontext_t,
+ pub uc_sigmask: sigset_t,
+ __private: [u8; 512],
+ }
+
+ #[repr(C)]
+ #[derive(Clone)]
+ #[doc(hidden)]
+ pub struct mcontext_t {
+ pub gregs: [i64; 23],
+ pub fpregs: *mut fpregset_t,
+ __reserved: [u64; 8],
+ }
+
+ #[repr(C)]
+ #[derive(Clone)]
+ #[doc(hidden)]
+ pub struct fpregset_t {
+ pub cwd: u16,
+ pub swd: u16,
+ pub ftw: u16,
+ pub fop: u16,
+ pub rip: u64,
+ pub rdp: u64,
+ pub mxcsr: u32,
+ pub mxcr_mask: u32,
+ pub st_space: [u32; 32],
+ pub xmm_space: [u32; 64],
+ __padding: [u64; 12],
+ }
+ } else if #[cfg(target_arch = "x86")] {
+ #[repr(C)]
+ #[derive(Clone)]
+ #[doc(hidden)]
+ pub struct ucontext_t {
+ pub uc_flags: u32,
+ uc_link: *mut ucontext_t,
+ pub uc_stack: stack_t,
+ pub uc_mcontext: mcontext_t,
+ pub uc_sigmask: sigset_t,
+ pub __fpregs_mem: [u32; 28],
+ }
+
+ #[repr(C)]
+ #[derive(Clone)]
+ #[doc(hidden)]
+ pub struct mcontext_t {
+ pub gregs: [i32; 23],
+ pub fpregs: *mut fpregset_t,
+ pub oldmask: u32,
+ pub cr2: u32,
+ }
+
+ #[repr(C)]
+ #[derive(Clone)]
+ #[doc(hidden)]
+ pub struct fpreg_t {
+ pub significand: [u16; 4],
+ pub exponent: u16,
+ }
+
+ #[repr(C)]
+ #[derive(Clone)]
+ #[doc(hidden)]
+ pub struct fpregset_t {
+ pub cw: u32,
+ pub sw: u32,
+ pub tag: u32,
+ pub ipoff: u32,
+ pub cssel: u32,
+ pub dataoff: u32,
+ pub datasel: u32,
+ pub _st: [fpreg_t; 8],
+ pub status: u32,
+ }
+ } else if #[cfg(target_arch = "aarch64")] {
+ #[repr(C)]
+ #[derive(Clone)]
+ #[doc(hidden)]
+ pub struct ucontext_t {
+ pub uc_flags: u64,
+ uc_link: *mut ucontext_t,
+ pub uc_stack: stack_t,
+ pub uc_sigmask: sigset_t,
+ pub uc_mcontext: mcontext_t,
+ }
+
+ // Note you might see this defined in C with `unsigned long` or
+ // `unsigned long long` and think, WTF, those aren't the same! Except
+ // `long` means either 32-bit _or_ 64-bit depending on the data model,
+ // and the default data model for C/C++ is LP64 which means long is
+ // 64-bit. I had forgotten what a trash type long was.
+ #[repr(C)]
+ #[derive(Clone)]
+ #[doc(hidden)]
+ pub struct mcontext_t {
+ // Note in the kernel this is just part of regs which is length 32
+ pub fault_address: u64,
+ pub regs: [u64; 31],
+ pub sp: u64,
+ pub pc: u64,
+ pub pstate: u64,
+ // Note that u128 is ABI safe on aarch64, this is actually a
+ // `long double` in C which Rust doesn't have native support
+ pub __reserved: [u128; 256],
+ }
+
+ /// Magic value written by the kernel and our custom getcontext
+ #[doc(hidden)]
+ pub const FPSIMD_MAGIC: u32 = 0x46508001;
+
+ #[repr(C)]
+ #[derive(Clone)]
+ #[doc(hidden)]
+ pub struct _aarch64_ctx {
+ pub magic: u32,
+ pub size: u32,
+ }
+
+ #[repr(C)]
+ #[derive(Clone)]
+ #[doc(hidden)]
+ pub struct fpsimd_context {
+ pub head: _aarch64_ctx,
+ pub fpsr: u32,
+ pub fpcr: u32,
+ pub vregs: [u128; 32],
+ }
+
+ #[doc(hidden)]
+ pub type fpregset_t = fpsimd_context;
+ } else if #[cfg(target_arch = "arm")] {
+ #[repr(C)]
+ #[derive(Clone)]
+ #[doc(hidden)]
+ pub struct ucontext_t {
+ pub uc_flags: u32,
+ uc_link: *mut ucontext_t,
+ pub uc_stack: stack_t,
+ // Note that the mcontext_t and sigset_t are swapped compared to
+ // all of the other arches currently supported :p
+ pub uc_mcontext: mcontext_t,
+ pub uc_sigmask: sigset_t,
+ pub uc_regspace: [u64; 64],
+ }
+
+ #[repr(C)]
+ #[derive(Clone)]
+ #[doc(hidden)]
+ pub struct mcontext_t {
+ pub trap_no: u32,
+ pub error_code: u32,
+ pub oldmask: u32,
+ pub arm_r0: u32,
+ pub arm_r1: u32,
+ pub arm_r2: u32,
+ pub arm_r3: u32,
+ pub arm_r4: u32,
+ pub arm_r5: u32,
+ pub arm_r6: u32,
+ pub arm_r7: u32,
+ pub arm_r8: u32,
+ pub arm_r9: u32,
+ pub arm_r10: u32,
+ pub arm_fp: u32,
+ pub arm_ip: u32,
+ pub arm_sp: u32,
+ pub arm_lr: u32,
+ pub arm_pc: u32,
+ pub arm_cpsr: u32,
+ pub fault_address: u32,
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ // Musl doesn't contain fpregs in libc because reasons https://github.com/rust-lang/libc/pull/1646
+ #[cfg(not(target_env = "musl"))]
+ #[test]
+ fn matches_libc() {
+ assert_eq!(
+ std::mem::size_of::<libc::ucontext_t>(),
+ std::mem::size_of::<super::ucontext_t>()
+ );
+ }
+}
diff --git a/third_party/rust/crash-context/src/linux/getcontext.rs b/third_party/rust/crash-context/src/linux/getcontext.rs
new file mode 100644
index 0000000000..93bb5fdaff
--- /dev/null
+++ b/third_party/rust/crash-context/src/linux/getcontext.rs
@@ -0,0 +1,24 @@
+//! Implementation of [`getcontext`](https://man7.org/linux/man-pages/man3/getcontext.3.html)
+
+extern "C" {
+ /// A portable implementation of [`getcontext`](https://man7.org/linux/man-pages/man3/getcontext.3.html)
+ /// since it is not supported by all libc implementations, namely `musl`, as
+ /// it has been deprecated from POSIX for over a decade
+ ///
+ /// The implementation is ported from Breakpad, which is itself ported from
+ /// libunwind
+ #[cfg_attr(target_arch = "aarch64", allow(improper_ctypes))]
+ pub fn crash_context_getcontext(ctx: *mut super::ucontext_t) -> i32;
+}
+
+cfg_if::cfg_if! {
+ if #[cfg(target_arch = "x86_64")] {
+ mod x86_64;
+ } else if #[cfg(target_arch = "x86")] {
+ mod x86;
+ } else if #[cfg(target_arch = "aarch64")] {
+ mod aarch64;
+ } else if #[cfg(target_arch = "arm")] {
+ mod arm;
+ }
+}
diff --git a/third_party/rust/crash-context/src/linux/getcontext/aarch64.rs b/third_party/rust/crash-context/src/linux/getcontext/aarch64.rs
new file mode 100644
index 0000000000..3cc7e66c97
--- /dev/null
+++ b/third_party/rust/crash-context/src/linux/getcontext/aarch64.rs
@@ -0,0 +1,86 @@
+// GREGS_OFFSET = 184
+// REGISTER_SIZE = 8
+// SIMD_REGISTER_SIZE = 16
+
+std::arch::global_asm! {
+ ".text",
+ ".global crash_context_getcontext",
+ ".hidden crash_context_getcontext",
+ ".type crash_context_getcontext, #function",
+ ".align 4",
+ ".cfi_startproc",
+"crash_context_getcontext:",
+
+ // The saved context will return to the getcontext() call point
+ // with a return value of 0
+ "str xzr, [x0, 184]", // GREGS_OFFSET
+
+ "stp x18, x19, [x0, 328]", // GREGS_OFFSET + 18 * REGISTER_SIZE
+ "stp x20, x21, [x0, 344]", // GREGS_OFFSET + 20 * REGISTER_SIZE
+ "stp x22, x23, [x0, 360]", // GREGS_OFFSET + 22 * REGISTER_SIZE
+ "stp x24, x25, [x0, 376]", // GREGS_OFFSET + 24 * REGISTER_SIZE
+ "stp x26, x27, [x0, 392]", // GREGS_OFFSET + 26 * REGISTER_SIZE
+ "stp x28, x29, [x0, 408]", // GREGS_OFFSET + 28 * REGISTER_SIZE
+ "str x30, [x0, 424]", // GREGS_OFFSET + 30 * REGISTER_SIZE
+
+ // Place LR into the saved PC, this will ensure that when switching to this
+ // saved context with setcontext() control will pass back to the caller of
+ // getcontext(), we have already arranged to return the appropriate return
+ // value in x0 above.
+ "str x30, [x0, 440]",
+
+ // Save the current SP
+ "mov x2, sp",
+ "str x2, [x0, 432]",
+
+ // Initialize the pstate.
+ "str xzr, [x0, 448]",
+
+ // Figure out where to place the first context extension block.
+ "add x2, x0, #464",
+
+ // Write the context extension fpsimd header.
+ // FPSIMD_MAGIC = 0x46508001
+ "mov w3, #(0x46508001 & 0xffff)",
+ "movk w3, #(0x46508001 >> 16), lsl #16",
+ "str w3, [x2, #0]", // FPSIMD_CONTEXT_MAGIC_OFFSET
+ "mov w3, #528", // FPSIMD_CONTEXT_SIZE
+ "str w3, [x2, #4]", // FPSIMD_CONTEXT_SIZE_OFFSET
+
+ // Fill in the FP SIMD context.
+ "add x3, x2, #144", // VREGS_OFFSET + 8 * SIMD_REGISTER_SIZE
+ "stp d8, d9, [x3], #32",
+ "stp d10, d11, [x3], #32",
+ "stp d12, d13, [x3], #32",
+ "stp d14, d15, [x3], #32",
+
+ "add x3, x2, 8", // FPSR_OFFSET
+
+ "mrs x4, fpsr",
+ "str w4, [x3]",
+
+ "mrs x4, fpcr",
+ "str w4, [x3, 4]", // FPCR_OFFSET - FPSR_OFFSET
+
+ // Write the termination context extension header.
+ "add x2, x2, #528", // FPSIMD_CONTEXT_SIZE
+
+ "str xzr, [x2, #0]", // FPSIMD_CONTEXT_MAGIC_OFFSET
+ "str xzr, [x2, #4]", // FPSIMD_CONTEXT_SIZE_OFFSET
+
+ // Grab the signal mask
+ // rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8)
+ "add x2, x0, #40", // UCONTEXT_SIGMASK_OFFSET
+ "mov x0, #0", // SIG_BLOCK
+ "mov x1, #0", // NULL
+ "mov x3, #(64 / 8)", // _NSIG / 8
+ "mov x8, #135", // __NR_rt_sigprocmask
+ "svc 0",
+
+ // Return x0 for success
+ "mov x0, 0",
+ "ret",
+
+ ".cfi_endproc",
+ ".size crash_context_getcontext, . - crash_context_getcontext",
+}
diff --git a/third_party/rust/crash-context/src/linux/getcontext/arm.rs b/third_party/rust/crash-context/src/linux/getcontext/arm.rs
new file mode 100644
index 0000000000..0734634a3e
--- /dev/null
+++ b/third_party/rust/crash-context/src/linux/getcontext/arm.rs
@@ -0,0 +1,53 @@
+std::arch::global_asm! {
+ ".text",
+ ".global crash_context_getcontext",
+ ".hidden crash_context_getcontext",
+ ".type crash_context_getcontext, #function",
+ ".align 0",
+ ".fnstart",
+"crash_context_getcontext:",
+
+ // First, save r4-r11
+ "add r1, r0, #(32 + 4 * 4)",
+ "stm r1, {{r4-r11}}",
+
+ // r12 is a scratch register, don't save it
+
+ // Save sp and lr explicitly.
+ // - sp can't be stored with stmia in Thumb-2
+ // - STM instructions that store sp and pc are deprecated in ARM
+ "str sp, [r0, #(32 + 13 * 4)]",
+ "str lr, [r0, #(32 + 14 * 4)]",
+
+ // Save the caller's address in 'pc'
+ "str lr, [r0, #(32 + 15 * 4)]",
+
+ // Save ucontext_t* pointer across next call
+ "mov r4, r0",
+
+ // Call sigprocmask(SIG_BLOCK, NULL, &(ucontext->uc_sigmask))
+ "mov r0, #0", // SIG_BLOCK
+ "mov r1, #0", // NULL
+ "add r2, r4, #104", // UCONTEXT_SIGMASK_OFFSET
+ "bl sigprocmask(PLT)",
+
+ /* Intentionally do not save the FPU state here. This is because on
+ * Linux/ARM, one should instead use ptrace(PTRACE_GETFPREGS) or
+ * ptrace(PTRACE_GETVFPREGS) to get it.
+ *
+ * Note that a real implementation of getcontext() would need to save
+ * this here to allow setcontext()/swapcontext() to work correctly.
+ */
+
+ // Restore the values of r4 and lr
+ "mov r0, r4",
+ "ldr lr, [r0, #(32 + 14 * 4)]",
+ "ldr r4, [r0, #(32 + 4 * 4)]",
+
+ // Return 0
+ "mov r0, #0",
+ "bx lr",
+
+ ".fnend",
+ ".size crash_context_getcontext, . - crash_context_getcontext",
+}
diff --git a/third_party/rust/crash-context/src/linux/getcontext/x86.rs b/third_party/rust/crash-context/src/linux/getcontext/x86.rs
new file mode 100644
index 0000000000..b9d4b5ab72
--- /dev/null
+++ b/third_party/rust/crash-context/src/linux/getcontext/x86.rs
@@ -0,0 +1,53 @@
+#[cfg(target_os = "android")]
+compile_error!("please file an issue if you care about this target");
+
+std::arch::global_asm! {
+ ".text",
+ ".global crash_context_getcontext",
+ ".hidden crash_context_getcontext",
+ ".align 4",
+ ".type crash_context_getcontext, @function",
+"crash_context_getcontext:",
+ "movl 4(%esp), %eax", // eax = uc
+
+ // Save register values
+ "movl %ecx, 0x3c(%eax)",
+ "movl %edx, 0x38(%eax)",
+ "movl %ebx, 0x34(%eax)",
+ "movl %edi, 0x24(%eax)",
+ "movl %esi, 0x28(%eax)",
+ "movl %ebp, 0x2c(%eax)",
+
+ "movl (%esp), %edx", /* return address */
+ "lea 4(%esp), %ecx", /* exclude return address from stack */
+ "mov %edx, 0x4c(%eax)",
+ "mov %ecx, 0x30(%eax)",
+
+ "xorl %ecx, %ecx",
+ "movw %fs, %cx",
+ "mov %ecx, 0x18(%eax)",
+
+ "movl $0, 0x40(%eax)",
+
+ // Save floating point state to fpregstate, then update
+ // the fpregs pointer to point to it
+ "leal 0xec(%eax), %ecx",
+ "fnstenv (%ecx)",
+ "fldenv (%ecx)",
+ "mov %ecx, 0x60(%eax)",
+
+ // Save signal mask: sigprocmask(SIGBLOCK, NULL, &uc->uc_sigmask)
+ "leal 0x6c(%eax), %edx",
+ "xorl %ecx, %ecx",
+ "push %edx", /* &uc->uc_sigmask */
+ "push %ecx", /* NULL */
+ "push %ecx", /* SIGBLOCK == 0 on i386 */
+ "call sigprocmask@PLT",
+ "addl $12, %esp",
+
+ "movl $0, %eax",
+ "ret",
+
+ ".size crash_context_getcontext, . - crash_context_getcontext",
+ options(att_syntax)
+}
diff --git a/third_party/rust/crash-context/src/linux/getcontext/x86_64.rs b/third_party/rust/crash-context/src/linux/getcontext/x86_64.rs
new file mode 100644
index 0000000000..0eba4b88ce
--- /dev/null
+++ b/third_party/rust/crash-context/src/linux/getcontext/x86_64.rs
@@ -0,0 +1,105 @@
+/* The x64 implementation of breakpad_getcontext was derived in part
+from the implementation of libunwind which requires the following
+notice. */
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2008 Google, Inc
+ Contributed by Paul Pluzhnikov <ppluzhnikov@google.com>
+ Copyright (C) 2010 Konstantin Belousov <kib@freebsd.org>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+// Oof, unfortunately android libc and normal linux libc don't have the same
+// fpregs offset in ucontext_t, but asm! in Rust is...not convenient for only
+// adding certain lines/blocks of asm based using cfg https://github.com/rust-lang/rust/issues/15701
+// and they're not really inputs, just literals, so...yah
+
+#[cfg(target_os = "linux")]
+
+// Unfortunately, the asm! macro has a few really annoying limitations at the
+// moment
+//
+// 1. const operands are unstable
+// 2. cfg attributes can't be used inside the asm macro at all
+//
+// and the worst part is we need it for literally only 1 thing, using a different
+// offset to the fpstate in ucontext depending on whether we are targeting android
+// or not :(
+macro_rules! asm_func {
+ ($offset:expr) => {
+ std::arch::global_asm! {
+ ".text",
+ ".global crash_context_getcontext",
+ ".hidden crash_context_getcontext",
+ ".align 4",
+ ".type crash_context_getcontext, @function",
+ "crash_context_getcontext:",
+ ".cfi_startproc",
+ // Callee saved: RBX, RBP, R12-R15
+ "movq %r12, 0x48(%rdi)",
+ "movq %r13, 0x50(%rdi)",
+ "movq %r14, 0x58(%rdi)",
+ "movq %r15, 0x60(%rdi)",
+ "movq %rbp, 0x78(%rdi)",
+ "movq %rbx, 0x80(%rdi)",
+
+ // Save argument registers
+ "movq %r8, 0x28(%rdi)",
+ "movq %r9, 0x30(%rdi)",
+ "movq %rdi, 0x68(%rdi)",
+ "movq %rsi, 0x70(%rdi)",
+ "movq %rdx, 0x88(%rdi)",
+ "movq %rax, 0x90(%rdi)",
+ "movq %rcx, 0x98(%rdi)",
+
+ // Save fp state
+ stringify!(leaq $offset(%rdi),%r8),
+ "movq %r8, 0xe0(%rdi)",
+ "fnstenv (%r8)",
+ "stmxcsr 0x18(%r8)",
+
+ // Exclude this call
+ "leaq 8(%rsp), %rax",
+ "movq %rax, 0xa0(%rdi)",
+
+ "movq 0(%rsp), %rax",
+ "movq %rax, 0xa8(%rdi)",
+
+ // Save signal mask: sigprocmask(SIGBLOCK, NULL, &uc->uc_sigmask)
+ "leaq 0x128(%rdi), %rdx", // arg3
+ "xorq %rsi, %rsi", // arg2 NULL
+ "xorq %rdi, %rdi", // arg1 SIGBLOCK == 0
+ "call sigprocmask@PLT",
+
+ // Always return 0 for success, even if sigprocmask failed.
+ "xorl %eax, %eax",
+ "ret",
+ ".cfi_endproc",
+ ".size crash_context_getcontext, . - crash_context_getcontext",
+ options(att_syntax)
+ }
+ };
+}
+
+#[cfg(target_os = "linux")]
+asm_func!(0x1a8);
+#[cfg(target_os = "android")]
+asm_func!(0x130);
diff --git a/third_party/rust/crash-context/src/mac.rs b/third_party/rust/crash-context/src/mac.rs
new file mode 100644
index 0000000000..fcbe97df69
--- /dev/null
+++ b/third_party/rust/crash-context/src/mac.rs
@@ -0,0 +1,32 @@
+pub mod guard;
+pub mod ipc;
+pub mod resource;
+
+use mach2::mach_types as mt;
+
+/// Information on the exception that caused the crash
+#[derive(Copy, Clone, Debug)]
+pub struct ExceptionInfo {
+ /// The exception kind
+ pub kind: u32,
+ /// The exception code
+ pub code: u64,
+ /// Optional subcode with different meanings depending on the exception type
+ /// * `EXC_BAD_ACCESS` - The address that caused the exception
+ /// * `EXC_GUARD` - The unique guard identifier that was guarding a resource
+ /// * `EXC_RESOURCE` - Additional details depending on the resource type
+ pub subcode: Option<u64>,
+}
+
+/// Full Macos crash context
+#[derive(Debug)]
+pub struct CrashContext {
+ /// The process which crashed
+ pub task: mt::task_t,
+ /// The thread in the process that crashed
+ pub thread: mt::thread_t,
+ /// The thread that handled the exception. This may be useful to ignore.
+ pub handler_thread: mt::thread_t,
+ /// Optional exception information
+ pub exception: Option<ExceptionInfo>,
+}
diff --git a/third_party/rust/crash-context/src/mac/guard.rs b/third_party/rust/crash-context/src/mac/guard.rs
new file mode 100644
index 0000000000..7caee5ffe3
--- /dev/null
+++ b/third_party/rust/crash-context/src/mac/guard.rs
@@ -0,0 +1,92 @@
+//! Contains types and helpers for dealing with `EXC_GUARD` exceptions.
+//!
+//! `EXC_GUARD` exceptions embed details about the guarded resource in the `code`
+//! and `subcode` fields of the exception
+//!
+//! See <https://github.com/apple-oss-distributions/xnu/blob/e7776783b89a353188416a9a346c6cdb4928faad/osfmk/kern/exc_guard.h>
+//! for the top level types that this module wraps.
+
+use mach2::exception_types::EXC_GUARD;
+
+/// The set of possible guard kinds
+#[derive(Copy, Clone, Debug)]
+#[repr(u8)]
+pub enum GuardKind {
+ /// Null variant
+ None = 0,
+ /// A `mach_port_t`
+ MachPort = 1,
+ /// File descriptor
+ Fd = 2,
+ /// Userland assertion
+ User = 3,
+ /// Vnode
+ Vnode = 4,
+ /// Virtual memory operation
+ VirtualMemory = 5,
+ /// Rejected system call trap
+ RejectedSyscall = 6,
+}
+
+#[inline]
+pub fn extract_guard_kind(code: u64) -> u8 {
+ ((code >> 61) & 0x7) as u8
+}
+
+#[inline]
+pub fn extract_guard_flavor(code: u64) -> u32 {
+ ((code >> 32) & 0x1fffffff) as u32
+}
+
+#[inline]
+pub fn extract_guard_target(code: u64) -> u32 {
+ code as u32
+}
+
+/// The extracted details of an `EXC_GUARD` exception
+pub struct GuardException {
+ /// One of [`GuardKind`]
+ pub kind: u8,
+ /// The specific guard flavor that was violated, specific to each `kind`
+ pub flavor: u32,
+ /// The resource that was guarded
+ pub target: u32,
+ /// Target specific guard information
+ pub identifier: u64,
+}
+
+/// Extracts the guard details from an exceptions code and subcode
+///
+/// code:
+/// +-------------------+----------------+--------------+
+/// |[63:61] guard type | [60:32] flavor | [31:0] target|
+/// +-------------------+----------------+--------------+
+///
+/// subcode:
+/// +---------------------------------------------------+
+/// |[63:0] guard identifier |
+/// +---------------------------------------------------+
+#[inline]
+pub fn extract_guard_exception(code: u64, subcode: u64) -> GuardException {
+ GuardException {
+ kind: extract_guard_kind(code),
+ flavor: extract_guard_flavor(code),
+ target: extract_guard_target(code),
+ identifier: subcode,
+ }
+}
+
+impl super::ExceptionInfo {
+ /// If this is an `EXC_GUARD` exception, retrieves the exception metadata
+ /// from the code, otherwise returns `None`
+ pub fn guard_exception(&self) -> Option<GuardException> {
+ if self.kind != EXC_GUARD {
+ return None;
+ }
+
+ Some(extract_guard_exception(
+ self.code,
+ self.subcode.unwrap_or_default(),
+ ))
+ }
+}
diff --git a/third_party/rust/crash-context/src/mac/ipc.rs b/third_party/rust/crash-context/src/mac/ipc.rs
new file mode 100644
index 0000000000..cb5fb2fcf1
--- /dev/null
+++ b/third_party/rust/crash-context/src/mac/ipc.rs
@@ -0,0 +1,553 @@
+//! Unfortunately, sending a [`CrashContext`] to another process on Macos
+//! needs to be done via mach ports, as, for example, `mach_task_self` is a
+//! special handle that needs to be translated into the "actual" task when used
+//! by another process, this _might_ be possible completely in userspace, but
+//! examining the source code for this leads me to believe that there are enough
+//! footguns, particularly around security, that this might take a while, so for
+//! now, if you need to use a [`CrashContext`] across processes, you need
+//! to use the IPC mechanisms here to get meaningful/accurate data
+//!
+//! Note that in all cases of an optional timeout, a `None` will return
+//! immediately regardless of whether the messaged has been enqueued or
+//! dequeued from the kernel queue, so it is _highly_ recommended to use
+//! reasonable timeouts for sending and receiving messages between processes.
+
+use crate::CrashContext;
+use mach2::{
+ bootstrap, kern_return::KERN_SUCCESS, mach_port, message as msg, port, task,
+ traps::mach_task_self,
+};
+pub use mach2::{kern_return::kern_return_t, message::mach_msg_return_t};
+use std::{ffi::CStr, time::Duration};
+
+extern "C" {
+ /// From <usr/include/mach/mach_traps.h>, there is no binding for this in mach2
+ pub fn pid_for_task(task: port::mach_port_name_t, pid: *mut i32) -> kern_return_t;
+}
+
+/// <https://github.com/apple-oss-distributions/xnu/blob/e6231be02a03711ca404e5121a151b24afbff733/osfmk/mach/message.h#L379-L391>
+#[repr(C, packed(4))]
+struct MachMsgPortDescriptor {
+ name: u32,
+ __pad1: u32,
+ __pad2: u16,
+ disposition: u8,
+ __type: u8,
+}
+
+impl MachMsgPortDescriptor {
+ fn new(name: u32, disposition: u32) -> Self {
+ Self {
+ name,
+ disposition: disposition as u8,
+ __pad1: 0,
+ __pad2: 0,
+ __type: msg::MACH_MSG_PORT_DESCRIPTOR as u8,
+ }
+ }
+}
+
+#[repr(C, packed(4))]
+struct MachMsgBody {
+ pub descriptor_count: u32,
+}
+
+#[repr(C, packed(4))]
+pub struct MachMsgTrailer {
+ pub kind: u32,
+ pub size: u32,
+}
+
+/// <https://github.com/apple-oss-distributions/xnu/blob/e6231be02a03711ca404e5121a151b24afbff733/osfmk/mach/message.h#L545-L552>
+#[repr(C, packed(4))]
+struct MachMsgHeader {
+ pub bits: u32,
+ pub size: u32,
+ pub remote_port: u32,
+ pub local_port: u32,
+ pub voucher_port: u32,
+ pub id: u32,
+}
+
+/// The actual crash context message sent and received. This message is a single
+/// struct since it needs to be contiguous block of memory. I suppose it's like
+/// this because people are expected to use MIG to generate the interface code,
+/// but it's ugly as hell regardless.
+#[repr(C, packed(4))]
+struct CrashContextMessage {
+ head: MachMsgHeader,
+ /// When providing port descriptors, this must be present to say how many
+ /// of them follow the header and body
+ body: MachMsgBody,
+ // These are the really the critical piece of the payload, during
+ // sending (or receiving?) these are turned into descriptors that
+ // can actually be used by another process
+ /// The task that crashed (ie `mach_task_self`)
+ task: MachMsgPortDescriptor,
+ /// The thread that crashed
+ crash_thread: MachMsgPortDescriptor,
+ /// The handler thread, probably, but not necessarily `mach_thread_self`
+ handler_thread: MachMsgPortDescriptor,
+ // Port opened by the client to receive an ack from the server
+ ack_port: MachMsgPortDescriptor,
+ /// Combination of the FLAG_* constants
+ flags: u32,
+ /// The exception type
+ exception_kind: u32,
+ /// The exception code
+ exception_code: u64,
+ /// The optional exception subcode
+ exception_subcode: u64,
+ /// We don't actually send this, but it's tacked on by the kernel :(
+ trailer: MachMsgTrailer,
+}
+
+const FLAG_HAS_EXCEPTION: u32 = 0x1;
+const FLAG_HAS_SUBCODE: u32 = 0x2;
+
+/// Message sent from the [`Receiver`] upon receiving and handling a [`CrashContextMessage`]
+#[repr(C, packed(4))]
+struct AcknowledgementMessage {
+ head: MachMsgHeader,
+ result: u32,
+}
+
+/// An error that can occur while interacting with mach ports
+#[derive(Copy, Clone, Debug)]
+pub enum Error {
+ /// A kernel error will generally indicate an error occurred while creating
+ /// or modifying a mach port
+ Kernel(kern_return_t),
+ /// A message error indicates an error occurred while sending or receiving
+ /// a message on a mach port
+ Message(mach_msg_return_t),
+}
+
+impl std::error::Error for Error {}
+
+use std::fmt;
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // TODO: use a good string for the error codes
+ write!(f, "{:?}", self)
+ }
+}
+
+macro_rules! kern {
+ ($call:expr) => {{
+ let res = $call;
+
+ if res != KERN_SUCCESS {
+ return Err(Error::Kernel(res));
+ }
+ }};
+}
+
+macro_rules! msg {
+ ($call:expr) => {{
+ let res = $call;
+
+ if res != msg::MACH_MSG_SUCCESS {
+ return Err(Error::Message(res));
+ }
+ }};
+}
+
+/// Sends a [`CrashContext`] from a crashing process to another process running
+/// a [`Server`] with the same name
+pub struct Client {
+ port: port::mach_port_t,
+}
+
+impl Client {
+ /// Attempts to create a new client that can send messages to a [`Server`]
+ /// that was created with the specified name.
+ ///
+ /// # Errors
+ ///
+ /// The specified port is not available for some reason, if you expect the
+ /// port to be created you can retry this function until it connects.
+ pub fn create(name: &CStr) -> Result<Self, Error> {
+ // SAFETY: syscalls. The user has no invariants to uphold, hence the
+ // unsafe not being on the function as a whole
+ unsafe {
+ let mut task_bootstrap_port = 0;
+ kern!(task::task_get_special_port(
+ mach_task_self(),
+ task::TASK_BOOTSTRAP_PORT,
+ &mut task_bootstrap_port
+ ));
+
+ let mut port = 0;
+ kern!(bootstrap::bootstrap_look_up(
+ task_bootstrap_port,
+ name.as_ptr(),
+ &mut port
+ ));
+
+ Ok(Self { port })
+ }
+ }
+
+ /// Sends the specified [`CrashContext`] to a [`Server`].
+ ///
+ /// If the ack from the [`Server`] times out `Ok(None)` is returned, otherwise
+ /// it is the value specified in the [`Server`] process to [`Acknowledger::send_ack`]
+ ///
+ /// # Errors
+ ///
+ /// The send of the [`CrashContext`] or the receive of the ack fails.
+ pub fn send_crash_context(
+ &self,
+ ctx: &CrashContext,
+ send_timeout: Option<Duration>,
+ receive_timeout: Option<Duration>,
+ ) -> Result<Option<u32>, Error> {
+ // SAFETY: syscalls. Again, the user has no invariants to uphold, so
+ // the function itself is not marked unsafe
+ unsafe {
+ // Create a new port to receive a response from the reciving end of
+ // this port so we that we know when it has actually processed the
+ // CrashContext, which is (presumably) interesting for the caller. If
+ // that is not interesting they can set the receive_timeout to 0 to
+ // just return immediately
+ let mut ack_port = AckReceiver::new()?;
+
+ let (flags, exception_kind, exception_code, exception_subcode) =
+ if let Some(exc) = ctx.exception {
+ (
+ FLAG_HAS_EXCEPTION
+ | if exc.subcode.is_some() {
+ FLAG_HAS_SUBCODE
+ } else {
+ 0
+ },
+ exc.kind,
+ exc.code,
+ exc.subcode.unwrap_or_default(),
+ )
+ } else {
+ (0, 0, 0, 0)
+ };
+
+ let mut msg = CrashContextMessage {
+ head: MachMsgHeader {
+ bits: msg::MACH_MSG_TYPE_COPY_SEND | msg::MACH_MSGH_BITS_COMPLEX,
+ // We don't send the trailer, that's added by the kernel
+ size: std::mem::size_of::<CrashContextMessage>() as u32 - 8,
+ remote_port: self.port,
+ local_port: port::MACH_PORT_NULL,
+ voucher_port: port::MACH_PORT_NULL,
+ id: 0,
+ },
+ body: MachMsgBody {
+ descriptor_count: 4,
+ },
+ task: MachMsgPortDescriptor::new(ctx.task, msg::MACH_MSG_TYPE_COPY_SEND),
+ crash_thread: MachMsgPortDescriptor::new(ctx.thread, msg::MACH_MSG_TYPE_COPY_SEND),
+ handler_thread: MachMsgPortDescriptor::new(
+ ctx.handler_thread,
+ msg::MACH_MSG_TYPE_COPY_SEND,
+ ),
+ ack_port: MachMsgPortDescriptor::new(ack_port.port, msg::MACH_MSG_TYPE_COPY_SEND),
+ flags,
+ exception_kind,
+ exception_code,
+ exception_subcode,
+ // We don't actually send this but I didn't feel like making
+ // two types
+ trailer: MachMsgTrailer { kind: 0, size: 8 },
+ };
+
+ // Try to actually send the message to the Server
+ msg!(msg::mach_msg(
+ ((&mut msg.head) as *mut MachMsgHeader).cast(),
+ msg::MACH_SEND_MSG | msg::MACH_SEND_TIMEOUT,
+ msg.head.size,
+ 0,
+ port::MACH_PORT_NULL,
+ send_timeout
+ .map(|st| st.as_millis() as u32)
+ .unwrap_or_default(),
+ port::MACH_PORT_NULL
+ ));
+
+ // Wait for a response from the Server
+ match ack_port.recv_ack(receive_timeout) {
+ Ok(result) => Ok(Some(result)),
+ Err(Error::Message(msg::MACH_RCV_TIMED_OUT)) => Ok(None),
+ Err(e) => Err(e),
+ }
+ }
+ }
+}
+
+/// Returned from [`Server::try_recv_crash_context`] when a [`Client`] has sent
+/// a crash context
+pub struct ReceivedCrashContext {
+ /// The crash context sent by a [`Client`]
+ pub crash_context: CrashContext,
+ /// Allows the sending of an ack back to the [`Client`] to acknowledge that
+ /// your code has received and processed the [`CrashContext`]
+ pub acker: Acknowledger,
+ /// The process id of the process the [`Client`] lives in. This is retrieved
+ /// via `pid_for_task`.
+ pub pid: u32,
+}
+
+/// Receives a [`CrashContext`] from another process
+pub struct Server {
+ port: port::mach_port_t,
+}
+
+impl Server {
+ /// Creates a new [`Server`] "bound" to the specified service name.
+ ///
+ /// # Errors
+ ///
+ /// We fail to acquire the bootstrap port, or fail to register the service.
+ pub fn create(name: &CStr) -> Result<Self, Error> {
+ // SAFETY: syscalls. Again, the caller has no invariants to uphold, so
+ // the entire function is not marked as unsafe
+ unsafe {
+ let mut task_bootstrap_port = 0;
+ kern!(task::task_get_special_port(
+ mach_task_self(),
+ task::TASK_BOOTSTRAP_PORT,
+ &mut task_bootstrap_port
+ ));
+
+ let mut port = 0;
+ // Note that Breakpad uses bootstrap_register instead of this function as
+ // MacOS 10.5 apparently deprecated bootstrap_register and then provided
+ // bootstrap_check_in, but broken. However, 10.5 had its most recent update
+ // over 13 years ago, and is not supported by Apple, so why should we?
+ kern!(bootstrap::bootstrap_check_in(
+ task_bootstrap_port,
+ name.as_ptr(),
+ &mut port,
+ ));
+
+ Ok(Self { port })
+ }
+ }
+
+ /// Attempts to retrieve a [`CrashContext`] sent from a crashing process.
+ ///
+ /// Note that in event of a timeout, this method will return `Ok(None)` to
+ /// indicate that a crash context was unavailable rather than an error.
+ ///
+ /// # Errors
+ ///
+ /// We fail to receive the [`CrashContext`] message for a reason other than
+ /// one not being in the queue, or we fail to translate the task identifier
+ /// into a pid
+ pub fn try_recv_crash_context(
+ &mut self,
+ timeout: Option<Duration>,
+ ) -> Result<Option<ReceivedCrashContext>, Error> {
+ // SAFETY: syscalls. The caller has no invariants to uphold, so the
+ // entire function is not marked unsafe.
+ unsafe {
+ let mut crash_ctx_msg: CrashContextMessage = std::mem::zeroed();
+ crash_ctx_msg.head.local_port = self.port;
+
+ let ret = msg::mach_msg(
+ ((&mut crash_ctx_msg.head) as *mut MachMsgHeader).cast(),
+ msg::MACH_RCV_MSG | msg::MACH_RCV_TIMEOUT,
+ 0,
+ std::mem::size_of::<CrashContextMessage>() as u32,
+ self.port,
+ timeout.map(|t| t.as_millis() as u32).unwrap_or_default(),
+ port::MACH_PORT_NULL,
+ );
+
+ if ret == msg::MACH_RCV_TIMED_OUT {
+ return Ok(None);
+ } else if ret != msg::MACH_MSG_SUCCESS {
+ return Err(Error::Message(ret));
+ }
+
+ // Reconstruct a crash context from the message we received
+ let exception = if crash_ctx_msg.flags & FLAG_HAS_EXCEPTION != 0 {
+ Some(crate::ExceptionInfo {
+ kind: crash_ctx_msg.exception_kind,
+ code: crash_ctx_msg.exception_code,
+ subcode: (crash_ctx_msg.flags & FLAG_HAS_SUBCODE != 0)
+ .then_some(crash_ctx_msg.exception_subcode),
+ })
+ } else {
+ None
+ };
+
+ let crash_context = CrashContext {
+ task: crash_ctx_msg.task.name,
+ thread: crash_ctx_msg.crash_thread.name,
+ handler_thread: crash_ctx_msg.handler_thread.name,
+ exception,
+ };
+
+ // Translate the task to a pid so the user doesn't have to do it
+ // since there is not a binding available in libc/mach/mach2 for it
+ let mut pid = 0;
+ kern!(pid_for_task(crash_ctx_msg.task.name, &mut pid));
+ let ack_port = crash_ctx_msg.ack_port.name;
+
+ // Provide a way for the user to tell the client when they are done
+ // processing the crash context, unless the specified port was not
+ // set or somehow died immediately
+ let acker = Acknowledger {
+ port: (ack_port != port::MACH_PORT_DEAD && ack_port != port::MACH_PORT_NULL)
+ .then_some(ack_port),
+ };
+
+ Ok(Some(ReceivedCrashContext {
+ crash_context,
+ acker,
+ pid: pid as u32,
+ }))
+ }
+ }
+}
+
+impl Drop for Server {
+ fn drop(&mut self) {
+ // SAFETY: syscall
+ unsafe {
+ mach_port::mach_port_deallocate(mach_task_self(), self.port);
+ }
+ }
+}
+
+/// Used by a process running the [`Server`] to send a response back to the
+/// [`Client`] that sent a [`CrashContext`] after it has finished
+/// processing.
+pub struct Acknowledger {
+ port: Option<port::mach_port_t>,
+}
+
+impl Acknowledger {
+ /// Sends an ack back to the client that sent a [`CrashContext`]
+ ///
+ /// # Errors
+ ///
+ /// We fail to send the ack to the port created in the [`Client`] process
+ pub fn send_ack(&mut self, ack: u32, timeout: Option<Duration>) -> Result<(), Error> {
+ if let Some(port) = self.port {
+ // SAFETY: syscalls. The caller has no invariants to uphold, so the
+ // entire function is not marked unsafe.
+ unsafe {
+ let mut msg = AcknowledgementMessage {
+ head: MachMsgHeader {
+ bits: msg::MACH_MSG_TYPE_COPY_SEND,
+ size: std::mem::size_of::<AcknowledgementMessage>() as u32,
+ remote_port: port,
+ local_port: port::MACH_PORT_NULL,
+ voucher_port: port::MACH_PORT_NULL,
+ id: 0,
+ },
+ result: ack,
+ };
+
+ // Try to actually send the message
+ msg!(msg::mach_msg(
+ ((&mut msg.head) as *mut MachMsgHeader).cast(),
+ msg::MACH_SEND_MSG | msg::MACH_SEND_TIMEOUT,
+ msg.head.size,
+ 0,
+ port::MACH_PORT_NULL,
+ timeout.map(|t| t.as_millis() as u32).unwrap_or_default(),
+ port::MACH_PORT_NULL
+ ));
+
+ Ok(())
+ }
+ } else {
+ Ok(())
+ }
+ }
+}
+
+/// Used by [`Sender::send_crash_context`] to create a port to receive the
+/// external process's response to sending a [`CrashContext`]
+struct AckReceiver {
+ port: port::mach_port_t,
+}
+
+impl AckReceiver {
+ /// Allocates a new port to receive an ack from a [`Server`]
+ ///
+ /// # Errors
+ ///
+ /// We fail to allocate a port, or fail to add a send right to it.
+ ///
+ /// # Safety
+ ///
+ /// Performs syscalls. Only used internally hence the entire function being
+ /// marked unsafe.
+ unsafe fn new() -> Result<Self, Error> {
+ let mut port = 0;
+ kern!(mach_port::mach_port_allocate(
+ mach_task_self(),
+ port::MACH_PORT_RIGHT_RECEIVE,
+ &mut port
+ ));
+
+ kern!(mach_port::mach_port_insert_right(
+ mach_task_self(),
+ port,
+ port,
+ msg::MACH_MSG_TYPE_MAKE_SEND
+ ));
+
+ Ok(Self { port })
+ }
+
+ /// Waits for the specified duration to receive a result from the [`Server`]
+ /// that was sent a [`CrashContext`]
+ ///
+ /// # Errors
+ ///
+ /// We fail to receive an ack for some reason
+ ///
+ /// # Safety
+ ///
+ /// Performs syscalls. Only used internally hence the entire function being
+ /// marked unsafe.
+ unsafe fn recv_ack(&mut self, timeout: Option<Duration>) -> Result<u32, Error> {
+ let mut ack = AcknowledgementMessage {
+ head: MachMsgHeader {
+ bits: 0,
+ size: std::mem::size_of::<AcknowledgementMessage>() as u32,
+ remote_port: port::MACH_PORT_NULL,
+ local_port: self.port,
+ voucher_port: port::MACH_PORT_NULL,
+ id: 0,
+ },
+ result: 0,
+ };
+
+ // Wait for a response from the Server
+ msg!(msg::mach_msg(
+ ((&mut ack.head) as *mut MachMsgHeader).cast(),
+ msg::MACH_RCV_MSG | msg::MACH_RCV_TIMEOUT,
+ 0,
+ ack.head.size,
+ self.port,
+ timeout.map(|t| t.as_millis() as u32).unwrap_or_default(),
+ port::MACH_PORT_NULL
+ ));
+
+ Ok(ack.result)
+ }
+}
+
+impl Drop for AckReceiver {
+ fn drop(&mut self) {
+ // SAFETY: syscall
+ unsafe {
+ mach_port::mach_port_deallocate(mach_task_self(), self.port);
+ }
+ }
+}
diff --git a/third_party/rust/crash-context/src/mac/resource.rs b/third_party/rust/crash-context/src/mac/resource.rs
new file mode 100644
index 0000000000..c93acfe97a
--- /dev/null
+++ b/third_party/rust/crash-context/src/mac/resource.rs
@@ -0,0 +1,516 @@
+//! Contains types and helpers for dealing with `EXC_RESOURCE` exceptions.
+//!
+//! `EXC_RESOURCE` exceptions embed details about the resource and the limits
+//! it exceeded within the `code` and, in some cases `subcode`, fields of the exception
+//!
+//! See <https://github.com/apple-oss-distributions/xnu/blob/e6231be02a03711ca404e5121a151b24afbff733/osfmk/kern/exc_resource.h>
+//! for the various constants and decoding of exception information wrapped in
+//! this module.
+
+use mach2::exception_types::EXC_RESOURCE;
+use std::time::Duration;
+
+/// The details for an `EXC_RESOURCE` exception as retrieved from the exception's
+/// code and subcode
+pub enum ResourceException {
+ /// This is sent by the kernel when the CPU usage monitor is tripped. Possibly fatal.
+ Cpu(CpuResourceException),
+ /// This is sent by the kernel when the platform idle wakeups monitor is tripped. Possibly fatal.
+ Wakeups(WakeupsResourceException),
+ /// This is sent by the kernel when a task crosses its high watermark memory limit. Never fatal at least on current MacOS versions.
+ Memory(MemoryResourceException),
+ /// This is sent by the kernel when a task crosses its I/O limits. Never fatal.
+ Io(IoResourceException),
+ /// This is sent by the kernel when a task crosses its thread limit. Always fatal.
+ Threads(ThreadsResourceException),
+ /// This is sent by the kernel when the process is leaking ipc ports and has
+ /// filled its port space. Always fatal.
+ Ports(PortsResourceException),
+ /// An unknown resource kind due to an addition to the set of possible
+ /// resource exception kinds in exc_resource.h
+ Unknown { kind: u8, flavor: u8 },
+}
+
+/// Each different resource exception type has 1 or more flavors that it can be,
+/// and while these most likely don't change often, we try to be forward
+/// compatible by not failing if a particular flavor is unknown
+#[derive(Copy, Clone, Debug)]
+pub enum Flavor<T: Copy + Clone + std::fmt::Debug> {
+ Known(T),
+ Unknown(u8),
+}
+
+impl<T: TryFrom<u8> + Copy + Clone + std::fmt::Debug> From<u64> for Flavor<T> {
+ #[inline]
+ fn from(code: u64) -> Self {
+ let flavor = resource_exc_flavor(code);
+ if let Ok(known) = T::try_from(flavor) {
+ Self::Known(known)
+ } else {
+ Self::Unknown(flavor)
+ }
+ }
+}
+
+impl<T: PartialEq + Copy + Clone + std::fmt::Debug> PartialEq<T> for Flavor<T> {
+ fn eq(&self, o: &T) -> bool {
+ match self {
+ Self::Known(flavor) => flavor == o,
+ Self::Unknown(_) => false,
+ }
+ }
+}
+
+/// Retrieves the resource exception kind from an exception code
+#[inline]
+pub fn resource_exc_kind(code: u64) -> u8 {
+ ((code >> 61) & 0x7) as u8
+}
+
+/// Retrieves the resource exception flavor from an exception code
+#[inline]
+pub fn resource_exc_flavor(code: u64) -> u8 {
+ ((code >> 58) & 0x7) as u8
+}
+
+impl super::ExceptionInfo {
+ /// If this is an `EXC_RESOURCE` exception, retrieves the exception metadata
+ /// from the code, otherwise returns `None`
+ pub fn resource_exception(&self) -> Option<ResourceException> {
+ if self.kind != EXC_RESOURCE {
+ return None;
+ }
+
+ let kind = resource_exc_kind(self.code);
+
+ let res_exc = if kind == ResourceKind::Cpu as u8 {
+ ResourceException::Cpu(CpuResourceException::from_exc_info(self.code, self.subcode))
+ } else if kind == ResourceKind::Wakeups as u8 {
+ ResourceException::Wakeups(WakeupsResourceException::from_exc_info(
+ self.code,
+ self.subcode,
+ ))
+ } else if kind == ResourceKind::Memory as u8 {
+ ResourceException::Memory(MemoryResourceException::from_exc_info(self.code))
+ } else if kind == ResourceKind::Io as u8 {
+ ResourceException::Io(IoResourceException::from_exc_info(self.code, self.subcode))
+ } else if kind == ResourceKind::Threads as u8 {
+ ResourceException::Threads(ThreadsResourceException::from_exc_info(self.code))
+ } else if kind == ResourceKind::Ports as u8 {
+ ResourceException::Ports(PortsResourceException::from_exc_info(self.code))
+ } else {
+ ResourceException::Unknown {
+ kind,
+ flavor: resource_exc_flavor(self.code),
+ }
+ };
+
+ Some(res_exc)
+ }
+}
+
+/// The types of resources that an `EXC_RESOURCE` exception can pertain to
+#[repr(u8)]
+pub enum ResourceKind {
+ Cpu = 1,
+ Wakeups = 2,
+ Memory = 3,
+ Io = 4,
+ Threads = 5,
+ Ports = 6,
+}
+
+/// The flavors for a [`CpuResourceException`]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(u8)]
+pub enum CpuFlavor {
+ /// The process has surpassed its CPU limit
+ Monitor = 1,
+ /// The process has surpassed its CPU limit, and the process has been configured
+ /// to make this exception fatal
+ MonitorFatal = 2,
+}
+
+impl TryFrom<u8> for CpuFlavor {
+ type Error = ();
+
+ fn try_from(flavor: u8) -> Result<Self, Self::Error> {
+ match flavor {
+ 1 => Ok(Self::Monitor),
+ 2 => Ok(Self::MonitorFatal),
+ _ => Err(()),
+ }
+ }
+}
+
+/// These exceptions _may_ be fatal. They are not fatal by default at task
+/// creation but can be made fatal by calling `proc_rlimit_control` with
+/// `RLIMIT_CPU_USAGE_MONITOR` as the second argument and `CPUMON_MAKE_FATAL`
+/// set in the flags. The flavor extracted from the exception code determines if
+/// the exception is fatal.
+///
+/// [Kernel code](https://github.com/apple-oss-distributions/xnu/blob/e7776783b89a353188416a9a346c6cdb4928faad/osfmk/kern/thread.c#L2475-L2616)
+#[derive(Copy, Clone, Debug)]
+pub struct CpuResourceException {
+ pub flavor: Flavor<CpuFlavor>,
+ /// If the exception is fatal. Currently only true if the flavor is [`CpuFlavor::MonitorFatal`]
+ pub is_fatal: bool,
+ /// The time period in which the CPU limit was surpassed
+ pub observation_interval: Duration,
+ /// The CPU % limit
+ pub limit: u8,
+ /// The CPU % consumed by the task
+ pub consumed: u8,
+}
+
+impl CpuResourceException {
+ /*
+ * code:
+ * +-----------------------------------------------+
+ * |[63:61] RESOURCE |[60:58] FLAVOR_CPU_ |[57:32] |
+ * |_TYPE_CPU |MONITOR[_FATAL] |Unused |
+ * +-----------------------------------------------+
+ * |[31:7] Interval (sec) | [6:0] CPU limit (%)|
+ * +-----------------------------------------------+
+ *
+ * subcode:
+ * +-----------------------------------------------+
+ * | | [6:0] % of CPU |
+ * | | actually consumed |
+ * +-----------------------------------------------+
+ *
+ */
+ #[inline]
+ pub fn from_exc_info(code: u64, subcode: Option<u64>) -> Self {
+ debug_assert_eq!(resource_exc_kind(code), ResourceKind::Cpu as u8);
+
+ let flavor = Flavor::from(code);
+ let interval_seconds = (code >> 7) & 0x1ffffff;
+ let limit = (code & 0x7f) as u8;
+ let consumed = subcode.map_or(0, |sc| sc & 0x7f) as u8;
+
+ // The default is that cpu resource exceptions are not fatal, so
+ // we only check the flavor against the (currently) one known value
+ // that indicates the exception is fatal
+ Self {
+ flavor,
+ is_fatal: flavor == CpuFlavor::MonitorFatal,
+ observation_interval: Duration::from_secs(interval_seconds),
+ limit,
+ consumed,
+ }
+ }
+}
+
+/// The flavors for a [`WakeupsResourceException`]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(u8)]
+pub enum WakeupsFlavor {
+ Monitor = 1,
+}
+
+impl TryFrom<u8> for WakeupsFlavor {
+ type Error = ();
+
+ fn try_from(flavor: u8) -> Result<Self, Self::Error> {
+ match flavor {
+ 1 => Ok(Self::Monitor),
+ _ => Err(()),
+ }
+ }
+}
+
+/// These exceptions may be fatal. They are not fatal by default at task
+/// creation, but can be made fatal by calling `proc_rlimit_control` with
+/// `RLIMIT_WAKEUPS_MONITOR` as the second argument and `WAKEMON_MAKE_FATAL`
+/// set in the flags. Calling [`proc_get_wakemon_params`](https://github.com/apple-oss-distributions/xnu/blob/e6231be02a03711ca404e5121a151b24afbff733/libsyscall/wrappers/libproc/libproc.c#L592-L608)
+/// determines whether these exceptions are fatal.
+///
+/// [Kernel source](https://github.com/apple-oss-distributions/xnu/blob/e6231be02a03711ca404e5121a151b24afbff733/osfmk/kern/task.c#L7501-L7580)
+pub struct WakeupsResourceException {
+ pub flavor: Flavor<WakeupsFlavor>,
+ /// The time period in which the number of wakeups was surpassed
+ pub observation_interval: Duration,
+ /// The number of wakeups permitted per second
+ pub permitted: u32,
+ /// The number of wakeups observed per second
+ pub observed: u32,
+}
+
+impl WakeupsResourceException {
+ /*
+ * code:
+ * +-----------------------------------------------+
+ * |[63:61] RESOURCE |[60:58] FLAVOR_ |[57:32] |
+ * |_TYPE_WAKEUPS |WAKEUPS_MONITOR |Unused |
+ * +-----------------------------------------------+
+ * | [31:20] Observation | [19:0] # of wakeups |
+ * | interval (sec) | permitted (per sec) |
+ * +-----------------------------------------------+
+ *
+ * subcode:
+ * +-----------------------------------------------+
+ * | | [19:0] # of wakeups |
+ * | | observed (per sec) |
+ * +-----------------------------------------------+
+ *
+ */
+ #[inline]
+ pub fn from_exc_info(code: u64, subcode: Option<u64>) -> Self {
+ debug_assert_eq!(resource_exc_kind(code), ResourceKind::Wakeups as u8);
+
+ let flavor = Flavor::from(code);
+ // Note that Apple has a bug in exc_resource.h where the masks in the
+ // decode macros for the interval and the permitted wakeups have been swapped
+ let interval_seconds = (code >> 20) & 0xfff;
+ let permitted = (code & 0xfffff) as u32;
+ let observed = subcode.map_or(0, |sc| sc & 0xfffff) as u32;
+
+ Self {
+ flavor,
+ observation_interval: Duration::from_secs(interval_seconds),
+ permitted,
+ observed,
+ }
+ }
+}
+
+/// The flavors for a [`MemoryResourceException`]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(u8)]
+pub enum MemoryFlavor {
+ HighWatermark = 1,
+}
+
+impl TryFrom<u8> for MemoryFlavor {
+ type Error = ();
+
+ fn try_from(flavor: u8) -> Result<Self, Self::Error> {
+ match flavor {
+ 1 => Ok(Self::HighWatermark),
+ _ => Err(()),
+ }
+ }
+}
+
+/// These exceptions, as of this writing, are never fatal.
+///
+/// While memory exceptions _can_ be fatal, this appears to only be possible if
+/// the kernel is built with `CONFIG_JETSAM` or in `DEVELOPMENT` or `DEBUG` modes,
+/// so as of now, they should never be considered fatal, at least on `MacOS`
+///
+/// [Kernel source](https://github.com/apple-oss-distributions/xnu/blob/e6231be02a03711ca404e5121a151b24afbff733/osfmk/kern/task.c#L6767-L6874)
+pub struct MemoryResourceException {
+ pub flavor: Flavor<MemoryFlavor>,
+ /// The limit in MiB of the high watermark
+ pub limit_mib: u16,
+}
+
+impl MemoryResourceException {
+ /*
+ * code:
+ * +------------------------------------------------+
+ * |[63:61] RESOURCE |[60:58] FLAVOR_HIGH_ |[57:32] |
+ * |_TYPE_MEMORY |WATERMARK |Unused |
+ * +------------------------------------------------+
+ * | | [12:0] HWM limit (MB)|
+ * +------------------------------------------------+
+ *
+ * subcode:
+ * +------------------------------------------------+
+ * | unused |
+ * +------------------------------------------------+
+ *
+ */
+ #[inline]
+ pub fn from_exc_info(code: u64) -> Self {
+ debug_assert_eq!(resource_exc_kind(code), ResourceKind::Memory as u8);
+
+ let flavor = Flavor::from(code);
+ let limit_mib = (code & 0x1fff) as u16;
+
+ Self { flavor, limit_mib }
+ }
+}
+
+/// The flavors for an [`IoResourceException`]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(u8)]
+pub enum IoFlavor {
+ PhysicalWrites = 1,
+ LogicalWrites = 2,
+}
+
+impl TryFrom<u8> for IoFlavor {
+ type Error = ();
+
+ fn try_from(flavor: u8) -> Result<Self, Self::Error> {
+ match flavor {
+ 1 => Ok(Self::PhysicalWrites),
+ 2 => Ok(Self::LogicalWrites),
+ _ => Err(()),
+ }
+ }
+}
+
+/// These exceptions are never fatal.
+///
+/// [Kernel source](https://github.com/apple-oss-distributions/xnu/blob/e6231be02a03711ca404e5121a151b24afbff733/osfmk/kern/task.c#L7739-L7792)
+pub struct IoResourceException {
+ pub flavor: Flavor<MemoryFlavor>,
+ /// The time period in which the I/O limit was surpassed
+ pub observation_interval: Duration,
+ /// The I/O limit in MiB of the high watermark
+ pub limit_mib: u16,
+ /// The observed I/O in MiB
+ pub observed_mib: u16,
+}
+
+impl IoResourceException {
+ /*
+ * code:
+ * +-----------------------------------------------+
+ * |[63:61] RESOURCE |[60:58] FLAVOR_IO_ |[57:32] |
+ * |_TYPE_IO |PHYSICAL/LOGICAL |Unused |
+ * +-----------------------------------------------+
+ * |[31:15] Interval (sec) | [14:0] Limit (MB) |
+ * +-----------------------------------------------+
+ *
+ * subcode:
+ * +-----------------------------------------------+
+ * | | [14:0] I/O Count |
+ * | | (in MB) |
+ * +-----------------------------------------------+
+ *
+ */
+ #[inline]
+ pub fn from_exc_info(code: u64, subcode: Option<u64>) -> Self {
+ debug_assert_eq!(resource_exc_kind(code), ResourceKind::Io as u8);
+
+ let flavor = Flavor::from(code);
+ let interval_seconds = (code >> 15) & 0x1ffff;
+ let limit_mib = (code & 0x7fff) as u16;
+ let observed_mib = subcode.map_or(0, |sc| sc & 0x7fff) as u16;
+
+ Self {
+ flavor,
+ observation_interval: Duration::from_secs(interval_seconds),
+ limit_mib,
+ observed_mib,
+ }
+ }
+}
+
+/// The flavors for a [`ThreadsResourceException`]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(u8)]
+pub enum ThreadsFlavor {
+ HighWatermark = 1,
+}
+
+impl TryFrom<u8> for ThreadsFlavor {
+ type Error = ();
+
+ fn try_from(flavor: u8) -> Result<Self, Self::Error> {
+ match flavor {
+ 1 => Ok(Self::HighWatermark),
+ _ => Err(()),
+ }
+ }
+}
+
+/// This exception is provided for completeness sake, but is only possible if
+/// the kernel is built in `DEVELOPMENT` or `DEBUG` modes.
+///
+/// [Kernel source](https://github.com/apple-oss-distributions/xnu/blob/e6231be02a03711ca404e5121a151b24afbff733/osfmk/kern/thread.c#L2575-L2620)
+pub struct ThreadsResourceException {
+ pub flavor: Flavor<ThreadsFlavor>,
+ /// The thread limit
+ pub limit: u16,
+}
+
+impl ThreadsResourceException {
+ /*
+ * code:
+ * +--------------------------------------------------+
+ * |[63:61] RESOURCE |[60:58] FLAVOR_ |[57:32] |
+ * |_TYPE_THREADS |THREADS_HIGH_WATERMARK |Unused |
+ * +--------------------------------------------------+
+ * |[31:15] Unused | [14:0] Limit |
+ * +--------------------------------------------------+
+ *
+ * subcode:
+ * +-----------------------------------------------+
+ * | | Unused |
+ * | | |
+ * +-----------------------------------------------+
+ *
+ */
+ #[inline]
+ pub fn from_exc_info(code: u64) -> Self {
+ debug_assert_eq!(resource_exc_kind(code), ResourceKind::Threads as u8);
+
+ let flavor = Flavor::from(code);
+ let limit = (code & 0x7fff) as u16;
+
+ Self { flavor, limit }
+ }
+}
+
+/// The flavors for a [`PortsResourceException`]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(u8)]
+pub enum PortsFlavor {
+ SpaceFull = 1,
+}
+
+impl TryFrom<u8> for PortsFlavor {
+ type Error = ();
+
+ fn try_from(flavor: u8) -> Result<Self, Self::Error> {
+ match flavor {
+ 1 => Ok(Self::SpaceFull),
+ _ => Err(()),
+ }
+ }
+}
+
+/// This exception is always fatal, and in fact I'm unsure if this exception
+/// is even observable, as the kernel will kill the offending process if
+/// the port space is full
+///
+/// [Kernel source](https://github.com/apple-oss-distributions/xnu/blob/e7776783b89a353188416a9a346c6cdb4928faad/osfmk/kern/task.c#L7907-L7969)
+pub struct PortsResourceException {
+ pub flavor: Flavor<ThreadsFlavor>,
+ /// The number of allocated ports
+ pub allocated: u32,
+}
+
+impl PortsResourceException {
+ /*
+ * code:
+ * +-----------------------------------------------+
+ * |[63:61] RESOURCE |[60:58] FLAVOR_ |[57:32] |
+ * |_TYPE_PORTS |PORT_SPACE_FULL |Unused |
+ * +-----------------------------------------------+
+ * | [31:24] Unused | [23:0] # of ports |
+ * | | allocated |
+ * +-----------------------------------------------+
+ *
+ * subcode:
+ * +-----------------------------------------------+
+ * | | Unused |
+ * | | |
+ * +-----------------------------------------------+
+ *
+ */
+ #[inline]
+ pub fn from_exc_info(code: u64) -> Self {
+ debug_assert_eq!(resource_exc_kind(code), ResourceKind::Ports as u8);
+
+ let flavor = Flavor::from(code);
+ let allocated = (code & 0xffffff) as u32;
+
+ Self { flavor, allocated }
+ }
+}
diff --git a/third_party/rust/crash-context/src/windows.rs b/third_party/rust/crash-context/src/windows.rs
new file mode 100644
index 0000000000..231a608c08
--- /dev/null
+++ b/third_party/rust/crash-context/src/windows.rs
@@ -0,0 +1,262 @@
+/// Full Windows crash context
+pub struct CrashContext {
+ /// The information on the exception.
+ ///
+ /// Note that this is a pointer into the actual memory of the crashed process,
+ /// and is a pointer to an [EXCEPTION_POINTERS](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-exception_pointers)
+ pub exception_pointers: *const EXCEPTION_POINTERS,
+ /// The top level exception code from the exception_pointers. This is provided
+ /// so that external processes don't need to use `ReadProcessMemory` to inspect
+ /// the exception code
+ pub exception_code: i32,
+ /// The pid of the process that crashed
+ pub process_id: u32,
+ /// The thread id on which the exception occurred
+ pub thread_id: u32,
+}
+
+#[link(name = "kernel32")]
+extern "system" {
+ #[link_name = "RtlCaptureContext"]
+ pub fn capture_context(ctx_rec: *mut CONTEXT);
+}
+
+cfg_if::cfg_if! {
+ if #[cfg(target_arch = "x86_64")] {
+ #[repr(C, align(16))]
+ pub struct M128A {
+ pub Low: u64,
+ pub High: i64,
+ }
+
+ #[repr(C)]
+ pub struct CONTEXT_0_0 {
+ pub Header: [M128A; 2],
+ pub Legacy: [M128A; 8],
+ pub Xmm0: M128A,
+ pub Xmm1: M128A,
+ pub Xmm2: M128A,
+ pub Xmm3: M128A,
+ pub Xmm4: M128A,
+ pub Xmm5: M128A,
+ pub Xmm6: M128A,
+ pub Xmm7: M128A,
+ pub Xmm8: M128A,
+ pub Xmm9: M128A,
+ pub Xmm10: M128A,
+ pub Xmm11: M128A,
+ pub Xmm12: M128A,
+ pub Xmm13: M128A,
+ pub Xmm14: M128A,
+ pub Xmm15: M128A,
+ }
+
+ #[repr(C, align(16))]
+ pub struct XSAVE_FORMAT {
+ pub ControlWord: u16,
+ pub StatusWord: u16,
+ pub TagWord: u8,
+ pub Reserved1: u8,
+ pub ErrorOpcode: u16,
+ pub ErrorOffset: u32,
+ pub ErrorSelector: u16,
+ pub Reserved2: u16,
+ pub DataOffset: u32,
+ pub DataSelector: u16,
+ pub Reserved3: u16,
+ pub MxCsr: u32,
+ pub MxCsr_Mask: u32,
+ pub FloatRegisters: [M128A; 8],
+ pub XmmRegisters: [M128A; 16],
+ pub Reserved4: [u8; 96],
+ }
+
+ #[repr(C)]
+ pub union CONTEXT_0 {
+ pub FltSave: std::mem::ManuallyDrop<XSAVE_FORMAT>,
+ pub Anonymous: std::mem::ManuallyDrop<CONTEXT_0_0>,
+ }
+
+ #[repr(C, align(16))]
+ pub struct CONTEXT {
+ pub P1Home: u64,
+ pub P2Home: u64,
+ pub P3Home: u64,
+ pub P4Home: u64,
+ pub P5Home: u64,
+ pub P6Home: u64,
+ pub ContextFlags: u32,
+ pub MxCsr: u32,
+ pub SegCs: u16,
+ pub SegDs: u16,
+ pub SegEs: u16,
+ pub SegFs: u16,
+ pub SegGs: u16,
+ pub SegSs: u16,
+ pub EFlags: u32,
+ pub Dr0: u64,
+ pub Dr1: u64,
+ pub Dr2: u64,
+ pub Dr3: u64,
+ pub Dr6: u64,
+ pub Dr7: u64,
+ pub Rax: u64,
+ pub Rcx: u64,
+ pub Rdx: u64,
+ pub Rbx: u64,
+ pub Rsp: u64,
+ pub Rbp: u64,
+ pub Rsi: u64,
+ pub Rdi: u64,
+ pub R8: u64,
+ pub R9: u64,
+ pub R10: u64,
+ pub R11: u64,
+ pub R12: u64,
+ pub R13: u64,
+ pub R14: u64,
+ pub R15: u64,
+ pub Rip: u64,
+ pub Anonymous: CONTEXT_0,
+ pub VectorRegister: [M128A; 26],
+ pub VectorControl: u64,
+ pub DebugControl: u64,
+ pub LastBranchToRip: u64,
+ pub LastBranchFromRip: u64,
+ pub LastExceptionToRip: u64,
+ pub LastExceptionFromRip: u64,
+ }
+ } else if #[cfg(target_arch = "x86")] {
+ #[repr(C)]
+ pub struct FLOATING_SAVE_AREA {
+ pub ControlWord: u32,
+ pub StatusWord: u32,
+ pub TagWord: u32,
+ pub ErrorOffset: u32,
+ pub ErrorSelector: u32,
+ pub DataOffset: u32,
+ pub DataSelector: u32,
+ pub RegisterArea: [u8; 80],
+ pub Spare0: u32,
+ }
+
+ #[repr(C, packed(4))]
+ pub struct CONTEXT {
+ pub ContextFlags: u32,
+ pub Dr0: u32,
+ pub Dr1: u32,
+ pub Dr2: u32,
+ pub Dr3: u32,
+ pub Dr6: u32,
+ pub Dr7: u32,
+ pub FloatSave: FLOATING_SAVE_AREA,
+ pub SegGs: u32,
+ pub SegFs: u32,
+ pub SegEs: u32,
+ pub SegDs: u32,
+ pub Edi: u32,
+ pub Esi: u32,
+ pub Ebx: u32,
+ pub Edx: u32,
+ pub Ecx: u32,
+ pub Eax: u32,
+ pub Ebp: u32,
+ pub Eip: u32,
+ pub SegCs: u32,
+ pub EFlags: u32,
+ pub Esp: u32,
+ pub SegSs: u32,
+ pub ExtendedRegisters: [u8; 512],
+ }
+ } else if #[cfg(target_arch = "aarch64")] {
+ #[repr(C)]
+ pub struct ARM64_NT_NEON128_0 {
+ pub Low: u64,
+ pub High: i64,
+ }
+ #[repr(C)]
+ pub union ARM64_NT_NEON128 {
+ pub Anonymous: std::mem::ManuallyDrop<ARM64_NT_NEON128_0>,
+ pub D: [f64; 2],
+ pub S: [f32; 4],
+ pub H: [u16; 8],
+ pub B: [u8; 16],
+ }
+
+ #[repr(C)]
+ pub struct CONTEXT_0_0 {
+ pub X0: u64,
+ pub X1: u64,
+ pub X2: u64,
+ pub X3: u64,
+ pub X4: u64,
+ pub X5: u64,
+ pub X6: u64,
+ pub X7: u64,
+ pub X8: u64,
+ pub X9: u64,
+ pub X10: u64,
+ pub X11: u64,
+ pub X12: u64,
+ pub X13: u64,
+ pub X14: u64,
+ pub X15: u64,
+ pub X16: u64,
+ pub X17: u64,
+ pub X18: u64,
+ pub X19: u64,
+ pub X20: u64,
+ pub X21: u64,
+ pub X22: u64,
+ pub X23: u64,
+ pub X24: u64,
+ pub X25: u64,
+ pub X26: u64,
+ pub X27: u64,
+ pub X28: u64,
+ pub Fp: u64,
+ pub Lr: u64,
+ }
+
+ #[repr(C)]
+ pub union CONTEXT_0 {
+ pub Anonymous: std::mem::ManuallyDrop<CONTEXT_0_0>,
+ pub X: [u64; 31],
+ }
+
+ #[repr(C, align(16))]
+ pub struct CONTEXT {
+ pub ContextFlags: u32,
+ pub Cpsr: u32,
+ pub Anonymous: CONTEXT_0,
+ pub Sp: u64,
+ pub Pc: u64,
+ pub V: [ARM64_NT_NEON128; 32],
+ pub Fpcr: u32,
+ pub Fpsr: u32,
+ pub Bcr: [u32; 8],
+ pub Bvr: [u64; 8],
+ pub Wcr: [u32; 2],
+ pub Wvr: [u64; 2],
+ }
+ }
+}
+
+pub type NTSTATUS = i32;
+pub type BOOL = i32;
+
+#[repr(C)]
+pub struct EXCEPTION_RECORD {
+ pub ExceptionCode: NTSTATUS,
+ pub ExceptionFlags: u32,
+ pub ExceptionRecord: *mut EXCEPTION_RECORD,
+ pub ExceptionAddress: *mut std::ffi::c_void,
+ pub NumberParameters: u32,
+ pub ExceptionInformation: [usize; 15],
+}
+
+#[repr(C)]
+pub struct EXCEPTION_POINTERS {
+ pub ExceptionRecord: *mut EXCEPTION_RECORD,
+ pub ContextRecord: *mut CONTEXT,
+}