Count references for groups instead of instances
Count references on the group level to avoid partially unloading function that might be referenced by other libraries in the local_group Bonus: with this change we can correctly unload recursively linked libraries. is_recursive check is removed. Also dynamic executables (not .so) with 0 DT_NEEDED libraries are now correctly linked. Change-Id: Idfa83baef402840599b93a875f2881d9f020dbcd
This commit is contained in:
parent
f64c43ba6c
commit
ab972b9adf
@ -236,16 +236,17 @@ static soinfo __libdl_info("libdl.so", nullptr, 0, RTLD_GLOBAL);
|
||||
|
||||
// This is used by the dynamic linker. Every process gets these symbols for free.
|
||||
soinfo* get_libdl_info() {
|
||||
if ((__libdl_info.flags & FLAG_LINKED) == 0) {
|
||||
__libdl_info.flags |= FLAG_LINKED;
|
||||
if ((__libdl_info.flags_ & FLAG_LINKED) == 0) {
|
||||
__libdl_info.flags_ |= FLAG_LINKED;
|
||||
__libdl_info.strtab_ = ANDROID_LIBDL_STRTAB;
|
||||
__libdl_info.symtab_ = g_libdl_symtab;
|
||||
__libdl_info.nbucket_ = sizeof(g_libdl_buckets)/sizeof(unsigned);
|
||||
__libdl_info.nchain_ = sizeof(g_libdl_chains)/sizeof(unsigned);
|
||||
__libdl_info.bucket_ = g_libdl_buckets;
|
||||
__libdl_info.chain_ = g_libdl_chains;
|
||||
__libdl_info.ref_count = 1;
|
||||
__libdl_info.ref_count_ = 1;
|
||||
__libdl_info.strtab_size_ = sizeof(ANDROID_LIBDL_STRTAB);
|
||||
__libdl_info.local_group_root_ = &__libdl_info;
|
||||
}
|
||||
|
||||
return &__libdl_info;
|
||||
|
@ -81,6 +81,14 @@ class LinkedList {
|
||||
return element;
|
||||
}
|
||||
|
||||
T* front() const {
|
||||
if (head_ == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return head_->element;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
while (head_ != nullptr) {
|
||||
LinkedListEntry<T>* p = head_;
|
||||
|
@ -230,7 +230,7 @@ static void remove_soinfo_from_debug_map(soinfo* info) {
|
||||
}
|
||||
|
||||
static void notify_gdb_of_load(soinfo* info) {
|
||||
if (info->flags & FLAG_EXE) {
|
||||
if (info->is_main_executable()) {
|
||||
// GDB already knows about the main executable
|
||||
return;
|
||||
}
|
||||
@ -247,7 +247,7 @@ static void notify_gdb_of_load(soinfo* info) {
|
||||
}
|
||||
|
||||
static void notify_gdb_of_unload(soinfo* info) {
|
||||
if (info->flags & FLAG_EXE) {
|
||||
if (info->is_main_executable()) {
|
||||
// GDB already knows about the main executable
|
||||
return;
|
||||
}
|
||||
@ -490,7 +490,7 @@ soinfo::soinfo(const char* name, const struct stat* file_stat, off64_t file_offs
|
||||
memset(this, 0, sizeof(*this));
|
||||
|
||||
strlcpy(this->name, name, sizeof(this->name));
|
||||
flags = FLAG_NEW_SOINFO;
|
||||
flags_ = FLAG_NEW_SOINFO;
|
||||
version_ = SOINFO_VERSION;
|
||||
|
||||
if (file_stat != nullptr) {
|
||||
@ -986,21 +986,6 @@ static soinfo* find_library_internal(LoadTaskList& load_tasks, const char* name,
|
||||
|
||||
static void soinfo_unload(soinfo* si);
|
||||
|
||||
static bool is_recursive(soinfo* si, soinfo* parent) {
|
||||
if (parent == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (si == parent) {
|
||||
DL_ERR("recursive link to \"%s\"", si->name);
|
||||
return true;
|
||||
}
|
||||
|
||||
return !parent->get_parents().visit([&](soinfo* grandparent) {
|
||||
return !is_recursive(si, grandparent);
|
||||
});
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -1067,15 +1052,14 @@ static bool find_libraries(soinfo* start_with, const char* const library_names[]
|
||||
|
||||
soinfo* needed_by = task->get_needed_by();
|
||||
|
||||
if (is_recursive(si, needed_by)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
si->ref_count++;
|
||||
if (needed_by != nullptr) {
|
||||
needed_by->add_child(si);
|
||||
}
|
||||
|
||||
if (si->is_linked()) {
|
||||
si->increment_ref_count();
|
||||
}
|
||||
|
||||
// When ld_preloads is not null, the first
|
||||
// ld_preloads_count libs are in fact ld_preloads.
|
||||
if (ld_preloads != nullptr && soinfos_count < ld_preloads_count) {
|
||||
@ -1102,12 +1086,16 @@ static bool find_libraries(soinfo* start_with, const char* const library_names[]
|
||||
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->flags & FLAG_LINKED) == 0) {
|
||||
if (!si->is_linked()) {
|
||||
if (!si->link_image(global_group, local_group, extinfo)) {
|
||||
return false;
|
||||
}
|
||||
si->flags |= FLAG_LINKED;
|
||||
si->set_linked();
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1117,72 +1105,101 @@ static bool find_libraries(soinfo* start_with, const char* const library_names[]
|
||||
failure_guard.disable();
|
||||
}
|
||||
|
||||
if (!was_local_group_root_linked) {
|
||||
local_group.front()->increment_ref_count();
|
||||
}
|
||||
|
||||
return linked;
|
||||
}
|
||||
|
||||
static soinfo* find_library(const char* name, int rtld_flags, const android_dlextinfo* extinfo) {
|
||||
if (name == nullptr) {
|
||||
somain->ref_count++;
|
||||
return somain;
|
||||
}
|
||||
|
||||
soinfo* si;
|
||||
|
||||
if (!find_libraries(nullptr, &name, 1, &si, nullptr, 0, rtld_flags, extinfo)) {
|
||||
if (name == nullptr) {
|
||||
si = somain;
|
||||
} else if (!find_libraries(nullptr, &name, 1, &si, nullptr, 0, rtld_flags, extinfo)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return si;
|
||||
}
|
||||
|
||||
static void soinfo_unload_schedule(soinfo::soinfo_list_t& unload_list, soinfo* si) {
|
||||
if (!si->can_unload()) {
|
||||
TRACE("not unloading '%s' - the binary is flagged with NODELETE", si->name);
|
||||
static void soinfo_unload(soinfo* root) {
|
||||
// Note that the library can be loaded but not linked;
|
||||
// in which case there is no root but we still need
|
||||
// to walk the tree and unload soinfos involved.
|
||||
//
|
||||
// This happens on unsuccessful dlopen, when one of
|
||||
// the DT_NEEDED libraries could not be linked/found.
|
||||
if (root->is_linked()) {
|
||||
root = root->get_local_group_root();
|
||||
}
|
||||
|
||||
if (!root->can_unload()) {
|
||||
TRACE("not unloading '%s' - the binary is flagged with NODELETE", root->name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (si->ref_count == 1) {
|
||||
unload_list.push_back(si);
|
||||
size_t ref_count = root->is_linked() ? root->decrement_ref_count() : 0;
|
||||
|
||||
if (si->has_min_version(0)) {
|
||||
soinfo* child = nullptr;
|
||||
while ((child = si->get_children().pop_front()) != nullptr) {
|
||||
TRACE("%s needs to unload %s", si->name, child->name);
|
||||
soinfo_unload_schedule(unload_list, child);
|
||||
}
|
||||
} else {
|
||||
for_each_dt_needed(si, [&] (const char* library_name) {
|
||||
TRACE("deprecated (old format of soinfo): %s needs to unload %s", si->name, library_name);
|
||||
soinfo* needed = find_library(library_name, RTLD_NOLOAD, nullptr);
|
||||
if (needed != nullptr) {
|
||||
soinfo_unload_schedule(unload_list, needed);
|
||||
} else {
|
||||
// Not found: for example if symlink was deleted between dlopen and dlclose
|
||||
// Since we cannot really handle errors at this point - print and continue.
|
||||
PRINT("warning: couldn't find %s needed by %s on unload.", library_name, si->name);
|
||||
if (ref_count == 0) {
|
||||
soinfo::soinfo_list_t local_unload_list;
|
||||
soinfo::soinfo_list_t external_unload_list;
|
||||
soinfo::soinfo_list_t depth_first_list;
|
||||
depth_first_list.push_back(root);
|
||||
soinfo* si = nullptr;
|
||||
|
||||
while ((si = depth_first_list.pop_front()) != nullptr) {
|
||||
local_unload_list.push_back(si);
|
||||
if (si->has_min_version(0)) {
|
||||
soinfo* child = nullptr;
|
||||
while ((child = si->get_children().pop_front()) != nullptr) {
|
||||
TRACE("%s needs to unload %s", si->name, child->name);
|
||||
if (local_unload_list.contains(child)) {
|
||||
continue;
|
||||
} else if (child->get_local_group_root() != root) {
|
||||
external_unload_list.push_back(child);
|
||||
} else {
|
||||
depth_first_list.push_front(child);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
for_each_dt_needed(si, [&] (const char* library_name) {
|
||||
TRACE("deprecated (old format of soinfo): %s needs to unload %s", si->name, library_name);
|
||||
soinfo* needed = find_library(library_name, RTLD_NOLOAD, nullptr);
|
||||
if (needed != nullptr) {
|
||||
// Not found: for example if symlink was deleted between dlopen and dlclose
|
||||
// Since we cannot really handle errors at this point - print and continue.
|
||||
PRINT("warning: couldn't find %s needed by %s on unload.", library_name, si->name);
|
||||
return;
|
||||
} else if (local_unload_list.contains(needed)) {
|
||||
// already visited
|
||||
return;
|
||||
} else if (needed->get_local_group_root() != root) {
|
||||
// external group
|
||||
external_unload_list.push_back(needed);
|
||||
} else {
|
||||
// local group
|
||||
depth_first_list.push_front(needed);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
si->ref_count = 0;
|
||||
local_unload_list.for_each([](soinfo* si) {
|
||||
si->call_destructors();
|
||||
});
|
||||
|
||||
while ((si = local_unload_list.pop_front()) != nullptr) {
|
||||
notify_gdb_of_unload(si);
|
||||
soinfo_free(si);
|
||||
}
|
||||
|
||||
while ((si = external_unload_list.pop_front()) != nullptr) {
|
||||
soinfo_unload(si);
|
||||
}
|
||||
} else {
|
||||
si->ref_count--;
|
||||
TRACE("not unloading '%s', decrementing ref_count to %zd", si->name, si->ref_count);
|
||||
}
|
||||
}
|
||||
|
||||
static void soinfo_unload(soinfo* root) {
|
||||
soinfo::soinfo_list_t unload_list;
|
||||
soinfo_unload_schedule(unload_list, root);
|
||||
unload_list.for_each([](soinfo* si) {
|
||||
si->call_destructors();
|
||||
});
|
||||
|
||||
soinfo* si = nullptr;
|
||||
while ((si = unload_list.pop_front()) != nullptr) {
|
||||
TRACE("unloading '%s'", si->name);
|
||||
notify_gdb_of_unload(si);
|
||||
soinfo_free(si);
|
||||
TRACE("not unloading '%s' group, decrementing ref_count to %zd", root->name, ref_count);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1838,7 +1855,7 @@ void soinfo::call_constructors() {
|
||||
// out above, the libc constructor will be called again (recursively!).
|
||||
constructors_called = true;
|
||||
|
||||
if ((flags & FLAG_EXE) == 0 && preinit_array_ != nullptr) {
|
||||
if (!is_main_executable() && preinit_array_ != nullptr) {
|
||||
// The GNU dynamic linker silently ignores these, but we warn the developer.
|
||||
PRINT("\"%s\": ignoring %zd-entry DT_PREINIT_ARRAY in shared library!",
|
||||
name, preinit_array_count_);
|
||||
@ -1992,13 +2009,45 @@ const char* soinfo::get_string(ElfW(Word) index) const {
|
||||
}
|
||||
|
||||
bool soinfo::is_gnu_hash() const {
|
||||
return (flags & FLAG_GNU_HASH) != 0;
|
||||
return (flags_ & FLAG_GNU_HASH) != 0;
|
||||
}
|
||||
|
||||
bool soinfo::can_unload() const {
|
||||
return (get_rtld_flags() & (RTLD_NODELETE | RTLD_GLOBAL)) == 0;
|
||||
}
|
||||
|
||||
bool soinfo::is_linked() const {
|
||||
return (flags_ & FLAG_LINKED) != 0;
|
||||
}
|
||||
|
||||
bool soinfo::is_main_executable() const {
|
||||
return (flags_ & FLAG_EXE) != 0;
|
||||
}
|
||||
|
||||
void soinfo::set_linked() {
|
||||
flags_ |= FLAG_LINKED;
|
||||
}
|
||||
|
||||
void soinfo::set_linker_flag() {
|
||||
flags_ |= FLAG_LINKER;
|
||||
}
|
||||
|
||||
void soinfo::set_main_executable() {
|
||||
flags_ |= FLAG_EXE;
|
||||
}
|
||||
|
||||
void soinfo::increment_ref_count() {
|
||||
local_group_root_->ref_count_++;
|
||||
}
|
||||
|
||||
size_t soinfo::decrement_ref_count() {
|
||||
return --local_group_root_->ref_count_;
|
||||
}
|
||||
|
||||
soinfo* soinfo::get_local_group_root() const {
|
||||
return local_group_root_;
|
||||
}
|
||||
|
||||
/* Force any of the closed stdin, stdout and stderr to be associated with
|
||||
/dev/null. */
|
||||
static int nullify_closed_stdio() {
|
||||
@ -2066,10 +2115,10 @@ bool soinfo::prelink_image() {
|
||||
phdr_table_get_dynamic_section(phdr, phnum, load_bias, &dynamic, &dynamic_flags);
|
||||
|
||||
/* We can't log anything until the linker is relocated */
|
||||
bool relocating_linker = (flags & FLAG_LINKER) != 0;
|
||||
bool relocating_linker = (flags_ & FLAG_LINKER) != 0;
|
||||
if (!relocating_linker) {
|
||||
INFO("[ linking %s ]", name);
|
||||
DEBUG("si->base = %p si->flags = 0x%08x", reinterpret_cast<void*>(base), flags);
|
||||
DEBUG("si->base = %p si->flags = 0x%08x", reinterpret_cast<void*>(base), flags_);
|
||||
}
|
||||
|
||||
if (dynamic == nullptr) {
|
||||
@ -2133,7 +2182,7 @@ bool soinfo::prelink_image() {
|
||||
}
|
||||
--gnu_maskwords_;
|
||||
|
||||
flags |= FLAG_GNU_HASH;
|
||||
flags_ |= FLAG_GNU_HASH;
|
||||
break;
|
||||
|
||||
case DT_STRTAB:
|
||||
@ -2406,6 +2455,11 @@ bool soinfo::prelink_image() {
|
||||
|
||||
bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group, const android_dlextinfo* extinfo) {
|
||||
|
||||
local_group_root_ = local_group.front();
|
||||
if (local_group_root_ == nullptr) {
|
||||
local_group_root_ = this;
|
||||
}
|
||||
|
||||
#if !defined(__LP64__)
|
||||
if (has_text_relocations) {
|
||||
// Make segments writable to allow text relocations to work properly. We will later call
|
||||
@ -2598,7 +2652,7 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW(
|
||||
}
|
||||
|
||||
/* bootstrap the link map, the main exe always needs to be first */
|
||||
si->flags |= FLAG_EXE;
|
||||
si->set_main_executable();
|
||||
link_map* map = &(si->link_map_head);
|
||||
|
||||
map->l_addr = 0;
|
||||
@ -2631,7 +2685,6 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW(
|
||||
}
|
||||
}
|
||||
si->dynamic = nullptr;
|
||||
si->ref_count = 1;
|
||||
|
||||
ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(si->base);
|
||||
if (elf_hdr->e_type != ET_DYN) {
|
||||
@ -2672,6 +2725,12 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW(
|
||||
if (needed_libraries_count > 0 && !find_libraries(si, needed_library_names, needed_libraries_count, nullptr, g_ld_preloads, ld_preloads_count, RTLD_GLOBAL, nullptr)) {
|
||||
__libc_format_fd(2, "CANNOT LINK EXECUTABLE: %s\n", linker_get_error_buffer());
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (needed_libraries_count == 0) {
|
||||
if (!si->link_image(g_empty_list, soinfo::soinfo_list_t::make_list(si), nullptr)) {
|
||||
__libc_format_fd(2, "CANNOT LINK EXECUTABLE: %s\n", linker_get_error_buffer());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
si->increment_ref_count();
|
||||
}
|
||||
|
||||
add_vdso(args);
|
||||
@ -2791,7 +2850,7 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) {
|
||||
linker_so.dynamic = nullptr;
|
||||
linker_so.phdr = phdr;
|
||||
linker_so.phnum = elf_hdr->e_phnum;
|
||||
linker_so.flags |= FLAG_LINKER;
|
||||
linker_so.set_linker_flag();
|
||||
|
||||
// This might not be obvious... The reasons why we pass g_empty_list
|
||||
// in place of local_group here are (1) we do not really need it, because
|
||||
|
@ -161,9 +161,9 @@ struct soinfo {
|
||||
#endif
|
||||
|
||||
soinfo* next;
|
||||
uint32_t flags;
|
||||
|
||||
private:
|
||||
uint32_t flags_;
|
||||
|
||||
const char* strtab_;
|
||||
ElfW(Sym)* symtab_;
|
||||
|
||||
@ -203,22 +203,21 @@ struct soinfo {
|
||||
linker_function_t init_func_;
|
||||
linker_function_t fini_func_;
|
||||
|
||||
public:
|
||||
#if defined(__arm__)
|
||||
public:
|
||||
// ARM EABI section used for stack unwinding.
|
||||
uint32_t* ARM_exidx;
|
||||
size_t ARM_exidx_count;
|
||||
#elif defined(__mips__)
|
||||
private:
|
||||
#elif defined(__mips__)
|
||||
uint32_t mips_symtabno_;
|
||||
uint32_t mips_local_gotno_;
|
||||
uint32_t mips_gotsym_;
|
||||
bool mips_relocate_got(const soinfo_list_t& global_group, const soinfo_list_t& local_group);
|
||||
|
||||
#endif
|
||||
|
||||
size_t ref_count_;
|
||||
public:
|
||||
size_t ref_count;
|
||||
link_map link_map_head;
|
||||
|
||||
bool constructors_called;
|
||||
@ -264,9 +263,21 @@ struct soinfo {
|
||||
bool is_gnu_hash() const;
|
||||
|
||||
bool inline has_min_version(uint32_t min_version) const {
|
||||
return (flags & FLAG_NEW_SOINFO) != 0 && version_ >= min_version;
|
||||
return (flags_ & FLAG_NEW_SOINFO) != 0 && version_ >= min_version;
|
||||
}
|
||||
|
||||
bool is_linked() const;
|
||||
bool is_main_executable() const;
|
||||
|
||||
void set_linked();
|
||||
void set_linker_flag();
|
||||
void set_main_executable();
|
||||
|
||||
void increment_ref_count();
|
||||
size_t decrement_ref_count();
|
||||
|
||||
soinfo* get_local_group_root() const;
|
||||
|
||||
private:
|
||||
ElfW(Sym)* elf_lookup(SymbolName& symbol_name);
|
||||
ElfW(Sym)* elf_addr_lookup(const void* addr);
|
||||
@ -303,9 +314,10 @@ struct soinfo {
|
||||
// version >= 2
|
||||
uint32_t gnu_maskwords_;
|
||||
uint32_t gnu_shift2_;
|
||||
|
||||
ElfW(Addr)* gnu_bloom_filter_;
|
||||
|
||||
soinfo* local_group_root_;
|
||||
|
||||
friend soinfo* get_libdl_info();
|
||||
};
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "gtest_ex.h"
|
||||
#include "private/ScopeGuard.h"
|
||||
|
||||
#include <string>
|
||||
@ -362,6 +363,48 @@ TEST(dlfcn, dlopen_check_order_reloc_nephew) {
|
||||
ASSERT_EQ(0, dlclose(handle));
|
||||
}
|
||||
|
||||
TEST(dlfcn, check_unload_after_reloc) {
|
||||
// This is how this one works:
|
||||
// libtest_two_parents_parent1 <- answer_impl() used by libtest_two_parents_child
|
||||
// |
|
||||
// +-> libtest_two_parents_child
|
||||
//
|
||||
// libtest_two_parents_parent2 <- answer_impl() not used by libtest_two_parents_child
|
||||
// |
|
||||
// +-> libtest_two_parents_child
|
||||
//
|
||||
// Test dlopens parent1 which loads and relocates libtest_two_parents_child.so
|
||||
// as a second step it dlopens parent2 and dlcloses parent1...
|
||||
|
||||
test_isolated([] {
|
||||
void* handle = dlopen("libtest_two_parents_parent1.so", RTLD_NOW | RTLD_LOCAL);
|
||||
ASSERT_TRUE(handle != nullptr) << dlerror();
|
||||
|
||||
void* handle2 = dlopen("libtest_two_parents_parent2.so", RTLD_NOW | RTLD_LOCAL);
|
||||
ASSERT_TRUE(handle2 != nullptr) << dlerror();
|
||||
|
||||
typedef int (*fn_t) (void);
|
||||
fn_t fn = reinterpret_cast<fn_t>(dlsym(handle2, "check_order_reloc_get_answer"));
|
||||
ASSERT_TRUE(fn != nullptr) << dlerror();
|
||||
ASSERT_EQ(42, fn());
|
||||
|
||||
ASSERT_EQ(0, dlclose(handle));
|
||||
|
||||
handle = dlopen("libtest_two_parents_parent1.so", RTLD_NOW | RTLD_LOCAL | RTLD_NOLOAD);
|
||||
ASSERT_TRUE(handle != nullptr);
|
||||
ASSERT_EQ(0, dlclose(handle));
|
||||
|
||||
fn = reinterpret_cast<fn_t>(dlsym(handle2, "check_order_reloc_get_answer"));
|
||||
ASSERT_TRUE(fn != nullptr) << dlerror();
|
||||
ASSERT_EQ(42, fn());
|
||||
|
||||
ASSERT_EQ(0, dlclose(handle2));
|
||||
|
||||
handle = dlopen("libtest_two_parents_parent1.so", RTLD_NOW | RTLD_LOCAL | RTLD_NOLOAD);
|
||||
ASSERT_TRUE(handle == nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" int check_order_reloc_root_get_answer_impl() {
|
||||
return 42;
|
||||
}
|
||||
@ -442,25 +485,25 @@ TEST(dlfcn, dlopen_check_rtld_global) {
|
||||
// libtest_with_dependency_loop_b.so -> libtest_with_dependency_loop_c.so ->
|
||||
// libtest_with_dependency_loop_a.so
|
||||
TEST(dlfcn, dlopen_check_loop) {
|
||||
void* handle = dlopen("libtest_with_dependency_loop.so", RTLD_NOW);
|
||||
#if defined(__BIONIC__)
|
||||
ASSERT_TRUE(handle == nullptr);
|
||||
ASSERT_STREQ("dlopen failed: recursive link to \"libtest_with_dependency_loop_a.so\"", dlerror());
|
||||
// This symbol should never be exposed
|
||||
void* f = dlsym(RTLD_DEFAULT, "dlopen_test_invalid_function");
|
||||
ASSERT_TRUE(f == nullptr);
|
||||
ASSERT_SUBSTR("undefined symbol: dlopen_test_invalid_function", dlerror());
|
||||
test_isolated([] {
|
||||
void* handle = dlopen("libtest_with_dependency_loop.so", RTLD_NOW);
|
||||
ASSERT_TRUE(handle != nullptr) << dlerror();
|
||||
void* f = dlsym(handle, "dlopen_test_loopy_function");
|
||||
ASSERT_TRUE(f != nullptr) << dlerror();
|
||||
EXPECT_TRUE(reinterpret_cast<bool (*)(void)>(f)());
|
||||
ASSERT_EQ(0, dlclose(handle));
|
||||
|
||||
// dlopen second time to make sure that the library wasn't loaded even though dlopen returned null.
|
||||
// This may happen if during cleanup the root library or one of the depended libs were not removed
|
||||
// from soinfo list.
|
||||
handle = dlopen("libtest_with_dependency_loop.so", RTLD_NOW | RTLD_NOLOAD);
|
||||
ASSERT_TRUE(handle == nullptr);
|
||||
ASSERT_STREQ("dlopen failed: library \"libtest_with_dependency_loop.so\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror());
|
||||
#else // glibc allows recursive links
|
||||
ASSERT_TRUE(handle != nullptr);
|
||||
dlclose(handle);
|
||||
// dlopen second time to make sure that the library was unloaded correctly
|
||||
handle = dlopen("libtest_with_dependency_loop.so", RTLD_NOW | RTLD_NOLOAD);
|
||||
ASSERT_TRUE(handle == nullptr);
|
||||
#ifdef __BIONIC__
|
||||
// TODO: glibc returns nullptr on dlerror() here. Is it bug?
|
||||
ASSERT_STREQ("dlopen failed: library \"libtest_with_dependency_loop.so\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror());
|
||||
#endif
|
||||
|
||||
handle = dlopen("libtest_with_dependency_a.so", RTLD_NOW | RTLD_NOLOAD);
|
||||
ASSERT_TRUE(handle == nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
TEST(dlfcn, dlopen_nodelete) {
|
||||
|
52
tests/libs/Android.build.dlopen_2_parents_reloc.mk
Normal file
52
tests/libs/Android.build.dlopen_2_parents_reloc.mk
Normal file
@ -0,0 +1,52 @@
|
||||
#
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Libraries used by dlfcn tests to verify local group ref_counting
|
||||
# libtest_two_parents*.so
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# ..._child.so - correct answer
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_two_parents_child_src_files := \
|
||||
dlopen_2_parents_reloc_answer.cpp
|
||||
|
||||
module := libtest_two_parents_child
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# ..._parent1.so - correct answer
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_two_parents_parent1_src_files := \
|
||||
dlopen_check_order_reloc_answer_impl.cpp
|
||||
|
||||
libtest_two_parents_parent1_shared_libraries := libtest_two_parents_child
|
||||
libtest_two_parents_parent1_cflags := -D__ANSWER=42
|
||||
module := libtest_two_parents_parent1
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# ..._parent2.so - incorrect answer
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_two_parents_parent2_src_files := \
|
||||
dlopen_check_order_reloc_answer_impl.cpp
|
||||
|
||||
libtest_two_parents_parent2_shared_libraries := libtest_two_parents_child
|
||||
libtest_two_parents_parent2_cflags := -D__ANSWER=1
|
||||
module := libtest_two_parents_parent2
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
@ -21,6 +21,7 @@ common_cppflags += -std=gnu++11
|
||||
common_additional_dependencies := \
|
||||
$(LOCAL_PATH)/Android.mk \
|
||||
$(LOCAL_PATH)/Android.build.dlext_testzip.mk \
|
||||
$(LOCAL_PATH)/Android.build.dlopen_2_parents_reloc.mk \
|
||||
$(LOCAL_PATH)/Android.build.dlopen_check_order_dlsym.mk \
|
||||
$(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk \
|
||||
$(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk \
|
||||
@ -165,6 +166,11 @@ libtest_nodelete_dt_flags_1_ldflags := -Wl,-z,nodelete
|
||||
module := libtest_nodelete_dt_flags_1
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Build library with two parents
|
||||
# -----------------------------------------------------------------------------
|
||||
include $(LOCAL_PATH)/Android.build.dlopen_2_parents_reloc.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Build libtest_check_order_dlsym.so with its dependencies.
|
||||
# -----------------------------------------------------------------------------
|
||||
@ -185,7 +191,7 @@ include $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk
|
||||
#
|
||||
# libtest_with_dependency_loop -> a -> b -> c -> a
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_with_dependency_loop_src_files := dlopen_testlib_invalid.cpp
|
||||
libtest_with_dependency_loop_src_files := dlopen_testlib_loopy_root.cpp
|
||||
|
||||
libtest_with_dependency_loop_shared_libraries := \
|
||||
libtest_with_dependency_loop_a
|
||||
@ -196,7 +202,7 @@ include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
# -----------------------------------------------------------------------------
|
||||
# libtest_with_dependency_loop_a.so
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_with_dependency_loop_a_src_files := dlopen_testlib_invalid.cpp
|
||||
libtest_with_dependency_loop_a_src_files := dlopen_testlib_loopy_a.cpp
|
||||
|
||||
libtest_with_dependency_loop_a_shared_libraries := \
|
||||
libtest_with_dependency_loop_b_tmp
|
||||
@ -209,7 +215,7 @@ include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
#
|
||||
# this is temporary placeholder - will be removed
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_with_dependency_loop_b_tmp_src_files := dlopen_testlib_invalid.cpp
|
||||
libtest_with_dependency_loop_b_tmp_src_files := dlopen_testlib_loopy_invalid.cpp
|
||||
libtest_with_dependency_loop_b_tmp_ldflags := -Wl,-soname=libtest_with_dependency_loop_b.so
|
||||
|
||||
module := libtest_with_dependency_loop_b_tmp
|
||||
@ -218,7 +224,7 @@ include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
# -----------------------------------------------------------------------------
|
||||
# libtest_with_dependency_loop_b.so
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_with_dependency_loop_b_src_files := dlopen_testlib_invalid.cpp
|
||||
libtest_with_dependency_loop_b_src_files := dlopen_testlib_loopy_b.cpp
|
||||
libtest_with_dependency_loop_b_shared_libraries := libtest_with_dependency_loop_c
|
||||
|
||||
module := libtest_with_dependency_loop_b
|
||||
@ -227,7 +233,7 @@ include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
# -----------------------------------------------------------------------------
|
||||
# libtest_with_dependency_loop_c.so
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_with_dependency_loop_c_src_files := dlopen_testlib_invalid.cpp
|
||||
libtest_with_dependency_loop_c_src_files := dlopen_testlib_loopy_c.cpp
|
||||
|
||||
libtest_with_dependency_loop_c_shared_libraries := \
|
||||
libtest_with_dependency_loop_a
|
||||
|
23
tests/libs/dlopen_2_parents_reloc_answer.cpp
Normal file
23
tests/libs/dlopen_2_parents_reloc_answer.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.
|
||||
*/
|
||||
|
||||
extern "C" int __attribute__((weak)) check_order_reloc_get_answer_impl() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int check_order_reloc_get_answer() {
|
||||
return check_order_reloc_get_answer_impl();
|
||||
}
|
25
tests/libs/dlopen_testlib_loopy_a.cpp
Normal file
25
tests/libs/dlopen_testlib_loopy_a.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 <stdlib.h>
|
||||
|
||||
extern "C" bool __attribute__((weak)) dlopen_test_loopy_function_impl() {
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" bool dlopen_test_loopy_function() {
|
||||
return dlopen_test_loopy_function_impl();
|
||||
}
|
21
tests/libs/dlopen_testlib_loopy_b.cpp
Normal file
21
tests/libs/dlopen_testlib_loopy_b.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 <stdlib.h>
|
||||
|
||||
extern "C" bool dlopen_test_loopy_function_impl() {
|
||||
return false;
|
||||
}
|
21
tests/libs/dlopen_testlib_loopy_c.cpp
Normal file
21
tests/libs/dlopen_testlib_loopy_c.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 <stdlib.h>
|
||||
|
||||
extern "C" bool dlopen_test_loopy_function_impl() {
|
||||
return false;
|
||||
}
|
@ -16,9 +16,8 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
// This file is used for libraries that are not supposed to
|
||||
// be successfully loaded/linked - therefore, this function should
|
||||
// not be visible via dlsym - (we are going to use this fact in tests)
|
||||
extern "C" int dlopen_test_invalid_function() {
|
||||
// This library should never be loaded
|
||||
static void __attribute__((constructor)) panic() {
|
||||
abort();
|
||||
}
|
||||
|
21
tests/libs/dlopen_testlib_loopy_root.cpp
Normal file
21
tests/libs/dlopen_testlib_loopy_root.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 <stdlib.h>
|
||||
|
||||
extern "C" bool dlopen_test_loopy_function_impl() {
|
||||
return true;
|
||||
}
|
Loading…
Reference in New Issue
Block a user