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