#
std/backtrace/src/backtrace/
libunwind.rs

    1//! Backtrace support using libunwind/gcc_s/etc APIs.
    2//!
    3//! This module contains the ability to unwind the stack using libunwind-style
    4//! APIs. Note that there's a whole bunch of implementations of the
    5//! libunwind-like API, and this is just trying to be compatible with most of
    6//! them all at once instead of being picky.
    7//!
    8//! The libunwind API is powered by `_Unwind_Backtrace` and is in practice very
    9//! reliable at generating a backtrace. It's not entirely clear how it does it
    10//! (frame pointers? eh_frame info? both?) but it seems to work!
    11//!
    12//! Most of the complexity of this module is handling the various platform
    13//! differences across libunwind implementations. Otherwise this is a pretty
    14//! straightforward Rust binding to the libunwind APIs.
    15//!
    16//! This is the default unwinding API for all non-Windows platforms currently.
    17
    18use core::ffi::c_void;
    19use core::ptr::addr_of_mut;
    20
    21pub enum Frame {
    22    Raw(*mut uw::_Unwind_Context),
    23    Cloned {
    24        ip: *mut c_void,
    25        sp: *mut c_void,
    26        symbol_address: *mut c_void,
    27    },
    28}
    29
    30// With a raw libunwind pointer it should only ever be access in a readonly
    31// threadsafe fashion, so it's `Sync`. When sending to other threads via `Clone`
    32// we always switch to a version which doesn't retain interior pointers, so we
    33// should be `Send` as well.
    34unsafe impl Send for Frame {}
    35unsafe impl Sync for Frame {}
    36
    37impl Frame {
    38    pub fn ip(&self) -> *mut c_void {
    39        let ctx = match *self {
    40            Frame::Raw(ctx) => ctx,
    41            Frame::Cloned { ip, .. } => return ip,
    42        };
    43        #[allow(unused_mut)]
    44        let mut ip = unsafe { uw::_Unwind_GetIP(ctx) as *mut c_void };
    45
    46        // To reduce TCB size in SGX enclaves, we do not want to implement
    47        // symbol resolution functionality. Rather, we can print the offset of
    48        // the address here, which could be later mapped to correct function.
    49        #[cfg(all(target_env = "sgx", target_vendor = "fortanix"))]
    50        {
    51            let image_base = super::sgx_image_base::get_image_base();
    52            ip = usize::wrapping_sub(ip as usize, image_base as _) as _;
    53        }
    54        ip
    55    }
    56
    57    pub fn sp(&self) -> *mut c_void {
    58        match *self {
    59            Frame::Raw(ctx) => unsafe { uw::get_sp(ctx) as *mut c_void },
    60            Frame::Cloned { sp, .. } => sp,
    61        }
    62    }
    63
    64    pub fn symbol_address(&self) -> *mut c_void {
    65        if let Frame::Cloned { symbol_address, .. } = *self {
    66            return symbol_address;
    67        }
    68
    69        // The macOS linker emits a "compact" unwind table that only includes an
    70        // entry for a function if that function either has an LSDA or its
    71        // encoding differs from that of the previous entry.  Consequently, on
    72        // macOS, `_Unwind_FindEnclosingFunction` is unreliable (it can return a
    73        // pointer to some totally unrelated function).  Instead, we just always
    74        // return the ip.
    75        //
    76        // https://github.com/rust-lang/rust/issues/74771#issuecomment-664056788
    77        //
    78        // Note the `skip_inner_frames.rs` test is skipped on macOS due to this
    79        // clause, and if this is fixed that test in theory can be run on macOS!
    80        if cfg!(target_vendor = "apple") {
    81            self.ip()
    82        } else {
    83            unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) }
    84        }
    85    }
    86
    87    pub fn module_base_address(&self) -> Option<*mut c_void> {
    88        None
    89    }
    90}
    91
    92impl Clone for Frame {
    93    fn clone(&self) -> Frame {
    94        Frame::Cloned {
    95            ip: self.ip(),
    96            sp: self.sp(),
    97            symbol_address: self.symbol_address(),
    98        }
    99    }
    100}
    101
    102struct Bomb {
    103    enabled: bool,
    104}
    105
    106impl Drop for Bomb {
    107    fn drop(&mut self) {
    108        if self.enabled {
    109            panic!("cannot panic during the backtrace function");
    110        }
    111    }
    112}
    113
    114#[inline(always)]
    115pub unsafe fn trace(mut cb: &mut dyn FnMut(&super::Frame) -> bool) {
    116    unsafe {
    117        uw::_Unwind_Backtrace(trace_fn, addr_of_mut!(cb).cast());
    118    }
    119
    120    extern "C" fn trace_fn(
    121        ctx: *mut uw::_Unwind_Context,
    122        arg: *mut c_void,
    123    ) -> uw::_Unwind_Reason_Code {
    124        let cb = unsafe { &mut *arg.cast::<&mut dyn FnMut(&super::Frame) -> bool>() };
    125        let cx = super::Frame {
    126            inner: Frame::Raw(ctx),
    127        };
    128
    129        let mut bomb = Bomb { enabled: true };
    130        let keep_going = cb(&cx);
    131        bomb.enabled = false;
    132
    133        if keep_going {
    134            uw::_URC_NO_REASON
    135        } else {
    136            uw::_URC_FAILURE
    137        }
    138    }
    139}
    140
    141/// Unwind library interface used for backtraces
    142///
    143/// Note that dead code is allowed as here are just bindings
    144/// iOS doesn't use all of them it but adding more
    145/// platform-specific configs pollutes the code too much
    146#[allow(non_camel_case_types)]
    147#[allow(non_snake_case)]
    148#[allow(dead_code)]
    149mod uw {
    150    pub use self::_Unwind_Reason_Code::*;
    151
    152    use core::ffi::c_void;
    153
    154    #[repr(C)]
    155    pub enum _Unwind_Reason_Code {
    156        _URC_NO_REASON = 0,
    157        _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
    158        _URC_FATAL_PHASE2_ERROR = 2,
    159        _URC_FATAL_PHASE1_ERROR = 3,
    160        _URC_NORMAL_STOP = 4,
    161        _URC_END_OF_STACK = 5,
    162        _URC_HANDLER_FOUND = 6,
    163        _URC_INSTALL_CONTEXT = 7,
    164        _URC_CONTINUE_UNWIND = 8,
    165        _URC_FAILURE = 9, // used only by ARM EABI
    166    }
    167
    168    pub enum _Unwind_Context {}
    169
    170    pub type _Unwind_Trace_Fn =
    171        extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code;
    172
    173    unsafe extern "C" {
    174        pub fn _Unwind_Backtrace(
    175            trace: _Unwind_Trace_Fn,
    176            trace_argument: *mut c_void,
    177        ) -> _Unwind_Reason_Code;
    178    }
    179
    180    cfg_if::cfg_if! {
    181        // available since GCC 4.2.0, should be fine for our purpose
    182        if #[cfg(all(
    183            not(all(target_os = "android", target_arch = "arm")),
    184            not(all(target_os = "freebsd", target_arch = "arm")),
    185            not(all(target_os = "linux", target_arch = "arm")),
    186            not(all(target_os = "horizon", target_arch = "arm")),
    187            not(all(target_os = "rtems", target_arch = "arm")),
    188            not(all(target_os = "vita", target_arch = "arm")),
    189            not(all(target_os = "nuttx", target_arch = "arm")),
    190        ))] {
    191            unsafe extern "C" {
    192                pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
    193                pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void;
    194
    195                #[cfg(not(all(target_os = "linux", target_arch = "s390x")))]
    196                // This function is a misnomer: rather than getting this frame's
    197                // Canonical Frame Address (aka the caller frame's SP) it
    198                // returns this frame's SP.
    199                //
    200                // https://github.com/libunwind/libunwind/blob/d32956507cf29d9b1a98a8bce53c78623908f4fe/src/unwind/GetCFA.c#L28-L35
    201                #[link_name = "_Unwind_GetCFA"]
    202                pub fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
    203
    204            }
    205
    206            // s390x uses a biased CFA value, therefore we need to use
    207            // _Unwind_GetGR to get the stack pointer register (%r15)
    208            // instead of relying on _Unwind_GetCFA.
    209            #[cfg(all(target_os = "linux", target_arch = "s390x"))]
    210            pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
    211                unsafe extern "C" {
    212                    pub fn _Unwind_GetGR(ctx: *mut _Unwind_Context, index: libc::c_int) -> libc::uintptr_t;
    213                }
    214                unsafe { _Unwind_GetGR(ctx, 15) }
    215            }
    216        } else {
    217            use core::ptr::addr_of_mut;
    218
    219            // On android and arm, the function `_Unwind_GetIP` and a bunch of
    220            // others are macros, so we define functions containing the
    221            // expansion of the macros.
    222            //
    223            // TODO: link to the header file that defines these macros, if you
    224            // can find it. (I, fitzgen, cannot find the header file that some
    225            // of these macro expansions were originally borrowed from.)
    226            #[repr(C)]
    227            enum _Unwind_VRS_Result {
    228                _UVRSR_OK = 0,
    229                _UVRSR_NOT_IMPLEMENTED = 1,
    230                _UVRSR_FAILED = 2,
    231            }
    232            #[repr(C)]
    233            enum _Unwind_VRS_RegClass {
    234                _UVRSC_CORE = 0,
    235                _UVRSC_VFP = 1,
    236                _UVRSC_FPA = 2,
    237                _UVRSC_WMMXD = 3,
    238                _UVRSC_WMMXC = 4,
    239            }
    240            #[repr(C)]
    241            enum _Unwind_VRS_DataRepresentation {
    242                _UVRSD_UINT32 = 0,
    243                _UVRSD_VFPX = 1,
    244                _UVRSD_FPAX = 2,
    245                _UVRSD_UINT64 = 3,
    246                _UVRSD_FLOAT = 4,
    247                _UVRSD_DOUBLE = 5,
    248            }
    249
    250            type _Unwind_Word = libc::c_uint;
    251            unsafe extern "C" {
    252                fn _Unwind_VRS_Get(
    253                    ctx: *mut _Unwind_Context,
    254                    klass: _Unwind_VRS_RegClass,
    255                    word: _Unwind_Word,
    256                    repr: _Unwind_VRS_DataRepresentation,
    257                    data: *mut c_void,
    258                ) -> _Unwind_VRS_Result;
    259            }
    260
    261            pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
    262                let mut val: _Unwind_Word = 0;
    263                let ptr = addr_of_mut!(val);
    264                unsafe {
    265                    let _ = _Unwind_VRS_Get(
    266                        ctx,
    267                        _Unwind_VRS_RegClass::_UVRSC_CORE,
    268                        15,
    269                        _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
    270                        ptr.cast::<c_void>(),
    271                    );
    272                }
    273                (val & !1) as libc::uintptr_t
    274            }
    275
    276            // R13 is the stack pointer on arm.
    277            const SP: _Unwind_Word = 13;
    278
    279            pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
    280                let mut val: _Unwind_Word = 0;
    281                let ptr = addr_of_mut!(val);
    282                unsafe {
    283                    let _ = _Unwind_VRS_Get(
    284                        ctx,
    285                        _Unwind_VRS_RegClass::_UVRSC_CORE,
    286                        SP,
    287                        _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
    288                        ptr.cast::<c_void>(),
    289                    );
    290                }
    291                val as libc::uintptr_t
    292            }
    293
    294            // This function also doesn't exist on Android or ARM/Linux, so make it
    295            // a no-op.
    296            pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void {
    297                pc
    298            }
    299        }
    300    }
    301}