wasmtime_internal_unwinder/
throw.rs

1//! Throw action computation (handler search).
2//!
3//! In order to throw exceptions from within Cranelift-compiled code,
4//! we provide a runtime function helper meant to be called by host
5//! code that is invoked by guest code.
6//!
7//! The helper below must be provided a delimited range on the stack
8//! corresponding to Cranelift frames above the current host code. It
9//! will look for any handlers in this code, given a closure that
10//! knows how to use an absolute PC to look up a module's exception
11//! table and its start-of-code-segment. If a handler is found, the
12//! helper below will return the SP, FP and PC that must be
13//! restored. Architecture-specific helpers are provided to jump to
14//! this new context with payload values. Otherwise, if no handler is
15//! found, the return type indicates this, and it is the caller's
16//! responsibility to invoke alternative behavior (e.g., abort the
17//! program or unwind all the way to initial Cranelift-code entry).
18
19use crate::{Frame, Unwind};
20use core::ops::ControlFlow;
21
22/// Description of a frame on the stack that is ready to catch an exception.
23#[derive(Debug)]
24pub struct Handler {
25    /// Program counter of handler return point.
26    pub pc: usize,
27    /// Stack pointer to restore before jumping to handler.
28    pub sp: usize,
29    /// Frame pointer to restore before jumping to handler.
30    pub fp: usize,
31}
32
33impl Handler {
34    /// Implementation of stack-walking to find a handler.
35    ///
36    /// This function searches for a handler in the given range of stack
37    /// frames, starting from the throw stub and up to a specified entry
38    /// frame.
39    ///
40    /// # Safety
41    ///
42    /// The safety of this function is the same as [`crate::visit_frames`] where the
43    /// values passed in configuring the frame pointer walk must be correct and
44    /// Wasm-defined for this to not have UB.
45    pub unsafe fn find<F: Fn(&Frame) -> Option<(usize, usize)>>(
46        unwind: &dyn Unwind,
47        frame_handler: F,
48        exit_pc: usize,
49        exit_trampoline_frame: usize,
50        entry_frame: usize,
51    ) -> Option<Handler> {
52        // SAFETY: the safety of `visit_frames` relies on the correctness of the
53        // parameters passed in which is forwarded as a contract to this function
54        // tiself.
55        let result = unsafe {
56            crate::stackwalk::visit_frames(
57                unwind,
58                exit_pc,
59                exit_trampoline_frame,
60                entry_frame,
61                |frame| {
62                    log::trace!("visit_frame: frame {frame:?}");
63                    if let Some((handler_pc, handler_sp)) = frame_handler(&frame) {
64                        return ControlFlow::Break(Handler {
65                            pc: handler_pc,
66                            sp: handler_sp,
67                            fp: frame.fp(),
68                        });
69                    }
70                    ControlFlow::Continue(())
71                },
72            )
73        };
74        match result {
75            ControlFlow::Break(handler) => Some(handler),
76            ControlFlow::Continue(()) => None,
77        }
78    }
79}