Skip to content

Instantly share code, notes, and snippets.

@Redrield
Last active April 25, 2020 04:23
Show Gist options
  • Save Redrield/49bfcfea48985fa0885d04d658cb211b to your computer and use it in GitHub Desktop.
Save Redrield/49bfcfea48985fa0885d04d658cb211b to your computer and use it in GitHub Desktop.
use wasmtime::*;
use wasmtime_wasi::*;
pub struct PluginInstance {
inst: Instance,
buf_ptr_base: i32,
buf_pool_size: usize,
write_offset: isize,
}
impl PluginInstance {
pub fn new(inst: Instance) -> PluginInstance {
// The exported memory is used by the plugins libstd allocator for heap structures
// so the best way to make sure that a region of memory wont interfere with structures
// that a plugin may be allocating is to let it give us the pointer, and make sure to
// remain in the asked for bounds when interacting with plugin memory
let alloc = inst.get_export("alloc_runtime_buffer").unwrap().func().unwrap();
let ret = alloc.call(&[Val::I32(1024)]).unwrap()[0].clone();
let ptr = ret.unwrap_i32();
println!("Host has 0x{:x}..0x{:x} to play with", ptr, ptr + 1024);
PluginInstance {
inst,
buf_ptr_base: ptr,
buf_pool_size: 1024,
write_offset: 0,
}
}
pub fn write_object_to_memory(&mut self, buf: &[u8]) -> (i32, i32) {
// Make sure we wont overflow our buffer
if self.buf_ptr_base + self.write_offset as i32 > self.buf_ptr_base + self.buf_pool_size as i32{
panic!("Out of mem");
}
let mem = self.inst.get_export("memory").unwrap().memory().unwrap();
unsafe {
// Get the write pointer based on our offset into WASM memory, and the offset into our buffer we've written to
let ptr = mem.data_ptr().offset(self.buf_ptr_base as isize + self.write_offset);
ptr.copy_from(buf.as_ptr(), buf.len());
}
// Keep track of where the newly written data starts, and increment the write offset by the data written
let ptr_handle = self.buf_ptr_base + self.write_offset as i32;
self.write_offset += buf.len() as isize;
(ptr_handle, buf.len() as i32)
}
pub fn call_function(&self, name: &str, args: &[Val]) -> Result<Box<[Val]>, Trap> {
let func = self.inst.get_export(name).unwrap().func().unwrap();
func.call(args)
}
}
fn main() {
let store = Store::default();
let module = Module::from_file(&store, "./target/wasm32-wasi/debug/plugin.wasm").unwrap();
let wasi = Wasi::new(&store, WasiCtx::new(std::env::args()).unwrap());
let mut imports = Vec::new();
for import in module.imports() {
if import.module() == "wasi_snapshot_preview1" {
if let Some(export) = wasi.get_export(import.name()) {
imports.push(Extern::from(export.clone()));
continue;
}
}
panic!(
"couldn't find import for `{}::{}`",
import.module(),
import.name()
);
}
let inst = Instance::new(&module, &imports).unwrap();
let mut plugin = PluginInstance::new(inst);
let s = api::CommonStruct { a: 5, b: 6, c: "Hello World!".to_string() };
let buf = api::bincode::serialize(&s).unwrap();
let (ptr, len) = plugin.write_object_to_memory(&buf[..]);
plugin.call_function("plugin_test_fn", &[Val::I32(ptr), Val::I32(len)]).unwrap();
}
/// Use our own allocator to create a buffer that the host can use to transfer
/// data without overwriting data from our plugin
#[no_mangle]
pub extern "C" fn alloc_runtime_buffer(size: usize) -> *mut u8 {
let mut buf = vec![0; size];
let ptr = buf.as_mut_ptr();
//TODO: docs on forget() say that the pointer might not be valid indefinitely.
// maybe a boxed buffer and Box::into_raw should be used instead of a Vec.
std::mem::forget(buf);
ptr
}
#[no_mangle]
pub extern "C" fn plugin_test_fn(buf: &[u8]) {
let s = api::bincode::deserialize::<api::CommonStruct>(buf).unwrap();
let a = 32;
let b = 3.0;
let c = "foobar";
println!("Random stack values {} {} {}", a, b, c);
println!("Hello from wasm! {:?}", s);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment