Bug 1391268 - 2. Expose mozglue linker error message to Java code; r?glandium draft
authorJim Chen <nchen@mozilla.com>
Wed, 31 Jan 2018 15:34:18 -0500
changeset 749616 20f0a95273efb12bd58c36a11691e0890e53c03a
parent 749615 2d18a06806fdbf37822fe1450719175ad91c5813
child 749617 9e0791e673e47a159b07ddd15062682a5dde8181
push id97462
push userbmo:nchen@mozilla.com
push dateWed, 31 Jan 2018 20:35:46 +0000
reviewersglandium
bugs1391268
milestone60.0a1
Bug 1391268 - 2. Expose mozglue linker error message to Java code; r?glandium This patch makes us save the last message from an ERROR() statement, and use that message when throwing an exception to Java code, in order to make lib loading crash messages a lot more useful. This does make ERROR() not thread-safe, but I think that is okay since it's only used by the single-threaded linker. MozReview-Commit-ID: fEXTYNKUUA
mozglue/android/APKOpen.cpp
mozglue/linker/ElfLoader.cpp
mozglue/linker/Logging.h
mozglue/linker/Mappable.cpp
--- a/mozglue/android/APKOpen.cpp
+++ b/mozglue/android/APKOpen.cpp
@@ -27,16 +27,17 @@
 #include <sys/time.h>
 #include <sys/syscall.h>
 #include <sys/resource.h>
 #include <sys/prctl.h>
 #include "sqlite3.h"
 #include "SQLiteBridge.h"
 #include "NSSBridge.h"
 #include "ElfLoader.h"
+#include "Logging.h"
 #include "application.ini.h"
 
 #include "mozilla/arm.h"
 #include "mozilla/Bootstrap.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
 #include "XREChildData.h"
 
@@ -107,16 +108,23 @@ JNI_Throw(JNIEnv* jenv, const char* clas
     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);
 }
 
+static void
+JNI_ThrowLinkerError(JNIEnv* jenv, const char* classname, const char* defaultMsg)
+{
+    JNI_Throw(jenv, classname, *gLastLinkerError ? gLastLinkerError : defaultMsg);
+    *gLastLinkerError = '\0';
+}
+
 namespace {
     JavaVM* sJavaVM;
 }
 
 void
 abortThroughJava(const char* msg)
 {
     struct sigaction sigact = {};
@@ -322,34 +330,34 @@ Java_org_mozilla_gecko_mozglue_GeckoLoad
   // 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");
+    JNI_ThrowLinkerError(jenv, "java/lang/Exception", "Error loading gecko libraries");
   }
   jenv->ReleaseStringUTFChars(jApkName, str);
 }
 
 extern "C" APKOPEN_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");
+      JNI_ThrowLinkerError(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" APKOPEN_EXPORT void MOZ_JNICALL
 Java_org_mozilla_gecko_mozglue_GeckoLoader_loadNSSLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName) {
   const char* str;
@@ -357,17 +365,17 @@ Java_org_mozilla_gecko_mozglue_GeckoLoad
   // 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");
+    JNI_ThrowLinkerError(jenv, "java/lang/Exception", "Error loading nss libraries");
   }
   __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load nss done\n");
   jenv->ReleaseStringUTFChars(jApkName, str);
 }
 
 static char**
 CreateArgvFromObjectArray(JNIEnv *jenv, jobjectArray jargs, int* length)
 {
--- a/mozglue/linker/ElfLoader.cpp
+++ b/mozglue/linker/ElfLoader.cpp
@@ -29,16 +29,18 @@
 extern "C" {
 
 inline int sigaltstack(const stack_t *ss, stack_t *oss) {
   return syscall(__NR_sigaltstack, ss, oss);
 }
 
 } /* extern "C" */
 #endif /* __ANDROID_API__ */
+
+char gLastLinkerError[256];
 #endif /* ANDROID */
 
 #ifdef __ARM_EABI__
 extern "C" MOZ_EXPORT const void *
 __gnu_Unwind_Find_exidx(void *pc, int *pcount) __attribute__((weak));
 #endif
 
 /* Ideally we'd #include <link.h>, but that's a world of pain
--- a/mozglue/linker/Logging.h
+++ b/mozglue/linker/Logging.h
@@ -5,19 +5,24 @@
 #ifndef Logging_h
 #define Logging_h
 
 #include "mozilla/Likely.h"
 #include "mozilla/MacroArgs.h"
 
 #ifdef ANDROID
 #include <android/log.h>
+#include <cstdio>
+extern char gLastLinkerError[256];
 #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__)
+#define ERROR(...) do { \
+    snprintf(gLastLinkerError, sizeof(gLastLinkerError), __VA_ARGS__); \
+    __android_log_write(ANDROID_LOG_ERROR, "GeckoLinker", gLastLinkerError); \
+  } while(0)
 #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
--- a/mozglue/linker/Mappable.cpp
+++ b/mozglue/linker/Mappable.cpp
@@ -223,17 +223,17 @@ MappableExtractFile::Create(const char *
                                        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 %" PRIuPTR, written);
     if (written != buffer.GetLength()) {
-      ERROR("Error decoding XZ file %s", file.get());
+      ERROR("Error decoding XZ file %s", name);
       return nullptr;
     }
   } else {
     return nullptr;
   }
 
   validator.CacheChecksum();
   return new MappableExtractFile(fd.forget(), file.release());