diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index 57b2c23b2..7ef94c059 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -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; diff --git a/linker/linked_list.h b/linker/linked_list.h index b08806161..a72b73ccd 100644 --- a/linker/linked_list.h +++ b/linker/linked_list.h @@ -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* p = head_; diff --git a/linker/linker.cpp b/linker/linker.cpp index 902584aca..fcd4824d5 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -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(base), flags); + DEBUG("si->base = %p si->flags = 0x%08x", reinterpret_cast(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: @@ -2408,6 +2457,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 @@ -2600,7 +2654,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; @@ -2633,7 +2687,6 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( } } si->dynamic = nullptr; - si->ref_count = 1; ElfW(Ehdr)* elf_hdr = reinterpret_cast(si->base); if (elf_hdr->e_type != ET_DYN) { @@ -2674,6 +2727,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); @@ -2793,7 +2852,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 diff --git a/linker/linker.h b/linker/linker.h index d28f70e14..f7aa11c70 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -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(); }; diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index ea20869d0..c988d29d2 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -22,6 +22,7 @@ #include #include +#include "gtest_ex.h" #include "private/ScopeGuard.h" #include @@ -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(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(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(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) { diff --git a/tests/libs/Android.build.dlopen_2_parents_reloc.mk b/tests/libs/Android.build.dlopen_2_parents_reloc.mk new file mode 100644 index 000000000..29ae10ddc --- /dev/null +++ b/tests/libs/Android.build.dlopen_2_parents_reloc.mk @@ -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 + diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk index fafb9e0b3..e4620f571 100644 --- a/tests/libs/Android.mk +++ b/tests/libs/Android.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 diff --git a/tests/libs/dlopen_2_parents_reloc_answer.cpp b/tests/libs/dlopen_2_parents_reloc_answer.cpp new file mode 100644 index 000000000..036670bee --- /dev/null +++ b/tests/libs/dlopen_2_parents_reloc_answer.cpp @@ -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(); +} diff --git a/tests/libs/dlopen_testlib_loopy_a.cpp b/tests/libs/dlopen_testlib_loopy_a.cpp new file mode 100644 index 000000000..4c0876444 --- /dev/null +++ b/tests/libs/dlopen_testlib_loopy_a.cpp @@ -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 + +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(); +} diff --git a/tests/libs/dlopen_testlib_loopy_b.cpp b/tests/libs/dlopen_testlib_loopy_b.cpp new file mode 100644 index 000000000..01dcda9de --- /dev/null +++ b/tests/libs/dlopen_testlib_loopy_b.cpp @@ -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 + +extern "C" bool dlopen_test_loopy_function_impl() { + return false; +} diff --git a/tests/libs/dlopen_testlib_loopy_c.cpp b/tests/libs/dlopen_testlib_loopy_c.cpp new file mode 100644 index 000000000..01dcda9de --- /dev/null +++ b/tests/libs/dlopen_testlib_loopy_c.cpp @@ -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 + +extern "C" bool dlopen_test_loopy_function_impl() { + return false; +} diff --git a/tests/libs/dlopen_testlib_invalid.cpp b/tests/libs/dlopen_testlib_loopy_invalid.cpp similarity index 72% rename from tests/libs/dlopen_testlib_invalid.cpp rename to tests/libs/dlopen_testlib_loopy_invalid.cpp index f2039c646..5aa11f87a 100644 --- a/tests/libs/dlopen_testlib_invalid.cpp +++ b/tests/libs/dlopen_testlib_loopy_invalid.cpp @@ -16,9 +16,8 @@ #include -// 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(); } + diff --git a/tests/libs/dlopen_testlib_loopy_root.cpp b/tests/libs/dlopen_testlib_loopy_root.cpp new file mode 100644 index 000000000..c9459f0e1 --- /dev/null +++ b/tests/libs/dlopen_testlib_loopy_root.cpp @@ -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 + +extern "C" bool dlopen_test_loopy_function_impl() { + return true; +}