linker: implement shared namespaces
Shared namespaces clone the list of loaded native libraries from the caller namespace. This allows classloaders for bundled apps to share already loaded libraries with default namespace. Bug: http://b/22548808 Bug: http://b/26165097 Change-Id: I8949d45937fdb38e1f586ff0679003adac0d9dad (cherry picked from commit e78deef364d952dd1141a2f3067a12060aaf11e6)
This commit is contained in:
parent
4afd635be1
commit
7331fe18d7
@ -142,6 +142,33 @@ extern void* android_dlopen_ext(const char* filename, int flag, const android_dl
|
||||
extern bool android_init_namespaces(const char* public_ns_sonames,
|
||||
const char* anon_ns_library_path);
|
||||
|
||||
|
||||
enum {
|
||||
/* A regular namespace is the namespace with a custom search path that does
|
||||
* not impose any restrictions on the location of native libraries.
|
||||
*/
|
||||
ANDROID_NAMESPACE_TYPE_REGULAR = 0,
|
||||
|
||||
/* An isolated namespace requires all the libraries to be on the search path
|
||||
* or under permitted_when_isolated_path. The search path is the union of
|
||||
* ld_library_path and default_library_path.
|
||||
*/
|
||||
ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
|
||||
|
||||
/* The shared namespace clones the list of libraries of the caller namespace upon creation
|
||||
* which means that they are shared between namespaces - the caller namespace and the new one
|
||||
* will use the same copy of a library if it was loaded prior to android_create_namespace call.
|
||||
*
|
||||
* Note that libraries loaded after the namespace is created will not be shared.
|
||||
*
|
||||
* Shared namespaces can be isolated or regular. Note that they do not inherit the search path nor
|
||||
* permitted_path from the caller's namespace.
|
||||
*/
|
||||
ANDROID_NAMESPACE_TYPE_SHARED = 2,
|
||||
ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED = ANDROID_NAMESPACE_TYPE_SHARED |
|
||||
ANDROID_NAMESPACE_TYPE_ISOLATED,
|
||||
};
|
||||
|
||||
/*
|
||||
* Creates new linker namespace.
|
||||
* ld_library_path and default_library_path represent the search path
|
||||
@ -152,19 +179,19 @@ extern bool android_init_namespaces(const char* public_ns_sonames,
|
||||
* 2. In directories specified by DT_RUNPATH of the "needed by" binary.
|
||||
* 3. deault_library_path (This of this as namespace-local default library path)
|
||||
*
|
||||
* When is_isolated is true the resulting namespace requires all of the libraries
|
||||
* to be on the search path or under the permitted_when_isolated_path; the search_path is
|
||||
* ld_library_path:default_library_path. Note that the permitted_when_isolated_path path
|
||||
* is not part of the search_path and does not affect the search order. It is a way
|
||||
* to allow loading libraries from specific locations when using absolute path.
|
||||
*
|
||||
* When type is ANDROID_NAMESPACE_TYPE_ISOLATED the resulting namespace requires all of
|
||||
* the libraries to be on the search path or under the permitted_when_isolated_path;
|
||||
* the search_path is ld_library_path:default_library_path. Note that the
|
||||
* permitted_when_isolated_path path is not part of the search_path and
|
||||
* does not affect the search order. It is a way to allow loading libraries from specific
|
||||
* locations when using absolute path.
|
||||
* If a library or any of its dependencies are outside of the permitted_when_isolated_path
|
||||
* and search_path, and it is not part of the public namespace dlopen will fail.
|
||||
*/
|
||||
extern struct android_namespace_t* android_create_namespace(const char* name,
|
||||
const char* ld_library_path,
|
||||
const char* default_library_path,
|
||||
bool is_isolated,
|
||||
uint64_t type,
|
||||
const char* permitted_when_isolated_path);
|
||||
|
||||
__END_DECLS
|
||||
|
@ -65,7 +65,7 @@ bool android_init_namespaces(const char* public_ns_sonames __unused,
|
||||
struct android_namespace_t* android_create_namespace(const char* name __unused,
|
||||
const char* ld_library_path __unused,
|
||||
const char* default_library_path __unused,
|
||||
bool isolated __unused,
|
||||
uint64_t type __unused,
|
||||
const char* permitted_when_isolated_path __unused) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -148,12 +148,14 @@ bool android_init_namespaces(const char* public_ns_sonames,
|
||||
}
|
||||
|
||||
android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path,
|
||||
const char* default_library_path, bool is_isolated,
|
||||
const char* default_library_path, uint64_t type,
|
||||
const char* permitted_when_isolated_path) {
|
||||
void* caller_addr = __builtin_return_address(0);
|
||||
ScopedPthreadMutexLocker locker(&g_dl_mutex);
|
||||
|
||||
android_namespace_t* result = create_namespace(name, ld_library_path, default_library_path,
|
||||
is_isolated, permitted_when_isolated_path);
|
||||
android_namespace_t* result = create_namespace(caller_addr, name, ld_library_path,
|
||||
default_library_path, type,
|
||||
permitted_when_isolated_path);
|
||||
|
||||
if (result == nullptr) {
|
||||
__bionic_format_dlerror("android_create_namespace failed", linker_get_error_buffer());
|
||||
|
@ -1747,7 +1747,6 @@ static bool load_library(android_namespace_t* ns,
|
||||
});
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
static bool load_library(android_namespace_t* ns,
|
||||
@ -2377,8 +2376,12 @@ bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_
|
||||
g_public_namespace_initialized = true;
|
||||
|
||||
// create anonymous namespace
|
||||
// When the caller is nullptr - create_namespace will take global group
|
||||
// from the anonymous namespace, which is fine because anonymous namespace
|
||||
// is still pointing to the default one.
|
||||
android_namespace_t* anon_ns =
|
||||
create_namespace("(anonymous)", nullptr, anon_ns_library_path, false, nullptr);
|
||||
create_namespace(nullptr, "(anonymous)", nullptr, anon_ns_library_path,
|
||||
ANDROID_NAMESPACE_TYPE_REGULAR, nullptr);
|
||||
|
||||
if (anon_ns == nullptr) {
|
||||
g_public_namespace_initialized = false;
|
||||
@ -2389,16 +2392,23 @@ bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_
|
||||
return true;
|
||||
}
|
||||
|
||||
android_namespace_t* create_namespace(const char* name,
|
||||
android_namespace_t* create_namespace(const void* caller_addr,
|
||||
const char* name,
|
||||
const char* ld_library_path,
|
||||
const char* default_library_path,
|
||||
bool is_isolated,
|
||||
uint64_t type,
|
||||
const char* permitted_when_isolated_path) {
|
||||
if (!g_public_namespace_initialized) {
|
||||
DL_ERR("cannot create namespace: public namespace is not initialized.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
soinfo* caller_soinfo = find_containing_library(caller_addr);
|
||||
|
||||
android_namespace_t* caller_ns = caller_soinfo != nullptr ?
|
||||
caller_soinfo->get_namespace() :
|
||||
g_anonymous_namespace;
|
||||
|
||||
ProtectedDataGuard guard;
|
||||
std::vector<std::string> ld_library_paths;
|
||||
std::vector<std::string> default_library_paths;
|
||||
@ -2410,14 +2420,20 @@ android_namespace_t* create_namespace(const char* name,
|
||||
|
||||
android_namespace_t* ns = new (g_namespace_allocator.alloc()) android_namespace_t();
|
||||
ns->set_name(name);
|
||||
ns->set_isolated(is_isolated);
|
||||
ns->set_isolated((type & ANDROID_NAMESPACE_TYPE_ISOLATED) != 0);
|
||||
ns->set_ld_library_paths(std::move(ld_library_paths));
|
||||
ns->set_default_library_paths(std::move(default_library_paths));
|
||||
ns->set_permitted_paths(std::move(permitted_paths));
|
||||
|
||||
// TODO(dimtiry): Should this be global group of caller's namespace?
|
||||
auto global_group = make_global_group(&g_default_namespace);
|
||||
std::copy(global_group.begin(), global_group.end(), std::back_inserter(ns->soinfo_list()));
|
||||
if ((type & ANDROID_NAMESPACE_TYPE_SHARED) != 0) {
|
||||
// If shared - clone the caller namespace
|
||||
auto& soinfo_list = caller_ns->soinfo_list();
|
||||
std::copy(soinfo_list.begin(), soinfo_list.end(), std::back_inserter(ns->soinfo_list()));
|
||||
} else {
|
||||
// If not shared - copy only the global group
|
||||
auto global_group = make_global_group(caller_ns);
|
||||
std::copy(global_group.begin(), global_group.end(), std::back_inserter(ns->soinfo_list()));
|
||||
}
|
||||
|
||||
return ns;
|
||||
}
|
||||
|
@ -444,8 +444,8 @@ void set_application_target_sdk_version(uint32_t target);
|
||||
uint32_t get_application_target_sdk_version();
|
||||
|
||||
bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_path);
|
||||
android_namespace_t* create_namespace(const char* name, const char* ld_library_path,
|
||||
const char* default_library_path, bool is_isolated,
|
||||
const char* permitted_when_isolated_path);
|
||||
android_namespace_t* create_namespace(const void* caller_addr, const char* name,
|
||||
const char* ld_library_path, const char* default_library_path,
|
||||
uint64_t type, const char* permitted_when_isolated_path);
|
||||
|
||||
#endif
|
||||
|
@ -55,6 +55,7 @@
|
||||
*********************************************************************/
|
||||
|
||||
#include "private/libc_logging.h"
|
||||
#include <unistd.h>
|
||||
|
||||
__LIBC_HIDDEN__ extern int g_ld_debug_verbosity;
|
||||
|
||||
|
@ -625,13 +625,21 @@ TEST(dlext, ns_smoke) {
|
||||
|
||||
// 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);
|
||||
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, nullptr);
|
||||
android_namespace_t* ns1 =
|
||||
android_create_namespace("private", nullptr,
|
||||
(lib_path + "/private_namespace_libs").c_str(),
|
||||
ANDROID_NAMESPACE_TYPE_REGULAR, nullptr);
|
||||
ASSERT_TRUE(ns1 != nullptr) << dlerror();
|
||||
|
||||
android_namespace_t* ns2 = android_create_namespace("private_isolated", nullptr, (lib_path + "/private_namespace_libs").c_str(), true, nullptr);
|
||||
android_namespace_t* ns2 =
|
||||
android_create_namespace("private_isolated", nullptr,
|
||||
(lib_path + "/private_namespace_libs").c_str(),
|
||||
ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr);
|
||||
ASSERT_TRUE(ns2 != nullptr) << dlerror();
|
||||
|
||||
// This should not have affect search path for default namespace:
|
||||
@ -732,13 +740,22 @@ TEST(dlext, ns_isolated) {
|
||||
|
||||
ASSERT_TRUE(android_init_namespaces(path.c_str(), nullptr)) << dlerror();
|
||||
|
||||
android_namespace_t* ns_not_isolated = android_create_namespace("private", nullptr, (lib_path + "/private_namespace_libs").c_str(), false, nullptr);
|
||||
android_namespace_t* ns_not_isolated =
|
||||
android_create_namespace("private", nullptr,
|
||||
(lib_path + "/private_namespace_libs").c_str(),
|
||||
ANDROID_NAMESPACE_TYPE_REGULAR, nullptr);
|
||||
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, nullptr);
|
||||
android_namespace_t* ns_isolated =
|
||||
android_create_namespace("private_isolated1", nullptr,
|
||||
(lib_path + "/private_namespace_libs").c_str(),
|
||||
ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr);
|
||||
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, lib_path.c_str());
|
||||
android_namespace_t* ns_isolated2 =
|
||||
android_create_namespace("private_isolated2",
|
||||
(lib_path + "/private_namespace_libs").c_str(),
|
||||
nullptr, ANDROID_NAMESPACE_TYPE_ISOLATED, lib_path.c_str());
|
||||
ASSERT_TRUE(ns_isolated2 != nullptr) << dlerror();
|
||||
|
||||
ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr);
|
||||
@ -808,6 +825,122 @@ TEST(dlext, ns_isolated) {
|
||||
dlclose(handle1);
|
||||
}
|
||||
|
||||
TEST(dlext, ns_shared) {
|
||||
static const char* root_lib = "libnstest_root_not_isolated.so";
|
||||
static const char* root_lib_isolated = "libnstest_root.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;
|
||||
const std::string lib_public_path = lib_path + "/public_namespace_libs/" + g_public_lib;
|
||||
void* handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
|
||||
ASSERT_TRUE(handle_public != nullptr) << dlerror();
|
||||
|
||||
android_set_application_target_sdk_version(42U); // something > 23
|
||||
|
||||
ASSERT_TRUE(android_init_namespaces(path.c_str(), nullptr)) << dlerror();
|
||||
|
||||
// preload this library to the default namespace to check if it
|
||||
// is shared later on.
|
||||
void* handle_dlopened =
|
||||
dlopen((lib_path + "/private_namespace_libs/libnstest_dlopened.so").c_str(), RTLD_NOW);
|
||||
ASSERT_TRUE(handle_dlopened != nullptr) << dlerror();
|
||||
|
||||
android_namespace_t* ns_not_isolated =
|
||||
android_create_namespace("private", nullptr,
|
||||
(lib_path + "/private_namespace_libs").c_str(),
|
||||
ANDROID_NAMESPACE_TYPE_REGULAR, nullptr);
|
||||
ASSERT_TRUE(ns_not_isolated != nullptr) << dlerror();
|
||||
|
||||
android_namespace_t* ns_isolated_shared =
|
||||
android_create_namespace("private_isolated_shared", nullptr,
|
||||
(lib_path + "/private_namespace_libs").c_str(),
|
||||
ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED,
|
||||
nullptr);
|
||||
ASSERT_TRUE(ns_isolated_shared != 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_shared;
|
||||
|
||||
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 + "\" is not accessible for the namespace \"private_isolated_shared\"", dlerror());
|
||||
|
||||
// load libnstest_root.so to shared namespace in order to check that everything is different
|
||||
// except shared libnstest_dlopened.so
|
||||
|
||||
handle2 = android_dlopen_ext(root_lib_isolated, RTLD_NOW, &extinfo);
|
||||
|
||||
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();
|
||||
fn_t ns_get_local_string_shared = reinterpret_cast<fn_t>(dlsym(handle2, "ns_get_local_string"));
|
||||
ASSERT_TRUE(ns_get_local_string_shared != nullptr) << dlerror();
|
||||
|
||||
ASSERT_STREQ("This string is local to root library", ns_get_local_string());
|
||||
ASSERT_STREQ("This string is local to root library", ns_get_local_string_shared());
|
||||
ASSERT_TRUE(ns_get_local_string() != ns_get_local_string_shared());
|
||||
|
||||
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();
|
||||
fn_t ns_get_private_extern_string_shared =
|
||||
reinterpret_cast<fn_t>(dlsym(handle2, "ns_get_private_extern_string"));
|
||||
ASSERT_TRUE(ns_get_private_extern_string_shared() != nullptr) << dlerror();
|
||||
|
||||
ASSERT_STREQ("This string is from private namespace", ns_get_private_extern_string());
|
||||
ASSERT_STREQ("This string is from private namespace", ns_get_private_extern_string_shared());
|
||||
ASSERT_TRUE(ns_get_private_extern_string() != ns_get_private_extern_string_shared());
|
||||
|
||||
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();
|
||||
fn_t ns_get_public_extern_string_shared =
|
||||
reinterpret_cast<fn_t>(dlsym(handle2, "ns_get_public_extern_string"));
|
||||
ASSERT_TRUE(ns_get_public_extern_string_shared != nullptr) << dlerror();
|
||||
|
||||
ASSERT_STREQ("This string is from public namespace", ns_get_public_extern_string());
|
||||
ASSERT_STREQ("This string is from public namespace", ns_get_public_extern_string_shared());
|
||||
ASSERT_TRUE(ns_get_public_extern_string() == ns_get_public_extern_string_shared());
|
||||
|
||||
fn_t ns_get_dlopened_string = reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_dlopened_string"));
|
||||
ASSERT_TRUE(ns_get_dlopened_string != nullptr) << dlerror();
|
||||
fn_t ns_get_dlopened_string_shared = reinterpret_cast<fn_t>(dlsym(handle2, "ns_get_dlopened_string"));
|
||||
ASSERT_TRUE(ns_get_dlopened_string_shared != nullptr) << dlerror();
|
||||
const char** ns_dlopened_string = static_cast<const char**>(dlsym(handle_dlopened, "g_private_dlopened_string"));
|
||||
ASSERT_TRUE(ns_dlopened_string != nullptr) << dlerror();
|
||||
|
||||
ASSERT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string());
|
||||
ASSERT_STREQ("This string is from private namespace (dlopened library)", *ns_dlopened_string);
|
||||
ASSERT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string_shared());
|
||||
ASSERT_TRUE(ns_get_dlopened_string() != ns_get_dlopened_string_shared());
|
||||
ASSERT_TRUE(*ns_dlopened_string == ns_get_dlopened_string_shared());
|
||||
|
||||
dlclose(handle1);
|
||||
dlclose(handle2);
|
||||
}
|
||||
|
||||
TEST(dlext, ns_anonymous) {
|
||||
static const char* root_lib = "libnstest_root.so";
|
||||
std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib;
|
||||
@ -825,7 +958,7 @@ TEST(dlext, ns_anonymous) {
|
||||
android_namespace_t* ns = android_create_namespace(
|
||||
"private", nullptr,
|
||||
(lib_path + "/private_namespace_libs").c_str(),
|
||||
false, nullptr);
|
||||
ANDROID_NAMESPACE_TYPE_REGULAR, nullptr);
|
||||
|
||||
ASSERT_TRUE(ns != nullptr) << dlerror();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user