linker: the global group is added to all built-in namespaces
With ld.config.txt, we now have multiple built-in namespaces other than the default namespace. Libs (and their dependents) listed in LD_PRELOAD must be visible to those additional namespaces as well. This also adds a debugging only feature: path to the linker config file can be customized via LD_CONFIG_FILE environment variable. This works only for debuggable builds. Bug: 38114603 Bug: 62815515 Test: 1. ./external/compiler-rt/lib/asan/scripts/asan_device_setup --lib prebuilts/clang/host/linux-x86/clang-stable/lib64/clang/5.0/lib/linux 2. enable talkback shortcut 3. in the home screen, hold vol-up/down together 4. device does not reboots and talkback shortcut is toggled Test: bionic-unit-tests and linker-unit-tests successful Change-Id: I9a03591053f4a9caea82f0dcb23e7a3d324bb9bd
This commit is contained in:
parent
5ac5a7c083
commit
02586a2a34
17 changed files with 472 additions and 100 deletions
|
@ -101,6 +101,12 @@ cc_binary {
|
|||
"-Werror",
|
||||
],
|
||||
|
||||
product_variables: {
|
||||
debuggable: {
|
||||
cppflags: ["-DUSE_LD_CONFIG_FILE"],
|
||||
},
|
||||
},
|
||||
|
||||
cppflags: ["-Wold-style-cast"],
|
||||
|
||||
// we are going to link libc++_static manually because
|
||||
|
|
|
@ -558,9 +558,10 @@ class LoadTask {
|
|||
|
||||
static LoadTask* create(const char* name,
|
||||
soinfo* needed_by,
|
||||
android_namespace_t* start_from,
|
||||
std::unordered_map<const soinfo*, ElfReader>* readers_map) {
|
||||
LoadTask* ptr = TypeBasedAllocator<LoadTask>::alloc();
|
||||
return new (ptr) LoadTask(name, needed_by, readers_map);
|
||||
return new (ptr) LoadTask(name, needed_by, start_from, readers_map);
|
||||
}
|
||||
|
||||
const char* get_name() const {
|
||||
|
@ -612,6 +613,11 @@ class LoadTask {
|
|||
is_dt_needed_ = is_dt_needed;
|
||||
}
|
||||
|
||||
// returns the namespace from where we need to start loading this.
|
||||
const android_namespace_t* get_start_from() const {
|
||||
return start_from_;
|
||||
}
|
||||
|
||||
const ElfReader& get_elf_reader() const {
|
||||
CHECK(si_ != nullptr);
|
||||
return (*elf_readers_map_)[si_];
|
||||
|
@ -650,10 +656,11 @@ class LoadTask {
|
|||
private:
|
||||
LoadTask(const char* name,
|
||||
soinfo* needed_by,
|
||||
android_namespace_t* start_from,
|
||||
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),
|
||||
is_dt_needed_(false) {}
|
||||
is_dt_needed_(false), start_from_(start_from) {}
|
||||
|
||||
~LoadTask() {
|
||||
if (fd_ != -1 && close_fd_) {
|
||||
|
@ -672,6 +679,7 @@ class LoadTask {
|
|||
// TODO(dimitry): needed by workaround for http://b/26394120 (the grey-list)
|
||||
bool is_dt_needed_;
|
||||
// END OF WORKAROUND
|
||||
const android_namespace_t* const start_from_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(LoadTask);
|
||||
};
|
||||
|
@ -1041,7 +1049,7 @@ static int open_library(android_namespace_t* ns,
|
|||
ZipArchiveCache* zip_archive_cache,
|
||||
const char* name, soinfo *needed_by,
|
||||
off64_t* file_offset, std::string* realpath) {
|
||||
TRACE("[ opening %s ]", name);
|
||||
TRACE("[ opening %s at namespace %s]", name, ns->get_name());
|
||||
|
||||
// If the name contains a slash, we should attempt to open it directly and not search the paths.
|
||||
if (strchr(name, '/') != nullptr) {
|
||||
|
@ -1273,7 +1281,7 @@ static bool load_library(android_namespace_t* ns,
|
|||
}
|
||||
|
||||
for_each_dt_needed(task->get_elf_reader(), [&](const char* name) {
|
||||
load_tasks->push_back(LoadTask::create(name, si, task->get_readers_map()));
|
||||
load_tasks->push_back(LoadTask::create(name, si, ns, task->get_readers_map()));
|
||||
});
|
||||
|
||||
return true;
|
||||
|
@ -1368,8 +1376,7 @@ static bool find_loaded_library_by_soname(android_namespace_t* ns,
|
|||
}
|
||||
|
||||
static bool find_library_in_linked_namespace(const android_namespace_link_t& namespace_link,
|
||||
LoadTask* task,
|
||||
int rtld_flags) {
|
||||
LoadTask* task) {
|
||||
android_namespace_t* ns = namespace_link.linked_namespace();
|
||||
|
||||
soinfo* candidate;
|
||||
|
@ -1394,29 +1401,10 @@ static bool find_library_in_linked_namespace(const android_namespace_link_t& nam
|
|||
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;
|
||||
// returning true with empty soinfo means that the library is okay to be
|
||||
// loaded in the namespace buy has not yet been loaded there before.
|
||||
task->set_soinfo(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool find_library_internal(android_namespace_t* ns,
|
||||
|
@ -1445,9 +1433,24 @@ static bool find_library_internal(android_namespace_t* ns,
|
|||
// 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;
|
||||
task)) {
|
||||
if (task->get_soinfo() == nullptr) {
|
||||
// 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.
|
||||
//
|
||||
// However, actual linking is deferred until when the global group
|
||||
// is fully identified and is applied to all namespaces.
|
||||
// Otherwise, the libs in the linked namespace won't get symbols from
|
||||
// the global group.
|
||||
if (load_library(linked_namespace.linked_namespace(), task, zip_archive_cache, load_tasks, rtld_flags, false)) {
|
||||
return true;
|
||||
}
|
||||
// lib was not found in the namespace. Try next linked namespace.
|
||||
} else {
|
||||
// lib is already loaded
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1458,44 +1461,6 @@ static bool find_library_internal(android_namespace_t* ns,
|
|||
static void soinfo_unload(soinfo* si);
|
||||
static void soinfo_unload(soinfo* soinfos[], size_t count);
|
||||
|
||||
// TODO: this is slightly unusual way to construct
|
||||
// the global group for relocation. Not every RTLD_GLOBAL
|
||||
// library is included in this group for backwards-compatibility
|
||||
// reasons.
|
||||
//
|
||||
// This group consists of the main executable, LD_PRELOADs
|
||||
// and libraries with the DF_1_GLOBAL flag set.
|
||||
static soinfo_list_t make_global_group(android_namespace_t* ns) {
|
||||
soinfo_list_t global_group;
|
||||
ns->soinfo_list().for_each([&](soinfo* si) {
|
||||
if ((si->get_dt_flags_1() & DF_1_GLOBAL) != 0) {
|
||||
global_group.push_back(si);
|
||||
}
|
||||
});
|
||||
|
||||
return global_group;
|
||||
}
|
||||
|
||||
// This function provides a list of libraries to be shared
|
||||
// by the namespace. For the default namespace this is the global
|
||||
// group (see make_global_group). For all others this is a group
|
||||
// of RTLD_GLOBAL libraries (which includes the global group from
|
||||
// the default namespace).
|
||||
static soinfo_list_t get_shared_group(android_namespace_t* ns) {
|
||||
if (ns == &g_default_namespace) {
|
||||
return make_global_group(ns);
|
||||
}
|
||||
|
||||
soinfo_list_t shared_group;
|
||||
ns->soinfo_list().for_each([&](soinfo* si) {
|
||||
if ((si->get_rtld_flags() & RTLD_GLOBAL) != 0) {
|
||||
shared_group.push_back(si);
|
||||
}
|
||||
});
|
||||
|
||||
return shared_group;
|
||||
}
|
||||
|
||||
static void shuffle(std::vector<LoadTask*>* v) {
|
||||
for (size_t i = 0, size = v->size(); i < size; ++i) {
|
||||
size_t n = size - i;
|
||||
|
@ -1518,19 +1483,17 @@ bool find_libraries(android_namespace_t* ns,
|
|||
int rtld_flags,
|
||||
const android_dlextinfo* extinfo,
|
||||
bool add_as_children,
|
||||
bool search_linked_namespaces) {
|
||||
bool search_linked_namespaces,
|
||||
std::unordered_map<const soinfo*, ElfReader>& readers_map,
|
||||
std::vector<android_namespace_t*>* namespaces) {
|
||||
// Step 0: prepare.
|
||||
LoadTaskList load_tasks;
|
||||
std::unordered_map<const soinfo*, ElfReader> readers_map;
|
||||
|
||||
for (size_t i = 0; i < library_names_count; ++i) {
|
||||
const char* name = library_names[i];
|
||||
load_tasks.push_back(LoadTask::create(name, start_with, &readers_map));
|
||||
load_tasks.push_back(LoadTask::create(name, start_with, ns, &readers_map));
|
||||
}
|
||||
|
||||
// Construct global_group.
|
||||
soinfo_list_t global_group = make_global_group(ns);
|
||||
|
||||
// If soinfos array is null allocate one on stack.
|
||||
// The array is needed in case of failure; for example
|
||||
// when library_names[] = {libone.so, libtwo.so} and libone.so
|
||||
|
@ -1570,7 +1533,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,
|
||||
// try to find the load.
|
||||
// Note: start from the namespace that is stored in the LoadTask. This namespace
|
||||
// is different from the current namespace when the LoadTask is for a transitive
|
||||
// dependency and the lib that created the LoadTask is not found in the
|
||||
// current namespace but in one of the linked namespace.
|
||||
if (!find_library_internal(const_cast<android_namespace_t*>(task->get_start_from()),
|
||||
task,
|
||||
&zip_archive_cache,
|
||||
&load_tasks,
|
||||
|
@ -1629,18 +1597,61 @@ bool find_libraries(android_namespace_t* ns,
|
|||
}
|
||||
}
|
||||
|
||||
// Step 4: Add LD_PRELOADed libraries to the global group for
|
||||
// future runs. There is no need to explicitly add them to
|
||||
// the global group for this run because they are going to
|
||||
// appear in the local group in the correct order.
|
||||
// Step 4: Construct the global group. Note: DF_1_GLOBAL bit of a library is
|
||||
// determined at step 3.
|
||||
|
||||
// Step 4-1: DF_1_GLOBAL bit is force set for LD_PRELOADed libs because they
|
||||
// must be added to the global group
|
||||
if (ld_preloads != nullptr) {
|
||||
for (auto&& si : *ld_preloads) {
|
||||
si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4-2: Gather all DF_1_GLOBAL libs which were newly loaded during this
|
||||
// run. These will be the new member of the global group
|
||||
soinfo_list_t new_global_group_members;
|
||||
for (auto&& task : load_tasks) {
|
||||
soinfo* si = task->get_soinfo();
|
||||
if (!si->is_linked() && (si->get_dt_flags_1() & DF_1_GLOBAL) != 0) {
|
||||
new_global_group_members.push_back(si);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5: link libraries.
|
||||
// Step 4-3: Add the new global group members to all the linked namespaces
|
||||
for (auto si : new_global_group_members) {
|
||||
for (auto linked_ns : *namespaces) {
|
||||
if (si->get_primary_namespace() != linked_ns) {
|
||||
linked_ns->add_soinfo(si);
|
||||
si->add_secondary_namespace(linked_ns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5: link libraries that are not destined to this namespace.
|
||||
// Do this by recursively calling find_libraries on the namespace where the lib
|
||||
// was found during Step 1.
|
||||
for (auto&& task : load_tasks) {
|
||||
soinfo* si = task->get_soinfo();
|
||||
if (si->get_primary_namespace() != ns) {
|
||||
const char* name = task->get_name();
|
||||
if (find_libraries(si->get_primary_namespace(), task->get_needed_by(), &name, 1,
|
||||
nullptr /* soinfos */, nullptr /* ld_preloads */, 0 /* ld_preload_count */,
|
||||
rtld_flags, nullptr /* extinfo */, false /* add_as_children */,
|
||||
false /* search_linked_namespaces */, readers_map, namespaces)) {
|
||||
// If this lib is directly needed by one of the libs in this namespace,
|
||||
// then increment the count
|
||||
soinfo* needed_by = task->get_needed_by();
|
||||
if (needed_by != nullptr && needed_by->get_primary_namespace() == ns && si->is_linked()) {
|
||||
si->increment_ref_count();
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6: link libraries in this namespace
|
||||
soinfo_list_t local_group;
|
||||
walk_dependencies_tree(
|
||||
(start_with != nullptr && add_as_children) ? &start_with : soinfos,
|
||||
|
@ -1654,6 +1665,7 @@ bool find_libraries(android_namespace_t* ns,
|
|||
}
|
||||
});
|
||||
|
||||
soinfo_list_t global_group = ns->get_global_group();
|
||||
bool linked = local_group.visit([&](soinfo* si) {
|
||||
if (!si->is_linked()) {
|
||||
if (!si->link_image(global_group, local_group, extinfo) ||
|
||||
|
@ -1684,6 +1696,9 @@ static soinfo* find_library(android_namespace_t* ns,
|
|||
soinfo* needed_by) {
|
||||
soinfo* si;
|
||||
|
||||
// readers_map is shared across recursive calls to find_libraries.
|
||||
// However, the map is not shared across different threads.
|
||||
std::unordered_map<const soinfo*, ElfReader> readers_map;
|
||||
if (name == nullptr) {
|
||||
si = solist_get_somain();
|
||||
} else if (!find_libraries(ns,
|
||||
|
@ -1696,7 +1711,8 @@ static soinfo* find_library(android_namespace_t* ns,
|
|||
rtld_flags,
|
||||
extinfo,
|
||||
false /* add_as_children */,
|
||||
true /* search_linked_namespaces */)) {
|
||||
true /* search_linked_namespaces */,
|
||||
readers_map)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -2208,7 +2224,7 @@ android_namespace_t* create_namespace(const void* caller_addr,
|
|||
}
|
||||
} else {
|
||||
// If not shared - copy only the shared group
|
||||
add_soinfos_to_namespace(get_shared_group(parent_namespace), ns);
|
||||
add_soinfos_to_namespace(parent_namespace->get_shared_group(), ns);
|
||||
}
|
||||
|
||||
ns->set_ld_library_paths(std::move(ld_library_paths));
|
||||
|
@ -3413,7 +3429,7 @@ bool soinfo::protect_relro() {
|
|||
return true;
|
||||
}
|
||||
|
||||
static void init_default_namespace_no_config(bool is_asan) {
|
||||
static std::vector<android_namespace_t*> init_default_namespace_no_config(bool is_asan) {
|
||||
g_default_namespace.set_isolated(false);
|
||||
auto default_ld_paths = is_asan ? kAsanDefaultLdPaths : kDefaultLdPaths;
|
||||
|
||||
|
@ -3428,9 +3444,13 @@ static void init_default_namespace_no_config(bool is_asan) {
|
|||
}
|
||||
|
||||
g_default_namespace.set_default_library_paths(std::move(ld_default_paths));
|
||||
|
||||
std::vector<android_namespace_t*> namespaces;
|
||||
namespaces.push_back(&g_default_namespace);
|
||||
return namespaces;
|
||||
}
|
||||
|
||||
void init_default_namespace(const char* executable_path) {
|
||||
std::vector<android_namespace_t*> init_default_namespaces(const char* executable_path) {
|
||||
g_default_namespace.set_name("(default)");
|
||||
|
||||
soinfo* somain = solist_get_somain();
|
||||
|
@ -3447,14 +3467,24 @@ void init_default_namespace(const char* executable_path) {
|
|||
|
||||
std::string error_msg;
|
||||
|
||||
if (!Config::read_binary_config(kLdConfigFilePath,
|
||||
const char* config_file = kLdConfigFilePath;
|
||||
#ifdef USE_LD_CONFIG_FILE
|
||||
// This is a debugging/testing only feature. Must not be available on
|
||||
// production builds.
|
||||
const char* ld_config_file = getenv("LD_CONFIG_FILE");
|
||||
if (ld_config_file != nullptr && file_exists(ld_config_file)) {
|
||||
config_file = ld_config_file;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!Config::read_binary_config(config_file,
|
||||
executable_path,
|
||||
g_is_asan,
|
||||
&config,
|
||||
&error_msg)) {
|
||||
if (!error_msg.empty()) {
|
||||
DL_WARN("error reading config file \"%s\" for \"%s\" (will use default configuration): %s",
|
||||
kLdConfigFilePath,
|
||||
config_file,
|
||||
executable_path,
|
||||
error_msg.c_str());
|
||||
}
|
||||
|
@ -3462,8 +3492,7 @@ void init_default_namespace(const char* executable_path) {
|
|||
}
|
||||
|
||||
if (config == nullptr) {
|
||||
init_default_namespace_no_config(g_is_asan);
|
||||
return;
|
||||
return init_default_namespace_no_config(g_is_asan);
|
||||
}
|
||||
|
||||
const auto& namespace_configs = config->namespace_configs();
|
||||
|
@ -3514,10 +3543,17 @@ void init_default_namespace(const char* executable_path) {
|
|||
soinfo* ld_android_so = solist_get_head();
|
||||
for (auto it : namespaces) {
|
||||
it.second->add_soinfo(ld_android_so);
|
||||
// TODO (dimitry): somain and ld_preloads should probably be added to all of these namespaces too?
|
||||
// somain and ld_preloads are added to these namespaces after LD_PRELOAD libs are linked
|
||||
}
|
||||
|
||||
set_application_target_sdk_version(config->target_sdk_version());
|
||||
|
||||
std::vector<android_namespace_t*> created_namespaces;
|
||||
created_namespaces.reserve(namespaces.size());
|
||||
for (auto kv : namespaces) {
|
||||
created_namespaces.push_back(kv.second);
|
||||
}
|
||||
return created_namespaces;
|
||||
}
|
||||
|
||||
// This function finds a namespace exported in ld.config.txt by its name.
|
||||
|
|
|
@ -341,12 +341,19 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args) {
|
|||
|
||||
somain = si;
|
||||
|
||||
init_default_namespace(executable_path);
|
||||
std::vector<android_namespace_t*> namespaces = init_default_namespaces(executable_path);
|
||||
|
||||
if (!si->prelink_image()) __linker_cannot_link(g_argv[0]);
|
||||
|
||||
// add somain to global group
|
||||
si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
|
||||
// ... and add it to all other linked namespaces
|
||||
for (auto linked_ns : namespaces) {
|
||||
if (linked_ns != &g_default_namespace) {
|
||||
linked_ns->add_soinfo(somain);
|
||||
somain->add_secondary_namespace(linked_ns);
|
||||
}
|
||||
}
|
||||
|
||||
// Load ld_preloads and dependencies.
|
||||
std::vector<const char*> needed_library_name_list;
|
||||
|
@ -364,6 +371,9 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args) {
|
|||
const char** needed_library_names = &needed_library_name_list[0];
|
||||
size_t needed_libraries_count = needed_library_name_list.size();
|
||||
|
||||
// readers_map is shared across recursive calls to find_libraries so that we
|
||||
// don't need to re-load elf headers.
|
||||
std::unordered_map<const soinfo*, ElfReader> readers_map;
|
||||
if (needed_libraries_count > 0 &&
|
||||
!find_libraries(&g_default_namespace,
|
||||
si,
|
||||
|
@ -375,7 +385,9 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args) {
|
|||
RTLD_GLOBAL,
|
||||
nullptr,
|
||||
true /* add_as_children */,
|
||||
true /* search_linked_namespaces */)) {
|
||||
true /* search_linked_namespaces */,
|
||||
readers_map,
|
||||
&namespaces)) {
|
||||
__linker_cannot_link(g_argv[0]);
|
||||
} else if (needed_libraries_count == 0) {
|
||||
if (!si->link_image(g_empty_list, soinfo_list_t::make_list(si), nullptr)) {
|
||||
|
|
|
@ -31,6 +31,9 @@
|
|||
|
||||
#include <android/dlext.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "linker_namespaces.h"
|
||||
#include "linker_soinfo.h"
|
||||
|
||||
|
@ -44,7 +47,9 @@ class ProtectedDataGuard {
|
|||
static size_t ref_count_;
|
||||
};
|
||||
|
||||
void init_default_namespace(const char* executable_path);
|
||||
class ElfReader;
|
||||
|
||||
std::vector<android_namespace_t*> init_default_namespaces(const char* executable_path);
|
||||
soinfo* soinfo_alloc(android_namespace_t* ns, const char* name,
|
||||
struct stat* file_stat, off64_t file_offset,
|
||||
uint32_t rtld_flags);
|
||||
|
@ -59,7 +64,9 @@ bool find_libraries(android_namespace_t* ns,
|
|||
int rtld_flags,
|
||||
const android_dlextinfo* extinfo,
|
||||
bool add_as_children,
|
||||
bool search_linked_namespaces);
|
||||
bool search_linked_namespaces,
|
||||
std::unordered_map<const soinfo*, ElfReader>& readers_map,
|
||||
std::vector<android_namespace_t*>* namespaces = nullptr);
|
||||
|
||||
void solist_add_soinfo(soinfo* si);
|
||||
bool solist_remove_soinfo(soinfo* si);
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include "linker_soinfo.h"
|
||||
#include "linker_utils.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
bool android_namespace_t::is_accessible(const std::string& file) {
|
||||
if (!is_isolated_) {
|
||||
return true;
|
||||
|
@ -86,3 +88,41 @@ bool android_namespace_t::is_accessible(soinfo* s) {
|
|||
return !is_accessible_ftor(si);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: this is slightly unusual way to construct
|
||||
// the global group for relocation. Not every RTLD_GLOBAL
|
||||
// library is included in this group for backwards-compatibility
|
||||
// reasons.
|
||||
//
|
||||
// This group consists of the main executable, LD_PRELOADs
|
||||
// and libraries with the DF_1_GLOBAL flag set.
|
||||
soinfo_list_t android_namespace_t::get_global_group() {
|
||||
soinfo_list_t global_group;
|
||||
soinfo_list().for_each([&](soinfo* si) {
|
||||
if ((si->get_dt_flags_1() & DF_1_GLOBAL) != 0) {
|
||||
global_group.push_back(si);
|
||||
}
|
||||
});
|
||||
|
||||
return global_group;
|
||||
}
|
||||
|
||||
// This function provides a list of libraries to be shared
|
||||
// by the namespace. For the default namespace this is the global
|
||||
// group (see get_global_group). For all others this is a group
|
||||
// of RTLD_GLOBAL libraries (which includes the global group from
|
||||
// the default namespace).
|
||||
soinfo_list_t android_namespace_t::get_shared_group() {
|
||||
if (this == &g_default_namespace) {
|
||||
return get_global_group();
|
||||
}
|
||||
|
||||
soinfo_list_t shared_group;
|
||||
soinfo_list().for_each([&](soinfo* si) {
|
||||
if ((si->get_rtld_flags() & RTLD_GLOBAL) != 0) {
|
||||
shared_group.push_back(si);
|
||||
}
|
||||
});
|
||||
|
||||
return shared_group;
|
||||
}
|
||||
|
|
|
@ -136,6 +136,9 @@ struct android_namespace_t {
|
|||
// or one of it's parent soinfos belongs to this namespace.
|
||||
bool is_accessible(soinfo* si);
|
||||
|
||||
soinfo_list_t get_global_group();
|
||||
soinfo_list_t get_shared_group();
|
||||
|
||||
private:
|
||||
const char* name_;
|
||||
bool is_isolated_;
|
||||
|
|
|
@ -147,8 +147,9 @@ ElfReader::ElfReader()
|
|||
}
|
||||
|
||||
bool ElfReader::Read(const char* name, int fd, off64_t file_offset, off64_t file_size) {
|
||||
CHECK(!did_read_);
|
||||
CHECK(!did_load_);
|
||||
if (did_read_) {
|
||||
return true;
|
||||
}
|
||||
name_ = name;
|
||||
fd_ = fd;
|
||||
file_offset_ = file_offset;
|
||||
|
@ -167,7 +168,9 @@ bool ElfReader::Read(const char* name, int fd, off64_t file_offset, off64_t file
|
|||
|
||||
bool ElfReader::Load(const android_dlextinfo* extinfo) {
|
||||
CHECK(did_read_);
|
||||
CHECK(!did_load_);
|
||||
if (did_load_) {
|
||||
return true;
|
||||
}
|
||||
if (ReserveAddressSpace(extinfo) &&
|
||||
LoadSegments() &&
|
||||
FindPhdr()) {
|
||||
|
|
|
@ -298,7 +298,13 @@ cc_test_library {
|
|||
"libLLVMSupport",
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
product_variables: {
|
||||
debuggable: {
|
||||
cppflags: ["-DUSE_LD_CONFIG_FILE"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -592,6 +598,12 @@ cc_test_host {
|
|||
sanitize: {
|
||||
never: false,
|
||||
},
|
||||
|
||||
product_variables: {
|
||||
debuggable: {
|
||||
cppflags: ["-DUSE_LD_CONFIG_FILE"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
subdirs = ["libs"]
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "gtest_globals.h"
|
||||
#include "utils.h"
|
||||
|
@ -109,4 +111,131 @@ TEST(dl, xfail_preinit_getauxval) {
|
|||
#endif
|
||||
}
|
||||
|
||||
// TODO: Add tests for LD_PRELOADs
|
||||
|
||||
TEST(dl, exec_without_ld_preload) {
|
||||
#if defined(__BIONIC__)
|
||||
std::string helper = get_testlib_root() +
|
||||
"/ld_preload_test_helper/ld_preload_test_helper";
|
||||
chmod(helper.c_str(), 0755);
|
||||
ExecTestHelper eth;
|
||||
eth.SetArgs({ helper.c_str(), nullptr });
|
||||
eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "12345");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(dl, exec_with_ld_preload) {
|
||||
#if defined(__BIONIC__)
|
||||
std::string helper = get_testlib_root() +
|
||||
"/ld_preload_test_helper/ld_preload_test_helper";
|
||||
std::string env = std::string("LD_PRELOAD=") + get_testlib_root() + "/ld_preload_test_helper_lib2.so";
|
||||
chmod(helper.c_str(), 0755);
|
||||
ExecTestHelper eth;
|
||||
eth.SetArgs({ helper.c_str(), nullptr });
|
||||
eth.SetEnv({ env.c_str(), nullptr });
|
||||
// ld_preload_test_helper calls get_value_from_lib() and returns the value.
|
||||
// The symbol is defined by two libs: ld_preload_test_helper_lib.so and
|
||||
// ld_preloaded_lib.so. The former is DT_NEEDED and the latter is LD_PRELOADED
|
||||
// via this execution. The main executable is linked to the LD_PRELOADED lib
|
||||
// and the value given from the lib is returned.
|
||||
eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "54321");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// ld_config_test_helper must fail because it is depending on a lib which is not
|
||||
// in the search path
|
||||
//
|
||||
// Call sequence is...
|
||||
// _helper -- (get_value_from_lib()) -->
|
||||
// _lib1.so -- (get_value_from_another_lib()) -->
|
||||
// _lib2.so (returns 12345)
|
||||
// The two libs are in ns2/ subdir.
|
||||
TEST(dl, exec_without_ld_config_file) {
|
||||
#if defined(__BIONIC__)
|
||||
std::string error_message = "CANNOT LINK EXECUTABLE \"" + get_testlib_root() + "/ld_config_test_helper/ld_config_test_helper\": library \"ld_config_test_helper_lib1.so\" not found\n";
|
||||
std::string helper = get_testlib_root() +
|
||||
"/ld_config_test_helper/ld_config_test_helper";
|
||||
chmod(helper.c_str(), 0755);
|
||||
ExecTestHelper eth;
|
||||
eth.SetArgs({ helper.c_str(), nullptr });
|
||||
eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, -6, error_message.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
static void create_ld_config_file(std::string& config_file) {
|
||||
std::ofstream fout(config_file.c_str(), std::ios::out);
|
||||
fout << "dir.test = " << get_testlib_root() << "/ld_config_test_helper/" << std::endl
|
||||
<< "[test]" << std::endl
|
||||
<< "additional.namespaces = ns2" << std::endl
|
||||
<< "namespace.default.search.paths = " << get_testlib_root() << std::endl
|
||||
<< "namespace.default.links = ns2" << std::endl
|
||||
<< "namespace.default.link.ns2.shared_libs = libc.so:libm.so:libdl.so:ld_config_test_helper_lib1.so" << std::endl
|
||||
<< "namespace.ns2.search.paths = /system/${LIB}:" << get_testlib_root() << "/ns2" << std::endl;
|
||||
fout.close();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_LD_CONFIG_FILE
|
||||
|
||||
// _lib1.so and _lib2.so are now searchable by having another namespace 'ns2'
|
||||
// whose search paths include the 'ns2/' subdir.
|
||||
TEST(dl, exec_with_ld_config_file) {
|
||||
#if defined(__BIONIC__)
|
||||
std::string helper = get_testlib_root() +
|
||||
"/ld_config_test_helper/ld_config_test_helper";
|
||||
std::string config_file = get_testlib_root() + "/ld.config.txt";
|
||||
create_ld_config_file(config_file);
|
||||
std::string env = std::string("LD_CONFIG_FILE=") + config_file;
|
||||
chmod(helper.c_str(), 0755);
|
||||
ExecTestHelper eth;
|
||||
eth.SetArgs({ helper.c_str(), nullptr });
|
||||
eth.SetEnv({ env.c_str(), nullptr });
|
||||
eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "12345");
|
||||
#endif
|
||||
}
|
||||
|
||||
// _lib3.so has same symbol as lib2.so but returns 54321. _lib3.so is
|
||||
// LD_PRELOADed. This test is to ensure LD_PRELOADed libs are available to
|
||||
// additional namespaces other than the default namespace.
|
||||
TEST(dl, exec_with_ld_config_file_with_ld_preload) {
|
||||
#if defined(__BIONIC__)
|
||||
std::string helper = get_testlib_root() +
|
||||
"/ld_config_test_helper/ld_config_test_helper";
|
||||
std::string config_file = get_testlib_root() + "/ld.config.txt";
|
||||
create_ld_config_file(config_file);
|
||||
std::string env = std::string("LD_CONFIG_FILE=") + config_file;
|
||||
std::string env2 = std::string("LD_PRELOAD=") + get_testlib_root() + "/ld_config_test_helper_lib3.so";
|
||||
chmod(helper.c_str(), 0755);
|
||||
ExecTestHelper eth;
|
||||
eth.SetArgs({ helper.c_str(), nullptr });
|
||||
eth.SetEnv({ env.c_str(), env2.c_str(), nullptr });
|
||||
eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "54321");
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // USE_LD_CONFIG_FILE
|
||||
|
||||
// ensures that LD_CONFIG_FILE env var does not work for production builds.
|
||||
// The test input is the same as exec_with_ld_config_file, but it must fail in
|
||||
// this case.
|
||||
TEST(dl, disable_ld_config_file) {
|
||||
#if defined(__BIONIC__)
|
||||
if (getuid() == 0) {
|
||||
// when executed from the shell (e.g. not as part of CTS), skip the test.
|
||||
// This test is only for CTS.
|
||||
return;
|
||||
}
|
||||
std::string error_message = "CANNOT LINK EXECUTABLE \"" + get_testlib_root() + "/ld_config_test_helper/ld_config_test_helper\": library \"ld_config_test_helper_lib1.so\" not found\n";
|
||||
std::string helper = get_testlib_root() +
|
||||
"/ld_config_test_helper/ld_config_test_helper";
|
||||
std::string config_file = get_testlib_root() + "/ld.config.txt";
|
||||
create_ld_config_file(config_file);
|
||||
std::string env = std::string("LD_CONFIG_FILE=") + config_file;
|
||||
chmod(helper.c_str(), 0755);
|
||||
ExecTestHelper eth;
|
||||
eth.SetArgs({ helper.c_str(), nullptr });
|
||||
eth.SetEnv({ env.c_str(), nullptr });
|
||||
eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, -6, error_message.c_str());
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -617,3 +617,59 @@ cc_test {
|
|||
defaults: ["bionic_testlib_defaults"],
|
||||
srcs: ["preinit_syscall_test_helper.cpp"],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "ld_preload_test_helper",
|
||||
host_supported: false,
|
||||
defaults: ["bionic_testlib_defaults"],
|
||||
srcs: ["ld_preload_test_helper.cpp"],
|
||||
shared_libs: ["ld_preload_test_helper_lib1"],
|
||||
ldflags: ["-Wl,--rpath,${ORIGIN}/.."],
|
||||
}
|
||||
|
||||
cc_test_library {
|
||||
name: "ld_preload_test_helper_lib1",
|
||||
host_supported: false,
|
||||
defaults: ["bionic_testlib_defaults"],
|
||||
srcs: ["ld_preload_test_helper_lib1.cpp"],
|
||||
}
|
||||
|
||||
cc_test_library {
|
||||
name: "ld_preload_test_helper_lib2",
|
||||
host_supported: false,
|
||||
defaults: ["bionic_testlib_defaults"],
|
||||
srcs: ["ld_preload_test_helper_lib2.cpp"],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "ld_config_test_helper",
|
||||
host_supported: false,
|
||||
defaults: ["bionic_testlib_defaults"],
|
||||
srcs: ["ld_config_test_helper.cpp"],
|
||||
shared_libs: ["ld_config_test_helper_lib1"],
|
||||
ldflags: ["-Wl,--rpath,${ORIGIN}/.."],
|
||||
}
|
||||
|
||||
cc_test_library {
|
||||
name: "ld_config_test_helper_lib1",
|
||||
host_supported: false,
|
||||
defaults: ["bionic_testlib_defaults"],
|
||||
srcs: ["ld_config_test_helper_lib1.cpp"],
|
||||
shared_libs: ["ld_config_test_helper_lib2"],
|
||||
relative_install_path: "/ns2",
|
||||
}
|
||||
|
||||
cc_test_library {
|
||||
name: "ld_config_test_helper_lib2",
|
||||
host_supported: false,
|
||||
defaults: ["bionic_testlib_defaults"],
|
||||
srcs: ["ld_config_test_helper_lib2.cpp"],
|
||||
relative_install_path: "/ns2",
|
||||
}
|
||||
|
||||
cc_test_library {
|
||||
name: "ld_config_test_helper_lib3",
|
||||
host_supported: false,
|
||||
defaults: ["bionic_testlib_defaults"],
|
||||
srcs: ["ld_config_test_helper_lib3.cpp"],
|
||||
}
|
||||
|
|
26
tests/libs/ld_config_test_helper.cpp
Normal file
26
tests/libs/ld_config_test_helper.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern int get_value_from_lib();
|
||||
|
||||
int main() {
|
||||
printf("%d", get_value_from_lib());
|
||||
return 0;
|
||||
}
|
4
tests/libs/ld_config_test_helper_lib1.cpp
Normal file
4
tests/libs/ld_config_test_helper_lib1.cpp
Normal file
|
@ -0,0 +1,4 @@
|
|||
extern int get_value_from_another_lib();
|
||||
int get_value_from_lib() {
|
||||
return get_value_from_another_lib();
|
||||
}
|
3
tests/libs/ld_config_test_helper_lib2.cpp
Normal file
3
tests/libs/ld_config_test_helper_lib2.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
int get_value_from_another_lib() {
|
||||
return 12345;
|
||||
}
|
3
tests/libs/ld_config_test_helper_lib3.cpp
Normal file
3
tests/libs/ld_config_test_helper_lib3.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
int get_value_from_another_lib() {
|
||||
return 54321;
|
||||
}
|
26
tests/libs/ld_preload_test_helper.cpp
Normal file
26
tests/libs/ld_preload_test_helper.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern int get_value_from_lib();
|
||||
|
||||
int main() {
|
||||
printf("%d", get_value_from_lib());
|
||||
return 0;
|
||||
}
|
3
tests/libs/ld_preload_test_helper_lib1.cpp
Normal file
3
tests/libs/ld_preload_test_helper_lib1.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
int get_value_from_lib() {
|
||||
return 12345;
|
||||
}
|
3
tests/libs/ld_preload_test_helper_lib2.cpp
Normal file
3
tests/libs/ld_preload_test_helper_lib2.cpp
Normal file
|
@ -0,0 +1,3 @@
|
|||
int get_value_from_lib() {
|
||||
return 54321;
|
||||
}
|
Loading…
Reference in a new issue