]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - android-sdk/platform-bionic.git/commitdiff
Count references for groups instead of instances
authorDmitriy Ivanov <dimitry@google.com>
Sat, 29 Nov 2014 21:57:41 +0000 (13:57 -0800)
committerDmitriy Ivanov <dimitry@google.com>
Tue, 2 Dec 2014 18:54:26 +0000 (10:54 -0800)
  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

13 files changed:
linker/dlfcn.cpp
linker/linked_list.h
linker/linker.cpp
linker/linker.h
tests/dlfcn_test.cpp
tests/libs/Android.build.dlopen_2_parents_reloc.mk [new file with mode: 0644]
tests/libs/Android.mk
tests/libs/dlopen_2_parents_reloc_answer.cpp [new file with mode: 0644]
tests/libs/dlopen_testlib_loopy_a.cpp [new file with mode: 0644]
tests/libs/dlopen_testlib_loopy_b.cpp [new file with mode: 0644]
tests/libs/dlopen_testlib_loopy_c.cpp [new file with mode: 0644]
tests/libs/dlopen_testlib_loopy_invalid.cpp [moved from tests/libs/dlopen_testlib_invalid.cpp with 72% similarity]
tests/libs/dlopen_testlib_loopy_root.cpp [new file with mode: 0644]

index 2486e021f18eabe34e15f1f8104ec39d2e89518e..6aa9cd750a051baa09055e08bd34d089d20576ff 100644 (file)
@@ -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;
index b088061611283e43e6a3368b1cc65f1e059b6049..a72b73ccd6753cf0d7a5b25b42819efbdcabce3f 100644 (file)
@@ -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_;
index 1d68db5a34a89df9cb2cd059f0724a89b70f03bf..f2b3d8bbc7916d1135de80bf85f774c7dd448e82 100644 (file)
@@ -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);
-    return;
+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 (si->ref_count == 1) {
-    unload_list.push_back(si);
+  if (!root->can_unload()) {
+    TRACE("not unloading '%s' - the binary is flagged with NODELETE", root->name);
+    return;
+  }
 
-    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);
+  size_t ref_count = root->is_linked() ? root->decrement_ref_count() : 0;
+
+  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;
-  } else {
-    si->ref_count--;
-    TRACE("not unloading '%s', decrementing ref_count to %zd", si->name, si->ref_count);
-  }
-}
+    local_unload_list.for_each([](soinfo* si) {
+      si->call_destructors();
+    });
 
-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();
-  });
+    while ((si = local_unload_list.pop_front()) != nullptr) {
+      notify_gdb_of_unload(si);
+      soinfo_free(si);
+    }
 
-  soinfo* si = nullptr;
-  while ((si = unload_list.pop_front()) != nullptr) {
-    TRACE("unloading '%s'", si->name);
-    notify_gdb_of_unload(si);
-    soinfo_free(si);
+    while ((si = external_unload_list.pop_front()) != nullptr) {
+      soinfo_unload(si);
+    }
+  } else {
+    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
index d28f70e148e3ee2084cf7ae2fb60955096dba357..f7aa11c70747c586bc2cb10631745792625493c3 100644 (file)
@@ -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();
 };
 
index ea20869d01a10f026b7f96ab9734926c61d9f19b..c988d29d204e4fca934406ab1ee5a47761d20170 100644 (file)
@@ -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());
-
-  // 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);
+  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 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 (file)
index 0000000..29ae10d
--- /dev/null
@@ -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
+
index fafb9e0b3e9612ef21bd1f8d4ac066088f19d878..e4620f5711ce4ff6e705d5f111646ddaccbd964c 100644 (file)
@@ -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 (file)
index 0000000..036670b
--- /dev/null
@@ -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 (file)
index 0000000..4c08764
--- /dev/null
@@ -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();
+}
diff --git a/tests/libs/dlopen_testlib_loopy_b.cpp b/tests/libs/dlopen_testlib_loopy_b.cpp
new file mode 100644 (file)
index 0000000..01dcda9
--- /dev/null
@@ -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;
+}
diff --git a/tests/libs/dlopen_testlib_loopy_c.cpp b/tests/libs/dlopen_testlib_loopy_c.cpp
new file mode 100644 (file)
index 0000000..01dcda9
--- /dev/null
@@ -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;
+}
similarity index 72%
rename from tests/libs/dlopen_testlib_invalid.cpp
rename to tests/libs/dlopen_testlib_loopy_invalid.cpp
index f2039c646d6272180a0076033c116759d0fa09d5..5aa11f87a8e51b9334d0b447f53e4f905629bbf4 100644 (file)
@@ -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();
 }
+
diff --git a/tests/libs/dlopen_testlib_loopy_root.cpp b/tests/libs/dlopen_testlib_loopy_root.cpp
new file mode 100644 (file)
index 0000000..c9459f0
--- /dev/null
@@ -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;
+}