unwind/
lib.rs

1//! An interface to [libunwind].
2//!
3//! libunwind provides access to the call chain of a process. It supports both local and remote
4//! processes.
5//!
6//! # Examples
7//!
8//! Printing a backtrace of the current thread:
9//!
10//! ```
11//! use unwind::{Cursor, RegNum, get_context};
12//!
13//! get_context!(context);
14//! let mut cursor = Cursor::local(context).unwrap();
15//!
16//! loop {
17//!     let ip = cursor.register(RegNum::IP).unwrap();
18//!
19//!     match (cursor.procedure_info(), cursor.procedure_name()) {
20//!         (Ok(ref info), Ok(ref name)) if ip == info.start_ip() + name.offset() => {
21//!             println!(
22//!                 "{:#016x} - {} ({:#016x}) + {:#x}",
23//!                 ip,
24//!                 name.name(),
25//!                 info.start_ip(),
26//!                 name.offset()
27//!             );
28//!         }
29//!         _ => println!("{:#016x} - ????", ip),
30//!     }
31//!
32//!     if !cursor.step().unwrap() {
33//!         break;
34//!     }
35//! }
36//! ```
37//!
38//! [libunwind]: http://www.nongnu.org/libunwind/
39#![doc(html_root_url = "https://sfackler.github.io/rstack/doc")]
40#![warn(missing_docs)]
41
42use foreign_types::Opaque;
43#[cfg(feature = "ptrace")]
44use foreign_types::{foreign_type, ForeignType};
45use libc::{c_char, c_int, c_void};
46use std::error;
47use std::ffi::CStr;
48use std::fmt;
49use std::marker::PhantomData;
50use std::marker::PhantomPinned;
51use std::mem::MaybeUninit;
52use std::ops::{Deref, DerefMut};
53use std::pin::Pin;
54use std::result;
55use unwind_sys::*;
56
57#[doc(hidden)]
58pub mod private {
59    pub use std::mem::MaybeUninit;
60    pub use std::pin::Pin;
61    pub use unwind_sys::unw_tdep_getcontext;
62}
63
64/// The result type returned by functions in this crate.
65pub type Result<T> = result::Result<T, Error>;
66
67/// An error returned from libunwind.
68#[derive(Copy, Clone, Debug, PartialEq, Eq)]
69pub struct Error(c_int);
70
71impl Error {
72    /// Unspecified error.
73    pub const UNSPEC: Error = Error(-UNW_EUNSPEC);
74
75    /// Out of memory.
76    pub const NOMEM: Error = Error(-UNW_ENOMEM);
77
78    /// Bad register number.
79    pub const BADREG: Error = Error(-UNW_EBADREG);
80
81    /// Attempt to write read-only register.
82    pub const READONLYREG: Error = Error(-UNW_EREADONLYREG);
83
84    /// Stop unwinding.
85    pub const STOPUNWIND: Error = Error(-UNW_ESTOPUNWIND);
86
87    /// Invalid IP.
88    pub const INVALIDIP: Error = Error(-UNW_EINVALIDIP);
89
90    /// Bad frame.
91    pub const BADFRAME: Error = Error(-UNW_EBADFRAME);
92
93    /// Unsupported operation or bad value.
94    pub const INVAL: Error = Error(-UNW_EINVAL);
95
96    /// Unwind info has unsupported value.
97    pub const BADVERSION: Error = Error(-UNW_EBADVERSION);
98
99    /// No unwind info found.
100    pub const NOINFO: Error = Error(-UNW_ENOINFO);
101}
102
103impl fmt::Display for Error {
104    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
105        unsafe {
106            let err = unw_strerror(self.0);
107            let err = CStr::from_ptr(err).to_string_lossy();
108            fmt.write_str(&err)
109        }
110    }
111}
112
113impl error::Error for Error {}
114
115/// The byteorder of an address space.
116#[derive(Copy, Clone)]
117pub struct Byteorder(c_int);
118
119impl Byteorder {
120    /// The default byte order of the unwind target.
121    pub const DEFAULT: Byteorder = Byteorder(0);
122
123    /// Little endian.
124    pub const LITTLE_ENDIAN: Byteorder = Byteorder(1234);
125
126    /// Big endian.
127    pub const BIG_ENDIAN: Byteorder = Byteorder(4321);
128
129    /// PDP endian.
130    pub const PDP_ENDIAN: Byteorder = Byteorder(3412);
131}
132
133#[cfg(feature = "ptrace")]
134foreign_type! {
135    /// The unwind state used by the ptrace accessors.
136    ///
137    /// The `ptrace` Cargo feature must be enabled to use this type.
138    pub unsafe type PTraceState {
139        type CType = c_void;
140        fn drop = _UPT_destroy;
141    }
142}
143
144#[cfg(feature = "ptrace")]
145impl PTraceState {
146    /// Constructs a new `PTraceState` for the specified PID.
147    ///
148    /// The process must already be attached and suspended before unwinding can be performed.
149    pub fn new(pid: u32) -> Result<PTraceState> {
150        unsafe {
151            let ptr = _UPT_create(pid as _);
152            if ptr.is_null() {
153                // this is documented to only fail on OOM
154                Err(Error(-UNW_ENOMEM))
155            } else {
156                Ok(PTraceState::from_ptr(ptr))
157            }
158        }
159    }
160}
161
162/// A collection of functions used to unwind an arbitrary process.
163pub struct Accessors<T>(unw_accessors_t, PhantomData<T>);
164
165#[cfg(feature = "ptrace")]
166impl Accessors<PTraceStateRef> {
167    /// Returns `Accessors` which use the ptrace system call to unwind a remote process.
168    ///
169    /// The `ptrace` Cargo feature must be enabled to use this type.
170    pub fn ptrace() -> &'static Accessors<PTraceStateRef> {
171        unsafe { &*(&_UPT_accessors as *const unw_accessors_t as *const Accessors<PTraceStateRef>) }
172    }
173}
174
175/// An address space upon which unwinding can be performed.
176pub struct AddressSpace<T>(unw_addr_space_t, PhantomData<T>);
177
178impl<T> Drop for AddressSpace<T> {
179    fn drop(&mut self) {
180        unsafe {
181            unw_destroy_addr_space(self.0);
182        }
183    }
184}
185
186impl<T> Deref for AddressSpace<T> {
187    type Target = AddressSpaceRef<T>;
188
189    fn deref(&self) -> &AddressSpaceRef<T> {
190        unsafe { &*(self.0 as *const AddressSpaceRef<T>) }
191    }
192}
193
194impl<T> DerefMut for AddressSpace<T> {
195    fn deref_mut(&mut self) -> &mut AddressSpaceRef<T> {
196        unsafe { &mut *(self.0 as *mut AddressSpaceRef<T>) }
197    }
198}
199
200impl<T> AddressSpace<T> {
201    /// Creates a new `AddressSpace`.
202    pub fn new(accessors: &Accessors<T>, byteorder: Byteorder) -> Result<AddressSpace<T>> {
203        unsafe {
204            let ptr = unw_create_addr_space(
205                &accessors.0 as *const unw_accessors_t as *mut unw_accessors_t,
206                byteorder.0,
207            );
208            if ptr.is_null() {
209                Err(Error(-UNW_EUNSPEC))
210            } else {
211                Ok(AddressSpace(ptr, PhantomData))
212            }
213        }
214    }
215}
216
217/// A borrowed reference to an [`AddressSpace`].
218///
219/// [`AddressSpace`]: struct.AddressSpace.html
220pub struct AddressSpaceRef<T>(Opaque, PhantomData<T>);
221
222impl<T> AddressSpaceRef<T> {
223    fn as_ptr(&self) -> unw_addr_space_t {
224        self as *const _ as *mut _
225    }
226}
227
228/// An identifier of a processor register.
229#[derive(Copy, Clone)]
230pub struct RegNum(c_int);
231
232impl RegNum {
233    /// A generic identifier for the register storing the instruction pointer.
234    pub const IP: RegNum = RegNum(UNW_REG_IP);
235
236    /// A generic identifier for the register storing the stack pointer.
237    pub const SP: RegNum = RegNum(UNW_REG_SP);
238}
239
240#[cfg(not(pre16))]
241#[cfg(target_arch = "x86_64")]
242mod x86_64;
243
244/// Information about a procedure.
245#[derive(Copy, Clone)]
246pub struct ProcedureInfo {
247    start_ip: u64,
248    end_ip: u64,
249}
250
251impl ProcedureInfo {
252    /// Returns the starting address of the procedure.
253    pub fn start_ip(&self) -> u64 {
254        self.start_ip
255    }
256
257    /// Returns the ending address of the procedure.
258    pub fn end_ip(&self) -> u64 {
259        self.end_ip
260    }
261}
262
263/// The name of a procedure.
264#[derive(Clone)]
265pub struct ProcedureName {
266    name: String,
267    offset: u64,
268}
269
270impl ProcedureName {
271    /// Returns the name of the procedure.
272    pub fn name(&self) -> &str {
273        &self.name
274    }
275
276    /// Returns the offset of the frame's instruction pointer from the starting address of the named
277    /// procedure.
278    pub fn offset(&self) -> u64 {
279        self.offset
280    }
281}
282
283/// A snapshot of the machine-state of a process.
284///
285/// A pinned context can be created with the `get_context!` macro.
286pub struct Context(#[doc(hidden)] pub unw_context_t, PhantomPinned);
287
288/// Creates a `Context` pinned to the stack.
289///
290/// This is a macro rather than a function due to the implementation of the libunwind library.
291///
292/// # Example
293///
294/// ```
295/// # use unwind::{get_context, Context};
296/// # use std::pin::Pin;
297///
298/// get_context!(context);
299/// let _: Pin<&mut Context> = context;
300/// ```
301#[macro_export]
302macro_rules! get_context {
303    ($name:ident) => {
304        // This is implemented using the same strategy as futures::pin_mut where the Pin shadows the original value,
305        // preventing it from being referenced and therefore moved.
306        let mut $name = $crate::private::MaybeUninit::<$crate::Context>::uninit();
307        unsafe {
308            $crate::private::unw_tdep_getcontext!(&mut (*$name.as_mut_ptr()).0);
309        }
310        let $name = unsafe { $crate::private::Pin::new_unchecked(&mut *$name.as_mut_ptr()) };
311    };
312}
313
314/// A cursor into a frame of a stack.
315///
316/// The cursor starts at the current (topmost) frame, and can be advanced downwards through the
317/// stack. While a cursor cannot be run "backwards", it can be cloned, and one of the copies
318/// advanced while the other continues to refer to the previous frame.
319#[derive(Clone)]
320pub struct Cursor<'a>(unw_cursor_t, PhantomData<&'a ()>);
321
322impl<'a> Cursor<'a> {
323    /// Creates a cursor over the stack of the calling thread.
324    pub fn local(context: Pin<&'a mut Context>) -> Result<Cursor<'a>> {
325        unsafe {
326            let mut cursor = MaybeUninit::uninit();
327            let ret = unw_init_local(cursor.as_mut_ptr(), &mut context.get_unchecked_mut().0);
328            if ret != UNW_ESUCCESS {
329                return Err(Error(ret));
330            }
331
332            Ok(Cursor(cursor.assume_init(), PhantomData))
333        }
334    }
335
336    /// Creates a cursor over the stack of a "remote" process.
337    pub fn remote<T>(address_space: &'a AddressSpaceRef<T>, state: &'a T) -> Result<Cursor<'a>> {
338        unsafe {
339            let mut cursor = MaybeUninit::uninit();
340            let ret = unw_init_remote(
341                cursor.as_mut_ptr(),
342                address_space.as_ptr(),
343                state as *const T as *mut c_void,
344            );
345            if ret == UNW_ESUCCESS {
346                Ok(Cursor(cursor.assume_init(), PhantomData))
347            } else {
348                Err(Error(ret))
349            }
350        }
351    }
352
353    /// Steps the cursor into the next older stack frame.
354    ///
355    /// A return value of `false` indicates that the cursor is at the last frame of the stack.
356    pub fn step(&mut self) -> Result<bool> {
357        unsafe {
358            // libunwind 1.1 seems to get confused and walks off the end of the stack. The last IP
359            // it reports is 0, so we'll stop if we're there.
360            if cfg!(pre12) && self.register(RegNum::IP).unwrap_or(1) == 0 {
361                return Ok(false);
362            }
363
364            let ret = unw_step(&mut self.0);
365            if ret > 0 {
366                Ok(true)
367            } else if ret == 0 {
368                Ok(false)
369            } else {
370                Err(Error(ret))
371            }
372        }
373    }
374
375    /// Returns the value of an integral register at the current frame.
376    ///
377    /// Based on the calling convention, some registers may not be available in a stack frame.
378    pub fn register(&mut self, num: RegNum) -> Result<u64> {
379        unsafe {
380            let mut val = 0;
381            let ret = unw_get_reg(&self.0 as *const _ as *mut _, num.0, &mut val);
382            if ret == UNW_ESUCCESS {
383                Ok(val as u64)
384            } else {
385                Err(Error(ret))
386            }
387        }
388    }
389
390    /// Returns information about the procedure at the current frame.
391    pub fn procedure_info(&mut self) -> Result<ProcedureInfo> {
392        unsafe {
393            let mut info = MaybeUninit::uninit();
394            let ret = unw_get_proc_info(&self.0 as *const _ as *mut _, info.as_mut_ptr());
395            if ret == UNW_ESUCCESS {
396                let info = info.assume_init();
397                Ok(ProcedureInfo {
398                    start_ip: info.start_ip as u64,
399                    end_ip: info.end_ip as u64,
400                })
401            } else {
402                Err(Error(ret))
403            }
404        }
405    }
406
407    /// Returns the name of the procedure of the current frame.
408    ///
409    /// The name is copied into the provided buffer, and is null-terminated. If the buffer is too
410    /// small to hold the full name, [`Error::NOMEM`] is returned and the buffer contains the
411    /// portion of the name that fits (including the null terminator).
412    ///
413    /// The offset of the instruction pointer from the beginning of the identified procedure is
414    /// copied into the `offset` parameter.
415    ///
416    /// The `procedure_name` method provides a higher level wrapper over this method.
417    ///
418    /// In certain contexts, particularly when the binary being unwound has been stripped, the
419    /// unwinder may not have enough information to properly identify the procedure and will simply
420    /// return the first label before the frame's instruction pointer. The offset will always be
421    /// relative to this label.
422    ///
423    /// [`Error::NOMEM`]: struct.Error.html#associatedconstant.NOMEM
424    pub fn procedure_name_raw(&mut self, buf: &mut [u8], offset: &mut u64) -> Result<()> {
425        unsafe {
426            let mut raw_off = 0;
427            let ret = unw_get_proc_name(
428                &self.0 as *const _ as *mut _,
429                buf.as_mut_ptr() as *mut c_char,
430                buf.len(),
431                &mut raw_off,
432            );
433            *offset = raw_off as u64;
434            if ret == UNW_ESUCCESS {
435                Ok(())
436            } else {
437                Err(Error(ret))
438            }
439        }
440    }
441
442    /// Returns the name of the procedure of the current frame.
443    ///
444    /// In certain contexts, particularly when the binary being unwound has been stripped, the
445    /// unwinder may not have enough information to properly identify the procedure and will simply
446    /// return the first label before the frame's instruction pointer. The offset will always be
447    /// relative to this label.
448    pub fn procedure_name(&mut self) -> Result<ProcedureName> {
449        let mut buf = vec![0; 256];
450        loop {
451            let mut offset = 0;
452            match self.procedure_name_raw(&mut buf, &mut offset) {
453                Ok(()) => {
454                    let len = buf.iter().position(|b| *b == 0).unwrap();
455                    buf.truncate(len);
456                    let name = String::from_utf8_lossy(&buf).into_owned();
457                    return Ok(ProcedureName { name, offset });
458                }
459                Err(Error::NOMEM) => {
460                    let len = buf.len() * 2;
461                    buf.resize(len, 0);
462                }
463                Err(e) => return Err(e),
464            }
465        }
466    }
467
468    /// Determines if the current frame is a signal frame.
469    ///
470    /// Signal frames are unique in several ways. More register state is available than normal, and
471    /// the instruction pointer references the currently executing instruction rather than the next
472    /// instruction.
473    pub fn is_signal_frame(&mut self) -> Result<bool> {
474        unsafe {
475            let ret = unw_is_signal_frame(&self.0 as *const _ as *mut _);
476            if ret < 0 {
477                Err(Error(ret))
478            } else {
479                Ok(ret != 0)
480            }
481        }
482    }
483}