Skip to content
This repository was archived by the owner on Mar 24, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
bf6a8dd
[packaging] use the new cargo-deb feature to rename the packages
acfoltzer Apr 15, 2019
3c12474
[lucet-wasi] use /dev/null by default for stdio handles
acfoltzer Apr 16, 2019
a77f2a1
[lucetc] add exact reserved size setting
acfoltzer Apr 16, 2019
608ca14
[lucet-wasi-sdk] refine API and be more verbose with commands
acfoltzer Apr 16, 2019
6627fd4
Merge branch 'master' into acf/terrarium-integration
acfoltzer Apr 24, 2019
577a541
[lucet-runtime] add macros for hostcalls and instance termination
acfoltzer Apr 24, 2019
2ae55a1
[lucet-runtime] RefCell-based `Vmctx` interface
acfoltzer Apr 26, 2019
ec39d3c
Merge remote-tracking branch 'origin/master' into acf/terrarium-integ…
acfoltzer Apr 26, 2019
153fe21
[lucet-runtime] silence panic output when terminating instances
acfoltzer Apr 26, 2019
d1ef54a
[lucet-wasi] expose `inherit_stdio` C API
acfoltzer Apr 26, 2019
64f8964
[lucet-benchmarks] add benchmarks for hostcall wrapper
acfoltzer Apr 26, 2019
7561b98
make `/host` ignore more specific, and check in missing test
acfoltzer Apr 26, 2019
0eb7b00
[lucet-benchmarks] hostcall overhead benches now take more arguments
acfoltzer Apr 26, 2019
c207a24
[lucet-runtime] always inline hostcall implementation; use `move`
acfoltzer Apr 26, 2019
a02912d
Merge remote-tracking branch 'origin/master' into acf/terrarium-integ…
acfoltzer Apr 26, 2019
e56975c
[lucet-runtime] tweak the terrarium-only vmctx testing interface
acfoltzer Apr 26, 2019
394494c
[lucet-runtime] remove obsolete testing functions
acfoltzer Apr 27, 2019
d65d75a
[lucet-runtime] 🐛 fix soundness of vmctx heap methods
acfoltzer Apr 29, 2019
031089e
[lucet-runtime] document the assumptions around the Vmctx heap view
acfoltzer Apr 29, 2019
476b4e2
Add another explicit page zeroing when MADV_DONTNEED is not enough
jedisct1 Apr 26, 2019
c20ca5b
Zero the heap only until the accessible size
jedisct1 Apr 26, 2019
0ffcccb
Update faerie to version 0.10 + support for extended ELF section
jedisct1 Apr 29, 2019
5ee3981
[lucet-runtime] make sigaltstack restore on a per-thread basis
acfoltzer Apr 30, 2019
1f25930
[lucet-runtime] improve clarity of Vmctx heap/globals views
acfoltzer Apr 30, 2019
ab496fb
Merge remote-tracking branch 'origin/master' into acf/terrarium-integ…
acfoltzer Apr 30, 2019
9f0eec1
Merge remote-tracking branch 'origin/master' into acf/terrarium-integ…
acfoltzer May 1, 2019
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
target/
*.rs.bk
*.pyc
host

# devenv-installed directory
/host

core.*
81 changes: 79 additions & 2 deletions benchmarks/lucet-benchmarks/src/modules.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use lucet_runtime::vmctx::lucet_vmctx;
use lucet_runtime::lucet_hostcalls;
use lucet_runtime::vmctx::{lucet_vmctx, Vmctx};
use lucet_runtime_internals::module::{HeapSpec, MockModuleBuilder, Module};
use lucet_wasi_sdk::{CompileOpts, Lucetc};
use lucetc::{Bindings, LucetcOpts};
Expand All @@ -11,7 +12,7 @@ fn wasi_bindings() -> Bindings {

pub fn compile_hello<P: AsRef<Path>>(so_file: P) {
let wasm_build = Lucetc::new(&["guests/hello.c"])
.print_output(true)
.with_print_output(true)
.with_cflag("-Wall")
.with_cflag("-Werror")
.with_bindings(wasi_bindings());
Expand Down Expand Up @@ -175,3 +176,79 @@ pub fn many_args_mock() -> Arc<dyn Module> {
.with_export_func(b"f", f as *const extern "C" fn())
.build()
}

pub fn hostcalls_mock() -> Arc<dyn Module> {
lucet_hostcalls! {
#[inline(never)]
#[no_mangle]
pub unsafe extern "C" fn hostcall_wrapped(
&mut vmctx,
x1: u64,
x2: u64,
x3: u64,
x4: u64,
x5: u64,
x6: u64,
x7: u64,
x8: u64,
x9: u64,
x10: u64,
x11: u64,
x12: u64,
x13: u64,
x14: u64,
x15: u64,
x16: u64,
) -> () {
vmctx.heap_mut()[0] =
(x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16)
as u8;
assert_eq!(vmctx.heap()[0], 136);
}
}

#[inline(never)]
#[no_mangle]
pub unsafe extern "C" fn hostcall_raw(
vmctx: *mut lucet_vmctx,
x1: u64,
x2: u64,
x3: u64,
x4: u64,
x5: u64,
x6: u64,
x7: u64,
x8: u64,
x9: u64,
x10: u64,
x11: u64,
x12: u64,
x13: u64,
x14: u64,
x15: u64,
x16: u64,
) {
let vmctx = Vmctx::from_raw(vmctx);
vmctx.heap_mut()[0] =
(x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16)
as u8;
assert_eq!(vmctx.heap()[0], 136);
}

unsafe extern "C" fn wrapped(vmctx: *mut lucet_vmctx) {
for _ in 0..1000 {
hostcall_wrapped(vmctx, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
}
}

unsafe extern "C" fn raw(vmctx: *mut lucet_vmctx) {
for _ in 0..1000 {
hostcall_raw(vmctx, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
}
}

MockModuleBuilder::new()
.with_export_func(b"wrapped", wrapped as *const extern "C" fn())
.with_export_func(b"raw", raw as *const extern "C" fn())
.build()
}
36 changes: 36 additions & 0 deletions benchmarks/lucet-benchmarks/src/seq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,40 @@ fn run_many_args<R: RegionCreate + 'static>(c: &mut Criterion) {
});
}

fn run_hostcall_wrapped<R: RegionCreate + 'static>(c: &mut Criterion) {
fn body(inst: &mut InstanceHandle) {
inst.run(b"wrapped", &[]).unwrap();
}

let module = hostcalls_mock();
let region = R::create(1, &Limits::default()).unwrap();

c.bench_function(&format!("run_hostcall_wrapped ({})", R::TYPE_NAME), move |b| {
b.iter_batched_ref(
|| region.new_instance(module.clone()).unwrap(),
|inst| body(inst),
criterion::BatchSize::PerIteration,
)
});
}

fn run_hostcall_raw<R: RegionCreate + 'static>(c: &mut Criterion) {
fn body(inst: &mut InstanceHandle) {
inst.run(b"raw", &[]).unwrap();
}

let module = hostcalls_mock();
let region = R::create(1, &Limits::default()).unwrap();

c.bench_function(&format!("run_hostcall_raw ({})", R::TYPE_NAME), move |b| {
b.iter_batched_ref(
|| region.new_instance(module.clone()).unwrap(),
|inst| body(inst),
criterion::BatchSize::PerIteration,
)
});
}

pub fn seq_benches<R: RegionCreate + 'static>(c: &mut Criterion) {
load_mkregion_and_instantiate::<R>(c);
instantiate::<R>(c);
Expand All @@ -362,4 +396,6 @@ pub fn seq_benches<R: RegionCreate + 'static>(c: &mut Criterion) {
run_fib::<R>(c);
run_hello::<R>(c);
run_many_args::<R>(c);
run_hostcall_wrapped::<R>(c);
run_hostcall_raw::<R>(c);
}
1 change: 1 addition & 0 deletions lucet-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ name = "lucet_runtime"
crate-type = ["rlib", "staticlib", "cdylib"]

[package.metadata.deb]
name = "fst-lucet-runtime"
maintainer = "Adam C. Foltzer <acfoltzer@fastly.com>"
depends = "$auto"
priority = "optional"
Expand Down
25 changes: 19 additions & 6 deletions lucet-runtime/lucet-runtime-internals/src/c_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ macro_rules! with_ffi_arcs {

/// Marker type for the `vmctx` pointer argument.
///
/// This type should only be used with [`Vmctx::from_raw()`](struct.Vmctx.html#method.from_raw).
/// This type should only be used with [`Vmctx::from_raw()`](struct.Vmctx.html#method.from_raw) or
/// the C API.
#[repr(C)]
pub struct lucet_vmctx {
_unused: [u8; 0],
Expand Down Expand Up @@ -194,8 +195,15 @@ pub type lucet_signal_handler = unsafe extern "C" fn(

pub type lucet_fatal_handler = unsafe extern "C" fn(inst: *mut lucet_instance);

pub struct CTerminationDetails {
pub details: *mut c_void,
}

unsafe impl Send for CTerminationDetails {}
unsafe impl Sync for CTerminationDetails {}

pub mod lucet_state {
use crate::c_api::lucet_val;
use crate::c_api::{lucet_val, CTerminationDetails};
use crate::instance::{State, TerminationDetails};
use crate::module::AddrDetails;
use crate::sysdeps::UContext;
Expand Down Expand Up @@ -243,15 +251,19 @@ pub mod lucet_state {
reason: lucet_terminated_reason::Signal,
provided: std::ptr::null_mut(),
},
TerminationDetails::GetEmbedCtx => lucet_terminated {
reason: lucet_terminated_reason::GetEmbedCtx,
TerminationDetails::CtxNotFound => lucet_terminated {
reason: lucet_terminated_reason::CtxNotFound,
provided: std::ptr::null_mut(),
},
TerminationDetails::BorrowError(_) => lucet_terminated {
reason: lucet_terminated_reason::BorrowError,
provided: std::ptr::null_mut(),
},
TerminationDetails::Provided(p) => lucet_terminated {
reason: lucet_terminated_reason::Provided,
provided: p
.downcast_ref()
.map(|v| *v)
.map(|CTerminationDetails { details }| *details)
.unwrap_or(std::ptr::null_mut()),
},
},
Expand Down Expand Up @@ -298,7 +310,8 @@ pub mod lucet_state {
#[derive(Clone, Copy)]
pub enum lucet_terminated_reason {
Signal,
GetEmbedCtx,
CtxNotFound,
BorrowError,
Provided,
}

Expand Down
30 changes: 20 additions & 10 deletions lucet-runtime/lucet-runtime-internals/src/embed_ctx.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use std::any::{Any, TypeId};
use std::cell::{BorrowError, BorrowMutError, Ref, RefCell, RefMut};
use std::collections::HashMap;

/// A map that holds at most one value of any type.
///
/// This is similar to the type provided by the `anymap` crate, but we can get away with simpler
/// types on the methods due to our more specialized use case.
pub struct CtxMap {
map: HashMap<TypeId, Box<dyn Any>>,
map: HashMap<TypeId, RefCell<Box<dyn Any>>>,
}

impl CtxMap {
Expand All @@ -18,25 +19,33 @@ impl CtxMap {
self.map.contains_key(&TypeId::of::<T>())
}

pub fn get<T: Any>(&self) -> Option<&T> {
pub fn try_get<T: Any>(&self) -> Option<Result<Ref<T>, BorrowError>> {
self.map.get(&TypeId::of::<T>()).map(|x| {
x.downcast_ref::<T>()
.expect("value stored with TypeId::of::<T> is always type T")
x.try_borrow().map(|r| {
Ref::map(r, |b| {
b.downcast_ref::<T>()
.expect("value stored with TypeId::of::<T> is always type T")
})
})
})
}

pub fn get_mut<T: Any>(&mut self) -> Option<&mut T> {
pub fn try_get_mut<T: Any>(&mut self) -> Option<Result<RefMut<T>, BorrowMutError>> {
self.map.get_mut(&TypeId::of::<T>()).map(|x| {
x.downcast_mut::<T>()
.expect("value stored with TypeId::of::<T> is always type T")
x.try_borrow_mut().map(|r| {
RefMut::map(r, |b| {
b.downcast_mut::<T>()
.expect("value stored with TypeId::of::<T> is always type T")
})
})
})
}

pub fn insert<T: Any>(&mut self, x: T) -> Option<T> {
self.map
.insert(TypeId::of::<T>(), Box::new(x) as Box<dyn Any>)
.insert(TypeId::of::<T>(), RefCell::new(Box::new(x) as Box<dyn Any>))
.map(|x_prev| {
*x_prev
*(x_prev.into_inner())
.downcast::<T>()
.expect("value stored with TypeId::of::<T> is always type T")
})
Expand All @@ -50,7 +59,8 @@ impl CtxMap {

pub fn remove<T: Any>(&mut self) -> Option<T> {
self.map.remove(&TypeId::of::<T>()).map(|x| {
*x.downcast::<T>()
*(x.into_inner())
.downcast::<T>()
.expect("value stored with TypeId::of::<T> is always type T")
})
}
Expand Down
92 changes: 92 additions & 0 deletions lucet-runtime/lucet-runtime-internals/src/hostcall_macros.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/// The macro that surrounds definitions of Lucet hostcalls in Rust.
///
/// It is important to use this macro for hostcalls, rather than exporting them directly, as it
/// installs unwind protection that prevents panics from unwinding into the guest stack.
///
/// Since this is not yet a proc macro, the syntax is unfortunately fairly brittle. The functions it
/// encloses must be of the form:
///
/// ```ignore
/// #[$attr1]
/// #[$attr2]
/// ... // any number of attributes are supported; in most cases you will want `#[no_mangle]`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would be a case where would not want #[no_mangle]?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's useful when we're defining hostcalls for a MockModuleBuilder, so hostcalls for different tests don't interfere with each other.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, these aren't technically hostcalls, but are the fake guest functions that go into the module

/// pub unsafe extern "C" fn $name( // must be `pub unsafe extern "C"`
/// &mut $vmctx,
/// $arg1: $arg1_ty,
/// $arg2: $arg2_ty,
/// ... , // trailing comma must always be present
/// ) -> $ret_ty { // return type must always be present even if it is `()`
/// // body
/// }
/// ```
#[macro_export]
macro_rules! lucet_hostcalls {
{
$(
$(#[$attr:meta])*
pub unsafe extern "C" fn $name:ident(
&mut $vmctx:ident
$(, $arg:ident : $arg_ty:ty )*,
) -> $ret_ty:ty {
$($body:tt)*
}
)*
} => {
$(
$(#[$attr])*
pub unsafe extern "C" fn $name(
vmctx_raw: *mut $crate::vmctx::lucet_vmctx,
$( $arg: $arg_ty ),*
) -> $ret_ty {
#[inline(always)]
unsafe fn hostcall_impl(
$vmctx: &mut $crate::vmctx::Vmctx,
$( $arg : $arg_ty ),*
) -> $ret_ty {
$($body)*
}
let res = std::panic::catch_unwind(move || {
hostcall_impl(&mut $crate::vmctx::Vmctx::from_raw(vmctx_raw), $( $arg ),*)
});
match res {
Ok(res) => res,
Err(e) => {
if let Some(details) = e.downcast_ref::<$crate::instance::TerminationDetails>() {
let mut vmctx = $crate::vmctx::Vmctx::from_raw(vmctx_raw);
vmctx.terminate_no_unwind(details.clone());
} else {
std::panic::resume_unwind(e);
}
}
}
}
)*
}
}

/// Terminate an instance from within a hostcall, returning an optional value as an error.
///
/// Use this instead of `panic!` when you want the instance to terminate, but not the entire host
/// program. Like `panic!`, you can pass a format string with arguments, a value that implements
/// `Any`, or nothing to return a default message.
///
/// Upon termination, the call to `Instance::run()` will return with an
/// `Err(Error::RuntimeTerminated)` value containing the value you pass to this macro.
///
/// This macro safely unwinds the hostcall stack out to the entrypoint of the hostcall, so any
/// resources that may have been acquired will be properly dropped.
#[macro_export]
macro_rules! lucet_hostcall_terminate {
() => {
lucet_hostcall_terminate!("lucet_hostcall_terminate")
};
( $payload:expr ) => {
panic!($crate::instance::TerminationDetails::provide($payload))
};
( $payload:expr, ) => {
lucet_hostcall_terminate!($payload)
};
( $fmt:expr, $($arg:tt)+ ) => {
lucet_hostcall_terminate!(format!($fmt, $($arg),+))
};
}
Loading