Make module information lookup from runtime safe.
This commit uses a two-phase lookup of stack map information from modules rather than giving back raw pointers to stack maps. First the runtime looks up information about a module from a pc value, which returns an `Arc` it keeps a reference on while completing the stack map lookup. Second it then queries the module information for the stack map from a pc value, getting a reference to the stack map (which is now safe because of the `Arc` held by the runtime).
This commit is contained in:
@@ -207,7 +207,7 @@ unsafe impl WasmTy for Option<ExternRef> {
|
||||
unsafe {
|
||||
store
|
||||
.externref_activations_table()
|
||||
.insert_with_gc(x.inner, store.stack_map_lookup());
|
||||
.insert_with_gc(x.inner, store.module_info_lookup());
|
||||
}
|
||||
abi
|
||||
} else {
|
||||
|
||||
@@ -524,7 +524,7 @@ impl<'a> Instantiator<'a> {
|
||||
externref_activations_table: self.store.externref_activations_table()
|
||||
as *const VMExternRefActivationsTable
|
||||
as *mut _,
|
||||
stack_map_lookup: Some(self.store.stack_map_lookup()),
|
||||
module_info_lookup: Some(self.store.module_info_lookup()),
|
||||
})?;
|
||||
|
||||
// After we've created the `InstanceHandle` we still need to run
|
||||
|
||||
@@ -6,10 +6,13 @@ use std::{
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use wasmtime_environ::{
|
||||
entity::EntityRef, ir, wasm::DefinedFuncIndex, FunctionAddressMap, TrapInformation,
|
||||
entity::EntityRef,
|
||||
ir::{self, StackMap},
|
||||
wasm::DefinedFuncIndex,
|
||||
FunctionAddressMap, TrapInformation,
|
||||
};
|
||||
use wasmtime_jit::CompiledModule;
|
||||
use wasmtime_runtime::{VMCallerCheckedAnyfunc, VMTrampoline};
|
||||
use wasmtime_runtime::{ModuleInfo, VMCallerCheckedAnyfunc, VMTrampoline};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref GLOBAL_MODULES: Mutex<GlobalModuleRegistry> = Default::default();
|
||||
@@ -27,7 +30,7 @@ fn func_by_pc(module: &CompiledModule, pc: usize) -> Option<(DefinedFuncIndex, u
|
||||
///
|
||||
/// The `BTreeMap` is used to quickly locate a module based on a program counter value.
|
||||
#[derive(Default)]
|
||||
pub struct ModuleRegistry(BTreeMap<usize, RegisteredModule>);
|
||||
pub struct ModuleRegistry(BTreeMap<usize, Arc<RegisteredModule>>);
|
||||
|
||||
impl ModuleRegistry {
|
||||
/// Fetches frame information about a program counter in a backtrace.
|
||||
@@ -48,12 +51,13 @@ impl ModuleRegistry {
|
||||
self.module(pc)?.lookup_trap_info(pc)
|
||||
}
|
||||
|
||||
/// Looks up a stack map from a program counter.
|
||||
pub fn lookup_stack_map<'a>(&'a self, pc: usize) -> Option<&'a ir::StackMap> {
|
||||
self.module(pc)?.lookup_stack_map(pc)
|
||||
/// Fetches information about a registered module given a program counter value.
|
||||
pub fn lookup_module(&self, pc: usize) -> Option<Arc<dyn ModuleInfo>> {
|
||||
self.module(pc)
|
||||
.map(|m| -> Arc<dyn ModuleInfo> { m.clone() })
|
||||
}
|
||||
|
||||
fn module(&self, pc: usize) -> Option<&RegisteredModule> {
|
||||
fn module(&self, pc: usize) -> Option<&Arc<RegisteredModule>> {
|
||||
let (end, info) = self.0.range(pc..).next()?;
|
||||
if pc < info.start || *end < pc {
|
||||
return None;
|
||||
@@ -94,11 +98,11 @@ impl ModuleRegistry {
|
||||
|
||||
let prev = self.0.insert(
|
||||
end,
|
||||
RegisteredModule {
|
||||
Arc::new(RegisteredModule {
|
||||
start,
|
||||
module: compiled_module.clone(),
|
||||
signatures: module.signatures().clone(),
|
||||
},
|
||||
}),
|
||||
);
|
||||
assert!(prev.is_none());
|
||||
|
||||
@@ -209,8 +213,29 @@ impl RegisteredModule {
|
||||
Some(&info.traps[idx])
|
||||
}
|
||||
|
||||
/// Looks up a stack map from a program counter
|
||||
pub fn lookup_stack_map(&self, pc: usize) -> Option<&ir::StackMap> {
|
||||
fn instr_pos(offset: u32, addr_map: &FunctionAddressMap) -> Option<usize> {
|
||||
// Use our relative position from the start of the function to find the
|
||||
// machine instruction that corresponds to `pc`, which then allows us to
|
||||
// map that to a wasm original source location.
|
||||
match addr_map
|
||||
.instructions
|
||||
.binary_search_by_key(&offset, |map| map.code_offset)
|
||||
{
|
||||
// Exact hit!
|
||||
Ok(pos) => Some(pos),
|
||||
|
||||
// This *would* be at the first slot in the array, so no
|
||||
// instructions cover `pc`.
|
||||
Err(0) => None,
|
||||
|
||||
// This would be at the `nth` slot, so we're at the `n-1`th slot.
|
||||
Err(n) => Some(n - 1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleInfo for RegisteredModule {
|
||||
fn lookup_stack_map(&self, pc: usize) -> Option<&StackMap> {
|
||||
let (index, offset) = func_by_pc(&self.module, pc)?;
|
||||
let info = self.module.func_info(index);
|
||||
|
||||
@@ -275,26 +300,6 @@ impl RegisteredModule {
|
||||
|
||||
Some(&info.stack_maps[index].stack_map)
|
||||
}
|
||||
|
||||
fn instr_pos(offset: u32, addr_map: &FunctionAddressMap) -> Option<usize> {
|
||||
// Use our relative position from the start of the function to find the
|
||||
// machine instruction that corresponds to `pc`, which then allows us to
|
||||
// map that to a wasm original source location.
|
||||
match addr_map
|
||||
.instructions
|
||||
.binary_search_by_key(&offset, |map| map.code_offset)
|
||||
{
|
||||
// Exact hit!
|
||||
Ok(pos) => Some(pos),
|
||||
|
||||
// This *would* be at the first slot in the array, so no
|
||||
// instructions cover `pc`.
|
||||
Err(0) => None,
|
||||
|
||||
// This would be at the `nth` slot, so we're at the `n-1`th slot.
|
||||
Err(n) => Some(n - 1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Counterpart to `RegisteredModule`, but stored in the global registry.
|
||||
|
||||
@@ -16,9 +16,9 @@ use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use wasmtime_runtime::{
|
||||
InstanceAllocator, InstanceHandle, OnDemandInstanceAllocator, SignalHandler, TrapInfo,
|
||||
VMCallerCheckedAnyfunc, VMContext, VMExternRef, VMExternRefActivationsTable, VMInterrupts,
|
||||
VMTrampoline,
|
||||
InstanceAllocator, InstanceHandle, ModuleInfo, OnDemandInstanceAllocator, SignalHandler,
|
||||
TrapInfo, VMCallerCheckedAnyfunc, VMContext, VMExternRef, VMExternRefActivationsTable,
|
||||
VMInterrupts, VMTrampoline,
|
||||
};
|
||||
|
||||
/// Used to associate instances with the store.
|
||||
@@ -440,7 +440,7 @@ impl Store {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn stack_map_lookup(&self) -> &dyn wasmtime_runtime::StackMapLookup {
|
||||
pub(crate) fn module_info_lookup(&self) -> &dyn wasmtime_runtime::ModuleInfoLookup {
|
||||
self.inner.as_ref()
|
||||
}
|
||||
|
||||
@@ -910,10 +910,9 @@ impl Drop for StoreInner {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl wasmtime_runtime::StackMapLookup for StoreInner {
|
||||
fn lookup(&self, pc: usize) -> Option<*const wasmtime_environ::ir::StackMap> {
|
||||
// The address of the stack map is stable for the lifetime of the store
|
||||
self.modules.borrow().lookup_stack_map(pc).map(|m| m as _)
|
||||
impl wasmtime_runtime::ModuleInfoLookup for StoreInner {
|
||||
fn lookup(&self, pc: usize) -> Option<Arc<dyn ModuleInfo>> {
|
||||
self.modules.borrow().lookup_module(pc)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ fn create_handle(
|
||||
externref_activations_table: store.externref_activations_table()
|
||||
as *const VMExternRefActivationsTable
|
||||
as *mut _,
|
||||
stack_map_lookup: Some(store.stack_map_lookup()),
|
||||
module_info_lookup: Some(store.module_info_lookup()),
|
||||
},
|
||||
)?;
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ pub(crate) fn create_handle(
|
||||
externref_activations_table: store.externref_activations_table()
|
||||
as *const VMExternRefActivationsTable
|
||||
as *mut _,
|
||||
stack_map_lookup: &store,
|
||||
module_info_lookup: &store,
|
||||
})?;
|
||||
|
||||
Ok(store.add_instance(handle, true))
|
||||
|
||||
@@ -287,7 +287,7 @@ pub fn create_function(
|
||||
host_state: Box::new(trampoline_state),
|
||||
interrupts: std::ptr::null(),
|
||||
externref_activations_table: std::ptr::null_mut(),
|
||||
stack_map_lookup: None,
|
||||
module_info_lookup: None,
|
||||
})?,
|
||||
trampoline,
|
||||
))
|
||||
@@ -319,7 +319,7 @@ pub unsafe fn create_raw_function(
|
||||
host_state,
|
||||
interrupts: std::ptr::null(),
|
||||
externref_activations_table: std::ptr::null_mut(),
|
||||
stack_map_lookup: None,
|
||||
module_info_lookup: None,
|
||||
})?,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ impl Val {
|
||||
let externref_ptr = x.inner.as_raw();
|
||||
store
|
||||
.externref_activations_table()
|
||||
.insert_with_gc(x.inner, store.stack_map_lookup());
|
||||
.insert_with_gc(x.inner, store.module_info_lookup());
|
||||
ptr::write(p as *mut *mut u8, externref_ptr)
|
||||
}
|
||||
Val::FuncRef(f) => ptr::write(
|
||||
|
||||
Reference in New Issue
Block a user