Bug 1370696: Use register state unwinding when interrupting within wasm; r=luke

MozReview-Commit-ID: 76RM1Gf0eAo

--HG--
extra : rebase_source : 60b3d43ebe5af0c88ae14e2f685f761012672110
extra : histedit_source : 882ad83e6b5033a08b1045892ab3f3b13b38c5b1
This commit is contained in:
Benjamin Bouvier 2017-06-21 12:19:33 +02:00
Родитель 99a29014d5
Коммит 771e53e98e
8 изменённых файлов: 175 добавлений и 46 удалений

Просмотреть файл

@ -1547,6 +1547,17 @@ Simulator::exclusiveMonitorClear()
exclusiveMonitorHeld_ = false;
}
void
Simulator::startInterrupt(WasmActivation* activation)
{
JS::ProfilingFrameIterator::RegisterState state;
state.pc = (void*) get_pc();
state.fp = (void*) get_register(fp);
state.sp = (void*) get_register(sp);
state.lr = (void*) get_register(lr);
activation->startInterrupt(state);
}
// The signal handler only redirects the PC to the interrupt stub when the PC is
// in function code. However, this guard is racy for the ARM simulator since the
// signal handler samples PC in the middle of simulating an instruction and thus
@ -1568,7 +1579,7 @@ Simulator::handleWasmInterrupt()
if (!fp)
return;
activation->startInterrupt(pc, fp);
startInterrupt(activation);
set_pc(int32_t(segment->interruptCode()));
}
@ -1594,7 +1605,7 @@ Simulator::handleWasmFault(int32_t addr, unsigned numBytes)
const wasm::CodeSegment* segment;
const wasm::MemoryAccess* memoryAccess = instance->code().lookupMemoryAccess(pc, &segment);
if (!memoryAccess) {
act->startInterrupt(pc, fp);
startInterrupt(act);
if (!instance->code().containsCodePC(pc, &segment))
MOZ_CRASH("Cannot map PC to trap handler");
set_pc(int32_t(segment->outOfBoundsCode()));

Просмотреть файл

@ -40,6 +40,9 @@
#include "vm/MutexIDs.h"
namespace js {
class WasmActivation;
namespace jit {
class Simulator;
@ -289,6 +292,7 @@ class Simulator
// Handle a wasm interrupt triggered by an async signal handler.
void handleWasmInterrupt();
void startInterrupt(WasmActivation* act);
// Handle any wasm faults, returning true if the fault was handled.
bool handleWasmFault(int32_t addr, unsigned numBytes);

Просмотреть файл

@ -249,7 +249,13 @@ void Simulator::handle_wasm_interrupt() {
if (!code || !segment->containsFunctionPC(pc))
return;
activation->startInterrupt(pc, fp);
JS::ProfilingFrameIterator::RegisterState state;
state.pc = pc;
state.fp = fp;
state.lr = (uint8_t*) xreg(30);
state.sp = (uint8_t*) xreg(31);
activation->startInterrupt(state);
set_pc((Instruction*)segment->interruptCode());
}

Просмотреть файл

@ -1089,13 +1089,26 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
// resume PC at a time.
js::ActiveThreadData<void*> wasmResumePC_;
// To ensure a consistent state of fp/pc, the unwound pc might be
// different from the resumePC, especially at call boundaries.
js::ActiveThreadData<void*> wasmUnwindPC_;
public:
void startWasmInterrupt(void* resumePC, void* unwindPC) {
MOZ_ASSERT(resumePC && unwindPC);
wasmResumePC_ = resumePC;
wasmUnwindPC_ = unwindPC;
}
void finishWasmInterrupt() {
MOZ_ASSERT(wasmResumePC_ && wasmUnwindPC_);
wasmResumePC_ = nullptr;
wasmUnwindPC_ = nullptr;
}
void* wasmResumePC() const {
return wasmResumePC_;
}
void setWasmResumePC(void* resumePC) {
MOZ_ASSERT(!!resumePC == !wasmResumePC_);
wasmResumePC_ = resumePC;
void* wasmUnwindPC() const {
return wasmUnwindPC_;
}
};

Просмотреть файл

@ -1652,19 +1652,25 @@ WasmActivation::unwindExitFP(wasm::Frame* exitFP)
}
void
WasmActivation::startInterrupt(void* pc, uint8_t* fp)
WasmActivation::startInterrupt(const JS::ProfilingFrameIterator::RegisterState& state)
{
MOZ_ASSERT(pc);
MOZ_ASSERT(fp);
MOZ_ASSERT(state.pc);
MOZ_ASSERT(state.fp);
// Execution can only be interrupted in function code. Afterwards, control
// flow does not reenter function code and thus there should be no
// interrupt-during-interrupt.
MOZ_ASSERT(!interrupted());
MOZ_ASSERT(compartment()->wasm.lookupCode(pc)->lookupRange(pc)->isFunction());
cx_->runtime()->setWasmResumePC(pc);
exitFP_ = reinterpret_cast<wasm::Frame*>(fp);
bool ignoredUnwound;
wasm::UnwindState unwindState;
MOZ_ALWAYS_TRUE(wasm::StartUnwinding(*this, state, &unwindState, &ignoredUnwound));
void* unwindPC = unwindState.pc;
MOZ_ASSERT(compartment()->wasm.lookupCode(unwindPC)->lookupRange(unwindPC)->isFunction());
cx_->runtime()->startWasmInterrupt(state.pc, unwindPC);
exitFP_ = reinterpret_cast<wasm::Frame*>(unwindState.fp);
MOZ_ASSERT(compartment() == exitFP_->tls->instance->compartment());
MOZ_ASSERT(interrupted());
@ -1676,14 +1682,14 @@ WasmActivation::finishInterrupt()
MOZ_ASSERT(interrupted());
MOZ_ASSERT(exitFP_);
cx_->runtime()->setWasmResumePC(nullptr);
cx_->runtime()->finishWasmInterrupt();
exitFP_ = nullptr;
}
bool
WasmActivation::interrupted() const
{
void* pc = cx_->runtime()->wasmResumePC();
void* pc = cx_->runtime()->wasmUnwindPC();
if (!pc)
return false;
@ -1699,6 +1705,13 @@ WasmActivation::interrupted() const
return true;
}
void*
WasmActivation::unwindPC() const
{
MOZ_ASSERT(interrupted());
return cx_->runtime()->wasmUnwindPC();
}
void*
WasmActivation::resumePC() const
{

Просмотреть файл

@ -1760,9 +1760,10 @@ class WasmActivation : public Activation
// Interrupts are started from the interrupt signal handler (or the ARM
// simulator) and cleared by WasmHandleExecutionInterrupt or WasmHandleThrow
// when the interrupt is handled.
void startInterrupt(void* pc, uint8_t* fp);
void startInterrupt(const JS::ProfilingFrameIterator::RegisterState& state);
void finishInterrupt();
bool interrupted() const;
void* unwindPC() const;
void* resumePC() const;
// Used by wasm::FrameIterator during stack unwinding.

Просмотреть файл

@ -72,11 +72,11 @@ FrameIterator::FrameIterator(WasmActivation* activation, Unwind unwind)
// but this is fine because CallSite is only used for line number for which
// we can use the beginning of the function from the CodeRange instead.
code_ = activation_->compartment()->wasm.lookupCode(activation->resumePC());
code_ = activation_->compartment()->wasm.lookupCode(activation->unwindPC());
MOZ_ASSERT(code_);
MOZ_ASSERT(&fp_->tls->instance->code() == code_);
codeRange_ = code_->lookupRange(activation->resumePC());
codeRange_ = code_->lookupRange(activation->unwindPC());
MOZ_ASSERT(codeRange_->kind() == CodeRange::Function);
MOZ_ASSERT(!done());

Просмотреть файл

@ -71,6 +71,7 @@ class AutoSetHandlingSegFault
# define XMM_sig(p,i) ((p)->Xmm##i)
# 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 RAX_sig(p) ((p)->Rax)
# define RCX_sig(p) ((p)->Rcx)
@ -92,6 +93,7 @@ class AutoSetHandlingSegFault
# define XMM_sig(p,i) ((p)->sc_fpstate->fx_xmm[i])
# 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 RAX_sig(p) ((p)->sc_rax)
# define RCX_sig(p) ((p)->sc_rcx)
@ -116,6 +118,7 @@ class AutoSetHandlingSegFault
# if defined(__aarch64__)
# define EPC_sig(p) ((p)->sc_elr)
# define RFP_sig(p) ((p)->sc_x[29])
# define RLR_sig(p) ((p)->sc_x[30])
# endif
# if defined(__mips__)
# define EPC_sig(p) ((p)->sc_pc)
@ -126,10 +129,12 @@ class AutoSetHandlingSegFault
# define XMM_sig(p,i) ((p)->uc_mcontext.fpregs->_xmm[i])
# 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 XMM_sig(p,i) ((p)->uc_mcontext.fpregs.fp_reg_set.fpchip_state.xmm[i])
# 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 RAX_sig(p) ((p)->uc_mcontext.gregs[REG_RAX])
@ -144,28 +149,31 @@ class AutoSetHandlingSegFault
# define R9_sig(p) ((p)->uc_mcontext.gregs[REG_R9])
# define R10_sig(p) ((p)->uc_mcontext.gregs[REG_R10])
# define R12_sig(p) ((p)->uc_mcontext.gregs[REG_R12])
# define R13_sig(p) ((p)->uc_mcontext.gregs[REG_R13])
# define R14_sig(p) ((p)->uc_mcontext.gregs[REG_R14])
# 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])
# endif
# if defined(__linux__) && defined(__mips__)
# define EPC_sig(p) ((p)->uc_mcontext.pc)
# define RSP_sig(p) ((p)->uc_mcontext.gregs[29])
# define RFP_sig(p) ((p)->uc_mcontext.gregs[30])
# endif
#elif defined(__NetBSD__)
# define XMM_sig(p,i) (((struct fxsave64*)(p)->uc_mcontext.__fpregs)->fx_xmm[i])
# 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 RAX_sig(p) ((p)->uc_mcontext.__gregs[_REG_RAX])
# define RCX_sig(p) ((p)->uc_mcontext.__gregs[_REG_RCX])
@ -186,6 +194,8 @@ class AutoSetHandlingSegFault
# 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 RSP_sig(p) ((p)->uc_mcontext.__gregs[_REG_X31])
# endif
# if defined(__mips__)
# define EPC_sig(p) ((p)->uc_mcontext.__gregs[_REG_EPC])
@ -199,6 +209,7 @@ class AutoSetHandlingSegFault
# endif
# 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 RAX_sig(p) ((p)->uc_mcontext.mc_rax)
# define RCX_sig(p) ((p)->uc_mcontext.mc_rcx)
@ -223,6 +234,7 @@ class AutoSetHandlingSegFault
# 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_x[30])
# endif
# if defined(__FreeBSD__) && defined(__mips__)
# define EPC_sig(p) ((p)->uc_mcontext.mc_pc)
@ -231,8 +243,10 @@ class AutoSetHandlingSegFault
#elif defined(XP_DARWIN)
# 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 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."
@ -375,15 +389,21 @@ struct macos_arm_context {
#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 PC_sig(p) R15_sig(p)
# 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(__aarch64__)
# 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) RLR_sig(p)
#elif defined(__mips__)
# define PC_sig(p) EPC_sig(p)
# define FP_sig(p) RFP_sig(p)
@ -409,7 +429,46 @@ ContextToFP(CONTEXT* context)
#endif
}
static uint8_t*
ContextToSP(CONTEXT* context)
{
#ifdef JS_CODEGEN_NONE
MOZ_CRASH();
#else
return reinterpret_cast<uint8_t*>(SP_sig(context));
#endif
}
#if defined(__arm__) || defined(__aarch64__)
static uint8_t*
ContextToLR(CONTEXT* context)
{
return reinterpret_cast<uint8_t*>(LR_sig(context));
}
#endif
#if defined(XP_DARWIN)
static uint8_t**
ContextToPC(EMULATOR_CONTEXT* context)
{
# if defined(__x86_64__)
static_assert(sizeof(context->thread.__rip) == sizeof(void*),
"stored IP should be compile-time pointer-sized");
return reinterpret_cast<uint8_t**>(&context->thread.__rip);
# elif defined(__i386__)
static_assert(sizeof(context->thread.uts.ts32.__eip) == sizeof(void*),
"stored IP should be compile-time pointer-sized");
return reinterpret_cast<uint8_t**>(&context->thread.uts.ts32.__eip);
# elif defined(__arm__)
static_assert(sizeof(context->thread.__pc) == sizeof(void*),
"stored IP should be compile-time pointer-sized");
return reinterpret_cast<uint8_t**>(&context->thread.__pc);
# else
# error Unsupported architecture
# endif
}
static uint8_t*
ContextToFP(EMULATOR_CONTEXT* context)
{
@ -423,7 +482,49 @@ ContextToFP(EMULATOR_CONTEXT* context)
# error Unsupported architecture
# endif
}
#endif // XP_DARWIN
static uint8_t*
ContextToSP(EMULATOR_CONTEXT* context)
{
# if defined(__x86_64__)
return (uint8_t*)context->thread.__rsp;
# elif defined(__i386__)
return (uint8_t*)context->thread.uts.ts32.__esp;
# elif defined(__arm__)
return (uint8_t*)context->thread.__sp;
# else
# error Unsupported architecture
# endif
}
static JS::ProfilingFrameIterator::RegisterState
ToRegisterState(EMULATOR_CONTEXT* context)
{
JS::ProfilingFrameIterator::RegisterState state;
state.fp = ContextToFP(context);
state.pc = *ContextToPC(context);
state.sp = ContextToSP(context);
// no ARM on Darwin => don't fill state.lr.
return state;
}
#endif // XP_DARWIN
static JS::ProfilingFrameIterator::RegisterState
ToRegisterState(CONTEXT* context)
{
#ifdef JS_CODEGEN_NONE
MOZ_CRASH();
#else
JS::ProfilingFrameIterator::RegisterState state;
state.fp = ContextToFP(context);
state.pc = *ContextToPC(context);
state.sp = ContextToSP(context);
# if defined(__arm__) || defined(__aarch64__)
state.lr = ContextToLR(context);
# endif
return state;
#endif
}
#if defined(WASM_HUGE_MEMORY)
MOZ_COLD static void
@ -672,7 +773,7 @@ HandleMemoryAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddr
// experimental SIMD.js or Atomics. When these are converted to
// non-experimental wasm features, this case, as well as outOfBoundsCode,
// can be removed.
activation->startInterrupt(pc, ContextToFP(context));
activation->startInterrupt(ToRegisterState(context));
if (!instance.code().containsCodePC(pc, &segment))
MOZ_CRASH("Cannot map PC to trap handler");
*ppc = segment->outOfBoundsCode();
@ -818,7 +919,7 @@ HandleMemoryAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddr
const MemoryAccess* memoryAccess = instance.code().lookupMemoryAccess(pc, &segment);
if (!memoryAccess) {
// See explanation in the WASM_HUGE_MEMORY HandleMemoryAccess.
activation->startInterrupt(pc, ContextToFP(context));
activation->startInterrupt(ToRegisterState(context));
if (!instance.code().containsCodePC(pc, &segment))
MOZ_CRASH("Cannot map PC to trap handler");
*ppc = segment->outOfBoundsCode();
@ -935,26 +1036,6 @@ WasmFaultHandler(LPEXCEPTION_POINTERS exception)
#elif defined(XP_DARWIN)
# include <mach/exc.h>
static uint8_t**
ContextToPC(EMULATOR_CONTEXT* context)
{
# if defined(__x86_64__)
static_assert(sizeof(context->thread.__rip) == sizeof(void*),
"stored IP should be compile-time pointer-sized");
return reinterpret_cast<uint8_t**>(&context->thread.__rip);
# elif defined(__i386__)
static_assert(sizeof(context->thread.uts.ts32.__eip) == sizeof(void*),
"stored IP should be compile-time pointer-sized");
return reinterpret_cast<uint8_t**>(&context->thread.uts.ts32.__eip);
# elif defined(__arm__)
static_assert(sizeof(context->thread.__pc) == sizeof(void*),
"stored IP should be compile-time pointer-sized");
return reinterpret_cast<uint8_t**>(&context->thread.__pc);
# else
# error Unsupported architecture
# endif
}
// This definition was generated by mig (the Mach Interface Generator) for the
// routine 'exception_raise' (exc.defs).
#pragma pack(4)
@ -1273,7 +1354,7 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
// partly overlaps the end of the heap. In this case, it is an out-of-bounds
// error and we should signal that properly, but to do so we must inspect
// the operand of the failed access.
activation->startInterrupt(pc, ContextToFP(context));
activation->startInterrupt(ToRegisterState(context));
*ppc = segment->unalignedAccessCode();
return true;
}
@ -1397,7 +1478,7 @@ RedirectJitCodeToInterruptCheck(JSContext* cx, CONTEXT* context)
if (activation->interrupted())
return false;
activation->startInterrupt(pc, fp);
activation->startInterrupt(ToRegisterState(context));
*ContextToPC(context) = codeSegment->interruptCode();
#endif