Expose precise offset information in wasmtime::FrameInfo (#1495)
* Consolidate trap/frame information This commit removes `TrapRegistry` in favor of consolidating this information in the `FRAME_INFO` we already have in the `wasmtime` crate. This allows us to keep information generally in one place and have one canonical location for "map this PC to some original wasm stuff". The intent for this is to next update with enough information to go from a program counter to a position in the original wasm file. * Expose module offset information in `FrameInfo` This commit implements functionality for `FrameInfo`, the wasm stack trace of a `Trap`, to return the module/function offset. This allows knowing the precise wasm location of each stack frame, instead of only the main trap itself. The intention here is to provide more visibility into the wasm source when something traps, so you know precisely where calls were and where traps were, in order to assist in debugging. Eventually we might use this information for mapping back to native source languages as well (given sufficient debug information). This change makes a previously-optional artifact of compilation always computed on the cranelift side of things. This `ModuleAddressMap` is then propagated to the same store of information other frame information is stored within. This also removes the need for passing a `SourceLoc` with wasm traps or to wasm trap creation, since the backtrace's wasm frames will be able to infer their own `SourceLoc` from the relevant program counters.
This commit is contained in:
@@ -2,10 +2,10 @@ use crate::trampoline::{generate_global_export, generate_memory_export, generate
|
|||||||
use crate::values::{from_checked_anyfunc, into_checked_anyfunc, Val};
|
use crate::values::{from_checked_anyfunc, into_checked_anyfunc, Val};
|
||||||
use crate::Mutability;
|
use crate::Mutability;
|
||||||
use crate::{ExternType, GlobalType, MemoryType, TableType, ValType};
|
use crate::{ExternType, GlobalType, MemoryType, TableType, ValType};
|
||||||
use crate::{Func, Store};
|
use crate::{Func, Store, Trap};
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use wasmtime_environ::{ir, wasm};
|
use wasmtime_environ::wasm;
|
||||||
use wasmtime_runtime::{self as runtime, InstanceHandle};
|
use wasmtime_runtime::{self as runtime, InstanceHandle};
|
||||||
|
|
||||||
// Externals
|
// Externals
|
||||||
@@ -422,14 +422,8 @@ impl Table {
|
|||||||
let src_table_index = src_table.wasmtime_table_index();
|
let src_table_index = src_table.wasmtime_table_index();
|
||||||
let src_table = src_table.wasmtime_handle.get_defined_table(src_table_index);
|
let src_table = src_table.wasmtime_handle.get_defined_table(src_table_index);
|
||||||
|
|
||||||
runtime::Table::copy(
|
runtime::Table::copy(dst_table, src_table, dst_index, src_index, len)
|
||||||
dst_table,
|
.map_err(Trap::from_jit)?;
|
||||||
src_table,
|
|
||||||
dst_index,
|
|
||||||
src_index,
|
|
||||||
len,
|
|
||||||
ir::SourceLoc::default(),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
use std::cmp;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use wasmtime_environ::entity::EntityRef;
|
use wasmtime_environ::entity::EntityRef;
|
||||||
|
use wasmtime_environ::ir;
|
||||||
use wasmtime_environ::wasm::FuncIndex;
|
use wasmtime_environ::wasm::FuncIndex;
|
||||||
use wasmtime_environ::Module;
|
use wasmtime_environ::{FunctionAddressMap, Module, TrapInformation};
|
||||||
use wasmtime_jit::CompiledModule;
|
use wasmtime_jit::CompiledModule;
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
@@ -11,7 +13,7 @@ lazy_static::lazy_static! {
|
|||||||
/// This global cache is used during `Trap` creation to symbolicate frames.
|
/// This global cache is used during `Trap` creation to symbolicate frames.
|
||||||
/// This is populated on module compilation, and it is cleared out whenever
|
/// This is populated on module compilation, and it is cleared out whenever
|
||||||
/// all references to a module are dropped.
|
/// all references to a module are dropped.
|
||||||
pub static ref FRAME_INFO: GlobalFrameInfo = GlobalFrameInfo::default();
|
pub static ref FRAME_INFO: RwLock<GlobalFrameInfo> = Default::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -25,7 +27,7 @@ pub struct GlobalFrameInfo {
|
|||||||
///
|
///
|
||||||
/// The key of this map is the highest address in the module and the value
|
/// The key of this map is the highest address in the module and the value
|
||||||
/// is the module's information, which also contains the start address.
|
/// is the module's information, which also contains the start address.
|
||||||
ranges: RwLock<BTreeMap<usize, ModuleFrameInfo>>,
|
ranges: BTreeMap<usize, ModuleFrameInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An RAII structure used to unregister a module's frame information when the
|
/// An RAII structure used to unregister a module's frame information when the
|
||||||
@@ -38,93 +40,164 @@ pub struct GlobalFrameInfoRegistration {
|
|||||||
|
|
||||||
struct ModuleFrameInfo {
|
struct ModuleFrameInfo {
|
||||||
start: usize,
|
start: usize,
|
||||||
functions: BTreeMap<usize, (usize, FuncIndex)>,
|
functions: BTreeMap<usize, FunctionInfo>,
|
||||||
module: Arc<Module>,
|
module: Arc<Module>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FunctionInfo {
|
||||||
|
start: usize,
|
||||||
|
index: FuncIndex,
|
||||||
|
traps: Vec<TrapInformation>,
|
||||||
|
instr_map: FunctionAddressMap,
|
||||||
|
}
|
||||||
|
|
||||||
impl GlobalFrameInfo {
|
impl GlobalFrameInfo {
|
||||||
/// Registers a new compiled module's frame information.
|
/// Fetches frame information about a program counter in a backtrace.
|
||||||
///
|
|
||||||
/// This function will register the `names` information for all of the
|
|
||||||
/// compiled functions within `module`. If the `module` has no functions
|
|
||||||
/// then `None` will be returned. Otherwise the returned object, when
|
|
||||||
/// dropped, will be used to unregister all name information from this map.
|
|
||||||
pub fn register(&self, module: &CompiledModule) -> Option<GlobalFrameInfoRegistration> {
|
|
||||||
let mut min = usize::max_value();
|
|
||||||
let mut max = 0;
|
|
||||||
let mut functions = BTreeMap::new();
|
|
||||||
for (i, allocated) in module.finished_functions() {
|
|
||||||
let (start, end) = unsafe {
|
|
||||||
let ptr = (**allocated).as_ptr();
|
|
||||||
let len = (**allocated).len();
|
|
||||||
(ptr as usize, ptr as usize + len)
|
|
||||||
};
|
|
||||||
if start < min {
|
|
||||||
min = start;
|
|
||||||
}
|
|
||||||
if end > max {
|
|
||||||
max = end;
|
|
||||||
}
|
|
||||||
let func_index = module.module().local.func_index(i);
|
|
||||||
assert!(functions.insert(end, (start, func_index)).is_none());
|
|
||||||
}
|
|
||||||
if functions.len() == 0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut ranges = self.ranges.write().unwrap();
|
|
||||||
// First up assert that our chunk of jit functions doesn't collide with
|
|
||||||
// any other known chunks of jit functions...
|
|
||||||
if let Some((_, prev)) = ranges.range(max..).next() {
|
|
||||||
assert!(prev.start > max);
|
|
||||||
}
|
|
||||||
if let Some((prev_end, _)) = ranges.range(..=min).next_back() {
|
|
||||||
assert!(*prev_end < min);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... then insert our range and assert nothing was there previously
|
|
||||||
let prev = ranges.insert(
|
|
||||||
max,
|
|
||||||
ModuleFrameInfo {
|
|
||||||
start: min,
|
|
||||||
functions,
|
|
||||||
module: module.module().clone(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
assert!(prev.is_none());
|
|
||||||
Some(GlobalFrameInfoRegistration { key: max })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches information about a program counter in a backtrace.
|
|
||||||
///
|
///
|
||||||
/// Returns an object if this `pc` is known to some previously registered
|
/// Returns an object if this `pc` is known to some previously registered
|
||||||
/// module, or returns `None` if no information can be found.
|
/// module, or returns `None` if no information can be found.
|
||||||
pub fn lookup(&self, pc: usize) -> Option<FrameInfo> {
|
pub fn lookup_frame_info(&self, pc: usize) -> Option<FrameInfo> {
|
||||||
let ranges = self.ranges.read().ok()?;
|
let (module, func) = self.func(pc)?;
|
||||||
let (end, info) = ranges.range(pc..).next()?;
|
|
||||||
|
// Use our relative position from the start of the function to find the
|
||||||
|
// machine instruction that corresponds to `pc`, which then allows us to
|
||||||
|
// map that to a wasm original source location.
|
||||||
|
let rel_pos = pc - func.start;
|
||||||
|
let pos = match func
|
||||||
|
.instr_map
|
||||||
|
.instructions
|
||||||
|
.binary_search_by_key(&rel_pos, |map| map.code_offset)
|
||||||
|
{
|
||||||
|
// Exact hit!
|
||||||
|
Ok(pos) => Some(pos),
|
||||||
|
|
||||||
|
// This *would* be at the first slot in the array, so no
|
||||||
|
// instructions cover `pc`.
|
||||||
|
Err(0) => None,
|
||||||
|
|
||||||
|
// This would be at the `nth` slot, so check `n-1` to see if we're
|
||||||
|
// part of that instruction. This happens due to the minus one when
|
||||||
|
// this function is called form trap symbolication, where we don't
|
||||||
|
// always get called with a `pc` that's an exact instruction
|
||||||
|
// boundary.
|
||||||
|
Err(n) => {
|
||||||
|
let instr = &func.instr_map.instructions[n - 1];
|
||||||
|
if instr.code_offset <= rel_pos && rel_pos < instr.code_offset + instr.code_len {
|
||||||
|
Some(n - 1)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// In debug mode for now assert that we found a mapping for `pc` within
|
||||||
|
// the function, because otherwise something is buggy along the way and
|
||||||
|
// not accounting for all the instructions. This isn't super critical
|
||||||
|
// though so we can omit this check in release mode.
|
||||||
|
debug_assert!(pos.is_some(), "failed to find instruction for {:x}", pc);
|
||||||
|
|
||||||
|
let instr = match pos {
|
||||||
|
Some(pos) => func.instr_map.instructions[pos].srcloc,
|
||||||
|
None => func.instr_map.start_srcloc,
|
||||||
|
};
|
||||||
|
Some(FrameInfo {
|
||||||
|
module_name: module.module.name.clone(),
|
||||||
|
func_index: func.index.index() as u32,
|
||||||
|
func_name: module.module.func_names.get(&func.index).cloned(),
|
||||||
|
instr,
|
||||||
|
func_start: func.instr_map.start_srcloc,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetches trap information about a program counter in a backtrace.
|
||||||
|
pub fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> {
|
||||||
|
let (_module, func) = self.func(pc)?;
|
||||||
|
let idx = func
|
||||||
|
.traps
|
||||||
|
.binary_search_by_key(&((pc - func.start) as u32), |info| info.code_offset)
|
||||||
|
.ok()?;
|
||||||
|
Some(&func.traps[idx])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn func(&self, pc: usize) -> Option<(&ModuleFrameInfo, &FunctionInfo)> {
|
||||||
|
let (end, info) = self.ranges.range(pc..).next()?;
|
||||||
if pc < info.start || *end < pc {
|
if pc < info.start || *end < pc {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let (end, (start, func_index)) = info.functions.range(pc..).next()?;
|
let (end, func) = info.functions.range(pc..).next()?;
|
||||||
if pc < *start || *end < pc {
|
if pc < func.start || *end < pc {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(FrameInfo {
|
Some((info, func))
|
||||||
module_name: info.module.name.clone(),
|
|
||||||
func_index: func_index.index() as u32,
|
|
||||||
func_name: info.module.func_names.get(func_index).cloned(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for GlobalFrameInfoRegistration {
|
impl Drop for GlobalFrameInfoRegistration {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Ok(mut map) = FRAME_INFO.ranges.write() {
|
if let Ok(mut info) = FRAME_INFO.write() {
|
||||||
map.remove(&self.key);
|
info.ranges.remove(&self.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Registers a new compiled module's frame information.
|
||||||
|
///
|
||||||
|
/// This function will register the `names` information for all of the
|
||||||
|
/// compiled functions within `module`. If the `module` has no functions
|
||||||
|
/// then `None` will be returned. Otherwise the returned object, when
|
||||||
|
/// dropped, will be used to unregister all name information from this map.
|
||||||
|
pub fn register(module: &CompiledModule) -> Option<GlobalFrameInfoRegistration> {
|
||||||
|
let mut min = usize::max_value();
|
||||||
|
let mut max = 0;
|
||||||
|
let mut functions = BTreeMap::new();
|
||||||
|
for (((i, allocated), traps), instrs) in module
|
||||||
|
.finished_functions()
|
||||||
|
.iter()
|
||||||
|
.zip(module.traps().values())
|
||||||
|
.zip(module.address_transform().values())
|
||||||
|
{
|
||||||
|
let (start, end) = unsafe {
|
||||||
|
let ptr = (**allocated).as_ptr();
|
||||||
|
let len = (**allocated).len();
|
||||||
|
(ptr as usize, ptr as usize + len)
|
||||||
|
};
|
||||||
|
min = cmp::min(min, start);
|
||||||
|
max = cmp::max(max, end);
|
||||||
|
let func = FunctionInfo {
|
||||||
|
start,
|
||||||
|
index: module.module().local.func_index(i),
|
||||||
|
traps: traps.to_vec(),
|
||||||
|
instr_map: (*instrs).clone(),
|
||||||
|
};
|
||||||
|
assert!(functions.insert(end, func).is_none());
|
||||||
|
}
|
||||||
|
if functions.len() == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut info = FRAME_INFO.write().unwrap();
|
||||||
|
// First up assert that our chunk of jit functions doesn't collide with
|
||||||
|
// any other known chunks of jit functions...
|
||||||
|
if let Some((_, prev)) = info.ranges.range(max..).next() {
|
||||||
|
assert!(prev.start > max);
|
||||||
|
}
|
||||||
|
if let Some((prev_end, _)) = info.ranges.range(..=min).next_back() {
|
||||||
|
assert!(*prev_end < min);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... then insert our range and assert nothing was there previously
|
||||||
|
let prev = info.ranges.insert(
|
||||||
|
max,
|
||||||
|
ModuleFrameInfo {
|
||||||
|
start: min,
|
||||||
|
functions,
|
||||||
|
module: module.module().clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert!(prev.is_none());
|
||||||
|
Some(GlobalFrameInfoRegistration { key: max })
|
||||||
|
}
|
||||||
|
|
||||||
/// Description of a frame in a backtrace for a [`Trap`].
|
/// Description of a frame in a backtrace for a [`Trap`].
|
||||||
///
|
///
|
||||||
/// Whenever a WebAssembly trap occurs an instance of [`Trap`] is created. Each
|
/// Whenever a WebAssembly trap occurs an instance of [`Trap`] is created. Each
|
||||||
@@ -137,6 +210,8 @@ pub struct FrameInfo {
|
|||||||
module_name: Option<String>,
|
module_name: Option<String>,
|
||||||
func_index: u32,
|
func_index: u32,
|
||||||
func_name: Option<String>,
|
func_name: Option<String>,
|
||||||
|
func_start: ir::SourceLoc,
|
||||||
|
instr: ir::SourceLoc,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FrameInfo {
|
impl FrameInfo {
|
||||||
@@ -178,4 +253,23 @@ impl FrameInfo {
|
|||||||
pub fn func_name(&self) -> Option<&str> {
|
pub fn func_name(&self) -> Option<&str> {
|
||||||
self.func_name.as_deref()
|
self.func_name.as_deref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the offset within the original wasm module this frame's program
|
||||||
|
/// counter was at.
|
||||||
|
///
|
||||||
|
/// The offset here is the offset from the beginning of the original wasm
|
||||||
|
/// module to the instruction that this frame points to.
|
||||||
|
pub fn module_offset(&self) -> usize {
|
||||||
|
self.instr.bits() as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the offset from the original wasm module's function to this
|
||||||
|
/// frame's program counter.
|
||||||
|
///
|
||||||
|
/// The offset here is the offset from the beginning of the defining
|
||||||
|
/// function of this frame (within the wasm module) to the instruction this
|
||||||
|
/// frame points to.
|
||||||
|
pub fn func_offset(&self) -> usize {
|
||||||
|
(self.instr.bits() - self.func_start.bits()) as usize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::frame_info::{GlobalFrameInfoRegistration, FRAME_INFO};
|
use crate::frame_info::GlobalFrameInfoRegistration;
|
||||||
use crate::runtime::Store;
|
use crate::runtime::Store;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
ExportType, ExternType, FuncType, GlobalType, ImportType, Limits, MemoryType, Mutability,
|
ExportType, ExternType, FuncType, GlobalType, ImportType, Limits, MemoryType, Mutability,
|
||||||
@@ -670,6 +670,6 @@ and for re-adding support for interface types you can see this issue:
|
|||||||
if info.is_some() {
|
if info.is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
*info = Some(FRAME_INFO.register(&self.inner.compiled));
|
*info = Some(super::frame_info::register(&self.inner.compiled));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ pub(crate) fn create_handle(
|
|||||||
unsafe {
|
unsafe {
|
||||||
Ok(InstanceHandle::new(
|
Ok(InstanceHandle::new(
|
||||||
Arc::new(module),
|
Arc::new(module),
|
||||||
store.compiler().trap_registry().register_traps(Vec::new()),
|
|
||||||
finished_functions.into_boxed_slice(),
|
finished_functions.into_boxed_slice(),
|
||||||
trampolines,
|
trampolines,
|
||||||
imports,
|
imports,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use crate::frame_info::FRAME_INFO;
|
use crate::frame_info::{GlobalFrameInfo, FRAME_INFO};
|
||||||
use crate::FrameInfo;
|
use crate::FrameInfo;
|
||||||
use backtrace::Backtrace;
|
use backtrace::Backtrace;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use wasmtime_environ::ir::TrapCode;
|
||||||
|
|
||||||
/// A struct representing an aborted instruction execution, with a message
|
/// A struct representing an aborted instruction execution, with a message
|
||||||
/// indicating the cause.
|
/// indicating the cause.
|
||||||
@@ -29,10 +30,12 @@ impl Trap {
|
|||||||
/// assert_eq!("unexpected error", trap.message());
|
/// assert_eq!("unexpected error", trap.message());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new<I: Into<String>>(message: I) -> Self {
|
pub fn new<I: Into<String>>(message: I) -> Self {
|
||||||
Trap::new_with_trace(message.into(), Backtrace::new_unresolved())
|
let info = FRAME_INFO.read().unwrap();
|
||||||
|
Trap::new_with_trace(&info, None, message.into(), Backtrace::new_unresolved())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_jit(jit: wasmtime_runtime::Trap) -> Self {
|
pub(crate) fn from_jit(jit: wasmtime_runtime::Trap) -> Self {
|
||||||
|
let info = FRAME_INFO.read().unwrap();
|
||||||
match jit {
|
match jit {
|
||||||
wasmtime_runtime::Trap::User(error) => {
|
wasmtime_runtime::Trap::User(error) => {
|
||||||
// Since we're the only one using the wasmtime internals (in
|
// Since we're the only one using the wasmtime internals (in
|
||||||
@@ -47,20 +50,72 @@ impl Trap {
|
|||||||
.downcast()
|
.downcast()
|
||||||
.expect("only `Trap` user errors are supported")
|
.expect("only `Trap` user errors are supported")
|
||||||
}
|
}
|
||||||
wasmtime_runtime::Trap::Wasm { desc, backtrace } => {
|
wasmtime_runtime::Trap::Jit { pc, backtrace } => {
|
||||||
Trap::new_with_trace(desc.to_string(), backtrace)
|
let code = info
|
||||||
|
.lookup_trap_info(pc)
|
||||||
|
.map(|info| info.trap_code)
|
||||||
|
.unwrap_or(TrapCode::StackOverflow);
|
||||||
|
Trap::new_wasm(&info, Some(pc), code, backtrace)
|
||||||
}
|
}
|
||||||
|
wasmtime_runtime::Trap::Wasm {
|
||||||
|
trap_code,
|
||||||
|
backtrace,
|
||||||
|
} => Trap::new_wasm(&info, None, trap_code, backtrace),
|
||||||
wasmtime_runtime::Trap::OOM { backtrace } => {
|
wasmtime_runtime::Trap::OOM { backtrace } => {
|
||||||
Trap::new_with_trace("out of memory".to_string(), backtrace)
|
Trap::new_with_trace(&info, None, "out of memory".to_string(), backtrace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_with_trace(message: String, native_trace: Backtrace) -> Self {
|
fn new_wasm(
|
||||||
|
info: &GlobalFrameInfo,
|
||||||
|
trap_pc: Option<usize>,
|
||||||
|
code: TrapCode,
|
||||||
|
backtrace: Backtrace,
|
||||||
|
) -> Self {
|
||||||
|
use wasmtime_environ::ir::TrapCode::*;
|
||||||
|
let desc = match code {
|
||||||
|
StackOverflow => "call stack exhausted",
|
||||||
|
HeapOutOfBounds => "out of bounds memory access",
|
||||||
|
TableOutOfBounds => "undefined element: out of bounds table access",
|
||||||
|
OutOfBounds => "out of bounds",
|
||||||
|
IndirectCallToNull => "uninitialized element",
|
||||||
|
BadSignature => "indirect call type mismatch",
|
||||||
|
IntegerOverflow => "integer overflow",
|
||||||
|
IntegerDivisionByZero => "integer divide by zero",
|
||||||
|
BadConversionToInteger => "invalid conversion to integer",
|
||||||
|
UnreachableCodeReached => "unreachable",
|
||||||
|
Interrupt => "interrupt",
|
||||||
|
User(_) => unreachable!(),
|
||||||
|
};
|
||||||
|
let msg = format!("wasm trap: {}", desc);
|
||||||
|
Trap::new_with_trace(info, trap_pc, msg, backtrace)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_with_trace(
|
||||||
|
info: &GlobalFrameInfo,
|
||||||
|
trap_pc: Option<usize>,
|
||||||
|
message: String,
|
||||||
|
native_trace: Backtrace,
|
||||||
|
) -> Self {
|
||||||
let mut wasm_trace = Vec::new();
|
let mut wasm_trace = Vec::new();
|
||||||
for frame in native_trace.frames() {
|
for frame in native_trace.frames() {
|
||||||
let pc = frame.ip() as usize;
|
let pc = frame.ip() as usize;
|
||||||
if let Some(info) = FRAME_INFO.lookup(pc) {
|
if pc == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Note that we need to be careful about the pc we pass in here to
|
||||||
|
// lookup frame information. This program counter is used to
|
||||||
|
// translate back to an original source location in the origin wasm
|
||||||
|
// module. If this pc is the exact pc that the trap happened at,
|
||||||
|
// then we look up that pc precisely. Otherwise backtrace
|
||||||
|
// information typically points at the pc *after* the call
|
||||||
|
// instruction (because otherwise it's likely a call instruction on
|
||||||
|
// the stack). In that case we want to lookup information for the
|
||||||
|
// previous instruction (the call instruction) so we subtract one as
|
||||||
|
// the lookup.
|
||||||
|
let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 };
|
||||||
|
if let Some(info) = info.lookup_frame_info(pc_to_lookup) {
|
||||||
wasm_trace.push(info);
|
wasm_trace.push(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,7 +160,7 @@ impl fmt::Display for Trap {
|
|||||||
writeln!(f, "\nwasm backtrace:")?;
|
writeln!(f, "\nwasm backtrace:")?;
|
||||||
for (i, frame) in self.trace().iter().enumerate() {
|
for (i, frame) in self.trace().iter().enumerate() {
|
||||||
let name = frame.module_name().unwrap_or("<unknown>");
|
let name = frame.module_name().unwrap_or("<unknown>");
|
||||||
write!(f, " {}: {}!", i, name)?;
|
write!(f, " {}: {:#6x} - {}!", i, frame.module_offset(), name)?;
|
||||||
match frame.func_name() {
|
match frame.func_name() {
|
||||||
Some(name) => match rustc_demangle::try_demangle(name) {
|
Some(name) => match rustc_demangle::try_demangle(name) {
|
||||||
Ok(name) => write!(f, "{}", name)?,
|
Ok(name) => write!(f, "{}", name)?,
|
||||||
|
|||||||
@@ -59,9 +59,13 @@ fn test_trap_trace() -> Result<()> {
|
|||||||
assert_eq!(trace[0].module_name().unwrap(), "hello_mod");
|
assert_eq!(trace[0].module_name().unwrap(), "hello_mod");
|
||||||
assert_eq!(trace[0].func_index(), 1);
|
assert_eq!(trace[0].func_index(), 1);
|
||||||
assert_eq!(trace[0].func_name(), Some("hello"));
|
assert_eq!(trace[0].func_name(), Some("hello"));
|
||||||
|
assert_eq!(trace[0].func_offset(), 1);
|
||||||
|
assert_eq!(trace[0].module_offset(), 0x26);
|
||||||
assert_eq!(trace[1].module_name().unwrap(), "hello_mod");
|
assert_eq!(trace[1].module_name().unwrap(), "hello_mod");
|
||||||
assert_eq!(trace[1].func_index(), 0);
|
assert_eq!(trace[1].func_index(), 0);
|
||||||
assert_eq!(trace[1].func_name(), None);
|
assert_eq!(trace[1].func_name(), None);
|
||||||
|
assert_eq!(trace[1].func_offset(), 1);
|
||||||
|
assert_eq!(trace[1].module_offset(), 0x21);
|
||||||
assert!(
|
assert!(
|
||||||
e.message().contains("unreachable"),
|
e.message().contains("unreachable"),
|
||||||
"wrong message: {}",
|
"wrong message: {}",
|
||||||
@@ -163,12 +167,12 @@ fn trap_display_pretty() -> Result<()> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
e.to_string(),
|
e.to_string(),
|
||||||
"\
|
"\
|
||||||
wasm trap: unreachable, source location: @0023
|
wasm trap: unreachable
|
||||||
wasm backtrace:
|
wasm backtrace:
|
||||||
0: m!die
|
0: 0x23 - m!die
|
||||||
1: m!<wasm function 1>
|
1: 0x27 - m!<wasm function 1>
|
||||||
2: m!foo
|
2: 0x2c - m!foo
|
||||||
3: m!<wasm function 3>
|
3: 0x31 - m!<wasm function 3>
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -207,14 +211,14 @@ fn trap_display_multi_module() -> Result<()> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
e.to_string(),
|
e.to_string(),
|
||||||
"\
|
"\
|
||||||
wasm trap: unreachable, source location: @0023
|
wasm trap: unreachable
|
||||||
wasm backtrace:
|
wasm backtrace:
|
||||||
0: a!die
|
0: 0x23 - a!die
|
||||||
1: a!<wasm function 1>
|
1: 0x27 - a!<wasm function 1>
|
||||||
2: a!foo
|
2: 0x2c - a!foo
|
||||||
3: a!<wasm function 3>
|
3: 0x31 - a!<wasm function 3>
|
||||||
4: b!middle
|
4: 0x29 - b!middle
|
||||||
5: b!<wasm function 2>
|
5: 0x2e - b!<wasm function 2>
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -371,10 +375,7 @@ fn call_signature_mismatch() -> Result<()> {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.downcast::<Trap>()
|
.downcast::<Trap>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(err.message(), "wasm trap: indirect call type mismatch");
|
||||||
err.message(),
|
|
||||||
"wasm trap: indirect call type mismatch, source location: @0030"
|
|
||||||
);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,12 +401,12 @@ fn start_trap_pretty() -> Result<()> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
e.to_string(),
|
e.to_string(),
|
||||||
"\
|
"\
|
||||||
wasm trap: unreachable, source location: @001d
|
wasm trap: unreachable
|
||||||
wasm backtrace:
|
wasm backtrace:
|
||||||
0: m!die
|
0: 0x1d - m!die
|
||||||
1: m!<wasm function 1>
|
1: 0x21 - m!<wasm function 1>
|
||||||
2: m!foo
|
2: 0x26 - m!foo
|
||||||
3: m!start
|
3: 0x2b - m!start
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -122,8 +122,9 @@ pub extern "C" fn wasmtime_frame_module_name(frame: &wasm_frame_t) -> Option<&wa
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn wasm_frame_func_offset(_arg1: *const wasm_frame_t) -> usize {
|
pub extern "C" fn wasm_frame_func_offset(frame: &wasm_frame_t) -> usize {
|
||||||
unimplemented!("wasm_frame_func_offset")
|
let trap = frame.trap.borrow();
|
||||||
|
trap.trace()[frame.idx].func_offset()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -132,6 +133,7 @@ pub extern "C" fn wasm_frame_instance(_arg1: *const wasm_frame_t) -> *mut wasm_i
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn wasm_frame_module_offset(_arg1: *const wasm_frame_t) -> usize {
|
pub extern "C" fn wasm_frame_module_offset(frame: &wasm_frame_t) -> usize {
|
||||||
unimplemented!("wasm_frame_module_offset")
|
let trap = frame.trap.borrow();
|
||||||
|
trap.trace()[frame.idx].module_offset()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ pub enum RelocationTarget {
|
|||||||
pub type Relocations = PrimaryMap<DefinedFuncIndex, Vec<Relocation>>;
|
pub type Relocations = PrimaryMap<DefinedFuncIndex, Vec<Relocation>>;
|
||||||
|
|
||||||
/// Information about trap.
|
/// Information about trap.
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct TrapInformation {
|
pub struct TrapInformation {
|
||||||
/// The offset of the trapping instruction in native code. It is relative to the beginning of the function.
|
/// The offset of the trapping instruction in native code. It is relative to the beginning of the function.
|
||||||
pub code_offset: binemit::CodeOffset,
|
pub code_offset: binemit::CodeOffset,
|
||||||
|
|||||||
@@ -268,12 +268,7 @@ fn compile(env: CompileEnv<'_>) -> Result<ModuleCacheDataTupleType, CompileError
|
|||||||
CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
|
CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let address_transform = if env.tunables.debug_info {
|
let address_transform = get_function_address_map(&context, input, code_buf.len(), isa);
|
||||||
let body_len = code_buf.len();
|
|
||||||
Some(get_function_address_map(&context, input, body_len, isa))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let frame_layout = if env.tunables.debug_info {
|
let frame_layout = if env.tunables.debug_info {
|
||||||
let (initial_commands, commands) = get_frame_layout(&context, isa);
|
let (initial_commands, commands) = get_frame_layout(&context, isa);
|
||||||
@@ -327,9 +322,7 @@ fn compile(env: CompileEnv<'_>) -> Result<ModuleCacheDataTupleType, CompileError
|
|||||||
unwind_info,
|
unwind_info,
|
||||||
});
|
});
|
||||||
relocations.push(relocs);
|
relocations.push(relocs);
|
||||||
if let Some(address_transform) = address_transform {
|
address_transforms.push(address_transform);
|
||||||
address_transforms.push(address_transform);
|
|
||||||
}
|
|
||||||
value_ranges.push(ranges.unwrap_or_default());
|
value_ranges.push(ranges.unwrap_or_default());
|
||||||
stack_slots.push(sss);
|
stack_slots.push(sss);
|
||||||
traps.push(function_traps);
|
traps.push(function_traps);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::module::{MemoryPlan, MemoryStyle, ModuleLocal, TableStyle};
|
use crate::module::{MemoryPlan, MemoryStyle, ModuleLocal, TableStyle};
|
||||||
use crate::vmoffsets::VMOffsets;
|
use crate::vmoffsets::VMOffsets;
|
||||||
use crate::WASM_PAGE_SIZE;
|
use crate::WASM_PAGE_SIZE;
|
||||||
use cranelift_codegen::cursor::{Cursor, FuncCursor};
|
use cranelift_codegen::cursor::FuncCursor;
|
||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
use cranelift_codegen::ir::condcodes::*;
|
use cranelift_codegen::ir::condcodes::*;
|
||||||
use cranelift_codegen::ir::immediates::{Offset32, Uimm64};
|
use cranelift_codegen::ir::immediates::{Offset32, Uimm64};
|
||||||
@@ -262,8 +262,6 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
|
|||||||
AbiParam::new(I32),
|
AbiParam::new(I32),
|
||||||
// Number of elements to copy.
|
// Number of elements to copy.
|
||||||
AbiParam::new(I32),
|
AbiParam::new(I32),
|
||||||
// Source location.
|
|
||||||
AbiParam::new(I32),
|
|
||||||
],
|
],
|
||||||
returns: vec![],
|
returns: vec![],
|
||||||
call_conv: self.target_config.default_call_conv,
|
call_conv: self.target_config.default_call_conv,
|
||||||
@@ -303,8 +301,6 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
|
|||||||
AbiParam::new(I32),
|
AbiParam::new(I32),
|
||||||
// Number of elements to initialize.
|
// Number of elements to initialize.
|
||||||
AbiParam::new(I32),
|
AbiParam::new(I32),
|
||||||
// Source location.
|
|
||||||
AbiParam::new(I32),
|
|
||||||
],
|
],
|
||||||
returns: vec![],
|
returns: vec![],
|
||||||
call_conv: self.target_config.default_call_conv,
|
call_conv: self.target_config.default_call_conv,
|
||||||
@@ -362,8 +358,6 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
|
|||||||
AbiParam::new(I32),
|
AbiParam::new(I32),
|
||||||
// Length.
|
// Length.
|
||||||
AbiParam::new(I32),
|
AbiParam::new(I32),
|
||||||
// Source location.
|
|
||||||
AbiParam::new(I32),
|
|
||||||
],
|
],
|
||||||
returns: vec![],
|
returns: vec![],
|
||||||
call_conv: self.target_config.default_call_conv,
|
call_conv: self.target_config.default_call_conv,
|
||||||
@@ -407,8 +401,6 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
|
|||||||
AbiParam::new(I32),
|
AbiParam::new(I32),
|
||||||
// Length.
|
// Length.
|
||||||
AbiParam::new(I32),
|
AbiParam::new(I32),
|
||||||
// Source location.
|
|
||||||
AbiParam::new(I32),
|
|
||||||
],
|
],
|
||||||
returns: vec![],
|
returns: vec![],
|
||||||
call_conv: self.target_config.default_call_conv,
|
call_conv: self.target_config.default_call_conv,
|
||||||
@@ -454,8 +446,6 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
|
|||||||
AbiParam::new(I32),
|
AbiParam::new(I32),
|
||||||
// Length.
|
// Length.
|
||||||
AbiParam::new(I32),
|
AbiParam::new(I32),
|
||||||
// Source location.
|
|
||||||
AbiParam::new(I32),
|
|
||||||
],
|
],
|
||||||
returns: vec![],
|
returns: vec![],
|
||||||
call_conv: self.target_config.default_call_conv,
|
call_conv: self.target_config.default_call_conv,
|
||||||
@@ -1102,13 +1092,10 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
|
|
||||||
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
|
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
|
||||||
|
|
||||||
let src_loc = pos.srcloc();
|
|
||||||
let src_loc_arg = pos.ins().iconst(I32, src_loc.bits() as i64);
|
|
||||||
|
|
||||||
pos.ins().call_indirect(
|
pos.ins().call_indirect(
|
||||||
func_sig,
|
func_sig,
|
||||||
func_addr,
|
func_addr,
|
||||||
&[vmctx, memory_index_arg, dst, src, len, src_loc_arg],
|
&[vmctx, memory_index_arg, dst, src, len],
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1130,13 +1117,10 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
|
|
||||||
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
|
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
|
||||||
|
|
||||||
let src_loc = pos.srcloc();
|
|
||||||
let src_loc_arg = pos.ins().iconst(I32, src_loc.bits() as i64);
|
|
||||||
|
|
||||||
pos.ins().call_indirect(
|
pos.ins().call_indirect(
|
||||||
func_sig,
|
func_sig,
|
||||||
func_addr,
|
func_addr,
|
||||||
&[vmctx, memory_index_arg, dst, val, len, src_loc_arg],
|
&[vmctx, memory_index_arg, dst, val, len],
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1156,23 +1140,13 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
|
|
||||||
let memory_index_arg = pos.ins().iconst(I32, memory_index.index() as i64);
|
let memory_index_arg = pos.ins().iconst(I32, memory_index.index() as i64);
|
||||||
let seg_index_arg = pos.ins().iconst(I32, seg_index as i64);
|
let seg_index_arg = pos.ins().iconst(I32, seg_index as i64);
|
||||||
let src_loc = pos.srcloc();
|
|
||||||
let src_loc_arg = pos.ins().iconst(I32, src_loc.bits() as i64);
|
|
||||||
|
|
||||||
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
|
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
|
||||||
|
|
||||||
pos.ins().call_indirect(
|
pos.ins().call_indirect(
|
||||||
func_sig,
|
func_sig,
|
||||||
func_addr,
|
func_addr,
|
||||||
&[
|
&[vmctx, memory_index_arg, seg_index_arg, dst, src, len],
|
||||||
vmctx,
|
|
||||||
memory_index_arg,
|
|
||||||
seg_index_arg,
|
|
||||||
dst,
|
|
||||||
src,
|
|
||||||
len,
|
|
||||||
src_loc_arg,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1215,9 +1189,6 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
let dst_table_index_arg = pos.ins().iconst(I32, dst_table_index_arg as i64);
|
let dst_table_index_arg = pos.ins().iconst(I32, dst_table_index_arg as i64);
|
||||||
let src_table_index_arg = pos.ins().iconst(I32, src_table_index_arg as i64);
|
let src_table_index_arg = pos.ins().iconst(I32, src_table_index_arg as i64);
|
||||||
|
|
||||||
let src_loc = pos.srcloc();
|
|
||||||
let src_loc_arg = pos.ins().iconst(I32, src_loc.bits() as i64);
|
|
||||||
|
|
||||||
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
|
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
|
||||||
|
|
||||||
pos.ins().call_indirect(
|
pos.ins().call_indirect(
|
||||||
@@ -1230,7 +1201,6 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
dst,
|
dst,
|
||||||
src,
|
src,
|
||||||
len,
|
len,
|
||||||
src_loc_arg,
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1253,23 +1223,12 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
let table_index_arg = pos.ins().iconst(I32, table_index_arg as i64);
|
let table_index_arg = pos.ins().iconst(I32, table_index_arg as i64);
|
||||||
let seg_index_arg = pos.ins().iconst(I32, seg_index as i64);
|
let seg_index_arg = pos.ins().iconst(I32, seg_index as i64);
|
||||||
|
|
||||||
let src_loc = pos.srcloc();
|
|
||||||
let src_loc_arg = pos.ins().iconst(I32, src_loc.bits() as i64);
|
|
||||||
|
|
||||||
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
|
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
|
||||||
|
|
||||||
pos.ins().call_indirect(
|
pos.ins().call_indirect(
|
||||||
func_sig,
|
func_sig,
|
||||||
func_addr,
|
func_addr,
|
||||||
&[
|
&[vmctx, table_index_arg, seg_index_arg, dst, src, len],
|
||||||
vmctx,
|
|
||||||
table_index_arg,
|
|
||||||
seg_index_arg,
|
|
||||||
dst,
|
|
||||||
src,
|
|
||||||
len,
|
|
||||||
src_loc_arg,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -9,19 +9,17 @@ use cranelift_codegen::Context;
|
|||||||
use cranelift_codegen::{binemit, ir};
|
use cranelift_codegen::{binemit, ir};
|
||||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryFrom;
|
|
||||||
use wasmtime_debug::{emit_debugsections_image, DebugInfoData};
|
use wasmtime_debug::{emit_debugsections_image, DebugInfoData};
|
||||||
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
|
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
|
||||||
use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa};
|
use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa};
|
||||||
use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex, MemoryIndex};
|
use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex, MemoryIndex};
|
||||||
use wasmtime_environ::{
|
use wasmtime_environ::{
|
||||||
CacheConfig, CompileError, CompiledFunction, CompiledFunctionUnwindInfo, Compiler as _C,
|
CacheConfig, CompileError, CompiledFunction, CompiledFunctionUnwindInfo, Compiler as _C,
|
||||||
ModuleMemoryOffset, ModuleTranslation, ModuleVmctxInfo, Relocation, RelocationTarget,
|
ModuleAddressMap, ModuleMemoryOffset, ModuleTranslation, ModuleVmctxInfo, Relocation,
|
||||||
Relocations, Traps, Tunables, VMOffsets,
|
RelocationTarget, Relocations, Traps, Tunables, VMOffsets,
|
||||||
};
|
};
|
||||||
use wasmtime_runtime::{
|
use wasmtime_runtime::{
|
||||||
InstantiationError, SignatureRegistry, TrapRegistration, TrapRegistry, VMFunctionBody,
|
InstantiationError, SignatureRegistry, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline,
|
||||||
VMSharedSignatureIndex, VMTrampoline,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Select which kind of compilation to use.
|
/// Select which kind of compilation to use.
|
||||||
@@ -50,7 +48,6 @@ pub struct Compiler {
|
|||||||
isa: Box<dyn TargetIsa>,
|
isa: Box<dyn TargetIsa>,
|
||||||
|
|
||||||
code_memory: CodeMemory,
|
code_memory: CodeMemory,
|
||||||
trap_registry: TrapRegistry,
|
|
||||||
signatures: SignatureRegistry,
|
signatures: SignatureRegistry,
|
||||||
strategy: CompilationStrategy,
|
strategy: CompilationStrategy,
|
||||||
cache_config: CacheConfig,
|
cache_config: CacheConfig,
|
||||||
@@ -70,7 +67,6 @@ impl Compiler {
|
|||||||
code_memory: CodeMemory::new(),
|
code_memory: CodeMemory::new(),
|
||||||
signatures: SignatureRegistry::new(),
|
signatures: SignatureRegistry::new(),
|
||||||
strategy,
|
strategy,
|
||||||
trap_registry: TrapRegistry::default(),
|
|
||||||
cache_config,
|
cache_config,
|
||||||
tunables,
|
tunables,
|
||||||
}
|
}
|
||||||
@@ -85,7 +81,8 @@ pub struct Compilation {
|
|||||||
pub trampoline_relocations: HashMap<VMSharedSignatureIndex, Vec<Relocation>>,
|
pub trampoline_relocations: HashMap<VMSharedSignatureIndex, Vec<Relocation>>,
|
||||||
pub jt_offsets: PrimaryMap<DefinedFuncIndex, ir::JumpTableOffsets>,
|
pub jt_offsets: PrimaryMap<DefinedFuncIndex, ir::JumpTableOffsets>,
|
||||||
pub dbg_image: Option<Vec<u8>>,
|
pub dbg_image: Option<Vec<u8>>,
|
||||||
pub trap_registration: TrapRegistration,
|
pub traps: Traps,
|
||||||
|
pub address_transform: ModuleAddressMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Compiler {
|
impl Compiler {
|
||||||
@@ -144,11 +141,6 @@ impl Compiler {
|
|||||||
)))
|
)))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Create a registration value for all traps in our allocated
|
|
||||||
// functions. This registration will allow us to map a trapping PC
|
|
||||||
// value to what the trap actually means if it came from JIT code.
|
|
||||||
let trap_registration = register_traps(&finished_functions, &traps, &self.trap_registry);
|
|
||||||
|
|
||||||
// Eagerly generate a entry trampoline for every type signature in the
|
// Eagerly generate a entry trampoline for every type signature in the
|
||||||
// module. This should be "relatively lightweight" for most modules and
|
// module. This should be "relatively lightweight" for most modules and
|
||||||
// guarantees that all functions (including indirect ones through
|
// guarantees that all functions (including indirect ones through
|
||||||
@@ -228,7 +220,8 @@ impl Compiler {
|
|||||||
trampoline_relocations,
|
trampoline_relocations,
|
||||||
jt_offsets,
|
jt_offsets,
|
||||||
dbg_image,
|
dbg_image,
|
||||||
trap_registration,
|
traps,
|
||||||
|
address_transform,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,11 +234,6 @@ impl Compiler {
|
|||||||
pub fn signatures(&self) -> &SignatureRegistry {
|
pub fn signatures(&self) -> &SignatureRegistry {
|
||||||
&self.signatures
|
&self.signatures
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shared registration of trap information
|
|
||||||
pub fn trap_registry(&self) -> &TrapRegistry {
|
|
||||||
&self.trap_registry
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a trampoline for invoking a function.
|
/// Create a trampoline for invoking a function.
|
||||||
@@ -392,26 +380,6 @@ fn allocate_functions(
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_traps(
|
|
||||||
allocated_functions: &PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
|
||||||
traps: &Traps,
|
|
||||||
registry: &TrapRegistry,
|
|
||||||
) -> TrapRegistration {
|
|
||||||
let traps =
|
|
||||||
allocated_functions
|
|
||||||
.values()
|
|
||||||
.zip(traps.values())
|
|
||||||
.flat_map(|(func_addr, func_traps)| {
|
|
||||||
func_traps.iter().map(move |trap_desc| {
|
|
||||||
let func_addr = *func_addr as *const u8 as usize;
|
|
||||||
let offset = usize::try_from(trap_desc.code_offset).unwrap();
|
|
||||||
let trap_addr = func_addr + offset;
|
|
||||||
(trap_addr, trap_desc.source_loc, trap_desc.trap_code)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
registry.register_traps(traps)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// We don't expect trampoline compilation to produce many relocations, so
|
/// We don't expect trampoline compilation to produce many relocations, so
|
||||||
/// this `RelocSink` just asserts that it doesn't recieve most of them, but
|
/// this `RelocSink` just asserts that it doesn't recieve most of them, but
|
||||||
/// handles libcall ones.
|
/// handles libcall ones.
|
||||||
|
|||||||
@@ -16,12 +16,13 @@ use wasmtime_debug::read_debuginfo;
|
|||||||
use wasmtime_environ::entity::{BoxedSlice, PrimaryMap};
|
use wasmtime_environ::entity::{BoxedSlice, PrimaryMap};
|
||||||
use wasmtime_environ::wasm::{DefinedFuncIndex, SignatureIndex};
|
use wasmtime_environ::wasm::{DefinedFuncIndex, SignatureIndex};
|
||||||
use wasmtime_environ::{
|
use wasmtime_environ::{
|
||||||
CompileError, DataInitializer, DataInitializerLocation, Module, ModuleEnvironment,
|
CompileError, DataInitializer, DataInitializerLocation, Module, ModuleAddressMap,
|
||||||
|
ModuleEnvironment, Traps,
|
||||||
};
|
};
|
||||||
use wasmtime_profiling::ProfilingAgent;
|
use wasmtime_profiling::ProfilingAgent;
|
||||||
use wasmtime_runtime::{
|
use wasmtime_runtime::{
|
||||||
GdbJitImageRegistration, InstanceHandle, InstantiationError, RuntimeMemoryCreator,
|
GdbJitImageRegistration, InstanceHandle, InstantiationError, RuntimeMemoryCreator,
|
||||||
SignatureRegistry, TrapRegistration, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline,
|
SignatureRegistry, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An error condition while setting up a wasm instance, be it validation,
|
/// An error condition while setting up a wasm instance, be it validation,
|
||||||
@@ -55,7 +56,8 @@ struct RawCompiledModule<'data> {
|
|||||||
data_initializers: Box<[DataInitializer<'data>]>,
|
data_initializers: Box<[DataInitializer<'data>]>,
|
||||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||||
dbg_jit_registration: Option<GdbJitImageRegistration>,
|
dbg_jit_registration: Option<GdbJitImageRegistration>,
|
||||||
trap_registration: TrapRegistration,
|
traps: Traps,
|
||||||
|
address_transform: ModuleAddressMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'data> RawCompiledModule<'data> {
|
impl<'data> RawCompiledModule<'data> {
|
||||||
@@ -119,7 +121,8 @@ impl<'data> RawCompiledModule<'data> {
|
|||||||
data_initializers: translation.data_initializers.into_boxed_slice(),
|
data_initializers: translation.data_initializers.into_boxed_slice(),
|
||||||
signatures: signatures.into_boxed_slice(),
|
signatures: signatures.into_boxed_slice(),
|
||||||
dbg_jit_registration,
|
dbg_jit_registration,
|
||||||
trap_registration: compilation.trap_registration,
|
traps: compilation.traps,
|
||||||
|
address_transform: compilation.address_transform,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,7 +135,8 @@ pub struct CompiledModule {
|
|||||||
data_initializers: Box<[OwnedDataInitializer]>,
|
data_initializers: Box<[OwnedDataInitializer]>,
|
||||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||||
dbg_jit_registration: Option<Rc<GdbJitImageRegistration>>,
|
dbg_jit_registration: Option<Rc<GdbJitImageRegistration>>,
|
||||||
trap_registration: TrapRegistration,
|
traps: Traps,
|
||||||
|
address_transform: ModuleAddressMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompiledModule {
|
impl CompiledModule {
|
||||||
@@ -155,7 +159,8 @@ impl CompiledModule {
|
|||||||
.into_boxed_slice(),
|
.into_boxed_slice(),
|
||||||
raw.signatures.clone(),
|
raw.signatures.clone(),
|
||||||
raw.dbg_jit_registration,
|
raw.dbg_jit_registration,
|
||||||
raw.trap_registration,
|
raw.traps,
|
||||||
|
raw.address_transform,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +172,8 @@ impl CompiledModule {
|
|||||||
data_initializers: Box<[OwnedDataInitializer]>,
|
data_initializers: Box<[OwnedDataInitializer]>,
|
||||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||||
dbg_jit_registration: Option<GdbJitImageRegistration>,
|
dbg_jit_registration: Option<GdbJitImageRegistration>,
|
||||||
trap_registration: TrapRegistration,
|
traps: Traps,
|
||||||
|
address_transform: ModuleAddressMap,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
module: Arc::new(module),
|
module: Arc::new(module),
|
||||||
@@ -176,7 +182,8 @@ impl CompiledModule {
|
|||||||
data_initializers,
|
data_initializers,
|
||||||
signatures,
|
signatures,
|
||||||
dbg_jit_registration: dbg_jit_registration.map(Rc::new),
|
dbg_jit_registration: dbg_jit_registration.map(Rc::new),
|
||||||
trap_registration,
|
traps,
|
||||||
|
address_transform,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +214,6 @@ impl CompiledModule {
|
|||||||
let imports = resolve_imports(&self.module, &sig_registry, resolver)?;
|
let imports = resolve_imports(&self.module, &sig_registry, resolver)?;
|
||||||
InstanceHandle::new(
|
InstanceHandle::new(
|
||||||
Arc::clone(&self.module),
|
Arc::clone(&self.module),
|
||||||
self.trap_registration.clone(),
|
|
||||||
self.finished_functions.clone(),
|
self.finished_functions.clone(),
|
||||||
self.trampolines.clone(),
|
self.trampolines.clone(),
|
||||||
imports,
|
imports,
|
||||||
@@ -239,6 +245,16 @@ impl CompiledModule {
|
|||||||
pub fn finished_functions(&self) -> &BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]> {
|
pub fn finished_functions(&self) -> &BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]> {
|
||||||
&self.finished_functions
|
&self.finished_functions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the a map for all traps in this module.
|
||||||
|
pub fn traps(&self) -> &Traps {
|
||||||
|
&self.traps
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a map of compiled addresses back to original bytecode offsets.
|
||||||
|
pub fn address_transform(&self) -> &ModuleAddressMap {
|
||||||
|
&self.address_transform
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Similar to `DataInitializer`, but owns its own copy of the data rather
|
/// Similar to `DataInitializer`, but owns its own copy of the data rather
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ use crate::vmcontext::{
|
|||||||
VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex,
|
VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex,
|
||||||
VMTableDefinition, VMTableImport, VMTrampoline,
|
VMTableDefinition, VMTableImport, VMTrampoline,
|
||||||
};
|
};
|
||||||
use crate::TrapRegistration;
|
|
||||||
use crate::{ExportFunction, ExportGlobal, ExportMemory, ExportTable};
|
use crate::{ExportFunction, ExportGlobal, ExportMemory, ExportTable};
|
||||||
use memoffset::offset_of;
|
use memoffset::offset_of;
|
||||||
use more_asserts::assert_lt;
|
use more_asserts::assert_lt;
|
||||||
@@ -111,10 +110,6 @@ pub(crate) struct Instance {
|
|||||||
/// Handler run when `SIGBUS`, `SIGFPE`, `SIGILL`, or `SIGSEGV` are caught by the instance thread.
|
/// Handler run when `SIGBUS`, `SIGFPE`, `SIGILL`, or `SIGSEGV` are caught by the instance thread.
|
||||||
pub(crate) signal_handler: Cell<Option<Box<SignalHandler>>>,
|
pub(crate) signal_handler: Cell<Option<Box<SignalHandler>>>,
|
||||||
|
|
||||||
/// Handle to our registration of traps so signals know what trap to return
|
|
||||||
/// when a segfault/sigill happens.
|
|
||||||
pub(crate) trap_registration: TrapRegistration,
|
|
||||||
|
|
||||||
/// Additional context used by compiled wasm code. This field is last, and
|
/// Additional context used by compiled wasm code. This field is last, and
|
||||||
/// represents a dynamically-sized array that extends beyond the nominal
|
/// represents a dynamically-sized array that extends beyond the nominal
|
||||||
/// end of the struct (similar to a flexible array member).
|
/// end of the struct (similar to a flexible array member).
|
||||||
@@ -603,7 +598,6 @@ impl Instance {
|
|||||||
dst: u32,
|
dst: u32,
|
||||||
src: u32,
|
src: u32,
|
||||||
len: u32,
|
len: u32,
|
||||||
source_loc: ir::SourceLoc,
|
|
||||||
) -> Result<(), Trap> {
|
) -> Result<(), Trap> {
|
||||||
// https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-init
|
// https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-init
|
||||||
|
|
||||||
@@ -619,7 +613,7 @@ impl Instance {
|
|||||||
.map_or(true, |n| n as usize > elem.len())
|
.map_or(true, |n| n as usize > elem.len())
|
||||||
|| dst.checked_add(len).map_or(true, |m| m > table.size())
|
|| dst.checked_add(len).map_or(true, |m| m > table.size())
|
||||||
{
|
{
|
||||||
return Err(Trap::wasm(source_loc, ir::TrapCode::TableOutOfBounds));
|
return Err(Trap::wasm(ir::TrapCode::TableOutOfBounds));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(#983): investigate replacing this get/set loop with a `memcpy`.
|
// TODO(#983): investigate replacing this get/set loop with a `memcpy`.
|
||||||
@@ -654,7 +648,6 @@ impl Instance {
|
|||||||
dst: u32,
|
dst: u32,
|
||||||
src: u32,
|
src: u32,
|
||||||
len: u32,
|
len: u32,
|
||||||
source_loc: ir::SourceLoc,
|
|
||||||
) -> Result<(), Trap> {
|
) -> Result<(), Trap> {
|
||||||
// https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy
|
// https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy
|
||||||
|
|
||||||
@@ -667,7 +660,7 @@ impl Instance {
|
|||||||
.checked_add(len)
|
.checked_add(len)
|
||||||
.map_or(true, |m| m as usize > memory.current_length)
|
.map_or(true, |m| m as usize > memory.current_length)
|
||||||
{
|
{
|
||||||
return Err(Trap::wasm(source_loc, ir::TrapCode::HeapOutOfBounds));
|
return Err(Trap::wasm(ir::TrapCode::HeapOutOfBounds));
|
||||||
}
|
}
|
||||||
|
|
||||||
let dst = usize::try_from(dst).unwrap();
|
let dst = usize::try_from(dst).unwrap();
|
||||||
@@ -691,14 +684,13 @@ impl Instance {
|
|||||||
dst: u32,
|
dst: u32,
|
||||||
src: u32,
|
src: u32,
|
||||||
len: u32,
|
len: u32,
|
||||||
source_loc: ir::SourceLoc,
|
|
||||||
) -> Result<(), Trap> {
|
) -> Result<(), Trap> {
|
||||||
let import = self.imported_memory(memory_index);
|
let import = self.imported_memory(memory_index);
|
||||||
unsafe {
|
unsafe {
|
||||||
let foreign_instance = (&*import.vmctx).instance();
|
let foreign_instance = (&*import.vmctx).instance();
|
||||||
let foreign_memory = &*import.from;
|
let foreign_memory = &*import.from;
|
||||||
let foreign_index = foreign_instance.memory_index(foreign_memory);
|
let foreign_index = foreign_instance.memory_index(foreign_memory);
|
||||||
foreign_instance.defined_memory_copy(foreign_index, dst, src, len, source_loc)
|
foreign_instance.defined_memory_copy(foreign_index, dst, src, len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -713,7 +705,6 @@ impl Instance {
|
|||||||
dst: u32,
|
dst: u32,
|
||||||
val: u32,
|
val: u32,
|
||||||
len: u32,
|
len: u32,
|
||||||
source_loc: ir::SourceLoc,
|
|
||||||
) -> Result<(), Trap> {
|
) -> Result<(), Trap> {
|
||||||
let memory = self.memory(memory_index);
|
let memory = self.memory(memory_index);
|
||||||
|
|
||||||
@@ -721,7 +712,7 @@ impl Instance {
|
|||||||
.checked_add(len)
|
.checked_add(len)
|
||||||
.map_or(true, |m| m as usize > memory.current_length)
|
.map_or(true, |m| m as usize > memory.current_length)
|
||||||
{
|
{
|
||||||
return Err(Trap::wasm(source_loc, ir::TrapCode::HeapOutOfBounds));
|
return Err(Trap::wasm(ir::TrapCode::HeapOutOfBounds));
|
||||||
}
|
}
|
||||||
|
|
||||||
let dst = isize::try_from(dst).unwrap();
|
let dst = isize::try_from(dst).unwrap();
|
||||||
@@ -748,14 +739,13 @@ impl Instance {
|
|||||||
dst: u32,
|
dst: u32,
|
||||||
val: u32,
|
val: u32,
|
||||||
len: u32,
|
len: u32,
|
||||||
source_loc: ir::SourceLoc,
|
|
||||||
) -> Result<(), Trap> {
|
) -> Result<(), Trap> {
|
||||||
let import = self.imported_memory(memory_index);
|
let import = self.imported_memory(memory_index);
|
||||||
unsafe {
|
unsafe {
|
||||||
let foreign_instance = (&*import.vmctx).instance();
|
let foreign_instance = (&*import.vmctx).instance();
|
||||||
let foreign_memory = &*import.from;
|
let foreign_memory = &*import.from;
|
||||||
let foreign_index = foreign_instance.memory_index(foreign_memory);
|
let foreign_index = foreign_instance.memory_index(foreign_memory);
|
||||||
foreign_instance.defined_memory_fill(foreign_index, dst, val, len, source_loc)
|
foreign_instance.defined_memory_fill(foreign_index, dst, val, len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -773,7 +763,6 @@ impl Instance {
|
|||||||
dst: u32,
|
dst: u32,
|
||||||
src: u32,
|
src: u32,
|
||||||
len: u32,
|
len: u32,
|
||||||
source_loc: ir::SourceLoc,
|
|
||||||
) -> Result<(), Trap> {
|
) -> Result<(), Trap> {
|
||||||
// https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-memory-init
|
// https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-memory-init
|
||||||
|
|
||||||
@@ -790,7 +779,7 @@ impl Instance {
|
|||||||
.checked_add(len)
|
.checked_add(len)
|
||||||
.map_or(true, |m| m as usize > memory.current_length)
|
.map_or(true, |m| m as usize > memory.current_length)
|
||||||
{
|
{
|
||||||
return Err(Trap::wasm(source_loc, ir::TrapCode::HeapOutOfBounds));
|
return Err(Trap::wasm(ir::TrapCode::HeapOutOfBounds));
|
||||||
}
|
}
|
||||||
|
|
||||||
let src_slice = &data[src as usize..(src + len) as usize];
|
let src_slice = &data[src as usize..(src + len) as usize];
|
||||||
@@ -859,7 +848,6 @@ impl InstanceHandle {
|
|||||||
/// safety.
|
/// safety.
|
||||||
pub unsafe fn new(
|
pub unsafe fn new(
|
||||||
module: Arc<Module>,
|
module: Arc<Module>,
|
||||||
trap_registration: TrapRegistration,
|
|
||||||
finished_functions: BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
finished_functions: BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||||
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
||||||
imports: Imports,
|
imports: Imports,
|
||||||
@@ -906,7 +894,6 @@ impl InstanceHandle {
|
|||||||
dbg_jit_registration,
|
dbg_jit_registration,
|
||||||
host_state,
|
host_state,
|
||||||
signal_handler: Cell::new(None),
|
signal_handler: Cell::new(None),
|
||||||
trap_registration,
|
|
||||||
vmctx: VMContext {},
|
vmctx: VMContext {},
|
||||||
};
|
};
|
||||||
let layout = instance.alloc_layout();
|
let layout = instance.alloc_layout();
|
||||||
@@ -1256,7 +1243,6 @@ fn initialize_tables(instance: &Instance) -> Result<(), InstantiationError> {
|
|||||||
.map_or(true, |end| end > table.size() as usize)
|
.map_or(true, |end| end > table.size() as usize)
|
||||||
{
|
{
|
||||||
return Err(InstantiationError::Trap(Trap::wasm(
|
return Err(InstantiationError::Trap(Trap::wasm(
|
||||||
ir::SourceLoc::default(),
|
|
||||||
ir::TrapCode::HeapOutOfBounds,
|
ir::TrapCode::HeapOutOfBounds,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
@@ -1332,7 +1318,6 @@ fn initialize_memories(
|
|||||||
.map_or(true, |end| end > memory.current_length)
|
.map_or(true, |end| end > memory.current_length)
|
||||||
{
|
{
|
||||||
return Err(InstantiationError::Trap(Trap::wasm(
|
return Err(InstantiationError::Trap(Trap::wasm(
|
||||||
ir::SourceLoc::default(),
|
|
||||||
ir::TrapCode::HeapOutOfBounds,
|
ir::TrapCode::HeapOutOfBounds,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
@@ -1407,9 +1392,9 @@ pub enum InstantiationError {
|
|||||||
|
|
||||||
/// A trap ocurred during instantiation, after linking.
|
/// A trap ocurred during instantiation, after linking.
|
||||||
#[error("Trap occurred during instantiation")]
|
#[error("Trap occurred during instantiation")]
|
||||||
Trap(#[source] Trap),
|
Trap(Trap),
|
||||||
|
|
||||||
/// A compilation error occured.
|
/// A compilation error occured.
|
||||||
#[error("Trap occurred while invoking start function")]
|
#[error("Trap occurred while invoking start function")]
|
||||||
StartTrap(#[source] Trap),
|
StartTrap(Trap),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ mod memory;
|
|||||||
mod mmap;
|
mod mmap;
|
||||||
mod sig_registry;
|
mod sig_registry;
|
||||||
mod table;
|
mod table;
|
||||||
mod trap_registry;
|
|
||||||
mod traphandlers;
|
mod traphandlers;
|
||||||
mod vmcontext;
|
mod vmcontext;
|
||||||
|
|
||||||
@@ -44,7 +43,6 @@ pub use crate::memory::{RuntimeLinearMemory, RuntimeMemoryCreator};
|
|||||||
pub use crate::mmap::Mmap;
|
pub use crate::mmap::Mmap;
|
||||||
pub use crate::sig_registry::SignatureRegistry;
|
pub use crate::sig_registry::SignatureRegistry;
|
||||||
pub use crate::table::Table;
|
pub use crate::table::Table;
|
||||||
pub use crate::trap_registry::{TrapDescription, TrapRegistration, TrapRegistry};
|
|
||||||
pub use crate::traphandlers::resume_panic;
|
pub use crate::traphandlers::resume_panic;
|
||||||
pub use crate::traphandlers::{catch_traps, raise_lib_trap, raise_user_trap, Trap};
|
pub use crate::traphandlers::{catch_traps, raise_lib_trap, raise_user_trap, Trap};
|
||||||
pub use crate::vmcontext::{
|
pub use crate::vmcontext::{
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
use crate::table::Table;
|
use crate::table::Table;
|
||||||
use crate::traphandlers::raise_lib_trap;
|
use crate::traphandlers::raise_lib_trap;
|
||||||
use crate::vmcontext::VMContext;
|
use crate::vmcontext::VMContext;
|
||||||
use wasmtime_environ::ir;
|
|
||||||
use wasmtime_environ::wasm::{DataIndex, DefinedMemoryIndex, ElemIndex, MemoryIndex, TableIndex};
|
use wasmtime_environ::wasm::{DataIndex, DefinedMemoryIndex, ElemIndex, MemoryIndex, TableIndex};
|
||||||
|
|
||||||
/// Implementation of f32.ceil
|
/// Implementation of f32.ceil
|
||||||
@@ -175,16 +174,14 @@ pub unsafe extern "C" fn wasmtime_table_copy(
|
|||||||
dst: u32,
|
dst: u32,
|
||||||
src: u32,
|
src: u32,
|
||||||
len: u32,
|
len: u32,
|
||||||
source_loc: u32,
|
|
||||||
) {
|
) {
|
||||||
let result = {
|
let result = {
|
||||||
let dst_table_index = TableIndex::from_u32(dst_table_index);
|
let dst_table_index = TableIndex::from_u32(dst_table_index);
|
||||||
let src_table_index = TableIndex::from_u32(src_table_index);
|
let src_table_index = TableIndex::from_u32(src_table_index);
|
||||||
let source_loc = ir::SourceLoc::new(source_loc);
|
|
||||||
let instance = (&mut *vmctx).instance();
|
let instance = (&mut *vmctx).instance();
|
||||||
let dst_table = instance.get_table(dst_table_index);
|
let dst_table = instance.get_table(dst_table_index);
|
||||||
let src_table = instance.get_table(src_table_index);
|
let src_table = instance.get_table(src_table_index);
|
||||||
Table::copy(dst_table, src_table, dst, src, len, source_loc)
|
Table::copy(dst_table, src_table, dst, src, len)
|
||||||
};
|
};
|
||||||
if let Err(trap) = result {
|
if let Err(trap) = result {
|
||||||
raise_lib_trap(trap);
|
raise_lib_trap(trap);
|
||||||
@@ -199,14 +196,12 @@ pub unsafe extern "C" fn wasmtime_table_init(
|
|||||||
dst: u32,
|
dst: u32,
|
||||||
src: u32,
|
src: u32,
|
||||||
len: u32,
|
len: u32,
|
||||||
source_loc: u32,
|
|
||||||
) {
|
) {
|
||||||
let result = {
|
let result = {
|
||||||
let table_index = TableIndex::from_u32(table_index);
|
let table_index = TableIndex::from_u32(table_index);
|
||||||
let source_loc = ir::SourceLoc::new(source_loc);
|
|
||||||
let elem_index = ElemIndex::from_u32(elem_index);
|
let elem_index = ElemIndex::from_u32(elem_index);
|
||||||
let instance = (&mut *vmctx).instance();
|
let instance = (&mut *vmctx).instance();
|
||||||
instance.table_init(table_index, elem_index, dst, src, len, source_loc)
|
instance.table_init(table_index, elem_index, dst, src, len)
|
||||||
};
|
};
|
||||||
if let Err(trap) = result {
|
if let Err(trap) = result {
|
||||||
raise_lib_trap(trap);
|
raise_lib_trap(trap);
|
||||||
@@ -227,13 +222,11 @@ pub unsafe extern "C" fn wasmtime_defined_memory_copy(
|
|||||||
dst: u32,
|
dst: u32,
|
||||||
src: u32,
|
src: u32,
|
||||||
len: u32,
|
len: u32,
|
||||||
source_loc: u32,
|
|
||||||
) {
|
) {
|
||||||
let result = {
|
let result = {
|
||||||
let memory_index = DefinedMemoryIndex::from_u32(memory_index);
|
let memory_index = DefinedMemoryIndex::from_u32(memory_index);
|
||||||
let source_loc = ir::SourceLoc::new(source_loc);
|
|
||||||
let instance = (&mut *vmctx).instance();
|
let instance = (&mut *vmctx).instance();
|
||||||
instance.defined_memory_copy(memory_index, dst, src, len, source_loc)
|
instance.defined_memory_copy(memory_index, dst, src, len)
|
||||||
};
|
};
|
||||||
if let Err(trap) = result {
|
if let Err(trap) = result {
|
||||||
raise_lib_trap(trap);
|
raise_lib_trap(trap);
|
||||||
@@ -247,13 +240,11 @@ pub unsafe extern "C" fn wasmtime_imported_memory_copy(
|
|||||||
dst: u32,
|
dst: u32,
|
||||||
src: u32,
|
src: u32,
|
||||||
len: u32,
|
len: u32,
|
||||||
source_loc: u32,
|
|
||||||
) {
|
) {
|
||||||
let result = {
|
let result = {
|
||||||
let memory_index = MemoryIndex::from_u32(memory_index);
|
let memory_index = MemoryIndex::from_u32(memory_index);
|
||||||
let source_loc = ir::SourceLoc::new(source_loc);
|
|
||||||
let instance = (&mut *vmctx).instance();
|
let instance = (&mut *vmctx).instance();
|
||||||
instance.imported_memory_copy(memory_index, dst, src, len, source_loc)
|
instance.imported_memory_copy(memory_index, dst, src, len)
|
||||||
};
|
};
|
||||||
if let Err(trap) = result {
|
if let Err(trap) = result {
|
||||||
raise_lib_trap(trap);
|
raise_lib_trap(trap);
|
||||||
@@ -267,13 +258,11 @@ pub unsafe extern "C" fn wasmtime_memory_fill(
|
|||||||
dst: u32,
|
dst: u32,
|
||||||
val: u32,
|
val: u32,
|
||||||
len: u32,
|
len: u32,
|
||||||
source_loc: u32,
|
|
||||||
) {
|
) {
|
||||||
let result = {
|
let result = {
|
||||||
let memory_index = DefinedMemoryIndex::from_u32(memory_index);
|
let memory_index = DefinedMemoryIndex::from_u32(memory_index);
|
||||||
let source_loc = ir::SourceLoc::new(source_loc);
|
|
||||||
let instance = (&mut *vmctx).instance();
|
let instance = (&mut *vmctx).instance();
|
||||||
instance.defined_memory_fill(memory_index, dst, val, len, source_loc)
|
instance.defined_memory_fill(memory_index, dst, val, len)
|
||||||
};
|
};
|
||||||
if let Err(trap) = result {
|
if let Err(trap) = result {
|
||||||
raise_lib_trap(trap);
|
raise_lib_trap(trap);
|
||||||
@@ -287,13 +276,11 @@ pub unsafe extern "C" fn wasmtime_imported_memory_fill(
|
|||||||
dst: u32,
|
dst: u32,
|
||||||
val: u32,
|
val: u32,
|
||||||
len: u32,
|
len: u32,
|
||||||
source_loc: u32,
|
|
||||||
) {
|
) {
|
||||||
let result = {
|
let result = {
|
||||||
let memory_index = MemoryIndex::from_u32(memory_index);
|
let memory_index = MemoryIndex::from_u32(memory_index);
|
||||||
let source_loc = ir::SourceLoc::new(source_loc);
|
|
||||||
let instance = (&mut *vmctx).instance();
|
let instance = (&mut *vmctx).instance();
|
||||||
instance.imported_memory_fill(memory_index, dst, val, len, source_loc)
|
instance.imported_memory_fill(memory_index, dst, val, len)
|
||||||
};
|
};
|
||||||
if let Err(trap) = result {
|
if let Err(trap) = result {
|
||||||
raise_lib_trap(trap);
|
raise_lib_trap(trap);
|
||||||
@@ -308,14 +295,12 @@ pub unsafe extern "C" fn wasmtime_memory_init(
|
|||||||
dst: u32,
|
dst: u32,
|
||||||
src: u32,
|
src: u32,
|
||||||
len: u32,
|
len: u32,
|
||||||
source_loc: u32,
|
|
||||||
) {
|
) {
|
||||||
let result = {
|
let result = {
|
||||||
let memory_index = MemoryIndex::from_u32(memory_index);
|
let memory_index = MemoryIndex::from_u32(memory_index);
|
||||||
let data_index = DataIndex::from_u32(data_index);
|
let data_index = DataIndex::from_u32(data_index);
|
||||||
let source_loc = ir::SourceLoc::new(source_loc);
|
|
||||||
let instance = (&mut *vmctx).instance();
|
let instance = (&mut *vmctx).instance();
|
||||||
instance.memory_init(memory_index, data_index, dst, src, len, source_loc)
|
instance.memory_init(memory_index, data_index, dst, src, len)
|
||||||
};
|
};
|
||||||
if let Err(trap) = result {
|
if let Err(trap) = result {
|
||||||
raise_lib_trap(trap);
|
raise_lib_trap(trap);
|
||||||
|
|||||||
@@ -100,7 +100,6 @@ impl Table {
|
|||||||
dst_index: u32,
|
dst_index: u32,
|
||||||
src_index: u32,
|
src_index: u32,
|
||||||
len: u32,
|
len: u32,
|
||||||
source_loc: ir::SourceLoc,
|
|
||||||
) -> Result<(), Trap> {
|
) -> Result<(), Trap> {
|
||||||
// https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-copy
|
// https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-copy
|
||||||
|
|
||||||
@@ -111,7 +110,7 @@ impl Table {
|
|||||||
.checked_add(len)
|
.checked_add(len)
|
||||||
.map_or(true, |m| m > dst_table.size())
|
.map_or(true, |m| m > dst_table.size())
|
||||||
{
|
{
|
||||||
return Err(Trap::wasm(source_loc, ir::TrapCode::TableOutOfBounds));
|
return Err(Trap::wasm(ir::TrapCode::TableOutOfBounds));
|
||||||
}
|
}
|
||||||
|
|
||||||
let srcs = src_index..src_index + len;
|
let srcs = src_index..src_index + len;
|
||||||
|
|||||||
@@ -1,173 +0,0 @@
|
|||||||
use std::collections::{BTreeMap, HashMap};
|
|
||||||
use std::fmt;
|
|
||||||
use std::sync::{Arc, RwLock};
|
|
||||||
use wasmtime_environ::ir;
|
|
||||||
|
|
||||||
/// The registry maintains descriptions of traps in currently allocated functions.
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct TrapRegistry {
|
|
||||||
// This data structure is intended to be safe to use across many threads
|
|
||||||
// since this is stored inside of a `Compiler` which, eventually, will be
|
|
||||||
// used across many threads. To that end this is internally use an `Arc`
|
|
||||||
// plus an `RwLock`.
|
|
||||||
//
|
|
||||||
// The problem that this data structure is solving is that when a
|
|
||||||
// segfault/illegal instruction happens we need to answer "given this
|
|
||||||
// hardware program counter what is the wasm reason this trap is being
|
|
||||||
// raised"?
|
|
||||||
//
|
|
||||||
// The way this is answered here is done to minimize the amount of
|
|
||||||
// synchronization (in theory) and have something like so:
|
|
||||||
//
|
|
||||||
// * Each module bulk-registers a list of in-memory pc addresses that have
|
|
||||||
// traps. We assume that the range of traps for each module are always
|
|
||||||
// disjoint.
|
|
||||||
// * Each key in this `BTreeMap` is the highest trapping address and the
|
|
||||||
// value contains the lowest address as well as all the individual
|
|
||||||
// addresses in their own `HashMap`.
|
|
||||||
// * Registration then looks by calculating the start/end and inserting
|
|
||||||
// into this map (with some assertions about disjointed-ness)
|
|
||||||
// * Lookup is done in two layers. First we find the corresponding entry
|
|
||||||
// in the map and verify that a program counter falls in the start/end
|
|
||||||
// range. Next we look up the address in the `traps` hash map below.
|
|
||||||
//
|
|
||||||
// The `register_traps` function works by returning an RAII guard that owns
|
|
||||||
// a handle to this `Arc` as well, and when that type is dropped it will
|
|
||||||
// automatically remove all trap information from this `ranges` list.
|
|
||||||
ranges: Arc<RwLock<BTreeMap<usize, TrapGroup>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct TrapGroup {
|
|
||||||
/// The lowest key in the `trap` field.
|
|
||||||
///
|
|
||||||
/// This represents the start of the range of this group of traps, and the
|
|
||||||
/// end of the range for this group of traps is stored as the key in the
|
|
||||||
/// `ranges` struct above in `TrapRegistry`.
|
|
||||||
start: usize,
|
|
||||||
|
|
||||||
/// All known traps in this group, mapped from program counter to the
|
|
||||||
/// description of the trap itself.
|
|
||||||
traps: HashMap<usize, TrapDescription>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// RAII structure returned from `TrapRegistry::register_trap` to unregister
|
|
||||||
/// trap information on drop.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TrapRegistration {
|
|
||||||
ranges: Arc<RwLock<BTreeMap<usize, TrapGroup>>>,
|
|
||||||
end: Option<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Description of a trap.
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
|
||||||
pub struct TrapDescription {
|
|
||||||
/// Location of the trap in source binary module.
|
|
||||||
pub source_loc: ir::SourceLoc,
|
|
||||||
/// Code of the trap.
|
|
||||||
pub trap_code: ir::TrapCode,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for TrapDescription {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"wasm trap: {}, source location: {}",
|
|
||||||
trap_code_to_expected_string(self.trap_code),
|
|
||||||
self.source_loc
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn trap_code_to_expected_string(trap_code: ir::TrapCode) -> String {
|
|
||||||
use ir::TrapCode::*;
|
|
||||||
match trap_code {
|
|
||||||
StackOverflow => "call stack exhausted".to_string(),
|
|
||||||
HeapOutOfBounds => "out of bounds memory access".to_string(),
|
|
||||||
TableOutOfBounds => "undefined element: out of bounds table access".to_string(),
|
|
||||||
OutOfBounds => "out of bounds".to_string(), // Note: not covered by the test suite
|
|
||||||
IndirectCallToNull => "uninitialized element".to_string(),
|
|
||||||
BadSignature => "indirect call type mismatch".to_string(),
|
|
||||||
IntegerOverflow => "integer overflow".to_string(),
|
|
||||||
IntegerDivisionByZero => "integer divide by zero".to_string(),
|
|
||||||
BadConversionToInteger => "invalid conversion to integer".to_string(),
|
|
||||||
UnreachableCodeReached => "unreachable".to_string(),
|
|
||||||
Interrupt => "interrupt".to_string(), // Note: not covered by the test suite
|
|
||||||
User(x) => format!("user trap {}", x), // Note: not covered by the test suite
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TrapRegistry {
|
|
||||||
/// Registers a list of traps.
|
|
||||||
///
|
|
||||||
/// Returns a RAII guard that deregisters all traps when dropped.
|
|
||||||
pub fn register_traps(
|
|
||||||
&self,
|
|
||||||
list: impl IntoIterator<Item = (usize, ir::SourceLoc, ir::TrapCode)>,
|
|
||||||
) -> TrapRegistration {
|
|
||||||
let mut start = usize::max_value();
|
|
||||||
let mut end = 0;
|
|
||||||
let mut traps = HashMap::new();
|
|
||||||
for (addr, source_loc, trap_code) in list.into_iter() {
|
|
||||||
traps.insert(
|
|
||||||
addr,
|
|
||||||
TrapDescription {
|
|
||||||
source_loc,
|
|
||||||
trap_code,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if addr < start {
|
|
||||||
start = addr;
|
|
||||||
}
|
|
||||||
if addr > end {
|
|
||||||
end = addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if traps.len() == 0 {
|
|
||||||
return TrapRegistration {
|
|
||||||
ranges: self.ranges.clone(),
|
|
||||||
end: None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let mut ranges = self.ranges.write().unwrap();
|
|
||||||
|
|
||||||
// Sanity check that no other group of traps overlaps with our
|
|
||||||
// registration...
|
|
||||||
if let Some((_, prev)) = ranges.range(end..).next() {
|
|
||||||
assert!(prev.start > end);
|
|
||||||
}
|
|
||||||
if let Some((prev_end, _)) = ranges.range(..=start).next_back() {
|
|
||||||
assert!(*prev_end < start);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... and then register ourselves
|
|
||||||
assert!(ranges.insert(end, TrapGroup { start, traps }).is_none());
|
|
||||||
TrapRegistration {
|
|
||||||
ranges: self.ranges.clone(),
|
|
||||||
end: Some(end),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TrapRegistration {
|
|
||||||
/// Gets a trap description at given address.
|
|
||||||
pub fn get_trap(&self, address: usize) -> Option<TrapDescription> {
|
|
||||||
let ranges = self.ranges.read().ok()?;
|
|
||||||
let (end, group) = ranges.range(address..).next()?;
|
|
||||||
if group.start <= address && address <= *end {
|
|
||||||
group.traps.get(&address).copied()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for TrapRegistration {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Some(end) = self.end {
|
|
||||||
if let Ok(mut ranges) = self.ranges.write() {
|
|
||||||
ranges.remove(&end);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,13 +2,11 @@
|
|||||||
//! signalhandling mechanisms.
|
//! signalhandling mechanisms.
|
||||||
|
|
||||||
use crate::instance::{InstanceHandle, SignalHandler};
|
use crate::instance::{InstanceHandle, SignalHandler};
|
||||||
use crate::trap_registry::TrapDescription;
|
|
||||||
use crate::vmcontext::VMContext;
|
use crate::vmcontext::VMContext;
|
||||||
use backtrace::Backtrace;
|
use backtrace::Backtrace;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::sync::Once;
|
use std::sync::Once;
|
||||||
@@ -319,11 +317,19 @@ fn reset_guard_page() {}
|
|||||||
pub enum Trap {
|
pub enum Trap {
|
||||||
/// A user-raised trap through `raise_user_trap`.
|
/// A user-raised trap through `raise_user_trap`.
|
||||||
User(Box<dyn Error + Send + Sync>),
|
User(Box<dyn Error + Send + Sync>),
|
||||||
/// A wasm-originating trap from wasm code itself.
|
|
||||||
|
/// A trap raised from jit code
|
||||||
|
Jit {
|
||||||
|
/// The program counter in JIT code where this trap happened.
|
||||||
|
pc: usize,
|
||||||
|
/// Native stack backtrace at the time the trap occurred
|
||||||
|
backtrace: Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A trap raised from a wasm libcall
|
||||||
Wasm {
|
Wasm {
|
||||||
/// What sort of trap happened, as well as where in the original wasm module
|
/// Code of the trap.
|
||||||
/// it happened.
|
trap_code: ir::TrapCode,
|
||||||
desc: TrapDescription,
|
|
||||||
/// Native stack backtrace at the time the trap occurred
|
/// Native stack backtrace at the time the trap occurred
|
||||||
backtrace: Backtrace,
|
backtrace: Backtrace,
|
||||||
},
|
},
|
||||||
@@ -335,36 +341,23 @@ pub enum Trap {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Trap {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Trap::User(user) => user.fmt(f),
|
|
||||||
Trap::Wasm { desc, .. } => desc.fmt(f),
|
|
||||||
Trap::OOM { .. } => write!(f, "Out of memory"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for Trap {}
|
|
||||||
|
|
||||||
impl Trap {
|
impl Trap {
|
||||||
/// Construct a new Wasm trap with the given source location and trap code.
|
/// Construct a new Wasm trap with the given source location and trap code.
|
||||||
///
|
///
|
||||||
/// Internally saves a backtrace when constructed.
|
/// Internally saves a backtrace when constructed.
|
||||||
pub fn wasm(source_loc: ir::SourceLoc, trap_code: ir::TrapCode) -> Self {
|
pub fn wasm(trap_code: ir::TrapCode) -> Self {
|
||||||
let desc = TrapDescription {
|
let backtrace = Backtrace::new_unresolved();
|
||||||
source_loc,
|
Trap::Wasm {
|
||||||
trap_code,
|
trap_code,
|
||||||
};
|
backtrace,
|
||||||
let backtrace = Backtrace::new();
|
}
|
||||||
Trap::Wasm { desc, backtrace }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new OOM trap with the given source location and trap code.
|
/// Construct a new OOM trap with the given source location and trap code.
|
||||||
///
|
///
|
||||||
/// Internally saves a backtrace when constructed.
|
/// Internally saves a backtrace when constructed.
|
||||||
pub fn oom() -> Self {
|
pub fn oom() -> Self {
|
||||||
let backtrace = Backtrace::new();
|
let backtrace = Backtrace::new_unresolved();
|
||||||
Trap::OOM { backtrace }
|
Trap::OOM { backtrace }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -413,7 +406,7 @@ enum UnwindReason {
|
|||||||
Panic(Box<dyn Any + Send>),
|
Panic(Box<dyn Any + Send>),
|
||||||
UserTrap(Box<dyn Error + Send + Sync>),
|
UserTrap(Box<dyn Error + Send + Sync>),
|
||||||
LibTrap(Trap),
|
LibTrap(Trap),
|
||||||
Trap { backtrace: Backtrace, pc: usize },
|
JitTrap { backtrace: Backtrace, pc: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CallThreadState {
|
impl CallThreadState {
|
||||||
@@ -442,21 +435,9 @@ impl CallThreadState {
|
|||||||
Err(Trap::User(data))
|
Err(Trap::User(data))
|
||||||
}
|
}
|
||||||
UnwindReason::LibTrap(trap) => Err(trap),
|
UnwindReason::LibTrap(trap) => Err(trap),
|
||||||
UnwindReason::Trap { backtrace, pc } => {
|
UnwindReason::JitTrap { backtrace, pc } => {
|
||||||
debug_assert_eq!(ret, 0);
|
debug_assert_eq!(ret, 0);
|
||||||
let instance = unsafe { InstanceHandle::from_vmctx(self.vmctx) };
|
Err(Trap::Jit { pc, backtrace })
|
||||||
|
|
||||||
Err(Trap::Wasm {
|
|
||||||
desc: instance
|
|
||||||
.instance()
|
|
||||||
.trap_registration
|
|
||||||
.get_trap(pc)
|
|
||||||
.unwrap_or_else(|| TrapDescription {
|
|
||||||
source_loc: ir::SourceLoc::default(),
|
|
||||||
trap_code: ir::TrapCode::StackOverflow,
|
|
||||||
}),
|
|
||||||
backtrace,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
UnwindReason::Panic(panic) => {
|
UnwindReason::Panic(panic) => {
|
||||||
debug_assert_eq!(ret, 0);
|
debug_assert_eq!(ret, 0);
|
||||||
@@ -546,7 +527,7 @@ impl CallThreadState {
|
|||||||
}
|
}
|
||||||
let backtrace = Backtrace::new_unresolved();
|
let backtrace = Backtrace::new_unresolved();
|
||||||
self.reset_guard_page.set(reset_guard_page);
|
self.reset_guard_page.set(reset_guard_page);
|
||||||
self.unwind.replace(UnwindReason::Trap {
|
self.unwind.replace(UnwindReason::JitTrap {
|
||||||
backtrace,
|
backtrace,
|
||||||
pc: pc as usize,
|
pc: pc as usize,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user