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 /// 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 { if bytes.len() != std::mem::size_of::() { return None; } unsafe { Some((*bytes.as_ptr().cast::()).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::(), std::mem::size_of::() ); } }