Skip to content
This repository has been archived by the owner on Jul 16, 2020. It is now read-only.

Commit

Permalink
[WIP]: initial custom WASI demo
Browse files Browse the repository at this point in the history
This demo is the trivial approach to implementing a custom WASI. It
contains a copy of the wasmtime-wasi implementation, which is rather a
short-term solution and not sustainable any longer than a few
iterations.

As a demo customization, it redirects all output from fd=2 (stderr) to
fd=1 (stdout).
  • Loading branch information
steveej committed Sep 19, 2019
1 parent 81ee4e4 commit cba0a87
Show file tree
Hide file tree
Showing 15 changed files with 3,032 additions and 0 deletions.
1,455 changes: 1,455 additions & 0 deletions wasmtime-wasi-custom/Cargo.lock

Large diffs are not rendered by default.

28 changes: 28 additions & 0 deletions wasmtime-wasi-custom/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "wasmtime-wasi-custom"
version = "0.1.0"
authors = ["Stefan Junker <[email protected]>"]
edition = "2018"
build = "build.rs"

[dependencies]
wasmtime-api = { git = "https://github.com/CraneStation/wasmtime", package = "wasmtime-api", rev = "9c747db4293192dffe659ed741070716caeb43b0" }
wasmtime-jit = { git = "https://github.com/CraneStation/wasmtime", package = "wasmtime-jit", rev = "9c747db4293192dffe659ed741070716caeb43b0" }
wasmtime-interface-types = { git = "https://github.com/CraneStation/wasmtime", package = "wasmtime-interface-types", rev = "9c747db4293192dffe659ed741070716caeb43b0" }

# wasmtime-api = { path = "/home/steveej/src/job-redhat/enarx/github_cranestation_wasmtime/wasmtime-api" }
# wasmtime-jit = { path = "/home/steveej/src/job-redhat/enarx/github_cranestation_wasmtime/wasmtime-jit" }
# wasmtime-interface-types = { path = "/home/steveej/src/job-redhat/enarx/github_cranestation_wasmtime/wasmtime-interface-types" }

# A copy of wasmtime-wasi at v0.3.0 made for customisation purposes
wasmtime-wasi = { path = "wasmtime-wasi" }
# wasmtime-wasi = { path = "/home/steveej/src/job-redhat/enarx/github_cranestation_wasmtime/wasmtime-wasi" }

log = "0.4"
env_logger = "0.6"

[features]
default = ["rust"]

c = []
rust = []
27 changes: 27 additions & 0 deletions wasmtime-wasi-custom/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Wasmtime Custom WASI Demo
This is an example crate demonstrating how Enarx may use Wasmtime, a Rust-powered JIT, to natively run programs from several different source languages (Rust/C/C++) compiled to WASI-compliant WASM.

## Running the demo

By default, the Rust demo should run with `cargo run` out of the box.

If you wish to compile the C demo, you'll need to install `lld` . On Fedora, this can be accomplished with `sudo dnf install lld`.

### Switching Languages

The WASM binary used for the demo can be compiled from either Rust or C. Compilation of this binary happens at build time, and the source language can be controlled via Cargo features.

Rust is compiled by default. If C is desired, the appropriate feature can be invoked via the `cargo` command:

```
cargo run --no-default-features --features c
```

Or, alternatively, by changing the `default` feature in `Cargo.toml` from `["rust"]` to `["c"]`.

## Changelog
This demo is expected to evolve over time.

### v0.1.0
Customizes `fd_write` so that all writes to fd=2 (stdin) are redirected to fd=1 (stdout).
Uses an in-tree copy of `wasmtime-wasi` with customizations to the glue code.
35 changes: 35 additions & 0 deletions wasmtime-wasi-custom/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use std::process::Command;

fn main() {
let out = std::env::var("OUT_DIR").unwrap();

if cfg!(feature = "c") {
println!("Compiling C app to WASM...");
let cc = std::env::var("CC").unwrap_or("clang".to_owned());
assert!(Command::new(cc)
.args(&["-o", &format!("{}/app.wasm", out)])
.arg("--target=wasm32-unknown-wasi")
.arg("-Wl,--export-all")
.arg("-Wl,--no-entry")
.arg("-nostdlib")
.arg("-O3")
.arg("src/app.c")
.status()
.expect("failed to compile WASM module")
.success());
}

if cfg!(feature = "rust") {
println!("Compiling Rust app to WASM...");
assert!(Command::new("rustc")
// .args(&["-C", "lto"])
// .args(&["-C", "opt-level=3"])
// .args(&["--target", "wasm32-unknown-unknown"])
.args(&["--target", "wasm32-wasi"])
.args(&["-o", &format!("{}/app.wasm", out)])
.arg("src/app.rs")
.status()
.expect("failed to compile WASM module")
.success());
}
}
3 changes: 3 additions & 0 deletions wasmtime-wasi-custom/src/app.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int add(int l, int r) {
return l + r;
}
7 changes: 7 additions & 0 deletions wasmtime-wasi-custom/src/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
fn main() {}

#[no_mangle]
pub extern fn hello_world() {
println!("Hello, world!");
eprintln!("Hello, stderr!");
}
93 changes: 93 additions & 0 deletions wasmtime-wasi-custom/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use log::{info, trace};
use std::collections::HashMap;
use std::error::Error;
use std::fs::read;
use wasmtime_api::{self, HostRef};

fn main() -> Result<(), Box<dyn Error>> {
let _ = env_logger::try_init_from_env(env_logger::Env::default());

// Before we begin, it'll be heplful to know which source language we're
// running, so let's print it out.
if cfg!(feature = "c") {
info!("WASM application binary has been compiled from C.");
}
if cfg!(feature = "rust") {
info!("WASM appliation binary has been compiled from Rust.");
}

// Load and instnatiate the WASM app
info!("Loading WASM application binary...");
let app_wasm = read(concat!(env!("OUT_DIR"), "/app.wasm")).unwrap();
info!("WASM application binary loaded.");

// wasmtime-api variables
let engine = HostRef::new(wasmtime_api::Engine::default());
let store = HostRef::new(wasmtime_api::Store::new(engine));
let mut module_registry: HashMap<String, (wasmtime_api::Instance, HashMap<String, usize>)> =
HashMap::new();

// Instantiate WASI
let custom_wasi = wasmtime_wasi::instantiate_wasi(
// prefix: &str,
"",
// global_exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>>,
store.borrow().global_exports().clone(),
// preopened_dirs: &[(String, File)],
Default::default(),
// argv: &[String],
Default::default(),
// environ: &[(String, String)],
Default::default(),
)
.expect("could nt instantiate WASI");
module_registry.insert(
"wasi_unstable".to_string(),
wasmtime_api::Instance::from_handle(store.clone(), custom_wasi)?,
);

let app_module = HostRef::new(wasmtime_api::Module::new(store.clone(), &app_wasm)?);

// Resolve import using module_registry.
let imports = {
// let module_borrow: std::cell::Ref<'_, wasmtime_api::Module> = app_module.borrow();

app_module
.borrow()
.imports()
.iter()
.map(|i| {
let module_name = i.module().to_string();
if let Some((instance, map)) = module_registry.get(&module_name) {
let field_name = i.name().to_string();
if let Some(export_index) = map.get(&field_name) {
trace!("found export: {}/{}", field_name, module_name);
Ok(instance.exports()[*export_index].clone())
} else {
Err(format!(
"Import {} was not found in module {}",
field_name, module_name
))
}
} else {
Err(format!("Import module {} was not found", module_name))
}
})
.collect::<Result<Vec<_>, _>>()?
};

let instance = HostRef::new(wasmtime_api::Instance::new(
store.clone(),
app_module.clone(),
&imports,
)?);

let mut handle = instance.borrow().handle().clone();
let mut context = store.borrow().engine().borrow().create_wasmtime_context();
let module_data = wasmtime_interface_types::ModuleData::new(&app_wasm)?;

// Invoke the function
module_data.invoke(&mut context, &mut handle, "hello_world", Default::default())?;

Ok(())
}
28 changes: 28 additions & 0 deletions wasmtime-wasi-custom/wasmtime-wasi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "wasmtime-wasi"
version = "0.2.0"
authors = ["The Cranelift Project Developers"]
description = "WASI API support for Wasmtime"
categories = ["wasm"]
repository = "https://github.com/CraneStation/wasmtime"
license = "Apache-2.0 WITH LLVM-exception"
readme = "README.md"
edition = "2018"

[dependencies]
# wasmtime-runtime = { path = "../wasmtime-runtime" }
wasmtime-runtime = { git = "https://github.com/CraneStation/wasmtime", package = "wasmtime-runtime", rev = "9c747db4293192dffe659ed741070716caeb43b0" }
# wasmtime-environ = { path = "../wasmtime-environ" }
wasmtime-environ = { git = "https://github.com/CraneStation/wasmtime", package = "wasmtime-environ", rev = "9c747db4293192dffe659ed741070716caeb43b0" }
# wasmtime-jit = { path = "../wasmtime-jit" }
wasmtime-jit = { git = "https://github.com/CraneStation/wasmtime", package = "wasmtime-jit", rev = "9c747db4293192dffe659ed741070716caeb43b0" }
wasi-common = { git = "https://github.com/CraneStation/wasi-common", rev = "8ea7a983d8b1364e5f62d2adf0e74b3b8db1c9b3" }
cranelift-codegen = { version = "0.41.0", features = ["enable-serde"] }
cranelift-entity = { version = "0.41.0", features = ["enable-serde"] }
cranelift-wasm = { version = "0.41.0", features = ["enable-serde"] }
target-lexicon = "0.4.0"
log = { version = "0.4.8", default-features = false }

[badges]
maintenance = { status = "experimental" }
travis-ci = { repository = "CraneStation/wasmtime" }
Loading

0 comments on commit cba0a87

Please sign in to comment.