]> git.proxmox.com Git - rustc.git/blame - library/backtrace/src/backtrace/libunwind.rs
New upstream version 1.57.0+dfsg1
[rustc.git] / library / backtrace / src / backtrace / libunwind.rs
CommitLineData
dc9dc135
XL
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
3dfed10e 18use super::super::Bomb;
416331ca 19use core::ffi::c_void;
7cac9316 20
dc9dc135
XL
21pub enum Frame {
22 Raw(*mut uw::_Unwind_Context),
23 Cloned {
24 ip: *mut c_void,
f035d41b 25 sp: *mut c_void,
dc9dc135
XL
26 symbol_address: *mut c_void,
27 },
7cac9316
XL
28}
29
dc9dc135
XL
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.
34unsafe impl Send for Frame {}
35unsafe impl Sync for Frame {}
36
7cac9316
XL
37impl Frame {
38 pub fn ip(&self) -> *mut c_void {
dc9dc135
XL
39 let ctx = match *self {
40 Frame::Raw(ctx) => ctx,
41 Frame::Cloned { ip, .. } => return ip,
7cac9316 42 };
74b04a01 43 unsafe { uw::_Unwind_GetIP(ctx) as *mut c_void }
7cac9316
XL
44 }
45
f035d41b
XL
46 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
7cac9316 53 pub fn symbol_address(&self) -> *mut c_void {
dc9dc135
XL
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.
7cac9316 63 //
dc9dc135
XL
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!
7cac9316
XL
66 if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
67 self.ip()
68 } else {
69 unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) }
70 }
71 }
29967ef6
XL
72
73 pub fn module_base_address(&self) -> Option<*mut c_void> {
74 None
75 }
7cac9316
XL
76}
77
dc9dc135
XL
78impl Clone for Frame {
79 fn clone(&self) -> Frame {
80 Frame::Cloned {
81 ip: self.ip(),
f035d41b 82 sp: self.sp(),
dc9dc135
XL
83 symbol_address: self.symbol_address(),
84 }
85 }
86}
87
7cac9316 88#[inline(always)]
f035d41b 89pub unsafe fn trace(mut cb: &mut dyn FnMut(&super::Frame) -> bool) {
0731742a 90 uw::_Unwind_Backtrace(trace_fn, &mut cb as *mut _ as *mut _);
7cac9316 91
74b04a01
XL
92 extern "C" fn trace_fn(
93 ctx: *mut uw::_Unwind_Context,
94 arg: *mut c_void,
95 ) -> uw::_Unwind_Reason_Code {
f035d41b 96 let cb = unsafe { &mut *(arg as *mut &mut dyn FnMut(&super::Frame) -> bool) };
7cac9316 97 let cx = super::Frame {
dc9dc135 98 inner: Frame::Raw(ctx),
7cac9316
XL
99 };
100
3dfed10e 101 let mut bomb = Bomb { enabled: true };
7cac9316
XL
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)]
121mod uw {
122 pub use self::_Unwind_Reason_Code::*;
123
416331ca 124 use core::ffi::c_void;
7cac9316
XL
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 =
74b04a01 143 extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code;
7cac9316 144
74b04a01 145 extern "C" {
7cac9316
XL
146 // No native _Unwind_Backtrace on iOS
147 #[cfg(not(all(target_os = "ios", target_arch = "arm")))]
74b04a01
XL
148 pub fn _Unwind_Backtrace(
149 trace: _Unwind_Trace_Fn,
150 trace_argument: *mut c_void,
151 ) -> _Unwind_Reason_Code;
7cac9316
XL
152
153 // available since GCC 4.2.0, should be fine for our purpose
74b04a01
XL
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 ))]
159 pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
160
161 #[cfg(all(
f035d41b 162 not(all(target_os = "android", target_arch = "arm")),
74b04a01
XL
163 not(all(target_os = "freebsd", target_arch = "arm")),
164 not(all(target_os = "linux", target_arch = "arm"))
165 ))]
166 pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void;
f035d41b
XL
167
168 #[cfg(all(
169 not(all(target_os = "android", target_arch = "arm")),
170 not(all(target_os = "freebsd", target_arch = "arm")),
fc512014
XL
171 not(all(target_os = "linux", target_arch = "arm")),
172 not(all(target_os = "linux", target_arch = "s390x"))
f035d41b
XL
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"]
180 pub fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
7cac9316
XL
181 }
182
fc512014
XL
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"))]
187 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
f035d41b
XL
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::*;
74b04a01
XL
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 ))]
f035d41b
XL
212 mod arm {
213 pub use super::*;
7cac9316
XL
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;
74b04a01
XL
239 extern "C" {
240 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;
7cac9316
XL
247 }
248
f035d41b
XL
249 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 }
7cac9316 261
f035d41b
XL
262 // R13 is the stack pointer on arm.
263 const SP: _Unwind_Word = 13;
264
265 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.
280 pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void {
281 pc
282 }
7cac9316
XL
283 }
284}