Support imported functions

This commit is contained in:
Lachlan Sneff
2019-01-08 21:57:28 -05:00
parent 55b7cae523
commit bba168e61e
14 changed files with 265 additions and 151 deletions

View File

@ -108,7 +108,6 @@ impl LocalBacking {
match table.elements {
TableElements::Anyfunc(ref mut elements) => {
for (i, &func_index) in init.elements.iter().enumerate() {
let sig_index = module.func_assoc[func_index];
let vm_sig_id = vm::SigId(sig_index.index() as u32);
@ -263,20 +262,29 @@ impl ImportBacking {
let sig_index = module.func_assoc[index];
let expected_sig = module.sig_registry.lookup_func_sig(sig_index);
let import = imports.get(mod_name, item_name);
if let Some(&Import::Func(func, ref signature)) = import {
if expected_sig == signature {
functions.push(vm::ImportedFunc {
func,
// vmctx: ptr::null_mut(),
});
} else {
match import {
Some(&Import::Func(func, ref signature)) => {
if expected_sig == signature {
functions.push(vm::ImportedFunc {
func,
// vmctx: ptr::null_mut(),
});
} else {
return Err(format!(
"unexpected signature for {:?}:{:?}",
mod_name, item_name
));
}
}
Some(_) => {
return Err(format!(
"unexpected signature for {:?}:{:?}",
"incorrect import type for {}:{}",
mod_name, item_name
));
}
} else {
return Err(format!("incorrect type for {:?}:{:?}", mod_name, item_name));
None => {
return Err(format!("import not found: {}:{}", mod_name, item_name));
}
}
}
@ -293,26 +301,35 @@ impl ImportBacking {
) in &module.imported_globals
{
let import = imports.get(mod_name, item_name);
if let Some(&Import::Global(val)) = import {
if val.ty() == global_desc.ty {
globals.push(vm::ImportedGlobal {
global: vm::LocalGlobal {
data: match val {
Value::I32(n) => n as u64,
Value::I64(n) => n as u64,
Value::F32(n) => n as u64,
Value::F64(n) => n,
match import {
Some(Import::Global(val)) => {
if val.ty() == global_desc.ty {
globals.push(vm::ImportedGlobal {
global: vm::LocalGlobal {
data: match val {
Value::I32(n) => *n as u64,
Value::I64(n) => *n as u64,
Value::F32(n) => *n as u64,
Value::F64(n) => *n,
},
},
},
});
} else {
});
} else {
return Err(format!(
"unexpected global type for {:?}:{:?}",
mod_name, item_name
));
}
}
Some(_) => {
return Err(format!(
"unexpected global type for {:?}:{:?}",
"incorrect import type for {}:{}",
mod_name, item_name
));
}
} else {
return Err(format!("incorrect type for {:?}:{:?}", mod_name, item_name));
None => {
return Err(format!("import not found: {}:{}", mod_name, item_name));
}
}
}

View File

@ -19,7 +19,10 @@ pub struct Instance {
}
impl Instance {
pub(crate) fn new(module: Module, imports: &dyn ImportResolver) -> Result<Box<Instance>, String> {
pub(crate) fn new(
module: Module,
imports: &dyn ImportResolver,
) -> Result<Box<Instance>, String> {
let import_backing = ImportBacking::new(&module, imports)?;
let backing = LocalBacking::new(&module, &import_backing);
@ -85,10 +88,7 @@ impl Instance {
// the vmctx will be located at the same place on the stack the entire time that this
// wasm function is running.
let mut vmctx = vm::Ctx::new(
&mut self.backing,
&mut self.import_backing,
);
let mut vmctx = vm::Ctx::new(&mut self.backing, &mut self.import_backing);
let vmctx_ptr = &mut vmctx as *mut vm::Ctx;
let libffi_args: Vec<_> = args
@ -102,15 +102,19 @@ impl Instance {
.chain(iter::once(libffi_arg(&vmctx_ptr)))
.collect();
let func_ptr = CodePtr::from_ptr(self.module
let func_ptr = CodePtr::from_ptr(
self.module
.func_resolver
.get(&self.module, func_index)
.expect("broken invariant, func resolver not synced with module.exports")
.cast()
.as_ptr());
.as_ptr(),
);
call_protected(|| {
self.module.sig_registry.lookup_func_sig(sig_index)
self.module
.sig_registry
.lookup_func_sig(sig_index)
.returns
.first()
.map(|ty| match ty {

View File

@ -4,26 +4,23 @@ mod backend;
mod backing;
mod instance;
mod memory;
mod sig_registry;
mod table;
mod recovery;
mod sighandler;
pub mod mmap;
pub mod module;
mod recovery;
mod sig_registry;
mod sighandler;
mod table;
pub mod types;
pub mod vm;
pub mod vmcalls;
pub use self::backend::{Compiler, FuncResolver};
pub use self::instance::{Import, ImportResolver, Imports, Instance};
pub use self::memory::LinearMemory;
pub use self::module::{Module, ModuleInner};
pub use self::sig_registry::SigRegistry;
pub use self::memory::LinearMemory;
/// Compile a webassembly module using the provided compiler.
pub fn compile(
wasm: &[u8],
compiler: &dyn Compiler,
) -> Result<Module, String> {
pub fn compile(wasm: &[u8], compiler: &dyn Compiler) -> Result<Module, String> {
compiler.compile(wasm)
}
}

View File

@ -5,7 +5,11 @@
//! mutable from both Rust and WebAssembly.
use std::ops::{Deref, DerefMut};
use crate::{types::Memory, vm::LocalMemory, mmap::{Mmap, Protect}};
use crate::{
mmap::{Mmap, Protect},
types::Memory,
vm::LocalMemory,
};
/// A linear memory instance.
#[derive(Debug)]
@ -69,8 +73,11 @@ impl LinearMemory {
// map initial pages as readwrite since the inital mmap is mapped as not accessible.
if initial_pages != 0 {
unsafe {
mmap.protect(0..(initial_pages as usize * Self::PAGE_SIZE as usize), Protect::ReadWrite)
.expect("unable to make memory accessible");
mmap.protect(
0..(initial_pages as usize * Self::PAGE_SIZE as usize),
Protect::ReadWrite,
)
.expect("unable to make memory accessible");
}
}
@ -191,7 +198,9 @@ impl LinearMemory {
let new_bytes = (new_pages * Self::PAGE_SIZE) as usize;
unsafe {
self.mmap.protect(prev_bytes..new_bytes, Protect::ReadWrite).ok()?;
self.mmap
.protect(prev_bytes..new_bytes, Protect::ReadWrite)
.ok()?;
}
self.current = new_pages;
@ -210,16 +219,12 @@ impl PartialEq for LinearMemory {
impl Deref for LinearMemory {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
self.mmap.as_slice()
}
unsafe { self.mmap.as_slice() }
}
}
impl DerefMut for LinearMemory {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
self.mmap.as_slice_mut()
}
unsafe { self.mmap.as_slice_mut() }
}
}

View File

@ -1,8 +1,8 @@
use std::{slice, ptr};
use std::ops::Range;
use errno;
use nix::libc;
use page_size;
use errno;
use std::ops::Range;
use std::{ptr, slice};
#[derive(Debug)]
pub struct Mmap {
@ -44,7 +44,9 @@ impl Mmap {
pub unsafe fn protect(&mut self, range: Range<usize>, protect: Protect) -> Result<(), String> {
let page_size = page_size::get();
let start = self.ptr.add(round_down_to_page_size(range.start, page_size));
let start = self
.ptr
.add(round_down_to_page_size(range.start, page_size));
let size = round_up_to_page_size(range.end - range.start, page_size);
assert!(size <= self.size);
@ -100,5 +102,5 @@ fn round_up_to_page_size(size: usize, page_size: usize) -> usize {
/// Round `size` down to the nearest multiple of `page_size`.
fn round_down_to_page_size(size: usize, page_size: usize) -> usize {
size & !(page_size-1)
}
size & !(page_size - 1)
}

View File

@ -1,16 +1,15 @@
use crate::{
backend::FuncResolver,
types::{
FuncIndex, Global, GlobalDesc, GlobalIndex, Map, MapIndex, Memory, MemoryIndex,
SigIndex, Table, TableIndex,
},
sig_registry::SigRegistry,
ImportResolver,
Instance,
types::{
FuncIndex, Global, GlobalDesc, GlobalIndex, Map, MapIndex, Memory, MemoryIndex, SigIndex,
Table, TableIndex,
},
ImportResolver, Instance,
};
use std::sync::Arc;
use std::ops::Deref;
use hashbrown::HashMap;
use std::ops::Deref;
use std::sync::Arc;
/// This is used to instantiate a new webassembly module.
pub struct ModuleInner {
@ -56,13 +55,12 @@ impl ModuleInner {
impl Deref for Module {
type Target = ModuleInner;
fn deref(&self) -> &ModuleInner {
&*self.0
}
}
#[derive(Debug, Clone)]
pub struct ImportName {
pub module: String,

View File

@ -1,5 +1,5 @@
use crate::{
types::{FuncSig, Map, SigIndex, MapIndex},
types::{FuncSig, Map, MapIndex, SigIndex},
vm,
};
use hashbrown::HashMap;
@ -16,13 +16,13 @@ impl SigRegistry {
sig_assoc: Map::new(),
}
}
pub fn register(&mut self, func_sig: FuncSig) -> SigIndex {
let func_table = &mut self.func_table;
let sig_assoc = &mut self.sig_assoc;
*func_table.entry(func_sig.clone()).or_insert_with(|| {
sig_assoc.push(func_sig)
})
*func_table
.entry(func_sig.clone())
.or_insert_with(|| sig_assoc.push(func_sig))
}
pub fn lookup_func_sig(&self, sig_index: SigIndex) -> &FuncSig {
@ -30,7 +30,11 @@ impl SigRegistry {
}
pub(crate) fn into_vm_sigid(&self) -> Box<[vm::SigId]> {
let v: Vec<_> = self.sig_assoc.iter().map(|(sig_index, _)| vm::SigId(sig_index.index() as u32)).collect();
let v: Vec<_> = self
.sig_assoc
.iter()
.map(|(sig_index, _)| vm::SigId(sig_index.index() as u32))
.collect();
v.into_boxed_slice()
}
}

View File

@ -17,7 +17,7 @@ pub enum Type {
F64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Value {
/// The `i32` type.
I32(i32),
@ -83,7 +83,7 @@ pub struct Table {
/// A global value initializer.
/// Overtime, this will be able to represent more and more
/// complex expressions.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Initializer {
/// Corresponds to a `const.*` instruction.
Const(Value),
@ -98,7 +98,7 @@ pub struct GlobalDesc {
}
/// A wasm global.
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone)]
pub struct Global {
pub desc: GlobalDesc,
pub init: Initializer,

View File

@ -33,10 +33,7 @@ pub struct Ctx {
}
impl Ctx {
pub fn new(
local_backing: &mut LocalBacking,
import_backing: &mut ImportBacking,
) -> Self {
pub fn new(local_backing: &mut LocalBacking, import_backing: &mut ImportBacking) -> Self {
Self {
memories: local_backing.vm_memories.as_mut_ptr(),
tables: local_backing.vm_tables.as_mut_ptr(),
@ -101,6 +98,10 @@ impl ImportedFunc {
pub fn offset_func() -> u8 {
0 * (mem::size_of::<usize>() as u8)
}
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
}
/// Definition of a table used by the VM. (obviously)