//! Module for System V ABI unwind registry. use anyhow::Result; /// Represents a registration of function unwind information for System V ABI. pub struct UnwindRegistration { registrations: Vec, } extern "C" { // libunwind import fn __register_frame(fde: *const u8); fn __deregister_frame(fde: *const u8); } impl UnwindRegistration { pub const SECTION_NAME: &str = ".eh_frame"; /// Registers precompiled unwinding information with the system. /// /// The `_base_address` field is ignored here (only used on other /// platforms), but the `unwind_info` and `unwind_len` parameters should /// describe an in-memory representation of a `.eh_frame` section. This is /// typically arranged for by the `wasmtime-obj` crate. pub unsafe fn new( _base_address: *const u8, unwind_info: *const u8, unwind_len: usize, ) -> Result { debug_assert_eq!( unwind_info as usize % wasmtime_runtime::page_size(), 0, "The unwind info must always be aligned to a page" ); let mut registrations = Vec::new(); if cfg!(any( all(target_os = "linux", target_env = "gnu"), target_os = "freebsd" )) { // On gnu (libgcc), `__register_frame` will walk the FDEs until an // entry of length 0 __register_frame(unwind_info); registrations.push(unwind_info as usize); } else { // For libunwind, `__register_frame` takes a pointer to a single // FDE. Note that we subtract 4 from the length of unwind info since // wasmtime-encode .eh_frame sections always have a trailing 32-bit // zero for the platforms above. let start = unwind_info; let end = start.add(unwind_len - 4); let mut current = start; // Walk all of the entries in the frame table and register them while current < end { let len = std::ptr::read::(current as *const u32) as usize; // Skip over the CIE if current != start { __register_frame(current); registrations.push(current as usize); } // Move to the next table entry (+4 because the length itself is // not inclusive) current = current.add(len + 4); } } Ok(UnwindRegistration { registrations }) } } impl Drop for UnwindRegistration { fn drop(&mut self) { unsafe { // libgcc stores the frame entries as a linked list in decreasing // sort order based on the PC value of the registered entry. // // As we store the registrations in increasing order, it would be // O(N^2) to deregister in that order. // // To ensure that we just pop off the first element in the list upon // every deregistration, walk our list of registrations backwards. for fde in self.registrations.iter().rev() { __deregister_frame(*fde as *const _); } } } }