Merge "Replace public library list with shared lib sonames (part 1/2)"
This commit is contained in:
commit
132768084e
5 changed files with 182 additions and 75 deletions
|
@ -186,7 +186,9 @@ static const char* const* g_default_ld_paths;
|
|||
static std::vector<std::string> g_ld_preload_names;
|
||||
|
||||
static bool g_public_namespace_initialized;
|
||||
static soinfo_list_t g_public_namespace;
|
||||
|
||||
// TODO (dimitry): Remove once interface between libnativeloader and the linker is updated
|
||||
static std::unordered_set<std::string> g_public_namespace_sonames;
|
||||
|
||||
#if STATS
|
||||
struct linker_stats_t {
|
||||
|
@ -525,7 +527,8 @@ class LoadTask {
|
|||
|
||||
static deleter_t deleter;
|
||||
|
||||
static LoadTask* create(const char* name, soinfo* needed_by,
|
||||
static LoadTask* create(const char* name,
|
||||
soinfo* needed_by,
|
||||
std::unordered_map<const soinfo*, ElfReader>* readers_map) {
|
||||
LoadTask* ptr = TypeBasedAllocator<LoadTask>::alloc();
|
||||
return new (ptr) LoadTask(name, needed_by, readers_map);
|
||||
|
@ -616,7 +619,8 @@ class LoadTask {
|
|||
}
|
||||
|
||||
private:
|
||||
LoadTask(const char* name, soinfo* needed_by,
|
||||
LoadTask(const char* name,
|
||||
soinfo* needed_by,
|
||||
std::unordered_map<const soinfo*, ElfReader>* readers_map)
|
||||
: name_(name), needed_by_(needed_by), si_(nullptr),
|
||||
fd_(-1), close_fd_(false), file_offset_(0), elf_readers_map_(readers_map),
|
||||
|
@ -1094,14 +1098,6 @@ static bool load_library(android_namespace_t* ns,
|
|||
|
||||
soinfo* si = ns->soinfo_list().find_if(predicate);
|
||||
|
||||
// check public namespace
|
||||
if (si == nullptr) {
|
||||
si = g_public_namespace.find_if(predicate);
|
||||
if (si != nullptr) {
|
||||
ns->add_soinfo(si);
|
||||
}
|
||||
}
|
||||
|
||||
if (si != nullptr) {
|
||||
TRACE("library \"%s\" is already loaded under different name/path \"%s\" - "
|
||||
"will return existing soinfo", name, si->get_realpath());
|
||||
|
@ -1117,6 +1113,9 @@ static bool load_library(android_namespace_t* ns,
|
|||
|
||||
if (!ns->is_accessible(realpath)) {
|
||||
// TODO(dimitry): workaround for http://b/26394120 - the grey-list
|
||||
|
||||
// TODO(dimitry) before O release: add a namespace attribute to have this enabled
|
||||
// only for classloader-namespaces
|
||||
const soinfo* needed_by = task->is_dt_needed() ? task->get_needed_by() : nullptr;
|
||||
if (is_greylisted(name, needed_by)) {
|
||||
// print warning only if needed by non-system library
|
||||
|
@ -1249,11 +1248,79 @@ static bool find_loaded_library_by_soname(android_namespace_t* ns,
|
|||
});
|
||||
}
|
||||
|
||||
static std::string resolve_soname(const std::string& name) {
|
||||
// We assume that soname equals to basename here
|
||||
|
||||
// TODO(dimitry): consider having honest absolute-path -> soname resolution
|
||||
// note that since we might end up refusing to load this library because
|
||||
// it is not in shared libs list we need to get the soname without actually loading
|
||||
// the library.
|
||||
//
|
||||
// On the other hand there are several places where we already assume that
|
||||
// soname == basename in particular for any not-loaded library mentioned
|
||||
// in DT_NEEDED list.
|
||||
return basename(name.c_str());
|
||||
}
|
||||
|
||||
|
||||
static bool find_library_in_linked_namespace(const android_namespace_link_t& namespace_link,
|
||||
LoadTask* task,
|
||||
int rtld_flags) {
|
||||
android_namespace_t* ns = namespace_link.linked_namespace();
|
||||
|
||||
soinfo* candidate;
|
||||
bool loaded = false;
|
||||
|
||||
std::string soname;
|
||||
if (find_loaded_library_by_soname(ns, task->get_name(), &candidate)) {
|
||||
loaded = true;
|
||||
soname = candidate->get_soname();
|
||||
} else {
|
||||
soname = resolve_soname(task->get_name());
|
||||
}
|
||||
|
||||
if (!namespace_link.is_accessible(soname.c_str())) {
|
||||
// the library is not accessible via namespace_link
|
||||
return false;
|
||||
}
|
||||
|
||||
// if library is already loaded - return it
|
||||
if (loaded) {
|
||||
task->set_soinfo(candidate);
|
||||
return true;
|
||||
}
|
||||
|
||||
// try to load the library - once namespace boundary is crossed
|
||||
// we need to load a library within separate load_group
|
||||
// to avoid using symbols from foreign namespace while.
|
||||
//
|
||||
// All symbols during relocation should be resolved within a
|
||||
// namespace to preserve library locality to a namespace.
|
||||
const char* name = task->get_name();
|
||||
if (find_libraries(ns,
|
||||
task->get_needed_by(),
|
||||
&name,
|
||||
1,
|
||||
&candidate,
|
||||
nullptr /* ld_preloads */,
|
||||
0 /* ld_preload_count*/,
|
||||
rtld_flags,
|
||||
nullptr /* extinfo*/,
|
||||
false /* add_as_children */,
|
||||
false /* search_linked_namespaces */)) {
|
||||
task->set_soinfo(candidate);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool find_library_internal(android_namespace_t* ns,
|
||||
LoadTask* task,
|
||||
ZipArchiveCache* zip_archive_cache,
|
||||
LoadTaskList* load_tasks,
|
||||
int rtld_flags) {
|
||||
int rtld_flags,
|
||||
bool search_linked_namespaces) {
|
||||
soinfo* candidate;
|
||||
|
||||
if (find_loaded_library_by_soname(ns, task->get_name(), &candidate)) {
|
||||
|
@ -1261,25 +1328,27 @@ static bool find_library_internal(android_namespace_t* ns,
|
|||
return true;
|
||||
}
|
||||
|
||||
if (ns != &g_default_namespace) {
|
||||
// check public namespace
|
||||
candidate = g_public_namespace.find_if([&](soinfo* si) {
|
||||
return strcmp(task->get_name(), si->get_soname()) == 0;
|
||||
});
|
||||
|
||||
if (candidate != nullptr) {
|
||||
ns->add_soinfo(candidate);
|
||||
task->set_soinfo(candidate);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Library might still be loaded, the accurate detection
|
||||
// of this fact is done by load_library.
|
||||
TRACE("[ \"%s\" find_loaded_library_by_soname failed (*candidate=%s@%p). Trying harder...]",
|
||||
task->get_name(), candidate == nullptr ? "n/a" : candidate->get_realpath(), candidate);
|
||||
|
||||
return load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags);
|
||||
if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (search_linked_namespaces) {
|
||||
// if a library was not found - look into linked namespaces
|
||||
for (auto& linked_namespace : ns->linked_namespaces()) {
|
||||
if (find_library_in_linked_namespace(linked_namespace,
|
||||
task,
|
||||
rtld_flags)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void soinfo_unload(soinfo* si);
|
||||
|
@ -1344,7 +1413,8 @@ bool find_libraries(android_namespace_t* ns,
|
|||
size_t ld_preloads_count,
|
||||
int rtld_flags,
|
||||
const android_dlextinfo* extinfo,
|
||||
bool add_as_children) {
|
||||
bool add_as_children,
|
||||
bool search_linked_namespaces) {
|
||||
// Step 0: prepare.
|
||||
LoadTaskList load_tasks;
|
||||
std::unordered_map<const soinfo*, ElfReader> readers_map;
|
||||
|
@ -1396,7 +1466,12 @@ bool find_libraries(android_namespace_t* ns,
|
|||
task->set_extinfo(is_dt_needed ? nullptr : extinfo);
|
||||
task->set_dt_needed(is_dt_needed);
|
||||
|
||||
if(!find_library_internal(ns, task, &zip_archive_cache, &load_tasks, rtld_flags)) {
|
||||
if (!find_library_internal(ns,
|
||||
task,
|
||||
&zip_archive_cache,
|
||||
&load_tasks,
|
||||
rtld_flags,
|
||||
search_linked_namespaces || is_dt_needed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1404,10 +1479,10 @@ bool find_libraries(android_namespace_t* ns,
|
|||
|
||||
if (is_dt_needed) {
|
||||
needed_by->add_child(si);
|
||||
}
|
||||
|
||||
if (si->is_linked()) {
|
||||
si->increment_ref_count();
|
||||
if (si->is_linked()) {
|
||||
si->increment_ref_count();
|
||||
}
|
||||
}
|
||||
|
||||
// When ld_preloads is not null, the first
|
||||
|
@ -1471,10 +1546,6 @@ bool find_libraries(android_namespace_t* ns,
|
|||
return true;
|
||||
});
|
||||
|
||||
// We need to increment ref_count in case
|
||||
// the root of the local group was not linked.
|
||||
bool was_local_group_root_linked = local_group.front()->is_linked();
|
||||
|
||||
bool linked = local_group.visit([&](soinfo* si) {
|
||||
if (!si->is_linked()) {
|
||||
if (!si->link_image(global_group, local_group, extinfo) ||
|
||||
|
@ -1496,10 +1567,6 @@ bool find_libraries(android_namespace_t* ns,
|
|||
failure_guard.disable();
|
||||
}
|
||||
|
||||
if (!was_local_group_root_linked) {
|
||||
local_group.front()->increment_ref_count();
|
||||
}
|
||||
|
||||
return linked;
|
||||
}
|
||||
|
||||
|
@ -1511,11 +1578,22 @@ static soinfo* find_library(android_namespace_t* ns,
|
|||
|
||||
if (name == nullptr) {
|
||||
si = solist_get_somain();
|
||||
} else if (!find_libraries(ns, needed_by, &name, 1, &si, nullptr, 0, rtld_flags,
|
||||
extinfo, /* add_as_children */ false)) {
|
||||
} else if (!find_libraries(ns,
|
||||
needed_by,
|
||||
&name,
|
||||
1,
|
||||
&si,
|
||||
nullptr,
|
||||
0,
|
||||
rtld_flags,
|
||||
extinfo,
|
||||
false /* add_as_children */,
|
||||
true /* search_linked_namespaces */)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
si->increment_ref_count();
|
||||
|
||||
return si;
|
||||
}
|
||||
|
||||
|
@ -1934,28 +2012,11 @@ bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_
|
|||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> sonames = android::base::Split(public_ns_sonames, ":");
|
||||
auto sonames = android::base::Split(public_ns_sonames, ":");
|
||||
|
||||
ProtectedDataGuard guard;
|
||||
|
||||
auto failure_guard = make_scope_guard([&]() {
|
||||
g_public_namespace.clear();
|
||||
});
|
||||
|
||||
for (const auto& soname : sonames) {
|
||||
soinfo* candidate = nullptr;
|
||||
|
||||
find_loaded_library_by_soname(&g_default_namespace, soname.c_str(), &candidate);
|
||||
|
||||
if (candidate == nullptr) {
|
||||
DL_ERR("error initializing public namespace: a library with soname \"%s\""
|
||||
" was not found in the default namespace", soname.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
candidate->set_nodelete();
|
||||
g_public_namespace.push_back(candidate);
|
||||
}
|
||||
g_public_namespace_sonames = std::unordered_set<std::string>(sonames.begin(), sonames.end());
|
||||
|
||||
g_public_namespace_initialized = true;
|
||||
|
||||
|
@ -1971,8 +2032,8 @@ bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_
|
|||
g_public_namespace_initialized = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
g_anonymous_namespace = anon_ns;
|
||||
failure_guard.disable();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2028,6 +2089,10 @@ android_namespace_t* create_namespace(const void* caller_addr,
|
|||
add_soinfos_to_namespace(get_shared_group(parent_namespace), ns);
|
||||
}
|
||||
|
||||
// link it to default namespace
|
||||
// TODO (dimitry): replace this with user-supplied link once interface is updated
|
||||
ns->add_linked_namespace(&g_default_namespace, g_public_namespace_sonames);
|
||||
|
||||
return ns;
|
||||
}
|
||||
|
||||
|
|
|
@ -356,9 +356,17 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW(
|
|||
size_t needed_libraries_count = needed_library_name_list.size();
|
||||
|
||||
if (needed_libraries_count > 0 &&
|
||||
!find_libraries(&g_default_namespace, si, needed_library_names, needed_libraries_count,
|
||||
nullptr, &g_ld_preloads, ld_preloads_count, RTLD_GLOBAL, nullptr,
|
||||
/* add_as_children */ true)) {
|
||||
!find_libraries(&g_default_namespace,
|
||||
si,
|
||||
needed_library_names,
|
||||
needed_libraries_count,
|
||||
nullptr,
|
||||
&g_ld_preloads,
|
||||
ld_preloads_count,
|
||||
RTLD_GLOBAL,
|
||||
nullptr,
|
||||
true /* add_as_children */,
|
||||
true /* search_linked_namespaces */)) {
|
||||
__libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer());
|
||||
} else if (needed_libraries_count == 0) {
|
||||
if (!si->link_image(g_empty_list, soinfo_list_t::make_list(si), nullptr)) {
|
||||
|
|
|
@ -58,7 +58,8 @@ bool find_libraries(android_namespace_t* ns,
|
|||
size_t ld_preloads_count,
|
||||
int rtld_flags,
|
||||
const android_dlextinfo* extinfo,
|
||||
bool add_as_children);
|
||||
bool add_as_children,
|
||||
bool search_linked_namespaces);
|
||||
|
||||
void solist_add_soinfo(soinfo* si);
|
||||
bool solist_remove_soinfo(soinfo* si);
|
||||
|
|
|
@ -33,6 +33,29 @@
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
struct android_namespace_t;
|
||||
|
||||
struct android_namespace_link_t {
|
||||
public:
|
||||
android_namespace_link_t(android_namespace_t* linked_namespace,
|
||||
const std::unordered_set<std::string>& shared_lib_sonames)
|
||||
: linked_namespace_(linked_namespace), shared_lib_sonames_(shared_lib_sonames)
|
||||
{}
|
||||
|
||||
android_namespace_t* linked_namespace() const {
|
||||
return linked_namespace_;
|
||||
}
|
||||
|
||||
bool is_accessible(const char* soname) const {
|
||||
return shared_lib_sonames_.find(soname) != shared_lib_sonames_.end();
|
||||
}
|
||||
|
||||
private:
|
||||
android_namespace_t* const linked_namespace_;
|
||||
const std::unordered_set<std::string> shared_lib_sonames_;
|
||||
};
|
||||
|
||||
struct android_namespace_t {
|
||||
public:
|
||||
|
@ -65,6 +88,14 @@ struct android_namespace_t {
|
|||
permitted_paths_ = permitted_paths;
|
||||
}
|
||||
|
||||
const std::vector<android_namespace_link_t>& linked_namespaces() const {
|
||||
return linked_namespaces_;
|
||||
}
|
||||
void add_linked_namespace(android_namespace_t* linked_namespace,
|
||||
const std::unordered_set<std::string>& shared_lib_sonames) {
|
||||
linked_namespaces_.push_back(android_namespace_link_t(linked_namespace, shared_lib_sonames));
|
||||
}
|
||||
|
||||
void add_soinfo(soinfo* si) {
|
||||
soinfo_list_.push_back(si);
|
||||
}
|
||||
|
@ -93,6 +124,11 @@ struct android_namespace_t {
|
|||
std::vector<std::string> ld_library_paths_;
|
||||
std::vector<std::string> default_library_paths_;
|
||||
std::vector<std::string> permitted_paths_;
|
||||
// Loader looks into linked namespace if it was not able
|
||||
// to find a library in this namespace. Note that library
|
||||
// lookup in linked namespaces are limited by the list of
|
||||
// shared sonames.
|
||||
std::vector<android_namespace_link_t> linked_namespaces_;
|
||||
soinfo_list_t soinfo_list_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(android_namespace_t);
|
||||
|
|
|
@ -621,12 +621,6 @@ 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_namespaces(path.c_str(), nullptr));
|
||||
ASSERT_STREQ("android_init_namespaces failed: error initializing public namespace: "
|
||||
"a library with soname \"libnstest_public.so\" was not found in the "
|
||||
"default namespace",
|
||||
dlerror());
|
||||
|
||||
ASSERT_FALSE(android_init_namespaces("", nullptr));
|
||||
ASSERT_STREQ("android_init_namespaces failed: error initializing public namespace: "
|
||||
"the list of public libraries is empty.", dlerror());
|
||||
|
@ -637,12 +631,15 @@ TEST(dlext, ns_smoke) {
|
|||
|
||||
ASSERT_TRUE(android_init_namespaces(path.c_str(), nullptr)) << dlerror();
|
||||
|
||||
// Check that libraries added to public namespace are NODELETE
|
||||
// Check that libraries added to public namespace are not NODELETE
|
||||
dlclose(handle_public);
|
||||
handle_public = dlopen((get_testlib_root() + "/public_namespace_libs/" + g_public_lib).c_str(),
|
||||
RTLD_NOW | RTLD_NOLOAD);
|
||||
handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW | RTLD_NOLOAD);
|
||||
|
||||
ASSERT_TRUE(handle_public != nullptr) << dlerror();
|
||||
ASSERT_TRUE(handle_public == nullptr);
|
||||
ASSERT_EQ(std::string("dlopen failed: library \"") + lib_public_path +
|
||||
"\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror());
|
||||
|
||||
handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
|
||||
|
||||
android_namespace_t* ns1 =
|
||||
android_create_namespace("private", nullptr,
|
||||
|
|
Loading…
Reference in a new issue