Back out bf4b1d3c624e (bug 732069) for startup crashes and failure to start in tests, a=mfinkle
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 17 Mar 2012 19:00:35 -0700
changeset 91852 1eb6d3a4018b49b6ba37040d753ef0df259f0d58
parent 91851 abf433daab8964f4a23b3300370e6e2cfcd28ce3
child 91853 5e80b801b6952d825bf1c7cd823e079c3a81c598
push idunknown
push userunknown
push dateunknown
reviewersmfinkle
bugs732069
milestone13.0a2
backs outbf4b1d3c624e3f217866ca5f32a72e566efef0ac
Back out bf4b1d3c624e (bug 732069) for startup crashes and failure to start in tests, a=mfinkle
embedding/android/GeckoAppShell.java
mobile/android/base/GeckoAppShell.java
mozglue/android/APKOpen.cpp
other-licenses/android/dlfcn.c
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -109,18 +109,18 @@ public class GeckoAppShell
     // helper methods
     public static native void setSurfaceView(GeckoSurfaceView sv);
     public static native void putenv(String map);
     public static native void onResume();
     public static native void onLowMemory();
     public static native void callObserver(String observerKey, String topic, String data);
     public static native void removeObserver(String observerKey);
     public static native void loadGeckoLibsNative(String apkName);
-    public static native void loadSQLiteLibsNative(String apkName);
-    public static native void loadNSSLibsNative(String apkName);
+    public static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract);
+    public static native void loadNSSLibsNative(String apkName, boolean shouldExtract);
     public static native void onChangeNetworkLinkStatus(String status);
     public static native void reportJavaCrash(String stack);
 
     public static native void processNextNativeEvent();
 
     public static native void notifyBatteryChange(double aLevel, boolean aCharging, double aRemainingTime);
 
     public static native void notifySmsReceived(String aSender, String aBody, long aTimestamp);
@@ -393,18 +393,18 @@ public class GeckoAppShell
                 Iterator cacheFiles = Arrays.asList(files).iterator();
                 while (cacheFiles.hasNext()) {
                     File libFile = (File)cacheFiles.next();
                     if (libFile.getName().endsWith(".so"))
                         libFile.delete();
                 }
             }
         }
-        loadSQLiteLibsNative(apkName);
-        loadNSSLibsNative(apkName);
+        loadSQLiteLibsNative(apkName, extractLibs);
+        loadNSSLibsNative(apkName, extractLibs);
         loadGeckoLibsNative(apkName);
     }
 
     private static void putLocaleEnv() {
         GeckoAppShell.putenv("LANG=" + Locale.getDefault().toString());
         NumberFormat nf = NumberFormat.getInstance();
         if (nf instanceof DecimalFormat) {
             DecimalFormat df = (DecimalFormat)nf;
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -136,18 +136,18 @@ public class GeckoAppShell
     //    public static native void setSurfaceView(GeckoSurfaceView sv);
     public static native void setSoftwareLayerClient(GeckoSoftwareLayerClient client);
     public static native void putenv(String map);
     public static native void onResume();
     public static native void onLowMemory();
     public static native void callObserver(String observerKey, String topic, String data);
     public static native void removeObserver(String observerKey);
     public static native void loadGeckoLibsNative(String apkName);
-    public static native void loadSQLiteLibsNative(String apkName);
-    public static native void loadNSSLibsNative(String apkName);
+    public static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract);
+    public static native void loadNSSLibsNative(String apkName, boolean shouldExtract);
     public static native void onChangeNetworkLinkStatus(String status);
 
     public static void registerGlobalExceptionHandler() {
         Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
             public void uncaughtException(Thread thread, Throwable e) {
                 Log.e(LOGTAG, ">>> REPORTING UNCAUGHT EXCEPTION FROM THREAD "
                               + thread.getId() + " (\"" + thread.getName() + "\")", e);
                 reportJavaCrash(getStackTraceString(e));
@@ -262,16 +262,25 @@ public class GeckoAppShell
 
         // The package data lib directory isn't placed in ld.so's
         // search path, so we have to manually load libraries that
         // libxul will depend on.  Not ideal.
         GeckoProfile profile = GeckoProfile.get(context);
 
         File cacheFile = getCacheDir(context);
         putenv("GRE_HOME=" + getGREDir(context).getPath());
+        File[] files = cacheFile.listFiles();
+        if (files != null) {
+            Iterator<File> cacheFiles = Arrays.asList(files).iterator();
+            while (cacheFiles.hasNext()) {
+                File libFile = cacheFiles.next();
+                if (libFile.getName().endsWith(".so"))
+                    libFile.delete();
+            }
+        }
 
         // setup the libs cache
         String linkerCache = System.getenv("MOZ_LINKER_CACHE");
         if (System.getenv("MOZ_LINKER_CACHE") == null) {
             GeckoAppShell.putenv("MOZ_LINKER_CACHE=" + cacheFile.getPath());
         }
         sLibsSetup = true;
     }
@@ -354,30 +363,30 @@ public class GeckoAppShell
         if (sSQLiteLibsLoaded)
             return;
         synchronized(sSQLiteLibsLoaded) {
             if (sSQLiteLibsLoaded)
                 return;
             loadMozGlue();
             // the extract libs parameter is being removed in bug 732069
             loadLibsSetup(context);
-            loadSQLiteLibsNative(apkName);
+            loadSQLiteLibsNative(apkName, false);
             sSQLiteLibsLoaded = true;
         }
     }
 
     public static void loadNSSLibs(Context context, String apkName) {
         if (sNSSLibsLoaded)
             return;
         synchronized(sNSSLibsLoaded) {
             if (sNSSLibsLoaded)
                 return;
             loadMozGlue();
             loadLibsSetup(context);
-            loadNSSLibsNative(apkName);
+            loadNSSLibsNative(apkName, false);
             sNSSLibsLoaded = true;
         }
     }
 
     public static void loadMozGlue() {
         System.loadLibrary("mozglue");
     }
 
--- a/mozglue/android/APKOpen.cpp
+++ b/mozglue/android/APKOpen.cpp
@@ -349,16 +349,90 @@ SHELL_WRAPPER3(notifyReadingMessageListF
 
 static void * xul_handle = NULL;
 static void * sqlite_handle = NULL;
 static void * nss_handle = NULL;
 static void * nspr_handle = NULL;
 static void * plc_handle = NULL;
 static bool simple_linker_initialized = false;
 
+#ifdef MOZ_OLD_LINKER
+static time_t apk_mtime = 0;
+#ifdef DEBUG
+extern "C" int extractLibs = 1;
+#else
+extern "C" int extractLibs = 0;
+#endif
+
+static void
+extractFile(const char * path, Zip::Stream &s)
+{
+  uint32_t size = s.GetUncompressedSize();
+
+  struct stat status;
+  if (!stat(path, &status) &&
+      status.st_size == size &&
+      apk_mtime < status.st_mtime)
+    return;
+
+  int fd = open(path, O_CREAT | O_NOATIME | O_TRUNC | O_RDWR, S_IRWXU);
+  if (fd == -1) {
+    __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't open %s to decompress library", path);
+    return;
+  }
+
+  if (ftruncate(fd, size) == -1) {
+    __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't ftruncate %s to decompress library", path);
+    close(fd);
+    return;
+  }
+
+  void * buf = mmap(NULL, size, PROT_READ | PROT_WRITE,
+                    MAP_SHARED, fd, 0);
+  if (buf == (void *)-1) {
+    __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't mmap decompression buffer");
+    close(fd);
+    return;
+  }
+
+  z_stream strm = {
+    next_in: (Bytef *)s.GetBuffer(),
+    avail_in: s.GetSize(),
+    total_in: 0,
+
+    next_out: (Bytef *)buf,
+    avail_out: size,
+    total_out: 0
+  };
+
+  int ret;
+  ret = inflateInit2(&strm, -MAX_WBITS);
+  if (ret != Z_OK)
+    __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "inflateInit failed: %s", strm.msg);
+
+  if (inflate(&strm, Z_FINISH) != Z_STREAM_END)
+    __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "inflate failed: %s", strm.msg);
+
+  if (strm.total_out != size)
+    __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "extracted %d, expected %d!", strm.total_out, size);
+
+  ret = inflateEnd(&strm);
+  if (ret != Z_OK)
+    __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "inflateEnd failed: %s", strm.msg);
+
+  close(fd);
+#ifdef ANDROID_ARM_LINKER
+  /* We just extracted data that is going to be executed in the future.
+   * We thus need to ensure Instruction and Data cache coherency. */
+  cacheflush((unsigned) buf, (unsigned) buf + size, 0);
+#endif
+  munmap(buf, size);
+}
+#endif
+
 #if defined(MOZ_CRASHREPORTER) || defined(MOZ_OLD_LINKER)
 static void
 extractLib(Zip::Stream &s, void * dest)
 {
   z_stream strm = {
     next_in: (Bytef *)s.GetBuffer(),
     avail_in: s.GetSize(),
     total_in: 0,
@@ -472,16 +546,35 @@ static void * mozload(const char * path,
   gettimeofday(&t0, 0);
 #endif
 
   void *handle;
   Zip::Stream s;
   if (!zip->GetStream(path, &s))
     return NULL;
 
+  if (extractLibs) {
+    char fullpath[PATH_MAX];
+    snprintf(fullpath, PATH_MAX, "%s/%s", getenv("MOZ_LINKER_CACHE"), path);
+    __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "resolved %s to %s", path, fullpath);
+    extractFile(fullpath, s);
+    handle = __wrap_dlopen(fullpath, RTLD_LAZY);
+    if (!handle)
+      __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't load %s because %s", fullpath, __wrap_dlerror());
+#ifdef DEBUG
+    gettimeofday(&t1, 0);
+    __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "%s: spent %d", path,
+                        (((long long)t1.tv_sec * 1000000LL) +
+                          (long long)t1.tv_usec) -
+                        (((long long)t0.tv_sec * 1000000LL) +
+                          (long long)t0.tv_usec));
+#endif
+    return handle;
+  }
+
   bool skipLibCache = false;
   int fd;
   void * buf = NULL;
   uint32_t lib_size = s.GetUncompressedSize();
   int cache_fd = 0;
   if (s.GetType() == Zip::Stream::DEFLATE) {
     cache_fd = lookupLibCacheFd(path);
     fd = cache_fd;
@@ -589,16 +682,22 @@ report_mapping(char *name, void *base, u
 extern "C" void simple_linker_init(void);
 #endif
 
 static mozglueresult
 loadGeckoLibs(const char *apkName)
 {
   chdir(getenv("GRE_HOME"));
 
+#ifdef MOZ_OLD_LINKER
+  struct stat status;
+  if (!stat(apkName, &status))
+    apk_mtime = status.st_mtime;
+#endif
+
   struct timeval t0, t1;
   gettimeofday(&t0, 0);
   struct rusage usage1;
   getrusage(RUSAGE_THREAD, &usage1);
   
   RefPtr<Zip> zip = new Zip(apkName);
 
 #ifdef MOZ_CRASHREPORTER
@@ -679,16 +778,20 @@ static int loadSQLiteLibs(const char *ap
 {
   chdir(getenv("GRE_HOME"));
 
 #ifdef MOZ_OLD_LINKER
   if (!simple_linker_initialized) {
     simple_linker_init();
     simple_linker_initialized = true;
   }
+
+  struct stat status;
+  if (!stat(apkName, &status))
+    apk_mtime = status.st_mtime;
 #endif
 
   RefPtr<Zip> zip = new Zip(apkName);
   if (!lib_mapping) {
     lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping));
   }
 
 #ifdef MOZ_CRASHREPORTER
@@ -726,16 +829,20 @@ loadNSSLibs(const char *apkName)
   __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "loadNSSLibs");
   chdir(getenv("GRE_HOME"));
 
 #ifdef MOZ_OLD_LINKER
   if (!simple_linker_initialized) {
     simple_linker_init();
     simple_linker_initialized = true;
   }
+
+  struct stat status;
+  if (!stat(apkName, &status))
+    apk_mtime = status.st_mtime;
 #endif
 
   Zip *zip = new Zip(apkName);
   if (!lib_mapping) {
     lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping));
   }
 
 #ifdef MOZ_CRASHREPORTER
@@ -812,18 +919,25 @@ Java_org_mozilla_gecko_GeckoAppShell_loa
   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 JNICALL
-Java_org_mozilla_gecko_GeckoAppShell_loadSQLiteLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName) {
-  putenv("MOZ_LINKER_EXTRACT=1");
+Java_org_mozilla_gecko_GeckoAppShell_loadSQLiteLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName, jboolean jShouldExtract) {
+  if (jShouldExtract) {
+#ifdef MOZ_OLD_LINKER
+    extractLibs = 1;
+#else
+    putenv("MOZ_LINKER_EXTRACT=1");
+#endif
+  }
+
   const char* str;
   // XXX: java doesn't give us true UTF8, we should figure out something
   // better to do here
   str = jenv->GetStringUTFChars(jApkName, NULL);
   if (str == NULL)
     return;
 
   __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load sqlite start\n");
@@ -831,18 +945,25 @@ Java_org_mozilla_gecko_GeckoAppShell_loa
   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 JNICALL
-Java_org_mozilla_gecko_GeckoAppShell_loadNSSLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName) {
-  putenv("MOZ_LINKER_EXTRACT=1");
+Java_org_mozilla_gecko_GeckoAppShell_loadNSSLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName, jboolean jShouldExtract) {
+  if (jShouldExtract) {
+#ifdef MOZ_OLD_LINKER
+    extractLibs = 1;
+#else
+    putenv("MOZ_LINKER_EXTRACT=1");
+#endif
+  }
+
   const char* str;
   // XXX: java doesn't give us true UTF8, we should figure out something
   // better to do here
   str = jenv->GetStringUTFChars(jApkName, NULL);
   if (str == NULL)
     return;
 
   __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load nss start\n");
--- a/other-licenses/android/dlfcn.c
+++ b/other-licenses/android/dlfcn.c
@@ -38,26 +38,30 @@ static const char *dl_errors[] = {
     [DL_ERR_SYMBOL_NOT_FOUND] = "Symbol not found",
     [DL_ERR_SYMBOL_NOT_GLOBAL] = "Symbol is not global",
 };
 
 #define likely(expr)   __builtin_expect (expr, 1)
 #define unlikely(expr) __builtin_expect (expr, 0)
 
 static pthread_mutex_t dl_lock = PTHREAD_MUTEX_INITIALIZER;
+extern int extractLibs;
 
 static void set_dlerror(int err)
 {
     format_buffer(dl_err_buf, sizeof(dl_err_buf), "%s: %s", dl_errors[err],
              linker_get_error());
     dl_err_str = (const char *)&dl_err_buf[0];
 }
 
 void *__wrap_dlopen(const char *filename, int flag)
 {
+    if (extractLibs)
+        return dlopen(filename, flag);
+
     soinfo *ret;
 
     pthread_mutex_lock(&dl_lock);
     ret = find_library(filename);
     if (unlikely(ret == NULL)) {
         set_dlerror(DL_ERR_CANNOT_LOAD_LIBRARY);
     } else {
         ret->refcount++;
@@ -79,23 +83,29 @@ void *moz_mapped_dlopen(const char *file
         ret->refcount++;
     }
     pthread_mutex_unlock(&dl_lock);
     return ret;
 }
 
 const char *__wrap_dlerror(void)
 {
+    if (extractLibs)
+        return dlerror();
+
     const char *tmp = dl_err_str;
     dl_err_str = NULL;
     return (const char *)tmp;
 }
 
 void *__wrap_dlsym(void *handle, const char *symbol)
 {
+    if (extractLibs)
+        return dlsym(handle, symbol);
+
     soinfo *found;
     Elf32_Sym *sym;
     unsigned bind;
 
     pthread_mutex_lock(&dl_lock);
 
     if(unlikely(handle == 0)) { 
         set_dlerror(DL_ERR_INVALID_LIBRARY_HANDLE);
@@ -168,16 +178,19 @@ int __wrap_dladdr(void *addr, Dl_info *i
 
     pthread_mutex_unlock(&dl_lock);
 
     return ret;
 }
 
 int __wrap_dlclose(void *handle)
 {
+    if (extractLibs)
+        return dlclose(handle);
+
     pthread_mutex_lock(&dl_lock);
     (void)unload_library((soinfo*)handle);
     pthread_mutex_unlock(&dl_lock);
     return 0;
 }
 
 #if defined(ANDROID_ARM_LINKER)
 //                     0000000 00011111 111112 22222222 2333333 333344444444445555555