b=851964; Odin/OSX, part 3. enable AsmJS on OSX by using new Breakpad user handler, r=luke
☠☠ backed out by 26653529ea8b ☠ ☠
authorVladimir Vukicevic <vladimir@pobox.com>
Mon, 18 Mar 2013 19:07:07 -0400
changeset 125550 fe29b2ae604b7e7cf570bd8c57c8d21415789c9f
parent 125549 8c6ec2899d894a7acb7abf8aa0c4b2852d7e25f7
child 125551 b00eb1ef151749fe8eb31a61227127b1939f1023
push id24992
push uservladimir@pobox.com
push dateWed, 20 Mar 2013 02:05:31 +0000
treeherdermozilla-inbound@fe29b2ae604b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs851964
milestone22.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
b=851964; Odin/OSX, part 3. enable AsmJS on OSX by using new Breakpad user handler, r=luke
js/src/ion/AsmJS.h
js/src/ion/AsmJSModule.h
js/src/ion/AsmJSSignalHandlers.cpp
js/src/jsapi.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
--- a/js/src/ion/AsmJS.h
+++ b/js/src/ion/AsmJS.h
@@ -8,17 +8,17 @@
 #if !defined(jsion_asmjs_h__)
 #define jsion_asmjs_h__
 
 // 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
 
 namespace js {
 
 class SPSProfiler;
 class AsmJSModule;
 namespace frontend { struct TokenStream; struct ParseNode; }
--- a/js/src/ion/AsmJSModule.h
+++ b/js/src/ion/AsmJSModule.h
@@ -507,16 +507,21 @@ class AsmJSModule
         totalBytes_ = totalBytes;
     }
     uint8_t *functionCode() const {
         JS_ASSERT(code_);
         JS_ASSERT(uintptr_t(code_) % gc::PageSize == 0);
         return code_;
     }
 
+    bool pcIsInModule(void *pc) const {
+        return pc >= functionCode() &&
+               pc < (functionCode() + functionBytes());
+    }
+
     void setOperationCallbackExit(uint8_t *ptr) {
         operationCallbackExit_ = ptr;
     }
     uint8_t *operationCallbackExit() const {
         return operationCallbackExit_;
     }
 
     void setIsLinked(Handle<ArrayBufferObject*> maybeHeap) {
--- a/js/src/ion/AsmJSSignalHandlers.cpp
+++ b/js/src/ion/AsmJSSignalHandlers.cpp
@@ -76,23 +76,16 @@ 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());
-}
-
 # if defined(JS_CPU_X64)
 template <class T>
 static void
 SetXMMRegToNaN(bool isFloat32, T *xmm_reg)
 {
     if (isFloat32) {
         JS_STATIC_ASSERT(sizeof(T) == 4 * sizeof(float));
         float *floats = reinterpret_cast<float*>(xmm_reg);
@@ -108,17 +101,17 @@ SetXMMRegToNaN(bool isFloat32, T *xmm_re
     }
 }
 
 // Perform a binary search on the projected offsets of the known heap accesses
 // in the module.
 static const AsmJSHeapAccess *
 LookupHeapAccess(const AsmJSModule &module, uint8_t *pc)
 {
-    JS_ASSERT(PCIsInModule(module, pc));
+    JS_ASSERT(module.pcIsInModule(pc));
     size_t targetOffset = pc - module.functionCode();
 
     if (module.numHeapAccesses() == 0)
         return NULL;
 
     size_t low = 0;
     size_t high = module.numHeapAccesses() - 1;
     while (high - low >= 2) {
@@ -216,30 +209,30 @@ HandleException(PEXCEPTION_POINTERS exce
     if (!activation)
         return false;
 
     uint8_t **ppc = ContextToPC(context);
     uint8_t *pc = *ppc;
 	JS_ASSERT(pc == record->ExceptionAddress);
 
     const AsmJSModule &module = activation->module();
-    if (!PCIsInModule(module, pc))
+    if (!module.pcIsInModule(pc))
         return false;
 
 	if (record->NumberParameters < 2)
 		return false;
 
     void *faultingAddress = (void*)record->ExceptionInformation[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 (PCIsInModule(module, faultingAddress)) {
+    if (module.pcIsInModule(faultingAddress)) {
         activation->setResumePC(pc);
         *ppc = module.operationCallbackExit();
         DWORD oldProtect;
         if (!VirtualProtect(module.functionCode(), module.functionBytes(), PAGE_EXECUTE, &oldProtect))
             MOZ_CRASH();
         return true;
     }
 
@@ -285,16 +278,37 @@ AsmJSExceptionHandler(LPEXCEPTION_POINTE
     // No need to worry about calling other handlers, the OS does this for us.
     return EXCEPTION_CONTINUE_SEARCH;
 }
 
 # else  // If not Windows, assume Unix
 #  include <signal.h>
 #  include <sys/mman.h>
 
+#if defined(XP_MACOSX)
+// Oh boy.  We get to do fun things to make this work with the Mach
+// exception handling installed by breakpad.  Note that we still
+// use and set up the signal() infrastructure; we'll fall back to
+// that if breakpad is not active.
+#include <mach/exc.h>
+#include <mach/mig.h>
+#include <mach/thread_act.h>
+
+extern "C" {
+    // this must be visible to dlsym(), and must have this name.
+    // Breakpad will call this before doing its thing if it exists
+    // in the process.
+    boolean_t BreakpadUserExceptionHandler64(exception_type_t extype,
+                                             mach_exception_data_t code,
+                                             mach_msg_type_number_t code_count,
+                                             mach_port_t thread)
+        __attribute__((visibility("default")));
+}
+#endif
+
 // 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)
     JS_STATIC_ASSERT(sizeof(context.gregs[REG_EIP]) == sizeof(void*));
@@ -362,59 +376,70 @@ ContextToPC(mcontext_t context)
 #   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
+SetFloatStateRegisterToCoercedUndefined(x86_float_state64_t *fs, bool isFloat32, AnyRegister reg)
+{
+    switch (reg.fpu().code()) {
+      case JSC::X86Registers::xmm0:  SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm0); break;
+      case JSC::X86Registers::xmm1:  SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm1); break;
+      case JSC::X86Registers::xmm2:  SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm2); break;
+      case JSC::X86Registers::xmm3:  SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm3); break;
+      case JSC::X86Registers::xmm4:  SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm4); break;
+      case JSC::X86Registers::xmm5:  SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm5); break;
+      case JSC::X86Registers::xmm6:  SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm6); break;
+      case JSC::X86Registers::xmm7:  SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm7); break;
+      case JSC::X86Registers::xmm8:  SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm8); break;
+      case JSC::X86Registers::xmm9:  SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm9); break;
+      case JSC::X86Registers::xmm10: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm10); break;
+      case JSC::X86Registers::xmm11: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm11); break;
+      case JSC::X86Registers::xmm12: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm12); break;
+      case JSC::X86Registers::xmm13: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm13); break;
+      case JSC::X86Registers::xmm14: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm14); break;
+      case JSC::X86Registers::xmm15: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm15); break;
+      default: MOZ_CRASH();
+    }
+}
+
+static void
+SetThreadStateRegisterToCoercedUndefined(x86_thread_state64_t *ss, bool isFloat32, AnyRegister reg)
+{
+    switch (reg.gpr().code()) {
+      case JSC::X86Registers::eax: ss->__rax = 0; break;
+      case JSC::X86Registers::ecx: ss->__rcx = 0; break;
+      case JSC::X86Registers::edx: ss->__rdx = 0; break;
+      case JSC::X86Registers::ebx: ss->__rbx = 0; break;
+      case JSC::X86Registers::esp: ss->__rsp = 0; break;
+      case JSC::X86Registers::ebp: ss->__rbp = 0; break;
+      case JSC::X86Registers::esi: ss->__rsi = 0; break;
+      case JSC::X86Registers::edi: ss->__rdi = 0; break;
+      case JSC::X86Registers::r8:  ss->__r8  = 0; break;
+      case JSC::X86Registers::r9:  ss->__r9  = 0; break;
+      case JSC::X86Registers::r10: ss->__r10 = 0; break;
+      case JSC::X86Registers::r11: ss->__r11 = 0; break;
+      case JSC::X86Registers::r12: ss->__r12 = 0; break;
+      case JSC::X86Registers::r13: ss->__r13 = 0; break;
+      case JSC::X86Registers::r14: ss->__r14 = 0; break;
+      case JSC::X86Registers::r15: ss->__r15 = 0; break;
+      default: MOZ_CRASH();
+    }
+}
+
+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();
-        }
-    }
+    if (reg.isFloat())
+        SetFloatStateRegisterToCoercedUndefined(&context->__fs, isFloat32, reg);
+    else
+        SetThreadStateRegisterToCoercedUndefined(&context->__ss, isFloat32, reg);
 }
 #   endif
 #  endif  // end of OS-specific mcontext accessors
 
 // Be very cautious and default to not handling; we don't want to accidentally
 // silence real crashes from real bugs.
 static bool
 HandleSignal(int signum, siginfo_t *info, void *ctx)
@@ -423,27 +448,27 @@ HandleSignal(int signum, siginfo_t *info
     if (!activation)
         return false;
 
     mcontext_t &context = reinterpret_cast<ucontext_t*>(ctx)->uc_mcontext;
     uint8_t **ppc = ContextToPC(context);
     uint8_t *pc = *ppc;
 
     const AsmJSModule &module = activation->module();
-    if (!PCIsInModule(module, pc))
+    if (!module.pcIsInModule(pc))
         return false;
 
     void *faultingAddress = info->si_addr;
 
     // 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 (PCIsInModule(module, faultingAddress)) {
+    if (module.pcIsInModule(faultingAddress)) {
         activation->setResumePC(pc);
         *ppc = module.operationCallbackExit();
         mprotect(module.functionCode(), module.functionBytes(), PROT_EXEC);
         return true;
     }
 
 #  if defined(JS_CPU_X64)
     // These checks aren't necessary, but, since we can, check anyway to make
@@ -494,16 +519,138 @@ AsmJSFaultHandler(int signum, siginfo_t 
         exit(signum);  // backstop
     } else if (sPrevHandler.sa_handler == SIG_DFL || sPrevHandler.sa_handler == SIG_IGN) {
         sigaction(signum, &sPrevHandler, NULL);
     } else {
         sPrevHandler.sa_handler(signum);
         exit(signum);  // backstop
     }
 }
+
+#if defined(XP_MACOSX)
+
+// This is the Mach-exceptions-in-conjunction-with-breakpad handler.
+// There's an unfortunate amount of code duplicated from HandleSignal
+// above, but there are enough differences in paths that unifying
+// them is a bit of a pain (differences in how you get or set register
+// state, how you look up the AsmJS Activation, etc.)
+boolean_t
+BreakpadUserExceptionHandler64(exception_type_t extype,
+                               mach_exception_data_t code,
+                               mach_msg_type_number_t code_count,
+                               mach_port_t thread)
+{
+    // we only care about BAD_ACCESS
+    if (extype != EXC_BAD_ACCESS)
+        return false;
+
+    kern_return_t kr;
+
+# if defined(JS_CPU_X64)
+    x86_thread_state64_t state;
+    unsigned int count = x86_THREAD_STATE64_COUNT;
+    unsigned int state_flavor = x86_THREAD_STATE64;
+    uint8_t **ppc = reinterpret_cast<uint8_t**>(&state.__rip);
+#else
+    x86_thread_state32_t state;
+    unsigned int count = x86_THREAD_STATE32_COUNT;
+    unsigned int state_flavor = x86_THREAD_STATE32;
+    uint8_t **ppc = reinterpret_cast<uint8_t**>(&state.__eip);
+#endif
+
+    kr = thread_get_state(thread, state_flavor, (thread_state_t) &state, &count);
+    if (kr != KERN_SUCCESS) {
+        // couldn't get state? welp, at least we'll get a breakpad stack!
+        return false;
+    }
+
+    uint8_t *pc = *ppc;
+
+    AsmJSActivation *activation = JSRuntime::findAsmJSActivationForPC(pc);
+    if (!activation) {
+        return false;
+    }
+
+    // we know pc is in module because we found it earlier by the pc
+    const AsmJSModule &module = activation->module();
+
+    void *faultingAddress = reinterpret_cast<void*>(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.pcIsInModule(faultingAddress)) {
+        activation->setResumePC(pc);
+        *ppc = module.operationCallbackExit();
+        mprotect(module.functionCode(), module.functionBytes(), PROT_EXEC);
+
+        // count better not have been modified since we called thread_get_state above
+        kr = thread_set_state(thread, state_flavor, (thread_state_t) &state, count);
+        if (kr != KERN_SUCCESS) {
+            return false;
+        }
+        
+        return true;
+    }
+
+# 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()) {
+        AnyRegister reg = heapAccess->loadedReg();
+        if (reg.isFloat()) {
+            // We have to grab the floating point registers
+            x86_float_state64_t fstate;
+            count = x86_FLOAT_STATE64_COUNT;
+            kr = thread_get_state(thread, x86_FLOAT_STATE64, (thread_state_t) &fstate, &count);
+            if (kr != KERN_SUCCESS)
+                return false;
+
+            SetFloatStateRegisterToCoercedUndefined(&fstate, heapAccess->isFloat32Load(), reg);
+
+            kr = thread_set_state(thread, x86_FLOAT_STATE64, (thread_state_t) &fstate, x86_FLOAT_STATE64_COUNT);
+            if (kr != KERN_SUCCESS)
+                return false;
+        } else {
+            SetThreadStateRegisterToCoercedUndefined(&state, heapAccess->isFloat32Load(), reg);
+        }
+    }
+
+    *ppc += heapAccess->opLength();
+
+    kr = thread_set_state(thread, x86_THREAD_STATE64, (thread_state_t) &state, x86_THREAD_STATE64_COUNT);
+    if (kr != KERN_SUCCESS)
+        return false;
+
+    return true;
+#  else
+    return false;
+#  endif
+}
+#endif
+
 # endif
 #endif // JS_ASMJS
 
 bool
 EnsureAsmJSSignalHandlersInstalled()
 {
 #if defined(JS_ASMJS)
     SignalMutex::Lock lock;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -84,18 +84,19 @@
 #include "vm/Stack-inl.h"
 #include "vm/String-inl.h"
 
 #if ENABLE_YARR_JIT
 #include "assembler/jit/ExecutableAllocator.h"
 #include "methodjit/Logging.h"
 #endif
 
-#ifdef JS_METHODJIT
+#ifdef JS_ION
 #include "ion/Ion.h"
+#include "ion/AsmJSModule.h"
 #endif
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 using mozilla::Maybe;
 
@@ -962,25 +963,59 @@ JSRuntime::init(uint32_t maxbytes)
     if (useHelperThreads() && !sourceCompressorThread.init())
         return false;
 #endif
 
     if (!evalCache.init())
         return false;
 
     nativeStackBase = GetNativeStackBase();
+
+#ifdef XP_MACOSX
+    if (!runtimeListLock_) {
+        runtimeListLock_ = PR_NewLock();
+        runtimeListHead_ = NULL;
+    }
+
+    PR_Lock(runtimeListLock_);
+    runtimeListNext_ = runtimeListHead_;
+    runtimeListHead_ = this;
+    PR_Unlock(runtimeListLock_);
+#endif
+
     return true;
 }
 
 JSRuntime::~JSRuntime()
 {
 #ifdef JS_THREADSAFE
     clearOwnerThread();
 #endif
 
+#ifdef XP_MACOSX
+    {
+        PR_Lock(runtimeListLock_);
+        JSRuntime *r = runtimeListHead_;
+        // find ourselves to remove us from the list
+        if (r == this) {
+            runtimeListHead_ = runtimeListNext_;
+        } else {
+            // look for the node that has us in the next
+            // pointer
+            while (r && r->runtimeListNext_ != this)
+                r = r->runtimeListNext_;
+            JS_ASSERT(r);
+            r->runtimeListNext_ = runtimeListNext_;
+        }
+
+        runtimeListNext_ = NULL;
+        PR_Unlock(runtimeListLock_);
+    }
+#endif
+
     /*
      * Even though all objects in the compartment are dead, we may have keep
      * some filenames around because of gcKeepAtoms.
      */
     FreeScriptData(this);
 
 #ifdef JS_THREADSAFE
 # ifdef JS_ION
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -44,16 +44,17 @@
 #include "jsopcode.h"
 #include "jspubtd.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jsworkers.h"
 #ifdef JS_ION
 #include "ion/Ion.h"
 #include "ion/IonFrames.h"
+#include "ion/AsmJSModule.h"
 #endif
 
 #ifdef JS_METHODJIT
 # include "assembler/assembler/MacroAssembler.h"
 # include "methodjit/MethodJIT.h"
 #endif
 #include "gc/Marking.h"
 #include "js/CharacterEncoding.h"
@@ -1401,16 +1402,41 @@ JSRuntime::onOutOfMemory(void *p, size_t
       p = js_realloc(p, nbytes);
     if (p)
         return p;
     if (cx)
         js_ReportOutOfMemory(cx);
     return NULL;
 }
 
+#ifdef XP_MACOSX
+PRLock *JSRuntime::runtimeListLock_ = NULL;
+JSRuntime *JSRuntime::runtimeListHead_ = NULL;
+
+AsmJSActivation*
+JSRuntime::findAsmJSActivationForPC(void *pc)
+{
+    PR_Lock(runtimeListLock_);
+
+    AsmJSActivation *activation = NULL;
+
+    JSRuntime *r = runtimeListHead_;
+    while (r) {
+        activation = r->mainThread.asmJSActivationStackFromAnyThread();
+        if (activation && activation->module().pcIsInModule(pc))
+            break;
+        activation = NULL;
+        r = r->runtimeListNext_;
+    }
+
+    PR_Unlock(runtimeListLock_);
+    return activation;
+}
+#endif
+
 void
 JSContext::purge()
 {
     if (!activeCompilations) {
         js_delete(parseMapPool_);
         parseMapPool_ = NULL;
     }
 }
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -672,16 +672,28 @@ struct JSRuntime : js::RuntimeFriendFiel
     void                *ownerThread_;
   public:
 #else
   public:
     void abortIfWrongThread() const {}
     void assertValidThread() const {}
 #endif
 
+#ifdef XP_MACOSX
+    // On OSX, we need a way to find all the live runtimes from an arbitrary
+    // thread (specifically, the exception thread, for fixing up Odin
+    // accesses).
+  private:
+    static PRLock *runtimeListLock_;
+    static JSRuntime *runtimeListHead_;
+    JSRuntime *runtimeListNext_;
+  public:
+    static js::AsmJSActivation* findAsmJSActivationForPC(void *pc);
+#endif
+
     /* Keeper of the contiguous stack used by all contexts in this thread. */
     js::StackSpace stackSpace;
 
     /* Temporary arena pool used while compiling and decompiling. */
     static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 4 * 1024;
     js::LifoAlloc tempLifoAlloc;
 
     /*