vex_libunwind/
lib.rs

1//! Idiomatic Rust bindings for LLVM `libunwind` on VEX V5 robots
2//!
3//! ```no_run
4//! # use vex_libunwind::*;
5//! let context = UnwindContext::new().unwrap();
6//! let mut cursor = UnwindCursor::new(&context);
7//!
8//! loop {
9//!     // Print instruction pointer (i.e. "program counter")
10//!     println!("{:?}", cursor.register(registers::UNW_REG_IP));
11//!
12//!     if !cursor.step().unwrap() {
13//!         // End of stack reached
14//!         break;
15//!     }
16//! }
17//! ```
18#![no_std]
19
20use core::{cell::RefCell, ffi::CStr, fmt::Debug, marker::PhantomData, mem::MaybeUninit};
21
22use snafu::Snafu;
23pub use vex_libunwind_sys::registers;
24use vex_libunwind_sys::*;
25
26/// An error that can occur during unwinding.
27#[derive(Debug, Snafu)]
28pub enum UnwindError {
29    /// Unspecified/general error.
30    Unspecified,
31    /// Out of memory
32    NoMemory,
33    /// Invalid register
34    BadRegister,
35    /// Attempt to write to a read-only register
36    WriteToReadOnlyRegister,
37    /// Stop unwinding
38    StopUnwinding,
39    /// Invalid instruction pointer
40    InvalidIP,
41    /// Bad frame
42    BadFrame,
43    /// Unsupported operation or bad value
44    BadValue,
45    /// Unwind info has unsupported version
46    BadVersion,
47    /// No unwind info found
48    NoInfo,
49    /// An error with an unknown error code occured
50    #[snafu(display("libunwind error {code}"))]
51    Unknown {
52        /// The error's code
53        code: uw_error_t,
54    },
55}
56
57impl UnwindError {
58    /// Creates a `Result` that is `Ok` if the error code represents a success
59    /// and `Err` if it represents an error.
60    pub const fn from_code(code: uw_error_t) -> Result<uw_error_t, UnwindError> {
61        if code >= error::UNW_ESUCCESS {
62            Ok(code)
63        } else {
64            Err(match code {
65                error::UNW_EUNSPEC => UnwindError::Unspecified,
66                error::UNW_ENOMEM => UnwindError::NoMemory,
67                error::UNW_EBADREG => UnwindError::BadRegister,
68                error::UNW_EREADONLYREG => UnwindError::WriteToReadOnlyRegister,
69                error::UNW_ESTOPUNWIND => UnwindError::StopUnwinding,
70                error::UNW_EINVALIDIP => UnwindError::InvalidIP,
71                error::UNW_EBADFRAME => UnwindError::BadFrame,
72                error::UNW_EINVAL => UnwindError::BadValue,
73                error::UNW_EBADVERSION => UnwindError::BadVersion,
74                error::UNW_ENOINFO => UnwindError::NoInfo,
75                code => UnwindError::Unknown { code },
76            })
77        }
78    }
79}
80
81/// Holds an immutable snapshot of the current CPU's registers at a certain point of
82/// execution.
83///
84/// The context does not contain an entire stack backtrace, just the information
85/// required to begin stepping through the call chain. To do so, create an
86/// [`UnwindCursor`] from the data in this snapshot.
87#[derive(Clone)]
88pub struct UnwindContext<'a> {
89    // RefCells are used because FFI functions that do not mutate take mutable pointers for some
90    // reason.
91    inner: RefCell<unw_context_t>,
92    _phantom: PhantomData<&'a ()>,
93}
94
95impl UnwindContext<'_> {
96    /// Captures a snapshot of the current CPU state, allowing for local
97    /// unwinding.
98    ///
99    /// The given handler function is passed the unwind context. The context
100    /// is not allowed to escape the current scope because it would be invalidated
101    /// if the stack frame it points to was destroyed.
102    #[inline(always)] // Inlining keeps this function from appearing in backtraces
103    pub fn capture<R>(handler: impl FnOnce(Self) -> Result<R, UnwindError>) -> Result<R, UnwindError> {
104        let mut inner = MaybeUninit::<unw_context_t>::uninit();
105
106        // SAFETY: `unw_getcontext` initializes the context struct. The context is
107        // created and dropped in the same function, so it will not outlive the current
108        // stack frame.
109        let ctx = unsafe {
110            UnwindError::from_code(unw_getcontext(inner.as_mut_ptr()))?;
111            Self::from_raw(inner.assume_init())
112        };
113
114        handler(ctx)
115    }
116
117    /// Creates a new UnwindContext from a raw context acquired through FFI.
118    ///
119    /// # Safety
120    ///
121    /// The returned context must be dropped before the stack frame it points at is
122    /// destroyed.
123    #[inline(always)]
124    pub const unsafe fn from_raw(raw: unw_context_t) -> Self {
125       Self {
126            inner: RefCell::new(raw),
127            _phantom: PhantomData,
128        }
129    }
130
131    /// Returns the underlying `libunwind` object.
132    pub fn as_mut_ptr(&mut self) -> *mut unw_context_t {
133        &mut *self.inner.get_mut()
134    }
135}
136
137impl Debug for UnwindContext<'_> {
138    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
139        f.debug_struct("UnwindContext").finish_non_exhaustive()
140    }
141}
142
143/// A cursor that can move up the call chain and gather information about stack
144/// frames.
145///
146/// This struct provides functionality for reading and writing the CPU registers
147/// that were preserved in stack frames, as well as moving "up" the call chain
148/// to previous function calls. It can be initialized to point to a certain frame
149/// using the data in an [`UnwindContext`].
150#[derive(Clone)]
151pub struct UnwindCursor<'a> {
152    inner: RefCell<unw_cursor_t>,
153    _phantom: PhantomData<&'a ()>,
154}
155
156impl<'a> UnwindCursor<'a> {
157    /// Initializes a cursor for local unwinding using the state captured by the
158    /// given [`UnwindContext`].
159    pub fn new(context: &UnwindContext<'a>) -> Result<Self, UnwindError> {
160        let mut cursor = MaybeUninit::<unw_cursor_t>::uninit();
161        // SAFETY: `unw_init_local` initializes the cursor struct. A reference to
162        // `context` is not stored in the cursor.
163        let cursor = unsafe {
164            UnwindError::from_code(unw_init_local(
165                cursor.as_mut_ptr(),
166                &mut *context.inner.borrow_mut(),
167            ))?;
168            cursor.assume_init()
169        };
170
171        Ok(Self {
172            inner: RefCell::new(cursor),
173            _phantom: context._phantom,
174        })
175    }
176
177    /// Advances to the next (older) frame of the call chain.
178    ///
179    /// Returns true if was another frame to step to or false
180    /// if the cursor has reached the end.
181    ///
182    /// # Errors
183    ///
184    /// This function may return one of the following errors:
185    ///
186    /// - [`UnwindError::Unspecified`] if an unspecified error occurred
187    /// - [`UnwindError::NoInfo`] if `libunwind` was unable to locate the
188    ///   required unwind info
189    /// - [`UnwindError::BadVersion`] if the unwind info has an unsupported
190    ///   version or format
191    /// - [`UnwindError::InvalidIP`] if the instruction pointer of the next
192    ///   frame is invalid
193    /// - [`UnwindError::BadFrame`] if the next frame is invalid
194    pub fn step(&mut self) -> Result<bool, UnwindError> {
195        let code = UnwindError::from_code(unsafe { unw_step(&mut *self.inner.borrow_mut()) })?;
196        Ok(code == UNW_STEP_SUCCESS)
197    }
198
199    /// Retrieves the value of the given register for the cursor's current
200    /// frame.
201    ///
202    /// # Errors
203    ///
204    /// This function may return one of the following errors:
205    ///
206    /// - [`UnwindError::Unspecified`] if an unspecified error occurred
207    /// - [`UnwindError::BadRegister`] if the register was invalid or
208    ///   inaccessible in the current frame
209    pub fn register(&self, register: unw_regnum_t) -> Result<usize, UnwindError> {
210        let mut reg_value = 0;
211        UnwindError::from_code(unsafe {
212            unw_get_reg(&mut *self.inner.borrow_mut(), register, &mut reg_value)
213        })?;
214        Ok(reg_value)
215    }
216
217    /// Sets the value of the given register in the cursor's current frame to
218    /// the given value.
219    ///
220    /// # Safety
221    ///
222    /// The caller must ensure that updating the stack frame as described above
223    /// will not cause undefined behavior.
224    ///
225    /// # Errors
226    ///
227    /// This function may return one of the following errors:
228    ///
229    /// - [`UnwindError::Unspecified`] if an unspecified error occurred
230    /// - [`UnwindError::BadRegister`] if the register was invalid or
231    ///   inaccessible in the current frame
232    /// - [`UnwindError::WriteToReadOnlyRegister`] if the register was read-only
233    pub unsafe fn set_register(
234        &self,
235        register: unw_regnum_t,
236        value: unw_word_t,
237    ) -> Result<(), UnwindError> {
238        UnwindError::from_code(unsafe {
239            unw_set_reg(&mut *self.inner.borrow_mut(), register, value)
240        })?;
241        Ok(())
242    }
243
244    /// Retrieves the value of the given floating point register for the
245    /// cursor's current frame.
246    ///
247    /// # Errors
248    ///
249    /// This function may return one of the following errors:
250    ///
251    /// - [`UnwindError::Unspecified`] if an unspecified error occurred
252    /// - [`UnwindError::BadRegister`] if the register was invalid or
253    ///   inaccessible in the current frame
254    pub fn fp_register(&self, register: unw_regnum_t) -> Result<usize, UnwindError> {
255        let mut reg_value = 0;
256        UnwindError::from_code(unsafe {
257            unw_get_reg(&mut *self.inner.borrow_mut(), register, &mut reg_value)
258        })?;
259        Ok(reg_value)
260    }
261
262    /// Sets the value of the given floating-point register in the cursor's
263    /// current frame to the given value.
264    ///
265    /// # Safety
266    ///
267    /// The caller must ensure that updating the stack frame as described above
268    /// will not cause undefined behavior.
269    ///
270    /// # Errors
271    ///
272    /// This function may return one of the following errors:
273    ///
274    /// - [`UnwindError::Unspecified`] if an unspecified error occurred
275    /// - [`UnwindError::BadRegister`] if the register was invalid or
276    ///   inaccessible in the current frame
277    /// - [`UnwindError::WriteToReadOnlyRegister`] if the register was read-only
278    pub unsafe fn set_fp_register(
279        &self,
280        register: unw_regnum_t,
281        value: unw_fpreg_t,
282    ) -> Result<(), UnwindError> {
283        UnwindError::from_code(unsafe {
284            unw_set_fpreg(&mut *self.inner.borrow_mut(), register, value)
285        })?;
286        Ok(())
287    }
288
289    /// Checks whether the given register is a floating-point register.
290    pub fn is_fp_register(&self, register: unw_regnum_t) -> bool {
291        unsafe { unw_is_fpreg(&mut *self.inner.borrow_mut(), register) > 0 }
292    }
293
294    /// Checks whether the current frame is a "signal frame," which is defined
295    /// as a frame created in response to a potentially asynchronous
296    /// interruption such as a device interrupt.
297    ///
298    /// Signal frames offer access to a larger range of registers because their
299    /// nature requires saving the contents of registers normally treated as
300    /// "scratch" registers.
301    ///
302    /// Corresponds to [`unw_is_signal_frame`](https://www.nongnu.org/libunwind/man/unw_is_signal_frame(3).html).
303    ///
304    /// # Errors
305    ///
306    /// If `libunwind` is unable to determine whether the cursor is pointing to
307    /// a signal frame, [`UnwindError::NoInfo`] is returned.
308    pub fn is_signal_frame(&self) -> Result<bool, UnwindError> {
309        let code = unsafe { unw_is_signal_frame(&mut *self.inner.borrow_mut()) };
310        UnwindError::from_code(code)?;
311        Ok(code > 0)
312    }
313
314    /// Returns the name of the given register as a string, or [`None`] if the
315    /// register does not exist.
316    pub fn register_name(&self, register: unw_regnum_t) -> Option<&'static CStr> {
317        let unknown = c"unknown register";
318        // SAFETY: libunwind guarantees string is statically allocated and valid
319        let str = unsafe { CStr::from_ptr(unw_regname(&mut *self.inner.borrow_mut(), register)) };
320        if str == unknown {
321            None
322        } else {
323            Some(str)
324        }
325    }
326
327    /// Creates a new UnwindCursor from a raw cursor acquired through FFI.
328    ///
329    /// # Safety
330    ///
331    /// The returned cursor must be dropped before the stack frame it points at is
332    /// destroyed.
333    #[inline(always)]
334    pub const unsafe fn from_raw(raw: unw_cursor_t) -> Self {
335       Self {
336            inner: RefCell::new(raw),
337            _phantom: PhantomData,
338        }
339    }
340}
341
342impl Debug for UnwindCursor<'_> {
343    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
344        let mut s = f.debug_struct("UnwindCursor");
345        if let Ok(ip) = self.register(registers::UNW_REG_IP) {
346            s.field("ip", &(ip as *const ())).finish()
347        } else {
348            s.finish_non_exhaustive()
349        }
350    }
351}