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