Skip to content

Commit

Permalink
Support running WASM+WASI binaries via the wasmi interpreter (#472)
Browse files Browse the repository at this point in the history
* See `applications/wasm` for examples on how to run WASM binaries.

* Key components and details:
    * `kernel/wasm_interpreter` is the main interface between `wasmi` and the rest of Theseus.
    * `applications/wasm` is the user frontend for invoking a WASM binary, which allows specifying access to directories, preopening files, and passing arguments to the WASM application.
    * Basic WASI system calls are supported.
    *  WASM binaries are currently located in `extra_files/`, as are some test/demo files that are used by those WASM binaries.
  • Loading branch information
vikrammullick committed Jan 24, 2022
1 parent a685e06 commit f4aa715
Show file tree
Hide file tree
Showing 25 changed files with 2,253 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -27,6 +27,7 @@ github_pages/doc/
*.idea/
*.vscode/
*.code-workspace
*.swp

# log files
*.pcap
Expand Down
99 changes: 99 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -132,7 +132,7 @@ iso: $(iso)


### This target builds an .iso OS image from all of the compiled crates.
$(iso): clean-old-build build
$(iso): clean-old-build build extra_files
# after building kernel and application modules, copy the kernel boot image files
@mkdir -p $(GRUB_ISOFILES)/boot/grub
@cp $(nano_core_binary) $(GRUB_ISOFILES)/boot/kernel.bin
Expand Down
24 changes: 24 additions & 0 deletions applications/wasm/Cargo.toml
@@ -0,0 +1,24 @@
[package]
name = "wasm"
version = "0.1.0"
authors = ["Vikram Mullick <vikram1.mullick@gmail.com>"]
edition = "2018"
description = "Application for running WASI-compliant WebAssembly binaries from Theseus command line"

[dependencies]
getopts = "0.2.21"

[dependencies.app_io]
path = "../../kernel/app_io"

[dependencies.task]
path = "../../kernel/task"

[dependencies.path]
path = "../../kernel/path"

[dependencies.fs_node]
path = "../../kernel/fs_node"

[dependencies.wasi_interpreter]
path = "../../kernel/wasi_interpreter"
150 changes: 150 additions & 0 deletions applications/wasm/src/lib.rs
@@ -0,0 +1,150 @@
//! Application for running WASI-compliant WebAssembly binaries from Theseus command line.
//!
//! USAGE:
//! wasm [option]... WASM_BINARY_PATH [arg]...
//!
//! EXAMPLES:
//!
//! Running a WebAssembly Binary:
//! wasm example.wasm
//!
//! Preopening Multiple Directories:
//! wasm --dir DIR1 --dir DIR2 example.wasm
//!
//! Passing Arguments/Flags to WebAssembly Binary:
//! wasm --dir . example.wasm ARG1 ARG2 ARG3
//!

#![no_std]

#[macro_use]
extern crate alloc;
#[macro_use]
extern crate app_io;
extern crate fs_node;
extern crate getopts;
extern crate path;
extern crate task;
extern crate wasi_interpreter;

use alloc::{string::String, sync::Arc, vec::Vec};
use fs_node::FileOrDir;
use getopts::{Options, ParsingStyle};
use path::Path;

pub fn main(args: Vec<String>) -> isize {
// Parse command line options.
let mut opts = Options::new();

// StopAtFirstFree allows arguments to be passed to WebAssembly program.
opts.parsing_style(ParsingStyle::StopAtFirstFree);

opts.optmulti("d", "dir", "directories to grant file system access", "DIR");
opts.optflag("h", "help", "print this help menu");

let matches = match opts.parse(&args) {
Ok(m) => m,
Err(_f) => {
println!("{}", _f);
print_usage(opts);
return -1;
}
};

if matches.opt_present("h") {
print_usage(opts);
return 0;
}

let preopened_dirs: Vec<String> = matches.opt_strs("d");

// Get current working directory.
let curr_wr = Arc::clone(
&task::get_my_current_task()
.unwrap()
.get_env()
.lock()
.working_dir,
);

// Verify passed preopened directories are real directories.
for dir in preopened_dirs.iter() {
let dir_path = Path::new(dir.clone());

match dir_path.get(&curr_wr) {
Some(file_dir_enum) => match file_dir_enum {
FileOrDir::Dir(_) => {}
FileOrDir::File(file) => {
println!("{:?} is a file.", file.lock().get_name());
return -1;
}
},
_ => {
println!("Couldn't find dir at path '{}'", dir_path);
return -1;
}
};
}

let args: Vec<String> = matches.free;

// Verify that arguments is non-empty.
if args.is_empty() {
println!("No WebAssembly path specified.");
print_usage(opts);
return -1;
}

let wasm_binary_path = Path::new(args[0].clone());

// Parse inputted WebAssembly binary path into byte array.
let wasm_binary: Vec<u8> = match wasm_binary_path.get(&curr_wr) {
Some(file_dir_enum) => match file_dir_enum {
FileOrDir::Dir(directory) => {
println!("{:?} is a directory.", directory.lock().get_name());
return -1;
}
FileOrDir::File(file) => {
let file_locked = file.lock();
let file_size = file_locked.size();
let mut wasm_binary_as_bytes = vec![0; file_size];

let _num_bytes_read = match file_locked.read(&mut wasm_binary_as_bytes, 0) {
Ok(num) => num,
Err(e) => {
println!("Failed to read {:?}, error {:?}", file_locked.get_name(), e);
return -1;
}
};
wasm_binary_as_bytes
}
},
_ => {
println!("Couldn't find file at path '{}'", wasm_binary_path);
return -1;
}
};

// Execute wasm binary.
wasi_interpreter::execute_binary(wasm_binary, args, preopened_dirs);

0
}

fn print_usage(opts: Options) {
println!("{}", opts.usage(USAGE));
}

const USAGE: &'static str = "USAGE:
wasm [option]... WASM_BINARY_PATH [arg]...
EXAMPLES:
Running a WebAssembly Binary:
wasm example.wasm
Preopening Multiple Directories:
wasm --dir DIR1 --dir DIR2 example.wasm
Passing Arguments/Flags to WebAssembly Binary:
wasm --dir . example.wasm ARG1 ARG2 ARG3";
21 changes: 21 additions & 0 deletions extra_files/test_files/text/a_new_hope.txt
@@ -0,0 +1,21 @@
It is a period of civil war.
Rebel spaceships, striking
from a hidden base, have won
their first victory against
the evil Galactic Empire.

During the battle, Rebel
spies managed to steal secret
plans to the Empire's
ultimate weapon, the DEATH
STAR, an armored space
station with enough power to
destroy an entire planet.

Pursued by the Empire's
sinister agents, Princess
Leia races home aboard her
starship, custodian of the
stolen plans that can save
her people and restore
freedom to the galaxy....
19 changes: 19 additions & 0 deletions extra_files/test_files/text/attack_of_the_clones.txt
@@ -0,0 +1,19 @@
There is unrest in the Galactic
Senate. Several thousand solar
systems have declared their
intentions to leave the Republic.

This separatist movement,
under the leadership of the
mysterious Count Dooku, has
made it difficult for the limited
number of Jedi Knights to maintain
peace and order in the galaxy.

Senator Amidala, the former
Queen of Naboo, is returning
to the Galactic Senate to vote
on the critical issue of creating
an ARMY OF THE REPUBLIC
to assist the overwhelmed
Jedi....
20 changes: 20 additions & 0 deletions extra_files/test_files/text/return_of_the_jedi.txt
@@ -0,0 +1,20 @@
Luke Skywalker has returned to
his home planet of Tatooine in
an attempt to rescue his
friend Han Solo from the
clutches of the vile gangster
Jabba the Hutt.

Little does Luke know that the
GALACTIC EMPIRE has secretly
begun construction on a new
armored space station even
more powerful than the first
dreaded Death Star.

When completed, this ultimate
weapon will spell certain doom
for the small band of rebels
struggling to restore freedom
to the galaxy...

0 comments on commit f4aa715

Please sign in to comment.