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