unwinding/unwinder/
mod.rs

1mod arch;
2mod find_fde;
3mod frame;
4
5use core::ffi::c_void;
6use core::ptr;
7use gimli::Register;
8
9use crate::abi::*;
10use crate::arch::*;
11use crate::util::*;
12use arch::*;
13use find_fde::FDEFinder;
14use frame::Frame;
15
16#[cfg(feature = "fde-custom")]
17pub use find_fde::custom_eh_frame_finder;
18
19// Helper function to turn `save_context` which takes function pointer to a closure-taking function.
20fn with_context<T, F: FnOnce(&mut Context) -> T>(f: F) -> T {
21    use core::mem::ManuallyDrop;
22
23    union Data<T, F> {
24        f: ManuallyDrop<F>,
25        t: ManuallyDrop<T>,
26    }
27
28    extern "C" fn delegate<T, F: FnOnce(&mut Context) -> T>(ctx: &mut Context, ptr: *mut ()) {
29        // SAFETY: This function is called exactly once; it extracts the function, call it and
30        // store the return value. This function is `extern "C"` so we don't need to worry about
31        // unwinding past it.
32        unsafe {
33            let data = &mut *ptr.cast::<Data<T, F>>();
34            let t = ManuallyDrop::take(&mut data.f)(ctx);
35            data.t = ManuallyDrop::new(t);
36        }
37    }
38
39    let mut data = Data {
40        f: ManuallyDrop::new(f),
41    };
42    save_context(delegate::<T, F>, ptr::addr_of_mut!(data).cast());
43    unsafe { ManuallyDrop::into_inner(data.t) }
44}
45
46#[repr(C)]
47pub struct UnwindException {
48    pub exception_class: u64,
49    pub exception_cleanup: Option<UnwindExceptionCleanupFn>,
50    private_1: Option<UnwindStopFn>,
51    private_2: usize,
52    private_unused: [usize; Arch::UNWIND_PRIVATE_DATA_SIZE - 2],
53}
54
55pub struct UnwindContext<'a> {
56    frame: Option<&'a Frame>,
57    ctx: &'a mut Context,
58    signal: bool,
59}
60
61#[unsafe(no_mangle)]
62pub extern "C" fn _Unwind_GetGR(unwind_ctx: &UnwindContext<'_>, index: c_int) -> usize {
63    unwind_ctx.ctx[Register(index as u16)]
64}
65
66#[unsafe(no_mangle)]
67pub extern "C" fn _Unwind_GetCFA(unwind_ctx: &UnwindContext<'_>) -> usize {
68    unwind_ctx.ctx[Arch::SP]
69}
70
71#[unsafe(no_mangle)]
72pub extern "C" fn _Unwind_SetGR(unwind_ctx: &mut UnwindContext<'_>, index: c_int, value: usize) {
73    unwind_ctx.ctx[Register(index as u16)] = value;
74}
75
76#[unsafe(no_mangle)]
77pub extern "C" fn _Unwind_GetIP(unwind_ctx: &UnwindContext<'_>) -> usize {
78    unwind_ctx.ctx[Arch::RA]
79}
80
81#[unsafe(no_mangle)]
82pub extern "C" fn _Unwind_GetIPInfo(
83    unwind_ctx: &UnwindContext<'_>,
84    ip_before_insn: &mut c_int,
85) -> usize {
86    *ip_before_insn = unwind_ctx.signal as _;
87    unwind_ctx.ctx[Arch::RA]
88}
89
90#[unsafe(no_mangle)]
91pub extern "C" fn _Unwind_SetIP(unwind_ctx: &mut UnwindContext<'_>, value: usize) {
92    unwind_ctx.ctx[Arch::RA] = value;
93}
94
95#[unsafe(no_mangle)]
96pub extern "C" fn _Unwind_GetLanguageSpecificData(unwind_ctx: &UnwindContext<'_>) -> *mut c_void {
97    unwind_ctx
98        .frame
99        .map(|f| f.lsda() as *mut c_void)
100        .unwrap_or(ptr::null_mut())
101}
102
103#[unsafe(no_mangle)]
104pub extern "C" fn _Unwind_GetRegionStart(unwind_ctx: &UnwindContext<'_>) -> usize {
105    unwind_ctx.frame.map(|f| f.initial_address()).unwrap_or(0)
106}
107
108#[unsafe(no_mangle)]
109pub extern "C" fn _Unwind_GetTextRelBase(unwind_ctx: &UnwindContext<'_>) -> usize {
110    unwind_ctx
111        .frame
112        .map(|f| f.bases().eh_frame.text.unwrap() as _)
113        .unwrap_or(0)
114}
115
116#[unsafe(no_mangle)]
117pub extern "C" fn _Unwind_GetDataRelBase(unwind_ctx: &UnwindContext<'_>) -> usize {
118    unwind_ctx
119        .frame
120        .map(|f| f.bases().eh_frame.data.unwrap() as _)
121        .unwrap_or(0)
122}
123
124#[unsafe(no_mangle)]
125pub extern "C" fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void {
126    find_fde::get_finder()
127        .find_fde(pc as usize - 1)
128        .map(|r| r.fde.initial_address() as usize as _)
129        .unwrap_or(ptr::null_mut())
130}
131
132macro_rules! try1 {
133    ($e: expr) => {{
134        match $e {
135            Ok(v) => v,
136            Err(_) => return UnwindReasonCode::FATAL_PHASE1_ERROR,
137        }
138    }};
139}
140
141macro_rules! try2 {
142    ($e: expr) => {{
143        match $e {
144            Ok(v) => v,
145            Err(_) => return UnwindReasonCode::FATAL_PHASE2_ERROR,
146        }
147    }};
148}
149
150#[inline(never)]
151#[unsafe(no_mangle)]
152pub unsafe extern "C-unwind" fn _Unwind_RaiseException(
153    exception: *mut UnwindException,
154) -> UnwindReasonCode {
155    with_context(|saved_ctx| {
156        // Phase 1: Search for handler
157        let mut ctx = saved_ctx.clone();
158        let mut signal = false;
159        loop {
160            if let Some(frame) = try1!(Frame::from_context(&ctx, signal)) {
161                if let Some(personality) = frame.personality() {
162                    let result = unsafe {
163                        personality(
164                            1,
165                            UnwindAction::SEARCH_PHASE,
166                            (*exception).exception_class,
167                            exception,
168                            &mut UnwindContext {
169                                frame: Some(&frame),
170                                ctx: &mut ctx,
171                                signal,
172                            },
173                        )
174                    };
175
176                    match result {
177                        UnwindReasonCode::CONTINUE_UNWIND => (),
178                        UnwindReasonCode::HANDLER_FOUND => {
179                            break;
180                        }
181                        _ => return UnwindReasonCode::FATAL_PHASE1_ERROR,
182                    }
183                }
184
185                ctx = try1!(frame.unwind(&ctx));
186                signal = frame.is_signal_trampoline();
187            } else {
188                return UnwindReasonCode::END_OF_STACK;
189            }
190        }
191
192        // Disambiguate normal frame and signal frame.
193        let handler_cfa = ctx[Arch::SP] - signal as usize;
194        unsafe {
195            (*exception).private_1 = None;
196            (*exception).private_2 = handler_cfa;
197        }
198
199        let code = raise_exception_phase2(exception, saved_ctx, handler_cfa);
200        match code {
201            UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(saved_ctx) },
202            _ => code,
203        }
204    })
205}
206
207fn raise_exception_phase2(
208    exception: *mut UnwindException,
209    ctx: &mut Context,
210    handler_cfa: usize,
211) -> UnwindReasonCode {
212    let mut signal = false;
213    loop {
214        if let Some(frame) = try2!(Frame::from_context(ctx, signal)) {
215            let frame_cfa = ctx[Arch::SP] - signal as usize;
216            if let Some(personality) = frame.personality() {
217                let code = unsafe {
218                    personality(
219                        1,
220                        UnwindAction::CLEANUP_PHASE
221                            | if frame_cfa == handler_cfa {
222                                UnwindAction::HANDLER_FRAME
223                            } else {
224                                UnwindAction::empty()
225                            },
226                        (*exception).exception_class,
227                        exception,
228                        &mut UnwindContext {
229                            frame: Some(&frame),
230                            ctx,
231                            signal,
232                        },
233                    )
234                };
235
236                match code {
237                    UnwindReasonCode::CONTINUE_UNWIND => (),
238                    UnwindReasonCode::INSTALL_CONTEXT => {
239                        frame.adjust_stack_for_args(ctx);
240                        return UnwindReasonCode::INSTALL_CONTEXT;
241                    }
242                    _ => return UnwindReasonCode::FATAL_PHASE2_ERROR,
243                }
244            }
245
246            *ctx = try2!(frame.unwind(ctx));
247            signal = frame.is_signal_trampoline();
248        } else {
249            return UnwindReasonCode::FATAL_PHASE2_ERROR;
250        }
251    }
252}
253
254#[inline(never)]
255#[unsafe(no_mangle)]
256pub unsafe extern "C-unwind" fn _Unwind_ForcedUnwind(
257    exception: *mut UnwindException,
258    stop: UnwindStopFn,
259    stop_arg: *mut c_void,
260) -> UnwindReasonCode {
261    with_context(|ctx| {
262        unsafe {
263            (*exception).private_1 = Some(stop);
264            (*exception).private_2 = stop_arg as _;
265        }
266
267        let code = force_unwind_phase2(exception, ctx, stop, stop_arg);
268        match code {
269            UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(ctx) },
270            _ => code,
271        }
272    })
273}
274
275fn force_unwind_phase2(
276    exception: *mut UnwindException,
277    ctx: &mut Context,
278    stop: UnwindStopFn,
279    stop_arg: *mut c_void,
280) -> UnwindReasonCode {
281    let mut signal = false;
282    loop {
283        let frame = try2!(Frame::from_context(ctx, signal));
284
285        let code = unsafe {
286            stop(
287                1,
288                UnwindAction::FORCE_UNWIND
289                    | UnwindAction::END_OF_STACK
290                    | if frame.is_none() {
291                        UnwindAction::END_OF_STACK
292                    } else {
293                        UnwindAction::empty()
294                    },
295                (*exception).exception_class,
296                exception,
297                &mut UnwindContext {
298                    frame: frame.as_ref(),
299                    ctx,
300                    signal,
301                },
302                stop_arg,
303            )
304        };
305        match code {
306            UnwindReasonCode::NO_REASON => (),
307            _ => return UnwindReasonCode::FATAL_PHASE2_ERROR,
308        }
309
310        if let Some(frame) = frame {
311            if let Some(personality) = frame.personality() {
312                let code = unsafe {
313                    personality(
314                        1,
315                        UnwindAction::FORCE_UNWIND | UnwindAction::CLEANUP_PHASE,
316                        (*exception).exception_class,
317                        exception,
318                        &mut UnwindContext {
319                            frame: Some(&frame),
320                            ctx,
321                            signal,
322                        },
323                    )
324                };
325
326                match code {
327                    UnwindReasonCode::CONTINUE_UNWIND => (),
328                    UnwindReasonCode::INSTALL_CONTEXT => {
329                        frame.adjust_stack_for_args(ctx);
330                        return UnwindReasonCode::INSTALL_CONTEXT;
331                    }
332                    _ => return UnwindReasonCode::FATAL_PHASE2_ERROR,
333                }
334            }
335
336            *ctx = try2!(frame.unwind(ctx));
337            signal = frame.is_signal_trampoline();
338        } else {
339            return UnwindReasonCode::END_OF_STACK;
340        }
341    }
342}
343
344#[inline(never)]
345#[unsafe(no_mangle)]
346pub unsafe extern "C-unwind" fn _Unwind_Resume(exception: *mut UnwindException) -> ! {
347    with_context(|ctx| {
348        let code = match unsafe { (*exception).private_1 } {
349            None => {
350                let handler_cfa = unsafe { (*exception).private_2 };
351                raise_exception_phase2(exception, ctx, handler_cfa)
352            }
353            Some(stop) => {
354                let stop_arg = unsafe { (*exception).private_2 as _ };
355                force_unwind_phase2(exception, ctx, stop, stop_arg)
356            }
357        };
358        assert!(code == UnwindReasonCode::INSTALL_CONTEXT);
359
360        unsafe { restore_context(ctx) }
361    })
362}
363
364#[inline(never)]
365#[unsafe(no_mangle)]
366pub unsafe extern "C-unwind" fn _Unwind_Resume_or_Rethrow(
367    exception: *mut UnwindException,
368) -> UnwindReasonCode {
369    let stop = match unsafe { (*exception).private_1 } {
370        None => return unsafe { _Unwind_RaiseException(exception) },
371        Some(v) => v,
372    };
373
374    with_context(|ctx| {
375        let stop_arg = unsafe { (*exception).private_2 as _ };
376        let code = force_unwind_phase2(exception, ctx, stop, stop_arg);
377        assert!(code == UnwindReasonCode::INSTALL_CONTEXT);
378
379        unsafe { restore_context(ctx) }
380    })
381}
382
383#[unsafe(no_mangle)]
384pub unsafe extern "C" fn _Unwind_DeleteException(exception: *mut UnwindException) {
385    if let Some(cleanup) = unsafe { (*exception).exception_cleanup } {
386        unsafe { cleanup(UnwindReasonCode::FOREIGN_EXCEPTION_CAUGHT, exception) };
387    }
388}
389
390#[inline(never)]
391#[unsafe(no_mangle)]
392pub extern "C-unwind" fn _Unwind_Backtrace(
393    trace: UnwindTraceFn,
394    trace_argument: *mut c_void,
395) -> UnwindReasonCode {
396    with_context(|ctx| {
397        let mut ctx = ctx.clone();
398        let mut signal = false;
399        let mut skipping = cfg!(feature = "hide-trace");
400
401        loop {
402            let frame = try1!(Frame::from_context(&ctx, signal));
403            if !skipping {
404                let code = trace(
405                    &UnwindContext {
406                        frame: frame.as_ref(),
407                        ctx: &mut ctx,
408                        signal,
409                    },
410                    trace_argument,
411                );
412                match code {
413                    UnwindReasonCode::NO_REASON => (),
414                    _ => return UnwindReasonCode::FATAL_PHASE1_ERROR,
415                }
416            }
417            if let Some(frame) = frame {
418                if skipping {
419                    if frame.initial_address() == _Unwind_Backtrace as usize {
420                        skipping = false;
421                    }
422                }
423                ctx = try1!(frame.unwind(&ctx));
424                signal = frame.is_signal_trampoline();
425            } else {
426                return UnwindReasonCode::END_OF_STACK;
427            }
428        }
429    })
430}