/rust/registry/src/index.crates.io-6f17d22bba15001f/backtrace-0.3.73/src/backtrace/libunwind.rs
Line | Count | Source (jump to first uncovered line) |
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 core::ffi::c_void; |
19 | | use core::ptr::addr_of_mut; |
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 { |
38 | 93.6k | pub fn ip(&self) -> *mut c_void { |
39 | 93.6k | let ctx = match *self { |
40 | 72.1k | Frame::Raw(ctx) => ctx, |
41 | 21.5k | Frame::Cloned { ip, .. } => return ip, |
42 | | }; |
43 | | #[allow(unused_mut)] |
44 | 72.1k | let mut ip = unsafe { uw::_Unwind_GetIP(ctx) as *mut c_void }; |
45 | 72.1k | |
46 | 72.1k | // To reduce TCB size in SGX enclaves, we do not want to implement |
47 | 72.1k | // symbol resolution functionality. Rather, we can print the offset of |
48 | 72.1k | // the address here, which could be later mapped to correct function. |
49 | 72.1k | #[cfg(all(target_env = "sgx", target_vendor = "fortanix"))] |
50 | 72.1k | { |
51 | 72.1k | let image_base = super::sgx_image_base::get_image_base(); |
52 | 72.1k | ip = usize::wrapping_sub(ip as usize, image_base as _) as _; |
53 | 72.1k | } |
54 | 72.1k | ip |
55 | 93.6k | } |
56 | | |
57 | 24.0k | pub fn sp(&self) -> *mut c_void { |
58 | 24.0k | match *self { |
59 | 24.0k | Frame::Raw(ctx) => unsafe { uw::get_sp(ctx) as *mut c_void }, |
60 | 0 | Frame::Cloned { sp, .. } => sp, |
61 | | } |
62 | 24.0k | } |
63 | | |
64 | 48.1k | pub fn symbol_address(&self) -> *mut c_void { |
65 | 48.1k | if let Frame::Cloned { symbol_address, .. } = *self { |
66 | 0 | return symbol_address; |
67 | 48.1k | } |
68 | 48.1k | |
69 | 48.1k | // The macOS linker emits a "compact" unwind table that only includes an |
70 | 48.1k | // entry for a function if that function either has an LSDA or its |
71 | 48.1k | // encoding differs from that of the previous entry. Consequently, on |
72 | 48.1k | // macOS, `_Unwind_FindEnclosingFunction` is unreliable (it can return a |
73 | 48.1k | // pointer to some totally unrelated function). Instead, we just always |
74 | 48.1k | // return the ip. |
75 | 48.1k | // |
76 | 48.1k | // https://github.com/rust-lang/rust/issues/74771#issuecomment-664056788 |
77 | 48.1k | // |
78 | 48.1k | // Note the `skip_inner_frames.rs` test is skipped on macOS due to this |
79 | 48.1k | // clause, and if this is fixed that test in theory can be run on macOS! |
80 | 48.1k | if cfg!(target_vendor = "apple") { |
81 | 0 | self.ip() |
82 | | } else { |
83 | 48.1k | unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) } |
84 | | } |
85 | 48.1k | } |
86 | | |
87 | 0 | pub fn module_base_address(&self) -> Option<*mut c_void> { |
88 | 0 | None |
89 | 0 | } |
90 | | } |
91 | | |
92 | | impl Clone for Frame { |
93 | 24.0k | fn clone(&self) -> Frame { |
94 | 24.0k | Frame::Cloned { |
95 | 24.0k | ip: self.ip(), |
96 | 24.0k | sp: self.sp(), |
97 | 24.0k | symbol_address: self.symbol_address(), |
98 | 24.0k | } |
99 | 24.0k | } |
100 | | } |
101 | | |
102 | | struct Bomb { |
103 | | enabled: bool, |
104 | | } |
105 | | |
106 | | impl Drop for Bomb { |
107 | 24.0k | fn drop(&mut self) { |
108 | 24.0k | if self.enabled { |
109 | 0 | panic!("cannot panic during the backtrace function"); |
110 | 24.0k | } |
111 | 24.0k | } |
112 | | } |
113 | | |
114 | | #[inline(always)] |
115 | 1.26k | pub unsafe fn trace(mut cb: &mut dyn FnMut(&super::Frame) -> bool) { |
116 | 1.26k | uw::_Unwind_Backtrace(trace_fn, addr_of_mut!(cb).cast()); |
117 | 1.26k | |
118 | 24.0k | extern "C" fn trace_fn( |
119 | 24.0k | ctx: *mut uw::_Unwind_Context, |
120 | 24.0k | arg: *mut c_void, |
121 | 24.0k | ) -> uw::_Unwind_Reason_Code { |
122 | 24.0k | let cb = unsafe { &mut *arg.cast::<&mut dyn FnMut(&super::Frame) -> bool>() }; |
123 | 24.0k | let cx = super::Frame { |
124 | 24.0k | inner: Frame::Raw(ctx), |
125 | 24.0k | }; |
126 | 24.0k | |
127 | 24.0k | let mut bomb = Bomb { enabled: true }; |
128 | 24.0k | let keep_going = cb(&cx); |
129 | 24.0k | bomb.enabled = false; |
130 | 24.0k | |
131 | 24.0k | if keep_going { |
132 | 24.0k | uw::_URC_NO_REASON |
133 | 1.26k | } else { |
134 | 1.26k | uw::_URC_FAILURE |
135 | 1.26k | } |
136 | 24.0k | } |
137 | 1.26k | } |
138 | | |
139 | | /// Unwind library interface used for backtraces |
140 | | /// |
141 | | /// Note that dead code is allowed as here are just bindings |
142 | | /// iOS doesn't use all of them it but adding more |
143 | | /// platform-specific configs pollutes the code too much |
144 | | #[allow(non_camel_case_types)] |
145 | | #[allow(non_snake_case)] |
146 | | #[allow(dead_code)] |
147 | | mod uw { |
148 | | pub use self::_Unwind_Reason_Code::*; |
149 | | |
150 | | use core::ffi::c_void; |
151 | | |
152 | | #[repr(C)] |
153 | | pub enum _Unwind_Reason_Code { |
154 | | _URC_NO_REASON = 0, |
155 | | _URC_FOREIGN_EXCEPTION_CAUGHT = 1, |
156 | | _URC_FATAL_PHASE2_ERROR = 2, |
157 | | _URC_FATAL_PHASE1_ERROR = 3, |
158 | | _URC_NORMAL_STOP = 4, |
159 | | _URC_END_OF_STACK = 5, |
160 | | _URC_HANDLER_FOUND = 6, |
161 | | _URC_INSTALL_CONTEXT = 7, |
162 | | _URC_CONTINUE_UNWIND = 8, |
163 | | _URC_FAILURE = 9, // used only by ARM EABI |
164 | | } |
165 | | |
166 | | pub enum _Unwind_Context {} |
167 | | |
168 | | pub type _Unwind_Trace_Fn = |
169 | | extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code; |
170 | | |
171 | | extern "C" { |
172 | | pub fn _Unwind_Backtrace( |
173 | | trace: _Unwind_Trace_Fn, |
174 | | trace_argument: *mut c_void, |
175 | | ) -> _Unwind_Reason_Code; |
176 | | } |
177 | | |
178 | | cfg_if::cfg_if! { |
179 | | // available since GCC 4.2.0, should be fine for our purpose |
180 | | if #[cfg(all( |
181 | | not(all(target_os = "android", target_arch = "arm")), |
182 | | not(all(target_os = "freebsd", target_arch = "arm")), |
183 | | not(all(target_os = "linux", target_arch = "arm")), |
184 | | not(all(target_os = "horizon", target_arch = "arm")), |
185 | | not(all(target_os = "vita", target_arch = "arm")), |
186 | | ))] { |
187 | | extern "C" { |
188 | | pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t; |
189 | | pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void; |
190 | | |
191 | | #[cfg(not(all(target_os = "linux", target_arch = "s390x")))] |
192 | | // This function is a misnomer: rather than getting this frame's |
193 | | // Canonical Frame Address (aka the caller frame's SP) it |
194 | | // returns this frame's SP. |
195 | | // |
196 | | // https://github.com/libunwind/libunwind/blob/d32956507cf29d9b1a98a8bce53c78623908f4fe/src/unwind/GetCFA.c#L28-L35 |
197 | | #[link_name = "_Unwind_GetCFA"] |
198 | | pub fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t; |
199 | | |
200 | | } |
201 | | |
202 | | // s390x uses a biased CFA value, therefore we need to use |
203 | | // _Unwind_GetGR to get the stack pointer register (%r15) |
204 | | // instead of relying on _Unwind_GetCFA. |
205 | | #[cfg(all(target_os = "linux", target_arch = "s390x"))] |
206 | | pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t { |
207 | | extern "C" { |
208 | | pub fn _Unwind_GetGR(ctx: *mut _Unwind_Context, index: libc::c_int) -> libc::uintptr_t; |
209 | | } |
210 | | _Unwind_GetGR(ctx, 15) |
211 | | } |
212 | | } else { |
213 | | use core::ptr::addr_of_mut; |
214 | | |
215 | | // On android and arm, the function `_Unwind_GetIP` and a bunch of |
216 | | // others are macros, so we define functions containing the |
217 | | // expansion of the macros. |
218 | | // |
219 | | // TODO: link to the header file that defines these macros, if you |
220 | | // can find it. (I, fitzgen, cannot find the header file that some |
221 | | // of these macro expansions were originally borrowed from.) |
222 | | #[repr(C)] |
223 | | enum _Unwind_VRS_Result { |
224 | | _UVRSR_OK = 0, |
225 | | _UVRSR_NOT_IMPLEMENTED = 1, |
226 | | _UVRSR_FAILED = 2, |
227 | | } |
228 | | #[repr(C)] |
229 | | enum _Unwind_VRS_RegClass { |
230 | | _UVRSC_CORE = 0, |
231 | | _UVRSC_VFP = 1, |
232 | | _UVRSC_FPA = 2, |
233 | | _UVRSC_WMMXD = 3, |
234 | | _UVRSC_WMMXC = 4, |
235 | | } |
236 | | #[repr(C)] |
237 | | enum _Unwind_VRS_DataRepresentation { |
238 | | _UVRSD_UINT32 = 0, |
239 | | _UVRSD_VFPX = 1, |
240 | | _UVRSD_FPAX = 2, |
241 | | _UVRSD_UINT64 = 3, |
242 | | _UVRSD_FLOAT = 4, |
243 | | _UVRSD_DOUBLE = 5, |
244 | | } |
245 | | |
246 | | type _Unwind_Word = libc::c_uint; |
247 | | extern "C" { |
248 | | fn _Unwind_VRS_Get( |
249 | | ctx: *mut _Unwind_Context, |
250 | | klass: _Unwind_VRS_RegClass, |
251 | | word: _Unwind_Word, |
252 | | repr: _Unwind_VRS_DataRepresentation, |
253 | | data: *mut c_void, |
254 | | ) -> _Unwind_VRS_Result; |
255 | | } |
256 | | |
257 | | pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t { |
258 | | let mut val: _Unwind_Word = 0; |
259 | | let ptr = addr_of_mut!(val); |
260 | | let _ = _Unwind_VRS_Get( |
261 | | ctx, |
262 | | _Unwind_VRS_RegClass::_UVRSC_CORE, |
263 | | 15, |
264 | | _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, |
265 | | ptr.cast::<c_void>(), |
266 | | ); |
267 | | (val & !1) as libc::uintptr_t |
268 | | } |
269 | | |
270 | | // R13 is the stack pointer on arm. |
271 | | const SP: _Unwind_Word = 13; |
272 | | |
273 | | pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t { |
274 | | let mut val: _Unwind_Word = 0; |
275 | | let ptr = addr_of_mut!(val); |
276 | | let _ = _Unwind_VRS_Get( |
277 | | ctx, |
278 | | _Unwind_VRS_RegClass::_UVRSC_CORE, |
279 | | SP, |
280 | | _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, |
281 | | ptr.cast::<c_void>(), |
282 | | ); |
283 | | val as libc::uintptr_t |
284 | | } |
285 | | |
286 | | // This function also doesn't exist on Android or ARM/Linux, so make it |
287 | | // a no-op. |
288 | | pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void { |
289 | | pc |
290 | | } |
291 | | } |
292 | | } |
293 | | } |