crash_context/
linux.rs

1mod getcontext;
2
3pub use getcontext::crash_context_getcontext;
4
5/// The full context for a Linux/Android crash
6#[repr(C)]
7#[derive(Clone)]
8pub struct CrashContext {
9    /// Crashing thread context.
10    ///
11    /// Note that we use [`crate::ucontext_t`] instead of [`libc::ucontext_t`]
12    /// as libc's differs between glibc and musl <https://github.com/rust-lang/libc/pull/1646>
13    /// even though the `ucontext_t` received from a signal will be the same
14    /// regardless of the libc implementation used as it is only arch specific
15    /// and not libc specific
16    ///
17    /// Note that we hide `ucontext_t::uc_link` as it is a pointer and thus can't
18    /// be accessed in a process other than the one the `CrashContext` was created
19    /// in. This is a just a self-reference so is not useful in practice.
20    ///
21    /// Note that the same applies to [`mcontext_t::fpregs`], but since that points
22    /// to floating point registers and _is_ interesting to read in another process,
23    /// those registers available as [`Self::float_state`], except on the `arm`
24    /// architecture since they aren't part of `mcontext_t` at all.
25    pub context: ucontext_t,
26    /// State of floating point registers.
27    ///
28    /// This isn't part of the user ABI for Linux arm
29    #[cfg(not(target_arch = "arm"))]
30    pub float_state: fpregset_t,
31    /// The signal info for the crash
32    pub siginfo: libc::signalfd_siginfo,
33    /// The id of the crashing process
34    pub pid: libc::pid_t,
35    /// The id of the crashing thread
36    pub tid: libc::pid_t,
37}
38
39unsafe impl Send for CrashContext {}
40
41impl CrashContext {
42    pub fn as_bytes(&self) -> &[u8] {
43        unsafe {
44            let size = std::mem::size_of_val(self);
45            let ptr = (self as *const Self).cast();
46            std::slice::from_raw_parts(ptr, size)
47        }
48    }
49
50    pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
51        if bytes.len() != std::mem::size_of::<Self>() {
52            return None;
53        }
54
55        unsafe { Some((*bytes.as_ptr().cast::<Self>()).clone()) }
56    }
57}
58
59#[repr(C)]
60#[derive(Clone)]
61#[doc(hidden)]
62pub struct sigset_t {
63    #[cfg(target_pointer_width = "32")]
64    __val: [u32; 32],
65    #[cfg(target_pointer_width = "64")]
66    __val: [u64; 16],
67}
68
69#[repr(C)]
70#[derive(Clone)]
71#[doc(hidden)]
72pub struct stack_t {
73    pub ss_sp: *mut std::ffi::c_void,
74    pub ss_flags: i32,
75    pub ss_size: usize,
76}
77
78cfg_if::cfg_if! {
79    if #[cfg(target_arch = "x86_64")] {
80        #[repr(C)]
81        #[derive(Clone)]
82        #[doc(hidden)]
83        pub struct ucontext_t {
84            pub uc_flags: u64,
85            uc_link: *mut ucontext_t,
86            pub uc_stack: stack_t,
87            pub uc_mcontext: mcontext_t,
88            pub uc_sigmask: sigset_t,
89            __private: [u8; 512],
90        }
91
92        #[repr(C)]
93        #[derive(Clone)]
94        #[doc(hidden)]
95        pub struct mcontext_t {
96            pub gregs: [i64; 23],
97            pub fpregs: *mut fpregset_t,
98            __reserved: [u64; 8],
99        }
100
101        #[repr(C)]
102        #[derive(Clone)]
103        #[doc(hidden)]
104        pub struct fpregset_t {
105            pub cwd: u16,
106            pub swd: u16,
107            pub ftw: u16,
108            pub fop: u16,
109            pub rip: u64,
110            pub rdp: u64,
111            pub mxcsr: u32,
112            pub mxcr_mask: u32,
113            pub st_space: [u32; 32],
114            pub xmm_space: [u32; 64],
115            __padding: [u64; 12],
116        }
117    } else if #[cfg(target_arch = "x86")] {
118        #[repr(C)]
119        #[derive(Clone)]
120        #[doc(hidden)]
121        pub struct ucontext_t {
122            pub uc_flags: u32,
123            uc_link: *mut ucontext_t,
124            pub uc_stack: stack_t,
125            pub uc_mcontext: mcontext_t,
126            pub uc_sigmask: sigset_t,
127            pub __fpregs_mem: [u32; 28],
128        }
129
130        #[repr(C)]
131        #[derive(Clone)]
132        #[doc(hidden)]
133        pub struct mcontext_t {
134            pub gregs: [i32; 23],
135            pub fpregs: *mut fpregset_t,
136            pub oldmask: u32,
137            pub cr2: u32,
138        }
139
140        #[repr(C)]
141        #[derive(Clone)]
142        #[doc(hidden)]
143        pub struct fpreg_t {
144            pub significand: [u16; 4],
145            pub exponent: u16,
146        }
147
148        #[repr(C)]
149        #[derive(Clone)]
150        #[doc(hidden)]
151        pub struct fpregset_t {
152            pub cw: u32,
153            pub sw: u32,
154            pub tag: u32,
155            pub ipoff: u32,
156            pub cssel: u32,
157            pub dataoff: u32,
158            pub datasel: u32,
159            pub _st: [fpreg_t; 8],
160            pub status: u32,
161        }
162    } else if #[cfg(target_arch = "aarch64")] {
163        #[repr(C)]
164        #[derive(Clone)]
165        #[doc(hidden)]
166        pub struct ucontext_t {
167            pub uc_flags: u64,
168            uc_link: *mut ucontext_t,
169            pub uc_stack: stack_t,
170            pub uc_sigmask: sigset_t,
171            pub uc_mcontext: mcontext_t,
172        }
173
174        // Note you might see this defined in C with `unsigned long` or
175        // `unsigned long long` and think, WTF, those aren't the same! Except
176        // `long` means either 32-bit _or_ 64-bit depending on the data model,
177        // and the default data model for C/C++ is LP64 which means long is
178        // 64-bit. I had forgotten what a trash type long was.
179        #[repr(C)]
180        #[derive(Clone)]
181        #[doc(hidden)]
182        pub struct mcontext_t {
183            // Note in the kernel this is just part of regs which is length 32
184            pub fault_address: u64,
185            pub regs: [u64; 31],
186            pub sp: u64,
187            pub pc: u64,
188            pub pstate: u64,
189            // Note that u128 is ABI safe on aarch64, this is actually a
190            // `long double` in C which Rust doesn't have native support
191            pub __reserved: [u128; 256],
192        }
193
194        /// Magic value written by the kernel and our custom getcontext
195        #[doc(hidden)]
196        pub const FPSIMD_MAGIC: u32 = 0x46508001;
197
198        #[repr(C)]
199        #[derive(Clone)]
200        #[doc(hidden)]
201        pub struct _aarch64_ctx {
202            pub magic: u32,
203            pub size: u32,
204        }
205
206        #[repr(C)]
207        #[derive(Clone)]
208        #[doc(hidden)]
209        pub struct fpsimd_context {
210            pub head: _aarch64_ctx,
211            pub fpsr: u32,
212            pub fpcr: u32,
213            pub vregs: [u128; 32],
214        }
215
216        #[doc(hidden)]
217        pub type fpregset_t = fpsimd_context;
218    } else if #[cfg(target_arch = "arm")] {
219        #[repr(C)]
220        #[derive(Clone)]
221        #[doc(hidden)]
222        pub struct ucontext_t {
223            pub uc_flags: u32,
224            uc_link: *mut ucontext_t,
225            pub uc_stack: stack_t,
226            // Note that the mcontext_t and sigset_t are swapped compared to
227            // all of the other arches currently supported :p
228            pub uc_mcontext: mcontext_t,
229            pub uc_sigmask: sigset_t,
230            pub uc_regspace: [u64; 64],
231        }
232
233        #[repr(C)]
234        #[derive(Clone)]
235        #[doc(hidden)]
236        pub struct mcontext_t {
237            pub trap_no: u32,
238            pub error_code: u32,
239            pub oldmask: u32,
240            pub arm_r0: u32,
241            pub arm_r1: u32,
242            pub arm_r2: u32,
243            pub arm_r3: u32,
244            pub arm_r4: u32,
245            pub arm_r5: u32,
246            pub arm_r6: u32,
247            pub arm_r7: u32,
248            pub arm_r8: u32,
249            pub arm_r9: u32,
250            pub arm_r10: u32,
251            pub arm_fp: u32,
252            pub arm_ip: u32,
253            pub arm_sp: u32,
254            pub arm_lr: u32,
255            pub arm_pc: u32,
256            pub arm_cpsr: u32,
257            pub fault_address: u32,
258        }
259    }
260}
261
262#[cfg(test)]
263mod test {
264    // Musl doesn't contain fpregs in libc because reasons https://github.com/rust-lang/libc/pull/1646
265    #[cfg(not(target_env = "musl"))]
266    #[test]
267    fn matches_libc() {
268        assert_eq!(
269            std::mem::size_of::<libc::ucontext_t>(),
270            std::mem::size_of::<super::ucontext_t>()
271        );
272    }
273}