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