mirror of
https://github.com/fluencelabs/wasmer
synced 2024-12-14 06:35:40 +00:00
Implemented protected call and floating point traps; passing all spectests!
This commit is contained in:
parent
1f8c644855
commit
f8fe999015
@ -14,3 +14,5 @@ dynasm = "0.3.0"
|
|||||||
dynasmrt = "0.3.1"
|
dynasmrt = "0.3.1"
|
||||||
lazy_static = "1.2.0"
|
lazy_static = "1.2.0"
|
||||||
byteorder = "1"
|
byteorder = "1"
|
||||||
|
nix = "0.13.0"
|
||||||
|
libc = "0.2.49"
|
||||||
|
@ -9,6 +9,7 @@ use wasmer_runtime_core::{
|
|||||||
use wasmparser::{Operator, Type as WpType};
|
use wasmparser::{Operator, Type as WpType};
|
||||||
|
|
||||||
pub trait ModuleCodeGenerator<FCG: FunctionCodeGenerator, PC: ProtectedCaller, FR: FuncResolver> {
|
pub trait ModuleCodeGenerator<FCG: FunctionCodeGenerator, PC: ProtectedCaller, FR: FuncResolver> {
|
||||||
|
fn check_precondition(&mut self, module_info: &ModuleInfo) -> Result<(), CodegenError>;
|
||||||
fn next_function(&mut self) -> Result<&mut FCG, CodegenError>;
|
fn next_function(&mut self) -> Result<&mut FCG, CodegenError>;
|
||||||
fn finalize(self, module_info: &ModuleInfo) -> Result<(PC, FR), CodegenError>;
|
fn finalize(self, module_info: &ModuleInfo) -> Result<(PC, FR), CodegenError>;
|
||||||
fn feed_signatures(
|
fn feed_signatures(
|
||||||
|
@ -24,6 +24,7 @@ use wasmer_runtime_core::{
|
|||||||
vm::{self, ImportBacking, LocalGlobal, LocalTable, LocalMemory},
|
vm::{self, ImportBacking, LocalGlobal, LocalTable, LocalMemory},
|
||||||
};
|
};
|
||||||
use wasmparser::{Operator, Type as WpType};
|
use wasmparser::{Operator, Type as WpType};
|
||||||
|
use crate::protect_unix;
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static CURRENT_EXECUTION_CONTEXT: RefCell<Vec<*const X64ExecutionContext>> = RefCell::new(Vec::new());
|
static CURRENT_EXECUTION_CONTEXT: RefCell<Vec<*const X64ExecutionContext>> = RefCell::new(Vec::new());
|
||||||
@ -384,6 +385,7 @@ impl ProtectedCaller for X64ExecutionContext {
|
|||||||
CURRENT_EXECUTION_CONTEXT.with(|x| x.borrow_mut().push(self));
|
CURRENT_EXECUTION_CONTEXT.with(|x| x.borrow_mut().push(self));
|
||||||
|
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
|
protect_unix::call_protected(|| {
|
||||||
CALL_WASM(
|
CALL_WASM(
|
||||||
param_buf.as_ptr(),
|
param_buf.as_ptr(),
|
||||||
param_buf.len(),
|
param_buf.len(),
|
||||||
@ -391,10 +393,13 @@ impl ProtectedCaller for X64ExecutionContext {
|
|||||||
memory_base,
|
memory_base,
|
||||||
_vmctx,
|
_vmctx,
|
||||||
)
|
)
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
CURRENT_EXECUTION_CONTEXT.with(|x| x.borrow_mut().pop().unwrap());
|
CURRENT_EXECUTION_CONTEXT.with(|x| x.borrow_mut().pop().unwrap());
|
||||||
|
|
||||||
|
let ret = ret?;
|
||||||
|
|
||||||
Ok(if let Some(ty) = return_ty {
|
Ok(if let Some(ty) = return_ty {
|
||||||
vec![match ty {
|
vec![match ty {
|
||||||
WpType::I32 => Value::I32(ret as i32),
|
WpType::I32 => Value::I32(ret as i32),
|
||||||
@ -524,6 +529,19 @@ impl X64ModuleCodeGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, X64RuntimeResolver> for X64ModuleCodeGenerator {
|
impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext, X64RuntimeResolver> for X64ModuleCodeGenerator {
|
||||||
|
fn check_precondition(&mut self, module_info: &ModuleInfo) -> Result<(), CodegenError> {
|
||||||
|
for mem in module_info.memories.iter().map(|(_, v)| v).chain(module_info.imported_memories.iter().map(|(_, v)| &v.1)) {
|
||||||
|
match mem.memory_type() {
|
||||||
|
MemoryType::Dynamic => return Err(CodegenError {
|
||||||
|
message: "dynamic memory isn't supported yet"
|
||||||
|
}),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn next_function(&mut self) -> Result<&mut X64FunctionCode, CodegenError> {
|
fn next_function(&mut self) -> Result<&mut X64FunctionCode, CodegenError> {
|
||||||
let (mut assembler, mut function_labels, br_table_data) = match self.functions.last_mut() {
|
let (mut assembler, mut function_labels, br_table_data) = match self.functions.last_mut() {
|
||||||
Some(x) => (
|
Some(x) => (
|
||||||
@ -1428,6 +1446,98 @@ impl X64FunctionCode {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn emit_f32_int_conv_check(
|
||||||
|
assembler: &mut Assembler,
|
||||||
|
reg: Register,
|
||||||
|
lower_bound: f32,
|
||||||
|
upper_bound: f32,
|
||||||
|
) {
|
||||||
|
let lower_bound = f32::to_bits(lower_bound);
|
||||||
|
let upper_bound = f32::to_bits(upper_bound);
|
||||||
|
|
||||||
|
dynasm!(
|
||||||
|
assembler
|
||||||
|
; movq xmm5, r15
|
||||||
|
|
||||||
|
// underflow
|
||||||
|
; movd xmm1, Rd(reg as u8)
|
||||||
|
; mov r15d, lower_bound as i32
|
||||||
|
; movd xmm2, r15d
|
||||||
|
; vcmpltss xmm0, xmm1, xmm2
|
||||||
|
; movd r15d, xmm0
|
||||||
|
; cmp r15d, 1
|
||||||
|
; je >trap
|
||||||
|
|
||||||
|
// overflow
|
||||||
|
; mov r15d, upper_bound as i32
|
||||||
|
; movd xmm2, r15d
|
||||||
|
; vcmpgtss xmm0, xmm1, xmm2
|
||||||
|
; movd r15d, xmm0
|
||||||
|
; cmp r15d, 1
|
||||||
|
; je >trap
|
||||||
|
|
||||||
|
// NaN
|
||||||
|
; vcmpeqss xmm0, xmm1, xmm1
|
||||||
|
; movd r15d, xmm0
|
||||||
|
; cmp r15d, 0
|
||||||
|
; je >trap
|
||||||
|
|
||||||
|
; movq r15, xmm5
|
||||||
|
; jmp >ok
|
||||||
|
|
||||||
|
; trap:
|
||||||
|
; ud2
|
||||||
|
|
||||||
|
; ok:
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_f64_int_conv_check(
|
||||||
|
assembler: &mut Assembler,
|
||||||
|
reg: Register,
|
||||||
|
lower_bound: f64,
|
||||||
|
upper_bound: f64,
|
||||||
|
) {
|
||||||
|
let lower_bound = f64::to_bits(lower_bound);
|
||||||
|
let upper_bound = f64::to_bits(upper_bound);
|
||||||
|
|
||||||
|
dynasm!(
|
||||||
|
assembler
|
||||||
|
; movq xmm5, r15
|
||||||
|
|
||||||
|
// underflow
|
||||||
|
; movq xmm1, Rq(reg as u8)
|
||||||
|
; mov r15, QWORD lower_bound as i64
|
||||||
|
; movq xmm2, r15
|
||||||
|
; vcmpltsd xmm0, xmm1, xmm2
|
||||||
|
; movd r15d, xmm0
|
||||||
|
; cmp r15d, 1
|
||||||
|
; je >trap
|
||||||
|
|
||||||
|
// overflow
|
||||||
|
; mov r15, QWORD upper_bound as i64
|
||||||
|
; movq xmm2, r15
|
||||||
|
; vcmpgtsd xmm0, xmm1, xmm2
|
||||||
|
; movd r15d, xmm0
|
||||||
|
; cmp r15d, 1
|
||||||
|
; je >trap
|
||||||
|
|
||||||
|
// NaN
|
||||||
|
; vcmpeqsd xmm0, xmm1, xmm1
|
||||||
|
; movd r15d, xmm0
|
||||||
|
; cmp r15d, 0
|
||||||
|
; je >trap
|
||||||
|
|
||||||
|
; movq r15, xmm5
|
||||||
|
; jmp >ok
|
||||||
|
|
||||||
|
; trap:
|
||||||
|
; ud2
|
||||||
|
|
||||||
|
; ok:
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn emit_native_call_trampoline<A: Copy + Sized, B: Copy + Sized>(
|
fn emit_native_call_trampoline<A: Copy + Sized, B: Copy + Sized>(
|
||||||
assembler: &mut Assembler,
|
assembler: &mut Assembler,
|
||||||
target: unsafe extern "C" fn(
|
target: unsafe extern "C" fn(
|
||||||
@ -4137,11 +4247,17 @@ impl FunctionCodeGenerator for X64FunctionCode {
|
|||||||
WpType::F32
|
WpType::F32
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Operator::I32TruncUF32 | Operator::I32TruncSF32 => {
|
Operator::I32TruncUF32 => {
|
||||||
Self::emit_unop(
|
Self::emit_unop(
|
||||||
assembler,
|
assembler,
|
||||||
&mut self.value_stack,
|
&mut self.value_stack,
|
||||||
|assembler, value_stack, reg| {
|
|assembler, value_stack, reg| {
|
||||||
|
Self::emit_f32_int_conv_check(
|
||||||
|
assembler,
|
||||||
|
reg,
|
||||||
|
-1.0,
|
||||||
|
4294967296.0,
|
||||||
|
);
|
||||||
dynasm!(
|
dynasm!(
|
||||||
assembler
|
assembler
|
||||||
; movd xmm1, Rd(reg as u8)
|
; movd xmm1, Rd(reg as u8)
|
||||||
@ -4153,11 +4269,61 @@ impl FunctionCodeGenerator for X64FunctionCode {
|
|||||||
WpType::I32
|
WpType::I32
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Operator::I64TruncUF32 | Operator::I64TruncSF32 => {
|
Operator::I32TruncSF32 => {
|
||||||
Self::emit_unop(
|
Self::emit_unop(
|
||||||
assembler,
|
assembler,
|
||||||
&mut self.value_stack,
|
&mut self.value_stack,
|
||||||
|assembler, value_stack, reg| {
|
|assembler, value_stack, reg| {
|
||||||
|
Self::emit_f32_int_conv_check(
|
||||||
|
assembler,
|
||||||
|
reg,
|
||||||
|
-2147483904.0,
|
||||||
|
2147483648.0
|
||||||
|
);
|
||||||
|
dynasm!(
|
||||||
|
assembler
|
||||||
|
; movd xmm1, Rd(reg as u8)
|
||||||
|
; roundss xmm1, xmm1, 3
|
||||||
|
; cvtss2si Rd(reg as u8), xmm1
|
||||||
|
);
|
||||||
|
},
|
||||||
|
WpType::F32,
|
||||||
|
WpType::I32
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Operator::I64TruncUF32 => {
|
||||||
|
Self::emit_unop(
|
||||||
|
assembler,
|
||||||
|
&mut self.value_stack,
|
||||||
|
|assembler, value_stack, reg| {
|
||||||
|
Self::emit_f32_int_conv_check(
|
||||||
|
assembler,
|
||||||
|
reg,
|
||||||
|
-1.0,
|
||||||
|
18446744073709551616.0,
|
||||||
|
);
|
||||||
|
dynasm!(
|
||||||
|
assembler
|
||||||
|
; movd xmm1, Rd(reg as u8)
|
||||||
|
; roundss xmm1, xmm1, 3
|
||||||
|
; cvtss2si Rq(reg as u8), xmm1
|
||||||
|
);
|
||||||
|
},
|
||||||
|
WpType::F32,
|
||||||
|
WpType::I64
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Operator::I64TruncSF32 => {
|
||||||
|
Self::emit_unop(
|
||||||
|
assembler,
|
||||||
|
&mut self.value_stack,
|
||||||
|
|assembler, value_stack, reg| {
|
||||||
|
Self::emit_f32_int_conv_check(
|
||||||
|
assembler,
|
||||||
|
reg,
|
||||||
|
-9223373136366403584.0,
|
||||||
|
9223372036854775808.0,
|
||||||
|
);
|
||||||
dynasm!(
|
dynasm!(
|
||||||
assembler
|
assembler
|
||||||
; movd xmm1, Rd(reg as u8)
|
; movd xmm1, Rd(reg as u8)
|
||||||
@ -4514,11 +4680,18 @@ impl FunctionCodeGenerator for X64FunctionCode {
|
|||||||
WpType::F64
|
WpType::F64
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Operator::I32TruncUF64 | Operator::I32TruncSF64 => {
|
Operator::I32TruncUF64 => {
|
||||||
Self::emit_unop(
|
Self::emit_unop(
|
||||||
assembler,
|
assembler,
|
||||||
&mut self.value_stack,
|
&mut self.value_stack,
|
||||||
|assembler, value_stack, reg| {
|
|assembler, value_stack, reg| {
|
||||||
|
Self::emit_f64_int_conv_check(
|
||||||
|
assembler,
|
||||||
|
reg,
|
||||||
|
-1.0,
|
||||||
|
4294967296.0,
|
||||||
|
);
|
||||||
|
|
||||||
dynasm!(
|
dynasm!(
|
||||||
assembler
|
assembler
|
||||||
; movq xmm1, Rq(reg as u8)
|
; movq xmm1, Rq(reg as u8)
|
||||||
@ -4530,11 +4703,64 @@ impl FunctionCodeGenerator for X64FunctionCode {
|
|||||||
WpType::I32
|
WpType::I32
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Operator::I64TruncUF64 | Operator::I64TruncSF64 => {
|
Operator::I32TruncSF64 => {
|
||||||
Self::emit_unop(
|
Self::emit_unop(
|
||||||
assembler,
|
assembler,
|
||||||
&mut self.value_stack,
|
&mut self.value_stack,
|
||||||
|assembler, value_stack, reg| {
|
|assembler, value_stack, reg| {
|
||||||
|
Self::emit_f64_int_conv_check(
|
||||||
|
assembler,
|
||||||
|
reg,
|
||||||
|
-2147483649.0,
|
||||||
|
2147483648.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
dynasm!(
|
||||||
|
assembler
|
||||||
|
; movq xmm1, Rq(reg as u8)
|
||||||
|
; roundsd xmm1, xmm1, 3
|
||||||
|
; cvtsd2si Rd(reg as u8), xmm1
|
||||||
|
);
|
||||||
|
},
|
||||||
|
WpType::F64,
|
||||||
|
WpType::I32
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Operator::I64TruncUF64 => {
|
||||||
|
Self::emit_unop(
|
||||||
|
assembler,
|
||||||
|
&mut self.value_stack,
|
||||||
|
|assembler, value_stack, reg| {
|
||||||
|
Self::emit_f64_int_conv_check(
|
||||||
|
assembler,
|
||||||
|
reg,
|
||||||
|
-1.0,
|
||||||
|
18446744073709551616.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
dynasm!(
|
||||||
|
assembler
|
||||||
|
; movq xmm1, Rq(reg as u8)
|
||||||
|
; roundsd xmm1, xmm1, 3
|
||||||
|
; cvtsd2si Rq(reg as u8), xmm1
|
||||||
|
);
|
||||||
|
},
|
||||||
|
WpType::F64,
|
||||||
|
WpType::I64
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Operator::I64TruncSF64 => {
|
||||||
|
Self::emit_unop(
|
||||||
|
assembler,
|
||||||
|
&mut self.value_stack,
|
||||||
|
|assembler, value_stack, reg| {
|
||||||
|
Self::emit_f64_int_conv_check(
|
||||||
|
assembler,
|
||||||
|
reg,
|
||||||
|
-9223372036854777856.0,
|
||||||
|
9223372036854775808.0,
|
||||||
|
);
|
||||||
|
|
||||||
dynasm!(
|
dynasm!(
|
||||||
assembler
|
assembler
|
||||||
; movq xmm1, Rq(reg as u8)
|
; movq xmm1, Rq(reg as u8)
|
||||||
@ -4553,7 +4779,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
|
|||||||
LocalOrImport::Local(local_mem_index) => {
|
LocalOrImport::Local(local_mem_index) => {
|
||||||
let mem_desc = &module_info.memories[local_mem_index];
|
let mem_desc = &module_info.memories[local_mem_index];
|
||||||
match mem_desc.memory_type() {
|
match mem_desc.memory_type() {
|
||||||
MemoryType::Dynamic => self.native_trampolines.memory_size_dynamic_local,
|
//MemoryType::Dynamic => self.native_trampolines.memory_size_dynamic_local,
|
||||||
|
MemoryType::Dynamic => unimplemented!(),
|
||||||
MemoryType::Static => self.native_trampolines.memory_size_static_local,
|
MemoryType::Static => self.native_trampolines.memory_size_static_local,
|
||||||
MemoryType::SharedStatic => self.native_trampolines.memory_size_shared_local,
|
MemoryType::SharedStatic => self.native_trampolines.memory_size_shared_local,
|
||||||
}
|
}
|
||||||
@ -4561,7 +4788,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
|
|||||||
LocalOrImport::Import(import_mem_index) => {
|
LocalOrImport::Import(import_mem_index) => {
|
||||||
let mem_desc = &module_info.imported_memories[import_mem_index].1;
|
let mem_desc = &module_info.imported_memories[import_mem_index].1;
|
||||||
match mem_desc.memory_type() {
|
match mem_desc.memory_type() {
|
||||||
MemoryType::Dynamic => self.native_trampolines.memory_size_dynamic_import,
|
//MemoryType::Dynamic => self.native_trampolines.memory_size_dynamic_import,
|
||||||
|
MemoryType::Dynamic => unimplemented!(),
|
||||||
MemoryType::Static => self.native_trampolines.memory_size_static_import,
|
MemoryType::Static => self.native_trampolines.memory_size_static_import,
|
||||||
MemoryType::SharedStatic => self.native_trampolines.memory_size_shared_import,
|
MemoryType::SharedStatic => self.native_trampolines.memory_size_shared_import,
|
||||||
}
|
}
|
||||||
@ -4581,7 +4809,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
|
|||||||
LocalOrImport::Local(local_mem_index) => {
|
LocalOrImport::Local(local_mem_index) => {
|
||||||
let mem_desc = &module_info.memories[local_mem_index];
|
let mem_desc = &module_info.memories[local_mem_index];
|
||||||
match mem_desc.memory_type() {
|
match mem_desc.memory_type() {
|
||||||
MemoryType::Dynamic => self.native_trampolines.memory_grow_dynamic_local,
|
//MemoryType::Dynamic => self.native_trampolines.memory_grow_dynamic_local,
|
||||||
|
MemoryType::Dynamic => unimplemented!(),
|
||||||
MemoryType::Static => self.native_trampolines.memory_grow_static_local,
|
MemoryType::Static => self.native_trampolines.memory_grow_static_local,
|
||||||
MemoryType::SharedStatic => self.native_trampolines.memory_grow_shared_local,
|
MemoryType::SharedStatic => self.native_trampolines.memory_grow_shared_local,
|
||||||
}
|
}
|
||||||
@ -4589,7 +4818,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
|
|||||||
LocalOrImport::Import(import_mem_index) => {
|
LocalOrImport::Import(import_mem_index) => {
|
||||||
let mem_desc = &module_info.imported_memories[import_mem_index].1;
|
let mem_desc = &module_info.imported_memories[import_mem_index].1;
|
||||||
match mem_desc.memory_type() {
|
match mem_desc.memory_type() {
|
||||||
MemoryType::Dynamic => self.native_trampolines.memory_grow_dynamic_import,
|
//MemoryType::Dynamic => self.native_trampolines.memory_grow_dynamic_import,
|
||||||
|
MemoryType::Dynamic => unimplemented!(),
|
||||||
MemoryType::Static => self.native_trampolines.memory_grow_static_import,
|
MemoryType::Static => self.native_trampolines.memory_grow_static_import,
|
||||||
MemoryType::SharedStatic => self.native_trampolines.memory_grow_shared_import,
|
MemoryType::SharedStatic => self.native_trampolines.memory_grow_shared_import,
|
||||||
}
|
}
|
||||||
@ -4725,13 +4955,20 @@ unsafe extern "C" fn call_indirect(
|
|||||||
CallIndirectLocalOrImport::Import => &*(*(*vmctx).imported_tables),
|
CallIndirectLocalOrImport::Import => &*(*(*vmctx).imported_tables),
|
||||||
} ;
|
} ;
|
||||||
if elem_index >= table.count as usize {
|
if elem_index >= table.count as usize {
|
||||||
panic!("element index out of bounds");
|
eprintln!("element index out of bounds");
|
||||||
|
unsafe { protect_unix::trigger_trap(); }
|
||||||
}
|
}
|
||||||
let anyfunc = &*(table.base as *mut vm::Anyfunc).offset(elem_index as isize);
|
let anyfunc = &*(table.base as *mut vm::Anyfunc).offset(elem_index as isize);
|
||||||
let ctx: &X64ExecutionContext =
|
let ctx: &X64ExecutionContext =
|
||||||
&*CURRENT_EXECUTION_CONTEXT.with(|x| *x.borrow().last().unwrap());
|
&*CURRENT_EXECUTION_CONTEXT.with(|x| *x.borrow().last().unwrap());
|
||||||
|
|
||||||
let func_index = anyfunc.func_index.unwrap();
|
let func_index = match anyfunc.func_index {
|
||||||
|
Some(x) => x,
|
||||||
|
None => {
|
||||||
|
eprintln!("empty table entry");
|
||||||
|
unsafe { protect_unix::trigger_trap(); }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/*println!(
|
/*println!(
|
||||||
"SIG INDEX = {}, FUNC INDEX = {:?}, ELEM INDEX = {}",
|
"SIG INDEX = {}, FUNC INDEX = {:?}, ELEM INDEX = {}",
|
||||||
@ -4741,7 +4978,8 @@ unsafe extern "C" fn call_indirect(
|
|||||||
if ctx.signatures[SigIndex::new(sig_index)]
|
if ctx.signatures[SigIndex::new(sig_index)]
|
||||||
!= ctx.signatures[ctx.function_signatures[func_index]]
|
!= ctx.signatures[ctx.function_signatures[func_index]]
|
||||||
{
|
{
|
||||||
panic!("signature mismatch");
|
eprintln!("signature mismatch");
|
||||||
|
unsafe { protect_unix::trigger_trap(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
let func = ctx.function_pointers[func_index.index() as usize].0;
|
let func = ctx.function_pointers[func_index.index() as usize].0;
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
#![feature(proc_macro_hygiene)]
|
#![feature(proc_macro_hygiene)]
|
||||||
|
|
||||||
|
#[cfg(not(any(
|
||||||
|
all(target_os = "macos", target_arch = "x86_64"),
|
||||||
|
all(target_os = "linux", target_arch = "x86_64"),
|
||||||
|
)))]
|
||||||
|
compile_error!("This crate doesn't yet support compiling on operating systems other than linux and macos and architectures other than x86_64");
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate dynasmrt;
|
extern crate dynasmrt;
|
||||||
|
|
||||||
@ -15,6 +21,7 @@ mod codegen;
|
|||||||
mod codegen_x64;
|
mod codegen_x64;
|
||||||
mod parse;
|
mod parse;
|
||||||
mod stack;
|
mod stack;
|
||||||
|
mod protect_unix;
|
||||||
|
|
||||||
use crate::codegen::{CodegenError, ModuleCodeGenerator};
|
use crate::codegen::{CodegenError, ModuleCodeGenerator};
|
||||||
use crate::parse::LoadError;
|
use crate::parse::LoadError;
|
||||||
|
@ -18,6 +18,7 @@ use wasmparser::{
|
|||||||
BinaryReaderError, CodeSectionReader, Data, DataKind, Element, ElementKind, Export,
|
BinaryReaderError, CodeSectionReader, Data, DataKind, Element, ElementKind, Export,
|
||||||
ExternalKind, FuncType, Import, ImportSectionEntryType, InitExpr, ModuleReader, Operator,
|
ExternalKind, FuncType, Import, ImportSectionEntryType, InitExpr, ModuleReader, Operator,
|
||||||
SectionCode, Type as WpType,
|
SectionCode, Type as WpType,
|
||||||
|
WasmDecoder,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -38,6 +39,30 @@ impl From<CodegenError> for LoadError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate(bytes: &[u8]) -> Result<(), LoadError> {
|
||||||
|
let mut parser = wasmparser::ValidatingParser::new(
|
||||||
|
bytes,
|
||||||
|
Some(wasmparser::ValidatingParserConfig {
|
||||||
|
operator_config: wasmparser::OperatorValidatorConfig {
|
||||||
|
enable_threads: false,
|
||||||
|
enable_reference_types: false,
|
||||||
|
enable_simd: false,
|
||||||
|
enable_bulk_memory: false,
|
||||||
|
},
|
||||||
|
mutable_global_imports: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let state = parser.read();
|
||||||
|
match *state {
|
||||||
|
wasmparser::ParserState::EndWasm => break Ok(()),
|
||||||
|
wasmparser::ParserState::Error(err) => Err(LoadError::Parse(err))?,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_module<
|
pub fn read_module<
|
||||||
MCG: ModuleCodeGenerator<FCG, PC, FR>,
|
MCG: ModuleCodeGenerator<FCG, PC, FR>,
|
||||||
FCG: FunctionCodeGenerator,
|
FCG: FunctionCodeGenerator,
|
||||||
@ -48,6 +73,7 @@ pub fn read_module<
|
|||||||
backend: Backend,
|
backend: Backend,
|
||||||
mcg: &mut MCG,
|
mcg: &mut MCG,
|
||||||
) -> Result<ModuleInfo, LoadError> {
|
) -> Result<ModuleInfo, LoadError> {
|
||||||
|
validate(wasm)?;
|
||||||
let mut info = ModuleInfo {
|
let mut info = ModuleInfo {
|
||||||
memories: Map::new(),
|
memories: Map::new(),
|
||||||
globals: Map::new(),
|
globals: Map::new(),
|
||||||
@ -279,6 +305,7 @@ pub fn read_module<
|
|||||||
}
|
}
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
|
mcg.check_precondition(&info)?;
|
||||||
for i in 0..code_reader.get_count() {
|
for i in 0..code_reader.get_count() {
|
||||||
let item = code_reader.read()?;
|
let item = code_reader.read()?;
|
||||||
let mut fcg = mcg.next_function()?;
|
let mut fcg = mcg.next_function()?;
|
||||||
|
202
lib/dynasm-backend/src/protect_unix.rs
Normal file
202
lib/dynasm-backend/src/protect_unix.rs
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
//! Installing signal handlers allows us to handle traps and out-of-bounds memory
|
||||||
|
//! accesses that occur when runniing webassembly.
|
||||||
|
//!
|
||||||
|
//! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622
|
||||||
|
//!
|
||||||
|
//! When a WebAssembly module triggers any traps, we perform recovery here.
|
||||||
|
//!
|
||||||
|
//! This module uses TLS (thread-local storage) to track recovery information. Since the four signals we're handling
|
||||||
|
//! are very special, the async signal unsafety of Rust's TLS implementation generally does not affect the correctness here
|
||||||
|
//! unless you have memory unsafety elsewhere in your code.
|
||||||
|
//!
|
||||||
|
use libc::{c_int, c_void, siginfo_t};
|
||||||
|
use nix::sys::signal::{
|
||||||
|
sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV,
|
||||||
|
};
|
||||||
|
use std::cell::{Cell, UnsafeCell};
|
||||||
|
use std::ptr;
|
||||||
|
use std::sync::Once;
|
||||||
|
use wasmer_runtime_core::error::{RuntimeError, RuntimeResult};
|
||||||
|
|
||||||
|
extern "C" fn signal_trap_handler(
|
||||||
|
signum: ::nix::libc::c_int,
|
||||||
|
siginfo: *mut siginfo_t,
|
||||||
|
ucontext: *mut c_void,
|
||||||
|
) {
|
||||||
|
unsafe {
|
||||||
|
do_unwind(signum, siginfo as _, ucontext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
pub fn setjmp(env: *mut c_void) -> c_int;
|
||||||
|
fn longjmp(env: *mut c_void, val: c_int) -> !;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn install_sighandler() {
|
||||||
|
let sa = SigAction::new(
|
||||||
|
SigHandler::SigAction(signal_trap_handler),
|
||||||
|
SaFlags::SA_ONSTACK,
|
||||||
|
SigSet::empty(),
|
||||||
|
);
|
||||||
|
sigaction(SIGFPE, &sa).unwrap();
|
||||||
|
sigaction(SIGILL, &sa).unwrap();
|
||||||
|
sigaction(SIGSEGV, &sa).unwrap();
|
||||||
|
sigaction(SIGBUS, &sa).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
const SETJMP_BUFFER_LEN: usize = 27;
|
||||||
|
pub static SIGHANDLER_INIT: Once = Once::new();
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
pub static SETJMP_BUFFER: UnsafeCell<[c_int; SETJMP_BUFFER_LEN]> = UnsafeCell::new([0; SETJMP_BUFFER_LEN]);
|
||||||
|
pub static CAUGHT_ADDRESSES: Cell<(*const c_void, *const c_void)> = Cell::new((ptr::null(), ptr::null()));
|
||||||
|
pub static CURRENT_EXECUTABLE_BUFFER: Cell<*const c_void> = Cell::new(ptr::null());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn trigger_trap() -> ! {
|
||||||
|
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
|
||||||
|
|
||||||
|
longjmp(jmp_buf as *mut c_void, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call_protected<T>(f: impl FnOnce() -> T) -> RuntimeResult<T> {
|
||||||
|
unsafe {
|
||||||
|
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
|
||||||
|
let prev_jmp_buf = *jmp_buf;
|
||||||
|
|
||||||
|
SIGHANDLER_INIT.call_once(|| {
|
||||||
|
install_sighandler();
|
||||||
|
});
|
||||||
|
|
||||||
|
let signum = setjmp(jmp_buf as *mut _);
|
||||||
|
if signum != 0 {
|
||||||
|
*jmp_buf = prev_jmp_buf;
|
||||||
|
|
||||||
|
let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get());
|
||||||
|
|
||||||
|
let signal = match Signal::from_c_int(signum) {
|
||||||
|
Ok(SIGFPE) => "floating-point exception",
|
||||||
|
Ok(SIGILL) => "illegal instruction",
|
||||||
|
Ok(SIGSEGV) => "segmentation violation",
|
||||||
|
Ok(SIGBUS) => "bus error",
|
||||||
|
Err(_) => "error while getting the Signal",
|
||||||
|
_ => "unkown trapped signal",
|
||||||
|
};
|
||||||
|
// When the trap-handler is fully implemented, this will return more information.
|
||||||
|
Err(RuntimeError::Trap {
|
||||||
|
msg: format!("unknown trap at {:p} - {}", faulting_addr, signal).into(),
|
||||||
|
}
|
||||||
|
.into())
|
||||||
|
} else {
|
||||||
|
let ret = f(); // TODO: Switch stack?
|
||||||
|
*jmp_buf = prev_jmp_buf;
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unwinds to last protected_call.
|
||||||
|
pub unsafe fn do_unwind(signum: i32, siginfo: *const c_void, ucontext: *const c_void) -> ! {
|
||||||
|
// Since do_unwind is only expected to get called from WebAssembly code which doesn't hold any host resources (locks etc.)
|
||||||
|
// itself, accessing TLS here is safe. In case any other code calls this, it often indicates a memory safety bug and you should
|
||||||
|
// temporarily disable the signal handlers to debug it.
|
||||||
|
|
||||||
|
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
|
||||||
|
if *jmp_buf == [0; SETJMP_BUFFER_LEN] {
|
||||||
|
::std::process::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
CAUGHT_ADDRESSES.with(|cell| cell.set(get_faulting_addr_and_ip(siginfo, ucontext)));
|
||||||
|
|
||||||
|
longjmp(jmp_buf as *mut ::nix::libc::c_void, signum)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||||
|
unsafe fn get_faulting_addr_and_ip(
|
||||||
|
siginfo: *const c_void,
|
||||||
|
ucontext: *const c_void,
|
||||||
|
) -> (*const c_void, *const c_void) {
|
||||||
|
use libc::{ucontext_t, RIP};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct siginfo_t {
|
||||||
|
si_signo: i32,
|
||||||
|
si_errno: i32,
|
||||||
|
si_code: i32,
|
||||||
|
si_addr: u64,
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
let siginfo = siginfo as *const siginfo_t;
|
||||||
|
let si_addr = (*siginfo).si_addr;
|
||||||
|
|
||||||
|
let ucontext = ucontext as *const ucontext_t;
|
||||||
|
let rip = (*ucontext).uc_mcontext.gregs[RIP as usize];
|
||||||
|
|
||||||
|
(si_addr as _, rip as _)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
|
||||||
|
unsafe fn get_faulting_addr_and_ip(
|
||||||
|
siginfo: *const c_void,
|
||||||
|
ucontext: *const c_void,
|
||||||
|
) -> (*const c_void, *const c_void) {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct ucontext_t {
|
||||||
|
uc_onstack: u32,
|
||||||
|
uc_sigmask: u32,
|
||||||
|
uc_stack: libc::stack_t,
|
||||||
|
uc_link: *const ucontext_t,
|
||||||
|
uc_mcsize: u64,
|
||||||
|
uc_mcontext: *const mcontext_t,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
struct exception_state {
|
||||||
|
trapno: u16,
|
||||||
|
cpu: u16,
|
||||||
|
err: u32,
|
||||||
|
faultvaddr: u64,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
struct regs {
|
||||||
|
rax: u64,
|
||||||
|
rbx: u64,
|
||||||
|
rcx: u64,
|
||||||
|
rdx: u64,
|
||||||
|
rdi: u64,
|
||||||
|
rsi: u64,
|
||||||
|
rbp: u64,
|
||||||
|
rsp: u64,
|
||||||
|
r8: u64,
|
||||||
|
r9: u64,
|
||||||
|
r10: u64,
|
||||||
|
r11: u64,
|
||||||
|
r12: u64,
|
||||||
|
r13: u64,
|
||||||
|
r14: u64,
|
||||||
|
r15: u64,
|
||||||
|
rip: u64,
|
||||||
|
rflags: u64,
|
||||||
|
cs: u64,
|
||||||
|
fs: u64,
|
||||||
|
gs: u64,
|
||||||
|
}
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct mcontext_t {
|
||||||
|
es: exception_state,
|
||||||
|
ss: regs,
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
let siginfo = siginfo as *const siginfo_t;
|
||||||
|
let si_addr = (*siginfo).si_addr;
|
||||||
|
|
||||||
|
let ucontext = ucontext as *const ucontext_t;
|
||||||
|
let rip = (*(*ucontext).uc_mcontext).ss.rip;
|
||||||
|
|
||||||
|
(si_addr, rip as _)
|
||||||
|
}
|
@ -251,8 +251,8 @@ impl MemoryDescriptor {
|
|||||||
pub fn memory_type(self) -> MemoryType {
|
pub fn memory_type(self) -> MemoryType {
|
||||||
match (self.maximum.is_some(), self.shared) {
|
match (self.maximum.is_some(), self.shared) {
|
||||||
(true, true) => MemoryType::SharedStatic,
|
(true, true) => MemoryType::SharedStatic,
|
||||||
(true, false) => MemoryType::Static,
|
(true, false) | (false, false) => MemoryType::Static,
|
||||||
(false, false) => MemoryType::Dynamic,
|
//(false, false) => MemoryType::Dynamic,
|
||||||
(false, true) => panic!("shared memory without a max is not allowed"),
|
(false, true) => panic!("shared memory without a max is not allowed"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user