Open libdexfile_external on demand in libdexfile_support.

This allows users to depend on libdexfile_support (typically via
libbacktrace) also in binaries that don't run java code. The drawback is one
extra address load in all calls.

Also add support for a nonfunctional fake with recovery and vendor variants,
so that clients don't need special cases to avoid the libdexfile dependency.

Bug: 123403798
Bug: 123186083
Test: Flash and boot
Test: atest -a libdexfile_support{,_static}_tests
Test: mmma art/libdexfile/
Test: mmma system/core/{libunwindstack,libbacktrace}, run host gtests (cannot get atest to work)
Change-Id: Ia99ad8039bb37fe25bf7009a85ae89c2aa660bd1
diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp
index feb5e38..2289a9c 100644
--- a/libdexfile/Android.bp
+++ b/libdexfile/Android.bp
@@ -205,6 +205,8 @@
 cc_library_headers {
     name: "libdexfile_external_headers",
     host_supported: true,
+    vendor_available: true,
+    recovery_available: true,
     header_libs: ["libbase_headers"],
     export_header_lib_headers: ["libbase_headers"],
     export_include_dirs: ["external/include"],
@@ -245,23 +247,37 @@
 }
 
 // Support library with a C++ API for accessing the libdexfile API for external
-// (non-ART) users. They should link to their own instance of this (either
-// statically or through linker namespaces).
+// (non-ART) users.
+//
+// This library dlopens libdexfile_external on first use, so there is no build
+// time dependency on dex file logic. It is therefore safe to use from binaries
+// compiled without dex file support, given they won't encounter any dex file
+// stack frames.
 cc_library {
     name: "libdexfile_support",
     host_supported: true,
+    vendor_available: true,
+    recovery_available: true,
     srcs: [
         "external/dex_file_supp.cc",
     ],
+    required: ["libdexfile_external"],
+    shared_libs: ["liblog"],
     header_libs: ["libdexfile_external_headers"],
-    shared_libs: ["libdexfile_external"],
     export_header_lib_headers: ["libdexfile_external_headers"],
+    target: {
+        recovery: {
+            cflags: ["-DNO_DEXFILE_SUPPORT"],
+        },
+        vendor: {
+            cflags: ["-DNO_DEXFILE_SUPPORT"],
+        },
+    },
 }
 
 art_cc_test {
     name: "art_libdexfile_support_tests",
     host_supported: true,
-    test_per_src: true,  // For consistency with other ART gtests.
     srcs: [
         "external/dex_file_supp_test.cc",
     ],
@@ -272,3 +288,36 @@
         "libdexfile_support",
     ],
 }
+
+cc_library_static {
+    name: "libdexfile_support_static",
+    host_supported: true,
+    defaults: ["libdexfile_static_defaults"],
+    srcs: [
+        "external/dex_file_supp.cc",
+    ],
+    cflags: ["-DSTATIC_LIB"],
+    // Using whole_static_libs here only as a "poor man's transitivity" kludge.
+    whole_static_libs: [
+        "libbase",
+        "libdexfile",
+        "libdexfile_external",
+        "liblog",
+        "libz",
+        "libziparchive",
+    ],
+    header_libs: ["libdexfile_external_headers"],
+    export_header_lib_headers: ["libdexfile_external_headers"],
+}
+
+art_cc_test {
+    name: "art_libdexfile_support_static_tests",
+    host_supported: true,
+    srcs: [
+        "external/dex_file_supp_test.cc",
+    ],
+    static_libs: [
+        "libbase",
+        "libdexfile_support_static",
+    ],
+}
diff --git a/libdexfile/external/dex_file_supp.cc b/libdexfile/external/dex_file_supp.cc
index 5bd25fc..ba684fe 100644
--- a/libdexfile/external/dex_file_supp.cc
+++ b/libdexfile/external/dex_file_supp.cc
@@ -16,10 +16,74 @@
 
 #include "art_api/dex_file_support.h"
 
+#include <dlfcn.h>
+#include <mutex>
+
+#ifndef STATIC_LIB
+// Not used in the static lib, so avoid a dependency on this header in
+// libdexfile_support_static.
+#include <log/log.h>
+#endif
+
 namespace art_api {
 namespace dex {
 
-DexFile::~DexFile() { ExtDexFileFree(ext_dex_file_); }
+#ifdef STATIC_LIB
+#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
+#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);
+
+#undef DEFINE_DLFUNC_PTR
+
+void LoadLibdexfileExternal() {
+#if defined(STATIC_LIB)
+  // Nothing to do here since all function pointers are initialised statically.
+#elif defined(NO_DEXFILE_SUPPORT)
+  LOG_FATAL("Dex file support not available.");
+#else
+  static std::once_flag dlopen_once;
+  std::call_once(dlopen_once, []() {
+    constexpr char kLibdexfileExternalLib[] = "libdexfile_external.so";
+    void* handle =
+        dlopen(kLibdexfileExternalLib, RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE);
+    LOG_ALWAYS_FATAL_IF(handle == nullptr, "Failed to load %s: %s",
+                        kLibdexfileExternalLib, dlerror());
+
+#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, \
+                        kLibdexfileExternalLib, \
+                        dlerror()); \
+  } while (0)
+
+    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);
+
+#undef SET_DLFUNC_PTR
+  });
+#endif  // !defined(NO_DEXFILE_SUPPORT) && !defined(STATIC_LIB)
+}
+
+DexFile::~DexFile() { g_ExtDexFileFree(ext_dex_file_); }
 
 MethodInfo DexFile::AbsorbMethodInfo(const ExtDexFileMethodInfo& ext_method_info) {
   return {ext_method_info.offset, ext_method_info.len, DexString(ext_method_info.name)};
diff --git a/libdexfile/external/include/art_api/dex_file_support.h b/libdexfile/external/include/art_api/dex_file_support.h
index 24222af..a98ff0e 100644
--- a/libdexfile/external/include/art_api/dex_file_support.h
+++ b/libdexfile/external/include/art_api/dex_file_support.h
@@ -33,17 +33,22 @@
 namespace art_api {
 namespace dex {
 
+// 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.
+void LoadLibdexfileExternal();
+
 // Minimal std::string look-alike for a string returned from libdexfile.
 class DexString final {
  public:
   DexString(DexString&& dex_str) noexcept : ext_string_(dex_str.ext_string_) {
-    dex_str.ext_string_ = ExtDexFileMakeString("", 0);
+    dex_str.ext_string_ = MakeExtDexFileString("", 0);
   }
   explicit DexString(const char* str = "")
-      : ext_string_(ExtDexFileMakeString(str, std::strlen(str))) {}
+      : ext_string_(MakeExtDexFileString(str, std::strlen(str))) {}
   explicit DexString(std::string_view str)
-      : ext_string_(ExtDexFileMakeString(str.data(), str.size())) {}
-  ~DexString() { ExtDexFileFreeString(ext_string_); }
+      : ext_string_(MakeExtDexFileString(str.data(), str.size())) {}
+  ~DexString() { g_ExtDexFileFreeString(ext_string_); }
 
   DexString& operator=(DexString&& dex_str) noexcept {
     std::swap(ext_string_, dex_str.ext_string_);
@@ -52,36 +57,48 @@
 
   const char* data() const {
     size_t ignored;
-    return ExtDexFileGetString(ext_string_, &ignored);
+    return g_ExtDexFileGetString(ext_string_, &ignored);
   }
   const char* c_str() const { return data(); }
 
   size_t size() const {
     size_t len;
-    (void)ExtDexFileGetString(ext_string_, &len);
+    (void)g_ExtDexFileGetString(ext_string_, &len);
     return len;
   }
   size_t length() const { return size(); }
 
   operator std::string_view() const {
     size_t len;
-    const char* chars = ExtDexFileGetString(ext_string_, &len);
+    const char* chars = g_ExtDexFileGetString(ext_string_, &len);
     return std::string_view(chars, len);
   }
 
  private:
+  friend void LoadLibdexfileExternal();
   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.
 
+  static decltype(ExtDexFileMakeString)* g_ExtDexFileMakeString;
+  static decltype(ExtDexFileGetString)* g_ExtDexFileGetString;
+  static decltype(ExtDexFileFreeString)* g_ExtDexFileFreeString;
+
+  static const struct ExtDexFileString* MakeExtDexFileString(const char* str, size_t size) {
+    if (UNLIKELY(g_ExtDexFileMakeString == nullptr)) {
+      LoadLibdexfileExternal();
+    }
+    return g_ExtDexFileMakeString(str, size);
+  }
+
   DISALLOW_COPY_AND_ASSIGN(DexString);
 };
 
 inline bool operator==(const DexString& s1, const DexString& s2) {
   size_t l1, l2;
-  const char* str1 = ExtDexFileGetString(s1.ext_string_, &l1);
-  const char* str2 = ExtDexFileGetString(s2.ext_string_, &l2);
+  const char* str1 = DexString::g_ExtDexFileGetString(s1.ext_string_, &l1);
+  const char* str2 = DexString::g_ExtDexFileGetString(s2.ext_string_, &l2);
   // Use memcmp to avoid assumption about absence of null characters in the strings.
   return l1 == l2 && !std::memcmp(str1, str2, l1);
 }
@@ -120,9 +137,14 @@
                                                  size_t* size,
                                                  const std::string& location,
                                                  /*out*/ std::string* error_msg) {
+    if (UNLIKELY(g_ExtDexFileOpenFromMemory == nullptr)) {
+      // Load libdexfile_external.so in this factory function, so instance
+      // methods don't need to check this.
+      LoadLibdexfileExternal();
+    }
     ExtDexFile* ext_dex_file;
     const ExtDexFileString* ext_error_msg = nullptr;
-    if (ExtDexFileOpenFromMemory(addr, size, location.c_str(), &ext_error_msg, &ext_dex_file)) {
+    if (g_ExtDexFileOpenFromMemory(addr, size, location.c_str(), &ext_error_msg, &ext_dex_file)) {
       return std::unique_ptr<DexFile>(new DexFile(ext_dex_file));
     }
     *error_msg = (ext_error_msg == nullptr) ? "" : std::string(DexString(ext_error_msg));
@@ -138,9 +160,14 @@
                                              off_t offset,
                                              const std::string& location,
                                              /*out*/ std::string* error_msg) {
+    if (UNLIKELY(g_ExtDexFileOpenFromFd == nullptr)) {
+      // Load libdexfile_external.so in this factory function, so instance
+      // methods don't need to check this.
+      LoadLibdexfileExternal();
+    }
     ExtDexFile* ext_dex_file;
     const ExtDexFileString* ext_error_msg = nullptr;
-    if (ExtDexFileOpenFromFd(fd, offset, location.c_str(), &ext_error_msg, &ext_dex_file)) {
+    if (g_ExtDexFileOpenFromFd(fd, offset, location.c_str(), &ext_error_msg, &ext_dex_file)) {
       return std::unique_ptr<DexFile>(new DexFile(ext_dex_file));
     }
     *error_msg = std::string(DexString(ext_error_msg));
@@ -154,10 +181,10 @@
   // class and method name only.
   MethodInfo GetMethodInfoForOffset(int64_t dex_offset, bool with_signature) {
     ExtDexFileMethodInfo ext_method_info;
-    if (ExtDexFileGetMethodInfoForOffset(ext_dex_file_,
-                                         dex_offset,
-                                         with_signature,
-                                         &ext_method_info)) {
+    if (g_ExtDexFileGetMethodInfoForOffset(ext_dex_file_,
+                                           dex_offset,
+                                           with_signature,
+                                           &ext_method_info)) {
       return AbsorbMethodInfo(ext_method_info);
     }
     return {/*offset=*/0, /*len=*/0, /*name=*/DexString()};
@@ -168,14 +195,15 @@
   // gets the class and method name only.
   std::vector<MethodInfo> GetAllMethodInfos(bool with_signature) {
     MethodInfoVector res;
-    ExtDexFileGetAllMethodInfos(ext_dex_file_,
-                                with_signature,
-                                AddMethodInfoCallback,
-                                static_cast<void*>(&res));
+    g_ExtDexFileGetAllMethodInfos(ext_dex_file_,
+                                  with_signature,
+                                  AddMethodInfoCallback,
+                                  static_cast<void*>(&res));
     return res;
   }
 
  private:
+  friend void LoadLibdexfileExternal();
   explicit DexFile(ExtDexFile* ext_dex_file) : ext_dex_file_(ext_dex_file) {}
   ExtDexFile* ext_dex_file_;  // Owned instance. nullptr only in moved-from zombies.
 
@@ -184,6 +212,12 @@
   static MethodInfo AbsorbMethodInfo(const ExtDexFileMethodInfo& ext_method_info);
   static void AddMethodInfoCallback(const ExtDexFileMethodInfo* ext_method_info, void* user_data);
 
+  static decltype(ExtDexFileOpenFromMemory)* g_ExtDexFileOpenFromMemory;
+  static decltype(ExtDexFileOpenFromFd)* g_ExtDexFileOpenFromFd;
+  static decltype(ExtDexFileGetMethodInfoForOffset)* g_ExtDexFileGetMethodInfoForOffset;
+  static decltype(ExtDexFileGetAllMethodInfos)* g_ExtDexFileGetAllMethodInfos;
+  static decltype(ExtDexFileFree)* g_ExtDexFileFree;
+
   DISALLOW_COPY_AND_ASSIGN(DexFile);
 };