diff options
Diffstat (limited to 'third_party/rust/crash-context/src/linux.rs')
-rw-r--r-- | third_party/rust/crash-context/src/linux.rs | 273 |
1 files changed, 273 insertions, 0 deletions
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>() + ); + } +} |