Load libc_malloc_* libraries from the runtime APEX

/system/lib/libc.so is a symlink to libc.so in the runtime APEX.
libc_malloc_* libraries are bundled with libc.so because they share
implementation details.

However, since libc.so is loaded in the default namespace where the
runtime APEX path (/apex/com.android.runtime/lib) is not accessible,
libc.so has been using libc_malloc_* from /system/lib. This is
wrong because libc.so (from the runtime APEX) and libc_malloc_* (from
the platform) may not be in-sync.

libc.so now uses android_dlopen_ext to load libc_malloc_* libraries
correctly from the "runtime" linker namespace.

Bug: 122566199
Test: bionic-unit-tests

Merged-In: I46980fbe89e93ea79a7760c9b8eb007af0ada8d8
Change-Id: I46980fbe89e93ea79a7760c9b8eb007af0ada8d8
(cherry picked from commit 4e46ac69c23c8585bce17c2e032986a37bf33aef)
diff --git a/libc/bionic/malloc_common_dynamic.cpp b/libc/bionic/malloc_common_dynamic.cpp
index 3ccaed6..cf25f8e 100644
--- a/libc/bionic/malloc_common_dynamic.cpp
+++ b/libc/bionic/malloc_common_dynamic.cpp
@@ -54,6 +54,8 @@
 #include <stdlib.h>
 #include <unistd.h>
 
+#include <android/dlext.h>
+
 #include <private/bionic_config.h>
 #include <private/bionic_defs.h>
 #include <private/bionic_malloc_dispatch.h>
@@ -277,8 +279,33 @@
   return true;
 }
 
+extern "C" struct android_namespace_t* android_get_exported_namespace(const char* name);
+
 void* LoadSharedLibrary(const char* shared_lib, const char* prefix, MallocDispatch* dispatch_table) {
-  void* impl_handle = dlopen(shared_lib, RTLD_NOW | RTLD_LOCAL);
+  void* impl_handle = nullptr;
+  // Try to load the libc_malloc_* libs from the "runtime" namespace and then
+  // fall back to dlopen() to load them from the default namespace.
+  //
+  // The libraries are packaged in the runtime APEX together with libc.so.
+  // However, since the libc.so is searched via the symlink in the system
+  // partition (/system/lib/libc.so -> /apex/com.android.runtime/bionic.libc.so)
+  // libc.so is loaded into the default namespace. If we just dlopen() here, the
+  // linker will load the libs found in /system/lib which might be incompatible
+  // with libc.so in the runtime APEX. Use android_dlopen_ext to explicitly load
+  // the ones in the runtime APEX.
+  struct android_namespace_t* runtime_ns = android_get_exported_namespace("runtime");
+  if (runtime_ns != nullptr) {
+    const android_dlextinfo dlextinfo = {
+      .flags = ANDROID_DLEXT_USE_NAMESPACE,
+      .library_namespace = runtime_ns,
+    };
+    impl_handle = android_dlopen_ext(shared_lib, RTLD_NOW | RTLD_LOCAL, &dlextinfo);
+  }
+
+  if (impl_handle == nullptr) {
+    impl_handle = dlopen(shared_lib, RTLD_NOW | RTLD_LOCAL);
+  }
+
   if (impl_handle == nullptr) {
     error_log("%s: Unable to open shared library %s: %s", getprogname(), shared_lib, dlerror());
     return nullptr;