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:
Peter Huene
2019-10-19 13:12:04 -07:00
parent ab80785c05
commit 920728d14d
16 changed files with 382 additions and 131 deletions

View File

@@ -404,7 +404,7 @@ static
__attribute__ ((warn_unused_result))
#endif
bool
HandleTrap(CONTEXT* context)
HandleTrap(CONTEXT* context, bool reset_guard_page)
{
assert(sAlreadyHandlingTrap);
@@ -412,7 +412,7 @@ HandleTrap(CONTEXT* context)
return false;
}
RecordTrap(ContextToPC(context));
RecordTrap(ContextToPC(context), reset_guard_page);
// Unwind calls longjmp, so it doesn't run the automatic
// sAlreadhHanldingTrap cleanups, so reset it manually before doing
@@ -467,7 +467,8 @@ WasmTrapHandler(LPEXCEPTION_POINTERS exception)
return EXCEPTION_CONTINUE_SEARCH;
}
if (!HandleTrap(exception->ContextRecord)) {
if (!HandleTrap(exception->ContextRecord,
record->ExceptionCode == EXCEPTION_STACK_OVERFLOW)) {
return EXCEPTION_CONTINUE_SEARCH;
}
@@ -549,7 +550,7 @@ HandleMachException(const ExceptionRequest& request)
{
AutoHandlingTrap aht;
if (!HandleTrap(&context)) {
if (!HandleTrap(&context, false)) {
return false;
}
}
@@ -632,7 +633,7 @@ WasmTrapHandler(int signum, siginfo_t* info, void* context)
if (!sAlreadyHandlingTrap) {
AutoHandlingTrap aht;
assert(signum == SIGSEGV || signum == SIGBUS || signum == SIGFPE || signum == SIGILL);
if (HandleTrap(static_cast<CONTEXT*>(context))) {
if (HandleTrap(static_cast<CONTEXT*>(context), false)) {
return;
}
}

View File

@@ -13,7 +13,7 @@ extern "C" {
int8_t CheckIfTrapAtAddress(const uint8_t* pc);
// Record the Trap code and wasm bytecode offset in TLS somewhere
void RecordTrap(const uint8_t* pc);
void RecordTrap(const uint8_t* pc, bool reset_guard_page);
void* EnterScope(void*);
void LeaveScope(void*);

View File

@@ -21,6 +21,7 @@ extern "C" {
thread_local! {
static RECORDED_TRAP: Cell<Option<TrapDescription>> = Cell::new(None);
static JMP_BUF: Cell<*const u8> = Cell::new(ptr::null());
static RESET_GUARD_PAGE: Cell<bool> = Cell::new(false);
}
/// Check if there is a trap at given PC
@@ -40,7 +41,7 @@ pub extern "C" fn CheckIfTrapAtAddress(_pc: *const u8) -> i8 {
#[doc(hidden)]
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn RecordTrap(pc: *const u8) {
pub extern "C" fn RecordTrap(pc: *const u8, reset_guard_page: bool) {
// TODO: please see explanation in CheckIfTrapAtAddress.
let registry = get_trap_registry();
let trap_desc = registry
@@ -49,6 +50,11 @@ pub extern "C" fn RecordTrap(pc: *const u8) {
source_loc: ir::SourceLoc::default(),
trap_code: ir::TrapCode::StackOverflow,
});
if reset_guard_page {
RESET_GUARD_PAGE.with(|v| v.set(true));
}
RECORDED_TRAP.with(|data| {
assert_eq!(
data.get(),
@@ -77,9 +83,32 @@ pub extern "C" fn GetScope() -> *const u8 {
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn LeaveScope(ptr: *const u8) {
RESET_GUARD_PAGE.with(|v| {
if v.get() {
reset_guard_page();
v.set(false);
}
});
JMP_BUF.with(|buf| buf.set(ptr))
}
#[cfg(target_os = "windows")]
fn reset_guard_page() {
extern "C" {
fn _resetstkoflw() -> winapi::ctypes::c_int;
}
// We need to restore guard page under stack to handle future stack overflows properly.
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/resetstkoflw?view=vs-2019
if unsafe { _resetstkoflw() } == 0 {
panic!("failed to restore stack guard page");
}
}
#[cfg(not(target_os = "windows"))]
fn reset_guard_page() {}
fn trap_message() -> String {
let trap_desc = RECORDED_TRAP
.with(|data| data.replace(None))