#![deny(missing_docs)]
#![deny(clippy::all)]
#![warn(rust_2018_idioms)]
#![feature(core_ffi_c)]
mod config;
mod loader;
use config::Config;
use loader::Loader;
use std::fs::File;
use std::io::Read;
use std::mem::forget;
use std::os::unix::io::FromRawFd;
use std::os::unix::prelude::AsRawFd;
use clap::Parser;
use std::path::PathBuf;
#[no_mangle]
pub extern "C" fn __set_thread_area(p: *mut std::ffi::c_void) -> std::ffi::c_int {
let mut rax: usize = 0;
if unsafe { core::arch::x86_64::__cpuid(7).ebx } & 1 == 1 {
unsafe {
std::arch::asm!("wrfsbase {}", in(reg) p);
}
} else {
const ARCH_SET_FS: std::ffi::c_int = 0x1002;
unsafe {
std::arch::asm!(
"syscall",
inlateout("rax") libc::SYS_arch_prctl => rax,
in("rdi") ARCH_SET_FS,
in("rsi") p,
lateout("rcx") _, lateout("r11") _, );
}
}
rax as _
}
#[derive(Parser, Debug)]
struct Args {
#[clap(short, long, value_name = "MODULE", parse(from_os_str))]
pub module: Option<PathBuf>,
#[clap(short, long, value_name = "CONFIG", parse(from_os_str))]
pub config: Option<PathBuf>,
}
fn main() -> anyhow::Result<()> {
env_logger::Builder::from_default_env().init();
let args = Args::parse();
let mut config = match (args.module, args.config) {
(Some(module), Some(config)) => {
let module = File::open(&module).expect("unable to open file");
let config = File::open(&config).expect("unable to open file");
assert_eq!(3, module.as_raw_fd());
forget(module); config
}
(None, None) => unsafe { File::from_raw_fd(4) },
_ => panic!("this configuration is unsupported"),
};
let mut buffer = String::new();
let config: Config = match config.read_to_string(&mut buffer) {
Ok(..) => toml::from_str(&buffer)?,
Err(..) => Config::default(),
};
let configured = Loader::from(config);
let requested = configured.next()?;
let attested = requested.next()?;
let acquired = attested.next()?;
let compiled = acquired.next()?;
let connected = compiled.next()?;
let completed = connected.next()?;
drop(completed);
Ok(())
}
#[cfg(test)]
mod test {
use crate::loader::Loader;
const NO_EXPORT_WAT: &str = r#"(module
(memory (export "") 1)
)"#;
const RETURN_1_WAT: &str = r#"(module
(func (export "") (result i32) i32.const 1)
)"#;
const HELLO_WASI_WAT: &str = r#"(module
(import "wasi_snapshot_preview1" "proc_exit"
(func $__wasi_proc_exit (param i32)))
(import "wasi_snapshot_preview1" "fd_write"
(func $__wasi_fd_write (param i32 i32 i32 i32) (result i32)))
(func $_start
(i32.store (i32.const 24) (i32.const 14))
(i32.store (i32.const 20) (i32.const 0))
(block
(br_if 0
(call $__wasi_fd_write
(i32.const 1)
(i32.const 20)
(i32.const 1)
(i32.const 16)))
(br_if 0 (i32.ne (i32.load (i32.const 16)) (i32.const 14)))
(br 1)
)
(call $__wasi_proc_exit (i32.const 1))
)
(memory 1)
(export "memory" (memory 0))
(export "_start" (func $_start))
(data (i32.const 0) "Hello, world!\0a")
)"#;
#[test]
fn workload_run_return_1() {
let bytes = wat::parse_str(RETURN_1_WAT).expect("error parsing wat");
let results: Vec<i32> = Loader::run(&bytes)
.unwrap()
.iter()
.map(wasmtime::Val::unwrap_i32)
.collect();
assert_eq!(results, vec![1]);
}
#[test]
fn workload_run_no_export() {
let bytes = wat::parse_str(NO_EXPORT_WAT).expect("error parsing wat");
match Loader::run(&bytes) {
Err(..) => (),
_ => panic!("unexpected success"),
}
}
#[test]
fn workload_run_hello_wasi() {
let bytes = wat::parse_str(HELLO_WASI_WAT).expect("error parsing wat");
let values = Loader::run(&bytes).unwrap();
assert_eq!(values.len(), 0);
}
}