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:
Jiyong Park 2017-05-20 01:01:24 +09:00
parent 5ac5a7c083
commit 02586a2a34
17 changed files with 472 additions and 100 deletions

View file

@ -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

View file

@ -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.

View file

@ -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)) {

View file

@ -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);

View file

@ -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;
}

View file

@ -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_;

View file

@ -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()) {

View file

@ -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"]

View file

@ -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
}

View file

@ -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"],
}

View 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;
}

View file

@ -0,0 +1,4 @@
extern int get_value_from_another_lib();
int get_value_from_lib() {
return get_value_from_another_lib();
}

View file

@ -0,0 +1,3 @@
int get_value_from_another_lib() {
return 12345;
}

View file

@ -0,0 +1,3 @@
int get_value_from_another_lib() {
return 54321;
}

View 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;
}

View file

@ -0,0 +1,3 @@
int get_value_from_lib() {
return 12345;
}

View file

@ -0,0 +1,3 @@
int get_value_from_lib() {
return 54321;
}