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.
1718use core::ffi::c_void;
19use core::ptr::addr_of_mut;
2021pub 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}
2930// 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 {}
3637impl Frame {
38pub fn ip(&self) -> *mut c_void {
39let ctx = match *self {
40 Frame::Raw(ctx) => ctx,
41 Frame::Cloned { ip, .. } => return ip,
42 };
43#[allow(unused_mut)]
44let mut ip = unsafe { uw::_Unwind_GetIP(ctx) as *mut c_void };
4546// 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{
51let image_base = super::sgx_image_base::get_image_base();
52 ip = usize::wrapping_sub(ip as usize, image_base as _) as _;
53 }
54 ip
55 }
5657pub fn sp(&self) -> *mut c_void {
58match *self {
59 Frame::Raw(ctx) => unsafe { uw::get_sp(ctx) as *mut c_void },
60 Frame::Cloned { sp, .. } => sp,
61 }
62 }
6364pub fn symbol_address(&self) -> *mut c_void {
65if let Frame::Cloned { symbol_address, .. } = *self {
66return symbol_address;
67 }
6869// 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!
80if cfg!(target_vendor = "apple") {
81self.ip()
82 } else {
83unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) }
84 }
85 }
8687pub fn module_base_address(&self) -> Option<*mut c_void> {
88None
89}
90}
9192impl Clone for Frame {
93fn clone(&self) -> Frame {
94 Frame::Cloned {
95 ip: self.ip(),
96 sp: self.sp(),
97 symbol_address: self.symbol_address(),
98 }
99 }
100}
101102struct Bomb {
103 enabled: bool,
104}
105106impl Drop for Bomb {
107fn drop(&mut self) {
108if self.enabled {
109panic!("cannot panic during the backtrace function");
110 }
111 }
112}
113114#[inline(always)]
115pub unsafe fn trace(mut cb: &mut dyn FnMut(&super::Frame) -> bool) {
116unsafe {
117 uw::_Unwind_Backtrace(trace_fn, addr_of_mut!(cb).cast());
118 }
119120extern "C" fn trace_fn(
121 ctx: *mut uw::_Unwind_Context,
122 arg: *mut c_void,
123 ) -> uw::_Unwind_Reason_Code {
124let cb = unsafe { &mut *arg.cast::<&mut dyn FnMut(&super::Frame) -> bool>() };
125let cx = super::Frame {
126 inner: Frame::Raw(ctx),
127 };
128129let mut bomb = Bomb { enabled: true };
130let keep_going = cb(&cx);
131 bomb.enabled = false;
132133if keep_going {
134 uw::_URC_NO_REASON
135 } else {
136 uw::_URC_FAILURE
137 }
138 }
139}
140141/// 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)]
149mod uw {
150pub use self::_Unwind_Reason_Code::*;
151152use core::ffi::c_void;
153154#[repr(C)]
155pub 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}
167168pub enum _Unwind_Context {}
169170pub type _Unwind_Trace_Fn =
171extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code;
172173unsafe extern "C" {
174pub fn _Unwind_Backtrace(
175 trace: _Unwind_Trace_Fn,
176 trace_argument: *mut c_void,
177 ) -> _Unwind_Reason_Code;
178 }
179180cfg_if::cfg_if! {
181// available since GCC 4.2.0, should be fine for our purpose
182if #[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 ))] {
191unsafe extern "C" {
192pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
193pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void;
194195#[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"]
202pub fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
203204 }
205206// 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"))]
210pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
211unsafe extern "C" {
212pub fn _Unwind_GetGR(ctx: *mut _Unwind_Context, index: libc::c_int) -> libc::uintptr_t;
213 }
214unsafe { _Unwind_GetGR(ctx, 15) }
215 }
216 } else {
217use core::ptr::addr_of_mut;
218219// 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)]
227enum _Unwind_VRS_Result {
228 _UVRSR_OK = 0,
229 _UVRSR_NOT_IMPLEMENTED = 1,
230 _UVRSR_FAILED = 2,
231 }
232#[repr(C)]
233enum _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)]
241enum _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 }
249250type _Unwind_Word = libc::c_uint;
251unsafe extern "C" {
252fn _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 }
260261pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
262let mut val: _Unwind_Word = 0;
263let ptr = addr_of_mut!(val);
264unsafe {
265let _ = _Unwind_VRS_Get(
266 ctx,
267 _Unwind_VRS_RegClass::_UVRSC_CORE,
26815,
269 _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
270 ptr.cast::<c_void>(),
271 );
272 }
273 (val & !1) as libc::uintptr_t
274 }
275276// R13 is the stack pointer on arm.
277const SP: _Unwind_Word = 13;
278279pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
280let mut val: _Unwind_Word = 0;
281let ptr = addr_of_mut!(val);
282unsafe {
283let _ = _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 }
293294// This function also doesn't exist on Android or ARM/Linux, so make it
295 // a no-op.
296pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void {
297 pc
298 }
299 }
300 }
301}