wasmtime_jit/unwind/
systemv.rs

1//! Module for System V ABI unwind registry.
2
3use anyhow::Result;
4
5/// Represents a registration of function unwind information for System V ABI.
6pub struct UnwindRegistration {
7    registrations: Vec<usize>,
8}
9
10extern "C" {
11    // libunwind import
12    fn __register_frame(fde: *const u8);
13    fn __deregister_frame(fde: *const u8);
14}
15
16impl UnwindRegistration {
17    pub const SECTION_NAME: &str = ".eh_frame";
18
19    /// Registers precompiled unwinding information with the system.
20    ///
21    /// The `_base_address` field is ignored here (only used on other
22    /// platforms), but the `unwind_info` and `unwind_len` parameters should
23    /// describe an in-memory representation of a `.eh_frame` section. This is
24    /// typically arranged for by the `wasmtime-obj` crate.
25    pub unsafe fn new(
26        _base_address: *const u8,
27        unwind_info: *const u8,
28        unwind_len: usize,
29    ) -> Result<UnwindRegistration> {
30        debug_assert_eq!(
31            unwind_info as usize % wasmtime_runtime::page_size(),
32            0,
33            "The unwind info must always be aligned to a page"
34        );
35
36        let mut registrations = Vec::new();
37        if cfg!(any(
38            all(target_os = "linux", target_env = "gnu"),
39            target_os = "freebsd"
40        )) {
41            // On gnu (libgcc), `__register_frame` will walk the FDEs until an
42            // entry of length 0
43            __register_frame(unwind_info);
44            registrations.push(unwind_info as usize);
45        } else {
46            // For libunwind, `__register_frame` takes a pointer to a single
47            // FDE. Note that we subtract 4 from the length of unwind info since
48            // wasmtime-encode .eh_frame sections always have a trailing 32-bit
49            // zero for the platforms above.
50            let start = unwind_info;
51            let end = start.add(unwind_len - 4);
52            let mut current = start;
53
54            // Walk all of the entries in the frame table and register them
55            while current < end {
56                let len = std::ptr::read::<u32>(current as *const u32) as usize;
57
58                // Skip over the CIE
59                if current != start {
60                    __register_frame(current);
61                    registrations.push(current as usize);
62                }
63
64                // Move to the next table entry (+4 because the length itself is
65                // not inclusive)
66                current = current.add(len + 4);
67            }
68        }
69
70        Ok(UnwindRegistration { registrations })
71    }
72}
73
74impl Drop for UnwindRegistration {
75    fn drop(&mut self) {
76        unsafe {
77            // libgcc stores the frame entries as a linked list in decreasing
78            // sort order based on the PC value of the registered entry.
79            //
80            // As we store the registrations in increasing order, it would be
81            // O(N^2) to deregister in that order.
82            //
83            // To ensure that we just pop off the first element in the list upon
84            // every deregistration, walk our list of registrations backwards.
85            for fde in self.registrations.iter().rev() {
86                __deregister_frame(*fde as *const _);
87            }
88        }
89    }
90}