Coverage Report

Created: 2024-10-16 07:58

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