ART: Clean up library loading

Retrieve the library path from the classloader before attempting to
load, instead of getting it passed down. This allows unifying said
loading behavior for follow-up changes.

Fix up test code to support the new required data in classloader
objects.

Bug: 70901841
Test: m test-art-host
Test: device boots
Change-Id: Iaccaeb56422877abac9f7fe6f5a17364c8adf4ca
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 727dd14..3f06d18 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -8706,9 +8706,9 @@
 }
 
 jobject ClassLinker::CreateWellKnownClassLoader(Thread* self,
-                                               const std::vector<const DexFile*>& dex_files,
-                                               jclass loader_class,
-                                               jobject parent_loader) {
+                                                const std::vector<const DexFile*>& dex_files,
+                                                jclass loader_class,
+                                                jobject parent_loader) {
   CHECK(self->GetJniEnv()->IsSameObject(loader_class,
                                         WellKnownClasses::dalvik_system_PathClassLoader) ||
         self->GetJniEnv()->IsSameObject(loader_class,
@@ -8784,6 +8784,29 @@
   DCHECK(h_dex_path_list != nullptr);
   // Set elements.
   dex_elements_field->SetObject<false>(h_dex_path_list.Get(), h_dex_elements.Get());
+  // Create an empty List for the "nativeLibraryDirectories," required for native tests.
+  // Note: this code is uncommon(oatdump)/testing-only, so don't add further WellKnownClasses
+  //       elements.
+  {
+    ArtField* native_lib_dirs = dex_elements_field->GetDeclaringClass()->
+        FindDeclaredInstanceField("nativeLibraryDirectories", "Ljava/util/List;");
+    DCHECK(native_lib_dirs != nullptr);
+    ObjPtr<mirror::Class> list_class = FindSystemClass(self, "Ljava/util/ArrayList;");
+    DCHECK(list_class != nullptr);
+    {
+      StackHandleScope<1> h_list_scope(self);
+      Handle<mirror::Class> h_list_class(h_list_scope.NewHandle<mirror::Class>(list_class));
+      bool list_init = EnsureInitialized(self, h_list_class, true, true);
+      DCHECK(list_init);
+      list_class = h_list_class.Get();
+    }
+    ObjPtr<mirror::Object> list_object = list_class->AllocObject(self);
+    // Note: we leave the object uninitialized. This must never leak into any non-testing code, but
+    //       is fine for testing. While it violates a Java-code invariant (the elementData field is
+    //       normally never null), as long as one does not try to add elements, this will still
+    //       work.
+    native_lib_dirs->SetObject<false>(h_dex_path_list.Get(), list_object);
+  }
 
   // Create the class loader..
   Handle<mirror::Class> h_loader_class = hs.NewHandle(soa.Decode<mirror::Class>(loader_class));
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index e159436..579552d 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -48,6 +48,7 @@
 #include "thread-inl.h"
 #include "thread_list.h"
 #include "ti/agent.h"
+#include "well_known_classes.h"
 
 namespace art {
 
@@ -853,7 +854,6 @@
 bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
                                   const std::string& path,
                                   jobject class_loader,
-                                  jstring library_path,
                                   std::string* error_msg) {
   error_msg->clear();
 
@@ -950,6 +950,9 @@
   // class unloading. Libraries will only be unloaded when the reference count (incremented by
   // dlopen) becomes zero from dlclose.
 
+  // Retrieve the library path from the classloader, if necessary.
+  ScopedLocalRef<jstring> library_path(env, GetLibrarySearchPath(env, class_loader));
+
   Locks::mutator_lock_->AssertNotHeld(self);
   const char* path_str = path.empty() ? nullptr : path.c_str();
   bool needs_native_bridge = false;
@@ -957,7 +960,7 @@
                                             runtime_->GetTargetSdkVersion(),
                                             path_str,
                                             class_loader,
-                                            library_path,
+                                            library_path.get(),
                                             &needs_native_bridge,
                                             error_msg);
 
@@ -1119,6 +1122,18 @@
   // The weak_globals table is visited by the GC itself (because it mutates the table).
 }
 
+jstring JavaVMExt::GetLibrarySearchPath(JNIEnv* env, jobject class_loader) {
+  if (class_loader == nullptr) {
+    return nullptr;
+  }
+  if (!env->IsInstanceOf(class_loader, WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
+    return nullptr;
+  }
+  return reinterpret_cast<jstring>(env->CallObjectMethod(
+      class_loader,
+      WellKnownClasses::dalvik_system_BaseDexClassLoader_getLdLibraryPath));
+}
+
 // JNI Invocation interface.
 
 extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
diff --git a/runtime/java_vm_ext.h b/runtime/java_vm_ext.h
index 7c2755f..8c81c25 100644
--- a/runtime/java_vm_ext.h
+++ b/runtime/java_vm_ext.h
@@ -101,7 +101,6 @@
   bool LoadNativeLibrary(JNIEnv* env,
                          const std::string& path,
                          jobject class_loader,
-                         jstring library_path,
                          std::string* error_msg);
 
   // Unload native libraries with cleared class loaders.
@@ -200,6 +199,11 @@
 
   static bool IsBadJniVersion(int version);
 
+  // Return the library search path for the given classloader, if the classloader is of a
+  // well-known type. The jobject will be a local reference and is expected to be managed by the
+  // caller.
+  static jstring GetLibrarySearchPath(JNIEnv* env, jobject class_loader);
+
  private:
   // The constructor should not be called directly. It may leave the object in
   // an erroneous state, and the result needs to be checked.
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 37ba9e4..441cd98 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1614,7 +1614,7 @@
   // libcore can't because it's the library that implements System.loadLibrary!
   {
     std::string error_msg;
-    if (!java_vm_->LoadNativeLibrary(env, "libjavacore.so", nullptr, nullptr, &error_msg)) {
+    if (!java_vm_->LoadNativeLibrary(env, "libjavacore.so", nullptr, &error_msg)) {
       LOG(FATAL) << "LoadNativeLibrary failed for \"libjavacore.so\": " << error_msg;
     }
   }
@@ -1623,7 +1623,7 @@
                                                 ? "libopenjdkd.so"
                                                 : "libopenjdk.so";
     std::string error_msg;
-    if (!java_vm_->LoadNativeLibrary(env, kOpenJdkLibrary, nullptr, nullptr, &error_msg)) {
+    if (!java_vm_->LoadNativeLibrary(env, kOpenJdkLibrary, nullptr, &error_msg)) {
       LOG(FATAL) << "LoadNativeLibrary failed for \"" << kOpenJdkLibrary << "\": " << error_msg;
     }
   }
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 5fef7df..dc57f81 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -81,6 +81,7 @@
 jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk;
 jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer;
 
+jmethodID WellKnownClasses::dalvik_system_BaseDexClassLoader_getLdLibraryPath;
 jmethodID WellKnownClasses::dalvik_system_VMRuntime_runFinalization;
 jmethodID WellKnownClasses::java_lang_Boolean_valueOf;
 jmethodID WellKnownClasses::java_lang_Byte_valueOf;
@@ -325,6 +326,7 @@
   org_apache_harmony_dalvik_ddmc_Chunk = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk");
   org_apache_harmony_dalvik_ddmc_DdmServer = CacheClass(env, "org/apache/harmony/dalvik/ddmc/DdmServer");
 
+  dalvik_system_BaseDexClassLoader_getLdLibraryPath = CacheMethod(env, dalvik_system_BaseDexClassLoader, false, "getLdLibraryPath", "()Ljava/lang/String;");
   dalvik_system_VMRuntime_runFinalization = CacheMethod(env, dalvik_system_VMRuntime, true, "runFinalization", "(J)V");
   java_lang_ClassNotFoundException_init = CacheMethod(env, java_lang_ClassNotFoundException, false, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V");
   java_lang_ClassLoader_loadClass = CacheMethod(env, java_lang_ClassLoader, false, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
@@ -406,7 +408,7 @@
   // to make sure these JNI methods are available.
   java_lang_Runtime_nativeLoad =
       CacheMethod(env, java_lang_Runtime.get(), true, "nativeLoad",
-                  "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)"
+                  "(Ljava/lang/String;Ljava/lang/ClassLoader;)"
                       "Ljava/lang/String;");
   java_lang_reflect_Proxy_invoke =
     CacheMethod(env, java_lang_reflect_Proxy, true, "invoke",
@@ -465,6 +467,7 @@
   org_apache_harmony_dalvik_ddmc_Chunk = nullptr;
   org_apache_harmony_dalvik_ddmc_DdmServer = nullptr;
 
+  dalvik_system_BaseDexClassLoader_getLdLibraryPath = nullptr;
   dalvik_system_VMRuntime_runFinalization = nullptr;
   java_lang_Boolean_valueOf = nullptr;
   java_lang_Byte_valueOf = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 3ebcc33..024971a 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -92,6 +92,7 @@
   static jclass org_apache_harmony_dalvik_ddmc_Chunk;
   static jclass org_apache_harmony_dalvik_ddmc_DdmServer;
 
+  static jmethodID dalvik_system_BaseDexClassLoader_getLdLibraryPath;
   static jmethodID dalvik_system_VMRuntime_runFinalization;
   static jmethodID java_lang_Boolean_valueOf;
   static jmethodID java_lang_Byte_valueOf;