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 types::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 {
52             uw::_Unwind_GetIP(ctx) as *mut c_void
53         }
54     }
55 
symbol_address(&self) -> *mut c_void56     pub fn symbol_address(&self) -> *mut c_void {
57         if let Frame::Cloned { symbol_address, .. } = *self {
58             return symbol_address;
59         }
60 
61         // It seems that on OSX `_Unwind_FindEnclosingFunction` returns a
62         // pointer to... something that's unclear. It's definitely not always
63         // the enclosing function for whatever reason. It's not entirely clear
64         // to me what's going on here, so pessimize this for now and just always
65         // return the ip.
66         //
67         // Note the `skip_inner_frames.rs` test is skipped on OSX due to this
68         // clause, and if this is fixed that test in theory can be run on OSX!
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 }
76 
77 impl Clone for Frame {
clone(&self) -> Frame78     fn clone(&self) -> Frame {
79         Frame::Cloned {
80             ip: self.ip(),
81             symbol_address: self.symbol_address(),
82         }
83     }
84 }
85 
86 #[inline(always)]
trace(mut cb: &mut FnMut(&super::Frame) -> bool)87 pub unsafe fn trace(mut cb: &mut FnMut(&super::Frame) -> bool) {
88     uw::_Unwind_Backtrace(trace_fn, &mut cb as *mut _ as *mut _);
89 
90     extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
91                        arg: *mut c_void) -> 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 = ::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 libc;
121     use types::c_void;
122 
123     #[repr(C)]
124     pub enum _Unwind_Reason_Code {
125         _URC_NO_REASON = 0,
126         _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
127         _URC_FATAL_PHASE2_ERROR = 2,
128         _URC_FATAL_PHASE1_ERROR = 3,
129         _URC_NORMAL_STOP = 4,
130         _URC_END_OF_STACK = 5,
131         _URC_HANDLER_FOUND = 6,
132         _URC_INSTALL_CONTEXT = 7,
133         _URC_CONTINUE_UNWIND = 8,
134         _URC_FAILURE = 9, // used only by ARM EABI
135     }
136 
137     pub enum _Unwind_Context {}
138 
139     pub type _Unwind_Trace_Fn =
140             extern fn(ctx: *mut _Unwind_Context,
141                       arg: *mut c_void) -> _Unwind_Reason_Code;
142 
143     extern {
144         // No native _Unwind_Backtrace on iOS
145         #[cfg(not(all(target_os = "ios", target_arch = "arm")))]
_Unwind_Backtrace(trace: _Unwind_Trace_Fn, trace_argument: *mut c_void) -> _Unwind_Reason_Code146         pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
147                                  trace_argument: *mut c_void)
148                     -> _Unwind_Reason_Code;
149 
150         // available since GCC 4.2.0, should be fine for our purpose
151         #[cfg(all(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"))))]
_Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t154         pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context)
155                     -> libc::uintptr_t;
156 
157         #[cfg(all(not(target_os = "android"),
158                   not(all(target_os = "freebsd", target_arch = "arm")),
159                   not(all(target_os = "linux", target_arch = "arm"))))]
_Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void160         pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void)
161             -> *mut c_void;
162     }
163 
164     // On android, the function _Unwind_GetIP is a macro, and this is the
165     // expansion of the macro. This is all copy/pasted directly from the
166     // header file with the definition of _Unwind_GetIP.
167     #[cfg(any(all(target_os = "android", target_arch = "arm"),
168               all(target_os = "freebsd", target_arch = "arm"),
169               all(target_os = "linux", target_arch = "arm")))]
_Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t170     pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
171         #[repr(C)]
172         enum _Unwind_VRS_Result {
173             _UVRSR_OK = 0,
174             _UVRSR_NOT_IMPLEMENTED = 1,
175             _UVRSR_FAILED = 2,
176         }
177         #[repr(C)]
178         enum _Unwind_VRS_RegClass {
179             _UVRSC_CORE = 0,
180             _UVRSC_VFP = 1,
181             _UVRSC_FPA = 2,
182             _UVRSC_WMMXD = 3,
183             _UVRSC_WMMXC = 4,
184         }
185         #[repr(C)]
186         enum _Unwind_VRS_DataRepresentation {
187             _UVRSD_UINT32 = 0,
188             _UVRSD_VFPX = 1,
189             _UVRSD_FPAX = 2,
190             _UVRSD_UINT64 = 3,
191             _UVRSD_FLOAT = 4,
192             _UVRSD_DOUBLE = 5,
193         }
194 
195         type _Unwind_Word = libc::c_uint;
196         extern {
197             fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context,
198                                klass: _Unwind_VRS_RegClass,
199                                word: _Unwind_Word,
200                                repr: _Unwind_VRS_DataRepresentation,
201                                data: *mut c_void)
202                 -> _Unwind_VRS_Result;
203         }
204 
205         let mut val: _Unwind_Word = 0;
206         let ptr = &mut val as *mut _Unwind_Word;
207         let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15,
208                                 _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
209                                 ptr as *mut c_void);
210         (val & !1) as libc::uintptr_t
211     }
212 
213     // This function also doesn't exist on Android or ARM/Linux, so make it
214     // a no-op
215     #[cfg(any(target_os = "android",
216               all(target_os = "freebsd", target_arch = "arm"),
217               all(target_os = "linux", target_arch = "arm")))]
_Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void218     pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void)
219         -> *mut c_void
220     {
221         pc
222     }
223 }
224 
225 
226