Coverage Report

Created: 2026-01-25 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/backtrace-0.3.76/src/backtrace/libunwind.rs
Line
Count
Source
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
0
    pub fn ip(&self) -> *mut c_void {
39
0
        let ctx = match *self {
40
0
            Frame::Raw(ctx) => ctx,
41
0
            Frame::Cloned { ip, .. } => return ip,
42
        };
43
        #[allow(unused_mut)]
44
0
        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::sgx_image_base::get_image_base();
52
            ip = usize::wrapping_sub(ip as usize, image_base as _) as _;
53
        }
54
0
        ip
55
0
    }
56
57
0
    pub fn sp(&self) -> *mut c_void {
58
0
        match *self {
59
0
            Frame::Raw(ctx) => unsafe { uw::get_sp(ctx) as *mut c_void },
60
0
            Frame::Cloned { sp, .. } => sp,
61
        }
62
0
    }
63
64
0
    pub fn symbol_address(&self) -> *mut c_void {
65
0
        if let Frame::Cloned { symbol_address, .. } = *self {
66
0
            return symbol_address;
67
0
        }
68
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
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!
80
0
        if cfg!(target_vendor = "apple") {
81
0
            self.ip()
82
        } else {
83
0
            unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) }
84
        }
85
0
    }
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
0
    fn clone(&self) -> Frame {
94
0
        Frame::Cloned {
95
0
            ip: self.ip(),
96
0
            sp: self.sp(),
97
0
            symbol_address: self.symbol_address(),
98
0
        }
99
0
    }
100
}
101
102
struct Bomb {
103
    enabled: bool,
104
}
105
106
impl Drop for Bomb {
107
0
    fn drop(&mut self) {
108
0
        if self.enabled {
109
0
            panic!("cannot panic during the backtrace function");
110
0
        }
111
0
    }
112
}
113
114
#[inline(always)]
115
0
pub unsafe fn trace(mut cb: &mut dyn FnMut(&super::Frame) -> bool) {
116
0
    unsafe {
117
0
        uw::_Unwind_Backtrace(trace_fn, addr_of_mut!(cb).cast());
118
0
    }
119
120
0
    extern "C" fn trace_fn(
121
0
        ctx: *mut uw::_Unwind_Context,
122
0
        arg: *mut c_void,
123
0
    ) -> uw::_Unwind_Reason_Code {
124
0
        let cb = unsafe { &mut *arg.cast::<&mut dyn FnMut(&super::Frame) -> bool>() };
125
0
        let cx = super::Frame {
126
0
            inner: Frame::Raw(ctx),
127
0
        };
128
129
0
        let mut bomb = Bomb { enabled: true };
130
0
        let keep_going = cb(&cx);
131
0
        bomb.enabled = false;
132
133
0
        if keep_going {
134
0
            uw::_URC_NO_REASON
135
        } else {
136
0
            uw::_URC_FAILURE
137
        }
138
0
    }
139
0
}
140
141
/// 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)]
149
mod uw {
150
    pub use self::_Unwind_Reason_Code::*;
151
152
    use core::ffi::c_void;
153
154
    #[repr(C)]
155
    pub 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
    }
167
168
    pub enum _Unwind_Context {}
169
170
    pub type _Unwind_Trace_Fn =
171
        extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code;
172
173
    unsafe extern "C" {
174
        pub fn _Unwind_Backtrace(
175
            trace: _Unwind_Trace_Fn,
176
            trace_argument: *mut c_void,
177
        ) -> _Unwind_Reason_Code;
178
    }
179
180
    cfg_if::cfg_if! {
181
        // available since GCC 4.2.0, should be fine for our purpose
182
        if #[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
        ))] {
191
            unsafe extern "C" {
192
                pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
193
                pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void;
194
195
                #[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"]
202
                pub fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
203
204
            }
205
206
            // 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"))]
210
            pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
211
                unsafe extern "C" {
212
                    pub fn _Unwind_GetGR(ctx: *mut _Unwind_Context, index: libc::c_int) -> libc::uintptr_t;
213
                }
214
                unsafe { _Unwind_GetGR(ctx, 15) }
215
            }
216
        } else {
217
            use core::ptr::addr_of_mut;
218
219
            // 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)]
227
            enum _Unwind_VRS_Result {
228
                _UVRSR_OK = 0,
229
                _UVRSR_NOT_IMPLEMENTED = 1,
230
                _UVRSR_FAILED = 2,
231
            }
232
            #[repr(C)]
233
            enum _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)]
241
            enum _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
            }
249
250
            type _Unwind_Word = libc::c_uint;
251
            unsafe extern "C" {
252
                fn _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
            }
260
261
            pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
262
                let mut val: _Unwind_Word = 0;
263
                let ptr = addr_of_mut!(val);
264
                unsafe {
265
                    let _ = _Unwind_VRS_Get(
266
                        ctx,
267
                        _Unwind_VRS_RegClass::_UVRSC_CORE,
268
                        15,
269
                        _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
270
                        ptr.cast::<c_void>(),
271
                    );
272
                }
273
                (val & !1) as libc::uintptr_t
274
            }
275
276
            // R13 is the stack pointer on arm.
277
            const SP: _Unwind_Word = 13;
278
279
            pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
280
                let mut val: _Unwind_Word = 0;
281
                let ptr = addr_of_mut!(val);
282
                unsafe {
283
                    let _ = _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
            }
293
294
            // This function also doesn't exist on Android or ARM/Linux, so make it
295
            // a no-op.
296
            pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void {
297
                pc
298
            }
299
        }
300
    }
301
}