OSDN Git Service

resolved conflicts for merge of c0133a73 to lmp-mr1-dev-plus-aosp
authorDmitriy Ivanov <dimitry@google.com>
Thu, 2 Oct 2014 18:52:06 +0000 (11:52 -0700)
committerDmitriy Ivanov <dimitry@google.com>
Thu, 2 Oct 2014 18:59:28 +0000 (11:59 -0700)
Change-Id: I31d81e91d796fa27f9c88a7edc96c97320ade72c

1  2 
linker/linker.cpp
linker/linker.h
linker/linker_phdr.cpp
tests/Android.mk
tests/libs/Android.mk

@@@ -770,81 -798,73 +769,83 @@@ static int open_library(const char* nam
    return fd;
  }
  
 -static soinfo* load_library(const char* name, int dlflags, const android_dlextinfo* extinfo) {
 -    int fd = -1;
 -    ScopedFd file_guard(-1);
 +template<typename F>
 +static void for_each_dt_needed(const soinfo* si, F action) {
 +  for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) {
 +    if (d->d_tag == DT_NEEDED) {
 +      action(si->get_string(d->d_un.d_val));
 +    }
 +  }
 +}
  
 -    if (extinfo != nullptr && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) != 0) {
 -      fd = extinfo->library_fd;
 -    } else {
 -      // Open the file.
 -      fd = open_library(name);
 -      if (fd == -1) {
 -        DL_ERR("library \"%s\" not found", name);
 -        return nullptr;
 -      }
 +static soinfo* load_library(LoadTaskList& load_tasks, const char* name, int rtld_flags, const android_dlextinfo* extinfo) {
 +  int fd = -1;
 +  ScopedFd file_guard(-1);
  
 -      file_guard.reset(fd);
 +  if (extinfo != nullptr && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) != 0) {
 +    fd = extinfo->library_fd;
 +  } else {
 +    // Open the file.
 +    fd = open_library(name);
 +    if (fd == -1) {
 +      DL_ERR("library \"%s\" not found", name);
 +      return nullptr;
      }
  
-   struct stat file_stat;
-   if (TEMP_FAILURE_RETRY(fstat(fd, &file_stat)) != 0) {
-     DL_ERR("unable to stat file for the library %s: %s", name, strerror(errno));
-     return nullptr;
-   }
 +    file_guard.reset(fd);
 +  }
 +
+     ElfReader elf_reader(name, fd);
  
-   // Check for symlink and other situations where
-   // file can have different names.
-   for (soinfo* si = solist; si != nullptr; si = si->next) {
-     if (si->get_st_dev() != 0 &&
-         si->get_st_ino() != 0 &&
-         si->get_st_dev() == file_stat.st_dev &&
-         si->get_st_ino() == file_stat.st_ino) {
-       TRACE("library \"%s\" is already loaded under different name/path \"%s\" - will return existing soinfo", name, si->name);
-       return si;
+     struct stat file_stat;
+     if (TEMP_FAILURE_RETRY(fstat(fd, &file_stat)) != 0) {
+       DL_ERR("unable to stat file for the library %s: %s", name, strerror(errno));
+       return nullptr;
+     }
+     // Check for symlink and other situations where
+     // file can have different names.
+     for (soinfo* si = solist; si != nullptr; si = si->next) {
+       if (si->get_st_dev() != 0 &&
+           si->get_st_ino() != 0 &&
+           si->get_st_dev() == file_stat.st_dev &&
+           si->get_st_ino() == file_stat.st_ino) {
+         TRACE("library \"%s\" is already loaded under different name/path \"%s\" - will return existing soinfo", name, si->name);
+         return si;
+       }
      }
-   }
  
 -    if ((dlflags & RTLD_NOLOAD) != 0) {
 -      return nullptr;
 -    }
 +  if ((rtld_flags & RTLD_NOLOAD) != 0) {
 +    DL_ERR("library \"%s\" wasn't loaded and RTLD_NOLOAD prevented it", name);
 +    return nullptr;
 +  }
  
-   // Read the ELF header and load the segments.
-   ElfReader elf_reader(name, fd);
-   if (!elf_reader.Load(extinfo)) {
-     return nullptr;
-   }
+     // Read the ELF header and load the segments.
+     if (!elf_reader.Load(extinfo)) {
+         return nullptr;
+     }
  
 -    soinfo* si = soinfo_alloc(SEARCH_NAME(name), &file_stat);
 -    if (si == nullptr) {
 -        return nullptr;
 -    }
 -    si->base = elf_reader.load_start();
 -    si->size = elf_reader.load_size();
 -    si->load_bias = elf_reader.load_bias();
 -    si->phnum = elf_reader.phdr_count();
 -    si->phdr = elf_reader.loaded_phdr();
 +  soinfo* si = soinfo_alloc(SEARCH_NAME(name), &file_stat, rtld_flags);
 +  if (si == nullptr) {
 +    return nullptr;
 +  }
 +  si->base = elf_reader.load_start();
 +  si->size = elf_reader.load_size();
 +  si->load_bias = elf_reader.load_bias();
 +  si->phnum = elf_reader.phdr_count();
 +  si->phdr = elf_reader.loaded_phdr();
  
-   if (!si->PrelinkImage()) {
-     soinfo_free(si);
-     return nullptr;
-   }
+     // At this point we know that whatever is loaded @ base is a valid ELF
+     // shared library whose segments are properly mapped in.
+     TRACE("[ load_library base=%p size=%zu name='%s' ]",
+           reinterpret_cast<void*>(si->base), si->size, si->name);
  
-   for_each_dt_needed(si, [&] (const char* name) {
-     load_tasks.push_back(LoadTask::create(name, si));
-   });
+     if (!si->LinkImage(extinfo)) {
+       soinfo_free(si);
+       return nullptr;
+     }
  
-   return si;
+     return si;
  }
  
  static soinfo *find_loaded_library_by_name(const char* name) {
@@@ -862,116 -885,25 +863,102 @@@ static soinfo* find_library_internal(Lo
    soinfo* si = find_loaded_library_by_name(name);
  
    // Library might still be loaded, the accurate detection
-   // of this fact is done by load_library.
+   // of this fact is done by load_library
    if (si == nullptr) {
      TRACE("[ '%s' has not been found by name.  Trying harder...]", name);
 -    si = load_library(name, dlflags, extinfo);
 +    si = load_library(load_tasks, name, rtld_flags, extinfo);
    }
  
-   return si;
- }
- static void soinfo_unload(soinfo* si);
- static bool is_recursive(soinfo* si, soinfo* parent) {
-   if (parent == nullptr) {
-     return false;
-   }
-   if (si == parent) {
+   if (si != nullptr && (si->flags & FLAG_LINKED) == 0) {
      DL_ERR("recursive link to \"%s\"", si->name);
 -    return nullptr;
 +    return true;
    }
  
 -  return si;
 +  return !parent->get_parents().visit([&](soinfo* grandparent) {
 +    return !is_recursive(si, grandparent);
 +  });
  }
  
 -static soinfo* find_library(const char* name, int dlflags, const android_dlextinfo* extinfo) {
 -  soinfo* si = find_library_internal(name, dlflags, extinfo);
 -  if (si != nullptr) {
 +static bool find_libraries(const char* const library_names[], size_t library_names_size, soinfo* soinfos[],
 +    soinfo* ld_preloads[], size_t ld_preloads_size, int rtld_flags, const android_dlextinfo* extinfo) {
 +  // Step 0: prepare.
 +  LoadTaskList load_tasks;
 +  for (size_t i = 0; i < library_names_size; ++i) {
 +    const char* name = library_names[i];
 +    load_tasks.push_back(LoadTask::create(name, nullptr));
 +  }
 +
 +  // Libraries added to this list in reverse order so that we can
 +  // start linking from bottom-up - see step 2.
 +  SoinfoLinkedList found_libs;
 +  size_t soinfos_size = 0;
 +
 +  auto failure_guard = make_scope_guard([&]() {
 +    // Housekeeping
 +    load_tasks.for_each([] (LoadTask* t) {
 +      LoadTask::deleter(t);
 +    });
 +
 +    for (size_t i = 0; i<soinfos_size; ++i) {
 +      soinfo_unload(soinfos[i]);
 +    }
 +  });
 +
 +  // Step 1: load and pre-link all DT_NEEDED libraries in breadth first order.
 +  for (LoadTask::unique_ptr task(load_tasks.pop_front()); task.get() != nullptr; task.reset(load_tasks.pop_front())) {
 +    soinfo* si = find_library_internal(load_tasks, task->get_name(), rtld_flags, extinfo);
 +    if (si == nullptr) {
 +      return false;
 +    }
 +
 +    soinfo* needed_by = task->get_needed_by();
 +
 +    if (is_recursive(si, needed_by)) {
 +      return false;
 +    }
 +
      si->ref_count++;
-   // All is well - found_libs and load_tasks are empty at this point
-   // and all libs are successfully linked.
-   failure_guard.disable();
-   return true;
 +    if (needed_by != nullptr) {
 +      needed_by->add_child(si);
 +    }
 +    found_libs.push_front(si);
 +
 +    // When ld_preloads is not null first
 +    // ld_preloads_size libs are in fact ld_preloads.
 +    if (ld_preloads != nullptr && soinfos_size < ld_preloads_size) {
 +      ld_preloads[soinfos_size] = si;
 +    }
 +
 +    if (soinfos_size<library_names_size) {
 +      soinfos[soinfos_size++] = si;
 +    }
 +  }
 +
 +  // Step 2: link libraries.
 +  soinfo* si;
 +  while ((si = found_libs.pop_front()) != nullptr) {
 +    if ((si->flags & FLAG_LINKED) == 0) {
 +      if (!si->LinkImage(extinfo)) {
 +        return false;
 +      }
 +      si->flags |= FLAG_LINKED;
 +    }
 +  }
 +
++  return si;
 +}
 +
 +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(&name, 1, &si, nullptr, 0, rtld_flags, extinfo)) {
 +    return nullptr;
    }
    return si;
  }
  
@@@ -981,23 -913,32 +968,26 @@@ static void soinfo_unload(soinfo* si) 
      si->CallDestructors();
  
      if (si->has_min_version(0)) {
 -      // It is not safe to do si->get_children().for_each, because
 -      // during soinfo_free the child will concurrently modify the si->children
 -      // list, therefore we create a copy and use it to unload children.
 -      size_t children_count = si->get_children().size();
 -      soinfo* children[children_count];
 -      si->get_children().copy_to_array(children, children_count);
 -
 -      for (size_t i = 0; i < children_count; ++i) {
 -        TRACE("%s needs to unload %s", si->name, children[i]->name);
 -        soinfo_unload(children[i]);
 +      soinfo* child = nullptr;
 +      while ((child = si->get_children().pop_front()) != nullptr) {
 +        TRACE("%s needs to unload %s", si->name, child->name);
 +        soinfo_unload(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(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);
+       for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) {
+         if (d->d_tag == DT_NEEDED) {
+           const char* library_name = si->strtab + d->d_un.d_val;
+           TRACE("%s needs to unload %s", si->name, library_name);
+           soinfo* needed = find_library(library_name, RTLD_NOLOAD, nullptr);
+           if (needed != nullptr) {
+             soinfo_unload(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);
+           }
          }
-       });
+       }
      }
  
      notify_gdb_of_unload(si);
@@@ -1079,12 -1061,8 +1072,8 @@@ int soinfo::Relocate(ElfW(Rela)* rela, 
      if (type == 0) { // R_*_NONE
        continue;
      }
-     ElfW(Sym)* s = nullptr;
-     soinfo* lsi = nullptr;
      if (sym != 0) {
 -      sym_name = reinterpret_cast<const char*>(strtab + symtab[sym].st_name);
 +      sym_name = get_string(symtab[sym].st_name);
        s = soinfo_do_lookup(this, sym_name, &lsi);
        if (s == nullptr) {
          // We only allow an undefined symbol if this is a weak reference...
          }
        } else {
          // We got a definition.
 -        sym_addr = static_cast<ElfW(Addr)>(s->st_value + lsi->load_bias);
 +        sym_addr = lsi->resolve_symbol_address(s);
        }
        count_relocation(kRelocSymbol);
+     } else {
+       s = nullptr;
      }
  
      switch (type) {
@@@ -1758,15 -1720,15 +1746,15 @@@ ino_t soinfo::get_st_ino() 
    return 0;
  }
  
 -bool soinfo::get_has_ifuncs() {
 +int soinfo::get_rtld_flags() {
    if (has_min_version(1)) {
 -    return has_ifuncs;
 +    return rtld_flags;
    }
  
 -  return false;
 +  return 0;
  }
  
- // This is a return on get_children()/get_parents() if
+ // This is a return on get_children() in case
  // 'this->flags' does not have FLAG_NEW_SOINFO set.
  static soinfo::soinfo_list_t g_empty_list;
  
@@@ -1805,350 -1743,336 +1793,380 @@@ const char* soinfo::get_string(ElfW(Wor
  /* Force any of the closed stdin, stdout and stderr to be associated with
     /dev/null. */
  static int nullify_closed_stdio() {
 -    int dev_null, i, status;
 -    int return_value = 0;
 +  int dev_null, i, status;
 +  int return_value = 0;
  
 -    dev_null = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
 -    if (dev_null < 0) {
 -        DL_ERR("cannot open /dev/null: %s", strerror(errno));
 -        return -1;
 -    }
 -    TRACE("[ Opened /dev/null file-descriptor=%d]", dev_null);
 +  dev_null = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
 +  if (dev_null < 0) {
 +    DL_ERR("cannot open /dev/null: %s", strerror(errno));
 +    return -1;
 +  }
 +  TRACE("[ Opened /dev/null file-descriptor=%d]", dev_null);
  
 -    /* If any of the stdio file descriptors is valid and not associated
 -       with /dev/null, dup /dev/null to it.  */
 -    for (i = 0; i < 3; i++) {
 -        /* If it is /dev/null already, we are done. */
 -        if (i == dev_null) {
 -            continue;
 -        }
 +  /* If any of the stdio file descriptors is valid and not associated
 +     with /dev/null, dup /dev/null to it.  */
 +  for (i = 0; i < 3; i++) {
 +    /* If it is /dev/null already, we are done. */
 +    if (i == dev_null) {
 +      continue;
 +    }
  
 -        TRACE("[ Nullifying stdio file descriptor %d]", i);
 -        status = TEMP_FAILURE_RETRY(fcntl(i, F_GETFL));
 +    TRACE("[ Nullifying stdio file descriptor %d]", i);
 +    status = TEMP_FAILURE_RETRY(fcntl(i, F_GETFL));
  
 -        /* If file is opened, we are good. */
 -        if (status != -1) {
 -            continue;
 -        }
 +    /* If file is opened, we are good. */
 +    if (status != -1) {
 +      continue;
 +    }
  
 -        /* The only error we allow is that the file descriptor does not
 -           exist, in which case we dup /dev/null to it. */
 -        if (errno != EBADF) {
 -            DL_ERR("fcntl failed: %s", strerror(errno));
 -            return_value = -1;
 -            continue;
 -        }
 +    /* The only error we allow is that the file descriptor does not
 +       exist, in which case we dup /dev/null to it. */
 +    if (errno != EBADF) {
 +      DL_ERR("fcntl failed: %s", strerror(errno));
 +      return_value = -1;
 +      continue;
 +    }
  
 -        /* Try dupping /dev/null to this stdio file descriptor and
 -           repeat if there is a signal.  Note that any errors in closing
 -           the stdio descriptor are lost.  */
 -        status = TEMP_FAILURE_RETRY(dup2(dev_null, i));
 -        if (status < 0) {
 -            DL_ERR("dup2 failed: %s", strerror(errno));
 -            return_value = -1;
 -            continue;
 -        }
 +    /* Try dupping /dev/null to this stdio file descriptor and
 +       repeat if there is a signal.  Note that any errors in closing
 +       the stdio descriptor are lost.  */
 +    status = TEMP_FAILURE_RETRY(dup2(dev_null, i));
 +    if (status < 0) {
 +      DL_ERR("dup2 failed: %s", strerror(errno));
 +      return_value = -1;
 +      continue;
      }
 +  }
  
 -    /* If /dev/null is not one of the stdio file descriptors, close it. */
 -    if (dev_null > 2) {
 -        TRACE("[ Closing /dev/null file-descriptor=%d]", dev_null);
 -        status = TEMP_FAILURE_RETRY(close(dev_null));
 -        if (status == -1) {
 -            DL_ERR("close failed: %s", strerror(errno));
 -            return_value = -1;
 -        }
 +  /* If /dev/null is not one of the stdio file descriptors, close it. */
 +  if (dev_null > 2) {
 +    TRACE("[ Closing /dev/null file-descriptor=%d]", dev_null);
 +    status = TEMP_FAILURE_RETRY(close(dev_null));
 +    if (status == -1) {
 +      DL_ERR("close failed: %s", strerror(errno));
 +      return_value = -1;
      }
 +  }
  
 -    return return_value;
 +  return return_value;
  }
  
 -bool soinfo::LinkImage(const android_dlextinfo* extinfo) {
 -    bool relocating_linker = (flags & FLAG_LINKER) != 0;
 +bool soinfo::PrelinkImage() {
 +  /* Extract dynamic section */
 +  ElfW(Word) dynamic_flags = 0;
 +  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;
 +  if (!relocating_linker) {
 +    INFO("[ linking %s ]", name);
 +    DEBUG("si->base = %p si->flags = 0x%08x", reinterpret_cast<void*>(base), flags);
 +  }
  
 -    /* We can't debug anything until the linker is relocated */
 +  if (dynamic == nullptr) {
      if (!relocating_linker) {
 -        INFO("[ linking %s ]", name);
 -        DEBUG("si->base = %p si->flags = 0x%08x", reinterpret_cast<void*>(base), flags);
 +      DL_ERR("missing PT_DYNAMIC in \"%s\"", name);
      }
 -
 -    /* Extract dynamic section */
 -    size_t dynamic_count;
 -    ElfW(Word) dynamic_flags;
 -    phdr_table_get_dynamic_section(phdr, phnum, load_bias, &dynamic,
 -                                   &dynamic_count, &dynamic_flags);
 -    if (dynamic == nullptr) {
 -        if (!relocating_linker) {
 -            DL_ERR("missing PT_DYNAMIC in \"%s\"", name);
 -        }
 -        return false;
 -    } else {
 -        if (!relocating_linker) {
 -            DEBUG("dynamic = %p", dynamic);
 -        }
 +    return false;
 +  } else {
 +    if (!relocating_linker) {
 +      DEBUG("dynamic = %p", dynamic);
      }
 +  }
  
  #if defined(__arm__)
 -    (void) phdr_table_get_arm_exidx(phdr, phnum, load_bias,
 -                                    &ARM_exidx, &ARM_exidx_count);
 +  (void) phdr_table_get_arm_exidx(phdr, phnum, load_bias,
 +                                  &ARM_exidx, &ARM_exidx_count);
  #endif
  
 -    // Extract useful information from dynamic section.
 -    uint32_t needed_count = 0;
 -    for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) {
 -        DEBUG("d = %p, d[0](tag) = %p d[1](val) = %p",
 -              d, reinterpret_cast<void*>(d->d_tag), reinterpret_cast<void*>(d->d_un.d_val));
 -        switch (d->d_tag) {
 -        case DT_HASH:
 -            nbucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0];
 -            nchain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1];
 -            bucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8);
 -            chain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8 + nbucket * 4);
 -            break;
 -        case DT_STRTAB:
 -            strtab = reinterpret_cast<const char*>(load_bias + d->d_un.d_ptr);
 -            break;
 -        case DT_SYMTAB:
 -            symtab = reinterpret_cast<ElfW(Sym)*>(load_bias + d->d_un.d_ptr);
 -            break;
 +  // Extract useful information from dynamic section.
 +  uint32_t needed_count = 0;
 +  for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) {
 +    DEBUG("d = %p, d[0](tag) = %p d[1](val) = %p",
 +          d, reinterpret_cast<void*>(d->d_tag), reinterpret_cast<void*>(d->d_un.d_val));
 +    switch (d->d_tag) {
 +      case DT_SONAME:
 +        // TODO: glibc dynamic linker uses this name for
 +        // initial library lookup; consider doing the same here.
 +        break;
 +      case DT_HASH:
 +        nbucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0];
 +        nchain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1];
 +        bucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8);
 +        chain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8 + nbucket * 4);
 +        break;
 +      case DT_STRTAB:
 +        strtab = reinterpret_cast<const char*>(load_bias + d->d_un.d_ptr);
 +        break;
 +      case DT_STRSZ:
 +        strtab_size = d->d_un.d_val;
 +        break;
 +      case DT_SYMTAB:
 +        symtab = reinterpret_cast<ElfW(Sym)*>(load_bias + d->d_un.d_ptr);
 +        break;
 +      case DT_SYMENT:
 +        if (d->d_un.d_val != sizeof(ElfW(Sym))) {
 +          DL_ERR("invalid DT_SYMENT: %zd", static_cast<size_t>(d->d_un.d_val));
 +          return false;
 +        }
 +        break;
  #if !defined(__LP64__)
 -        case DT_PLTREL:
 -            if (d->d_un.d_val != DT_REL) {
 -                DL_ERR("unsupported DT_RELA in \"%s\"", name);
 -                return false;
 -            }
 -            break;
 +      case DT_PLTREL:
 +        if (d->d_un.d_val != DT_REL) {
 +          DL_ERR("unsupported DT_RELA in \"%s\"", name);
 +          return false;
 +        }
 +        break;
  #endif
 -        case DT_JMPREL:
 +      case DT_JMPREL:
  #if defined(USE_RELA)
 -            plt_rela = reinterpret_cast<ElfW(Rela)*>(load_bias + d->d_un.d_ptr);
 +        plt_rela = reinterpret_cast<ElfW(Rela)*>(load_bias + d->d_un.d_ptr);
  #else
 -            plt_rel = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr);
 +        plt_rel = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr);
  #endif
 -            break;
 -        case DT_PLTRELSZ:
 +        break;
 +      case DT_PLTRELSZ:
  #if defined(USE_RELA)
 -            plt_rela_count = d->d_un.d_val / sizeof(ElfW(Rela));
 +        plt_rela_count = d->d_un.d_val / sizeof(ElfW(Rela));
  #else
 -            plt_rel_count = d->d_un.d_val / sizeof(ElfW(Rel));
 +        plt_rel_count = d->d_un.d_val / sizeof(ElfW(Rel));
  #endif
 -            break;
 +        break;
 +      case DT_PLTGOT:
  #if defined(__mips__)
 -        case DT_PLTGOT:
 -            // Used by mips and mips64.
 -            plt_got = reinterpret_cast<ElfW(Addr)**>(load_bias + d->d_un.d_ptr);
 -            break;
 +        // Used by mips and mips64.
 +        plt_got = reinterpret_cast<ElfW(Addr)**>(load_bias + d->d_un.d_ptr);
  #endif
 -        case DT_DEBUG:
 -            // Set the DT_DEBUG entry to the address of _r_debug for GDB
 -            // if the dynamic table is writable
 +        // Ignore for other platforms... (because RTLD_LAZY is not supported)
 +        break;
 +      case DT_DEBUG:
 +        // Set the DT_DEBUG entry to the address of _r_debug for GDB
 +        // if the dynamic table is writable
  // FIXME: not working currently for N64
  // The flags for the LOAD and DYNAMIC program headers do not agree.
- // The LOAD section containing the dynamic table has been mapped as
+ // The LOAD section containng the dynamic table has been mapped as
  // read-only, but the DYNAMIC header claims it is writable.
  #if !(defined(__mips__) && defined(__LP64__))
 -            if ((dynamic_flags & PF_W) != 0) {
 -                d->d_un.d_val = reinterpret_cast<uintptr_t>(&_r_debug);
 -            }
 -            break;
 +        if ((dynamic_flags & PF_W) != 0) {
 +          d->d_un.d_val = reinterpret_cast<uintptr_t>(&_r_debug);
 +        }
 +        break;
  #endif
  #if defined(USE_RELA)
 -         case DT_RELA:
 -            rela = reinterpret_cast<ElfW(Rela)*>(load_bias + d->d_un.d_ptr);
 -            break;
 -         case DT_RELASZ:
 -            rela_count = d->d_un.d_val / sizeof(ElfW(Rela));
 -            break;
 -        case DT_REL:
 -            DL_ERR("unsupported DT_REL in \"%s\"", name);
 -            return false;
 -        case DT_RELSZ:
 -            DL_ERR("unsupported DT_RELSZ in \"%s\"", name);
 -            return false;
 +      case DT_RELA:
 +        rela = reinterpret_cast<ElfW(Rela)*>(load_bias + d->d_un.d_ptr);
 +        break;
 +      case DT_RELASZ:
 +        rela_count = d->d_un.d_val / sizeof(ElfW(Rela));
 +        break;
 +      case DT_RELAENT:
 +        if (d->d_un.d_val != sizeof(ElfW(Rela))) {
 +          DL_ERR("invalid DT_RELAENT: %zd", static_cast<size_t>(d->d_un.d_val));
 +          return false;
 +        }
 +        break;
 +      case DT_RELACOUNT:
 +        // ignored (see DT_RELCOUNT comments for details)
 +        break;
 +      case DT_REL:
 +        DL_ERR("unsupported DT_REL in \"%s\"", name);
 +        return false;
 +      case DT_RELSZ:
 +        DL_ERR("unsupported DT_RELSZ in \"%s\"", name);
 +        return false;
  #else
 -        case DT_REL:
 -            rel = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr);
 -            break;
 -        case DT_RELSZ:
 -            rel_count = d->d_un.d_val / sizeof(ElfW(Rel));
 -            break;
 -         case DT_RELA:
 -            DL_ERR("unsupported DT_RELA in \"%s\"", name);
 -            return false;
 +      case DT_REL:
 +        rel = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr);
 +        break;
 +      case DT_RELSZ:
 +        rel_count = d->d_un.d_val / sizeof(ElfW(Rel));
 +        break;
 +      case DT_RELENT:
 +        if (d->d_un.d_val != sizeof(ElfW(Rel))) {
 +          DL_ERR("invalid DT_RELENT: %zd", static_cast<size_t>(d->d_un.d_val));
 +          return false;
 +        }
 +        break;
 +      case DT_RELCOUNT:
 +        // "Indicates that all RELATIVE relocations have been concatenated together,
 +        // and specifies the RELATIVE relocation count."
 +        //
 +        // TODO: Spec also mentions that this can be used to optimize relocation process;
 +        // Not currently used by bionic linker - ignored.
 +        break;
 +      case DT_RELA:
 +        DL_ERR("unsupported DT_RELA in \"%s\"", name);
 +        return false;
  #endif
 -        case DT_INIT:
 -            init_func = reinterpret_cast<linker_function_t>(load_bias + d->d_un.d_ptr);
 -            DEBUG("%s constructors (DT_INIT) found at %p", name, init_func);
 -            break;
 -        case DT_FINI:
 -            fini_func = reinterpret_cast<linker_function_t>(load_bias + d->d_un.d_ptr);
 -            DEBUG("%s destructors (DT_FINI) found at %p", name, fini_func);
 -            break;
 -        case DT_INIT_ARRAY:
 -            init_array = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
 -            DEBUG("%s constructors (DT_INIT_ARRAY) found at %p", name, init_array);
 -            break;
 -        case DT_INIT_ARRAYSZ:
 -            init_array_count = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
 -            break;
 -        case DT_FINI_ARRAY:
 -            fini_array = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
 -            DEBUG("%s destructors (DT_FINI_ARRAY) found at %p", name, fini_array);
 -            break;
 -        case DT_FINI_ARRAYSZ:
 -            fini_array_count = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
 -            break;
 -        case DT_PREINIT_ARRAY:
 -            preinit_array = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
 -            DEBUG("%s constructors (DT_PREINIT_ARRAY) found at %p", name, preinit_array);
 -            break;
 -        case DT_PREINIT_ARRAYSZ:
 -            preinit_array_count = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
 -            break;
 -        case DT_TEXTREL:
 +      case DT_INIT:
 +        init_func = reinterpret_cast<linker_function_t>(load_bias + d->d_un.d_ptr);
 +        DEBUG("%s constructors (DT_INIT) found at %p", name, init_func);
 +        break;
 +      case DT_FINI:
 +        fini_func = reinterpret_cast<linker_function_t>(load_bias + d->d_un.d_ptr);
 +        DEBUG("%s destructors (DT_FINI) found at %p", name, fini_func);
 +        break;
 +      case DT_INIT_ARRAY:
 +        init_array = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
 +        DEBUG("%s constructors (DT_INIT_ARRAY) found at %p", name, init_array);
 +        break;
 +      case DT_INIT_ARRAYSZ:
 +        init_array_count = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
 +        break;
 +      case DT_FINI_ARRAY:
 +        fini_array = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
 +        DEBUG("%s destructors (DT_FINI_ARRAY) found at %p", name, fini_array);
 +        break;
 +      case DT_FINI_ARRAYSZ:
 +        fini_array_count = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
 +        break;
 +      case DT_PREINIT_ARRAY:
 +        preinit_array = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
 +        DEBUG("%s constructors (DT_PREINIT_ARRAY) found at %p", name, preinit_array);
 +        break;
 +      case DT_PREINIT_ARRAYSZ:
 +        preinit_array_count = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
 +        break;
 +      case DT_TEXTREL:
  #if defined(__LP64__)
 -            DL_ERR("text relocations (DT_TEXTREL) found in 64-bit ELF file \"%s\"", name);
 -            return false;
 +        DL_ERR("text relocations (DT_TEXTREL) found in 64-bit ELF file \"%s\"", name);
 +        return false;
  #else
 -            has_text_relocations = true;
 -            break;
 +        has_text_relocations = true;
 +        break;
  #endif
 -        case DT_SYMBOLIC:
 -            has_DT_SYMBOLIC = true;
 -            break;
 -        case DT_NEEDED:
 -            ++needed_count;
 -            break;
 -        case DT_FLAGS:
 -            if (d->d_un.d_val & DF_TEXTREL) {
 +      case DT_SYMBOLIC:
 +        has_DT_SYMBOLIC = true;
 +        break;
 +      case DT_NEEDED:
 +        ++needed_count;
 +        break;
 +      case DT_FLAGS:
 +        if (d->d_un.d_val & DF_TEXTREL) {
  #if defined(__LP64__)
 -                DL_ERR("text relocations (DF_TEXTREL) found in 64-bit ELF file \"%s\"", name);
 -                return false;
 +          DL_ERR("text relocations (DF_TEXTREL) found in 64-bit ELF file \"%s\"", name);
 +          return false;
  #else
 -                has_text_relocations = true;
 +          has_text_relocations = true;
  #endif
 -            }
 -            if (d->d_un.d_val & DF_SYMBOLIC) {
 -                has_DT_SYMBOLIC = true;
 -            }
 -            break;
 +        }
 +        if (d->d_un.d_val & DF_SYMBOLIC) {
 +          has_DT_SYMBOLIC = true;
 +        }
 +        break;
 +      case DT_FLAGS_1:
 +        if ((d->d_un.d_val & DF_1_GLOBAL) != 0) {
 +          rtld_flags |= RTLD_GLOBAL;
 +        }
 +        // TODO: Implement other flags
 +
 +        if ((d->d_un.d_val & ~(DF_1_NOW | DF_1_GLOBAL)) != 0) {
 +          DL_WARN("Unsupported flags DT_FLAGS_1=%p", reinterpret_cast<void*>(d->d_un.d_val));
 +        }
 +        break;
  #if defined(__mips__)
 -        case DT_STRSZ:
 -        case DT_SYMENT:
 -        case DT_RELENT:
 -             break;
 -        case DT_MIPS_RLD_MAP:
 -            // Set the DT_MIPS_RLD_MAP entry to the address of _r_debug for GDB.
 -            {
 -              r_debug** dp = reinterpret_cast<r_debug**>(load_bias + d->d_un.d_ptr);
 -              *dp = &_r_debug;
 -            }
 -            break;
 -        case DT_MIPS_RLD_VERSION:
 -        case DT_MIPS_FLAGS:
 -        case DT_MIPS_BASE_ADDRESS:
 -        case DT_MIPS_UNREFEXTNO:
 -            break;
 +      case DT_MIPS_RLD_MAP:
 +        // Set the DT_MIPS_RLD_MAP entry to the address of _r_debug for GDB.
 +        {
 +          r_debug** dp = reinterpret_cast<r_debug**>(load_bias + d->d_un.d_ptr);
 +          *dp = &_r_debug;
 +        }
 +        break;
 +      case DT_MIPS_RLD_VERSION:
 +      case DT_MIPS_FLAGS:
 +      case DT_MIPS_BASE_ADDRESS:
 +      case DT_MIPS_UNREFEXTNO:
 +        break;
  
 -        case DT_MIPS_SYMTABNO:
 -            mips_symtabno = d->d_un.d_val;
 -            break;
 +      case DT_MIPS_SYMTABNO:
 +        mips_symtabno = d->d_un.d_val;
 +        break;
  
 -        case DT_MIPS_LOCAL_GOTNO:
 -            mips_local_gotno = d->d_un.d_val;
 -            break;
 +      case DT_MIPS_LOCAL_GOTNO:
 +        mips_local_gotno = d->d_un.d_val;
 +        break;
  
 -        case DT_MIPS_GOTSYM:
 -            mips_gotsym = d->d_un.d_val;
 -            break;
 +      case DT_MIPS_GOTSYM:
 +        mips_gotsym = d->d_un.d_val;
 +        break;
  #endif
  
 -        default:
 -            DEBUG("Unused DT entry: type %p arg %p",
 -                  reinterpret_cast<void*>(d->d_tag), reinterpret_cast<void*>(d->d_un.d_val));
 -            break;
 +      default:
 +        if (!relocating_linker) {
 +          DL_WARN("%s: unused DT entry: type %p arg %p", name,
 +              reinterpret_cast<void*>(d->d_tag), reinterpret_cast<void*>(d->d_un.d_val));
          }
 +        break;
      }
 +  }
  
 -    DEBUG("si->base = %p, si->strtab = %p, si->symtab = %p",
 -          reinterpret_cast<void*>(base), strtab, symtab);
 +  DEBUG("si->base = %p, si->strtab = %p, si->symtab = %p",
 +        reinterpret_cast<void*>(base), strtab, symtab);
  
 -    // Sanity checks.
 -    if (relocating_linker && needed_count != 0) {
 -        DL_ERR("linker cannot have DT_NEEDED dependencies on other libraries");
 -        return false;
 -    }
 -    if (nbucket == 0) {
 -        DL_ERR("empty/missing DT_HASH in \"%s\" (built with --hash-style=gnu?)", name);
 -        return false;
 -    }
 -    if (strtab == 0) {
 -        DL_ERR("empty/missing DT_STRTAB in \"%s\"", name);
 -        return false;
 -    }
 -    if (symtab == 0) {
 -        DL_ERR("empty/missing DT_SYMTAB in \"%s\"", name);
 -        return false;
 -    }
 +  // Sanity checks.
 +  if (relocating_linker && needed_count != 0) {
 +    DL_ERR("linker cannot have DT_NEEDED dependencies on other libraries");
 +    return false;
 +  }
 +  if (nbucket == 0) {
 +    DL_ERR("empty/missing DT_HASH in \"%s\" (built with --hash-style=gnu?)", name);
 +    return false;
 +  }
 +  if (strtab == 0) {
 +    DL_ERR("empty/missing DT_STRTAB in \"%s\"", name);
 +    return false;
 +  }
 +  if (symtab == 0) {
 +    DL_ERR("empty/missing DT_SYMTAB in \"%s\"", name);
 +    return false;
 +  }
 +  return true;
 +}
  
- bool soinfo::LinkImage(const android_dlextinfo* extinfo) {
+     // If this is the main executable, then load all of the libraries from LD_PRELOAD now.
+     if (flags & FLAG_EXE) {
+         memset(g_ld_preloads, 0, sizeof(g_ld_preloads));
+         size_t preload_count = 0;
+         for (size_t i = 0; g_ld_preload_names[i] != nullptr; i++) {
+             soinfo* lsi = find_library(g_ld_preload_names[i], 0, nullptr);
+             if (lsi != nullptr) {
+                 g_ld_preloads[preload_count++] = lsi;
+             } else {
+                 // As with glibc, failure to load an LD_PRELOAD library is just a warning.
+                 DL_WARN("could not load library \"%s\" from LD_PRELOAD for \"%s\"; caused by %s",
+                         g_ld_preload_names[i], name, linker_get_error_buffer());
+             }
+         }
+     }
+     for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) {
+         if (d->d_tag == DT_NEEDED) {
+             const char* library_name = strtab + d->d_un.d_val;
+             DEBUG("%s needs %s", name, library_name);
+             soinfo* lsi = find_library(library_name, 0, nullptr);
+             if (lsi == nullptr) {
+                 strlcpy(tmp_err_buf, linker_get_error_buffer(), sizeof(tmp_err_buf));
+                 DL_ERR("could not load library \"%s\" needed by \"%s\"; caused by %s",
+                        library_name, name, tmp_err_buf);
+                 return false;
+             }
+             add_child(lsi);
+         }
+     }
  
  #if !defined(__LP64__)
 -    if (has_text_relocations) {
 -        // Make segments writable to allow text relocations to work properly. We will later call
 -        // phdr_table_protect_segments() after all of them are applied and all constructors are run.
 -        DL_WARN("%s has text relocations. This is wasting memory and prevents "
 -                "security hardening. Please fix.", name);
 -        if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) {
 -            DL_ERR("can't unprotect loadable segments for \"%s\": %s",
 -                   name, strerror(errno));
 -            return false;
 -        }
 +  if (has_text_relocations) {
 +    // Make segments writable to allow text relocations to work properly. We will later call
 +    // phdr_table_protect_segments() after all of them are applied and all constructors are run.
 +    DL_WARN("%s has text relocations. This is wasting memory and prevents "
 +            "security hardening. Please fix.", name);
 +    if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) {
 +      DL_ERR("can't unprotect loadable segments for \"%s\": %s",
 +             name, strerror(errno));
 +      return false;
      }
 +  }
  #endif
  
  #if defined(USE_RELA)
diff --cc linker/linker.h
@@@ -219,18 -216,9 +218,17 @@@ struct soinfo 
    dev_t get_st_dev();
    bool get_has_ifuncs();
  
 +
 +
 +  int get_rtld_flags();
 +
    soinfo_list_t& get_children();
-   soinfo_list_t& get_parents();
  
 -  bool inline has_min_version(uint32_t min_version) {
 +  ElfW(Addr) resolve_symbol_address(ElfW(Sym)* s);
 +
 +  const char* get_string(ElfW(Word) index) const;
 +
 +  bool inline has_min_version(uint32_t min_version) const {
      return (flags & FLAG_NEW_SOINFO) != 0 && version >= min_version;
    }
   private:
@@@ -707,17 -708,28 +707,30 @@@ int phdr_table_get_arm_exidx(const ElfW
   *   void
   */
  void phdr_table_get_dynamic_section(const ElfW(Phdr)* phdr_table, size_t phdr_count,
 -                                    ElfW(Addr) load_bias,
 -                                    ElfW(Dyn)** dynamic, size_t* dynamic_count, ElfW(Word)* dynamic_flags) {
 -  const ElfW(Phdr)* phdr = phdr_table;
 -  const ElfW(Phdr)* phdr_limit = phdr + phdr_count;
 -
 -  for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
 -    if (phdr->p_type != PT_DYNAMIC) {
 -      continue;
 +                                    ElfW(Addr) load_bias, ElfW(Dyn)** dynamic,
 +                                    ElfW(Word)* dynamic_flags) {
 +  *dynamic = nullptr;
 +  for (const ElfW(Phdr)* phdr = phdr_table, *phdr_limit = phdr + phdr_count; phdr < phdr_limit; phdr++) {
 +    if (phdr->p_type == PT_DYNAMIC) {
 +      *dynamic = reinterpret_cast<ElfW(Dyn)*>(load_bias + phdr->p_vaddr);
 +      if (dynamic_flags) {
 +        *dynamic_flags = phdr->p_flags;
 +      }
 +      return;
      }
+     *dynamic = reinterpret_cast<ElfW(Dyn)*>(load_bias + phdr->p_vaddr);
+     if (dynamic_count) {
+       *dynamic_count = (unsigned)(phdr->p_memsz / 8);
+     }
+     if (dynamic_flags) {
+       *dynamic_flags = phdr->p_flags;
+     }
+     return;
+   }
+   *dynamic = nullptr;
+   if (dynamic_count) {
+     *dynamic_count = 0;
    }
  }
  
Simple merge
@@@ -101,314 -97,11 +101,160 @@@ libtest_simple_src_files := 
      dlopen_testlib_simple.cpp
  
  module := libtest_simple
 -build_type := target
 -build_target := SHARED_LIBRARY
 -include $(TEST_PATH)/Android.build.mk
 +include $(LOCAL_PATH)/Android.build.testlib.mk
 +
 +# -----------------------------------------------------------------------------
 +# Libraries used by dlfcn tests to verify correct load order:
 +# libtest_check_order_2_right.so
 +# -----------------------------------------------------------------------------
 +libtest_check_order_2_right_src_files := \
 +    dlopen_testlib_answer.cpp
 +
 +libtest_check_order_2_right_cflags := -D__ANSWER=42
 +module := libtest_check_order_2_right
 +include $(LOCAL_PATH)/Android.build.testlib.mk
 +
 +# -----------------------------------------------------------------------------
 +# libtest_check_order_a.so
 +# -----------------------------------------------------------------------------
 +libtest_check_order_a_src_files := \
 +    dlopen_testlib_answer.cpp
 +
 +libtest_check_order_a_cflags := -D__ANSWER=1
 +module := libtest_check_order_a
 +include $(LOCAL_PATH)/Android.build.testlib.mk
 +
 +# -----------------------------------------------------------------------------
 +# libtest_check_order_b.so
 +# -----------------------------------------------------------------------------
 +libtest_check_order_b_src_files := \
 +    dlopen_testlib_answer.cpp
 +
 +libtest_check_order_b_cflags := -D__ANSWER=2 -D__ANSWER2=43
 +module := libtest_check_order_b
 +include $(LOCAL_PATH)/Android.build.testlib.mk
 +
 +# -----------------------------------------------------------------------------
 +# libtest_check_order_c.so
 +# -----------------------------------------------------------------------------
 +libtest_check_order_3_c_src_files := \
 +    dlopen_testlib_answer.cpp
 +
 +libtest_check_order_3_c_cflags := -D__ANSWER=3
 +module := libtest_check_order_3_c
 +include $(LOCAL_PATH)/Android.build.testlib.mk
 +
 +# -----------------------------------------------------------------------------
 +# libtest_check_order_d.so
 +# -----------------------------------------------------------------------------
 +libtest_check_order_d_src_files := \
 +   dlopen_testlib_answer.cpp
 +
 +libtest_check_order_d_shared_libraries := libtest_check_order_b
 +libtest_check_order_d_cflags := -D__ANSWER=4 -D__ANSWER2=4
 +module := libtest_check_order_d
 +include $(LOCAL_PATH)/Android.build.testlib.mk
 +
 +# -----------------------------------------------------------------------------
 +# libtest_check_order_left.so
 +# -----------------------------------------------------------------------------
 +libtest_check_order_1_left_src_files := \
 +    empty.cpp
 +
 +libtest_check_order_1_left_shared_libraries := libtest_check_order_a libtest_check_order_b
 +
 +module := libtest_check_order_1_left
 +include $(LOCAL_PATH)/Android.build.testlib.mk
 +
 +# -----------------------------------------------------------------------------
 +# libtest_check_order.so
 +# -----------------------------------------------------------------------------
 +libtest_check_order_src_files := \
 +    empty.cpp
 +
 +libtest_check_order_shared_libraries := libtest_check_order_1_left \
 +  libtest_check_order_2_right libtest_check_order_3_c
 +
 +module := libtest_check_order
 +include $(LOCAL_PATH)/Android.build.testlib.mk
 +
 +# -----------------------------------------------------------------------------
 +# Library with dependency loop used by dlfcn tests
 +#
 +# libtest_with_dependency_loop -> a -> b -> c -> a
 +# -----------------------------------------------------------------------------
 +libtest_with_dependency_loop_src_files := dlopen_testlib_invalid.cpp
 +
 +libtest_with_dependency_loop_shared_libraries := \
 +    libtest_with_dependency_loop_a
 +
 +module := libtest_with_dependency_loop
 +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_shared_libraries := \
 +    libtest_with_dependency_loop_b_tmp
 +
 +module := libtest_with_dependency_loop_a
 +include $(LOCAL_PATH)/Android.build.testlib.mk
 +
 +# -----------------------------------------------------------------------------
 +# libtest_with_dependency_loop_b.so
 +#
 +# 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_ldflags := -Wl,-soname=libtest_with_dependency_loop_b.so
 +
 +module := libtest_with_dependency_loop_b_tmp
 +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_shared_libraries := libtest_with_dependency_loop_c
 +
 +module := libtest_with_dependency_loop_b
 +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_shared_libraries := \
 +    libtest_with_dependency_loop_a
 +
 +module := libtest_with_dependency_loop_c
 +include $(LOCAL_PATH)/Android.build.testlib.mk
 +
 +# -----------------------------------------------------------------------------
 +# libtest_relo_check_dt_needed_order.so
 +# |
 +# +-> libtest_relo_check_dt_needed_order_1.so
 +# |
 +# +-> libtest_relo_check_dt_needed_order_2.so
 +# -----------------------------------------------------------------------------
 +libtest_relo_check_dt_needed_order_shared_libraries := \
 +    libtest_relo_check_dt_needed_order_1 libtest_relo_check_dt_needed_order_2
 +
 +libtest_relo_check_dt_needed_order_src_files := dlopen_testlib_relo_check_dt_needed_order.cpp
 +libtest_relo_check_dt_needed_order_1_src_files := dlopen_testlib_relo_check_dt_needed_order_1.cpp
 +libtest_relo_check_dt_needed_order_2_src_files := dlopen_testlib_relo_check_dt_needed_order_2.cpp
 +
 +module := libtest_relo_check_dt_needed_order
 +include $(LOCAL_PATH)/Android.build.testlib.mk
 +module := libtest_relo_check_dt_needed_order_1
 +include $(LOCAL_PATH)/Android.build.testlib.mk
 +module := libtest_relo_check_dt_needed_order_2
 +include $(LOCAL_PATH)/Android.build.testlib.mk
  
  # -----------------------------------------------------------------------------
- # Libraries used by dlfcn tests to verify correct load order:
- # libtest_check_order_2_right.so
- # -----------------------------------------------------------------------------
- libtest_check_order_2_right_src_files := \
-     dlopen_testlib_answer.cpp
- libtest_check_order_2_right_cflags := -D__ANSWER=42
- module := libtest_check_order_2_right
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
- # -----------------------------------------------------------------------------
- # libtest_check_order_a.so
- # -----------------------------------------------------------------------------
- libtest_check_order_a_src_files := \
-     dlopen_testlib_answer.cpp
- libtest_check_order_a_cflags := -D__ANSWER=1
- module := libtest_check_order_a
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
- # -----------------------------------------------------------------------------
- # libtest_check_order_b.so
- # -----------------------------------------------------------------------------
- libtest_check_order_b_src_files := \
-     dlopen_testlib_answer.cpp
- libtest_check_order_b_cflags := -D__ANSWER=2 -D__ANSWER2=43
- module := libtest_check_order_b
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
- # -----------------------------------------------------------------------------
- # libtest_check_order_c.so
- # -----------------------------------------------------------------------------
- libtest_check_order_3_c_src_files := \
-     dlopen_testlib_answer.cpp
- libtest_check_order_3_c_cflags := -D__ANSWER=3
- module := libtest_check_order_3_c
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
- # -----------------------------------------------------------------------------
- # libtest_check_order_d.so
- # -----------------------------------------------------------------------------
- libtest_check_order_d_src_files := \
-    dlopen_testlib_answer.cpp
- libtest_check_order_d_shared_libraries := libtest_check_order_b
- libtest_check_order_d_cflags := -D__ANSWER=4 -D__ANSWER2=4
- module := libtest_check_order_d
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
- # -----------------------------------------------------------------------------
- # libtest_check_order_left.so
- # -----------------------------------------------------------------------------
- libtest_check_order_1_left_src_files := \
-     empty.cpp
- libtest_check_order_1_left_shared_libraries := libtest_check_order_a libtest_check_order_b
- module := libtest_check_order_1_left
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
- # -----------------------------------------------------------------------------
- # libtest_check_order.so
- # -----------------------------------------------------------------------------
- libtest_check_order_src_files := \
-     empty.cpp
- libtest_check_order_shared_libraries := libtest_check_order_1_left \
-   libtest_check_order_2_right libtest_check_order_3_c
- module := libtest_check_order
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
- # -----------------------------------------------------------------------------
- # Library with dependency loop used by dlfcn tests
- #
- # libtest_with_dependency_loop -> a -> b -> c -> a
- # -----------------------------------------------------------------------------
- libtest_with_dependency_loop_src_files := empty.cpp
- libtest_with_dependency_loop_shared_libraries := \
-     libtest_with_dependency_loop_a
- module := libtest_with_dependency_loop
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
- # -----------------------------------------------------------------------------
- # libtest_with_dependency_loop_a.so
- # -----------------------------------------------------------------------------
- libtest_with_dependency_loop_a_src_files := empty.cpp
- libtest_with_dependency_loop_a_shared_libraries := \
-     libtest_with_dependency_loop_b_tmp
- module := libtest_with_dependency_loop_a
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
- # -----------------------------------------------------------------------------
- # libtest_with_dependency_loop_b.so
- #
- # this is temporary placeholder - will be removed
- # -----------------------------------------------------------------------------
- libtest_with_dependency_loop_b_tmp_src_files := empty.cpp
- libtest_with_dependency_loop_b_tmp_ldflags := -Wl,-soname=libtest_with_dependency_loop_b.so
- module := libtest_with_dependency_loop_b_tmp
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
- # -----------------------------------------------------------------------------
- # libtest_with_dependency_loop_b.so
- # -----------------------------------------------------------------------------
- libtest_with_dependency_loop_b_src_files := empty.cpp
- libtest_with_dependency_loop_b_shared_libraries := libtest_with_dependency_loop_c
- module := libtest_with_dependency_loop_b
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
- # -----------------------------------------------------------------------------
- # libtest_with_dependency_loop_c.so
- # -----------------------------------------------------------------------------
- libtest_with_dependency_loop_c_src_files := empty.cpp
- libtest_with_dependency_loop_c_shared_libraries := \
-     libtest_with_dependency_loop_a
- module := libtest_with_dependency_loop_c
- build_type := target
- build_target := SHARED_LIBRARY
- include $(TEST_PATH)/Android.build.mk
- # -----------------------------------------------------------------------------
  # libtest_relo_check_dt_needed_order.so
  # |
  # +-> libtest_relo_check_dt_needed_order_1.so