use std::collections::HashMap;
use wasmer_types::CompiledFunctionUnwindInfoReference;
use winapi::um::winnt;
pub struct UnwindRegistry {
functions: HashMap<usize, Vec<winnt::RUNTIME_FUNCTION>>,
published: bool,
}
impl UnwindRegistry {
pub fn new() -> Self {
Self {
functions: HashMap::new(),
published: false,
}
}
pub fn register(
&mut self,
base_address: usize,
func_start: u32,
func_len: u32,
info: &CompiledFunctionUnwindInfoReference,
) -> Result<(), String> {
if self.published {
return Err("unwind registry has already been published".to_string());
}
match info {
CompiledFunctionUnwindInfoReference::WindowsX64(_) => {}
_ => return Err("unsupported unwind information".to_string()),
};
let mut entry = winnt::RUNTIME_FUNCTION::default();
entry.BeginAddress = func_start;
entry.EndAddress = func_start + func_len;
unsafe {
*entry.u.UnwindInfoAddress_mut() = (entry.EndAddress + 3) & !3;
}
let entries = self.functions.entry(base_address).or_insert_with(Vec::new);
entries.push(entry);
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());
}
self.published = true;
if !self.functions.is_empty() {
for (base_address, functions) in self.functions.iter_mut() {
assert_eq!(
(functions.as_mut_ptr() as u64) % 4,
0,
"function table allocation was not aligned"
);
unsafe {
if winnt::RtlAddFunctionTable(
functions.as_mut_ptr(),
functions.len() as u32,
*base_address as u64,
) == 0
{
return Err("failed to register function tables".to_string());
}
}
}
}
Ok(())
}
}
impl Drop for UnwindRegistry {
fn drop(&mut self) {
if self.published {
unsafe {
for functions in self.functions.values_mut() {
winnt::RtlDeleteFunctionTable(functions.as_mut_ptr());
}
}
}
}
}