summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/moz.configure/init.configure7
-rw-r--r--config/external/nspr/prcpucfg.h4
-rw-r--r--dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp9
-rw-r--r--dom/plugins/ipc/PluginInstanceChild.cpp4
-rw-r--r--dom/plugins/ipc/PluginInstanceChild.h2
-rw-r--r--dom/plugins/ipc/PluginInstanceParent.cpp2
-rw-r--r--dom/plugins/ipc/PluginMessageUtils.h2
-rw-r--r--gfx/2d/2D.h5
-rw-r--r--gfx/2d/Factory.cpp21
-rw-r--r--ipc/glue/SharedMemoryBasic.h4
-rw-r--r--js/src/ds/MemoryProtectionExceptionHandler.cpp466
-rw-r--r--js/src/jsmath.cpp2
-rw-r--r--js/src/jsnativestack.cpp7
-rw-r--r--js/src/jsstr.cpp2
-rw-r--r--js/src/threading/posix/Thread.cpp4
-rw-r--r--js/src/vm/Runtime.cpp6
-rw-r--r--js/src/vm/Runtime.h7
-rw-r--r--js/src/wasm/WasmSignalHandlers.cpp367
-rw-r--r--js/src/wasm/WasmSignalHandlers.h29
-rw-r--r--media/ffvpx/config.h2
-rw-r--r--media/libav/config.h2
-rw-r--r--memory/build/mozmemory.h15
-rw-r--r--memory/build/mozmemory_wrap.c2
-rw-r--r--memory/build/mozmemory_wrap.h2
-rw-r--r--memory/build/replace_malloc.c111
-rw-r--r--memory/mozalloc/mozalloc.cpp19
-rw-r--r--memory/volatile/VolatileBuffer.h4
-rw-r--r--mozglue/misc/StackWalk.cpp116
-rw-r--r--mozglue/misc/TimeStamp.h9
-rw-r--r--netwerk/base/nsSocketTransport2.cpp11
-rw-r--r--xpcom/ds/nsMathUtils.h4
-rw-r--r--xpcom/io/nsLocalFileUnix.h4
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_x86_64_unix.S15
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_arm.cpp4
34 files changed, 1240 insertions, 30 deletions
diff --git a/build/moz.configure/init.configure b/build/moz.configure/init.configure
index b2d05e795a..893da821cd 100644
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -541,6 +541,13 @@ def target_is_unix(target):
set_define('XP_UNIX', target_is_unix)
@depends(target)
+def target_is_darwin(target):
+ if target.kernel == 'Darwin':
+ return True
+
+set_define('XP_DARWIN', target_is_darwin)
+
+@depends(target)
def target_is_osx(target):
if target.kernel == 'Darwin' and target.os == 'OSX':
return True
diff --git a/config/external/nspr/prcpucfg.h b/config/external/nspr/prcpucfg.h
index 8f455b509c..8769abeeb8 100644
--- a/config/external/nspr/prcpucfg.h
+++ b/config/external/nspr/prcpucfg.h
@@ -10,7 +10,9 @@
* Need to support conditionals that are defined in both the top-level build
* system as well as NSS' build system for now.
*/
-#if defined(XP_WIN) || defined(_WINDOWS)
+#if defined(XP_DARWIN) || defined(DARWIN)
+#include "md/_darwin.cfg"
+#elif defined(XP_WIN) || defined(_WINDOWS)
#include "md/_win95.cfg"
#elif defined(__FreeBSD__)
#include "md/_freebsd.cfg"
diff --git a/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp b/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
index 513a8998b8..4671499e5d 100644
--- a/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
@@ -26,6 +26,14 @@ public:
static FFmpegLibWrapper sLibAV;
static const char* sLibs[] = {
+#if defined(XP_DARWIN)
+ "libavcodec.58.dylib",
+ "libavcodec.57.dylib",
+ "libavcodec.56.dylib",
+ "libavcodec.55.dylib",
+ "libavcodec.54.dylib",
+ "libavcodec.53.dylib",
+#else
"libavcodec.so.58",
"libavcodec-ffmpeg.so.58",
"libavcodec-ffmpeg.so.57",
@@ -35,6 +43,7 @@ static const char* sLibs[] = {
"libavcodec.so.55",
"libavcodec.so.54",
"libavcodec.so.53",
+#endif
};
/* static */ bool
diff --git a/dom/plugins/ipc/PluginInstanceChild.cpp b/dom/plugins/ipc/PluginInstanceChild.cpp
index 03f7fa1611..e96ebb1473 100644
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -137,7 +137,7 @@ PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
, mMode(aMode)
, mNames(aNames)
, mValues(aValues)
-#if defined (XP_WIN)
+#if defined(XP_DARWIN) || defined (XP_WIN)
, mContentsScaleFactor(1.0)
#endif
, mPostingKeyEvents(0)
@@ -3216,6 +3216,7 @@ PluginInstanceChild::MaybeCreatePlatformHelperSurface(void)
bool
PluginInstanceChild::EnsureCurrentBuffer(void)
{
+#ifndef XP_DARWIN
nsIntRect toInvalidate(0, 0, 0, 0);
IntSize winSize = IntSize(mWindow.width, mWindow.height);
@@ -3259,6 +3260,7 @@ PluginInstanceChild::EnsureCurrentBuffer(void)
NS_ERROR("Cannot create helper surface");
return false;
}
+#endif
return true;
}
diff --git a/dom/plugins/ipc/PluginInstanceChild.h b/dom/plugins/ipc/PluginInstanceChild.h
index 627d37c367..e07431c52a 100644
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -414,7 +414,7 @@ private:
InfallibleTArray<nsCString> mValues;
NPP_t mData;
NPWindow mWindow;
-#if defined(XP_WIN)
+#if defined(XP_DARWIN) || defined(XP_WIN)
double mContentsScaleFactor;
#endif
double mCSSZoomFactor;
diff --git a/dom/plugins/ipc/PluginInstanceParent.cpp b/dom/plugins/ipc/PluginInstanceParent.cpp
index d72c9de49b..85c95077aa 100644
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -256,6 +256,8 @@ PluginInstanceParent::AnswerNPN_GetValue_NPNVnetscapeWindow(NativeWindowHandle*
HWND id;
#elif defined(MOZ_X11)
XID id;
+#elif defined(XP_DARWIN)
+ intptr_t id;
#else
#warning Implement me
#endif
diff --git a/dom/plugins/ipc/PluginMessageUtils.h b/dom/plugins/ipc/PluginMessageUtils.h
index e06c2cee24..e04e906382 100644
--- a/dom/plugins/ipc/PluginMessageUtils.h
+++ b/dom/plugins/ipc/PluginMessageUtils.h
@@ -106,6 +106,8 @@ struct NPAudioDeviceChangeDetailsIPC
typedef HWND NativeWindowHandle;
#elif defined(MOZ_X11)
typedef XID NativeWindowHandle;
+#elif defined(XP_DARWIN)
+typedef intptr_t NativeWindowHandle; // never actually used, will always be 0
#else
#error Need NativeWindowHandle for this platform
#endif
diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h
index 89de5630c6..e2020dc9e6 100644
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1481,6 +1481,11 @@ public:
static already_AddRefed<DrawTarget> CreateDrawTargetWithSkCanvas(SkCanvas* aCanvas);
#endif
+#ifdef XP_DARWIN
+ static already_AddRefed<GlyphRenderingOptions>
+ CreateCGGlyphRenderingOptions(const Color &aFontSmoothingBackgroundColor);
+#endif
+
#ifdef WIN32
static already_AddRefed<DrawTarget> CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat);
diff --git a/gfx/2d/Factory.cpp b/gfx/2d/Factory.cpp
index 22352b20c6..5cd5d14eab 100644
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -25,6 +25,11 @@
#include "NativeFontResourceGDI.h"
#endif
+#ifdef XP_DARWIN
+#include "ScaledFontMac.h"
+#include "NativeFontResourceMac.h"
+#endif
+
#ifdef MOZ_WIDGET_GTK
#include "ScaledFontFontconfig.h"
#endif
@@ -476,6 +481,12 @@ Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSiz
}
#endif
#endif
+#ifdef XP_DARWIN
+ case NativeFontType::MAC_FONT_FACE:
+ {
+ return MakeAndAddRef<ScaledFontMac>(static_cast<CGFontRef>(aNativeFont.mFont), aSize);
+ }
+#endif
#if defined(USE_CAIRO) || defined(USE_SKIA_FREETYPE)
case NativeFontType::CAIRO_FONT_FACE:
{
@@ -513,6 +524,8 @@ Factory::CreateNativeFontResource(uint8_t *aData, uint32_t aSize,
return NativeFontResourceGDI::Create(aData, aSize,
/* aNeedsCairo = */ true);
}
+#elif XP_DARWIN
+ return NativeFontResourceMac::Create(aData, aSize);
#else
gfxWarning() << "Unable to create cairo scaled font from truetype data";
return nullptr;
@@ -789,6 +802,14 @@ Factory::CreateWrappingDataSourceSurface(uint8_t *aData,
return newSurf.forget();
}
+#ifdef XP_DARWIN
+already_AddRefed<GlyphRenderingOptions>
+Factory::CreateCGGlyphRenderingOptions(const Color &aFontSmoothingBackgroundColor)
+{
+ return MakeAndAddRef<GlyphRenderingOptionsCG>(aFontSmoothingBackgroundColor);
+}
+#endif
+
already_AddRefed<DataSourceSurface>
Factory::CreateDataSourceSurface(const IntSize &aSize,
SurfaceFormat aFormat,
diff --git a/ipc/glue/SharedMemoryBasic.h b/ipc/glue/SharedMemoryBasic.h
index 9b9668f689..d3c3e62860 100644
--- a/ipc/glue/SharedMemoryBasic.h
+++ b/ipc/glue/SharedMemoryBasic.h
@@ -6,6 +6,10 @@
#ifndef mozilla_ipc_SharedMemoryBasic_h
#define mozilla_ipc_SharedMemoryBasic_h
+#ifdef XP_DARWIN
+# include "mozilla/ipc/SharedMemoryBasic_mach.h"
+#else
# include "mozilla/ipc/SharedMemoryBasic_chromium.h"
+#endif
#endif // ifndef mozilla_ipc_SharedMemoryBasic_h
diff --git a/js/src/ds/MemoryProtectionExceptionHandler.cpp b/js/src/ds/MemoryProtectionExceptionHandler.cpp
index 881a0e63d5..e617556182 100644
--- a/js/src/ds/MemoryProtectionExceptionHandler.cpp
+++ b/js/src/ds/MemoryProtectionExceptionHandler.cpp
@@ -10,10 +10,13 @@
#if defined(XP_WIN)
# include "jswin.h"
-#elif defined(XP_UNIX)
+#elif defined(XP_UNIX) && !defined(XP_DARWIN)
# include <signal.h>
# include <sys/types.h>
# include <unistd.h>
+#elif defined(XP_DARWIN)
+# include <mach/mach.h>
+# include <unistd.h>
#endif
#include "ds/SplayTree.h"
@@ -201,7 +204,7 @@ MemoryProtectionExceptionHandler::uninstall()
}
}
-#elif defined(XP_UNIX)
+#elif defined(XP_UNIX) && !defined(XP_DARWIN)
static struct sigaction sPrevSEGVHandler = {};
@@ -282,6 +285,465 @@ MemoryProtectionExceptionHandler::uninstall()
}
}
+#elif defined(XP_DARWIN)
+
+/*
+ * The fact that we need to be able to forward to other exception handlers
+ * makes this code much more complicated. The forwarding logic and the
+ * structures required to make it work are heavily based on the code at
+ * www.ravenbrook.com/project/mps/prototype/2013-06-24/machtest/machtest/main.c
+ */
+
+/* -------------------------------------------------------------------------- */
+/* Begin Mach definitions and helper functions */
+/* -------------------------------------------------------------------------- */
+
+/*
+ * These are the message IDs associated with each exception type.
+ * We'll be using sIDRequest64, but we need the others for forwarding.
+ */
+static const mach_msg_id_t sIDRequest32 = 2401;
+static const mach_msg_id_t sIDRequestState32 = 2402;
+static const mach_msg_id_t sIDRequestStateIdentity32 = 2403;
+
+static const mach_msg_id_t sIDRequest64 = 2405;
+static const mach_msg_id_t sIDRequestState64 = 2406;
+static const mach_msg_id_t sIDRequestStateIdentity64 = 2407;
+
+/*
+ * Each message ID has an associated Mach message structure.
+ * We use the preprocessor to make defining them a little less arduous.
+ */
+# define REQUEST_HEADER_FIELDS\
+ mach_msg_header_t header;
+
+# define REQUEST_IDENTITY_FIELDS\
+ mach_msg_body_t msgh_body;\
+ mach_msg_port_descriptor_t thread;\
+ mach_msg_port_descriptor_t task;
+
+# define REQUEST_GENERAL_FIELDS(bits)\
+ NDR_record_t NDR;\
+ exception_type_t exception;\
+ mach_msg_type_number_t code_count;\
+ int##bits##_t code[2];
+
+# define REQUEST_STATE_FIELDS\
+ int flavor;\
+ mach_msg_type_number_t old_state_count;\
+ natural_t old_state[THREAD_STATE_MAX];
+
+# define REQUEST_TRAILER_FIELDS\
+ mach_msg_trailer_t trailer;
+
+# define EXCEPTION_REQUEST(bits)\
+ struct ExceptionRequest##bits\
+ {\
+ REQUEST_HEADER_FIELDS\
+ REQUEST_IDENTITY_FIELDS\
+ REQUEST_GENERAL_FIELDS(bits)\
+ REQUEST_TRAILER_FIELDS\
+ };\
+
+# define EXCEPTION_REQUEST_STATE(bits)\
+ struct ExceptionRequestState##bits\
+ {\
+ REQUEST_HEADER_FIELDS\
+ REQUEST_GENERAL_FIELDS(bits)\
+ REQUEST_STATE_FIELDS\
+ REQUEST_TRAILER_FIELDS\
+ };\
+
+# define EXCEPTION_REQUEST_STATE_IDENTITY(bits)\
+ struct ExceptionRequestStateIdentity##bits\
+ {\
+ REQUEST_HEADER_FIELDS\
+ REQUEST_IDENTITY_FIELDS\
+ REQUEST_GENERAL_FIELDS(bits)\
+ REQUEST_STATE_FIELDS\
+ REQUEST_TRAILER_FIELDS\
+ };\
+
+/* This is needed because not all fields are naturally aligned on 64-bit. */
+# ifdef __MigPackStructs
+# pragma pack(4)
+# endif
+
+EXCEPTION_REQUEST(32)
+EXCEPTION_REQUEST(64)
+EXCEPTION_REQUEST_STATE(32)
+EXCEPTION_REQUEST_STATE(64)
+EXCEPTION_REQUEST_STATE_IDENTITY(32)
+EXCEPTION_REQUEST_STATE_IDENTITY(64)
+
+/* We use this as a common base when forwarding to the previous handler. */
+union ExceptionRequestUnion {
+ mach_msg_header_t header;
+ ExceptionRequest32 r32;
+ ExceptionRequest64 r64;
+ ExceptionRequestState32 rs32;
+ ExceptionRequestState64 rs64;
+ ExceptionRequestStateIdentity32 rsi32;
+ ExceptionRequestStateIdentity64 rsi64;
+};
+
+/* This isn't really a full Mach message, but it's all we need to send. */
+struct ExceptionReply
+{
+ mach_msg_header_t header;
+ NDR_record_t NDR;
+ kern_return_t RetCode;
+};
+
+# ifdef __MigPackStructs
+# pragma pack()
+# endif
+
+# undef EXCEPTION_REQUEST_STATE_IDENTITY
+# undef EXCEPTION_REQUEST_STATE
+# undef EXCEPTION_REQUEST
+# undef REQUEST_STATE_FIELDS
+# undef REQUEST_GENERAL_FIELDS
+# undef REQUEST_IDENTITY_FIELDS
+# undef REQUEST_HEADER_FIELDS
+
+/*
+ * The exception handler we're forwarding to may not have the same behavior
+ * or thread state flavor as what we're using. These macros help populate
+ * the fields of the message we're about to send to the previous handler.
+ */
+# define COPY_REQUEST_COMMON(bits, id)\
+ dst.header = src.header;\
+ dst.header.msgh_id = id;\
+ dst.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(dst) - sizeof(dst.trailer));\
+ dst.NDR = src.NDR;\
+ dst.exception = src.exception;\
+ dst.code_count = src.code_count;\
+ dst.code[0] = int##bits##_t(src.code[0]);\
+ dst.code[1] = int##bits##_t(src.code[1]);
+
+# define COPY_REQUEST_IDENTITY\
+ dst.msgh_body = src.msgh_body;\
+ dst.thread = src.thread;\
+ dst.task = src.task;
+
+# define COPY_REQUEST_STATE(flavor, stateCount, state)\
+ mach_msg_size_t stateSize = stateCount * sizeof(natural_t);\
+ dst.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(dst) - sizeof(dst.trailer) -\
+ sizeof(dst.old_state) + stateSize);\
+ dst.flavor = flavor;\
+ dst.old_state_count = stateCount;\
+ memcpy(dst.old_state, state, stateSize);
+
+# define COPY_EXCEPTION_REQUEST(bits)\
+ static void\
+ CopyExceptionRequest##bits(ExceptionRequest64& src,\
+ ExceptionRequest##bits& dst)\
+ {\
+ COPY_REQUEST_COMMON(bits, sIDRequest##bits)\
+ COPY_REQUEST_IDENTITY\
+ }
+
+# define COPY_EXCEPTION_REQUEST_STATE(bits)\
+ static void\
+ CopyExceptionRequestState##bits(ExceptionRequest64& src,\
+ ExceptionRequestState##bits& dst,\
+ thread_state_flavor_t flavor,\
+ mach_msg_type_number_t stateCount,\
+ thread_state_t state)\
+ {\
+ COPY_REQUEST_COMMON(bits, sIDRequestState##bits)\
+ COPY_REQUEST_STATE(flavor, stateCount, state)\
+ }
+
+# define COPY_EXCEPTION_REQUEST_STATE_IDENTITY(bits)\
+ static void\
+ CopyExceptionRequestStateIdentity##bits(ExceptionRequest64& src,\
+ ExceptionRequestStateIdentity##bits& dst,\
+ thread_state_flavor_t flavor,\
+ mach_msg_type_number_t stateCount,\
+ thread_state_t state)\
+ {\
+ COPY_REQUEST_COMMON(bits, sIDRequestStateIdentity##bits)\
+ COPY_REQUEST_IDENTITY\
+ COPY_REQUEST_STATE(flavor, stateCount, state)\
+ }
+
+COPY_EXCEPTION_REQUEST(32)
+COPY_EXCEPTION_REQUEST_STATE(32)
+COPY_EXCEPTION_REQUEST_STATE_IDENTITY(32)
+COPY_EXCEPTION_REQUEST(64)
+COPY_EXCEPTION_REQUEST_STATE(64)
+COPY_EXCEPTION_REQUEST_STATE_IDENTITY(64)
+
+# undef COPY_EXCEPTION_REQUEST_STATE_IDENTITY
+# undef COPY_EXCEPTION_REQUEST_STATE
+# undef COPY_EXCEPTION_REQUEST
+# undef COPY_REQUEST_STATE
+# undef COPY_REQUEST_IDENTITY
+# undef COPY_REQUEST_COMMON
+
+/* -------------------------------------------------------------------------- */
+/* End Mach definitions and helper functions */
+/* -------------------------------------------------------------------------- */
+
+/* Every Mach exception handler is parameterized by these four properties. */
+struct MachExceptionParameters
+{
+ exception_mask_t mask;
+ mach_port_t port;
+ exception_behavior_t behavior;
+ thread_state_flavor_t flavor;
+};
+
+struct ExceptionHandlerState
+{
+ MachExceptionParameters current;
+ MachExceptionParameters previous;
+
+ /* Each Mach exception handler runs in its own thread. */
+ Thread handlerThread;
+};
+
+/* This choice of ID is arbitrary, but must not match our exception ID. */
+static const mach_msg_id_t sIDQuit = 42;
+
+static ExceptionHandlerState sMachExceptionState;
+
+/*
+ * The meat of our exception handler. This thread waits for an exception
+ * message, annotates the exception if needed, then forwards it to the
+ * previously installed handler (which will likely terminate the process).
+ */
+static void
+MachExceptionHandler()
+{
+ kern_return_t ret;
+ MachExceptionParameters& current = sMachExceptionState.current;
+ MachExceptionParameters& previous = sMachExceptionState.previous;
+
+ // We use the simplest kind of 64-bit exception message here.
+ ExceptionRequest64 request = {};
+ request.header.msgh_local_port = current.port;
+ request.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(request));
+ ret = mach_msg(&request.header, MACH_RCV_MSG, 0, request.header.msgh_size,
+ current.port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+
+ // Restore the previous handler. We're going to forward to it
+ // anyway, and if we crash while doing so we don't want to hang.
+ task_set_exception_ports(mach_task_self(), previous.mask, previous.port,
+ previous.behavior, previous.flavor);
+
+ // If we failed even receiving the message, just give up.
+ if (ret != MACH_MSG_SUCCESS)
+ MOZ_CRASH("MachExceptionHandler: mach_msg failed to receive a message!");
+
+ // Terminate the thread if we're shutting down.
+ if (request.header.msgh_id == sIDQuit)
+ return;
+
+ // The only other valid message ID is the one associated with the
+ // EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES behavior we chose.
+ if (request.header.msgh_id != sIDRequest64)
+ MOZ_CRASH("MachExceptionHandler: Unexpected Message ID!");
+
+ // Make sure we can understand the exception we received.
+ if (request.exception != EXC_BAD_ACCESS || request.code_count != 2)
+ MOZ_CRASH("MachExceptionHandler: Unexpected exception type!");
+
+ // Get the address that the offending code tried to access.
+ uintptr_t address = uintptr_t(request.code[1]);
+
+ // If the faulting address is inside one of our protected regions, we
+ // want to annotate the crash to make it stand out from the crowd.
+ if (sProtectedRegions.isProtected(address)) {
+ ReportCrashIfDebug("Hit MOZ_CRASH(Tried to access a protected region!)\n");
+ MOZ_CRASH_ANNOTATE("MOZ_CRASH(Tried to access a protected region!)");
+ }
+
+ // Forward to the previous handler which may be a debugger, the unix
+ // signal handler, the crash reporter or something else entirely.
+ if (previous.port != MACH_PORT_NULL) {
+ mach_msg_type_number_t stateCount;
+ thread_state_data_t state;
+ if ((uint32_t(previous.behavior) & ~MACH_EXCEPTION_CODES) != EXCEPTION_DEFAULT) {
+ // If the previous handler requested thread state, get it here.
+ stateCount = THREAD_STATE_MAX;
+ ret = thread_get_state(request.thread.name, previous.flavor, state, &stateCount);
+ if (ret != KERN_SUCCESS)
+ MOZ_CRASH("MachExceptionHandler: Could not get the thread state to forward!");
+ }
+
+ // Depending on the behavior of the previous handler, the forwarded
+ // exception message will have a different set of fields.
+ // Of particular note is that exception handlers that lack
+ // MACH_EXCEPTION_CODES will get 32-bit fields even on 64-bit
+ // systems. It appears that OSX simply truncates these fields.
+ ExceptionRequestUnion forward;
+ switch (uint32_t(previous.behavior)) {
+ case EXCEPTION_DEFAULT:
+ CopyExceptionRequest32(request, forward.r32);
+ break;
+ case EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES:
+ CopyExceptionRequest64(request, forward.r64);
+ break;
+ case EXCEPTION_STATE:
+ CopyExceptionRequestState32(request, forward.rs32,
+ previous.flavor, stateCount, state);
+ break;
+ case EXCEPTION_STATE | MACH_EXCEPTION_CODES:
+ CopyExceptionRequestState64(request, forward.rs64,
+ previous.flavor, stateCount, state);
+ break;
+ case EXCEPTION_STATE_IDENTITY:
+ CopyExceptionRequestStateIdentity32(request, forward.rsi32,
+ previous.flavor, stateCount, state);
+ break;
+ case EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES:
+ CopyExceptionRequestStateIdentity64(request, forward.rsi64,
+ previous.flavor, stateCount, state);
+ break;
+ default:
+ MOZ_CRASH("MachExceptionHandler: Unknown previous handler behavior!");
+ }
+
+ // Forward the generated message to the old port. The local and remote
+ // port fields *and their rights* are swapped on arrival, so we need to
+ // swap them back first.
+ forward.header.msgh_bits = (request.header.msgh_bits & ~MACH_MSGH_BITS_PORTS_MASK) |
+ MACH_MSGH_BITS(MACH_MSGH_BITS_LOCAL(request.header.msgh_bits),
+ MACH_MSGH_BITS_REMOTE(request.header.msgh_bits));
+ forward.header.msgh_local_port = forward.header.msgh_remote_port;
+ forward.header.msgh_remote_port = previous.port;
+ ret = mach_msg(&forward.header, MACH_SEND_MSG, forward.header.msgh_size, 0,
+ MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ if (ret != MACH_MSG_SUCCESS)
+ MOZ_CRASH("MachExceptionHandler: Failed to forward to the previous handler!");
+ } else {
+ // There was no previous task-level exception handler, so defer to the
+ // host level one instead. We set the return code to KERN_FAILURE to
+ // indicate that we did not handle the exception.
+ // The reply message ID is always the request ID + 100.
+ ExceptionReply reply = {};
+ reply.header.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request.header.msgh_bits), 0);
+ reply.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(reply));
+ reply.header.msgh_remote_port = request.header.msgh_remote_port;
+ reply.header.msgh_local_port = MACH_PORT_NULL;
+ reply.header.msgh_id = request.header.msgh_id + 100;
+ reply.NDR = request.NDR;
+ reply.RetCode = KERN_FAILURE;
+ ret = mach_msg(&reply.header, MACH_SEND_MSG, reply.header.msgh_size, 0,
+ MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ if (ret != MACH_MSG_SUCCESS)
+ MOZ_CRASH("MachExceptionHandler: Failed to forward to the host level!");
+ }
+}
+
+static void
+TerminateMachExceptionHandlerThread()
+{
+ // Send a simple quit message to the exception handler thread.
+ mach_msg_header_t msg;
+ msg.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+ msg.msgh_size = static_cast<mach_msg_size_t>(sizeof(msg));
+ msg.msgh_remote_port = sMachExceptionState.current.port;
+ msg.msgh_local_port = MACH_PORT_NULL;
+ msg.msgh_reserved = 0;
+ msg.msgh_id = sIDQuit;
+ kern_return_t ret = mach_msg(&msg, MACH_SEND_MSG, sizeof(msg), 0, MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+
+ if (ret == MACH_MSG_SUCCESS)
+ sMachExceptionState.handlerThread.join();
+ else
+ MOZ_CRASH("MachExceptionHandler: Handler thread failed to terminate!");
+}
+
+bool
+MemoryProtectionExceptionHandler::install()
+{
+ MOZ_ASSERT(!sExceptionHandlerInstalled);
+
+ // If the exception handler is disabled, report success anyway.
+ if (MemoryProtectionExceptionHandler::isDisabled())
+ return true;
+
+ kern_return_t ret;
+ mach_port_t task = mach_task_self();
+
+ // Allocate a new exception port with receive rights.
+ sMachExceptionState.current = {};
+ MachExceptionParameters& current = sMachExceptionState.current;
+ ret = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &current.port);
+ if (ret != KERN_SUCCESS)
+ return false;
+
+ // Give the new port send rights as well.
+ ret = mach_port_insert_right(task, current.port, current.port, MACH_MSG_TYPE_MAKE_SEND);
+ if (ret != KERN_SUCCESS) {
+ mach_port_deallocate(task, current.port);
+ current = {};
+ return false;
+ }
+
+ // Start the thread that will receive the messages from our exception port.
+ if (!sMachExceptionState.handlerThread.init(MachExceptionHandler)) {
+ mach_port_deallocate(task, current.port);
+ current = {};
+ return false;
+ }
+
+ // Set the other properties of our new exception handler.
+ current.mask = EXC_MASK_BAD_ACCESS;
+ current.behavior = exception_behavior_t(EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES);
+ current.flavor = THREAD_STATE_NONE;
+
+ // Tell the task to use our exception handler, and save the previous one.
+ sMachExceptionState.previous = {};
+ MachExceptionParameters& previous = sMachExceptionState.previous;
+ mach_msg_type_number_t previousCount = 1;
+ ret = task_swap_exception_ports(task, current.mask, current.port, current.behavior,
+ current.flavor, &previous.mask, &previousCount,
+ &previous.port, &previous.behavior, &previous.flavor);
+ if (ret != KERN_SUCCESS) {
+ TerminateMachExceptionHandlerThread();
+ mach_port_deallocate(task, current.port);
+ previous = {};
+ current = {};
+ return false;
+ }
+
+ // We should have info on the previous exception handler, even if it's null.
+ MOZ_ASSERT(previousCount == 1);
+
+ sExceptionHandlerInstalled = true;
+ return sExceptionHandlerInstalled;
+}
+
+void
+MemoryProtectionExceptionHandler::uninstall()
+{
+ if (sExceptionHandlerInstalled) {
+ mach_port_t task = mach_task_self();
+
+ // Restore the previous exception handler.
+ MachExceptionParameters& previous = sMachExceptionState.previous;
+ task_set_exception_ports(task, previous.mask, previous.port,
+ previous.behavior, previous.flavor);
+
+ TerminateMachExceptionHandlerThread();
+
+ // Release the Mach IPC port we used.
+ mach_port_deallocate(task, sMachExceptionState.current.port);
+
+ sMachExceptionState.current = {};
+ sMachExceptionState.previous = {};
+
+ sExceptionHandlerInstalled = false;
+ }
+}
+
#else
#error "This platform is not supported!"
diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp
index 64c2da8122..75ab8fcb29 100644
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -49,7 +49,7 @@
# undef SystemFunction036
#endif
-#if defined(__DragonFly__) || \
+#if defined(XP_DARWIN) || defined(__DragonFly__) || \
defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
# include <stdlib.h>
# define HAVE_ARC4RANDOM
diff --git a/js/src/jsnativestack.cpp b/js/src/jsnativestack.cpp
index 34131f9667..42c84b2a42 100644
--- a/js/src/jsnativestack.cpp
+++ b/js/src/jsnativestack.cpp
@@ -8,7 +8,7 @@
#ifdef XP_WIN
# include "jswin.h"
-#elif defined(XP_UNIX)
+#elif defined(XP_DARWIN) || defined(DARWIN) || defined(XP_UNIX)
# include <pthread.h>
# if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
@@ -127,6 +127,10 @@ void*
js::GetNativeStackBaseImpl()
{
pthread_t thread = pthread_self();
+# if defined(XP_DARWIN) || defined(DARWIN)
+ return pthread_get_stackaddr_np(thread);
+
+# else
pthread_attr_t sattr;
pthread_attr_init(&sattr);
# if defined(__OpenBSD__)
@@ -166,6 +170,7 @@ js::GetNativeStackBaseImpl()
# else
return static_cast<char*>(stackBase) + stackSize;
# endif
+# endif
}
#endif /* !XP_WIN */
diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp
index b44fbdebca..6eb11b1cc1 100644
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1177,7 +1177,7 @@ FirstCharMatcher8bit(const char* text, uint32_t n, const char pat)
static const char16_t*
FirstCharMatcher16bit(const char16_t* text, uint32_t n, const char16_t pat)
{
-#if defined(XP_WIN)
+#if defined(XP_DARWIN) || defined(XP_WIN)
/*
* Performance of memchr is horrible in OSX. Windows is better,
* but it is still better to use UnrolledMatcher.
diff --git a/js/src/threading/posix/Thread.cpp b/js/src/threading/posix/Thread.cpp
index a5c2d2eacb..faca05c24d 100644
--- a/js/src/threading/posix/Thread.cpp
+++ b/js/src/threading/posix/Thread.cpp
@@ -151,7 +151,9 @@ js::ThisThread::SetName(const char* name)
#endif
int rv;
-#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+#ifdef XP_DARWIN
+ rv = pthread_setname_np(name);
+#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
pthread_set_name_np(pthread_self(), name);
rv = 0;
#elif defined(__NetBSD__)
diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp
index 23c50814fc..251c8258cf 100644
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -10,12 +10,14 @@
#include "mozilla/ThreadLocal.h"
#include "mozilla/Unused.h"
-#if defined(XP_UNIX)
+#if defined(XP_DARWIN)
+#include <mach/mach.h>
+#elif defined(XP_UNIX)
#include <sys/resource.h>
#elif defined(XP_WIN)
#include <processthreadsapi.h>
#include <windows.h>
-#endif // defined(XP_UNIX) || defined(XP_WIN)
+#endif // defined(XP_DARWIN) || defined(XP_UNIX) || defined(XP_WIN)
#include <locale.h>
#include <string.h>
diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h
index eefc90cb4c..f1f2e07094 100644
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -21,6 +21,9 @@
#include "jsclist.h"
#include "jsscript.h"
+#ifdef XP_DARWIN
+# include "wasm/WasmSignalHandlers.h"
+#endif
#include "builtin/AtomicsObject.h"
#include "builtin/Intl.h"
#include "builtin/Promise.h"
@@ -929,6 +932,10 @@ struct JSRuntime : public JS::shadow::Runtime,
*/
JSCList onNewGlobalObjectWatchers;
+#if defined(XP_DARWIN)
+ js::wasm::MachExceptionHandler wasmMachExceptionHandler;
+#endif
+
private:
js::FreeOp* defaultFreeOp_;
diff --git a/js/src/wasm/WasmSignalHandlers.cpp b/js/src/wasm/WasmSignalHandlers.cpp
index dae590acba..7699b336cc 100644
--- a/js/src/wasm/WasmSignalHandlers.cpp
+++ b/js/src/wasm/WasmSignalHandlers.cpp
@@ -1,7 +1,6 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* Copyright 2014 Mozilla Foundation
- * Copyright 2021 Moonchild Productions
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -229,6 +228,10 @@ class AutoSetHandlingSegFault
# define EPC_sig(p) ((p)->uc_mcontext.mc_pc)
# define RFP_sig(p) ((p)->uc_mcontext.mc_regs[30])
# endif
+#elif defined(XP_DARWIN)
+# define EIP_sig(p) ((p)->uc_mcontext->__ss.__eip)
+# define RIP_sig(p) ((p)->uc_mcontext->__ss.__rip)
+# 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
@@ -258,8 +261,34 @@ class AutoSetHandlingSegFault
#endif
// Define a context type for use in the emulator code. This is usually just
-// the same as CONTEXT.
+// the same as CONTEXT, but on Mac we use a different structure since we call
+// into the emulator code from a Mach exception handler rather than a
+// sigaction-style signal handler.
+#if defined(XP_DARWIN)
+# if defined(JS_CPU_X64)
+struct macos_x64_context {
+ x86_thread_state64_t thread;
+ x86_float_state64_t float_;
+};
+# define EMULATOR_CONTEXT macos_x64_context
+# elif defined(JS_CPU_X86)
+struct macos_x86_context {
+ x86_thread_state_t thread;
+ x86_float_state_t float_;
+};
+# define EMULATOR_CONTEXT macos_x86_context
+# elif defined(JS_CPU_ARM)
+struct macos_arm_context {
+ arm_thread_state_t thread;
+ arm_neon_state_t float_;
+};
+# define EMULATOR_CONTEXT macos_arm_context
+# else
+# error Unsupported architecture
+# endif
+#else
# define EMULATOR_CONTEXT CONTEXT
+#endif
#if defined(JS_CPU_X64)
# define PC_sig(p) RIP_sig(p)
@@ -351,6 +380,7 @@ StoreValueFromGPImm(SharedMem<void*> addr, size_t size, int32_t imm)
AtomicOperations::memcpySafeWhenRacy(addr, static_cast<void*>(&imm), size);
}
+# if !defined(XP_DARWIN)
MOZ_COLD static void*
AddressOfFPRegisterSlot(CONTEXT* context, FloatRegisters::Encoding encoding)
{
@@ -400,6 +430,57 @@ AddressOfGPRegisterSlot(EMULATOR_CONTEXT* context, Registers::Code code)
}
MOZ_CRASH();
}
+# else
+MOZ_COLD static void*
+AddressOfFPRegisterSlot(EMULATOR_CONTEXT* context, FloatRegisters::Encoding encoding)
+{
+ switch (encoding) {
+ case X86Encoding::xmm0: return &context->float_.__fpu_xmm0;
+ case X86Encoding::xmm1: return &context->float_.__fpu_xmm1;
+ case X86Encoding::xmm2: return &context->float_.__fpu_xmm2;
+ case X86Encoding::xmm3: return &context->float_.__fpu_xmm3;
+ case X86Encoding::xmm4: return &context->float_.__fpu_xmm4;
+ case X86Encoding::xmm5: return &context->float_.__fpu_xmm5;
+ case X86Encoding::xmm6: return &context->float_.__fpu_xmm6;
+ case X86Encoding::xmm7: return &context->float_.__fpu_xmm7;
+ case X86Encoding::xmm8: return &context->float_.__fpu_xmm8;
+ case X86Encoding::xmm9: return &context->float_.__fpu_xmm9;
+ case X86Encoding::xmm10: return &context->float_.__fpu_xmm10;
+ case X86Encoding::xmm11: return &context->float_.__fpu_xmm11;
+ case X86Encoding::xmm12: return &context->float_.__fpu_xmm12;
+ case X86Encoding::xmm13: return &context->float_.__fpu_xmm13;
+ case X86Encoding::xmm14: return &context->float_.__fpu_xmm14;
+ case X86Encoding::xmm15: return &context->float_.__fpu_xmm15;
+ default: break;
+ }
+ MOZ_CRASH();
+}
+
+MOZ_COLD static void*
+AddressOfGPRegisterSlot(EMULATOR_CONTEXT* context, Registers::Code code)
+{
+ switch (code) {
+ case X86Encoding::rax: return &context->thread.__rax;
+ case X86Encoding::rcx: return &context->thread.__rcx;
+ case X86Encoding::rdx: return &context->thread.__rdx;
+ case X86Encoding::rbx: return &context->thread.__rbx;
+ case X86Encoding::rsp: return &context->thread.__rsp;
+ case X86Encoding::rbp: return &context->thread.__rbp;
+ case X86Encoding::rsi: return &context->thread.__rsi;
+ case X86Encoding::rdi: return &context->thread.__rdi;
+ case X86Encoding::r8: return &context->thread.__r8;
+ case X86Encoding::r9: return &context->thread.__r9;
+ case X86Encoding::r10: return &context->thread.__r10;
+ case X86Encoding::r11: return &context->thread.__r11;
+ case X86Encoding::r12: return &context->thread.__r12;
+ case X86Encoding::r13: return &context->thread.__r13;
+ case X86Encoding::r14: return &context->thread.__r14;
+ case X86Encoding::r15: return &context->thread.__r15;
+ default: break;
+ }
+ MOZ_CRASH();
+}
+# endif // !XP_DARWIN
MOZ_COLD static void
SetRegisterToCoercedUndefined(EMULATOR_CONTEXT* context, size_t size,
@@ -705,7 +786,276 @@ WasmFaultHandler(LPEXCEPTION_POINTERS exception)
return EXCEPTION_CONTINUE_SEARCH;
}
-#else // If not Windows, assume Unix-like
+#elif defined(XP_DARWIN)
+# include <mach/exc.h>
+
+static uint8_t**
+ContextToPC(EMULATOR_CONTEXT* context)
+{
+# if defined(JS_CPU_X64)
+ 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(JS_CPU_X86)
+ 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(JS_CPU_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)
+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)
+{
+ // Don't allow recursive handling of signals, see AutoSetHandlingSegFault.
+ if (rt->handlingSegFault)
+ return false;
+ AutoSetHandlingSegFault handling(rt);
+
+ // 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.
+ EMULATOR_CONTEXT context;
+# if defined(JS_CPU_X64)
+ 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(JS_CPU_X86)
+ 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(JS_CPU_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(rtThread, thread_state,
+ (thread_state_t)&context.thread, &thread_state_count);
+ if (kret != KERN_SUCCESS)
+ return false;
+ kret = thread_get_state(rtThread, float_state,
+ (thread_state_t)&context.float_, &float_state_count);
+ if (kret != KERN_SUCCESS)
+ return false;
+
+ uint8_t** ppc = ContextToPC(&context);
+ uint8_t* pc = *ppc;
+
+ if (request.body.exception != EXC_BAD_ACCESS || request.body.codeCnt != 2)
+ return false;
+
+ WasmActivation* activation = rt->wasmActivationStack();
+ if (!activation)
+ return false;
+
+ const Instance* instance = activation->compartment()->wasm.lookupInstanceDeprecated(pc);
+ if (!instance || !instance->codeSegment().containsFunctionPC(pc))
+ return false;
+
+ uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(request.body.code[1]);
+
+ // This check isn't necessary, but, since we can, check anyway to make
+ // sure we aren't covering up a real bug.
+ if (!IsHeapAccessAddress(*instance, faultingAddress))
+ return false;
+
+ HandleMemoryAccess(&context, pc, faultingAddress, *instance, ppc);
+
+ // Update the thread state with the new pc and register values.
+ kret = thread_set_state(rtThread, float_state, (thread_state_t)&context.float_, float_state_count);
+ if (kret != KERN_SUCCESS)
+ return false;
+ kret = thread_set_state(rtThread, thread_state, (thread_state_t)&context.thread, thread_state_count);
+ if (kret != KERN_SUCCESS)
+ return false;
+
+ return true;
+}
+
+// Taken from mach_exc in /usr/include/mach/mach_exc.defs.
+static const mach_msg_id_t sExceptionId = 2405;
+
+// The choice of id here is arbitrary, the only constraint is that sQuitId != sExceptionId.
+static const mach_msg_id_t sQuitId = 42;
+
+static void
+MachExceptionHandlerThread(JSRuntime* rt)
+{
+ mach_port_t port = rt->wasmMachExceptionHandler.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, "MachExceptionHandlerThread: mach_msg failed with %d\n", (int)kret);
+ MOZ_CRASH();
+ }
+
+ // There are only two messages we should be receiving: an exception
+ // message that occurs when the runtime's thread faults and the quit
+ // message sent when the runtime is shutting down.
+ if (request.body.Head.msgh_id == sQuitId)
+ break;
+ if (request.body.Head.msgh_id != sExceptionId) {
+ fprintf(stderr, "Unexpected msg header id %d\n", (int)request.body.Head.msgh_bits);
+ 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);
+ }
+}
+
+MachExceptionHandler::MachExceptionHandler()
+ : installed_(false),
+ thread_(),
+ port_(MACH_PORT_NULL)
+{}
+
+void
+MachExceptionHandler::uninstall()
+{
+ if (installed_) {
+ thread_port_t thread = mach_thread_self();
+ kern_return_t kret = thread_set_exception_ports(thread,
+ EXC_MASK_BAD_ACCESS,
+ MACH_PORT_NULL,
+ EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
+ THREAD_STATE_NONE);
+ mach_port_deallocate(mach_task_self(), thread);
+ if (kret != KERN_SUCCESS)
+ MOZ_CRASH();
+ installed_ = false;
+ }
+ if (thread_.joinable()) {
+ // Break the handler thread out of the mach_msg loop.
+ mach_msg_header_t msg;
+ msg.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+ msg.msgh_size = sizeof(msg);
+ msg.msgh_remote_port = port_;
+ msg.msgh_local_port = MACH_PORT_NULL;
+ msg.msgh_reserved = 0;
+ msg.msgh_id = sQuitId;
+ kern_return_t kret = mach_msg(&msg, MACH_SEND_MSG, sizeof(msg), 0, MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ if (kret != KERN_SUCCESS) {
+ fprintf(stderr, "MachExceptionHandler: failed to send quit message: %d\n", (int)kret);
+ MOZ_CRASH();
+ }
+
+ // Wait for the handler thread to complete before deallocating the port.
+ thread_.join();
+ }
+ if (port_ != MACH_PORT_NULL) {
+ DebugOnly<kern_return_t> kret = mach_port_destroy(mach_task_self(), port_);
+ MOZ_ASSERT(kret == KERN_SUCCESS);
+ port_ = MACH_PORT_NULL;
+ }
+}
+
+bool
+MachExceptionHandler::install(JSRuntime* rt)
+{
+ MOZ_ASSERT(!installed());
+ kern_return_t kret;
+ mach_port_t thread;
+
+ // 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_.
+ if (!thread_.init(MachExceptionHandlerThread, 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.
+ thread = mach_thread_self();
+ kret = thread_set_exception_ports(thread,
+ EXC_MASK_BAD_ACCESS,
+ port_,
+ EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
+ THREAD_STATE_NONE);
+ mach_port_deallocate(mach_task_self(), thread);
+ if (kret != KERN_SUCCESS)
+ goto error;
+
+ installed_ = true;
+ return true;
+
+ error:
+ uninstall();
+ return false;
+}
+
+#else // If not Windows or Mac, assume Unix
enum class Signal {
SegFault,
@@ -809,7 +1159,7 @@ WasmFaultHandler(int signum, siginfo_t* info, void* context)
else
previousSignal->sa_handler(signum);
}
-# endif // XP_WIN || assume Unix-like
+# endif // XP_WIN || XP_DARWIN || assume unix
static void
RedirectIonBackedgesToInterruptCheck(JSRuntime* rt)
@@ -918,6 +1268,9 @@ ProcessHasSignalHandlers()
# if defined(XP_WIN)
if (!AddVectoredExceptionHandler(/* FirstHandler = */ true, WasmFaultHandler))
return false;
+# elif defined(XP_DARWIN)
+ // OSX handles seg faults via the Mach exception handler above, so don't
+ // install WasmFaultHandler.
# else
// 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
@@ -953,6 +1306,12 @@ wasm::EnsureSignalHandlers(JSRuntime* rt)
if (!ProcessHasSignalHandlers())
return true;
+#if defined(XP_DARWIN)
+ // On OSX, each JSRuntime gets its own handler thread.
+ if (!rt->wasmMachExceptionHandler.installed() && !rt->wasmMachExceptionHandler.install(rt))
+ return false;
+#endif
+
return true;
}
diff --git a/js/src/wasm/WasmSignalHandlers.h b/js/src/wasm/WasmSignalHandlers.h
index 7fe3dd86e6..87faa15025 100644
--- a/js/src/wasm/WasmSignalHandlers.h
+++ b/js/src/wasm/WasmSignalHandlers.h
@@ -1,7 +1,6 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* Copyright 2014 Mozilla Foundation
- * Copyright 2021 Moonchild Productions
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +19,10 @@
#define wasm_signal_handlers_h
#include "mozilla/Attributes.h"
+
+#if defined(XP_DARWIN)
+# include <mach/mach.h>
+#endif
#include "threading/Thread.h"
struct JSRuntime;
@@ -43,6 +46,30 @@ EnsureSignalHandlers(JSRuntime* rt);
bool
HaveSignalHandlers();
+#if defined(XP_DARWIN)
+// On OSX we are forced to use the lower-level Mach exception mechanism instead
+// of Unix signals. Mach exceptions are not handled on the victim's stack but
+// rather require an extra thread. For simplicity, we create one such thread
+// per JSRuntime (upon the first use of asm.js in the JSRuntime). This thread
+// and related resources are owned by AsmJSMachExceptionHandler which is owned
+// by JSRuntime.
+class MachExceptionHandler
+{
+ bool installed_;
+ js::Thread thread_;
+ mach_port_t port_;
+
+ void uninstall();
+
+ public:
+ MachExceptionHandler();
+ ~MachExceptionHandler() { uninstall(); }
+ mach_port_t port() const { return port_; }
+ bool installed() const { return installed_; }
+ bool install(JSRuntime* rt);
+};
+#endif
+
// Test whether the given PC is within the innermost wasm activation. Return
// false if it is not, or it cannot be determined.
bool IsPCInWasmCode(void *pc);
diff --git a/media/ffvpx/config.h b/media/ffvpx/config.h
index f43da8f284..db2f7b42e1 100644
--- a/media/ffvpx/config.h
+++ b/media/ffvpx/config.h
@@ -26,6 +26,8 @@
#undef HAVE_LIBC_MSVCRT
#define HAVE_LIBC_MSVCRT 0
#endif
+#elif defined(XP_DARWIN)
+#include "config_darwin64.h"
#elif defined(XP_UNIX)
#if defined(HAVE_64BIT_BUILD)
#include "config_unix64.h"
diff --git a/media/libav/config.h b/media/libav/config.h
index 4abd411041..96c5ea9506 100644
--- a/media/libav/config.h
+++ b/media/libav/config.h
@@ -17,6 +17,8 @@
#define MOZ_LIBAV_CONFIG_H
#if defined(XP_WIN)
#include "config_win.h"
+#elif defined(XP_DARWIN)
+#include "config_darwin.h"
#elif defined(XP_UNIX)
#include "config_unix.h"
#endif
diff --git a/memory/build/mozmemory.h b/memory/build/mozmemory.h
index 2ed63b9e1f..84007fffb2 100644
--- a/memory/build/mozmemory.h
+++ b/memory/build/mozmemory.h
@@ -25,6 +25,13 @@
MOZ_BEGIN_EXTERN_C
+/*
+ * On OSX, malloc/malloc.h contains the declaration for malloc_good_size,
+ * which will call back in jemalloc, through the zone allocator so just use it.
+ */
+#ifdef XP_DARWIN
+# include <malloc/malloc.h>
+#else
MOZ_MEMORY_API size_t malloc_good_size_impl(size_t size);
/* Note: the MOZ_GLUE_IN_PROGRAM ifdef below is there to avoid -Werror turning
@@ -32,15 +39,15 @@ MOZ_MEMORY_API size_t malloc_good_size_impl(size_t size);
* to use weak imports. */
static inline size_t _malloc_good_size(size_t size) {
-#if defined(MOZ_GLUE_IN_PROGRAM) && !defined(IMPL_MFBT)
+# if defined(MOZ_GLUE_IN_PROGRAM) && !defined(IMPL_MFBT)
if (!malloc_good_size)
return size;
-#endif
+# endif
return malloc_good_size_impl(size);
}
-#define malloc_good_size _malloc_good_size
-
+# define malloc_good_size _malloc_good_size
+#endif
MOZ_JEMALLOC_API void jemalloc_stats(jemalloc_stats_t *stats);
diff --git a/memory/build/mozmemory_wrap.c b/memory/build/mozmemory_wrap.c
index 409b39da23..fdb8447d33 100644
--- a/memory/build/mozmemory_wrap.c
+++ b/memory/build/mozmemory_wrap.c
@@ -68,6 +68,7 @@ mozmem_malloc_impl(_ZdaPvRKSt9nothrow_t)(void *ptr)
#undef strndup
#undef strdup
+#ifndef XP_DARWIN
MOZ_MEMORY_API char *
strndup_impl(const char *src, size_t len)
{
@@ -85,6 +86,7 @@ strdup_impl(const char *src)
size_t len = strlen(src);
return strndup_impl(src, len);
}
+#endif /* XP_DARWIN */
#ifdef XP_WIN
/*
diff --git a/memory/build/mozmemory_wrap.h b/memory/build/mozmemory_wrap.h
index aa305588d4..da4fd27bb1 100644
--- a/memory/build/mozmemory_wrap.h
+++ b/memory/build/mozmemory_wrap.h
@@ -112,7 +112,7 @@
# define mozmem_jemalloc_impl(a) je_ ## a
# else
# define MOZ_JEMALLOC_API MFBT_API
-# if defined(XP_WIN)
+# if (defined(XP_WIN) || defined(XP_DARWIN))
# if defined(MOZ_REPLACE_MALLOC)
# define mozmem_malloc_impl(a) a ## _impl
# else
diff --git a/memory/build/replace_malloc.c b/memory/build/replace_malloc.c
index 135b566630..91f86497c5 100644
--- a/memory/build/replace_malloc.c
+++ b/memory/build/replace_malloc.c
@@ -26,7 +26,9 @@
* function resolved with GetProcAddress() instead of weak definitions
* of functions.
*/
-#if defined(XP_WIN)
+#ifdef XP_DARWIN
+# define MOZ_REPLACE_WEAK __attribute__((weak_import))
+#elif defined(XP_WIN)
# define MOZ_NO_REPLACE_FUNC_DECL
#elif defined(__GNUC__)
# define MOZ_REPLACE_WEAK __attribute__((weak))
@@ -282,6 +284,111 @@ MOZ_MEMORY_API __memalign_hook_type __memalign_hook = memalign_impl;
* owned by the allocator.
*/
+#ifdef XP_DARWIN
+#include <stdlib.h>
+#include <malloc/malloc.h>
+#include "mozilla/Assertions.h"
+
+static size_t
+zone_size(malloc_zone_t *zone, void *ptr)
+{
+ return malloc_usable_size_impl(ptr);
+}
+
+static void *
+zone_malloc(malloc_zone_t *zone, size_t size)
+{
+ return malloc_impl(size);
+}
+
+static void *
+zone_calloc(malloc_zone_t *zone, size_t num, size_t size)
+{
+ return calloc_impl(num, size);
+}
+
+static void *
+zone_realloc(malloc_zone_t *zone, void *ptr, size_t size)
+{
+ if (malloc_usable_size_impl(ptr))
+ return realloc_impl(ptr, size);
+ return realloc(ptr, size);
+}
+
+static void
+zone_free(malloc_zone_t *zone, void *ptr)
+{
+ if (malloc_usable_size_impl(ptr)) {
+ free_impl(ptr);
+ return;
+ }
+ free(ptr);
+}
+
+static void
+zone_free_definite_size(malloc_zone_t *zone, void *ptr, size_t size)
+{
+ size_t current_size = malloc_usable_size_impl(ptr);
+ if (current_size) {
+ MOZ_ASSERT(current_size == size);
+ free_impl(ptr);
+ return;
+ }
+ free(ptr);
+}
+
+static void *
+zone_memalign(malloc_zone_t *zone, size_t alignment, size_t size)
+{
+ void *ptr;
+ if (posix_memalign_impl(&ptr, alignment, size) == 0)
+ return ptr;
+ return NULL;
+}
+
+static void *
+zone_valloc(malloc_zone_t *zone, size_t size)
+{
+ return valloc_impl(size);
+}
+
+static void *
+zone_destroy(malloc_zone_t *zone)
+{
+ /* This function should never be called. */
+ MOZ_CRASH();
+}
+
+static size_t
+zone_good_size(malloc_zone_t *zone, size_t size)
+{
+ return malloc_good_size_impl(size);
+}
+
+#ifdef MOZ_JEMALLOC
+
+#include "jemalloc/internal/jemalloc_internal.h"
+
+static void
+zone_force_lock(malloc_zone_t *zone)
+{
+ /* /!\ This calls into jemalloc. It works because we're linked in the
+ * same library. Stolen from jemalloc's zone.c. */
+ if (isthreaded)
+ jemalloc_prefork();
+}
+
+static void
+zone_force_unlock(malloc_zone_t *zone)
+{
+ /* /!\ This calls into jemalloc. It works because we're linked in the
+ * same library. Stolen from jemalloc's zone.c. */
+ if (isthreaded)
+ jemalloc_postfork_parent();
+}
+
+#else
+
#define JEMALLOC_ZONE_VERSION 6
/* Empty implementations are needed, because fork() calls zone->force_(un)lock
@@ -296,7 +403,7 @@ zone_force_unlock(malloc_zone_t *zone)
{
}
-/* --- */
+#endif
static malloc_zone_t zone;
static struct malloc_introspection_t zone_introspect;
diff --git a/memory/mozalloc/mozalloc.cpp b/memory/mozalloc/mozalloc.cpp
index 1ae071ea04..55d36d80be 100644
--- a/memory/mozalloc/mozalloc.cpp
+++ b/memory/mozalloc/mozalloc.cpp
@@ -19,6 +19,10 @@
#define MOZ_MEMORY_IMPL
#include "mozmemory_wrap.h"
+#if defined(XP_DARWIN)
+#include <malloc/malloc.h> // for malloc_size
+#endif
+
// See mozmemory_wrap.h for more details. This file is part of libmozglue, so
// it needs to use _impl suffixes. However, with libmozglue growing, this is
// becoming cumbersome, so we will likely use a malloc.h wrapper of some sort
@@ -149,6 +153,17 @@ moz_posix_memalign(void **ptr, size_t alignment, size_t size)
if (code)
return code;
+#if defined(XP_DARWIN)
+ // Workaround faulty OSX posix_memalign, which provides memory with the
+ // incorrect alignment sometimes, but returns 0 as if nothing was wrong.
+ size_t mask = alignment - 1;
+ if (((size_t)(*ptr) & mask) != 0) {
+ void* old = *ptr;
+ code = moz_posix_memalign(ptr, alignment, size);
+ free(old);
+ }
+#endif
+
return code;
}
@@ -188,7 +203,9 @@ moz_malloc_usable_size(void *ptr)
if (!ptr)
return 0;
-#if defined(HAVE_MALLOC_USABLE_SIZE) || defined(MOZ_MEMORY)
+#if defined(XP_DARWIN)
+ return malloc_size(ptr);
+#elif defined(HAVE_MALLOC_USABLE_SIZE) || defined(MOZ_MEMORY)
return malloc_usable_size_impl(ptr);
#elif defined(XP_WIN)
return _msize(ptr);
diff --git a/memory/volatile/VolatileBuffer.h b/memory/volatile/VolatileBuffer.h
index 19aeb9df2b..b6bcdfcb74 100644
--- a/memory/volatile/VolatileBuffer.h
+++ b/memory/volatile/VolatileBuffer.h
@@ -76,7 +76,9 @@ private:
void* mBuf;
size_t mSize;
int mLockCount;
-#if defined(XP_WIN)
+#ifdef XP_DARWIN
+ bool mHeap;
+#elif defined(XP_WIN)
bool mHeap;
bool mFirstLock;
#endif
diff --git a/mozglue/misc/StackWalk.cpp b/mozglue/misc/StackWalk.cpp
index 7d853b075d..8925ae9488 100644
--- a/mozglue/misc/StackWalk.cpp
+++ b/mozglue/misc/StackWalk.cpp
@@ -29,10 +29,17 @@ static CriticalAddress gCriticalAddress;
#define _GNU_SOURCE
#endif
-#if defined(HAVE_DLOPEN)
+#if defined(HAVE_DLOPEN) || defined(XP_DARWIN)
#include <dlfcn.h>
#endif
+#if (defined(XP_DARWIN) && \
+ (defined(__i386) || defined(__ppc__) || defined(HAVE__UNWIND_BACKTRACE)))
+#define MOZ_STACKWALK_SUPPORTS_MACOSX 1
+#else
+#define MOZ_STACKWALK_SUPPORTS_MACOSX 0
+#endif
+
#if (defined(linux) && \
((defined(__GNUC__) && (defined(__i386) || defined(PPC))) || \
defined(HAVE__UNWIND_BACKTRACE)))
@@ -51,18 +58,121 @@ static CriticalAddress gCriticalAddress;
extern MOZ_EXPORT void* __libc_stack_end; // from ld-linux.so
#endif
+#if MOZ_STACKWALK_SUPPORTS_MACOSX
+#include <pthread.h>
+#include <sys/errno.h>
+#ifdef MOZ_WIDGET_COCOA
+#include <CoreServices/CoreServices.h>
+#endif
+
+typedef void
+malloc_logger_t(uint32_t aType,
+ uintptr_t aArg1, uintptr_t aArg2, uintptr_t aArg3,
+ uintptr_t aResult, uint32_t aNumHotFramesToSkip);
+extern malloc_logger_t* malloc_logger;
+
+static void
+stack_callback(uint32_t aFrameNumber, void* aPc, void* aSp, void* aClosure)
+{
+ const char* name = static_cast<char*>(aClosure);
+ Dl_info info;
+
+ // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
+ // stack shows up as having two pthread_cond_wait$UNIX2003 frames. The
+ // correct one is the first that we find on our way up, so the
+ // following check for gCriticalAddress.mAddr is critical.
+ if (gCriticalAddress.mAddr || dladdr(aPc, &info) == 0 ||
+ !info.dli_sname || strcmp(info.dli_sname, name) != 0) {
+ return;
+ }
+ gCriticalAddress.mAddr = aPc;
+}
+
+static void
+my_malloc_logger(uint32_t aType,
+ uintptr_t aArg1, uintptr_t aArg2, uintptr_t aArg3,
+ uintptr_t aResult, uint32_t aNumHotFramesToSkip)
+{
+ static bool once = false;
+ if (once) {
+ return;
+ }
+ once = true;
+
+ // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
+ // stack shows up as having two pthread_cond_wait$UNIX2003 frames.
+ const char* name = "new_sem_from_pool";
+ MozStackWalk(stack_callback, /* skipFrames */ 0, /* maxFrames */ 0,
+ const_cast<char*>(name), 0, nullptr);
+}
+
+// This is called from NS_LogInit() and from the stack walking functions, but
+// only the first call has any effect. We need to call this function from both
+// places because it must run before any mutexes are created, and also before
+// any objects whose refcounts we're logging are created. Running this
+// function during NS_LogInit() ensures that we meet the first criterion, and
+// running this function during the stack walking functions ensures we meet the
+// second criterion.
+MFBT_API void
+StackWalkInitCriticalAddress()
+{
+ if (gCriticalAddress.mInit) {
+ return;
+ }
+ gCriticalAddress.mInit = true;
+ // We must not do work when 'new_sem_from_pool' calls realloc, since
+ // it holds a non-reentrant spin-lock and we will quickly deadlock.
+ // new_sem_from_pool is not directly accessible using dlsym, so
+ // we force a situation where new_sem_from_pool is on the stack and
+ // use dladdr to check the addresses.
+
+ // malloc_logger can be set by external tools like 'Instruments' or 'leaks'
+ malloc_logger_t* old_malloc_logger = malloc_logger;
+ malloc_logger = my_malloc_logger;
+
+ pthread_cond_t cond;
+ int r = pthread_cond_init(&cond, 0);
+ MOZ_ASSERT(r == 0);
+ pthread_mutex_t mutex;
+ r = pthread_mutex_init(&mutex, 0);
+ MOZ_ASSERT(r == 0);
+ r = pthread_mutex_lock(&mutex);
+ MOZ_ASSERT(r == 0);
+ struct timespec abstime = { 0, 1 };
+ r = pthread_cond_timedwait_relative_np(&cond, &mutex, &abstime);
+
+ // restore the previous malloc logger
+ malloc_logger = old_malloc_logger;
+
+ MOZ_ASSERT(r == ETIMEDOUT);
+ r = pthread_mutex_unlock(&mutex);
+ MOZ_ASSERT(r == 0);
+ r = pthread_mutex_destroy(&mutex);
+ MOZ_ASSERT(r == 0);
+ r = pthread_cond_destroy(&cond);
+ MOZ_ASSERT(r == 0);
+}
+
+static bool
+IsCriticalAddress(void* aPC)
+{
+ return gCriticalAddress.mAddr == aPC;
+}
+#else
static bool
IsCriticalAddress(void* aPC)
{
return false;
}
// We still initialize gCriticalAddress.mInit so that this code behaves
-// the same on all platforms.
+// the same on all platforms. Otherwise a failure to init would be visible
+// only on OS X.
MFBT_API void
StackWalkInitCriticalAddress()
{
gCriticalAddress.mInit = true;
}
+#endif
#if defined(_WIN32) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_IA64)) // WIN32 x86 stack walking code
@@ -799,6 +909,8 @@ MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
void* stackEnd;
#if HAVE___LIBC_STACK_END
stackEnd = __libc_stack_end;
+#elif defined(XP_DARWIN)
+ stackEnd = pthread_get_stackaddr_np(pthread_self());
#else
# error Unsupported configuration
#endif
diff --git a/mozglue/misc/TimeStamp.h b/mozglue/misc/TimeStamp.h
index 20e70cbdc1..019393b284 100644
--- a/mozglue/misc/TimeStamp.h
+++ b/mozglue/misc/TimeStamp.h
@@ -399,9 +399,18 @@ public:
* retrieved by mozilla::TimeStamp. Since we need this for
* vsync timestamps, we enable the creation of mozilla::TimeStamps
* on platforms that support vsync aligned refresh drivers / compositors
+ * Verified true as of Jan 31, 2015: OS X
* False on Windows 7
* UNTESTED ON OTHER PLATFORMS
*/
+#if defined(XP_DARWIN)
+ static TimeStamp FromSystemTime(int64_t aSystemTime)
+ {
+ static_assert(sizeof(aSystemTime) == sizeof(TimeStampValue),
+ "System timestamp should be same units as TimeStampValue");
+ return TimeStamp(aSystemTime);
+ }
+#endif
/**
* Return true if this is the "null" moment
diff --git a/netwerk/base/nsSocketTransport2.cpp b/netwerk/base/nsSocketTransport2.cpp
index 3c6804760a..c1011b1053 100644
--- a/netwerk/base/nsSocketTransport2.cpp
+++ b/netwerk/base/nsSocketTransport2.cpp
@@ -3039,6 +3039,17 @@ nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals(bool aEnabled,
}
return NS_OK;
+#elif defined(XP_DARWIN)
+ // Darwin uses sec; only supports idle time being set.
+ int err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE,
+ &aIdleTime, sizeof(aIdleTime));
+ if (NS_WARN_IF(err)) {
+ LogOSError("nsSocketTransport Failed setting TCP_KEEPALIVE",
+ mSocketTransport);
+ return NS_ERROR_UNEXPECTED;
+ }
+ return NS_OK;
+
#elif defined(XP_UNIX)
// Not all *nix OSes support the following setsockopt() options
// build errors will tell us if they are not.
diff --git a/xpcom/ds/nsMathUtils.h b/xpcom/ds/nsMathUtils.h
index 57228d6634..a3058faaaf 100644
--- a/xpcom/ds/nsMathUtils.h
+++ b/xpcom/ds/nsMathUtils.h
@@ -103,6 +103,10 @@ NS_finite(double aNum)
#ifdef WIN32
// NOTE: '!!' casts an int to bool without spamming MSVC warning C4800.
return !!_finite(aNum);
+#elif defined(XP_DARWIN)
+ // Darwin has deprecated |finite| and recommends |isfinite|. The former is
+ // not present in the iOS SDK.
+ return std::isfinite(aNum);
#else
return finite(aNum);
#endif
diff --git a/xpcom/io/nsLocalFileUnix.h b/xpcom/io/nsLocalFileUnix.h
index 5bdc6a3da0..c49f448fd1 100644
--- a/xpcom/io/nsLocalFileUnix.h
+++ b/xpcom/io/nsLocalFileUnix.h
@@ -61,7 +61,9 @@
#define F_BSIZE f_bsize
#endif
-#if defined(HAVE_STAT64) && defined(HAVE_LSTAT64)
+// stat64 and lstat64 are deprecated on OS X. Normal stat and lstat are
+// 64-bit by default on OS X 10.6+.
+#if defined(HAVE_STAT64) && defined(HAVE_LSTAT64) && !defined(XP_DARWIN)
#define STAT stat64
#define LSTAT lstat64
#define HAVE_STATS64 1
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_x86_64_unix.S b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_x86_64_unix.S
index 92788667cd..131cfc3343 100644
--- a/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_x86_64_unix.S
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_asm_x86_64_unix.S
@@ -2,6 +2,16 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+# Darwin gives a leading '_' to symbols defined in C code.
+#ifdef XP_DARWIN
+#define SYM(x) _ ## x
+#define CFI_STARTPROC
+#define CFI_ENDPROC
+#define CFI_DEF_CFA_OFFSET(offset)
+#define CFI_OFFSET(reg, offset)
+#define CFI_DEF_CFA_REGISTER(reg)
+#define CFI_DEF_CFA(reg, offset)
+#else
#define SYM(x) x
#define CFI_STARTPROC .cfi_startproc
#define CFI_ENDPROC .cfi_endproc
@@ -9,6 +19,7 @@
#define CFI_OFFSET(reg, offset) .cfi_offset reg, offset
#define CFI_DEF_CFA_REGISTER(reg) .cfi_def_cfa_register reg
#define CFI_DEF_CFA(reg, offset) .cfi_def_cfa reg, offset
+#endif
.intel_syntax noprefix
@@ -16,7 +27,9 @@
# uint32_t argc, nsXPTCVariant* argv);
.text
.global SYM(NS_InvokeByIndex)
+#ifndef XP_DARWIN
.type NS_InvokeByIndex, @function
+#endif
.align 4
SYM(NS_InvokeByIndex):
CFI_STARTPROC
@@ -103,5 +116,7 @@ SYM(NS_InvokeByIndex):
ret
CFI_ENDPROC
+#ifndef XP_DARWIN
// Magic indicating no need for an executable stack
.section .note.GNU-stack, "", @progbits ; .previous
+#endif
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_arm.cpp b/xpcom/reflect/xptcall/md/unix/xptcstubs_arm.cpp
index cf68c0d6eb..78664dfcfb 100644
--- a/xpcom/reflect/xptcall/md/unix/xptcstubs_arm.cpp
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_arm.cpp
@@ -8,8 +8,8 @@
#include "xptcprivate.h"
#include "xptiprivate.h"
-#if !defined(__arm__) && !defined(LINUX)
-#error "This code is for Linux ARM only. Please check if it works for you, too.\nDepends strongly on gcc behaviour."
+#if !defined(__arm__) && !(defined(LINUX) || defined(XP_DARWIN))
+#error "This code is for Linux/iOS ARM only. Please check if it works for you, too.\nDepends strongly on gcc behaviour."
#endif
/* Specify explicitly a symbol for this function, don't try to guess the c++ mangled symbol. */