Remove C++ dependency from wasmtime (#1365)

* Remove C++ dependency from `wasmtime`

This commit removes the last wads of C++ that we have in wasmtime,
meaning that building wasmtime no longer requires a C++ compiler. It
still does require a C toolchain for some minor purposes, but hopefully
we can remove that over time too!

The motivation for doing this is to consolidate all our signal-handling
code into one location in one language so you don't have to keep
crossing back and forth when understanding what's going on. This also
allows us to remove some extra cruft that wasn't necessary from the C++
original implementation. Additionally this should also make building
wasmtime a bit more portable since it's often easier to acquire a C
toolchain than it is to acquire a C++ toolchain. (e.g. if you're
cross-compiling to a musl target)

* Typos
This commit is contained in:
Alex Crichton
2020-03-20 15:21:42 -05:00
committed by GitHub
parent c50c24e699
commit f700efeb03
9 changed files with 260 additions and 879 deletions

View File

@@ -22,7 +22,7 @@ cfg-if = "0.1.9"
backtrace = "0.3.42" backtrace = "0.3.42"
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3.7", features = ["winbase", "memoryapi"] } winapi = { version = "0.3.7", features = ["winbase", "memoryapi", "errhandlingapi"] }
[build-dependencies] [build-dependencies]
cc = "1.0" cc = "1.0"

View File

@@ -1,20 +1,7 @@
fn main() { fn main() {
println!("cargo:rerun-if-changed=signalhandlers/SignalHandlers.cpp"); println!("cargo:rerun-if-changed=src/helpers.c");
println!("cargo:rerun-if-changed=signalhandlers/SignalHandlers.hpp"); cc::Build::new()
println!("cargo:rerun-if-changed=signalhandlers/Trampolines.cpp"); .warnings(true)
let target = std::env::var("TARGET").unwrap(); .file("src/helpers.c")
let mut build = cc::Build::new(); .compile("helpers");
build
.cpp(true)
.warnings(false)
.file("signalhandlers/SignalHandlers.cpp")
.file("signalhandlers/Trampolines.cpp");
if !target.contains("windows") {
build
.flag("-std=c++11")
.flag("-fno-exceptions")
.flag("-fno-rtti");
}
build.compile("signalhandlers");
} }

View File

@@ -1,755 +0,0 @@
//! This file is largely derived from the code in WasmSignalHandlers.cpp in SpiderMonkey:
//!
//! https://dxr.mozilla.org/mozilla-central/source/js/src/wasm/WasmSignalHandlers.cpp
//!
//! Use of Mach ports on Darwin platforms (the USE_APPLE_MACH_PORTS code below) is
//! currently disabled.
#include "SignalHandlers.hpp"
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#if defined(_WIN32)
# include <windows.h>
# include <winternl.h>
#elif defined(USE_APPLE_MACH_PORTS)
# include <mach/exc.h>
# include <mach/mach.h>
# include <pthread.h>
#else
# include <signal.h>
#endif
// =============================================================================
// This following pile of macros and includes defines the ToRegisterState() and
// the ContextToPC() functions from the (highly) platform-specific CONTEXT
// struct which is provided to the signal handler.
// =============================================================================
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
# include <sys/ucontext.h> // for ucontext_t, mcontext_t
#endif
#if defined(__x86_64__)
# if defined(__DragonFly__)
# include <machine/npx.h> // for union savefpu
# elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
defined(__NetBSD__) || defined(__OpenBSD__)
# include <machine/fpu.h> // for struct savefpu/fxsave64
# endif
#endif
#if defined(_WIN32)
# define EIP_sig(p) ((p)->Eip)
# define EBP_sig(p) ((p)->Ebp)
# define ESP_sig(p) ((p)->Esp)
# define RIP_sig(p) ((p)->Rip)
# define RSP_sig(p) ((p)->Rsp)
# define RBP_sig(p) ((p)->Rbp)
# define R11_sig(p) ((p)->R11)
# define R13_sig(p) ((p)->R13)
# define R14_sig(p) ((p)->R14)
# define R15_sig(p) ((p)->R15)
# define EPC_sig(p) ((p)->Pc)
# define RFP_sig(p) ((p)->Fp)
# define R31_sig(p) ((p)->Sp)
# define RLR_sig(p) ((p)->Lr)
#elif defined(__OpenBSD__)
# define EIP_sig(p) ((p)->sc_eip)
# define EBP_sig(p) ((p)->sc_ebp)
# define ESP_sig(p) ((p)->sc_esp)
# define RIP_sig(p) ((p)->sc_rip)
# define RSP_sig(p) ((p)->sc_rsp)
# define RBP_sig(p) ((p)->sc_rbp)
# define R11_sig(p) ((p)->sc_r11)
# if defined(__arm__)
# define R13_sig(p) ((p)->sc_usr_sp)
# define R14_sig(p) ((p)->sc_usr_lr)
# define R15_sig(p) ((p)->sc_pc)
# else
# define R13_sig(p) ((p)->sc_r13)
# define R14_sig(p) ((p)->sc_r14)
# define R15_sig(p) ((p)->sc_r15)
# endif
# if defined(__aarch64__)
# define EPC_sig(p) ((p)->sc_elr)
# define RFP_sig(p) ((p)->sc_x[29])
# define RLR_sig(p) ((p)->sc_lr)
# define R31_sig(p) ((p)->sc_sp)
# endif
# if defined(__mips__)
# define EPC_sig(p) ((p)->sc_pc)
# define RFP_sig(p) ((p)->sc_regs[30])
# endif
#elif defined(__linux__) || defined(__sun)
# if defined(__linux__)
# define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_EIP])
# define EBP_sig(p) ((p)->uc_mcontext.gregs[REG_EBP])
# define ESP_sig(p) ((p)->uc_mcontext.gregs[REG_ESP])
# else
# define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_PC])
# define EBP_sig(p) ((p)->uc_mcontext.gregs[REG_EBP])
# define ESP_sig(p) ((p)->uc_mcontext.gregs[REG_ESP])
# endif
# define RIP_sig(p) ((p)->uc_mcontext.gregs[REG_RIP])
# define RSP_sig(p) ((p)->uc_mcontext.gregs[REG_RSP])
# define RBP_sig(p) ((p)->uc_mcontext.gregs[REG_RBP])
# if defined(__linux__) && defined(__arm__)
# define R11_sig(p) ((p)->uc_mcontext.arm_fp)
# define R13_sig(p) ((p)->uc_mcontext.arm_sp)
# define R14_sig(p) ((p)->uc_mcontext.arm_lr)
# define R15_sig(p) ((p)->uc_mcontext.arm_pc)
# else
# define R11_sig(p) ((p)->uc_mcontext.gregs[REG_R11])
# define R13_sig(p) ((p)->uc_mcontext.gregs[REG_R13])
# define R14_sig(p) ((p)->uc_mcontext.gregs[REG_R14])
# define R15_sig(p) ((p)->uc_mcontext.gregs[REG_R15])
# endif
# if defined(__linux__) && defined(__aarch64__)
# define EPC_sig(p) ((p)->uc_mcontext.pc)
# define RFP_sig(p) ((p)->uc_mcontext.regs[29])
# define RLR_sig(p) ((p)->uc_mcontext.regs[30])
# define R31_sig(p) ((p)->uc_mcontext.regs[31])
# endif
# if defined(__linux__) && defined(__mips__)
# define EPC_sig(p) ((p)->uc_mcontext.pc)
# define RFP_sig(p) ((p)->uc_mcontext.gregs[30])
# define RSP_sig(p) ((p)->uc_mcontext.gregs[29])
# define R31_sig(p) ((p)->uc_mcontext.gregs[31])
# endif
# if defined(__linux__) && (defined(__sparc__) && defined(__arch64__))
# define PC_sig(p) ((p)->uc_mcontext.mc_gregs[MC_PC])
# define FP_sig(p) ((p)->uc_mcontext.mc_fp)
# define SP_sig(p) ((p)->uc_mcontext.mc_i7)
# endif
# if defined(__linux__) && \
(defined(__ppc64__) || defined (__PPC64__) || defined(__ppc64le__) || defined (__PPC64LE__))
# define R01_sig(p) ((p)->uc_mcontext.gp_regs[1])
# define R32_sig(p) ((p)->uc_mcontext.gp_regs[32])
# endif
#elif defined(__NetBSD__)
# define EIP_sig(p) ((p)->uc_mcontext.__gregs[_REG_EIP])
# define EBP_sig(p) ((p)->uc_mcontext.__gregs[_REG_EBP])
# define ESP_sig(p) ((p)->uc_mcontext.__gregs[_REG_ESP])
# define RIP_sig(p) ((p)->uc_mcontext.__gregs[_REG_RIP])
# define RSP_sig(p) ((p)->uc_mcontext.__gregs[_REG_RSP])
# define RBP_sig(p) ((p)->uc_mcontext.__gregs[_REG_RBP])
# define R11_sig(p) ((p)->uc_mcontext.__gregs[_REG_R11])
# define R13_sig(p) ((p)->uc_mcontext.__gregs[_REG_R13])
# define R14_sig(p) ((p)->uc_mcontext.__gregs[_REG_R14])
# define R15_sig(p) ((p)->uc_mcontext.__gregs[_REG_R15])
# if defined(__aarch64__)
# define EPC_sig(p) ((p)->uc_mcontext.__gregs[_REG_PC])
# define RFP_sig(p) ((p)->uc_mcontext.__gregs[_REG_X29])
# define RLR_sig(p) ((p)->uc_mcontext.__gregs[_REG_X30])
# define R31_sig(p) ((p)->uc_mcontext.__gregs[_REG_SP])
# endif
# if defined(__mips__)
# define EPC_sig(p) ((p)->uc_mcontext.__gregs[_REG_EPC])
# define RFP_sig(p) ((p)->uc_mcontext.__gregs[_REG_S8])
# endif
#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
# define EIP_sig(p) ((p)->uc_mcontext.mc_eip)
# define EBP_sig(p) ((p)->uc_mcontext.mc_ebp)
# define ESP_sig(p) ((p)->uc_mcontext.mc_esp)
# define RIP_sig(p) ((p)->uc_mcontext.mc_rip)
# define RSP_sig(p) ((p)->uc_mcontext.mc_rsp)
# define RBP_sig(p) ((p)->uc_mcontext.mc_rbp)
# if defined(__FreeBSD__) && defined(__arm__)
# define R11_sig(p) ((p)->uc_mcontext.__gregs[_REG_R11])
# define R13_sig(p) ((p)->uc_mcontext.__gregs[_REG_R13])
# define R14_sig(p) ((p)->uc_mcontext.__gregs[_REG_R14])
# define R15_sig(p) ((p)->uc_mcontext.__gregs[_REG_R15])
# else
# define R11_sig(p) ((p)->uc_mcontext.mc_r11)
# define R13_sig(p) ((p)->uc_mcontext.mc_r13)
# define R14_sig(p) ((p)->uc_mcontext.mc_r14)
# define R15_sig(p) ((p)->uc_mcontext.mc_r15)
# endif
# if defined(__FreeBSD__) && defined(__aarch64__)
# define EPC_sig(p) ((p)->uc_mcontext.mc_gpregs.gp_elr)
# define RFP_sig(p) ((p)->uc_mcontext.mc_gpregs.gp_x[29])
# define RLR_sig(p) ((p)->uc_mcontext.mc_gpregs.gp_lr)
# define R31_sig(p) ((p)->uc_mcontext.mc_gpregs.gp_sp)
# endif
# if defined(__FreeBSD__) && defined(__mips__)
# define EPC_sig(p) ((p)->uc_mcontext.mc_pc)
# define RFP_sig(p) ((p)->uc_mcontext.mc_regs[30])
# endif
#elif defined(USE_APPLE_MACH_PORTS)
# define EIP_sig(p) ((p)->thread.uts.ts32.__eip)
# define EBP_sig(p) ((p)->thread.uts.ts32.__ebp)
# define ESP_sig(p) ((p)->thread.uts.ts32.__esp)
# define RIP_sig(p) ((p)->thread.__rip)
# define RBP_sig(p) ((p)->thread.__rbp)
# define RSP_sig(p) ((p)->thread.__rsp)
# define R11_sig(p) ((p)->thread.__r[11])
# define R13_sig(p) ((p)->thread.__sp)
# define R14_sig(p) ((p)->thread.__lr)
# define R15_sig(p) ((p)->thread.__pc)
#elif defined(__APPLE__)
# define EIP_sig(p) ((p)->uc_mcontext->__ss.__eip)
# define EBP_sig(p) ((p)->uc_mcontext->__ss.__ebp)
# define ESP_sig(p) ((p)->uc_mcontext->__ss.__esp)
# define RIP_sig(p) ((p)->uc_mcontext->__ss.__rip)
# define RBP_sig(p) ((p)->uc_mcontext->__ss.__rbp)
# define RSP_sig(p) ((p)->uc_mcontext->__ss.__rsp)
# define R11_sig(p) ((p)->uc_mcontext->__ss.__r11)
# define R13_sig(p) ((p)->uc_mcontext->__ss.__sp)
# define R14_sig(p) ((p)->uc_mcontext->__ss.__lr)
# define R15_sig(p) ((p)->uc_mcontext->__ss.__pc)
#else
# error "Don't know how to read/write to the thread state via the mcontext_t."
#endif
#if defined(ANDROID)
// Not all versions of the Android NDK define ucontext_t or mcontext_t.
// Detect this and provide custom but compatible definitions. Note that these
// follow the GLibc naming convention to access register values from
// mcontext_t.
//
// See: https://chromiumcodereview.appspot.com/10829122/
// See: http://code.google.com/p/android/issues/detail?id=34784
# if !defined(__BIONIC_HAVE_UCONTEXT_T)
# if defined(__arm__)
// GLibc on ARM defines mcontext_t has a typedef for 'struct sigcontext'.
// Old versions of the C library <signal.h> didn't define the type.
# if !defined(__BIONIC_HAVE_STRUCT_SIGCONTEXT)
# include <asm/sigcontext.h>
# endif
typedef struct sigcontext mcontext_t;
typedef struct ucontext {
uint32_t uc_flags;
struct ucontext* uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
// Other fields are not used so don't define them here.
} ucontext_t;
# elif defined(__mips__)
typedef struct {
uint32_t regmask;
uint32_t status;
uint64_t pc;
uint64_t gregs[32];
uint64_t fpregs[32];
uint32_t acx;
uint32_t fpc_csr;
uint32_t fpc_eir;
uint32_t used_math;
uint32_t dsp;
uint64_t mdhi;
uint64_t mdlo;
uint32_t hi1;
uint32_t lo1;
uint32_t hi2;
uint32_t lo2;
uint32_t hi3;
uint32_t lo3;
} mcontext_t;
typedef struct ucontext {
uint32_t uc_flags;
struct ucontext* uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
// Other fields are not used so don't define them here.
} ucontext_t;
# elif defined(__i386__)
// x86 version for Android.
typedef struct {
uint32_t gregs[19];
void* fpregs;
uint32_t oldmask;
uint32_t cr2;
} mcontext_t;
typedef uint32_t kernel_sigset_t[2]; // x86 kernel uses 64-bit signal masks
typedef struct ucontext {
uint32_t uc_flags;
struct ucontext* uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
// Other fields are not used by V8, don't define them here.
} ucontext_t;
enum { REG_EIP = 14 };
# endif // defined(__i386__)
# endif // !defined(__BIONIC_HAVE_UCONTEXT_T)
#endif // defined(ANDROID)
#if defined(USE_APPLE_MACH_PORTS)
# if defined(__x86_64__)
struct macos_x64_context {
x86_thread_state64_t thread;
x86_float_state64_t float_;
};
# define CONTEXT macos_x64_context
# elif defined(__i386__)
struct macos_x86_context {
x86_thread_state_t thread;
x86_float_state_t float_;
};
# define CONTEXT macos_x86_context
# elif defined(__arm__)
struct macos_arm_context {
arm_thread_state_t thread;
arm_neon_state_t float_;
};
# define CONTEXT macos_arm_context
# else
# error Unsupported architecture
# endif
#elif !defined(_WIN32)
# define CONTEXT ucontext_t
#endif
#if defined(_M_X64) || defined(__x86_64__)
# define PC_sig(p) RIP_sig(p)
# define FP_sig(p) RBP_sig(p)
# define SP_sig(p) RSP_sig(p)
#elif defined(_M_IX86) || defined(__i386__)
# define PC_sig(p) EIP_sig(p)
# define FP_sig(p) EBP_sig(p)
# define SP_sig(p) ESP_sig(p)
#elif defined(__arm__)
# define FP_sig(p) R11_sig(p)
# define SP_sig(p) R13_sig(p)
# define LR_sig(p) R14_sig(p)
# define PC_sig(p) R15_sig(p)
#elif defined(_M_ARM64) || defined(__aarch64__)
# define PC_sig(p) EPC_sig(p)
# define FP_sig(p) RFP_sig(p)
# define SP_sig(p) R31_sig(p)
# define LR_sig(p) RLR_sig(p)
#elif defined(__mips__)
# define PC_sig(p) EPC_sig(p)
# define FP_sig(p) RFP_sig(p)
# define SP_sig(p) RSP_sig(p)
# define LR_sig(p) R31_sig(p)
#elif defined(__ppc64__) || defined (__PPC64__) || defined(__ppc64le__) || defined (__PPC64LE__)
# define PC_sig(p) R32_sig(p)
# define SP_sig(p) R01_sig(p)
# define FP_sig(p) R01_sig(p)
#endif
static void
SetContextPC(CONTEXT* context, const uint8_t* pc)
{
#ifdef PC_sig
PC_sig(context) = reinterpret_cast<uintptr_t>(pc);
#else
abort();
#endif
}
static const uint8_t*
ContextToPC(CONTEXT* context)
{
#ifdef PC_sig
return reinterpret_cast<const uint8_t*>(static_cast<uintptr_t>(PC_sig(context)));
#else
abort();
#endif
}
// =============================================================================
// All signals/exceptions funnel down to this one trap-handling function which
// tests whether the pc is in a wasm module and, if so, whether there is
// actually a trap expected at this pc. These tests both avoid real bugs being
// silently converted to wasm traps and provides the trapping wasm bytecode
// offset we need to report in the error.
//
// Crashing inside wasm trap handling (due to a bug in trap handling or exposed
// during trap handling) must be reported like a normal crash, not cause the
// crash report to be lost. On Windows and non-Mach Unix, a crash during the
// handler reenters the handler, possibly repeatedly until exhausting the stack,
// and so we prevent recursion with the thread-local sAlreadyHandlingTrap. On
// Mach, the wasm exception handler has its own thread and is installed only on
// the thread-level debugging ports of our threads, so a crash on
// exception handler thread will not recurse; it will bubble up to the
// process-level debugging ports (where Breakpad is installed).
// =============================================================================
static thread_local bool sAlreadyHandlingTrap;
namespace {
struct AutoHandlingTrap
{
AutoHandlingTrap() {
assert(!sAlreadyHandlingTrap);
sAlreadyHandlingTrap = true;
}
~AutoHandlingTrap() {
assert(sAlreadyHandlingTrap);
sAlreadyHandlingTrap = false;
}
};
}
// =============================================================================
// The following platform-specific handlers funnel all signals/exceptions into
// the HandleTrap() function defined in Rust. Note that the Rust function has a
// different ABI depending on the platform.
// =============================================================================
#if defined(_WIN32)
// Obtained empirically from thread_local codegen on x86/x64/arm64.
// Compiled in all user binaries, so should be stable over time.
static const unsigned sThreadLocalArrayPointerIndex = 11;
static LONG WINAPI
WasmTrapHandler(LPEXCEPTION_POINTERS exception)
{
// Make sure TLS is initialized before reading sAlreadyHandlingTrap.
if (!NtCurrentTeb()->Reserved1[sThreadLocalArrayPointerIndex]) {
return EXCEPTION_CONTINUE_SEARCH;
}
if (sAlreadyHandlingTrap) {
return EXCEPTION_CONTINUE_SEARCH;
}
AutoHandlingTrap aht;
EXCEPTION_RECORD* record = exception->ExceptionRecord;
if (record->ExceptionCode != EXCEPTION_ACCESS_VIOLATION &&
record->ExceptionCode != EXCEPTION_ILLEGAL_INSTRUCTION &&
record->ExceptionCode != EXCEPTION_STACK_OVERFLOW &&
record->ExceptionCode != EXCEPTION_INT_DIVIDE_BY_ZERO &&
record->ExceptionCode != EXCEPTION_INT_OVERFLOW)
{
return EXCEPTION_CONTINUE_SEARCH;
}
void *JmpBuf = HandleTrap(ContextToPC(exception->ContextRecord), exception);
// Test if a custom instance signal handler handled the exception
if (((size_t) JmpBuf) == 1)
return EXCEPTION_CONTINUE_EXECUTION;
// Otherwise test if we need to longjmp to this buffer
if (JmpBuf != nullptr) {
sAlreadyHandlingTrap = false;
Unwind(JmpBuf);
}
// ... and otherwise keep looking for a handler
return EXCEPTION_CONTINUE_SEARCH;
}
#elif defined(USE_APPLE_MACH_PORTS)
// On OSX we are forced to use the lower-level Mach exception mechanism instead
// of Unix signals because breakpad uses Mach exceptions and would otherwise
// report a crash before wasm gets a chance to handle the exception.
// This definition was generated by mig (the Mach Interface Generator) for the
// routine 'exception_raise' (exc.defs).
#pragma pack(4)
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
int64_t code[2];
} Request__mach_exception_raise_t;
#pragma pack()
// The full Mach message also includes a trailer.
struct ExceptionRequest
{
Request__mach_exception_raise_t body;
mach_msg_trailer_t trailer;
};
static bool
HandleMachException(const ExceptionRequest& request)
{
// Get the port of the thread from the message.
mach_port_t cxThread = request.body.thread.name;
// Read out the thread's register state.
CONTEXT context;
# if defined(__x86_64__)
unsigned int thread_state_count = x86_THREAD_STATE64_COUNT;
unsigned int float_state_count = x86_FLOAT_STATE64_COUNT;
int thread_state = x86_THREAD_STATE64;
int float_state = x86_FLOAT_STATE64;
# elif defined(__i386__)
unsigned int thread_state_count = x86_THREAD_STATE_COUNT;
unsigned int float_state_count = x86_FLOAT_STATE_COUNT;
int thread_state = x86_THREAD_STATE;
int float_state = x86_FLOAT_STATE;
# elif defined(__arm__)
unsigned int thread_state_count = ARM_THREAD_STATE_COUNT;
unsigned int float_state_count = ARM_NEON_STATE_COUNT;
int thread_state = ARM_THREAD_STATE;
int float_state = ARM_NEON_STATE;
# else
# error Unsupported architecture
# endif
kern_return_t kret;
kret = thread_get_state(cxThread, thread_state,
(thread_state_t)&context.thread, &thread_state_count);
if (kret != KERN_SUCCESS) {
return false;
}
kret = thread_get_state(cxThread, float_state,
(thread_state_t)&context.float_, &float_state_count);
if (kret != KERN_SUCCESS) {
return false;
}
if (request.body.exception != EXC_BAD_ACCESS &&
request.body.exception != EXC_BAD_INSTRUCTION)
{
return false;
}
{
AutoHandlingTrap aht;
if (!HandleTrap(&context, false)) {
return false;
}
}
// Update the thread state with the new pc and register values.
kret = thread_set_state(cxThread, float_state, (thread_state_t)&context.float_, float_state_count);
if (kret != KERN_SUCCESS) {
return false;
}
kret = thread_set_state(cxThread, thread_state, (thread_state_t)&context.thread, thread_state_count);
if (kret != KERN_SUCCESS) {
return false;
}
return true;
}
static mach_port_t sMachDebugPort = MACH_PORT_NULL;
static void*
MachExceptionHandlerThread(void* arg)
{
// Taken from mach_exc in /usr/include/mach/mach_exc.defs.
static const unsigned EXCEPTION_MSG_ID = 2405;
while (true) {
ExceptionRequest request;
kern_return_t kret = mach_msg(&request.body.Head, MACH_RCV_MSG, 0, sizeof(request),
sMachDebugPort, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
// If we fail even receiving the message, we can't even send a reply!
// Rather than hanging the faulting thread (hanging the browser), crash.
if (kret != KERN_SUCCESS) {
fprintf(stderr, "MachExceptionHandlerThread: mach_msg failed with %d\n", (int)kret);
abort();
}
if (request.body.Head.msgh_id != EXCEPTION_MSG_ID) {
fprintf(stderr, "Unexpected msg header id %d\n", (int)request.body.Head.msgh_bits);
abort();
}
// Some thread just commited an EXC_BAD_ACCESS and has been suspended by
// the kernel. The kernel is waiting for us to reply with instructions.
// Our default is the "not handled" reply (by setting the RetCode field
// of the reply to KERN_FAILURE) which tells the kernel to continue
// searching at the process and system level. If this is an
// expected exception, we handle it and return KERN_SUCCESS.
bool handled = HandleMachException(request);
kern_return_t replyCode = handled ? KERN_SUCCESS : KERN_FAILURE;
// This magic incantation to send a reply back to the kernel was
// derived from the exc_server generated by
// 'mig -v /usr/include/mach/mach_exc.defs'.
__Reply__exception_raise_t reply;
reply.Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request.body.Head.msgh_bits), 0);
reply.Head.msgh_size = sizeof(reply);
reply.Head.msgh_remote_port = request.body.Head.msgh_remote_port;
reply.Head.msgh_local_port = MACH_PORT_NULL;
reply.Head.msgh_id = request.body.Head.msgh_id + 100;
reply.NDR = NDR_record;
reply.RetCode = replyCode;
mach_msg(&reply.Head, MACH_SEND_MSG, sizeof(reply), 0, MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
}
return nullptr;
}
#else // If not Windows or Mac, assume Unix
static struct sigaction sPrevSIGSEGVHandler;
static struct sigaction sPrevSIGBUSHandler;
static struct sigaction sPrevSIGILLHandler;
static struct sigaction sPrevSIGFPEHandler;
static void
WasmTrapHandler(int signum, siginfo_t* info, void* context)
{
if (!sAlreadyHandlingTrap) {
AutoHandlingTrap aht;
assert(signum == SIGSEGV || signum == SIGBUS || signum == SIGFPE || signum == SIGILL);
void *JmpBuf = HandleTrap(ContextToPC(static_cast<CONTEXT*>(context)), signum, info, context);
// Test if a custom instance signal handler handled the exception
if (((size_t) JmpBuf) == 1)
return;
// Otherwise test if we need to longjmp to this buffer
if (JmpBuf != nullptr) {
sAlreadyHandlingTrap = false;
Unwind(JmpBuf);
}
// ... and otherwise call the previous signal handler, if one is there
}
struct sigaction* previousSignal = nullptr;
switch (signum) {
case SIGSEGV: previousSignal = &sPrevSIGSEGVHandler; break;
case SIGBUS: previousSignal = &sPrevSIGBUSHandler; break;
case SIGFPE: previousSignal = &sPrevSIGFPEHandler; break;
case SIGILL: previousSignal = &sPrevSIGILLHandler; break;
}
assert(previousSignal);
// This signal is not for any compiled wasm code we expect, so we need to
// forward the signal to the next handler. If there is no next handler (SIG_IGN
// or SIG_DFL), then it's time to crash. To do this, we set the signal back to
// its original disposition and return. This will cause the faulting op to
// be re-executed which will crash in the normal way. The advantage of
// doing this to calling _exit() is that we remove ourselves from the crash
// stack which improves crash reports. If there is a next handler, call it.
// It will either crash synchronously, fix up the instruction so that
// execution can continue and return, or trigger a crash by returning the
// signal to it's original disposition and returning.
//
// Note: the order of these tests matter.
if (previousSignal->sa_flags & SA_SIGINFO) {
previousSignal->sa_sigaction(signum, info, context);
} else if (previousSignal->sa_handler == SIG_DFL || previousSignal->sa_handler == SIG_IGN) {
sigaction(signum, previousSignal, nullptr);
} else {
previousSignal->sa_handler(signum);
}
}
# endif // _WIN32 || __APPLE__ || assume unix
#if defined(ANDROID) && defined(MOZ_LINKER)
extern "C" MFBT_API bool IsSignalHandlingBroken();
#endif
int
EnsureEagerSignalHandlers()
{
#if defined(ANDROID) && defined(MOZ_LINKER)
// Signal handling is broken on some android systems.
if (IsSignalHandlingBroken()) {
return false;
}
#endif
sAlreadyHandlingTrap = false;
// Install whatever exception/signal handler is appropriate for the OS.
#if defined(_WIN32)
# if defined(MOZ_ASAN)
// Under ASan we need to let the ASan runtime's ShadowExceptionHandler stay
// in the first handler position. This requires some coordination with
// MemoryProtectionExceptionHandler::isDisabled().
const bool firstHandler = false;
# else
// Otherwise, WasmTrapHandler needs to go first, so that we can recover
// from wasm faults and continue execution without triggering handlers
// such as MemoryProtectionExceptionHandler that assume we are crashing.
const bool firstHandler = true;
# endif
if (!AddVectoredExceptionHandler(firstHandler, WasmTrapHandler)) {
// Windows has all sorts of random security knobs for disabling things
// so make this a dynamic failure that disables wasm, not an abort().
return false;
}
#elif defined(USE_APPLE_MACH_PORTS)
// All the Mach setup in EnsureDarwinMachPorts.
#else
// SA_ONSTACK allows us to handle signals on an alternate stack, so that
// the handler can run in response to running out of stack space on the
// main stack. Rust installs an alternate stack with sigaltstack, so we
// rely on that.
// SA_NODEFER allows us to reenter the signal handler if we crash while
// handling the signal, and fall through to the Breakpad handler by testing
// handlingSegFault.
// Allow handling OOB with signals on all architectures
struct sigaction faultHandler;
faultHandler.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
faultHandler.sa_sigaction = WasmTrapHandler;
sigemptyset(&faultHandler.sa_mask);
if (sigaction(SIGSEGV, &faultHandler, &sPrevSIGSEGVHandler)) {
perror("unable to install SIGSEGV handler");
abort();
}
# if defined(__arm__) || defined(__APPLE__)
// On ARM, handle Unaligned Accesses.
// On Darwin, guard page accesses are raised as SIGBUS.
struct sigaction busHandler;
busHandler.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
busHandler.sa_sigaction = WasmTrapHandler;
sigemptyset(&busHandler.sa_mask);
if (sigaction(SIGBUS, &busHandler, &sPrevSIGBUSHandler)) {
perror("unable to install SIGBUS handler");
abort();
}
# endif
# if !defined(__mips__)
// Wasm traps for MIPS currently only raise integer overflow fp exception.
struct sigaction illHandler;
illHandler.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
illHandler.sa_sigaction = WasmTrapHandler;
sigemptyset(&illHandler.sa_mask);
if (sigaction(SIGILL, &illHandler, &sPrevSIGILLHandler)) {
perror("unable to install wasm SIGILL handler");
abort();
}
# endif
# if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
// x86 uses SIGFPE to report division by zero, and wasm traps for MIPS
// currently raise integer overflow fp exception.
struct sigaction fpeHandler;
fpeHandler.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
fpeHandler.sa_sigaction = WasmTrapHandler;
sigemptyset(&fpeHandler.sa_mask);
if (sigaction(SIGFPE, &fpeHandler, &sPrevSIGFPEHandler)) {
perror("unable to install wasm SIGFPE handler");
abort();
}
# endif
#endif
return true;
}

View File

@@ -1,41 +0,0 @@
#ifndef signal_handlers_h
#define signal_handlers_h
#include <stdint.h>
#include <setjmp.h>
#ifndef __cplusplus
#include <stdbool.h>
#endif
#include <signal.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(_WIN32)
#include <windows.h>
#include <winternl.h>
void* HandleTrap(const uint8_t*, LPEXCEPTION_POINTERS);
#else
void* HandleTrap(const uint8_t*, int, siginfo_t *, void *);
#endif
void Unwind(void*);
// This function performs the low-overhead signal handler initialization that we
// want to do eagerly to ensure a more-deterministic global process state. This
// is especially relevant for signal handlers since handler ordering depends on
// installation order: the wasm signal handler must run *before* the other crash
// handlers and since POSIX signal handlers work LIFO, this function needs to be
// called at the end of the startup process, after other handlers have been
// installed. This function can thus be called multiple times, having no effect
// after the first call.
int
EnsureEagerSignalHandlers(void);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // signal_handlers_h

View File

@@ -1,8 +1,5 @@
#include <setjmp.h> #include <setjmp.h>
#include "SignalHandlers.hpp"
extern "C"
int RegisterSetjmp( int RegisterSetjmp(
void **buf_storage, void **buf_storage,
void (*body)(void*), void (*body)(void*),
@@ -16,8 +13,16 @@ int RegisterSetjmp(
return 1; return 1;
} }
extern "C"
void Unwind(void *JmpBuf) { void Unwind(void *JmpBuf) {
jmp_buf *buf = (jmp_buf*) JmpBuf; jmp_buf *buf = (jmp_buf*) JmpBuf;
longjmp(*buf, 1); longjmp(*buf, 1);
} }
#ifdef __APPLE__
#include <sys/ucontext.h>
void* GetPcFromUContext(ucontext_t *cx) {
return (void*) cx->uc_mcontext->__ss.__rip;
}
#endif

View File

@@ -6,8 +6,8 @@ use crate::export::Export;
use crate::imports::Imports; use crate::imports::Imports;
use crate::jit_int::GdbJitImageRegistration; use crate::jit_int::GdbJitImageRegistration;
use crate::memory::LinearMemory; use crate::memory::LinearMemory;
use crate::signalhandlers;
use crate::table::Table; use crate::table::Table;
use crate::traphandlers;
use crate::traphandlers::{catch_traps, Trap}; use crate::traphandlers::{catch_traps, Trap};
use crate::vmcontext::{ use crate::vmcontext::{
VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMFunctionImport, VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMFunctionImport,
@@ -980,7 +980,7 @@ impl InstanceHandle {
// Ensure that our signal handlers are ready for action. // Ensure that our signal handlers are ready for action.
// TODO: Move these calls out of `InstanceHandle`. // TODO: Move these calls out of `InstanceHandle`.
signalhandlers::init(); traphandlers::init();
// The WebAssembly spec specifies that the start function is // The WebAssembly spec specifies that the start function is
// invoked automatically at instantiation time. // invoked automatically at instantiation time.

View File

@@ -28,7 +28,6 @@ mod jit_int;
mod memory; mod memory;
mod mmap; mod mmap;
mod sig_registry; mod sig_registry;
mod signalhandlers;
mod table; mod table;
mod trap_registry; mod trap_registry;
mod traphandlers; mod traphandlers;

View File

@@ -1,38 +0,0 @@
//! Interface to low-level signal-handling mechanisms.
use std::sync::Once;
extern "C" {
fn EnsureEagerSignalHandlers() -> libc::c_int;
}
/// This function performs the low-overhead signal handler initialization that
/// we want to do eagerly to ensure a more-deterministic global process state.
///
/// This is especially relevant for signal handlers since handler ordering
/// depends on installation order: the wasm signal handler must run *before*
/// the other crash handlers and since POSIX signal handlers work LIFO, this
/// function needs to be called at the end of the startup process, after other
/// handlers have been installed. This function can thus be called multiple
/// times, having no effect after the first call.
pub fn init() {
static INIT: Once = Once::new();
INIT.call_once(real_init);
}
fn real_init() {
// This is a really weird and unfortunate function call. For all the gory
// details see #829, but the tl;dr; is that in a trap handler we have 2
// pages of stack space on Linux, and calling into libunwind which triggers
// the dynamic loader blows the stack.
//
// This is a dumb hack to work around this system-specific issue by
// capturing a backtrace once in the lifetime of a process to ensure that
// when we capture a backtrace in the trap handler all caches are primed,
// aka the dynamic loader has resolved all the relevant symbols.
drop(backtrace::Backtrace::new_unresolved());
if unsafe { EnsureEagerSignalHandlers() == 0 } {
panic!("failed to install signal handlers");
}
}

View File

@@ -9,7 +9,9 @@ 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::fmt;
use std::io;
use std::ptr; use std::ptr;
use std::sync::Once;
use wasmtime_environ::ir; use wasmtime_environ::ir;
extern "C" { extern "C" {
@@ -23,40 +25,248 @@ extern "C" {
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(unix)] { if #[cfg(unix)] {
#[no_mangle] use std::mem::{self, MaybeUninit};
pub unsafe extern "C" fn HandleTrap(
pc: *mut u8, static mut PREV_SIGSEGV: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
static mut PREV_SIGBUS: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
static mut PREV_SIGILL: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
static mut PREV_SIGFPE: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
unsafe fn platform_init() {
let register = |slot: &mut MaybeUninit<libc::sigaction>, signal: i32| {
let mut handler: libc::sigaction = mem::zeroed();
// The flags here are relatively careful, and they are...
//
// SA_SIGINFO gives us access to information like the program
// counter from where the fault happened.
//
// SA_ONSTACK allows us to handle signals on an alternate stack,
// so that the handler can run in response to running out of
// stack space on the main stack. Rust installs an alternate
// stack with sigaltstack, so we rely on that.
//
// SA_NODEFER allows us to reenter the signal handler if we
// crash while handling the signal, and fall through to the
// Breakpad handler by testing handlingSegFault.
handler.sa_flags = libc::SA_SIGINFO | libc::SA_NODEFER | libc::SA_ONSTACK;
handler.sa_sigaction = trap_handler as usize;
libc::sigemptyset(&mut handler.sa_mask);
if libc::sigaction(signal, &handler, slot.as_mut_ptr()) != 0 {
panic!(
"unable to install signal handler: {}",
io::Error::last_os_error(),
);
}
};
// Allow handling OOB with signals on all architectures
register(&mut PREV_SIGSEGV, libc::SIGSEGV);
// Handle `unreachable` instructions which execute `ud2` right now
register(&mut PREV_SIGILL, libc::SIGILL);
// x86 uses SIGFPE to report division by zero
if cfg!(target_arch = "x86") || cfg!(target_arch = "x86_64") {
register(&mut PREV_SIGFPE, libc::SIGFPE);
}
// On ARM, handle Unaligned Accesses.
// On Darwin, guard page accesses are raised as SIGBUS.
if cfg!(target_arch = "arm") || cfg!(target_os = "macos") {
register(&mut PREV_SIGBUS, libc::SIGBUS);
}
}
unsafe extern "C" fn trap_handler(
signum: libc::c_int, signum: libc::c_int,
siginfo: *mut libc::siginfo_t, siginfo: *mut libc::siginfo_t,
context: *mut libc::c_void, context: *mut libc::c_void,
) -> *const u8 { ) {
tls::with(|info| { let previous = match signum {
match info { libc::SIGSEGV => &PREV_SIGSEGV,
Some(info) => info.handle_trap(pc, false, |handler| handler(signum, siginfo, context)), libc::SIGBUS => &PREV_SIGBUS,
None => ptr::null(), libc::SIGFPE => &PREV_SIGFPE,
libc::SIGILL => &PREV_SIGILL,
_ => panic!("unknown signal: {}", signum),
};
let handled = tls::with(|info| {
// If no wasm code is executing, we don't handle this as a wasm
// trap.
let info = match info {
Some(info) => info,
None => return false,
};
// If we hit an exception while handling a previous trap, that's
// quite bad, so bail out and let the system handle this
// recursive segfault.
//
// Otherwise flag ourselves as handling a trap, do the trap
// handling, and reset our trap handling flag. Then we figure
// out what to do based on the result of the trap handling.
let jmp_buf = info.handle_trap(
get_pc(context),
false,
|handler| handler(signum, siginfo, context),
);
// Figure out what to do based on the result of this handling of
// the trap. Note that our sentinel value of 1 means that the
// exception was handled by a custom exception handler, so we
// keep executing.
if jmp_buf.is_null() {
return false;
} else if jmp_buf as usize == 1 {
return true;
} else {
Unwind(jmp_buf)
} }
}) });
if handled {
return;
}
// This signal is not for any compiled wasm code we expect, so we
// need to forward the signal to the next handler. If there is no
// next handler (SIG_IGN or SIG_DFL), then it's time to crash. To do
// this, we set the signal back to its original disposition and
// return. This will cause the faulting op to be re-executed which
// will crash in the normal way. If there is a next handler, call
// it. It will either crash synchronously, fix up the instruction
// so that execution can continue and return, or trigger a crash by
// returning the signal to it's original disposition and returning.
let previous = &*previous.as_ptr();
if previous.sa_flags & libc::SA_SIGINFO != 0 {
mem::transmute::<
usize,
extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void),
>(previous.sa_sigaction)(signum, siginfo, context)
} else if previous.sa_sigaction == libc::SIG_DFL ||
previous.sa_sigaction == libc::SIG_IGN
{
libc::sigaction(signum, previous, ptr::null_mut());
} else {
mem::transmute::<usize, extern "C" fn(libc::c_int)>(
previous.sa_sigaction
)(signum)
}
}
unsafe fn get_pc(cx: *mut libc::c_void) -> *const u8 {
cfg_if::cfg_if! {
if #[cfg(all(target_os = "linux", target_arch = "x86_64"))] {
let cx = &*(cx as *const libc::ucontext_t);
cx.uc_mcontext.gregs[libc::REG_RIP as usize] as *const u8
} else if #[cfg(target_os = "macos")] {
// FIXME(rust-lang/libc#1702) - once that lands and is
// released we should inline the definition here
extern "C" {
fn GetPcFromUContext(cx: *mut libc::c_void) -> *const u8;
}
GetPcFromUContext(cx)
} else {
compile_error!("unsupported platform");
}
}
} }
} else if #[cfg(target_os = "windows")] { } else if #[cfg(target_os = "windows")] {
use winapi::um::winnt::PEXCEPTION_POINTERS; use winapi::um::errhandlingapi::*;
use winapi::um::minwinbase::EXCEPTION_STACK_OVERFLOW; use winapi::um::winnt::*;
use winapi::um::minwinbase::*;
use winapi::vc::excpt::*;
#[no_mangle] unsafe fn platform_init() {
pub unsafe extern "C" fn HandleTrap( // our trap handler needs to go first, so that we can recover from
pc: *mut u8, // wasm faults and continue execution, so pass `1` as a true value
// here.
if AddVectoredExceptionHandler(1, Some(exception_handler)).is_null() {
panic!("failed to add exception handler: {}", io::Error::last_os_error());
}
}
unsafe extern "system" fn exception_handler(
exception_info: PEXCEPTION_POINTERS exception_info: PEXCEPTION_POINTERS
) -> *const u8 { ) -> LONG {
// Check the kind of exception, since we only handle a subset within
// wasm code. If anything else happens we want to defer to whatever
// the rest of the system wants to do for this exception.
let record = &*(*exception_info).ExceptionRecord;
if record.ExceptionCode != EXCEPTION_ACCESS_VIOLATION &&
record.ExceptionCode != EXCEPTION_ILLEGAL_INSTRUCTION &&
record.ExceptionCode != EXCEPTION_STACK_OVERFLOW &&
record.ExceptionCode != EXCEPTION_INT_DIVIDE_BY_ZERO &&
record.ExceptionCode != EXCEPTION_INT_OVERFLOW
{
return EXCEPTION_CONTINUE_SEARCH;
}
// FIXME: this is what the previous C++ did to make sure that TLS
// works by the time we execute this trap handling code. This isn't
// exactly super easy to call from Rust though and it's not clear we
// necessarily need to do so. Leaving this here in case we need this
// in the future, but for now we can probably wait until we see a
// strange fault before figuring out how to reimplement this in
// Rust.
//
// if (!NtCurrentTeb()->Reserved1[sThreadLocalArrayPointerIndex]) {
// return EXCEPTION_CONTINUE_SEARCH;
// }
// This is basically the same as the unix version above, only with a
// few parameters tweaked here and there.
tls::with(|info| { tls::with(|info| {
let reset_guard_page = (*(*exception_info).ExceptionRecord).ExceptionCode == EXCEPTION_STACK_OVERFLOW; let info = match info {
match info { Some(info) => info,
Some(info) => info.handle_trap(pc, reset_guard_page, |handler| handler(exception_info)), None => return EXCEPTION_CONTINUE_SEARCH,
None => ptr::null(), };
let jmp_buf = info.handle_trap(
(*(*exception_info).ContextRecord).Rip as *const u8,
record.ExceptionCode == EXCEPTION_STACK_OVERFLOW,
|handler| handler(exception_info),
);
if jmp_buf.is_null() {
EXCEPTION_CONTINUE_SEARCH
} else if jmp_buf as usize == 1 {
EXCEPTION_CONTINUE_EXECUTION
} else {
Unwind(jmp_buf)
} }
}) })
} }
} }
} }
/// This function performs the low-overhead signal handler initialization that
/// we want to do eagerly to ensure a more-deterministic global process state.
///
/// This is especially relevant for signal handlers since handler ordering
/// depends on installation order: the wasm signal handler must run *before*
/// the other crash handlers and since POSIX signal handlers work LIFO, this
/// function needs to be called at the end of the startup process, after other
/// handlers have been installed. This function can thus be called multiple
/// times, having no effect after the first call.
pub fn init() {
static INIT: Once = Once::new();
INIT.call_once(real_init);
}
fn real_init() {
// This is a really weird and unfortunate function call. For all the gory
// details see #829, but the tl;dr; is that in a trap handler we have 2
// pages of stack space on Linux, and calling into libunwind which triggers
// the dynamic loader blows the stack.
//
// This is a dumb hack to work around this system-specific issue by
// capturing a backtrace once in the lifetime of a process to ensure that
// when we capture a backtrace in the trap handler all caches are primed,
// aka the dynamic loader has resolved all the relevant symbols.
drop(backtrace::Backtrace::new_unresolved());
unsafe {
platform_init();
}
}
/// Raises a user-defined trap immediately. /// Raises a user-defined trap immediately.
/// ///
/// This function performs as-if a wasm trap was just executed, only the trap /// This function performs as-if a wasm trap was just executed, only the trap
@@ -186,6 +396,7 @@ pub struct CallThreadState {
reset_guard_page: Cell<bool>, reset_guard_page: Cell<bool>,
prev: Option<*const CallThreadState>, prev: Option<*const CallThreadState>,
vmctx: *mut VMContext, vmctx: *mut VMContext,
handling_trap: Cell<bool>,
} }
enum UnwindReason { enum UnwindReason {
@@ -204,6 +415,7 @@ impl CallThreadState {
jmp_buf: Cell::new(ptr::null()), jmp_buf: Cell::new(ptr::null()),
reset_guard_page: Cell::new(false), reset_guard_page: Cell::new(false),
prev: None, prev: None,
handling_trap: Cell::new(false),
} }
} }
@@ -287,6 +499,15 @@ impl CallThreadState {
reset_guard_page: bool, reset_guard_page: bool,
call_handler: impl Fn(&SignalHandler) -> bool, call_handler: impl Fn(&SignalHandler) -> bool,
) -> *const u8 { ) -> *const u8 {
// If we hit a fault while handling a previous trap, that's quite bad,
// so bail out and let the system handle this recursive segfault.
//
// Otherwise flag ourselves as handling a trap, do the trap handling,
// and reset our trap handling flag.
if self.handling_trap.replace(true) {
return ptr::null();
}
// First up see if any instance registered has a custom trap handler, // First up see if any instance registered has a custom trap handler,
// in which case run them all. If anything handles the trap then we // in which case run them all. If anything handles the trap then we
// return that the trap was handled. // return that the trap was handled.
@@ -299,6 +520,7 @@ impl CallThreadState {
i.instance().signal_handler.set(Some(handler)); i.instance().signal_handler.set(Some(handler));
return result; return result;
}) { }) {
self.handling_trap.set(false);
return 1 as *const _; return 1 as *const _;
} }
@@ -310,6 +532,7 @@ impl CallThreadState {
// doesn't trap. Then, if we have called some WebAssembly code, it // doesn't trap. Then, if we have called some WebAssembly code, it
// means the trap is stack overflow. // means the trap is stack overflow.
if self.jmp_buf.get().is_null() { if self.jmp_buf.get().is_null() {
self.handling_trap.set(false);
return ptr::null(); return ptr::null();
} }
let backtrace = Backtrace::new_unresolved(); let backtrace = Backtrace::new_unresolved();
@@ -318,6 +541,7 @@ impl CallThreadState {
backtrace, backtrace,
pc: pc as usize, pc: pc as usize,
}); });
self.handling_trap.set(false);
self.jmp_buf.get() self.jmp_buf.get()
} }
} }