michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * This custom library loading code is only meant to be called michael@0: * during initialization. As a result, it takes no special michael@0: * precautions to be threadsafe. Any of the library loading functions michael@0: * like mozload should not be available to other code. michael@0: */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include "dlfcn.h" michael@0: #include "APKOpen.h" michael@0: #include michael@0: #include michael@0: #include michael@0: #include "Zip.h" michael@0: #include "sqlite3.h" michael@0: #include "SQLiteBridge.h" michael@0: #include "NSSBridge.h" michael@0: #include "ElfLoader.h" michael@0: #include "application.ini.h" michael@0: michael@0: /* Android headers don't define RUSAGE_THREAD */ michael@0: #ifndef RUSAGE_THREAD michael@0: #define RUSAGE_THREAD 1 michael@0: #endif michael@0: michael@0: #ifndef RELEASE_BUILD michael@0: /* Official builds have the debuggable flag set to false, which disables michael@0: * the backtrace dumper from bionic. However, as it is useful for native michael@0: * crashes happening before the crash reporter is registered, re-enable michael@0: * it on non release builds (i.e. nightly and aurora). michael@0: * Using a constructor so that it is re-enabled as soon as libmozglue.so michael@0: * is loaded. michael@0: */ michael@0: __attribute__((constructor)) michael@0: void make_dumpable() { michael@0: prctl(PR_SET_DUMPABLE, 1); michael@0: } michael@0: #endif michael@0: michael@0: extern "C" { michael@0: /* michael@0: * To work around http://code.google.com/p/android/issues/detail?id=23203 michael@0: * we don't link with the crt objects. In some configurations, this means michael@0: * a lack of the __dso_handle symbol because it is defined there, and michael@0: * depending on the android platform and ndk versions used, it may or may michael@0: * not be defined in libc.so. In the latter case, we fail to link. Defining michael@0: * it here as weak makes us provide the symbol when it's not provided by michael@0: * the crt objects, making the change transparent for future NDKs that michael@0: * would fix the original problem. On older NDKs, it is not a problem michael@0: * either because the way __dso_handle was used was already broken (and michael@0: * the custom linker works around it). michael@0: */ michael@0: NS_EXPORT __attribute__((weak)) void *__dso_handle; michael@0: } michael@0: michael@0: typedef int mozglueresult; michael@0: michael@0: enum StartupEvent { michael@0: #define mozilla_StartupTimeline_Event(ev, z) ev, michael@0: #include "StartupTimeline.h" michael@0: #undef mozilla_StartupTimeline_Event michael@0: MAX_STARTUP_EVENT_ID michael@0: }; michael@0: michael@0: using namespace mozilla; michael@0: michael@0: /** michael@0: * Local TimeStamp::Now()-compatible implementation used to record timestamps michael@0: * which will be passed to XRE_StartupTimelineRecord(). michael@0: */ michael@0: michael@0: static uint64_t TimeStamp_Now() michael@0: { michael@0: struct timespec ts; michael@0: int rv = clock_gettime(CLOCK_MONOTONIC, &ts); michael@0: michael@0: if (rv != 0) { michael@0: return 0; michael@0: } michael@0: michael@0: uint64_t baseNs = (uint64_t)ts.tv_sec * 1000000000; michael@0: return baseNs + (uint64_t)ts.tv_nsec; michael@0: } michael@0: michael@0: static struct mapping_info * lib_mapping = nullptr; michael@0: michael@0: NS_EXPORT const struct mapping_info * michael@0: getLibraryMapping() michael@0: { michael@0: return lib_mapping; michael@0: } michael@0: michael@0: void michael@0: JNI_Throw(JNIEnv* jenv, const char* classname, const char* msg) michael@0: { michael@0: __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Throw\n"); michael@0: jclass cls = jenv->FindClass(classname); michael@0: if (cls == nullptr) { michael@0: __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't find exception class (or exception pending) %s\n", classname); michael@0: exit(FAILURE); michael@0: } michael@0: int rc = jenv->ThrowNew(cls, msg); michael@0: if (rc < 0) { michael@0: __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Error throwing exception %s\n", msg); michael@0: exit(FAILURE); michael@0: } michael@0: jenv->DeleteLocalRef(cls); michael@0: } michael@0: michael@0: #define JNI_STUBS michael@0: #include "jni-stubs.inc" michael@0: #undef JNI_STUBS michael@0: michael@0: static void * xul_handle = nullptr; michael@0: #ifndef MOZ_FOLD_LIBS michael@0: static void * sqlite_handle = nullptr; michael@0: static void * nspr_handle = nullptr; michael@0: static void * plc_handle = nullptr; michael@0: #else michael@0: #define sqlite_handle nss_handle michael@0: #define nspr_handle nss_handle michael@0: #define plc_handle nss_handle michael@0: #endif michael@0: static void * nss_handle = nullptr; michael@0: michael@0: template inline void michael@0: xul_dlsym(const char *symbolName, T *value) michael@0: { michael@0: *value = (T) (uintptr_t) __wrap_dlsym(xul_handle, symbolName); michael@0: } michael@0: michael@0: static int mapping_count = 0; michael@0: michael@0: #define MAX_MAPPING_INFO 32 michael@0: michael@0: extern "C" void michael@0: report_mapping(char *name, void *base, uint32_t len, uint32_t offset) michael@0: { michael@0: if (mapping_count >= MAX_MAPPING_INFO) michael@0: return; michael@0: michael@0: struct mapping_info *info = &lib_mapping[mapping_count++]; michael@0: info->name = strdup(name); michael@0: info->base = (uintptr_t)base; michael@0: info->len = len; michael@0: info->offset = offset; michael@0: } michael@0: michael@0: static mozglueresult michael@0: loadGeckoLibs(const char *apkName) michael@0: { michael@0: chdir(getenv("GRE_HOME")); michael@0: michael@0: uint64_t t0 = TimeStamp_Now(); michael@0: struct rusage usage1_thread, usage1; michael@0: getrusage(RUSAGE_THREAD, &usage1_thread); michael@0: getrusage(RUSAGE_SELF, &usage1); michael@0: michael@0: RefPtr zip = ZipCollection::GetZip(apkName); michael@0: michael@0: char *file = new char[strlen(apkName) + sizeof("!/assets/libxul.so")]; michael@0: sprintf(file, "%s!/assets/libxul.so", apkName); michael@0: xul_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY); michael@0: delete[] file; michael@0: michael@0: if (!xul_handle) { michael@0: __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libxul!"); michael@0: return FAILURE; michael@0: } michael@0: michael@0: #define JNI_BINDINGS michael@0: #include "jni-stubs.inc" michael@0: #undef JNI_BINDINGS michael@0: michael@0: void (*XRE_StartupTimelineRecord)(int, uint64_t); michael@0: xul_dlsym("XRE_StartupTimelineRecord", &XRE_StartupTimelineRecord); michael@0: michael@0: uint64_t t1 = TimeStamp_Now(); michael@0: struct rusage usage2_thread, usage2; michael@0: getrusage(RUSAGE_THREAD, &usage2_thread); michael@0: getrusage(RUSAGE_SELF, &usage2); michael@0: michael@0: #define RUSAGE_TIMEDIFF(u1, u2, field) \ michael@0: ((u2.ru_ ## field.tv_sec - u1.ru_ ## field.tv_sec) * 1000 + \ michael@0: (u2.ru_ ## field.tv_usec - u1.ru_ ## field.tv_usec) / 1000) michael@0: michael@0: __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loaded libs in %lldms total, %ldms(%ldms) user, %ldms(%ldms) system, %ld(%ld) faults", michael@0: (t1 - t0) / 1000000, michael@0: RUSAGE_TIMEDIFF(usage1_thread, usage2_thread, utime), michael@0: RUSAGE_TIMEDIFF(usage1, usage2, utime), michael@0: RUSAGE_TIMEDIFF(usage1_thread, usage2_thread, stime), michael@0: RUSAGE_TIMEDIFF(usage1, usage2, stime), michael@0: usage2_thread.ru_majflt - usage1_thread.ru_majflt, michael@0: usage2.ru_majflt - usage1.ru_majflt); michael@0: michael@0: XRE_StartupTimelineRecord(LINKER_INITIALIZED, t0); michael@0: XRE_StartupTimelineRecord(LIBRARIES_LOADED, t1); michael@0: return SUCCESS; michael@0: } michael@0: michael@0: static mozglueresult loadNSSLibs(const char *apkName); michael@0: michael@0: static mozglueresult michael@0: loadSQLiteLibs(const char *apkName) michael@0: { michael@0: if (sqlite_handle) michael@0: return SUCCESS; michael@0: michael@0: #ifdef MOZ_FOLD_LIBS michael@0: if (loadNSSLibs(apkName) != SUCCESS) michael@0: return FAILURE; michael@0: #else michael@0: chdir(getenv("GRE_HOME")); michael@0: michael@0: RefPtr zip = ZipCollection::GetZip(apkName); michael@0: if (!lib_mapping) { michael@0: lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping)); michael@0: } michael@0: michael@0: char *file = new char[strlen(apkName) + sizeof("!/assets/libmozsqlite3.so")]; michael@0: sprintf(file, "%s!/assets/libmozsqlite3.so", apkName); michael@0: sqlite_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY); michael@0: delete [] file; michael@0: michael@0: if (!sqlite_handle) { michael@0: __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libmozsqlite3!"); michael@0: return FAILURE; michael@0: } michael@0: #endif michael@0: michael@0: setup_sqlite_functions(sqlite_handle); michael@0: return SUCCESS; michael@0: } michael@0: michael@0: static mozglueresult michael@0: loadNSSLibs(const char *apkName) michael@0: { michael@0: if (nss_handle && nspr_handle && plc_handle) michael@0: return SUCCESS; michael@0: michael@0: chdir(getenv("GRE_HOME")); michael@0: michael@0: RefPtr zip = ZipCollection::GetZip(apkName); michael@0: if (!lib_mapping) { michael@0: lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping)); michael@0: } michael@0: michael@0: char *file = new char[strlen(apkName) + sizeof("!/assets/libnss3.so")]; michael@0: sprintf(file, "%s!/assets/libnss3.so", apkName); michael@0: nss_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY); michael@0: delete [] file; michael@0: michael@0: #ifndef MOZ_FOLD_LIBS michael@0: file = new char[strlen(apkName) + sizeof("!/assets/libnspr4.so")]; michael@0: sprintf(file, "%s!/assets/libnspr4.so", apkName); michael@0: nspr_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY); michael@0: delete [] file; michael@0: michael@0: file = new char[strlen(apkName) + sizeof("!/assets/libplc4.so")]; michael@0: sprintf(file, "%s!/assets/libplc4.so", apkName); michael@0: plc_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY); michael@0: delete [] file; michael@0: #endif michael@0: michael@0: if (!nss_handle) { michael@0: __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libnss3!"); michael@0: return FAILURE; michael@0: } michael@0: michael@0: #ifndef MOZ_FOLD_LIBS michael@0: if (!nspr_handle) { michael@0: __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libnspr4!"); michael@0: return FAILURE; michael@0: } michael@0: michael@0: if (!plc_handle) { michael@0: __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libplc4!"); michael@0: return FAILURE; michael@0: } michael@0: #endif michael@0: michael@0: return setup_nss_functions(nss_handle, nspr_handle, plc_handle); michael@0: } michael@0: michael@0: extern "C" NS_EXPORT void JNICALL michael@0: Java_org_mozilla_gecko_mozglue_GeckoLoader_loadGeckoLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName) michael@0: { michael@0: const char* str; michael@0: // XXX: java doesn't give us true UTF8, we should figure out something michael@0: // better to do here michael@0: str = jenv->GetStringUTFChars(jApkName, nullptr); michael@0: if (str == nullptr) michael@0: return; michael@0: michael@0: int res = loadGeckoLibs(str); michael@0: if (res != SUCCESS) { michael@0: JNI_Throw(jenv, "java/lang/Exception", "Error loading gecko libraries"); michael@0: } michael@0: jenv->ReleaseStringUTFChars(jApkName, str); michael@0: } michael@0: michael@0: extern "C" NS_EXPORT void JNICALL michael@0: Java_org_mozilla_gecko_mozglue_GeckoLoader_loadSQLiteLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName, jboolean jShouldExtract) { michael@0: if (jShouldExtract) { michael@0: putenv("MOZ_LINKER_EXTRACT=1"); michael@0: } michael@0: michael@0: const char* str; michael@0: // XXX: java doesn't give us true UTF8, we should figure out something michael@0: // better to do here michael@0: str = jenv->GetStringUTFChars(jApkName, nullptr); michael@0: if (str == nullptr) michael@0: return; michael@0: michael@0: __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load sqlite start\n"); michael@0: mozglueresult rv = loadSQLiteLibs(str); michael@0: if (rv != SUCCESS) { michael@0: JNI_Throw(jenv, "java/lang/Exception", "Error loading sqlite libraries"); michael@0: } michael@0: __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load sqlite done\n"); michael@0: jenv->ReleaseStringUTFChars(jApkName, str); michael@0: } michael@0: michael@0: extern "C" NS_EXPORT void JNICALL michael@0: Java_org_mozilla_gecko_mozglue_GeckoLoader_loadNSSLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName, jboolean jShouldExtract) { michael@0: if (jShouldExtract) { michael@0: putenv("MOZ_LINKER_EXTRACT=1"); michael@0: } michael@0: michael@0: const char* str; michael@0: // XXX: java doesn't give us true UTF8, we should figure out something michael@0: // better to do here michael@0: str = jenv->GetStringUTFChars(jApkName, nullptr); michael@0: if (str == nullptr) michael@0: return; michael@0: michael@0: __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load nss start\n"); michael@0: mozglueresult rv = loadNSSLibs(str); michael@0: if (rv != SUCCESS) { michael@0: JNI_Throw(jenv, "java/lang/Exception", "Error loading nss libraries"); michael@0: } michael@0: __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load nss done\n"); michael@0: jenv->ReleaseStringUTFChars(jApkName, str); michael@0: } michael@0: michael@0: typedef void (*GeckoStart_t)(void *, const nsXREAppData *); michael@0: michael@0: extern "C" NS_EXPORT void JNICALL michael@0: Java_org_mozilla_gecko_mozglue_GeckoLoader_nativeRun(JNIEnv *jenv, jclass jc, jstring jargs) michael@0: { michael@0: GeckoStart_t GeckoStart; michael@0: xul_dlsym("GeckoStart", &GeckoStart); michael@0: if (GeckoStart == nullptr) michael@0: return; michael@0: // XXX: java doesn't give us true UTF8, we should figure out something michael@0: // better to do here michael@0: int len = jenv->GetStringUTFLength(jargs); michael@0: // GeckoStart needs to write in the args buffer, so we need a copy. michael@0: char *args = (char *) malloc(len + 1); michael@0: jenv->GetStringUTFRegion(jargs, 0, len, args); michael@0: args[len] = '\0'; michael@0: GeckoStart(args, &sAppData); michael@0: free(args); michael@0: } michael@0: michael@0: typedef int GeckoProcessType; michael@0: michael@0: extern "C" NS_EXPORT mozglueresult michael@0: ChildProcessInit(int argc, char* argv[]) michael@0: { michael@0: int i; michael@0: for (i = 0; i < (argc - 1); i++) { michael@0: if (strcmp(argv[i], "-greomni")) michael@0: continue; michael@0: michael@0: i = i + 1; michael@0: break; michael@0: } michael@0: michael@0: if (loadNSSLibs(argv[i]) != SUCCESS) { michael@0: return FAILURE; michael@0: } michael@0: if (loadSQLiteLibs(argv[i]) != SUCCESS) { michael@0: return FAILURE; michael@0: } michael@0: if (loadGeckoLibs(argv[i]) != SUCCESS) { michael@0: return FAILURE; michael@0: } michael@0: michael@0: GeckoProcessType (*fXRE_StringToChildProcessType)(char*); michael@0: xul_dlsym("XRE_StringToChildProcessType", &fXRE_StringToChildProcessType); michael@0: michael@0: mozglueresult (*fXRE_InitChildProcess)(int, char**, GeckoProcessType); michael@0: xul_dlsym("XRE_InitChildProcess", &fXRE_InitChildProcess); michael@0: michael@0: GeckoProcessType proctype = fXRE_StringToChildProcessType(argv[--argc]); michael@0: michael@0: return fXRE_InitChildProcess(argc, argv, proctype); michael@0: } michael@0: