Backtrace WebAssembly function JIT frames (#759)

* Create backtrace

* Extend unwind information with FDE data.

* Expose backtrace via API/Trap

* wasmtime_call returns not-str

* Return Arc<JITFrameTag>

* rename frame -> function

* Fix windows crashes and unwrap UNWIND_HISTORY_TABLE

* mmaps -> entries

* pass a backtrace in ActionOutcome

* add test_trap_stack_overflow

* Update cranelift version.
This commit is contained in:
Yury Delendik
2020-01-15 13:48:24 -06:00
committed by GitHub
parent 0848a7eaaa
commit 2a50701f0a
26 changed files with 803 additions and 149 deletions

42
Cargo.lock generated
View File

@@ -341,24 +341,25 @@ dependencies = [
[[package]]
name = "cranelift-bforest"
version = "0.54.0"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd3225fff1be118941c5fb66f1fb1f7f3e86468fac0e7364713c4fb99b72632b"
checksum = "40af6e6f7419110906d0f7b4b8084d3216be64d7da77aa12887885ebe0fc2776"
dependencies = [
"cranelift-entity",
]
[[package]]
name = "cranelift-codegen"
version = "0.54.0"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3e3e6679892029f76a99b9059d2b74e77ac03637d573bb014bc21579ec1b7da"
checksum = "88583eb22e5cd0fe1ef66f0b80d0063d21d30e84e887d08b3d369def3ea7b4be"
dependencies = [
"byteorder",
"cranelift-bforest",
"cranelift-codegen-meta",
"cranelift-codegen-shared",
"cranelift-entity",
"gimli",
"log",
"serde",
"smallvec",
@@ -368,9 +369,9 @@ dependencies = [
[[package]]
name = "cranelift-codegen-meta"
version = "0.54.0"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cabe691548e28ca82ebd218f2fe76eec4c5629b64ef3db8b58443b7d9047275"
checksum = "353872c984943b9134d7c835eb9d12932bd90f4992dbe666593771bee920d673"
dependencies = [
"cranelift-codegen-shared",
"cranelift-entity",
@@ -378,24 +379,24 @@ dependencies = [
[[package]]
name = "cranelift-codegen-shared"
version = "0.54.0"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d173252ffade4aa6e929090977b9a4cd5ac28e15a42626f878be3844b3000ad9"
checksum = "cd1e96479a56981cce5c8f14d26773195d662ccdbbeca39fb8eba22b5ca8ea6a"
[[package]]
name = "cranelift-entity"
version = "0.54.0"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3498ad9ba021715716a1c52e2b31d7829a149913fb0d88493e4b07d3b772b656"
checksum = "37bc8dc3d4274ededc687d84821f491a8a03447dbb7481983936220892cf55b4"
dependencies = [
"serde",
]
[[package]]
name = "cranelift-frontend"
version = "0.54.0"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521a30773b8de74345c807a38853f055aca8fecaa39a0fc7698bfebc5a3da515"
checksum = "4ea33e55bda8c425f3f533355b03e3a43cf29b4e228b35b868b6a1c43b6a139e"
dependencies = [
"cranelift-codegen",
"log",
@@ -405,9 +406,9 @@ dependencies = [
[[package]]
name = "cranelift-native"
version = "0.54.0"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624e755cbe984e437308968239736e7f9aa3193e99928fb76eec7a1946627b34"
checksum = "2c22cfaaa5e69eddaddd6cfa7a76233de964b9b2245e81a5f47dae739931ad0d"
dependencies = [
"cranelift-codegen",
"raw-cpuid",
@@ -416,9 +417,9 @@ dependencies = [
[[package]]
name = "cranelift-wasm"
version = "0.54.0"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0320733e518ab9e0e2d1a22034d40e2851fb403ed14db5220cf9b86576b9cfd4"
checksum = "038f8fd636abc83ccd6f110e0891766521c3599b52173c0f37e61c684f19a15d"
dependencies = [
"cranelift-codegen",
"cranelift-entity",
@@ -426,7 +427,7 @@ dependencies = [
"log",
"serde",
"thiserror",
"wasmparser 0.45.2",
"wasmparser 0.47.0",
]
[[package]]
@@ -1964,12 +1965,6 @@ version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1527c84a5bd585215f29c06b0e2a5274e478ad4dfc970d26ffad66fdc6cb311d"
[[package]]
name = "wasmparser"
version = "0.45.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b4eab1d9971d0803729cba3617b56eb04fcb4bd25361cb63880ed41a42f20d5"
[[package]]
name = "wasmparser"
version = "0.47.0"
@@ -2188,6 +2183,7 @@ dependencies = [
name = "wasmtime-runtime"
version = "0.9.0"
dependencies = [
"backtrace",
"cc",
"cfg-if",
"indexmap",

View File

@@ -147,7 +147,7 @@ impl WrappedCallable for WasmtimeFn {
.map_err(|e| Trap::new(format!("trampoline error: {:?}", e)))?;
// Call the trampoline.
if let Err(message) = unsafe {
if let Err(error) = unsafe {
self.instance.with_signals_on(|| {
wasmtime_runtime::wasmtime_call_trampoline(
vmctx,
@@ -156,8 +156,12 @@ impl WrappedCallable for WasmtimeFn {
)
})
} {
let trap =
take_api_trap().unwrap_or_else(|| Trap::new(format!("call error: {}", message)));
let message = error.0;
let backtrace = error.1;
let trap = take_api_trap().unwrap_or_else(|| {
Trap::new_with_trace(format!("call error: {}", message), backtrace)
});
return Err(trap);
}

View File

@@ -11,7 +11,9 @@ use wasmtime_environ::entity::{EntityRef, PrimaryMap};
use wasmtime_environ::ir::types;
use wasmtime_environ::isa::TargetIsa;
use wasmtime_environ::wasm::{DefinedFuncIndex, FuncIndex};
use wasmtime_environ::{ir, settings, CompiledFunction, Export, Module, TrapInformation};
use wasmtime_environ::{
ir, settings, CompiledFunction, CompiledFunctionUnwindInfo, Export, Module, TrapInformation,
};
use wasmtime_jit::trampoline::ir::{
ExternalName, Function, InstBuilder, MemFlags, StackSlotData, StackSlotKind,
};
@@ -136,6 +138,7 @@ fn make_trampoline(
let mut context = Context::new();
context.func = Function::with_name_signature(ExternalName::user(0, 0), signature.clone());
context.func.collect_frame_layout_info();
let ss = context.func.create_stack_slot(StackSlotData::new(
StackSlotKind::ExplicitSlot,
@@ -213,8 +216,7 @@ fn make_trampoline(
.map_err(|error| pretty_error(&context.func, Some(isa), error))
.expect("compile_and_emit");
let mut unwind_info = Vec::new();
context.emit_unwind_info(isa, &mut unwind_info);
let unwind_info = CompiledFunctionUnwindInfo::new(isa, &context);
let traps = trap_sink.traps;

View File

@@ -1,6 +1,7 @@
use crate::instance::Instance;
use std::fmt;
use std::sync::Arc;
use wasmtime_runtime::{get_backtrace, Backtrace, BacktraceFrame};
/// A struct representing an aborted instruction execution, with a message
/// indicating the cause.
@@ -26,10 +27,21 @@ impl Trap {
/// assert_eq!("unexpected error", trap.message());
/// ```
pub fn new<I: Into<String>>(message: I) -> Self {
Self::new_with_trace(message, get_backtrace())
}
pub(crate) fn new_with_trace<I: Into<String>>(message: I, backtrace: Backtrace) -> Self {
let mut trace = Vec::with_capacity(backtrace.len());
for i in 0..backtrace.len() {
// Don't include frames without backtrace info.
if let Some(info) = FrameInfo::try_from(backtrace[i]) {
trace.push(info);
}
}
Trap {
inner: Arc::new(TrapInner {
message: message.into(),
trace: Vec::new(),
trace,
}),
}
}
@@ -62,22 +74,42 @@ impl fmt::Display for Trap {
impl std::error::Error for Trap {}
#[derive(Debug)]
pub struct FrameInfo;
pub struct FrameInfo {
module_name: Option<String>,
func_index: u32,
}
impl FrameInfo {
pub fn instance(&self) -> *const Instance {
unimplemented!("FrameInfo::instance");
}
pub fn func_index() -> usize {
unimplemented!("FrameInfo::func_index");
pub fn func_index(&self) -> u32 {
self.func_index
}
pub fn func_offset() -> usize {
pub fn func_offset(&self) -> usize {
unimplemented!("FrameInfo::func_offset");
}
pub fn module_offset() -> usize {
pub fn module_offset(&self) -> usize {
unimplemented!("FrameInfo::module_offset");
}
pub fn module_name(&self) -> Option<&str> {
self.module_name.as_deref()
}
pub(crate) fn try_from(backtrace: BacktraceFrame) -> Option<FrameInfo> {
if let Some(tag) = backtrace.tag() {
let func_index = tag.func_index as u32;
let module_name = tag.module_id.clone();
Some(FrameInfo {
func_index,
module_name,
})
} else {
None
}
}
}

View File

@@ -41,3 +41,116 @@ fn test_trap_return() -> Result<(), String> {
Ok(())
}
#[test]
fn test_trap_trace() -> Result<(), String> {
let store = Store::default();
let binary = parse_str(
r#"
(module $hello_mod
(func (export "run") (call $hello))
(func $hello (unreachable))
)
"#,
)
.map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?;
let module =
Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?;
let instance = Instance::new(&module, &[])
.map_err(|e| format!("failed to instantiate module: {:?}", e))?;
let run_func = instance.exports()[0]
.func()
.expect("expected function export");
let e = run_func.call(&[]).err().expect("error calling function");
let trace = e.trace();
assert_eq!(trace.len(), 2);
assert_eq!(trace[0].module_name().unwrap(), "hello_mod");
assert_eq!(trace[0].func_index(), 1);
assert_eq!(trace[1].module_name().unwrap(), "hello_mod");
assert_eq!(trace[1].func_index(), 0);
assert!(e.message().contains("unreachable"));
Ok(())
}
#[test]
fn test_trap_trace_cb() -> Result<(), String> {
struct ThrowCallback;
impl Callable for ThrowCallback {
fn call(&self, _params: &[Val], _results: &mut [Val]) -> Result<(), Trap> {
Err(Trap::new("cb throw"))
}
}
let store = Store::default();
let binary = parse_str(
r#"
(module $hello_mod
(import "" "throw" (func $throw))
(func (export "run") (call $hello))
(func $hello (call $throw))
)
"#,
)
.map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?;
let fn_type = FuncType::new(Box::new([]), Box::new([]));
let fn_func = Func::new(&store, fn_type, Rc::new(ThrowCallback));
let module =
Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?;
let instance = Instance::new(&module, &[fn_func.into()])
.map_err(|e| format!("failed to instantiate module: {:?}", e))?;
let run_func = instance.exports()[0]
.func()
.expect("expected function export");
let e = run_func.call(&[]).err().expect("error calling function");
let trace = e.trace();
assert_eq!(trace.len(), 2);
assert_eq!(trace[0].module_name().unwrap(), "hello_mod");
assert_eq!(trace[0].func_index(), 1);
assert_eq!(trace[1].module_name().unwrap(), "hello_mod");
assert_eq!(trace[1].func_index(), 0);
assert_eq!(e.message(), "cb throw");
Ok(())
}
#[test]
fn test_trap_stack_overflow() -> Result<(), String> {
let store = Store::default();
let binary = parse_str(
r#"
(module $rec_mod
(func $run (export "run") (call $run))
)
"#,
)
.map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?;
let module =
Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?;
let instance = Instance::new(&module, &[])
.map_err(|e| format!("failed to instantiate module: {:?}", e))?;
let run_func = instance.exports()[0]
.func()
.expect("expected function export");
let e = run_func.call(&[]).err().expect("error calling function");
let trace = e.trace();
assert!(trace.len() >= 32);
for i in 0..trace.len() {
assert_eq!(trace[i].module_name().unwrap(), "rec_mod");
assert_eq!(trace[i].func_index(), 0);
}
assert!(e.message().contains("call stack exhausted"));
Ok(())
}

View File

@@ -13,9 +13,9 @@ edition = "2018"
[dependencies]
anyhow = "1.0"
cranelift-codegen = { version = "0.54", features = ["enable-serde"] }
cranelift-entity = { version = "0.54", features = ["enable-serde"] }
cranelift-wasm = { version = "0.54", features = ["enable-serde"] }
cranelift-codegen = { version = "0.55", features = ["enable-serde"] }
cranelift-entity = { version = "0.55", features = ["enable-serde"] }
cranelift-wasm = { version = "0.55", features = ["enable-serde"] }
wasmparser = "0.47.0"
lightbeam = { path = "../lightbeam", optional = true, version = "0.9.0" }
indexmap = "1.0.2"
@@ -46,7 +46,7 @@ tempfile = "3"
target-lexicon = { version = "0.10.0", default-features = false }
pretty_env_logger = "0.3.0"
rand = { version = "0.7.0", default-features = false, features = ["small_rng"] }
cranelift-codegen = { version = "0.54", features = ["enable-serde", "all-arch"] }
cranelift-codegen = { version = "0.55", features = ["enable-serde", "all-arch"] }
filetime = "0.2.7"
[badges]

View File

@@ -1,7 +1,9 @@
use super::config::tests::test_prolog;
use super::*;
use crate::address_map::{FunctionAddressMap, InstructionAddressMap};
use crate::compilation::{CompiledFunction, Relocation, RelocationTarget, TrapInformation};
use crate::compilation::{
CompiledFunction, CompiledFunctionUnwindInfo, Relocation, RelocationTarget, TrapInformation,
};
use crate::module::{MemoryPlan, MemoryStyle, Module};
use cranelift_codegen::{binemit, ir, isa, settings, ValueLocRange};
use cranelift_entity::EntityRef;
@@ -259,7 +261,7 @@ fn new_module_cache_data(rng: &mut impl Rng) -> ModuleCacheData {
CompiledFunction {
body: (0..(i * 3 / 2)).collect(),
jt_offsets: sm,
unwind_info: (0..(i * 3 / 2)).collect(),
unwind_info: CompiledFunctionUnwindInfo::Windows((0..(i * 3 / 2)).collect()),
}
})
.collect();

View File

@@ -4,13 +4,136 @@
use crate::cache::ModuleCacheDataTupleType;
use crate::module;
use crate::module_environ::FunctionBodyData;
use cranelift_codegen::{binemit, ir, isa};
use cranelift_codegen::{binemit, ir, isa, Context};
use cranelift_entity::PrimaryMap;
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, ModuleTranslationState, WasmError};
use serde::{Deserialize, Serialize};
use std::ops::Range;
use thiserror::Error;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct FDERelocEntry(pub i64, pub usize, pub u8);
/// Relocation entry for unwind info.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct CompiledFunctionUnwindInfoReloc {
/// Entry offest in the code block.
pub offset: u32,
/// Entry addend relative to the code block.
pub addend: u32,
}
/// Compiled function unwind information.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum CompiledFunctionUnwindInfo {
/// No info.
None,
/// Windows UNWIND_INFO.
Windows(Vec<u8>),
/// Frame layout info.
FrameLayout(Vec<u8>, usize, Vec<FDERelocEntry>),
}
impl CompiledFunctionUnwindInfo {
/// Constructs unwind info object.
pub fn new(isa: &dyn isa::TargetIsa, context: &Context) -> Self {
use cranelift_codegen::binemit::{
FrameUnwindKind, FrameUnwindOffset, FrameUnwindSink, Reloc,
};
use cranelift_codegen::isa::CallConv;
struct Sink(Vec<u8>, usize, Vec<FDERelocEntry>);
impl FrameUnwindSink for Sink {
fn len(&self) -> FrameUnwindOffset {
self.0.len()
}
fn bytes(&mut self, b: &[u8]) {
self.0.extend_from_slice(b);
}
fn reserve(&mut self, len: usize) {
self.0.reserve(len)
}
fn reloc(&mut self, r: Reloc, off: FrameUnwindOffset) {
self.2.push(FDERelocEntry(
0,
off,
match r {
Reloc::Abs4 => 4,
Reloc::Abs8 => 8,
_ => {
panic!("unexpected reloc type");
}
},
))
}
fn set_entry_offset(&mut self, off: FrameUnwindOffset) {
self.1 = off;
}
}
let kind = match context.func.signature.call_conv {
CallConv::SystemV | CallConv::Fast | CallConv::Cold => FrameUnwindKind::Libunwind,
CallConv::WindowsFastcall => FrameUnwindKind::Fastcall,
_ => {
return CompiledFunctionUnwindInfo::None;
}
};
let mut sink = Sink(Vec::new(), 0, Vec::new());
context.emit_unwind_info(isa, kind, &mut sink);
let Sink(data, offset, relocs) = sink;
if data.is_empty() {
return CompiledFunctionUnwindInfo::None;
}
match kind {
FrameUnwindKind::Fastcall => CompiledFunctionUnwindInfo::Windows(data),
FrameUnwindKind::Libunwind => {
CompiledFunctionUnwindInfo::FrameLayout(data, offset, relocs)
}
}
}
/// Retuns true is no unwind info data.
pub fn is_empty(&self) -> bool {
match self {
CompiledFunctionUnwindInfo::None => true,
CompiledFunctionUnwindInfo::Windows(d) => d.is_empty(),
CompiledFunctionUnwindInfo::FrameLayout(c, _, _) => c.is_empty(),
}
}
/// Returns size of serilized unwind info.
pub fn len(&self) -> usize {
match self {
CompiledFunctionUnwindInfo::None => 0,
CompiledFunctionUnwindInfo::Windows(d) => d.len(),
CompiledFunctionUnwindInfo::FrameLayout(c, _, _) => c.len(),
}
}
/// Serializes data into byte array.
pub fn serialize(&self, dest: &mut [u8], relocs: &mut Vec<CompiledFunctionUnwindInfoReloc>) {
match self {
CompiledFunctionUnwindInfo::None => (),
CompiledFunctionUnwindInfo::Windows(d) => {
dest.copy_from_slice(d);
}
CompiledFunctionUnwindInfo::FrameLayout(code, _fde_offset, r) => {
dest.copy_from_slice(code);
r.iter().for_each(move |r| {
assert_eq!(r.2, 8);
relocs.push(CompiledFunctionUnwindInfoReloc {
offset: r.1 as u32,
addend: r.0 as u32,
})
});
}
}
}
}
/// Compiled function: machine code body, jump table offsets, and unwind information.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct CompiledFunction {
@@ -21,7 +144,7 @@ pub struct CompiledFunction {
pub jt_offsets: ir::JumpTableOffsets,
/// The unwind information.
pub unwind_info: Vec<u8>,
pub unwind_info: CompiledFunctionUnwindInfo,
}
type Functions = PrimaryMap<DefinedFuncIndex, CompiledFunction>;
@@ -50,7 +173,7 @@ impl Compilation {
.map(|(body_range, jt_offsets, unwind_range)| CompiledFunction {
body: buffer[body_range].to_vec(),
jt_offsets,
unwind_info: buffer[unwind_range].to_vec(),
unwind_info: CompiledFunctionUnwindInfo::Windows(buffer[unwind_range].to_vec()),
})
.collect(),
)

View File

@@ -3,7 +3,8 @@
use crate::address_map::{FunctionAddressMap, InstructionAddressMap};
use crate::cache::{ModuleCacheData, ModuleCacheDataTupleType, ModuleCacheEntry};
use crate::compilation::{
Compilation, CompileError, CompiledFunction, Relocation, RelocationTarget, TrapInformation,
Compilation, CompileError, CompiledFunction, CompiledFunctionUnwindInfo, Relocation,
RelocationTarget, TrapInformation,
};
use crate::func_environ::{
get_func_name, get_imported_memory32_grow_name, get_imported_memory32_size_name,
@@ -204,6 +205,7 @@ impl crate::compilation::Compiler for Cranelift {
context.func.name = get_func_name(func_index);
context.func.signature =
module.signatures[module.functions[func_index]].clone();
context.func.collect_frame_layout_info();
if generate_debug_info {
context.func.collect_debug_info();
}
@@ -217,7 +219,6 @@ impl crate::compilation::Compiler for Cranelift {
)?;
let mut code_buf: Vec<u8> = Vec::new();
let mut unwind_info = Vec::new();
let mut reloc_sink = RelocSink::new(func_index);
let mut trap_sink = TrapSink::new();
let mut stackmap_sink = binemit::NullStackmapSink {};
@@ -233,7 +234,7 @@ impl crate::compilation::Compiler for Cranelift {
CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
})?;
context.emit_unwind_info(isa, &mut unwind_info);
let unwind_info = CompiledFunctionUnwindInfo::new(isa, &context);
let address_transform = if generate_debug_info {
let body_len = code_buf.len();

View File

@@ -362,8 +362,8 @@ impl<'module_environment> TargetEnvironment for FuncEnvironment<'module_environm
}
impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'module_environment> {
fn is_wasm_parameter(&self, func: &ir::Function, index: usize) -> bool {
func.signature.params[index].purpose == ir::ArgumentPurpose::Normal
fn is_wasm_parameter(&self, signature: &ir::Signature, index: usize) -> bool {
signature.params[index].purpose == ir::ArgumentPurpose::Normal
}
fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> WasmResult<ir::Table> {

View File

@@ -44,8 +44,9 @@ pub use crate::address_map::{
};
pub use crate::cache::{create_new_config as cache_create_new_config, init as cache_init};
pub use crate::compilation::{
Compilation, CompileError, CompiledFunction, Compiler, Relocation, RelocationTarget,
Relocations, TrapInformation, Traps,
Compilation, CompileError, CompiledFunction, CompiledFunctionUnwindInfo,
CompiledFunctionUnwindInfoReloc, Compiler, Relocation, RelocationTarget, Relocations,
TrapInformation, Traps,
};
pub use crate::cranelift::Cranelift;
pub use crate::data_structures::*;

View File

@@ -11,11 +11,11 @@ readme = "README.md"
edition = "2018"
[dependencies]
cranelift-codegen = { version = "0.54", features = ["enable-serde"] }
cranelift-entity = { version = "0.54", features = ["enable-serde"] }
cranelift-wasm = { version = "0.54", features = ["enable-serde"] }
cranelift-native = "0.54"
cranelift-frontend = "0.54"
cranelift-codegen = { version = "0.55", features = ["enable-serde"] }
cranelift-entity = { version = "0.55", features = ["enable-serde"] }
cranelift-wasm = { version = "0.55", features = ["enable-serde"] }
cranelift-native = "0.55"
cranelift-frontend = "0.55"
wasmtime-environ = { path = "../environ", version = "0.9.0" }
wasmtime-runtime = { path = "../runtime", version = "0.9.0" }
wasmtime-debug = { path = "../debug", version = "0.9.0" }

View File

@@ -6,7 +6,10 @@ use std::cmp::max;
use std::{fmt, mem, ptr, slice};
use thiserror::Error;
use wasmtime_environ::ir;
use wasmtime_runtime::{wasmtime_call_trampoline, Export, InstanceHandle, VMInvokeArgument};
use wasmtime_runtime::{
wasmtime_call_trampoline, Backtrace, Export, InstanceHandle, TrapMessageAndStack,
VMInvokeArgument,
};
/// A runtime value.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -103,6 +106,8 @@ pub enum ActionOutcome {
Trapped {
/// The trap message.
message: String,
/// Backtrace.
trace: Backtrace,
},
}
@@ -191,7 +196,7 @@ pub fn invoke(
compiler.publish_compiled_code();
// Call the trampoline.
if let Err(message) = unsafe {
if let Err(TrapMessageAndStack(message, trace)) = unsafe {
instance.with_signals_on(|| {
wasmtime_call_trampoline(
callee_vmctx,
@@ -200,7 +205,7 @@ pub fn invoke(
)
})
} {
return Ok(ActionOutcome::Trapped { message });
return Ok(ActionOutcome::Trapped { message, trace });
}
// Load the return values out of `values_vec`.

View File

@@ -2,14 +2,45 @@
use crate::function_table::FunctionTable;
use region;
use std::mem::ManuallyDrop;
use std::{cmp, mem};
use wasmtime_environ::{Compilation, CompiledFunction};
use wasmtime_runtime::{Mmap, VMFunctionBody};
struct CodeMemoryEntry {
mmap: ManuallyDrop<Mmap>,
table: ManuallyDrop<FunctionTable>,
}
impl CodeMemoryEntry {
fn new() -> Self {
Self {
mmap: ManuallyDrop::new(Mmap::new()),
table: ManuallyDrop::new(FunctionTable::new()),
}
}
fn with_capacity(cap: usize) -> Result<Self, String> {
Ok(Self {
mmap: ManuallyDrop::new(Mmap::with_at_least(cap)?),
table: ManuallyDrop::new(FunctionTable::new()),
})
}
}
impl Drop for CodeMemoryEntry {
fn drop(&mut self) {
unsafe {
// Table needs to be freed before mmap.
ManuallyDrop::drop(&mut self.table);
ManuallyDrop::drop(&mut self.mmap);
}
}
}
/// Memory manager for executable code.
pub struct CodeMemory {
current: (Mmap, FunctionTable),
mmaps: Vec<(Mmap, FunctionTable)>,
current: CodeMemoryEntry,
entries: Vec<CodeMemoryEntry>,
position: usize,
published: usize,
}
@@ -23,8 +54,8 @@ impl CodeMemory {
/// Create a new `CodeMemory` instance.
pub fn new() -> Self {
Self {
current: (Mmap::new(), FunctionTable::new()),
mmaps: Vec::new(),
current: CodeMemoryEntry::new(),
entries: Vec::new(),
position: 0,
published: 0,
}
@@ -81,19 +112,20 @@ impl CodeMemory {
self.push_current(0)
.expect("failed to push current memory map");
for (m, t) in &mut self.mmaps[self.published..] {
for CodeMemoryEntry { mmap: m, table: t } in &mut self.entries[self.published..] {
// Remove write access to the pages due to the relocation fixups.
t.publish(m.as_ptr() as u64)
.expect("failed to publish function table");
if !m.is_empty() {
unsafe {
region::protect(m.as_mut_ptr(), m.len(), region::Protection::ReadExecute)
}
.expect("unable to make memory readonly and executable");
}
t.publish(m.as_ptr() as u64)
.expect("failed to publish function table");
}
self.published = self.mmaps.len();
self.published = self.entries.len();
}
/// Allocate `size` bytes of memory which can be made executable later by
@@ -103,7 +135,7 @@ impl CodeMemory {
///
/// TODO: Add an alignment flag.
fn allocate(&mut self, size: usize) -> Result<(&mut [u8], &mut FunctionTable), String> {
if self.current.0.len() - self.position < size {
if self.current.mmap.len() - self.position < size {
self.push_current(cmp::max(0x10000, size))?;
}
@@ -111,8 +143,8 @@ impl CodeMemory {
self.position += size;
Ok((
&mut self.current.0.as_mut_slice()[old_position..self.position],
&mut self.current.1,
&mut self.current.mmap.as_mut_slice()[old_position..self.position],
&mut self.current.table,
))
}
@@ -153,12 +185,19 @@ impl CodeMemory {
// Keep unwind information 32-bit aligned (round up to the nearest 4 byte boundary)
let padding = ((func.body.len() + 3) & !3) - func.body.len();
let (unwind, remainder) = remainder.split_at_mut(padding + func.unwind_info.len());
unwind[padding..].copy_from_slice(&func.unwind_info);
let mut relocs = Vec::new();
func.unwind_info
.serialize(&mut unwind[padding..], &mut relocs);
let unwind_start = func_end + (padding as u32);
let unwind_end = unwind_start + (func.unwind_info.len() as u32);
table.add_function(func_start, func_end, unwind_start);
relocs.iter_mut().for_each(move |r| {
r.offset += unwind_start;
r.addend += func_start;
});
table.add_function(func_start, func_end, unwind_start, &relocs);
(unwind_end, remainder, table, vmfunc)
}
@@ -174,20 +213,17 @@ impl CodeMemory {
fn push_current(&mut self, new_size: usize) -> Result<(), String> {
let previous = mem::replace(
&mut self.current,
(
if new_size == 0 {
Mmap::new()
} else {
Mmap::with_at_least(cmp::max(0x10000, new_size))?
},
FunctionTable::new(),
),
if new_size == 0 {
CodeMemoryEntry::new()
} else {
CodeMemoryEntry::with_capacity(cmp::max(0x10000, new_size))?
},
);
if !previous.0.is_empty() {
self.mmaps.push(previous);
if !previous.mmap.is_empty() {
self.entries.push(previous);
} else {
assert_eq!(previous.1.len(), 0);
assert_eq!(previous.table.len(), 0);
}
self.position = 0;

View File

@@ -16,12 +16,12 @@ use wasmtime_environ::entity::{EntityRef, PrimaryMap};
use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa};
use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex};
use wasmtime_environ::{
Compilation, CompileError, CompiledFunction, Compiler as _C, FunctionBodyData, Module,
ModuleVmctxInfo, Relocations, Traps, Tunables, VMOffsets,
Compilation, CompileError, CompiledFunction, CompiledFunctionUnwindInfo, Compiler as _C,
FunctionBodyData, Module, ModuleVmctxInfo, Relocations, Traps, Tunables, VMOffsets,
};
use wasmtime_runtime::{
get_mut_trap_registry, InstantiationError, SignatureRegistry, TrapRegistrationGuard,
VMFunctionBody,
get_mut_trap_registry, jit_function_registry, InstantiationError, SignatureRegistry,
TrapRegistrationGuard, VMFunctionBody,
};
/// Select which kind of compilation to use.
@@ -51,6 +51,7 @@ pub struct Compiler {
code_memory: CodeMemory,
trap_registration_guards: Vec<TrapRegistrationGuard>,
jit_function_ranges: Vec<(usize, usize)>,
trampoline_park: HashMap<*const VMFunctionBody, *const VMFunctionBody>,
signatures: SignatureRegistry,
strategy: CompilationStrategy,
@@ -66,6 +67,7 @@ impl Compiler {
isa,
code_memory: CodeMemory::new(),
trap_registration_guards: Vec::new(),
jit_function_ranges: Vec::new(),
trampoline_park: HashMap::new(),
signatures: SignatureRegistry::new(),
fn_builder_ctx: FunctionBuilderContext::new(),
@@ -85,6 +87,10 @@ impl Drop for Compiler {
// Having a custom drop implementation we are independent from the field order
// in the struct what reduces potential human error.
self.trap_registration_guards.clear();
for (start, end) in self.jit_function_ranges.iter() {
jit_function_registry::unregister(*start, *end);
}
}
}
@@ -155,6 +161,18 @@ impl Compiler {
&mut self.trap_registration_guards,
);
for (i, allocated) in allocated_functions.iter() {
let ptr = (*allocated) as *const VMFunctionBody;
let body_len = compilation.get(i).body.len();
self.jit_function_ranges
.push((ptr as usize, ptr as usize + body_len));
let tag = jit_function_registry::JITFunctionTag {
module_id: module.name.clone(),
func_index: i.index(),
};
jit_function_registry::register(ptr as usize, ptr as usize + body_len, tag);
}
let dbg = if let Some(debug_data) = debug_data {
let target_config = self.isa.frontend_config();
let ofs = VMOffsets::new(target_config.pointer_bytes(), &module);
@@ -215,6 +233,7 @@ impl Compiler {
signature,
value_size,
)?;
entry.insert(body);
body
}
@@ -266,6 +285,7 @@ fn make_trampoline(
let mut context = Context::new();
context.func = ir::Function::with_name_signature(ir::ExternalName::user(0, 0), wrapper_sig);
context.func.collect_frame_layout_info();
{
let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx);
@@ -326,7 +346,6 @@ fn make_trampoline(
}
let mut code_buf = Vec::new();
let mut unwind_info = Vec::new();
let mut reloc_sink = RelocSink {};
let mut trap_sink = binemit::NullTrapSink {};
let mut stackmap_sink = binemit::NullStackmapSink {};
@@ -346,7 +365,7 @@ fn make_trampoline(
)))
})?;
context.emit_unwind_info(isa, &mut unwind_info);
let unwind_info = CompiledFunctionUnwindInfo::new(isa, &context);
Ok(code_memory
.allocate_for_function(&CompiledFunction {

View File

@@ -2,42 +2,7 @@
//!
//! This module is primarily used to track JIT functions on Windows for stack walking and unwind.
/// Represents a runtime function table.
///
/// The runtime function table is not implemented for non-Windows target platforms.
#[cfg(not(target_os = "windows"))]
pub(crate) struct FunctionTable;
#[cfg(not(target_os = "windows"))]
impl FunctionTable {
/// Creates a new function table.
pub fn new() -> Self {
Self
}
/// Returns the number of functions in the table, also referred to as its 'length'.
///
/// For non-Windows platforms, the table will always be empty.
pub fn len(&self) -> usize {
0
}
/// Adds a function to the table based off of the start offset, end offset, and unwind offset.
///
/// The offsets are from the "module base", which is provided when the table is published.
///
/// For non-Windows platforms, this is a no-op.
pub fn add_function(&mut self, _start: u32, _end: u32, _unwind: u32) {}
/// Publishes the function table using the given base address.
///
/// A published function table will automatically be deleted when it is dropped.
///
/// For non-Windows platforms, this is a no-op.
pub fn publish(&mut self, _base_address: u64) -> Result<(), String> {
Ok(())
}
}
type FunctionTableReloc = wasmtime_environ::CompiledFunctionUnwindInfoReloc;
/// Represents a runtime function table.
///
@@ -66,7 +31,14 @@ impl FunctionTable {
/// Adds a function to the table based off of the start offset, end offset, and unwind offset.
///
/// The offsets are from the "module base", which is provided when the table is published.
pub fn add_function(&mut self, start: u32, end: u32, unwind: u32) {
pub fn add_function(
&mut self,
start: u32,
end: u32,
unwind: u32,
_relocs: &[FunctionTableReloc],
) {
assert_eq!(_relocs.len(), 0);
use winapi::um::winnt;
assert!(!self.published, "table has already been published");
@@ -133,3 +105,106 @@ impl Drop for FunctionTable {
}
}
}
/// Represents a runtime function table.
///
/// This is used to register JIT code with the operating system to enable stack walking and unwinding.
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub(crate) struct FunctionTable {
functions: Vec<u32>,
relocs: Vec<FunctionTableReloc>,
published: Option<Vec<usize>>,
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
impl FunctionTable {
/// Creates a new function table.
pub fn new() -> Self {
Self {
functions: Vec::new(),
relocs: Vec::new(),
published: None,
}
}
/// Returns the number of functions in the table, also referred to as its 'length'.
pub fn len(&self) -> usize {
self.functions.len()
}
/// Adds a function to the table based off of the start offset, end offset, and unwind offset.
///
/// The offsets are from the "module base", which is provided when the table is published.
pub fn add_function(
&mut self,
_start: u32,
_end: u32,
unwind: u32,
relocs: &[FunctionTableReloc],
) {
assert!(self.published.is_none(), "table has already been published");
self.functions.push(unwind);
self.relocs.extend_from_slice(relocs);
}
/// Publishes the function table using the given base address.
///
/// A published function table will automatically be deleted when it is dropped.
pub fn publish(&mut self, base_address: u64) -> Result<(), String> {
if self.published.is_some() {
return Err("function table was already published".into());
}
if self.functions.is_empty() {
assert_eq!(self.relocs.len(), 0);
self.published = Some(vec![]);
return Ok(());
}
extern "C" {
// libunwind import
fn __register_frame(fde: *const u8);
}
for reloc in self.relocs.iter() {
let addr = base_address + (reloc.offset as u64);
let target = base_address + (reloc.addend as u64);
unsafe {
std::ptr::write(addr as *mut u64, target);
}
}
let mut fdes = Vec::with_capacity(self.functions.len());
for unwind_offset in self.functions.iter() {
let addr = base_address + (*unwind_offset as u64);
let off = unsafe { std::ptr::read::<u32>(addr as *const u32) } as usize + 4;
let fde = (addr + off as u64) as usize;
unsafe {
__register_frame(fde as *const _);
}
fdes.push(fde);
}
self.published = Some(fdes);
Ok(())
}
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
impl Drop for FunctionTable {
fn drop(&mut self) {
extern "C" {
// libunwind import
fn __deregister_frame(fde: *const u8);
}
if self.published.is_some() {
unsafe {
for fde in self.published.as_ref().unwrap() {
__deregister_frame(*fde as *const _);
}
}
}
}
}

View File

@@ -19,7 +19,7 @@ memoffset = "0.5.3"
itertools = "0.8.2"
capstone = "0.6.0"
thiserror = "1.0.9"
cranelift-codegen = "0.54"
cranelift-codegen = "0.55"
multi_mut = "0.1"
either = "1.5"
typemap = "0.3"

View File

@@ -20,6 +20,7 @@ indexmap = "1.0.2"
thiserror = "1.0.4"
more-asserts = "0.2.1"
cfg-if = "0.1.9"
backtrace = "0.3.40"
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3.7", features = ["winbase", "memoryapi"] }

View File

@@ -0,0 +1,148 @@
//! Backtrace object and utilities.
use crate::jit_function_registry;
use std::sync::Arc;
/// Information about backtrace frame.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BacktraceFrame {
pc: usize,
}
impl Default for BacktraceFrame {
fn default() -> Self {
Self { pc: 0 }
}
}
impl BacktraceFrame {
/// Current PC or IP pointer for the frame.
pub fn pc(&self) -> usize {
self.pc
}
/// Additinal frame information.
pub fn tag(&self) -> Option<Arc<jit_function_registry::JITFunctionTag>> {
jit_function_registry::find(self.pc)
}
}
const BACKTRACE_LIMIT: usize = 32;
/// Backtrace during WebAssembly trap.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Backtrace {
len: usize,
frames: [BacktraceFrame; BACKTRACE_LIMIT],
}
impl Backtrace {
fn new() -> Self {
Self {
len: 0,
frames: [Default::default(); BACKTRACE_LIMIT],
}
}
fn full(&self) -> bool {
self.len >= BACKTRACE_LIMIT
}
fn push(&mut self, frame: BacktraceFrame) {
assert!(self.len < BACKTRACE_LIMIT);
self.frames[self.len] = frame;
self.len += 1;
}
/// Amount of the backtrace frames.
pub fn len(&self) -> usize {
self.len
}
}
impl std::ops::Index<usize> for Backtrace {
type Output = BacktraceFrame;
fn index(&self, index: usize) -> &Self::Output {
assert!(index < self.len);
&self.frames[index]
}
}
impl std::fmt::Debug for Backtrace {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Backtrace![")?;
for i in 0..self.len() {
let frame = &self.frames[i];
writeln!(f, " {:x}: {:?}", frame.pc(), frame.tag())?;
}
write!(f, "]")?;
Ok(())
}
}
#[cfg(not(all(target_os = "windows", target_arch = "x86_64")))]
fn capture_stack<F>(mut f: F)
where
F: FnMut(usize) -> bool,
{
use backtrace::trace;
trace(|frame| {
let pc = frame.ip() as usize;
f(pc)
});
}
#[cfg(all(target_os = "windows", target_arch = "x86_64"))]
fn capture_stack<F>(mut f: F)
where
F: FnMut(usize) -> bool,
{
use std::mem::MaybeUninit;
use std::ptr;
use winapi::um::winnt::{
RtlCaptureContext, RtlLookupFunctionEntry, RtlVirtualUnwind, CONTEXT, UNW_FLAG_NHANDLER,
};
#[repr(C, align(16))]
struct WrappedContext(CONTEXT);
unsafe {
let mut ctx = WrappedContext(MaybeUninit::uninit().assume_init());
RtlCaptureContext(&mut ctx.0);
let mut unwind_history_table = MaybeUninit::zeroed().assume_init();
while ctx.0.Rip != 0 {
let cont = f(ctx.0.Rip as usize);
if !cont {
break;
}
let mut image_base: u64 = 0;
let mut handler_data: *mut core::ffi::c_void = ptr::null_mut();
let mut establisher_frame: u64 = 0;
let rf = RtlLookupFunctionEntry(ctx.0.Rip, &mut image_base, &mut unwind_history_table);
if rf.is_null() {
ctx.0.Rip = ptr::read(ctx.0.Rsp as *const u64);
ctx.0.Rsp += 8;
} else {
RtlVirtualUnwind(
UNW_FLAG_NHANDLER,
image_base,
ctx.0.Rip,
rf,
&mut ctx.0,
&mut handler_data,
&mut establisher_frame,
ptr::null_mut(),
);
}
}
}
}
/// Returns current backtrace. Only registered wasmtime functions will be listed.
pub fn get_backtrace() -> Backtrace {
let mut frames = Backtrace::new();
capture_stack(|pc| {
if let Some(_) = jit_function_registry::find(pc) {
frames.push(BacktraceFrame { pc });
}
!frames.full()
});
frames
}

View File

@@ -9,7 +9,7 @@ use crate::memory::LinearMemory;
use crate::mmap::Mmap;
use crate::signalhandlers::{wasmtime_init_eager, wasmtime_init_finish};
use crate::table::Table;
use crate::traphandlers::wasmtime_call;
use crate::traphandlers::{wasmtime_call, TrapMessageAndStack};
use crate::vmcontext::{
VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMFunctionImport,
VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex,
@@ -568,7 +568,7 @@ impl Instance {
// Make the call.
unsafe { wasmtime_call(callee_vmctx, callee_address) }
.map_err(InstantiationError::StartTrap)
.map_err(|TrapMessageAndStack(msg, _)| InstantiationError::StartTrap(msg))
}
/// Invoke the WebAssembly start function of the instance, if one is present.

View File

@@ -0,0 +1,83 @@
#![allow(missing_docs)]
use lazy_static::lazy_static;
use std::collections::BTreeMap;
use std::sync::{Arc, RwLock};
lazy_static! {
static ref REGISTRY: RwLock<JITFunctionRegistry> = RwLock::new(JITFunctionRegistry::default());
}
#[derive(Clone)]
pub struct JITFunctionTag {
pub module_id: Option<String>,
pub func_index: usize,
}
impl std::fmt::Debug for JITFunctionTag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(ref module_id) = self.module_id {
write!(f, "{}", module_id)?;
} else {
write!(f, "(module)")?;
}
write!(f, ":{}", self.func_index)
}
}
struct JITFunctionRegistry {
ranges: BTreeMap<usize, (usize, Arc<JITFunctionTag>)>,
}
impl Default for JITFunctionRegistry {
fn default() -> Self {
Self {
ranges: Default::default(),
}
}
}
impl JITFunctionRegistry {
fn register(&mut self, fn_start: usize, fn_end: usize, tag: JITFunctionTag) {
self.ranges.insert(fn_end, (fn_start, Arc::new(tag)));
}
fn unregister(&mut self, fn_end: usize) {
self.ranges.remove(&fn_end);
}
fn find(&self, pc: usize) -> Option<&Arc<JITFunctionTag>> {
self.ranges
.range(pc..)
.next()
.and_then(|(end, (start, s))| {
if *start <= pc && pc < *end {
Some(s)
} else {
None
}
})
}
}
pub fn register(fn_start: usize, fn_end: usize, tag: JITFunctionTag) {
REGISTRY
.write()
.expect("jit function registry lock got poisoned")
.register(fn_start, fn_end, tag);
}
pub fn unregister(_fn_start: usize, fn_end: usize) {
REGISTRY
.write()
.expect("jit function registry lock got poisoned")
.unregister(fn_end);
}
pub fn find(pc: usize) -> Option<Arc<JITFunctionTag>> {
REGISTRY
.read()
.expect("jit function registry lock got poisoned")
.find(pc)
.cloned()
}

View File

@@ -21,6 +21,7 @@
)
)]
mod backtrace;
mod export;
mod imports;
mod instance;
@@ -34,8 +35,10 @@ mod trap_registry;
mod traphandlers;
mod vmcontext;
pub mod jit_function_registry;
pub mod libcalls;
pub use crate::backtrace::{get_backtrace, Backtrace, BacktraceFrame};
pub use crate::export::Export;
pub use crate::imports::Imports;
pub use crate::instance::{InstanceHandle, InstantiationError, LinkError};
@@ -44,7 +47,7 @@ pub use crate::mmap::Mmap;
pub use crate::sig_registry::SignatureRegistry;
pub use crate::signalhandlers::{wasmtime_init_eager, wasmtime_init_finish};
pub use crate::trap_registry::{get_mut_trap_registry, get_trap_registry, TrapRegistrationGuard};
pub use crate::traphandlers::{wasmtime_call, wasmtime_call_trampoline};
pub use crate::traphandlers::{wasmtime_call, wasmtime_call_trampoline, TrapMessageAndStack};
pub use crate::vmcontext::{
VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMFunctionImport, VMGlobalDefinition,
VMGlobalImport, VMInvokeArgument, VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex,

View File

@@ -1,6 +1,7 @@
//! WebAssembly trap handling, which is built on top of the lower-level
//! signalhandling mechanisms.
use crate::backtrace::{get_backtrace, Backtrace};
use crate::trap_registry::get_trap_registry;
use crate::trap_registry::TrapDescription;
use crate::vmcontext::{VMContext, VMFunctionBody};
@@ -18,7 +19,7 @@ extern "C" {
}
thread_local! {
static RECORDED_TRAP: Cell<Option<TrapDescription>> = Cell::new(None);
static RECORDED_TRAP: Cell<Option<(TrapDescription, Backtrace)>> = Cell::new(None);
static JMP_BUF: Cell<*const u8> = Cell::new(ptr::null());
static RESET_GUARD_PAGE: Cell<bool> = Cell::new(false);
}
@@ -50,6 +51,8 @@ pub extern "C" fn RecordTrap(pc: *const u8, reset_guard_page: bool) {
trap_code: ir::TrapCode::StackOverflow,
});
let trap_backtrace = get_backtrace();
if reset_guard_page {
RESET_GUARD_PAGE.with(|v| v.set(true));
}
@@ -60,7 +63,7 @@ pub extern "C" fn RecordTrap(pc: *const u8, reset_guard_page: bool) {
None,
"Only one trap per thread can be recorded at a moment!"
);
data.set(Some(trap_desc))
data.set(Some((trap_desc, trap_backtrace)))
});
}
@@ -108,15 +111,22 @@ fn reset_guard_page() {
#[cfg(not(target_os = "windows"))]
fn reset_guard_page() {}
fn trap_message() -> String {
/// Stores trace message with backtrace.
#[derive(Debug)]
pub struct TrapMessageAndStack(pub String, pub Backtrace);
fn trap_message_and_stack() -> TrapMessageAndStack {
let trap_desc = RECORDED_TRAP
.with(|data| data.replace(None))
.expect("trap_message must be called after trap occurred");
format!(
"wasm trap: {}, source location: {}",
trap_code_to_expected_string(trap_desc.trap_code),
trap_desc.source_loc,
TrapMessageAndStack(
format!(
"wasm trap: {}, source location: {}",
trap_code_to_expected_string(trap_desc.0.trap_code),
trap_desc.0.source_loc,
),
trap_desc.1,
)
}
@@ -146,9 +156,9 @@ pub unsafe extern "C" fn wasmtime_call_trampoline(
vmctx: *mut VMContext,
callee: *const VMFunctionBody,
values_vec: *mut u8,
) -> Result<(), String> {
) -> Result<(), TrapMessageAndStack> {
if WasmtimeCallTrampoline(vmctx as *mut u8, callee, values_vec) == 0 {
Err(trap_message())
Err(trap_message_and_stack())
} else {
Ok(())
}
@@ -160,9 +170,9 @@ pub unsafe extern "C" fn wasmtime_call_trampoline(
pub unsafe extern "C" fn wasmtime_call(
vmctx: *mut VMContext,
callee: *const VMFunctionBody,
) -> Result<(), String> {
) -> Result<(), TrapMessageAndStack> {
if WasmtimeCall(vmctx as *mut u8, callee) == 0 {
Err(trap_message())
Err(trap_message_and_stack())
} else {
Ok(())
}

View File

@@ -14,9 +14,9 @@ edition = "2018"
wasmtime-runtime = { path = "../runtime", version = "0.9.0" }
wasmtime-environ = { path = "../environ", version = "0.9.0" }
wasmtime-jit = { path = "../jit", version = "0.9.0" }
cranelift-codegen = { version = "0.54", features = ["enable-serde"] }
cranelift-entity = { version = "0.54", features = ["enable-serde"] }
cranelift-wasm = { version = "0.54", features = ["enable-serde"] }
cranelift-codegen = { version = "0.55", features = ["enable-serde"] }
cranelift-entity = { version = "0.55", features = ["enable-serde"] }
cranelift-wasm = { version = "0.55", features = ["enable-serde"] }
target-lexicon = "0.10.0"
log = { version = "0.4.8", default-features = false }
libc = "0.2.60"

View File

@@ -16,9 +16,9 @@ wasmtime-runtime = { path = "../runtime", version = "0.9.0" }
wasmtime-environ = { path = "../environ", version = "0.9.0" }
wasmtime-jit = { path = "../jit", version = "0.9.0" }
wasi-common = { path = "../wasi-common", version = "0.9.0" }
cranelift-codegen = { version = "0.54", features = ["enable-serde"] }
cranelift-entity = { version = "0.54", features = ["enable-serde"] }
cranelift-wasm = { version = "0.54", features = ["enable-serde"] }
cranelift-codegen = { version = "0.55", features = ["enable-serde"] }
cranelift-entity = { version = "0.55", features = ["enable-serde"] }
cranelift-wasm = { version = "0.55", features = ["enable-serde"] }
target-lexicon = "0.10.0"
log = { version = "0.4.8", default-features = false }
wig = { path = "../wasi-common/wig", version = "0.9.2" }

View File

@@ -9,7 +9,7 @@ topdir=$(dirname "$0")/..
cd "$topdir"
# All the cranelift-* crates have the same version number
version="0.53"
version="0.55"
# Update all of the Cargo.toml files.
echo "Updating crate versions to $version"