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