From 0416d88f9c90dcb1b97947a27a7c05f3627484c4 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Tue, 4 Nov 2014 09:38:18 -0800 Subject: [PATCH] Revert "Revert "Fix symbol lookup order during relocation"" This reverts commit f947be2889639defc6424b1813ccc779528b7598. --- linker/linked_list.h | 12 +++ linker/linker.cpp | 162 +++++++++++++++++----------- linker/linker.h | 23 ++-- tests/Android.mk | 13 ++- tests/dl_test.cpp | 72 +++++++++++++ tests/dlfcn_test.cpp | 14 +++ tests/libs/Android.mk | 36 +++++++ tests/libs/dl_df_1_global.cpp | 19 ++++ tests/libs/dl_df_1_use_global.cpp | 23 ++++ tests/libs/dl_preempt_library_1.cpp | 45 ++++++++ tests/libs/dl_preempt_library_2.cpp | 37 +++++++ 11 files changed, 385 insertions(+), 71 deletions(-) create mode 100644 tests/dl_test.cpp create mode 100644 tests/libs/dl_df_1_global.cpp create mode 100644 tests/libs/dl_df_1_use_global.cpp create mode 100644 tests/libs/dl_preempt_library_1.cpp create mode 100644 tests/libs/dl_preempt_library_2.cpp diff --git a/linker/linked_list.h b/linker/linked_list.h index 72a32b4ba..b08806161 100644 --- a/linker/linked_list.h +++ b/linker/linked_list.h @@ -36,6 +36,12 @@ class LinkedList { clear(); } + LinkedList(LinkedList&& that) { + this->head_ = that.head_; + this->tail_ = that.tail_; + that.head_ = that.tail_ = nullptr; + } + void push_front(T* const element) { LinkedListEntry* new_entry = Allocator::alloc(); new_entry->next = head_; @@ -140,6 +146,12 @@ class LinkedList { return false; } + static LinkedList make_list(T* const element) { + LinkedList one_element_list; + one_element_list.push_back(element); + return one_element_list; + } + private: LinkedListEntry* head_; LinkedListEntry* tail_; diff --git a/linker/linker.cpp b/linker/linker.cpp index f14d8b48d..ab0fc0762 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -282,7 +282,7 @@ static void protect_data(int protection) { g_soinfo_links_allocator.protect_all(protection); } -static soinfo* soinfo_alloc(const char* name, struct stat* file_stat, off64_t file_offset, int rtld_flags) { +static soinfo* soinfo_alloc(const char* name, struct stat* file_stat, off64_t file_offset, uint32_t rtld_flags) { if (strlen(name) >= SOINFO_NAME_LEN) { DL_ERR("library name \"%s\" too long", name); return nullptr; @@ -481,7 +481,8 @@ static unsigned elfhash(const char* _name) { return h; } -static ElfW(Sym)* soinfo_do_lookup(soinfo* si, const char* name, soinfo** lsi, const soinfo::soinfo_list_t& local_group) { +static ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in, + const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group) { unsigned elf_hash = elfhash(name); ElfW(Sym)* s = nullptr; @@ -496,49 +497,40 @@ static ElfW(Sym)* soinfo_do_lookup(soinfo* si, const char* name, soinfo** lsi, c * Note that this is unlikely since static linker avoids generating * relocations for -Bsymbolic linked dynamic executables. */ - if (si->has_DT_SYMBOLIC) { - DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si->name, name); - s = soinfo_elf_lookup(si, elf_hash, name); + if (si_from->has_DT_SYMBOLIC) { + DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->name, name); + s = soinfo_elf_lookup(si_from, elf_hash, name); if (s != nullptr) { - *lsi = si; + *si_found_in = si_from; } } - if (s == nullptr && somain != nullptr) { - // 1. Look for it in the main executable unless we already did. - if (si != somain || !si->has_DT_SYMBOLIC) { - DEBUG("%s: looking up %s in executable %s", - si->name, name, somain->name); - s = soinfo_elf_lookup(somain, elf_hash, name); + // 1. Look for it in global_group + if (s == nullptr) { + global_group.visit([&](soinfo* global_si) { + DEBUG("%s: looking up %s in %s (from global group)", si_from->name, name, global_si->name); + s = soinfo_elf_lookup(global_si, elf_hash, name); if (s != nullptr) { - *lsi = somain; + *si_found_in = global_si; + return false; } - } - // 2. Look for it in the ld_preloads - if (s == nullptr) { - for (int i = 0; g_ld_preloads[i] != NULL; i++) { - s = soinfo_elf_lookup(g_ld_preloads[i], elf_hash, name); - if (s != nullptr) { - *lsi = g_ld_preloads[i]; - break; - } - } - } + return true; + }); } - // 3. Look for it in the local group + // 2. Look for it in the local group if (s == nullptr) { local_group.visit([&](soinfo* local_si) { - if (local_si == si && si->has_DT_SYMBOLIC) { + if (local_si == si_from && si_from->has_DT_SYMBOLIC) { // we already did this - skip return true; } - DEBUG("%s: looking up %s in %s (from local group)", si->name, name, local_si->name); + DEBUG("%s: looking up %s in %s (from local group)", si_from->name, name, local_si->name); s = soinfo_elf_lookup(local_si, elf_hash, name); if (s != nullptr) { - *lsi = local_si; + *si_found_in = local_si; return false; } @@ -549,9 +541,9 @@ static ElfW(Sym)* soinfo_do_lookup(soinfo* si, const char* name, soinfo** lsi, c if (s != nullptr) { TRACE_TYPE(LOOKUP, "si %s sym %s s->st_value = %p, " "found in %s, base = %p, load bias = %p", - si->name, name, reinterpret_cast(s->st_value), - (*lsi)->name, reinterpret_cast((*lsi)->base), - reinterpret_cast((*lsi)->load_bias)); + si_from->name, name, reinterpret_cast(s->st_value), + (*si_found_in)->name, reinterpret_cast((*si_found_in)->base), + reinterpret_cast((*si_found_in)->load_bias)); } return s; @@ -916,6 +908,24 @@ static bool is_recursive(soinfo* si, soinfo* parent) { }); } +// 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::soinfo_list_t make_global_group() { + soinfo::soinfo_list_t global_group; + for (soinfo* si = somain; si != nullptr; si = si->next) { + if ((si->get_dt_flags_1() & DF_1_GLOBAL) != 0) { + global_group.push_back(si); + } + } + + return global_group; +} + static bool find_libraries(soinfo* start_with, const char* const library_names[], size_t library_names_count, soinfo* soinfos[], soinfo* ld_preloads[], size_t ld_preloads_count, int rtld_flags, const android_dlextinfo* extinfo) { // Step 0: prepare. @@ -925,6 +935,9 @@ static bool find_libraries(soinfo* start_with, const char* const library_names[] load_tasks.push_back(LoadTask::create(name, start_with)); } + // Construct global_group. + soinfo::soinfo_list_t global_group = make_global_group(); + // 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 @@ -973,6 +986,11 @@ static bool find_libraries(soinfo* start_with, const char* const library_names[] // 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) { + // 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. + si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL); ld_preloads[soinfos_count] = si; } @@ -993,7 +1011,7 @@ static bool find_libraries(soinfo* start_with, const char* const library_names[] bool linked = local_group.visit([&](soinfo* si) { if ((si->flags & FLAG_LINKED) == 0) { - if (!si->LinkImage(local_group, extinfo)) { + if (!si->LinkImage(global_group, local_group, extinfo)) { return false; } si->flags |= FLAG_LINKED; @@ -1128,7 +1146,7 @@ static ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) { } #if defined(USE_RELA) -int soinfo::Relocate(ElfW(Rela)* rela, unsigned count, const soinfo_list_t& local_group) { +int soinfo::Relocate(ElfW(Rela)* rela, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) { for (size_t idx = 0; idx < count; ++idx, ++rela) { unsigned type = ELFW(R_TYPE)(rela->r_info); unsigned sym = ELFW(R_SYM)(rela->r_info); @@ -1146,7 +1164,7 @@ int soinfo::Relocate(ElfW(Rela)* rela, unsigned count, const soinfo_list_t& loca if (sym != 0) { sym_name = get_string(symtab[sym].st_name); - s = soinfo_do_lookup(this, sym_name, &lsi, local_group); + s = soinfo_do_lookup(this, sym_name, &lsi, global_group,local_group); if (s == nullptr) { // We only allow an undefined symbol if this is a weak reference... s = &symtab[sym]; @@ -1405,7 +1423,7 @@ int soinfo::Relocate(ElfW(Rela)* rela, unsigned count, const soinfo_list_t& loca } #else // REL, not RELA. -int soinfo::Relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& local_group) { +int soinfo::Relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) { for (size_t idx = 0; idx < count; ++idx, ++rel) { unsigned type = ELFW(R_TYPE)(rel->r_info); // TODO: don't use unsigned for 'sym'. Use uint32_t or ElfW(Addr) instead. @@ -1424,7 +1442,7 @@ int soinfo::Relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& local_ if (sym != 0) { sym_name = get_string(symtab[sym].st_name); - s = soinfo_do_lookup(this, sym_name, &lsi, local_group); + s = soinfo_do_lookup(this, sym_name, &lsi, global_group, local_group); if (s == nullptr) { // We only allow an undefined symbol if this is a weak reference... s = &symtab[sym]; @@ -1610,7 +1628,7 @@ int soinfo::Relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& local_ #endif #if defined(__mips__) -static bool mips_relocate_got(soinfo* si, const soinfo::soinfo_list_t& local_group) { +static bool mips_relocate_got(soinfo* si, const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group) { ElfW(Addr)** got = si->plt_got; if (got == nullptr) { return true; @@ -1643,7 +1661,7 @@ static bool mips_relocate_got(soinfo* si, const soinfo::soinfo_list_t& local_gro // This is an undefined reference... try to locate it. const char* sym_name = si->get_string(sym->st_name); soinfo* lsi = nullptr; - ElfW(Sym)* s = soinfo_do_lookup(si, sym_name, &lsi, local_group); + ElfW(Sym)* s = soinfo_do_lookup(si, sym_name, &lsi, global_group, local_group); if (s == nullptr) { // We only allow an undefined symbol if this is a weak reference. s = &symtab[g]; @@ -1783,7 +1801,7 @@ void soinfo::remove_all_links() { children.clear(); } -dev_t soinfo::get_st_dev() { +dev_t soinfo::get_st_dev() const { if (has_min_version(0)) { return st_dev; } @@ -1791,7 +1809,7 @@ dev_t soinfo::get_st_dev() { return 0; }; -ino_t soinfo::get_st_ino() { +ino_t soinfo::get_st_ino() const { if (has_min_version(0)) { return st_ino; } @@ -1799,7 +1817,7 @@ ino_t soinfo::get_st_ino() { return 0; } -off64_t soinfo::get_file_offset() { +off64_t soinfo::get_file_offset() const { if (has_min_version(1)) { return file_offset; } @@ -1807,7 +1825,7 @@ off64_t soinfo::get_file_offset() { return 0; } -int soinfo::get_rtld_flags() { +uint32_t soinfo::get_rtld_flags() const { if (has_min_version(1)) { return rtld_flags; } @@ -1815,6 +1833,27 @@ int soinfo::get_rtld_flags() { return 0; } +uint32_t soinfo::get_dt_flags_1() const { + if (has_min_version(1)) { + return dt_flags_1; + } + + return 0; +} +void soinfo::set_dt_flags_1(uint32_t dt_flags_1) { + if (has_min_version(1)) { + if ((dt_flags_1 & DF_1_GLOBAL) != 0) { + rtld_flags |= RTLD_GLOBAL; + } + + if ((dt_flags_1 & DF_1_NODELETE) != 0) { + rtld_flags |= RTLD_NODELETE; + } + + this->dt_flags_1 = dt_flags_1; + } +} + // This is a return on get_children()/get_parents() if // 'this->flags' does not have FLAG_NEW_SOINFO set. static soinfo::soinfo_list_t g_empty_list; @@ -1852,8 +1891,9 @@ const char* soinfo::get_string(ElfW(Word) index) const { } bool soinfo::can_unload() const { - return (rtld_flags & (RTLD_NODELETE | RTLD_GLOBAL)) == 0; + return (get_rtld_flags() & (RTLD_NODELETE | RTLD_GLOBAL)) == 0; } + /* Force any of the closed stdin, stdout and stderr to be associated with /dev/null. */ static int nullify_closed_stdio() { @@ -2154,16 +2194,9 @@ bool soinfo::PrelinkImage() { break; case DT_FLAGS_1: - if ((d->d_un.d_val & DF_1_GLOBAL) != 0) { - rtld_flags |= RTLD_GLOBAL; - } + set_dt_flags_1(d->d_un.d_val); - if ((d->d_un.d_val & DF_1_NODELETE) != 0) { - rtld_flags |= RTLD_NODELETE; - } - // TODO: Implement other flags - - if ((d->d_un.d_val & ~(DF_1_NOW | DF_1_GLOBAL | DF_1_NODELETE)) != 0) { + if ((d->d_un.d_val & ~SUPPORTED_DT_FLAGS_1) != 0) { DL_WARN("Unsupported flags DT_FLAGS_1=%p", reinterpret_cast(d->d_un.d_val)); } break; @@ -2236,7 +2269,7 @@ bool soinfo::PrelinkImage() { return true; } -bool soinfo::LinkImage(const soinfo_list_t& local_group, const android_dlextinfo* extinfo) { +bool soinfo::LinkImage(const soinfo_list_t& global_group, const soinfo_list_t& local_group, const android_dlextinfo* extinfo) { #if !defined(__LP64__) if (has_text_relocations) { @@ -2255,33 +2288,33 @@ bool soinfo::LinkImage(const soinfo_list_t& local_group, const android_dlextinfo #if defined(USE_RELA) if (rela != nullptr) { DEBUG("[ relocating %s ]", name); - if (Relocate(rela, rela_count, local_group)) { + if (Relocate(rela, rela_count, global_group, local_group)) { return false; } } if (plt_rela != nullptr) { DEBUG("[ relocating %s plt ]", name); - if (Relocate(plt_rela, plt_rela_count, local_group)) { + if (Relocate(plt_rela, plt_rela_count, global_group, local_group)) { return false; } } #else if (rel != nullptr) { DEBUG("[ relocating %s ]", name); - if (Relocate(rel, rel_count, local_group)) { + if (Relocate(rel, rel_count, global_group, local_group)) { return false; } } if (plt_rel != nullptr) { DEBUG("[ relocating %s plt ]", name); - if (Relocate(plt_rel, plt_rel_count, local_group)) { + if (Relocate(plt_rel, plt_rel_count, global_group, local_group)) { return false; } } #endif #if defined(__mips__) - if (!mips_relocate_got(this, local_group)) { + if (!mips_relocate_got(this, global_group, local_group)) { return false; } #endif @@ -2348,7 +2381,7 @@ static void add_vdso(KernelArgumentBlock& args __unused) { si->load_bias = get_elf_exec_load_bias(ehdr_vdso); si->PrelinkImage(); - si->LinkImage(g_empty_list, nullptr); + si->LinkImage(g_empty_list, soinfo::soinfo_list_t::make_list(si), nullptr); #endif } @@ -2479,6 +2512,9 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( si->PrelinkImage(); + // add somain to global group + si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL); + // Load ld_preloads and dependencies. StringLinkedList needed_library_name_list; size_t needed_libraries_count = 0; @@ -2622,7 +2658,13 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { linker_so.phnum = elf_hdr->e_phnum; linker_so.flags |= FLAG_LINKER; - if (!(linker_so.PrelinkImage() && linker_so.LinkImage(g_empty_list, nullptr))) { + // 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 + // linker is built with DT_SYMBOLIC and therefore relocates its symbols against + // itself without having to look into local_group and (2) allocators + // are not yet initialized, and therefore we cannot use linked_list.push_* + // functions at this point. + if (!(linker_so.PrelinkImage() && linker_so.LinkImage(g_empty_list, g_empty_list, nullptr))) { // It would be nice to print an error message, but if the linker // can't link itself, there's no guarantee that we'll be able to // call write() (because it involves a GOT reference). We may as diff --git a/linker/linker.h b/linker/linker.h index 222aca11e..0a98b40dc 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -89,7 +89,9 @@ #define FLAG_LINKER 0x00000010 // The linker itself #define FLAG_NEW_SOINFO 0x40000000 // new soinfo format -#define SOINFO_VERSION 0 +#define SUPPORTED_DT_FLAGS_1 (DF_1_NOW | DF_1_GLOBAL | DF_1_NODELETE) + +#define SOINFO_VERSION 1 #define SOINFO_NAME_LEN 128 @@ -207,16 +209,18 @@ struct soinfo { void CallDestructors(); void CallPreInitConstructors(); bool PrelinkImage(); - bool LinkImage(const soinfo_list_t& local_group, const android_dlextinfo* extinfo); + bool LinkImage(const soinfo_list_t& global_group, const soinfo_list_t& local_group, const android_dlextinfo* extinfo); void add_child(soinfo* child); void remove_all_links(); - ino_t get_st_ino(); - dev_t get_st_dev(); - off64_t get_file_offset(); + ino_t get_st_ino() const; + dev_t get_st_dev() const; + off64_t get_file_offset() const; - int get_rtld_flags(); + uint32_t get_rtld_flags() const; + uint32_t get_dt_flags_1() const; + void set_dt_flags_1(uint32_t dt_flags_1); soinfo_list_t& get_children(); soinfo_list_t& get_parents(); @@ -234,9 +238,9 @@ struct soinfo { void CallArray(const char* array_name, linker_function_t* functions, size_t count, bool reverse); void CallFunction(const char* function_name, linker_function_t function); #if defined(USE_RELA) - int Relocate(ElfW(Rela)* rela, unsigned count, const soinfo_list_t& local_group); + int Relocate(ElfW(Rela)* rela, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group); #else - int Relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& local_group); + int Relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group); #endif private: @@ -254,7 +258,8 @@ struct soinfo { // version >= 1 off64_t file_offset; - int rtld_flags; + uint32_t rtld_flags; + uint32_t dt_flags_1; size_t strtab_size; friend soinfo* get_libdl_info(); diff --git a/tests/Android.mk b/tests/Android.mk index 2f182654e..82eae3da4 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -233,6 +233,7 @@ bionic-unit-tests_static_libraries := \ bionic-unit-tests_src_files := \ atexit_test.cpp \ + dl_test.cpp \ dlext_test.cpp \ dlfcn_test.cpp \ @@ -245,8 +246,7 @@ bionic-unit-tests_conlyflags := \ bionic-unit-tests_cppflags := $(test_cppflags) bionic-unit-tests_ldflags := \ - -Wl,--export-dynamic \ - -Wl,-u,DlSymTestFunction \ + -Wl,--export-dynamic bionic-unit-tests_c_includes := \ bionic/libc \ @@ -255,6 +255,9 @@ bionic-unit-tests_c_includes := \ bionic-unit-tests_shared_libraries_target := \ libdl \ libpagemap \ + libdl_preempt_test_1 \ + libdl_preempt_test_2 \ + libdl_test_df_1_global module := bionic-unit-tests module_tag := optional @@ -302,6 +305,12 @@ ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x bionic-unit-tests-glibc_src_files := \ atexit_test.cpp \ dlfcn_test.cpp \ + dl_test.cpp \ + +bionic-unit-tests-glibc_shared_libraries := \ + libdl_preempt_test_1 \ + libdl_preempt_test_2 \ + libdl_test_df_1_global bionic-unit-tests-glibc_whole_static_libraries := \ libBionicStandardTests \ diff --git a/tests/dl_test.cpp b/tests/dl_test.cpp new file mode 100644 index 000000000..74c7b510f --- /dev/null +++ b/tests/dl_test.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2012 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 + +#include +#include +#include +#include +#include + +#include + +extern "C" int main_global_default_serial() { + return 3370318; +} + +extern "C" int main_global_protected_serial() { + return 2716057; +} + +// The following functions are defined in DT_NEEDED +// libdl_preempt_test.so library. + +// This one calls main_global_default_serial +extern "C" int main_global_default_get_serial(); + +// This one calls main_global_protected_serial +extern "C" int main_global_protected_get_serial(); + +// This one calls lib_global_default_serial +extern "C" int lib_global_default_get_serial(); + +// This one calls lib_global_protected_serial +extern "C" int lib_global_protected_get_serial(); + +// This test verifies that the global default function +// main_global_default_serial() is preempted by +// the function defined above. +TEST(dl, main_preempts_global_default) { + ASSERT_EQ(3370318, main_global_default_get_serial()); +} + +// This one makes sure that the global protected +// symbols do not get preempted +TEST(dl, main_does_not_preempt_global_protected) { + ASSERT_EQ(3370318, main_global_protected_get_serial()); +} + +// check same things for lib +TEST(dl, lib_preempts_global_default) { + ASSERT_EQ(3370318, lib_global_default_get_serial()); +} + +TEST(dl, lib_does_not_preempt_global_protected) { + ASSERT_EQ(3370318, lib_global_protected_get_serial()); +} + +// TODO: Add tests for LD_PRELOADs diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index 2ab3dc1ed..060f7e09c 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -499,6 +499,20 @@ TEST(dlfcn, dlopen_nodelete_dt_flags_1) { ASSERT_TRUE(!is_unloaded); } +TEST(dlfcn, dlsym_df_1_global) { +#if !defined(__arm__) + void* handle = dlopen("libtest_dlsym_df_1_global.so", RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + int (*get_answer)(); + get_answer = reinterpret_cast(dlsym(handle, "dl_df_1_global_get_answer")); + ASSERT_TRUE(get_answer != nullptr) << dlerror(); + ASSERT_EQ(42, get_answer()); + ASSERT_EQ(0, dlclose(handle)); +#else + GTEST_LOG_(INFO) << "This test does nothing on arm (to be reenabled once b/18137520 or b/18130452 are fixed).\n"; +#endif +} + TEST(dlfcn, dlopen_failure) { void* self = dlopen("/does/not/exist", RTLD_NOW); ASSERT_TRUE(self == NULL); diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk index e4fee6a6c..a45442031 100644 --- a/tests/libs/Android.mk +++ b/tests/libs/Android.mk @@ -290,6 +290,42 @@ libtest_atexit_src_files := \ module := libtest_atexit include $(LOCAL_PATH)/Android.build.testlib.mk +# ----------------------------------------------------------------------------- +# This library is used by dl_load test to check symbol preempting +# by main executable +# ----------------------------------------------------------------------------- +libdl_preempt_test_1_src_files := dl_preempt_library_1.cpp + +module := libdl_preempt_test_1 +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# This library is used by dl_load test to check symbol preempting +# by libdl_preempt_test_1.so +# ----------------------------------------------------------------------------- +libdl_preempt_test_2_src_files := dl_preempt_library_2.cpp + +module := libdl_preempt_test_2 +include $(LOCAL_PATH)/Android.build.testlib.mk + +# ----------------------------------------------------------------------------- +# Library with DF_1_GLOBAL +# ----------------------------------------------------------------------------- +# TODO: re-enable arm once b/18137520 or b/18130452 are fixed +ifeq ($(filter $(TARGET_ARCH),arm),) +libdl_test_df_1_global_src_files := dl_df_1_global.cpp +libdl_test_df_1_global_ldflags := -fuse-ld=bfd -Wl,-z,global +module := libdl_test_df_1_global +include $(LOCAL_PATH)/Android.build.testlib.mk +endif + +# ----------------------------------------------------------------------------- +# Library using symbol from libdl_test_df_1_global +# ----------------------------------------------------------------------------- +libtest_dlsym_df_1_global_src_files := dl_df_1_use_global.cpp +module := libtest_dlsym_df_1_global +include $(LOCAL_PATH)/Android.build.testlib.mk + # ----------------------------------------------------------------------------- # Library with weak function # ----------------------------------------------------------------------------- diff --git a/tests/libs/dl_df_1_global.cpp b/tests/libs/dl_df_1_global.cpp new file mode 100644 index 000000000..39856fd50 --- /dev/null +++ b/tests/libs/dl_df_1_global.cpp @@ -0,0 +1,19 @@ +/* + * 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 dl_df_1_global_get_answer_impl() { + return 42; +} diff --git a/tests/libs/dl_df_1_use_global.cpp b/tests/libs/dl_df_1_use_global.cpp new file mode 100644 index 000000000..e14910d1c --- /dev/null +++ b/tests/libs/dl_df_1_use_global.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)) dl_df_1_global_get_answer_impl() { + return 0; +} + +extern "C" int dl_df_1_global_get_answer() { + return dl_df_1_global_get_answer_impl(); +} diff --git a/tests/libs/dl_preempt_library_1.cpp b/tests/libs/dl_preempt_library_1.cpp new file mode 100644 index 000000000..b4d81d5ac --- /dev/null +++ b/tests/libs/dl_preempt_library_1.cpp @@ -0,0 +1,45 @@ +/* + * 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. + */ + +// This one should be preempted by the function +// defined in the main executable. +extern "C" int __attribute__((weak)) main_global_default_serial() { + return 2716057; +} + +// Even though this one is defined by the main +// executable it should not be preempted +// because of protected visibility +extern "C" int __attribute__((weak, visibility("protected"))) main_global_protected_serial() { + return 3370318; +} + +extern "C" int main_global_default_get_serial() { + return main_global_default_serial(); +} + +extern "C" int main_global_protected_get_serial() { + return main_global_protected_serial(); +} + +// Trying to preempt functions from a DT_NEEDED .so +extern "C" int lib_global_default_serial() { + return 3370318; +} + +extern "C" int lib_global_protected_serial() { + return 2716057; +} diff --git a/tests/libs/dl_preempt_library_2.cpp b/tests/libs/dl_preempt_library_2.cpp new file mode 100644 index 000000000..8df9a1667 --- /dev/null +++ b/tests/libs/dl_preempt_library_2.cpp @@ -0,0 +1,37 @@ +/* + * 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. + */ + +// This one should be preempted by the function +// defined in libdl_preempt_test_1.so +extern "C" int __attribute__((weak)) lib_global_default_serial() { + return 2716057; +} + +// Even though this one is defined by +// libdl_preempt_test_1.so it should not be +// preempted because of protected visibility +extern "C" int __attribute__((weak,visibility("protected"))) lib_global_protected_serial() { + return 3370318; +} + +extern "C" int lib_global_default_get_serial() { + return lib_global_default_serial(); +} + +extern "C" int lib_global_protected_get_serial() { + return lib_global_protected_serial(); +} +