1 // Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10 
11 //! Backtrace support using libunwind/gcc_s/etc APIs.
12 //!
13 //! This module contains the ability to unwind the stack using libunwind-style
14 //! APIs. Note that there's a whole bunch of implementations of the
15 //! libunwind-like API, and this is just trying to be compatible with most of
16 //! them all at once instead of being picky.
17 //!
18 //! The libunwind API is powered by `_Unwind_Backtrace` and is in practice very
19 //! reliable at generating a backtrace. It's not entirely clear how it does it
20 //! (frame pointers? eh_frame info? both?) but it seems to work!
21 //!
22 //! Most of the complexity of this module is handling the various platform
23 //! differences across libunwind implementations. Otherwise this is a pretty
24 //! straightforward Rust binding to the libunwind APIs.
25 //!
26 //! This is the default unwinding API for all non-Windows platforms currently.
27 
28 use core::ffi::c_void;
29 
30 pub enum Frame {
31     Raw(*mut uw::_Unwind_Context),
32     Cloned {
33         ip: *mut c_void,
34         symbol_address: *mut c_void,
35     },
36 }
37 
38 // With a raw libunwind pointer it should only ever be access in a readonly
39 // threadsafe fashion, so it's `Sync`. When sending to other threads via `Clone`
40 // we always switch to a version which doesn't retain interior pointers, so we
41 // should be `Send` as well.
42 unsafe impl Send for Frame {}
43 unsafe impl Sync for Frame {}
44 
45 impl Frame {
ip(&self) -> *mut c_void46     pub fn ip(&self) -> *mut c_void {
47         let ctx = match *self {
48             Frame::Raw(ctx) => ctx,
49             Frame::Cloned { ip, .. } => return ip,
50         };
51         unsafe { uw::_Unwind_GetIP(ctx) as *mut c_void }
52     }
53 
symbol_address(&self) -> *mut c_void54     pub fn symbol_address(&self) -> *mut c_void {
55         if let Frame::Cloned { symbol_address, .. } = *self {
56             return symbol_address;
57         }
58 
59         // It seems that on OSX `_Unwind_FindEnclosingFunction` returns a
60         // pointer to... something that's unclear. It's definitely not always
61         // the enclosing function for whatever reason. It's not entirely clear
62         // to me what's going on here, so pessimize this for now and just always
63         // return the ip.
64         //
65         // Note the `skip_inner_frames.rs` test is skipped on OSX due to this
66         // clause, and if this is fixed that test in theory can be run on OSX!
67         if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
68             self.ip()
69         } else {
70             unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) }
71         }
72     }
73 }
74 
75 impl Clone for Frame {
clone(&self) -> Frame76     fn clone(&self) -> Frame {
77         Frame::Cloned {
78             ip: self.ip(),
79             symbol_address: self.symbol_address(),
80         }
81     }
82 }
83 
84 #[inline(always)]
trace(mut cb: &mut FnMut(&super::Frame) -> bool)85 pub unsafe fn trace(mut cb: &mut FnMut(&super::Frame) -> bool) {
86     uw::_Unwind_Backtrace(trace_fn, &mut cb as *mut _ as *mut _);
87 
88     extern "C" fn trace_fn(
89         ctx: *mut uw::_Unwind_Context,
90         arg: *mut c_void,
91     ) -> uw::_Unwind_Reason_Code {
92         let cb = unsafe { &mut *(arg as *mut &mut FnMut(&super::Frame) -> bool) };
93         let cx = super::Frame {
94             inner: Frame::Raw(ctx),
95         };
96 
97         let mut bomb = crate::Bomb { enabled: true };
98         let keep_going = cb(&cx);
99         bomb.enabled = false;
100 
101         if keep_going {
102             uw::_URC_NO_REASON
103         } else {
104             uw::_URC_FAILURE
105         }
106     }
107 }
108 
109 /// Unwind library interface used for backtraces
110 ///
111 /// Note that dead code is allowed as here are just bindings
112 /// iOS doesn't use all of them it but adding more
113 /// platform-specific configs pollutes the code too much
114 #[allow(non_camel_case_types)]
115 #[allow(non_snake_case)]
116 #[allow(dead_code)]
117 mod uw {
118     pub use self::_Unwind_Reason_Code::*;
119 
120     use core::ffi::c_void;
121 
122     #[repr(C)]
123     pub enum _Unwind_Reason_Code {
124         _URC_NO_REASON = 0,
125         _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
126         _URC_FATAL_PHASE2_ERROR = 2,
127         _URC_FATAL_PHASE1_ERROR = 3,
128         _URC_NORMAL_STOP = 4,
129         _URC_END_OF_STACK = 5,
130         _URC_HANDLER_FOUND = 6,
131         _URC_INSTALL_CONTEXT = 7,
132         _URC_CONTINUE_UNWIND = 8,
133         _URC_FAILURE = 9, // used only by ARM EABI
134     }
135 
136     pub enum _Unwind_Context {}
137 
138     pub type _Unwind_Trace_Fn =
139         extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code;
140 
141     extern "C" {
142         // No native _Unwind_Backtrace on iOS
143         #[cfg(not(all(target_os = "ios", target_arch = "arm")))]
_Unwind_Backtrace( trace: _Unwind_Trace_Fn, trace_argument: *mut c_void, ) -> _Unwind_Reason_Code144         pub fn _Unwind_Backtrace(
145             trace: _Unwind_Trace_Fn,
146             trace_argument: *mut c_void,
147         ) -> _Unwind_Reason_Code;
148 
149         // available since GCC 4.2.0, should be fine for our purpose
150         #[cfg(all(
151             not(all(target_os = "android", target_arch = "arm")),
152             not(all(target_os = "freebsd", target_arch = "arm")),
153             not(all(target_os = "linux", target_arch = "arm"))
154         ))]
_Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t155         pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
156 
157         #[cfg(all(
158             not(target_os = "android"),
159             not(all(target_os = "freebsd", target_arch = "arm")),
160             not(all(target_os = "linux", target_arch = "arm"))
161         ))]
_Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void162         pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void;
163     }
164 
165     // On android, the function _Unwind_GetIP is a macro, and this is the
166     // expansion of the macro. This is all copy/pasted directly from the
167     // header file with the definition of _Unwind_GetIP.
168     #[cfg(any(
169         all(target_os = "android", target_arch = "arm"),
170         all(target_os = "freebsd", target_arch = "arm"),
171         all(target_os = "linux", target_arch = "arm")
172     ))]
_Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t173     pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
174         #[repr(C)]
175         enum _Unwind_VRS_Result {
176             _UVRSR_OK = 0,
177             _UVRSR_NOT_IMPLEMENTED = 1,
178             _UVRSR_FAILED = 2,
179         }
180         #[repr(C)]
181         enum _Unwind_VRS_RegClass {
182             _UVRSC_CORE = 0,
183             _UVRSC_VFP = 1,
184             _UVRSC_FPA = 2,
185             _UVRSC_WMMXD = 3,
186             _UVRSC_WMMXC = 4,
187         }
188         #[repr(C)]
189         enum _Unwind_VRS_DataRepresentation {
190             _UVRSD_UINT32 = 0,
191             _UVRSD_VFPX = 1,
192             _UVRSD_FPAX = 2,
193             _UVRSD_UINT64 = 3,
194             _UVRSD_FLOAT = 4,
195             _UVRSD_DOUBLE = 5,
196         }
197 
198         type _Unwind_Word = libc::c_uint;
199         extern "C" {
200             fn _Unwind_VRS_Get(
201                 ctx: *mut _Unwind_Context,
202                 klass: _Unwind_VRS_RegClass,
203                 word: _Unwind_Word,
204                 repr: _Unwind_VRS_DataRepresentation,
205                 data: *mut c_void,
206             ) -> _Unwind_VRS_Result;
207         }
208 
209         let mut val: _Unwind_Word = 0;
210         let ptr = &mut val as *mut _Unwind_Word;
211         let _ = _Unwind_VRS_Get(
212             ctx,
213             _Unwind_VRS_RegClass::_UVRSC_CORE,
214             15,
215             _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
216             ptr as *mut c_void,
217         );
218         (val & !1) as libc::uintptr_t
219     }
220 
221     // This function also doesn't exist on Android or ARM/Linux, so make it
222     // a no-op
223     #[cfg(any(
224         target_os = "android",
225         all(target_os = "freebsd", target_arch = "arm"),
226         all(target_os = "linux", target_arch = "arm")
227     ))]
_Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void228     pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void {
229         pc
230     }
231 }
232