Implement registering JIT unwind information on Windows.
This commit implements registering unwind information for JIT functions on Windows so that the operating system can both walk and unwind stacks containing JIT frames. Currently this only works with Cranelift as lightbeam does not emit unwind information yet. This commit also resets the stack guard page on Windows for stack overflow exceptions, allowing reliable stack overflow traps. With these changes, all previously disabled test suite tests (not including the multi-value tests) on Windows are now passing. Fixes #291.
This commit is contained in:
5
wasmtime-environ/src/cache/tests.rs
vendored
5
wasmtime-environ/src/cache/tests.rs
vendored
@@ -1,7 +1,7 @@
|
||||
use super::config::tests::test_prolog;
|
||||
use super::*;
|
||||
use crate::address_map::{FunctionAddressMap, InstructionAddressMap};
|
||||
use crate::compilation::{CodeAndJTOffsets, Relocation, RelocationTarget, TrapInformation};
|
||||
use crate::compilation::{CompiledFunction, Relocation, RelocationTarget, TrapInformation};
|
||||
use crate::module::{MemoryPlan, MemoryStyle, Module};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
@@ -258,9 +258,10 @@ fn new_module_cache_data(rng: &mut impl Rng) -> ModuleCacheData {
|
||||
*v = (j as u32) * 3 / 4
|
||||
}
|
||||
});
|
||||
CodeAndJTOffsets {
|
||||
CompiledFunction {
|
||||
body: (0..(i * 3 / 2)).collect(),
|
||||
jt_offsets: sm,
|
||||
unwind_info: (0..(i * 3 / 2)).collect(),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -12,17 +12,20 @@ use serde::{Deserialize, Serialize};
|
||||
use std::ops::Range;
|
||||
use thiserror::Error;
|
||||
|
||||
/// Compiled machine code: body and jump table offsets.
|
||||
/// Compiled function: machine code body, jump table offsets, and unwind information.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CodeAndJTOffsets {
|
||||
pub struct CompiledFunction {
|
||||
/// The function body.
|
||||
pub body: Vec<u8>,
|
||||
|
||||
/// The jump tables offsets (in the body).
|
||||
pub jt_offsets: ir::JumpTableOffsets,
|
||||
|
||||
/// The unwind information.
|
||||
pub unwind_info: Vec<u8>,
|
||||
}
|
||||
|
||||
type Functions = PrimaryMap<DefinedFuncIndex, CodeAndJTOffsets>;
|
||||
type Functions = PrimaryMap<DefinedFuncIndex, CompiledFunction>;
|
||||
|
||||
/// The result of compiling a WebAssembly module's functions.
|
||||
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
|
||||
@@ -40,21 +43,22 @@ impl Compilation {
|
||||
/// Allocates the compilation result with the given function bodies.
|
||||
pub fn from_buffer(
|
||||
buffer: Vec<u8>,
|
||||
functions: impl IntoIterator<Item = (Range<usize>, ir::JumpTableOffsets)>,
|
||||
functions: impl IntoIterator<Item = (Range<usize>, ir::JumpTableOffsets, Range<usize>)>,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
functions
|
||||
.into_iter()
|
||||
.map(|(range, jt_offsets)| CodeAndJTOffsets {
|
||||
body: buffer[range].to_vec(),
|
||||
.map(|(body_range, jt_offsets, unwind_range)| CompiledFunction {
|
||||
body: buffer[body_range].to_vec(),
|
||||
jt_offsets,
|
||||
unwind_info: buffer[unwind_range].to_vec(),
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Gets the bytes of a single function
|
||||
pub fn get(&self, func: DefinedFuncIndex) -> &CodeAndJTOffsets {
|
||||
pub fn get(&self, func: DefinedFuncIndex) -> &CompiledFunction {
|
||||
&self.functions[func]
|
||||
}
|
||||
|
||||
@@ -67,7 +71,7 @@ impl Compilation {
|
||||
pub fn get_jt_offsets(&self) -> PrimaryMap<DefinedFuncIndex, ir::JumpTableOffsets> {
|
||||
self.functions
|
||||
.iter()
|
||||
.map(|(_, code_and_jt)| code_and_jt.jt_offsets.clone())
|
||||
.map(|(_, func)| func.jt_offsets.clone())
|
||||
.collect::<PrimaryMap<DefinedFuncIndex, _>>()
|
||||
}
|
||||
}
|
||||
@@ -88,7 +92,7 @@ pub struct Iter<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Iter<'a> {
|
||||
type Item = &'a CodeAndJTOffsets;
|
||||
type Item = &'a CompiledFunction;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iterator.next().map(|(_, b)| b)
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::address_map::{
|
||||
};
|
||||
use crate::cache::{ModuleCacheData, ModuleCacheEntry};
|
||||
use crate::compilation::{
|
||||
CodeAndJTOffsets, Compilation, CompileError, Relocation, RelocationTarget, Relocations,
|
||||
Compilation, CompileError, CompiledFunction, Relocation, RelocationTarget, Relocations,
|
||||
TrapInformation, Traps,
|
||||
};
|
||||
use crate::func_environ::{
|
||||
@@ -235,6 +235,7 @@ 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 {};
|
||||
@@ -246,7 +247,7 @@ impl crate::compilation::Compiler for Cranelift {
|
||||
&mut stackmap_sink,
|
||||
)?;
|
||||
|
||||
let jt_offsets = context.func.jt_offsets.clone();
|
||||
context.emit_unwind_info(isa, &mut unwind_info);
|
||||
|
||||
let address_transform = if generate_debug_info {
|
||||
let body_len = code_buf.len();
|
||||
@@ -261,16 +262,15 @@ impl crate::compilation::Compiler for Cranelift {
|
||||
None
|
||||
};
|
||||
|
||||
let stack_slots = context.func.stack_slots.clone();
|
||||
|
||||
Ok((
|
||||
code_buf,
|
||||
jt_offsets,
|
||||
context.func.jt_offsets,
|
||||
reloc_sink.func_relocs,
|
||||
address_transform,
|
||||
ranges,
|
||||
stack_slots,
|
||||
context.func.stack_slots,
|
||||
trap_sink.traps,
|
||||
unwind_info,
|
||||
))
|
||||
},
|
||||
)
|
||||
@@ -285,10 +285,12 @@ impl crate::compilation::Compiler for Cranelift {
|
||||
ranges,
|
||||
sss,
|
||||
function_traps,
|
||||
unwind_info,
|
||||
)| {
|
||||
functions.push(CodeAndJTOffsets {
|
||||
functions.push(CompiledFunction {
|
||||
body: function,
|
||||
jt_offsets: func_jt_offsets,
|
||||
unwind_info,
|
||||
});
|
||||
relocations.push(relocs);
|
||||
if let Some(address_transform) = address_transform {
|
||||
|
||||
@@ -46,8 +46,8 @@ 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, Compiler, Relocation, RelocationTarget, Relocations,
|
||||
TrapInformation, Traps,
|
||||
Compilation, CompileError, CompiledFunction, Compiler, Relocation, RelocationTarget,
|
||||
Relocations, TrapInformation, Traps,
|
||||
};
|
||||
pub use crate::cranelift::Cranelift;
|
||||
pub use crate::func_environ::BuiltinFunctionIndex;
|
||||
|
||||
@@ -65,10 +65,11 @@ impl crate::compilation::Compiler for Lightbeam {
|
||||
|
||||
// TODO pass jump table offsets to Compilation::from_buffer() when they
|
||||
// are implemented in lightbeam -- using empty set of offsets for now.
|
||||
// TODO: pass an empty range for the unwind information until lightbeam emits it
|
||||
let code_section_ranges_and_jt = code_section
|
||||
.funcs()
|
||||
.into_iter()
|
||||
.map(|r| (r, SecondaryMap::new()));
|
||||
.map(|r| (r, SecondaryMap::new(), 0..0));
|
||||
|
||||
Ok((
|
||||
Compilation::from_buffer(code_section.buffer().to_vec(), code_section_ranges_and_jt),
|
||||
|
||||
Reference in New Issue
Block a user