use loupe::MemoryUsage;
use wasmer_compiler::CompiledFunctionUnwindInfo;
#[derive(MemoryUsage)]
pub struct UnwindRegistry {
registrations: Vec<usize>,
published: bool,
}
extern "C" {
fn __register_frame(fde: *const u8);
fn __deregister_frame(fde: *const u8);
}
impl UnwindRegistry {
pub fn new() -> Self {
Self {
registrations: Vec::new(),
published: false,
}
}
pub fn register(
&mut self,
_base_address: usize,
_func_start: u32,
_func_len: u32,
info: &CompiledFunctionUnwindInfo,
) -> Result<(), String> {
match info {
CompiledFunctionUnwindInfo::Dwarf => {}
_ => return Err("unsupported unwind information".to_string()),
};
Ok(())
}
pub fn publish(&mut self, eh_frame: Option<&[u8]>) -> Result<(), String> {
if self.published {
return Err("unwind registry has already been published".to_string());
}
if let Some(eh_frame) = eh_frame {
unsafe {
self.register_frames(eh_frame);
}
}
self.published = true;
Ok(())
}
#[allow(clippy::cast_ptr_alignment)]
unsafe fn register_frames(&mut self, eh_frame: &[u8]) {
if cfg!(all(target_os = "linux", target_env = "gnu")) {
debug_assert_ne!(
eh_frame,
&[0, 0, 0, 0],
"`eh_frame` seems to contain empty FDEs"
);
let ptr = eh_frame.as_ptr();
__register_frame(ptr);
self.registrations.push(ptr as usize);
} else {
let start = eh_frame.as_ptr();
let end = start.add(eh_frame.len());
let mut current = start;
while current < end {
let len = std::ptr::read::<u32>(current as *const u32) as usize;
if current != start && len != 0 {
__register_frame(current);
self.registrations.push(current as usize);
}
current = current.add(len + 4);
}
}
}
}
impl Drop for UnwindRegistry {
fn drop(&mut self) {
if self.published {
unsafe {
for fde in self.registrations.iter().rev() {
__deregister_frame(*fde as *const _);
}
}
}
}
}