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}