add wasmptr memory abstraction to emscripten; update wasi array access

This commit is contained in:
Mark McCaskey
2019-06-06 15:45:19 -07:00
parent 4913cfaff4
commit d7ea46bab7
4 changed files with 145 additions and 19 deletions

View File

@ -42,6 +42,7 @@ mod lock;
mod math; mod math;
mod memory; mod memory;
mod process; mod process;
mod ptr;
mod signal; mod signal;
mod storage; mod storage;
mod syscalls; mod syscalls;

119
lib/emscripten/src/ptr.rs Normal file
View File

@ -0,0 +1,119 @@
use std::{cell::Cell, fmt, marker::PhantomData, mem};
use wasmer_runtime_core::{
memory::Memory,
types::{ValueType, WasmExternType},
};
pub struct Array;
pub struct Item;
// TODO: before shipping, refactor this and the wasi code into a common crate
// and wrap it when used in the context of wasi to return the proper error codes
#[repr(transparent)]
pub struct WasmPtr<T: Copy, Ty = Item> {
offset: u32,
_phantom: PhantomData<(T, Ty)>,
}
impl<T: Copy, Ty> WasmPtr<T, Ty> {
#[inline]
pub fn new(offset: u32) -> Self {
Self {
offset,
_phantom: PhantomData,
}
}
#[inline]
pub fn offset(self) -> i32 {
self.offset as i32
}
}
#[inline(always)]
fn align_pointer(ptr: usize, align: usize) -> usize {
// clears bits below aligment amount (assumes power of 2) to align pointer
debug_assert!(align.count_ones() == 1);
ptr & !(align - 1)
}
impl<T: Copy + ValueType> WasmPtr<T, Item> {
#[inline]
pub fn deref<'a>(self, memory: &'a Memory) -> Option<&'a Cell<T>> {
if (self.offset as usize) + mem::size_of::<T>() >= memory.size().bytes().0 {
return None;
}
unsafe {
let cell_ptr = align_pointer(
memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
mem::align_of::<T>(),
) as *const Cell<T>;
Some(&*cell_ptr)
}
}
}
impl<T: Copy + ValueType> WasmPtr<T, Array> {
#[inline]
pub fn deref<'a>(self, memory: &'a Memory, index: u32, length: u32) -> Option<&'a [Cell<T>]> {
if (self.offset as usize) + (mem::size_of::<T>() * ((index + length) as usize))
>= memory.size().bytes().0
{
return None;
}
// gets the size of the item in the array with padding added such that
// for any index, we will always result an aligned memory access
let item_size = mem::size_of::<T>() + (mem::size_of::<T>() % mem::align_of::<T>());
let base_idx = (self.offset as usize) / item_size;
unsafe {
let cell_ptrs = memory
.view::<T>()
.get_unchecked(base_idx + (index as usize)..base_idx + ((index + length) as usize))
as *const _;
Some(&*cell_ptrs)
}
}
}
unsafe impl<T: Copy, Ty> WasmExternType for WasmPtr<T, Ty> {
type Native = i32;
fn to_native(self) -> Self::Native {
self.offset as i32
}
fn from_native(n: Self::Native) -> Self {
Self {
offset: n as u32,
_phantom: PhantomData,
}
}
}
unsafe impl<T: Copy, Ty> ValueType for WasmPtr<T, Ty> {}
impl<T: Copy, Ty> Clone for WasmPtr<T, Ty> {
fn clone(&self) -> Self {
Self {
offset: self.offset,
_phantom: PhantomData,
}
}
}
impl<T: Copy, Ty> Copy for WasmPtr<T, Ty> {}
impl<T: Copy, Ty> PartialEq for WasmPtr<T, Ty> {
fn eq(&self, other: &Self) -> bool {
self.offset == other.offset
}
}
impl<T: Copy, Ty> Eq for WasmPtr<T, Ty> {}
impl<T: Copy, Ty> fmt::Debug for WasmPtr<T, Ty> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "WasmPtr({:#x})", self.offset)
}
}

View File

@ -10,6 +10,7 @@ pub use self::unix::*;
#[cfg(windows)] #[cfg(windows)]
pub use self::windows::*; pub use self::windows::*;
use crate::ptr::{Array, WasmPtr};
use crate::utils::{copy_stat_into_wasm, get_cstr_path, get_current_directory}; use crate::utils::{copy_stat_into_wasm, get_cstr_path, get_current_directory};
use super::varargs::VarArgs; use super::varargs::VarArgs;
@ -77,7 +78,7 @@ pub fn ___syscall3(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {
pub fn ___syscall4(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int { pub fn ___syscall4(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall4 (write) {}", _which); debug!("emscripten::___syscall4 (write) {}", _which);
let fd: i32 = varargs.get(ctx); let fd: i32 = varargs.get(ctx);
let buf: u32 = varargs.get(ctx); let buf: i32 = varargs.get(ctx);
let count: i32 = varargs.get(ctx); let count: i32 = varargs.get(ctx);
debug!("=> fd: {}, buf: {}, count: {}", fd, buf, count); debug!("=> fd: {}, buf: {}, count: {}", fd, buf, count);
let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *const c_void; let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as *const c_void;
@ -270,21 +271,18 @@ pub fn ___syscall110(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
// getcwd // getcwd
pub fn ___syscall183(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 { pub fn ___syscall183(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 {
debug!("emscripten::___syscall183"); debug!("emscripten::___syscall183");
let buf_offset: c_int = varargs.get(ctx); let buf_offset: WasmPtr<libc::c_char, Array> = varargs.get(ctx);
let _size: c_int = varargs.get(ctx); let _size: c_int = varargs.get(ctx);
let path = get_current_directory(ctx); let path = get_current_directory(ctx);
let path_string = path.unwrap().display().to_string(); let path_string = path.unwrap().display().to_string();
let len = path_string.len(); let len = path_string.len();
unsafe {
let pointer_to_buffer = let buf_writer = buf_offset.deref(ctx.memory(0), 0, len as u32 + 1).unwrap();
emscripten_memory_pointer!(ctx.memory(0), buf_offset) as *mut libc::c_char; for (i, byte) in path_string.bytes().enumerate() {
let slice = slice::from_raw_parts_mut(pointer_to_buffer, len.clone()); buf_writer[i].set(byte as i8);
for (byte, loc) in path_string.bytes().zip(slice.iter_mut()) {
*loc = byte as _;
} }
*pointer_to_buffer.add(len.clone()) = 0; buf_writer[len].set(0);
} buf_offset.offset()
buf_offset
} }
// mmap2 // mmap2

View File

@ -29,6 +29,13 @@ impl<T: Copy, Ty> WasmPtr<T, Ty> {
} }
} }
#[inline(always)]
fn align_pointer(ptr: usize, align: usize) -> usize {
// clears bits below aligment amount (assumes power of 2) to align pointer
debug_assert!(align.count_ones() == 1);
ptr & !(align - 1)
}
impl<T: Copy + ValueType> WasmPtr<T, Item> { impl<T: Copy + ValueType> WasmPtr<T, Item> {
#[inline] #[inline]
pub fn deref<'a>(self, memory: &'a Memory) -> Result<&'a Cell<T>, __wasi_errno_t> { pub fn deref<'a>(self, memory: &'a Memory) -> Result<&'a Cell<T>, __wasi_errno_t> {
@ -36,9 +43,7 @@ impl<T: Copy + ValueType> WasmPtr<T, Item> {
return Err(__WASI_EFAULT); return Err(__WASI_EFAULT);
} }
unsafe { unsafe {
// clears bits below aligment amount (assumes power of 2) to align pointer let cell_ptr = align_pointer(
let aligner = |ptr: usize, align: usize| ptr & !(align - 1);
let cell_ptr = aligner(
memory.view::<u8>().as_ptr().add(self.offset as usize) as usize, memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
mem::align_of::<T>(), mem::align_of::<T>(),
) as *const Cell<T>; ) as *const Cell<T>;
@ -61,12 +66,15 @@ impl<T: Copy + ValueType> WasmPtr<T, Array> {
return Err(__WASI_EFAULT); return Err(__WASI_EFAULT);
} }
// gets the size of the item in the array with padding added such that
// for any index, we will always result an aligned memory access
let item_size = mem::size_of::<T>() + (mem::size_of::<T>() % mem::align_of::<T>());
let base_idx = (self.offset as usize) / item_size;
unsafe { unsafe {
let cell_ptrs = memory.view::<T>().get_unchecked( let cell_ptrs = memory
((self.offset as usize) / mem::size_of::<T>()) + (index as usize) .view::<T>()
..((self.offset() as usize) / mem::size_of::<T>()) .get_unchecked(base_idx + (index as usize)..base_idx + ((index + length) as usize))
+ ((index + length) as usize), as *const _;
) as *const _;
Ok(&*cell_ptrs) Ok(&*cell_ptrs)
} }
} }