mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 851964 - Re-enable OdinMonkey on OSX (r=vlad)
--HG-- extra : rebase_source : aff0da8d3695b7327d2daa367ce01f7766e38200
This commit is contained in:
parent
f767c9dfcc
commit
c7574816db
@ -4954,7 +4954,7 @@ Warn(JSContext *cx, int code, const char *str = NULL)
|
||||
}
|
||||
|
||||
extern bool
|
||||
EnsureAsmJSSignalHandlersInstalled();
|
||||
EnsureAsmJSSignalHandlersInstalled(JSRuntime *rt);
|
||||
|
||||
bool
|
||||
js::CompileAsmJS(JSContext *cx, TokenStream &ts, ParseNode *fn, HandleScript script)
|
||||
@ -4970,7 +4970,7 @@ js::CompileAsmJS(JSContext *cx, TokenStream &ts, ParseNode *fn, HandleScript scr
|
||||
return Warn(cx, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by debugger");
|
||||
|
||||
#ifdef JS_ASMJS
|
||||
if (!EnsureAsmJSSignalHandlersInstalled())
|
||||
if (!EnsureAsmJSSignalHandlersInstalled(cx->runtime))
|
||||
return Warn(cx, JSMSG_USE_ASM_TYPE_FAIL, "Platform missing signal handler support");
|
||||
|
||||
ScopedJSDeletePtr<AsmJSModule> module;
|
||||
|
@ -8,12 +8,17 @@
|
||||
#if !defined(jsion_asmjs_h__)
|
||||
#define jsion_asmjs_h__
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
# include <pthread.h>
|
||||
# include <mach/mach.h>
|
||||
#endif
|
||||
|
||||
// asm.js compilation is only available on desktop x86/x64 at the moment.
|
||||
// Don't panic, mobile support is coming soon.
|
||||
#if defined(JS_ION) && \
|
||||
!defined(ANDROID) && \
|
||||
(defined(JS_CPU_X86) || defined(JS_CPU_X64)) && \
|
||||
(defined(__linux__) || defined(XP_WIN))
|
||||
(defined(__linux__) || defined(XP_WIN) || defined(XP_MACOSX))
|
||||
# define JS_ASMJS
|
||||
#endif
|
||||
|
||||
@ -93,12 +98,32 @@ class AsmJSActivation
|
||||
// The asm.js spec requires that the ArrayBuffer's byteLength be a multiple of 4096.
|
||||
static const size_t AsmJSAllocationGranularity = 4096;
|
||||
|
||||
#ifdef JS_CPU_X64
|
||||
// On x64, the internal ArrayBuffer data array is inflated to 4GiB (only the
|
||||
// byteLength portion of which is accessible) so that out-of-bounds accesses
|
||||
// (made using a uint32 index) are guaranteed to raise a SIGSEGV.
|
||||
# ifdef JS_CPU_X64
|
||||
static const size_t AsmJSBufferProtectedSize = 4 * 1024ULL * 1024ULL * 1024ULL;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
class AsmJSMachExceptionHandler
|
||||
{
|
||||
bool installed_;
|
||||
pthread_t thread_;
|
||||
mach_port_t port_;
|
||||
|
||||
void release();
|
||||
|
||||
public:
|
||||
AsmJSMachExceptionHandler();
|
||||
~AsmJSMachExceptionHandler() { release(); }
|
||||
mach_port_t port() const { return port_; }
|
||||
bool installed() const { return installed_; }
|
||||
bool install(JSRuntime *rt);
|
||||
void clearCurrentThread();
|
||||
void setCurrentThread();
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace js
|
||||
|
||||
|
@ -481,6 +481,10 @@ class AsmJSModule
|
||||
JS_ASSERT(functionBytes_ % gc::PageSize == 0);
|
||||
return functionBytes_;
|
||||
}
|
||||
bool containsPC(void *pc) const {
|
||||
uint8_t *code = functionCode();
|
||||
return pc >= code && pc < (code + functionBytes());
|
||||
}
|
||||
|
||||
bool addHeapAccesses(const Vector<ion::AsmJSHeapAccess> &accesses) {
|
||||
if (!heapAccesses_.reserve(heapAccesses_.length() + accesses.length()))
|
||||
|
@ -18,21 +18,39 @@ using namespace js::ion;
|
||||
|
||||
#ifdef JS_ASMJS
|
||||
|
||||
// Prevent races trying to install the signal handlers.
|
||||
#ifdef JS_THREADSAFE
|
||||
# include "jslock.h"
|
||||
// For platforms where the signal/exception handler runs on the same
|
||||
// thread/stack as the victim (Unix and Windows), we can use TLS to find any
|
||||
// currently executing asm.js code.
|
||||
#if !defined(XP_MACOSX)
|
||||
static AsmJSActivation *
|
||||
InnermostAsmJSActivation()
|
||||
{
|
||||
PerThreadData *threadData = TlsPerThreadData.get();
|
||||
if (!threadData)
|
||||
return NULL;
|
||||
|
||||
class SignalMutex
|
||||
return threadData->asmJSActivationStackFromOwnerThread();
|
||||
}
|
||||
#endif
|
||||
|
||||
// For platforms that install a single, process-wide signal handler (Unix and
|
||||
// Windows), the InstallSignalHandlersMutex prevents races between JSRuntimes
|
||||
// installing signal handlers.
|
||||
#if !defined(XP_MACOSX)
|
||||
# ifdef JS_THREADSAFE
|
||||
# include "jslock.h"
|
||||
|
||||
class InstallSignalHandlersMutex
|
||||
{
|
||||
PRLock *mutex_;
|
||||
|
||||
public:
|
||||
SignalMutex() {
|
||||
InstallSignalHandlersMutex() {
|
||||
mutex_ = PR_NewLock();
|
||||
if (!mutex_)
|
||||
MOZ_CRASH();
|
||||
}
|
||||
~SignalMutex() {
|
||||
~InstallSignalHandlersMutex() {
|
||||
PR_DestroyLock(mutex_);
|
||||
}
|
||||
class Lock {
|
||||
@ -45,19 +63,19 @@ class SignalMutex
|
||||
};
|
||||
} signalMutex;
|
||||
|
||||
bool SignalMutex::Lock::sHandlersInstalled = false;
|
||||
bool InstallSignalHandlersMutex::Lock::sHandlersInstalled = false;
|
||||
|
||||
SignalMutex::Lock::Lock()
|
||||
InstallSignalHandlersMutex::Lock::Lock()
|
||||
{
|
||||
PR_Lock(signalMutex.mutex_);
|
||||
}
|
||||
|
||||
SignalMutex::Lock::~Lock()
|
||||
InstallSignalHandlersMutex::Lock::~Lock()
|
||||
{
|
||||
PR_Unlock(signalMutex.mutex_);
|
||||
}
|
||||
#else
|
||||
struct SignalMutex
|
||||
# else // JS_THREADSAFE
|
||||
struct InstallSignalHandlersMutex
|
||||
{
|
||||
class Lock {
|
||||
static bool sHandlersInstalled;
|
||||
@ -68,25 +86,9 @@ struct SignalMutex
|
||||
};
|
||||
};
|
||||
|
||||
bool SignalMutex::Lock::sHandlersInstalled = false;
|
||||
#endif
|
||||
|
||||
static AsmJSActivation *
|
||||
InnermostAsmJSActivation()
|
||||
{
|
||||
PerThreadData *threadData = TlsPerThreadData.get();
|
||||
if (!threadData)
|
||||
return NULL;
|
||||
|
||||
return threadData->asmJSActivationStackFromOwnerThread();
|
||||
}
|
||||
|
||||
static bool
|
||||
PCIsInModule(const AsmJSModule &module, void *pc)
|
||||
{
|
||||
uint8_t *code = module.functionCode();
|
||||
return pc >= code && pc < (code + module.functionBytes());
|
||||
}
|
||||
bool InstallSignalHandlersMutex::Lock::sHandlersInstalled = false;
|
||||
# endif // JS_THREADSAFE
|
||||
#endif // !XP_MACOSX
|
||||
|
||||
# if defined(JS_CPU_X64)
|
||||
template <class T>
|
||||
@ -113,7 +115,7 @@ SetXMMRegToNaN(bool isFloat32, T *xmm_reg)
|
||||
static const AsmJSHeapAccess *
|
||||
LookupHeapAccess(const AsmJSModule &module, uint8_t *pc)
|
||||
{
|
||||
JS_ASSERT(PCIsInModule(module, pc));
|
||||
JS_ASSERT(module.containsPC(pc));
|
||||
size_t targetOffset = pc - module.functionCode();
|
||||
|
||||
if (module.numHeapAccesses() == 0)
|
||||
@ -221,7 +223,7 @@ HandleException(PEXCEPTION_POINTERS exception)
|
||||
JS_ASSERT(pc == record->ExceptionAddress);
|
||||
|
||||
const AsmJSModule &module = activation->module();
|
||||
if (!PCIsInModule(module, pc))
|
||||
if (!module.containsPC(pc))
|
||||
return false;
|
||||
|
||||
if (record->NumberParameters < 2)
|
||||
@ -234,7 +236,7 @@ HandleException(PEXCEPTION_POINTERS exception)
|
||||
// execution to a trampoline which will call js_HandleExecutionInterrupt.
|
||||
// The trampoline will jump to activation->resumePC if execution isn't
|
||||
// interrupted.
|
||||
if (PCIsInModule(module, faultingAddress)) {
|
||||
if (module.containsPC(faultingAddress)) {
|
||||
activation->setResumePC(pc);
|
||||
*ppc = module.operationCallbackExit();
|
||||
DWORD oldProtect;
|
||||
@ -286,26 +288,351 @@ AsmJSExceptionHandler(LPEXCEPTION_POINTERS exception)
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
# else // If not Windows, assume Unix
|
||||
# elif defined(XP_MACOSX)
|
||||
# include <sys/mman.h>
|
||||
# include <mach/exc.h>
|
||||
|
||||
static uint8_t **
|
||||
ContextToPC(x86_thread_state_t &state)
|
||||
{
|
||||
# if defined(JS_CPU_X64)
|
||||
JS_STATIC_ASSERT(sizeof(state.uts.ts64.__rip) == sizeof(void*));
|
||||
return reinterpret_cast<uint8_t**>(&state.uts.ts64.__rip);
|
||||
# else
|
||||
JS_STATIC_ASSERT(sizeof(state.uts.ts32.__eip) == sizeof(void*));
|
||||
return reinterpret_cast<uint8_t**>(&state.uts.ts32.__eip);
|
||||
# endif
|
||||
}
|
||||
|
||||
# if defined(JS_CPU_X64)
|
||||
static bool
|
||||
SetRegisterToCoercedUndefined(mach_port_t rtThread, x86_thread_state64_t &state,
|
||||
const AsmJSHeapAccess &heapAccess)
|
||||
{
|
||||
if (heapAccess.loadedReg().isFloat()) {
|
||||
kern_return_t kret;
|
||||
|
||||
x86_float_state64_t fstate;
|
||||
unsigned int count = x86_FLOAT_STATE64_COUNT;
|
||||
kret = thread_get_state(rtThread, x86_FLOAT_STATE64, (thread_state_t) &fstate, &count);
|
||||
if (kret != KERN_SUCCESS)
|
||||
return false;
|
||||
|
||||
bool f32 = heapAccess.isFloat32Load();
|
||||
switch (heapAccess.loadedReg().fpu().code()) {
|
||||
case JSC::X86Registers::xmm0: SetXMMRegToNaN(f32, &fstate.__fpu_xmm0); break;
|
||||
case JSC::X86Registers::xmm1: SetXMMRegToNaN(f32, &fstate.__fpu_xmm1); break;
|
||||
case JSC::X86Registers::xmm2: SetXMMRegToNaN(f32, &fstate.__fpu_xmm2); break;
|
||||
case JSC::X86Registers::xmm3: SetXMMRegToNaN(f32, &fstate.__fpu_xmm3); break;
|
||||
case JSC::X86Registers::xmm4: SetXMMRegToNaN(f32, &fstate.__fpu_xmm4); break;
|
||||
case JSC::X86Registers::xmm5: SetXMMRegToNaN(f32, &fstate.__fpu_xmm5); break;
|
||||
case JSC::X86Registers::xmm6: SetXMMRegToNaN(f32, &fstate.__fpu_xmm6); break;
|
||||
case JSC::X86Registers::xmm7: SetXMMRegToNaN(f32, &fstate.__fpu_xmm7); break;
|
||||
case JSC::X86Registers::xmm8: SetXMMRegToNaN(f32, &fstate.__fpu_xmm8); break;
|
||||
case JSC::X86Registers::xmm9: SetXMMRegToNaN(f32, &fstate.__fpu_xmm9); break;
|
||||
case JSC::X86Registers::xmm10: SetXMMRegToNaN(f32, &fstate.__fpu_xmm10); break;
|
||||
case JSC::X86Registers::xmm11: SetXMMRegToNaN(f32, &fstate.__fpu_xmm11); break;
|
||||
case JSC::X86Registers::xmm12: SetXMMRegToNaN(f32, &fstate.__fpu_xmm12); break;
|
||||
case JSC::X86Registers::xmm13: SetXMMRegToNaN(f32, &fstate.__fpu_xmm13); break;
|
||||
case JSC::X86Registers::xmm14: SetXMMRegToNaN(f32, &fstate.__fpu_xmm14); break;
|
||||
case JSC::X86Registers::xmm15: SetXMMRegToNaN(f32, &fstate.__fpu_xmm15); break;
|
||||
default: MOZ_CRASH();
|
||||
}
|
||||
|
||||
kret = thread_set_state(rtThread, x86_FLOAT_STATE64, (thread_state_t)&fstate, x86_FLOAT_STATE64_COUNT);
|
||||
if (kret != KERN_SUCCESS)
|
||||
return false;
|
||||
} else {
|
||||
switch (heapAccess.loadedReg().gpr().code()) {
|
||||
case JSC::X86Registers::eax: state.__rax = 0; break;
|
||||
case JSC::X86Registers::ecx: state.__rcx = 0; break;
|
||||
case JSC::X86Registers::edx: state.__rdx = 0; break;
|
||||
case JSC::X86Registers::ebx: state.__rbx = 0; break;
|
||||
case JSC::X86Registers::esp: state.__rsp = 0; break;
|
||||
case JSC::X86Registers::ebp: state.__rbp = 0; break;
|
||||
case JSC::X86Registers::esi: state.__rsi = 0; break;
|
||||
case JSC::X86Registers::edi: state.__rdi = 0; break;
|
||||
case JSC::X86Registers::r8: state.__r8 = 0; break;
|
||||
case JSC::X86Registers::r9: state.__r9 = 0; break;
|
||||
case JSC::X86Registers::r10: state.__r10 = 0; break;
|
||||
case JSC::X86Registers::r11: state.__r11 = 0; break;
|
||||
case JSC::X86Registers::r12: state.__r12 = 0; break;
|
||||
case JSC::X86Registers::r13: state.__r13 = 0; break;
|
||||
case JSC::X86Registers::r14: state.__r14 = 0; break;
|
||||
case JSC::X86Registers::r15: state.__r15 = 0; break;
|
||||
default: MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
# endif
|
||||
|
||||
// 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(JSRuntime *rt, const ExceptionRequest &request)
|
||||
{
|
||||
// Get the port of the JSRuntime's thread from the message.
|
||||
mach_port_t rtThread = request.body.thread.name;
|
||||
|
||||
// Read out the JSRuntime thread's register state.
|
||||
x86_thread_state_t state;
|
||||
unsigned int count = x86_THREAD_STATE_COUNT;
|
||||
kern_return_t kret;
|
||||
kret = thread_get_state(rtThread, x86_THREAD_STATE, (thread_state_t)&state, &count);
|
||||
if (kret != KERN_SUCCESS)
|
||||
return false;
|
||||
|
||||
AsmJSActivation *activation = rt->mainThread.asmJSActivationStackFromAnyThread();
|
||||
if (!activation)
|
||||
return false;
|
||||
|
||||
uint8_t **ppc = ContextToPC(state);
|
||||
uint8_t *pc = *ppc;
|
||||
|
||||
const AsmJSModule &module = activation->module();
|
||||
if (!module.containsPC(pc))
|
||||
return false;
|
||||
|
||||
if (request.body.exception != EXC_BAD_ACCESS || request.body.codeCnt != 2)
|
||||
return false;
|
||||
|
||||
void *faultingAddress = (void*)request.body.code[1];
|
||||
|
||||
// If we faulted trying to execute code in 'module', this must be an
|
||||
// operation callback (see TriggerOperationCallbackForAsmJSCode). Redirect
|
||||
// execution to a trampoline which will call js_HandleExecutionInterrupt.
|
||||
// The trampoline will jump to activation->resumePC if execution isn't
|
||||
// interrupted.
|
||||
if (module.containsPC(faultingAddress)) {
|
||||
activation->setResumePC(pc);
|
||||
*ppc = module.operationCallbackExit();
|
||||
mprotect(module.functionCode(), module.functionBytes(), PROT_EXEC);
|
||||
|
||||
// Update the thread state with the new pc.
|
||||
kret = thread_set_state(rtThread, x86_THREAD_STATE, (thread_state_t)&state, x86_THREAD_STATE_COUNT);
|
||||
return kret == KERN_SUCCESS;
|
||||
}
|
||||
|
||||
# if defined(JS_CPU_X64)
|
||||
// These checks aren't necessary, but, since we can, check anyway to make
|
||||
// sure we aren't covering up a real bug.
|
||||
if (!module.maybeHeap() ||
|
||||
faultingAddress < module.maybeHeap() ||
|
||||
faultingAddress >= module.maybeHeap() + AsmJSBufferProtectedSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const AsmJSHeapAccess *heapAccess = LookupHeapAccess(module, pc);
|
||||
if (!heapAccess)
|
||||
return false;
|
||||
|
||||
// We now know that this is an out-of-bounds access made by an asm.js
|
||||
// load/store that we should handle. If this is a load, assign the
|
||||
// JS-defined result value to the destination register (ToInt32(undefined)
|
||||
// or ToNumber(undefined), determined by the type of the destination
|
||||
// register) and set the PC to the next op. Upon return from the handler,
|
||||
// execution will resume at this next PC.
|
||||
if (heapAccess->isLoad()) {
|
||||
if (!SetRegisterToCoercedUndefined(rtThread, state.uts.ts64, *heapAccess))
|
||||
return false;
|
||||
}
|
||||
*ppc += heapAccess->opLength();
|
||||
|
||||
// Update the thread state with the new pc.
|
||||
kret = thread_set_state(rtThread, x86_THREAD_STATE, (thread_state_t)&state, x86_THREAD_STATE_COUNT);
|
||||
if (kret != KERN_SUCCESS)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
# else
|
||||
return false;
|
||||
# endif
|
||||
}
|
||||
|
||||
void *
|
||||
AsmJSMachExceptionHandlerThread(void *threadArg)
|
||||
{
|
||||
JSRuntime *rt = reinterpret_cast<JSRuntime*>(threadArg);
|
||||
mach_port_t port = rt->asmJSMachExceptionHandler.port();
|
||||
kern_return_t kret;
|
||||
|
||||
while(true) {
|
||||
ExceptionRequest request;
|
||||
kret = mach_msg(&request.body.Head, MACH_RCV_MSG, 0, sizeof(request),
|
||||
port, 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, "AsmJSMachExceptionHandlerThread: mach_msg failed with %d\n", (int)kret);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
// 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 asm.js
|
||||
// expected exception, we handle it and return KERN_SUCCESS.
|
||||
bool handled = HandleMachException(rt, 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);
|
||||
}
|
||||
|
||||
JS_NOT_REACHED("The exception handler thread should never return");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AsmJSMachExceptionHandler::AsmJSMachExceptionHandler()
|
||||
: installed_(false),
|
||||
thread_(NULL),
|
||||
port_(MACH_PORT_NULL)
|
||||
{}
|
||||
|
||||
void
|
||||
AsmJSMachExceptionHandler::release()
|
||||
{
|
||||
if (installed_) {
|
||||
clearCurrentThread();
|
||||
installed_ = false;
|
||||
}
|
||||
if (thread_ != NULL) {
|
||||
pthread_cancel(thread_);
|
||||
thread_ = NULL;
|
||||
}
|
||||
if (port_ != MACH_PORT_NULL) {
|
||||
mach_port_deallocate(mach_task_self(), port_);
|
||||
port_ = MACH_PORT_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AsmJSMachExceptionHandler::clearCurrentThread()
|
||||
{
|
||||
if (!installed_)
|
||||
return;
|
||||
|
||||
kern_return_t kret = thread_set_exception_ports(mach_thread_self(),
|
||||
EXC_MASK_BAD_ACCESS,
|
||||
MACH_PORT_NULL,
|
||||
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
|
||||
THREAD_STATE_NONE);
|
||||
if (kret != KERN_SUCCESS)
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
void
|
||||
AsmJSMachExceptionHandler::setCurrentThread()
|
||||
{
|
||||
if (!installed_)
|
||||
return;
|
||||
|
||||
kern_return_t kret = thread_set_exception_ports(mach_thread_self(),
|
||||
EXC_MASK_BAD_ACCESS,
|
||||
port_,
|
||||
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
|
||||
THREAD_STATE_NONE);
|
||||
if (kret != KERN_SUCCESS)
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
bool
|
||||
AsmJSMachExceptionHandler::install(JSRuntime *rt)
|
||||
{
|
||||
JS_ASSERT(!installed());
|
||||
kern_return_t kret;
|
||||
|
||||
// Get a port which can send and receive data.
|
||||
kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port_);
|
||||
if (kret != KERN_SUCCESS)
|
||||
goto error;
|
||||
kret = mach_port_insert_right(mach_task_self(), port_, port_, MACH_MSG_TYPE_MAKE_SEND);
|
||||
if (kret != KERN_SUCCESS)
|
||||
goto error;
|
||||
|
||||
// Create a thread to block on reading port_.
|
||||
pthread_attr_t attrs;
|
||||
pthread_attr_init(&attrs);
|
||||
pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
|
||||
if (pthread_create(&thread_, &attrs, AsmJSMachExceptionHandlerThread, rt))
|
||||
goto error;
|
||||
|
||||
// Direct exceptions on this thread to port_ (and thus our handler thread).
|
||||
// Note: we are totally clobbering any existing *thread* exception ports and
|
||||
// not even attempting to forward. Breakpad and gdb both use the *process*
|
||||
// exception ports which are only called if the thread doesn't handle the
|
||||
// exception, so we should be fine.
|
||||
kret = thread_set_exception_ports(mach_thread_self(),
|
||||
EXC_MASK_BAD_ACCESS,
|
||||
port_,
|
||||
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
|
||||
THREAD_STATE_NONE);
|
||||
if (kret != KERN_SUCCESS)
|
||||
goto error;
|
||||
|
||||
installed_ = true;
|
||||
return true;
|
||||
|
||||
error:
|
||||
release();
|
||||
return false;
|
||||
}
|
||||
|
||||
# else // If not Windows or Mac, assume Unix
|
||||
# include <signal.h>
|
||||
# include <sys/mman.h>
|
||||
|
||||
// Unfortunately, we still need OS-specific code to read/write to the thread
|
||||
// state via the mcontext_t.
|
||||
# if defined(__linux__)
|
||||
static uint8_t **
|
||||
ContextToPC(mcontext_t &context)
|
||||
{
|
||||
# if defined(JS_CPU_X86)
|
||||
# if defined(JS_CPU_X86)
|
||||
JS_STATIC_ASSERT(sizeof(context.gregs[REG_EIP]) == sizeof(void*));
|
||||
return reinterpret_cast<uint8_t**>(&context.gregs[REG_EIP]);
|
||||
# else
|
||||
# else
|
||||
JS_STATIC_ASSERT(sizeof(context.gregs[REG_RIP]) == sizeof(void*));
|
||||
return reinterpret_cast<uint8_t**>(&context.gregs[REG_RIP]);
|
||||
# endif
|
||||
# endif
|
||||
}
|
||||
|
||||
# if defined(JS_CPU_X64)
|
||||
# if defined(JS_CPU_X64)
|
||||
static void
|
||||
SetRegisterToCoercedUndefined(mcontext_t &context, bool isFloat32, AnyRegister reg)
|
||||
{
|
||||
@ -351,68 +678,7 @@ SetRegisterToCoercedUndefined(mcontext_t &context, bool isFloat32, AnyRegister r
|
||||
}
|
||||
}
|
||||
}
|
||||
# endif
|
||||
# elif defined(XP_MACOSX)
|
||||
static uint8_t **
|
||||
ContextToPC(mcontext_t context)
|
||||
{
|
||||
# if defined(JS_CPU_X86)
|
||||
JS_STATIC_ASSERT(sizeof(context->__ss.__eip) == sizeof(void*));
|
||||
return reinterpret_cast<uint8_t **>(&context->__ss.__eip);
|
||||
# else
|
||||
JS_STATIC_ASSERT(sizeof(context->__ss.__rip) == sizeof(void*));
|
||||
return reinterpret_cast<uint8_t **>(&context->__ss.__rip);
|
||||
# endif
|
||||
}
|
||||
|
||||
# if defined(JS_CPU_X64)
|
||||
static void
|
||||
SetRegisterToCoercedUndefined(mcontext_t &context, bool isFloat32, AnyRegister reg)
|
||||
{
|
||||
if (reg.isFloat()) {
|
||||
switch (reg.fpu().code()) {
|
||||
case JSC::X86Registers::xmm0: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm0); break;
|
||||
case JSC::X86Registers::xmm1: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm1); break;
|
||||
case JSC::X86Registers::xmm2: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm2); break;
|
||||
case JSC::X86Registers::xmm3: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm3); break;
|
||||
case JSC::X86Registers::xmm4: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm4); break;
|
||||
case JSC::X86Registers::xmm5: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm5); break;
|
||||
case JSC::X86Registers::xmm6: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm6); break;
|
||||
case JSC::X86Registers::xmm7: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm7); break;
|
||||
case JSC::X86Registers::xmm8: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm8); break;
|
||||
case JSC::X86Registers::xmm9: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm9); break;
|
||||
case JSC::X86Registers::xmm10: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm10); break;
|
||||
case JSC::X86Registers::xmm11: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm11); break;
|
||||
case JSC::X86Registers::xmm12: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm12); break;
|
||||
case JSC::X86Registers::xmm13: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm13); break;
|
||||
case JSC::X86Registers::xmm14: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm14); break;
|
||||
case JSC::X86Registers::xmm15: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm15); break;
|
||||
default: MOZ_CRASH();
|
||||
}
|
||||
} else {
|
||||
switch (reg.gpr().code()) {
|
||||
case JSC::X86Registers::eax: context->__ss.__rax = 0; break;
|
||||
case JSC::X86Registers::ecx: context->__ss.__rcx = 0; break;
|
||||
case JSC::X86Registers::edx: context->__ss.__rdx = 0; break;
|
||||
case JSC::X86Registers::ebx: context->__ss.__rbx = 0; break;
|
||||
case JSC::X86Registers::esp: context->__ss.__rsp = 0; break;
|
||||
case JSC::X86Registers::ebp: context->__ss.__rbp = 0; break;
|
||||
case JSC::X86Registers::esi: context->__ss.__rsi = 0; break;
|
||||
case JSC::X86Registers::edi: context->__ss.__rdi = 0; break;
|
||||
case JSC::X86Registers::r8: context->__ss.__r8 = 0; break;
|
||||
case JSC::X86Registers::r9: context->__ss.__r9 = 0; break;
|
||||
case JSC::X86Registers::r10: context->__ss.__r10 = 0; break;
|
||||
case JSC::X86Registers::r11: context->__ss.__r11 = 0; break;
|
||||
case JSC::X86Registers::r12: context->__ss.__r12 = 0; break;
|
||||
case JSC::X86Registers::r13: context->__ss.__r13 = 0; break;
|
||||
case JSC::X86Registers::r14: context->__ss.__r14 = 0; break;
|
||||
case JSC::X86Registers::r15: context->__ss.__r15 = 0; break;
|
||||
default: MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
}
|
||||
# endif
|
||||
# endif // end of OS-specific mcontext accessors
|
||||
# endif
|
||||
|
||||
// Be very cautious and default to not handling; we don't want to accidentally
|
||||
// silence real crashes from real bugs.
|
||||
@ -428,7 +694,7 @@ HandleSignal(int signum, siginfo_t *info, void *ctx)
|
||||
uint8_t *pc = *ppc;
|
||||
|
||||
const AsmJSModule &module = activation->module();
|
||||
if (!PCIsInModule(module, pc))
|
||||
if (!module.containsPC(pc))
|
||||
return false;
|
||||
|
||||
void *faultingAddress = info->si_addr;
|
||||
@ -438,7 +704,7 @@ HandleSignal(int signum, siginfo_t *info, void *ctx)
|
||||
// execution to a trampoline which will call js_HandleExecutionInterrupt.
|
||||
// The trampoline will jump to activation->resumePC if execution isn't
|
||||
// interrupted.
|
||||
if (PCIsInModule(module, faultingAddress)) {
|
||||
if (module.containsPC(faultingAddress)) {
|
||||
activation->setResumePC(pc);
|
||||
*ppc = module.operationCallbackExit();
|
||||
mprotect(module.functionCode(), module.functionBytes(), PROT_EXEC);
|
||||
@ -503,17 +769,23 @@ AsmJSFaultHandler(int signum, siginfo_t *info, void *context)
|
||||
#endif // JS_ASMJS
|
||||
|
||||
bool
|
||||
EnsureAsmJSSignalHandlersInstalled()
|
||||
EnsureAsmJSSignalHandlersInstalled(JSRuntime *rt)
|
||||
{
|
||||
#if defined(JS_ASMJS)
|
||||
SignalMutex::Lock lock;
|
||||
# if defined(XP_MACOSX)
|
||||
// On OSX, each JSRuntime gets its own handler.
|
||||
return rt->asmJSMachExceptionHandler.installed() || rt->asmJSMachExceptionHandler.install(rt);
|
||||
# else
|
||||
// Assume Windows or Unix. For these platforms, there is a single,
|
||||
// process-wide signal handler installed. Take care to only install it once.
|
||||
InstallSignalHandlersMutex::Lock lock;
|
||||
if (lock.handlersInstalled())
|
||||
return true;
|
||||
|
||||
#if defined(XP_WIN)
|
||||
# if defined(XP_WIN)
|
||||
if (!AddVectoredExceptionHandler(/* FirstHandler = */true, AsmJSExceptionHandler))
|
||||
return false;
|
||||
#else
|
||||
# else // assume Unix
|
||||
struct sigaction sigAction;
|
||||
sigAction.sa_sigaction = &AsmJSFaultHandler;
|
||||
sigemptyset(&sigAction.sa_mask);
|
||||
@ -522,9 +794,10 @@ EnsureAsmJSSignalHandlersInstalled()
|
||||
return false;
|
||||
if (sigaction(SIGBUS, &sigAction, &sPrevHandler))
|
||||
return false;
|
||||
#endif
|
||||
# endif
|
||||
|
||||
lock.setHandlersInstalled();
|
||||
# endif
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
@ -555,7 +828,7 @@ js::TriggerOperationCallbackForAsmJSCode(JSRuntime *rt)
|
||||
DWORD oldProtect;
|
||||
if (!VirtualProtect(module.functionCode(), 4096, PAGE_NOACCESS, &oldProtect))
|
||||
MOZ_CRASH();
|
||||
# else
|
||||
# else // assume Unix
|
||||
if (mprotect(module.functionCode(), module.functionBytes(), PROT_NONE))
|
||||
MOZ_CRASH();
|
||||
# endif
|
||||
|
@ -1054,6 +1054,9 @@ JSRuntime::setOwnerThread()
|
||||
nativeStackBase = GetNativeStackBase();
|
||||
if (nativeStackQuota)
|
||||
JS_SetNativeStackQuota(this, nativeStackQuota);
|
||||
#ifdef XP_MACOSX
|
||||
asmJSMachExceptionHandler.setCurrentThread();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -1070,6 +1073,9 @@ JSRuntime::clearOwnerThread()
|
||||
#else
|
||||
mainThread.nativeStackLimit = 0;
|
||||
#endif
|
||||
#ifdef XP_MACOSX
|
||||
asmJSMachExceptionHandler.clearCurrentThread();
|
||||
#endif
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
|
@ -1146,6 +1146,10 @@ struct JSRuntime : js::RuntimeFriendFields,
|
||||
|
||||
js::GCHelperThread gcHelperThread;
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
js::AsmJSMachExceptionHandler asmJSMachExceptionHandler;
|
||||
#endif
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
# ifdef JS_ION
|
||||
js::WorkerThreadState *workerThreadState;
|
||||
|
Loading…
Reference in New Issue
Block a user