mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-29 10:37:33 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
465
mozglue/android/APKOpen.cpp
Normal file
465
mozglue/android/APKOpen.cpp
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
||||
39
mozglue/android/APKOpen.h
Normal file
39
mozglue/android/APKOpen.h
Normal file
|
|
@ -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 */
|
||||
295
mozglue/android/NSSBridge.cpp
Normal file
295
mozglue/android/NSSBridge.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
46
mozglue/android/NSSBridge.h
Normal file
46
mozglue/android/NSSBridge.h
Normal file
|
|
@ -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 */
|
||||
132
mozglue/android/NativeCrypto.cpp
Normal file
132
mozglue/android/NativeCrypto.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
53
mozglue/android/NativeCrypto.h
Normal file
53
mozglue/android/NativeCrypto.h
Normal file
|
|
@ -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
|
||||
413
mozglue/android/SQLiteBridge.cpp
Normal file
413
mozglue/android/SQLiteBridge.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
34
mozglue/android/SQLiteBridge.h
Normal file
34
mozglue/android/SQLiteBridge.h
Normal file
|
|
@ -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 */
|
||||
65
mozglue/android/SharedMemNatives.cpp
Normal file
65
mozglue/android/SharedMemNatives.cpp
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
58
mozglue/android/moz.build
Normal file
58
mozglue/android/moz.build
Normal file
|
|
@ -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']
|
||||
124
mozglue/android/nsGeckoUtils.cpp
Normal file
124
mozglue/android/nsGeckoUtils.cpp
Normal file
|
|
@ -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());
|
||||
}
|
||||
432
mozglue/android/pbkdf2_sha256.c
Normal file
432
mozglue/android/pbkdf2_sha256.c
Normal file
|
|
@ -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));
|
||||
}
|
||||
70
mozglue/android/pbkdf2_sha256.h
Normal file
70
mozglue/android/pbkdf2_sha256.h
Normal file
|
|
@ -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_ */
|
||||
35
mozglue/build/AsanOptions.cpp
Normal file
35
mozglue/build/AsanOptions.cpp
Normal file
|
|
@ -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
|
||||
157
mozglue/build/BionicGlue.cpp
Normal file
157
mozglue/build/BionicGlue.cpp
Normal file
|
|
@ -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
|
||||
|
||||
23
mozglue/build/Makefile.in
Normal file
23
mozglue/build/Makefile.in
Normal file
|
|
@ -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
|
||||
206
mozglue/build/SSE.cpp
Normal file
206
mozglue/build/SSE.cpp
Normal file
|
|
@ -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
|
||||
332
mozglue/build/SSE.h
Normal file
332
mozglue/build/SSE.h
Normal file
|
|
@ -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_) */
|
||||
831
mozglue/build/WindowsDllBlocklist.cpp
Normal file
831
mozglue/build/WindowsDllBlocklist.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
39
mozglue/build/WindowsDllBlocklist.h
Normal file
39
mozglue/build/WindowsDllBlocklist.h
Normal file
|
|
@ -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
|
||||
4
mozglue/build/arm-eabi-filter
Normal file
4
mozglue/build/arm-eabi-filter
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
local:
|
||||
__aeabi*;
|
||||
};
|
||||
147
mozglue/build/arm.cpp
Normal file
147
mozglue/build/arm.cpp
Normal file
|
|
@ -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
|
||||
146
mozglue/build/arm.h
Normal file
146
mozglue/build/arm.h
Normal file
|
|
@ -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_) */
|
||||
61
mozglue/build/cpuacct.c
Normal file
61
mozglue/build/cpuacct.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
41
mozglue/build/cpuacct.h
Normal file
41
mozglue/build/cpuacct.h
Normal file
|
|
@ -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 */
|
||||
6
mozglue/build/dummy.cpp
Normal file
6
mozglue/build/dummy.cpp
Normal file
|
|
@ -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) {
|
||||
}
|
||||
47
mozglue/build/mips.cpp
Normal file
47
mozglue/build/mips.cpp
Normal file
|
|
@ -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
|
||||
29
mozglue/build/mips.h
Normal file
29
mozglue/build/mips.h
Normal file
|
|
@ -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_) */
|
||||
108
mozglue/build/moz.build
Normal file
108
mozglue/build/moz.build
Normal file
|
|
@ -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
|
||||
39
mozglue/build/mozglue.def.in
Normal file
39
mozglue/build/mozglue.def.in
Normal file
|
|
@ -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
|
||||
32
mozglue/build/replace_malloc.mk
Normal file
32
mozglue/build/replace_malloc.mk
Normal file
|
|
@ -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
|
||||
215
mozglue/linker/BaseElf.cpp
Normal file
215
mozglue/linker/BaseElf.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
141
mozglue/linker/BaseElf.h
Normal file
141
mozglue/linker/BaseElf.h
Normal file
|
|
@ -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 */
|
||||
774
mozglue/linker/CustomElf.cpp
Normal file
774
mozglue/linker/CustomElf.cpp
Normal file
|
|
@ -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());
|
||||
}
|
||||
159
mozglue/linker/CustomElf.h
Normal file
159
mozglue/linker/CustomElf.h
Normal file
|
|
@ -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 */
|
||||
1310
mozglue/linker/ElfLoader.cpp
Normal file
1310
mozglue/linker/ElfLoader.cpp
Normal file
File diff suppressed because it is too large
Load diff
674
mozglue/linker/ElfLoader.h
Normal file
674
mozglue/linker/ElfLoader.h
Normal file
|
|
@ -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 */
|
||||
241
mozglue/linker/Elfxx.h
Normal file
241
mozglue/linker/Elfxx.h
Normal file
|
|
@ -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 */
|
||||
87
mozglue/linker/Logging.h
Normal file
87
mozglue/linker/Logging.h
Normal file
|
|
@ -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 */
|
||||
681
mozglue/linker/Mappable.cpp
Normal file
681
mozglue/linker/Mappable.cpp
Normal file
|
|
@ -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();
|
||||
}
|
||||
267
mozglue/linker/Mappable.h
Normal file
267
mozglue/linker/Mappable.h
Normal file
|
|
@ -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 */
|
||||
261
mozglue/linker/SeekableZStream.cpp
Normal file
261
mozglue/linker/SeekableZStream.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
154
mozglue/linker/SeekableZStream.h
Normal file
154
mozglue/linker/SeekableZStream.h
Normal file
|
|
@ -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 */
|
||||
618
mozglue/linker/Utils.h
Normal file
618
mozglue/linker/Utils.h
Normal file
|
|
@ -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 */
|
||||
|
||||
213
mozglue/linker/XZStream.cpp
Normal file
213
mozglue/linker/XZStream.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
48
mozglue/linker/XZStream.h
Normal file
48
mozglue/linker/XZStream.h
Normal file
|
|
@ -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
|
||||
229
mozglue/linker/Zip.cpp
Normal file
229
mozglue/linker/Zip.cpp
Normal file
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
500
mozglue/linker/Zip.h
Normal file
500
mozglue/linker/Zip.h
Normal file
|
|
@ -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 */
|
||||
16
mozglue/linker/dladdr.h
Normal file
16
mozglue/linker/dladdr.h
Normal file
|
|
@ -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
|
||||
54
mozglue/linker/moz.build
Normal file
54
mozglue/linker/moz.build
Normal file
|
|
@ -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',
|
||||
]
|
||||
593
mozglue/linker/szip.cpp
Normal file
593
mozglue/linker/szip.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
69
mozglue/linker/tests/TestZip.cpp
Normal file
69
mozglue/linker/tests/TestZip.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
23
mozglue/linker/tests/moz.build
Normal file
23
mozglue/linker/tests/moz.build
Normal file
|
|
@ -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']
|
||||
BIN
mozglue/linker/tests/no_central_dir.zip
Normal file
BIN
mozglue/linker/tests/no_central_dir.zip
Normal file
Binary file not shown.
21
mozglue/linker/tests/run_test_zip.py
Normal file
21
mozglue/linker/tests/run_test_zip.py
Normal file
|
|
@ -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()
|
||||
BIN
mozglue/linker/tests/test.zip
Normal file
BIN
mozglue/linker/tests/test.zip
Normal file
Binary file not shown.
1204
mozglue/misc/StackWalk.cpp
Normal file
1204
mozglue/misc/StackWalk.cpp
Normal file
File diff suppressed because it is too large
Load diff
163
mozglue/misc/StackWalk.h
Normal file
163
mozglue/misc/StackWalk.h
Normal file
|
|
@ -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
|
||||
21
mozglue/misc/StackWalk_windows.h
Normal file
21
mozglue/misc/StackWalk_windows.h
Normal file
|
|
@ -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
|
||||
92
mozglue/misc/TimeStamp.cpp
Normal file
92
mozglue/misc/TimeStamp.cpp
Normal file
|
|
@ -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
|
||||
609
mozglue/misc/TimeStamp.h
Normal file
609
mozglue/misc/TimeStamp.h
Normal file
|
|
@ -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 */
|
||||
206
mozglue/misc/TimeStamp_darwin.cpp
Normal file
206
mozglue/misc/TimeStamp_darwin.cpp
Normal file
|
|
@ -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
|
||||
362
mozglue/misc/TimeStamp_posix.cpp
Normal file
362
mozglue/misc/TimeStamp_posix.cpp
Normal file
|
|
@ -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
|
||||
576
mozglue/misc/TimeStamp_windows.cpp
Normal file
576
mozglue/misc/TimeStamp_windows.cpp
Normal file
|
|
@ -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
|
||||
83
mozglue/misc/TimeStamp_windows.h
Normal file
83
mozglue/misc/TimeStamp_windows.h
Normal file
|
|
@ -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 */
|
||||
37
mozglue/misc/moz.build
Normal file
37
mozglue/misc/moz.build
Normal file
|
|
@ -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')
|
||||
19
mozglue/moz.build
Normal file
19
mozglue/moz.build
Normal file
|
|
@ -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']
|
||||
123
mozglue/tests/ShowSSEConfig.cpp
Normal file
123
mozglue/tests/ShowSSEConfig.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
11
mozglue/tests/moz.build
Normal file
11
mozglue/tests/moz.build
Normal file
|
|
@ -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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue