Add a function that fails softly if libdexfile(d)_external.so cannot be
loaded.

libunwindstack needs it since it is used in situations where we cannot
guarantee that libdexfile_external.so is available, e.g. from
libc_malloc_debug.so in the bootstrap Bionic.

Test: m test-art-host-gtest-art_libdexfile_support_tests
Test: m test-art-host-gtest-art_libdexfile_support_static_tests
Bug: 139408016
Change-Id: I0c311222baaafbf5c3a0f7d3f9f2d83d4abbaf90
diff --git a/libdexfile/external/dex_file_supp.cc b/libdexfile/external/dex_file_supp.cc
index 446fe16..e207953 100644
--- a/libdexfile/external/dex_file_supp.cc
+++ b/libdexfile/external/dex_file_supp.cc
@@ -28,65 +28,81 @@
 namespace art_api {
 namespace dex {
 
+#define FOR_ALL_DLFUNCS(MACRO) \
+  MACRO(DexString, ExtDexFileMakeString) \
+  MACRO(DexString, ExtDexFileGetString) \
+  MACRO(DexString, ExtDexFileFreeString) \
+  MACRO(DexFile, ExtDexFileOpenFromMemory) \
+  MACRO(DexFile, ExtDexFileOpenFromFd) \
+  MACRO(DexFile, ExtDexFileGetMethodInfoForOffset) \
+  MACRO(DexFile, ExtDexFileGetAllMethodInfos) \
+  MACRO(DexFile, ExtDexFileFree)
+
 #ifdef STATIC_LIB
-#define DEFINE_DLFUNC_PTR(CLASS, DLFUNC) decltype(DLFUNC)* CLASS::g_##DLFUNC = DLFUNC
+#define DEFINE_DLFUNC_PTR(CLASS, DLFUNC) decltype(DLFUNC)* CLASS::g_##DLFUNC = DLFUNC;
 #else
-#define DEFINE_DLFUNC_PTR(CLASS, DLFUNC) decltype(DLFUNC)* CLASS::g_##DLFUNC = nullptr
+#define DEFINE_DLFUNC_PTR(CLASS, DLFUNC) decltype(DLFUNC)* CLASS::g_##DLFUNC = nullptr;
 #endif
-
-DEFINE_DLFUNC_PTR(DexString, ExtDexFileMakeString);
-DEFINE_DLFUNC_PTR(DexString, ExtDexFileGetString);
-DEFINE_DLFUNC_PTR(DexString, ExtDexFileFreeString);
-DEFINE_DLFUNC_PTR(DexFile, ExtDexFileOpenFromMemory);
-DEFINE_DLFUNC_PTR(DexFile, ExtDexFileOpenFromFd);
-DEFINE_DLFUNC_PTR(DexFile, ExtDexFileGetMethodInfoForOffset);
-DEFINE_DLFUNC_PTR(DexFile, ExtDexFileGetAllMethodInfos);
-DEFINE_DLFUNC_PTR(DexFile, ExtDexFileFree);
-
+FOR_ALL_DLFUNCS(DEFINE_DLFUNC_PTR)
 #undef DEFINE_DLFUNC_PTR
 
-void LoadLibdexfileExternal() {
+bool TryLoadLibdexfileExternal([[maybe_unused]] std::string* err_msg) {
 #if defined(STATIC_LIB)
   // Nothing to do here since all function pointers are initialised statically.
+  return true;
 #elif defined(NO_DEXFILE_SUPPORT)
-  LOG_FATAL("Dex file support not available.");
+  *err_msg = "Dex file support not available.";
+  return false;
 #else
-  static std::once_flag dlopen_once;
-  std::call_once(dlopen_once, []() {
-    // Check which version is already loaded to avoid loading both debug and release builds.
-    // We might also be backtracing from separate process, in which case neither is loaded.
+  // Use a plain old mutex since we want to try again if loading fails (to set
+  // err_msg, if nothing else).
+  static std::mutex load_mutex;
+  static bool is_loaded = false;
+  std::lock_guard<std::mutex> lock(load_mutex);
+
+  if (!is_loaded) {
+    // Check which version is already loaded to avoid loading both debug and
+    // release builds. We might also be backtracing from separate process, in
+    // which case neither is loaded.
     const char* so_name = "libdexfiled_external.so";
     void* handle = dlopen(so_name, RTLD_NOLOAD | RTLD_NOW | RTLD_NODELETE);
     if (handle == nullptr) {
       so_name = "libdexfile_external.so";
       handle = dlopen(so_name, RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE);
     }
-    LOG_ALWAYS_FATAL_IF(handle == nullptr, "Failed to load %s: %s", so_name, dlerror());
+    if (handle == nullptr) {
+      *err_msg = dlerror();
+      return false;
+    }
 
-#define SET_DLFUNC_PTR(CLASS, DLFUNC) \
-  do { \
-    CLASS::g_##DLFUNC = reinterpret_cast<decltype(DLFUNC)*>(dlsym(handle, #DLFUNC)); \
-    LOG_ALWAYS_FATAL_IF(CLASS::g_##DLFUNC == nullptr, \
-                        "Failed to find %s in %s: %s", \
-                        #DLFUNC, \
-                        so_name, \
-                        dlerror()); \
-  } while (0)
+#define RESOLVE_DLFUNC_PTR(CLASS, DLFUNC) \
+    decltype(DLFUNC)* DLFUNC##_ptr = reinterpret_cast<decltype(DLFUNC)*>(dlsym(handle, #DLFUNC)); \
+    if (DLFUNC == nullptr) { \
+      *err_msg = dlerror(); \
+      return false; \
+    }
+    FOR_ALL_DLFUNCS(RESOLVE_DLFUNC_PTR);
+#undef RESOLVE_DLFUNC_PTR
 
-    SET_DLFUNC_PTR(DexString, ExtDexFileMakeString);
-    SET_DLFUNC_PTR(DexString, ExtDexFileGetString);
-    SET_DLFUNC_PTR(DexString, ExtDexFileFreeString);
-    SET_DLFUNC_PTR(DexFile, ExtDexFileOpenFromMemory);
-    SET_DLFUNC_PTR(DexFile, ExtDexFileOpenFromFd);
-    SET_DLFUNC_PTR(DexFile, ExtDexFileGetMethodInfoForOffset);
-    SET_DLFUNC_PTR(DexFile, ExtDexFileGetAllMethodInfos);
-    SET_DLFUNC_PTR(DexFile, ExtDexFileFree);
-
+#define SET_DLFUNC_PTR(CLASS, DLFUNC) CLASS::g_##DLFUNC = DLFUNC##_ptr;
+    FOR_ALL_DLFUNCS(SET_DLFUNC_PTR);
 #undef SET_DLFUNC_PTR
-  });
+
+    is_loaded = true;
+  }
+
+  return is_loaded;
 #endif  // !defined(NO_DEXFILE_SUPPORT) && !defined(STATIC_LIB)
 }
 
+void LoadLibdexfileExternal() {
+#ifndef STATIC_LIB
+  if (std::string err_msg; !TryLoadLibdexfileExternal(&err_msg)) {
+    LOG_ALWAYS_FATAL("%s", err_msg.c_str());
+  }
+#endif
+}
+
 DexFile::~DexFile() { g_ExtDexFileFree(ext_dex_file_); }
 
 MethodInfo DexFile::AbsorbMethodInfo(const ExtDexFileMethodInfo& ext_method_info) {
diff --git a/libdexfile/external/include/art_api/dex_file_support.h b/libdexfile/external/include/art_api/dex_file_support.h
index a98ff0e..9287dba 100644
--- a/libdexfile/external/include/art_api/dex_file_support.h
+++ b/libdexfile/external/include/art_api/dex_file_support.h
@@ -33,6 +33,12 @@
 namespace art_api {
 namespace dex {
 
+// Returns true if libdexfile_external.so is already loaded. Otherwise tries to
+// load it and returns true if successful. Otherwise returns false and sets
+// *error_msg. If false is returned then calling any function below may abort
+// the process. Thread safe.
+bool TryLoadLibdexfileExternal(std::string* error_msg);
+
 // Loads the libdexfile_external.so library and sets up function pointers.
 // Aborts with a fatal error on any error. For internal use by the classes
 // below.
@@ -75,12 +81,13 @@
   }
 
  private:
-  friend void LoadLibdexfileExternal();
+  friend bool TryLoadLibdexfileExternal(std::string* error_msg);
   friend class DexFile;
   friend bool operator==(const DexString&, const DexString&);
   explicit DexString(const ExtDexFileString* ext_string) : ext_string_(ext_string) {}
   const ExtDexFileString* ext_string_;  // Owned instance. Never nullptr.
 
+  // These are initialized by TryLoadLibdexfileExternal.
   static decltype(ExtDexFileMakeString)* g_ExtDexFileMakeString;
   static decltype(ExtDexFileGetString)* g_ExtDexFileGetString;
   static decltype(ExtDexFileFreeString)* g_ExtDexFileFreeString;
@@ -203,7 +210,7 @@
   }
 
  private:
-  friend void LoadLibdexfileExternal();
+  friend bool TryLoadLibdexfileExternal(std::string* error_msg);
   explicit DexFile(ExtDexFile* ext_dex_file) : ext_dex_file_(ext_dex_file) {}
   ExtDexFile* ext_dex_file_;  // Owned instance. nullptr only in moved-from zombies.
 
@@ -212,6 +219,7 @@
   static MethodInfo AbsorbMethodInfo(const ExtDexFileMethodInfo& ext_method_info);
   static void AddMethodInfoCallback(const ExtDexFileMethodInfo* ext_method_info, void* user_data);
 
+  // These are initialized by TryLoadLibdexfileExternal.
   static decltype(ExtDexFileOpenFromMemory)* g_ExtDexFileOpenFromMemory;
   static decltype(ExtDexFileOpenFromFd)* g_ExtDexFileOpenFromFd;
   static decltype(ExtDexFileGetMethodInfoForOffset)* g_ExtDexFileGetMethodInfoForOffset;