linker: add android_get_exported_namespace

Depending on how ld.config.txt is configured, there can be multiple
built-in namespaces created by the linker from the beginning of a
process. android_get_exported_namespace is a platform only API for
getting a handle (android_namespace_t*) to one of the built-in namespaces
with given name. The returned namespace can then be given to
android_dlopen_ext in order to explicitly specify the target namespace
where the library is searched and loaded from.

Note that this function only returns 'exported' namespaces created via
ld.config.txt file. In order to export a namespace, the visible property
should be set to true:

namespace.<name>.visible = true

Namespaces are hidden by default. Hidden namespaces and namespaces
that are created programmatically, notably 'classloader-namespace',
aren't returned by this function.

Bug: 36851137
Test: confirmed that namespaces created with ld.config.txt is retrieved.
Test: linker-unit-tests passes
Change-Id: I0d05fa7e0e116009edf8ea362ab46774bc617cbf
diff --git a/libdl/libdl.arm.map b/libdl/libdl.arm.map
index c0dcd5d..668f008 100644
--- a/libdl/libdl.arm.map
+++ b/libdl/libdl.arm.map
@@ -47,4 +47,5 @@
     android_init_anonymous_namespace;
     android_create_namespace;
     android_link_namespaces;
+    android_get_exported_namespace;
 } LIBC_N;
diff --git a/libdl/libdl.arm64.map b/libdl/libdl.arm64.map
index 3b797f7..8270fe9 100644
--- a/libdl/libdl.arm64.map
+++ b/libdl/libdl.arm64.map
@@ -46,4 +46,5 @@
     android_init_anonymous_namespace;
     android_create_namespace;
     android_link_namespaces;
+    android_get_exported_namespace;
 } LIBC_N;
diff --git a/libdl/libdl.c b/libdl/libdl.c
index 6a95629..14e745b 100644
--- a/libdl/libdl.c
+++ b/libdl/libdl.c
@@ -94,6 +94,9 @@
 __attribute__((__weak__, visibility("default")))
 void __loader_android_dlwarning(void* obj, void (*f)(void*, const char*));
 
+__attribute__((__weak__, visibility("default")))
+struct android_namespace_t* __loader_android_get_exported_namespace(const char* name);
+
 // Proxy calls to bionic loader
 void* dlopen(const char* filename, int flag) {
   const void* caller_addr = __builtin_return_address(0);
@@ -182,3 +185,7 @@
 void android_dlwarning(void* obj, void (*f)(void*, const char*)) {
   __loader_android_dlwarning(obj, f);
 }
+
+struct android_namespace_t* android_get_exported_namespace(const char* name) {
+  return __loader_android_get_exported_namespace(name);
+}
diff --git a/libdl/libdl.map.txt b/libdl/libdl.map.txt
index 245e016..a4c6483 100644
--- a/libdl/libdl.map.txt
+++ b/libdl/libdl.map.txt
@@ -46,4 +46,5 @@
     android_init_anonymous_namespace;
     android_create_namespace;
     android_link_namespaces;
+    android_get_exported_namespace;
 } LIBC_N;
diff --git a/libdl/libdl.mips.map b/libdl/libdl.mips.map
index 3b797f7..8270fe9 100644
--- a/libdl/libdl.mips.map
+++ b/libdl/libdl.mips.map
@@ -46,4 +46,5 @@
     android_init_anonymous_namespace;
     android_create_namespace;
     android_link_namespaces;
+    android_get_exported_namespace;
 } LIBC_N;
diff --git a/libdl/libdl.mips64.map b/libdl/libdl.mips64.map
index 3b797f7..8270fe9 100644
--- a/libdl/libdl.mips64.map
+++ b/libdl/libdl.mips64.map
@@ -46,4 +46,5 @@
     android_init_anonymous_namespace;
     android_create_namespace;
     android_link_namespaces;
+    android_get_exported_namespace;
 } LIBC_N;
diff --git a/libdl/libdl.x86.map b/libdl/libdl.x86.map
index 3b797f7..8270fe9 100644
--- a/libdl/libdl.x86.map
+++ b/libdl/libdl.x86.map
@@ -46,4 +46,5 @@
     android_init_anonymous_namespace;
     android_create_namespace;
     android_link_namespaces;
+    android_get_exported_namespace;
 } LIBC_N;
diff --git a/libdl/libdl.x86_64.map b/libdl/libdl.x86_64.map
index 3b797f7..8270fe9 100644
--- a/libdl/libdl.x86_64.map
+++ b/libdl/libdl.x86_64.map
@@ -46,4 +46,5 @@
     android_init_anonymous_namespace;
     android_create_namespace;
     android_link_namespaces;
+    android_get_exported_namespace;
 } LIBC_N;
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index 96dd477..0d8beec 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -214,6 +214,10 @@
   return success;
 }
 
+android_namespace_t* __android_get_exported_namespace(const char* name) {
+  return get_exported_namespace(name);
+}
+
 void __cfi_fail(uint64_t CallSiteTypeId, void* Ptr, void *DiagData, void *CallerPc) {
   CFIShadowWriter::CfiFail(CallSiteTypeId, Ptr, DiagData, CallerPc);
 }
@@ -256,9 +260,13 @@
   // 4*
   // 0000000 000111111111122222222223333 333333444444444455 555555556666666666777777777788888 888889999999999
   // 0123456 789012345678901234567890123 456789012345678901 234567890123456789012345678901234 567890123456789
-    "dlvsym\0__loader_android_dlwarning\0__loader_cfi_fail\0__loader_android_link_namespaces\0"
+    "dlvsym\0__loader_android_dlwarning\0__loader_cfi_fail\0__loader_android_link_namespaces\0__loader_androi"
+  // 5*
+  // 0000000000111111111122222 22222
+  // 0123456789012345678901234 56789
+    "d_get_exported_namespace\0"
 #if defined(__arm__)
-  // 485
+  // 525
     "__loader_dl_unwind_find_exidx\0"
 #endif
     ;
@@ -286,8 +294,9 @@
   ELFW(SYM_INITIALIZER)(407, &__android_dlwarning, 1),
   ELFW(SYM_INITIALIZER)(434, &__cfi_fail, 1),
   ELFW(SYM_INITIALIZER)(452, &__android_link_namespaces, 1),
+  ELFW(SYM_INITIALIZER)(485, &__android_get_exported_namespace, 1),
 #if defined(__arm__)
-  ELFW(SYM_INITIALIZER)(485, &__dl_unwind_find_exidx, 1),
+  ELFW(SYM_INITIALIZER)(525, &__dl_unwind_find_exidx, 1),
 #endif
 };
 
@@ -304,9 +313,9 @@
 // Note that adding any new symbols here requires stubbing them out in libdl.
 static unsigned g_libdl_buckets[1] = { 1 };
 #if defined(__arm__)
-static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 0 };
+static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0 };
 #else
-static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 0 };
+static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 0 };
 #endif
 
 static uint8_t __libdl_info_buf[sizeof(soinfo)] __attribute__((aligned(8)));
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 2777d73..66bec58 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -71,6 +71,7 @@
 #define ELF_ST_TYPE(x) (static_cast<uint32_t>(x) & 0xf)
 
 static android_namespace_t* g_anonymous_namespace = &g_default_namespace;
+static std::unordered_map<std::string, android_namespace_t*> g_exported_namespaces;
 
 static LinkerTypeAllocator<soinfo> g_soinfo_allocator;
 static LinkerTypeAllocator<LinkedListEntry<soinfo>> g_soinfo_links_allocator;
@@ -3461,6 +3462,9 @@
     ns->set_permitted_paths(ns_config->permitted_paths());
 
     namespaces[ns_config->name()] = ns;
+    if (ns_config->visible()) {
+      g_exported_namespaces[ns_config->name()] = ns;
+    }
   }
 
   // 3. Establish links between namespaces
@@ -3485,3 +3489,16 @@
 
   set_application_target_sdk_version(config->target_sdk_version());
 }
+
+// This function finds a namespace exported in ld.config.txt by its name.
+// A namespace can be exported by setting .visible property to true.
+android_namespace_t* get_exported_namespace(const char* name) {
+  if (name == nullptr) {
+    return nullptr;
+  }
+  auto it = g_exported_namespaces.find(std::string(name));
+  if (it == g_exported_namespaces.end()) {
+    return nullptr;
+  }
+  return it->second;
+}
diff --git a/linker/linker.h b/linker/linker.h
index d5d4980..53dac6c 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -173,4 +173,6 @@
                      android_namespace_t* namespace_to,
                      const char* shared_lib_sonames);
 
+android_namespace_t* get_exported_namespace(const char* name);
+
 #endif
diff --git a/linker/linker_config.cpp b/linker/linker_config.cpp
index 33616f7..b9205cc 100644
--- a/linker/linker_config.cpp
+++ b/linker/linker_config.cpp
@@ -459,6 +459,7 @@
     }
 
     ns_config->set_isolated(properties.get_bool(property_name_prefix + ".isolated"));
+    ns_config->set_visible(properties.get_bool(property_name_prefix + ".visible"));
 
     // these are affected by is_asan flag
     if (is_asan) {
diff --git a/linker/linker_config.h b/linker/linker_config.h
index 4ec8b26..6f8bffb 100644
--- a/linker/linker_config.h
+++ b/linker/linker_config.h
@@ -62,7 +62,7 @@
 class NamespaceConfig {
  public:
   explicit NamespaceConfig(const std::string& name)
-      : name_(name), isolated_(false)
+      : name_(name), isolated_(false), visible_(false)
   {}
 
   const char* name() const {
@@ -73,6 +73,10 @@
     return isolated_;
   }
 
+  bool visible() const {
+    return visible_;
+  }
+
   const std::vector<std::string>& search_paths() const {
     return search_paths_;
   }
@@ -93,6 +97,10 @@
     isolated_ = isolated;
   }
 
+  void set_visible(bool visible) {
+    visible_ = visible;
+  }
+
   void set_search_paths(std::vector<std::string>&& search_paths) {
     search_paths_ = search_paths;
   }
@@ -103,6 +111,7 @@
  private:
   const std::string name_;
   bool isolated_;
+  bool visible_;
   std::vector<std::string> search_paths_;
   std::vector<std::string> permitted_paths_;
   std::vector<NamespaceLinkConfig> namespace_links_;
diff --git a/linker/tests/linker_config_test.cpp b/linker/tests/linker_config_test.cpp
index 64ab00f..4889b14 100644
--- a/linker/tests/linker_config_test.cpp
+++ b/linker/tests/linker_config_test.cpp
@@ -59,6 +59,7 @@
   "namespace.default.links = system\n"
   "namespace.default.link.system.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so\n"
   "namespace.system.isolated = true\n"
+  "namespace.system.visible = true\n"
   "namespace.system.search.paths = /system/${LIB}\n"
   "namespace.system.permitted.paths = /system/${LIB}\n"
   "namespace.system.asan.search.paths = /data:/system/${LIB}\n"
@@ -139,6 +140,7 @@
   ASSERT_TRUE(default_ns_config != nullptr);
 
   ASSERT_TRUE(default_ns_config->isolated());
+  ASSERT_FALSE(default_ns_config->visible());
   ASSERT_EQ(kExpectedDefaultSearchPath, default_ns_config->search_paths());
   ASSERT_EQ(kExpectedDefaultPermittedPath, default_ns_config->permitted_paths());
 
@@ -165,6 +167,7 @@
   ASSERT_TRUE(ns_system != nullptr) << "system namespace was not found";
 
   ASSERT_TRUE(ns_system->isolated());
+  ASSERT_TRUE(ns_system->visible());
   ASSERT_EQ(kExpectedSystemSearchPath, ns_system->search_paths());
   ASSERT_EQ(kExpectedSystemPermittedPath, ns_system->permitted_paths());
 }