use loupe::{MemoryUsage, MemoryUsageTracker};
use std::collections::HashMap;
use wasmer_compiler::CompiledFunctionUnwindInfo;
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: &CompiledFunctionUnwindInfo,
) -> Result<(), String> {
if self.published {
return Err("unwind registry has already been published".to_string());
}
match info {
CompiledFunctionUnwindInfo::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());
}
}
}
}
}
impl MemoryUsage for UnwindRegistry {
fn size_of_val(&self, tracker: &mut dyn MemoryUsageTracker) -> usize {
self.functions
.iter()
.map(|(_, _)| std::mem::size_of::<u64>() * 3)
.sum::<usize>()
+ self.published.size_of_val(tracker)
}
}