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 
18 use super::super::Bomb;
19 use core::ffi::c_void;
20 
21 pub 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.
34 unsafe impl Send for Frame {}
35 unsafe impl Sync for Frame {}
36 
37 impl Frame {
ip(&self) -> *mut c_void38     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         unsafe { uw::_Unwind_GetIP(ctx) as *mut c_void }
44     }
45 
sp(&self) -> *mut c_void46     pub fn sp(&self) -> *mut c_void {
47         match *self {
48             Frame::Raw(ctx) => unsafe { uw::get_sp(ctx) as *mut c_void },
49             Frame::Cloned { sp, .. } => sp,
50         }
51     }
52 
symbol_address(&self) -> *mut c_void53     pub fn symbol_address(&self) -> *mut c_void {
54         if let Frame::Cloned { symbol_address, .. } = *self {
55             return symbol_address;
56         }
57 
58         // The macOS linker emits a "compact" unwind table that only includes an
59         // entry for a function if that function either has an LSDA or its
60         // encoding differs from that of the previous entry.  Consequently, on
61         // macOS, `_Unwind_FindEnclosingFunction` is unreliable (it can return a
62         // pointer to some totally unrelated function).  Instead, we just always
63         // return the ip.
64         //
65         // https://github.com/rust-lang/rust/issues/74771#issuecomment-664056788
66         //
67         // Note the `skip_inner_frames.rs` test is skipped on macOS due to this
68         // clause, and if this is fixed that test in theory can be run on macOS!
69         if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
70             self.ip()
71         } else {
72             unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) }
73         }
74     }
75 
module_base_address(&self) -> Option<*mut c_void>76     pub fn module_base_address(&self) -> Option<*mut c_void> {
77         None
78     }
79 }
80 
81 impl Clone for Frame {
clone(&self) -> Frame82     fn clone(&self) -> Frame {
83         Frame::Cloned {
84             ip: self.ip(),
85             sp: self.sp(),
86             symbol_address: self.symbol_address(),
87         }
88     }
89 }
90 
91 #[inline(always)]
trace(mut cb: &mut dyn FnMut(&super::Frame) -> bool)92 pub unsafe fn trace(mut cb: &mut dyn FnMut(&super::Frame) -> bool) {
93     uw::_Unwind_Backtrace(trace_fn, &mut cb as *mut _ as *mut _);
94 
95     extern "C" fn trace_fn(
96         ctx: *mut uw::_Unwind_Context,
97         arg: *mut c_void,
98     ) -> uw::_Unwind_Reason_Code {
99         let cb = unsafe { &mut *(arg as *mut &mut dyn FnMut(&super::Frame) -> bool) };
100         let cx = super::Frame {
101             inner: Frame::Raw(ctx),
102         };
103 
104         let mut bomb = Bomb { enabled: true };
105         let keep_going = cb(&cx);
106         bomb.enabled = false;
107 
108         if keep_going {
109             uw::_URC_NO_REASON
110         } else {
111             uw::_URC_FAILURE
112         }
113     }
114 }
115 
116 /// Unwind library interface used for backtraces
117 ///
118 /// Note that dead code is allowed as here are just bindings
119 /// iOS doesn't use all of them it but adding more
120 /// platform-specific configs pollutes the code too much
121 #[allow(non_camel_case_types)]
122 #[allow(non_snake_case)]
123 #[allow(dead_code)]
124 mod uw {
125     pub use self::_Unwind_Reason_Code::*;
126 
127     use core::ffi::c_void;
128 
129     #[repr(C)]
130     pub enum _Unwind_Reason_Code {
131         _URC_NO_REASON = 0,
132         _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
133         _URC_FATAL_PHASE2_ERROR = 2,
134         _URC_FATAL_PHASE1_ERROR = 3,
135         _URC_NORMAL_STOP = 4,
136         _URC_END_OF_STACK = 5,
137         _URC_HANDLER_FOUND = 6,
138         _URC_INSTALL_CONTEXT = 7,
139         _URC_CONTINUE_UNWIND = 8,
140         _URC_FAILURE = 9, // used only by ARM EABI
141     }
142 
143     pub enum _Unwind_Context {}
144 
145     pub type _Unwind_Trace_Fn =
146         extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code;
147 
148     extern "C" {
_Unwind_Backtrace( trace: _Unwind_Trace_Fn, trace_argument: *mut c_void, ) -> _Unwind_Reason_Code149         pub fn _Unwind_Backtrace(
150             trace: _Unwind_Trace_Fn,
151             trace_argument: *mut c_void,
152         ) -> _Unwind_Reason_Code;
153     }
154 
155     cfg_if::cfg_if! {
156         // available since GCC 4.2.0, should be fine for our purpose
157         if #[cfg(all(
158             not(all(target_os = "android", target_arch = "arm")),
159             not(all(target_os = "freebsd", target_arch = "arm")),
160             not(all(target_os = "linux", target_arch = "arm")),
161             not(all(target_os = "horizon", target_arch = "arm"))
162         ))] {
163             extern "C" {
164                 pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
165                 pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void;
166 
167                 #[cfg(not(all(target_os = "linux", target_arch = "s390x")))]
168                 // This function is a misnomer: rather than getting this frame's
169                 // Canonical Frame Address (aka the caller frame's SP) it
170                 // returns this frame's SP.
171                 //
172                 // https://github.com/libunwind/libunwind/blob/d32956507cf29d9b1a98a8bce53c78623908f4fe/src/unwind/GetCFA.c#L28-L35
173                 #[link_name = "_Unwind_GetCFA"]
174                 pub fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
175 
176             }
177 
178             // s390x uses a biased CFA value, therefore we need to use
179             // _Unwind_GetGR to get the stack pointer register (%r15)
180             // instead of relying on _Unwind_GetCFA.
181             #[cfg(all(target_os = "linux", target_arch = "s390x"))]
182             pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
183                 extern "C" {
184                     pub fn _Unwind_GetGR(ctx: *mut _Unwind_Context, index: libc::c_int) -> libc::uintptr_t;
185                 }
186                 _Unwind_GetGR(ctx, 15)
187             }
188         } else {
189             // On android and arm, the function `_Unwind_GetIP` and a bunch of
190             // others are macros, so we define functions containing the
191             // expansion of the macros.
192             //
193             // TODO: link to the header file that defines these macros, if you
194             // can find it. (I, fitzgen, cannot find the header file that some
195             // of these macro expansions were originally borrowed from.)
196             #[repr(C)]
197             enum _Unwind_VRS_Result {
198                 _UVRSR_OK = 0,
199                 _UVRSR_NOT_IMPLEMENTED = 1,
200                 _UVRSR_FAILED = 2,
201             }
202             #[repr(C)]
203             enum _Unwind_VRS_RegClass {
204                 _UVRSC_CORE = 0,
205                 _UVRSC_VFP = 1,
206                 _UVRSC_FPA = 2,
207                 _UVRSC_WMMXD = 3,
208                 _UVRSC_WMMXC = 4,
209             }
210             #[repr(C)]
211             enum _Unwind_VRS_DataRepresentation {
212                 _UVRSD_UINT32 = 0,
213                 _UVRSD_VFPX = 1,
214                 _UVRSD_FPAX = 2,
215                 _UVRSD_UINT64 = 3,
216                 _UVRSD_FLOAT = 4,
217                 _UVRSD_DOUBLE = 5,
218             }
219 
220             type _Unwind_Word = libc::c_uint;
221             extern "C" {
222                 fn _Unwind_VRS_Get(
223                     ctx: *mut _Unwind_Context,
224                     klass: _Unwind_VRS_RegClass,
225                     word: _Unwind_Word,
226                     repr: _Unwind_VRS_DataRepresentation,
227                     data: *mut c_void,
228                 ) -> _Unwind_VRS_Result;
229             }
230 
231             pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
232                 let mut val: _Unwind_Word = 0;
233                 let ptr = &mut val as *mut _Unwind_Word;
234                 let _ = _Unwind_VRS_Get(
235                     ctx,
236                     _Unwind_VRS_RegClass::_UVRSC_CORE,
237                     15,
238                     _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
239                     ptr as *mut c_void,
240                 );
241                 (val & !1) as libc::uintptr_t
242             }
243 
244             // R13 is the stack pointer on arm.
245             const SP: _Unwind_Word = 13;
246 
247             pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
248                 let mut val: _Unwind_Word = 0;
249                 let ptr = &mut val as *mut _Unwind_Word;
250                 let _ = _Unwind_VRS_Get(
251                     ctx,
252                     _Unwind_VRS_RegClass::_UVRSC_CORE,
253                     SP,
254                     _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
255                     ptr as *mut c_void,
256                 );
257                 val as libc::uintptr_t
258             }
259 
260             // This function also doesn't exist on Android or ARM/Linux, so make it
261             // a no-op.
262             pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void {
263                 pc
264             }
265         }
266     }
267 }
268