Introducing linker namespaces

Bug: http://b/22548808
Change-Id: Ia3af3c0a167f1d16447a3d83bb045d143319b1e1
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index c7d4d46..83bd5cc 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -52,14 +52,14 @@
 #define LIBSIZE 1024*1024 // how much address space to reserve for it
 
 #if defined(__LP64__)
-#define LIBPATH_PREFIX "/nativetest64/"
+#define NATIVE_TESTS_PATH "/nativetest64"
 #else
-#define LIBPATH_PREFIX "/nativetest/"
+#define NATIVE_TESTS_PATH "/nativetest"
 #endif
 
-#define LIBPATH LIBPATH_PREFIX "libdlext_test_fd/libdlext_test_fd.so"
-#define LIBZIPPATH LIBPATH_PREFIX "libdlext_test_zip/libdlext_test_zip_zipaligned.zip"
-#define LIBZIPPATH_WITH_RUNPATH LIBPATH_PREFIX "libdlext_test_runpath_zip/libdlext_test_runpath_zip_zipaligned.zip"
+#define LIBPATH NATIVE_TESTS_PATH "/libdlext_test_fd/libdlext_test_fd.so"
+#define LIBZIPPATH NATIVE_TESTS_PATH "/libdlext_test_zip/libdlext_test_zip_zipaligned.zip"
+#define LIBZIPPATH_WITH_RUNPATH NATIVE_TESTS_PATH "/libdlext_test_runpath_zip/libdlext_test_runpath_zip_zipaligned.zip"
 
 #define LIBZIP_OFFSET PAGE_SIZE
 
@@ -602,3 +602,192 @@
     ASSERT_EQ(0, WEXITSTATUS(status));
   }
 }
+
+// Testing namespaces
+static const char* g_public_lib = "libnstest_public.so";
+
+TEST(dlext, ns_smoke) {
+  static const char* root_lib = "libnstest_root.so";
+  std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib;
+
+  ASSERT_FALSE(android_init_public_namespace(path.c_str()));
+  ASSERT_STREQ("android_init_public_namespace failed: Error initializing public namespace: "
+               "\"libnstest_public.so\" was not found in the default namespace", dlerror());
+
+  const std::string lib_path = std::string(getenv("ANDROID_DATA")) + NATIVE_TESTS_PATH;
+
+  void* handle_public = dlopen((lib_path + "/public_namespace_libs/" + g_public_lib).c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle_public != nullptr) << dlerror();
+
+  ASSERT_TRUE(android_init_public_namespace(path.c_str())) << dlerror();
+
+  // Check that libraries added to public namespace are NODELETE
+  dlclose(handle_public);
+  handle_public = dlopen((lib_path + "/public_namespace_libs/" + g_public_lib).c_str(), RTLD_NOW | RTLD_NOLOAD);
+  ASSERT_TRUE(handle_public != nullptr) << dlerror();
+
+  android_namespace_t* ns1 = android_create_namespace("private", nullptr, (lib_path + "/private_namespace_libs").c_str(), false);
+  ASSERT_TRUE(ns1 != nullptr) << dlerror();
+
+  android_namespace_t* ns2 = android_create_namespace("private_isolated", nullptr, (lib_path + "/private_namespace_libs").c_str(), true);
+  ASSERT_TRUE(ns2 != nullptr) << dlerror();
+
+  // This should not have affect search path for default namespace:
+  ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr);
+  void* handle = dlopen(g_public_lib, RTLD_NOW);
+  ASSERT_TRUE(handle != nullptr) << dlerror();
+  dlclose(handle);
+
+  android_dlextinfo extinfo;
+  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+  extinfo.library_namespace = ns1;
+
+  void* handle1 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle1 != nullptr) << dlerror();
+
+  extinfo.library_namespace = ns2;
+  void* handle2 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle2 != nullptr) << dlerror();
+
+  ASSERT_TRUE(handle1 != handle2);
+
+  typedef const char* (*fn_t)();
+
+  fn_t ns_get_local_string1 = reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_local_string"));
+  ASSERT_TRUE(ns_get_local_string1 != nullptr) << dlerror();
+  fn_t ns_get_local_string2 = reinterpret_cast<fn_t>(dlsym(handle2, "ns_get_local_string"));
+  ASSERT_TRUE(ns_get_local_string2 != nullptr) << dlerror();
+
+  EXPECT_STREQ("This string is local to root library", ns_get_local_string1());
+  EXPECT_STREQ("This string is local to root library", ns_get_local_string2());
+
+  ASSERT_TRUE(ns_get_local_string1() != ns_get_local_string2());
+
+  fn_t ns_get_private_extern_string1 =
+          reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_private_extern_string"));
+  ASSERT_TRUE(ns_get_private_extern_string1 != nullptr) << dlerror();
+  fn_t ns_get_private_extern_string2 =
+          reinterpret_cast<fn_t>(dlsym(handle2, "ns_get_private_extern_string"));
+  ASSERT_TRUE(ns_get_private_extern_string2 != nullptr) << dlerror();
+
+  EXPECT_STREQ("This string is from private namespace", ns_get_private_extern_string1());
+  EXPECT_STREQ("This string is from private namespace", ns_get_private_extern_string2());
+
+  ASSERT_TRUE(ns_get_private_extern_string1() != ns_get_private_extern_string2());
+
+  fn_t ns_get_public_extern_string1 =
+          reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_public_extern_string"));
+  ASSERT_TRUE(ns_get_public_extern_string1 != nullptr) << dlerror();
+  fn_t ns_get_public_extern_string2 =
+          reinterpret_cast<fn_t>(dlsym(handle2, "ns_get_public_extern_string"));
+  ASSERT_TRUE(ns_get_public_extern_string2 != nullptr) << dlerror();
+
+  EXPECT_STREQ("This string is from public namespace", ns_get_public_extern_string1());
+  ASSERT_TRUE(ns_get_public_extern_string1() == ns_get_public_extern_string2());
+
+  // and now check that dlopen() does the right thing in terms of preserving namespace
+  fn_t ns_get_dlopened_string1 = reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_dlopened_string"));
+  ASSERT_TRUE(ns_get_dlopened_string1 != nullptr) << dlerror();
+  fn_t ns_get_dlopened_string2 = reinterpret_cast<fn_t>(dlsym(handle2, "ns_get_dlopened_string"));
+  ASSERT_TRUE(ns_get_dlopened_string2 != nullptr) << dlerror();
+
+  EXPECT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string1());
+  EXPECT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string2());
+
+  ASSERT_TRUE(ns_get_dlopened_string1() != ns_get_dlopened_string2());
+
+  dlclose(handle1);
+
+  // Check if handle2 is still alive (and well)
+  ASSERT_STREQ("This string is local to root library", ns_get_local_string2());
+  ASSERT_STREQ("This string is from private namespace", ns_get_private_extern_string2());
+  ASSERT_STREQ("This string is from public namespace", ns_get_public_extern_string2());
+  ASSERT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string2());
+
+  dlclose(handle2);
+}
+
+TEST(dlext, ns_isolated) {
+  static const char* root_lib = "libnstest_root_not_isolated.so";
+  std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib;
+
+  const std::string lib_path = std::string(getenv("ANDROID_DATA")) + NATIVE_TESTS_PATH;
+  void* handle_public = dlopen((lib_path + "/public_namespace_libs/" + g_public_lib).c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle_public != nullptr) << dlerror();
+
+  ASSERT_TRUE(android_init_public_namespace(path.c_str())) << dlerror();
+
+  android_namespace_t* ns_not_isolated = android_create_namespace("private", nullptr, (lib_path + "/private_namespace_libs").c_str(), false);
+  ASSERT_TRUE(ns_not_isolated != nullptr) << dlerror();
+
+  android_namespace_t* ns_isolated = android_create_namespace("private_isolated1", nullptr, (lib_path + "/private_namespace_libs").c_str(), true);
+  ASSERT_TRUE(ns_isolated != nullptr) << dlerror();
+
+  android_namespace_t* ns_isolated2 = android_create_namespace("private_isolated2", (lib_path + "/private_namespace_libs").c_str(), nullptr, true);
+  ASSERT_TRUE(ns_isolated2 != nullptr) << dlerror();
+
+  ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr);
+  ASSERT_STREQ("dlopen failed: library \"libnstest_root_not_isolated.so\" not found", dlerror());
+
+  std::string lib_private_external_path =
+      lib_path + "/private_namespace_libs_external/libnstest_private_external.so";
+
+  // Load lib_private_external_path to default namespace
+  // (it should remain invisible for the isolated namespaces after this)
+  void* handle = dlopen(lib_private_external_path.c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle != nullptr) << dlerror();
+
+  android_dlextinfo extinfo;
+  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+  extinfo.library_namespace = ns_not_isolated;
+
+  void* handle1 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle1 != nullptr) << dlerror();
+
+  extinfo.library_namespace = ns_isolated;
+
+  void* handle2 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle2 == nullptr);
+  ASSERT_STREQ("dlopen failed: library \"libnstest_private_external.so\" not found", dlerror());
+
+  // Check dlopen by absolute path
+  handle2 = android_dlopen_ext(lib_private_external_path.c_str(), RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle2 == nullptr);
+  ASSERT_EQ("dlopen failed: library \"" + lib_private_external_path + "\" not found", dlerror());
+
+  extinfo.library_namespace = ns_isolated2;
+
+  handle2 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle2 == nullptr);
+  ASSERT_STREQ("dlopen failed: library \"libnstest_private_external.so\" not found", dlerror());
+
+  // Check dlopen by absolute path
+  handle2 = android_dlopen_ext(lib_private_external_path.c_str(), RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle2 == nullptr);
+  ASSERT_EQ("dlopen failed: library \"" + lib_private_external_path + "\" not found", dlerror());
+
+  typedef const char* (*fn_t)();
+  fn_t ns_get_local_string = reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_local_string"));
+  ASSERT_TRUE(ns_get_local_string != nullptr) << dlerror();
+
+  ASSERT_STREQ("This string is local to root library", ns_get_local_string());
+
+  fn_t ns_get_private_extern_string =
+          reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_private_extern_string"));
+  ASSERT_TRUE(ns_get_private_extern_string != nullptr) << dlerror();
+
+  ASSERT_STREQ("This string is from private namespace", ns_get_private_extern_string());
+
+  fn_t ns_get_public_extern_string =
+          reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_public_extern_string"));
+  ASSERT_TRUE(ns_get_public_extern_string != nullptr) << dlerror();
+
+  ASSERT_STREQ("This string is from public namespace", ns_get_public_extern_string());
+
+  fn_t ns_get_dlopened_string = reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_dlopened_string"));
+  ASSERT_TRUE(ns_get_dlopened_string != nullptr) << dlerror();
+
+  ASSERT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string());
+
+  dlclose(handle1);
+}