mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-23 13:41:32 +00:00
Code loader framework.
This commit is contained in:
@ -80,6 +80,9 @@ pub trait RunnableModule: Send + Sync {
|
|||||||
fn get_trampoline(&self, info: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm>;
|
fn get_trampoline(&self, info: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm>;
|
||||||
|
|
||||||
unsafe fn do_early_trap(&self, data: Box<dyn Any>) -> !;
|
unsafe fn do_early_trap(&self, data: Box<dyn Any>) -> !;
|
||||||
|
|
||||||
|
fn get_code(&self) -> Option<&[u8]> { None }
|
||||||
|
fn get_offsets(&self) -> Option<Vec<usize>> { None }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CacheGen: Send + Sync {
|
pub trait CacheGen: Send + Sync {
|
||||||
|
@ -12,6 +12,8 @@ use crate::{
|
|||||||
typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList},
|
typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList},
|
||||||
types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value},
|
types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value},
|
||||||
vm,
|
vm,
|
||||||
|
loader::{self, Loader, Instance as _},
|
||||||
|
structures::TypedIndex,
|
||||||
};
|
};
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use std::{mem, ptr::NonNull, sync::Arc};
|
use std::{mem, ptr::NonNull, sync::Arc};
|
||||||
@ -38,7 +40,7 @@ impl Drop for InstanceInner {
|
|||||||
///
|
///
|
||||||
/// [`ImportObject`]: struct.ImportObject.html
|
/// [`ImportObject`]: struct.ImportObject.html
|
||||||
pub struct Instance {
|
pub struct Instance {
|
||||||
module: Arc<ModuleInner>,
|
pub module: Arc<ModuleInner>,
|
||||||
inner: Box<InstanceInner>,
|
inner: Box<InstanceInner>,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
import_object: ImportObject,
|
import_object: ImportObject,
|
||||||
@ -127,6 +129,10 @@ impl Instance {
|
|||||||
Ok(instance)
|
Ok(instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load<T: Loader>(&self, loader: T) -> ::std::result::Result<T::Instance, T::Error> {
|
||||||
|
loader.load(&*self.module.runnable_module, &self.module.info, unsafe { &(*self.inner.vmctx).internal })
|
||||||
|
}
|
||||||
|
|
||||||
/// Through generic magic and the awe-inspiring power of traits, we bring you...
|
/// Through generic magic and the awe-inspiring power of traits, we bring you...
|
||||||
///
|
///
|
||||||
/// # "Func"
|
/// # "Func"
|
||||||
@ -214,6 +220,32 @@ impl Instance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolve_local_func(&self, name: &str) -> ResolveResult<usize> {
|
||||||
|
let export_index =
|
||||||
|
self.module
|
||||||
|
.info
|
||||||
|
.exports
|
||||||
|
.get(name)
|
||||||
|
.ok_or_else(|| ResolveError::ExportNotFound {
|
||||||
|
name: name.to_string(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if let ExportIndex::Func(func_index) = export_index {
|
||||||
|
match func_index.local_or_import(&self.module.info) {
|
||||||
|
LocalOrImport::Local(x) => Ok(x.index()),
|
||||||
|
LocalOrImport::Import(x) => Err(ResolveError::ExportWrongType {
|
||||||
|
name: name.to_string(),
|
||||||
|
}
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(ResolveError::ExportWrongType {
|
||||||
|
name: name.to_string(),
|
||||||
|
}
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This returns the representation of a function that can be called
|
/// This returns the representation of a function that can be called
|
||||||
/// safely.
|
/// safely.
|
||||||
///
|
///
|
||||||
|
@ -33,6 +33,7 @@ pub mod units;
|
|||||||
pub mod vm;
|
pub mod vm;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod vmcalls;
|
pub mod vmcalls;
|
||||||
|
pub mod loader;
|
||||||
|
|
||||||
use self::error::CompileResult;
|
use self::error::CompileResult;
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
|
130
lib/runtime-core/src/loader.rs
Normal file
130
lib/runtime-core/src/loader.rs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
use std::{
|
||||||
|
fmt::Debug,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
backend::RunnableModule,
|
||||||
|
vm::InternalCtx,
|
||||||
|
module::ModuleInfo,
|
||||||
|
types::Value,
|
||||||
|
};
|
||||||
|
use libc::{
|
||||||
|
c_char, mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_NONE, PROT_READ,
|
||||||
|
PROT_WRITE,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub trait Loader {
|
||||||
|
type Instance: Instance;
|
||||||
|
type Error: Debug;
|
||||||
|
|
||||||
|
fn load(&self, rm: &dyn RunnableModule, module: &ModuleInfo, ctx: &InternalCtx) -> Result<Self::Instance, Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Instance {
|
||||||
|
type Error: Debug;
|
||||||
|
fn call(&mut self, id: usize, args: &[Value]) -> Result<u64, Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LocalLoader;
|
||||||
|
|
||||||
|
impl Loader for LocalLoader {
|
||||||
|
type Instance = LocalInstance;
|
||||||
|
type Error = String;
|
||||||
|
|
||||||
|
fn load(&self, rm: &dyn RunnableModule, module: &ModuleInfo, ctx: &InternalCtx) -> Result<Self::Instance, Self::Error> {
|
||||||
|
let code = rm.get_code().unwrap();
|
||||||
|
let mut code_mem = CodeMemory::new(code.len());
|
||||||
|
code_mem[..code.len()].copy_from_slice(code);
|
||||||
|
code_mem.make_executable();
|
||||||
|
|
||||||
|
Ok(LocalInstance {
|
||||||
|
code: code_mem,
|
||||||
|
offsets: rm.get_offsets().unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LocalInstance {
|
||||||
|
code: CodeMemory,
|
||||||
|
offsets: Vec<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instance for LocalInstance {
|
||||||
|
type Error = String;
|
||||||
|
fn call(&mut self, id: usize, args: &[Value]) -> Result<u64, Self::Error> {
|
||||||
|
let offset = self.offsets[id];
|
||||||
|
let addr: *const u8 = unsafe { self.code.as_ptr().offset(offset as isize) };
|
||||||
|
use std::mem::transmute;
|
||||||
|
Ok(unsafe {
|
||||||
|
match args.len() {
|
||||||
|
0 => (transmute::<_, extern "C" fn() -> u64>(addr))(),
|
||||||
|
1 => (transmute::<_, extern "C" fn(u64) -> u64>(addr))(args[0].to_u64()),
|
||||||
|
2 => (transmute::<_, extern "C" fn(u64, u64) -> u64>(addr))(args[0].to_u64(), args[1].to_u64()),
|
||||||
|
3 => (transmute::<_, extern "C" fn(u64, u64, u64) -> u64>(addr))(args[0].to_u64(), args[1].to_u64(), args[2].to_u64()),
|
||||||
|
4 => (transmute::<_, extern "C" fn(u64, u64, u64, u64) -> u64>(addr))(args[0].to_u64(), args[1].to_u64(), args[2].to_u64(), args[3].to_u64()),
|
||||||
|
5 => (transmute::<_, extern "C" fn(u64, u64, u64, u64, u64) -> u64>(addr))(args[0].to_u64(), args[1].to_u64(), args[2].to_u64(), args[3].to_u64(), args[4].to_u64()),
|
||||||
|
_ => return Err("too many arguments".into())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CodeMemory {
|
||||||
|
ptr: *mut u8,
|
||||||
|
size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CodeMemory {
|
||||||
|
pub fn new(size: usize) -> CodeMemory {
|
||||||
|
fn round_up_to_page_size(size: usize) -> usize {
|
||||||
|
(size + (4096 - 1)) & !(4096 - 1)
|
||||||
|
}
|
||||||
|
let size = round_up_to_page_size(size);
|
||||||
|
let ptr = unsafe {
|
||||||
|
mmap(
|
||||||
|
::std::ptr::null_mut(),
|
||||||
|
size,
|
||||||
|
PROT_READ | PROT_WRITE,
|
||||||
|
MAP_PRIVATE | MAP_ANON,
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if ptr as isize == -1 {
|
||||||
|
panic!("cannot allocate code memory");
|
||||||
|
}
|
||||||
|
CodeMemory {
|
||||||
|
ptr: ptr as _,
|
||||||
|
size: size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_executable(&mut self) {
|
||||||
|
if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_EXEC) } != 0 {
|
||||||
|
panic!("cannot set code memory to executable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for CodeMemory {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { munmap(self.ptr as _, self.size); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for CodeMemory {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
::std::slice::from_raw_parts(self.ptr, self.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for CodeMemory {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
::std::slice::from_raw_parts_mut(self.ptr, self.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -45,6 +45,15 @@ impl Value {
|
|||||||
Value::F64(_) => Type::F64,
|
Value::F64(_) => Type::F64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_u64(&self) -> u64 {
|
||||||
|
match *self {
|
||||||
|
Value::I32(x) => x as u32 as u64,
|
||||||
|
Value::I64(x) => x as u64,
|
||||||
|
Value::F32(x) => f32::to_bits(x) as u64,
|
||||||
|
Value::F64(x) => f64::to_bits(x),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<i32> for Value {
|
impl From<i32> for Value {
|
||||||
|
@ -159,6 +159,7 @@ pub struct X64ExecutionContext {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
functions: Vec<X64FunctionCode>,
|
functions: Vec<X64FunctionCode>,
|
||||||
function_pointers: Vec<FuncPtr>,
|
function_pointers: Vec<FuncPtr>,
|
||||||
|
function_offsets: Vec<AssemblyOffset>,
|
||||||
signatures: Arc<Map<SigIndex, FuncSig>>,
|
signatures: Arc<Map<SigIndex, FuncSig>>,
|
||||||
_br_table_data: Vec<Vec<usize>>,
|
_br_table_data: Vec<Vec<usize>>,
|
||||||
breakpoints: Arc<HashMap<usize, Box<Fn(BkptInfo) + Send + Sync + 'static>>>,
|
breakpoints: Arc<HashMap<usize, Box<Fn(BkptInfo) + Send + Sync + 'static>>>,
|
||||||
@ -270,6 +271,14 @@ impl RunnableModule for X64ExecutionContext {
|
|||||||
protect_unix::TRAP_EARLY_DATA.with(|x| x.set(Some(data)));
|
protect_unix::TRAP_EARLY_DATA.with(|x| x.set(Some(data)));
|
||||||
protect_unix::trigger_trap();
|
protect_unix::trigger_trap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_code(&self) -> Option<&[u8]> {
|
||||||
|
Some(&self.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_offsets(&self) -> Option<Vec<usize>> {
|
||||||
|
Some(self.function_offsets.iter().map(|x| x.0).collect())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -376,6 +385,7 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
|
|||||||
self.function_labels.as_ref().unwrap()
|
self.function_labels.as_ref().unwrap()
|
||||||
};
|
};
|
||||||
let mut out_labels: Vec<FuncPtr> = vec![];
|
let mut out_labels: Vec<FuncPtr> = vec![];
|
||||||
|
let mut out_offsets: Vec<AssemblyOffset> = vec![];
|
||||||
|
|
||||||
for i in 0..function_labels.len() {
|
for i in 0..function_labels.len() {
|
||||||
let (_, offset) = match function_labels.get(&i) {
|
let (_, offset) = match function_labels.get(&i) {
|
||||||
@ -395,6 +405,7 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
out_labels.push(FuncPtr(output.ptr(*offset) as _));
|
out_labels.push(FuncPtr(output.ptr(*offset) as _));
|
||||||
|
out_offsets.push(*offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
let breakpoints: Arc<HashMap<_, _>> = Arc::new(
|
let breakpoints: Arc<HashMap<_, _>> = Arc::new(
|
||||||
@ -412,6 +423,7 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, CodegenError>
|
|||||||
breakpoints: breakpoints,
|
breakpoints: breakpoints,
|
||||||
func_import_count: self.func_import_count,
|
func_import_count: self.func_import_count,
|
||||||
function_pointers: out_labels,
|
function_pointers: out_labels,
|
||||||
|
function_offsets: out_offsets,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ use wasmer_runtime::{
|
|||||||
use wasmer_runtime_core::{
|
use wasmer_runtime_core::{
|
||||||
self,
|
self,
|
||||||
backend::{Compiler, CompilerConfig},
|
backend::{Compiler, CompilerConfig},
|
||||||
|
loader::{self, Loader, Instance as _, LocalLoader},
|
||||||
};
|
};
|
||||||
#[cfg(feature = "backend:singlepass")]
|
#[cfg(feature = "backend:singlepass")]
|
||||||
use wasmer_singlepass_backend::SinglePassCompiler;
|
use wasmer_singlepass_backend::SinglePassCompiler;
|
||||||
@ -90,6 +91,13 @@ struct Run {
|
|||||||
#[structopt(long = "dir", multiple = true, group = "wasi")]
|
#[structopt(long = "dir", multiple = true, group = "wasi")]
|
||||||
pre_opened_directories: Vec<String>,
|
pre_opened_directories: Vec<String>,
|
||||||
|
|
||||||
|
// Custom code loader
|
||||||
|
#[structopt(
|
||||||
|
long = "loader",
|
||||||
|
raw(possible_values = "LoaderName::variants()", case_insensitive = "true")
|
||||||
|
)]
|
||||||
|
loader: Option<LoaderName>,
|
||||||
|
|
||||||
#[structopt(long = "command-name", hidden = true)]
|
#[structopt(long = "command-name", hidden = true)]
|
||||||
command_name: Option<String>,
|
command_name: Option<String>,
|
||||||
|
|
||||||
@ -98,6 +106,30 @@ struct Run {
|
|||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
enum LoaderName {
|
||||||
|
Local,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoaderName {
|
||||||
|
pub fn variants() -> &'static [&'static str] {
|
||||||
|
&[
|
||||||
|
"local",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for LoaderName {
|
||||||
|
type Err = String;
|
||||||
|
fn from_str(s: &str) -> Result<LoaderName, String> {
|
||||||
|
match s.to_lowercase().as_str() {
|
||||||
|
"local" => Ok(LoaderName::Local),
|
||||||
|
_ => Err(format!("The loader {} doesn't exist", s)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Backend {
|
enum Backend {
|
||||||
@ -303,6 +335,28 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
|
|||||||
.map_err(|e| format!("Can't compile module: {:?}", e))?
|
.map_err(|e| format!("Can't compile module: {:?}", e))?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(loader) = options.loader {
|
||||||
|
let import_object = wasmer_runtime_core::import::ImportObject::new();
|
||||||
|
let instance = module
|
||||||
|
.instantiate(&import_object)
|
||||||
|
.map_err(|e| format!("Can't instantiate module: {:?}", e))?;
|
||||||
|
|
||||||
|
let args: Vec<Value> = options
|
||||||
|
.args
|
||||||
|
.iter()
|
||||||
|
.map(|arg| arg.as_str())
|
||||||
|
.map(|x| Value::I32(x.parse().unwrap()))
|
||||||
|
.collect();
|
||||||
|
let index = instance.resolve_local_func("main").unwrap();
|
||||||
|
match loader {
|
||||||
|
LoaderName::Local => {
|
||||||
|
let mut ins = instance.load(LocalLoader).unwrap();
|
||||||
|
println!("{:?}", ins.call(index, &args));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: refactor this
|
// TODO: refactor this
|
||||||
if wasmer_emscripten::is_emscripten_module(&module) {
|
if wasmer_emscripten::is_emscripten_module(&module) {
|
||||||
let mut emscripten_globals = wasmer_emscripten::EmscriptenGlobals::new(&module);
|
let mut emscripten_globals = wasmer_emscripten::EmscriptenGlobals::new(&module);
|
||||||
|
Reference in New Issue
Block a user