Merge "linker: implement shared namespaces"
This commit is contained in:
commit
be7c7fe218
@ -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…
Reference in New Issue
Block a user