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