Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add useful functions for external use of WASI filesystem #595

Merged
merged 4 commits into from
Jul 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ All PRs to the Wasmer repository must add to this file.
Blocks of changes will separated by version increments.

## **[Unreleased]**
- [#595](https://github.com/wasmerio/wasmer/pull/595) Add unstable public API for interfacing with the WASI file system in plugin-like usecases
- [#598](https://github.com/wasmerio/wasmer/pull/598) LLVM Backend is now supported in Windows
- [#599](https://github.com/wasmerio/wasmer/pull/599) Fix llvm backend failures in fat spec tests and simd_binaryen spec test.
- [#579](https://github.com/wasmerio/wasmer/pull/579) Fix bug in caching with LLVM and Singlepass backends.
Expand Down
Binary file modified examples/plugin-for-example.wasm
Binary file not shown.
2 changes: 2 additions & 0 deletions examples/plugin-for-example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ In this example, we instantiate a system with an extended (WASI)[wasi] ABI, allo

Because the Rust WASI doesn't support the crate type of `cdylib`, we have to include a main function which we don't use. This is being discussed [here](https://github.com/WebAssembly/WASI/issues/24).

We call the main function to initialize WASI's libpreopen internal datastructures and have the module call back into the host to set swap out the modules implementation of stdout. The host then provides a wrapper around stdout, allowing the guest's writes to stdout to be formatted in a host-appropriate manner.

[wasi]: https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/
5 changes: 4 additions & 1 deletion examples/plugin-for-example/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
extern "C" {
fn it_works() -> i32;
fn initialize();
}

#[no_mangle]
Expand All @@ -9,4 +10,6 @@ pub fn plugin_entrypoint(n: i32) -> i32 {
result + n
}

pub fn main() {}
pub fn main() {
unsafe { initialize() };
}
110 changes: 109 additions & 1 deletion examples/plugin.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use wasmer_runtime::{func, imports, instantiate};
use wasmer_runtime_core::vm::Ctx;
use wasmer_wasi::generate_import_object;
use wasmer_wasi::{
generate_import_object,
state::{self, WasiFile},
types,
};

static PLUGIN_LOCATION: &'static str = "examples/plugin-for-example.wasm";

Expand All @@ -9,6 +13,107 @@ fn it_works(_ctx: &mut Ctx) -> i32 {
5
}

#[derive(Debug)]
pub struct LoggingWrapper {
pub wasm_module_name: String,
}

// std io trait boiler plate so we can implement WasiFile
// LoggingWrapper is a write-only type so we just want to immediately
// fail when reading or Seeking
impl std::io::Read for LoggingWrapper {
fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from logging wrapper",
))
}
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> std::io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from logging wrapper",
))
}
fn read_to_string(&mut self, _buf: &mut String) -> std::io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from logging wrapper",
))
}
fn read_exact(&mut self, _buf: &mut [u8]) -> std::io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from logging wrapper",
))
}
}
impl std::io::Seek for LoggingWrapper {
fn seek(&mut self, _pos: std::io::SeekFrom) -> std::io::Result<u64> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not seek logging wrapper",
))
}
}
impl std::io::Write for LoggingWrapper {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let stdout = std::io::stdout();
let mut out = stdout.lock();
out.write(b"[")?;
out.write(self.wasm_module_name.as_bytes())?;
out.write(b"]: ")?;
out.write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
std::io::stdout().flush()
}
fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
let stdout = std::io::stdout();
let mut out = stdout.lock();
out.write(b"[")?;
out.write(self.wasm_module_name.as_bytes())?;
out.write(b"]: ")?;
out.write_all(buf)
}
fn write_fmt(&mut self, fmt: std::fmt::Arguments) -> std::io::Result<()> {
let stdout = std::io::stdout();
let mut out = stdout.lock();
out.write(b"[")?;
out.write(self.wasm_module_name.as_bytes())?;
out.write(b"]: ")?;
out.write_fmt(fmt)
}
}

// the WasiFile methods aren't relevant for a write-only Stdout-like implementation
impl WasiFile for LoggingWrapper {
fn last_accessed(&self) -> u64 {
0
}
fn last_modified(&self) -> u64 {
0
}
fn created_time(&self) -> u64 {
0
}
fn size(&self) -> u64 {
0
}
}

/// Called by the program when it wants to set itself up
fn initialize(ctx: &mut Ctx) {
let state = state::get_wasi_state(ctx);
let wasi_file_inner = LoggingWrapper {
wasm_module_name: "example module name".to_string(),
};
// swap stdout with our new wasifile
let _old_stdout = state
.fs
.swap_file(types::__WASI_STDOUT_FILENO, Box::new(wasi_file_inner))
.unwrap();
}

fn main() {
// Load the plugin data
let wasm_bytes = std::fs::read(PLUGIN_LOCATION).expect(&format!(
Expand All @@ -22,6 +127,7 @@ fn main() {
let custom_imports = imports! {
"env" => {
"it_works" => func!(it_works),
"initialize" => func!(initialize),
},
};
// The WASI imports object contains all required import functions for a WASI module to run.
Expand All @@ -30,6 +136,8 @@ fn main() {
let instance =
instantiate(&wasm_bytes[..], &base_imports).expect("failed to instantiate wasm module");

let main = instance.func::<(), ()>("_start").unwrap();
main.call().expect("Could not initialize");
// get a reference to the function "plugin_entrypoint" which takes an i32 and returns an i32
let entry_point = instance.func::<(i32), i32>("plugin_entrypoint").unwrap();
// call the "entry_point" function in WebAssembly with the number "2" as the i32 argument
Expand Down
3 changes: 2 additions & 1 deletion lib/wasi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ extern crate winapi;
#[macro_use]
mod macros;
mod ptr;
mod state;
pub mod state;
mod syscalls;
mod utils;

use self::state::{WasiFs, WasiState};
pub use self::syscalls::types;
use self::syscalls::*;

use std::ffi::c_void;
Expand Down
Loading