]> git.proxmox.com Git - rustc.git/blob - vendor/backtrace/src/backtrace/libunwind.rs
New upstream version 1.38.0+dfsg1
[rustc.git] / vendor / backtrace / src / backtrace / libunwind.rs
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 {
46 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
56 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 {
78 fn clone(&self) -> Frame {
79 Frame::Cloned {
80 ip: self.ip(),
81 symbol_address: self.symbol_address(),
82 }
83 }
84 }
85
86 #[inline(always)]
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 = 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 fn(ctx: *mut _Unwind_Context,
140 arg: *mut c_void) -> _Unwind_Reason_Code;
141
142 extern {
143 // No native _Unwind_Backtrace on iOS
144 #[cfg(not(all(target_os = "ios", target_arch = "arm")))]
145 pub fn _Unwind_Backtrace(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(not(all(target_os = "android", target_arch = "arm")),
151 not(all(target_os = "freebsd", target_arch = "arm")),
152 not(all(target_os = "linux", target_arch = "arm"))))]
153 pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context)
154 -> libc::uintptr_t;
155
156 #[cfg(all(not(target_os = "android"),
157 not(all(target_os = "freebsd", target_arch = "arm")),
158 not(all(target_os = "linux", target_arch = "arm"))))]
159 pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void)
160 -> *mut c_void;
161 }
162
163 // On android, the function _Unwind_GetIP is a macro, and this is the
164 // expansion of the macro. This is all copy/pasted directly from the
165 // header file with the definition of _Unwind_GetIP.
166 #[cfg(any(all(target_os = "android", target_arch = "arm"),
167 all(target_os = "freebsd", target_arch = "arm"),
168 all(target_os = "linux", target_arch = "arm")))]
169 pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
170 #[repr(C)]
171 enum _Unwind_VRS_Result {
172 _UVRSR_OK = 0,
173 _UVRSR_NOT_IMPLEMENTED = 1,
174 _UVRSR_FAILED = 2,
175 }
176 #[repr(C)]
177 enum _Unwind_VRS_RegClass {
178 _UVRSC_CORE = 0,
179 _UVRSC_VFP = 1,
180 _UVRSC_FPA = 2,
181 _UVRSC_WMMXD = 3,
182 _UVRSC_WMMXC = 4,
183 }
184 #[repr(C)]
185 enum _Unwind_VRS_DataRepresentation {
186 _UVRSD_UINT32 = 0,
187 _UVRSD_VFPX = 1,
188 _UVRSD_FPAX = 2,
189 _UVRSD_UINT64 = 3,
190 _UVRSD_FLOAT = 4,
191 _UVRSD_DOUBLE = 5,
192 }
193
194 type _Unwind_Word = libc::c_uint;
195 extern {
196 fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context,
197 klass: _Unwind_VRS_RegClass,
198 word: _Unwind_Word,
199 repr: _Unwind_VRS_DataRepresentation,
200 data: *mut c_void)
201 -> _Unwind_VRS_Result;
202 }
203
204 let mut val: _Unwind_Word = 0;
205 let ptr = &mut val as *mut _Unwind_Word;
206 let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15,
207 _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
208 ptr as *mut c_void);
209 (val & !1) as libc::uintptr_t
210 }
211
212 // This function also doesn't exist on Android or ARM/Linux, so make it
213 // a no-op
214 #[cfg(any(target_os = "android",
215 all(target_os = "freebsd", target_arch = "arm"),
216 all(target_os = "linux", target_arch = "arm")))]
217 pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void)
218 -> *mut c_void
219 {
220 pc
221 }
222 }
223
224