]> git.proxmox.com Git - rustc.git/blame - library/backtrace/src/backtrace/libunwind.rs
New upstream version 1.49.0~beta.4+dfsg1
[rustc.git] / library / backtrace / src / backtrace / libunwind.rs
CommitLineData
7cac9316
XL
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
dc9dc135
XL
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
3dfed10e 28use super::super::Bomb;
416331ca 29use core::ffi::c_void;
7cac9316 30
dc9dc135
XL
31pub enum Frame {
32 Raw(*mut uw::_Unwind_Context),
33 Cloned {
34 ip: *mut c_void,
f035d41b 35 sp: *mut c_void,
dc9dc135
XL
36 symbol_address: *mut c_void,
37 },
7cac9316
XL
38}
39
dc9dc135
XL
40// With a raw libunwind pointer it should only ever be access in a readonly
41// threadsafe fashion, so it's `Sync`. When sending to other threads via `Clone`
42// we always switch to a version which doesn't retain interior pointers, so we
43// should be `Send` as well.
44unsafe impl Send for Frame {}
45unsafe impl Sync for Frame {}
46
7cac9316
XL
47impl Frame {
48 pub fn ip(&self) -> *mut c_void {
dc9dc135
XL
49 let ctx = match *self {
50 Frame::Raw(ctx) => ctx,
51 Frame::Cloned { ip, .. } => return ip,
7cac9316 52 };
74b04a01 53 unsafe { uw::_Unwind_GetIP(ctx) as *mut c_void }
7cac9316
XL
54 }
55
f035d41b
XL
56 pub fn sp(&self) -> *mut c_void {
57 match *self {
58 Frame::Raw(ctx) => unsafe { uw::get_sp(ctx) as *mut c_void },
59 Frame::Cloned { sp, .. } => sp,
60 }
61 }
62
7cac9316 63 pub fn symbol_address(&self) -> *mut c_void {
dc9dc135
XL
64 if let Frame::Cloned { symbol_address, .. } = *self {
65 return symbol_address;
66 }
67
68 // It seems that on OSX `_Unwind_FindEnclosingFunction` returns a
69 // pointer to... something that's unclear. It's definitely not always
70 // the enclosing function for whatever reason. It's not entirely clear
71 // to me what's going on here, so pessimize this for now and just always
72 // return the ip.
7cac9316 73 //
dc9dc135
XL
74 // Note the `skip_inner_frames.rs` test is skipped on OSX due to this
75 // clause, and if this is fixed that test in theory can be run on OSX!
7cac9316
XL
76 if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
77 self.ip()
78 } else {
79 unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) }
80 }
81 }
29967ef6
XL
82
83 pub fn module_base_address(&self) -> Option<*mut c_void> {
84 None
85 }
7cac9316
XL
86}
87
dc9dc135
XL
88impl Clone for Frame {
89 fn clone(&self) -> Frame {
90 Frame::Cloned {
91 ip: self.ip(),
f035d41b 92 sp: self.sp(),
dc9dc135
XL
93 symbol_address: self.symbol_address(),
94 }
95 }
96}
97
7cac9316 98#[inline(always)]
f035d41b 99pub unsafe fn trace(mut cb: &mut dyn FnMut(&super::Frame) -> bool) {
0731742a 100 uw::_Unwind_Backtrace(trace_fn, &mut cb as *mut _ as *mut _);
7cac9316 101
74b04a01
XL
102 extern "C" fn trace_fn(
103 ctx: *mut uw::_Unwind_Context,
104 arg: *mut c_void,
105 ) -> uw::_Unwind_Reason_Code {
f035d41b 106 let cb = unsafe { &mut *(arg as *mut &mut dyn FnMut(&super::Frame) -> bool) };
7cac9316 107 let cx = super::Frame {
dc9dc135 108 inner: Frame::Raw(ctx),
7cac9316
XL
109 };
110
3dfed10e 111 let mut bomb = Bomb { enabled: true };
7cac9316
XL
112 let keep_going = cb(&cx);
113 bomb.enabled = false;
114
115 if keep_going {
116 uw::_URC_NO_REASON
117 } else {
118 uw::_URC_FAILURE
119 }
120 }
121}
122
123/// Unwind library interface used for backtraces
124///
125/// Note that dead code is allowed as here are just bindings
126/// iOS doesn't use all of them it but adding more
127/// platform-specific configs pollutes the code too much
128#[allow(non_camel_case_types)]
129#[allow(non_snake_case)]
130#[allow(dead_code)]
131mod uw {
132 pub use self::_Unwind_Reason_Code::*;
133
416331ca 134 use core::ffi::c_void;
7cac9316
XL
135
136 #[repr(C)]
137 pub enum _Unwind_Reason_Code {
138 _URC_NO_REASON = 0,
139 _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
140 _URC_FATAL_PHASE2_ERROR = 2,
141 _URC_FATAL_PHASE1_ERROR = 3,
142 _URC_NORMAL_STOP = 4,
143 _URC_END_OF_STACK = 5,
144 _URC_HANDLER_FOUND = 6,
145 _URC_INSTALL_CONTEXT = 7,
146 _URC_CONTINUE_UNWIND = 8,
147 _URC_FAILURE = 9, // used only by ARM EABI
148 }
149
150 pub enum _Unwind_Context {}
151
152 pub type _Unwind_Trace_Fn =
74b04a01 153 extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code;
7cac9316 154
74b04a01 155 extern "C" {
7cac9316
XL
156 // No native _Unwind_Backtrace on iOS
157 #[cfg(not(all(target_os = "ios", target_arch = "arm")))]
74b04a01
XL
158 pub fn _Unwind_Backtrace(
159 trace: _Unwind_Trace_Fn,
160 trace_argument: *mut c_void,
161 ) -> _Unwind_Reason_Code;
7cac9316
XL
162
163 // available since GCC 4.2.0, should be fine for our purpose
74b04a01
XL
164 #[cfg(all(
165 not(all(target_os = "android", target_arch = "arm")),
166 not(all(target_os = "freebsd", target_arch = "arm")),
167 not(all(target_os = "linux", target_arch = "arm"))
168 ))]
169 pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
170
171 #[cfg(all(
f035d41b 172 not(all(target_os = "android", target_arch = "arm")),
74b04a01
XL
173 not(all(target_os = "freebsd", target_arch = "arm")),
174 not(all(target_os = "linux", target_arch = "arm"))
175 ))]
176 pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void;
f035d41b
XL
177
178 #[cfg(all(
179 not(all(target_os = "android", target_arch = "arm")),
180 not(all(target_os = "freebsd", target_arch = "arm")),
181 not(all(target_os = "linux", target_arch = "arm"))
182 ))]
183 // This function is a misnomer: rather than getting this frame's
184 // Canonical Frame Address (aka the caller frame's SP) it
185 // returns this frame's SP.
186 //
187 // https://github.com/libunwind/libunwind/blob/d32956507cf29d9b1a98a8bce53c78623908f4fe/src/unwind/GetCFA.c#L28-L35
188 #[link_name = "_Unwind_GetCFA"]
189 pub fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
7cac9316
XL
190 }
191
f035d41b
XL
192 // On android and arm, the function `_Unwind_GetIP` and a bunch of others
193 // are macros, so we define functions containing the expansion of the
194 // macros.
195 //
196 // TODO: link to the header file that defines these macros, if you can find
197 // it. (I, fitzgen, cannot find the header file that some of these macro
198 // expansions were originally borrowed from.)
199 #[cfg(any(
200 all(target_os = "android", target_arch = "arm"),
201 all(target_os = "freebsd", target_arch = "arm"),
202 all(target_os = "linux", target_arch = "arm")
203 ))]
204 pub use self::arm::*;
74b04a01
XL
205 #[cfg(any(
206 all(target_os = "android", target_arch = "arm"),
207 all(target_os = "freebsd", target_arch = "arm"),
208 all(target_os = "linux", target_arch = "arm")
209 ))]
f035d41b
XL
210 mod arm {
211 pub use super::*;
7cac9316
XL
212 #[repr(C)]
213 enum _Unwind_VRS_Result {
214 _UVRSR_OK = 0,
215 _UVRSR_NOT_IMPLEMENTED = 1,
216 _UVRSR_FAILED = 2,
217 }
218 #[repr(C)]
219 enum _Unwind_VRS_RegClass {
220 _UVRSC_CORE = 0,
221 _UVRSC_VFP = 1,
222 _UVRSC_FPA = 2,
223 _UVRSC_WMMXD = 3,
224 _UVRSC_WMMXC = 4,
225 }
226 #[repr(C)]
227 enum _Unwind_VRS_DataRepresentation {
228 _UVRSD_UINT32 = 0,
229 _UVRSD_VFPX = 1,
230 _UVRSD_FPAX = 2,
231 _UVRSD_UINT64 = 3,
232 _UVRSD_FLOAT = 4,
233 _UVRSD_DOUBLE = 5,
234 }
235
236 type _Unwind_Word = libc::c_uint;
74b04a01
XL
237 extern "C" {
238 fn _Unwind_VRS_Get(
239 ctx: *mut _Unwind_Context,
240 klass: _Unwind_VRS_RegClass,
241 word: _Unwind_Word,
242 repr: _Unwind_VRS_DataRepresentation,
243 data: *mut c_void,
244 ) -> _Unwind_VRS_Result;
7cac9316
XL
245 }
246
f035d41b
XL
247 pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
248 let mut val: _Unwind_Word = 0;
249 let ptr = &mut val as *mut _Unwind_Word;
250 let _ = _Unwind_VRS_Get(
251 ctx,
252 _Unwind_VRS_RegClass::_UVRSC_CORE,
253 15,
254 _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
255 ptr as *mut c_void,
256 );
257 (val & !1) as libc::uintptr_t
258 }
7cac9316 259
f035d41b
XL
260 // R13 is the stack pointer on arm.
261 const SP: _Unwind_Word = 13;
262
263 pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
264 let mut val: _Unwind_Word = 0;
265 let ptr = &mut val as *mut _Unwind_Word;
266 let _ = _Unwind_VRS_Get(
267 ctx,
268 _Unwind_VRS_RegClass::_UVRSC_CORE,
269 SP,
270 _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
271 ptr as *mut c_void,
272 );
273 val as libc::uintptr_t
274 }
275
276 // This function also doesn't exist on Android or ARM/Linux, so make it
277 // a no-op.
278 pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void {
279 pc
280 }
7cac9316
XL
281 }
282}