Skip to content
This repository was archived by the owner on Mar 24, 2022. It is now read-only.
Draft
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions lucet-runtime/Cargo.toml
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ lucet-runtime-internals = { path = "lucet-runtime-internals", version = "=0.7.0-
lucet-module = { path = "../lucet-module", version = "=0.7.0-dev" }
num-traits = "0.2"
num-derive = "0.3.0"
cfg-if = "*"

[dev-dependencies]
byteorder = "1.2"
2 changes: 2 additions & 0 deletions lucet-runtime/include/lucet_types.h
Original file line number Diff line number Diff line change
@@ -16,6 +16,8 @@
#include <ucontext.h>
#endif

#include <signal.h>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is only present because I was wildly flailing around trying to fix header issues that were caused by fiddling with an include path I shouldn't have. I left it because lucet_types.h references siginfo_t without including signal.h. This isn't an issue because lucet.h includes signal.h before lucet_types.h, but someone using lucet_types.h individually for some reason would be in for a surprise.

... I ought to make this a PR on its own.


enum lucet_error {
lucet_error_ok,
lucet_error_invalid_argument,
1 change: 1 addition & 0 deletions lucet-runtime/lucet-runtime-internals/Cargo.toml
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ edition = "2018"
lucet-module = { path = "../../lucet-module", version = "=0.7.0-dev" }
lucet-runtime-macros = { path = "../lucet-runtime-macros", version = "=0.7.0-dev" }

cfg-if = "*"
anyhow = "1.0"
bitflags = "1.0"
bincode = "1.1.4"
10 changes: 9 additions & 1 deletion lucet-runtime/lucet-runtime-internals/build.rs
Original file line number Diff line number Diff line change
@@ -3,8 +3,16 @@ use std::fs::File;
use std::path::Path;

fn main() {
let context_asm_arch = match env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str() {
"x86_64" => "x86_64",
"x86" => "i686",
arch => {
panic!("unsupported architecture {}", arch);
}
};

cc::Build::new()
.file("src/context/context_asm.S")
.file(&format!("src/context/sysdep/{}/context_asm.S", context_asm_arch))
.compile("context_context_asm");
cc::Build::new()
.file("src/instance/siginfo_ext.c")
45 changes: 38 additions & 7 deletions lucet-runtime/lucet-runtime-internals/src/alloc/mod.rs
Original file line number Diff line number Diff line change
@@ -339,7 +339,8 @@ impl Alloc {
/// Since the stack grows down, `alloc.stack_mut()[0]` is the top of the stack, and
/// `alloc.stack_mut()[alloc.limits.stack_size - 1]` is the last word at the bottom of the
/// stack.
pub unsafe fn stack_u64_mut(&mut self) -> &mut [u64] {
#[cfg(target_pointer_width = "64")]
pub unsafe fn stack_words_mut(&mut self) -> &mut [u64] {
assert!(
self.slot().stack as usize % 8 == 0,
"stack is 8-byte aligned"
@@ -353,6 +354,21 @@ impl Alloc {
self.slot().limits.stack_size / 8,
)
}
#[cfg(target_pointer_width = "32")]
pub unsafe fn stack_words_mut(&mut self) -> &mut [u32] {
assert!(
self.slot().stack as usize % 4 == 0,
"stack is 4-byte aligned"
);
assert!(
self.slot().limits.stack_size % 4 == 0,
"stack size is multiple of 4-bytes"
);
std::slice::from_raw_parts_mut(
self.slot().stack as *mut u32,
self.slot().limits.stack_size / 4,
)
}

/// Return the globals as a slice.
pub unsafe fn globals(&self) -> &[GlobalValue] {
@@ -454,12 +470,27 @@ pub const DEFAULT_SIGNAL_STACK_SIZE: usize = {

impl Limits {
pub const fn default() -> Limits {
Limits {
heap_memory_size: 16 * 64 * 1024,
heap_address_space_size: 0x0002_0000_0000,
stack_size: 128 * 1024,
globals_size: 4096,
signal_stack_size: DEFAULT_SIGNAL_STACK_SIZE,
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(target_pointer_width = "64")] {
Limits {
heap_memory_size: 16 * 64 * 1024,
heap_address_space_size: 0x0002_0000_0000,
stack_size: 128 * 1024,
globals_size: 4096,
signal_stack_size: DEFAULT_SIGNAL_STACK_SIZE,
}
} else if #[cfg(target_pointer_width = "32")] {
Limits {
heap_memory_size: 16 * 64 * 1024,
heap_address_space_size: 0xffff_ffff,
stack_size: 128 * 1024,
globals_size: 4096,
signal_stack_size: DEFAULT_SIGNAL_STACK_SIZE,
}
} else {
panic!("unsupported architecture!");
}
}
}
}
4 changes: 2 additions & 2 deletions lucet-runtime/lucet-runtime-internals/src/alloc/tests.rs
Original file line number Diff line number Diff line change
@@ -650,7 +650,7 @@ macro_rules! alloc_tests {
unsafe {
let heap_ptr = inst.alloc_mut().heap_mut().as_ptr() as *mut c_void;
let mut child = ContextHandle::create_and_init(
inst.alloc_mut().stack_u64_mut(),
inst.alloc_mut().stack_words_mut(),
heap_touching_child as usize,
&[Val::CPtr(heap_ptr)],
)
@@ -699,7 +699,7 @@ macro_rules! alloc_tests {
unsafe {
let heap_ptr = inst.alloc_mut().heap_mut().as_ptr() as *mut c_void;
let mut child = ContextHandle::create_and_init(
inst.alloc_mut().stack_u64_mut(),
inst.alloc_mut().stack_words_mut(),
stack_pattern_child as usize,
&[Val::CPtr(heap_ptr)],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod val;
318 changes: 318 additions & 0 deletions lucet-runtime/lucet-runtime-internals/src/archdeps/i686/val.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
//! Typed values for passing into and returning from sandboxed
//! programs.
use libc::c_void;
use std::arch::x86::{
__m128, _mm_castpd_ps, _mm_castps_pd, _mm_load_pd1, _mm_load_ps1, _mm_setzero_ps,
_mm_storeu_pd, _mm_storeu_ps,
};

use lucet_module::ValueType;

impl Val {
pub fn value_type(&self) -> ValueType {
match self {
// USize, ISize, and CPtr are all as fits for definitions on the target architecture
// (wasm) which is all 32-bit.
Val::USize(_) | Val::ISize(_) | Val::CPtr(_) => ValueType::I32,
Val::GuestPtr(_) => ValueType::I32,
Val::I8(_) | Val::U8(_) | Val::I16(_) | Val::U16(_) | Val::I32(_) | Val::U32(_) => {
ValueType::I32
}
Val::I64(_) | Val::U64(_) => ValueType::I64,
Val::Bool(_) => ValueType::I32,
Val::F32(_) => ValueType::F32,
Val::F64(_) => ValueType::F64,
}
}
}

/// Typed values used for passing arguments into guest functions.
#[derive(Clone, Copy, Debug)]
pub enum Val {
CPtr(*const c_void),
/// A WebAssembly linear memory address
GuestPtr(u32),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
USize(usize),
ISize(isize),
Bool(bool),
F32(f32),
F64(f64),
}

// the pointer variant is just a wrapper; the caller will know they're still responsible for their
// safety
unsafe impl Send for Val {}
unsafe impl Sync for Val {}

impl<T> From<*const T> for Val {
fn from(x: *const T) -> Val {
Val::CPtr(x as *const c_void)
}
}

impl<T> From<*mut T> for Val {
fn from(x: *mut T) -> Val {
Val::CPtr(x as *mut c_void)
}
}

macro_rules! impl_from_scalars {
( { $( $ctor:ident : $ty:ty ),* } ) => {
$(
impl From<$ty> for Val {
fn from(x: $ty) -> Val {
Val::$ctor(x)
}
}
)*
};
}

// Since there is overlap in these enum variants, we can't have instances for all of them, such as
// GuestPtr
impl_from_scalars!({
U8: u8,
U16: u16,
U32: u32,
U64: u64,
I8: i8,
I16: i16,
I32: i32,
I64: i64,
USize: usize,
ISize: isize,
Bool: bool,
F32: f32,
F64: f64
});

/// Register representation of `Val`.
///
/// When mapping `Val`s to x86_64 registers, we map floating point
/// values into the SSE registers _xmmN_, and all other values into
/// general-purpose (integer) registers.
pub enum RegVal {
GpReg(u32),
FpReg(__m128),
}

/// Convert a `Val` to its representation when stored in an
/// argument register.
pub fn val_to_reg(val: &Val) -> RegVal {
use self::RegVal::*;
use self::Val::*;
match *val {
CPtr(v) => GpReg(v as u32),
GuestPtr(v) => GpReg(v as u32),
U8(v) => GpReg(v as u32),
U16(v) => GpReg(v as u32),
U32(v) => GpReg(v as u32),
U64(v) => GpReg(v as u32), // TODO: aaaaAAAAA
I8(v) => GpReg(v as u32),
I16(v) => GpReg(v as u32),
I32(v) => GpReg(v as u32),
I64(v) => GpReg(v as u32), // TODO: aaaaAAAA
USize(v) => GpReg(v as u32),
ISize(v) => GpReg(v as u32),
Bool(false) => GpReg(0u32),
Bool(true) => GpReg(1u32),
Val::F32(v) => FpReg(unsafe { _mm_load_ps1(&v as *const f32) }),
Val::F64(v) => FpReg(unsafe { _mm_castpd_ps(_mm_load_pd1(&v as *const f64)) }),
}
}

/// Convert a `Val` to its representation when spilled onto the
/// stack.
pub fn val_to_stack(val: &Val) -> u32 {
use self::Val::*;
match *val {
CPtr(v) => v as u32,
GuestPtr(v) => v as u32,
U8(v) => v as u32,
U16(v) => v as u32,
U32(v) => v as u32,
U64(v) => v as u32, // TODO: aaaAAA
I8(v) => v as u32,
I16(v) => v as u32,
I32(v) => v as u32,
I64(v) => v as u32, // TODO: aaaAAA
USize(v) => v as u32,
ISize(v) => v as u32,
Bool(false) => 0u32,
Bool(true) => 1u32,
F32(v) => v.to_bits(),
F64(v) => v.to_bits() as u32, // TODO: aaaAAA
}
}

/// A value returned by a guest function.
///
/// Since the Rust type system cannot know the type of the returned value, the user must use the
/// appropriate `From` implementation or `as_T` method.
#[derive(Clone, Copy, Debug)]
pub struct UntypedRetVal {
fp: __m128,
gp: u32,
}

impl std::fmt::Display for UntypedRetVal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<untyped return value>")
}
}

impl UntypedRetVal {
pub(crate) fn new(gp: u32, fp: __m128) -> UntypedRetVal {
UntypedRetVal { gp, fp }
}
}

impl From<RegVal> for UntypedRetVal {
fn from(reg: RegVal) -> UntypedRetVal {
match reg {
RegVal::GpReg(r) => UntypedRetVal::new(r, unsafe { _mm_setzero_ps() }),
RegVal::FpReg(r) => UntypedRetVal::new(0, r),
}
}
}

impl<T: Into<Val>> From<T> for UntypedRetVal {
fn from(v: T) -> UntypedRetVal {
val_to_reg(&v.into()).into()
}
}

macro_rules! impl_from_fp {
( $ty:ty, $f:ident, $as:ident ) => {
impl From<UntypedRetVal> for $ty {
fn from(retval: UntypedRetVal) -> $ty {
$f(retval.fp)
}
}

impl From<&UntypedRetVal> for $ty {
fn from(retval: &UntypedRetVal) -> $ty {
$f(retval.fp)
}
}

impl UntypedRetVal {
pub fn $as(&self) -> $ty {
$f(self.fp)
}
}
};
}

impl_from_fp!(f32, __m128_as_f32, as_f32);
impl_from_fp!(f64, __m128_as_f64, as_f64);

macro_rules! impl_from_gp {
( $ty:ty, $as:ident ) => {
impl From<UntypedRetVal> for $ty {
fn from(retval: UntypedRetVal) -> $ty {
retval.gp as $ty
}
}

impl From<&UntypedRetVal> for $ty {
fn from(retval: &UntypedRetVal) -> $ty {
retval.gp as $ty
}
}

impl UntypedRetVal {
pub fn $as(&self) -> $ty {
self.gp as $ty
}
}
};
}

impl_from_gp!(u8, as_u8);
impl_from_gp!(u16, as_u16);
impl_from_gp!(u32, as_u32);
impl_from_gp!(u64, as_u64);

impl_from_gp!(i8, as_i8);
impl_from_gp!(i16, as_i16);
impl_from_gp!(i32, as_i32);
impl_from_gp!(i64, as_i64);

impl From<UntypedRetVal> for bool {
fn from(retval: UntypedRetVal) -> bool {
retval.gp != 0
}
}

impl From<&UntypedRetVal> for bool {
fn from(retval: &UntypedRetVal) -> bool {
retval.gp != 0
}
}

impl UntypedRetVal {
pub fn as_bool(&self) -> bool {
self.gp != 0
}

pub fn as_ptr<T>(&self) -> *const T {
self.gp as *const T
}

pub fn as_mut<T>(&self) -> *mut T {
self.gp as *mut T
}
}

impl Default for UntypedRetVal {
fn default() -> UntypedRetVal {
let fp = unsafe { _mm_setzero_ps() };
UntypedRetVal { fp, gp: 0 }
}
}

pub trait UntypedRetValInternal {
fn fp(&self) -> __m128;
fn gp(&self) -> u32;
}

impl UntypedRetValInternal for UntypedRetVal {
fn fp(&self) -> __m128 {
self.fp
}

fn gp(&self) -> u32 {
self.gp
}
}

// Helpers that we might want to put in a utils module someday

/// Interpret the contents of a `__m128` register as an `f32`.
pub fn __m128_as_f32(v: __m128) -> f32 {
let mut out: [f32; 4] = [0.0; 4];
unsafe {
_mm_storeu_ps(&mut out[0] as *mut f32, v);
}
out[0]
}

/// Interpret the contents of a `__m128` register as an `f64`.
pub fn __m128_as_f64(v: __m128) -> f64 {
let mut out: [f64; 2] = [0.0; 2];
unsafe {
let vd = _mm_castps_pd(v);
_mm_storeu_pd(&mut out[0] as *mut f64, vd);
}
out[0]
}
21 changes: 21 additions & 0 deletions lucet-runtime/lucet-runtime-internals/src/archdeps/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use cfg_if::cfg_if;

cfg_if! {
if #[cfg(target_arch = "x86")] {
pub mod i686;
pub use i686 as arch_impl;
} else if #[cfg(target_arch = "x86_64")] {
pub mod x86_64;
pub use x86_64 as arch_impl;
}
}

pub mod val {
use crate::archdeps::arch_impl;
pub use arch_impl::val::Val as Val;
pub use arch_impl::val::RegVal as RegVal;
pub use arch_impl::val::UntypedRetVal as UntypedRetVal;
pub(crate) use arch_impl::val::UntypedRetValInternal as UntypedRetValInternal;
pub use arch_impl::val::val_to_reg as val_to_reg;
pub use arch_impl::val::val_to_stack as val_to_stack;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub use val;
27 changes: 21 additions & 6 deletions lucet-runtime/lucet-runtime-internals/src/c_api.rs
Original file line number Diff line number Diff line change
@@ -664,12 +664,27 @@ pub mod lucet_val {
fp: [0; 16],
gp: [0; 8],
};
unsafe {
core::arch::x86_64::_mm_storeu_ps(
v.fp.as_mut().as_mut_ptr() as *mut f32,
retval.fp(),
);
*(v.gp.as_mut().as_mut_ptr() as *mut u64) = retval.gp();
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(target_arch = "x86")] {
unsafe {
core::arch::x86::_mm_storeu_ps(
v.fp.as_mut().as_mut_ptr() as *mut f32,
retval.fp(),
);
*(v.gp.as_mut().as_mut_ptr() as *mut u32) = retval.gp();
}
} else if #[cfg(target_arch = "x86_64")] {
unsafe {
core::arch::x86_64::_mm_storeu_ps(
v.fp.as_mut().as_mut_ptr() as *mut f32,
retval.fp(),
);
*(v.gp.as_mut().as_mut_ptr() as *mut u64) = retval.gp();
}
} else {
panic!("unsupported architecture!");
}
}
v
}
738 changes: 6 additions & 732 deletions lucet-runtime/lucet-runtime-internals/src/context/mod.rs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
/*
The lucet_context_swap function is taken from Xudong Huang's
generator-rs project. Its MIT license is provided below.
Copyright (c) 2017 Xudong Huang
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

.text
.globl lucet_context_bootstrap
#ifdef __ELF__
.type lucet_context_bootstrap,@function
#else
.globl _lucet_context_bootstrap
#endif
.align 16
lucet_context_bootstrap:
_lucet_context_bootstrap:
// Move each of the argument values into the corresponding call
// argument register.
// pop %r9
// pop %r8
pop %ecx
pop %edx
pop %esi
pop %edi

/* the next thing on the stack is the guest function - return to it */
ret
#ifdef __ELF__
.size lucet_context_bootstrap,.-lucet_context_bootstrap
#endif

.text
.globl lucet_context_backstop
#ifdef __ELF__
.type lucet_context_backstop,@function
#else
.globl _lucet_context_backstop
#endif
.align 16
lucet_context_backstop:
_lucet_context_backstop:
// Note that `rbp` here really has no relation to any stack!
// Instead, it's a pointer to the guest context.
mov (10*8 + 8*16 + 8*2 + 16)(%ebp), %edi /* load the parent context to forward values in return value registers */
mov %eax, (10*8 + 8*16 + 8*0)(%ebp) /* store return values before swapping back -- offset is offsetof(struct lucet_context, retvals) */
mov %edx, (10*8 + 8*16 + 8*1)(%ebp)
movdqu %xmm0, (10*8 + 8*16 + 8*2)(%ebp) /* floating-point return value */

// load `backstop_callback`, but skip calling it if it's null
mov (10*8 + 8*16 + 8*2 + 16 + 8)(%ebp), %esi
test %esi, %esi
#ifdef __ELF__
jz no_backstop_callback@PLT
#else
jz no_backstop_callback
#endif

// load `callback_data`, arg 1
mov (10*8 + 8*16 + 8*2 + 16 + 8 + 8)(%ebp), %edi
// call `backstop_callback`
call *%esi

no_backstop_callback:
mov %ebp, %edi /* load the guest context to the "from" argument */
mov (10*8 + 8*16 + 8*2 + 16)(%ebp), %esi /* load the parent context to the "to" argument */

#ifdef __ELF__
jmp lucet_context_swap@PLT
#else
jmp lucet_context_swap
#endif
#ifdef __ELF__
.size lucet_context_backstop,.-lucet_context_backstop
#endif

.text
.globl lucet_context_swap
#ifdef __ELF__
.type lucet_context_swap,@function
#else
.globl _lucet_context_swap
#endif
.align 16
lucet_context_swap:
_lucet_context_swap:
// store everything in offsets from rdi (1st arg)
mov %ebx, (0*8)(%edi)
mov %esp, (1*8)(%edi)
mov %ebp, (2*8)(%edi)
mov %edi, (3*8)(%edi)
// mov %r12, (4*8)(%rdi)
// mov %r13, (5*8)(%rdi)
// mov %r14, (6*8)(%rdi)
// mov %r15, (7*8)(%rdi)
mov %esi, (8*8)(%edi)

movdqu %xmm0, (10*8 + 0*16)(%edi)
movdqu %xmm1, (10*8 + 1*16)(%edi)
movdqu %xmm2, (10*8 + 2*16)(%edi)
movdqu %xmm3, (10*8 + 3*16)(%edi)
movdqu %xmm4, (10*8 + 4*16)(%edi)
movdqu %xmm5, (10*8 + 5*16)(%edi)
movdqu %xmm6, (10*8 + 6*16)(%edi)
movdqu %xmm7, (10*8 + 7*16)(%edi)

// load everything from offsets from rsi (2nd arg)
mov (0*8)(%esi), %ebx
mov (1*8)(%esi), %esp
mov (2*8)(%esi), %ebp
mov (3*8)(%esi), %edi
// mov (4*8)(%rsi), %r12
// mov (5*8)(%rsi), %r13
// mov (6*8)(%rsi), %r14
// mov (7*8)(%rsi), %r15

movdqu (10*8 + 0*16)(%esi), %xmm0
movdqu (10*8 + 1*16)(%esi), %xmm1
movdqu (10*8 + 2*16)(%esi), %xmm2
movdqu (10*8 + 3*16)(%esi), %xmm3
movdqu (10*8 + 4*16)(%esi), %xmm4
movdqu (10*8 + 5*16)(%esi), %xmm5
movdqu (10*8 + 6*16)(%esi), %xmm6
movdqu (10*8 + 7*16)(%esi), %xmm7

// restore rsi when we're done with the context pointer
mov (8*8)(%esi), %esi

ret
#ifdef __ELF__
.size lucet_context_swap,.-lucet_context_swap
#endif

.text
.globl lucet_context_set
#ifdef __ELF__
.type lucet_context_set,@function
#else
.globl _lucet_context_set
#endif
.align 16
lucet_context_set:
_lucet_context_set:
// load everything from offsets from rdi (1st arg)
mov (0*8)(%edi), %ebx
mov (1*8)(%edi), %esp
mov (2*8)(%edi), %ebp
// mov (4*8)(%edi), %r12
// mov (5*8)(%edi), %r13
// mov (6*8)(%edi), %r14
// mov (7*8)(%edi), %r15
mov (8*8)(%edi), %esi

movdqu (10*8 + 0*16)(%edi), %xmm0
movdqu (10*8 + 1*16)(%edi), %xmm1
movdqu (10*8 + 2*16)(%edi), %xmm2
movdqu (10*8 + 3*16)(%edi), %xmm3
movdqu (10*8 + 4*16)(%edi), %xmm4
movdqu (10*8 + 5*16)(%edi), %xmm5
movdqu (10*8 + 6*16)(%edi), %xmm6
movdqu (10*8 + 7*16)(%edi), %xmm7

// load rdi from itself last
mov (3*8)(%edi), %edi
ret
#ifdef __ELF__
.size lucet_context_set,.-lucet_context_set
#endif

.text
.globl lucet_context_activate
#ifdef __ELF__
.type lucet_context_activate,@function
#else
.globl _lucet_context_activate
#endif
.align 16
// `lucet_context_activate` is essentially a function with three arguments:
// * rdi: the data for the entry callback.
// * rsi: the address of the entry callback.
// * rbx: the address of the guest code to execute.
//
// See `lucet_runtime_internals::context::lucet_context_activate` for more info.
//
// Note that `rbx` is used to store the address of the guest code because it is
// a callee-saved register in the System V calling convention. It is also a
// non-violatile register on Windows, which is a nice benefit.
lucet_context_activate:
_lucet_context_activate:
// First, we call the entry callback whose address is stored in `rsi`,
// passing along the value of `rdi` as the first argument.
call *%esi
// Now, jump to the guest code at the address in `rbx`.
jmp *%ebx
#ifdef __ELF__
.size lucet_context_activate,.-lucet_context_activate
#endif

/* Mark that we don't need executable stack. */
#if defined(__linux__) && defined(__ELF__)
.section .note.GNU-stack,"",%progbits
#endif
730 changes: 730 additions & 0 deletions lucet-runtime/lucet-runtime-internals/src/context/sysdep/i686/mod.rs

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions lucet-runtime/lucet-runtime-internals/src/context/sysdep/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use cfg_if::cfg_if;

cfg_if! {
if #[cfg(target_arch = "x86")] {
pub mod i686;
pub use i686 as arch_impl;
} else if #[cfg(target_arch = "x86_64")] {
pub mod x86_64;
pub use x86_64 as arch_impl;
}
}
732 changes: 732 additions & 0 deletions lucet-runtime/lucet-runtime-internals/src/context/sysdep/x86_64/mod.rs

Large diffs are not rendered by default.

45 changes: 33 additions & 12 deletions lucet-runtime/lucet-runtime-internals/src/instance.rs
Original file line number Diff line number Diff line change
@@ -977,7 +977,7 @@ impl Instance {

let self_ptr = self as *mut _;
Context::init_with_callback(
unsafe { self.alloc.stack_u64_mut() },
unsafe { self.alloc.stack_words_mut() },
&mut self.ctx,
execution::exit_guest_region,
self_ptr,
@@ -1009,17 +1009,38 @@ impl Instance {
/// `execution::enter_guest_region` for more info.
// TODO KTM 2020-03-13: This should be a method on `Context`.
fn install_activator(&mut self) {
unsafe {
// Get a raw pointer to the top of the guest stack.
let top_of_stack = self.ctx.gpr.rsp as *mut u64;
// Move the guest code address to rbx, and then put the address of the activation thunk
// at the top of the stack, so that we will start execution at `enter_guest_region`.
self.ctx.gpr.rbx = *top_of_stack;
*top_of_stack = crate::context::lucet_context_activate as u64;
// Pass a pointer to our guest-side entrypoint bootstrap code in `rsi`, and then put
// its first argument (a raw pointer to `self`) in `rdi`.
self.ctx.gpr.rsi = execution::enter_guest_region as u64;
self.ctx.gpr.rdi = self.ctx.callback_data_ptr() as u64;
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(target_arch = "x86")] {
unsafe {
// Get a raw pointer to the top of the guest stack.
let top_of_stack = self.ctx.gpr.esp as *mut u32;
// Move the guest code address to rbx, and then put the address of the activation thunk
// at the top of the stack, so that we will start execution at `enter_guest_region`.
self.ctx.gpr.ebx = *top_of_stack;
*top_of_stack = crate::context::lucet_context_activate as u32;
// Pass a pointer to our guest-side entrypoint bootstrap code in `rsi`, and then put
// its first argument (a raw pointer to `self`) in `rdi`.
self.ctx.gpr.esi = execution::enter_guest_region as u32;
self.ctx.gpr.edi = self.ctx.callback_data_ptr() as u32;
}

} else if #[cfg(target_arch = "x86_64")] {
unsafe {
// Get a raw pointer to the top of the guest stack.
let top_of_stack = self.ctx.gpr.rsp as *mut u64;
// Move the guest code address to rbx, and then put the address of the activation thunk
// at the top of the stack, so that we will start execution at `enter_guest_region`.
self.ctx.gpr.rbx = *top_of_stack;
*top_of_stack = crate::context::lucet_context_activate as u64;
// Pass a pointer to our guest-side entrypoint bootstrap code in `rsi`, and then put
// its first argument (a raw pointer to `self`) in `rdi`.
self.ctx.gpr.rsi = execution::enter_guest_region as u64;
self.ctx.gpr.rdi = self.ctx.callback_data_ptr() as u64;
}
} else {
panic!("unsupported architecture");
}
}
}

3 changes: 2 additions & 1 deletion lucet-runtime/lucet-runtime-internals/src/lib.rs
Original file line number Diff line number Diff line change
@@ -24,7 +24,8 @@ pub mod lock_testpoints;
pub mod module;
pub mod region;
pub mod sysdeps;
pub mod val;
pub mod archdeps;
pub use archdeps::val as val;
pub mod vmctx;

/// The size of a page in WebAssembly heaps.
20 changes: 16 additions & 4 deletions lucet-runtime/lucet-runtime-internals/src/sysdeps/linux.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
use libc::{c_void, ucontext_t, REG_RDI, REG_RIP};
use libc::{c_void, ucontext_t};
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(target_arch = "x86")] {
use libc::{REG_EDI, REG_EIP};
use REG_EDI as REG_DI;
use REG_EIP as REG_IP;
} else if #[cfg(target_arch = "x86_64")] {
use libc::{REG_RDI, REG_RIP};
use REG_RDI as REG_DI;
use REG_RIP as REG_IP;
}
}

#[derive(Clone, Copy, Debug)]
pub struct UContextPtr(*mut ucontext_t);
@@ -13,19 +25,19 @@ impl UContextPtr {
#[inline]
pub fn get_ip(self) -> *const c_void {
let mcontext = &unsafe { self.0.as_ref().unwrap() }.uc_mcontext;
mcontext.gregs[REG_RIP as usize] as *const _
mcontext.gregs[REG_IP as usize] as *const _
}

#[inline]
pub fn set_ip(self, new_ip: *const c_void) {
let mut mcontext = &mut unsafe { self.0.as_mut().unwrap() }.uc_mcontext;
mcontext.gregs[REG_RIP as usize] = new_ip as i64;
mcontext.gregs[REG_IP as usize] = new_ip as _;
}

#[inline]
pub fn set_rdi(self, new_rdi: u64) {
let mut mcontext = &mut unsafe { self.0.as_mut().unwrap() }.uc_mcontext;
mcontext.gregs[REG_RDI as usize] = new_rdi as i64;
mcontext.gregs[REG_DI as usize] = new_rdi as _;
}
}

14 changes: 12 additions & 2 deletions lucet-runtime/lucet-runtime-tests/build.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
use std::env;

fn main() {
// TODO: this should only be built for tests, but Cargo doesn't
// currently let you specify different build.rs options for tests:
// <https://github.com/rust-lang/cargo/issues/1581>
let traps_asm_file = match env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str() {
"x86_64" => "traps_x86_64.S",
"x86" => "traps_i686.S",
arch => {
panic!("unsupported architecture {}", arch);
}
};

cc::Build::new()
.file("src/guest_fault/traps.S")
.compile("guest_fault_traps");
.file(&format!("src/guest_fault/{}", traps_asm_file))
.compile("context_context_asm");
}
70 changes: 70 additions & 0 deletions lucet-runtime/lucet-runtime-tests/src/guest_fault/traps_i686.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
.text
.globl guest_func_illegal_instr # -- Begin function guest_func_illegal_instr
#ifdef __ELF__
.type guest_func_illegal_instr,@function
#else
.globl _guest_func_illegal_instr
#endif
.p2align 4, 0x90
guest_func_illegal_instr: # @guest_func_illegal_instr
_guest_func_illegal_instr:
.cfi_startproc
# %bb.0:
push %ebp
.cfi_def_cfa_offset 8
.cfi_offset %ebp, -8
mov %esp, %ebp
.cfi_def_cfa_register %ebp
mov %edi, -4(%ebp)
#APP
ud2
#NO_APP
pop %ebp
.cfi_def_cfa %esp, 4
ret
.Lfunc_end0:
#ifdef ___ELF__
.size guest_func_illegal_instr, .Lfunc_end0-guest_func_illegal_instr
#endif
.cfi_endproc
# -- End function
.globl guest_func_oob # -- Begin function guest_func_oob
#ifdef __ELF__
.type guest_func_oob,@function
#else
.globl _guest_func_oob
#endif
.p2align 4, 0x90
guest_func_oob: # @guest_func_oob
_guest_func_oob:
.cfi_startproc
# %bb.0:
push %ebp
.cfi_def_cfa_offset 8
.cfi_offset %ebp, -8
mov %esp, %ebp
.cfi_def_cfa_register %ebp
sub $8, %esp
mov %edi, -4(%ebp)
mov -4(%ebp), %edi
#ifdef __ELF__
call lucet_vmctx_get_heap@PLT
#else
call _lucet_vmctx_get_heap
#endif
mov %eax, -8(%ebp)
mov -8(%ebp), %eax
movb $0, 65537(%eax)
add $8, %esp
pop %ebp
.cfi_def_cfa %esp, 4
ret
.Lfunc_end1:
#ifdef __ELF__
.size guest_func_oob, .Lfunc_end1-guest_func_oob
#endif
.cfi_endproc

#if defined(__linux__) && defined(__ELF__)
.section ".note.GNU-stack","",@progbits
#endif
25 changes: 21 additions & 4 deletions lucet-runtime/src/c_api.rs
Original file line number Diff line number Diff line change
@@ -351,22 +351,39 @@ pub unsafe extern "C" fn lucet_retval_gp(retval: *const lucet_untyped_retval) ->
}
}

use cfg_if::cfg_if;
cfg_if! {
if #[cfg(target_arch = "x86_64")] {
use core::arch::x86_64::_mm_storeu_ps;
use core::arch::x86_64::_mm_loadu_ps;
use core::arch::x86_64::_mm_storeu_pd;
use core::arch::x86_64::_mm_loadu_pd;
} else if #[cfg(target_arch = "x86")] {
use core::arch::x86::_mm_storeu_ps;
use core::arch::x86::_mm_loadu_ps;
use core::arch::x86::_mm_storeu_pd;
use core::arch::x86::_mm_loadu_pd;
} else {
panic!("unsupported architecture!");
}
}

#[no_mangle]
pub unsafe extern "C" fn lucet_retval_f32(retval: *const lucet_untyped_retval) -> f32 {
let mut v = 0.0f32;
core::arch::x86_64::_mm_storeu_ps(
_mm_storeu_ps(
&mut v as *mut f32,
core::arch::x86_64::_mm_loadu_ps((*retval).fp.as_ptr() as *const f32),
_mm_loadu_ps((*retval).fp.as_ptr() as *const f32),
);
v
}

#[no_mangle]
pub unsafe extern "C" fn lucet_retval_f64(retval: *const lucet_untyped_retval) -> f64 {
let mut v = 0.0f64;
core::arch::x86_64::_mm_storeu_pd(
_mm_storeu_pd(
&mut v as *mut f64,
core::arch::x86_64::_mm_loadu_pd((*retval).fp.as_ptr() as *const f64),
_mm_loadu_pd((*retval).fp.as_ptr() as *const f64),
);
v
}
2 changes: 1 addition & 1 deletion lucet-spectest/src/script.rs
Original file line number Diff line number Diff line change
@@ -100,7 +100,7 @@ impl ScriptEnv {
let lucet_region = MmapRegion::create(
1,
&lucet_runtime::Limits {
heap_memory_size: 4 * 1024 * 1024 * 1024,
heap_memory_size: 1023 * 1024 * 1024,
..lucet_runtime::Limits::default()
},
)
6 changes: 6 additions & 0 deletions lucetc/src/pointer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
use cranelift_codegen::ir;

#[cfg(target_pointer_width = "64")]
pub const NATIVE_POINTER: ir::Type = ir::types::I64;
#[cfg(target_pointer_width = "32")]
pub const NATIVE_POINTER: ir::Type = ir::types::I32;
#[cfg(target_pointer_width = "64")]
pub const NATIVE_POINTER_SIZE: usize = 8;
#[cfg(target_pointer_width = "32")]
pub const NATIVE_POINTER_SIZE: usize = 4;
2 changes: 1 addition & 1 deletion lucetc/src/types.rs
Original file line number Diff line number Diff line change
@@ -67,7 +67,7 @@ pub fn to_lucet_signature(value: &ir::Signature) -> Result<Signature, SignatureE
extension: ir::ArgumentExtension::None,
location: ir::ArgumentLoc::Unassigned,
} => {
if value.is_int() && value.bits() == 64 {
if value.is_int() && value.bits() == (crate::pointer::NATIVE_POINTER_SIZE as u16 * 8) {
// this is VMContext, so we can move on.
} else {
return Err(SignatureError::BadElement(