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 
19 use crate::{Frame, Unwind};
20 use core::ops::ControlFlow;
21 
22 /// Description of a frame on the stack that is ready to catch an exception.
23 #[derive(Debug)]
24 pub 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 
33 impl 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.
find<F: Fn(&Frame) -> Option<(usize, usize)>>( unwind: &dyn Unwind, frame_handler: F, exit_pc: usize, exit_trampoline_frame: usize, entry_frame: usize, ) -> Option<Handler>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 }
80