summaryrefslogtreecommitdiffstats
path: root/mozglue
diff options
context:
space:
mode:
Diffstat (limited to 'mozglue')
-rw-r--r--mozglue/android/APKOpen.cpp465
-rw-r--r--mozglue/android/APKOpen.h39
-rw-r--r--mozglue/android/NSSBridge.cpp295
-rw-r--r--mozglue/android/NSSBridge.h46
-rw-r--r--mozglue/android/NativeCrypto.cpp132
-rw-r--r--mozglue/android/NativeCrypto.h53
-rw-r--r--mozglue/android/SQLiteBridge.cpp413
-rw-r--r--mozglue/android/SQLiteBridge.h34
-rw-r--r--mozglue/android/SharedMemNatives.cpp65
-rw-r--r--mozglue/android/moz.build58
-rw-r--r--mozglue/android/nsGeckoUtils.cpp124
-rw-r--r--mozglue/android/pbkdf2_sha256.c432
-rw-r--r--mozglue/android/pbkdf2_sha256.h70
-rw-r--r--mozglue/build/AsanOptions.cpp35
-rw-r--r--mozglue/build/BionicGlue.cpp157
-rw-r--r--mozglue/build/Makefile.in23
-rw-r--r--mozglue/build/SSE.cpp206
-rw-r--r--mozglue/build/SSE.h332
-rw-r--r--mozglue/build/WindowsDllBlocklist.cpp831
-rw-r--r--mozglue/build/WindowsDllBlocklist.h39
-rw-r--r--mozglue/build/arm-eabi-filter4
-rw-r--r--mozglue/build/arm.cpp147
-rw-r--r--mozglue/build/arm.h146
-rw-r--r--mozglue/build/cpuacct.c61
-rw-r--r--mozglue/build/cpuacct.h41
-rw-r--r--mozglue/build/dummy.cpp6
-rw-r--r--mozglue/build/mips.cpp47
-rw-r--r--mozglue/build/mips.h29
-rw-r--r--mozglue/build/moz.build108
-rw-r--r--mozglue/build/mozglue.def.in39
-rw-r--r--mozglue/build/replace_malloc.mk32
-rw-r--r--mozglue/linker/BaseElf.cpp215
-rw-r--r--mozglue/linker/BaseElf.h141
-rw-r--r--mozglue/linker/CustomElf.cpp774
-rw-r--r--mozglue/linker/CustomElf.h159
-rw-r--r--mozglue/linker/ElfLoader.cpp1310
-rw-r--r--mozglue/linker/ElfLoader.h674
-rw-r--r--mozglue/linker/Elfxx.h241
-rw-r--r--mozglue/linker/Logging.h87
-rw-r--r--mozglue/linker/Mappable.cpp681
-rw-r--r--mozglue/linker/Mappable.h267
-rw-r--r--mozglue/linker/SeekableZStream.cpp261
-rw-r--r--mozglue/linker/SeekableZStream.h154
-rw-r--r--mozglue/linker/Utils.h618
-rw-r--r--mozglue/linker/XZStream.cpp213
-rw-r--r--mozglue/linker/XZStream.h48
-rw-r--r--mozglue/linker/Zip.cpp229
-rw-r--r--mozglue/linker/Zip.h500
-rw-r--r--mozglue/linker/dladdr.h16
-rw-r--r--mozglue/linker/moz.build54
-rw-r--r--mozglue/linker/szip.cpp593
-rw-r--r--mozglue/linker/tests/TestZip.cpp69
-rw-r--r--mozglue/linker/tests/moz.build23
-rw-r--r--mozglue/linker/tests/no_central_dir.zipbin0 -> 281 bytes
-rw-r--r--mozglue/linker/tests/run_test_zip.py21
-rw-r--r--mozglue/linker/tests/test.zipbin0 -> 574 bytes
-rw-r--r--mozglue/misc/StackWalk.cpp1204
-rw-r--r--mozglue/misc/StackWalk.h163
-rw-r--r--mozglue/misc/StackWalk_windows.h21
-rw-r--r--mozglue/misc/TimeStamp.cpp92
-rw-r--r--mozglue/misc/TimeStamp.h609
-rw-r--r--mozglue/misc/TimeStamp_darwin.cpp206
-rw-r--r--mozglue/misc/TimeStamp_posix.cpp362
-rw-r--r--mozglue/misc/TimeStamp_windows.cpp576
-rw-r--r--mozglue/misc/TimeStamp_windows.h83
-rw-r--r--mozglue/misc/moz.build37
-rw-r--r--mozglue/moz.build19
-rw-r--r--mozglue/tests/ShowSSEConfig.cpp123
-rw-r--r--mozglue/tests/moz.build11
69 files changed, 15363 insertions, 0 deletions
diff --git a/mozglue/android/APKOpen.cpp b/mozglue/android/APKOpen.cpp
new file mode 100644
index 000000000..1aabc155e
--- /dev/null
+++ b/mozglue/android/APKOpen.cpp
@@ -0,0 +1,465 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+/*
+ * This custom library loading code is only meant to be called
+ * during initialization. As a result, it takes no special
+ * precautions to be threadsafe. Any of the library loading functions
+ * like mozload should not be available to other code.
+ */
+
+#include <jni.h>
+#include <android/log.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/limits.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <zlib.h>
+#include "dlfcn.h"
+#include "APKOpen.h"
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/prctl.h>
+#include "sqlite3.h"
+#include "SQLiteBridge.h"
+#include "NSSBridge.h"
+#include "ElfLoader.h"
+#include "application.ini.h"
+
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
+#include "XREChildData.h"
+
+/* Android headers don't define RUSAGE_THREAD */
+#ifndef RUSAGE_THREAD
+#define RUSAGE_THREAD 1
+#endif
+
+#ifndef RELEASE_OR_BETA
+/* Official builds have the debuggable flag set to false, which disables
+ * the backtrace dumper from bionic. However, as it is useful for native
+ * crashes happening before the crash reporter is registered, re-enable
+ * it on non release builds (i.e. nightly and aurora).
+ * Using a constructor so that it is re-enabled as soon as libmozglue.so
+ * is loaded.
+ */
+__attribute__((constructor))
+void make_dumpable() {
+ prctl(PR_SET_DUMPABLE, 1);
+}
+#endif
+
+extern "C" {
+/*
+ * To work around http://code.google.com/p/android/issues/detail?id=23203
+ * we don't link with the crt objects. In some configurations, this means
+ * a lack of the __dso_handle symbol because it is defined there, and
+ * depending on the android platform and ndk versions used, it may or may
+ * not be defined in libc.so. In the latter case, we fail to link. Defining
+ * it here as weak makes us provide the symbol when it's not provided by
+ * the crt objects, making the change transparent for future NDKs that
+ * would fix the original problem. On older NDKs, it is not a problem
+ * either because the way __dso_handle was used was already broken (and
+ * the custom linker works around it).
+ */
+ NS_EXPORT __attribute__((weak)) void *__dso_handle;
+}
+
+typedef int mozglueresult;
+
+enum StartupEvent {
+#define mozilla_StartupTimeline_Event(ev, z) ev,
+#include "StartupTimeline.h"
+#undef mozilla_StartupTimeline_Event
+ MAX_STARTUP_EVENT_ID
+};
+
+using namespace mozilla;
+
+static const int MAX_MAPPING_INFO = 32;
+static mapping_info lib_mapping[MAX_MAPPING_INFO];
+
+NS_EXPORT const struct mapping_info *
+getLibraryMapping()
+{
+ return lib_mapping;
+}
+
+void
+JNI_Throw(JNIEnv* jenv, const char* classname, const char* msg)
+{
+ __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Throw\n");
+ jclass cls = jenv->FindClass(classname);
+ if (cls == nullptr) {
+ __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't find exception class (or exception pending) %s\n", classname);
+ exit(FAILURE);
+ }
+ int rc = jenv->ThrowNew(cls, msg);
+ if (rc < 0) {
+ __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Error throwing exception %s\n", msg);
+ exit(FAILURE);
+ }
+ jenv->DeleteLocalRef(cls);
+}
+
+namespace {
+ JavaVM* sJavaVM;
+ pthread_t sJavaUiThread;
+}
+
+void
+abortThroughJava(const char* msg)
+{
+ struct sigaction sigact = {};
+ if (SEGVHandler::__wrap_sigaction(SIGSEGV, nullptr, &sigact)) {
+ return; // sigaction call failed.
+ }
+
+ Dl_info info = {};
+ if ((sigact.sa_flags & SA_SIGINFO) &&
+ __wrap_dladdr(reinterpret_cast<void*>(sigact.sa_sigaction), &info) &&
+ info.dli_fname && strstr(info.dli_fname, "libxul.so")) {
+
+ return; // Existing signal handler is in libxul (i.e. we have crash reporter).
+ }
+
+ JNIEnv* env = nullptr;
+ if (!sJavaVM || sJavaVM->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
+ return;
+ }
+
+ if (!env || env->PushLocalFrame(2) != JNI_OK) {
+ return;
+ }
+
+ jclass loader = env->FindClass("org/mozilla/gecko/mozglue/GeckoLoader");
+ if (!loader) {
+ return;
+ }
+
+ jmethodID method = env->GetStaticMethodID(loader, "abort", "(Ljava/lang/String;)V");
+ jstring str = env->NewStringUTF(msg);
+
+ if (method && str) {
+ env->CallStaticVoidMethod(loader, method, str);
+ }
+
+ env->PopLocalFrame(nullptr);
+}
+
+NS_EXPORT pthread_t
+getJavaUiThread()
+{
+ return sJavaUiThread;
+}
+
+extern "C" NS_EXPORT void MOZ_JNICALL
+Java_org_mozilla_gecko_GeckoThread_registerUiThread(JNIEnv*, jclass)
+{
+ sJavaUiThread = pthread_self();
+}
+
+static void * xul_handle = nullptr;
+#ifndef MOZ_FOLD_LIBS
+static void * sqlite_handle = nullptr;
+static void * nspr_handle = nullptr;
+static void * plc_handle = nullptr;
+#else
+#define sqlite_handle nss_handle
+#define nspr_handle nss_handle
+#define plc_handle nss_handle
+#endif
+static void * nss_handle = nullptr;
+
+template <typename T> inline void
+xul_dlsym(const char *symbolName, T *value)
+{
+ *value = (T) (uintptr_t) __wrap_dlsym(xul_handle, symbolName);
+}
+
+static int mapping_count = 0;
+
+extern "C" void
+report_mapping(char *name, void *base, uint32_t len, uint32_t offset)
+{
+ if (mapping_count >= MAX_MAPPING_INFO)
+ return;
+
+ struct mapping_info *info = &lib_mapping[mapping_count++];
+ info->name = strdup(name);
+ info->base = (uintptr_t)base;
+ info->len = len;
+ info->offset = offset;
+}
+
+extern "C" void
+delete_mapping(const char *name)
+{
+ for (int pos = 0; pos < mapping_count; ++pos) {
+ struct mapping_info *info = &lib_mapping[pos];
+ if (!strcmp(info->name, name)) {
+ struct mapping_info *last = &lib_mapping[mapping_count - 1];
+ free(info->name);
+ *info = *last;
+ --mapping_count;
+ break;
+ }
+ }
+}
+
+static void*
+dlopenAPKLibrary(const char* apkName, const char* libraryName)
+{
+#define APK_ASSETS_PATH "!/assets/" ANDROID_CPU_ARCH "/"
+ size_t filenameLength = strlen(apkName) +
+ sizeof(APK_ASSETS_PATH) + // includes \0 terminator
+ strlen(libraryName);
+ auto file = MakeUnique<char[]>(filenameLength);
+ snprintf(file.get(), filenameLength, "%s" APK_ASSETS_PATH "%s",
+ apkName, libraryName);
+ return __wrap_dlopen(file.get(), RTLD_GLOBAL | RTLD_LAZY);
+#undef APK_ASSETS_PATH
+}
+static mozglueresult
+loadGeckoLibs(const char *apkName)
+{
+ TimeStamp t0 = TimeStamp::Now();
+ struct rusage usage1_thread, usage1;
+ getrusage(RUSAGE_THREAD, &usage1_thread);
+ getrusage(RUSAGE_SELF, &usage1);
+
+ xul_handle = dlopenAPKLibrary(apkName, "libxul.so");
+ if (!xul_handle) {
+ __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libxul!");
+ return FAILURE;
+ }
+
+ void (*XRE_StartupTimelineRecord)(int, TimeStamp);
+ xul_dlsym("XRE_StartupTimelineRecord", &XRE_StartupTimelineRecord);
+
+ TimeStamp t1 = TimeStamp::Now();
+ struct rusage usage2_thread, usage2;
+ getrusage(RUSAGE_THREAD, &usage2_thread);
+ getrusage(RUSAGE_SELF, &usage2);
+
+#define RUSAGE_TIMEDIFF(u1, u2, field) \
+ ((u2.ru_ ## field.tv_sec - u1.ru_ ## field.tv_sec) * 1000 + \
+ (u2.ru_ ## field.tv_usec - u1.ru_ ## field.tv_usec) / 1000)
+
+ __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loaded libs in %fms total, %ldms(%ldms) user, %ldms(%ldms) system, %ld(%ld) faults",
+ (t1 - t0).ToMilliseconds(),
+ RUSAGE_TIMEDIFF(usage1_thread, usage2_thread, utime),
+ RUSAGE_TIMEDIFF(usage1, usage2, utime),
+ RUSAGE_TIMEDIFF(usage1_thread, usage2_thread, stime),
+ RUSAGE_TIMEDIFF(usage1, usage2, stime),
+ usage2_thread.ru_majflt - usage1_thread.ru_majflt,
+ usage2.ru_majflt - usage1.ru_majflt);
+
+ XRE_StartupTimelineRecord(LINKER_INITIALIZED, t0);
+ XRE_StartupTimelineRecord(LIBRARIES_LOADED, t1);
+ return SUCCESS;
+}
+
+static mozglueresult loadNSSLibs(const char *apkName);
+
+static mozglueresult
+loadSQLiteLibs(const char *apkName)
+{
+ if (sqlite_handle)
+ return SUCCESS;
+
+#ifdef MOZ_FOLD_LIBS
+ if (loadNSSLibs(apkName) != SUCCESS)
+ return FAILURE;
+#else
+
+ sqlite_handle = dlopenAPKLibrary(apkName, "libmozsqlite3.so");
+ if (!sqlite_handle) {
+ __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libmozsqlite3!");
+ return FAILURE;
+ }
+#endif
+
+ setup_sqlite_functions(sqlite_handle);
+ return SUCCESS;
+}
+
+static mozglueresult
+loadNSSLibs(const char *apkName)
+{
+ if (nss_handle && nspr_handle && plc_handle)
+ return SUCCESS;
+
+ nss_handle = dlopenAPKLibrary(apkName, "libnss3.so");
+
+#ifndef MOZ_FOLD_LIBS
+ nspr_handle = dlopenAPKLibrary(apkName, "libnspr4.so");
+
+ plc_handle = dlopenAPKLibrary(apkName, "libplc4.so");
+#endif
+
+ if (!nss_handle) {
+ __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libnss3!");
+ return FAILURE;
+ }
+
+#ifndef MOZ_FOLD_LIBS
+ if (!nspr_handle) {
+ __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libnspr4!");
+ return FAILURE;
+ }
+
+ if (!plc_handle) {
+ __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libplc4!");
+ return FAILURE;
+ }
+#endif
+
+ return setup_nss_functions(nss_handle, nspr_handle, plc_handle);
+}
+
+extern "C" NS_EXPORT void MOZ_JNICALL
+Java_org_mozilla_gecko_mozglue_GeckoLoader_extractGeckoLibsNative(
+ JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName)
+{
+ MOZ_ALWAYS_TRUE(!jenv->GetJavaVM(&sJavaVM));
+
+ const char* apkName = jenv->GetStringUTFChars(jApkName, nullptr);
+ if (apkName == nullptr) {
+ return;
+ }
+
+ // Extract and cache native lib to allow for efficient startup from cache.
+ void* handle = dlopenAPKLibrary(apkName, "libxul.so");
+ if (handle) {
+ __android_log_print(ANDROID_LOG_INFO, "GeckoLibLoad",
+ "Extracted and cached libxul.so.");
+ // We have extracted and cached the lib, we can close it now.
+ __wrap_dlclose(handle);
+ } else {
+ JNI_Throw(jenv, "java/lang/Exception", "Error extracting gecko libraries");
+ }
+
+ jenv->ReleaseStringUTFChars(jApkName, apkName);
+}
+
+extern "C" NS_EXPORT void MOZ_JNICALL
+Java_org_mozilla_gecko_mozglue_GeckoLoader_loadGeckoLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName)
+{
+ jenv->GetJavaVM(&sJavaVM);
+
+ const char* str;
+ // XXX: java doesn't give us true UTF8, we should figure out something
+ // better to do here
+ str = jenv->GetStringUTFChars(jApkName, nullptr);
+ if (str == nullptr)
+ return;
+
+ int res = loadGeckoLibs(str);
+ if (res != SUCCESS) {
+ JNI_Throw(jenv, "java/lang/Exception", "Error loading gecko libraries");
+ }
+ jenv->ReleaseStringUTFChars(jApkName, str);
+}
+
+extern "C" NS_EXPORT void MOZ_JNICALL
+Java_org_mozilla_gecko_mozglue_GeckoLoader_loadSQLiteLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName) {
+ const char* str;
+ // XXX: java doesn't give us true UTF8, we should figure out something
+ // better to do here
+ str = jenv->GetStringUTFChars(jApkName, nullptr);
+ if (str == nullptr)
+ return;
+
+ __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load sqlite start\n");
+ mozglueresult rv = loadSQLiteLibs(str);
+ if (rv != SUCCESS) {
+ JNI_Throw(jenv, "java/lang/Exception", "Error loading sqlite libraries");
+ }
+ __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load sqlite done\n");
+ jenv->ReleaseStringUTFChars(jApkName, str);
+}
+
+extern "C" NS_EXPORT void MOZ_JNICALL
+Java_org_mozilla_gecko_mozglue_GeckoLoader_loadNSSLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName) {
+ const char* str;
+ // XXX: java doesn't give us true UTF8, we should figure out something
+ // better to do here
+ str = jenv->GetStringUTFChars(jApkName, nullptr);
+ if (str == nullptr)
+ return;
+
+ __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load nss start\n");
+ mozglueresult rv = loadNSSLibs(str);
+ if (rv != SUCCESS) {
+ JNI_Throw(jenv, "java/lang/Exception", "Error loading nss libraries");
+ }
+ __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load nss done\n");
+ jenv->ReleaseStringUTFChars(jApkName, str);
+}
+
+typedef void (*GeckoStart_t)(JNIEnv*, char*, const nsXREAppData*);
+
+extern "C" NS_EXPORT void MOZ_JNICALL
+Java_org_mozilla_gecko_mozglue_GeckoLoader_nativeRun(JNIEnv *jenv, jclass jc, jstring jargs)
+{
+ GeckoStart_t GeckoStart;
+ xul_dlsym("GeckoStart", &GeckoStart);
+ if (GeckoStart == nullptr)
+ return;
+ // XXX: java doesn't give us true UTF8, we should figure out something
+ // better to do here
+ int len = jenv->GetStringUTFLength(jargs);
+ // GeckoStart needs to write in the args buffer, so we need a copy.
+ char *args = (char *) malloc(len + 1);
+ jenv->GetStringUTFRegion(jargs, 0, len, args);
+ args[len] = '\0';
+ ElfLoader::Singleton.ExpectShutdown(false);
+ GeckoStart(jenv, args, &sAppData);
+ ElfLoader::Singleton.ExpectShutdown(true);
+ free(args);
+}
+
+typedef int GeckoProcessType;
+
+extern "C" NS_EXPORT mozglueresult
+ChildProcessInit(int argc, char* argv[])
+{
+ int i;
+ for (i = 0; i < (argc - 1); i++) {
+ if (strcmp(argv[i], "-greomni"))
+ continue;
+
+ i = i + 1;
+ break;
+ }
+
+ if (loadNSSLibs(argv[i]) != SUCCESS) {
+ return FAILURE;
+ }
+ if (loadSQLiteLibs(argv[i]) != SUCCESS) {
+ return FAILURE;
+ }
+ if (loadGeckoLibs(argv[i]) != SUCCESS) {
+ return FAILURE;
+ }
+
+ void (*fXRE_SetProcessType)(char*);
+ xul_dlsym("XRE_SetProcessType", &fXRE_SetProcessType);
+
+ mozglueresult (*fXRE_InitChildProcess)(int, char**, void*);
+ xul_dlsym("XRE_InitChildProcess", &fXRE_InitChildProcess);
+
+ fXRE_SetProcessType(argv[--argc]);
+
+ XREChildData childData;
+ return fXRE_InitChildProcess(argc, argv, &childData);
+}
+
diff --git a/mozglue/android/APKOpen.h b/mozglue/android/APKOpen.h
new file mode 100644
index 000000000..f28458feb
--- /dev/null
+++ b/mozglue/android/APKOpen.h
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef APKOpen_h
+#define APKOpen_h
+
+#include <jni.h>
+#include <pthread.h>
+
+#ifndef NS_EXPORT
+#define NS_EXPORT __attribute__ ((visibility("default")))
+#endif
+
+struct mapping_info {
+ char * name;
+ uintptr_t base;
+ size_t len;
+ size_t offset;
+};
+
+NS_EXPORT const struct mapping_info * getLibraryMapping();
+NS_EXPORT void abortThroughJava(const char* msg);
+NS_EXPORT pthread_t getJavaUiThread();
+
+static const int SUCCESS = 0;
+static const int FAILURE = 1;
+void JNI_Throw(JNIEnv* jenv, const char* classname, const char* msg);
+
+// Bug 1207642 - Work around Dalvik bug by realigning stack on JNI entry
+#ifndef MOZ_JNICALL
+# ifdef __i386__
+# define MOZ_JNICALL JNICALL __attribute__((force_align_arg_pointer))
+# else
+# define MOZ_JNICALL JNICALL
+# endif
+#endif
+
+#endif /* APKOpen_h */
diff --git a/mozglue/android/NSSBridge.cpp b/mozglue/android/NSSBridge.cpp
new file mode 100644
index 000000000..2a9b5771d
--- /dev/null
+++ b/mozglue/android/NSSBridge.cpp
@@ -0,0 +1,295 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include <stdlib.h>
+#include "dlfcn.h"
+#include "NSSBridge.h"
+#include "APKOpen.h"
+#ifdef ANDROID
+#include <jni.h>
+#include <android/log.h>
+#endif
+
+#include "ElfLoader.h"
+
+#ifdef DEBUG
+#define LOG(x...) __android_log_print(ANDROID_LOG_INFO, "GeckoJNI", x)
+#else
+#define LOG(x...)
+#endif
+
+static bool initialized = false;
+
+#define NSS_WRAPPER_INT(name) name ## _t f_ ## name;
+NSS_WRAPPER_INT(NSS_Initialize)
+NSS_WRAPPER_INT(NSS_Shutdown)
+NSS_WRAPPER_INT(SECITEM_ZfreeItem)
+NSS_WRAPPER_INT(PK11SDR_Encrypt)
+NSS_WRAPPER_INT(PK11SDR_Decrypt)
+NSS_WRAPPER_INT(PK11_GetInternalKeySlot)
+NSS_WRAPPER_INT(PK11_NeedUserInit)
+NSS_WRAPPER_INT(PK11_InitPin)
+NSS_WRAPPER_INT(PR_ErrorToString)
+NSS_WRAPPER_INT(PR_GetError)
+NSS_WRAPPER_INT(PR_Free)
+NSS_WRAPPER_INT(PL_Base64Encode)
+NSS_WRAPPER_INT(PL_Base64Decode)
+NSS_WRAPPER_INT(PL_strfree)
+
+int
+setup_nss_functions(void *nss_handle,
+ void *nspr_handle,
+ void *plc_handle)
+{
+ if (nss_handle == nullptr || nspr_handle == nullptr || plc_handle == nullptr) {
+ LOG("Missing handle\n");
+ return FAILURE;
+ }
+#define GETFUNC(name) f_ ## name = (name ## _t) (uintptr_t) __wrap_dlsym(nss_handle, #name); \
+ if (!f_ ##name) { __android_log_print(ANDROID_LOG_ERROR, "GeckoJNI", "missing %s", #name); return FAILURE; }
+ GETFUNC(NSS_Initialize);
+ GETFUNC(NSS_Shutdown);
+ GETFUNC(PK11SDR_Encrypt);
+ GETFUNC(PK11SDR_Decrypt);
+ GETFUNC(PK11_GetInternalKeySlot);
+ GETFUNC(PK11_NeedUserInit);
+ GETFUNC(PK11_InitPin);
+ GETFUNC(SECITEM_ZfreeItem);
+#undef GETFUNC
+#define NSPRFUNC(name) f_ ## name = (name ## _t) (uintptr_t) __wrap_dlsym(nspr_handle, #name); \
+ if (!f_ ##name) { __android_log_print(ANDROID_LOG_ERROR, "GeckoJNI", "missing %s", #name); return FAILURE; }
+ NSPRFUNC(PR_ErrorToString);
+ NSPRFUNC(PR_GetError);
+ NSPRFUNC(PR_Free);
+#undef NSPRFUNC
+#define PLCFUNC(name) f_ ## name = (name ## _t) (uintptr_t) __wrap_dlsym(plc_handle, #name); \
+ if (!f_ ##name) { __android_log_print(ANDROID_LOG_ERROR, "GeckoJNI", "missing %s", #name); return FAILURE; }
+ PLCFUNC(PL_Base64Encode);
+ PLCFUNC(PL_Base64Decode);
+ PLCFUNC(PL_strfree);
+#undef PLCFUNC
+
+ return SUCCESS;
+}
+
+/* Throws the current NSS error. */
+static void
+throwError(JNIEnv* jenv, const char * funcString) {
+ char *msg;
+
+ PRErrorCode perr = f_PR_GetError();
+ char * errString = f_PR_ErrorToString(perr, 0);
+ asprintf(&msg, "%s returned error %d: %s\n", funcString, perr, errString);
+ LOG("Throwing error: %s\n", msg);
+
+ JNI_Throw(jenv, "java/lang/Exception", msg);
+ free(msg);
+ LOG("Error thrown\n");
+}
+
+extern "C" NS_EXPORT jstring MOZ_JNICALL
+Java_org_mozilla_gecko_NSSBridge_nativeEncrypt(JNIEnv* jenv, jclass,
+ jstring jPath,
+ jstring jValue)
+{
+ jstring ret = jenv->NewStringUTF("");
+
+ const char* path;
+ path = jenv->GetStringUTFChars(jPath, nullptr);
+
+ const char* value;
+ value = jenv->GetStringUTFChars(jValue, nullptr);
+
+ char* result;
+ SECStatus rv = doCrypto(jenv, path, value, &result, true);
+ if (rv == SECSuccess) {
+ ret = jenv->NewStringUTF(result);
+ free(result);
+ }
+
+ jenv->ReleaseStringUTFChars(jValue, value);
+ jenv->ReleaseStringUTFChars(jPath, path);
+
+ return ret;
+}
+
+extern "C" NS_EXPORT jstring MOZ_JNICALL
+Java_org_mozilla_gecko_NSSBridge_nativeDecrypt(JNIEnv* jenv, jclass,
+ jstring jPath,
+ jstring jValue)
+{
+ jstring ret = jenv->NewStringUTF("");
+
+ const char* path;
+ path = jenv->GetStringUTFChars(jPath, nullptr);
+
+ const char* value;
+ value = jenv->GetStringUTFChars(jValue, nullptr);
+
+ char* result;
+ SECStatus rv = doCrypto(jenv, path, value, &result, false);
+ if (rv == SECSuccess) {
+ ret = jenv->NewStringUTF(result);
+ free(result);
+ }
+
+ jenv->ReleaseStringUTFChars(jValue, value);
+ jenv->ReleaseStringUTFChars(jPath, path);
+
+ return ret;
+}
+
+
+/* Encrypts or decrypts a string. result should be freed with free() when done */
+SECStatus
+doCrypto(JNIEnv* jenv, const char *path, const char *value, char** result, bool encrypt)
+{
+ SECStatus rv;
+ PK11SlotInfo *slot;
+ if (!initialized) {
+ LOG("Initialize crypto in %s\n", path);
+ rv = f_NSS_Initialize(path, "", "", "secmod.db", NSS_INIT_NOROOTINIT);
+ if (rv != SECSuccess) {
+ throwError(jenv, "NSS_Initialize");
+ return rv;
+ }
+ initialized = true;
+ }
+
+ slot = f_PK11_GetInternalKeySlot();
+ if (!slot) {
+ throwError(jenv, "PK11_GetInternalKeySlot");
+ return SECFailure;
+ }
+
+ if (f_PK11_NeedUserInit(slot)) {
+ LOG("Initializing key3.db with default blank password.\n");
+ rv = f_PK11_InitPin(slot, nullptr, nullptr);
+ if (rv != SECSuccess) {
+ throwError(jenv, "PK11_InitPin");
+ return rv;
+ }
+ }
+
+ SECItem request;
+ SECItem reply;
+
+ reply.data = 0;
+ reply.len = 0;
+
+ if (encrypt) {
+ // This can print sensitive data. Uncomment if you need it.
+ // LOG("Encrypting: %s\n", value);
+ request.data = (unsigned char*)value;
+ request.len = strlen(value);
+
+ SECItem keyid;
+ keyid.data = 0;
+ keyid.len = 0;
+ rv = f_PK11SDR_Encrypt(&keyid, &request, &reply, nullptr);
+
+ if (rv != SECSuccess) {
+ throwError(jenv, "PK11SDR_Encrypt");
+ goto done;
+ }
+
+ rv = encode(reply.data, reply.len, result);
+ if (rv != SECSuccess) {
+ throwError(jenv, "encode");
+ goto done;
+ }
+ LOG("Encrypted: %s\n", *result);
+ } else {
+ LOG("Decoding: %s\n", value);
+ rv = decode(value, &request.data, (int32_t*)&request.len);
+ if (rv != SECSuccess) {
+ throwError(jenv, "decode");
+ return rv;
+ }
+
+ rv = f_PK11SDR_Decrypt(&request, &reply, nullptr);
+ if (rv != SECSuccess) {
+ throwError(jenv, "PK11SDR_Decrypt");
+ goto done;
+ }
+
+ *result = (char *)malloc(reply.len+1);
+ strncpy(*result, (char *)reply.data, reply.len);
+ (*result)[reply.len] = '\0';
+
+ // This can print sensitive data. Uncomment if you need it.
+ // LOG("Decoded %i letters: %s\n", reply.len, *result);
+ free(request.data);
+ }
+
+done:
+ f_SECITEM_ZfreeItem(&reply, false);
+ return rv;
+}
+
+/*
+ * Base64 encodes the data passed in. The caller must deallocate _retval using free();
+ */
+SECStatus
+encode(const unsigned char *data, int32_t dataLen, char **_retval)
+{
+ SECStatus rv = SECSuccess;
+ char *encoded = f_PL_Base64Encode((const char *)data, dataLen, nullptr);
+ if (!encoded)
+ rv = SECFailure;
+ if (!*encoded)
+ rv = SECFailure;
+
+ if (rv == SECSuccess) {
+ *_retval = (char *)malloc(strlen(encoded)+1);
+ strcpy(*_retval, encoded);
+ }
+
+ if (encoded) {
+ f_PR_Free(encoded);
+ }
+
+ return rv;
+}
+
+/*
+ * Base64 decodes the data passed in. The caller must deallocate result using free();
+ */
+SECStatus
+decode(const char *data, unsigned char **result, int32_t *length)
+{
+ SECStatus rv = SECSuccess;
+ uint32_t len = strlen(data);
+ int adjust = 0;
+
+ /* Compute length adjustment */
+ if (len > 0 && data[len-1] == '=') {
+ adjust++;
+ if (data[len-2] == '=') adjust++;
+ }
+
+ char *decoded;
+ decoded = f_PL_Base64Decode(data, len, nullptr);
+ if (!decoded) {
+ return SECFailure;
+ }
+ if (!*decoded) {
+ return SECFailure;
+ }
+
+ *length = (len*3)/4 - adjust;
+ LOG("Decoded %i chars into %i chars\n", len, *length);
+
+ *result = (unsigned char*)malloc((size_t)len);
+
+ if (!*result) {
+ rv = SECFailure;
+ } else {
+ memcpy((char*)*result, decoded, len);
+ }
+ f_PR_Free(decoded);
+ return rv;
+}
+
+
diff --git a/mozglue/android/NSSBridge.h b/mozglue/android/NSSBridge.h
new file mode 100644
index 000000000..77bcd1172
--- /dev/null
+++ b/mozglue/android/NSSBridge.h
@@ -0,0 +1,46 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef NSSBridge_h
+#define NSSBridge_h
+
+#include "nss.h"
+#include "pk11func.h"
+#include "pk11sdr.h"
+#include "seccomon.h"
+#include "secitem.h"
+#include "secmodt.h"
+
+#include "prerror.h"
+#include "plstr.h"
+#include "prmem.h"
+
+#include <jni.h>
+
+int setup_nss_functions(void *nss_handle, void *nssutil_handle, void *plc_handle);
+
+#define NSS_WRAPPER(name, return_type, args...) \
+typedef return_type (*name ## _t)(args); \
+extern name ## _t f_ ## name;
+
+NSS_WRAPPER(NSS_Initialize, SECStatus, const char*, const char*, const char*, const char*, uint32_t)
+NSS_WRAPPER(NSS_Shutdown, void, void)
+NSS_WRAPPER(PK11SDR_Encrypt, SECStatus, SECItem *, SECItem *, SECItem *, void *)
+NSS_WRAPPER(PK11SDR_Decrypt, SECStatus, SECItem *, SECItem *, void *)
+NSS_WRAPPER(SECITEM_ZfreeItem, void, SECItem*, PRBool)
+NSS_WRAPPER(PR_ErrorToString, char *, PRErrorCode, PRLanguageCode)
+NSS_WRAPPER(PR_GetError, PRErrorCode, void)
+NSS_WRAPPER(PR_Free, PRErrorCode, char *)
+NSS_WRAPPER(PL_Base64Encode, char*, const char*, uint32_t, char*)
+NSS_WRAPPER(PL_Base64Decode, char*, const char*, uint32_t, char*)
+NSS_WRAPPER(PL_strfree, void, char*)
+NSS_WRAPPER(PK11_GetInternalKeySlot, PK11SlotInfo *, void)
+NSS_WRAPPER(PK11_NeedUserInit, PRBool, PK11SlotInfo *)
+NSS_WRAPPER(PK11_InitPin, SECStatus, PK11SlotInfo*, const char*, const char*)
+
+bool setPassword(PK11SlotInfo *slot);
+SECStatus doCrypto(JNIEnv* jenv, const char *path, const char *value, char** result, bool doEncrypt);
+SECStatus encode(const unsigned char *data, int32_t dataLen, char **_retval);
+SECStatus decode(const char *data, unsigned char **result, int32_t * _retval);
+#endif /* NSS_h */
diff --git a/mozglue/android/NativeCrypto.cpp b/mozglue/android/NativeCrypto.cpp
new file mode 100644
index 000000000..9a3632e8d
--- /dev/null
+++ b/mozglue/android/NativeCrypto.cpp
@@ -0,0 +1,132 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include "NativeCrypto.h"
+#include "APKOpen.h"
+
+#include <jni.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "mozilla/SHA1.h"
+#include "pbkdf2_sha256.h"
+
+/**
+ * Helper function to invoke native PBKDF2 function with JNI
+ * arguments.
+ */
+extern "C" JNIEXPORT jbyteArray MOZ_JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_pbkdf2SHA256
+ (JNIEnv *env, jclass jc, jbyteArray jpassword, jbyteArray jsalt, jint c, jint dkLen) {
+ if (dkLen < 0) {
+ env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"),
+ "dkLen should not be less than 0");
+ return nullptr;
+ }
+
+ jbyte *password = env->GetByteArrayElements(jpassword, nullptr);
+ size_t passwordLen = env->GetArrayLength(jpassword);
+
+ jbyte *salt = env->GetByteArrayElements(jsalt, nullptr);
+ size_t saltLen = env->GetArrayLength(jsalt);
+
+ uint8_t hashResult[dkLen];
+ PBKDF2_SHA256((uint8_t *) password, passwordLen, (uint8_t *) salt, saltLen,
+ (uint64_t) c, hashResult, (size_t) dkLen);
+
+ env->ReleaseByteArrayElements(jpassword, password, JNI_ABORT);
+ env->ReleaseByteArrayElements(jsalt, salt, JNI_ABORT);
+
+ jbyteArray out = env->NewByteArray(dkLen);
+ if (out == nullptr) {
+ return nullptr;
+ }
+ env->SetByteArrayRegion(out, 0, dkLen, (jbyte *) hashResult);
+
+ return out;
+}
+
+using namespace mozilla;
+
+/**
+ * Helper function to invoke native SHA-1 function with JNI arguments.
+ */
+extern "C" JNIEXPORT jbyteArray MOZ_JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha1
+ (JNIEnv *env, jclass jc, jbyteArray jstr) {
+ jbyte *str = env->GetByteArrayElements(jstr, nullptr);
+ size_t strLen = env->GetArrayLength(jstr);
+
+ SHA1Sum sha1;
+ SHA1Sum::Hash hashResult;
+ sha1.update((void *) str, (uint32_t) strLen);
+ sha1.finish(hashResult);
+
+ env->ReleaseByteArrayElements(jstr, str, JNI_ABORT);
+
+ jbyteArray out = env->NewByteArray(SHA1Sum::kHashSize);
+ if (out == nullptr) {
+ return nullptr;
+ }
+ env->SetByteArrayRegion(out, 0, SHA1Sum::kHashSize, (jbyte *) hashResult);
+
+ return out;
+}
+
+/**
+ * Helper function to invoke native SHA-256 init with JNI arguments.
+ */
+extern "C" JNIEXPORT jbyteArray MOZ_JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256init
+ (JNIEnv *env, jclass jc) {
+ jbyteArray out = env->NewByteArray(sizeof(SHA256_CTX));
+ if (nullptr == out) {
+ return nullptr;
+ }
+
+ SHA256_CTX *shaContext = (SHA256_CTX*)env->GetByteArrayElements(out, nullptr);
+ SHA256_Init(shaContext);
+
+ env->ReleaseByteArrayElements(out, (jbyte*)shaContext, 0);
+
+ return out;
+}
+
+/**
+ * Helper function to invoke native SHA-256 update with JNI arguments.
+ */
+extern "C" JNIEXPORT void MOZ_JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256update
+ (JNIEnv *env, jclass jc, jbyteArray jctx, jbyteArray jstr, jint len) {
+ jbyte *str = env->GetByteArrayElements(jstr, nullptr);
+
+ SHA256_CTX *shaContext = (SHA256_CTX*)env->GetByteArrayElements(jctx, nullptr);
+
+ SHA256_Update(shaContext, (void*)str, (size_t) len);
+
+ env->ReleaseByteArrayElements(jstr, str, JNI_ABORT);
+ env->ReleaseByteArrayElements(jctx, (jbyte*)shaContext, 0);
+
+ return;
+}
+
+/**
+ * Helper function to invoke native SHA-256 finalize with JNI arguments.
+ */
+extern "C" JNIEXPORT jbyteArray MOZ_JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256finalize
+ (JNIEnv *env, jclass jc, jbyteArray jctx) {
+ SHA256_CTX *shaContext = (SHA256_CTX*)env->GetByteArrayElements(jctx, nullptr);
+
+ unsigned char* digest = new unsigned char[32];
+ SHA256_Final(digest, shaContext);
+
+ env->ReleaseByteArrayElements(jctx, (jbyte*)shaContext, JNI_ABORT);
+
+ jbyteArray out = env->NewByteArray(32);
+ if (nullptr != out) {
+ env->SetByteArrayRegion(out, 0, 32, (jbyte*)digest);
+ }
+
+ delete[] digest;
+
+ return out;
+}
diff --git a/mozglue/android/NativeCrypto.h b/mozglue/android/NativeCrypto.h
new file mode 100644
index 000000000..2850e2bb7
--- /dev/null
+++ b/mozglue/android/NativeCrypto.h
@@ -0,0 +1,53 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_mozilla_gecko_background_nativecode_NativeCrypto */
+
+#ifndef _Included_org_mozilla_gecko_background_nativecode_NativeCrypto
+#define _Included_org_mozilla_gecko_background_nativecode_NativeCrypto
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: org_mozilla_gecko_background_nativecode_NativeCrypto
+ * Method: pbkdf2SHA256
+ * Signature: ([B[BII)[B
+ */
+JNIEXPORT jbyteArray JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_pbkdf2SHA256
+ (JNIEnv *, jclass, jbyteArray, jbyteArray, jint, jint);
+
+/*
+ * Class: org_mozilla_gecko_background_nativecode_NativeCrypto
+ * Method: sha1
+ * Signature: ([B)[B
+ */
+JNIEXPORT jbyteArray JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha1
+ (JNIEnv *, jclass, jbyteArray);
+
+/*
+ * Class: org_mozilla_gecko_background_nativecode_NativeCrypto
+ * Method: sha256init
+ * Signature: ()[B
+ */
+JNIEXPORT jbyteArray JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256init
+ (JNIEnv *, jclass);
+
+/*
+ * Class: org_mozilla_gecko_background_nativecode_NativeCrypto
+ * Method: sha256update
+ * Signature: ([B[B)V
+ */
+JNIEXPORT void JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256update
+ (JNIEnv *, jclass, jbyteArray, jbyteArray, jint);
+
+/*
+ * Class: org_mozilla_gecko_background_nativecode_NativeCrypto
+ * Method: sha256finalize
+ * Signature: ([B)[B
+ */
+JNIEXPORT jbyteArray JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256finalize
+ (JNIEnv *, jclass, jbyteArray);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/mozglue/android/SQLiteBridge.cpp b/mozglue/android/SQLiteBridge.cpp
new file mode 100644
index 000000000..187900bad
--- /dev/null
+++ b/mozglue/android/SQLiteBridge.cpp
@@ -0,0 +1,413 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <jni.h>
+#include <android/log.h>
+#include "dlfcn.h"
+#include "APKOpen.h"
+#include "ElfLoader.h"
+#include "SQLiteBridge.h"
+
+#ifdef DEBUG
+#define LOG(x...) __android_log_print(ANDROID_LOG_INFO, "GeckoJNI", x)
+#else
+#define LOG(x...)
+#endif
+
+#define SQLITE_WRAPPER_INT(name) name ## _t f_ ## name;
+
+SQLITE_WRAPPER_INT(sqlite3_open)
+SQLITE_WRAPPER_INT(sqlite3_errmsg)
+SQLITE_WRAPPER_INT(sqlite3_prepare_v2)
+SQLITE_WRAPPER_INT(sqlite3_bind_parameter_count)
+SQLITE_WRAPPER_INT(sqlite3_bind_null)
+SQLITE_WRAPPER_INT(sqlite3_bind_text)
+SQLITE_WRAPPER_INT(sqlite3_step)
+SQLITE_WRAPPER_INT(sqlite3_column_count)
+SQLITE_WRAPPER_INT(sqlite3_finalize)
+SQLITE_WRAPPER_INT(sqlite3_close)
+SQLITE_WRAPPER_INT(sqlite3_column_name)
+SQLITE_WRAPPER_INT(sqlite3_column_type)
+SQLITE_WRAPPER_INT(sqlite3_column_blob)
+SQLITE_WRAPPER_INT(sqlite3_column_bytes)
+SQLITE_WRAPPER_INT(sqlite3_column_text)
+SQLITE_WRAPPER_INT(sqlite3_changes)
+SQLITE_WRAPPER_INT(sqlite3_last_insert_rowid)
+
+void setup_sqlite_functions(void *sqlite_handle)
+{
+#define GETFUNC(name) f_ ## name = (name ## _t) (uintptr_t) __wrap_dlsym(sqlite_handle, #name)
+ GETFUNC(sqlite3_open);
+ GETFUNC(sqlite3_errmsg);
+ GETFUNC(sqlite3_prepare_v2);
+ GETFUNC(sqlite3_bind_parameter_count);
+ GETFUNC(sqlite3_bind_null);
+ GETFUNC(sqlite3_bind_text);
+ GETFUNC(sqlite3_step);
+ GETFUNC(sqlite3_column_count);
+ GETFUNC(sqlite3_finalize);
+ GETFUNC(sqlite3_close);
+ GETFUNC(sqlite3_column_name);
+ GETFUNC(sqlite3_column_type);
+ GETFUNC(sqlite3_column_blob);
+ GETFUNC(sqlite3_column_bytes);
+ GETFUNC(sqlite3_column_text);
+ GETFUNC(sqlite3_changes);
+ GETFUNC(sqlite3_last_insert_rowid);
+#undef GETFUNC
+}
+
+static bool initialized = false;
+static jclass stringClass;
+static jclass objectClass;
+static jclass byteBufferClass;
+static jclass cursorClass;
+static jmethodID jByteBufferAllocateDirect;
+static jmethodID jCursorConstructor;
+static jmethodID jCursorAddRow;
+
+static jobject sqliteInternalCall(JNIEnv* jenv, sqlite3 *db, jstring jQuery,
+ jobjectArray jParams, jlongArray jQueryRes);
+
+static void throwSqliteException(JNIEnv* jenv, const char* aFormat, ...)
+{
+ va_list ap;
+ va_start(ap, aFormat);
+ char* msg = nullptr;
+ vasprintf(&msg, aFormat, ap);
+ LOG("Error in SQLiteBridge: %s\n", msg);
+ JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException", msg);
+ free(msg);
+ va_end(ap);
+}
+
+static void
+JNI_Setup(JNIEnv* jenv)
+{
+ if (initialized) return;
+
+ jclass lObjectClass = jenv->FindClass("java/lang/Object");
+ jclass lStringClass = jenv->FindClass("java/lang/String");
+ jclass lByteBufferClass = jenv->FindClass("java/nio/ByteBuffer");
+ jclass lCursorClass = jenv->FindClass("org/mozilla/gecko/sqlite/MatrixBlobCursor");
+
+ if (lStringClass == nullptr
+ || lObjectClass == nullptr
+ || lByteBufferClass == nullptr
+ || lCursorClass == nullptr) {
+ throwSqliteException(jenv, "FindClass error");
+ return;
+ }
+
+ // Those are only local references. Make them global so they work
+ // across calls and threads.
+ objectClass = (jclass)jenv->NewGlobalRef(lObjectClass);
+ stringClass = (jclass)jenv->NewGlobalRef(lStringClass);
+ byteBufferClass = (jclass)jenv->NewGlobalRef(lByteBufferClass);
+ cursorClass = (jclass)jenv->NewGlobalRef(lCursorClass);
+
+ if (stringClass == nullptr || objectClass == nullptr
+ || byteBufferClass == nullptr
+ || cursorClass == nullptr) {
+ throwSqliteException(jenv, "NewGlobalRef error");
+ return;
+ }
+
+ // public static ByteBuffer allocateDirect(int capacity)
+ jByteBufferAllocateDirect =
+ jenv->GetStaticMethodID(byteBufferClass, "allocateDirect", "(I)Ljava/nio/ByteBuffer;");
+ // new MatrixBlobCursor(String [])
+ jCursorConstructor =
+ jenv->GetMethodID(cursorClass, "<init>", "([Ljava/lang/String;)V");
+ // public void addRow (Object[] columnValues)
+ jCursorAddRow =
+ jenv->GetMethodID(cursorClass, "addRow", "([Ljava/lang/Object;)V");
+
+ if (jByteBufferAllocateDirect == nullptr
+ || jCursorConstructor == nullptr
+ || jCursorAddRow == nullptr) {
+ throwSqliteException(jenv, "GetMethodId error");
+ return;
+ }
+
+ initialized = true;
+}
+
+extern "C" NS_EXPORT jobject MOZ_JNICALL
+Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCall(JNIEnv* jenv, jclass,
+ jstring jDb,
+ jstring jQuery,
+ jobjectArray jParams,
+ jlongArray jQueryRes)
+{
+ JNI_Setup(jenv);
+
+ int rc;
+ jobject jCursor = nullptr;
+ const char* dbPath;
+ sqlite3 *db;
+
+ dbPath = jenv->GetStringUTFChars(jDb, nullptr);
+ rc = f_sqlite3_open(dbPath, &db);
+ jenv->ReleaseStringUTFChars(jDb, dbPath);
+ if (rc != SQLITE_OK) {
+ throwSqliteException(jenv,
+ "Can't open database: %s", f_sqlite3_errmsg(db));
+ f_sqlite3_close(db); // close db even if open failed
+ return nullptr;
+ }
+ jCursor = sqliteInternalCall(jenv, db, jQuery, jParams, jQueryRes);
+ f_sqlite3_close(db);
+ return jCursor;
+}
+
+extern "C" NS_EXPORT jobject MOZ_JNICALL
+Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCallWithDb(JNIEnv* jenv, jclass,
+ jlong jDb,
+ jstring jQuery,
+ jobjectArray jParams,
+ jlongArray jQueryRes)
+{
+ JNI_Setup(jenv);
+
+ jobject jCursor = nullptr;
+ sqlite3 *db = (sqlite3*)jDb;
+ jCursor = sqliteInternalCall(jenv, db, jQuery, jParams, jQueryRes);
+ return jCursor;
+}
+
+extern "C" NS_EXPORT jlong MOZ_JNICALL
+Java_org_mozilla_gecko_sqlite_SQLiteBridge_openDatabase(JNIEnv* jenv, jclass,
+ jstring jDb)
+{
+ JNI_Setup(jenv);
+
+ int rc;
+ const char* dbPath;
+ sqlite3 *db;
+
+ dbPath = jenv->GetStringUTFChars(jDb, nullptr);
+ rc = f_sqlite3_open(dbPath, &db);
+ jenv->ReleaseStringUTFChars(jDb, dbPath);
+ if (rc != SQLITE_OK) {
+ throwSqliteException(jenv,
+ "Can't open database: %s", f_sqlite3_errmsg(db));
+ f_sqlite3_close(db); // close db even if open failed
+ return 0;
+ }
+ return (jlong)db;
+}
+
+extern "C" NS_EXPORT void MOZ_JNICALL
+Java_org_mozilla_gecko_sqlite_SQLiteBridge_closeDatabase(JNIEnv* jenv, jclass,
+ jlong jDb)
+{
+ JNI_Setup(jenv);
+
+ sqlite3 *db = (sqlite3*)jDb;
+ f_sqlite3_close(db);
+}
+
+static jobject
+sqliteInternalCall(JNIEnv* jenv,
+ sqlite3 *db,
+ jstring jQuery,
+ jobjectArray jParams,
+ jlongArray jQueryRes)
+{
+ JNI_Setup(jenv);
+
+ jobject jCursor = nullptr;
+ jsize numPars = 0;
+
+ const char *pzTail;
+ sqlite3_stmt *ppStmt;
+ int rc;
+
+ const char* queryStr;
+ queryStr = jenv->GetStringUTFChars(jQuery, nullptr);
+
+ rc = f_sqlite3_prepare_v2(db, queryStr, -1, &ppStmt, &pzTail);
+ if (rc != SQLITE_OK || ppStmt == nullptr) {
+ throwSqliteException(jenv,
+ "Can't prepare statement: %s", f_sqlite3_errmsg(db));
+ return nullptr;
+ }
+ jenv->ReleaseStringUTFChars(jQuery, queryStr);
+
+ // Check if number of parameters matches
+ if (jParams != nullptr) {
+ numPars = jenv->GetArrayLength(jParams);
+ }
+ int sqlNumPars;
+ sqlNumPars = f_sqlite3_bind_parameter_count(ppStmt);
+ if (numPars != sqlNumPars) {
+ throwSqliteException(jenv,
+ "Passed parameter count (%d) "
+ "doesn't match SQL parameter count (%d)",
+ numPars, sqlNumPars);
+ return nullptr;
+ }
+
+ if (jParams != nullptr) {
+ // Bind parameters, if any
+ if (numPars > 0) {
+ for (int i = 0; i < numPars; i++) {
+ jobject jObjectParam = jenv->GetObjectArrayElement(jParams, i);
+ // IsInstanceOf or isAssignableFrom? String is final, so IsInstanceOf
+ // should be OK.
+ jboolean isString = jenv->IsInstanceOf(jObjectParam, stringClass);
+ if (isString != JNI_TRUE) {
+ throwSqliteException(jenv,
+ "Parameter is not of String type");
+ return nullptr;
+ }
+
+ // SQLite parameters index from 1.
+ if (jObjectParam == nullptr) {
+ rc = f_sqlite3_bind_null(ppStmt, i + 1);
+ } else {
+ jstring jStringParam = (jstring) jObjectParam;
+ const char* paramStr = jenv->GetStringUTFChars(jStringParam, nullptr);
+ rc = f_sqlite3_bind_text(ppStmt, i + 1, paramStr, -1, SQLITE_TRANSIENT);
+ jenv->ReleaseStringUTFChars(jStringParam, paramStr);
+ }
+
+ if (rc != SQLITE_OK) {
+ throwSqliteException(jenv, "Error binding query parameter");
+ return nullptr;
+ }
+ }
+ }
+ }
+
+ // Execute the query and step through the results
+ rc = f_sqlite3_step(ppStmt);
+ if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
+ throwSqliteException(jenv,
+ "Can't step statement: (%d) %s", rc, f_sqlite3_errmsg(db));
+ return nullptr;
+ }
+
+ // Get the column count and names
+ int cols;
+ cols = f_sqlite3_column_count(ppStmt);
+
+ {
+ // Allocate a String[cols]
+ jobjectArray jStringArray = jenv->NewObjectArray(cols,
+ stringClass,
+ nullptr);
+ if (jStringArray == nullptr) {
+ throwSqliteException(jenv, "Can't allocate String[]");
+ return nullptr;
+ }
+
+ // Assign column names to the String[]
+ for (int i = 0; i < cols; i++) {
+ const char* colName = f_sqlite3_column_name(ppStmt, i);
+ jstring jStr = jenv->NewStringUTF(colName);
+ jenv->SetObjectArrayElement(jStringArray, i, jStr);
+ }
+
+ // Construct the MatrixCursor(String[]) with given column names
+ jCursor = jenv->NewObject(cursorClass,
+ jCursorConstructor,
+ jStringArray);
+ if (jCursor == nullptr) {
+ throwSqliteException(jenv, "Can't allocate MatrixBlobCursor");
+ return nullptr;
+ }
+ }
+
+ // Return the id and number of changed rows in jQueryRes
+ {
+ jlong id = f_sqlite3_last_insert_rowid(db);
+ jenv->SetLongArrayRegion(jQueryRes, 0, 1, &id);
+
+ jlong changed = f_sqlite3_changes(db);
+ jenv->SetLongArrayRegion(jQueryRes, 1, 1, &changed);
+ }
+
+ // For each row, add an Object[] to the passed ArrayList,
+ // with that containing either String or ByteArray objects
+ // containing the columns
+ while (rc != SQLITE_DONE) {
+ // Process row
+ // Construct Object[]
+ jobjectArray jRow = jenv->NewObjectArray(cols,
+ objectClass,
+ nullptr);
+ if (jRow == nullptr) {
+ throwSqliteException(jenv, "Can't allocate jRow Object[]");
+ return nullptr;
+ }
+
+ for (int i = 0; i < cols; i++) {
+ int colType = f_sqlite3_column_type(ppStmt, i);
+ if (colType == SQLITE_BLOB) {
+ // Treat as blob
+ const void* blob = f_sqlite3_column_blob(ppStmt, i);
+ int colLen = f_sqlite3_column_bytes(ppStmt, i);
+
+ // Construct ByteBuffer of correct size
+ jobject jByteBuffer =
+ jenv->CallStaticObjectMethod(byteBufferClass,
+ jByteBufferAllocateDirect,
+ colLen);
+ if (jByteBuffer == nullptr) {
+ throwSqliteException(jenv,
+ "Failure calling ByteBuffer.allocateDirect");
+ return nullptr;
+ }
+
+ // Get its backing array
+ void* bufferArray = jenv->GetDirectBufferAddress(jByteBuffer);
+ if (bufferArray == nullptr) {
+ throwSqliteException(jenv,
+ "Failure calling GetDirectBufferAddress");
+ return nullptr;
+ }
+ memcpy(bufferArray, blob, colLen);
+
+ jenv->SetObjectArrayElement(jRow, i, jByteBuffer);
+ jenv->DeleteLocalRef(jByteBuffer);
+ } else if (colType == SQLITE_NULL) {
+ jenv->SetObjectArrayElement(jRow, i, nullptr);
+ } else {
+ // Treat everything else as text
+ const char* txt = (const char*)f_sqlite3_column_text(ppStmt, i);
+ jstring jStr = jenv->NewStringUTF(txt);
+ jenv->SetObjectArrayElement(jRow, i, jStr);
+ jenv->DeleteLocalRef(jStr);
+ }
+ }
+
+ // Append Object[] to Cursor
+ jenv->CallVoidMethod(jCursor, jCursorAddRow, jRow);
+
+ // Clean up
+ jenv->DeleteLocalRef(jRow);
+
+ // Get next row
+ rc = f_sqlite3_step(ppStmt);
+ // Real error?
+ if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
+ throwSqliteException(jenv,
+ "Can't re-step statement:(%d) %s", rc, f_sqlite3_errmsg(db));
+ return nullptr;
+ }
+ }
+
+ rc = f_sqlite3_finalize(ppStmt);
+ if (rc != SQLITE_OK) {
+ throwSqliteException(jenv,
+ "Can't finalize statement: %s", f_sqlite3_errmsg(db));
+ return nullptr;
+ }
+
+ return jCursor;
+}
diff --git a/mozglue/android/SQLiteBridge.h b/mozglue/android/SQLiteBridge.h
new file mode 100644
index 000000000..f2ede820f
--- /dev/null
+++ b/mozglue/android/SQLiteBridge.h
@@ -0,0 +1,34 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef SQLiteBridge_h
+#define SQLiteBridge_h
+
+#include "sqlite3.h"
+
+void setup_sqlite_functions(void *sqlite_handle);
+
+#define SQLITE_WRAPPER(name, return_type, args...) \
+typedef return_type (*name ## _t)(args); \
+extern name ## _t f_ ## name;
+
+SQLITE_WRAPPER(sqlite3_open, int, const char*, sqlite3**)
+SQLITE_WRAPPER(sqlite3_errmsg, const char*, sqlite3*)
+SQLITE_WRAPPER(sqlite3_prepare_v2, int, sqlite3*, const char*, int, sqlite3_stmt**, const char**)
+SQLITE_WRAPPER(sqlite3_bind_parameter_count, int, sqlite3_stmt*)
+SQLITE_WRAPPER(sqlite3_bind_text, int, sqlite3_stmt*, int, const char*, int, void(*)(void*))
+SQLITE_WRAPPER(sqlite3_bind_null, int, sqlite3_stmt*, int)
+SQLITE_WRAPPER(sqlite3_step, int, sqlite3_stmt*)
+SQLITE_WRAPPER(sqlite3_column_count, int, sqlite3_stmt*)
+SQLITE_WRAPPER(sqlite3_finalize, int, sqlite3_stmt*)
+SQLITE_WRAPPER(sqlite3_close, int, sqlite3*)
+SQLITE_WRAPPER(sqlite3_column_name, const char*, sqlite3_stmt*, int)
+SQLITE_WRAPPER(sqlite3_column_type, int, sqlite3_stmt*, int)
+SQLITE_WRAPPER(sqlite3_column_blob, const void*, sqlite3_stmt*, int)
+SQLITE_WRAPPER(sqlite3_column_bytes, int, sqlite3_stmt*, int)
+SQLITE_WRAPPER(sqlite3_column_text, const unsigned char*, sqlite3_stmt*, int)
+SQLITE_WRAPPER(sqlite3_changes, int, sqlite3*)
+SQLITE_WRAPPER(sqlite3_last_insert_rowid, sqlite3_int64, sqlite3*)
+
+#endif /* SQLiteBridge_h */
diff --git a/mozglue/android/SharedMemNatives.cpp b/mozglue/android/SharedMemNatives.cpp
new file mode 100644
index 000000000..d186d6e21
--- /dev/null
+++ b/mozglue/android/SharedMemNatives.cpp
@@ -0,0 +1,65 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include <jni.h>
+#include <string.h>
+#include <sys/mman.h>
+
+extern "C" {
+
+JNIEXPORT
+void JNICALL
+Java_org_mozilla_gecko_mozglue_SharedMemBuffer_nativeReadFromDirectBuffer(JNIEnv* jenv, jclass, jobject src, jlong dest, jint offset, jint size)
+{
+ uint8_t* from = static_cast<uint8_t*>(jenv->GetDirectBufferAddress(src));
+ if (from == nullptr) {
+ jenv->ThrowNew(jenv->FindClass("java/lang/NullPointerException"), "Null direct buffer");
+ return;
+ }
+
+ void* to = reinterpret_cast<void*>(dest);
+ if (to == nullptr) {
+ jenv->ThrowNew(jenv->FindClass("java/lang/NullPointerException"), "Null shared memory buffer");
+ return;
+ }
+
+ memcpy(to, from + offset, size);
+}
+
+JNIEXPORT
+void JNICALL
+Java_org_mozilla_gecko_mozglue_SharedMemBuffer_nativeWriteToDirectBuffer(JNIEnv* jenv, jclass, jlong src, jobject dest, jint offset, jint size)
+{
+ uint8_t* from = reinterpret_cast<uint8_t*>(src);
+ if (from == nullptr) {
+ jenv->ThrowNew(jenv->FindClass("java/lang/NullPointerException"), "Null shared memory buffer");
+ return;
+ }
+
+ void* to = jenv->GetDirectBufferAddress(dest);
+ if (to == nullptr) {
+ jenv->ThrowNew(jenv->FindClass("java/lang/NullPointerException"), "Null direct buffer");
+ return;
+ }
+
+ memcpy(to, from + offset, size);
+}
+
+JNIEXPORT
+jlong JNICALL
+Java_org_mozilla_gecko_mozglue_SharedMemory_map(JNIEnv *env, jobject jobj, jint fd, jint length)
+{
+ void* address = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ return jlong(address);
+}
+
+JNIEXPORT
+void JNICALL
+Java_org_mozilla_gecko_mozglue_SharedMemory_unmap(JNIEnv *env, jobject jobj, jlong address, jint size)
+{
+ munmap((void*)address, (size_t)size);
+}
+
+} \ No newline at end of file
diff --git a/mozglue/android/moz.build b/mozglue/android/moz.build
new file mode 100644
index 000000000..cdc5ffb3c
--- /dev/null
+++ b/mozglue/android/moz.build
@@ -0,0 +1,58 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# 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/.
+
+EXPORTS += [
+ 'APKOpen.h',
+]
+
+SOURCES += [
+ 'APKOpen.cpp',
+ 'NativeCrypto.cpp',
+ 'nsGeckoUtils.cpp',
+ 'NSSBridge.cpp',
+ 'pbkdf2_sha256.c',
+ 'SharedMemNatives.cpp',
+ 'SQLiteBridge.cpp',
+]
+
+FINAL_LIBRARY = 'mozglue'
+
+for var in ('ANDROID_PACKAGE_NAME',
+ 'ANDROID_CPU_ARCH'):
+ DEFINES[var] = '"%s"' % CONFIG[var]
+
+if CONFIG['MOZ_FOLD_LIBS']:
+ DEFINES['MOZ_FOLD_LIBS'] = True
+
+LOCAL_INCLUDES += [
+ '!/build',
+ '../linker',
+ '/db/sqlite3/src',
+ '/ipc/chromium/src',
+ '/nsprpub/lib/ds',
+ '/nsprpub/lib/libc/include',
+ '/nsprpub/pr/include',
+ '/security/nss/lib/base',
+ '/security/nss/lib/certdb',
+ '/security/nss/lib/cryptohi',
+ '/security/nss/lib/dev',
+ '/security/nss/lib/freebl',
+ '/security/nss/lib/nss',
+ '/security/nss/lib/pk11wrap',
+ '/security/nss/lib/pkcs7',
+ '/security/nss/lib/pki',
+ '/security/nss/lib/smime',
+ '/security/nss/lib/softoken',
+ '/security/nss/lib/ssl',
+ '/security/nss/lib/util',
+ '/toolkit/components/startup',
+ '/xpcom/build',
+]
+
+DISABLE_STL_WRAPPING = True
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/mozglue/android/nsGeckoUtils.cpp b/mozglue/android/nsGeckoUtils.cpp
new file mode 100644
index 000000000..22818a746
--- /dev/null
+++ b/mozglue/android/nsGeckoUtils.cpp
@@ -0,0 +1,124 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include <jni.h>
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include "APKOpen.h"
+#include "Zip.h"
+#include "mozilla/RefPtr.h"
+
+extern "C"
+__attribute__ ((visibility("default")))
+void MOZ_JNICALL
+Java_org_mozilla_gecko_mozglue_GeckoLoader_putenv(JNIEnv *jenv, jclass, jstring map)
+{
+ const char* str;
+ // XXX: java doesn't give us true UTF8, we should figure out something
+ // better to do here
+ str = jenv->GetStringUTFChars(map, nullptr);
+ if (str == nullptr)
+ return;
+ putenv(strdup(str));
+ jenv->ReleaseStringUTFChars(map, str);
+}
+
+extern "C"
+__attribute__ ((visibility("default")))
+jobject MOZ_JNICALL
+Java_org_mozilla_gecko_mozglue_DirectBufferAllocator_nativeAllocateDirectBuffer(JNIEnv *jenv, jclass, jlong size)
+{
+ jobject buffer = nullptr;
+ void* mem = malloc(size);
+ if (mem) {
+ buffer = jenv->NewDirectByteBuffer(mem, size);
+ if (!buffer)
+ free(mem);
+ }
+ return buffer;
+}
+
+extern "C"
+__attribute__ ((visibility("default")))
+void MOZ_JNICALL
+Java_org_mozilla_gecko_mozglue_DirectBufferAllocator_nativeFreeDirectBuffer(JNIEnv *jenv, jclass, jobject buf)
+{
+ free(jenv->GetDirectBufferAddress(buf));
+}
+
+extern "C"
+__attribute__ ((visibility("default")))
+jlong MOZ_JNICALL
+Java_org_mozilla_gecko_mozglue_NativeZip_getZip(JNIEnv *jenv, jclass, jstring path)
+{
+ const char* str;
+ str = jenv->GetStringUTFChars(path, nullptr);
+ if (!str || !*str) {
+ if (str)
+ jenv->ReleaseStringUTFChars(path, str);
+ JNI_Throw(jenv, "java/lang/IllegalArgumentException", "Invalid path");
+ return 0;
+ }
+ RefPtr<Zip> zip = ZipCollection::GetZip(str);
+ jenv->ReleaseStringUTFChars(path, str);
+ if (!zip) {
+ JNI_Throw(jenv, "java/lang/IllegalArgumentException", "Invalid path or invalid zip");
+ return 0;
+ }
+ return reinterpret_cast<jlong>(zip.forget().take());
+}
+
+extern "C"
+__attribute__ ((visibility("default")))
+jlong MOZ_JNICALL
+Java_org_mozilla_gecko_mozglue_NativeZip_getZipFromByteBuffer(JNIEnv *jenv, jclass, jobject buffer)
+{
+ void *buf = jenv->GetDirectBufferAddress(buffer);
+ size_t size = jenv->GetDirectBufferCapacity(buffer);
+ RefPtr<Zip> zip = Zip::Create(buf, size);
+ if (!zip) {
+ JNI_Throw(jenv, "java/lang/IllegalArgumentException", "Invalid zip");
+ return 0;
+ }
+ return reinterpret_cast<jlong>(zip.forget().take());
+}
+
+ extern "C"
+__attribute__ ((visibility("default")))
+void MOZ_JNICALL
+Java_org_mozilla_gecko_mozglue_NativeZip__1release(JNIEnv *jenv, jclass, jlong obj)
+{
+ Zip *zip = (Zip *)obj;
+ zip->Release();
+}
+
+extern "C"
+__attribute__ ((visibility("default")))
+jobject MOZ_JNICALL
+Java_org_mozilla_gecko_mozglue_NativeZip__1getInputStream(JNIEnv *jenv, jobject jzip, jlong obj, jstring path)
+{
+ Zip *zip = (Zip *)obj;
+ const char* str;
+ str = jenv->GetStringUTFChars(path, nullptr);
+
+ Zip::Stream stream;
+ bool res = zip->GetStream(str, &stream);
+ jenv->ReleaseStringUTFChars(path, str);
+ if (!res) {
+ return nullptr;
+ }
+ jobject buf = jenv->NewDirectByteBuffer(const_cast<void *>(stream.GetBuffer()), stream.GetSize());
+ if (!buf) {
+ JNI_Throw(jenv, "java/lang/RuntimeException", "Failed to create ByteBuffer");
+ return nullptr;
+ }
+ jclass nativeZip = jenv->GetObjectClass(jzip);
+ jmethodID method = jenv->GetMethodID(nativeZip, "createInputStream", "(Ljava/nio/ByteBuffer;I)Ljava/io/InputStream;");
+ // Since this function is only expected to be called from Java, it is safe
+ // to skip exception checking for the method call below, as long as no
+ // other Native -> Java call doesn't happen before returning to Java.
+ return jenv->CallObjectMethod(jzip, method, buf, (jint) stream.GetType());
+}
diff --git a/mozglue/android/pbkdf2_sha256.c b/mozglue/android/pbkdf2_sha256.c
new file mode 100644
index 000000000..8e90f386a
--- /dev/null
+++ b/mozglue/android/pbkdf2_sha256.c
@@ -0,0 +1,432 @@
+/*-
+ * Copyright 2005,2007,2009 Colin Percival
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/types.h>
+
+#include <stdint.h>
+#include <string.h>
+
+#include <sys/endian.h>
+
+#include "pbkdf2_sha256.h"
+
+static inline uint32_t
+be32dec(const void *pp)
+{
+ const uint8_t *p = (uint8_t const *)pp;
+
+ return ((uint32_t)(p[3]) +
+ ((uint32_t)(p[2]) << 8) +
+ ((uint32_t)(p[1]) << 16) +
+ ((uint32_t)(p[0]) << 24));
+}
+
+static inline void
+be32enc(void *pp, uint32_t x)
+{
+ uint8_t * p = (uint8_t *)pp;
+
+ p[3] = x & 0xff;
+ p[2] = (x >> 8) & 0xff;
+ p[1] = (x >> 16) & 0xff;
+ p[0] = (x >> 24) & 0xff;
+}
+
+/*
+ * Encode a length len/4 vector of (uint32_t) into a length len vector of
+ * (unsigned char) in big-endian form. Assumes len is a multiple of 4.
+ */
+static void
+be32enc_vect(unsigned char *dst, const uint32_t *src, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len / 4; i++)
+ be32enc(dst + i * 4, src[i]);
+}
+
+/*
+ * Decode a big-endian length len vector of (unsigned char) into a length
+ * len/4 vector of (uint32_t). Assumes len is a multiple of 4.
+ */
+static void
+be32dec_vect(uint32_t *dst, const unsigned char *src, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len / 4; i++)
+ dst[i] = be32dec(src + i * 4);
+}
+
+/* Elementary functions used by SHA256 */
+#define Ch(x, y, z) ((x & (y ^ z)) ^ z)
+#define Maj(x, y, z) ((x & (y | z)) | (y & z))
+#define SHR(x, n) (x >> n)
+#define ROTR(x, n) ((x >> n) | (x << (32 - n)))
+#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
+#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
+#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3))
+#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10))
+
+/* SHA256 round function */
+#define RND(a, b, c, d, e, f, g, h, k) \
+ t0 = h + S1(e) + Ch(e, f, g) + k; \
+ t1 = S0(a) + Maj(a, b, c); \
+ d += t0; \
+ h = t0 + t1;
+
+/* Adjusted round function for rotating state */
+#define RNDr(S, W, i, k) \
+ RND(S[(64 - i) % 8], S[(65 - i) % 8], \
+ S[(66 - i) % 8], S[(67 - i) % 8], \
+ S[(68 - i) % 8], S[(69 - i) % 8], \
+ S[(70 - i) % 8], S[(71 - i) % 8], \
+ W[i] + k)
+
+/*
+ * SHA256 block compression function. The 256-bit state is transformed via
+ * the 512-bit input block to produce a new state.
+ */
+static void
+SHA256_Transform(uint32_t * state, const unsigned char block[64])
+{
+ uint32_t W[64];
+ uint32_t S[8];
+ uint32_t t0, t1;
+ int i;
+
+ /* 1. Prepare message schedule W. */
+ be32dec_vect(W, block, 64);
+ for (i = 16; i < 64; i++)
+ W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16];
+
+ /* 2. Initialize working variables. */
+ memcpy(S, state, 32);
+
+ /* 3. Mix. */
+ RNDr(S, W, 0, 0x428a2f98);
+ RNDr(S, W, 1, 0x71374491);
+ RNDr(S, W, 2, 0xb5c0fbcf);
+ RNDr(S, W, 3, 0xe9b5dba5);
+ RNDr(S, W, 4, 0x3956c25b);
+ RNDr(S, W, 5, 0x59f111f1);
+ RNDr(S, W, 6, 0x923f82a4);
+ RNDr(S, W, 7, 0xab1c5ed5);
+ RNDr(S, W, 8, 0xd807aa98);
+ RNDr(S, W, 9, 0x12835b01);
+ RNDr(S, W, 10, 0x243185be);
+ RNDr(S, W, 11, 0x550c7dc3);
+ RNDr(S, W, 12, 0x72be5d74);
+ RNDr(S, W, 13, 0x80deb1fe);
+ RNDr(S, W, 14, 0x9bdc06a7);
+ RNDr(S, W, 15, 0xc19bf174);
+ RNDr(S, W, 16, 0xe49b69c1);
+ RNDr(S, W, 17, 0xefbe4786);
+ RNDr(S, W, 18, 0x0fc19dc6);
+ RNDr(S, W, 19, 0x240ca1cc);
+ RNDr(S, W, 20, 0x2de92c6f);
+ RNDr(S, W, 21, 0x4a7484aa);
+ RNDr(S, W, 22, 0x5cb0a9dc);
+ RNDr(S, W, 23, 0x76f988da);
+ RNDr(S, W, 24, 0x983e5152);
+ RNDr(S, W, 25, 0xa831c66d);
+ RNDr(S, W, 26, 0xb00327c8);
+ RNDr(S, W, 27, 0xbf597fc7);
+ RNDr(S, W, 28, 0xc6e00bf3);
+ RNDr(S, W, 29, 0xd5a79147);
+ RNDr(S, W, 30, 0x06ca6351);
+ RNDr(S, W, 31, 0x14292967);
+ RNDr(S, W, 32, 0x27b70a85);
+ RNDr(S, W, 33, 0x2e1b2138);
+ RNDr(S, W, 34, 0x4d2c6dfc);
+ RNDr(S, W, 35, 0x53380d13);
+ RNDr(S, W, 36, 0x650a7354);
+ RNDr(S, W, 37, 0x766a0abb);
+ RNDr(S, W, 38, 0x81c2c92e);
+ RNDr(S, W, 39, 0x92722c85);
+ RNDr(S, W, 40, 0xa2bfe8a1);
+ RNDr(S, W, 41, 0xa81a664b);
+ RNDr(S, W, 42, 0xc24b8b70);
+ RNDr(S, W, 43, 0xc76c51a3);
+ RNDr(S, W, 44, 0xd192e819);
+ RNDr(S, W, 45, 0xd6990624);
+ RNDr(S, W, 46, 0xf40e3585);
+ RNDr(S, W, 47, 0x106aa070);
+ RNDr(S, W, 48, 0x19a4c116);
+ RNDr(S, W, 49, 0x1e376c08);
+ RNDr(S, W, 50, 0x2748774c);
+ RNDr(S, W, 51, 0x34b0bcb5);
+ RNDr(S, W, 52, 0x391c0cb3);
+ RNDr(S, W, 53, 0x4ed8aa4a);
+ RNDr(S, W, 54, 0x5b9cca4f);
+ RNDr(S, W, 55, 0x682e6ff3);
+ RNDr(S, W, 56, 0x748f82ee);
+ RNDr(S, W, 57, 0x78a5636f);
+ RNDr(S, W, 58, 0x84c87814);
+ RNDr(S, W, 59, 0x8cc70208);
+ RNDr(S, W, 60, 0x90befffa);
+ RNDr(S, W, 61, 0xa4506ceb);
+ RNDr(S, W, 62, 0xbef9a3f7);
+ RNDr(S, W, 63, 0xc67178f2);
+
+ /* 4. Mix local working variables into global state. */
+ for (i = 0; i < 8; i++)
+ state[i] += S[i];
+
+ /* Clean the stack. */
+ memset(W, 0, 256);
+ memset(S, 0, 32);
+ t0 = t1 = 0;
+}
+
+static unsigned char PAD[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* Add padding and terminating bit-count. */
+static void
+SHA256_Pad(SHA256_CTX * ctx)
+{
+ unsigned char len[8];
+ uint32_t r, plen;
+
+ /*
+ * Convert length to a vector of bytes -- we do this now rather
+ * than later because the length will change after we pad.
+ */
+ be32enc_vect(len, ctx->count, 8);
+
+ /* Add 1--64 bytes so that the resulting length is 56 mod 64. */
+ r = (ctx->count[1] >> 3) & 0x3f;
+ plen = (r < 56) ? (56 - r) : (120 - r);
+ SHA256_Update(ctx, PAD, (size_t)plen);
+
+ /* Add the terminating bit-count. */
+ SHA256_Update(ctx, len, 8);
+}
+
+/* SHA-256 initialization. Begins a SHA-256 operation. */
+void
+SHA256_Init(SHA256_CTX * ctx)
+{
+
+ /* Zero bits processed so far. */
+ ctx->count[0] = ctx->count[1] = 0;
+
+ /* Magic initialization constants. */
+ ctx->state[0] = 0x6A09E667;
+ ctx->state[1] = 0xBB67AE85;
+ ctx->state[2] = 0x3C6EF372;
+ ctx->state[3] = 0xA54FF53A;
+ ctx->state[4] = 0x510E527F;
+ ctx->state[5] = 0x9B05688C;
+ ctx->state[6] = 0x1F83D9AB;
+ ctx->state[7] = 0x5BE0CD19;
+}
+
+/* Add bytes into the hash. */
+void
+SHA256_Update(SHA256_CTX * ctx, const void *in, size_t len)
+{
+ uint32_t bitlen[2];
+ uint32_t r;
+ const unsigned char *src = in;
+
+ /* Number of bytes left in the buffer from previous updates. */
+ r = (ctx->count[1] >> 3) & 0x3f;
+
+ /* Convert the length into a number of bits. */
+ bitlen[1] = ((uint32_t)len) << 3;
+ bitlen[0] = (uint32_t)(len >> 29);
+
+ /* Update number of bits. */
+ if ((ctx->count[1] += bitlen[1]) < bitlen[1])
+ ctx->count[0]++;
+ ctx->count[0] += bitlen[0];
+
+ /* Handle the case where we don't need to perform any transforms. */
+ if (len < 64 - r) {
+ memcpy(&ctx->buf[r], src, len);
+ return;
+ }
+
+ /* Finish the current block. */
+ memcpy(&ctx->buf[r], src, 64 - r);
+ SHA256_Transform(ctx->state, ctx->buf);
+ src += 64 - r;
+ len -= 64 - r;
+
+ /* Perform complete blocks. */
+ while (len >= 64) {
+ SHA256_Transform(ctx->state, src);
+ src += 64;
+ len -= 64;
+ }
+
+ /* Copy left over data into buffer. */
+ memcpy(ctx->buf, src, len);
+}
+
+/*
+ * SHA-256 finalization. Pads the input data, exports the hash value,
+ * and clears the context state.
+ */
+void
+SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx)
+{
+
+ /* Add padding. */
+ SHA256_Pad(ctx);
+
+ /* Write the hash. */
+ be32enc_vect(digest, ctx->state, 32);
+
+ /* Clear the context state. */
+ memset((void *)ctx, 0, sizeof(*ctx));
+}
+
+/* Initialize an HMAC-SHA256 operation with the given key. */
+void
+HMAC_SHA256_Init(HMAC_SHA256_CTX * ctx, const void * _K, size_t Klen)
+{
+ unsigned char pad[64];
+ unsigned char khash[32];
+ const unsigned char * K = _K;
+ size_t i;
+
+ /* If Klen > 64, the key is really SHA256(K). */
+ if (Klen > 64) {
+ SHA256_Init(&ctx->ictx);
+ SHA256_Update(&ctx->ictx, K, Klen);
+ SHA256_Final(khash, &ctx->ictx);
+ K = khash;
+ Klen = 32;
+ }
+
+ /* Inner SHA256 operation is SHA256(K xor [block of 0x36] || data). */
+ SHA256_Init(&ctx->ictx);
+ memset(pad, 0x36, 64);
+ for (i = 0; i < Klen; i++)
+ pad[i] ^= K[i];
+ SHA256_Update(&ctx->ictx, pad, 64);
+
+ /* Outer SHA256 operation is SHA256(K xor [block of 0x5c] || hash). */
+ SHA256_Init(&ctx->octx);
+ memset(pad, 0x5c, 64);
+ for (i = 0; i < Klen; i++)
+ pad[i] ^= K[i];
+ SHA256_Update(&ctx->octx, pad, 64);
+
+ /* Clean the stack. */
+ memset(khash, 0, 32);
+}
+
+/* Add bytes to the HMAC-SHA256 operation. */
+void
+HMAC_SHA256_Update(HMAC_SHA256_CTX * ctx, const void *in, size_t len)
+{
+
+ /* Feed data to the inner SHA256 operation. */
+ SHA256_Update(&ctx->ictx, in, len);
+}
+
+/* Finish an HMAC-SHA256 operation. */
+void
+HMAC_SHA256_Final(unsigned char digest[32], HMAC_SHA256_CTX * ctx)
+{
+ unsigned char ihash[32];
+
+ /* Finish the inner SHA256 operation. */
+ SHA256_Final(ihash, &ctx->ictx);
+
+ /* Feed the inner hash to the outer SHA256 operation. */
+ SHA256_Update(&ctx->octx, ihash, 32);
+
+ /* Finish the outer SHA256 operation. */
+ SHA256_Final(digest, &ctx->octx);
+
+ /* Clean the stack. */
+ memset(ihash, 0, 32);
+}
+
+/**
+ * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen):
+ * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and
+ * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1).
+ */
+void
+PBKDF2_SHA256(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt,
+ size_t saltlen, uint64_t c, uint8_t * buf, size_t dkLen)
+{
+ HMAC_SHA256_CTX PShctx, hctx;
+ size_t i;
+ uint8_t ivec[4];
+ uint8_t U[32];
+ uint8_t T[32];
+ uint64_t j;
+ int k;
+ size_t clen;
+
+ /* Compute HMAC state after processing P and S. */
+ HMAC_SHA256_Init(&PShctx, passwd, passwdlen);
+ HMAC_SHA256_Update(&PShctx, salt, saltlen);
+
+ /* Iterate through the blocks. */
+ for (i = 0; i * 32 < dkLen; i++) {
+ /* Generate INT(i + 1). */
+ be32enc(ivec, (uint32_t)(i + 1));
+
+ /* Compute U_1 = PRF(P, S || INT(i)). */
+ memcpy(&hctx, &PShctx, sizeof(HMAC_SHA256_CTX));
+ HMAC_SHA256_Update(&hctx, ivec, 4);
+ HMAC_SHA256_Final(U, &hctx);
+
+ /* T_i = U_1 ... */
+ memcpy(T, U, 32);
+
+ for (j = 2; j <= c; j++) {
+ /* Compute U_j. */
+ HMAC_SHA256_Init(&hctx, passwd, passwdlen);
+ HMAC_SHA256_Update(&hctx, U, 32);
+ HMAC_SHA256_Final(U, &hctx);
+
+ /* ... xor U_j ... */
+ for (k = 0; k < 32; k++)
+ T[k] ^= U[k];
+ }
+
+ /* Copy as many bytes as necessary into buf. */
+ clen = dkLen - i * 32;
+ if (clen > 32)
+ clen = 32;
+ memcpy(&buf[i * 32], T, clen);
+ }
+
+ /* Clean PShctx, since we never called _Final on it. */
+ memset(&PShctx, 0, sizeof(HMAC_SHA256_CTX));
+}
diff --git a/mozglue/android/pbkdf2_sha256.h b/mozglue/android/pbkdf2_sha256.h
new file mode 100644
index 000000000..ca2af7a8f
--- /dev/null
+++ b/mozglue/android/pbkdf2_sha256.h
@@ -0,0 +1,70 @@
+/*-
+ * Copyright 2005,2007,2009 Colin Percival
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libmd/sha256.h,v 1.2 2006/01/17 15:35:56 phk Exp $
+ */
+
+#ifndef _SHA256_H_
+#define _SHA256_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+#include <stdint.h>
+
+typedef struct SHA256Context {
+ uint32_t state[8];
+ uint32_t count[2];
+ unsigned char buf[64];
+} SHA256_CTX;
+
+typedef struct HMAC_SHA256Context {
+ SHA256_CTX ictx;
+ SHA256_CTX octx;
+} HMAC_SHA256_CTX;
+
+void SHA256_Init(SHA256_CTX *);
+void SHA256_Update(SHA256_CTX *, const void *, size_t);
+void SHA256_Final(unsigned char [32], SHA256_CTX *);
+void HMAC_SHA256_Init(HMAC_SHA256_CTX *, const void *, size_t);
+void HMAC_SHA256_Update(HMAC_SHA256_CTX *, const void *, size_t);
+void HMAC_SHA256_Final(unsigned char [32], HMAC_SHA256_CTX *);
+
+/**
+ * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen):
+ * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and
+ * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1).
+ */
+void PBKDF2_SHA256(const uint8_t *, size_t, const uint8_t *, size_t,
+ uint64_t, uint8_t *, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_SHA256_H_ */
diff --git a/mozglue/build/AsanOptions.cpp b/mozglue/build/AsanOptions.cpp
new file mode 100644
index 000000000..729c61ef3
--- /dev/null
+++ b/mozglue/build/AsanOptions.cpp
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include "mozilla/Attributes.h"
+
+#ifndef _MSC_VER // Not supported by clang-cl yet
+
+// When running with AddressSanitizer, we need to explicitly set some
+// options specific to our codebase to prevent errors during runtime.
+// To override these, set the ASAN_OPTIONS environment variable.
+//
+// Currently, these are:
+//
+// allow_user_segv_handler=1 - Tell ASan to allow our code to use its
+// own SIGSEGV handlers. This is required by ASM.js internally.
+//
+// alloc_dealloc_mismatch=0 - Disable alloc-dealloc mismatch checking
+// in ASan. This is required because we define our own new/delete
+// operators that are backed by malloc/free. If one of them gets inlined
+// while the other doesn't, ASan will report false positives.
+//
+// detect_leaks=0 - Disable LeakSanitizer. This is required because
+// otherwise leak checking will be enabled for various building and
+// testing executables where we don't care much about leaks. Enabling
+// this will also likely require setting LSAN_OPTIONS with a suppression
+// file, as in build/sanitizers/lsan_suppressions.txt.
+//
+extern "C" MOZ_ASAN_BLACKLIST
+const char* __asan_default_options() {
+ return "allow_user_segv_handler=1:alloc_dealloc_mismatch=0:detect_leaks=0";
+}
+
+#endif
diff --git a/mozglue/build/BionicGlue.cpp b/mozglue/build/BionicGlue.cpp
new file mode 100644
index 000000000..208fcce68
--- /dev/null
+++ b/mozglue/build/BionicGlue.cpp
@@ -0,0 +1,157 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include <pthread.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <android/log.h>
+#include <sys/syscall.h>
+
+#include "mozilla/Alignment.h"
+
+#include <vector>
+
+#define NS_EXPORT __attribute__ ((visibility("default")))
+
+#if ANDROID_VERSION < 17 || defined(MOZ_WIDGET_ANDROID)
+/* Android doesn't have pthread_atfork(), so we need to use our own. */
+struct AtForkFuncs {
+ void (*prepare)(void);
+ void (*parent)(void);
+ void (*child)(void);
+};
+
+/* jemalloc's initialization calls pthread_atfork. When pthread_atfork (see
+ * further below) stores the corresponding data, it's going to allocate memory,
+ * which will loop back to jemalloc's initialization, leading to a dead-lock.
+ * So, for that specific vector, we use a special allocator that returns a
+ * static buffer for small sizes, and force the initial vector capacity to
+ * a size enough to store one atfork function table. */
+template <typename T>
+struct SpecialAllocator: public std::allocator<T>
+{
+ SpecialAllocator(): bufUsed(false) {}
+
+ inline typename std::allocator<T>::pointer allocate(typename std::allocator<T>::size_type n, const void * = 0) {
+ if (!bufUsed && n == 1) {
+ bufUsed = true;
+ return buf.addr();
+ }
+ return reinterpret_cast<T *>(::operator new(sizeof(T) * n));
+ }
+
+ inline void deallocate(typename std::allocator<T>::pointer p, typename std::allocator<T>::size_type n) {
+ if (p == buf.addr())
+ bufUsed = false;
+ else
+ ::operator delete(p);
+ }
+
+ template<typename U>
+ struct rebind {
+ typedef SpecialAllocator<U> other;
+ };
+
+private:
+ mozilla::AlignedStorage2<T> buf;
+ bool bufUsed;
+};
+
+static std::vector<AtForkFuncs, SpecialAllocator<AtForkFuncs> > atfork;
+#endif
+
+#ifdef MOZ_WIDGET_GONK
+#include "cpuacct.h"
+
+#if ANDROID_VERSION < 17 || defined(MOZ_WIDGET_ANDROID)
+extern "C" NS_EXPORT int
+timer_create(clockid_t, struct sigevent*, timer_t*)
+{
+ __android_log_print(ANDROID_LOG_ERROR, "BionicGlue", "timer_create not supported!");
+ abort();
+ return -1;
+}
+#endif
+
+#else
+#define cpuacct_add(x)
+#endif
+
+#if ANDROID_VERSION < 17 || defined(MOZ_WIDGET_ANDROID)
+extern "C" NS_EXPORT int
+pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
+{
+ AtForkFuncs funcs;
+ funcs.prepare = prepare;
+ funcs.parent = parent;
+ funcs.child = child;
+ if (!atfork.capacity())
+ atfork.reserve(1);
+ atfork.push_back(funcs);
+ return 0;
+}
+
+extern "C" NS_EXPORT pid_t __fork(void);
+
+extern "C" NS_EXPORT pid_t
+fork(void)
+{
+ pid_t pid;
+ for (auto it = atfork.rbegin();
+ it < atfork.rend(); ++it)
+ if (it->prepare)
+ it->prepare();
+
+ switch ((pid = syscall(__NR_clone, SIGCHLD, NULL, NULL, NULL, NULL))) {
+ case 0:
+ cpuacct_add(getuid());
+ for (auto it = atfork.begin();
+ it < atfork.end(); ++it)
+ if (it->child)
+ it->child();
+ break;
+ default:
+ for (auto it = atfork.begin();
+ it < atfork.end(); ++it)
+ if (it->parent)
+ it->parent();
+ }
+ return pid;
+}
+#endif
+
+extern "C" NS_EXPORT int
+raise(int sig)
+{
+ // Bug 741272: Bionic incorrectly uses kill(), which signals the
+ // process, and thus could signal another thread (and let this one
+ // return "successfully" from raising a fatal signal).
+ //
+ // Bug 943170: POSIX specifies pthread_kill(pthread_self(), sig) as
+ // equivalent to raise(sig), but Bionic also has a bug with these
+ // functions, where a forked child will kill its parent instead.
+
+ extern pid_t gettid(void);
+ return syscall(__NR_tgkill, getpid(), gettid(), sig);
+}
+
+/* Flash plugin uses symbols that are not present in Android >= 4.4 */
+#ifndef MOZ_WIDGET_GONK
+namespace android {
+ namespace VectorImpl {
+ NS_EXPORT void reservedVectorImpl1(void) { }
+ NS_EXPORT void reservedVectorImpl2(void) { }
+ NS_EXPORT void reservedVectorImpl3(void) { }
+ NS_EXPORT void reservedVectorImpl4(void) { }
+ NS_EXPORT void reservedVectorImpl5(void) { }
+ NS_EXPORT void reservedVectorImpl6(void) { }
+ NS_EXPORT void reservedVectorImpl7(void) { }
+ NS_EXPORT void reservedVectorImpl8(void) { }
+ }
+}
+#endif
+
diff --git a/mozglue/build/Makefile.in b/mozglue/build/Makefile.in
new file mode 100644
index 000000000..1cfa41ea0
--- /dev/null
+++ b/mozglue/build/Makefile.in
@@ -0,0 +1,23 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# 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/.
+
+# For FORCE_SHARED_LIB
+include $(topsrcdir)/config/config.mk
+
+ifeq (WINNT,$(OS_TARGET))
+mozglue.def: mozglue.def.in $(GLOBAL_DEPS)
+ $(call py_action,preprocessor,$(if $(MOZ_REPLACE_MALLOC),-DMOZ_REPLACE_MALLOC) $(ACDEFINES) $< -o $@)
+
+GARBAGE += mozglue.def
+endif
+
+include $(topsrcdir)/mozglue/build/replace_malloc.mk
+
+ifdef MOZ_LINKER
+ifeq (arm, $(TARGET_CPU))
+OS_LDFLAGS += -Wl,-version-script,$(srcdir)/arm-eabi-filter
+endif
+
+endif
diff --git a/mozglue/build/SSE.cpp b/mozglue/build/SSE.cpp
new file mode 100644
index 000000000..8ad228684
--- /dev/null
+++ b/mozglue/build/SSE.cpp
@@ -0,0 +1,206 @@
+/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+/* compile-time and runtime tests for whether to use SSE instructions */
+
+#include "SSE.h"
+
+#ifdef HAVE_CPUID_H
+// cpuid.h is available on gcc 4.3 and higher on i386 and x86_64
+#include <cpuid.h>
+#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64))
+// MSVC 2005 or newer on x86-32 or x86-64
+#include <intrin.h>
+#endif
+
+namespace {
+
+// SSE.h has parallel #ifs which declare MOZILLA_SSE_HAVE_CPUID_DETECTION.
+// We can't declare these functions in the header file, however, because
+// <intrin.h> conflicts with <windows.h> on MSVC 2005, and some files want to
+// include both SSE.h and <windows.h>.
+
+#ifdef HAVE_CPUID_H
+
+enum CPUIDRegister { eax = 0, ebx = 1, ecx = 2, edx = 3 };
+
+static bool
+has_cpuid_bits(unsigned int level, CPUIDRegister reg, unsigned int bits)
+{
+ unsigned int regs[4];
+ unsigned int eax, ebx, ecx, edx;
+ unsigned max = __get_cpuid_max(0, NULL);
+ if (level > max)
+ return false;
+ __cpuid_count(level, 0, eax, ebx, ecx, edx);
+ regs[0] = eax;
+ regs[1] = ebx;
+ regs[2] = ecx;
+ regs[3] = edx;
+ return (regs[reg] & bits) == bits;
+}
+
+#if !defined(MOZILLA_PRESUME_AVX)
+static uint64_t xgetbv(uint32_t xcr) {
+ uint32_t eax, edx;
+ __asm__ ( ".byte 0x0f, 0x01, 0xd0" : "=a"(eax), "=d"(edx) : "c"(xcr));
+ return (uint64_t)(edx) << 32 | eax;
+}
+#endif
+
+#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64))
+
+enum CPUIDRegister { eax = 0, ebx = 1, ecx = 2, edx = 3 };
+
+static bool
+has_cpuid_bits(unsigned int level, CPUIDRegister reg, unsigned int bits)
+{
+ // Check that the level in question is supported.
+ int regs[4];
+ __cpuid(regs, level & 0x80000000u);
+ if (unsigned(regs[0]) < level)
+ return false;
+
+ // "The __cpuid intrinsic clears the ECX register before calling the cpuid instruction."
+ __cpuid(regs, level);
+ return (unsigned(regs[reg]) & bits) == bits;
+}
+
+#if !defined(MOZILLA_PRESUME_AVX)
+static uint64_t xgetbv(uint32_t xcr) { return _xgetbv(xcr); }
+#endif
+
+#elif (defined(__GNUC__) || defined(__SUNPRO_CC)) && (defined(__i386) || defined(__x86_64__))
+
+enum CPUIDRegister { eax = 0, ebx = 1, ecx = 2, edx = 3 };
+
+#ifdef __i386
+static void
+moz_cpuid(int CPUInfo[4], int InfoType)
+{
+ asm (
+ "xchg %esi, %ebx\n"
+ "xor %ecx, %ecx\n" // ecx is the sub-leaf (we only ever need 0)
+ "cpuid\n"
+ "movl %eax, (%edi)\n"
+ "movl %ebx, 4(%edi)\n"
+ "movl %ecx, 8(%edi)\n"
+ "movl %edx, 12(%edi)\n"
+ "xchg %esi, %ebx\n"
+ :
+ : "a"(InfoType), // %eax
+ "D"(CPUInfo) // %edi
+ : "%ecx", "%edx", "%esi"
+ );
+}
+#else
+static void
+moz_cpuid(int CPUInfo[4], int InfoType)
+{
+ asm (
+ "xchg %rsi, %rbx\n"
+ "xor %ecx, %ecx\n" // ecx is the sub-leaf (we only ever need 0)
+ "cpuid\n"
+ "movl %eax, (%rdi)\n"
+ "movl %ebx, 4(%rdi)\n"
+ "movl %ecx, 8(%rdi)\n"
+ "movl %edx, 12(%rdi)\n"
+ "xchg %rsi, %rbx\n"
+ :
+ : "a"(InfoType), // %eax
+ "D"(CPUInfo) // %rdi
+ : "%ecx", "%edx", "%rsi"
+ );
+}
+#endif
+
+static bool
+has_cpuid_bits(unsigned int level, CPUIDRegister reg, unsigned int bits)
+{
+ // Check that the level in question is supported.
+ volatile int regs[4];
+ moz_cpuid((int *)regs, level & 0x80000000u);
+ if (unsigned(regs[0]) < level)
+ return false;
+
+ moz_cpuid((int *)regs, level);
+ return (unsigned(regs[reg]) & bits) == bits;
+}
+
+#endif // end CPUID declarations
+
+} // namespace
+
+namespace mozilla {
+
+namespace sse_private {
+
+#if defined(MOZILLA_SSE_HAVE_CPUID_DETECTION)
+
+#if !defined(MOZILLA_PRESUME_MMX)
+ bool mmx_enabled = has_cpuid_bits(1u, edx, (1u<<23));
+#endif
+
+#if !defined(MOZILLA_PRESUME_SSE)
+ bool sse_enabled = has_cpuid_bits(1u, edx, (1u<<25));
+#endif
+
+#if !defined(MOZILLA_PRESUME_SSE2)
+ bool sse2_enabled = has_cpuid_bits(1u, edx, (1u<<26));
+#endif
+
+#if !defined(MOZILLA_PRESUME_SSE3)
+ bool sse3_enabled = has_cpuid_bits(1u, ecx, (1u<<0));
+#endif
+
+#if !defined(MOZILLA_PRESUME_SSSE3)
+ bool ssse3_enabled = has_cpuid_bits(1u, ecx, (1u<<9));
+#endif
+
+#if !defined(MOZILLA_PRESUME_SSE4A)
+ bool sse4a_enabled = has_cpuid_bits(0x80000001u, ecx, (1u<<6));
+#endif
+
+#if !defined(MOZILLA_PRESUME_SSE4_1)
+ bool sse4_1_enabled = has_cpuid_bits(1u, ecx, (1u<<19));
+#endif
+
+#if !defined(MOZILLA_PRESUME_SSE4_2)
+ bool sse4_2_enabled = has_cpuid_bits(1u, ecx, (1u<<20));
+#endif
+
+#if !defined(MOZILLA_PRESUME_AVX) || !defined(MOZILLA_PRESUME_AVX2)
+ static bool has_avx()
+ {
+#if defined(MOZILLA_PRESUME_AVX)
+ return true;
+#else
+ const unsigned AVX = 1u << 28;
+ const unsigned OSXSAVE = 1u << 27;
+ const unsigned XSAVE = 1u << 26;
+
+ const unsigned XMM_STATE = 1u << 1;
+ const unsigned YMM_STATE = 1u << 2;
+ const unsigned AVX_STATE = XMM_STATE | YMM_STATE;
+
+ return has_cpuid_bits(1u, ecx, AVX | OSXSAVE | XSAVE) &&
+ // ensure the OS supports XSAVE of YMM registers
+ (xgetbv(0) & AVX_STATE) == AVX_STATE;
+#endif // MOZILLA_PRESUME_AVX
+ }
+#endif // !MOZILLA_PRESUME_AVX || !MOZILLA_PRESUME_AVX2
+
+#if !defined(MOZILLA_PRESUME_AVX)
+ bool avx_enabled = has_avx();
+#endif
+
+#if !defined(MOZILLA_PRESUME_AVX2)
+ bool avx2_enabled = has_avx() && has_cpuid_bits(7u, ebx, (1u<<5));
+#endif
+
+#endif
+
+} // namespace sse_private
+} // namespace mozilla
diff --git a/mozglue/build/SSE.h b/mozglue/build/SSE.h
new file mode 100644
index 000000000..fe7788640
--- /dev/null
+++ b/mozglue/build/SSE.h
@@ -0,0 +1,332 @@
+/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+/* compile-time and runtime tests for whether to use SSE instructions */
+
+#ifndef mozilla_SSE_h_
+#define mozilla_SSE_h_
+
+// for definition of MFBT_DATA
+#include "mozilla/Types.h"
+
+/**
+ * The public interface of this header consists of a set of macros and
+ * functions for Intel CPU features.
+ *
+ * DETECTING ISA EXTENSIONS
+ * ========================
+ *
+ * This header provides the following functions for determining whether the
+ * current CPU supports a particular instruction set extension:
+ *
+ * mozilla::supports_mmx
+ * mozilla::supports_sse
+ * mozilla::supports_sse2
+ * mozilla::supports_sse3
+ * mozilla::supports_ssse3
+ * mozilla::supports_sse4a
+ * mozilla::supports_sse4_1
+ * mozilla::supports_sse4_2
+ * mozilla::supports_avx
+ * mozilla::supports_avx2
+ *
+ * If you're writing code using inline assembly, you should guard it with a
+ * call to one of these functions. For instance:
+ *
+ * if (mozilla::supports_sse2()) {
+ * asm(" ... ");
+ * }
+ * else {
+ * ...
+ * }
+ *
+ * Note that these functions depend on cpuid intrinsics only available in gcc
+ * 4.3 or later and MSVC 8.0 (Visual C++ 2005) or later, so they return false
+ * in older compilers. (This could be fixed by replacing the code with inline
+ * assembly.)
+ *
+ *
+ * USING INTRINSICS
+ * ================
+ *
+ * This header also provides support for coding using CPU intrinsics.
+ *
+ * For each mozilla::supports_abc function, we define a MOZILLA_MAY_SUPPORT_ABC
+ * macro which indicates that the target/compiler combination we're using is
+ * compatible with the ABC extension. For instance, x86_64 with MSVC 2003 is
+ * compatible with SSE2 but not SSE3, since although there exist x86_64 CPUs
+ * with SSE3 support, MSVC 2003 only supports through SSE2.
+ *
+ * Until gcc fixes #pragma target [1] [2] or our x86 builds require SSE2,
+ * you'll need to separate code using intrinsics into a file separate from your
+ * regular code. Here's the recommended pattern:
+ *
+ * #ifdef MOZILLA_MAY_SUPPORT_ABC
+ * namespace mozilla {
+ * namespace ABC {
+ * void foo();
+ * }
+ * }
+ * #endif
+ *
+ * void foo() {
+ * #ifdef MOZILLA_MAY_SUPPORT_ABC
+ * if (mozilla::supports_abc()) {
+ * mozilla::ABC::foo(); // in a separate file
+ * return;
+ * }
+ * #endif
+ *
+ * foo_unvectorized();
+ * }
+ *
+ * You'll need to define mozilla::ABC::foo() in a separate file and add the
+ * -mabc flag when using gcc.
+ *
+ * [1] http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39787 and
+ * [2] http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41201 being fixed.
+ *
+ */
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+
+#ifdef __MMX__
+ // It's ok to use MMX instructions based on the -march option (or
+ // the default for x86_64 or for Intel Mac).
+ #define MOZILLA_PRESUME_MMX 1
+#endif
+#ifdef __SSE__
+ // It's ok to use SSE instructions based on the -march option (or
+ // the default for x86_64 or for Intel Mac).
+ #define MOZILLA_PRESUME_SSE 1
+#endif
+#ifdef __SSE2__
+ // It's ok to use SSE2 instructions based on the -march option (or
+ // the default for x86_64 or for Intel Mac).
+ #define MOZILLA_PRESUME_SSE2 1
+#endif
+#ifdef __SSE3__
+ // It's ok to use SSE3 instructions based on the -march option (or the
+ // default for Intel Mac).
+ #define MOZILLA_PRESUME_SSE3 1
+#endif
+#ifdef __SSSE3__
+ // It's ok to use SSSE3 instructions based on the -march option.
+ #define MOZILLA_PRESUME_SSSE3 1
+#endif
+#ifdef __SSE4A__
+ // It's ok to use SSE4A instructions based on the -march option.
+ #define MOZILLA_PRESUME_SSE4A 1
+#endif
+#ifdef __SSE4_1__
+ // It's ok to use SSE4.1 instructions based on the -march option.
+ #define MOZILLA_PRESUME_SSE4_1 1
+#endif
+#ifdef __SSE4_2__
+ // It's ok to use SSE4.2 instructions based on the -march option.
+ #define MOZILLA_PRESUME_SSE4_2 1
+#endif
+#ifdef __AVX__
+ // It's ok to use AVX instructions based on the -march option.
+ #define MOZILLA_PRESUME_AVX 1
+#endif
+#ifdef __AVX2__
+ // It's ok to use AVX instructions based on the -march option.
+ #define MOZILLA_PRESUME_AVX2 1
+#endif
+
+
+
+#ifdef HAVE_CPUID_H
+ #define MOZILLA_SSE_HAVE_CPUID_DETECTION
+#endif
+
+#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64))
+
+#define MOZILLA_SSE_HAVE_CPUID_DETECTION
+
+#if defined(_M_IX86_FP)
+
+#if _M_IX86_FP >= 1
+ // It's ok to use SSE instructions based on the /arch option
+ #define MOZILLA_PRESUME_SSE
+#endif
+#if _M_IX86_FP >= 2
+ // It's ok to use SSE2 instructions based on the /arch option
+ #define MOZILLA_PRESUME_SSE2
+#endif
+
+#elif defined(_M_AMD64)
+ // MSVC for AMD64 doesn't support MMX, so don't presume it here.
+
+ // SSE is always available on AMD64.
+ #define MOZILLA_PRESUME_SSE
+ // SSE2 is always available on AMD64.
+ #define MOZILLA_PRESUME_SSE2
+#endif
+
+#elif defined(__SUNPRO_CC) && (defined(__i386) || defined(__x86_64__))
+// Sun Studio on x86 or amd64
+
+#define MOZILLA_SSE_HAVE_CPUID_DETECTION
+
+#if defined(__x86_64__)
+ // MMX is always available on AMD64.
+ #define MOZILLA_PRESUME_MMX
+ // SSE is always available on AMD64.
+ #define MOZILLA_PRESUME_SSE
+ // SSE2 is always available on AMD64.
+ #define MOZILLA_PRESUME_SSE2
+#endif
+
+#endif
+
+namespace mozilla {
+
+ namespace sse_private {
+#if defined(MOZILLA_SSE_HAVE_CPUID_DETECTION)
+#if !defined(MOZILLA_PRESUME_MMX)
+ extern bool MFBT_DATA mmx_enabled;
+#endif
+#if !defined(MOZILLA_PRESUME_SSE)
+ extern bool MFBT_DATA sse_enabled;
+#endif
+#if !defined(MOZILLA_PRESUME_SSE2)
+ extern bool MFBT_DATA sse2_enabled;
+#endif
+#if !defined(MOZILLA_PRESUME_SSE3)
+ extern bool MFBT_DATA sse3_enabled;
+#endif
+#if !defined(MOZILLA_PRESUME_SSSE3)
+ extern bool MFBT_DATA ssse3_enabled;
+#endif
+#if !defined(MOZILLA_PRESUME_SSE4A)
+ extern bool MFBT_DATA sse4a_enabled;
+#endif
+#if !defined(MOZILLA_PRESUME_SSE4_1)
+ extern bool MFBT_DATA sse4_1_enabled;
+#endif
+#if !defined(MOZILLA_PRESUME_SSE4_2)
+ extern bool MFBT_DATA sse4_2_enabled;
+#endif
+#if !defined(MOZILLA_PRESUME_AVX)
+ extern bool MFBT_DATA avx_enabled;
+#endif
+#if !defined(MOZILLA_PRESUME_AVX2)
+ extern bool MFBT_DATA avx2_enabled;
+#endif
+
+
+#endif
+ } // namespace sse_private
+
+#if defined(MOZILLA_PRESUME_MMX)
+#define MOZILLA_MAY_SUPPORT_MMX 1
+ inline bool supports_mmx() { return true; }
+#elif defined(MOZILLA_SSE_HAVE_CPUID_DETECTION)
+#if !(defined(_MSC_VER) && defined(_M_AMD64))
+ // Define MOZILLA_MAY_SUPPORT_MMX only if we're not on MSVC for
+ // AMD64, since that compiler doesn't support MMX.
+#define MOZILLA_MAY_SUPPORT_MMX 1
+#endif
+ inline bool supports_mmx() { return sse_private::mmx_enabled; }
+#else
+ inline bool supports_mmx() { return false; }
+#endif
+
+#if defined(MOZILLA_PRESUME_SSE)
+#define MOZILLA_MAY_SUPPORT_SSE 1
+ inline bool supports_sse() { return true; }
+#elif defined(MOZILLA_SSE_HAVE_CPUID_DETECTION)
+#define MOZILLA_MAY_SUPPORT_SSE 1
+ inline bool supports_sse() { return sse_private::sse_enabled; }
+#else
+ inline bool supports_sse() { return false; }
+#endif
+
+#if defined(MOZILLA_PRESUME_SSE2)
+#define MOZILLA_MAY_SUPPORT_SSE2 1
+ inline bool supports_sse2() { return true; }
+#elif defined(MOZILLA_SSE_HAVE_CPUID_DETECTION)
+#define MOZILLA_MAY_SUPPORT_SSE2 1
+ inline bool supports_sse2() { return sse_private::sse2_enabled; }
+#else
+ inline bool supports_sse2() { return false; }
+#endif
+
+#if defined(MOZILLA_PRESUME_SSE3)
+#define MOZILLA_MAY_SUPPORT_SSE3 1
+ inline bool supports_sse3() { return true; }
+#elif defined(MOZILLA_SSE_HAVE_CPUID_DETECTION)
+#define MOZILLA_MAY_SUPPORT_SSE3 1
+ inline bool supports_sse3() { return sse_private::sse3_enabled; }
+#else
+ inline bool supports_sse3() { return false; }
+#endif
+
+#if defined(MOZILLA_PRESUME_SSSE3)
+#define MOZILLA_MAY_SUPPORT_SSSE3 1
+ inline bool supports_ssse3() { return true; }
+#elif defined(MOZILLA_SSE_HAVE_CPUID_DETECTION)
+#define MOZILLA_MAY_SUPPORT_SSSE3 1
+ inline bool supports_ssse3() { return sse_private::ssse3_enabled; }
+#else
+ inline bool supports_ssse3() { return false; }
+#endif
+
+#if defined(MOZILLA_PRESUME_SSE4A)
+#define MOZILLA_MAY_SUPPORT_SSE4A 1
+ inline bool supports_sse4a() { return true; }
+#elif defined(MOZILLA_SSE_HAVE_CPUID_DETECTION)
+#define MOZILLA_MAY_SUPPORT_SSE4A 1
+ inline bool supports_sse4a() { return sse_private::sse4a_enabled; }
+#else
+ inline bool supports_sse4a() { return false; }
+#endif
+
+#if defined(MOZILLA_PRESUME_SSE4_1)
+#define MOZILLA_MAY_SUPPORT_SSE4_1 1
+ inline bool supports_sse4_1() { return true; }
+#elif defined(MOZILLA_SSE_HAVE_CPUID_DETECTION)
+#define MOZILLA_MAY_SUPPORT_SSE4_1 1
+ inline bool supports_sse4_1() { return sse_private::sse4_1_enabled; }
+#else
+ inline bool supports_sse4_1() { return false; }
+#endif
+
+#if defined(MOZILLA_PRESUME_SSE4_2)
+#define MOZILLA_MAY_SUPPORT_SSE4_2 1
+ inline bool supports_sse4_2() { return true; }
+#elif defined(MOZILLA_SSE_HAVE_CPUID_DETECTION)
+#define MOZILLA_MAY_SUPPORT_SSE4_2 1
+ inline bool supports_sse4_2() { return sse_private::sse4_2_enabled; }
+#else
+ inline bool supports_sse4_2() { return false; }
+#endif
+
+#if defined(MOZILLA_PRESUME_AVX)
+#define MOZILLA_MAY_SUPPORT_AVX 1
+ inline bool supports_avx() { return true; }
+#elif defined(MOZILLA_SSE_HAVE_CPUID_DETECTION)
+#define MOZILLA_MAY_SUPPORT_AVX 1
+ inline bool supports_avx() { return sse_private::avx_enabled; }
+#else
+ inline bool supports_avx() { return false; }
+#endif
+
+#if defined(MOZILLA_PRESUME_AVX2)
+#define MOZILLA_MAY_SUPPORT_AVX2 1
+ inline bool supports_avx2() { return true; }
+#elif defined(MOZILLA_SSE_HAVE_CPUID_DETECTION)
+#define MOZILLA_MAY_SUPPORT_AVX2 1
+ inline bool supports_avx2() { return sse_private::avx2_enabled; }
+#else
+ inline bool supports_avx2() { return false; }
+#endif
+
+
+} // namespace mozilla
+
+#endif /* !defined(mozilla_SSE_h_) */
diff --git a/mozglue/build/WindowsDllBlocklist.cpp b/mozglue/build/WindowsDllBlocklist.cpp
new file mode 100644
index 000000000..a3c662723
--- /dev/null
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -0,0 +1,831 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifdef MOZ_MEMORY
+#define MOZ_MEMORY_IMPL
+#include "mozmemory_wrap.h"
+#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC
+// See mozmemory_wrap.h for more details. This file is part of libmozglue, so
+// it needs to use _impl suffixes.
+#define MALLOC_DECL(name, return_type, ...) \
+ extern "C" MOZ_MEMORY_API return_type name ## _impl(__VA_ARGS__);
+#include "malloc_decls.h"
+#endif
+
+#include <windows.h>
+#include <winternl.h>
+#include <io.h>
+
+#pragma warning( push )
+#pragma warning( disable : 4275 4530 ) // See msvc-stl-wrapper.template.h
+#include <map>
+#pragma warning( pop )
+
+#include "nsAutoPtr.h"
+
+#include "nsWindowsDllInterceptor.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/WindowsVersion.h"
+#include "nsWindowsHelpers.h"
+#include "WindowsDllBlocklist.h"
+
+using namespace mozilla;
+
+#define ALL_VERSIONS ((unsigned long long)-1LL)
+
+// DLLs sometimes ship without a version number, particularly early
+// releases. Blocking "version <= 0" has the effect of blocking unversioned
+// DLLs (since the call to get version info fails), but not blocking
+// any versioned instance.
+#define UNVERSIONED ((unsigned long long)0LL)
+
+// Convert the 4 (decimal) components of a DLL version number into a
+// single unsigned long long, as needed by the blocklist
+#define MAKE_VERSION(a,b,c,d)\
+ ((a##ULL << 48) + (b##ULL << 32) + (c##ULL << 16) + d##ULL)
+
+struct DllBlockInfo {
+ // The name of the DLL -- in LOWERCASE! It will be compared to
+ // a lowercase version of the DLL name only.
+ const char *name;
+
+ // If maxVersion is ALL_VERSIONS, we'll block all versions of this
+ // dll. Otherwise, we'll block all versions less than or equal to
+ // the given version, as queried by GetFileVersionInfo and
+ // VS_FIXEDFILEINFO's dwFileVersionMS and dwFileVersionLS fields.
+ //
+ // Note that the version is usually 4 components, which is A.B.C.D
+ // encoded as 0x AAAA BBBB CCCC DDDD ULL (spaces added for clarity),
+ // but it's not required to be of that format.
+ //
+ // If the USE_TIMESTAMP flag is set, then we use the timestamp from
+ // the IMAGE_FILE_HEADER in lieu of a version number.
+ unsigned long long maxVersion;
+
+ enum {
+ FLAGS_DEFAULT = 0,
+ BLOCK_WIN8PLUS_ONLY = 1,
+ BLOCK_XP_ONLY = 2,
+ USE_TIMESTAMP = 4,
+ } flags;
+};
+
+static DllBlockInfo sWindowsDllBlocklist[] = {
+ // EXAMPLE:
+ // { "uxtheme.dll", ALL_VERSIONS },
+ // { "uxtheme.dll", 0x0000123400000000ULL },
+ // The DLL name must be in lowercase!
+ // The version field is a maximum, that is, we block anything that is
+ // less-than or equal to that version.
+
+ // NPFFAddon - Known malware
+ { "npffaddon.dll", ALL_VERSIONS},
+
+ // AVG 8 - Antivirus vendor AVG, old version, plugin already blocklisted
+ {"avgrsstx.dll", MAKE_VERSION(8,5,0,401)},
+
+ // calc.dll - Suspected malware
+ {"calc.dll", MAKE_VERSION(1,0,0,1)},
+
+ // hook.dll - Suspected malware
+ {"hook.dll", ALL_VERSIONS},
+
+ // GoogleDesktopNetwork3.dll - Extremely old, unversioned instances
+ // of this DLL cause crashes
+ {"googledesktopnetwork3.dll", UNVERSIONED},
+
+ // rdolib.dll - Suspected malware
+ {"rdolib.dll", MAKE_VERSION(6,0,88,4)},
+
+ // fgjk4wvb.dll - Suspected malware
+ {"fgjk4wvb.dll", MAKE_VERSION(8,8,8,8)},
+
+ // radhslib.dll - Naomi internet filter - unmaintained since 2006
+ {"radhslib.dll", UNVERSIONED},
+
+ // Music download filter for vkontakte.ru - old instances
+ // of this DLL cause crashes
+ {"vksaver.dll", MAKE_VERSION(2,2,2,0)},
+
+ // Topcrash in Firefox 4.0b1
+ {"rlxf.dll", MAKE_VERSION(1,2,323,1)},
+
+ // psicon.dll - Topcrashes in Thunderbird, and some crashes in Firefox
+ // Adobe photoshop library, now redundant in later installations
+ {"psicon.dll", ALL_VERSIONS},
+
+ // Topcrash in Firefox 4 betas (bug 618899)
+ {"accelerator.dll", MAKE_VERSION(3,2,1,6)},
+
+ // Topcrash with Roboform in Firefox 8 (bug 699134)
+ {"rf-firefox.dll", MAKE_VERSION(7,6,1,0)},
+ {"roboform.dll", MAKE_VERSION(7,6,1,0)},
+
+ // Topcrash with Babylon Toolbar on FF16+ (bug 721264)
+ {"babyfox.dll", ALL_VERSIONS},
+
+ // sprotector.dll crashes, bug 957258
+ {"sprotector.dll", ALL_VERSIONS},
+
+ // leave these two in always for tests
+ { "mozdllblockingtest.dll", ALL_VERSIONS },
+ { "mozdllblockingtest_versioned.dll", 0x0000000400000000ULL },
+
+ // Windows Media Foundation FLAC decoder and type sniffer (bug 839031).
+ { "mfflac.dll", ALL_VERSIONS },
+
+ // Older Relevant Knowledge DLLs cause us to crash (bug 904001).
+ { "rlnx.dll", MAKE_VERSION(1, 3, 334, 9) },
+ { "pmnx.dll", MAKE_VERSION(1, 3, 334, 9) },
+ { "opnx.dll", MAKE_VERSION(1, 3, 334, 9) },
+ { "prnx.dll", MAKE_VERSION(1, 3, 334, 9) },
+
+ // Older belgian ID card software causes Firefox to crash or hang on
+ // shutdown, bug 831285 and 918399.
+ { "beid35cardlayer.dll", MAKE_VERSION(3, 5, 6, 6968) },
+
+ // bug 925459, bitguard crashes
+ { "bitguard.dll", ALL_VERSIONS },
+
+ // bug 812683 - crashes in Windows library when Asus Gamer OSD is installed
+ // Software is discontinued/unsupported
+ { "atkdx11disp.dll", ALL_VERSIONS },
+
+ // Topcrash with Conduit SearchProtect, bug 944542
+ { "spvc32.dll", ALL_VERSIONS },
+
+ // XP topcrash with F-Secure, bug 970362
+ { "fs_ccf_ni_umh32.dll", MAKE_VERSION(1, 42, 101, 0), DllBlockInfo::BLOCK_XP_ONLY },
+
+ // Topcrash with V-bates, bug 1002748 and bug 1023239
+ { "libinject.dll", UNVERSIONED },
+ { "libinject2.dll", 0x537DDC93, DllBlockInfo::USE_TIMESTAMP },
+ { "libredir2.dll", 0x5385B7ED, DllBlockInfo::USE_TIMESTAMP },
+
+ // Crashes with RoboForm2Go written against old SDK, bug 988311/1196859
+ { "rf-firefox-22.dll", ALL_VERSIONS },
+ { "rf-firefox-40.dll", ALL_VERSIONS },
+
+ // Crashes with DesktopTemperature, bug 1046382
+ { "dtwxsvc.dll", 0x53153234, DllBlockInfo::USE_TIMESTAMP },
+
+ // Startup crashes with Lenovo Onekey Theater, bug 1123778
+ { "activedetect32.dll", UNVERSIONED },
+ { "activedetect64.dll", UNVERSIONED },
+ { "windowsapihookdll32.dll", UNVERSIONED },
+ { "windowsapihookdll64.dll", UNVERSIONED },
+
+ // Flash crashes with RealNetworks RealDownloader, bug 1132663
+ { "rndlnpshimswf.dll", ALL_VERSIONS },
+ { "rndlmainbrowserrecordplugin.dll", ALL_VERSIONS },
+
+ // Startup crashes with RealNetworks Browser Record Plugin, bug 1170141
+ { "nprpffbrowserrecordext.dll", ALL_VERSIONS },
+ { "nprndlffbrowserrecordext.dll", ALL_VERSIONS },
+
+ // Crashes with CyberLink YouCam, bug 1136968
+ { "ycwebcamerasource.ax", MAKE_VERSION(2, 0, 0, 1611) },
+
+ // Old version of WebcamMax crashes WebRTC, bug 1130061
+ { "vwcsource.ax", MAKE_VERSION(1, 5, 0, 0) },
+
+ // NetOp School, discontinued product, bug 763395
+ { "nlsp.dll", MAKE_VERSION(6, 23, 2012, 19) },
+
+ // Orbit Downloader, bug 1222819
+ { "grabdll.dll", MAKE_VERSION(2, 6, 1, 0) },
+ { "grabkernel.dll", MAKE_VERSION(1, 0, 0, 1) },
+
+ // ESET, bug 1229252
+ { "eoppmonitor.dll", ALL_VERSIONS },
+
+ // SS2OSD, bug 1262348
+ { "ss2osd.dll", ALL_VERSIONS },
+ { "ss2devprops.dll", ALL_VERSIONS },
+
+ // NHASUSSTRIXOSD.DLL, bug 1269244
+ { "nhasusstrixosd.dll", ALL_VERSIONS },
+ { "nhasusstrixdevprops.dll", ALL_VERSIONS },
+
+ // Crashes with PremierOpinion/RelevantKnowledge, bug 1277846
+ { "opls.dll", ALL_VERSIONS },
+ { "opls64.dll", ALL_VERSIONS },
+ { "pmls.dll", ALL_VERSIONS },
+ { "pmls64.dll", ALL_VERSIONS },
+ { "prls.dll", ALL_VERSIONS },
+ { "prls64.dll", ALL_VERSIONS },
+ { "rlls.dll", ALL_VERSIONS },
+ { "rlls64.dll", ALL_VERSIONS },
+
+ // Vorbis DirectShow filters, bug 1239690.
+ { "vorbis.acm", MAKE_VERSION(0, 0, 3, 6) },
+
+ // AhnLab Internet Security, bug 1311969
+ { "nzbrcom.dll", ALL_VERSIONS },
+
+ // K7TotalSecurity, bug 1339083.
+ { "k7pswsen.dll", MAKE_VERSION(15, 2, 2, 95) },
+
+ { nullptr, 0 }
+};
+
+#ifndef STATUS_DLL_NOT_FOUND
+#define STATUS_DLL_NOT_FOUND ((DWORD)0xC0000135L)
+#endif
+
+// define this for very verbose dll load debug spew
+#undef DEBUG_very_verbose
+
+static const char kBlockedDllsParameter[] = "BlockedDllList=";
+static const int kBlockedDllsParameterLen =
+ sizeof(kBlockedDllsParameter) - 1;
+
+static const char kBlocklistInitFailedParameter[] = "BlocklistInitFailed=1\n";
+static const int kBlocklistInitFailedParameterLen =
+ sizeof(kBlocklistInitFailedParameter) - 1;
+
+static const char kUser32BeforeBlocklistParameter[] = "User32BeforeBlocklist=1\n";
+static const int kUser32BeforeBlocklistParameterLen =
+ sizeof(kUser32BeforeBlocklistParameter) - 1;
+
+static DWORD sThreadLoadingXPCOMModule;
+static bool sBlocklistInitAttempted;
+static bool sBlocklistInitFailed;
+static bool sUser32BeforeBlocklist;
+
+// Duplicated from xpcom glue. Ideally this should be shared.
+void
+printf_stderr(const char *fmt, ...)
+{
+ if (IsDebuggerPresent()) {
+ char buf[2048];
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ buf[sizeof(buf) - 1] = '\0';
+ va_end(args);
+ OutputDebugStringA(buf);
+ }
+
+ FILE *fp = _fdopen(_dup(2), "a");
+ if (!fp)
+ return;
+
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(fp, fmt, args);
+ va_end(args);
+
+ fclose(fp);
+}
+
+namespace {
+
+typedef NTSTATUS (NTAPI *LdrLoadDll_func) (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle);
+
+static LdrLoadDll_func stub_LdrLoadDll = 0;
+
+template <class T>
+struct RVAMap {
+ RVAMap(HANDLE map, DWORD offset) {
+ SYSTEM_INFO info;
+ GetSystemInfo(&info);
+
+ DWORD alignedOffset = (offset / info.dwAllocationGranularity) *
+ info.dwAllocationGranularity;
+
+ MOZ_ASSERT(offset - alignedOffset < info.dwAllocationGranularity, "Wtf");
+
+ mRealView = ::MapViewOfFile(map, FILE_MAP_READ, 0, alignedOffset,
+ sizeof(T) + (offset - alignedOffset));
+
+ mMappedView = mRealView ? reinterpret_cast<T*>((char*)mRealView + (offset - alignedOffset)) :
+ nullptr;
+ }
+ ~RVAMap() {
+ if (mRealView) {
+ ::UnmapViewOfFile(mRealView);
+ }
+ }
+ operator const T*() const { return mMappedView; }
+ const T* operator->() const { return mMappedView; }
+private:
+ const T* mMappedView;
+ void* mRealView;
+};
+
+bool
+CheckASLR(const wchar_t* path)
+{
+ bool retval = false;
+
+ HANDLE file = ::CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
+ nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+ nullptr);
+ if (file != INVALID_HANDLE_VALUE) {
+ HANDLE map = ::CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0,
+ nullptr);
+ if (map) {
+ RVAMap<IMAGE_DOS_HEADER> peHeader(map, 0);
+ if (peHeader) {
+ RVAMap<IMAGE_NT_HEADERS> ntHeader(map, peHeader->e_lfanew);
+ if (ntHeader) {
+ // If the DLL has no code, permit it regardless of ASLR status.
+ if (ntHeader->OptionalHeader.SizeOfCode == 0) {
+ retval = true;
+ }
+ // Check to see if the DLL supports ASLR
+ else if ((ntHeader->OptionalHeader.DllCharacteristics &
+ IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0) {
+ retval = true;
+ }
+ }
+ }
+ ::CloseHandle(map);
+ }
+ ::CloseHandle(file);
+ }
+
+ return retval;
+}
+
+DWORD
+GetTimestamp(const wchar_t* path)
+{
+ DWORD timestamp = 0;
+
+ HANDLE file = ::CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
+ nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+ nullptr);
+ if (file != INVALID_HANDLE_VALUE) {
+ HANDLE map = ::CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0,
+ nullptr);
+ if (map) {
+ RVAMap<IMAGE_DOS_HEADER> peHeader(map, 0);
+ if (peHeader) {
+ RVAMap<IMAGE_NT_HEADERS> ntHeader(map, peHeader->e_lfanew);
+ if (ntHeader) {
+ timestamp = ntHeader->FileHeader.TimeDateStamp;
+ }
+ }
+ ::CloseHandle(map);
+ }
+ ::CloseHandle(file);
+ }
+
+ return timestamp;
+}
+
+// This lock protects both the reentrancy sentinel and the crash reporter
+// data structures.
+static CRITICAL_SECTION sLock;
+
+/**
+ * Some versions of Windows call LoadLibraryEx to get the version information
+ * for a DLL, which causes our patched LdrLoadDll implementation to re-enter
+ * itself and cause infinite recursion and a stack-exhaustion crash. We protect
+ * against reentrancy by allowing recursive loads of the same DLL.
+ *
+ * Note that we don't use __declspec(thread) because that doesn't work in DLLs
+ * loaded via LoadLibrary and there can be a limited number of TLS slots, so
+ * we roll our own.
+ */
+class ReentrancySentinel
+{
+public:
+ explicit ReentrancySentinel(const char* dllName)
+ {
+ DWORD currentThreadId = GetCurrentThreadId();
+ AutoCriticalSection lock(&sLock);
+ mPreviousDllName = (*sThreadMap)[currentThreadId];
+
+ // If there is a DLL currently being loaded and it has the same name
+ // as the current attempt, we're re-entering.
+ mReentered = mPreviousDllName && !stricmp(mPreviousDllName, dllName);
+ (*sThreadMap)[currentThreadId] = dllName;
+ }
+
+ ~ReentrancySentinel()
+ {
+ DWORD currentThreadId = GetCurrentThreadId();
+ AutoCriticalSection lock(&sLock);
+ (*sThreadMap)[currentThreadId] = mPreviousDllName;
+ }
+
+ bool BailOut() const
+ {
+ return mReentered;
+ };
+
+ static void InitializeStatics()
+ {
+ InitializeCriticalSection(&sLock);
+ sThreadMap = new std::map<DWORD, const char*>;
+ }
+
+private:
+ static std::map<DWORD, const char*>* sThreadMap;
+
+ const char* mPreviousDllName;
+ bool mReentered;
+};
+
+std::map<DWORD, const char*>* ReentrancySentinel::sThreadMap;
+
+/**
+ * This is a linked list of DLLs that have been blocked. It doesn't use
+ * mozilla::LinkedList because this is an append-only list and doesn't need
+ * to be doubly linked.
+ */
+class DllBlockSet
+{
+public:
+ static void Add(const char* name, unsigned long long version);
+
+ // Write the list of blocked DLLs to a file HANDLE. This method is run after
+ // a crash occurs and must therefore not use the heap, etc.
+ static void Write(HANDLE file);
+
+private:
+ DllBlockSet(const char* name, unsigned long long version)
+ : mName(name)
+ , mVersion(version)
+ , mNext(nullptr)
+ {
+ }
+
+ const char* mName; // points into the sWindowsDllBlocklist string
+ unsigned long long mVersion;
+ DllBlockSet* mNext;
+
+ static DllBlockSet* gFirst;
+};
+
+DllBlockSet* DllBlockSet::gFirst;
+
+void
+DllBlockSet::Add(const char* name, unsigned long long version)
+{
+ AutoCriticalSection lock(&sLock);
+ for (DllBlockSet* b = gFirst; b; b = b->mNext) {
+ if (0 == strcmp(b->mName, name) && b->mVersion == version) {
+ return;
+ }
+ }
+ // Not already present
+ DllBlockSet* n = new DllBlockSet(name, version);
+ n->mNext = gFirst;
+ gFirst = n;
+}
+
+void
+DllBlockSet::Write(HANDLE file)
+{
+ // It would be nicer to use AutoCriticalSection here. However, its destructor
+ // might not run if an exception occurs, in which case we would never leave
+ // the critical section. (MSVC warns about this possibility.) So we
+ // enter and leave manually.
+ ::EnterCriticalSection(&sLock);
+
+ // Because this method is called after a crash occurs, and uses heap memory,
+ // protect this entire block with a structured exception handler.
+ MOZ_SEH_TRY {
+ DWORD nBytes;
+ for (DllBlockSet* b = gFirst; b; b = b->mNext) {
+ // write name[,v.v.v.v];
+ WriteFile(file, b->mName, strlen(b->mName), &nBytes, nullptr);
+ if (b->mVersion != -1) {
+ WriteFile(file, ",", 1, &nBytes, nullptr);
+ uint16_t parts[4];
+ parts[0] = b->mVersion >> 48;
+ parts[1] = (b->mVersion >> 32) & 0xFFFF;
+ parts[2] = (b->mVersion >> 16) & 0xFFFF;
+ parts[3] = b->mVersion & 0xFFFF;
+ for (int p = 0; p < 4; ++p) {
+ char buf[32];
+ ltoa(parts[p], buf, 10);
+ WriteFile(file, buf, strlen(buf), &nBytes, nullptr);
+ if (p != 3) {
+ WriteFile(file, ".", 1, &nBytes, nullptr);
+ }
+ }
+ }
+ WriteFile(file, ";", 1, &nBytes, nullptr);
+ }
+ }
+ MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { }
+
+ ::LeaveCriticalSection(&sLock);
+}
+
+static UniquePtr<wchar_t[]>
+getFullPath (PWCHAR filePath, wchar_t* fname)
+{
+ // In Windows 8, the first parameter seems to be used for more than just the
+ // path name. For example, its numerical value can be 1. Passing a non-valid
+ // pointer to SearchPathW will cause a crash, so we need to check to see if we
+ // are handed a valid pointer, and otherwise just pass nullptr to SearchPathW.
+ PWCHAR sanitizedFilePath = nullptr;
+ if ((uintptr_t(filePath) >= 65536) && ((uintptr_t(filePath) & 1) == 0)) {
+ sanitizedFilePath = filePath;
+ }
+
+ // figure out the length of the string that we need
+ DWORD pathlen = SearchPathW(sanitizedFilePath, fname, L".dll", 0, nullptr,
+ nullptr);
+ if (pathlen == 0) {
+ return nullptr;
+ }
+
+ auto full_fname = MakeUnique<wchar_t[]>(pathlen+1);
+ if (!full_fname) {
+ // couldn't allocate memory?
+ return nullptr;
+ }
+
+ // now actually grab it
+ SearchPathW(sanitizedFilePath, fname, L".dll", pathlen + 1, full_fname.get(),
+ nullptr);
+ return full_fname;
+}
+
+// No builtin function to find the last character matching a set
+static wchar_t* lastslash(wchar_t* s, int len)
+{
+ for (wchar_t* c = s + len - 1; c >= s; --c) {
+ if (*c == L'\\' || *c == L'/') {
+ return c;
+ }
+ }
+ return nullptr;
+}
+
+static NTSTATUS NTAPI
+patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle)
+{
+ // We have UCS2 (UTF16?), we want ASCII, but we also just want the filename portion
+#define DLLNAME_MAX 128
+ char dllName[DLLNAME_MAX+1];
+ wchar_t *dll_part;
+ char *dot;
+ DllBlockInfo *info;
+
+ int len = moduleFileName->Length / 2;
+ wchar_t *fname = moduleFileName->Buffer;
+ UniquePtr<wchar_t[]> full_fname;
+
+ // The filename isn't guaranteed to be null terminated, but in practice
+ // it always will be; ensure that this is so, and bail if not.
+ // This is done instead of the more robust approach because of bug 527122,
+ // where lots of weird things were happening when we tried to make a copy.
+ if (moduleFileName->MaximumLength < moduleFileName->Length+2 ||
+ fname[len] != 0)
+ {
+#ifdef DEBUG
+ printf_stderr("LdrLoadDll: non-null terminated string found!\n");
+#endif
+ goto continue_loading;
+ }
+
+ dll_part = lastslash(fname, len);
+ if (dll_part) {
+ dll_part = dll_part + 1;
+ len -= dll_part - fname;
+ } else {
+ dll_part = fname;
+ }
+
+#ifdef DEBUG_very_verbose
+ printf_stderr("LdrLoadDll: dll_part '%S' %d\n", dll_part, len);
+#endif
+
+ // if it's too long, then, we assume we won't want to block it,
+ // since DLLNAME_MAX should be at least long enough to hold the longest
+ // entry in our blocklist.
+ if (len > DLLNAME_MAX) {
+#ifdef DEBUG
+ printf_stderr("LdrLoadDll: len too long! %d\n", len);
+#endif
+ goto continue_loading;
+ }
+
+ // copy over to our char byte buffer, lowercasing ASCII as we go
+ for (int i = 0; i < len; i++) {
+ wchar_t c = dll_part[i];
+
+ if (c > 0x7f) {
+ // welp, it's not ascii; if we need to add non-ascii things to
+ // our blocklist, we'll have to remove this limitation.
+ goto continue_loading;
+ }
+
+ // ensure that dll name is all lowercase
+ if (c >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+
+ dllName[i] = (char) c;
+ }
+
+ dllName[len] = 0;
+
+#ifdef DEBUG_very_verbose
+ printf_stderr("LdrLoadDll: dll name '%s'\n", dllName);
+#endif
+
+ // Block a suspicious binary that uses various 12-digit hex strings
+ // e.g. MovieMode.48CA2AEFA22D.dll (bug 973138)
+ dot = strchr(dllName, '.');
+ if (dot && (strchr(dot+1, '.') == dot+13)) {
+ char * end = nullptr;
+ _strtoui64(dot+1, &end, 16);
+ if (end == dot+13) {
+ return STATUS_DLL_NOT_FOUND;
+ }
+ }
+ // Block binaries where the filename is at least 16 hex digits
+ if (dot && ((dot - dllName) >= 16)) {
+ char * current = dllName;
+ while (current < dot && isxdigit(*current)) {
+ current++;
+ }
+ if (current == dot) {
+ return STATUS_DLL_NOT_FOUND;
+ }
+ }
+
+ // then compare to everything on the blocklist
+ info = &sWindowsDllBlocklist[0];
+ while (info->name) {
+ if (strcmp(info->name, dllName) == 0)
+ break;
+
+ info++;
+ }
+
+ if (info->name) {
+ bool load_ok = false;
+
+#ifdef DEBUG_very_verbose
+ printf_stderr("LdrLoadDll: info->name: '%s'\n", info->name);
+#endif
+
+ if ((info->flags == DllBlockInfo::BLOCK_WIN8PLUS_ONLY) &&
+ !IsWin8OrLater()) {
+ goto continue_loading;
+ }
+
+ if ((info->flags == DllBlockInfo::BLOCK_XP_ONLY) &&
+ IsWin2003OrLater()) {
+ goto continue_loading;
+ }
+
+ unsigned long long fVersion = ALL_VERSIONS;
+
+ if (info->maxVersion != ALL_VERSIONS) {
+ ReentrancySentinel sentinel(dllName);
+ if (sentinel.BailOut()) {
+ goto continue_loading;
+ }
+
+ full_fname = getFullPath(filePath, fname);
+ if (!full_fname) {
+ // uh, we couldn't find the DLL at all, so...
+ printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName);
+ return STATUS_DLL_NOT_FOUND;
+ }
+
+ if (info->flags & DllBlockInfo::USE_TIMESTAMP) {
+ fVersion = GetTimestamp(full_fname.get());
+ if (fVersion > info->maxVersion) {
+ load_ok = true;
+ }
+ } else {
+ DWORD zero;
+ DWORD infoSize = GetFileVersionInfoSizeW(full_fname.get(), &zero);
+
+ // If we failed to get the version information, we block.
+
+ if (infoSize != 0) {
+ auto infoData = MakeUnique<unsigned char[]>(infoSize);
+ VS_FIXEDFILEINFO *vInfo;
+ UINT vInfoLen;
+
+ if (GetFileVersionInfoW(full_fname.get(), 0, infoSize, infoData.get()) &&
+ VerQueryValueW(infoData.get(), L"\\", (LPVOID*) &vInfo, &vInfoLen))
+ {
+ fVersion =
+ ((unsigned long long)vInfo->dwFileVersionMS) << 32 |
+ ((unsigned long long)vInfo->dwFileVersionLS);
+
+ // finally do the version check, and if it's greater than our block
+ // version, keep loading
+ if (fVersion > info->maxVersion)
+ load_ok = true;
+ }
+ }
+ }
+ }
+
+ if (!load_ok) {
+ printf_stderr("LdrLoadDll: Blocking load of '%s' -- see http://www.mozilla.com/en-US/blocklist/\n", dllName);
+ DllBlockSet::Add(info->name, fVersion);
+ return STATUS_DLL_NOT_FOUND;
+ }
+ }
+
+continue_loading:
+#ifdef DEBUG_very_verbose
+ printf_stderr("LdrLoadDll: continuing load... ('%S')\n", moduleFileName->Buffer);
+#endif
+
+ if (GetCurrentThreadId() == sThreadLoadingXPCOMModule) {
+ // Check to ensure that the DLL has ASLR.
+ full_fname = getFullPath(filePath, fname);
+ if (!full_fname) {
+ // uh, we couldn't find the DLL at all, so...
+ printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName);
+ return STATUS_DLL_NOT_FOUND;
+ }
+
+ if (IsVistaOrLater() && !CheckASLR(full_fname.get())) {
+ printf_stderr("LdrLoadDll: Blocking load of '%s'. XPCOM components must support ASLR.\n", dllName);
+ return STATUS_DLL_NOT_FOUND;
+ }
+ }
+
+ return stub_LdrLoadDll(filePath, flags, moduleFileName, handle);
+}
+
+WindowsDllInterceptor NtDllIntercept;
+
+} // namespace
+
+MFBT_API void
+DllBlocklist_Initialize()
+{
+ if (sBlocklistInitAttempted) {
+ return;
+ }
+ sBlocklistInitAttempted = true;
+
+ if (GetModuleHandleA("user32.dll")) {
+ sUser32BeforeBlocklist = true;
+ }
+
+ NtDllIntercept.Init("ntdll.dll");
+
+ ReentrancySentinel::InitializeStatics();
+
+ // We specifically use a detour, because there are cases where external
+ // code also tries to hook LdrLoadDll, and doesn't know how to relocate our
+ // nop space patches. (Bug 951827)
+ bool ok = NtDllIntercept.AddDetour("LdrLoadDll", reinterpret_cast<intptr_t>(patched_LdrLoadDll), (void**) &stub_LdrLoadDll);
+
+ if (!ok) {
+ sBlocklistInitFailed = true;
+#ifdef DEBUG
+ printf_stderr ("LdrLoadDll hook failed, no dll blocklisting active\n");
+#endif
+ }
+}
+
+MFBT_API void
+DllBlocklist_SetInXPCOMLoadOnMainThread(bool inXPCOMLoadOnMainThread)
+{
+ if (inXPCOMLoadOnMainThread) {
+ MOZ_ASSERT(sThreadLoadingXPCOMModule == 0, "Only one thread should be doing this");
+ sThreadLoadingXPCOMModule = GetCurrentThreadId();
+ } else {
+ sThreadLoadingXPCOMModule = 0;
+ }
+}
+
+MFBT_API void
+DllBlocklist_WriteNotes(HANDLE file)
+{
+ DWORD nBytes;
+
+ WriteFile(file, kBlockedDllsParameter, kBlockedDllsParameterLen, &nBytes, nullptr);
+ DllBlockSet::Write(file);
+ WriteFile(file, "\n", 1, &nBytes, nullptr);
+
+ if (sBlocklistInitFailed) {
+ WriteFile(file, kBlocklistInitFailedParameter,
+ kBlocklistInitFailedParameterLen, &nBytes, nullptr);
+ }
+
+ if (sUser32BeforeBlocklist) {
+ WriteFile(file, kUser32BeforeBlocklistParameter,
+ kUser32BeforeBlocklistParameterLen, &nBytes, nullptr);
+ }
+}
+
+MFBT_API bool
+DllBlocklist_CheckStatus()
+{
+ if (sBlocklistInitFailed || sUser32BeforeBlocklist)
+ return false;
+ return true;
+}
diff --git a/mozglue/build/WindowsDllBlocklist.h b/mozglue/build/WindowsDllBlocklist.h
new file mode 100644
index 000000000..5afe6b8ce
--- /dev/null
+++ b/mozglue/build/WindowsDllBlocklist.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef mozilla_windowsdllblocklist_h
+#define mozilla_windowsdllblocklist_h
+
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+
+#include <windows.h>
+#include "mozilla/GuardObjects.h"
+#include "mozilla/Attributes.h"
+
+#define HAS_DLL_BLOCKLIST
+
+MFBT_API void DllBlocklist_Initialize();
+MFBT_API void DllBlocklist_SetInXPCOMLoadOnMainThread(bool inXPCOMLoadOnMainThread);
+MFBT_API void DllBlocklist_WriteNotes(HANDLE file);
+MFBT_API bool DllBlocklist_CheckStatus();
+
+class MOZ_RAII AutoSetXPCOMLoadOnMainThread
+{
+ public:
+ AutoSetXPCOMLoadOnMainThread(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ DllBlocklist_SetInXPCOMLoadOnMainThread(true);
+ }
+
+ ~AutoSetXPCOMLoadOnMainThread() {
+ DllBlocklist_SetInXPCOMLoadOnMainThread(false);
+ }
+
+ private:
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+#endif // defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+#endif // mozilla_windowsdllblocklist_h
diff --git a/mozglue/build/arm-eabi-filter b/mozglue/build/arm-eabi-filter
new file mode 100644
index 000000000..401454ee8
--- /dev/null
+++ b/mozglue/build/arm-eabi-filter
@@ -0,0 +1,4 @@
+{
+ local:
+ __aeabi*;
+};
diff --git a/mozglue/build/arm.cpp b/mozglue/build/arm.cpp
new file mode 100644
index 000000000..74b856a8f
--- /dev/null
+++ b/mozglue/build/arm.cpp
@@ -0,0 +1,147 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+/* compile-time and runtime tests for whether to use various ARM extensions */
+
+#include "arm.h"
+
+#if defined(MOZILLA_ARM_HAVE_CPUID_DETECTION)
+
+// arm.h has parallel #ifs which declare MOZILLA_ARM_HAVE_CPUID_DETECTION.
+// We don't check it here so that we get compile errors if it's defined, but
+// we don't compile one of these detection methods. The detection code here is
+// based on the CPU detection in libtheora.
+
+# if defined(__linux__) || defined(ANDROID)
+# include <stdio.h>
+# include <stdlib.h>
+# include <string.h>
+
+enum{
+ MOZILLA_HAS_EDSP_FLAG=1,
+ MOZILLA_HAS_ARMV6_FLAG=2,
+ MOZILLA_HAS_ARMV7_FLAG=4,
+ MOZILLA_HAS_NEON_FLAG=8
+};
+
+static unsigned
+get_arm_cpu_flags(void)
+{
+ unsigned flags;
+ FILE *fin;
+ bool armv6_processor = false;
+ flags = 0;
+ /*Reading /proc/self/auxv would be easier, but that doesn't work reliably on
+ Android. This also means that detection will fail in Scratchbox, which is
+ desirable, as NEON does not work in the qemu shipped with the Maemo 5 SDK.
+ I don't know if /proc/self/auxv would do any better in that case, anyway,
+ or if it would return random flags from the host CPU.*/
+ fin = fopen ("/proc/cpuinfo","r");
+ if (fin != nullptr)
+ {
+ /*512 should be enough for anybody (it's even enough for all the flags that
+ x86 has accumulated... so far).*/
+ char buf[512];
+ while (fgets(buf, 511, fin) != nullptr)
+ {
+ if (memcmp(buf, "Features", 8) == 0)
+ {
+ char *p;
+ p = strstr(buf, " edsp");
+ if (p != nullptr && (p[5] == ' ' || p[5] == '\n'))
+ flags |= MOZILLA_HAS_EDSP_FLAG;
+ p = strstr(buf, " neon");
+ if( p != nullptr && (p[5] == ' ' || p[5] == '\n'))
+ flags |= MOZILLA_HAS_NEON_FLAG;
+ }
+ if (memcmp(buf, "CPU architecture:", 17) == 0)
+ {
+ int version;
+ version = atoi(buf + 17);
+ if (version >= 6)
+ flags |= MOZILLA_HAS_ARMV6_FLAG;
+ if (version >= 7)
+ flags |= MOZILLA_HAS_ARMV7_FLAG;
+ }
+ /* media/webrtc/trunk/src/system_wrappers/source/cpu_features_arm.c
+ * Unfortunately, it seems that certain ARMv6-based CPUs
+ * report an incorrect architecture number of 7!
+ *
+ * We try to correct this by looking at the 'elf_format'
+ * field reported by the 'Processor' field, which is of the
+ * form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
+ * an ARMv6-one.
+ */
+ if (memcmp(buf, "Processor\t:", 11) == 0) {
+ if (strstr(buf, "(v6l)") != 0) {
+ armv6_processor = true;
+ }
+ }
+ }
+ fclose(fin);
+ }
+ if (armv6_processor) {
+ // ARMv6 pretending to be ARMv7? clear flag
+ if (flags & MOZILLA_HAS_ARMV7_FLAG) {
+ flags &= ~MOZILLA_HAS_ARMV7_FLAG;
+ }
+ }
+ return flags;
+}
+
+// Cache a local copy so we only have to read /proc/cpuinfo once.
+static unsigned arm_cpu_flags = get_arm_cpu_flags();
+
+# if !defined(MOZILLA_PRESUME_EDSP)
+static bool
+check_edsp(void)
+{
+ return (arm_cpu_flags & MOZILLA_HAS_EDSP_FLAG) != 0;
+}
+# endif
+
+# if !defined(MOZILLA_PRESUME_ARMV6)
+static bool
+check_armv6(void)
+{
+ return (arm_cpu_flags & MOZILLA_HAS_ARMV6_FLAG) != 0;
+}
+# endif
+
+# if !defined(MOZILLA_PRESUME_ARMV7)
+static bool
+check_armv7(void)
+{
+ return (arm_cpu_flags & MOZILLA_HAS_ARMV7_FLAG) != 0;
+}
+# endif
+
+# if !defined(MOZILLA_PRESUME_NEON)
+static bool
+check_neon(void)
+{
+ return (arm_cpu_flags & MOZILLA_HAS_NEON_FLAG) != 0;
+}
+# endif
+
+# endif // defined(__linux__) || defined(ANDROID)
+
+namespace mozilla {
+ namespace arm_private {
+# if !defined(MOZILLA_PRESUME_EDSP)
+ bool edsp_enabled = check_edsp();
+# endif
+# if !defined(MOZILLA_PRESUME_ARMV6)
+ bool armv6_enabled = check_armv6();
+# endif
+# if !defined(MOZILLA_PRESUME_ARMV7)
+ bool armv7_enabled = check_armv7();
+# endif
+# if !defined(MOZILLA_PRESUME_NEON)
+ bool neon_enabled = check_neon();
+# endif
+ } // namespace arm_private
+} // namespace mozilla
+
+#endif // MOZILLA_ARM_HAVE_CPUID_DETECTION
diff --git a/mozglue/build/arm.h b/mozglue/build/arm.h
new file mode 100644
index 000000000..e3379f67b
--- /dev/null
+++ b/mozglue/build/arm.h
@@ -0,0 +1,146 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+/* compile-time and runtime tests for whether to use SSE instructions */
+
+#ifndef mozilla_arm_h_
+#define mozilla_arm_h_
+
+// for definition of MFBT_DATA
+#include "mozilla/Types.h"
+
+/* This is patterned after SSE.h, but provides ARMv5E, ARMv6, and NEON
+ detection. For reasons similar to the SSE code, code using NEON (even just
+ in inline asm) needs to be in a separate compilation unit from the regular
+ code, because it requires an ".fpu neon" directive which can't be undone.
+ ARMv5E and ARMv6 code may also require an .arch directive, since by default
+ the assembler refuses to generate code for opcodes outside of its current
+ .arch setting.
+
+ TODO: Add Thumb, Thumb2, VFP, iwMMX, etc. detection, if we need it. */
+
+#if defined(__GNUC__) && defined(__arm__)
+
+# define MOZILLA_ARM_ARCH 3
+
+# if defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) \
+ || defined(_ARM_ARCH_4)
+# undef MOZILLA_ARM_ARCH
+# define MOZILLA_ARM_ARCH 4
+# endif
+
+# if defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) \
+ || defined(__ARM_ARCH_5E__) || defined(__ARM_ARCH_5TE__) \
+ || defined(__ARM_ARCH_5TEJ__) || defined(_ARM_ARCH_5)
+# undef MOZILLA_ARM_ARCH
+# define MOZILLA_ARM_ARCH 5
+# endif
+
+# if defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \
+ || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \
+ || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) \
+ || defined(__ARM_ARCH_6M__) || defined(_ARM_ARCH_6)
+# undef MOZILLA_ARM_ARCH
+# define MOZILLA_ARM_ARCH 6
+# endif
+
+# if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \
+ || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \
+ || defined(__ARM_ARCH_7EM__) || defined(_ARM_ARCH_7)
+# undef MOZILLA_ARM_ARCH
+# define MOZILLA_ARM_ARCH 7
+# endif
+
+
+# ifdef __GNUC__
+# define MOZILLA_MAY_SUPPORT_EDSP 1
+
+# if defined(HAVE_ARM_SIMD)
+# define MOZILLA_MAY_SUPPORT_ARMV6 1
+# endif
+
+# if defined(HAVE_ARM_NEON)
+# define MOZILLA_MAY_SUPPORT_NEON 1
+# endif
+
+# if defined(HAVE_ARM_SIMD)
+# define MOZILLA_MAY_SUPPORT_ARMV7 1
+# endif
+# endif
+
+ // When using -mfpu=neon, gcc generates neon instructions.
+
+# if defined(__ARM_NEON__)
+# define MOZILLA_PRESUME_NEON 1
+# endif
+
+ // Currently we only have CPU detection for Linux via /proc/cpuinfo
+# if defined(__linux__) || defined(ANDROID)
+# define MOZILLA_ARM_HAVE_CPUID_DETECTION 1
+# endif
+
+#endif
+
+namespace mozilla {
+
+ namespace arm_private {
+#if defined(MOZILLA_ARM_HAVE_CPUID_DETECTION)
+#if !defined(MOZILLA_PRESUME_EDSP)
+ extern bool MFBT_DATA edsp_enabled;
+#endif
+#if !defined(MOZILLA_PRESUME_ARMV6)
+ extern bool MFBT_DATA armv6_enabled;
+#endif
+#if !defined(MOZILLA_PRESUME_ARMV7)
+ extern bool MFBT_DATA armv7_enabled;
+#endif
+#if !defined(MOZILLA_PRESUME_NEON)
+ extern bool MFBT_DATA neon_enabled;
+#endif
+#endif
+ } // namespace arm_private
+
+#if defined(MOZILLA_PRESUME_EDSP)
+# define MOZILLA_MAY_SUPPORT_EDSP 1
+ inline bool supports_edsp() { return true; }
+#elif defined(MOZILLA_MAY_SUPPORT_EDSP) \
+ && defined(MOZILLA_ARM_HAVE_CPUID_DETECTION)
+ inline bool supports_edsp() { return arm_private::edsp_enabled; }
+#else
+ inline bool supports_edsp() { return false; }
+#endif
+
+#if defined(MOZILLA_PRESUME_ARMV6)
+# define MOZILLA_MAY_SUPPORT_ARMV6 1
+ inline bool supports_armv6() { return true; }
+#elif defined(MOZILLA_MAY_SUPPORT_ARMV6) \
+ && defined(MOZILLA_ARM_HAVE_CPUID_DETECTION)
+ inline bool supports_armv6() { return arm_private::armv6_enabled; }
+#else
+ inline bool supports_armv6() { return false; }
+#endif
+
+#if defined(MOZILLA_PRESUME_ARMV7)
+# define MOZILLA_MAY_SUPPORT_ARMV7 1
+ inline bool supports_armv7() { return true; }
+#elif defined(MOZILLA_MAY_SUPPORT_ARMV7) \
+ && defined(MOZILLA_ARM_HAVE_CPUID_DETECTION)
+ inline bool supports_armv7() { return arm_private::armv7_enabled; }
+#else
+ inline bool supports_armv7() { return false; }
+#endif
+
+#if defined(MOZILLA_PRESUME_NEON)
+# define MOZILLA_MAY_SUPPORT_NEON 1
+ inline bool supports_neon() { return true; }
+#elif defined(MOZILLA_MAY_SUPPORT_NEON) \
+ && defined(MOZILLA_ARM_HAVE_CPUID_DETECTION)
+ inline bool supports_neon() { return arm_private::neon_enabled; }
+#else
+ inline bool supports_neon() { return false; }
+#endif
+
+} // namespace mozilla
+
+#endif /* !defined(mozilla_arm_h_) */
diff --git a/mozglue/build/cpuacct.c b/mozglue/build/cpuacct.c
new file mode 100644
index 000000000..ce7d98eda
--- /dev/null
+++ b/mozglue/build/cpuacct.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include "cpuacct.h"
+
+int cpuacct_add(uid_t uid)
+{
+ int count;
+ int fd;
+ char buf[80];
+
+ count = snprintf(buf, sizeof(buf), "/acct/uid/%d/tasks", uid);
+ fd = open(buf, O_RDWR|O_CREAT|O_TRUNC|O_SYNC);
+ if (fd < 0) {
+ /* Note: sizeof("tasks") returns 6, which includes the NULL char */
+ buf[count - sizeof("tasks")] = 0;
+ if (mkdir(buf, 0775) < 0)
+ return -errno;
+
+ /* Note: sizeof("tasks") returns 6, which includes the NULL char */
+ buf[count - sizeof("tasks")] = '/';
+ fd = open(buf, O_RDWR|O_CREAT|O_TRUNC|O_SYNC);
+ }
+ if (fd < 0)
+ return -errno;
+
+ write(fd, "0", 2);
+ if (close(fd))
+ return -errno;
+
+ return 0;
+}
diff --git a/mozglue/build/cpuacct.h b/mozglue/build/cpuacct.h
new file mode 100644
index 000000000..8e24c8cc8
--- /dev/null
+++ b/mozglue/build/cpuacct.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _BIONIC_CPUACCT_H
+#define _BIONIC_CPUACCT_H
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+extern int cpuacct_add(uid_t uid);
+
+__END_DECLS
+
+#endif /* _BIONIC_CPUACCT_H */
diff --git a/mozglue/build/dummy.cpp b/mozglue/build/dummy.cpp
new file mode 100644
index 000000000..47866547f
--- /dev/null
+++ b/mozglue/build/dummy.cpp
@@ -0,0 +1,6 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+void _dummy(void) {
+}
diff --git a/mozglue/build/mips.cpp b/mozglue/build/mips.cpp
new file mode 100644
index 000000000..6a015db8a
--- /dev/null
+++ b/mozglue/build/mips.cpp
@@ -0,0 +1,47 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+/* compile-time and runtime tests for whether to use MIPS-specific extensions */
+
+#include "mips.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+enum{
+ MIPS_FLAG_LOONGSON3 = 1,
+};
+
+static unsigned
+get_mips_cpu_flags(void)
+{
+ unsigned flags = 0;
+ FILE *fin;
+
+ fin = fopen("/proc/cpuinfo","r");
+ if (fin != nullptr) {
+ char buf[1024];
+ memset(buf, 0, sizeof(buf));
+ fread(buf, sizeof(char), sizeof(buf) - 1, fin);
+ fclose(fin);
+ if (strstr(buf, "Loongson-3"))
+ flags |= MIPS_FLAG_LOONGSON3;
+ }
+ return flags;
+}
+
+static bool
+check_loongson3(void)
+{
+ // Cache a local copy so we only have to read /proc/cpuinfo once.
+ static unsigned mips_cpu_flags = get_mips_cpu_flags();
+ return (mips_cpu_flags & MIPS_FLAG_LOONGSON3) != 0;
+}
+
+namespace mozilla {
+ namespace mips_private {
+ bool isLoongson3 = check_loongson3();
+ } // namespace mips_private
+} // namespace mozilla
diff --git a/mozglue/build/mips.h b/mozglue/build/mips.h
new file mode 100644
index 000000000..ff31cbc87
--- /dev/null
+++ b/mozglue/build/mips.h
@@ -0,0 +1,29 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+/* compile-time and runtime tests for whether to use MIPS-specific extensions */
+
+#ifndef mozilla_mips_h_
+#define mozilla_mips_h_
+
+// for definition of MFBT_DATA
+#include "mozilla/Types.h"
+
+namespace mozilla {
+
+ namespace mips_private {
+ extern bool MFBT_DATA isLoongson3;
+ } // namespace mips_private
+
+ inline bool supports_mmi() {
+#ifdef __mips__
+ return mips_private::isLoongson3;
+#else
+ return false;
+#endif
+ }
+
+} // namespace mozilla
+
+#endif /* !defined(mozilla_mips_h_) */
diff --git a/mozglue/build/moz.build b/mozglue/build/moz.build
new file mode 100644
index 000000000..d28974778
--- /dev/null
+++ b/mozglue/build/moz.build
@@ -0,0 +1,108 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# 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/.
+
+# Build mozglue as a shared lib on Windows, OSX and Android.
+# If this is ever changed, update MOZ_SHARED_MOZGLUE in browser/installer/Makefile.in
+if CONFIG['OS_TARGET'] in ('WINNT', 'Darwin', 'Android'):
+ SharedLibrary('mozglue')
+else:
+ Library('mozglue')
+
+SDK_LIBRARY = True
+
+if CONFIG['OS_TARGET'] == 'Android':
+ SOURCES += [
+ 'BionicGlue.cpp',
+ ]
+
+if CONFIG['MOZ_ASAN']:
+ SOURCES += [
+ 'AsanOptions.cpp',
+ ]
+
+if CONFIG['OS_TARGET'] == 'WINNT':
+ DEFFILE = 'mozglue.def'
+ # We'll break the DLL blocklist if we immediately load user32.dll
+ DELAYLOAD_DLLS += [
+ 'user32.dll',
+ ]
+
+if not CONFIG['JS_STANDALONE']:
+
+ if CONFIG['MOZ_MEMORY'] and (CONFIG['MOZ_SYSTEM_JEMALLOC'] or FORCE_SHARED_LIB):
+ pass
+ # TODO: SHARED_LIBRARY_LIBS go here
+ else:
+ # Temporary, until bug 662814 lands
+ NO_VISIBILITY_FLAGS = True
+ SOURCES += [
+ 'dummy.cpp',
+ ]
+
+ if CONFIG['OS_TARGET'] == 'WINNT':
+ LOCAL_INCLUDES += [
+ '/memory/build',
+ ]
+ SOURCES += [
+ 'WindowsDllBlocklist.cpp',
+ ]
+ DISABLE_STL_WRAPPING = True
+ OS_LIBS += [
+ 'version',
+ ]
+
+ EXPORTS.mozilla += [
+ 'arm.h',
+ 'mips.h',
+ 'SSE.h',
+ 'WindowsDllBlocklist.h',
+ ]
+
+ if CONFIG['CPU_ARCH'].startswith('x86'):
+ SOURCES += [
+ 'SSE.cpp',
+ ]
+
+ if CONFIG['CPU_ARCH'] == 'arm':
+ SOURCES += [
+ 'arm.cpp',
+ ]
+
+ if CONFIG['CPU_ARCH'].startswith('mips'):
+ SOURCES += [
+ 'mips.cpp',
+ ]
+
+ if CONFIG['MOZ_LINKER']:
+ USE_LIBS += [
+ 'zlib',
+ ]
+
+ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+ SOURCES += [
+ 'cpuacct.c',
+ ]
+
+USE_LIBS += [
+ 'mfbt',
+]
+
+DEFINES['IMPL_MFBT'] = True
+LIBRARY_DEFINES['MOZ_HAS_MOZGLUE'] = True
+
+LDFLAGS += CONFIG['MOZ_GLUE_WRAP_LDFLAGS']
+
+if CONFIG['OS_TARGET'] == 'Darwin':
+ # On OSX 10.10.3, a dead lock happens in some cases involving dynamic
+ # symbol resolution for symbols that jemalloc itself uses. While it
+ # might be possible to find a way to avoid all such symbol resolutions,
+ # it's currently not possible because at the very least there's a call
+ # to pthread_self from tsd_init_check_recursion, which is necessary
+ # because somehow clang doesn't want to accept the __thread keyword
+ # for TLS.
+ LDFLAGS += ['-Wl,-bind_at_load']
+
+DIST_INSTALL = True
diff --git a/mozglue/build/mozglue.def.in b/mozglue/build/mozglue.def.in
new file mode 100644
index 000000000..62eb3caed
--- /dev/null
+++ b/mozglue/build/mozglue.def.in
@@ -0,0 +1,39 @@
+; This Source Code Form is subject to the terms of the Mozilla Public
+; 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/.
+
+LIBRARY mozglue.dll
+
+EXPORTS
+#ifdef MOZ_MEMORY
+ ; symbols that are actually useful
+#ifdef MOZ_REPLACE_MALLOC
+ malloc=malloc_impl
+ calloc=calloc_impl
+ realloc=realloc_impl
+ free=free_impl
+ posix_memalign=posix_memalign_impl
+ malloc_usable_size=malloc_usable_size_impl
+ malloc_good_size=malloc_good_size_impl
+ _aligned_free=free_impl
+#else
+ malloc=je_malloc
+ calloc=je_calloc
+ realloc=je_realloc
+ free=je_free
+ posix_memalign=je_posix_memalign
+ malloc_usable_size=je_malloc_usable_size
+ malloc_good_size=je_malloc_good_size
+ _aligned_free=je_free
+#endif
+ _aligned_malloc
+ strndup=wrap_strndup
+ strdup=wrap_strdup
+ _strdup=wrap_strdup
+ wcsdup=wrap_wcsdup
+ _wcsdup=wrap_wcsdup
+ jemalloc_stats
+ jemalloc_free_dirty_pages
+ ; A hack to work around the CRT (see giant comment in Makefile.in)
+ frex=dumb_free_thunk
+#endif
diff --git a/mozglue/build/replace_malloc.mk b/mozglue/build/replace_malloc.mk
new file mode 100644
index 000000000..7f436f2ee
--- /dev/null
+++ b/mozglue/build/replace_malloc.mk
@@ -0,0 +1,32 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# 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/.
+
+ifeq (Darwin_1,$(OS_TARGET)_$(MOZ_REPLACE_MALLOC))
+OS_LDFLAGS += \
+ -Wl,-U,_replace_init \
+ -Wl,-U,_replace_get_bridge \
+ -Wl,-U,_replace_malloc \
+ -Wl,-U,_replace_posix_memalign \
+ -Wl,-U,_replace_aligned_alloc \
+ -Wl,-U,_replace_calloc \
+ -Wl,-U,_replace_realloc \
+ -Wl,-U,_replace_free \
+ -Wl,-U,_replace_memalign \
+ -Wl,-U,_replace_valloc \
+ -Wl,-U,_replace_malloc_usable_size \
+ -Wl,-U,_replace_malloc_good_size \
+ -Wl,-U,_replace_jemalloc_stats \
+ -Wl,-U,_replace_jemalloc_purge_freed_pages \
+ -Wl,-U,_replace_jemalloc_free_dirty_pages \
+ $(NULL)
+
+ifneq ($(MOZ_REPLACE_MALLOC_LINKAGE),compiler support)
+OS_LDFLAGS += -flat_namespace
+endif
+ifeq ($(MOZ_REPLACE_MALLOC_LINKAGE),dummy library)
+OS_LDFLAGS += -Wl,-weak_library,$(DEPTH)/memory/replace/dummy/$(DLL_PREFIX)dummy_replace_malloc$(DLL_SUFFIX)
+endif
+
+EXTRA_DEPS += $(topsrcdir)/mozglue/build/replace_malloc.mk
+endif
diff --git a/mozglue/linker/BaseElf.cpp b/mozglue/linker/BaseElf.cpp
new file mode 100644
index 000000000..8a78285ef
--- /dev/null
+++ b/mozglue/linker/BaseElf.cpp
@@ -0,0 +1,215 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include "BaseElf.h"
+#include "Elfxx.h"
+#include "Logging.h"
+#include "mozilla/RefPtr.h"
+
+using namespace Elf;
+using namespace mozilla;
+
+unsigned long
+BaseElf::Hash(const char *symbol)
+{
+ const unsigned char *sym = reinterpret_cast<const unsigned char *>(symbol);
+ unsigned long h = 0, g;
+ while (*sym) {
+ h = (h << 4) + *sym++;
+ g = h & 0xf0000000;
+ h ^= g;
+ h ^= g >> 24;
+ }
+ return h;
+}
+
+void *
+BaseElf::GetSymbolPtr(const char *symbol) const
+{
+ return GetSymbolPtr(symbol, Hash(symbol));
+}
+
+void *
+BaseElf::GetSymbolPtr(const char *symbol, unsigned long hash) const
+{
+ const Sym *sym = GetSymbol(symbol, hash);
+ void *ptr = nullptr;
+ if (sym && sym->st_shndx != SHN_UNDEF)
+ ptr = GetPtr(sym->st_value);
+ DEBUG_LOG("BaseElf::GetSymbolPtr(%p [\"%s\"], \"%s\") = %p",
+ reinterpret_cast<const void *>(this), GetPath(), symbol, ptr);
+ return ptr;
+}
+
+const Sym *
+BaseElf::GetSymbol(const char *symbol, unsigned long hash) const
+{
+ /* Search symbol with the buckets and chains tables.
+ * The hash computed from the symbol name gives an index in the buckets
+ * table. The corresponding value in the bucket table is an index in the
+ * symbols table and in the chains table.
+ * If the corresponding symbol in the symbols table matches, we're done.
+ * Otherwise, the corresponding value in the chains table is a new index
+ * in both tables, which corresponding symbol is tested and so on and so
+ * forth */
+ size_t bucket = hash % buckets.numElements();
+ for (size_t y = buckets[bucket]; y != STN_UNDEF; y = chains[y]) {
+ if (strcmp(symbol, strtab.GetStringAt(symtab[y].st_name)))
+ continue;
+ return &symtab[y];
+ }
+ return nullptr;
+}
+
+bool
+BaseElf::Contains(void *addr) const
+{
+ return base.Contains(addr);
+}
+
+#ifdef __ARM_EABI__
+const void *
+BaseElf::FindExidx(int *pcount) const
+{
+ if (arm_exidx) {
+ *pcount = arm_exidx.numElements();
+ return arm_exidx;
+ }
+ *pcount = 0;
+ return nullptr;
+}
+#endif
+
+already_AddRefed<LibHandle>
+LoadedElf::Create(const char *path, void *base_addr)
+{
+ DEBUG_LOG("LoadedElf::Create(\"%s\", %p) = ...", path, base_addr);
+
+ uint8_t mapped;
+ /* If the page is not mapped, mincore returns an error. If base_addr is
+ * nullptr, as would happen if the corresponding binary is prelinked with
+ * the prelink look (but not with the android apriori tool), no page being
+ * mapped there (right?), mincore returns an error, too, which makes
+ * prelinked libraries on glibc unsupported. This is not an interesting
+ * use case for now, so don't try supporting that case.
+ */
+ if (mincore(const_cast<void*>(base_addr), PageSize(), &mapped))
+ return nullptr;
+
+ RefPtr<LoadedElf> elf = new LoadedElf(path);
+
+ const Ehdr *ehdr = Ehdr::validate(base_addr);
+ if (!ehdr)
+ return nullptr;
+
+ Addr min_vaddr = (Addr) -1; // We want to find the lowest and biggest
+ Addr max_vaddr = 0; // virtual address used by this Elf.
+ const Phdr *dyn = nullptr;
+#ifdef __ARM_EABI__
+ const Phdr *arm_exidx_phdr = nullptr;
+#endif
+
+ Array<Phdr> phdrs(reinterpret_cast<const char *>(ehdr) + ehdr->e_phoff,
+ ehdr->e_phnum);
+ for (auto phdr = phdrs.begin(); phdr < phdrs.end(); ++phdr) {
+ switch (phdr->p_type) {
+ case PT_LOAD:
+ if (phdr->p_vaddr < min_vaddr)
+ min_vaddr = phdr->p_vaddr;
+ if (max_vaddr < phdr->p_vaddr + phdr->p_memsz)
+ max_vaddr = phdr->p_vaddr + phdr->p_memsz;
+ break;
+ case PT_DYNAMIC:
+ dyn = &*phdr;
+ break;
+#ifdef __ARM_EABI__
+ case PT_ARM_EXIDX:
+ /* We cannot initialize arm_exidx here
+ because we don't have a base yet */
+ arm_exidx_phdr = &*phdr;
+ break;
+#endif
+ }
+ }
+
+ /* If the lowest PT_LOAD virtual address in headers is not 0, then the ELF
+ * is either prelinked or a non-PIE executable. The former case is not
+ * possible, because base_addr would be nullptr and the mincore test above
+ * would already have made us return.
+ * For a non-PIE executable, PT_LOADs contain absolute addresses, so in
+ * practice, this means min_vaddr should be equal to base_addr. max_vaddr
+ * can thus be adjusted accordingly.
+ */
+ if (min_vaddr != 0) {
+ void *min_vaddr_ptr = reinterpret_cast<void *>(
+ static_cast<uintptr_t>(min_vaddr));
+ if (min_vaddr_ptr != base_addr) {
+ LOG("%s: %p != %p", elf->GetPath(), min_vaddr_ptr, base_addr);
+ return nullptr;
+ }
+ max_vaddr -= min_vaddr;
+ }
+ if (!dyn) {
+ LOG("%s: No PT_DYNAMIC segment found", elf->GetPath());
+ return nullptr;
+ }
+
+ elf->base.Assign(base_addr, max_vaddr);
+
+ if (!elf->InitDyn(dyn))
+ return nullptr;
+
+#ifdef __ARM_EABI__
+ if (arm_exidx_phdr)
+ elf->arm_exidx.InitSize(elf->GetPtr(arm_exidx_phdr->p_vaddr),
+ arm_exidx_phdr->p_memsz);
+#endif
+
+ DEBUG_LOG("LoadedElf::Create(\"%s\", %p) = %p", path, base_addr,
+ static_cast<void *>(elf));
+
+ ElfLoader::Singleton.Register(elf);
+ return elf.forget();
+}
+
+bool
+LoadedElf::InitDyn(const Phdr *pt_dyn)
+{
+ Array<Dyn> dyns;
+ dyns.InitSize(GetPtr<Dyn>(pt_dyn->p_vaddr), pt_dyn->p_filesz);
+
+ size_t symnum = 0;
+ for (auto dyn = dyns.begin(); dyn < dyns.end() && dyn->d_tag; ++dyn) {
+ switch (dyn->d_tag) {
+ case DT_HASH:
+ {
+ DEBUG_LOG("%s 0x%08" PRIxAddr, "DT_HASH", dyn->d_un.d_val);
+ const Elf::Word *hash_table_header = \
+ GetPtr<Elf::Word>(dyn->d_un.d_ptr);
+ symnum = hash_table_header[1];
+ buckets.Init(&hash_table_header[2], hash_table_header[0]);
+ chains.Init(&*buckets.end());
+ }
+ break;
+ case DT_STRTAB:
+ DEBUG_LOG("%s 0x%08" PRIxAddr, "DT_STRTAB", dyn->d_un.d_val);
+ strtab.Init(GetPtr(dyn->d_un.d_ptr));
+ break;
+ case DT_SYMTAB:
+ DEBUG_LOG("%s 0x%08" PRIxAddr, "DT_SYMTAB", dyn->d_un.d_val);
+ symtab.Init(GetPtr(dyn->d_un.d_ptr));
+ break;
+ }
+ }
+ if (!buckets || !symnum) {
+ ERROR("%s: Missing or broken DT_HASH", GetPath());
+ } else if (!strtab) {
+ ERROR("%s: Missing DT_STRTAB", GetPath());
+ } else if (!symtab) {
+ ERROR("%s: Missing DT_SYMTAB", GetPath());
+ } else {
+ return true;
+ }
+ return false;
+}
diff --git a/mozglue/linker/BaseElf.h b/mozglue/linker/BaseElf.h
new file mode 100644
index 000000000..19f9061bc
--- /dev/null
+++ b/mozglue/linker/BaseElf.h
@@ -0,0 +1,141 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef BaseElf_h
+#define BaseElf_h
+
+#include "ElfLoader.h"
+#include "Elfxx.h"
+
+
+/**
+ * Base class for ELF libraries. This class includes things that will be
+ * common between SystemElfs and CustomElfs.
+ */
+class BaseElf: public LibHandle
+{
+public:
+ /**
+ * Hash function for symbol lookup, as defined in ELF standard for System V.
+ */
+ static unsigned long Hash(const char *symbol);
+
+ /**
+ * Returns the address corresponding to the given symbol name (with a
+ * pre-computed hash).
+ */
+ void *GetSymbolPtr(const char *symbol, unsigned long hash) const;
+
+ /**
+ * Returns a pointer to the Elf Symbol in the Dynamic Symbol table
+ * corresponding to the given symbol name (with a pre-computed hash).
+ */
+ const Elf::Sym *GetSymbol(const char *symbol, unsigned long hash) const;
+
+ BaseElf(const char *path, Mappable *mappable = nullptr)
+ : LibHandle(path)
+ , mappable(mappable)
+ {
+ }
+
+protected:
+ /**
+ * Inherited from LibHandle. Those are temporary and are not supposed to
+ * be used.
+ */
+ virtual void *GetSymbolPtr(const char *symbol) const;
+ virtual bool Contains(void *addr) const;
+ virtual void *GetBase() const { return GetPtr(0); }
+
+#ifdef __ARM_EABI__
+ virtual const void *FindExidx(int *pcount) const;
+#endif
+
+ virtual Mappable *GetMappable() const { return NULL; };
+
+public:
+/* private: */
+ /**
+ * Returns a pointer relative to the base address where the library is
+ * loaded.
+ */
+ void *GetPtr(const Elf::Addr offset) const
+ {
+ if (reinterpret_cast<void *>(offset) > base)
+ return reinterpret_cast<void *>(offset);
+ return base + offset;
+ }
+
+ /**
+ * Like the above, but returns a typed (const) pointer
+ */
+ template <typename T>
+ const T *GetPtr(const Elf::Addr offset) const
+ {
+ if (reinterpret_cast<void *>(offset) > base)
+ return reinterpret_cast<const T *>(offset);
+ return reinterpret_cast<const T *>(base + offset);
+ }
+
+ /* Appropriated Mappable */
+ /* /!\ we rely on this being nullptr for BaseElf instances, but not
+ * CustomElf instances. */
+ RefPtr<Mappable> mappable;
+
+ /* Base address where the library is loaded */
+ MappedPtr base;
+
+ /* Buckets and chains for the System V symbol hash table */
+ Array<Elf::Word> buckets;
+ UnsizedArray<Elf::Word> chains;
+
+/* protected: */
+ /* String table */
+ Elf::Strtab strtab;
+
+ /* Symbol table */
+ UnsizedArray<Elf::Sym> symtab;
+
+#ifdef __ARM_EABI__
+ /* ARM.exidx information used by FindExidx */
+ Array<uint32_t[2]> arm_exidx;
+#endif
+};
+
+
+/**
+ * Class for ELF libraries that already loaded in memory.
+ */
+class LoadedElf: public BaseElf
+{
+public:
+ /**
+ * Returns a LoadedElf corresponding to the already loaded ELF
+ * at the given base address.
+ */
+ static already_AddRefed<LibHandle> Create(const char *path,
+ void *base_addr);
+
+private:
+ LoadedElf(const char *path)
+ : BaseElf(path) { }
+
+ ~LoadedElf()
+ {
+ /* Avoid base's destructor unmapping something that doesn't actually
+ * belong to it. */
+ base.release();
+ ElfLoader::Singleton.Forget(this);
+ }
+
+ /**
+ * Initializes the library according to information found in the given
+ * PT_DYNAMIC header.
+ * Returns whether this succeeded or failed.
+ */
+ bool InitDyn(const Elf::Phdr *pt_dyn);
+};
+
+
+#endif /* BaseElf_h */
diff --git a/mozglue/linker/CustomElf.cpp b/mozglue/linker/CustomElf.cpp
new file mode 100644
index 000000000..dbab0cf0d
--- /dev/null
+++ b/mozglue/linker/CustomElf.cpp
@@ -0,0 +1,774 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include <cstring>
+#include <sys/mman.h>
+#include <vector>
+#include <dlfcn.h>
+#include <signal.h>
+#include <string.h>
+#include "CustomElf.h"
+#include "BaseElf.h"
+#include "Mappable.h"
+#include "Logging.h"
+
+using namespace Elf;
+using namespace mozilla;
+
+/* TODO: Fill ElfLoader::Singleton.lastError on errors. */
+
+/* Function used to report library mappings from the custom linker to Gecko
+ * crash reporter */
+#ifdef ANDROID
+extern "C" {
+ void report_mapping(char *name, void *base, uint32_t len, uint32_t offset);
+ void delete_mapping(const char *name);
+}
+#else
+#define report_mapping(...)
+#define delete_mapping(...)
+#endif
+
+const Ehdr *Ehdr::validate(const void *buf)
+{
+ if (!buf || buf == MAP_FAILED)
+ return nullptr;
+
+ const Ehdr *ehdr = reinterpret_cast<const Ehdr *>(buf);
+
+ /* Only support ELF executables or libraries for the host system */
+ if (memcmp(ELFMAG, &ehdr->e_ident, SELFMAG) ||
+ ehdr->e_ident[EI_CLASS] != ELFCLASS ||
+ ehdr->e_ident[EI_DATA] != ELFDATA ||
+ ehdr->e_ident[EI_VERSION] != 1 ||
+ (ehdr->e_ident[EI_OSABI] != ELFOSABI && ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE) ||
+#ifdef EI_ABIVERSION
+ ehdr->e_ident[EI_ABIVERSION] != ELFABIVERSION ||
+#endif
+ (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) ||
+ ehdr->e_machine != ELFMACHINE ||
+ ehdr->e_version != 1 ||
+ ehdr->e_phentsize != sizeof(Phdr))
+ return nullptr;
+
+ return ehdr;
+}
+
+namespace {
+
+void debug_phdr(const char *type, const Phdr *phdr)
+{
+ DEBUG_LOG("%s @0x%08" PRIxAddr " ("
+ "filesz: 0x%08" PRIxAddr ", "
+ "memsz: 0x%08" PRIxAddr ", "
+ "offset: 0x%08" PRIxAddr ", "
+ "flags: %c%c%c)",
+ type, phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz,
+ phdr->p_offset, phdr->p_flags & PF_R ? 'r' : '-',
+ phdr->p_flags & PF_W ? 'w' : '-', phdr->p_flags & PF_X ? 'x' : '-');
+}
+
+static int p_flags_to_mprot(Word flags)
+{
+ return ((flags & PF_X) ? PROT_EXEC : 0) |
+ ((flags & PF_W) ? PROT_WRITE : 0) |
+ ((flags & PF_R) ? PROT_READ : 0);
+}
+
+} /* anonymous namespace */
+
+/**
+ * RAII wrapper for a mapping of the first page off a Mappable object.
+ * This calls Mappable::munmap instead of system munmap.
+ */
+class Mappable1stPagePtr: public GenericMappedPtr<Mappable1stPagePtr> {
+public:
+ Mappable1stPagePtr(Mappable *mappable)
+ : GenericMappedPtr<Mappable1stPagePtr>(
+ mappable->mmap(nullptr, PageSize(), PROT_READ, MAP_PRIVATE, 0))
+ , mappable(mappable)
+ {
+ /* Ensure the content of this page */
+ mappable->ensure(*this);
+ }
+
+private:
+ friend class GenericMappedPtr<Mappable1stPagePtr>;
+ void munmap(void *buf, size_t length) {
+ mappable->munmap(buf, length);
+ }
+
+ RefPtr<Mappable> mappable;
+};
+
+
+already_AddRefed<LibHandle>
+CustomElf::Load(Mappable *mappable, const char *path, int flags)
+{
+ DEBUG_LOG("CustomElf::Load(\"%s\", 0x%x) = ...", path, flags);
+ if (!mappable)
+ return nullptr;
+ /* Keeping a RefPtr of the CustomElf is going to free the appropriate
+ * resources when returning nullptr */
+ RefPtr<CustomElf> elf = new CustomElf(mappable, path);
+ /* Map the first page of the Elf object to access Elf and program headers */
+ Mappable1stPagePtr ehdr_raw(mappable);
+ if (ehdr_raw == MAP_FAILED)
+ return nullptr;
+
+ const Ehdr *ehdr = Ehdr::validate(ehdr_raw);
+ if (!ehdr)
+ return nullptr;
+
+ /* Scan Elf Program Headers and gather some information about them */
+ std::vector<const Phdr *> pt_loads;
+ Addr min_vaddr = (Addr) -1; // We want to find the lowest and biggest
+ Addr max_vaddr = 0; // virtual address used by this Elf.
+ const Phdr *dyn = nullptr;
+
+ const Phdr *first_phdr = reinterpret_cast<const Phdr *>(
+ reinterpret_cast<const char *>(ehdr) + ehdr->e_phoff);
+ const Phdr *end_phdr = &first_phdr[ehdr->e_phnum];
+#ifdef __ARM_EABI__
+ const Phdr *arm_exidx_phdr = nullptr;
+#endif
+
+ for (const Phdr *phdr = first_phdr; phdr < end_phdr; phdr++) {
+ switch (phdr->p_type) {
+ case PT_LOAD:
+ debug_phdr("PT_LOAD", phdr);
+ pt_loads.push_back(phdr);
+ if (phdr->p_vaddr < min_vaddr)
+ min_vaddr = phdr->p_vaddr;
+ if (max_vaddr < phdr->p_vaddr + phdr->p_memsz)
+ max_vaddr = phdr->p_vaddr + phdr->p_memsz;
+ break;
+ case PT_DYNAMIC:
+ debug_phdr("PT_DYNAMIC", phdr);
+ if (!dyn) {
+ dyn = phdr;
+ } else {
+ ERROR("%s: Multiple PT_DYNAMIC segments detected", elf->GetPath());
+ return nullptr;
+ }
+ break;
+ case PT_TLS:
+ debug_phdr("PT_TLS", phdr);
+ if (phdr->p_memsz) {
+ ERROR("%s: TLS is not supported", elf->GetPath());
+ return nullptr;
+ }
+ break;
+ case PT_GNU_STACK:
+ debug_phdr("PT_GNU_STACK", phdr);
+// Skip on Android until bug 706116 is fixed
+#ifndef ANDROID
+ if (phdr->p_flags & PF_X) {
+ ERROR("%s: Executable stack is not supported", elf->GetPath());
+ return nullptr;
+ }
+#endif
+ break;
+#ifdef __ARM_EABI__
+ case PT_ARM_EXIDX:
+ /* We cannot initialize arm_exidx here
+ because we don't have a base yet */
+ arm_exidx_phdr = phdr;
+ break;
+#endif
+ default:
+ DEBUG_LOG("%s: Program header type #%d not handled",
+ elf->GetPath(), phdr->p_type);
+ }
+ }
+
+ if (min_vaddr != 0) {
+ ERROR("%s: Unsupported minimal virtual address: 0x%08" PRIxAddr,
+ elf->GetPath(), min_vaddr);
+ return nullptr;
+ }
+ if (!dyn) {
+ ERROR("%s: No PT_DYNAMIC segment found", elf->GetPath());
+ return nullptr;
+ }
+
+ /* Reserve enough memory to map the complete virtual address space for this
+ * library.
+ * As we are using the base address from here to mmap something else with
+ * MAP_FIXED | MAP_SHARED, we need to make sure these mmaps will work. For
+ * instance, on armv6, MAP_SHARED mappings require a 16k alignment, but mmap
+ * MAP_PRIVATE only returns a 4k aligned address. So we first get a base
+ * address with MAP_SHARED, which guarantees the kernel returns an address
+ * that we'll be able to use with MAP_FIXED, and then remap MAP_PRIVATE at
+ * the same address, because of some bad side effects of keeping it as
+ * MAP_SHARED. */
+ elf->base.Assign(MemoryRange::mmap(nullptr, max_vaddr, PROT_NONE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0));
+ if ((elf->base == MAP_FAILED) ||
+ (mmap(elf->base, max_vaddr, PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != elf->base)) {
+ ERROR("%s: Failed to mmap", elf->GetPath());
+ return nullptr;
+ }
+
+ /* Load and initialize library */
+ for (std::vector<const Phdr *>::iterator it = pt_loads.begin();
+ it < pt_loads.end(); ++it)
+ if (!elf->LoadSegment(*it))
+ return nullptr;
+
+ /* We're not going to mmap anymore */
+ mappable->finalize();
+
+ report_mapping(const_cast<char *>(elf->GetName()), elf->base,
+ (max_vaddr + PAGE_SIZE - 1) & PAGE_MASK, 0);
+
+ elf->l_addr = elf->base;
+ elf->l_name = elf->GetPath();
+ elf->l_ld = elf->GetPtr<Dyn>(dyn->p_vaddr);
+ ElfLoader::Singleton.Register(elf);
+
+ if (!elf->InitDyn(dyn))
+ return nullptr;
+
+ if (elf->has_text_relocs) {
+ for (std::vector<const Phdr *>::iterator it = pt_loads.begin();
+ it < pt_loads.end(); ++it)
+ mprotect(PageAlignedPtr(elf->GetPtr((*it)->p_vaddr)),
+ PageAlignedEndPtr((*it)->p_memsz),
+ p_flags_to_mprot((*it)->p_flags) | PROT_WRITE);
+ }
+
+ if (!elf->Relocate() || !elf->RelocateJumps())
+ return nullptr;
+
+ if (elf->has_text_relocs) {
+ for (std::vector<const Phdr *>::iterator it = pt_loads.begin();
+ it < pt_loads.end(); ++it)
+ mprotect(PageAlignedPtr(elf->GetPtr((*it)->p_vaddr)),
+ PageAlignedEndPtr((*it)->p_memsz),
+ p_flags_to_mprot((*it)->p_flags));
+ }
+
+ if (!elf->CallInit())
+ return nullptr;
+
+#ifdef __ARM_EABI__
+ if (arm_exidx_phdr)
+ elf->arm_exidx.InitSize(elf->GetPtr(arm_exidx_phdr->p_vaddr),
+ arm_exidx_phdr->p_memsz);
+#endif
+
+ if (MOZ_UNLIKELY(Logging::isVerbose())) {
+ elf->stats("oneLibLoaded");
+ }
+ DEBUG_LOG("CustomElf::Load(\"%s\", 0x%x) = %p", path, flags,
+ static_cast<void *>(elf));
+ return elf.forget();
+}
+
+CustomElf::~CustomElf()
+{
+ DEBUG_LOG("CustomElf::~CustomElf(%p [\"%s\"])",
+ reinterpret_cast<void *>(this), GetPath());
+ CallFini();
+ /* Normally, __cxa_finalize is called by the .fini function. However,
+ * Android NDK before r6b doesn't do that. Our wrapped cxa_finalize only
+ * calls destructors once, so call it in all cases. */
+ ElfLoader::__wrap_cxa_finalize(this);
+ ElfLoader::Singleton.Forget(this);
+ delete_mapping(GetName());
+}
+
+void *
+CustomElf::GetSymbolPtrInDeps(const char *symbol) const
+{
+ /* Resolve dlopen and related functions to point to ours */
+ if (symbol[0] == 'd' && symbol[1] == 'l') {
+ if (strcmp(symbol + 2, "open") == 0)
+ return FunctionPtr(__wrap_dlopen);
+ if (strcmp(symbol + 2, "error") == 0)
+ return FunctionPtr(__wrap_dlerror);
+ if (strcmp(symbol + 2, "close") == 0)
+ return FunctionPtr(__wrap_dlclose);
+ if (strcmp(symbol + 2, "sym") == 0)
+ return FunctionPtr(__wrap_dlsym);
+ if (strcmp(symbol + 2, "addr") == 0)
+ return FunctionPtr(__wrap_dladdr);
+ if (strcmp(symbol + 2, "_iterate_phdr") == 0)
+ return FunctionPtr(__wrap_dl_iterate_phdr);
+ } else if (symbol[0] == '_' && symbol[1] == '_') {
+ /* Resolve a few C++ ABI specific functions to point to ours */
+#ifdef __ARM_EABI__
+ if (strcmp(symbol + 2, "aeabi_atexit") == 0)
+ return FunctionPtr(&ElfLoader::__wrap_aeabi_atexit);
+#else
+ if (strcmp(symbol + 2, "cxa_atexit") == 0)
+ return FunctionPtr(&ElfLoader::__wrap_cxa_atexit);
+#endif
+ if (strcmp(symbol + 2, "cxa_finalize") == 0)
+ return FunctionPtr(&ElfLoader::__wrap_cxa_finalize);
+ if (strcmp(symbol + 2, "dso_handle") == 0)
+ return const_cast<CustomElf *>(this);
+ if (strcmp(symbol + 2, "moz_linker_stats") == 0)
+ return FunctionPtr(&ElfLoader::stats);
+#ifdef __ARM_EABI__
+ if (strcmp(symbol + 2, "gnu_Unwind_Find_exidx") == 0)
+ return FunctionPtr(__wrap___gnu_Unwind_Find_exidx);
+#endif
+ } else if (symbol[0] == 's' && symbol[1] == 'i') {
+ if (strcmp(symbol + 2, "gnal") == 0)
+ return FunctionPtr(signal);
+ if (strcmp(symbol + 2, "gaction") == 0)
+ return FunctionPtr(sigaction);
+ }
+
+ void *sym;
+
+ unsigned long hash = Hash(symbol);
+
+ /* self_elf should never be NULL, but better safe than sorry. */
+ if (ElfLoader::Singleton.self_elf) {
+ /* We consider the library containing this code a permanent LD_PRELOAD,
+ * so, check if the symbol exists here first. */
+ sym = static_cast<BaseElf *>(
+ ElfLoader::Singleton.self_elf.get())->GetSymbolPtr(symbol, hash);
+ if (sym)
+ return sym;
+ }
+
+ /* Then search the symbol in our dependencies. Since we already searched in
+ * libraries the system linker loaded, skip those (on glibc systems). We
+ * also assume the symbol is to be found in one of the dependent libraries
+ * directly, not in their own dependent libraries. Building libraries with
+ * --no-allow-shlib-undefined ensures such indirect symbol dependency don't
+ * happen. */
+ for (std::vector<RefPtr<LibHandle> >::const_iterator it = dependencies.begin();
+ it < dependencies.end(); ++it) {
+ /* Skip if it's the library containing this code, since we've already
+ * looked at it above. */
+ if (*it == ElfLoader::Singleton.self_elf)
+ continue;
+ if (BaseElf *be = (*it)->AsBaseElf()) {
+ sym = be->GetSymbolPtr(symbol, hash);
+ } else {
+ sym = (*it)->GetSymbolPtr(symbol);
+ }
+ if (sym)
+ return sym;
+ }
+ return nullptr;
+}
+
+void
+CustomElf::stats(const char *when) const
+{
+ mappable->stats(when, GetPath());
+}
+
+bool
+CustomElf::LoadSegment(const Phdr *pt_load) const
+{
+ if (pt_load->p_type != PT_LOAD) {
+ DEBUG_LOG("%s: Elf::LoadSegment only takes PT_LOAD program headers", GetPath());
+ return false;;
+ }
+
+ int prot = p_flags_to_mprot(pt_load->p_flags);
+
+ /* Mmap at page boundary */
+ Addr align = PageSize();
+ Addr align_offset;
+ void *mapped, *where;
+ do {
+ align_offset = pt_load->p_vaddr - AlignedPtr(pt_load->p_vaddr, align);
+ where = GetPtr(pt_load->p_vaddr - align_offset);
+ DEBUG_LOG("%s: Loading segment @%p %c%c%c", GetPath(), where,
+ prot & PROT_READ ? 'r' : '-',
+ prot & PROT_WRITE ? 'w' : '-',
+ prot & PROT_EXEC ? 'x' : '-');
+ mapped = mappable->mmap(where, pt_load->p_filesz + align_offset,
+ prot, MAP_PRIVATE | MAP_FIXED,
+ pt_load->p_offset - align_offset);
+ if ((mapped != MAP_FAILED) || (pt_load->p_vaddr == 0) ||
+ (pt_load->p_align == align))
+ break;
+ /* The virtual address space for the library is properly aligned at
+ * 16k on ARMv6 (see CustomElf::Load), and so is the first segment
+ * (p_vaddr == 0). But subsequent segments may not be 16k aligned
+ * and fail to mmap. In such case, try to mmap again at the p_align
+ * boundary instead of page boundary. */
+ DEBUG_LOG("%s: Failed to mmap, retrying", GetPath());
+ align = pt_load->p_align;
+ } while (1);
+
+ if (mapped != where) {
+ if (mapped == MAP_FAILED) {
+ ERROR("%s: Failed to mmap", GetPath());
+ } else {
+ ERROR("%s: Didn't map at the expected location (wanted: %p, got: %p)",
+ GetPath(), where, mapped);
+ }
+ return false;
+ }
+
+ /* Ensure the availability of all pages within the mapping if on-demand
+ * decompression is disabled (MOZ_LINKER_ONDEMAND=0 or signal handler not
+ * registered). */
+ const char *ondemand = getenv("MOZ_LINKER_ONDEMAND");
+ if (!ElfLoader::Singleton.hasRegisteredHandler() ||
+ (ondemand && !strncmp(ondemand, "0", 2 /* Including '\0' */))) {
+ for (Addr off = 0; off < pt_load->p_filesz + align_offset;
+ off += PageSize()) {
+ mappable->ensure(reinterpret_cast<char *>(mapped) + off);
+ }
+ }
+ /* When p_memsz is greater than p_filesz, we need to have nulled out memory
+ * after p_filesz and before p_memsz.
+ * Above the end of the last page, and up to p_memsz, we already have nulled
+ * out memory because we mapped anonymous memory on the whole library virtual
+ * address space. We just need to adjust this anonymous memory protection
+ * flags. */
+ if (pt_load->p_memsz > pt_load->p_filesz) {
+ Addr file_end = pt_load->p_vaddr + pt_load->p_filesz;
+ Addr mem_end = pt_load->p_vaddr + pt_load->p_memsz;
+ Addr next_page = PageAlignedEndPtr(file_end);
+ if (next_page > file_end) {
+ /* The library is not registered at this point, so we can't rely on
+ * on-demand decompression to handle missing pages here. */
+ void *ptr = GetPtr(file_end);
+ mappable->ensure(ptr);
+ memset(ptr, 0, next_page - file_end);
+ }
+ if (mem_end > next_page) {
+ if (mprotect(GetPtr(next_page), mem_end - next_page, prot) < 0) {
+ ERROR("%s: Failed to mprotect", GetPath());
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+namespace {
+
+void debug_dyn(const char *type, const Dyn *dyn)
+{
+ DEBUG_LOG("%s 0x%08" PRIxAddr, type, dyn->d_un.d_val);
+}
+
+} /* anonymous namespace */
+
+bool
+CustomElf::InitDyn(const Phdr *pt_dyn)
+{
+ /* Scan PT_DYNAMIC segment and gather some information */
+ const Dyn *first_dyn = GetPtr<Dyn>(pt_dyn->p_vaddr);
+ const Dyn *end_dyn = GetPtr<Dyn>(pt_dyn->p_vaddr + pt_dyn->p_filesz);
+ std::vector<Word> dt_needed;
+ size_t symnum = 0;
+ for (const Dyn *dyn = first_dyn; dyn < end_dyn && dyn->d_tag; dyn++) {
+ switch (dyn->d_tag) {
+ case DT_NEEDED:
+ debug_dyn("DT_NEEDED", dyn);
+ dt_needed.push_back(dyn->d_un.d_val);
+ break;
+ case DT_HASH:
+ {
+ debug_dyn("DT_HASH", dyn);
+ const Word *hash_table_header = GetPtr<Word>(dyn->d_un.d_ptr);
+ symnum = hash_table_header[1];
+ buckets.Init(&hash_table_header[2], hash_table_header[0]);
+ chains.Init(&*buckets.end());
+ }
+ break;
+ case DT_STRTAB:
+ debug_dyn("DT_STRTAB", dyn);
+ strtab.Init(GetPtr(dyn->d_un.d_ptr));
+ break;
+ case DT_SYMTAB:
+ debug_dyn("DT_SYMTAB", dyn);
+ symtab.Init(GetPtr(dyn->d_un.d_ptr));
+ break;
+ case DT_SYMENT:
+ debug_dyn("DT_SYMENT", dyn);
+ if (dyn->d_un.d_val != sizeof(Sym)) {
+ ERROR("%s: Unsupported DT_SYMENT", GetPath());
+ return false;
+ }
+ break;
+ case DT_TEXTREL:
+ if (strcmp("libflashplayer.so", GetName()) == 0) {
+ has_text_relocs = true;
+ } else {
+ ERROR("%s: Text relocations are not supported", GetPath());
+ return false;
+ }
+ break;
+ case DT_STRSZ: /* Ignored */
+ debug_dyn("DT_STRSZ", dyn);
+ break;
+ case UNSUPPORTED_RELOC():
+ case UNSUPPORTED_RELOC(SZ):
+ case UNSUPPORTED_RELOC(ENT):
+ ERROR("%s: Unsupported relocations", GetPath());
+ return false;
+ case RELOC():
+ debug_dyn(STR_RELOC(), dyn);
+ relocations.Init(GetPtr(dyn->d_un.d_ptr));
+ break;
+ case RELOC(SZ):
+ debug_dyn(STR_RELOC(SZ), dyn);
+ relocations.InitSize(dyn->d_un.d_val);
+ break;
+ case RELOC(ENT):
+ debug_dyn(STR_RELOC(ENT), dyn);
+ if (dyn->d_un.d_val != sizeof(Reloc)) {
+ ERROR("%s: Unsupported DT_RELENT", GetPath());
+ return false;
+ }
+ break;
+ case DT_JMPREL:
+ debug_dyn("DT_JMPREL", dyn);
+ jumprels.Init(GetPtr(dyn->d_un.d_ptr));
+ break;
+ case DT_PLTRELSZ:
+ debug_dyn("DT_PLTRELSZ", dyn);
+ jumprels.InitSize(dyn->d_un.d_val);
+ break;
+ case DT_PLTGOT:
+ debug_dyn("DT_PLTGOT", dyn);
+ break;
+ case DT_INIT:
+ debug_dyn("DT_INIT", dyn);
+ init = dyn->d_un.d_ptr;
+ break;
+ case DT_INIT_ARRAY:
+ debug_dyn("DT_INIT_ARRAY", dyn);
+ init_array.Init(GetPtr(dyn->d_un.d_ptr));
+ break;
+ case DT_INIT_ARRAYSZ:
+ debug_dyn("DT_INIT_ARRAYSZ", dyn);
+ init_array.InitSize(dyn->d_un.d_val);
+ break;
+ case DT_FINI:
+ debug_dyn("DT_FINI", dyn);
+ fini = dyn->d_un.d_ptr;
+ break;
+ case DT_FINI_ARRAY:
+ debug_dyn("DT_FINI_ARRAY", dyn);
+ fini_array.Init(GetPtr(dyn->d_un.d_ptr));
+ break;
+ case DT_FINI_ARRAYSZ:
+ debug_dyn("DT_FINI_ARRAYSZ", dyn);
+ fini_array.InitSize(dyn->d_un.d_val);
+ break;
+ case DT_PLTREL:
+ if (dyn->d_un.d_val != RELOC()) {
+ ERROR("%s: Error: DT_PLTREL is not " STR_RELOC(), GetPath());
+ return false;
+ }
+ break;
+ case DT_FLAGS:
+ {
+ Addr flags = dyn->d_un.d_val;
+ /* Treat as a DT_TEXTREL tag */
+ if (flags & DF_TEXTREL) {
+ if (strcmp("libflashplayer.so", GetName()) == 0) {
+ has_text_relocs = true;
+ } else {
+ ERROR("%s: Text relocations are not supported", GetPath());
+ return false;
+ }
+ }
+ /* we can treat this like having a DT_SYMBOLIC tag */
+ flags &= ~DF_SYMBOLIC;
+ if (flags)
+ WARN("%s: unhandled flags #%" PRIxAddr" not handled",
+ GetPath(), flags);
+ }
+ break;
+ case DT_SONAME: /* Should match GetName(), but doesn't matter */
+ case DT_SYMBOLIC: /* Indicates internal symbols should be looked up in
+ * the library itself first instead of the executable,
+ * which is actually what this linker does by default */
+ case RELOC(COUNT): /* Indicates how many relocations are relative, which
+ * is usually used to skip relocations on prelinked
+ * libraries. They are not supported anyways. */
+ case UNSUPPORTED_RELOC(COUNT): /* This should error out, but it doesn't
+ * really matter. */
+ case DT_FLAGS_1: /* Additional linker-internal flags that we don't care about. See
+ * DF_1_* values in src/include/elf/common.h in binutils. */
+ case DT_VERSYM: /* DT_VER* entries are used for symbol versioning, which */
+ case DT_VERDEF: /* this linker doesn't support yet. */
+ case DT_VERDEFNUM:
+ case DT_VERNEED:
+ case DT_VERNEEDNUM:
+ /* Ignored */
+ break;
+ default:
+ WARN("%s: dynamic header type #%" PRIxAddr" not handled",
+ GetPath(), dyn->d_tag);
+ }
+ }
+
+ if (!buckets || !symnum) {
+ ERROR("%s: Missing or broken DT_HASH", GetPath());
+ return false;
+ }
+ if (!strtab) {
+ ERROR("%s: Missing DT_STRTAB", GetPath());
+ return false;
+ }
+ if (!symtab) {
+ ERROR("%s: Missing DT_SYMTAB", GetPath());
+ return false;
+ }
+
+ /* Load dependent libraries */
+ for (size_t i = 0; i < dt_needed.size(); i++) {
+ const char *name = strtab.GetStringAt(dt_needed[i]);
+ RefPtr<LibHandle> handle =
+ ElfLoader::Singleton.Load(name, RTLD_GLOBAL | RTLD_LAZY, this);
+ if (!handle)
+ return false;
+ dependencies.push_back(handle);
+ }
+
+ return true;
+}
+
+bool
+CustomElf::Relocate()
+{
+ DEBUG_LOG("Relocate %s @%p", GetPath(), static_cast<void *>(base));
+ uint32_t symtab_index = (uint32_t) -1;
+ void *symptr = nullptr;
+ for (Array<Reloc>::iterator rel = relocations.begin();
+ rel < relocations.end(); ++rel) {
+ /* Location of the relocation */
+ void *ptr = GetPtr(rel->r_offset);
+
+ /* R_*_RELATIVE relocations apply directly at the given location */
+ if (ELF_R_TYPE(rel->r_info) == R_RELATIVE) {
+ *(void **) ptr = GetPtr(rel->GetAddend(base));
+ continue;
+ }
+ /* Other relocation types need a symbol resolution */
+ /* Avoid symbol resolution when it's the same symbol as last iteration */
+ if (symtab_index != ELF_R_SYM(rel->r_info)) {
+ symtab_index = ELF_R_SYM(rel->r_info);
+ const Sym sym = symtab[symtab_index];
+ if (sym.st_shndx != SHN_UNDEF) {
+ symptr = GetPtr(sym.st_value);
+ } else {
+ /* TODO: handle symbol resolving to nullptr vs. being undefined. */
+ symptr = GetSymbolPtrInDeps(strtab.GetStringAt(sym.st_name));
+ }
+ }
+
+ if (symptr == nullptr)
+ WARN("%s: Relocation to NULL @0x%08" PRIxAddr,
+ GetPath(), rel->r_offset);
+
+ /* Apply relocation */
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_GLOB_DAT:
+ /* R_*_GLOB_DAT relocations simply use the symbol value */
+ *(void **) ptr = symptr;
+ break;
+ case R_ABS:
+ /* R_*_ABS* relocations add the relocation added to the symbol value */
+ *(const char **) ptr = (const char *)symptr + rel->GetAddend(base);
+ break;
+ default:
+ ERROR("%s: Unsupported relocation type: 0x%" PRIxAddr,
+ GetPath(), ELF_R_TYPE(rel->r_info));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+CustomElf::RelocateJumps()
+{
+ /* TODO: Dynamic symbol resolution */
+ for (Array<Reloc>::iterator rel = jumprels.begin();
+ rel < jumprels.end(); ++rel) {
+ /* Location of the relocation */
+ void *ptr = GetPtr(rel->r_offset);
+
+ /* Only R_*_JMP_SLOT relocations are expected */
+ if (ELF_R_TYPE(rel->r_info) != R_JMP_SLOT) {
+ ERROR("%s: Jump relocation type mismatch", GetPath());
+ return false;
+ }
+
+ /* TODO: Avoid code duplication with the relocations above */
+ const Sym sym = symtab[ELF_R_SYM(rel->r_info)];
+ void *symptr;
+ if (sym.st_shndx != SHN_UNDEF)
+ symptr = GetPtr(sym.st_value);
+ else
+ symptr = GetSymbolPtrInDeps(strtab.GetStringAt(sym.st_name));
+
+ if (symptr == nullptr) {
+ if (ELF_ST_BIND(sym.st_info) == STB_WEAK) {
+ WARN("%s: Relocation to NULL @0x%08" PRIxAddr " for symbol \"%s\"",
+ GetPath(),
+ rel->r_offset, strtab.GetStringAt(sym.st_name));
+ } else {
+ ERROR("%s: Relocation to NULL @0x%08" PRIxAddr " for symbol \"%s\"",
+ GetPath(),
+ rel->r_offset, strtab.GetStringAt(sym.st_name));
+ return false;
+ }
+ }
+ /* Apply relocation */
+ *(void **) ptr = symptr;
+ }
+ return true;
+}
+
+bool
+CustomElf::CallInit()
+{
+ if (init)
+ CallFunction(init);
+
+ for (Array<void *>::iterator it = init_array.begin();
+ it < init_array.end(); ++it) {
+ /* Android x86 NDK wrongly puts 0xffffffff in INIT_ARRAY */
+ if (*it && *it != reinterpret_cast<void *>(-1))
+ CallFunction(*it);
+ }
+ initialized = true;
+ return true;
+}
+
+void
+CustomElf::CallFini()
+{
+ if (!initialized)
+ return;
+ for (Array<void *>::reverse_iterator it = fini_array.rbegin();
+ it < fini_array.rend(); ++it) {
+ /* Android x86 NDK wrongly puts 0xffffffff in FINI_ARRAY */
+ if (*it && *it != reinterpret_cast<void *>(-1))
+ CallFunction(*it);
+ }
+ if (fini)
+ CallFunction(fini);
+}
+
+Mappable *
+CustomElf::GetMappable() const
+{
+ if (!mappable)
+ return nullptr;
+ if (mappable->GetKind() == Mappable::MAPPABLE_EXTRACT_FILE)
+ return mappable;
+ return ElfLoader::GetMappableFromPath(GetPath());
+}
diff --git a/mozglue/linker/CustomElf.h b/mozglue/linker/CustomElf.h
new file mode 100644
index 000000000..5ecc7da08
--- /dev/null
+++ b/mozglue/linker/CustomElf.h
@@ -0,0 +1,159 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef CustomElf_h
+#define CustomElf_h
+
+#include "ElfLoader.h"
+#include "BaseElf.h"
+#include "Logging.h"
+#include "Elfxx.h"
+
+/**
+ * Library Handle class for ELF libraries we don't let the system linker
+ * handle.
+ */
+class CustomElf: public BaseElf, private ElfLoader::link_map
+{
+ friend class ElfLoader;
+ friend class SEGVHandler;
+public:
+ /**
+ * Returns a new CustomElf using the given file descriptor to map ELF
+ * content. The file descriptor ownership is stolen, and it will be closed
+ * in CustomElf's destructor if an instance is created, or by the Load
+ * method otherwise. The path corresponds to the file descriptor, and flags
+ * are the same kind of flags that would be given to dlopen(), though
+ * currently, none are supported and the behaviour is more or less that of
+ * RTLD_GLOBAL | RTLD_BIND_NOW.
+ */
+ static already_AddRefed<LibHandle> Load(Mappable *mappable,
+ const char *path, int flags);
+
+ /**
+ * Inherited from LibHandle/BaseElf
+ */
+ virtual ~CustomElf();
+
+protected:
+ virtual Mappable *GetMappable() const;
+
+public:
+ /**
+ * Shows some stats about the Mappable instance. The when argument is to be
+ * used by the caller to give an identifier of the when the stats call is
+ * made.
+ */
+ virtual void stats(const char *when) const;
+
+ /**
+ * Returns the instance, casted as BaseElf. (short of a better way to do
+ * this without RTTI)
+ */
+ virtual BaseElf *AsBaseElf() { return this; }
+
+private:
+ /**
+ * Scan dependent libraries to find the address corresponding to the
+ * given symbol name. This is used to find symbols that are undefined
+ * in the Elf object.
+ */
+ void *GetSymbolPtrInDeps(const char *symbol) const;
+
+ /**
+ * Private constructor
+ */
+ CustomElf(Mappable *mappable, const char *path)
+ : BaseElf(path, mappable)
+ , link_map()
+ , init(0)
+ , fini(0)
+ , initialized(false)
+ , has_text_relocs(false)
+ { }
+
+ /**
+ * Loads an Elf segment defined by the given PT_LOAD header.
+ * Returns whether this succeeded or failed.
+ */
+ bool LoadSegment(const Elf::Phdr *pt_load) const;
+
+ /**
+ * Initializes the library according to information found in the given
+ * PT_DYNAMIC header.
+ * Returns whether this succeeded or failed.
+ */
+ bool InitDyn(const Elf::Phdr *pt_dyn);
+
+ /**
+ * Apply .rel.dyn/.rela.dyn relocations.
+ * Returns whether this succeeded or failed.
+ */
+ bool Relocate();
+
+ /**
+ * Apply .rel.plt/.rela.plt relocations.
+ * Returns whether this succeeded or failed.
+ */
+ bool RelocateJumps();
+
+ /**
+ * Call initialization functions (.init/.init_array)
+ * Returns true;
+ */
+ bool CallInit();
+
+ /**
+ * Call destructor functions (.fini_array/.fini)
+ * Returns whether this succeeded or failed.
+ */
+ void CallFini();
+
+ /**
+ * Call a function given a pointer to its location.
+ */
+ void CallFunction(void *ptr) const
+ {
+ /* C++ doesn't allow direct conversion between pointer-to-object
+ * and pointer-to-function. */
+ union {
+ void *ptr;
+ void (*func)(void);
+ } f;
+ f.ptr = ptr;
+ DEBUG_LOG("%s: Calling function @%p", GetPath(), ptr);
+ f.func();
+ }
+
+ /**
+ * Call a function given a an address relative to the library base
+ */
+ void CallFunction(Elf::Addr addr) const
+ {
+ return CallFunction(GetPtr(addr));
+ }
+
+ /* List of dependent libraries */
+ std::vector<RefPtr<LibHandle> > dependencies;
+
+ /* List of .rel.dyn/.rela.dyn relocations */
+ Array<Elf::Reloc> relocations;
+
+ /* List of .rel.plt/.rela.plt relocation */
+ Array<Elf::Reloc> jumprels;
+
+ /* Relative address of the initialization and destruction functions
+ * (.init/.fini) */
+ Elf::Addr init, fini;
+
+ /* List of initialization and destruction functions
+ * (.init_array/.fini_array) */
+ Array<void *> init_array, fini_array;
+
+ bool initialized;
+
+ bool has_text_relocs;
+};
+
+#endif /* CustomElf_h */
diff --git a/mozglue/linker/ElfLoader.cpp b/mozglue/linker/ElfLoader.cpp
new file mode 100644
index 000000000..76225d1e7
--- /dev/null
+++ b/mozglue/linker/ElfLoader.cpp
@@ -0,0 +1,1310 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include <string>
+#include <cstring>
+#include <cstdlib>
+#include <cstdio>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <errno.h>
+#include <algorithm>
+#include <fcntl.h>
+#include "ElfLoader.h"
+#include "BaseElf.h"
+#include "CustomElf.h"
+#include "Mappable.h"
+#include "Logging.h"
+#include <inttypes.h>
+
+#if defined(ANDROID)
+#include <sys/syscall.h>
+
+#include <android/api-level.h>
+#if __ANDROID_API__ < 8
+/* Android API < 8 doesn't provide sigaltstack */
+
+extern "C" {
+
+inline int sigaltstack(const stack_t *ss, stack_t *oss) {
+ return syscall(__NR_sigaltstack, ss, oss);
+}
+
+} /* extern "C" */
+#endif /* __ANDROID_API__ */
+#endif /* ANDROID */
+
+#ifdef __ARM_EABI__
+extern "C" MOZ_EXPORT const void *
+__gnu_Unwind_Find_exidx(void *pc, int *pcount) __attribute__((weak));
+#endif
+
+/* Pointer to the PT_DYNAMIC section of the executable or library
+ * containing this code. */
+extern "C" Elf::Dyn _DYNAMIC[];
+
+using namespace mozilla;
+
+/**
+ * dlfcn.h replacements functions
+ */
+
+void *
+__wrap_dlopen(const char *path, int flags)
+{
+ RefPtr<LibHandle> handle = ElfLoader::Singleton.Load(path, flags);
+ if (handle)
+ handle->AddDirectRef();
+ return handle;
+}
+
+const char *
+__wrap_dlerror(void)
+{
+ const char *error = ElfLoader::Singleton.lastError;
+ ElfLoader::Singleton.lastError = nullptr;
+ return error;
+}
+
+void *
+__wrap_dlsym(void *handle, const char *symbol)
+{
+ if (!handle) {
+ ElfLoader::Singleton.lastError = "dlsym(NULL, sym) unsupported";
+ return nullptr;
+ }
+ if (handle != RTLD_DEFAULT && handle != RTLD_NEXT) {
+ LibHandle *h = reinterpret_cast<LibHandle *>(handle);
+ return h->GetSymbolPtr(symbol);
+ }
+ return dlsym(handle, symbol);
+}
+
+int
+__wrap_dlclose(void *handle)
+{
+ if (!handle) {
+ ElfLoader::Singleton.lastError = "No handle given to dlclose()";
+ return -1;
+ }
+ reinterpret_cast<LibHandle *>(handle)->ReleaseDirectRef();
+ return 0;
+}
+
+int
+__wrap_dladdr(void *addr, Dl_info *info)
+{
+ RefPtr<LibHandle> handle = ElfLoader::Singleton.GetHandleByPtr(addr);
+ if (!handle) {
+ return dladdr(addr, info);
+ }
+ info->dli_fname = handle->GetPath();
+ info->dli_fbase = handle->GetBase();
+ return 1;
+}
+
+int
+__wrap_dl_iterate_phdr(dl_phdr_cb callback, void *data)
+{
+ if (!ElfLoader::Singleton.dbg)
+ return -1;
+
+ int pipefd[2];
+ bool valid_pipe = (pipe(pipefd) == 0);
+ AutoCloseFD read_fd(pipefd[0]);
+ AutoCloseFD write_fd(pipefd[1]);
+
+ for (ElfLoader::DebuggerHelper::iterator it = ElfLoader::Singleton.dbg.begin();
+ it < ElfLoader::Singleton.dbg.end(); ++it) {
+ dl_phdr_info info;
+ info.dlpi_addr = reinterpret_cast<Elf::Addr>(it->l_addr);
+ info.dlpi_name = it->l_name;
+ info.dlpi_phdr = nullptr;
+ info.dlpi_phnum = 0;
+
+ // Assuming l_addr points to Elf headers (in most cases, this is true),
+ // get the Phdr location from there.
+ // Unfortunately, when l_addr doesn't point to Elf headers, it may point
+ // to unmapped memory, or worse, unreadable memory. The only way to detect
+ // the latter without causing a SIGSEGV is to use the pointer in a system
+ // call that will try to read from there, and return an EFAULT error if
+ // it can't. One such system call is write(). It used to be possible to
+ // use a file descriptor on /dev/null for these kind of things, but recent
+ // Linux kernels never return an EFAULT error when using /dev/null.
+ // So instead, we use a self pipe. We do however need to read() from the
+ // read end of the pipe as well so as to not fill up the pipe buffer and
+ // block on subsequent writes.
+ // In the unlikely event reads from or write to the pipe fail for some
+ // other reason than EFAULT, we don't try any further and just skip setting
+ // the Phdr location for all subsequent libraries, rather than trying to
+ // start over with a new pipe.
+ int can_read = true;
+ if (valid_pipe) {
+ int ret;
+ char raw_ehdr[sizeof(Elf::Ehdr)];
+ static_assert(sizeof(raw_ehdr) < PIPE_BUF, "PIPE_BUF is too small");
+ do {
+ // writes are atomic when smaller than PIPE_BUF, per POSIX.1-2008.
+ ret = write(write_fd, it->l_addr, sizeof(raw_ehdr));
+ } while (ret == -1 && errno == EINTR);
+ if (ret != sizeof(raw_ehdr)) {
+ if (ret == -1 && errno == EFAULT) {
+ can_read = false;
+ } else {
+ valid_pipe = false;
+ }
+ } else {
+ size_t nbytes = 0;
+ do {
+ // Per POSIX.1-2008, interrupted reads can return a length smaller
+ // than the given one instead of failing with errno EINTR.
+ ret = read(read_fd, raw_ehdr + nbytes, sizeof(raw_ehdr) - nbytes);
+ if (ret > 0)
+ nbytes += ret;
+ } while ((nbytes != sizeof(raw_ehdr) && ret > 0) ||
+ (ret == -1 && errno == EINTR));
+ if (nbytes != sizeof(raw_ehdr)) {
+ valid_pipe = false;
+ }
+ }
+ }
+
+ if (valid_pipe && can_read) {
+ const Elf::Ehdr *ehdr = Elf::Ehdr::validate(it->l_addr);
+ if (ehdr) {
+ info.dlpi_phdr = reinterpret_cast<const Elf::Phdr *>(
+ reinterpret_cast<const char *>(ehdr) + ehdr->e_phoff);
+ info.dlpi_phnum = ehdr->e_phnum;
+ }
+ }
+
+ int ret = callback(&info, sizeof(dl_phdr_info), data);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+#ifdef __ARM_EABI__
+const void *
+__wrap___gnu_Unwind_Find_exidx(void *pc, int *pcount)
+{
+ RefPtr<LibHandle> handle = ElfLoader::Singleton.GetHandleByPtr(pc);
+ if (handle)
+ return handle->FindExidx(pcount);
+ if (__gnu_Unwind_Find_exidx)
+ return __gnu_Unwind_Find_exidx(pc, pcount);
+ *pcount = 0;
+ return nullptr;
+}
+#endif
+
+/**
+ * faulty.lib public API
+ */
+
+MFBT_API size_t
+__dl_get_mappable_length(void *handle) {
+ if (!handle)
+ return 0;
+ return reinterpret_cast<LibHandle *>(handle)->GetMappableLength();
+}
+
+MFBT_API void *
+__dl_mmap(void *handle, void *addr, size_t length, off_t offset)
+{
+ if (!handle)
+ return nullptr;
+ return reinterpret_cast<LibHandle *>(handle)->MappableMMap(addr, length,
+ offset);
+}
+
+MFBT_API void
+__dl_munmap(void *handle, void *addr, size_t length)
+{
+ if (!handle)
+ return;
+ return reinterpret_cast<LibHandle *>(handle)->MappableMUnmap(addr, length);
+}
+
+MFBT_API bool
+IsSignalHandlingBroken()
+{
+ return ElfLoader::Singleton.isSignalHandlingBroken();
+}
+
+namespace {
+
+/**
+ * Returns the part after the last '/' for the given path
+ */
+const char *
+LeafName(const char *path)
+{
+ const char *lastSlash = strrchr(path, '/');
+ if (lastSlash)
+ return lastSlash + 1;
+ return path;
+}
+
+} /* Anonymous namespace */
+
+/**
+ * LibHandle
+ */
+LibHandle::~LibHandle()
+{
+ free(path);
+}
+
+const char *
+LibHandle::GetName() const
+{
+ return path ? LeafName(path) : nullptr;
+}
+
+size_t
+LibHandle::GetMappableLength() const
+{
+ if (!mappable)
+ mappable = GetMappable();
+ if (!mappable)
+ return 0;
+ return mappable->GetLength();
+}
+
+void *
+LibHandle::MappableMMap(void *addr, size_t length, off_t offset) const
+{
+ if (!mappable)
+ mappable = GetMappable();
+ if (!mappable)
+ return MAP_FAILED;
+ void* mapped = mappable->mmap(addr, length, PROT_READ, MAP_PRIVATE, offset);
+ if (mapped != MAP_FAILED) {
+ /* Ensure the availability of all pages within the mapping */
+ for (size_t off = 0; off < length; off += PageSize()) {
+ mappable->ensure(reinterpret_cast<char *>(mapped) + off);
+ }
+ }
+ return mapped;
+}
+
+void
+LibHandle::MappableMUnmap(void *addr, size_t length) const
+{
+ if (mappable)
+ mappable->munmap(addr, length);
+}
+
+/**
+ * SystemElf
+ */
+already_AddRefed<LibHandle>
+SystemElf::Load(const char *path, int flags)
+{
+ /* The Android linker returns a handle when the file name matches an
+ * already loaded library, even when the full path doesn't exist */
+ if (path && path[0] == '/' && (access(path, F_OK) == -1)){
+ DEBUG_LOG("dlopen(\"%s\", 0x%x) = %p", path, flags, (void *)nullptr);
+ return nullptr;
+ }
+
+ void *handle = dlopen(path, flags);
+ DEBUG_LOG("dlopen(\"%s\", 0x%x) = %p", path, flags, handle);
+ ElfLoader::Singleton.lastError = dlerror();
+ if (handle) {
+ SystemElf *elf = new SystemElf(path, handle);
+ ElfLoader::Singleton.Register(elf);
+ RefPtr<LibHandle> lib(elf);
+ return lib.forget();
+ }
+ return nullptr;
+}
+
+SystemElf::~SystemElf()
+{
+ if (!dlhandle)
+ return;
+ DEBUG_LOG("dlclose(%p [\"%s\"])", dlhandle, GetPath());
+ dlclose(dlhandle);
+ ElfLoader::Singleton.lastError = dlerror();
+ ElfLoader::Singleton.Forget(this);
+}
+
+void *
+SystemElf::GetSymbolPtr(const char *symbol) const
+{
+ void *sym = dlsym(dlhandle, symbol);
+ DEBUG_LOG("dlsym(%p [\"%s\"], \"%s\") = %p", dlhandle, GetPath(), symbol, sym);
+ ElfLoader::Singleton.lastError = dlerror();
+ return sym;
+}
+
+Mappable *
+SystemElf::GetMappable() const
+{
+ const char *path = GetPath();
+ if (!path)
+ return nullptr;
+#ifdef ANDROID
+ /* On Android, if we don't have the full path, try in /system/lib */
+ const char *name = LeafName(path);
+ std::string systemPath;
+ if (name == path) {
+ systemPath = "/system/lib/";
+ systemPath += path;
+ path = systemPath.c_str();
+ }
+#endif
+
+ return MappableFile::Create(path);
+}
+
+#ifdef __ARM_EABI__
+const void *
+SystemElf::FindExidx(int *pcount) const
+{
+ /* TODO: properly implement when ElfLoader::GetHandleByPtr
+ does return SystemElf handles */
+ *pcount = 0;
+ return nullptr;
+}
+#endif
+
+/**
+ * ElfLoader
+ */
+
+/* Unique ElfLoader instance */
+ElfLoader ElfLoader::Singleton;
+
+already_AddRefed<LibHandle>
+ElfLoader::Load(const char *path, int flags, LibHandle *parent)
+{
+ /* Ensure logging is initialized or refresh if environment changed. */
+ Logging::Init();
+
+ /* Ensure self_elf initialization. */
+ if (!self_elf)
+ Init();
+
+ RefPtr<LibHandle> handle;
+
+ /* Handle dlopen(nullptr) directly. */
+ if (!path) {
+ handle = SystemElf::Load(nullptr, flags);
+ return handle.forget();
+ }
+
+ /* TODO: Handle relative paths correctly */
+ const char *name = LeafName(path);
+
+ /* Search the list of handles we already have for a match. When the given
+ * path is not absolute, compare file names, otherwise compare full paths. */
+ if (name == path) {
+ for (LibHandleList::iterator it = handles.begin(); it < handles.end(); ++it)
+ if ((*it)->GetName() && (strcmp((*it)->GetName(), name) == 0)) {
+ handle = *it;
+ return handle.forget();
+ }
+ } else {
+ for (LibHandleList::iterator it = handles.begin(); it < handles.end(); ++it)
+ if ((*it)->GetPath() && (strcmp((*it)->GetPath(), path) == 0)) {
+ handle = *it;
+ return handle.forget();
+ }
+ }
+
+ char *abs_path = nullptr;
+ const char *requested_path = path;
+
+ /* When the path is not absolute and the library is being loaded for
+ * another, first try to load the library from the directory containing
+ * that parent library. */
+ if ((name == path) && parent) {
+ const char *parentPath = parent->GetPath();
+ abs_path = new char[strlen(parentPath) + strlen(path)];
+ strcpy(abs_path, parentPath);
+ char *slash = strrchr(abs_path, '/');
+ strcpy(slash + 1, path);
+ path = abs_path;
+ }
+
+ Mappable *mappable = GetMappableFromPath(path);
+
+ /* Try loading with the custom linker if we have a Mappable */
+ if (mappable)
+ handle = CustomElf::Load(mappable, path, flags);
+
+ /* Try loading with the system linker if everything above failed */
+ if (!handle)
+ handle = SystemElf::Load(path, flags);
+
+ /* If we didn't have an absolute path and haven't been able to load
+ * a library yet, try in the system search path */
+ if (!handle && abs_path)
+ handle = SystemElf::Load(name, flags);
+
+ delete [] abs_path;
+ DEBUG_LOG("ElfLoader::Load(\"%s\", 0x%x, %p [\"%s\"]) = %p", requested_path, flags,
+ reinterpret_cast<void *>(parent), parent ? parent->GetPath() : "",
+ static_cast<void *>(handle));
+
+ return handle.forget();
+}
+
+already_AddRefed<LibHandle>
+ElfLoader::GetHandleByPtr(void *addr)
+{
+ /* Scan the list of handles we already have for a match */
+ for (LibHandleList::iterator it = handles.begin(); it < handles.end(); ++it) {
+ if ((*it)->Contains(addr)) {
+ RefPtr<LibHandle> lib = *it;
+ return lib.forget();
+ }
+ }
+ return nullptr;
+}
+
+Mappable *
+ElfLoader::GetMappableFromPath(const char *path)
+{
+ const char *name = LeafName(path);
+ Mappable *mappable = nullptr;
+ RefPtr<Zip> zip;
+ const char *subpath;
+ if ((subpath = strchr(path, '!'))) {
+ char *zip_path = strndup(path, subpath - path);
+ while (*(++subpath) == '/') { }
+ zip = ZipCollection::GetZip(zip_path);
+ free(zip_path);
+ Zip::Stream s;
+ if (zip && zip->GetStream(subpath, &s)) {
+ /* When the MOZ_LINKER_EXTRACT environment variable is set to "1",
+ * compressed libraries are going to be (temporarily) extracted as
+ * files, in the directory pointed by the MOZ_LINKER_CACHE
+ * environment variable. */
+ const char *extract = getenv("MOZ_LINKER_EXTRACT");
+ if (extract && !strncmp(extract, "1", 2 /* Including '\0' */))
+ mappable = MappableExtractFile::Create(name, zip, &s);
+ if (!mappable) {
+ if (s.GetType() == Zip::Stream::DEFLATE) {
+ mappable = MappableDeflate::Create(name, zip, &s);
+ } else if (s.GetType() == Zip::Stream::STORE) {
+ mappable = MappableSeekableZStream::Create(name, zip, &s);
+ }
+ }
+ }
+ }
+ /* If we couldn't load above, try with a MappableFile */
+ if (!mappable && !zip)
+ mappable = MappableFile::Create(path);
+
+ return mappable;
+}
+
+void
+ElfLoader::Register(LibHandle *handle)
+{
+ handles.push_back(handle);
+}
+
+void
+ElfLoader::Register(CustomElf *handle)
+{
+ Register(static_cast<LibHandle *>(handle));
+ if (dbg) {
+ dbg.Add(handle);
+ }
+}
+
+void
+ElfLoader::Forget(LibHandle *handle)
+{
+ /* Ensure logging is initialized or refresh if environment changed. */
+ Logging::Init();
+
+ LibHandleList::iterator it = std::find(handles.begin(), handles.end(), handle);
+ if (it != handles.end()) {
+ DEBUG_LOG("ElfLoader::Forget(%p [\"%s\"])", reinterpret_cast<void *>(handle),
+ handle->GetPath());
+ handles.erase(it);
+ } else {
+ DEBUG_LOG("ElfLoader::Forget(%p [\"%s\"]): Handle not found",
+ reinterpret_cast<void *>(handle), handle->GetPath());
+ }
+}
+
+void
+ElfLoader::Forget(CustomElf *handle)
+{
+ Forget(static_cast<LibHandle *>(handle));
+ if (dbg) {
+ dbg.Remove(handle);
+ }
+}
+
+void
+ElfLoader::Init()
+{
+ Dl_info info;
+ /* On Android < 4.1 can't reenter dl* functions. So when the library
+ * containing this code is dlopen()ed, it can't call dladdr from a
+ * static initializer. */
+ if (dladdr(_DYNAMIC, &info) != 0) {
+ self_elf = LoadedElf::Create(info.dli_fname, info.dli_fbase);
+ }
+#if defined(ANDROID)
+ if (dladdr(FunctionPtr(syscall), &info) != 0) {
+ libc = LoadedElf::Create(info.dli_fname, info.dli_fbase);
+ }
+#endif
+}
+
+ElfLoader::~ElfLoader()
+{
+ LibHandleList list;
+
+ if (!Singleton.IsShutdownExpected()) {
+ MOZ_CRASH("Unexpected shutdown");
+ }
+
+ /* Release self_elf and libc */
+ self_elf = nullptr;
+#if defined(ANDROID)
+ libc = nullptr;
+#endif
+
+ /* Build up a list of all library handles with direct (external) references.
+ * We actually skip system library handles because we want to keep at least
+ * some of these open. Most notably, Mozilla codebase keeps a few libgnome
+ * libraries deliberately open because of the mess that libORBit destruction
+ * is. dlclose()ing these libraries actually leads to problems. */
+ for (LibHandleList::reverse_iterator it = handles.rbegin();
+ it < handles.rend(); ++it) {
+ if ((*it)->DirectRefCount()) {
+ if (SystemElf *se = (*it)->AsSystemElf()) {
+ se->Forget();
+ } else {
+ list.push_back(*it);
+ }
+ }
+ }
+ /* Force release all external references to the handles collected above */
+ for (LibHandleList::iterator it = list.begin(); it < list.end(); ++it) {
+ while ((*it)->ReleaseDirectRef()) { }
+ }
+ /* Remove the remaining system handles. */
+ if (handles.size()) {
+ list = handles;
+ for (LibHandleList::reverse_iterator it = list.rbegin();
+ it < list.rend(); ++it) {
+ if ((*it)->AsSystemElf()) {
+ DEBUG_LOG("ElfLoader::~ElfLoader(): Remaining handle for \"%s\" "
+ "[%d direct refs, %d refs total]", (*it)->GetPath(),
+ (*it)->DirectRefCount(), (*it)->refCount());
+ } else {
+ DEBUG_LOG("ElfLoader::~ElfLoader(): Unexpected remaining handle for \"%s\" "
+ "[%d direct refs, %d refs total]", (*it)->GetPath(),
+ (*it)->DirectRefCount(), (*it)->refCount());
+ /* Not removing, since it could have references to other libraries,
+ * destroying them as a side effect, and possibly leaving dangling
+ * pointers in the handle list we're scanning */
+ }
+ }
+ }
+}
+
+void
+ElfLoader::stats(const char *when)
+{
+ if (MOZ_LIKELY(!Logging::isVerbose()))
+ return;
+
+ for (LibHandleList::iterator it = Singleton.handles.begin();
+ it < Singleton.handles.end(); ++it)
+ (*it)->stats(when);
+}
+
+#ifdef __ARM_EABI__
+int
+ElfLoader::__wrap_aeabi_atexit(void *that, ElfLoader::Destructor destructor,
+ void *dso_handle)
+{
+ Singleton.destructors.push_back(
+ DestructorCaller(destructor, that, dso_handle));
+ return 0;
+}
+#else
+int
+ElfLoader::__wrap_cxa_atexit(ElfLoader::Destructor destructor, void *that,
+ void *dso_handle)
+{
+ Singleton.destructors.push_back(
+ DestructorCaller(destructor, that, dso_handle));
+ return 0;
+}
+#endif
+
+void
+ElfLoader::__wrap_cxa_finalize(void *dso_handle)
+{
+ /* Call all destructors for the given DSO handle in reverse order they were
+ * registered. */
+ std::vector<DestructorCaller>::reverse_iterator it;
+ for (it = Singleton.destructors.rbegin();
+ it < Singleton.destructors.rend(); ++it) {
+ if (it->IsForHandle(dso_handle)) {
+ it->Call();
+ }
+ }
+}
+
+void
+ElfLoader::DestructorCaller::Call()
+{
+ if (destructor) {
+ DEBUG_LOG("ElfLoader::DestructorCaller::Call(%p, %p, %p)",
+ FunctionPtr(destructor), object, dso_handle);
+ destructor(object);
+ destructor = nullptr;
+ }
+}
+
+ElfLoader::DebuggerHelper::DebuggerHelper(): dbg(nullptr), firstAdded(nullptr)
+{
+ /* Find ELF auxiliary vectors.
+ *
+ * The kernel stores the following data on the stack when starting a
+ * program:
+ * argc
+ * argv[0] (pointer into argv strings defined below)
+ * argv[1] (likewise)
+ * ...
+ * argv[argc - 1] (likewise)
+ * nullptr
+ * envp[0] (pointer into environment strings defined below)
+ * envp[1] (likewise)
+ * ...
+ * envp[n] (likewise)
+ * nullptr
+ * ... (more NULLs on some platforms such as Android 4.3)
+ * auxv[0] (first ELF auxiliary vector)
+ * auxv[1] (second ELF auxiliary vector)
+ * ...
+ * auxv[p] (last ELF auxiliary vector)
+ * (AT_NULL, nullptr)
+ * padding
+ * argv strings, separated with '\0'
+ * environment strings, separated with '\0'
+ * nullptr
+ *
+ * What we are after are the auxv values defined by the following struct.
+ */
+ struct AuxVector {
+ Elf::Addr type;
+ Elf::Addr value;
+ };
+
+ /* Pointer to the environment variables list */
+ extern char **environ;
+
+ /* The environment may have changed since the program started, in which
+ * case the environ variables list isn't the list the kernel put on stack
+ * anymore. But in this new list, variables that didn't change still point
+ * to the strings the kernel put on stack. It is quite unlikely that two
+ * modified environment variables point to two consecutive strings in memory,
+ * so we assume that if two consecutive environment variables point to two
+ * consecutive strings, we found strings the kernel put on stack. */
+ char **env;
+ for (env = environ; *env; env++)
+ if (*env + strlen(*env) + 1 == env[1])
+ break;
+ if (!*env)
+ return;
+
+ /* Next, we scan the stack backwards to find a pointer to one of those
+ * strings we found above, which will give us the location of the original
+ * envp list. As we are looking for pointers, we need to look at 32-bits or
+ * 64-bits aligned values, depening on the architecture. */
+ char **scan = reinterpret_cast<char **>(
+ reinterpret_cast<uintptr_t>(*env) & ~(sizeof(void *) - 1));
+ while (*env != *scan)
+ scan--;
+
+ /* Finally, scan forward to find the last environment variable pointer and
+ * thus the first auxiliary vector. */
+ while (*scan++);
+
+ /* Some platforms have more NULLs here, so skip them if we encounter them */
+ while (!*scan)
+ scan++;
+
+ AuxVector *auxv = reinterpret_cast<AuxVector *>(scan);
+
+ /* The two values of interest in the auxiliary vectors are AT_PHDR and
+ * AT_PHNUM, which gives us the the location and size of the ELF program
+ * headers. */
+ Array<Elf::Phdr> phdrs;
+ char *base = nullptr;
+ while (auxv->type) {
+ if (auxv->type == AT_PHDR) {
+ phdrs.Init(reinterpret_cast<Elf::Phdr*>(auxv->value));
+ /* Assume the base address is the first byte of the same page */
+ base = reinterpret_cast<char *>(PageAlignedPtr(auxv->value));
+ }
+ if (auxv->type == AT_PHNUM)
+ phdrs.Init(auxv->value);
+ auxv++;
+ }
+
+ if (!phdrs) {
+ DEBUG_LOG("Couldn't find program headers");
+ return;
+ }
+
+ /* In some cases, the address for the program headers we get from the
+ * auxiliary vectors is not mapped, because of the PT_LOAD segments
+ * definitions in the program executable. Trying to map anonymous memory
+ * with a hint giving the base address will return a different address
+ * if something is mapped there, and the base address otherwise. */
+ MappedPtr mem(MemoryRange::mmap(base, PageSize(), PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+ if (mem == base) {
+ /* If program headers aren't mapped, try to map them */
+ int fd = open("/proc/self/exe", O_RDONLY);
+ if (fd == -1) {
+ DEBUG_LOG("Failed to open /proc/self/exe");
+ return;
+ }
+ mem.Assign(MemoryRange::mmap(base, PageSize(), PROT_READ, MAP_PRIVATE,
+ fd, 0));
+ /* If we don't manage to map at the right address, just give up. */
+ if (mem != base) {
+ DEBUG_LOG("Couldn't read program headers");
+ return;
+ }
+ }
+ /* Sanity check: the first bytes at the base address should be an ELF
+ * header. */
+ if (!Elf::Ehdr::validate(base)) {
+ DEBUG_LOG("Couldn't find program base");
+ return;
+ }
+
+ /* Search for the program PT_DYNAMIC segment */
+ Array<Elf::Dyn> dyns;
+ for (Array<Elf::Phdr>::iterator phdr = phdrs.begin(); phdr < phdrs.end();
+ ++phdr) {
+ /* While the program headers are expected within the first mapped page of
+ * the program executable, the executable PT_LOADs may actually make them
+ * loaded at an address that is not the wanted base address of the
+ * library. We thus need to adjust the base address, compensating for the
+ * virtual address of the PT_LOAD segment corresponding to offset 0. */
+ if (phdr->p_type == PT_LOAD && phdr->p_offset == 0)
+ base -= phdr->p_vaddr;
+ if (phdr->p_type == PT_DYNAMIC)
+ dyns.Init(base + phdr->p_vaddr, phdr->p_filesz);
+ }
+ if (!dyns) {
+ DEBUG_LOG("Failed to find PT_DYNAMIC section in program");
+ return;
+ }
+
+ /* Search for the DT_DEBUG information */
+ for (Array<Elf::Dyn>::iterator dyn = dyns.begin(); dyn < dyns.end(); ++dyn) {
+ if (dyn->d_tag == DT_DEBUG) {
+ dbg = reinterpret_cast<r_debug *>(dyn->d_un.d_ptr);
+ break;
+ }
+ }
+ DEBUG_LOG("DT_DEBUG points at %p", static_cast<void *>(dbg));
+}
+
+/**
+ * Helper class to ensure the given pointer is writable within the scope of
+ * an instance. Permissions to the memory page where the pointer lies are
+ * restored to their original value when the instance is destroyed.
+ */
+class EnsureWritable
+{
+public:
+ template <typename T>
+ EnsureWritable(T *ptr, size_t length_ = sizeof(T))
+ {
+ MOZ_ASSERT(length_ < PageSize());
+ prot = -1;
+ page = MAP_FAILED;
+
+ char *firstPage = PageAlignedPtr(reinterpret_cast<char *>(ptr));
+ char *lastPageEnd = PageAlignedEndPtr(reinterpret_cast<char *>(ptr) + length_);
+ length = lastPageEnd - firstPage;
+ uintptr_t start = reinterpret_cast<uintptr_t>(firstPage);
+ uintptr_t end;
+
+ prot = getProt(start, &end);
+ if (prot == -1 || (start + length) > end)
+ MOZ_CRASH();
+
+ if (prot & PROT_WRITE)
+ return;
+
+ page = firstPage;
+ mprotect(page, length, prot | PROT_WRITE);
+ }
+
+ ~EnsureWritable()
+ {
+ if (page != MAP_FAILED) {
+ mprotect(page, length, prot);
+}
+ }
+
+private:
+ int getProt(uintptr_t addr, uintptr_t *end)
+ {
+ /* The interesting part of the /proc/self/maps format looks like:
+ * startAddr-endAddr rwxp */
+ int result = 0;
+ AutoCloseFILE f(fopen("/proc/self/maps", "r"));
+ while (f) {
+ unsigned long long startAddr, endAddr;
+ char perms[5];
+ if (fscanf(f, "%llx-%llx %4s %*1024[^\n] ", &startAddr, &endAddr, perms) != 3)
+ return -1;
+ if (addr < startAddr || addr >= endAddr)
+ continue;
+ if (perms[0] == 'r')
+ result |= PROT_READ;
+ else if (perms[0] != '-')
+ return -1;
+ if (perms[1] == 'w')
+ result |= PROT_WRITE;
+ else if (perms[1] != '-')
+ return -1;
+ if (perms[2] == 'x')
+ result |= PROT_EXEC;
+ else if (perms[2] != '-')
+ return -1;
+ *end = endAddr;
+ return result;
+ }
+ return -1;
+ }
+
+ int prot;
+ void *page;
+ size_t length;
+};
+
+/**
+ * The system linker maintains a doubly linked list of library it loads
+ * for use by the debugger. Unfortunately, it also uses the list pointers
+ * in a lot of operations and adding our data in the list is likely to
+ * trigger crashes when the linker tries to use data we don't provide or
+ * that fall off the amount data we allocated. Fortunately, the linker only
+ * traverses the list forward and accesses the head of the list from a
+ * private pointer instead of using the value in the r_debug structure.
+ * This means we can safely add members at the beginning of the list.
+ * Unfortunately, gdb checks the coherency of l_prev values, so we have
+ * to adjust the l_prev value for the first element the system linker
+ * knows about. Fortunately, it doesn't use l_prev, and the first element
+ * is not ever going to be released before our elements, since it is the
+ * program executable, so the system linker should not be changing
+ * r_debug::r_map.
+ */
+void
+ElfLoader::DebuggerHelper::Add(ElfLoader::link_map *map)
+{
+ if (!dbg->r_brk)
+ return;
+ dbg->r_state = r_debug::RT_ADD;
+ dbg->r_brk();
+ map->l_prev = nullptr;
+ map->l_next = dbg->r_map;
+ if (!firstAdded) {
+ firstAdded = map;
+ /* When adding a library for the first time, r_map points to data
+ * handled by the system linker, and that data may be read-only */
+ EnsureWritable w(&dbg->r_map->l_prev);
+ dbg->r_map->l_prev = map;
+ } else
+ dbg->r_map->l_prev = map;
+ dbg->r_map = map;
+ dbg->r_state = r_debug::RT_CONSISTENT;
+ dbg->r_brk();
+}
+
+void
+ElfLoader::DebuggerHelper::Remove(ElfLoader::link_map *map)
+{
+ if (!dbg->r_brk)
+ return;
+ dbg->r_state = r_debug::RT_DELETE;
+ dbg->r_brk();
+ if (dbg->r_map == map)
+ dbg->r_map = map->l_next;
+ else if (map->l_prev) {
+ map->l_prev->l_next = map->l_next;
+ }
+ if (map == firstAdded) {
+ firstAdded = map->l_prev;
+ /* When removing the first added library, its l_next is going to be
+ * data handled by the system linker, and that data may be read-only */
+ EnsureWritable w(&map->l_next->l_prev);
+ map->l_next->l_prev = map->l_prev;
+ } else if (map->l_next) {
+ map->l_next->l_prev = map->l_prev;
+ }
+ dbg->r_state = r_debug::RT_CONSISTENT;
+ dbg->r_brk();
+}
+
+#if defined(ANDROID)
+/* As some system libraries may be calling signal() or sigaction() to
+ * set a SIGSEGV handler, effectively breaking MappableSeekableZStream,
+ * or worse, restore our SIGSEGV handler with wrong flags (which using
+ * signal() will do), we want to hook into the system's sigaction() to
+ * replace it with our own wrapper instead, so that our handler is never
+ * replaced. We used to only do that with libraries this linker loads,
+ * but it turns out at least one system library does call signal() and
+ * breaks us (libsc-a3xx.so on the Samsung Galaxy S4).
+ * As libc's signal (bsd_signal/sysv_signal, really) calls sigaction
+ * under the hood, instead of calling the signal system call directly,
+ * we only need to hook sigaction. This is true for both bionic and
+ * glibc.
+ */
+
+/* libc's sigaction */
+extern "C" int
+sigaction(int signum, const struct sigaction *act,
+ struct sigaction *oldact);
+
+/* Simple reimplementation of sigaction. This is roughly equivalent
+ * to the assembly that comes in bionic, but not quite equivalent to
+ * glibc's implementation, so we only use this on Android. */
+int
+sys_sigaction(int signum, const struct sigaction *act,
+ struct sigaction *oldact)
+{
+ return syscall(__NR_sigaction, signum, act, oldact);
+}
+
+/* Replace the first instructions of the given function with a jump
+ * to the given new function. */
+template <typename T>
+static bool
+Divert(T func, T new_func)
+{
+ void *ptr = FunctionPtr(func);
+ uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
+
+#if defined(__i386__)
+ // A 32-bit jump is a 5 bytes instruction.
+ EnsureWritable w(ptr, 5);
+ *reinterpret_cast<unsigned char *>(addr) = 0xe9; // jmp
+ *reinterpret_cast<intptr_t *>(addr + 1) =
+ reinterpret_cast<uintptr_t>(new_func) - addr - 5; // target displacement
+ return true;
+#elif defined(__arm__)
+ const unsigned char trampoline[] = {
+ // .thumb
+ 0x46, 0x04, // nop
+ 0x78, 0x47, // bx pc
+ 0x46, 0x04, // nop
+ // .arm
+ 0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc, #-4]
+ // .word <new_func>
+ };
+ const unsigned char *start;
+ if (addr & 0x01) {
+ /* Function is thumb, the actual address of the code is without the
+ * least significant bit. */
+ addr--;
+ /* The arm part of the trampoline needs to be 32-bit aligned */
+ if (addr & 0x02)
+ start = trampoline;
+ else
+ start = trampoline + 2;
+ } else {
+ /* Function is arm, we only need the arm part of the trampoline */
+ start = trampoline + 6;
+ }
+
+ size_t len = sizeof(trampoline) - (start - trampoline);
+ EnsureWritable w(reinterpret_cast<void *>(addr), len + sizeof(void *));
+ memcpy(reinterpret_cast<void *>(addr), start, len);
+ *reinterpret_cast<void **>(addr + len) = FunctionPtr(new_func);
+ cacheflush(addr, addr + len + sizeof(void *), 0);
+ return true;
+#else
+ return false;
+#endif
+}
+#else
+#define sys_sigaction sigaction
+template <typename T>
+static bool
+Divert(T func, T new_func)
+{
+ return false;
+}
+#endif
+
+namespace {
+
+/* Clock that only accounts for time spent in the current process. */
+static uint64_t ProcessTimeStamp_Now()
+{
+ struct timespec ts;
+ int rv = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
+
+ if (rv != 0) {
+ return 0;
+ }
+
+ uint64_t baseNs = (uint64_t)ts.tv_sec * 1000000000;
+ return baseNs + (uint64_t)ts.tv_nsec;
+}
+
+}
+
+/* Data structure used to pass data to the temporary signal handler,
+ * as well as triggering a test crash. */
+struct TmpData {
+ volatile int crash_int;
+ volatile uint64_t crash_timestamp;
+};
+
+SEGVHandler::SEGVHandler()
+: initialized(false), registeredHandler(false), signalHandlingBroken(true)
+, signalHandlingSlow(true)
+{
+ /* Ensure logging is initialized before the DEBUG_LOG in the test_handler.
+ * As this constructor runs before the ElfLoader constructor (by effect
+ * of ElfLoader inheriting from this class), this also initializes on behalf
+ * of ElfLoader and DebuggerHelper. */
+ Logging::Init();
+
+ /* Initialize oldStack.ss_flags to an invalid value when used to set
+ * an alternative stack, meaning we haven't got information about the
+ * original alternative stack and thus don't mean to restore it in
+ * the destructor. */
+ oldStack.ss_flags = SS_ONSTACK;
+
+ /* Get the current segfault signal handler. */
+ struct sigaction old_action;
+ sys_sigaction(SIGSEGV, nullptr, &old_action);
+
+ /* Some devices don't provide useful information to their SIGSEGV handlers,
+ * making it impossible for on-demand decompression to work. To check if
+ * we're on such a device, setup a temporary handler and deliberately
+ * trigger a segfault. The handler will set signalHandlingBroken if the
+ * provided information is bogus.
+ * Some other devices have a kernel option enabled that makes SIGSEGV handler
+ * have an overhead so high that it affects how on-demand decompression
+ * performs. The handler will also set signalHandlingSlow if the triggered
+ * SIGSEGV took too much time. */
+ struct sigaction action;
+ action.sa_sigaction = &SEGVHandler::test_handler;
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = SA_SIGINFO | SA_NODEFER;
+ action.sa_restorer = nullptr;
+ stackPtr.Assign(MemoryRange::mmap(nullptr, PageSize(),
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+ if (stackPtr.get() == MAP_FAILED)
+ return;
+ if (sys_sigaction(SIGSEGV, &action, nullptr))
+ return;
+
+ TmpData *data = reinterpret_cast<TmpData*>(stackPtr.get());
+ data->crash_timestamp = ProcessTimeStamp_Now();
+ mprotect(stackPtr, stackPtr.GetLength(), PROT_NONE);
+ data->crash_int = 123;
+ /* Restore the original segfault signal handler. */
+ sys_sigaction(SIGSEGV, &old_action, nullptr);
+ stackPtr.Assign(MAP_FAILED, 0);
+}
+
+void
+SEGVHandler::FinishInitialization()
+{
+ /* Ideally, we'd need some locking here, but in practice, we're not
+ * going to race with another thread. */
+ initialized = true;
+
+ if (signalHandlingBroken || signalHandlingSlow)
+ return;
+
+ typedef int (*sigaction_func)(int, const struct sigaction *,
+ struct sigaction *);
+
+ sigaction_func libc_sigaction;
+
+#if defined(ANDROID)
+ /* Android > 4.4 comes with a sigaction wrapper in a LD_PRELOADed library
+ * (libsigchain) for ART. That wrapper kind of does the same trick as we
+ * do, so we need extra care in handling it.
+ * - Divert the libc's sigaction, assuming the LD_PRELOADed library uses
+ * it under the hood (which is more or less true according to the source
+ * of that library, since it's doing a lookup in RTLD_NEXT)
+ * - With the LD_PRELOADed library in place, all calls to sigaction from
+ * from system libraries will go to the LD_PRELOADed library.
+ * - The LD_PRELOADed library calls to sigaction go to our __wrap_sigaction.
+ * - The calls to sigaction from libraries faulty.lib loads are sent to
+ * the LD_PRELOADed library.
+ * In practice, for signal handling, this means:
+ * - The signal handler registered to the kernel is ours.
+ * - Our handler redispatches to the LD_PRELOADed library's if there's a
+ * segfault we don't handle.
+ * - The LD_PRELOADed library redispatches according to whatever system
+ * library or faulty.lib-loaded library set with sigaction.
+ *
+ * When there is no sigaction wrapper in place:
+ * - Divert the libc's sigaction.
+ * - Calls to sigaction from system library and faulty.lib-loaded libraries
+ * all go to the libc's sigaction, which end up in our __wrap_sigaction.
+ * - The signal handler registered to the kernel is ours.
+ * - Our handler redispatches according to whatever system library or
+ * faulty.lib-loaded library set with sigaction.
+ */
+ void *libc = dlopen("libc.so", RTLD_GLOBAL | RTLD_LAZY);
+ if (libc) {
+ /*
+ * Lollipop bionic only has a small trampoline in sigaction, with the real
+ * work happening in __sigaction. Divert there instead of sigaction if it exists.
+ * Bug 1154803
+ */
+ libc_sigaction = reinterpret_cast<sigaction_func>(dlsym(libc, "__sigaction"));
+
+ if (!libc_sigaction) {
+ libc_sigaction =
+ reinterpret_cast<sigaction_func>(dlsym(libc, "sigaction"));
+ }
+ } else
+#endif
+ {
+ libc_sigaction = sigaction;
+ }
+
+ if (!Divert(libc_sigaction, __wrap_sigaction))
+ return;
+
+ /* Setup an alternative stack if the already existing one is not big
+ * enough, or if there is none. */
+ if (sigaltstack(nullptr, &oldStack) == 0) {
+ if (oldStack.ss_flags == SS_ONSTACK)
+ oldStack.ss_flags = 0;
+ if (!oldStack.ss_sp || oldStack.ss_size < stackSize) {
+ stackPtr.Assign(MemoryRange::mmap(nullptr, stackSize,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+ if (stackPtr.get() == MAP_FAILED)
+ return;
+ stack_t stack;
+ stack.ss_sp = stackPtr;
+ stack.ss_size = stackSize;
+ stack.ss_flags = 0;
+ if (sigaltstack(&stack, nullptr) != 0)
+ return;
+ }
+ }
+ /* Register our own handler, and store the already registered one in
+ * SEGVHandler's struct sigaction member */
+ action.sa_sigaction = &SEGVHandler::handler;
+ action.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
+ registeredHandler = !sys_sigaction(SIGSEGV, &action, &this->action);
+}
+
+SEGVHandler::~SEGVHandler()
+{
+ /* Restore alternative stack for signals */
+ if (oldStack.ss_flags != SS_ONSTACK)
+ sigaltstack(&oldStack, nullptr);
+ /* Restore original signal handler */
+ if (registeredHandler)
+ sys_sigaction(SIGSEGV, &this->action, nullptr);
+}
+
+/* Test handler for a deliberately triggered SIGSEGV that determines whether
+ * useful information is provided to signal handlers, particularly whether
+ * si_addr is filled in properly, and whether the segfault handler is called
+ * quickly enough. */
+void SEGVHandler::test_handler(int signum, siginfo_t *info, void *context)
+{
+ SEGVHandler &that = ElfLoader::Singleton;
+ if (signum == SIGSEGV && info &&
+ info->si_addr == that.stackPtr.get())
+ that.signalHandlingBroken = false;
+ mprotect(that.stackPtr, that.stackPtr.GetLength(), PROT_READ | PROT_WRITE);
+ TmpData *data = reinterpret_cast<TmpData*>(that.stackPtr.get());
+ uint64_t latency = ProcessTimeStamp_Now() - data->crash_timestamp;
+ DEBUG_LOG("SEGVHandler latency: %" PRIu64, latency);
+ /* See bug 886736 for timings on different devices, 150 µs is reasonably above
+ * the latency on "working" devices and seems to be short enough to not incur
+ * a huge overhead to on-demand decompression. */
+ if (latency <= 150000)
+ that.signalHandlingSlow = false;
+}
+
+/* TODO: "properly" handle signal masks and flags */
+void SEGVHandler::handler(int signum, siginfo_t *info, void *context)
+{
+ //ASSERT(signum == SIGSEGV);
+ DEBUG_LOG("Caught segmentation fault @%p", info->si_addr);
+
+ /* Check whether we segfaulted in the address space of a CustomElf. We're
+ * only expecting that to happen as an access error. */
+ if (info->si_code == SEGV_ACCERR) {
+ RefPtr<LibHandle> handle =
+ ElfLoader::Singleton.GetHandleByPtr(info->si_addr);
+ BaseElf *elf;
+ if (handle && (elf = handle->AsBaseElf())) {
+ DEBUG_LOG("Within the address space of %s", handle->GetPath());
+ if (elf->mappable && elf->mappable->ensure(info->si_addr)) {
+ return;
+ }
+ }
+ }
+
+ /* Redispatch to the registered handler */
+ SEGVHandler &that = ElfLoader::Singleton;
+ if (that.action.sa_flags & SA_SIGINFO) {
+ DEBUG_LOG("Redispatching to registered handler @%p",
+ FunctionPtr(that.action.sa_sigaction));
+ that.action.sa_sigaction(signum, info, context);
+ } else if (that.action.sa_handler == SIG_DFL) {
+ DEBUG_LOG("Redispatching to default handler");
+ /* Reset the handler to the default one, and trigger it. */
+ sys_sigaction(signum, &that.action, nullptr);
+ raise(signum);
+ } else if (that.action.sa_handler != SIG_IGN) {
+ DEBUG_LOG("Redispatching to registered handler @%p",
+ FunctionPtr(that.action.sa_handler));
+ that.action.sa_handler(signum);
+ } else {
+ DEBUG_LOG("Ignoring");
+ }
+}
+
+int
+SEGVHandler::__wrap_sigaction(int signum, const struct sigaction *act,
+ struct sigaction *oldact)
+{
+ SEGVHandler &that = ElfLoader::Singleton;
+
+ /* Use system sigaction() function for all but SIGSEGV signals. */
+ if (!that.registeredHandler || (signum != SIGSEGV))
+ return sys_sigaction(signum, act, oldact);
+
+ if (oldact)
+ *oldact = that.action;
+ if (act)
+ that.action = *act;
+ return 0;
+}
+
+Logging Logging::Singleton;
diff --git a/mozglue/linker/ElfLoader.h b/mozglue/linker/ElfLoader.h
new file mode 100644
index 000000000..0d26a011e
--- /dev/null
+++ b/mozglue/linker/ElfLoader.h
@@ -0,0 +1,674 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef ElfLoader_h
+#define ElfLoader_h
+
+#include <vector>
+#include <dlfcn.h>
+#include <signal.h>
+#include "mozilla/RefCounted.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "Zip.h"
+#include "Elfxx.h"
+#include "Mappable.h"
+
+/**
+ * dlfcn.h replacement functions
+ */
+extern "C" {
+ void *__wrap_dlopen(const char *path, int flags);
+ const char *__wrap_dlerror(void);
+ void *__wrap_dlsym(void *handle, const char *symbol);
+ int __wrap_dlclose(void *handle);
+
+#ifndef HAVE_DLADDR
+ typedef struct {
+ const char *dli_fname;
+ void *dli_fbase;
+ const char *dli_sname;
+ void *dli_saddr;
+ } Dl_info;
+#endif
+ int __wrap_dladdr(void *addr, Dl_info *info);
+
+ struct dl_phdr_info {
+ Elf::Addr dlpi_addr;
+ const char *dlpi_name;
+ const Elf::Phdr *dlpi_phdr;
+ Elf::Half dlpi_phnum;
+ };
+
+ typedef int (*dl_phdr_cb)(struct dl_phdr_info *, size_t, void *);
+ int __wrap_dl_iterate_phdr(dl_phdr_cb callback, void *data);
+
+#ifdef __ARM_EABI__
+ const void *__wrap___gnu_Unwind_Find_exidx(void *pc, int *pcount);
+#endif
+
+/**
+ * faulty.lib public API
+ */
+MFBT_API size_t
+__dl_get_mappable_length(void *handle);
+
+MFBT_API void *
+__dl_mmap(void *handle, void *addr, size_t length, off_t offset);
+
+MFBT_API void
+__dl_munmap(void *handle, void *addr, size_t length);
+
+MFBT_API bool
+IsSignalHandlingBroken();
+
+}
+
+/* Forward declarations for use in LibHandle */
+class BaseElf;
+class CustomElf;
+class SystemElf;
+
+/**
+ * Specialize RefCounted template for LibHandle. We may get references to
+ * LibHandles during the execution of their destructor, so we need
+ * RefCounted<LibHandle>::Release to support some reentrancy. See further
+ * below.
+ */
+class LibHandle;
+
+namespace mozilla {
+namespace detail {
+
+template <> inline void RefCounted<LibHandle, AtomicRefCount>::Release() const;
+
+template <> inline RefCounted<LibHandle, AtomicRefCount>::~RefCounted()
+{
+ MOZ_ASSERT(mRefCnt == 0x7fffdead);
+}
+
+} /* namespace detail */
+} /* namespace mozilla */
+
+/**
+ * Abstract class for loaded libraries. Libraries may be loaded through the
+ * system linker or this linker, both cases will be derived from this class.
+ */
+class LibHandle: public mozilla::external::AtomicRefCounted<LibHandle>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(LibHandle)
+ /**
+ * Constructor. Takes the path of the loaded library and will store a copy
+ * of the leaf name.
+ */
+ LibHandle(const char *path)
+ : directRefCnt(0), path(path ? strdup(path) : nullptr), mappable(nullptr) { }
+
+ /**
+ * Destructor.
+ */
+ virtual ~LibHandle();
+
+ /**
+ * Returns the pointer to the address to which the given symbol resolves
+ * inside the library. It is not supposed to resolve the symbol in other
+ * libraries, although in practice, it will for system libraries.
+ */
+ virtual void *GetSymbolPtr(const char *symbol) const = 0;
+
+ /**
+ * Returns whether the given address is part of the virtual address space
+ * covered by the loaded library.
+ */
+ virtual bool Contains(void *addr) const = 0;
+
+ /**
+ * Returns the base address of the loaded library.
+ */
+ virtual void *GetBase() const = 0;
+
+ /**
+ * Returns the file name of the library without the containing directory.
+ */
+ const char *GetName() const;
+
+ /**
+ * Returns the full path of the library, when available. Otherwise, returns
+ * the file name.
+ */
+ const char *GetPath() const
+ {
+ return path;
+ }
+
+ /**
+ * Library handles can be referenced from other library handles or
+ * externally (when dlopen()ing using this linker). We need to be
+ * able to distinguish between the two kind of referencing for better
+ * bookkeeping.
+ */
+ void AddDirectRef()
+ {
+ ++directRefCnt;
+ mozilla::external::AtomicRefCounted<LibHandle>::AddRef();
+ }
+
+ /**
+ * Releases a direct reference, and returns whether there are any direct
+ * references left.
+ */
+ bool ReleaseDirectRef()
+ {
+ bool ret = false;
+ if (directRefCnt) {
+ MOZ_ASSERT(directRefCnt <=
+ mozilla::external::AtomicRefCounted<LibHandle>::refCount());
+ if (--directRefCnt)
+ ret = true;
+ mozilla::external::AtomicRefCounted<LibHandle>::Release();
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the number of direct references
+ */
+ MozRefCountType DirectRefCount()
+ {
+ return directRefCnt;
+ }
+
+ /**
+ * Returns the complete size of the file or stream behind the library
+ * handle.
+ */
+ size_t GetMappableLength() const;
+
+ /**
+ * Returns a memory mapping of the file or stream behind the library
+ * handle.
+ */
+ void *MappableMMap(void *addr, size_t length, off_t offset) const;
+
+ /**
+ * Unmaps a memory mapping of the file or stream behind the library
+ * handle.
+ */
+ void MappableMUnmap(void *addr, size_t length) const;
+
+#ifdef __ARM_EABI__
+ /**
+ * Find the address and entry count of the ARM.exidx section
+ * associated with the library
+ */
+ virtual const void *FindExidx(int *pcount) const = 0;
+#endif
+
+ /**
+ * Shows some stats about the Mappable instance. The when argument is to be
+ * used by the caller to give an identifier of the when the stats call is
+ * made.
+ */
+ virtual void stats(const char *when) const { };
+
+protected:
+ /**
+ * Returns a mappable object for use by MappableMMap and related functions.
+ */
+ virtual Mappable *GetMappable() const = 0;
+
+ /**
+ * Returns the instance, casted as the wanted type. Returns nullptr if
+ * that's not the actual type. (short of a better way to do this without
+ * RTTI)
+ */
+ friend class ElfLoader;
+ friend class CustomElf;
+ friend class SEGVHandler;
+ virtual BaseElf *AsBaseElf() { return nullptr; }
+ virtual SystemElf *AsSystemElf() { return nullptr; }
+
+private:
+ MozRefCountType directRefCnt;
+ char *path;
+
+ /* Mappable object keeping the result of GetMappable() */
+ mutable RefPtr<Mappable> mappable;
+};
+
+/**
+ * Specialized RefCounted<LibHandle>::Release. Under normal operation, when
+ * mRefCnt reaches 0, the LibHandle is deleted. Its mRefCnt is however
+ * increased to 1 on normal builds, and 0x7fffdead on debug builds so that the
+ * LibHandle can still be referenced while the destructor is executing. The
+ * mRefCnt is allowed to grow > 0x7fffdead, but not to decrease under that
+ * value, which would mean too many Releases from within the destructor.
+ */
+namespace mozilla {
+namespace detail {
+
+template <> inline void RefCounted<LibHandle, AtomicRefCount>::Release() const {
+#ifdef DEBUG
+ if (mRefCnt > 0x7fff0000)
+ MOZ_ASSERT(mRefCnt > 0x7fffdead);
+#endif
+ MOZ_ASSERT(mRefCnt > 0);
+ if (mRefCnt > 0) {
+ if (0 == --mRefCnt) {
+#ifdef DEBUG
+ mRefCnt = 0x7fffdead;
+#else
+ mRefCnt = 1;
+#endif
+ delete static_cast<const LibHandle*>(this);
+ }
+ }
+}
+
+} /* namespace detail */
+} /* namespace mozilla */
+
+/**
+ * Class handling libraries loaded by the system linker
+ */
+class SystemElf: public LibHandle
+{
+public:
+ /**
+ * Returns a new SystemElf for the given path. The given flags are passed
+ * to dlopen().
+ */
+ static already_AddRefed<LibHandle> Load(const char *path, int flags);
+
+ /**
+ * Inherited from LibHandle
+ */
+ virtual ~SystemElf();
+ virtual void *GetSymbolPtr(const char *symbol) const;
+ virtual bool Contains(void *addr) const { return false; /* UNIMPLEMENTED */ }
+ virtual void *GetBase() const { return nullptr; /* UNIMPLEMENTED */ }
+
+#ifdef __ARM_EABI__
+ virtual const void *FindExidx(int *pcount) const;
+#endif
+
+protected:
+ virtual Mappable *GetMappable() const;
+
+ /**
+ * Returns the instance, casted as SystemElf. (short of a better way to do
+ * this without RTTI)
+ */
+ friend class ElfLoader;
+ virtual SystemElf *AsSystemElf() { return this; }
+
+ /**
+ * Remove the reference to the system linker handle. This avoids dlclose()
+ * being called when the instance is destroyed.
+ */
+ void Forget()
+ {
+ dlhandle = nullptr;
+ }
+
+private:
+ /**
+ * Private constructor
+ */
+ SystemElf(const char *path, void *handle)
+ : LibHandle(path), dlhandle(handle) { }
+
+ /* Handle as returned by system dlopen() */
+ void *dlhandle;
+};
+
+/**
+ * The ElfLoader registers its own SIGSEGV handler to handle segmentation
+ * faults within the address space of the loaded libraries. It however
+ * allows a handler to be set for faults in other places, and redispatches
+ * to the handler set through signal() or sigaction().
+ */
+class SEGVHandler
+{
+public:
+ bool hasRegisteredHandler() {
+ if (! initialized)
+ FinishInitialization();
+ return registeredHandler;
+ }
+
+ bool isSignalHandlingBroken() {
+ return signalHandlingBroken;
+ }
+
+ static int __wrap_sigaction(int signum, const struct sigaction *act,
+ struct sigaction *oldact);
+
+protected:
+ SEGVHandler();
+ ~SEGVHandler();
+
+private:
+
+ /**
+ * The constructor doesn't do all initialization, and the tail is done
+ * at a later time.
+ */
+ void FinishInitialization();
+
+ /**
+ * SIGSEGV handler registered with __wrap_signal or __wrap_sigaction.
+ */
+ struct sigaction action;
+
+ /**
+ * ElfLoader SIGSEGV handler.
+ */
+ static void handler(int signum, siginfo_t *info, void *context);
+
+ /**
+ * Temporary test handler.
+ */
+ static void test_handler(int signum, siginfo_t *info, void *context);
+
+ /**
+ * Size of the alternative stack. The printf family requires more than 8KB
+ * of stack, and our signal handler may print a few things.
+ */
+ static const size_t stackSize = 12 * 1024;
+
+ /**
+ * Alternative stack information used before initialization.
+ */
+ stack_t oldStack;
+
+ /**
+ * Pointer to an alternative stack for signals. Only set if oldStack is
+ * not set or not big enough.
+ */
+ MappedPtr stackPtr;
+
+ bool initialized;
+ bool registeredHandler;
+ bool signalHandlingBroken;
+ bool signalHandlingSlow;
+};
+
+/**
+ * Elf Loader class in charge of loading and bookkeeping libraries.
+ */
+class ElfLoader: public SEGVHandler
+{
+public:
+ /**
+ * The Elf Loader instance
+ */
+ static ElfLoader Singleton;
+
+ /**
+ * Loads the given library with the given flags. Equivalent to dlopen()
+ * The extra "parent" argument optionally gives the handle of the library
+ * requesting the given library to be loaded. The loader may look in the
+ * directory containing that parent library for the library to load.
+ */
+ already_AddRefed<LibHandle> Load(const char *path, int flags,
+ LibHandle *parent = nullptr);
+
+ /**
+ * Returns the handle of the library containing the given address in
+ * its virtual address space, i.e. the library handle for which
+ * LibHandle::Contains returns true. Its purpose is to allow to
+ * implement dladdr().
+ */
+ already_AddRefed<LibHandle> GetHandleByPtr(void *addr);
+
+ /**
+ * Returns a Mappable object for the path. Paths in the form
+ * /foo/bar/baz/archive!/directory/lib.so
+ * try to load the directory/lib.so in /foo/bar/baz/archive, provided
+ * that file is a Zip archive.
+ */
+ static Mappable *GetMappableFromPath(const char *path);
+
+ void ExpectShutdown(bool val) { expect_shutdown = val; }
+ bool IsShutdownExpected() { return expect_shutdown; }
+
+private:
+ bool expect_shutdown;
+
+protected:
+ /**
+ * Registers the given handle. This method is meant to be called by
+ * LibHandle subclass creators.
+ */
+ void Register(LibHandle *handle);
+ void Register(CustomElf *handle);
+
+ /**
+ * Forget about the given handle. This method is meant to be called by
+ * LibHandle subclass destructors.
+ */
+ void Forget(LibHandle *handle);
+ void Forget(CustomElf *handle);
+
+ /* Last error. Used for dlerror() */
+ friend class SystemElf;
+ friend const char *__wrap_dlerror(void);
+ friend void *__wrap_dlsym(void *handle, const char *symbol);
+ friend int __wrap_dlclose(void *handle);
+ const char *lastError;
+
+private:
+ ElfLoader() : expect_shutdown(true) {}
+ ~ElfLoader();
+
+ /* Initialization code that can't run during static initialization. */
+ void Init();
+
+ /* System loader handle for the library/program containing our code. This
+ * is used to resolve wrapped functions. */
+ RefPtr<LibHandle> self_elf;
+
+#if defined(ANDROID)
+ /* System loader handle for the libc. This is used to resolve weak symbols
+ * that some libcs contain that the Android linker won't dlsym(). Normally,
+ * we wouldn't treat non-Android differently, but glibc uses versioned
+ * symbols which this linker doesn't support. */
+ RefPtr<LibHandle> libc;
+#endif
+
+ /* Bookkeeping */
+ typedef std::vector<LibHandle *> LibHandleList;
+ LibHandleList handles;
+
+protected:
+ friend class CustomElf;
+ friend class LoadedElf;
+ /**
+ * Show some stats about Mappables in CustomElfs. The when argument is to
+ * be used by the caller to give an identifier of the when the stats call
+ * is made.
+ */
+ static void stats(const char *when);
+
+ /* Definition of static destructors as to be used for C++ ABI compatibility */
+ typedef void (*Destructor)(void *object);
+
+ /**
+ * C++ ABI makes static initializers register destructors through a specific
+ * atexit interface. On glibc/linux systems, the dso_handle is a pointer
+ * within a given library. On bionic/android systems, it is an undefined
+ * symbol. Making sense of the value is not really important, and all that
+ * is really important is that it is different for each loaded library, so
+ * that they can be discriminated when shutting down. For convenience, on
+ * systems where the dso handle is a symbol, that symbol is resolved to
+ * point at corresponding CustomElf.
+ *
+ * Destructors are registered with __*_atexit with an associated object to
+ * be passed as argument when it is called.
+ *
+ * When __cxa_finalize is called, destructors registered for the given
+ * DSO handle are called in the reverse order they were registered.
+ */
+#ifdef __ARM_EABI__
+ static int __wrap_aeabi_atexit(void *that, Destructor destructor,
+ void *dso_handle);
+#else
+ static int __wrap_cxa_atexit(Destructor destructor, void *that,
+ void *dso_handle);
+#endif
+
+ static void __wrap_cxa_finalize(void *dso_handle);
+
+ /**
+ * Registered destructor. Keeps track of the destructor function pointer,
+ * associated object to call it with, and DSO handle.
+ */
+ class DestructorCaller {
+ public:
+ DestructorCaller(Destructor destructor, void *object, void *dso_handle)
+ : destructor(destructor), object(object), dso_handle(dso_handle) { }
+
+ /**
+ * Call the destructor function with the associated object.
+ * Call only once, see CustomElf::~CustomElf.
+ */
+ void Call();
+
+ /**
+ * Returns whether the destructor is associated to the given DSO handle
+ */
+ bool IsForHandle(void *handle) const
+ {
+ return handle == dso_handle;
+ }
+
+ private:
+ Destructor destructor;
+ void *object;
+ void *dso_handle;
+ };
+
+private:
+ /* Keep track of all registered destructors */
+ std::vector<DestructorCaller> destructors;
+
+ /* Forward declaration, see further below */
+ class DebuggerHelper;
+public:
+ /* Loaded object descriptor for the debugger interface below*/
+ struct link_map {
+ /* Base address of the loaded object. */
+ const void *l_addr;
+ /* File name */
+ const char *l_name;
+ /* Address of the PT_DYNAMIC segment. */
+ const void *l_ld;
+
+ private:
+ friend class ElfLoader::DebuggerHelper;
+ /* Double linked list of loaded objects. */
+ link_map *l_next, *l_prev;
+ };
+
+private:
+ /* Data structure used by the linker to give details about shared objects it
+ * loaded to debuggers. This is normally defined in link.h, but Android
+ * headers lack this file. */
+ struct r_debug {
+ /* Version number of the protocol. */
+ int r_version;
+
+ /* Head of the linked list of loaded objects. */
+ link_map *r_map;
+
+ /* Function to be called when updates to the linked list of loaded objects
+ * are going to occur. The function is to be called before and after
+ * changes. */
+ void (*r_brk)(void);
+
+ /* Indicates to the debugger what state the linked list of loaded objects
+ * is in when the function above is called. */
+ enum {
+ RT_CONSISTENT, /* Changes are complete */
+ RT_ADD, /* Beginning to add a new object */
+ RT_DELETE /* Beginning to remove an object */
+ } r_state;
+ };
+
+ /* Memory representation of ELF Auxiliary Vectors */
+ struct AuxVector {
+ Elf::Addr type;
+ Elf::Addr value;
+ };
+
+ /* Helper class used to integrate libraries loaded by this linker in
+ * r_debug */
+ class DebuggerHelper
+ {
+ public:
+ DebuggerHelper();
+
+ void Init(AuxVector *auvx);
+
+ operator bool()
+ {
+ return dbg;
+ }
+
+ /* Make the debugger aware of a new loaded object */
+ void Add(link_map *map);
+
+ /* Make the debugger aware of the unloading of an object */
+ void Remove(link_map *map);
+
+ /* Iterates over all link_maps */
+ class iterator
+ {
+ public:
+ const link_map *operator ->() const
+ {
+ return item;
+ }
+
+ const link_map &operator ++()
+ {
+ item = item->l_next;
+ return *item;
+ }
+
+ bool operator<(const iterator &other) const
+ {
+ if (other.item == nullptr)
+ return item ? true : false;
+ MOZ_CRASH("DebuggerHelper::iterator::operator< called with something else than DebuggerHelper::end()");
+ }
+ protected:
+ friend class DebuggerHelper;
+ iterator(const link_map *item): item(item) { }
+
+ private:
+ const link_map *item;
+ };
+
+ iterator begin() const
+ {
+ return iterator(dbg ? dbg->r_map : nullptr);
+ }
+
+ iterator end() const
+ {
+ return iterator(nullptr);
+ }
+
+ private:
+ r_debug *dbg;
+ link_map *firstAdded;
+ };
+ friend int __wrap_dl_iterate_phdr(dl_phdr_cb callback, void *data);
+ DebuggerHelper dbg;
+};
+
+#endif /* ElfLoader_h */
diff --git a/mozglue/linker/Elfxx.h b/mozglue/linker/Elfxx.h
new file mode 100644
index 000000000..b21a89336
--- /dev/null
+++ b/mozglue/linker/Elfxx.h
@@ -0,0 +1,241 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef Elfxx_h
+#define Elfxx_h
+
+/**
+ * Android system headers have two different elf.h file. The one under linux/
+ * is the most complete on older android API versions.
+ */
+#if defined(ANDROID) && __ANDROID_API__ < 21
+#include <linux/elf.h>
+#else
+#include <elf.h>
+#endif
+#include <endian.h>
+
+#if defined(__ARM_EABI__) && !defined(PT_ARM_EXIDX)
+#define PT_ARM_EXIDX 0x70000001
+#endif
+
+/**
+ * Generic ELF macros for the target system
+ */
+#ifdef __LP64__
+#define Elf_(type) Elf64_ ## type
+#define ELFCLASS ELFCLASS64
+#define ELF_R_TYPE ELF64_R_TYPE
+#define ELF_R_SYM ELF64_R_SYM
+#ifndef ELF_ST_BIND
+#define ELF_ST_BIND ELF64_ST_BIND
+#endif
+#else
+#define Elf_(type) Elf32_ ## type
+#define ELFCLASS ELFCLASS32
+#define ELF_R_TYPE ELF32_R_TYPE
+#define ELF_R_SYM ELF32_R_SYM
+#ifndef ELF_ST_BIND
+#define ELF_ST_BIND ELF32_ST_BIND
+#endif
+#endif
+
+#ifndef __BYTE_ORDER
+#error Cannot find endianness
+#endif
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define ELFDATA ELFDATA2LSB
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define ELFDATA ELFDATA2MSB
+#endif
+
+#ifdef __linux__
+#define ELFOSABI ELFOSABI_LINUX
+#ifdef EI_ABIVERSION
+#define ELFABIVERSION 0
+#endif
+#else
+#error Unknown ELF OSABI
+#endif
+
+#if defined(__i386__)
+#define ELFMACHINE EM_386
+
+// Doing this way probably doesn't scale to other architectures
+#define R_ABS R_386_32
+#define R_GLOB_DAT R_386_GLOB_DAT
+#define R_JMP_SLOT R_386_JMP_SLOT
+#define R_RELATIVE R_386_RELATIVE
+#define RELOC(n) DT_REL ## n
+#define UNSUPPORTED_RELOC(n) DT_RELA ## n
+#define STR_RELOC(n) "DT_REL" # n
+#define Reloc Rel
+
+#elif defined(__x86_64__)
+#define ELFMACHINE EM_X86_64
+
+#define R_ABS R_X86_64_64
+#define R_GLOB_DAT R_X86_64_GLOB_DAT
+#define R_JMP_SLOT R_X86_64_JUMP_SLOT
+#define R_RELATIVE R_X86_64_RELATIVE
+#define RELOC(n) DT_RELA ## n
+#define UNSUPPORTED_RELOC(n) DT_REL ## n
+#define STR_RELOC(n) "DT_RELA" # n
+#define Reloc Rela
+
+#elif defined(__arm__)
+#define ELFMACHINE EM_ARM
+
+#ifndef R_ARM_ABS32
+#define R_ARM_ABS32 2
+#endif
+#ifndef R_ARM_GLOB_DAT
+#define R_ARM_GLOB_DAT 21
+#endif
+#ifndef R_ARM_JUMP_SLOT
+#define R_ARM_JUMP_SLOT 22
+#endif
+#ifndef R_ARM_RELATIVE
+#define R_ARM_RELATIVE 23
+#endif
+
+#define R_ABS R_ARM_ABS32
+#define R_GLOB_DAT R_ARM_GLOB_DAT
+#define R_JMP_SLOT R_ARM_JUMP_SLOT
+#define R_RELATIVE R_ARM_RELATIVE
+#define RELOC(n) DT_REL ## n
+#define UNSUPPORTED_RELOC(n) DT_RELA ## n
+#define STR_RELOC(n) "DT_REL" # n
+#define Reloc Rel
+
+#else
+#error Unknown ELF machine type
+#endif
+
+/**
+ * Android system headers don't have all definitions
+ */
+#ifndef STN_UNDEF
+#define STN_UNDEF 0
+#endif
+#ifndef DT_INIT_ARRAY
+#define DT_INIT_ARRAY 25
+#endif
+#ifndef DT_FINI_ARRAY
+#define DT_FINI_ARRAY 26
+#endif
+#ifndef DT_INIT_ARRAYSZ
+#define DT_INIT_ARRAYSZ 27
+#endif
+#ifndef DT_FINI_ARRAYSZ
+#define DT_FINI_ARRAYSZ 28
+#endif
+#ifndef DT_RELACOUNT
+#define DT_RELACOUNT 0x6ffffff9
+#endif
+#ifndef DT_RELCOUNT
+#define DT_RELCOUNT 0x6ffffffa
+#endif
+#ifndef DT_VERSYM
+#define DT_VERSYM 0x6ffffff0
+#endif
+#ifndef DT_VERDEF
+#define DT_VERDEF 0x6ffffffc
+#endif
+#ifndef DT_VERDEFNUM
+#define DT_VERDEFNUM 0x6ffffffd
+#endif
+#ifndef DT_VERNEED
+#define DT_VERNEED 0x6ffffffe
+#endif
+#ifndef DT_VERNEEDNUM
+#define DT_VERNEEDNUM 0x6fffffff
+#endif
+#ifndef DT_FLAGS_1
+#define DT_FLAGS_1 0x6ffffffb
+#endif
+#ifndef DT_FLAGS
+#define DT_FLAGS 30
+#endif
+#ifndef DF_SYMBOLIC
+#define DF_SYMBOLIC 0x00000002
+#endif
+#ifndef DF_TEXTREL
+#define DF_TEXTREL 0x00000004
+#endif
+
+namespace Elf {
+
+/**
+ * Define a few basic Elf Types
+ */
+typedef Elf_(Phdr) Phdr;
+typedef Elf_(Dyn) Dyn;
+typedef Elf_(Sym) Sym;
+typedef Elf_(Addr) Addr;
+typedef Elf_(Word) Word;
+typedef Elf_(Half) Half;
+
+/**
+ * Helper class around the standard Elf header struct
+ */
+struct Ehdr: public Elf_(Ehdr)
+{
+ /**
+ * Equivalent to reinterpret_cast<const Ehdr *>(buf), but additionally
+ * checking that this is indeed an Elf header and that the Elf type
+ * corresponds to that of the system
+ */
+ static const Ehdr *validate(const void *buf);
+};
+
+/**
+ * Elf String table
+ */
+class Strtab: public UnsizedArray<const char>
+{
+public:
+ /**
+ * Returns the string at the given index in the table
+ */
+ const char *GetStringAt(off_t index) const
+ {
+ return &UnsizedArray<const char>::operator[](index);
+ }
+};
+
+/**
+ * Helper class around Elf relocation.
+ */
+struct Rel: public Elf_(Rel)
+{
+ /**
+ * Returns the addend for the relocation, which is the value stored
+ * at r_offset.
+ */
+ Addr GetAddend(void *base) const
+ {
+ return *(reinterpret_cast<const Addr *>(
+ reinterpret_cast<const char *>(base) + r_offset));
+ }
+};
+
+/**
+ * Helper class around Elf relocation with addend.
+ */
+struct Rela: public Elf_(Rela)
+{
+ /**
+ * Returns the addend for the relocation.
+ */
+ Addr GetAddend(void *base) const
+ {
+ return r_addend;
+ }
+};
+
+} /* namespace Elf */
+
+#endif /* Elfxx_h */
diff --git a/mozglue/linker/Logging.h b/mozglue/linker/Logging.h
new file mode 100644
index 000000000..046d918f4
--- /dev/null
+++ b/mozglue/linker/Logging.h
@@ -0,0 +1,87 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef Logging_h
+#define Logging_h
+
+#include "mozilla/Likely.h"
+
+#ifdef ANDROID
+#include <android/log.h>
+#define LOG(...) __android_log_print(ANDROID_LOG_INFO, "GeckoLinker", __VA_ARGS__)
+#define WARN(...) __android_log_print(ANDROID_LOG_WARN, "GeckoLinker", __VA_ARGS__)
+#define ERROR(...) __android_log_print(ANDROID_LOG_ERROR, "GeckoLinker", __VA_ARGS__)
+#else
+#include <cstdio>
+
+/* Expand to 1 or m depending on whether there is one argument or more
+ * given. */
+#define MOZ_ONE_OR_MORE_ARGS_IMPL2(_1, _2, _3, _4, _5, _6, _7, _8, _9, N, ...) \
+ N
+#define MOZ_ONE_OR_MORE_ARGS_IMPL(args) MOZ_ONE_OR_MORE_ARGS_IMPL2 args
+#define MOZ_ONE_OR_MORE_ARGS(...) \
+ MOZ_ONE_OR_MORE_ARGS_IMPL((__VA_ARGS__, m, m, m, m, m, m, m, m, 1, 0))
+
+#define MOZ_MACRO_GLUE(a, b) a b
+#define MOZ_CONCAT2(a, b) a ## b
+#define MOZ_CONCAT1(a, b) MOZ_CONCAT2(a, b)
+#define MOZ_CONCAT(a, b) MOZ_CONCAT1(a, b)
+
+/* Some magic to choose between LOG1 and LOGm depending on the number of
+ * arguments */
+#define MOZ_CHOOSE_LOG(...) \
+ MOZ_MACRO_GLUE(MOZ_CONCAT(LOG, MOZ_ONE_OR_MORE_ARGS(__VA_ARGS__)), \
+ (__VA_ARGS__))
+
+#define LOG1(format) fprintf(stderr, format "\n")
+#define LOGm(format, ...) fprintf(stderr, format "\n", __VA_ARGS__)
+#define LOG(...) MOZ_CHOOSE_LOG(__VA_ARGS__)
+#define WARN(...) MOZ_CHOOSE_LOG("Warning: " __VA_ARGS__)
+#define ERROR(...) MOZ_CHOOSE_LOG("Error: " __VA_ARGS__)
+
+#endif
+
+class Logging
+{
+public:
+ static bool isVerbose()
+ {
+ return Singleton.verbose;
+ }
+
+private:
+ bool verbose;
+
+public:
+ static void Init()
+ {
+ const char *env = getenv("MOZ_DEBUG_LINKER");
+ if (env && *env == '1')
+ Singleton.verbose = true;
+ }
+
+private:
+ static Logging Singleton;
+};
+
+#define DEBUG_LOG(...) \
+ do { \
+ if (MOZ_UNLIKELY(Logging::isVerbose())) { \
+ LOG(__VA_ARGS__); \
+ } \
+ } while(0)
+
+#if defined(__LP64__)
+# define PRIxAddr "lx"
+# define PRIxSize "lx"
+# define PRIdSize "ld"
+# define PRIuSize "lu"
+#else
+# define PRIxAddr "x"
+# define PRIxSize "x"
+# define PRIdSize "d"
+# define PRIuSize "u"
+#endif
+
+#endif /* Logging_h */
diff --git a/mozglue/linker/Mappable.cpp b/mozglue/linker/Mappable.cpp
new file mode 100644
index 000000000..47b883d2d
--- /dev/null
+++ b/mozglue/linker/Mappable.cpp
@@ -0,0 +1,681 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <cstring>
+#include <cstdlib>
+#include <cstdio>
+#include <string>
+
+#include "Mappable.h"
+
+#include "mozilla/UniquePtr.h"
+
+#ifdef ANDROID
+#include <linux/ashmem.h>
+#endif
+#include <sys/stat.h>
+#include <errno.h>
+#include "ElfLoader.h"
+#include "SeekableZStream.h"
+#include "XZStream.h"
+#include "Logging.h"
+
+using mozilla::MakeUnique;
+using mozilla::UniquePtr;
+
+class CacheValidator
+{
+public:
+ CacheValidator(const char* aCachedLibPath, Zip* aZip, Zip::Stream* aStream)
+ : mCachedLibPath(aCachedLibPath)
+ {
+ static const char kChecksumSuffix[] = ".crc";
+
+ mCachedChecksumPath =
+ MakeUnique<char[]>(strlen(aCachedLibPath) + sizeof(kChecksumSuffix));
+ sprintf(mCachedChecksumPath.get(), "%s%s", aCachedLibPath, kChecksumSuffix);
+ DEBUG_LOG("mCachedChecksumPath: %s", mCachedChecksumPath.get());
+
+ mChecksum = aStream->GetCRC32();
+ DEBUG_LOG("mChecksum: %x", mChecksum);
+ }
+
+ // Returns whether the cache is valid and up-to-date.
+ bool IsValid() const
+ {
+ // Validate based on checksum.
+ RefPtr<Mappable> checksumMap = MappableFile::Create(mCachedChecksumPath.get());
+ if (!checksumMap) {
+ // Force caching if checksum is missing in cache.
+ return false;
+ }
+
+ DEBUG_LOG("Comparing %x with %s", mChecksum, mCachedChecksumPath.get());
+ MappedPtr checksumBuf = checksumMap->mmap(nullptr, checksumMap->GetLength(),
+ PROT_READ, MAP_PRIVATE, 0);
+ if (checksumBuf == MAP_FAILED) {
+ WARN("Couldn't map %s to validate checksum", mCachedChecksumPath.get());
+ return false;
+ }
+ if (memcmp(checksumBuf, &mChecksum, sizeof(mChecksum))) {
+ return false;
+ }
+ return !access(mCachedLibPath.c_str(), R_OK);
+ }
+
+ // Caches the APK-provided checksum used in future cache validations.
+ void CacheChecksum() const
+ {
+ AutoCloseFD fd(open(mCachedChecksumPath.get(),
+ O_TRUNC | O_RDWR | O_CREAT | O_NOATIME,
+ S_IRUSR | S_IWUSR));
+ if (fd == -1) {
+ WARN("Couldn't open %s to update checksum", mCachedChecksumPath.get());
+ return;
+ }
+
+ DEBUG_LOG("Updating checksum %s", mCachedChecksumPath.get());
+
+ const size_t size = sizeof(mChecksum);
+ size_t written = 0;
+ while (written < size) {
+ ssize_t ret = write(fd,
+ reinterpret_cast<const uint8_t*>(&mChecksum) + written,
+ size - written);
+ if (ret >= 0) {
+ written += ret;
+ } else if (errno != EINTR) {
+ WARN("Writing checksum %s failed with errno %d",
+ mCachedChecksumPath.get(), errno);
+ break;
+ }
+ }
+ }
+
+private:
+ const std::string mCachedLibPath;
+ UniquePtr<char[]> mCachedChecksumPath;
+ uint32_t mChecksum;
+};
+
+Mappable *
+MappableFile::Create(const char *path)
+{
+ int fd = open(path, O_RDONLY);
+ if (fd != -1)
+ return new MappableFile(fd);
+ return nullptr;
+}
+
+MemoryRange
+MappableFile::mmap(const void *addr, size_t length, int prot, int flags,
+ off_t offset)
+{
+ MOZ_ASSERT(fd != -1);
+ MOZ_ASSERT(!(flags & MAP_SHARED));
+ flags |= MAP_PRIVATE;
+
+ return MemoryRange::mmap(const_cast<void *>(addr), length, prot, flags,
+ fd, offset);
+}
+
+void
+MappableFile::finalize()
+{
+ /* Close file ; equivalent to close(fd.forget()) */
+ fd = -1;
+}
+
+size_t
+MappableFile::GetLength() const
+{
+ struct stat st;
+ return fstat(fd, &st) ? 0 : st.st_size;
+}
+
+Mappable *
+MappableExtractFile::Create(const char *name, Zip *zip, Zip::Stream *stream)
+{
+ MOZ_ASSERT(zip && stream);
+
+ const char *cachePath = getenv("MOZ_LINKER_CACHE");
+ if (!cachePath || !*cachePath) {
+ WARN("MOZ_LINKER_EXTRACT is set, but not MOZ_LINKER_CACHE; "
+ "not extracting");
+ return nullptr;
+ }
+
+ // Ensure that the cache dir is private.
+ chmod(cachePath, 0770);
+
+ UniquePtr<char[]> path =
+ MakeUnique<char[]>(strlen(cachePath) + strlen(name) + 2);
+ sprintf(path.get(), "%s/%s", cachePath, name);
+
+ CacheValidator validator(path.get(), zip, stream);
+ if (validator.IsValid()) {
+ DEBUG_LOG("Reusing %s", static_cast<char *>(path.get()));
+ return MappableFile::Create(path.get());
+ }
+ DEBUG_LOG("Extracting to %s", static_cast<char *>(path.get()));
+ AutoCloseFD fd;
+ fd = open(path.get(), O_TRUNC | O_RDWR | O_CREAT | O_NOATIME,
+ S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ ERROR("Couldn't open %s to decompress library", path.get());
+ return nullptr;
+ }
+ AutoUnlinkFile file(path.release());
+ if (stream->GetType() == Zip::Stream::DEFLATE) {
+ if (ftruncate(fd, stream->GetUncompressedSize()) == -1) {
+ ERROR("Couldn't ftruncate %s to decompress library", file.get());
+ return nullptr;
+ }
+ /* Map the temporary file for use as inflate buffer */
+ MappedPtr buffer(MemoryRange::mmap(nullptr, stream->GetUncompressedSize(),
+ PROT_WRITE, MAP_SHARED, fd, 0));
+ if (buffer == MAP_FAILED) {
+ ERROR("Couldn't map %s to decompress library", file.get());
+ return nullptr;
+ }
+
+ zxx_stream zStream = stream->GetZStream(buffer);
+
+ /* Decompress */
+ if (inflateInit2(&zStream, -MAX_WBITS) != Z_OK) {
+ ERROR("inflateInit failed: %s", zStream.msg);
+ return nullptr;
+ }
+ if (inflate(&zStream, Z_FINISH) != Z_STREAM_END) {
+ ERROR("inflate failed: %s", zStream.msg);
+ return nullptr;
+ }
+ if (inflateEnd(&zStream) != Z_OK) {
+ ERROR("inflateEnd failed: %s", zStream.msg);
+ return nullptr;
+ }
+ if (zStream.total_out != stream->GetUncompressedSize()) {
+ ERROR("File not fully uncompressed! %ld / %d", zStream.total_out,
+ static_cast<unsigned int>(stream->GetUncompressedSize()));
+ return nullptr;
+ }
+ } else if (XZStream::IsXZ(stream->GetBuffer(), stream->GetSize())) {
+ XZStream xzStream(stream->GetBuffer(), stream->GetSize());
+
+ if (!xzStream.Init()) {
+ ERROR("Couldn't initialize XZ decoder");
+ return nullptr;
+ }
+ DEBUG_LOG("XZStream created, compressed=%u, uncompressed=%u",
+ xzStream.Size(), xzStream.UncompressedSize());
+
+ if (ftruncate(fd, xzStream.UncompressedSize()) == -1) {
+ ERROR("Couldn't ftruncate %s to decompress library", file.get());
+ return nullptr;
+ }
+ MappedPtr buffer(MemoryRange::mmap(nullptr, xzStream.UncompressedSize(),
+ PROT_WRITE, MAP_SHARED, fd, 0));
+ if (buffer == MAP_FAILED) {
+ ERROR("Couldn't map %s to decompress library", file.get());
+ return nullptr;
+ }
+ const size_t written = xzStream.Decode(buffer, buffer.GetLength());
+ DEBUG_LOG("XZStream decoded %u", written);
+ if (written != buffer.GetLength()) {
+ ERROR("Error decoding XZ file %s", file.get());
+ return nullptr;
+ }
+ } else if (stream->GetType() == Zip::Stream::STORE) {
+ SeekableZStream zStream;
+ if (!zStream.Init(stream->GetBuffer(), stream->GetSize())) {
+ ERROR("Couldn't initialize SeekableZStream for %s", name);
+ return nullptr;
+ }
+ if (ftruncate(fd, zStream.GetUncompressedSize()) == -1) {
+ ERROR("Couldn't ftruncate %s to decompress library", file.get());
+ return nullptr;
+ }
+ MappedPtr buffer(MemoryRange::mmap(nullptr, zStream.GetUncompressedSize(),
+ PROT_WRITE, MAP_SHARED, fd, 0));
+ if (buffer == MAP_FAILED) {
+ ERROR("Couldn't map %s to decompress library", file.get());
+ return nullptr;
+ }
+
+ if (!zStream.Decompress(buffer, 0, zStream.GetUncompressedSize())) {
+ ERROR("%s: failed to decompress", name);
+ return nullptr;
+ }
+ } else {
+ return nullptr;
+ }
+
+ validator.CacheChecksum();
+ return new MappableExtractFile(fd.forget(), file.release());
+}
+
+/**
+ * _MappableBuffer is a buffer which content can be mapped at different
+ * locations in the virtual address space.
+ * On Linux, uses a (deleted) temporary file on a tmpfs for sharable content.
+ * On Android, uses ashmem.
+ */
+class _MappableBuffer: public MappedPtr
+{
+public:
+ /**
+ * Returns a _MappableBuffer instance with the given name and the given
+ * length.
+ */
+ static _MappableBuffer *Create(const char *name, size_t length)
+ {
+ AutoCloseFD fd;
+#ifdef ANDROID
+ /* On Android, initialize an ashmem region with the given length */
+ fd = open("/" ASHMEM_NAME_DEF, O_RDWR, 0600);
+ if (fd == -1)
+ return nullptr;
+ char str[ASHMEM_NAME_LEN];
+ strlcpy(str, name, sizeof(str));
+ ioctl(fd, ASHMEM_SET_NAME, str);
+ if (ioctl(fd, ASHMEM_SET_SIZE, length))
+ return nullptr;
+
+ /* The Gecko crash reporter is confused by adjacent memory mappings of
+ * the same file and chances are we're going to map from the same file
+ * descriptor right away. To avoid problems with the crash reporter,
+ * create an empty anonymous page before or after the ashmem mapping,
+ * depending on how mappings grow in the address space.
+ */
+#if defined(__arm__)
+ void *buf = ::mmap(nullptr, length + PAGE_SIZE, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ if (buf != MAP_FAILED) {
+ ::mmap(AlignedEndPtr(reinterpret_cast<char *>(buf) + length, PAGE_SIZE),
+ PAGE_SIZE, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ DEBUG_LOG("Decompression buffer of size 0x%x in ashmem \"%s\", mapped @%p",
+ length, str, buf);
+ return new _MappableBuffer(fd.forget(), buf, length);
+ }
+#elif defined(__i386__)
+ size_t anon_mapping_length = length + PAGE_SIZE;
+ void *buf = ::mmap(nullptr, anon_mapping_length, PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (buf != MAP_FAILED) {
+ char *first_page = reinterpret_cast<char *>(buf);
+ char *map_page = first_page + PAGE_SIZE;
+
+ void *actual_buf = ::mmap(map_page, length, PROT_READ | PROT_WRITE,
+ MAP_FIXED | MAP_SHARED, fd, 0);
+ if (actual_buf == MAP_FAILED) {
+ ::munmap(buf, anon_mapping_length);
+ DEBUG_LOG("Fixed allocation of decompression buffer at %p failed", map_page);
+ return nullptr;
+ }
+
+ DEBUG_LOG("Decompression buffer of size 0x%x in ashmem \"%s\", mapped @%p",
+ length, str, actual_buf);
+ return new _MappableBuffer(fd.forget(), actual_buf, length);
+ }
+#else
+#error need to add a case for your CPU
+#endif
+#else
+ /* On Linux, use /dev/shm as base directory for temporary files, assuming
+ * it's on tmpfs */
+ /* TODO: check that /dev/shm is tmpfs */
+ char path[256];
+ sprintf(path, "/dev/shm/%s.XXXXXX", name);
+ fd = mkstemp(path);
+ if (fd == -1)
+ return nullptr;
+ unlink(path);
+ ftruncate(fd, length);
+
+ void *buf = ::mmap(nullptr, length, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ if (buf != MAP_FAILED) {
+ DEBUG_LOG("Decompression buffer of size %ld in \"%s\", mapped @%p",
+ length, path, buf);
+ return new _MappableBuffer(fd.forget(), buf, length);
+ }
+#endif
+ return nullptr;
+ }
+
+ void *mmap(const void *addr, size_t length, int prot, int flags, off_t offset)
+ {
+ MOZ_ASSERT(fd != -1);
+#ifdef ANDROID
+ /* Mapping ashmem MAP_PRIVATE is like mapping anonymous memory, even when
+ * there is content in the ashmem */
+ if (flags & MAP_PRIVATE) {
+ flags &= ~MAP_PRIVATE;
+ flags |= MAP_SHARED;
+ }
+#endif
+ return ::mmap(const_cast<void *>(addr), length, prot, flags, fd, offset);
+ }
+
+#ifdef ANDROID
+ ~_MappableBuffer() {
+ /* Free the additional page we allocated. See _MappableBuffer::Create */
+#if defined(__arm__)
+ ::munmap(AlignedEndPtr(*this + GetLength(), PAGE_SIZE), PAGE_SIZE);
+#elif defined(__i386__)
+ ::munmap(*this - PAGE_SIZE, GetLength() + PAGE_SIZE);
+#else
+#error need to add a case for your CPU
+#endif
+ }
+#endif
+
+private:
+ _MappableBuffer(int fd, void *buf, size_t length)
+ : MappedPtr(buf, length), fd(fd) { }
+
+ /* File descriptor for the temporary file or ashmem */
+ AutoCloseFD fd;
+};
+
+
+Mappable *
+MappableDeflate::Create(const char *name, Zip *zip, Zip::Stream *stream)
+{
+ MOZ_ASSERT(stream->GetType() == Zip::Stream::DEFLATE);
+ _MappableBuffer *buf = _MappableBuffer::Create(name, stream->GetUncompressedSize());
+ if (buf)
+ return new MappableDeflate(buf, zip, stream);
+ return nullptr;
+}
+
+MappableDeflate::MappableDeflate(_MappableBuffer *buf, Zip *zip,
+ Zip::Stream *stream)
+: zip(zip), buffer(buf), zStream(stream->GetZStream(*buf)) { }
+
+MappableDeflate::~MappableDeflate() { }
+
+MemoryRange
+MappableDeflate::mmap(const void *addr, size_t length, int prot, int flags, off_t offset)
+{
+ MOZ_ASSERT(buffer);
+ MOZ_ASSERT(!(flags & MAP_SHARED));
+ flags |= MAP_PRIVATE;
+
+ /* The deflate stream is uncompressed up to the required offset + length, if
+ * it hasn't previously been uncompressed */
+ ssize_t missing = offset + length + zStream.avail_out - buffer->GetLength();
+ if (missing > 0) {
+ uInt avail_out = zStream.avail_out;
+ zStream.avail_out = missing;
+ if ((*buffer == zStream.next_out) &&
+ (inflateInit2(&zStream, -MAX_WBITS) != Z_OK)) {
+ ERROR("inflateInit failed: %s", zStream.msg);
+ return MemoryRange(MAP_FAILED, 0);
+ }
+ int ret = inflate(&zStream, Z_SYNC_FLUSH);
+ if (ret < 0) {
+ ERROR("inflate failed: %s", zStream.msg);
+ return MemoryRange(MAP_FAILED, 0);
+ }
+ if (ret == Z_NEED_DICT) {
+ ERROR("zstream requires a dictionary. %s", zStream.msg);
+ return MemoryRange(MAP_FAILED, 0);
+ }
+ zStream.avail_out = avail_out - missing + zStream.avail_out;
+ if (ret == Z_STREAM_END) {
+ if (inflateEnd(&zStream) != Z_OK) {
+ ERROR("inflateEnd failed: %s", zStream.msg);
+ return MemoryRange(MAP_FAILED, 0);
+ }
+ if (zStream.total_out != buffer->GetLength()) {
+ ERROR("File not fully uncompressed! %ld / %d", zStream.total_out,
+ static_cast<unsigned int>(buffer->GetLength()));
+ return MemoryRange(MAP_FAILED, 0);
+ }
+ }
+ }
+#if defined(ANDROID) && defined(__arm__)
+ if (prot & PROT_EXEC) {
+ /* We just extracted data that may be executed in the future.
+ * We thus need to ensure Instruction and Data cache coherency. */
+ DEBUG_LOG("cacheflush(%p, %p)", *buffer + offset, *buffer + (offset + length));
+ cacheflush(reinterpret_cast<uintptr_t>(*buffer + offset),
+ reinterpret_cast<uintptr_t>(*buffer + (offset + length)), 0);
+ }
+#endif
+
+ return MemoryRange(buffer->mmap(addr, length, prot, flags, offset), length);
+}
+
+void
+MappableDeflate::finalize()
+{
+ /* Free zlib internal buffers */
+ inflateEnd(&zStream);
+ /* Free decompression buffer */
+ buffer = nullptr;
+ /* Remove reference to Zip archive */
+ zip = nullptr;
+}
+
+size_t
+MappableDeflate::GetLength() const
+{
+ return buffer->GetLength();
+}
+
+Mappable *
+MappableSeekableZStream::Create(const char *name, Zip *zip,
+ Zip::Stream *stream)
+{
+ MOZ_ASSERT(stream->GetType() == Zip::Stream::STORE);
+ UniquePtr<MappableSeekableZStream> mappable(new MappableSeekableZStream(zip));
+
+ pthread_mutexattr_t recursiveAttr;
+ pthread_mutexattr_init(&recursiveAttr);
+ pthread_mutexattr_settype(&recursiveAttr, PTHREAD_MUTEX_RECURSIVE);
+
+ if (pthread_mutex_init(&mappable->mutex, &recursiveAttr))
+ return nullptr;
+
+ if (!mappable->zStream.Init(stream->GetBuffer(), stream->GetSize()))
+ return nullptr;
+
+ mappable->buffer.reset(_MappableBuffer::Create(name,
+ mappable->zStream.GetUncompressedSize()));
+ if (!mappable->buffer)
+ return nullptr;
+
+ mappable->chunkAvail = MakeUnique<unsigned char[]>(mappable->zStream.GetChunksNum());
+
+ return mappable.release();
+}
+
+MappableSeekableZStream::MappableSeekableZStream(Zip *zip)
+: zip(zip), chunkAvailNum(0) { }
+
+MappableSeekableZStream::~MappableSeekableZStream()
+{
+ pthread_mutex_destroy(&mutex);
+}
+
+MemoryRange
+MappableSeekableZStream::mmap(const void *addr, size_t length, int prot,
+ int flags, off_t offset)
+{
+ /* Map with PROT_NONE so that accessing the mapping would segfault, and
+ * bring us to ensure() */
+ void *res = buffer->mmap(addr, length, PROT_NONE, flags, offset);
+ if (res == MAP_FAILED)
+ return MemoryRange(MAP_FAILED, 0);
+
+ /* Store the mapping, ordered by offset and length */
+ std::vector<LazyMap>::reverse_iterator it;
+ for (it = lazyMaps.rbegin(); it < lazyMaps.rend(); ++it) {
+ if ((it->offset < offset) ||
+ ((it->offset == offset) && (it->length < length)))
+ break;
+ }
+ LazyMap map = { res, length, prot, offset };
+ lazyMaps.insert(it.base(), map);
+ return MemoryRange(res, length);
+}
+
+void
+MappableSeekableZStream::munmap(void *addr, size_t length)
+{
+ std::vector<LazyMap>::iterator it;
+ for (it = lazyMaps.begin(); it < lazyMaps.end(); ++it)
+ if ((it->addr = addr) && (it->length == length)) {
+ lazyMaps.erase(it);
+ ::munmap(addr, length);
+ return;
+ }
+ MOZ_CRASH("munmap called with unknown mapping");
+}
+
+void
+MappableSeekableZStream::finalize() { }
+
+bool
+MappableSeekableZStream::ensure(const void *addr)
+{
+ DEBUG_LOG("ensure @%p", addr);
+ const void *addrPage = PageAlignedPtr(addr);
+ /* Find the mapping corresponding to the given page */
+ std::vector<LazyMap>::iterator map;
+ for (map = lazyMaps.begin(); map < lazyMaps.end(); ++map) {
+ if (map->Contains(addrPage))
+ break;
+ }
+ if (map == lazyMaps.end())
+ return false;
+
+ /* Find corresponding chunk */
+ off_t mapOffset = map->offsetOf(addrPage);
+ off_t chunk = mapOffset / zStream.GetChunkSize();
+
+ /* In the typical case, we just need to decompress the chunk entirely. But
+ * when the current mapping ends in the middle of the chunk, we want to
+ * stop at the end of the corresponding page.
+ * However, if another mapping needs the last part of the chunk, we still
+ * need to continue. As mappings are ordered by offset and length, we don't
+ * need to scan the entire list of mappings.
+ * It is safe to run through lazyMaps here because the linker is never
+ * going to call mmap (which adds lazyMaps) while this function is
+ * called. */
+ size_t length = zStream.GetChunkSize(chunk);
+ off_t chunkStart = chunk * zStream.GetChunkSize();
+ off_t chunkEnd = chunkStart + length;
+ std::vector<LazyMap>::iterator it;
+ for (it = map; it < lazyMaps.end(); ++it) {
+ if (chunkEnd <= it->endOffset())
+ break;
+ }
+ if ((it == lazyMaps.end()) || (chunkEnd > it->endOffset())) {
+ /* The mapping "it" points at now is past the interesting one */
+ --it;
+ length = it->endOffset() - chunkStart;
+ }
+
+ length = PageAlignedSize(length);
+
+ /* The following lock can be re-acquired by the thread holding it.
+ * If this happens, it means the following code is interrupted somehow by
+ * some signal, and ends up retriggering a chunk decompression for the
+ * same MappableSeekableZStream.
+ * If the chunk to decompress is different the second time, then everything
+ * is safe as the only common data touched below is chunkAvailNum, and it is
+ * atomically updated (leaving out any chance of an interruption while it is
+ * updated affecting the result). If the chunk to decompress is the same, the
+ * worst thing that can happen is chunkAvailNum being incremented one too
+ * many times, which doesn't affect functionality. The chances of it
+ * happening being pretty slim, and the effect being harmless, we can just
+ * ignore the issue. Other than that, we'd just be wasting time decompressing
+ * the same chunk twice. */
+ AutoLock lock(&mutex);
+
+ /* The very first page is mapped and accessed separately of the rest, and
+ * as such, only the first page of the first chunk is decompressed this way.
+ * When we fault in the remaining pages of that chunk, we want to decompress
+ * the complete chunk again. Short of doing that, we would end up with
+ * no data between PageSize() and chunkSize, which would effectively corrupt
+ * symbol resolution in the underlying library. */
+ if (chunkAvail[chunk] < PageNumber(length)) {
+ if (!zStream.DecompressChunk(*buffer + chunkStart, chunk, length))
+ return false;
+
+#if defined(ANDROID) && defined(__arm__)
+ if (map->prot & PROT_EXEC) {
+ /* We just extracted data that may be executed in the future.
+ * We thus need to ensure Instruction and Data cache coherency. */
+ DEBUG_LOG("cacheflush(%p, %p)", *buffer + chunkStart, *buffer + (chunkStart + length));
+ cacheflush(reinterpret_cast<uintptr_t>(*buffer + chunkStart),
+ reinterpret_cast<uintptr_t>(*buffer + (chunkStart + length)), 0);
+ }
+#endif
+ /* Only count if we haven't already decompressed parts of the chunk */
+ if (chunkAvail[chunk] == 0)
+ chunkAvailNum++;
+
+ chunkAvail[chunk] = PageNumber(length);
+ }
+
+ /* Flip the chunk mapping protection to the recorded flags. We could
+ * also flip the protection for other mappings of the same chunk,
+ * but it's easier to skip that and let further segfaults call
+ * ensure again. */
+ const void *chunkAddr = reinterpret_cast<const void *>
+ (reinterpret_cast<uintptr_t>(addrPage)
+ - mapOffset % zStream.GetChunkSize());
+ const void *chunkEndAddr = reinterpret_cast<const void *>
+ (reinterpret_cast<uintptr_t>(chunkAddr) + length);
+
+ const void *start = std::max(map->addr, chunkAddr);
+ const void *end = std::min(map->end(), chunkEndAddr);
+ length = reinterpret_cast<uintptr_t>(end)
+ - reinterpret_cast<uintptr_t>(start);
+
+ if (mprotect(const_cast<void *>(start), length, map->prot) == 0) {
+ DEBUG_LOG("mprotect @%p, 0x%" PRIxSize ", 0x%x", start, length, map->prot);
+ return true;
+ }
+
+ ERROR("mprotect @%p, 0x%" PRIxSize ", 0x%x failed with errno %d",
+ start, length, map->prot, errno);
+ return false;
+}
+
+void
+MappableSeekableZStream::stats(const char *when, const char *name) const
+{
+ size_t nEntries = zStream.GetChunksNum();
+ DEBUG_LOG("%s: %s; %" PRIdSize "/%" PRIdSize " chunks decompressed",
+ name, when, static_cast<size_t>(chunkAvailNum), nEntries);
+
+ size_t len = 64;
+ UniquePtr<char[]> map = MakeUnique<char[]>(len + 3);
+ map[0] = '[';
+
+ for (size_t i = 0, j = 1; i < nEntries; i++, j++) {
+ map[j] = chunkAvail[i] ? '*' : '_';
+ if ((j == len) || (i == nEntries - 1)) {
+ map[j + 1] = ']';
+ map[j + 2] = '\0';
+ DEBUG_LOG("%s", static_cast<char *>(map.get()));
+ j = 0;
+ }
+ }
+}
+
+size_t
+MappableSeekableZStream::GetLength() const
+{
+ return buffer->GetLength();
+}
diff --git a/mozglue/linker/Mappable.h b/mozglue/linker/Mappable.h
new file mode 100644
index 000000000..0224ae3b5
--- /dev/null
+++ b/mozglue/linker/Mappable.h
@@ -0,0 +1,267 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef Mappable_h
+#define Mappable_h
+
+#include "Zip.h"
+#include "SeekableZStream.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "zlib.h"
+
+/**
+ * Abstract class to handle mmap()ing from various kind of entities, such as
+ * plain files or Zip entries. The virtual members are meant to act as the
+ * equivalent system functions, except mapped memory is always MAP_PRIVATE,
+ * even though a given implementation may use something different internally.
+ */
+class Mappable: public mozilla::RefCounted<Mappable>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(Mappable)
+ virtual ~Mappable() { }
+
+ virtual MemoryRange mmap(const void *addr, size_t length, int prot, int flags,
+ off_t offset) = 0;
+
+ enum Kind {
+ MAPPABLE_FILE,
+ MAPPABLE_EXTRACT_FILE,
+ MAPPABLE_DEFLATE,
+ MAPPABLE_SEEKABLE_ZSTREAM
+ };
+
+ virtual Kind GetKind() const = 0;
+
+private:
+ virtual void munmap(void *addr, size_t length) {
+ ::munmap(addr, length);
+ }
+ /* Limit use of Mappable::munmap to classes that keep track of the address
+ * and size of the mapping. This allows to ignore ::munmap return value. */
+ friend class Mappable1stPagePtr;
+ friend class LibHandle;
+
+public:
+ /**
+ * Ensures the availability of the memory pages for the page(s) containing
+ * the given address. Returns whether the pages were successfully made
+ * available.
+ */
+ virtual bool ensure(const void *addr) { return true; }
+
+ /**
+ * Indicate to a Mappable instance that no further mmap is going to happen.
+ */
+ virtual void finalize() = 0;
+
+ /**
+ * Shows some stats about the Mappable instance.
+ * Meant for MappableSeekableZStream only.
+ * As Mappables don't keep track of what they are instanciated for, the name
+ * argument is used to make the stats logging useful to the reader. The when
+ * argument is to be used by the caller to give an identifier of the when
+ * the stats call is made.
+ */
+ virtual void stats(const char *when, const char *name) const { }
+
+ /**
+ * Returns the maximum length that can be mapped from this Mappable for
+ * offset = 0.
+ */
+ virtual size_t GetLength() const = 0;
+};
+
+/**
+ * Mappable implementation for plain files
+ */
+class MappableFile: public Mappable
+{
+public:
+ ~MappableFile() { }
+
+ /**
+ * Create a MappableFile instance for the given file path.
+ */
+ static Mappable *Create(const char *path);
+
+ /* Inherited from Mappable */
+ virtual MemoryRange mmap(const void *addr, size_t length, int prot, int flags, off_t offset);
+ virtual void finalize();
+ virtual size_t GetLength() const;
+
+ virtual Kind GetKind() const { return MAPPABLE_FILE; };
+protected:
+ MappableFile(int fd): fd(fd) { }
+
+private:
+ /* File descriptor */
+ AutoCloseFD fd;
+};
+
+/**
+ * Mappable implementation for deflated stream in a Zip archive
+ * Inflates the complete stream into a cache file.
+ */
+class MappableExtractFile: public MappableFile
+{
+public:
+ ~MappableExtractFile() = default;
+
+ /**
+ * Create a MappableExtractFile instance for the given Zip stream. The name
+ * argument is used to create the cache file in the cache directory.
+ */
+ static Mappable *Create(const char *name, Zip *zip, Zip::Stream *stream);
+
+ /* Override finalize from MappableFile */
+ virtual void finalize() {}
+
+ virtual Kind GetKind() const { return MAPPABLE_EXTRACT_FILE; };
+private:
+ /**
+ * AutoUnlinkFile keeps track of a file name and removes (unlinks) the file
+ * when the instance is destroyed.
+ */
+ struct UnlinkFile
+ {
+ void operator()(char *value) {
+ unlink(value);
+ delete [] value;
+ }
+ };
+ typedef mozilla::UniquePtr<char[], UnlinkFile> AutoUnlinkFile;
+
+ MappableExtractFile(int fd, const char* path)
+ : MappableFile(fd), path(path) { }
+
+ /* Extracted file path */
+ mozilla::UniquePtr<const char[]> path;
+};
+
+class _MappableBuffer;
+
+/**
+ * Mappable implementation for deflated stream in a Zip archive.
+ * Inflates the mapped bits in a temporary buffer.
+ */
+class MappableDeflate: public Mappable
+{
+public:
+ ~MappableDeflate();
+
+ /**
+ * Create a MappableDeflate instance for the given Zip stream. The name
+ * argument is used for an appropriately named temporary file, and the Zip
+ * instance is given for the MappableDeflate to keep a reference of it.
+ */
+ static Mappable *Create(const char *name, Zip *zip, Zip::Stream *stream);
+
+ /* Inherited from Mappable */
+ virtual MemoryRange mmap(const void *addr, size_t length, int prot, int flags, off_t offset);
+ virtual void finalize();
+ virtual size_t GetLength() const;
+
+ virtual Kind GetKind() const { return MAPPABLE_DEFLATE; };
+private:
+ MappableDeflate(_MappableBuffer *buf, Zip *zip, Zip::Stream *stream);
+
+ /* Zip reference */
+ RefPtr<Zip> zip;
+
+ /* Decompression buffer */
+ mozilla::UniquePtr<_MappableBuffer> buffer;
+
+ /* Zlib data */
+ zxx_stream zStream;
+};
+
+/**
+ * Mappable implementation for seekable zStreams.
+ * Inflates the mapped bits in a temporary buffer, on demand.
+ */
+class MappableSeekableZStream: public Mappable
+{
+public:
+ ~MappableSeekableZStream();
+
+ /**
+ * Create a MappableSeekableZStream instance for the given Zip stream. The
+ * name argument is used for an appropriately named temporary file, and the
+ * Zip instance is given for the MappableSeekableZStream to keep a reference
+ * of it.
+ */
+ static Mappable *Create(const char *name, Zip *zip,
+ Zip::Stream *stream);
+
+ /* Inherited from Mappable */
+ virtual MemoryRange mmap(const void *addr, size_t length, int prot, int flags, off_t offset);
+ virtual void munmap(void *addr, size_t length);
+ virtual void finalize();
+ virtual bool ensure(const void *addr);
+ virtual void stats(const char *when, const char *name) const;
+ virtual size_t GetLength() const;
+
+ virtual Kind GetKind() const { return MAPPABLE_SEEKABLE_ZSTREAM; };
+private:
+ MappableSeekableZStream(Zip *zip);
+
+ /* Zip reference */
+ RefPtr<Zip> zip;
+
+ /* Decompression buffer */
+ mozilla::UniquePtr<_MappableBuffer> buffer;
+
+ /* Seekable ZStream */
+ SeekableZStream zStream;
+
+ /* Keep track of mappings performed with MappableSeekableZStream::mmap so
+ * that they can be realized by MappableSeekableZStream::ensure.
+ * Values stored in the struct are those passed to mmap */
+ struct LazyMap
+ {
+ const void *addr;
+ size_t length;
+ int prot;
+ off_t offset;
+
+ /* Returns addr + length, as a pointer */
+ const void *end() const {
+ return reinterpret_cast<const void *>
+ (reinterpret_cast<const unsigned char *>(addr) + length);
+ }
+
+ /* Returns offset + length */
+ off_t endOffset() const {
+ return offset + length;
+ }
+
+ /* Returns the offset corresponding to the given address */
+ off_t offsetOf(const void *ptr) const {
+ return reinterpret_cast<uintptr_t>(ptr)
+ - reinterpret_cast<uintptr_t>(addr) + offset;
+ }
+
+ /* Returns whether the given address is in the LazyMap range */
+ bool Contains(const void *ptr) const {
+ return (ptr >= addr) && (ptr < end());
+ }
+ };
+
+ /* List of all mappings */
+ std::vector<LazyMap> lazyMaps;
+
+ /* Array keeping track of which chunks have already been decompressed.
+ * Each value is the number of pages decompressed for the given chunk. */
+ mozilla::UniquePtr<unsigned char[]> chunkAvail;
+
+ /* Number of chunks that have already been decompressed. */
+ mozilla::Atomic<size_t> chunkAvailNum;
+
+ /* Mutex protecting decompression */
+ pthread_mutex_t mutex;
+};
+
+#endif /* Mappable_h */
diff --git a/mozglue/linker/SeekableZStream.cpp b/mozglue/linker/SeekableZStream.cpp
new file mode 100644
index 000000000..6dd0ef6d5
--- /dev/null
+++ b/mozglue/linker/SeekableZStream.cpp
@@ -0,0 +1,261 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include <algorithm>
+#include "SeekableZStream.h"
+#include "Logging.h"
+
+bool
+SeekableZStream::Init(const void *buf, size_t length)
+{
+ const SeekableZStreamHeader *header = SeekableZStreamHeader::validate(buf);
+ if (!header) {
+ ERROR("Not a seekable zstream");
+ return false;
+ }
+
+ buffer = reinterpret_cast<const unsigned char *>(buf);
+ totalSize = header->totalSize;
+ chunkSize = header->chunkSize;
+ lastChunkSize = header->lastChunkSize;
+ windowBits = header->windowBits;
+ dictionary.Init(buffer + sizeof(SeekableZStreamHeader), header->dictSize);
+ offsetTable.Init(buffer + sizeof(SeekableZStreamHeader) + header->dictSize,
+ header->nChunks);
+ filter = GetFilter(header->filter);
+
+ /* Sanity check */
+ if ((chunkSize == 0) ||
+ (!IsPageAlignedSize(chunkSize)) ||
+ (chunkSize > 8 * PageSize()) ||
+ (offsetTable.numElements() < 1) ||
+ (lastChunkSize == 0) ||
+ (lastChunkSize > chunkSize) ||
+ (length < totalSize)) {
+ ERROR("Malformed or broken seekable zstream");
+ return false;
+ }
+
+ return true;
+}
+
+bool
+SeekableZStream::Decompress(void *where, size_t chunk, size_t length)
+{
+ while (length) {
+ size_t len = std::min(length, static_cast<size_t>(chunkSize));
+ if (!DecompressChunk(where, chunk, len))
+ return false;
+ where = reinterpret_cast<unsigned char *>(where) + len;
+ length -= len;
+ chunk++;
+ }
+ return true;
+}
+
+bool
+SeekableZStream::DecompressChunk(void *where, size_t chunk, size_t length)
+{
+ if (chunk >= offsetTable.numElements()) {
+ ERROR("DecompressChunk: chunk #%" PRIdSize " out of range [0-%" PRIdSize ")",
+ chunk, offsetTable.numElements());
+ return false;
+ }
+
+ bool isLastChunk = (chunk == offsetTable.numElements() - 1);
+
+ size_t chunkLen = isLastChunk ? lastChunkSize : chunkSize;
+
+ if (length == 0 || length > chunkLen)
+ length = chunkLen;
+
+ DEBUG_LOG("DecompressChunk #%" PRIdSize " @%p (%" PRIdSize "/% " PRIdSize ")",
+ chunk, where, length, chunkLen);
+ zxx_stream zStream(&allocator);
+ zStream.avail_in = (isLastChunk ? totalSize : uint32_t(offsetTable[chunk + 1]))
+ - uint32_t(offsetTable[chunk]);
+ zStream.next_in = const_cast<Bytef *>(buffer + uint32_t(offsetTable[chunk]));
+ zStream.avail_out = length;
+ zStream.next_out = reinterpret_cast<Bytef *>(where);
+
+ /* Decompress chunk */
+ if (inflateInit2(&zStream, windowBits) != Z_OK) {
+ ERROR("inflateInit failed: %s", zStream.msg);
+ return false;
+ }
+ if (dictionary && inflateSetDictionary(&zStream, dictionary,
+ dictionary.numElements()) != Z_OK) {
+ ERROR("inflateSetDictionary failed: %s", zStream.msg);
+ return false;
+ }
+ if (inflate(&zStream, (length == chunkLen) ? Z_FINISH : Z_SYNC_FLUSH)
+ != (length == chunkLen) ? Z_STREAM_END : Z_OK) {
+ ERROR("inflate failed: %s", zStream.msg);
+ return false;
+ }
+ if (inflateEnd(&zStream) != Z_OK) {
+ ERROR("inflateEnd failed: %s", zStream.msg);
+ return false;
+ }
+ if (filter)
+ filter(chunk * chunkSize, UNFILTER, (unsigned char *)where, chunkLen);
+
+ return true;
+}
+
+/* Branch/Call/Jump conversion filter for Thumb, derived from xz-utils
+ * by Igor Pavlov and Lasse Collin, published in the public domain */
+static void
+BCJ_Thumb_filter(off_t offset, SeekableZStream::FilterDirection dir,
+ unsigned char *buf, size_t size)
+{
+ size_t i;
+ for (i = 0; i + 4 <= size; i += 2) {
+ if ((buf[i + 1] & 0xf8) == 0xf0 && (buf[i + 3] & 0xf8) == 0xf8) {
+ uint32_t src = (buf[i] << 11)
+ | ((buf[i + 1] & 0x07) << 19)
+ | buf[i + 2]
+ | ((buf[i + 3] & 0x07) << 8);
+ src <<= 1;
+ uint32_t dest;
+ if (dir == SeekableZStream::FILTER)
+ dest = offset + (uint32_t)(i) + 4 + src;
+ else
+ dest = src - (offset + (uint32_t)(i) + 4);
+
+ dest >>= 1;
+ buf[i] = dest >> 11;
+ buf[i + 1] = 0xf0 | ((dest >> 19) & 0x07);
+ buf[i + 2] = dest;
+ buf[i + 3] = 0xf8 | ((dest >> 8) & 0x07);
+ i += 2;
+ }
+ }
+}
+
+/* Branch/Call/Jump conversion filter for ARM, derived from xz-utils
+ * by Igor Pavlov and Lasse Collin, published in the public domain */
+static void
+BCJ_ARM_filter(off_t offset, SeekableZStream::FilterDirection dir,
+ unsigned char *buf, size_t size)
+{
+ size_t i;
+ for (i = 0; i + 4 <= size; i += 4) {
+ if (buf[i + 3] == 0xeb) {
+ uint32_t src = buf[i]
+ | (buf[i + 1] << 8)
+ | (buf[i + 2] << 16);
+ src <<= 2;
+ uint32_t dest;
+ if (dir == SeekableZStream::FILTER)
+ dest = offset + (uint32_t)(i) + 8 + src;
+ else
+ dest = src - (offset + (uint32_t)(i) + 8);
+
+ dest >>= 2;
+ buf[i] = dest;
+ buf[i + 1] = dest >> 8;
+ buf[i + 2] = dest >> 16;
+ }
+ }
+}
+
+/* Branch/Call/Jump conversion filter for x86, derived from xz-utils
+ * by Igor Pavlov and Lasse Collin, published in the public domain */
+
+#define Test86MSByte(b) ((b) == 0 || (b) == 0xff)
+
+static void
+BCJ_X86_filter(off_t offset, SeekableZStream::FilterDirection dir,
+ unsigned char *buf, size_t size)
+{
+ static const bool MASK_TO_ALLOWED_STATUS[8] =
+ { true, true, true, false, true, false, false, false };
+
+ static const uint32_t MASK_TO_BIT_NUMBER[8] =
+ { 0, 1, 2, 2, 3, 3, 3, 3 };
+
+ uint32_t prev_mask = 0;
+ uint32_t prev_pos = 0;
+
+ for (size_t i = 0; i + 5 <= size;) {
+ uint8_t b = buf[i];
+ if (b != 0xe8 && b != 0xe9) {
+ ++i;
+ continue;
+ }
+
+ const uint32_t off = offset + (uint32_t)(i) - prev_pos;
+ prev_pos = offset + (uint32_t)(i);
+
+ if (off > 5) {
+ prev_mask = 0;
+ } else {
+ for (uint32_t i = 0; i < off; ++i) {
+ prev_mask &= 0x77;
+ prev_mask <<= 1;
+ }
+ }
+
+ b = buf[i + 4];
+
+ if (Test86MSByte(b) && MASK_TO_ALLOWED_STATUS[(prev_mask >> 1) & 0x7]
+ && (prev_mask >> 1) < 0x10) {
+
+ uint32_t src = ((uint32_t)(b) << 24)
+ | ((uint32_t)(buf[i + 3]) << 16)
+ | ((uint32_t)(buf[i + 2]) << 8)
+ | (buf[i + 1]);
+
+ uint32_t dest;
+ while (true) {
+ if (dir == SeekableZStream::FILTER)
+ dest = src + (offset + (uint32_t)(i) + 5);
+ else
+ dest = src - (offset + (uint32_t)(i) + 5);
+
+ if (prev_mask == 0)
+ break;
+
+ const uint32_t i = MASK_TO_BIT_NUMBER[prev_mask >> 1];
+
+ b = (uint8_t)(dest >> (24 - i * 8));
+
+ if (!Test86MSByte(b))
+ break;
+
+ src = dest ^ ((1 << (32 - i * 8)) - 1);
+ }
+
+ buf[i + 4] = (uint8_t)(~(((dest >> 24) & 1) - 1));
+ buf[i + 3] = (uint8_t)(dest >> 16);
+ buf[i + 2] = (uint8_t)(dest >> 8);
+ buf[i + 1] = (uint8_t)(dest);
+ i += 5;
+ prev_mask = 0;
+
+ } else {
+ ++i;
+ prev_mask |= 1;
+ if (Test86MSByte(b))
+ prev_mask |= 0x10;
+ }
+ }
+}
+
+SeekableZStream::ZStreamFilter
+SeekableZStream::GetFilter(SeekableZStream::FilterId id)
+{
+ switch (id) {
+ case BCJ_THUMB:
+ return BCJ_Thumb_filter;
+ case BCJ_ARM:
+ return BCJ_ARM_filter;
+ case BCJ_X86:
+ return BCJ_X86_filter;
+ default:
+ return nullptr;
+ }
+ return nullptr;
+}
diff --git a/mozglue/linker/SeekableZStream.h b/mozglue/linker/SeekableZStream.h
new file mode 100644
index 000000000..3505c681e
--- /dev/null
+++ b/mozglue/linker/SeekableZStream.h
@@ -0,0 +1,154 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef SeekableZStream_h
+#define SeekableZStream_h
+
+#include "Zip.h"
+
+/**
+ * Seekable compressed stream are created by splitting the original
+ * decompressed data in small chunks and compress these chunks
+ * individually.
+ *
+ * The seekable compressed file format consists in a header defined below,
+ * followed by a table of 32-bits words containing the offsets for each
+ * individual compressed chunk, then followed by the compressed chunks.
+ */
+
+#pragma pack(1)
+struct SeekableZStreamHeader: public Zip::SignedEntity<SeekableZStreamHeader>
+{
+ SeekableZStreamHeader()
+ : Zip::SignedEntity<SeekableZStreamHeader>(magic)
+ , totalSize(0), chunkSize(0), dictSize(0), nChunks(0), lastChunkSize(0)
+ , windowBits(0), filter(0) { }
+
+ /* Reuse Zip::SignedEntity to handle the magic number used in the Seekable
+ * ZStream file format. The magic number is "SeZz". */
+ static const uint32_t magic = 0x7a5a6553;
+
+ /* Total size of the stream, including the 4 magic bytes. */
+ le_uint32 totalSize;
+
+ /* Chunk size */
+ le_uint16 chunkSize;
+
+ /* Size of the dictionary */
+ le_uint16 dictSize;
+
+ /* Number of chunks */
+ le_uint32 nChunks;
+
+ /* Size of last chunk (> 0, <= Chunk size) */
+ le_uint16 lastChunkSize;
+
+ /* windowBits value used when deflating */
+ signed char windowBits;
+
+ /* Filter Id */
+ unsigned char filter;
+};
+#pragma pack()
+
+static_assert(sizeof(SeekableZStreamHeader) == 5 * 4,
+ "SeekableZStreamHeader should be 5 32-bits words");
+
+/**
+ * Helper class used to decompress Seekable ZStreams.
+ */
+class SeekableZStream {
+public:
+ /* Initialize from the given buffer. Returns whether initialization
+ * succeeded (true) or failed (false). */
+ bool Init(const void *buf, size_t length);
+
+ /* Decompresses starting from the given chunk. The decompressed data is
+ * stored at the given location. The given length, in bytes, indicates
+ * how much data to decompress. If length is 0, then exactly one chunk
+ * is decompressed.
+ * Returns whether decompression succeeded (true) or failed (false). */
+ bool Decompress(void *where, size_t chunk, size_t length = 0);
+
+ /* Decompresses the given chunk at the given address. If a length is given,
+ * only decompresses that amount of data instead of the entire chunk.
+ * Returns whether decompression succeeded (true) or failed (false). */
+ bool DecompressChunk(void *where, size_t chunk, size_t length = 0);
+
+ /* Returns the uncompressed size of the complete zstream */
+ size_t GetUncompressedSize() const
+ {
+ return (offsetTable.numElements() - 1) * chunkSize + lastChunkSize;
+ }
+
+ /* Returns the chunk size of the given chunk */
+ size_t GetChunkSize(size_t chunk = 0) const {
+ return (chunk == offsetTable.numElements() - 1) ? lastChunkSize : chunkSize;
+ }
+
+ /* Returns the number of chunks */
+ size_t GetChunksNum() const {
+ return offsetTable.numElements();
+ }
+
+ /**
+ * Filters used to improve compression rate.
+ */
+ enum FilterDirection {
+ FILTER,
+ UNFILTER
+ };
+ typedef void (*ZStreamFilter)(off_t, FilterDirection,
+ unsigned char *, size_t);
+
+ enum FilterId {
+ NONE,
+ BCJ_THUMB,
+ BCJ_ARM,
+ BCJ_X86,
+ FILTER_MAX
+ };
+ static ZStreamFilter GetFilter(FilterId id);
+
+ static ZStreamFilter GetFilter(uint16_t id) {
+ return GetFilter(static_cast<FilterId>(id));
+ }
+
+private:
+ /* RAW Seekable SZtream buffer */
+ const unsigned char *buffer;
+
+ /* Total size of the stream, including the 4 magic bytes. */
+ uint32_t totalSize;
+
+ /* Chunk size */
+ uint32_t chunkSize;
+
+ /* Size of last chunk (> 0, <= Chunk size) */
+ uint32_t lastChunkSize;
+
+ /* windowBits value used when deflating */
+ int windowBits;
+
+ /* Offsets table */
+ Array<le_uint32> offsetTable;
+
+ /* Filter */
+ ZStreamFilter filter;
+
+ /* Deflate dictionary */
+ Array<unsigned char> dictionary;
+
+ /* Special allocator for inflate to use the same buffers for every chunk */
+ zxx_stream::StaticAllocator allocator;
+};
+
+inline void
+operator++(SeekableZStream::FilterId &other)
+{
+ const int orig = static_cast<int>(other);
+ other = static_cast<SeekableZStream::FilterId>(orig + 1);
+}
+
+#endif /* SeekableZStream_h */
diff --git a/mozglue/linker/Utils.h b/mozglue/linker/Utils.h
new file mode 100644
index 000000000..c5314ef60
--- /dev/null
+++ b/mozglue/linker/Utils.h
@@ -0,0 +1,618 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef Utils_h
+#define Utils_h
+
+#include <pthread.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include "mozilla/Assertions.h"
+#include "mozilla/Scoped.h"
+
+/**
+ * On architectures that are little endian and that support unaligned reads,
+ * we can use direct type, but on others, we want to have a special class
+ * to handle conversion and alignment issues.
+ */
+#if !defined(DEBUG) && (defined(__i386__) || defined(__x86_64__))
+typedef uint16_t le_uint16;
+typedef uint32_t le_uint32;
+#else
+
+/**
+ * Template that allows to find an unsigned int type from a (computed) bit size
+ */
+template <int s> struct UInt { };
+template <> struct UInt<16> { typedef uint16_t Type; };
+template <> struct UInt<32> { typedef uint32_t Type; };
+
+/**
+ * Template to access 2 n-bit sized words as a 2*n-bit sized word, doing
+ * conversion from little endian and avoiding alignment issues.
+ */
+template <typename T>
+class le_to_cpu
+{
+public:
+ typedef typename UInt<16 * sizeof(T)>::Type Type;
+
+ operator Type() const
+ {
+ return (b << (sizeof(T) * 8)) | a;
+ }
+
+ const le_to_cpu& operator =(const Type &v)
+ {
+ a = v & ((1 << (sizeof(T) * 8)) - 1);
+ b = v >> (sizeof(T) * 8);
+ return *this;
+ }
+
+ le_to_cpu() { }
+ le_to_cpu(const Type &v)
+ {
+ operator =(v);
+ }
+
+ const le_to_cpu& operator +=(const Type &v)
+ {
+ return operator =(operator Type() + v);
+ }
+
+ const le_to_cpu& operator ++(int)
+ {
+ return operator =(operator Type() + 1);
+ }
+
+private:
+ T a, b;
+};
+
+/**
+ * Type definitions
+ */
+typedef le_to_cpu<unsigned char> le_uint16;
+typedef le_to_cpu<le_uint16> le_uint32;
+#endif
+
+
+/**
+ * AutoCloseFD is a RAII wrapper for POSIX file descriptors
+ */
+struct AutoCloseFDTraits
+{
+ typedef int type;
+ static int empty() { return -1; }
+ static void release(int fd) { if (fd != -1) close(fd); }
+};
+typedef mozilla::Scoped<AutoCloseFDTraits> AutoCloseFD;
+
+/**
+ * AutoCloseFILE is a RAII wrapper for POSIX streams
+ */
+struct AutoCloseFILETraits
+{
+ typedef FILE *type;
+ static FILE *empty() { return nullptr; }
+ static void release(FILE *f) { if (f) fclose(f); }
+};
+typedef mozilla::Scoped<AutoCloseFILETraits> AutoCloseFILE;
+
+/**
+ * Page alignment helpers
+ */
+static inline size_t PageSize()
+{
+ return 4096;
+}
+
+static inline uintptr_t AlignedPtr(uintptr_t ptr, size_t alignment)
+{
+ return ptr & ~(alignment - 1);
+}
+
+template <typename T>
+static inline T *AlignedPtr(T *ptr, size_t alignment)
+{
+ return reinterpret_cast<T *>(
+ AlignedPtr(reinterpret_cast<uintptr_t>(ptr), alignment));
+}
+
+template <typename T>
+static inline T PageAlignedPtr(T ptr)
+{
+ return AlignedPtr(ptr, PageSize());
+}
+
+static inline uintptr_t AlignedEndPtr(uintptr_t ptr, size_t alignment)
+{
+ return AlignedPtr(ptr + alignment - 1, alignment);
+}
+
+template <typename T>
+static inline T *AlignedEndPtr(T *ptr, size_t alignment)
+{
+ return reinterpret_cast<T *>(
+ AlignedEndPtr(reinterpret_cast<uintptr_t>(ptr), alignment));
+}
+
+template <typename T>
+static inline T PageAlignedEndPtr(T ptr)
+{
+ return AlignedEndPtr(ptr, PageSize());
+}
+
+static inline size_t AlignedSize(size_t size, size_t alignment)
+{
+ return (size + alignment - 1) & ~(alignment - 1);
+}
+
+static inline size_t PageAlignedSize(size_t size)
+{
+ return AlignedSize(size, PageSize());
+}
+
+static inline bool IsAlignedPtr(uintptr_t ptr, size_t alignment)
+{
+ return ptr % alignment == 0;
+}
+
+template <typename T>
+static inline bool IsAlignedPtr(T *ptr, size_t alignment)
+{
+ return IsAlignedPtr(reinterpret_cast<uintptr_t>(ptr), alignment);
+}
+
+template <typename T>
+static inline bool IsPageAlignedPtr(T ptr)
+{
+ return IsAlignedPtr(ptr, PageSize());
+}
+
+static inline bool IsAlignedSize(size_t size, size_t alignment)
+{
+ return size % alignment == 0;
+}
+
+static inline bool IsPageAlignedSize(size_t size)
+{
+ return IsAlignedSize(size, PageSize());
+}
+
+static inline size_t PageNumber(size_t size)
+{
+ return (size + PageSize() - 1) / PageSize();
+}
+
+/**
+ * MemoryRange stores a pointer, size pair.
+ */
+class MemoryRange
+{
+public:
+ MemoryRange(void *buf, size_t length): buf(buf), length(length) { }
+
+ void Assign(void *b, size_t len) {
+ buf = b;
+ length = len;
+ }
+
+ void Assign(const MemoryRange& other) {
+ buf = other.buf;
+ length = other.length;
+ }
+
+ void *get() const
+ {
+ return buf;
+ }
+
+ operator void *() const
+ {
+ return buf;
+ }
+
+ operator unsigned char *() const
+ {
+ return reinterpret_cast<unsigned char *>(buf);
+ }
+
+ bool operator ==(void *ptr) const {
+ return buf == ptr;
+ }
+
+ bool operator ==(unsigned char *ptr) const {
+ return buf == ptr;
+ }
+
+ void *operator +(off_t offset) const
+ {
+ return reinterpret_cast<char *>(buf) + offset;
+ }
+
+ /**
+ * Returns whether the given address is within the mapped range
+ */
+ bool Contains(void *ptr) const
+ {
+ return (ptr >= buf) && (ptr < reinterpret_cast<char *>(buf) + length);
+ }
+
+ /**
+ * Returns the length of the mapped range
+ */
+ size_t GetLength() const
+ {
+ return length;
+ }
+
+ static MemoryRange mmap(void *addr, size_t length, int prot, int flags,
+ int fd, off_t offset) {
+ return MemoryRange(::mmap(addr, length, prot, flags, fd, offset), length);
+ }
+
+private:
+ void *buf;
+ size_t length;
+};
+
+/**
+ * MappedPtr is a RAII wrapper for mmap()ed memory. It can be used as
+ * a simple void * or unsigned char *.
+ *
+ * It is defined as a derivative of a template that allows to use a
+ * different unmapping strategy.
+ */
+template <typename T>
+class GenericMappedPtr: public MemoryRange
+{
+public:
+ GenericMappedPtr(void *buf, size_t length): MemoryRange(buf, length) { }
+ GenericMappedPtr(const MemoryRange& other): MemoryRange(other) { }
+ GenericMappedPtr(): MemoryRange(MAP_FAILED, 0) { }
+
+ void Assign(void *b, size_t len) {
+ if (get() != MAP_FAILED)
+ static_cast<T *>(this)->munmap(get(), GetLength());
+ MemoryRange::Assign(b, len);
+ }
+
+ void Assign(const MemoryRange& other) {
+ Assign(other.get(), other.GetLength());
+ }
+
+ ~GenericMappedPtr()
+ {
+ if (get() != MAP_FAILED)
+ static_cast<T *>(this)->munmap(get(), GetLength());
+ }
+
+ void release()
+ {
+ MemoryRange::Assign(MAP_FAILED, 0);
+ }
+};
+
+struct MappedPtr: public GenericMappedPtr<MappedPtr>
+{
+ MappedPtr(void *buf, size_t length)
+ : GenericMappedPtr<MappedPtr>(buf, length) { }
+ MappedPtr(const MemoryRange& other)
+ : GenericMappedPtr<MappedPtr>(other) { }
+ MappedPtr(): GenericMappedPtr<MappedPtr>() { }
+
+private:
+ friend class GenericMappedPtr<MappedPtr>;
+ void munmap(void *buf, size_t length)
+ {
+ ::munmap(buf, length);
+ }
+};
+
+/**
+ * UnsizedArray is a way to access raw arrays of data in memory.
+ *
+ * struct S { ... };
+ * UnsizedArray<S> a(buf);
+ * UnsizedArray<S> b; b.Init(buf);
+ *
+ * This is roughly equivalent to
+ * const S *a = reinterpret_cast<const S *>(buf);
+ * const S *b = nullptr; b = reinterpret_cast<const S *>(buf);
+ *
+ * An UnsizedArray has no known length, and it's up to the caller to make
+ * sure the accessed memory is mapped and makes sense.
+ */
+template <typename T>
+class UnsizedArray
+{
+public:
+ typedef size_t idx_t;
+
+ /**
+ * Constructors and Initializers
+ */
+ UnsizedArray(): contents(nullptr) { }
+ UnsizedArray(const void *buf): contents(reinterpret_cast<const T *>(buf)) { }
+
+ void Init(const void *buf)
+ {
+ MOZ_ASSERT(contents == nullptr);
+ contents = reinterpret_cast<const T *>(buf);
+ }
+
+ /**
+ * Returns the nth element of the array
+ */
+ const T &operator[](const idx_t index) const
+ {
+ MOZ_ASSERT(contents);
+ return contents[index];
+ }
+
+ operator const T *() const
+ {
+ return contents;
+ }
+ /**
+ * Returns whether the array points somewhere
+ */
+ operator bool() const
+ {
+ return contents != nullptr;
+ }
+private:
+ const T *contents;
+};
+
+/**
+ * Array, like UnsizedArray, is a way to access raw arrays of data in memory.
+ * Unlike UnsizedArray, it has a known length, and is enumerable with an
+ * iterator.
+ *
+ * struct S { ... };
+ * Array<S> a(buf, len);
+ * UnsizedArray<S> b; b.Init(buf, len);
+ *
+ * In the above examples, len is the number of elements in the array. It is
+ * also possible to initialize an Array with the buffer size:
+ *
+ * Array<S> c; c.InitSize(buf, size);
+ *
+ * It is also possible to initialize an Array in two steps, only providing
+ * one data at a time:
+ *
+ * Array<S> d;
+ * d.Init(buf);
+ * d.Init(len); // or d.InitSize(size);
+ *
+ */
+template <typename T>
+class Array: public UnsizedArray<T>
+{
+public:
+ typedef typename UnsizedArray<T>::idx_t idx_t;
+
+ /**
+ * Constructors and Initializers
+ */
+ Array(): UnsizedArray<T>(), length(0) { }
+ Array(const void *buf, const idx_t length)
+ : UnsizedArray<T>(buf), length(length) { }
+
+ void Init(const void *buf)
+ {
+ UnsizedArray<T>::Init(buf);
+ }
+
+ void Init(const idx_t len)
+ {
+ MOZ_ASSERT(length == 0);
+ length = len;
+ }
+
+ void InitSize(const idx_t size)
+ {
+ Init(size / sizeof(T));
+ }
+
+ void Init(const void *buf, const idx_t len)
+ {
+ UnsizedArray<T>::Init(buf);
+ Init(len);
+ }
+
+ void InitSize(const void *buf, const idx_t size)
+ {
+ UnsizedArray<T>::Init(buf);
+ InitSize(size);
+ }
+
+ /**
+ * Returns the nth element of the array
+ */
+ const T &operator[](const idx_t index) const
+ {
+ MOZ_ASSERT(index < length);
+ MOZ_ASSERT(operator bool());
+ return UnsizedArray<T>::operator[](index);
+ }
+
+ /**
+ * Returns the number of elements in the array
+ */
+ idx_t numElements() const
+ {
+ return length;
+ }
+
+ /**
+ * Returns whether the array points somewhere and has at least one element.
+ */
+ operator bool() const
+ {
+ return (length > 0) && UnsizedArray<T>::operator bool();
+ }
+
+ /**
+ * Iterator for an Array. Use is similar to that of STL const_iterators:
+ *
+ * struct S { ... };
+ * Array<S> a(buf, len);
+ * for (Array<S>::iterator it = a.begin(); it < a.end(); ++it) {
+ * // Do something with *it.
+ * }
+ */
+ class iterator
+ {
+ public:
+ iterator(): item(nullptr) { }
+
+ const T &operator *() const
+ {
+ return *item;
+ }
+
+ const T *operator ->() const
+ {
+ return item;
+ }
+
+ iterator &operator ++()
+ {
+ ++item;
+ return *this;
+ }
+
+ bool operator<(const iterator &other) const
+ {
+ return item < other.item;
+ }
+ protected:
+ friend class Array<T>;
+ iterator(const T &item): item(&item) { }
+
+ private:
+ const T *item;
+ };
+
+ /**
+ * Returns an iterator pointing at the beginning of the Array
+ */
+ iterator begin() const {
+ if (length)
+ return iterator(UnsizedArray<T>::operator[](0));
+ return iterator();
+ }
+
+ /**
+ * Returns an iterator pointing past the end of the Array
+ */
+ iterator end() const {
+ if (length)
+ return iterator(UnsizedArray<T>::operator[](length));
+ return iterator();
+ }
+
+ /**
+ * Reverse iterator for an Array. Use is similar to that of STL
+ * const_reverse_iterators:
+ *
+ * struct S { ... };
+ * Array<S> a(buf, len);
+ * for (Array<S>::reverse_iterator it = a.rbegin(); it < a.rend(); ++it) {
+ * // Do something with *it.
+ * }
+ */
+ class reverse_iterator
+ {
+ public:
+ reverse_iterator(): item(nullptr) { }
+
+ const T &operator *() const
+ {
+ const T *tmp = item;
+ return *--tmp;
+ }
+
+ const T *operator ->() const
+ {
+ return &operator*();
+ }
+
+ reverse_iterator &operator ++()
+ {
+ --item;
+ return *this;
+ }
+
+ bool operator<(const reverse_iterator &other) const
+ {
+ return item > other.item;
+ }
+ protected:
+ friend class Array<T>;
+ reverse_iterator(const T &item): item(&item) { }
+
+ private:
+ const T *item;
+ };
+
+ /**
+ * Returns a reverse iterator pointing at the end of the Array
+ */
+ reverse_iterator rbegin() const {
+ if (length)
+ return reverse_iterator(UnsizedArray<T>::operator[](length));
+ return reverse_iterator();
+ }
+
+ /**
+ * Returns a reverse iterator pointing past the beginning of the Array
+ */
+ reverse_iterator rend() const {
+ if (length)
+ return reverse_iterator(UnsizedArray<T>::operator[](0));
+ return reverse_iterator();
+ }
+private:
+ idx_t length;
+};
+
+/**
+ * Transforms a pointer-to-function to a pointer-to-object pointing at the
+ * same address.
+ */
+template <typename T>
+void *FunctionPtr(T func)
+{
+ union {
+ void *ptr;
+ T func;
+ } f;
+ f.func = func;
+ return f.ptr;
+}
+
+class AutoLock {
+public:
+ AutoLock(pthread_mutex_t *mutex): mutex(mutex)
+ {
+ if (pthread_mutex_lock(mutex))
+ MOZ_CRASH("pthread_mutex_lock failed");
+ }
+ ~AutoLock()
+ {
+ if (pthread_mutex_unlock(mutex))
+ MOZ_CRASH("pthread_mutex_unlock failed");
+ }
+private:
+ pthread_mutex_t *mutex;
+};
+
+#endif /* Utils_h */
+
diff --git a/mozglue/linker/XZStream.cpp b/mozglue/linker/XZStream.cpp
new file mode 100644
index 000000000..bd71655f5
--- /dev/null
+++ b/mozglue/linker/XZStream.cpp
@@ -0,0 +1,213 @@
+#include "XZStream.h"
+
+#include <algorithm>
+#include "mozilla/Assertions.h"
+#include "Logging.h"
+
+// LZMA dictionary size, should have a minimum size for the given compression
+// rate, see XZ Utils docs for details.
+static const uint32_t kDictSize = 1 << 24;
+
+static const size_t kFooterSize = 12;
+
+// Parses a variable-length integer (VLI),
+// see http://tukaani.org/xz/xz-file-format.txt for details.
+static size_t
+ParseVarLenInt(const uint8_t* aBuf, size_t aBufSize, uint64_t* aValue)
+{
+ if (!aBufSize) {
+ return 0;
+ }
+ aBufSize = std::min(9u, aBufSize);
+
+ *aValue = aBuf[0] & 0x7F;
+ size_t i = 0;
+
+ while (aBuf[i++] & 0x80) {
+ if (i >= aBufSize || aBuf[i] == 0x0) {
+ return 0;
+ }
+ *aValue |= static_cast<uint64_t>(aBuf[i] & 0x7F) << (i * 7);
+ }
+ return i;
+}
+
+/* static */ bool
+XZStream::IsXZ(const void* aBuf, size_t aBufSize)
+{
+ static const uint8_t kXzMagic[] = {0xfd, '7', 'z', 'X', 'Z', 0x0};
+ MOZ_ASSERT(aBuf);
+ return aBufSize > sizeof(kXzMagic) &&
+ !memcmp(reinterpret_cast<const void*>(kXzMagic), aBuf, sizeof(kXzMagic));
+}
+
+XZStream::XZStream(const void* aInBuf, size_t aInSize)
+ : mInBuf(static_cast<const uint8_t*>(aInBuf))
+ , mUncompSize(0)
+ , mDec(nullptr)
+{
+ mBuffers.in = mInBuf;
+ mBuffers.in_pos = 0;
+ mBuffers.in_size = aInSize;
+}
+
+XZStream::~XZStream()
+{
+ xz_dec_end(mDec);
+}
+
+bool
+XZStream::Init()
+{
+#ifdef XZ_USE_CRC64
+ xz_crc64_init();
+#endif
+ xz_crc32_init();
+
+ mDec = xz_dec_init(XZ_DYNALLOC, kDictSize);
+
+ if (!mDec) {
+ return false;
+ }
+
+ mUncompSize = ParseUncompressedSize();
+
+ return true;
+}
+
+size_t
+XZStream::Decode(void* aOutBuf, size_t aOutSize)
+{
+ if (!mDec) {
+ return 0;
+ }
+
+ mBuffers.out = static_cast<uint8_t*>(aOutBuf);
+ mBuffers.out_pos = 0;
+ mBuffers.out_size = aOutSize;
+
+ while (mBuffers.in_pos < mBuffers.in_size &&
+ mBuffers.out_pos < mBuffers.out_size) {
+ const xz_ret ret = xz_dec_run(mDec, &mBuffers);
+
+ switch (ret) {
+ case XZ_STREAM_END:
+ // Stream ended, the next loop iteration should terminate.
+ MOZ_ASSERT(mBuffers.in_pos == mBuffers.in_size);
+ MOZ_FALLTHROUGH;
+#ifdef XZ_DEC_ANY_CHECK
+ case XZ_UNSUPPORTED_CHECK:
+ // Ignore unsupported check.
+ MOZ_FALLTHROUGH;
+#endif
+ case XZ_OK:
+ // Chunk decoded, proceed.
+ break;
+
+ case XZ_MEM_ERROR:
+ ERROR("XZ decoding: memory allocation failed");
+ return 0;
+
+ case XZ_MEMLIMIT_ERROR:
+ ERROR("XZ decoding: memory usage limit reached");
+ return 0;
+
+ case XZ_FORMAT_ERROR:
+ ERROR("XZ decoding: invalid stream format");
+ return 0;
+
+ case XZ_OPTIONS_ERROR:
+ ERROR("XZ decoding: unsupported header options");
+ return 0;
+
+ case XZ_DATA_ERROR:
+ MOZ_FALLTHROUGH;
+ case XZ_BUF_ERROR:
+ ERROR("XZ decoding: corrupt input stream");
+ return 0;
+
+ default:
+ MOZ_ASSERT_UNREACHABLE("XZ decoding: unknown error condition");
+ return 0;
+ }
+ }
+ return mBuffers.out_pos;
+}
+
+size_t
+XZStream::RemainingInput() const
+{
+ return mBuffers.in_size - mBuffers.in_pos;
+}
+
+size_t
+XZStream::Size() const
+{
+ return mBuffers.in_size;
+}
+
+size_t
+XZStream::UncompressedSize() const
+{
+ return mUncompSize;
+}
+
+size_t
+XZStream::ParseIndexSize() const
+{
+ static const uint8_t kFooterMagic[] = {'Y', 'Z'};
+
+ const uint8_t* footer = mInBuf + mBuffers.in_size - kFooterSize;
+ // The magic bytes are at the end of the footer.
+ if (memcmp(reinterpret_cast<const void*>(kFooterMagic),
+ footer + kFooterSize - sizeof(kFooterMagic),
+ sizeof(kFooterMagic))) {
+ // Not a valid footer at stream end.
+ return 0;
+ }
+ // Backward size is a 32 bit LE integer field positioned after the 32 bit CRC32
+ // code. It encodes the index size as a multiple of 4 bytes with a minimum
+ // size of 4 bytes.
+ const uint32_t backwardSize = *(footer + 4);
+ return (backwardSize + 1) * 4;
+}
+
+size_t
+XZStream::ParseUncompressedSize() const
+{
+ static const uint8_t kIndexIndicator[] = {0x0};
+
+ const size_t indexSize = ParseIndexSize();
+ if (!indexSize) {
+ return 0;
+ }
+ // The footer follows directly the index, so we can use it as a reference.
+ const uint8_t* end = mInBuf + mBuffers.in_size;
+ const uint8_t* index = end - kFooterSize - indexSize;
+
+ // The index consists of a one byte indicator followed by a VLI field for the
+ // number of records (1 expected) followed by a list of records. One record
+ // contains a VLI field for unpadded size followed by a VLI field for
+ // uncompressed size.
+ if (memcmp(reinterpret_cast<const void*>(kIndexIndicator),
+ index, sizeof(kIndexIndicator))) {
+ // Not a valid index.
+ return 0;
+ }
+
+ index += sizeof(kIndexIndicator);
+ uint64_t numRecords = 0;
+ index += ParseVarLenInt(index, end - index, &numRecords);
+ if (!numRecords) {
+ return 0;
+ }
+ uint64_t unpaddedSize = 0;
+ index += ParseVarLenInt(index, end - index, &unpaddedSize);
+ if (!unpaddedSize) {
+ return 0;
+ }
+ uint64_t uncompressedSize = 0;
+ index += ParseVarLenInt(index, end - index, &uncompressedSize);
+
+ return uncompressedSize;
+}
diff --git a/mozglue/linker/XZStream.h b/mozglue/linker/XZStream.h
new file mode 100644
index 000000000..76a979b13
--- /dev/null
+++ b/mozglue/linker/XZStream.h
@@ -0,0 +1,48 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef XZSTREAM_h
+#define XZSTREAM_h
+
+#include <cstdlib>
+
+#define XZ_DEC_DYNALLOC
+#include "xz.h"
+
+// Used to decode XZ stream buffers.
+class XZStream
+{
+public:
+ // Returns whether the provided buffer is likely a XZ stream.
+ static bool IsXZ(const void* aBuf, size_t aBufSize);
+
+ // Creates a XZ stream object for the given input buffer.
+ XZStream(const void* aInBuf, size_t aInSize);
+ ~XZStream();
+
+ // Initializes the decoder and returns whether decoding may commence.
+ bool Init();
+ // Decodes the next chunk of input into the given output buffer.
+ size_t Decode(void* aOutBuf, size_t aOutSize);
+ // Returns the number of yet undecoded bytes in the input buffer.
+ size_t RemainingInput() const;
+ // Returns the total number of bytes in the input buffer (compressed size).
+ size_t Size() const;
+ // Returns the expected final number of bytes in the output buffer.
+ // Note: will return 0 before successful Init().
+ size_t UncompressedSize() const;
+
+private:
+ // Parses the stream footer and returns the size of the index in bytes.
+ size_t ParseIndexSize() const;
+ // Parses the stream index and returns the expected uncompressed size in bytes.
+ size_t ParseUncompressedSize() const;
+
+ const uint8_t* mInBuf;
+ size_t mUncompSize;
+ xz_buf mBuffers;
+ xz_dec* mDec;
+};
+
+#endif // XZSTREAM_h
diff --git a/mozglue/linker/Zip.cpp b/mozglue/linker/Zip.cpp
new file mode 100644
index 000000000..d141eaa45
--- /dev/null
+++ b/mozglue/linker/Zip.cpp
@@ -0,0 +1,229 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <cstdlib>
+#include <algorithm>
+#include "Logging.h"
+#include "Zip.h"
+
+already_AddRefed<Zip>
+Zip::Create(const char *filename)
+{
+ /* Open and map the file in memory */
+ AutoCloseFD fd(open(filename, O_RDONLY));
+ if (fd == -1) {
+ ERROR("Error opening %s: %s", filename, strerror(errno));
+ return nullptr;
+ }
+ struct stat st;
+ if (fstat(fd, &st) == -1) {
+ ERROR("Error stating %s: %s", filename, strerror(errno));
+ return nullptr;
+ }
+ size_t size = st.st_size;
+ if (size <= sizeof(CentralDirectoryEnd)) {
+ ERROR("Error reading %s: too short", filename);
+ return nullptr;
+ }
+ void *mapped = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);
+ if (mapped == MAP_FAILED) {
+ ERROR("Error mmapping %s: %s", filename, strerror(errno));
+ return nullptr;
+ }
+ DEBUG_LOG("Mapped %s @%p", filename, mapped);
+
+ return Create(filename, mapped, size);
+}
+
+already_AddRefed<Zip>
+Zip::Create(const char *filename, void *mapped, size_t size)
+{
+ RefPtr<Zip> zip = new Zip(filename, mapped, size);
+
+ // If neither the first Local File entry nor central directory entries
+ // have been found, the zip was invalid.
+ if (!zip->nextFile && !zip->entries) {
+ ERROR("%s - Invalid zip", filename);
+ return nullptr;
+ }
+
+ ZipCollection::Singleton.Register(zip);
+ return zip.forget();
+}
+
+Zip::Zip(const char *filename, void *mapped, size_t size)
+: name(filename ? strdup(filename) : nullptr)
+, mapped(mapped)
+, size(size)
+, nextFile(LocalFile::validate(mapped)) // first Local File entry
+, nextDir(nullptr)
+, entries(nullptr)
+{
+ pthread_mutex_init(&mutex, nullptr);
+ // If the first local file entry couldn't be found (which can happen
+ // with optimized jars), check the first central directory entry.
+ if (!nextFile)
+ GetFirstEntry();
+}
+
+Zip::~Zip()
+{
+ ZipCollection::Forget(this);
+ if (name) {
+ munmap(mapped, size);
+ DEBUG_LOG("Unmapped %s @%p", name, mapped);
+ free(name);
+ }
+ pthread_mutex_destroy(&mutex);
+}
+
+bool
+Zip::GetStream(const char *path, Zip::Stream *out) const
+{
+ AutoLock lock(&mutex);
+
+ DEBUG_LOG("%s - GetFile %s", name, path);
+ /* Fast path: if the Local File header on store matches, we can return the
+ * corresponding stream right away.
+ * However, the Local File header may not contain enough information, in
+ * which case the 3rd bit on the generalFlag is set. Unfortunately, this
+ * bit is also set in some archives even when we do have the data (most
+ * notably the android packages as built by the Mozilla build system).
+ * So instead of testing the generalFlag bit, only use the fast path when
+ * we haven't read the central directory entries yet, and when the
+ * compressed size as defined in the header is not filled (which is a
+ * normal condition for the bit to be set). */
+ if (nextFile && nextFile->GetName().Equals(path) &&
+ !entries && (nextFile->compressedSize != 0)) {
+ DEBUG_LOG("%s - %s was next file: fast path", name, path);
+ /* Fill Stream info from Local File header content */
+ const char *data = reinterpret_cast<const char *>(nextFile->GetData());
+ out->compressedBuf = data;
+ out->compressedSize = nextFile->compressedSize;
+ out->uncompressedSize = nextFile->uncompressedSize;
+ out->CRC32 = nextFile->CRC32;
+ out->type = static_cast<Stream::Type>(uint16_t(nextFile->compression));
+
+ /* Find the next Local File header. It is usually simply following the
+ * compressed stream, but in cases where the 3rd bit of the generalFlag
+ * is set, there is a Data Descriptor header before. */
+ data += nextFile->compressedSize;
+ if ((nextFile->generalFlag & 0x8) && DataDescriptor::validate(data)) {
+ data += sizeof(DataDescriptor);
+ }
+ nextFile = LocalFile::validate(data);
+ return true;
+ }
+
+ /* If the directory entry we have in store doesn't match, scan the Central
+ * Directory for the entry corresponding to the given path */
+ if (!nextDir || !nextDir->GetName().Equals(path)) {
+ const DirectoryEntry *entry = GetFirstEntry();
+ DEBUG_LOG("%s - Scan directory entries in search for %s", name, path);
+ while (entry && !entry->GetName().Equals(path)) {
+ entry = entry->GetNext();
+ }
+ nextDir = entry;
+ }
+ if (!nextDir) {
+ DEBUG_LOG("%s - Couldn't find %s", name, path);
+ return false;
+ }
+
+ /* Find the Local File header corresponding to the Directory entry that
+ * was found. */
+ nextFile = LocalFile::validate(static_cast<const char *>(mapped)
+ + nextDir->offset);
+ if (!nextFile) {
+ ERROR("%s - Couldn't find the Local File header for %s", name, path);
+ return false;
+ }
+
+ /* Fill Stream info from Directory entry content */
+ const char *data = reinterpret_cast<const char *>(nextFile->GetData());
+ out->compressedBuf = data;
+ out->compressedSize = nextDir->compressedSize;
+ out->uncompressedSize = nextDir->uncompressedSize;
+ out->CRC32 = nextDir->CRC32;
+ out->type = static_cast<Stream::Type>(uint16_t(nextDir->compression));
+
+ /* Store the next directory entry */
+ nextDir = nextDir->GetNext();
+ nextFile = nullptr;
+ return true;
+}
+
+const Zip::DirectoryEntry *
+Zip::GetFirstEntry() const
+{
+ if (entries)
+ return entries;
+
+ const CentralDirectoryEnd *end = nullptr;
+ const char *_end = static_cast<const char *>(mapped) + size
+ - sizeof(CentralDirectoryEnd);
+
+ /* Scan for the Central Directory End */
+ for (; _end > mapped && !end; _end--)
+ end = CentralDirectoryEnd::validate(_end);
+ if (!end) {
+ ERROR("%s - Couldn't find end of central directory record", name);
+ return nullptr;
+ }
+
+ entries = DirectoryEntry::validate(static_cast<const char *>(mapped)
+ + end->offset);
+ if (!entries) {
+ ERROR("%s - Couldn't find central directory record", name);
+ }
+ return entries;
+}
+
+ZipCollection ZipCollection::Singleton;
+
+static pthread_mutex_t sZipCollectionMutex = PTHREAD_MUTEX_INITIALIZER;
+
+already_AddRefed<Zip>
+ZipCollection::GetZip(const char *path)
+{
+ {
+ AutoLock lock(&sZipCollectionMutex);
+ /* Search the list of Zips we already have for a match */
+ for (std::vector<Zip *>::iterator it = Singleton.zips.begin();
+ it < Singleton.zips.end(); ++it) {
+ if ((*it)->GetName() && (strcmp((*it)->GetName(), path) == 0)) {
+ RefPtr<Zip> zip = *it;
+ return zip.forget();
+ }
+ }
+ }
+ return Zip::Create(path);
+}
+
+void
+ZipCollection::Register(Zip *zip)
+{
+ AutoLock lock(&sZipCollectionMutex);
+ DEBUG_LOG("ZipCollection::Register(\"%s\")", zip->GetName());
+ Singleton.zips.push_back(zip);
+}
+
+void
+ZipCollection::Forget(Zip *zip)
+{
+ AutoLock lock(&sZipCollectionMutex);
+ DEBUG_LOG("ZipCollection::Forget(\"%s\")", zip->GetName());
+ std::vector<Zip *>::iterator it = std::find(Singleton.zips.begin(),
+ Singleton.zips.end(), zip);
+ if (*it == zip) {
+ Singleton.zips.erase(it);
+ } else {
+ DEBUG_LOG("ZipCollection::Forget: didn't find \"%s\" in bookkeeping", zip->GetName());
+ }
+}
diff --git a/mozglue/linker/Zip.h b/mozglue/linker/Zip.h
new file mode 100644
index 000000000..29e42592a
--- /dev/null
+++ b/mozglue/linker/Zip.h
@@ -0,0 +1,500 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef Zip_h
+#define Zip_h
+
+#include <cstring>
+#include <stdint.h>
+#include <vector>
+#include <zlib.h>
+#include <pthread.h>
+#include "Utils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/RefCounted.h"
+#include "mozilla/RefPtr.h"
+
+/**
+ * Helper class wrapping z_stream to avoid malloc() calls during
+ * inflate. Do not use for deflate.
+ * inflateInit allocates two buffers:
+ * - one for its internal state, which is "approximately 10K bytes" according
+ * to inflate.h from zlib.
+ * - one for the compression window, which depends on the window size passed
+ * to inflateInit2, but is never greater than 32K (1 << MAX_WBITS).
+ * Those buffers are created at instantiation time instead of when calling
+ * inflateInit2. When inflateInit2 is called, it will call zxx_stream::Alloc
+ * to get both these buffers. zxx_stream::Alloc will choose one of the
+ * pre-allocated buffers depending on the requested size.
+ */
+class zxx_stream: public z_stream
+{
+public:
+ /* Forward declaration */
+ class StaticAllocator;
+
+ explicit zxx_stream(StaticAllocator *allocator_=nullptr)
+ : allocator(allocator_)
+ {
+ memset(this, 0, sizeof(z_stream));
+ zalloc = Alloc;
+ zfree = Free;
+ opaque = this;
+ }
+
+private:
+ static void *Alloc(void *data, uInt items, uInt size)
+ {
+ zxx_stream *zStream = reinterpret_cast<zxx_stream *>(data);
+ if (zStream->allocator) {
+ return zStream->allocator->Alloc(items, size);
+ }
+ size_t buf_size = items * size;
+ return ::operator new(buf_size);
+ }
+
+ static void Free(void *data, void *ptr)
+ {
+ zxx_stream *zStream = reinterpret_cast<zxx_stream *>(data);
+ if (zStream->allocator) {
+ zStream->allocator->Free(ptr);
+ } else {
+ ::operator delete(ptr);
+ }
+ }
+
+ /**
+ * Helper class for each buffer in StaticAllocator.
+ */
+ template <size_t Size>
+ class ZStreamBuf
+ {
+ public:
+ ZStreamBuf() : inUse(false) { }
+
+ bool get(char*& out)
+ {
+ if (!inUse) {
+ inUse = true;
+ out = buf;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void Release()
+ {
+ memset(buf, 0, Size);
+ inUse = false;
+ }
+
+ bool Equals(const void *other) { return other == buf; }
+
+ static const size_t size = Size;
+
+ private:
+ char buf[Size];
+ bool inUse;
+ };
+
+public:
+ /**
+ * Special allocator that uses static buffers to allocate from.
+ */
+ class StaticAllocator
+ {
+ public:
+ void *Alloc(uInt items, uInt size)
+ {
+ if (items == 1 && size <= stateBuf1.size) {
+ char* res = nullptr;
+ if (stateBuf1.get(res) || stateBuf2.get(res)) {
+ return res;
+ }
+ MOZ_CRASH("ZStreamBuf already in use");
+ } else if (items * size == windowBuf1.size) {
+ char* res = nullptr;
+ if (windowBuf1.get(res) || windowBuf2.get(res)) {
+ return res;
+ }
+ MOZ_CRASH("ZStreamBuf already in use");
+ } else {
+ MOZ_CRASH("No ZStreamBuf for allocation");
+ }
+ }
+
+ void Free(void *ptr)
+ {
+ if (stateBuf1.Equals(ptr)) {
+ stateBuf1.Release();
+ } else if (stateBuf2.Equals(ptr)) {
+ stateBuf2.Release();
+ }else if (windowBuf1.Equals(ptr)) {
+ windowBuf1.Release();
+ } else if (windowBuf2.Equals(ptr)) {
+ windowBuf2.Release();
+ } else {
+ MOZ_CRASH("Pointer doesn't match a ZStreamBuf");
+ }
+ }
+
+ // 0x3000 is an arbitrary size above 10K.
+ ZStreamBuf<0x3000> stateBuf1, stateBuf2;
+ ZStreamBuf<1 << MAX_WBITS> windowBuf1, windowBuf2;
+ };
+
+private:
+ StaticAllocator *allocator;
+};
+
+/**
+ * Forward declaration
+ */
+class ZipCollection;
+
+/**
+ * Class to handle access to Zip archive streams. The Zip archive is mapped
+ * in memory, and streams are direct references to that mapped memory.
+ * Zip files are assumed to be correctly formed. No boundary checks are
+ * performed, which means hand-crafted malicious Zip archives can make the
+ * code fail in bad ways. However, since the only intended use is to load
+ * libraries from Zip archives, there is no interest in making this code
+ * safe, since the libraries could contain malicious code anyways.
+ */
+class Zip: public mozilla::external::AtomicRefCounted<Zip>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(Zip)
+ /**
+ * Create a Zip instance for the given file name. Returns nullptr in case
+ * of failure.
+ */
+ static already_AddRefed<Zip> Create(const char *filename);
+
+ /**
+ * Create a Zip instance using the given buffer.
+ */
+ static already_AddRefed<Zip> Create(void *buffer, size_t size) {
+ return Create(nullptr, buffer, size);
+ }
+
+private:
+ static already_AddRefed<Zip> Create(const char *filename,
+ void *buffer, size_t size);
+
+ /**
+ * Private constructor
+ */
+ Zip(const char *filename, void *buffer, size_t size);
+
+public:
+ /**
+ * Destructor
+ */
+ ~Zip();
+
+ /**
+ * Class used to access Zip archive item streams
+ */
+ class Stream
+ {
+ public:
+ /**
+ * Stream types
+ */
+ enum Type {
+ STORE = 0,
+ DEFLATE = 8
+ };
+
+ /**
+ * Constructor
+ */
+ Stream(): compressedBuf(nullptr), compressedSize(0), uncompressedSize(0)
+ , CRC32(0)
+ , type(STORE) { }
+
+ /**
+ * Getters
+ */
+ const void *GetBuffer() { return compressedBuf; }
+ size_t GetSize() { return compressedSize; }
+ size_t GetUncompressedSize() { return uncompressedSize; }
+ size_t GetCRC32() { return CRC32; }
+ Type GetType() { return type; }
+
+ /**
+ * Returns a zxx_stream for use with inflate functions using the given
+ * buffer as inflate output. The caller is expected to allocate enough
+ * memory for the Stream uncompressed size.
+ */
+ zxx_stream GetZStream(void *buf)
+ {
+ zxx_stream zStream;
+ zStream.avail_in = compressedSize;
+ zStream.next_in = reinterpret_cast<Bytef *>(
+ const_cast<void *>(compressedBuf));
+ zStream.avail_out = uncompressedSize;
+ zStream.next_out = static_cast<Bytef *>(buf);
+ return zStream;
+ }
+
+ protected:
+ friend class Zip;
+ const void *compressedBuf;
+ size_t compressedSize;
+ size_t uncompressedSize;
+ size_t CRC32;
+ Type type;
+ };
+
+ /**
+ * Returns a stream from the Zip archive.
+ */
+ bool GetStream(const char *path, Stream *out) const;
+
+ /**
+ * Returns the file name of the archive
+ */
+ const char *GetName() const
+ {
+ return name;
+ }
+
+private:
+ /* File name of the archive */
+ char *name;
+ /* Address where the Zip archive is mapped */
+ void *mapped;
+ /* Size of the archive */
+ size_t size;
+
+ /**
+ * Strings (file names, comments, etc.) in the Zip headers are NOT zero
+ * terminated. This class is a helper around them.
+ */
+ class StringBuf
+ {
+ public:
+ /**
+ * Constructor
+ */
+ StringBuf(const char *buf, size_t length): buf(buf), length(length) { }
+
+ /**
+ * Returns whether the string has the same content as the given zero
+ * terminated string.
+ */
+ bool Equals(const char *str) const
+ {
+ return (strncmp(str, buf, length) == 0 && str[length] == '\0');
+ }
+
+ private:
+ const char *buf;
+ size_t length;
+ };
+
+/* All the following types need to be packed */
+#pragma pack(1)
+public:
+ /**
+ * A Zip archive is an aggregate of entities which all start with a
+ * signature giving their type. This template is to be used as a base
+ * class for these entities.
+ */
+ template <typename T>
+ class SignedEntity
+ {
+ public:
+ /**
+ * Equivalent to reinterpret_cast<const T *>(buf), with an additional
+ * check of the signature.
+ */
+ static const T *validate(const void *buf)
+ {
+ const T *ret = static_cast<const T *>(buf);
+ if (ret->signature == T::magic)
+ return ret;
+ return nullptr;
+ }
+
+ SignedEntity(uint32_t magic): signature(magic) { }
+ private:
+ le_uint32 signature;
+ };
+
+private:
+ /**
+ * Header used to describe a Local File entry. The header is followed by
+ * the file name and an extra field, then by the data stream.
+ */
+ struct LocalFile: public SignedEntity<LocalFile>
+ {
+ /* Signature for a Local File header */
+ static const uint32_t magic = 0x04034b50;
+
+ /**
+ * Returns the file name
+ */
+ StringBuf GetName() const
+ {
+ return StringBuf(reinterpret_cast<const char *>(this) + sizeof(*this),
+ filenameSize);
+ }
+
+ /**
+ * Returns a pointer to the data associated with this header
+ */
+ const void *GetData() const
+ {
+ return reinterpret_cast<const char *>(this) + sizeof(*this)
+ + filenameSize + extraFieldSize;
+ }
+
+ le_uint16 minVersion;
+ le_uint16 generalFlag;
+ le_uint16 compression;
+ le_uint16 lastModifiedTime;
+ le_uint16 lastModifiedDate;
+ le_uint32 CRC32;
+ le_uint32 compressedSize;
+ le_uint32 uncompressedSize;
+ le_uint16 filenameSize;
+ le_uint16 extraFieldSize;
+ };
+
+ /**
+ * In some cases, when a zip archive is created, compressed size and CRC
+ * are not known when writing the Local File header. In these cases, the
+ * 3rd bit of the general flag in the Local File header is set, and there
+ * is an additional header following the compressed data.
+ */
+ struct DataDescriptor: public SignedEntity<DataDescriptor>
+ {
+ /* Signature for a Data Descriptor header */
+ static const uint32_t magic = 0x08074b50;
+
+ le_uint32 CRC32;
+ le_uint32 compressedSize;
+ le_uint32 uncompressedSize;
+ };
+
+ /**
+ * Header used to describe a Central Directory Entry. The header is
+ * followed by the file name, an extra field, and a comment.
+ */
+ struct DirectoryEntry: public SignedEntity<DirectoryEntry>
+ {
+ /* Signature for a Central Directory Entry header */
+ static const uint32_t magic = 0x02014b50;
+
+ /**
+ * Returns the file name
+ */
+ StringBuf GetName() const
+ {
+ return StringBuf(reinterpret_cast<const char *>(this) + sizeof(*this),
+ filenameSize);
+ }
+
+ /**
+ * Returns the Central Directory Entry following this one.
+ */
+ const DirectoryEntry *GetNext() const
+ {
+ return validate(reinterpret_cast<const char *>(this) + sizeof(*this)
+ + filenameSize + extraFieldSize + fileCommentSize);
+ }
+
+ le_uint16 creatorVersion;
+ le_uint16 minVersion;
+ le_uint16 generalFlag;
+ le_uint16 compression;
+ le_uint16 lastModifiedTime;
+ le_uint16 lastModifiedDate;
+ le_uint32 CRC32;
+ le_uint32 compressedSize;
+ le_uint32 uncompressedSize;
+ le_uint16 filenameSize;
+ le_uint16 extraFieldSize;
+ le_uint16 fileCommentSize;
+ le_uint16 diskNum;
+ le_uint16 internalAttributes;
+ le_uint32 externalAttributes;
+ le_uint32 offset;
+ };
+
+ /**
+ * Header used to describe the End of Central Directory Record.
+ */
+ struct CentralDirectoryEnd: public SignedEntity<CentralDirectoryEnd>
+ {
+ /* Signature for the End of Central Directory Record */
+ static const uint32_t magic = 0x06054b50;
+
+ le_uint16 diskNum;
+ le_uint16 startDisk;
+ le_uint16 recordsOnDisk;
+ le_uint16 records;
+ le_uint32 size;
+ le_uint32 offset;
+ le_uint16 commentSize;
+ };
+#pragma pack()
+
+ /**
+ * Returns the first Directory entry
+ */
+ const DirectoryEntry *GetFirstEntry() const;
+
+ /* Pointer to the Local File Entry following the last one GetStream() used.
+ * This is used by GetStream to avoid scanning the Directory Entries when the
+ * requested entry is that one. */
+ mutable const LocalFile *nextFile;
+
+ /* Likewise for the next Directory entry */
+ mutable const DirectoryEntry *nextDir;
+
+ /* Pointer to the Directory entries */
+ mutable const DirectoryEntry *entries;
+
+ mutable pthread_mutex_t mutex;
+};
+
+/**
+ * Class for bookkeeping Zip instances
+ */
+class ZipCollection
+{
+public:
+ static ZipCollection Singleton;
+
+ /**
+ * Get a Zip instance for the given path. If there is an existing one
+ * already, return that one, otherwise create a new one.
+ */
+ static already_AddRefed<Zip> GetZip(const char *path);
+
+protected:
+ friend class Zip;
+ /**
+ * Register the given Zip instance. This method is meant to be called
+ * by Zip::Create.
+ */
+ static void Register(Zip *zip);
+
+ /**
+ * Forget about the given Zip instance. This method is meant to be called
+ * by the Zip destructor.
+ */
+ static void Forget(Zip *zip);
+
+private:
+ /* Zip instances bookkept in this collection */
+ std::vector<Zip *> zips;
+};
+
+#endif /* Zip_h */
diff --git a/mozglue/linker/dladdr.h b/mozglue/linker/dladdr.h
new file mode 100644
index 000000000..9a209547a
--- /dev/null
+++ b/mozglue/linker/dladdr.h
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include <dlfcn.h>
+
+#ifndef HAVE_DLADDR
+typedef struct {
+ const char *dli_fname;
+ void *dli_fbase;
+ const char *dli_sname;
+ void *dli_saddr;
+} Dl_info;
+extern int dladdr(void *addr, Dl_info *info) __attribute__((weak));
+#define HAVE_DLADDR 1
+#endif
diff --git a/mozglue/linker/moz.build b/mozglue/linker/moz.build
new file mode 100644
index 000000000..49f4d62f0
--- /dev/null
+++ b/mozglue/linker/moz.build
@@ -0,0 +1,54 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# 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/.
+
+SOURCES += [
+ 'BaseElf.cpp',
+ 'CustomElf.cpp',
+ 'ElfLoader.cpp',
+ 'Mappable.cpp',
+ 'SeekableZStream.cpp',
+ 'XZStream.cpp',
+ 'Zip.cpp',
+]
+
+Library('linker')
+
+HOST_SOURCES += [
+ 'SeekableZStream.cpp',
+ 'szip.cpp',
+]
+
+HostProgram('szip')
+
+FINAL_LIBRARY = 'mozglue'
+
+DEFINES['IMPL_MFBT'] = True
+
+DISABLE_STL_WRAPPING = True
+
+TEST_DIRS += ['tests']
+
+HOST_OS_LIBS += [
+ 'z',
+]
+
+if CONFIG['TARGET_CPU'] == 'arm':
+ if CONFIG['MOZ_THUMB2']:
+ HOST_DEFINES['TARGET_THUMB'] = True
+ else:
+ HOST_DEFINES['TARGET_ARM'] = True
+
+if CONFIG['CPU_ARCH'] == 'x86':
+ HOST_DEFINES['TARGET_X86'] = True
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
+
+DEFINES['XZ_USE_CRC64'] = 1
+
+USE_LIBS += [
+ 'xz-embedded',
+]
diff --git a/mozglue/linker/szip.cpp b/mozglue/linker/szip.cpp
new file mode 100644
index 000000000..bfc882fbe
--- /dev/null
+++ b/mozglue/linker/szip.cpp
@@ -0,0 +1,593 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include <algorithm>
+#include <map>
+#include <sys/stat.h>
+#include <string>
+#include <sstream>
+#include <cstring>
+#include <cstdlib>
+#include <zlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "mozilla/Assertions.h"
+#include "mozilla/Scoped.h"
+#include "mozilla/UniquePtr.h"
+#include "SeekableZStream.h"
+#include "Utils.h"
+#include "Logging.h"
+
+Logging Logging::Singleton;
+
+const char *filterName[] = {
+ "none",
+ "thumb",
+ "arm",
+ "x86",
+ "auto"
+};
+
+/* Maximum supported size for chunkSize */
+static const size_t maxChunkSize =
+ 1 << (8 * std::min(sizeof(((SeekableZStreamHeader *)nullptr)->chunkSize),
+ sizeof(((SeekableZStreamHeader *)nullptr)->lastChunkSize)) - 1);
+
+class Buffer: public MappedPtr
+{
+public:
+ virtual ~Buffer() { }
+
+ virtual bool Resize(size_t size)
+ {
+ MemoryRange buf = mmap(nullptr, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (buf == MAP_FAILED)
+ return false;
+ if (*this != MAP_FAILED)
+ memcpy(buf, *this, std::min(size, GetLength()));
+ Assign(buf);
+ return true;
+ }
+
+ bool Fill(Buffer &other)
+ {
+ size_t size = other.GetLength();
+ if (!size || !Resize(size))
+ return false;
+ memcpy(static_cast<void *>(*this), static_cast<void *>(other), size);
+ return true;
+ }
+};
+
+class FileBuffer: public Buffer
+{
+public:
+ bool Init(const char *name, bool writable_ = false)
+ {
+ fd = open(name, writable_ ? O_RDWR | O_CREAT | O_TRUNC : O_RDONLY, 0666);
+ if (fd == -1)
+ return false;
+ writable = writable_;
+ return true;
+ }
+
+ virtual bool Resize(size_t size)
+ {
+ if (writable) {
+ if (ftruncate(fd, size) == -1)
+ return false;
+ }
+ Assign(MemoryRange::mmap(nullptr, size,
+ PROT_READ | (writable ? PROT_WRITE : 0),
+ writable ? MAP_SHARED : MAP_PRIVATE, fd, 0));
+ return this != MAP_FAILED;
+ }
+
+ int getFd()
+ {
+ return fd;
+ }
+
+private:
+ AutoCloseFD fd;
+ bool writable;
+};
+
+class FilteredBuffer: public Buffer
+{
+public:
+ void Filter(Buffer &other, SeekableZStream::FilterId filter, size_t chunkSize)
+ {
+ SeekableZStream::ZStreamFilter filterCB =
+ SeekableZStream::GetFilter(filter);
+ MOZ_ASSERT(filterCB);
+ Fill(other);
+ size_t size = other.GetLength();
+ Bytef *data = reinterpret_cast<Bytef *>(static_cast<void *>(*this));
+ size_t avail = 0;
+ /* Filter needs to be applied in chunks. */
+ while (size) {
+ avail = std::min(size, chunkSize);
+ filterCB(data - static_cast<unsigned char *>(static_cast<void *>(*this)),
+ SeekableZStream::FILTER, data, avail);
+ size -= avail;
+ data += avail;
+ }
+ }
+};
+
+template <typename T>
+class Dictionary: public Buffer
+{
+ typedef T piece;
+ typedef std::pair<piece, int> stat_pair;
+
+ static bool stat_cmp(stat_pair a, stat_pair b)
+ {
+ return a.second < b.second;
+ }
+
+public:
+ Dictionary(Buffer &inBuf, size_t size)
+ {
+ if (!size || !Resize(size))
+ return;
+ DEBUG_LOG("Creating dictionary");
+ piece *origBufPieces = reinterpret_cast<piece *>(
+ static_cast<void *>(inBuf));
+ std::map<piece, int> stats;
+ for (unsigned int i = 0; i < inBuf.GetLength() / sizeof(piece); i++) {
+ stats[origBufPieces[i]]++;
+ }
+ std::vector<stat_pair> statsVec(stats.begin(), stats.end());
+ std::sort(statsVec.begin(), statsVec.end(), stat_cmp);
+
+ piece *dictPieces = reinterpret_cast<piece *>(
+ static_cast<void *>(*this));
+ typename std::vector<stat_pair>::reverse_iterator it = statsVec.rbegin();
+ for (int i = size / sizeof(piece); i > 0 && it < statsVec.rend();
+ i--, ++it) {
+ dictPieces[i - 1] = it->first;
+ }
+ }
+};
+
+class SzipAction
+{
+public:
+ virtual int run(const char *name, Buffer &origBuf,
+ const char *outName, Buffer &outBuf) = 0;
+
+ virtual ~SzipAction() {}
+};
+
+class SzipDecompress: public SzipAction
+{
+public:
+ int run(const char *name, Buffer &origBuf,
+ const char *outName, Buffer &outBuf);
+};
+
+
+class SzipCompress: public SzipAction
+{
+public:
+ int run(const char *name, Buffer &origBuf,
+ const char *outName, Buffer &outBuf);
+
+ SzipCompress(size_t aChunkSize, SeekableZStream::FilterId aFilter,
+ size_t aDictSize)
+ : chunkSize(aChunkSize ? aChunkSize : 16384)
+ , filter(aFilter)
+ , dictSize(aDictSize)
+ {}
+
+ const static signed char winSizeLog = 15;
+ const static size_t winSize = 1 << winSizeLog;
+
+ const static SeekableZStream::FilterId DEFAULT_FILTER =
+#if defined(TARGET_THUMB)
+ SeekableZStream::BCJ_THUMB;
+#elif defined(TARGET_ARM)
+ SeekableZStream::BCJ_ARM;
+#elif defined(TARGET_X86)
+ SeekableZStream::BCJ_X86;
+#else
+ SeekableZStream::NONE;
+#endif
+
+private:
+
+ int do_compress(Buffer &origBuf, Buffer &outBuf, const unsigned char *aDict,
+ size_t aDictSize, SeekableZStream::FilterId aFilter);
+
+ size_t chunkSize;
+ SeekableZStream::FilterId filter;
+ size_t dictSize;
+};
+
+/* Decompress a seekable compressed stream */
+int SzipDecompress::run(const char *name, Buffer &origBuf,
+ const char *outName, Buffer &outBuf)
+{
+ size_t origSize = origBuf.GetLength();
+ if (origSize < sizeof(SeekableZStreamHeader)) {
+ ERROR("%s is not compressed", name);
+ return 0;
+ }
+
+ SeekableZStream zstream;
+ if (!zstream.Init(origBuf, origSize))
+ return 0;
+
+ size_t size = zstream.GetUncompressedSize();
+
+ /* Give enough room for the uncompressed data */
+ if (!outBuf.Resize(size)) {
+ ERROR("Error resizing %s: %s", outName, strerror(errno));
+ return 1;
+ }
+
+ if (!zstream.Decompress(outBuf, 0, size))
+ return 1;
+
+ return 0;
+}
+
+/* Generate a seekable compressed stream. */
+int SzipCompress::run(const char *name, Buffer &origBuf,
+ const char *outName, Buffer &outBuf)
+{
+ size_t origSize = origBuf.GetLength();
+ if (origSize == 0) {
+ ERROR("Won't compress %s: it's empty", name);
+ return 1;
+ }
+ if (SeekableZStreamHeader::validate(origBuf)) {
+ WARN("Skipping %s: it's already a szip", name);
+ return 0;
+ }
+ bool compressed = false;
+ LOG("Size = %" PRIuSize, origSize);
+
+ /* Allocate a buffer the size of the uncompressed data: we don't want
+ * a compressed file larger than that anyways. */
+ if (!outBuf.Resize(origSize)) {
+ ERROR("Couldn't allocate output buffer: %s", strerror(errno));
+ return 1;
+ }
+
+ /* Find the most appropriate filter */
+ SeekableZStream::FilterId firstFilter, lastFilter;
+ bool scanFilters;
+ if (filter == SeekableZStream::FILTER_MAX) {
+ firstFilter = SeekableZStream::NONE;
+ lastFilter = SeekableZStream::FILTER_MAX;
+ scanFilters = true;
+ } else {
+ firstFilter = lastFilter = filter;
+ ++lastFilter;
+ scanFilters = false;
+ }
+
+ mozilla::UniquePtr<Buffer> filteredBuf;
+ Buffer *origData;
+ for (SeekableZStream::FilterId f = firstFilter; f < lastFilter; ++f) {
+ mozilla::UniquePtr<FilteredBuffer> filteredTmp;
+ Buffer tmpBuf;
+ if (f != SeekableZStream::NONE) {
+ DEBUG_LOG("Applying filter \"%s\"", filterName[f]);
+ filteredTmp = mozilla::MakeUnique<FilteredBuffer>();
+ filteredTmp->Filter(origBuf, f, chunkSize);
+ origData = filteredTmp.get();
+ } else {
+ origData = &origBuf;
+ }
+ if (dictSize && !scanFilters) {
+ filteredBuf = mozilla::Move(filteredTmp);
+ break;
+ }
+ DEBUG_LOG("Compressing with no dictionary");
+ if (do_compress(*origData, tmpBuf, nullptr, 0, f) == 0) {
+ if (tmpBuf.GetLength() < outBuf.GetLength()) {
+ outBuf.Fill(tmpBuf);
+ compressed = true;
+ filter = f;
+ filteredBuf = mozilla::Move(filteredTmp);
+ continue;
+ }
+ }
+ }
+
+ origData = filteredBuf ? filteredBuf.get() : &origBuf;
+
+ if (dictSize) {
+ Dictionary<uint64_t> dict(*origData, dictSize ? SzipCompress::winSize : 0);
+
+ /* Find the most appropriate dictionary size */
+ size_t firstDictSize, lastDictSize;
+ if (dictSize == (size_t) -1) {
+ /* If we scanned for filters, we effectively already tried dictSize=0 */
+ firstDictSize = scanFilters ? 4096 : 0;
+ lastDictSize = SzipCompress::winSize;
+ } else {
+ firstDictSize = lastDictSize = dictSize;
+ }
+
+ Buffer tmpBuf;
+ for (size_t d = firstDictSize; d <= lastDictSize; d += 4096) {
+ DEBUG_LOG("Compressing with dictionary of size %" PRIuSize, d);
+ if (do_compress(*origData, tmpBuf, static_cast<unsigned char *>(dict)
+ + SzipCompress::winSize - d, d, filter))
+ continue;
+ if (!compressed || tmpBuf.GetLength() < outBuf.GetLength()) {
+ outBuf.Fill(tmpBuf);
+ compressed = true;
+ dictSize = d;
+ }
+ }
+ }
+
+ if (!compressed) {
+ outBuf.Fill(origBuf);
+ LOG("Not compressed");
+ return 0;
+ }
+
+ if (dictSize == (size_t) -1)
+ dictSize = 0;
+
+ DEBUG_LOG("Used filter \"%s\" and dictionary size of %" PRIuSize,
+ filterName[filter], dictSize);
+ LOG("Compressed size is %" PRIuSize, outBuf.GetLength());
+
+ /* Sanity check */
+ Buffer tmpBuf;
+ SzipDecompress decompress;
+ if (decompress.run("buffer", outBuf, "buffer", tmpBuf))
+ return 1;
+
+ size_t size = tmpBuf.GetLength();
+ if (size != origSize) {
+ ERROR("Compression error: %" PRIuSize " != %" PRIuSize, size, origSize);
+ return 1;
+ }
+ if (memcmp(static_cast<void *>(origBuf), static_cast<void *>(tmpBuf), size)) {
+ ERROR("Compression error: content mismatch");
+ return 1;
+ }
+ return 0;
+}
+
+int SzipCompress::do_compress(Buffer &origBuf, Buffer &outBuf,
+ const unsigned char *aDict, size_t aDictSize,
+ SeekableZStream::FilterId aFilter)
+{
+ size_t origSize = origBuf.GetLength();
+ MOZ_ASSERT(origSize != 0);
+
+ /* Expected total number of chunks */
+ size_t nChunks = ((origSize + chunkSize - 1) / chunkSize);
+
+ /* The first chunk is going to be stored after the header, the dictionary
+ * and the offset table */
+ size_t offset = sizeof(SeekableZStreamHeader) + aDictSize
+ + nChunks * sizeof(uint32_t);
+
+ if (offset >= origSize)
+ return 1;
+
+ /* Allocate a buffer the size of the uncompressed data: we don't want
+ * a compressed file larger than that anyways. */
+ if (!outBuf.Resize(origSize)) {
+ ERROR("Couldn't allocate output buffer: %s", strerror(errno));
+ return 1;
+ }
+
+ SeekableZStreamHeader *header = new (outBuf) SeekableZStreamHeader;
+ unsigned char *dictionary = static_cast<unsigned char *>(
+ outBuf + sizeof(SeekableZStreamHeader));
+ le_uint32 *entry =
+ reinterpret_cast<le_uint32 *>(dictionary + aDictSize);
+
+ /* Initialize header */
+ header->chunkSize = chunkSize;
+ header->dictSize = aDictSize;
+ header->totalSize = offset;
+ header->windowBits = -SzipCompress::winSizeLog; // Raw stream,
+ // window size of 32k.
+ header->filter = aFilter;
+ if (aDictSize)
+ memcpy(dictionary, aDict, aDictSize);
+
+ /* Initialize zlib structure */
+ z_stream zStream;
+ memset(&zStream, 0, sizeof(zStream));
+ zStream.avail_out = origSize - offset;
+ zStream.next_out = static_cast<Bytef*>(outBuf) + offset;
+
+ size_t avail = 0;
+ size_t size = origSize;
+ unsigned char *data = reinterpret_cast<unsigned char *>(
+ static_cast<void *>(origBuf));
+ while (size) {
+ avail = std::min(size, chunkSize);
+
+ /* Compress chunk */
+ int ret = deflateInit2(&zStream, 9, Z_DEFLATED, header->windowBits,
+ MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+ if (aDictSize)
+ deflateSetDictionary(&zStream, dictionary, aDictSize);
+ MOZ_ASSERT(ret == Z_OK);
+ zStream.avail_in = avail;
+ zStream.next_in = data;
+ ret = deflate(&zStream, Z_FINISH);
+ /* Under normal conditions, deflate returns Z_STREAM_END. If there is not
+ * enough room to compress, deflate returns Z_OK and avail_out is 0. We
+ * still want to deflateEnd in that case, so fall through. It will bail
+ * on the avail_out test that follows. */
+ MOZ_ASSERT(ret == Z_STREAM_END || ret == Z_OK);
+ ret = deflateEnd(&zStream);
+ MOZ_ASSERT(ret == Z_OK);
+ if (zStream.avail_out <= 0)
+ return 1;
+
+ size_t len = origSize - offset - zStream.avail_out;
+
+ /* Adjust headers */
+ header->totalSize += len;
+ *entry++ = offset;
+ header->nChunks++;
+
+ /* Prepare for next iteration */
+ size -= avail;
+ data += avail;
+ offset += len;
+ }
+ header->lastChunkSize = avail;
+ MOZ_ASSERT(header->totalSize == offset);
+ MOZ_ASSERT(header->nChunks == nChunks);
+
+ if (!outBuf.Resize(offset)) {
+ ERROR("Error truncating output: %s", strerror(errno));
+ return 1;
+ }
+
+ return 0;
+
+}
+
+bool GetSize(const char *str, size_t *out)
+{
+ char *end;
+ MOZ_ASSERT(out);
+ errno = 0;
+ *out = strtol(str, &end, 10);
+ return (!errno && !*end);
+}
+
+int main(int argc, char* argv[])
+{
+ mozilla::UniquePtr<SzipAction> action;
+ char **firstArg;
+ bool compress = true;
+ size_t chunkSize = 0;
+ SeekableZStream::FilterId filter = SzipCompress::DEFAULT_FILTER;
+ size_t dictSize = (size_t) 0;
+
+ Logging::Init();
+
+ for (firstArg = &argv[1]; argc > 2; argc--, firstArg++) {
+ if (!firstArg[0] || firstArg[0][0] != '-')
+ break;
+ if (strcmp(firstArg[0], "-d") == 0) {
+ compress = false;
+ } else if (strcmp(firstArg[0], "-c") == 0) {
+ firstArg++;
+ argc--;
+ if (!firstArg[0])
+ break;
+ if (!GetSize(firstArg[0], &chunkSize) || !chunkSize ||
+ (chunkSize % 4096) || (chunkSize > maxChunkSize)) {
+ ERROR("Invalid chunk size");
+ return 1;
+ }
+ } else if (strcmp(firstArg[0], "-f") == 0) {
+ firstArg++;
+ argc--;
+ if (!firstArg[0])
+ break;
+ bool matched = false;
+ for (unsigned int i = 0; i < sizeof(filterName) / sizeof(char *); ++i) {
+ if (strcmp(firstArg[0], filterName[i]) == 0) {
+ filter = static_cast<SeekableZStream::FilterId>(i);
+ matched = true;
+ break;
+ }
+ }
+ if (!matched) {
+ ERROR("Invalid filter");
+ return 1;
+ }
+ } else if (strcmp(firstArg[0], "-D") == 0) {
+ firstArg++;
+ argc--;
+ if (!firstArg[0])
+ break;
+ if (strcmp(firstArg[0], "auto") == 0) {
+ dictSize = -1;
+ } else if (!GetSize(firstArg[0], &dictSize) || (dictSize >= 1 << 16)) {
+ ERROR("Invalid dictionary size");
+ return 1;
+ }
+ }
+ }
+
+ if (argc != 2 || !firstArg[0]) {
+ LOG("usage: %s [-d] [-c CHUNKSIZE] [-f FILTER] [-D DICTSIZE] file",
+ argv[0]);
+ return 1;
+ }
+
+ if (compress) {
+ action.reset(new SzipCompress(chunkSize, filter, dictSize));
+ } else {
+ if (chunkSize) {
+ ERROR("-c is incompatible with -d");
+ return 1;
+ }
+ if (dictSize) {
+ ERROR("-D is incompatible with -d");
+ return 1;
+ }
+ action.reset(new SzipDecompress());
+ }
+
+ std::stringstream tmpOutStream;
+ tmpOutStream << firstArg[0] << ".sz." << getpid();
+ std::string tmpOut(tmpOutStream.str());
+ int ret;
+ struct stat st;
+ {
+ FileBuffer origBuf;
+ if (!origBuf.Init(firstArg[0])) {
+ ERROR("Couldn't open %s: %s", firstArg[0], strerror(errno));
+ return 1;
+ }
+
+ ret = fstat(origBuf.getFd(), &st);
+ if (ret == -1) {
+ ERROR("Couldn't stat %s: %s", firstArg[0], strerror(errno));
+ return 1;
+ }
+
+ size_t origSize = st.st_size;
+
+ /* Mmap the original file */
+ if (!origBuf.Resize(origSize)) {
+ ERROR("Couldn't mmap %s: %s", firstArg[0], strerror(errno));
+ return 1;
+ }
+
+ /* Create the compressed file */
+ FileBuffer outBuf;
+ if (!outBuf.Init(tmpOut.c_str(), true)) {
+ ERROR("Couldn't open %s: %s", tmpOut.c_str(), strerror(errno));
+ return 1;
+ }
+
+ ret = action->run(firstArg[0], origBuf, tmpOut.c_str(), outBuf);
+ if ((ret == 0) && (fstat(outBuf.getFd(), &st) == -1)) {
+ st.st_size = 0;
+ }
+ }
+
+ if ((ret == 0) && st.st_size) {
+ rename(tmpOut.c_str(), firstArg[0]);
+ } else {
+ unlink(tmpOut.c_str());
+ }
+ return ret;
+}
diff --git a/mozglue/linker/tests/TestZip.cpp b/mozglue/linker/tests/TestZip.cpp
new file mode 100644
index 000000000..25873f5e5
--- /dev/null
+++ b/mozglue/linker/tests/TestZip.cpp
@@ -0,0 +1,69 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include <cstdio>
+#include <unistd.h>
+#include "Zip.h"
+#include "mozilla/RefPtr.h"
+
+extern "C" void report_mapping() { }
+extern "C" void delete_mapping() { }
+
+/**
+ * test.zip is a basic test zip file with a central directory. It contains
+ * four entries, in the following order:
+ * "foo", "bar", "baz", "qux".
+ * The entries are going to be read out of order.
+ */
+const char *test_entries[] = {
+ "baz", "foo", "bar", "qux"
+};
+
+/**
+ * no_central_dir.zip is a hand crafted test zip with no central directory
+ * entries. The Zip reader is expected to be able to traverse these entries
+ * if requested in order, without reading a central directory
+ * - First entry is a file "a", STOREd.
+ * - Second entry is a file "b", STOREd, using a data descriptor. CRC is
+ * unknown, but compressed and uncompressed sizes are known in the local
+ * file header.
+ * - Third entry is a file "c", DEFLATEd, using a data descriptor. CRC,
+ * compressed and uncompressed sizes are known in the local file header.
+ * This is the kind of entry that can be found in a zip that went through
+ * zipalign if it had a data descriptor originally.
+ * - Fourth entry is a file "d", STOREd.
+ */
+const char *no_central_dir_entries[] = {
+ "a", "b", "c", "d"
+};
+
+int main(int argc, char *argv[])
+{
+ if (argc != 2) {
+ fprintf(stderr, "TEST-FAIL | TestZip | Expecting the directory containing test Zips\n");
+ return 1;
+ }
+ chdir(argv[1]);
+ Zip::Stream s;
+ RefPtr<Zip> z = ZipCollection::GetZip("test.zip");
+ for (size_t i = 0; i < sizeof(test_entries) / sizeof(*test_entries); i++) {
+ if (!z->GetStream(test_entries[i], &s)) {
+ fprintf(stderr, "TEST-UNEXPECTED-FAIL | TestZip | test.zip: Couldn't get entry \"%s\"\n", test_entries[i]);
+ return 1;
+ }
+ }
+ fprintf(stderr, "TEST-PASS | TestZip | test.zip could be accessed fully\n");
+
+ z = ZipCollection::GetZip("no_central_dir.zip");
+ for (size_t i = 0; i < sizeof(no_central_dir_entries)
+ / sizeof(*no_central_dir_entries); i++) {
+ if (!z->GetStream(no_central_dir_entries[i], &s)) {
+ fprintf(stderr, "TEST-UNEXPECTED-FAIL | TestZip | no_central_dir.zip: Couldn't get entry \"%s\"\n", no_central_dir_entries[i]);
+ return 1;
+ }
+ }
+ fprintf(stderr, "TEST-PASS | TestZip | no_central_dir.zip could be accessed in order\n");
+
+ return 0;
+}
diff --git a/mozglue/linker/tests/moz.build b/mozglue/linker/tests/moz.build
new file mode 100644
index 000000000..3d4c1c616
--- /dev/null
+++ b/mozglue/linker/tests/moz.build
@@ -0,0 +1,23 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# 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/.
+
+DIST_INSTALL = False
+
+SimplePrograms([
+ 'TestZip',
+])
+LOCAL_INCLUDES += ['..']
+USE_LIBS += [
+ 'linker',
+ 'mfbt',
+]
+OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
+DISABLE_STL_WRAPPING = True
+
+PYTHON_UNIT_TESTS += ['run_test_zip.py']
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/mozglue/linker/tests/no_central_dir.zip b/mozglue/linker/tests/no_central_dir.zip
new file mode 100644
index 000000000..df882220d
--- /dev/null
+++ b/mozglue/linker/tests/no_central_dir.zip
Binary files differ
diff --git a/mozglue/linker/tests/run_test_zip.py b/mozglue/linker/tests/run_test_zip.py
new file mode 100644
index 000000000..e606e16d4
--- /dev/null
+++ b/mozglue/linker/tests/run_test_zip.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+#
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+import buildconfig
+import mozpack.path as mozpath
+import mozunit
+import subprocess
+import unittest
+
+class TestZip(unittest.TestCase):
+ def test_zip(self):
+ srcdir = mozpath.dirname(__file__)
+ relsrcdir = mozpath.relpath(srcdir, buildconfig.topsrcdir)
+ test_bin = mozpath.join(buildconfig.topobjdir, relsrcdir,
+ 'TestZip' + buildconfig.substs['BIN_SUFFIX'])
+ self.assertEqual(0, subprocess.call([test_bin, srcdir]))
+
+if __name__ == '__main__':
+ mozunit.main()
diff --git a/mozglue/linker/tests/test.zip b/mozglue/linker/tests/test.zip
new file mode 100644
index 000000000..657835b0c
--- /dev/null
+++ b/mozglue/linker/tests/test.zip
Binary files differ
diff --git a/mozglue/misc/StackWalk.cpp b/mozglue/misc/StackWalk.cpp
new file mode 100644
index 000000000..bb23b922a
--- /dev/null
+++ b/mozglue/misc/StackWalk.cpp
@@ -0,0 +1,1204 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+/* API for getting a stack trace of the C/C++ stack on the current thread */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/StackWalk.h"
+
+#include <string.h>
+
+using namespace mozilla;
+
+// The presence of this address is the stack must stop the stack walk. If
+// there is no such address, the structure will be {nullptr, true}.
+struct CriticalAddress
+{
+ void* mAddr;
+ bool mInit;
+};
+static CriticalAddress gCriticalAddress;
+
+// for _Unwind_Backtrace from libcxxrt or libunwind
+// cxxabi.h from libcxxrt implicitly includes unwind.h first
+#if defined(HAVE__UNWIND_BACKTRACE) && !defined(_GNU_SOURCE)
+#define _GNU_SOURCE
+#endif
+
+#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)))
+#define MOZ_STACKWALK_SUPPORTS_LINUX 1
+#else
+#define MOZ_STACKWALK_SUPPORTS_LINUX 0
+#endif
+
+#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
+#define HAVE___LIBC_STACK_END 1
+#else
+#define HAVE___LIBC_STACK_END 0
+#endif
+
+#if HAVE___LIBC_STACK_END
+extern MOZ_EXPORT void* __libc_stack_end; // from ld-linux.so
+#endif
+
+#ifdef ANDROID
+#include <algorithm>
+#include <unistd.h>
+#include <pthread.h>
+#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. 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
+
+#include <windows.h>
+#include <process.h>
+#include <stdio.h>
+#include <malloc.h>
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/StackWalk_windows.h"
+
+#include <imagehlp.h>
+// We need a way to know if we are building for WXP (or later), as if we are, we
+// need to use the newer 64-bit APIs. API_VERSION_NUMBER seems to fit the bill.
+// A value of 9 indicates we want to use the new APIs.
+#if API_VERSION_NUMBER < 9
+#error Too old imagehlp.h
+#endif
+
+struct WalkStackData
+{
+ // Are we walking the stack of the calling thread? Note that we need to avoid
+ // calling fprintf and friends if this is false, in order to avoid deadlocks.
+ bool walkCallingThread;
+ uint32_t skipFrames;
+ HANDLE thread;
+ HANDLE process;
+ HANDLE eventStart;
+ HANDLE eventEnd;
+ void** pcs;
+ uint32_t pc_size;
+ uint32_t pc_count;
+ uint32_t pc_max;
+ void** sps;
+ uint32_t sp_size;
+ uint32_t sp_count;
+ void* platformData;
+};
+
+DWORD gStackWalkThread;
+CRITICAL_SECTION gDbgHelpCS;
+
+// Routine to print an error message to standard error.
+static void
+PrintError(const char* aPrefix)
+{
+ LPSTR lpMsgBuf;
+ DWORD lastErr = GetLastError();
+ FormatMessageA(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr,
+ lastErr,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPSTR)&lpMsgBuf,
+ 0,
+ nullptr
+ );
+ fprintf(stderr, "### ERROR: %s: %s",
+ aPrefix, lpMsgBuf ? lpMsgBuf : "(null)\n");
+ fflush(stderr);
+ LocalFree(lpMsgBuf);
+}
+
+static unsigned int WINAPI WalkStackThread(void* aData);
+
+static bool
+EnsureWalkThreadReady()
+{
+ static bool walkThreadReady = false;
+ static HANDLE stackWalkThread = nullptr;
+ static HANDLE readyEvent = nullptr;
+
+ if (walkThreadReady) {
+ return walkThreadReady;
+ }
+
+ if (!stackWalkThread) {
+ readyEvent = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
+ FALSE /* initially non-signaled */,
+ nullptr);
+ if (!readyEvent) {
+ PrintError("CreateEvent");
+ return false;
+ }
+
+ unsigned int threadID;
+ stackWalkThread = (HANDLE)_beginthreadex(nullptr, 0, WalkStackThread,
+ (void*)readyEvent, 0, &threadID);
+ if (!stackWalkThread) {
+ PrintError("CreateThread");
+ ::CloseHandle(readyEvent);
+ readyEvent = nullptr;
+ return false;
+ }
+ gStackWalkThread = threadID;
+ ::CloseHandle(stackWalkThread);
+ }
+
+ MOZ_ASSERT((stackWalkThread && readyEvent) ||
+ (!stackWalkThread && !readyEvent));
+
+ // The thread was created. Try to wait an arbitrary amount of time (1 second
+ // should be enough) for its event loop to start before posting events to it.
+ DWORD waitRet = ::WaitForSingleObject(readyEvent, 1000);
+ if (waitRet == WAIT_TIMEOUT) {
+ // We get a timeout if we're called during static initialization because
+ // the thread will only start executing after we return so it couldn't
+ // have signalled the event. If that is the case, give up for now and
+ // try again next time we're called.
+ return false;
+ }
+ ::CloseHandle(readyEvent);
+ stackWalkThread = nullptr;
+ readyEvent = nullptr;
+
+
+ ::InitializeCriticalSection(&gDbgHelpCS);
+
+ return walkThreadReady = true;
+}
+
+static void
+WalkStackMain64(struct WalkStackData* aData)
+{
+ // Get a context for the specified thread.
+ CONTEXT context;
+ if (!aData->platformData) {
+ memset(&context, 0, sizeof(CONTEXT));
+ context.ContextFlags = CONTEXT_FULL;
+ if (!GetThreadContext(aData->thread, &context)) {
+ if (aData->walkCallingThread) {
+ PrintError("GetThreadContext");
+ }
+ return;
+ }
+ } else {
+ context = *static_cast<CONTEXT*>(aData->platformData);
+ }
+
+#if defined(_M_IX86) || defined(_M_IA64)
+ // Setup initial stack frame to walk from.
+ STACKFRAME64 frame64;
+ memset(&frame64, 0, sizeof(frame64));
+#ifdef _M_IX86
+ frame64.AddrPC.Offset = context.Eip;
+ frame64.AddrStack.Offset = context.Esp;
+ frame64.AddrFrame.Offset = context.Ebp;
+#elif defined _M_IA64
+ frame64.AddrPC.Offset = context.StIIP;
+ frame64.AddrStack.Offset = context.SP;
+ frame64.AddrFrame.Offset = context.RsBSP;
+#endif
+ frame64.AddrPC.Mode = AddrModeFlat;
+ frame64.AddrStack.Mode = AddrModeFlat;
+ frame64.AddrFrame.Mode = AddrModeFlat;
+ frame64.AddrReturn.Mode = AddrModeFlat;
+#endif
+
+ // Skip our own stack walking frames.
+ int skip = (aData->walkCallingThread ? 3 : 0) + aData->skipFrames;
+
+ // Now walk the stack.
+ while (true) {
+ DWORD64 addr;
+ DWORD64 spaddr;
+
+#if defined(_M_IX86) || defined(_M_IA64)
+ // 32-bit frame unwinding.
+ // Debug routines are not threadsafe, so grab the lock.
+ EnterCriticalSection(&gDbgHelpCS);
+ BOOL ok = StackWalk64(
+#if defined _M_IA64
+ IMAGE_FILE_MACHINE_IA64,
+#elif defined _M_IX86
+ IMAGE_FILE_MACHINE_I386,
+#endif
+ aData->process,
+ aData->thread,
+ &frame64,
+ &context,
+ nullptr,
+ SymFunctionTableAccess64, // function table access routine
+ SymGetModuleBase64, // module base routine
+ 0
+ );
+ LeaveCriticalSection(&gDbgHelpCS);
+
+ if (ok) {
+ addr = frame64.AddrPC.Offset;
+ spaddr = frame64.AddrStack.Offset;
+ } else {
+ addr = 0;
+ spaddr = 0;
+ if (aData->walkCallingThread) {
+ PrintError("WalkStack64");
+ }
+ }
+
+ if (!ok) {
+ break;
+ }
+
+#elif defined(_M_AMD64)
+ // 64-bit frame unwinding.
+ // Try to look up unwind metadata for the current function.
+ ULONG64 imageBase;
+ PRUNTIME_FUNCTION runtimeFunction =
+ RtlLookupFunctionEntry(context.Rip, &imageBase, NULL);
+
+ if (!runtimeFunction) {
+ // Alas, this is probably a JIT frame, for which we don't generate unwind
+ // info and so we have to give up.
+ break;
+ }
+
+ PVOID dummyHandlerData;
+ ULONG64 dummyEstablisherFrame;
+ RtlVirtualUnwind(UNW_FLAG_NHANDLER,
+ imageBase,
+ context.Rip,
+ runtimeFunction,
+ &context,
+ &dummyHandlerData,
+ &dummyEstablisherFrame,
+ nullptr);
+
+ addr = context.Rip;
+ spaddr = context.Rsp;
+
+#else
+#error "unknown platform"
+#endif
+
+ if (addr == 0) {
+ break;
+ }
+
+ if (skip-- > 0) {
+ continue;
+ }
+
+ if (aData->pc_count < aData->pc_size) {
+ aData->pcs[aData->pc_count] = (void*)addr;
+ }
+ ++aData->pc_count;
+
+ if (aData->sp_count < aData->sp_size) {
+ aData->sps[aData->sp_count] = (void*)spaddr;
+ }
+ ++aData->sp_count;
+
+ if (aData->pc_max != 0 && aData->pc_count == aData->pc_max) {
+ break;
+ }
+
+#if defined(_M_IX86) || defined(_M_IA64)
+ if (frame64.AddrReturn.Offset == 0) {
+ break;
+ }
+#endif
+ }
+}
+
+// The JIT needs to allocate executable memory. Because of the inanity of
+// the win64 APIs, this requires locks that stalk walkers also need. Provide
+// another lock to allow synchronization around these resources.
+#ifdef _M_AMD64
+
+struct CriticalSectionAutoInitializer {
+ CRITICAL_SECTION lock;
+
+ CriticalSectionAutoInitializer() {
+ InitializeCriticalSection(&lock);
+ }
+};
+
+static CriticalSectionAutoInitializer gWorkaroundLock;
+
+#endif // _M_AMD64
+
+MFBT_API void
+AcquireStackWalkWorkaroundLock()
+{
+#ifdef _M_AMD64
+ EnterCriticalSection(&gWorkaroundLock.lock);
+#endif
+}
+
+MFBT_API bool
+TryAcquireStackWalkWorkaroundLock()
+{
+#ifdef _M_AMD64
+ return TryEnterCriticalSection(&gWorkaroundLock.lock);
+#else
+ return true;
+#endif
+}
+
+MFBT_API void
+ReleaseStackWalkWorkaroundLock()
+{
+#ifdef _M_AMD64
+ LeaveCriticalSection(&gWorkaroundLock.lock);
+#endif
+}
+
+static unsigned int WINAPI
+WalkStackThread(void* aData)
+{
+ BOOL msgRet;
+ MSG msg;
+
+ // Call PeekMessage to force creation of a message queue so that
+ // other threads can safely post events to us.
+ ::PeekMessage(&msg, nullptr, WM_USER, WM_USER, PM_NOREMOVE);
+
+ // and tell the thread that created us that we're ready.
+ HANDLE readyEvent = (HANDLE)aData;
+ ::SetEvent(readyEvent);
+
+ while ((msgRet = ::GetMessage(&msg, (HWND)-1, 0, 0)) != 0) {
+ if (msgRet == -1) {
+ PrintError("GetMessage");
+ } else {
+ DWORD ret;
+
+ struct WalkStackData* data = (WalkStackData*)msg.lParam;
+ if (!data) {
+ continue;
+ }
+
+ // Don't suspend the calling thread until it's waiting for
+ // us; otherwise the number of frames on the stack could vary.
+ ret = ::WaitForSingleObject(data->eventStart, INFINITE);
+ if (ret != WAIT_OBJECT_0) {
+ PrintError("WaitForSingleObject");
+ }
+
+ // Suspend the calling thread, dump his stack, and then resume him.
+ // He's currently waiting for us to finish so now should be a good time.
+ ret = ::SuspendThread(data->thread);
+ if (ret == -1) {
+ PrintError("ThreadSuspend");
+ } else {
+ WalkStackMain64(data);
+
+ ret = ::ResumeThread(data->thread);
+ if (ret == -1) {
+ PrintError("ThreadResume");
+ }
+ }
+
+ ::SetEvent(data->eventEnd);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Walk the stack, translating PC's found into strings and recording the
+ * chain in aBuffer. For this to work properly, the DLLs must be rebased
+ * so that the address in the file agrees with the address in memory.
+ * Otherwise StackWalk will return FALSE when it hits a frame in a DLL
+ * whose in memory address doesn't match its in-file address.
+ */
+
+MFBT_API bool
+MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
+ uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
+ void* aPlatformData)
+{
+ StackWalkInitCriticalAddress();
+ static HANDLE myProcess = nullptr;
+ HANDLE myThread;
+ DWORD walkerReturn;
+ struct WalkStackData data;
+
+ if (!EnsureWalkThreadReady()) {
+ return false;
+ }
+
+ HANDLE currentThread = ::GetCurrentThread();
+ HANDLE targetThread =
+ aThread ? reinterpret_cast<HANDLE>(aThread) : currentThread;
+ data.walkCallingThread = (targetThread == currentThread);
+
+ // Have to duplicate handle to get a real handle.
+ if (!myProcess) {
+ if (!::DuplicateHandle(::GetCurrentProcess(),
+ ::GetCurrentProcess(),
+ ::GetCurrentProcess(),
+ &myProcess,
+ PROCESS_ALL_ACCESS, FALSE, 0)) {
+ if (data.walkCallingThread) {
+ PrintError("DuplicateHandle (process)");
+ }
+ return false;
+ }
+ }
+ if (!::DuplicateHandle(::GetCurrentProcess(),
+ targetThread,
+ ::GetCurrentProcess(),
+ &myThread,
+ THREAD_ALL_ACCESS, FALSE, 0)) {
+ if (data.walkCallingThread) {
+ PrintError("DuplicateHandle (thread)");
+ }
+ return false;
+ }
+
+ data.skipFrames = aSkipFrames;
+ data.thread = myThread;
+ data.process = myProcess;
+ void* local_pcs[1024];
+ data.pcs = local_pcs;
+ data.pc_count = 0;
+ data.pc_size = ArrayLength(local_pcs);
+ data.pc_max = aMaxFrames;
+ void* local_sps[1024];
+ data.sps = local_sps;
+ data.sp_count = 0;
+ data.sp_size = ArrayLength(local_sps);
+ data.platformData = aPlatformData;
+
+ if (aThread) {
+ // If we're walking the stack of another thread, we don't need to
+ // use a separate walker thread.
+ WalkStackMain64(&data);
+
+ if (data.pc_count > data.pc_size) {
+ data.pcs = (void**)_alloca(data.pc_count * sizeof(void*));
+ data.pc_size = data.pc_count;
+ data.pc_count = 0;
+ data.sps = (void**)_alloca(data.sp_count * sizeof(void*));
+ data.sp_size = data.sp_count;
+ data.sp_count = 0;
+ WalkStackMain64(&data);
+ }
+ } else {
+ data.eventStart = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
+ FALSE /* initially non-signaled */, nullptr);
+ data.eventEnd = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
+ FALSE /* initially non-signaled */, nullptr);
+
+ ::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data);
+
+ walkerReturn = ::SignalObjectAndWait(data.eventStart,
+ data.eventEnd, INFINITE, FALSE);
+ if (walkerReturn != WAIT_OBJECT_0 && data.walkCallingThread) {
+ PrintError("SignalObjectAndWait (1)");
+ }
+ if (data.pc_count > data.pc_size) {
+ data.pcs = (void**)_alloca(data.pc_count * sizeof(void*));
+ data.pc_size = data.pc_count;
+ data.pc_count = 0;
+ data.sps = (void**)_alloca(data.sp_count * sizeof(void*));
+ data.sp_size = data.sp_count;
+ data.sp_count = 0;
+ ::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data);
+ walkerReturn = ::SignalObjectAndWait(data.eventStart,
+ data.eventEnd, INFINITE, FALSE);
+ if (walkerReturn != WAIT_OBJECT_0 && data.walkCallingThread) {
+ PrintError("SignalObjectAndWait (2)");
+ }
+ }
+
+ ::CloseHandle(data.eventStart);
+ ::CloseHandle(data.eventEnd);
+ }
+
+ ::CloseHandle(myThread);
+
+ for (uint32_t i = 0; i < data.pc_count; ++i) {
+ (*aCallback)(i + 1, data.pcs[i], data.sps[i], aClosure);
+ }
+
+ return data.pc_count != 0;
+}
+
+
+static BOOL CALLBACK
+callbackEspecial64(
+ PCSTR aModuleName,
+ DWORD64 aModuleBase,
+ ULONG aModuleSize,
+ PVOID aUserContext)
+{
+ BOOL retval = TRUE;
+ DWORD64 addr = *(DWORD64*)aUserContext;
+
+ /*
+ * You'll want to control this if we are running on an
+ * architecture where the addresses go the other direction.
+ * Not sure this is even a realistic consideration.
+ */
+ const BOOL addressIncreases = TRUE;
+
+ /*
+ * If it falls in side the known range, load the symbols.
+ */
+ if (addressIncreases
+ ? (addr >= aModuleBase && addr <= (aModuleBase + aModuleSize))
+ : (addr <= aModuleBase && addr >= (aModuleBase - aModuleSize))
+ ) {
+ retval = !!SymLoadModule64(GetCurrentProcess(), nullptr,
+ (PSTR)aModuleName, nullptr,
+ aModuleBase, aModuleSize);
+ if (!retval) {
+ PrintError("SymLoadModule64");
+ }
+ }
+
+ return retval;
+}
+
+/*
+ * SymGetModuleInfoEspecial
+ *
+ * Attempt to determine the module information.
+ * Bug 112196 says this DLL may not have been loaded at the time
+ * SymInitialize was called, and thus the module information
+ * and symbol information is not available.
+ * This code rectifies that problem.
+ */
+
+// New members were added to IMAGEHLP_MODULE64 (that show up in the
+// Platform SDK that ships with VC8, but not the Platform SDK that ships
+// with VC7.1, i.e., between DbgHelp 6.0 and 6.1), but we don't need to
+// use them, and it's useful to be able to function correctly with the
+// older library. (Stock Windows XP SP2 seems to ship with dbghelp.dll
+// version 5.1.) Since Platform SDK version need not correspond to
+// compiler version, and the version number in debughlp.h was NOT bumped
+// when these changes were made, ifdef based on a constant that was
+// added between these versions.
+#ifdef SSRVOPT_SETCONTEXT
+#define NS_IMAGEHLP_MODULE64_SIZE (((offsetof(IMAGEHLP_MODULE64, LoadedPdbName) + sizeof(DWORD64) - 1) / sizeof(DWORD64)) * sizeof(DWORD64))
+#else
+#define NS_IMAGEHLP_MODULE64_SIZE sizeof(IMAGEHLP_MODULE64)
+#endif
+
+BOOL SymGetModuleInfoEspecial64(HANDLE aProcess, DWORD64 aAddr,
+ PIMAGEHLP_MODULE64 aModuleInfo,
+ PIMAGEHLP_LINE64 aLineInfo)
+{
+ BOOL retval = FALSE;
+
+ /*
+ * Init the vars if we have em.
+ */
+ aModuleInfo->SizeOfStruct = NS_IMAGEHLP_MODULE64_SIZE;
+ if (aLineInfo) {
+ aLineInfo->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ }
+
+ /*
+ * Give it a go.
+ * It may already be loaded.
+ */
+ retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo);
+ if (retval == FALSE) {
+ /*
+ * Not loaded, here's the magic.
+ * Go through all the modules.
+ */
+ // Need to cast to PENUMLOADED_MODULES_CALLBACK64 because the
+ // constness of the first parameter of
+ // PENUMLOADED_MODULES_CALLBACK64 varies over SDK versions (from
+ // non-const to const over time). See bug 391848 and bug
+ // 415426.
+ BOOL enumRes = EnumerateLoadedModules64(
+ aProcess,
+ (PENUMLOADED_MODULES_CALLBACK64)callbackEspecial64,
+ (PVOID)&aAddr);
+ if (enumRes != FALSE) {
+ /*
+ * One final go.
+ * If it fails, then well, we have other problems.
+ */
+ retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo);
+ }
+ }
+
+ /*
+ * If we got module info, we may attempt line info as well.
+ * We will not report failure if this does not work.
+ */
+ if (retval != FALSE && aLineInfo) {
+ DWORD displacement = 0;
+ BOOL lineRes = FALSE;
+ lineRes = SymGetLineFromAddr64(aProcess, aAddr, &displacement, aLineInfo);
+ if (!lineRes) {
+ // Clear out aLineInfo to indicate that it's not valid
+ memset(aLineInfo, 0, sizeof(*aLineInfo));
+ }
+ }
+
+ return retval;
+}
+
+static bool
+EnsureSymInitialized()
+{
+ static bool gInitialized = false;
+ bool retStat;
+
+ if (gInitialized) {
+ return gInitialized;
+ }
+
+ if (!EnsureWalkThreadReady()) {
+ return false;
+ }
+
+ SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
+ retStat = SymInitialize(GetCurrentProcess(), nullptr, TRUE);
+ if (!retStat) {
+ PrintError("SymInitialize");
+ }
+
+ gInitialized = retStat;
+ /* XXX At some point we need to arrange to call SymCleanup */
+
+ return retStat;
+}
+
+
+MFBT_API bool
+MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails)
+{
+ aDetails->library[0] = '\0';
+ aDetails->loffset = 0;
+ aDetails->filename[0] = '\0';
+ aDetails->lineno = 0;
+ aDetails->function[0] = '\0';
+ aDetails->foffset = 0;
+
+ if (!EnsureSymInitialized()) {
+ return false;
+ }
+
+ HANDLE myProcess = ::GetCurrentProcess();
+ BOOL ok;
+
+ // debug routines are not threadsafe, so grab the lock.
+ EnterCriticalSection(&gDbgHelpCS);
+
+ //
+ // Attempt to load module info before we attempt to resolve the symbol.
+ // This just makes sure we get good info if available.
+ //
+
+ DWORD64 addr = (DWORD64)aPC;
+ IMAGEHLP_MODULE64 modInfo;
+ IMAGEHLP_LINE64 lineInfo;
+ BOOL modInfoRes;
+ modInfoRes = SymGetModuleInfoEspecial64(myProcess, addr, &modInfo, &lineInfo);
+
+ if (modInfoRes) {
+ strncpy(aDetails->library, modInfo.LoadedImageName,
+ sizeof(aDetails->library));
+ aDetails->library[mozilla::ArrayLength(aDetails->library) - 1] = '\0';
+ aDetails->loffset = (char*)aPC - (char*)modInfo.BaseOfImage;
+
+ if (lineInfo.FileName) {
+ strncpy(aDetails->filename, lineInfo.FileName,
+ sizeof(aDetails->filename));
+ aDetails->filename[mozilla::ArrayLength(aDetails->filename) - 1] = '\0';
+ aDetails->lineno = lineInfo.LineNumber;
+ }
+ }
+
+ ULONG64 buffer[(sizeof(SYMBOL_INFO) +
+ MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
+ PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
+ pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+ pSymbol->MaxNameLen = MAX_SYM_NAME;
+
+ DWORD64 displacement;
+ ok = SymFromAddr(myProcess, addr, &displacement, pSymbol);
+
+ if (ok) {
+ strncpy(aDetails->function, pSymbol->Name,
+ sizeof(aDetails->function));
+ aDetails->function[mozilla::ArrayLength(aDetails->function) - 1] = '\0';
+ aDetails->foffset = static_cast<ptrdiff_t>(displacement);
+ }
+
+ LeaveCriticalSection(&gDbgHelpCS); // release our lock
+ return true;
+}
+
+// i386 or PPC Linux stackwalking code
+#elif HAVE_DLADDR && (HAVE__UNWIND_BACKTRACE || MOZ_STACKWALK_SUPPORTS_LINUX || MOZ_STACKWALK_SUPPORTS_MACOSX)
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+// On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
+// if __USE_GNU is defined. I suppose its some kind of standards
+// adherence thing.
+//
+#if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
+#define __USE_GNU
+#endif
+
+// This thing is exported by libstdc++
+// Yes, this is a gcc only hack
+#if defined(MOZ_DEMANGLE_SYMBOLS)
+#include <cxxabi.h>
+#endif // MOZ_DEMANGLE_SYMBOLS
+
+void DemangleSymbol(const char* aSymbol,
+ char* aBuffer,
+ int aBufLen)
+{
+ aBuffer[0] = '\0';
+
+#if defined(MOZ_DEMANGLE_SYMBOLS)
+ /* See demangle.h in the gcc source for the voodoo */
+ char* demangled = abi::__cxa_demangle(aSymbol, 0, 0, 0);
+
+ if (demangled) {
+ strncpy(aBuffer, demangled, aBufLen);
+ aBuffer[aBufLen - 1] = '\0';
+ free(demangled);
+ }
+#endif // MOZ_DEMANGLE_SYMBOLS
+}
+
+// {x86, ppc} x {Linux, Mac} stackwalking code.
+#if ((defined(__i386) || defined(PPC) || defined(__ppc__)) && \
+ (MOZ_STACKWALK_SUPPORTS_MACOSX || MOZ_STACKWALK_SUPPORTS_LINUX))
+
+MFBT_API bool
+MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
+ uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
+ void* aPlatformData)
+{
+ MOZ_ASSERT(!aThread);
+ MOZ_ASSERT(!aPlatformData);
+ StackWalkInitCriticalAddress();
+
+ // Get the frame pointer
+ void** bp = (void**)__builtin_frame_address(0);
+
+ void* stackEnd;
+#if HAVE___LIBC_STACK_END
+ stackEnd = __libc_stack_end;
+#elif defined(XP_DARWIN)
+ stackEnd = pthread_get_stackaddr_np(pthread_self());
+#elif defined(ANDROID)
+ pthread_attr_t sattr;
+ pthread_attr_init(&sattr);
+ pthread_getattr_np(pthread_self(), &sattr);
+ void* stackBase = stackEnd = nullptr;
+ size_t stackSize = 0;
+ if (gettid() != getpid()) {
+ // bionic's pthread_attr_getstack doesn't tell the truth for the main
+ // thread (see bug 846670). So don't use it for the main thread.
+ if (!pthread_attr_getstack(&sattr, &stackBase, &stackSize)) {
+ stackEnd = static_cast<char*>(stackBase) + stackSize;
+ } else {
+ stackEnd = nullptr;
+ }
+ }
+ if (!stackEnd) {
+ // So consider the current frame pointer + an arbitrary size of 8MB
+ // (modulo overflow ; not really arbitrary as it's the default stack
+ // size for the main thread) if pthread_attr_getstack failed for
+ // some reason (or was skipped).
+ static const uintptr_t kMaxStackSize = 8 * 1024 * 1024;
+ uintptr_t maxStackStart = uintptr_t(-1) - kMaxStackSize;
+ uintptr_t stackStart = std::max(maxStackStart, uintptr_t(bp));
+ stackEnd = reinterpret_cast<void*>(stackStart + kMaxStackSize);
+ }
+#else
+# error Unsupported configuration
+#endif
+ return FramePointerStackWalk(aCallback, aSkipFrames, aMaxFrames,
+ aClosure, bp, stackEnd);
+}
+
+#elif defined(HAVE__UNWIND_BACKTRACE)
+
+// libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0
+#include <unwind.h>
+
+struct unwind_info
+{
+ MozWalkStackCallback callback;
+ int skip;
+ int maxFrames;
+ int numFrames;
+ bool isCriticalAbort;
+ void* closure;
+};
+
+static _Unwind_Reason_Code
+unwind_callback(struct _Unwind_Context* context, void* closure)
+{
+ unwind_info* info = static_cast<unwind_info*>(closure);
+ void* pc = reinterpret_cast<void*>(_Unwind_GetIP(context));
+ // TODO Use something like '_Unwind_GetGR()' to get the stack pointer.
+ if (IsCriticalAddress(pc)) {
+ info->isCriticalAbort = true;
+ // We just want to stop the walk, so any error code will do. Using
+ // _URC_NORMAL_STOP would probably be the most accurate, but it is not
+ // defined on Android for ARM.
+ return _URC_FOREIGN_EXCEPTION_CAUGHT;
+ }
+ if (--info->skip < 0) {
+ info->numFrames++;
+ (*info->callback)(info->numFrames, pc, nullptr, info->closure);
+ if (info->maxFrames != 0 && info->numFrames == info->maxFrames) {
+ // Again, any error code that stops the walk will do.
+ return _URC_FOREIGN_EXCEPTION_CAUGHT;
+ }
+ }
+ return _URC_NO_REASON;
+}
+
+MFBT_API bool
+MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
+ uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
+ void* aPlatformData)
+{
+ MOZ_ASSERT(!aThread);
+ MOZ_ASSERT(!aPlatformData);
+ StackWalkInitCriticalAddress();
+ unwind_info info;
+ info.callback = aCallback;
+ info.skip = aSkipFrames + 1;
+ info.maxFrames = aMaxFrames;
+ info.numFrames = 0;
+ info.isCriticalAbort = false;
+ info.closure = aClosure;
+
+ (void)_Unwind_Backtrace(unwind_callback, &info);
+
+ // We ignore the return value from _Unwind_Backtrace and instead determine
+ // the outcome from |info|. There are two main reasons for this:
+ // - On ARM/Android bionic's _Unwind_Backtrace usually (always?) returns
+ // _URC_FAILURE. See
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=717853#c110.
+ // - If aMaxFrames != 0, we want to stop early, and the only way to do that
+ // is to make unwind_callback return something other than _URC_NO_REASON,
+ // which causes _Unwind_Backtrace to return a non-success code.
+ if (info.isCriticalAbort) {
+ return false;
+ }
+ return info.numFrames != 0;
+}
+
+#endif
+
+bool MFBT_API
+MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails)
+{
+ aDetails->library[0] = '\0';
+ aDetails->loffset = 0;
+ aDetails->filename[0] = '\0';
+ aDetails->lineno = 0;
+ aDetails->function[0] = '\0';
+ aDetails->foffset = 0;
+
+ Dl_info info;
+ int ok = dladdr(aPC, &info);
+ if (!ok) {
+ return true;
+ }
+
+ strncpy(aDetails->library, info.dli_fname, sizeof(aDetails->library));
+ aDetails->library[mozilla::ArrayLength(aDetails->library) - 1] = '\0';
+ aDetails->loffset = (char*)aPC - (char*)info.dli_fbase;
+
+ const char* symbol = info.dli_sname;
+ if (!symbol || symbol[0] == '\0') {
+ return true;
+ }
+
+ DemangleSymbol(symbol, aDetails->function, sizeof(aDetails->function));
+
+ if (aDetails->function[0] == '\0') {
+ // Just use the mangled symbol if demangling failed.
+ strncpy(aDetails->function, symbol, sizeof(aDetails->function));
+ aDetails->function[mozilla::ArrayLength(aDetails->function) - 1] = '\0';
+ }
+
+ aDetails->foffset = (char*)aPC - (char*)info.dli_saddr;
+ return true;
+}
+
+#else // unsupported platform.
+
+MFBT_API bool
+MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
+ uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
+ void* aPlatformData)
+{
+ MOZ_ASSERT(!aThread);
+ MOZ_ASSERT(!aPlatformData);
+ return false;
+}
+
+MFBT_API bool
+MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails)
+{
+ aDetails->library[0] = '\0';
+ aDetails->loffset = 0;
+ aDetails->filename[0] = '\0';
+ aDetails->lineno = 0;
+ aDetails->function[0] = '\0';
+ aDetails->foffset = 0;
+ return false;
+}
+
+#endif
+
+#if defined(XP_WIN) || defined (XP_MACOSX) || defined (XP_LINUX)
+namespace mozilla {
+bool
+FramePointerStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
+ uint32_t aMaxFrames, void* aClosure, void** bp,
+ void* aStackEnd)
+{
+ // Stack walking code courtesy Kipp's "leaky".
+
+ int32_t skip = aSkipFrames;
+ uint32_t numFrames = 0;
+ while (bp) {
+ void** next = (void**)*bp;
+ // bp may not be a frame pointer on i386 if code was compiled with
+ // -fomit-frame-pointer, so do some sanity checks.
+ // (bp should be a frame pointer on ppc(64) but checking anyway may help
+ // a little if the stack has been corrupted.)
+ // We don't need to check against the begining of the stack because
+ // we can assume that bp > sp
+ if (next <= bp ||
+ next > aStackEnd ||
+ (uintptr_t(next) & 3)) {
+ break;
+ }
+#if (defined(__ppc__) && defined(XP_MACOSX)) || defined(__powerpc64__)
+ // ppc mac or powerpc64 linux
+ void* pc = *(bp + 2);
+ bp += 3;
+#else // i386 or powerpc32 linux
+ void* pc = *(bp + 1);
+ bp += 2;
+#endif
+ if (IsCriticalAddress(pc)) {
+ return false;
+ }
+ if (--skip < 0) {
+ // Assume that the SP points to the BP of the function
+ // it called. We can't know the exact location of the SP
+ // but this should be sufficient for our use the SP
+ // to order elements on the stack.
+ numFrames++;
+ (*aCallback)(numFrames, pc, bp, aClosure);
+ if (aMaxFrames != 0 && numFrames == aMaxFrames) {
+ break;
+ }
+ }
+ bp = next;
+ }
+ return numFrames != 0;
+}
+} // namespace mozilla
+
+#else
+
+namespace mozilla {
+MFBT_API bool
+FramePointerStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
+ void* aClosure, void** aBp)
+{
+ return false;
+}
+}
+
+#endif
+
+MFBT_API void
+MozFormatCodeAddressDetails(char* aBuffer, uint32_t aBufferSize,
+ uint32_t aFrameNumber, void* aPC,
+ const MozCodeAddressDetails* aDetails)
+{
+ MozFormatCodeAddress(aBuffer, aBufferSize,
+ aFrameNumber, aPC, aDetails->function,
+ aDetails->library, aDetails->loffset,
+ aDetails->filename, aDetails->lineno);
+}
+
+MFBT_API void
+MozFormatCodeAddress(char* aBuffer, uint32_t aBufferSize, uint32_t aFrameNumber,
+ const void* aPC, const char* aFunction,
+ const char* aLibrary, ptrdiff_t aLOffset,
+ const char* aFileName, uint32_t aLineNo)
+{
+ const char* function = aFunction && aFunction[0] ? aFunction : "???";
+ if (aFileName && aFileName[0]) {
+ // We have a filename and (presumably) a line number. Use them.
+ snprintf(aBuffer, aBufferSize,
+ "#%02u: %s (%s:%u)",
+ aFrameNumber, function, aFileName, aLineNo);
+ } else if (aLibrary && aLibrary[0]) {
+ // We have no filename, but we do have a library name. Use it and the
+ // library offset, and print them in a way that scripts like
+ // fix_{linux,macosx}_stacks.py can easily post-process.
+ snprintf(aBuffer, aBufferSize,
+ "#%02u: %s[%s +0x%" PRIxPTR "]",
+ aFrameNumber, function, aLibrary, static_cast<uintptr_t>(aLOffset));
+ } else {
+ // We have nothing useful to go on. (The format string is split because
+ // '??)' is a trigraph and causes a warning, sigh.)
+ snprintf(aBuffer, aBufferSize,
+ "#%02u: ??? (???:???" ")",
+ aFrameNumber);
+ }
+}
diff --git a/mozglue/misc/StackWalk.h b/mozglue/misc/StackWalk.h
new file mode 100644
index 000000000..534c0bd82
--- /dev/null
+++ b/mozglue/misc/StackWalk.h
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+/* API for getting a stack trace of the C/C++ stack on the current thread */
+
+#ifndef mozilla_StackWalk_h
+#define mozilla_StackWalk_h
+
+/* WARNING: This file is intended to be included from C or C++ files. */
+
+#include "mozilla/Types.h"
+#include <stdint.h>
+
+/**
+ * The callback for MozStackWalk.
+ *
+ * @param aFrameNumber The frame number (starts at 1, not 0).
+ * @param aPC The program counter value.
+ * @param aSP The best approximation possible of what the stack
+ * pointer will be pointing to when the execution returns
+ * to executing that at aPC. If no approximation can
+ * be made it will be nullptr.
+ * @param aClosure Extra data passed in via MozStackWalk().
+ */
+typedef void
+(*MozWalkStackCallback)(uint32_t aFrameNumber, void* aPC, void* aSP,
+ void* aClosure);
+
+/**
+ * Call aCallback for the C/C++ stack frames on the current thread, from
+ * the caller of MozStackWalk to main (or above).
+ *
+ * @param aCallback Callback function, called once per frame.
+ * @param aSkipFrames Number of initial frames to skip. 0 means that
+ * the first callback will be for the caller of
+ * MozStackWalk.
+ * @param aMaxFrames Maximum number of frames to trace. 0 means no limit.
+ * @param aClosure Caller-supplied data passed through to aCallback.
+ * @param aThread The thread for which the stack is to be retrieved.
+ * Passing null causes us to walk the stack of the
+ * current thread. On Windows, this is a thread HANDLE.
+ * It is currently not supported on any other platform.
+ * @param aPlatformData Platform specific data that can help in walking the
+ * stack, this should be nullptr unless you really know
+ * what you're doing! This needs to be a pointer to a
+ * CONTEXT on Windows and should not be passed on other
+ * platforms.
+ *
+ * May skip some stack frames due to compiler optimizations or code
+ * generation.
+ *
+ */
+MFBT_API bool
+MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
+ uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
+ void* aPlatformData);
+
+typedef struct
+{
+ /*
+ * The name of the shared library or executable containing an
+ * address and the address's offset within that library, or empty
+ * string and zero if unknown.
+ */
+ char library[256];
+ ptrdiff_t loffset;
+ /*
+ * The name of the file name and line number of the code
+ * corresponding to the address, or empty string and zero if
+ * unknown.
+ */
+ char filename[256];
+ unsigned long lineno;
+ /*
+ * The name of the function containing an address and the address's
+ * offset within that function, or empty string and zero if unknown.
+ */
+ char function[256];
+ ptrdiff_t foffset;
+} MozCodeAddressDetails;
+
+/**
+ * For a given pointer to code, fill in the pieces of information used
+ * when printing a stack trace.
+ *
+ * @param aPC The code address.
+ * @param aDetails A structure to be filled in with the result.
+ */
+MFBT_API bool
+MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails);
+
+/**
+ * Format the information about a code address in a format suitable for
+ * stack traces on the current platform. When available, this string
+ * should contain the function name, source file, and line number. When
+ * these are not available, library and offset should be reported, if
+ * possible.
+ *
+ * Note that this output is parsed by several scripts including the fix*.py and
+ * make-tree.pl scripts in tools/rb/. It should only be change with care, and
+ * in conjunction with those scripts.
+ *
+ * @param aBuffer A string to be filled in with the description.
+ * The string will always be null-terminated.
+ * @param aBufferSize The size, in bytes, of aBuffer, including
+ * room for the terminating null. If the information
+ * to be printed would be larger than aBuffer, it
+ * will be truncated so that aBuffer[aBufferSize-1]
+ * is the terminating null.
+ * @param aFrameNumber The frame number.
+ * @param aPC The code address.
+ * @param aFunction The function name. Possibly null or the empty string.
+ * @param aLibrary The library name. Possibly null or the empty string.
+ * @param aLOffset The library offset.
+ * @param aFileName The filename. Possibly null or the empty string.
+ * @param aLineNo The line number. Possibly zero.
+ */
+MFBT_API void
+MozFormatCodeAddress(char* aBuffer, uint32_t aBufferSize, uint32_t aFrameNumber,
+ const void* aPC, const char* aFunction,
+ const char* aLibrary, ptrdiff_t aLOffset,
+ const char* aFileName, uint32_t aLineNo);
+
+/**
+ * Format the information about a code address in the same fashion as
+ * MozFormatCodeAddress.
+ *
+ * @param aBuffer A string to be filled in with the description.
+ * The string will always be null-terminated.
+ * @param aBufferSize The size, in bytes, of aBuffer, including
+ * room for the terminating null. If the information
+ * to be printed would be larger than aBuffer, it
+ * will be truncated so that aBuffer[aBufferSize-1]
+ * is the terminating null.
+ * @param aFrameNumber The frame number.
+ * @param aPC The code address.
+ * @param aDetails The value filled in by MozDescribeCodeAddress(aPC).
+ */
+MFBT_API void
+MozFormatCodeAddressDetails(char* aBuffer, uint32_t aBufferSize,
+ uint32_t aFrameNumber, void* aPC,
+ const MozCodeAddressDetails* aDetails);
+
+namespace mozilla {
+
+MFBT_API bool
+FramePointerStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
+ uint32_t aMaxFrames, void* aClosure, void** aBp,
+ void* aStackEnd);
+
+} // namespace mozilla
+
+/**
+ * Initialize the critical sections for this platform so that we can
+ * abort stack walks when needed.
+ */
+MFBT_API void
+StackWalkInitCriticalAddress(void);
+
+#endif
diff --git a/mozglue/misc/StackWalk_windows.h b/mozglue/misc/StackWalk_windows.h
new file mode 100644
index 000000000..5ffdd2068
--- /dev/null
+++ b/mozglue/misc/StackWalk_windows.h
@@ -0,0 +1,21 @@
+#ifndef mozilla_StackWalk_windows_h
+#define mozilla_StackWalk_windows_h
+
+#include "mozilla/Types.h"
+
+/**
+ * Allow stack walkers to work around the egregious win64 dynamic lookup table
+ * list API by locking around SuspendThread to avoid deadlock.
+ *
+ * See comment in StackWalk.cpp
+ */
+MFBT_API void
+AcquireStackWalkWorkaroundLock();
+
+MFBT_API bool
+TryAcquireStackWalkWorkaroundLock();
+
+MFBT_API void
+ReleaseStackWalkWorkaroundLock();
+
+#endif // mozilla_StackWalk_windows_h
diff --git a/mozglue/misc/TimeStamp.cpp b/mozglue/misc/TimeStamp.cpp
new file mode 100644
index 000000000..932b75c99
--- /dev/null
+++ b/mozglue/misc/TimeStamp.cpp
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+/*
+ * Implementation of the OS-independent methods of the TimeStamp class
+ */
+
+#include "mozilla/TimeStamp.h"
+#include <stdio.h>
+#include <string.h>
+
+namespace mozilla {
+
+/**
+ * Wrapper class used to initialize static data used by the TimeStamp class
+ */
+struct TimeStampInitialization
+{
+ /**
+ * First timestamp taken when the class static initializers are run. This
+ * timestamp is used to sanitize timestamps coming from different sources.
+ */
+ TimeStamp mFirstTimeStamp;
+
+ /**
+ * Timestamp representing the time when the process was created. This field
+ * is populated lazily the first time this information is required and is
+ * replaced every time the process is restarted.
+ */
+ TimeStamp mProcessCreation;
+
+ TimeStampInitialization()
+ {
+ TimeStamp::Startup();
+ mFirstTimeStamp = TimeStamp::Now();
+ };
+
+ ~TimeStampInitialization()
+ {
+ TimeStamp::Shutdown();
+ };
+};
+
+static TimeStampInitialization sInitOnce;
+
+MFBT_API TimeStamp
+TimeStamp::ProcessCreation(bool& aIsInconsistent)
+{
+ aIsInconsistent = false;
+
+ if (sInitOnce.mProcessCreation.IsNull()) {
+ char* mozAppRestart = getenv("MOZ_APP_RESTART");
+ TimeStamp ts;
+
+ /* When calling PR_SetEnv() with an empty value the existing variable may
+ * be unset or set to the empty string depending on the underlying platform
+ * thus we have to check if the variable is present and not empty. */
+ if (mozAppRestart && (strcmp(mozAppRestart, "") != 0)) {
+ /* Firefox was restarted, use the first time-stamp we've taken as the new
+ * process startup time. */
+ ts = sInitOnce.mFirstTimeStamp;
+ } else {
+ TimeStamp now = Now();
+ uint64_t uptime = ComputeProcessUptime();
+
+ ts = now - TimeDuration::FromMicroseconds(uptime);
+
+ if ((ts > sInitOnce.mFirstTimeStamp) || (uptime == 0)) {
+ /* If the process creation timestamp was inconsistent replace it with
+ * the first one instead and notify that a telemetry error was
+ * detected. */
+ aIsInconsistent = true;
+ ts = sInitOnce.mFirstTimeStamp;
+ }
+ }
+
+ sInitOnce.mProcessCreation = ts;
+ }
+
+ return sInitOnce.mProcessCreation;
+}
+
+void
+TimeStamp::RecordProcessRestart()
+{
+ sInitOnce.mProcessCreation = TimeStamp();
+}
+
+} // namespace mozilla
diff --git a/mozglue/misc/TimeStamp.h b/mozglue/misc/TimeStamp.h
new file mode 100644
index 000000000..a1a0eb360
--- /dev/null
+++ b/mozglue/misc/TimeStamp.h
@@ -0,0 +1,609 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef mozilla_TimeStamp_h
+#define mozilla_TimeStamp_h
+
+#include <stdint.h>
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/TypeTraits.h"
+#include "mozilla/Types.h"
+
+namespace IPC {
+template<typename T> struct ParamTraits;
+} // namespace IPC
+
+#ifdef XP_WIN
+// defines TimeStampValue as a complex value keeping both
+// GetTickCount and QueryPerformanceCounter values
+#include "TimeStamp_windows.h"
+#endif
+
+namespace mozilla {
+
+#ifndef XP_WIN
+typedef uint64_t TimeStampValue;
+#endif
+
+class TimeStamp;
+
+/**
+ * Platform-specific implementation details of BaseTimeDuration.
+ */
+class BaseTimeDurationPlatformUtils
+{
+public:
+ static MFBT_API double ToSeconds(int64_t aTicks);
+ static MFBT_API double ToSecondsSigDigits(int64_t aTicks);
+ static MFBT_API int64_t TicksFromMilliseconds(double aMilliseconds);
+ static MFBT_API int64_t ResolutionInTicks();
+};
+
+/**
+ * Instances of this class represent the length of an interval of time.
+ * Negative durations are allowed, meaning the end is before the start.
+ *
+ * Internally the duration is stored as a int64_t in units of
+ * PR_TicksPerSecond() when building with NSPR interval timers, or a
+ * system-dependent unit when building with system clocks. The
+ * system-dependent unit must be constant, otherwise the semantics of
+ * this class would be broken.
+ *
+ * The ValueCalculator template parameter determines how arithmetic
+ * operations are performed on the integer count of ticks (mValue).
+ */
+template <typename ValueCalculator>
+class BaseTimeDuration
+{
+public:
+ // The default duration is 0.
+ constexpr BaseTimeDuration() : mValue(0) {}
+ // Allow construction using '0' as the initial value, for readability,
+ // but no other numbers (so we don't have any implicit unit conversions).
+ struct _SomethingVeryRandomHere;
+ MOZ_IMPLICIT BaseTimeDuration(_SomethingVeryRandomHere* aZero) : mValue(0)
+ {
+ MOZ_ASSERT(!aZero, "Who's playing funny games here?");
+ }
+ // Default copy-constructor and assignment are OK
+
+ // Converting copy-constructor and assignment operator
+ template <typename E>
+ explicit BaseTimeDuration(const BaseTimeDuration<E>& aOther)
+ : mValue(aOther.mValue)
+ { }
+
+ template <typename E>
+ BaseTimeDuration& operator=(const BaseTimeDuration<E>& aOther)
+ {
+ mValue = aOther.mValue;
+ return *this;
+ }
+
+ double ToSeconds() const
+ {
+ if (mValue == INT64_MAX) {
+ return PositiveInfinity<double>();
+ }
+ if (mValue == INT64_MIN) {
+ return NegativeInfinity<double>();
+ }
+ return BaseTimeDurationPlatformUtils::ToSeconds(mValue);
+ }
+ // Return a duration value that includes digits of time we think to
+ // be significant. This method should be used when displaying a
+ // time to humans.
+ double ToSecondsSigDigits() const
+ {
+ if (mValue == INT64_MAX) {
+ return PositiveInfinity<double>();
+ }
+ if (mValue == INT64_MIN) {
+ return NegativeInfinity<double>();
+ }
+ return BaseTimeDurationPlatformUtils::ToSecondsSigDigits(mValue);
+ }
+ double ToMilliseconds() const { return ToSeconds() * 1000.0; }
+ double ToMicroseconds() const { return ToMilliseconds() * 1000.0; }
+
+ // Using a double here is safe enough; with 53 bits we can represent
+ // durations up to over 280,000 years exactly. If the units of
+ // mValue do not allow us to represent durations of that length,
+ // long durations are clamped to the max/min representable value
+ // instead of overflowing.
+ static inline BaseTimeDuration FromSeconds(double aSeconds)
+ {
+ return FromMilliseconds(aSeconds * 1000.0);
+ }
+ static BaseTimeDuration FromMilliseconds(double aMilliseconds)
+ {
+ if (aMilliseconds == PositiveInfinity<double>()) {
+ return Forever();
+ }
+ if (aMilliseconds == NegativeInfinity<double>()) {
+ return FromTicks(INT64_MIN);
+ }
+ return FromTicks(
+ BaseTimeDurationPlatformUtils::TicksFromMilliseconds(aMilliseconds));
+ }
+ static inline BaseTimeDuration FromMicroseconds(double aMicroseconds)
+ {
+ return FromMilliseconds(aMicroseconds / 1000.0);
+ }
+
+ static BaseTimeDuration Forever()
+ {
+ return FromTicks(INT64_MAX);
+ }
+
+ BaseTimeDuration operator+(const BaseTimeDuration& aOther) const
+ {
+ return FromTicks(ValueCalculator::Add(mValue, aOther.mValue));
+ }
+ BaseTimeDuration operator-(const BaseTimeDuration& aOther) const
+ {
+ return FromTicks(ValueCalculator::Subtract(mValue, aOther.mValue));
+ }
+ BaseTimeDuration& operator+=(const BaseTimeDuration& aOther)
+ {
+ mValue = ValueCalculator::Add(mValue, aOther.mValue);
+ return *this;
+ }
+ BaseTimeDuration& operator-=(const BaseTimeDuration& aOther)
+ {
+ mValue = ValueCalculator::Subtract(mValue, aOther.mValue);
+ return *this;
+ }
+ BaseTimeDuration operator-() const
+ {
+ // We don't just use FromTicks(ValueCalculator::Subtract(0, mValue))
+ // since that won't give the correct result for -TimeDuration::Forever().
+ int64_t ticks;
+ if (mValue == INT64_MAX) {
+ ticks = INT64_MIN;
+ } else if (mValue == INT64_MIN) {
+ ticks = INT64_MAX;
+ } else {
+ ticks = -mValue;
+ }
+
+ return FromTicks(ticks);
+ }
+
+private:
+ // Block double multiplier (slower, imprecise if long duration) - Bug 853398.
+ // If required, use MultDouble explicitly and with care.
+ BaseTimeDuration operator*(const double aMultiplier) const = delete;
+
+ // Block double divisor (for the same reason, and because dividing by
+ // fractional values would otherwise invoke the int64_t variant, and rounding
+ // the passed argument can then cause divide-by-zero) - Bug 1147491.
+ BaseTimeDuration operator/(const double aDivisor) const = delete;
+
+public:
+ BaseTimeDuration MultDouble(double aMultiplier) const
+ {
+ return FromTicks(ValueCalculator::Multiply(mValue, aMultiplier));
+ }
+ BaseTimeDuration operator*(const int32_t aMultiplier) const
+ {
+ return FromTicks(ValueCalculator::Multiply(mValue, aMultiplier));
+ }
+ BaseTimeDuration operator*(const uint32_t aMultiplier) const
+ {
+ return FromTicks(ValueCalculator::Multiply(mValue, aMultiplier));
+ }
+ BaseTimeDuration operator*(const int64_t aMultiplier) const
+ {
+ return FromTicks(ValueCalculator::Multiply(mValue, aMultiplier));
+ }
+ BaseTimeDuration operator*(const uint64_t aMultiplier) const
+ {
+ if (aMultiplier > INT64_MAX) {
+ return Forever();
+ }
+ return FromTicks(ValueCalculator::Multiply(mValue, aMultiplier));
+ }
+ BaseTimeDuration operator/(const int64_t aDivisor) const
+ {
+ MOZ_ASSERT(aDivisor != 0, "Division by zero");
+ return FromTicks(ValueCalculator::Divide(mValue, aDivisor));
+ }
+ double operator/(const BaseTimeDuration& aOther) const
+ {
+#ifndef MOZ_B2G
+ // Bug 1066388 - This fails on B2G ICS Emulator
+ MOZ_ASSERT(aOther.mValue != 0, "Division by zero");
+#endif
+ return ValueCalculator::DivideDouble(mValue, aOther.mValue);
+ }
+ BaseTimeDuration operator%(const BaseTimeDuration& aOther) const
+ {
+ MOZ_ASSERT(aOther.mValue != 0, "Division by zero");
+ return FromTicks(ValueCalculator::Modulo(mValue, aOther.mValue));
+ }
+
+ template<typename E>
+ bool operator<(const BaseTimeDuration<E>& aOther) const
+ {
+ return mValue < aOther.mValue;
+ }
+ template<typename E>
+ bool operator<=(const BaseTimeDuration<E>& aOther) const
+ {
+ return mValue <= aOther.mValue;
+ }
+ template<typename E>
+ bool operator>=(const BaseTimeDuration<E>& aOther) const
+ {
+ return mValue >= aOther.mValue;
+ }
+ template<typename E>
+ bool operator>(const BaseTimeDuration<E>& aOther) const
+ {
+ return mValue > aOther.mValue;
+ }
+ template<typename E>
+ bool operator==(const BaseTimeDuration<E>& aOther) const
+ {
+ return mValue == aOther.mValue;
+ }
+ template<typename E>
+ bool operator!=(const BaseTimeDuration<E>& aOther) const
+ {
+ return mValue != aOther.mValue;
+ }
+ bool IsZero() const
+ {
+ return mValue == 0;
+ }
+ explicit operator bool() const
+ {
+ return mValue != 0;
+ }
+
+ // Return a best guess at the system's current timing resolution,
+ // which might be variable. BaseTimeDurations below this order of
+ // magnitude are meaningless, and those at the same order of
+ // magnitude or just above are suspect.
+ static BaseTimeDuration Resolution() {
+ return FromTicks(BaseTimeDurationPlatformUtils::ResolutionInTicks());
+ }
+
+ // We could define additional operators here:
+ // -- convert to/from other time units
+ // -- scale duration by a float
+ // but let's do that on demand.
+ // Comparing durations for equality will only lead to bugs on
+ // platforms with high-resolution timers.
+
+private:
+ friend class TimeStamp;
+ friend struct IPC::ParamTraits<mozilla::BaseTimeDuration<ValueCalculator>>;
+ template <typename>
+ friend class BaseTimeDuration;
+
+ static BaseTimeDuration FromTicks(int64_t aTicks)
+ {
+ BaseTimeDuration t;
+ t.mValue = aTicks;
+ return t;
+ }
+
+ static BaseTimeDuration FromTicks(double aTicks)
+ {
+ // NOTE: this MUST be a >= test, because int64_t(double(INT64_MAX))
+ // overflows and gives INT64_MIN.
+ if (aTicks >= double(INT64_MAX)) {
+ return FromTicks(INT64_MAX);
+ }
+
+ // This MUST be a <= test.
+ if (aTicks <= double(INT64_MIN)) {
+ return FromTicks(INT64_MIN);
+ }
+
+ return FromTicks(int64_t(aTicks));
+ }
+
+ // Duration, result is implementation-specific difference of two TimeStamps
+ int64_t mValue;
+};
+
+/**
+ * Perform arithmetic operations on the value of a BaseTimeDuration without
+ * doing strict checks on the range of values.
+ */
+class TimeDurationValueCalculator
+{
+public:
+ static int64_t Add(int64_t aA, int64_t aB) { return aA + aB; }
+ static int64_t Subtract(int64_t aA, int64_t aB) { return aA - aB; }
+
+ template <typename T>
+ static int64_t Multiply(int64_t aA, T aB)
+ {
+ static_assert(IsIntegral<T>::value,
+ "Using integer multiplication routine with non-integer type."
+ " Further specialization required");
+ return aA * static_cast<int64_t>(aB);
+ }
+
+ static int64_t Divide(int64_t aA, int64_t aB) { return aA / aB; }
+ static double DivideDouble(int64_t aA, int64_t aB)
+ {
+ return static_cast<double>(aA) / aB;
+ }
+ static int64_t Modulo(int64_t aA, int64_t aB) { return aA % aB; }
+};
+
+template <>
+inline int64_t
+TimeDurationValueCalculator::Multiply<double>(int64_t aA, double aB)
+{
+ return static_cast<int64_t>(aA * aB);
+}
+
+/**
+ * Specialization of BaseTimeDuration that uses TimeDurationValueCalculator for
+ * arithmetic on the mValue member.
+ *
+ * Use this class for time durations that are *not* expected to hold values of
+ * Forever (or the negative equivalent) or when such time duration are *not*
+ * expected to be used in arithmetic operations.
+ */
+typedef BaseTimeDuration<TimeDurationValueCalculator> TimeDuration;
+
+/**
+ * Instances of this class represent moments in time, or a special
+ * "null" moment. We do not use the non-monotonic system clock or
+ * local time, since they can be reset, causing apparent backward
+ * travel in time, which can confuse algorithms. Instead we measure
+ * elapsed time according to the system. This time can never go
+ * backwards (i.e. it never wraps around, at least not in less than
+ * five million years of system elapsed time). It might not advance
+ * while the system is sleeping. If TimeStamp::SetNow() is not called
+ * at all for hours or days, we might not notice the passage of some
+ * of that time.
+ *
+ * We deliberately do not expose a way to convert TimeStamps to some
+ * particular unit. All you can do is compute a difference between two
+ * TimeStamps to get a TimeDuration. You can also add a TimeDuration
+ * to a TimeStamp to get a new TimeStamp. You can't do something
+ * meaningless like add two TimeStamps.
+ *
+ * Internally this is implemented as either a wrapper around
+ * - high-resolution, monotonic, system clocks if they exist on this
+ * platform
+ * - PRIntervalTime otherwise. We detect wraparounds of
+ * PRIntervalTime and work around them.
+ *
+ * This class is similar to C++11's time_point, however it is
+ * explicitly nullable and provides an IsNull() method. time_point
+ * is initialized to the clock's epoch and provides a
+ * time_since_epoch() method that functions similiarly. i.e.
+ * t.IsNull() is equivalent to t.time_since_epoch() == decltype(t)::duration::zero();
+ */
+class TimeStamp
+{
+public:
+ /**
+ * Initialize to the "null" moment
+ */
+ constexpr TimeStamp() : mValue(0) {}
+ // Default copy-constructor and assignment are OK
+
+ /**
+ * The system timestamps are the same as the TimeStamp
+ * 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: B2G and OS X
+ * False on Windows 7
+ * UNTESTED ON OTHER PLATFORMS
+ */
+#if defined(MOZ_WIDGET_GONK) || 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
+ */
+ bool IsNull() const { return mValue == 0; }
+
+ /**
+ * Return true if this is not the "null" moment, may be used in tests, e.g.:
+ * |if (timestamp) { ... }|
+ */
+ explicit operator bool() const
+ {
+ return mValue != 0;
+ }
+
+ /**
+ * Return a timestamp reflecting the current elapsed system time. This
+ * is monotonically increasing (i.e., does not decrease) over the
+ * lifetime of this process' XPCOM session.
+ *
+ * Now() is trying to ensure the best possible precision on each platform,
+ * at least one millisecond.
+ *
+ * NowLoRes() has been introduced to workaround performance problems of
+ * QueryPerformanceCounter on the Windows platform. NowLoRes() is giving
+ * lower precision, usually 15.6 ms, but with very good performance benefit.
+ * Use it for measurements of longer times, like >200ms timeouts.
+ */
+ static TimeStamp Now() { return Now(true); }
+ static TimeStamp NowLoRes() { return Now(false); }
+
+ /**
+ * Return a timestamp representing the time when the current process was
+ * created which will be comparable with other timestamps taken with this
+ * class. If the actual process creation time is detected to be inconsistent
+ * the @a aIsInconsistent parameter will be set to true, the returned
+ * timestamp however will still be valid though inaccurate.
+ *
+ * @param aIsInconsistent Set to true if an inconsistency was detected in the
+ * process creation time
+ * @returns A timestamp representing the time when the process was created,
+ * this timestamp is always valid even when errors are reported
+ */
+ static MFBT_API TimeStamp ProcessCreation(bool& aIsInconsistent);
+
+ /**
+ * Records a process restart. After this call ProcessCreation() will return
+ * the time when the browser was restarted instead of the actual time when
+ * the process was created.
+ */
+ static MFBT_API void RecordProcessRestart();
+
+ /**
+ * Compute the difference between two timestamps. Both must be non-null.
+ */
+ TimeDuration operator-(const TimeStamp& aOther) const
+ {
+ MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+ MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
+ static_assert(-INT64_MAX > INT64_MIN, "int64_t sanity check");
+ int64_t ticks = int64_t(mValue - aOther.mValue);
+ // Check for overflow.
+ if (mValue > aOther.mValue) {
+ if (ticks < 0) {
+ ticks = INT64_MAX;
+ }
+ } else {
+ if (ticks > 0) {
+ ticks = INT64_MIN;
+ }
+ }
+ return TimeDuration::FromTicks(ticks);
+ }
+
+ TimeStamp operator+(const TimeDuration& aOther) const
+ {
+ TimeStamp result = *this;
+ result += aOther;
+ return result;
+ }
+ TimeStamp operator-(const TimeDuration& aOther) const
+ {
+ TimeStamp result = *this;
+ result -= aOther;
+ return result;
+ }
+ TimeStamp& operator+=(const TimeDuration& aOther)
+ {
+ MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+ TimeStampValue value = mValue + aOther.mValue;
+ // Check for underflow.
+ // (We don't check for overflow because it's not obvious what the error
+ // behavior should be in that case.)
+ if (aOther.mValue < 0 && value > mValue) {
+ value = 0;
+ }
+ mValue = value;
+ return *this;
+ }
+ TimeStamp& operator-=(const TimeDuration& aOther)
+ {
+ MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+ TimeStampValue value = mValue - aOther.mValue;
+ // Check for underflow.
+ // (We don't check for overflow because it's not obvious what the error
+ // behavior should be in that case.)
+ if (aOther.mValue > 0 && value > mValue) {
+ value = 0;
+ }
+ mValue = value;
+ return *this;
+ }
+
+ bool operator<(const TimeStamp& aOther) const
+ {
+ MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+ MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
+ return mValue < aOther.mValue;
+ }
+ bool operator<=(const TimeStamp& aOther) const
+ {
+ MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+ MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
+ return mValue <= aOther.mValue;
+ }
+ bool operator>=(const TimeStamp& aOther) const
+ {
+ MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+ MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
+ return mValue >= aOther.mValue;
+ }
+ bool operator>(const TimeStamp& aOther) const
+ {
+ MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+ MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
+ return mValue > aOther.mValue;
+ }
+ bool operator==(const TimeStamp& aOther) const
+ {
+ return IsNull()
+ ? aOther.IsNull()
+ : !aOther.IsNull() && mValue == aOther.mValue;
+ }
+ bool operator!=(const TimeStamp& aOther) const
+ {
+ return !(*this == aOther);
+ }
+
+ // Comparing TimeStamps for equality should be discouraged. Adding
+ // two TimeStamps, or scaling TimeStamps, is nonsense and must never
+ // be allowed.
+
+ static MFBT_API void Startup();
+ static MFBT_API void Shutdown();
+
+private:
+ friend struct IPC::ParamTraits<mozilla::TimeStamp>;
+ friend void StartupTimelineRecordExternal(int, uint64_t);
+
+ MOZ_IMPLICIT TimeStamp(TimeStampValue aValue) : mValue(aValue) {}
+
+ static MFBT_API TimeStamp Now(bool aHighResolution);
+
+ /**
+ * Computes the uptime of the current process in microseconds. The result
+ * is platform-dependent and needs to be checked against existing timestamps
+ * for consistency.
+ *
+ * @returns The number of microseconds since the calling process was started
+ * or 0 if an error was encountered while computing the uptime
+ */
+ static MFBT_API uint64_t ComputeProcessUptime();
+
+ /**
+ * When built with PRIntervalTime, a value of 0 means this instance
+ * is "null". Otherwise, the low 32 bits represent a PRIntervalTime,
+ * and the high 32 bits represent a counter of the number of
+ * rollovers of PRIntervalTime that we've seen. This counter starts
+ * at 1 to avoid a real time colliding with the "null" value.
+ *
+ * PR_INTERVAL_MAX is set at 100,000 ticks per second. So the minimum
+ * time to wrap around is about 2^64/100000 seconds, i.e. about
+ * 5,849,424 years.
+ *
+ * When using a system clock, a value is system dependent.
+ */
+ TimeStampValue mValue;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_TimeStamp_h */
diff --git a/mozglue/misc/TimeStamp_darwin.cpp b/mozglue/misc/TimeStamp_darwin.cpp
new file mode 100644
index 000000000..f30bc9846
--- /dev/null
+++ b/mozglue/misc/TimeStamp_darwin.cpp
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+//
+// Implement TimeStamp::Now() with mach_absolute_time
+//
+// The "tick" unit for mach_absolute_time is defined using mach_timebase_info() which
+// gives a conversion ratio to nanoseconds. For more information see Apple's QA1398.
+//
+// This code is inspired by Chromium's time_mac.cc. The biggest
+// differences are that we explicitly initialize using
+// TimeStamp::Initialize() instead of lazily in Now() and that
+// we store the time value in ticks and convert when needed instead
+// of storing the time value in nanoseconds.
+
+#include <mach/mach_time.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "mozilla/TimeStamp.h"
+
+// Estimate of the smallest duration of time we can measure.
+static uint64_t sResolution;
+static uint64_t sResolutionSigDigs;
+
+static const uint64_t kNsPerMs = 1000000;
+static const uint64_t kUsPerSec = 1000000;
+static const double kNsPerMsd = 1000000.0;
+static const double kNsPerSecd = 1000000000.0;
+
+static bool gInitialized = false;
+static double sNsPerTick;
+
+static uint64_t
+ClockTime()
+{
+ // mach_absolute_time is it when it comes to ticks on the Mac. Other calls
+ // with less precision (such as TickCount) just call through to
+ // mach_absolute_time.
+ //
+ // At the time of writing mach_absolute_time returns the number of nanoseconds
+ // since boot. This won't overflow 64bits for 500+ years so we aren't going
+ // to worry about that possiblity
+ return mach_absolute_time();
+}
+
+static uint64_t
+ClockResolutionNs()
+{
+ uint64_t start = ClockTime();
+ uint64_t end = ClockTime();
+ uint64_t minres = (end - start);
+
+ // 10 total trials is arbitrary: what we're trying to avoid by
+ // looping is getting unlucky and being interrupted by a context
+ // switch or signal, or being bitten by paging/cache effects
+ for (int i = 0; i < 9; ++i) {
+ start = ClockTime();
+ end = ClockTime();
+
+ uint64_t candidate = (start - end);
+ if (candidate < minres) {
+ minres = candidate;
+ }
+ }
+
+ if (0 == minres) {
+ // measurable resolution is either incredibly low, ~1ns, or very
+ // high. fall back on NSPR's resolution assumption
+ minres = 1 * kNsPerMs;
+ }
+
+ return minres;
+}
+
+namespace mozilla {
+
+double
+BaseTimeDurationPlatformUtils::ToSeconds(int64_t aTicks)
+{
+ MOZ_ASSERT(gInitialized, "calling TimeDuration too early");
+ return (aTicks * sNsPerTick) / kNsPerSecd;
+}
+
+double
+BaseTimeDurationPlatformUtils::ToSecondsSigDigits(int64_t aTicks)
+{
+ MOZ_ASSERT(gInitialized, "calling TimeDuration too early");
+ // don't report a value < mResolution ...
+ int64_t valueSigDigs = sResolution * (aTicks / sResolution);
+ // and chop off insignificant digits
+ valueSigDigs = sResolutionSigDigs * (valueSigDigs / sResolutionSigDigs);
+ return (valueSigDigs * sNsPerTick) / kNsPerSecd;
+}
+
+int64_t
+BaseTimeDurationPlatformUtils::TicksFromMilliseconds(double aMilliseconds)
+{
+ MOZ_ASSERT(gInitialized, "calling TimeDuration too early");
+ double result = (aMilliseconds * kNsPerMsd) / sNsPerTick;
+ if (result > INT64_MAX) {
+ return INT64_MAX;
+ } else if (result < INT64_MIN) {
+ return INT64_MIN;
+ }
+
+ return result;
+}
+
+int64_t
+BaseTimeDurationPlatformUtils::ResolutionInTicks()
+{
+ MOZ_ASSERT(gInitialized, "calling TimeDuration too early");
+ return static_cast<int64_t>(sResolution);
+}
+
+void
+TimeStamp::Startup()
+{
+ if (gInitialized) {
+ return;
+ }
+
+ mach_timebase_info_data_t timebaseInfo;
+ // Apple's QA1398 suggests that the output from mach_timebase_info
+ // will not change while a program is running, so it should be safe
+ // to cache the result.
+ kern_return_t kr = mach_timebase_info(&timebaseInfo);
+ if (kr != KERN_SUCCESS) {
+ MOZ_RELEASE_ASSERT(false, "mach_timebase_info failed");
+ }
+
+ sNsPerTick = double(timebaseInfo.numer) / timebaseInfo.denom;
+
+ sResolution = ClockResolutionNs();
+
+ // find the number of significant digits in sResolution, for the
+ // sake of ToSecondsSigDigits()
+ for (sResolutionSigDigs = 1;
+ !(sResolutionSigDigs == sResolution ||
+ 10 * sResolutionSigDigs > sResolution);
+ sResolutionSigDigs *= 10);
+
+ gInitialized = true;
+
+ return;
+}
+
+void
+TimeStamp::Shutdown()
+{
+}
+
+TimeStamp
+TimeStamp::Now(bool aHighResolution)
+{
+ return TimeStamp(ClockTime());
+}
+
+// Computes and returns the process uptime in microseconds.
+// Returns 0 if an error was encountered.
+
+uint64_t
+TimeStamp::ComputeProcessUptime()
+{
+ struct timeval tv;
+ int rv = gettimeofday(&tv, nullptr);
+
+ if (rv == -1) {
+ return 0;
+ }
+
+ int mib[] = {
+ CTL_KERN,
+ KERN_PROC,
+ KERN_PROC_PID,
+ getpid(),
+ };
+ u_int mibLen = sizeof(mib) / sizeof(mib[0]);
+
+ struct kinfo_proc proc;
+ size_t bufferSize = sizeof(proc);
+ rv = sysctl(mib, mibLen, &proc, &bufferSize, nullptr, 0);
+
+ if (rv == -1) {
+ return 0;
+ }
+
+ uint64_t startTime =
+ ((uint64_t)proc.kp_proc.p_un.__p_starttime.tv_sec * kUsPerSec) +
+ proc.kp_proc.p_un.__p_starttime.tv_usec;
+ uint64_t now = (tv.tv_sec * kUsPerSec) + tv.tv_usec;
+
+ if (startTime > now) {
+ return 0;
+ }
+
+ return now - startTime;
+}
+
+} // namespace mozilla
diff --git a/mozglue/misc/TimeStamp_posix.cpp b/mozglue/misc/TimeStamp_posix.cpp
new file mode 100644
index 000000000..05deddea4
--- /dev/null
+++ b/mozglue/misc/TimeStamp_posix.cpp
@@ -0,0 +1,362 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+//
+// Implement TimeStamp::Now() with POSIX clocks.
+//
+// The "tick" unit for POSIX clocks is simply a nanosecond, as this is
+// the smallest unit of time representable by struct timespec. That
+// doesn't mean that a nanosecond is the resolution of TimeDurations
+// obtained with this API; see TimeDuration::Resolution;
+//
+
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+
+#if defined(__DragonFly__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#endif
+
+#if defined(__DragonFly__) || defined(__FreeBSD__)
+#include <sys/user.h>
+#endif
+
+#if defined(__NetBSD__)
+#undef KERN_PROC
+#define KERN_PROC KERN_PROC2
+#define KINFO_PROC struct kinfo_proc2
+#else
+#define KINFO_PROC struct kinfo_proc
+#endif
+
+#if defined(__DragonFly__)
+#define KP_START_SEC kp_start.tv_sec
+#define KP_START_USEC kp_start.tv_usec
+#elif defined(__FreeBSD__)
+#define KP_START_SEC ki_start.tv_sec
+#define KP_START_USEC ki_start.tv_usec
+#else
+#define KP_START_SEC p_ustart_sec
+#define KP_START_USEC p_ustart_usec
+#endif
+
+#include "mozilla/Sprintf.h"
+#include "mozilla/TimeStamp.h"
+#include <pthread.h>
+
+// Estimate of the smallest duration of time we can measure.
+static uint64_t sResolution;
+static uint64_t sResolutionSigDigs;
+
+static const uint16_t kNsPerUs = 1000;
+static const uint64_t kNsPerMs = 1000000;
+static const uint64_t kNsPerSec = 1000000000;
+static const double kNsPerMsd = 1000000.0;
+static const double kNsPerSecd = 1000000000.0;
+
+static uint64_t
+TimespecToNs(const struct timespec& aTs)
+{
+ uint64_t baseNs = uint64_t(aTs.tv_sec) * kNsPerSec;
+ return baseNs + uint64_t(aTs.tv_nsec);
+}
+
+static uint64_t
+ClockTimeNs()
+{
+ struct timespec ts;
+ // this can't fail: we know &ts is valid, and TimeStamp::Startup()
+ // checks that CLOCK_MONOTONIC is supported (and aborts if not)
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ // tv_sec is defined to be relative to an arbitrary point in time,
+ // but it would be madness for that point in time to be earlier than
+ // the Epoch. So we can safely assume that even if time_t is 32
+ // bits, tv_sec won't overflow while the browser is open. Revisit
+ // this argument if we're still building with 32-bit time_t around
+ // the year 2037.
+ return TimespecToNs(ts);
+}
+
+static uint64_t
+ClockResolutionNs()
+{
+ // NB: why not rely on clock_getres()? Two reasons: (i) it might
+ // lie, and (ii) it might return an "ideal" resolution that while
+ // theoretically true, could never be measured in practice. Since
+ // clock_gettime() likely involves a system call on your platform,
+ // the "actual" timing resolution shouldn't be lower than syscall
+ // overhead.
+
+ uint64_t start = ClockTimeNs();
+ uint64_t end = ClockTimeNs();
+ uint64_t minres = (end - start);
+
+ // 10 total trials is arbitrary: what we're trying to avoid by
+ // looping is getting unlucky and being interrupted by a context
+ // switch or signal, or being bitten by paging/cache effects
+ for (int i = 0; i < 9; ++i) {
+ start = ClockTimeNs();
+ end = ClockTimeNs();
+
+ uint64_t candidate = (start - end);
+ if (candidate < minres) {
+ minres = candidate;
+ }
+ }
+
+ if (0 == minres) {
+ // measurable resolution is either incredibly low, ~1ns, or very
+ // high. fall back on clock_getres()
+ struct timespec ts;
+ if (0 == clock_getres(CLOCK_MONOTONIC, &ts)) {
+ minres = TimespecToNs(ts);
+ }
+ }
+
+ if (0 == minres) {
+ // clock_getres probably failed. fall back on NSPR's resolution
+ // assumption
+ minres = 1 * kNsPerMs;
+ }
+
+ return minres;
+}
+
+namespace mozilla {
+
+double
+BaseTimeDurationPlatformUtils::ToSeconds(int64_t aTicks)
+{
+ return double(aTicks) / kNsPerSecd;
+}
+
+double
+BaseTimeDurationPlatformUtils::ToSecondsSigDigits(int64_t aTicks)
+{
+ // don't report a value < mResolution ...
+ int64_t valueSigDigs = sResolution * (aTicks / sResolution);
+ // and chop off insignificant digits
+ valueSigDigs = sResolutionSigDigs * (valueSigDigs / sResolutionSigDigs);
+ return double(valueSigDigs) / kNsPerSecd;
+}
+
+int64_t
+BaseTimeDurationPlatformUtils::TicksFromMilliseconds(double aMilliseconds)
+{
+ double result = aMilliseconds * kNsPerMsd;
+ if (result > INT64_MAX) {
+ return INT64_MAX;
+ } else if (result < INT64_MIN) {
+ return INT64_MIN;
+ }
+
+ return result;
+}
+
+int64_t
+BaseTimeDurationPlatformUtils::ResolutionInTicks()
+{
+ return static_cast<int64_t>(sResolution);
+}
+
+static bool gInitialized = false;
+
+void
+TimeStamp::Startup()
+{
+ if (gInitialized) {
+ return;
+ }
+
+ struct timespec dummy;
+ if (clock_gettime(CLOCK_MONOTONIC, &dummy) != 0) {
+ MOZ_CRASH("CLOCK_MONOTONIC is absent!");
+ }
+
+ sResolution = ClockResolutionNs();
+
+ // find the number of significant digits in sResolution, for the
+ // sake of ToSecondsSigDigits()
+ for (sResolutionSigDigs = 1;
+ !(sResolutionSigDigs == sResolution ||
+ 10 * sResolutionSigDigs > sResolution);
+ sResolutionSigDigs *= 10);
+
+ gInitialized = true;
+
+ return;
+}
+
+void
+TimeStamp::Shutdown()
+{
+}
+
+TimeStamp
+TimeStamp::Now(bool aHighResolution)
+{
+ return TimeStamp(ClockTimeNs());
+}
+
+#if defined(XP_LINUX) || defined(ANDROID)
+
+// Calculates the amount of jiffies that have elapsed since boot and up to the
+// starttime value of a specific process as found in its /proc/*/stat file.
+// Returns 0 if an error occurred.
+
+static uint64_t
+JiffiesSinceBoot(const char* aFile)
+{
+ char stat[512];
+
+ FILE* f = fopen(aFile, "r");
+ if (!f) {
+ return 0;
+ }
+
+ int n = fread(&stat, 1, sizeof(stat) - 1, f);
+
+ fclose(f);
+
+ if (n <= 0) {
+ return 0;
+ }
+
+ stat[n] = 0;
+
+ long long unsigned startTime = 0; // instead of uint64_t to keep GCC quiet
+ char* s = strrchr(stat, ')');
+
+ if (!s) {
+ return 0;
+ }
+
+ int rv = sscanf(s + 2,
+ "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u "
+ "%*u %*u %*u %*d %*d %*d %*d %*d %*d %llu",
+ &startTime);
+
+ if (rv != 1 || !startTime) {
+ return 0;
+ }
+
+ return startTime;
+}
+
+// Computes the interval that has elapsed between the thread creation and the
+// process creation by comparing the starttime fields in the respective
+// /proc/*/stat files. The resulting value will be a good approximation of the
+// process uptime. This value will be stored at the address pointed by aTime;
+// if an error occurred 0 will be stored instead.
+
+static void*
+ComputeProcessUptimeThread(void* aTime)
+{
+ uint64_t* uptime = static_cast<uint64_t*>(aTime);
+ long hz = sysconf(_SC_CLK_TCK);
+
+ *uptime = 0;
+
+ if (!hz) {
+ return nullptr;
+ }
+
+ char threadStat[40];
+ SprintfLiteral(threadStat, "/proc/self/task/%d/stat", (pid_t)syscall(__NR_gettid));
+
+ uint64_t threadJiffies = JiffiesSinceBoot(threadStat);
+ uint64_t selfJiffies = JiffiesSinceBoot("/proc/self/stat");
+
+ if (!threadJiffies || !selfJiffies) {
+ return nullptr;
+ }
+
+ *uptime = ((threadJiffies - selfJiffies) * kNsPerSec) / hz;
+ return nullptr;
+}
+
+// Computes and returns the process uptime in us on Linux & its derivatives.
+// Returns 0 if an error was encountered.
+
+uint64_t
+TimeStamp::ComputeProcessUptime()
+{
+ uint64_t uptime = 0;
+ pthread_t uptime_pthread;
+
+ if (pthread_create(&uptime_pthread, nullptr, ComputeProcessUptimeThread, &uptime)) {
+ MOZ_CRASH("Failed to create process uptime thread.");
+ return 0;
+ }
+
+ pthread_join(uptime_pthread, NULL);
+
+ return uptime / kNsPerUs;
+}
+
+#elif defined(__DragonFly__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__)
+
+// Computes and returns the process uptime in us on various BSD flavors.
+// Returns 0 if an error was encountered.
+
+uint64_t
+TimeStamp::ComputeProcessUptime()
+{
+ struct timespec ts;
+ int rv = clock_gettime(CLOCK_REALTIME, &ts);
+
+ if (rv == -1) {
+ return 0;
+ }
+
+ int mib[] = {
+ CTL_KERN,
+ KERN_PROC,
+ KERN_PROC_PID,
+ getpid(),
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ sizeof(KINFO_PROC),
+ 1,
+#endif
+ };
+ u_int mibLen = sizeof(mib) / sizeof(mib[0]);
+
+ KINFO_PROC proc;
+ size_t bufferSize = sizeof(proc);
+ rv = sysctl(mib, mibLen, &proc, &bufferSize, nullptr, 0);
+
+ if (rv == -1) {
+ return 0;
+ }
+
+ uint64_t startTime = ((uint64_t)proc.KP_START_SEC * kNsPerSec) +
+ (proc.KP_START_USEC * kNsPerUs);
+ uint64_t now = ((uint64_t)ts.tv_sec * kNsPerSec) + ts.tv_nsec;
+
+ if (startTime > now) {
+ return 0;
+ }
+
+ return (now - startTime) / kNsPerUs;
+}
+
+#else
+
+uint64_t
+TimeStamp::ComputeProcessUptime()
+{
+ return 0;
+}
+
+#endif
+
+} // namespace mozilla
diff --git a/mozglue/misc/TimeStamp_windows.cpp b/mozglue/misc/TimeStamp_windows.cpp
new file mode 100644
index 000000000..cd519affd
--- /dev/null
+++ b/mozglue/misc/TimeStamp_windows.cpp
@@ -0,0 +1,576 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+// Implement TimeStamp::Now() with QueryPerformanceCounter() controlled with
+// values of GetTickCount().
+
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/TimeStamp.h"
+
+#include <stdio.h>
+#include <intrin.h>
+#include <windows.h>
+
+// To enable logging define to your favorite logging API
+#define LOG(x)
+
+class AutoCriticalSection
+{
+public:
+ AutoCriticalSection(LPCRITICAL_SECTION aSection)
+ : mSection(aSection)
+ {
+ ::EnterCriticalSection(mSection);
+ }
+ ~AutoCriticalSection()
+ {
+ ::LeaveCriticalSection(mSection);
+ }
+private:
+ LPCRITICAL_SECTION mSection;
+};
+
+// Estimate of the smallest duration of time we can measure.
+static volatile ULONGLONG sResolution;
+static volatile ULONGLONG sResolutionSigDigs;
+static const double kNsPerSecd = 1000000000.0;
+static const LONGLONG kNsPerMillisec = 1000000;
+
+// ----------------------------------------------------------------------------
+// Global constants
+// ----------------------------------------------------------------------------
+
+// Tolerance to failures settings.
+//
+// What is the interval we want to have failure free.
+// in [ms]
+static const uint32_t kFailureFreeInterval = 5000;
+// How many failures we are willing to tolerate in the interval.
+static const uint32_t kMaxFailuresPerInterval = 4;
+// What is the threshold to treat fluctuations as actual failures.
+// in [ms]
+static const uint32_t kFailureThreshold = 50;
+
+// If we are not able to get the value of GTC time increment, use this value
+// which is the most usual increment.
+static const DWORD kDefaultTimeIncrement = 156001;
+
+// ----------------------------------------------------------------------------
+// Global variables, not changing at runtime
+// ----------------------------------------------------------------------------
+
+/**
+ * The [mt] unit:
+ *
+ * Many values are kept in ticks of the Performance Coutner x 1000,
+ * further just referred as [mt], meaning milli-ticks.
+ *
+ * This is needed to preserve maximum precision of the performance frequency
+ * representation. GetTickCount values in milliseconds are multiplied with
+ * frequency per second. Therefor we need to multiply QPC value by 1000 to
+ * have the same units to allow simple arithmentic with both QPC and GTC.
+ */
+
+#define ms2mt(x) ((x) * sFrequencyPerSec)
+#define mt2ms(x) ((x) / sFrequencyPerSec)
+#define mt2ms_f(x) (double(x) / sFrequencyPerSec)
+
+// Result of QueryPerformanceFrequency
+static LONGLONG sFrequencyPerSec = 0;
+
+// How much we are tolerant to GTC occasional loose of resoltion.
+// This number says how many multiples of the minimal GTC resolution
+// detected on the system are acceptable. This number is empirical.
+static const LONGLONG kGTCTickLeapTolerance = 4;
+
+// Base tolerance (more: "inability of detection" range) threshold is calculated
+// dynamically, and kept in sGTCResulutionThreshold.
+//
+// Schematically, QPC worked "100%" correctly if ((GTC_now - GTC_epoch) -
+// (QPC_now - QPC_epoch)) was in [-sGTCResulutionThreshold, sGTCResulutionThreshold]
+// interval every time we'd compared two time stamps.
+// If not, then we check the overflow behind this basic threshold
+// is in kFailureThreshold. If not, we condider it as a QPC failure. If too many
+// failures in short time are detected, QPC is considered faulty and disabled.
+//
+// Kept in [mt]
+static LONGLONG sGTCResulutionThreshold;
+
+// If QPC is found faulty for two stamps in this interval, we engage
+// the fault detection algorithm. For duration larger then this limit
+// we bypass using durations calculated from QPC when jitter is detected,
+// but don't touch the sUseQPC flag.
+//
+// Value is in [ms].
+static const uint32_t kHardFailureLimit = 2000;
+// Conversion to [mt]
+static LONGLONG sHardFailureLimit;
+
+// Conversion of kFailureFreeInterval and kFailureThreshold to [mt]
+static LONGLONG sFailureFreeInterval;
+static LONGLONG sFailureThreshold;
+
+// ----------------------------------------------------------------------------
+// Systemm status flags
+// ----------------------------------------------------------------------------
+
+// Flag for stable TSC that indicates platform where QPC is stable.
+static bool sHasStableTSC = false;
+
+// ----------------------------------------------------------------------------
+// Global state variables, changing at runtime
+// ----------------------------------------------------------------------------
+
+// Initially true, set to false when QPC is found unstable and never
+// returns back to true since that time.
+static bool volatile sUseQPC = true;
+
+// ----------------------------------------------------------------------------
+// Global lock
+// ----------------------------------------------------------------------------
+
+// Thread spin count before entering the full wait state for sTimeStampLock.
+// Inspired by Rob Arnold's work on PRMJ_Now().
+static const DWORD kLockSpinCount = 4096;
+
+// Common mutex (thanks the relative complexity of the logic, this is better
+// then using CMPXCHG8B.)
+// It is protecting the globals bellow.
+static CRITICAL_SECTION sTimeStampLock;
+
+// ----------------------------------------------------------------------------
+// Global lock protected variables
+// ----------------------------------------------------------------------------
+
+// Timestamp in future until QPC must behave correctly.
+// Set to now + kFailureFreeInterval on first QPC failure detection.
+// Set to now + E * kFailureFreeInterval on following errors,
+// where E is number of errors detected during last kFailureFreeInterval
+// milliseconds, calculated simply as:
+// E = (sFaultIntoleranceCheckpoint - now) / kFailureFreeInterval + 1.
+// When E > kMaxFailuresPerInterval -> disable QPC.
+//
+// Kept in [mt]
+static ULONGLONG sFaultIntoleranceCheckpoint = 0;
+
+// Used only when GetTickCount64 is not available on the platform.
+// Last result of GetTickCount call.
+//
+// Kept in [ms]
+static DWORD sLastGTCResult = 0;
+
+// Higher part of the 64-bit value of MozGetTickCount64,
+// incremented atomically.
+static DWORD sLastGTCRollover = 0;
+
+namespace mozilla {
+
+typedef ULONGLONG (WINAPI* GetTickCount64_t)();
+static GetTickCount64_t sGetTickCount64 = nullptr;
+
+// Function protecting GetTickCount result from rolling over,
+// result is in [ms]
+static ULONGLONG WINAPI
+MozGetTickCount64()
+{
+ DWORD GTC = ::GetTickCount();
+
+ // Cheaper then CMPXCHG8B
+ AutoCriticalSection lock(&sTimeStampLock);
+
+ // Pull the rollover counter forward only if new value of GTC goes way
+ // down under the last saved result
+ if ((sLastGTCResult > GTC) && ((sLastGTCResult - GTC) > (1UL << 30))) {
+ ++sLastGTCRollover;
+ }
+
+ sLastGTCResult = GTC;
+ return ULONGLONG(sLastGTCRollover) << 32 | sLastGTCResult;
+}
+
+// Result is in [mt]
+static inline ULONGLONG
+PerformanceCounter()
+{
+ LARGE_INTEGER pc;
+ ::QueryPerformanceCounter(&pc);
+ return pc.QuadPart * 1000ULL;
+}
+
+static void
+InitThresholds()
+{
+ DWORD timeAdjustment = 0, timeIncrement = 0;
+ BOOL timeAdjustmentDisabled;
+ GetSystemTimeAdjustment(&timeAdjustment,
+ &timeIncrement,
+ &timeAdjustmentDisabled);
+
+ LOG(("TimeStamp: timeIncrement=%d [100ns]", timeIncrement));
+
+ if (!timeIncrement) {
+ timeIncrement = kDefaultTimeIncrement;
+ }
+
+ // Ceiling to a millisecond
+ // Example values: 156001, 210000
+ DWORD timeIncrementCeil = timeIncrement;
+ // Don't want to round up if already rounded, values will be: 156000, 209999
+ timeIncrementCeil -= 1;
+ // Convert to ms, values will be: 15, 20
+ timeIncrementCeil /= 10000;
+ // Round up, values will be: 16, 21
+ timeIncrementCeil += 1;
+ // Convert back to 100ns, values will be: 160000, 210000
+ timeIncrementCeil *= 10000;
+
+ // How many milli-ticks has the interval rounded up
+ LONGLONG ticksPerGetTickCountResolutionCeiling =
+ (int64_t(timeIncrementCeil) * sFrequencyPerSec) / 10000LL;
+
+ // GTC may jump by 32 (2*16) ms in two steps, therefor use the ceiling value.
+ sGTCResulutionThreshold =
+ LONGLONG(kGTCTickLeapTolerance * ticksPerGetTickCountResolutionCeiling);
+
+ sHardFailureLimit = ms2mt(kHardFailureLimit);
+ sFailureFreeInterval = ms2mt(kFailureFreeInterval);
+ sFailureThreshold = ms2mt(kFailureThreshold);
+}
+
+static void
+InitResolution()
+{
+ // 10 total trials is arbitrary: what we're trying to avoid by
+ // looping is getting unlucky and being interrupted by a context
+ // switch or signal, or being bitten by paging/cache effects
+
+ ULONGLONG minres = ~0ULL;
+ int loops = 10;
+ do {
+ ULONGLONG start = PerformanceCounter();
+ ULONGLONG end = PerformanceCounter();
+
+ ULONGLONG candidate = (end - start);
+ if (candidate < minres) {
+ minres = candidate;
+ }
+ } while (--loops && minres);
+
+ if (0 == minres) {
+ minres = 1;
+ }
+
+ // Converting minres that is in [mt] to nanosecods, multiplicating
+ // the argument to preserve resolution.
+ ULONGLONG result = mt2ms(minres * kNsPerMillisec);
+ if (0 == result) {
+ result = 1;
+ }
+
+ sResolution = result;
+
+ // find the number of significant digits in mResolution, for the
+ // sake of ToSecondsSigDigits()
+ ULONGLONG sigDigs;
+ for (sigDigs = 1;
+ !(sigDigs == result || 10 * sigDigs > result);
+ sigDigs *= 10);
+
+ sResolutionSigDigs = sigDigs;
+}
+
+// ----------------------------------------------------------------------------
+// TimeStampValue implementation
+// ----------------------------------------------------------------------------
+MFBT_API
+TimeStampValue::TimeStampValue(ULONGLONG aGTC, ULONGLONG aQPC, bool aHasQPC)
+ : mGTC(aGTC)
+ , mQPC(aQPC)
+ , mHasQPC(aHasQPC)
+ , mIsNull(false)
+{
+}
+
+MFBT_API TimeStampValue&
+TimeStampValue::operator+=(const int64_t aOther)
+{
+ mGTC += aOther;
+ mQPC += aOther;
+ return *this;
+}
+
+MFBT_API TimeStampValue&
+TimeStampValue::operator-=(const int64_t aOther)
+{
+ mGTC -= aOther;
+ mQPC -= aOther;
+ return *this;
+}
+
+// If the duration is less then two seconds, perform check of QPC stability
+// by comparing both GTC and QPC calculated durations of this and aOther.
+MFBT_API uint64_t
+TimeStampValue::CheckQPC(const TimeStampValue& aOther) const
+{
+ uint64_t deltaGTC = mGTC - aOther.mGTC;
+
+ if (!mHasQPC || !aOther.mHasQPC) { // Both not holding QPC
+ return deltaGTC;
+ }
+
+ uint64_t deltaQPC = mQPC - aOther.mQPC;
+
+ if (sHasStableTSC) { // For stable TSC there is no need to check
+ return deltaQPC;
+ }
+
+ // Check QPC is sane before using it.
+ int64_t diff = DeprecatedAbs(int64_t(deltaQPC) - int64_t(deltaGTC));
+ if (diff <= sGTCResulutionThreshold) {
+ return deltaQPC;
+ }
+
+ // Treat absolutely for calibration purposes
+ int64_t duration = DeprecatedAbs(int64_t(deltaGTC));
+ int64_t overflow = diff - sGTCResulutionThreshold;
+
+ LOG(("TimeStamp: QPC check after %llums with overflow %1.4fms",
+ mt2ms(duration), mt2ms_f(overflow)));
+
+ if (overflow <= sFailureThreshold) { // We are in the limit, let go.
+ return deltaQPC;
+ }
+
+ // QPC deviates, don't use it, since now this method may only return deltaGTC.
+
+ if (!sUseQPC) { // QPC already disabled, no need to run the fault tolerance algorithm.
+ return deltaGTC;
+ }
+
+ LOG(("TimeStamp: QPC jittered over failure threshold"));
+
+ if (duration < sHardFailureLimit) {
+ // Interval between the two time stamps is very short, consider
+ // QPC as unstable and record a failure.
+ uint64_t now = ms2mt(sGetTickCount64());
+
+ AutoCriticalSection lock(&sTimeStampLock);
+
+ if (sFaultIntoleranceCheckpoint && sFaultIntoleranceCheckpoint > now) {
+ // There's already been an error in the last fault intollerant interval.
+ // Time since now to the checkpoint actually holds information on how many
+ // failures there were in the failure free interval we have defined.
+ uint64_t failureCount =
+ (sFaultIntoleranceCheckpoint - now + sFailureFreeInterval - 1) /
+ sFailureFreeInterval;
+ if (failureCount > kMaxFailuresPerInterval) {
+ sUseQPC = false;
+ LOG(("TimeStamp: QPC disabled"));
+ } else {
+ // Move the fault intolerance checkpoint more to the future, prolong it
+ // to reflect the number of detected failures.
+ ++failureCount;
+ sFaultIntoleranceCheckpoint = now + failureCount * sFailureFreeInterval;
+ LOG(("TimeStamp: recording %dth QPC failure", failureCount));
+ }
+ } else {
+ // Setup fault intolerance checkpoint in the future for first detected error.
+ sFaultIntoleranceCheckpoint = now + sFailureFreeInterval;
+ LOG(("TimeStamp: recording 1st QPC failure"));
+ }
+ }
+
+ return deltaGTC;
+}
+
+MFBT_API uint64_t
+TimeStampValue::operator-(const TimeStampValue& aOther) const
+{
+ if (mIsNull && aOther.mIsNull) {
+ return uint64_t(0);
+ }
+
+ return CheckQPC(aOther);
+}
+
+// ----------------------------------------------------------------------------
+// TimeDuration and TimeStamp implementation
+// ----------------------------------------------------------------------------
+
+MFBT_API double
+BaseTimeDurationPlatformUtils::ToSeconds(int64_t aTicks)
+{
+ // Converting before arithmetic avoids blocked store forward
+ return double(aTicks) / (double(sFrequencyPerSec) * 1000.0);
+}
+
+MFBT_API double
+BaseTimeDurationPlatformUtils::ToSecondsSigDigits(int64_t aTicks)
+{
+ // don't report a value < mResolution ...
+ LONGLONG resolution = sResolution;
+ LONGLONG resolutionSigDigs = sResolutionSigDigs;
+ LONGLONG valueSigDigs = resolution * (aTicks / resolution);
+ // and chop off insignificant digits
+ valueSigDigs = resolutionSigDigs * (valueSigDigs / resolutionSigDigs);
+ return double(valueSigDigs) / kNsPerSecd;
+}
+
+MFBT_API int64_t
+BaseTimeDurationPlatformUtils::TicksFromMilliseconds(double aMilliseconds)
+{
+ double result = ms2mt(aMilliseconds);
+ if (result > INT64_MAX) {
+ return INT64_MAX;
+ } else if (result < INT64_MIN) {
+ return INT64_MIN;
+ }
+
+ return result;
+}
+
+MFBT_API int64_t
+BaseTimeDurationPlatformUtils::ResolutionInTicks()
+{
+ return static_cast<int64_t>(sResolution);
+}
+
+static bool
+HasStableTSC()
+{
+ union
+ {
+ int regs[4];
+ struct
+ {
+ int nIds;
+ char cpuString[12];
+ };
+ } cpuInfo;
+
+ __cpuid(cpuInfo.regs, 0);
+ // Only allow Intel CPUs for now
+ // The order of the registers is reg[1], reg[3], reg[2]. We just adjust the
+ // string so that we can compare in one go.
+ if (_strnicmp(cpuInfo.cpuString, "GenuntelineI",
+ sizeof(cpuInfo.cpuString))) {
+ return false;
+ }
+
+ int regs[4];
+
+ // detect if the Advanced Power Management feature is supported
+ __cpuid(regs, 0x80000000);
+ if (regs[0] < 0x80000007) {
+ return false;
+ }
+
+ __cpuid(regs, 0x80000007);
+ // if bit 8 is set than TSC will run at a constant rate
+ // in all ACPI P-state, C-states and T-states
+ return regs[3] & (1 << 8);
+}
+
+static bool gInitialized = false;
+
+MFBT_API void
+TimeStamp::Startup()
+{
+ if (gInitialized) {
+ return;
+ }
+
+ gInitialized = true;
+
+ // Decide which implementation to use for the high-performance timer.
+
+ HMODULE kernelDLL = GetModuleHandleW(L"kernel32.dll");
+ sGetTickCount64 = reinterpret_cast<GetTickCount64_t>(
+ GetProcAddress(kernelDLL, "GetTickCount64"));
+ if (!sGetTickCount64) {
+ // If the platform does not support the GetTickCount64 (Windows XP doesn't),
+ // then use our fallback implementation based on GetTickCount.
+ sGetTickCount64 = MozGetTickCount64;
+ }
+
+ InitializeCriticalSectionAndSpinCount(&sTimeStampLock, kLockSpinCount);
+
+ sHasStableTSC = HasStableTSC();
+ LOG(("TimeStamp: HasStableTSC=%d", sHasStableTSC));
+
+ LARGE_INTEGER freq;
+ sUseQPC = ::QueryPerformanceFrequency(&freq);
+ if (!sUseQPC) {
+ // No Performance Counter. Fall back to use GetTickCount.
+ InitResolution();
+
+ LOG(("TimeStamp: using GetTickCount"));
+ return;
+ }
+
+ sFrequencyPerSec = freq.QuadPart;
+ LOG(("TimeStamp: QPC frequency=%llu", sFrequencyPerSec));
+
+ InitThresholds();
+ InitResolution();
+
+ return;
+}
+
+MFBT_API void
+TimeStamp::Shutdown()
+{
+ DeleteCriticalSection(&sTimeStampLock);
+}
+
+MFBT_API TimeStamp
+TimeStamp::Now(bool aHighResolution)
+{
+ // sUseQPC is volatile
+ bool useQPC = (aHighResolution && sUseQPC);
+
+ // Both values are in [mt] units.
+ ULONGLONG QPC = useQPC ? PerformanceCounter() : uint64_t(0);
+ ULONGLONG GTC = ms2mt(sGetTickCount64());
+ return TimeStamp(TimeStampValue(GTC, QPC, useQPC));
+}
+
+// Computes and returns the process uptime in microseconds.
+// Returns 0 if an error was encountered.
+
+MFBT_API uint64_t
+TimeStamp::ComputeProcessUptime()
+{
+ SYSTEMTIME nowSys;
+ GetSystemTime(&nowSys);
+
+ FILETIME now;
+ bool success = SystemTimeToFileTime(&nowSys, &now);
+
+ if (!success) {
+ return 0;
+ }
+
+ FILETIME start, foo, bar, baz;
+ success = GetProcessTimes(GetCurrentProcess(), &start, &foo, &bar, &baz);
+
+ if (!success) {
+ return 0;
+ }
+
+ ULARGE_INTEGER startUsec = {{
+ start.dwLowDateTime,
+ start.dwHighDateTime
+ }};
+ ULARGE_INTEGER nowUsec = {{
+ now.dwLowDateTime,
+ now.dwHighDateTime
+ }};
+
+ return (nowUsec.QuadPart - startUsec.QuadPart) / 10ULL;
+}
+
+} // namespace mozilla
diff --git a/mozglue/misc/TimeStamp_windows.h b/mozglue/misc/TimeStamp_windows.h
new file mode 100644
index 000000000..c87d34efc
--- /dev/null
+++ b/mozglue/misc/TimeStamp_windows.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef mozilla_TimeStamp_windows_h
+#define mozilla_TimeStamp_windows_h
+
+#include "mozilla/Types.h"
+
+namespace mozilla {
+
+class TimeStamp;
+
+class TimeStampValue
+{
+ friend struct IPC::ParamTraits<mozilla::TimeStampValue>;
+ friend class TimeStamp;
+ friend void StartupTimelineRecordExternal(int, uint64_t);
+
+ // Both QPC and GTC are kept in [mt] units.
+ uint64_t mGTC;
+ uint64_t mQPC;
+ bool mHasQPC;
+ bool mIsNull;
+
+ MFBT_API TimeStampValue(uint64_t aGTC, uint64_t aQPC, bool aHasQPC);
+
+ MFBT_API uint64_t CheckQPC(const TimeStampValue& aOther) const;
+
+ struct _SomethingVeryRandomHere;
+ constexpr TimeStampValue(_SomethingVeryRandomHere* aNullValue)
+ : mGTC(0)
+ , mQPC(0)
+ , mHasQPC(false)
+ , mIsNull(true)
+ {
+ }
+
+public:
+ MFBT_API uint64_t operator-(const TimeStampValue& aOther) const;
+
+ TimeStampValue operator+(const int64_t aOther) const
+ {
+ return TimeStampValue(mGTC + aOther, mQPC + aOther, mHasQPC);
+ }
+ TimeStampValue operator-(const int64_t aOther) const
+ {
+ return TimeStampValue(mGTC - aOther, mQPC - aOther, mHasQPC);
+ }
+ MFBT_API TimeStampValue& operator+=(const int64_t aOther);
+ MFBT_API TimeStampValue& operator-=(const int64_t aOther);
+
+ bool operator<(const TimeStampValue& aOther) const
+ {
+ return int64_t(*this - aOther) < 0;
+ }
+ bool operator>(const TimeStampValue& aOther) const
+ {
+ return int64_t(*this - aOther) > 0;
+ }
+ bool operator<=(const TimeStampValue& aOther) const
+ {
+ return int64_t(*this - aOther) <= 0;
+ }
+ bool operator>=(const TimeStampValue& aOther) const
+ {
+ return int64_t(*this - aOther) >= 0;
+ }
+ bool operator==(const TimeStampValue& aOther) const
+ {
+ return int64_t(*this - aOther) == 0;
+ }
+ bool operator!=(const TimeStampValue& aOther) const
+ {
+ return int64_t(*this - aOther) != 0;
+ }
+};
+
+}
+
+#endif /* mozilla_TimeStamp_h */
diff --git a/mozglue/misc/moz.build b/mozglue/misc/moz.build
new file mode 100644
index 000000000..9c2ef399c
--- /dev/null
+++ b/mozglue/misc/moz.build
@@ -0,0 +1,37 @@
+FINAL_LIBRARY = 'mozglue'
+
+EXPORTS.mozilla += [
+ 'StackWalk.h',
+ 'TimeStamp.h',
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ EXPORTS.mozilla += [
+ 'StackWalk_windows.h',
+ 'TimeStamp_windows.h',
+ ]
+
+SOURCES += [
+ 'StackWalk.cpp',
+ 'TimeStamp.cpp',
+]
+
+OS_LIBS += CONFIG['REALTIME_LIBS']
+
+DEFINES['IMPL_MFBT'] = True
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ SOURCES += [
+ 'TimeStamp_windows.cpp',
+ ]
+ OS_LIBS += ['dbghelp']
+elif CONFIG['HAVE_CLOCK_MONOTONIC']:
+ SOURCES += [
+ 'TimeStamp_posix.cpp',
+ ]
+elif CONFIG['OS_ARCH'] == 'Darwin':
+ SOURCES += [
+ 'TimeStamp_darwin.cpp',
+ ]
+elif CONFIG['COMPILE_ENVIRONMENT']:
+ error('No TimeStamp implementation on this platform. Build will not succeed')
diff --git a/mozglue/moz.build b/mozglue/moz.build
new file mode 100644
index 000000000..a951d6427
--- /dev/null
+++ b/mozglue/moz.build
@@ -0,0 +1,19 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# 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/.
+
+if CONFIG['MOZ_LINKER']:
+ DIRS += ['linker']
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+ DIRS += ['android']
+
+DIRS += [
+ 'build',
+ 'misc',
+]
+
+if not CONFIG['JS_STANDALONE']:
+ TEST_DIRS += ['tests']
diff --git a/mozglue/tests/ShowSSEConfig.cpp b/mozglue/tests/ShowSSEConfig.cpp
new file mode 100644
index 000000000..be17ffb52
--- /dev/null
+++ b/mozglue/tests/ShowSSEConfig.cpp
@@ -0,0 +1,123 @@
+/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include "mozilla/SSE.h"
+#include <stdio.h>
+
+int main()
+{
+ printf("CPUID detection present: %s\n",
+#ifdef MOZILLA_SSE_HAVE_CPUID_DETECTION
+ "yes"
+#else
+ "no"
+#endif
+ );
+
+#ifdef MOZILLA_COMPILE_WITH_MMX
+#define COMPILE_MMX_STRING "Y"
+#else
+#define COMPILE_MMX_STRING "-"
+#endif
+#ifdef MOZILLA_PRESUME_MMX
+#define PRESUME_MMX_STRING "Y"
+#else
+#define PRESUME_MMX_STRING "-"
+#endif
+
+#ifdef MOZILLA_COMPILE_WITH_SSE
+#define COMPILE_SSE_STRING "Y"
+#else
+#define COMPILE_SSE_STRING "-"
+#endif
+#ifdef MOZILLA_PRESUME_SSE
+#define PRESUME_SSE_STRING "Y"
+#else
+#define PRESUME_SSE_STRING "-"
+#endif
+
+#ifdef MOZILLA_COMPILE_WITH_SSE2
+#define COMPILE_SSE2_STRING "Y"
+#else
+#define COMPILE_SSE2_STRING "-"
+#endif
+#ifdef MOZILLA_PRESUME_SSE2
+#define PRESUME_SSE2_STRING "Y"
+#else
+#define PRESUME_SSE2_STRING "-"
+#endif
+
+#ifdef MOZILLA_COMPILE_WITH_SSE3
+#define COMPILE_SSE3_STRING "Y"
+#else
+#define COMPILE_SSE3_STRING "-"
+#endif
+#ifdef MOZILLA_PRESUME_SSE3
+#define PRESUME_SSE3_STRING "Y"
+#else
+#define PRESUME_SSE3_STRING "-"
+#endif
+
+#ifdef MOZILLA_COMPILE_WITH_SSSE3
+#define COMPILE_SSSE3_STRING "Y"
+#else
+#define COMPILE_SSSE3_STRING "-"
+#endif
+#ifdef MOZILLA_PRESUME_SSSE3
+#define PRESUME_SSSE3_STRING "Y"
+#else
+#define PRESUME_SSSE3_STRING "-"
+#endif
+
+#ifdef MOZILLA_COMPILE_WITH_SSE4A
+#define COMPILE_SSE4A_STRING "Y"
+#else
+#define COMPILE_SSE4A_STRING "-"
+#endif
+#ifdef MOZILLA_PRESUME_SSE4A
+#define PRESUME_SSE4A_STRING "Y"
+#else
+#define PRESUME_SSE4A_STRING "-"
+#endif
+
+#ifdef MOZILLA_COMPILE_WITH_SSE4_1
+#define COMPILE_SSE4_1_STRING "Y"
+#else
+#define COMPILE_SSE4_1_STRING "-"
+#endif
+#ifdef MOZILLA_PRESUME_SSE4_1
+#define PRESUME_SSE4_1_STRING "Y"
+#else
+#define PRESUME_SSE4_1_STRING "-"
+#endif
+
+#ifdef MOZILLA_COMPILE_WITH_SSE4_2
+#define COMPILE_SSE4_2_STRING "Y"
+#else
+#define COMPILE_SSE4_2_STRING "-"
+#endif
+#ifdef MOZILLA_PRESUME_SSE4_2
+#define PRESUME_SSE4_2_STRING "Y"
+#else
+#define PRESUME_SSE4_2_STRING "-"
+#endif
+
+ printf("Feature Presume Compile Support Use\n");
+#define SHOW_INFO(featurelc_, featureuc_) \
+ printf( "%7s %1s %1s %1s\n", \
+ #featurelc_, \
+ PRESUME_##featureuc_##_STRING, \
+ COMPILE_##featureuc_##_STRING, \
+ (mozilla::supports_##featurelc_() ? "Y" : "-"));
+ SHOW_INFO(mmx, MMX)
+ SHOW_INFO(sse, SSE)
+ SHOW_INFO(sse2, SSE2)
+ SHOW_INFO(sse3, SSE3)
+ SHOW_INFO(ssse3, SSSE3)
+ SHOW_INFO(sse4a, SSE4A)
+ SHOW_INFO(sse4_1, SSE4_1)
+ SHOW_INFO(sse4_2, SSE4_2)
+ return 0;
+}
diff --git a/mozglue/tests/moz.build b/mozglue/tests/moz.build
new file mode 100644
index 000000000..0c8c9c588
--- /dev/null
+++ b/mozglue/tests/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# 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/.
+
+DISABLE_STL_WRAPPING = True
+
+GeckoCppUnitTests([
+ 'ShowSSEConfig',
+], linkage=None)