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            /// Shadow stack
91            __ssp: [u64; 4],
92        }
93
94        #[repr(C)]
95        #[derive(Clone)]
96        #[doc(hidden)]
97        pub struct mcontext_t {
98            pub gregs: [i64; 23],
99            pub fpregs: *mut fpregset_t,
100            __reserved: [u64; 8],
101        }
102
103        #[repr(C)]
104        #[derive(Clone)]
105        #[doc(hidden)]
106        pub struct fpregset_t {
107            pub cwd: u16,
108            pub swd: u16,
109            pub ftw: u16,
110            pub fop: u16,
111            pub rip: u64,
112            pub rdp: u64,
113            pub mxcsr: u32,
114            pub mxcr_mask: u32,
115            pub st_space: [u32; 32],
116            pub xmm_space: [u32; 64],
117            __padding: [u64; 12],
118        }
119    } else if #[cfg(target_arch = "x86")] {
120        #[repr(C)]
121        #[derive(Clone)]
122        #[doc(hidden)]
123        pub struct ucontext_t {
124            pub uc_flags: u32,
125            uc_link: *mut ucontext_t,
126            pub uc_stack: stack_t,
127            pub uc_mcontext: mcontext_t,
128            pub uc_sigmask: sigset_t,
129            pub __fpregs_mem: [u32; 28],
130        }
131
132        #[repr(C)]
133        #[derive(Clone)]
134        #[doc(hidden)]
135        pub struct mcontext_t {
136            pub gregs: [i32; 23],
137            pub fpregs: *mut fpregset_t,
138            pub oldmask: u32,
139            pub cr2: u32,
140        }
141
142        #[repr(C)]
143        #[derive(Clone)]
144        #[doc(hidden)]
145        pub struct fpreg_t {
146            pub significand: [u16; 4],
147            pub exponent: u16,
148        }
149
150        #[repr(C)]
151        #[derive(Clone)]
152        #[doc(hidden)]
153        pub struct fpregset_t {
154            pub cw: u32,
155            pub sw: u32,
156            pub tag: u32,
157            pub ipoff: u32,
158            pub cssel: u32,
159            pub dataoff: u32,
160            pub datasel: u32,
161            pub _st: [fpreg_t; 8],
162            pub status: u32,
163        }
164    } else if #[cfg(target_arch = "aarch64")] {
165        #[repr(C)]
166        #[derive(Clone)]
167        #[doc(hidden)]
168        pub struct ucontext_t {
169            pub uc_flags: u64,
170            uc_link: *mut ucontext_t,
171            pub uc_stack: stack_t,
172            pub uc_sigmask: sigset_t,
173            pub uc_mcontext: mcontext_t,
174        }
175
176        // Note you might see this defined in C with `unsigned long` or
177        // `unsigned long long` and think, WTF, those aren't the same! Except
178        // `long` means either 32-bit _or_ 64-bit depending on the data model,
179        // and the default data model for C/C++ is LP64 which means long is
180        // 64-bit. I had forgotten what a trash type long was.
181        #[repr(C)]
182        #[derive(Clone)]
183        #[doc(hidden)]
184        pub struct mcontext_t {
185            // Note in the kernel this is just part of regs which is length 32
186            pub fault_address: u64,
187            pub regs: [u64; 31],
188            pub sp: u64,
189            pub pc: u64,
190            pub pstate: u64,
191            // Note that u128 is ABI safe on aarch64, this is actually a
192            // `long double` in C which Rust doesn't have native support
193            pub __reserved: [u128; 256],
194        }
195
196        /// Magic value written by the kernel and our custom getcontext
197        #[doc(hidden)]
198        pub const FPSIMD_MAGIC: u32 = 0x46508001;
199
200        #[repr(C)]
201        #[derive(Clone)]
202        #[doc(hidden)]
203        pub struct _aarch64_ctx {
204            pub magic: u32,
205            pub size: u32,
206        }
207
208        #[repr(C)]
209        #[derive(Clone)]
210        #[doc(hidden)]
211        pub struct fpsimd_context {
212            pub head: _aarch64_ctx,
213            pub fpsr: u32,
214            pub fpcr: u32,
215            pub vregs: [u128; 32],
216        }
217
218        #[doc(hidden)]
219        pub type fpregset_t = fpsimd_context;
220    } else if #[cfg(target_arch = "arm")] {
221        #[repr(C)]
222        #[derive(Clone)]
223        #[doc(hidden)]
224        pub struct ucontext_t {
225            pub uc_flags: u32,
226            uc_link: *mut ucontext_t,
227            pub uc_stack: stack_t,
228            // Note that the mcontext_t and sigset_t are swapped compared to
229            // all of the other arches currently supported :p
230            pub uc_mcontext: mcontext_t,
231            pub uc_sigmask: sigset_t,
232            pub uc_regspace: [u64; 64],
233        }
234
235        #[repr(C)]
236        #[derive(Clone)]
237        #[doc(hidden)]
238        pub struct mcontext_t {
239            pub trap_no: u32,
240            pub error_code: u32,
241            pub oldmask: u32,
242            pub arm_r0: u32,
243            pub arm_r1: u32,
244            pub arm_r2: u32,
245            pub arm_r3: u32,
246            pub arm_r4: u32,
247            pub arm_r5: u32,
248            pub arm_r6: u32,
249            pub arm_r7: u32,
250            pub arm_r8: u32,
251            pub arm_r9: u32,
252            pub arm_r10: u32,
253            pub arm_fp: u32,
254            pub arm_ip: u32,
255            pub arm_sp: u32,
256            pub arm_lr: u32,
257            pub arm_pc: u32,
258            pub arm_cpsr: u32,
259            pub fault_address: u32,
260        }
261    } else if #[cfg(target_arch = "riscv64")] {
262        #[repr(C)]
263        #[derive(Clone)]
264        #[doc(hidden)]
265        pub struct ucontext_t {
266            pub __uc_flags: u64,
267            pub uc_link: *mut ucontext_t,
268            pub uc_stack: stack_t,
269            pub uc_sigmask: sigset_t,
270            pub uc_mcontext: mcontext_t,
271        }
272
273        #[repr(C, align(16))]
274        #[derive(Clone)]
275        #[doc(hidden)]
276        pub struct mcontext_t {
277            pub __gregs: [u64; 32],
278            pub __fpregs: __riscv_mc_fp_state,
279        }
280
281        #[repr(C)]
282        #[derive(Clone, Copy)]
283        #[doc(hidden)]
284        pub union __riscv_mc_fp_state {
285            pub __f: __riscv_mc_f_ext_state,
286            pub __d: __riscv_mc_d_ext_state,
287            pub __q: __riscv_mc_q_ext_state,
288        }
289
290        #[repr(C)]
291        #[derive(Clone, Copy)]
292        #[doc(hidden)]
293        pub struct __riscv_mc_f_ext_state {
294            pub __f: [u32; 32],
295            pub __fcsr: u32,
296        }
297
298        #[repr(C)]
299        #[derive(Clone, Copy)]
300        #[doc(hidden)]
301        pub struct __riscv_mc_d_ext_state {
302            pub __f: [u64; 32],
303            pub __fcsr: u32,
304        }
305
306        #[repr(C, align(16))]
307        #[derive(Clone, Copy)]
308        #[doc(hidden)]
309        pub struct __riscv_mc_q_ext_state {
310            pub __f: [u64; 64],
311            pub __fcsr: u32,
312            pub __reserved: [u32; 3],
313        }
314
315        pub type fpregset_t = __riscv_mc_fp_state;
316    } else if #[cfg(target_arch = "s390x")] {
317        #[repr(C)]
318        #[derive(Clone)]
319        #[doc(hidden)]
320        pub struct ucontext_t {
321            pub uc_flags: u64,
322            pub uc_link: *mut ucontext_t,
323            pub uc_stack: stack_t,
324            pub uc_mcontext: mcontext_t,
325            pub uc_sigmask: sigset_t,
326        }
327
328        #[repr(C, align(8))]
329        #[derive(Clone,Copy)]
330        #[doc(hidden)]
331        pub struct mcontext_t {
332            pub psw: psw_t,
333            pub gregs: [u64; 16],
334            pub aregs: [u32; 16],
335            pub __fpregs: fpregset_t,
336        }
337
338        #[repr(C)]
339        #[derive(Clone,Copy)]
340        #[doc(hidden)]
341        pub struct fpregset_t {
342            pub fpc: u32,
343            __pad: u32,
344            pub fprs: [fpreg_t; 16],
345        }
346
347        #[repr(C)]
348        #[derive(Clone,Copy)]
349        #[doc(hidden)]
350        pub struct fpreg_t {
351            pub d: f64,
352            // pub f: f32,
353        }
354
355        #[repr(C)]
356        #[derive(Clone,Copy)]
357        #[doc(hidden)]
358        pub struct psw_t {
359            pub mask: u64,
360            pub addr: u64,
361        }
362    }
363}
364
365#[cfg(test)]
366mod test {
367    // Musl doesn't contain fpregs in libc because reasons https://github.com/rust-lang/libc/pull/1646
368    #[cfg(not(target_env = "musl"))]
369    #[test]
370    fn matches_libc() {
371        assert_eq!(
372            std::mem::size_of::<libc::ucontext_t>(),
373            std::mem::size_of::<super::ucontext_t>()
374        );
375    }
376}