Skip to content

Commit

Permalink
Merge pull request #34 from fgh1999/rust-wrapper
Browse files Browse the repository at this point in the history
An example WASI wrapper based on Rust std
  • Loading branch information
ainozaki authored Jun 20, 2024
2 parents 3c26a66 + 36a5e71 commit be66b30
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
*.o
*.wasm
*.ll
*.a
misc
misc/*
misc/*
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ ELF file generated by Wasker is OS-independent: WASI calls from Wasm application

Please write your own WASI wrapper for your favorite OS to be linked with Wasker output.

Here, we'll show a [tiny example](./examples/wasi-wrapper/wasi-wrapper-linux.c) of running Wasker output on Linux.
Here, we'll show a [tiny example](./examples/wasi-wrapper/c/wasi-wrapper-linux.c) of running Wasker output on Linux.

Link Wasker output and WASI wapper for Linux
```
gcc -no-pie ./examples/wasi-wrapper/wasi-wrapper-linux.c ./wasm.o -o hello
gcc -no-pie ./examples/wasi-wrapper/c/wasi-wrapper-linux.c ./wasm.o -o hello
```

Run!!
Expand Down
File renamed without changes.
3 changes: 3 additions & 0 deletions examples/wasi-wrapper/rust/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target
*.a
.vscode
16 changes: 16 additions & 0 deletions examples/wasi-wrapper/rust/Cargo.lock

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

8 changes: 8 additions & 0 deletions examples/wasi-wrapper/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "wasi-wrapper"
version = "0.1.0"
edition = "2021"
build = "build.rs"

[dependencies]
wasi = "0.11.0+wasi-snapshot-preview1"
16 changes: 16 additions & 0 deletions examples/wasi-wrapper/rust/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# An Example of Running Wasker Output Based on Rust Std

Similar to the [Linux WASI wrapper](../c/wasi-wrapper-linux.c),
4 mock WASI interfaces are implemented in order to support [the rust example](../../rust/README.md).

## Prerequisite

Have Wasker installed.

## Run the example

Just run. Check build options in [`build.rs`](./build.rs).

```shell
cargo run
```
41 changes: 41 additions & 0 deletions examples/wasi-wrapper/rust/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use std::{path::Path, process::Command};

fn path_to_str(path: &Path) -> &str {
path.as_os_str().to_str().unwrap()
}

fn main() {
let target_name = "wasm";
let target_obj_name = format!("{}.o", target_name);
let wasm_path = Path::new("../../rust");
let target_path = wasm_path.join(&target_obj_name);
println!("cargo:rerun-if-changed={}", path_to_str(&target_path));

std::env::set_current_dir(wasm_path).unwrap();
Command::new("cargo")
.args(["build", "--target=wasm32-wasi"])
.status()
.expect("failed to compile target into WASM");
Command::new("wasker")
.args([
"-o",
&target_obj_name,
"target/wasm32-wasi/debug/rust.wasm",
])
.status()
.expect("failed to compile target into obj");

let target_lib_name = format!("lib{}.a", target_name);
Command::new("ar")
.args([
"rcs",
&target_lib_name,
&target_obj_name,
])
.status()
.expect("failed to convert obj into lib");

println!("cargo:rustc-link-arg=-no-pie");
println!("cargo:rustc-link-search=native={}", path_to_str(wasm_path));
println!("cargo:rustc-link-lib=static={}", target_name);
}
14 changes: 14 additions & 0 deletions examples/wasi-wrapper/rust/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
mod memory;
mod wasi;

extern "C" {
fn wasker_main();
}

fn main() {
unsafe {
// Entrypoint of ELF generated by Wasker
wasker_main();
}
println!("wasker_main from the compiled target WASM done.");
}
56 changes: 56 additions & 0 deletions examples/wasi-wrapper/rust/src/memory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//! Linear Memory
use std::sync::Mutex;

const LINEAR_MEMORY_BLOCK_SIZE: i32 = 64 * 1024;
const LINEAR_MEMORY_BLOCK_NUM_MAX: i32 = 32;

static mut LINEAR_MEMORY_BASE: *mut u8 = 0 as _;
static LINEAR_MEMORY_BLOCK_NUM: Mutex<i32> = Mutex::new(0);

#[inline]
pub unsafe fn get_memory_base() -> *mut u8 {
unsafe { LINEAR_MEMORY_BASE }
}

unsafe fn alloc_memory() -> *mut u8 {
use std::alloc::{alloc, Layout};
unsafe {
LINEAR_MEMORY_BASE = alloc(
Layout::from_size_align(
(LINEAR_MEMORY_BLOCK_SIZE * LINEAR_MEMORY_BLOCK_NUM_MAX) as usize,
8,
)
.unwrap(),
);
LINEAR_MEMORY_BASE
}
}

// fn get_memory_block_num() -> i32 {
// LINEAR_MEMORY_BLOCK_NUM.lock().unwrap().clone()
// }

fn inc_memory_block_num(block_num: i32) -> i32 {
assert!(
block_num >= 0,
"block_num must be greater than or equal to 0"
);
let mut num = LINEAR_MEMORY_BLOCK_NUM.lock().unwrap();
let old_val = *num;
if num.checked_add(block_num).unwrap() > LINEAR_MEMORY_BLOCK_NUM_MAX {
println!("memory_grow: failed to grow memory");
return -1;
}
*num += block_num;
old_val
}

#[no_mangle]
pub extern "C" fn memory_base() -> i32 {
unsafe { alloc_memory() as i32 }
}

#[no_mangle]
pub extern "C" fn memory_grow(block_num: i32) -> i32 {
inc_memory_block_num(block_num)
}
62 changes: 62 additions & 0 deletions examples/wasi-wrapper/rust/src/wasi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//! WASI Implementation based on Rust Std Lib
use crate::memory::get_memory_base;
use wasi::{Errno, ERRNO_SUCCESS};

#[repr(C)]
#[derive(Clone, Copy)]
struct IoVec {
iov_base: i32,
iov_len: i32,
}

type WasiError = i32;

const fn errno2i32(errno: &Errno) -> WasiError {
errno.raw() as WasiError
}

#[no_mangle]
pub extern "C" fn fd_write(
_fd: i32,
buf_iovec_addr: i32,
vec_len: i32,
size_addr: i32,
) -> WasiError {
let vec_len = vec_len as usize;
let memory_base = unsafe { get_memory_base() };
let iovec: *const IoVec = unsafe { memory_base.offset(buf_iovec_addr as isize) } as *const _;

let mut len = 0;
for i in 0..vec_len {
let IoVec { iov_base, iov_len } = unsafe { *iovec.add(i) };
let buf = unsafe { memory_base.add(iov_base as usize) };
let slice = unsafe { std::slice::from_raw_parts(buf, iov_len as usize) };

if slice.is_empty() {
continue;
}
print!("{}", String::from_utf8_lossy(slice));
len += slice.len();
}

unsafe {
let size_ptr = memory_base.offset(size_addr as isize);
*(size_ptr as *mut i32) = len as i32;
}
errno2i32(&ERRNO_SUCCESS)
}

#[no_mangle]
pub extern "C" fn environ_get(_env_addrs: i32, _env_buf_addr: i32) -> WasiError {
errno2i32(&ERRNO_SUCCESS)
}

#[no_mangle]
pub extern "C" fn environ_sizes_get(_env_count_addr: i32, _env_buf_size_addr: i32) -> WasiError {
errno2i32(&ERRNO_SUCCESS)
}

#[no_mangle]
pub extern "C" fn proc_exit(code: i32) -> ! {
std::process::exit(code);
}

0 comments on commit be66b30

Please sign in to comment.