linker: the global group is added to all built-in namespaces

With ld.config.txt, we now have multiple built-in namespaces other than
the default namespace. Libs (and their dependents) listed in LD_PRELOAD
must be visible to those additional namespaces as well.

This also adds a debugging only feature: path to the linker config file
can be customized via LD_CONFIG_FILE environment variable. This works
only for debuggable builds.

Bug: 38114603
Bug: 62815515
Test: 1. ./external/compiler-rt/lib/asan/scripts/asan_device_setup --lib
prebuilts/clang/host/linux-x86/clang-stable/lib64/clang/5.0/lib/linux
2. enable talkback shortcut
3. in the home screen, hold vol-up/down together
4. device does not reboots and talkback shortcut is toggled
Test: bionic-unit-tests and linker-unit-tests successful

Change-Id: I9a03591053f4a9caea82f0dcb23e7a3d324bb9bd
diff --git a/linker/Android.bp b/linker/Android.bp
index efd91ac..fda7eb5 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -101,6 +101,12 @@
         "-Werror",
     ],
 
+    product_variables: {
+        debuggable: {
+            cppflags: ["-DUSE_LD_CONFIG_FILE"],
+        },
+    },
+
     cppflags: ["-Wold-style-cast"],
 
     // we are going to link libc++_static manually because
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 8e7a141..a212624 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -558,9 +558,10 @@
 
   static LoadTask* create(const char* name,
                           soinfo* needed_by,
+                          android_namespace_t* start_from,
                           std::unordered_map<const soinfo*, ElfReader>* readers_map) {
     LoadTask* ptr = TypeBasedAllocator<LoadTask>::alloc();
-    return new (ptr) LoadTask(name, needed_by, readers_map);
+    return new (ptr) LoadTask(name, needed_by, start_from, readers_map);
   }
 
   const char* get_name() const {
@@ -612,6 +613,11 @@
     is_dt_needed_ = is_dt_needed;
   }
 
+  // returns the namespace from where we need to start loading this.
+  const android_namespace_t* get_start_from() const {
+    return start_from_;
+  }
+
   const ElfReader& get_elf_reader() const {
     CHECK(si_ != nullptr);
     return (*elf_readers_map_)[si_];
@@ -650,10 +656,11 @@
  private:
   LoadTask(const char* name,
            soinfo* needed_by,
+           android_namespace_t* start_from,
            std::unordered_map<const soinfo*, ElfReader>* readers_map)
     : name_(name), needed_by_(needed_by), si_(nullptr),
       fd_(-1), close_fd_(false), file_offset_(0), elf_readers_map_(readers_map),
-      is_dt_needed_(false) {}
+      is_dt_needed_(false), start_from_(start_from) {}
 
   ~LoadTask() {
     if (fd_ != -1 && close_fd_) {
@@ -672,6 +679,7 @@
   // TODO(dimitry): needed by workaround for http://b/26394120 (the grey-list)
   bool is_dt_needed_;
   // END OF WORKAROUND
+  const android_namespace_t* const start_from_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(LoadTask);
 };
@@ -1041,7 +1049,7 @@
                         ZipArchiveCache* zip_archive_cache,
                         const char* name, soinfo *needed_by,
                         off64_t* file_offset, std::string* realpath) {
-  TRACE("[ opening %s ]", name);
+  TRACE("[ opening %s at namespace %s]", name, ns->get_name());
 
   // If the name contains a slash, we should attempt to open it directly and not search the paths.
   if (strchr(name, '/') != nullptr) {
@@ -1273,7 +1281,7 @@
   }
 
   for_each_dt_needed(task->get_elf_reader(), [&](const char* name) {
-    load_tasks->push_back(LoadTask::create(name, si, task->get_readers_map()));
+    load_tasks->push_back(LoadTask::create(name, si, ns, task->get_readers_map()));
   });
 
   return true;
@@ -1368,8 +1376,7 @@
 }
 
 static bool find_library_in_linked_namespace(const android_namespace_link_t& namespace_link,
-                                             LoadTask* task,
-                                             int rtld_flags) {
+                                             LoadTask* task) {
   android_namespace_t* ns = namespace_link.linked_namespace();
 
   soinfo* candidate;
@@ -1394,29 +1401,10 @@
     return true;
   }
 
-  // try to load the library - once namespace boundary is crossed
-  // we need to load a library within separate load_group
-  // to avoid using symbols from foreign namespace while.
-  //
-  // All symbols during relocation should be resolved within a
-  // namespace to preserve library locality to a namespace.
-  const char* name = task->get_name();
-  if (find_libraries(ns,
-                     task->get_needed_by(),
-                     &name,
-                     1,
-                     &candidate,
-                     nullptr /* ld_preloads */,
-                     0 /* ld_preload_count*/,
-                     rtld_flags,
-                     nullptr /* extinfo*/,
-                     false /* add_as_children */,
-                     false /* search_linked_namespaces */)) {
-    task->set_soinfo(candidate);
-    return true;
-  }
-
-  return false;
+  // returning true with empty soinfo means that the library is okay to be
+  // loaded in the namespace buy has not yet been loaded there before.
+  task->set_soinfo(nullptr);
+  return true;
 }
 
 static bool find_library_internal(android_namespace_t* ns,
@@ -1445,9 +1433,24 @@
     // if a library was not found - look into linked namespaces
     for (auto& linked_namespace : ns->linked_namespaces()) {
       if (find_library_in_linked_namespace(linked_namespace,
-                                           task,
-                                           rtld_flags)) {
-        return true;
+                                           task)) {
+        if (task->get_soinfo() == nullptr) {
+          // try to load the library - once namespace boundary is crossed
+          // we need to load a library within separate load_group
+          // to avoid using symbols from foreign namespace while.
+          //
+          // However, actual linking is deferred until when the global group
+          // is fully identified and is applied to all namespaces.
+          // Otherwise, the libs in the linked namespace won't get symbols from
+          // the global group.
+          if (load_library(linked_namespace.linked_namespace(), task, zip_archive_cache, load_tasks, rtld_flags, false)) {
+            return true;
+          }
+          // lib was not found in the namespace. Try next linked namespace.
+        } else {
+          // lib is already loaded
+          return true;
+        }
       }
     }
   }
@@ -1458,44 +1461,6 @@
 static void soinfo_unload(soinfo* si);
 static void soinfo_unload(soinfo* soinfos[], size_t count);
 
-// 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_list_t make_global_group(android_namespace_t* ns) {
-  soinfo_list_t global_group;
-  ns->soinfo_list().for_each([&](soinfo* si) {
-    if ((si->get_dt_flags_1() & DF_1_GLOBAL) != 0) {
-      global_group.push_back(si);
-    }
-  });
-
-  return global_group;
-}
-
-// This function provides a list of libraries to be shared
-// by the namespace. For the default namespace this is the global
-// group (see make_global_group). For all others this is a group
-// of RTLD_GLOBAL libraries (which includes the global group from
-// the default namespace).
-static soinfo_list_t get_shared_group(android_namespace_t* ns) {
-  if (ns == &g_default_namespace) {
-    return make_global_group(ns);
-  }
-
-  soinfo_list_t shared_group;
-  ns->soinfo_list().for_each([&](soinfo* si) {
-    if ((si->get_rtld_flags() & RTLD_GLOBAL) != 0) {
-      shared_group.push_back(si);
-    }
-  });
-
-  return shared_group;
-}
-
 static void shuffle(std::vector<LoadTask*>* v) {
   for (size_t i = 0, size = v->size(); i < size; ++i) {
     size_t n = size - i;
@@ -1518,19 +1483,17 @@
                     int rtld_flags,
                     const android_dlextinfo* extinfo,
                     bool add_as_children,
-                    bool search_linked_namespaces) {
+                    bool search_linked_namespaces,
+                    std::unordered_map<const soinfo*, ElfReader>& readers_map,
+                    std::vector<android_namespace_t*>* namespaces) {
   // Step 0: prepare.
   LoadTaskList load_tasks;
-  std::unordered_map<const soinfo*, ElfReader> readers_map;
 
   for (size_t i = 0; i < library_names_count; ++i) {
     const char* name = library_names[i];
-    load_tasks.push_back(LoadTask::create(name, start_with, &readers_map));
+    load_tasks.push_back(LoadTask::create(name, start_with, ns, &readers_map));
   }
 
-  // Construct global_group.
-  soinfo_list_t global_group = make_global_group(ns);
-
   // 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
@@ -1570,7 +1533,12 @@
     task->set_extinfo(is_dt_needed ? nullptr : extinfo);
     task->set_dt_needed(is_dt_needed);
 
-    if (!find_library_internal(ns,
+    // try to find the load.
+    // Note: start from the namespace that is stored in the LoadTask. This namespace
+    // is different from the current namespace when the LoadTask is for a transitive
+    // dependency and the lib that created the LoadTask is not found in the
+    // current namespace but in one of the linked namespace.
+    if (!find_library_internal(const_cast<android_namespace_t*>(task->get_start_from()),
                                task,
                                &zip_archive_cache,
                                &load_tasks,
@@ -1629,18 +1597,61 @@
     }
   }
 
-  // Step 4: 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.
+  // Step 4: Construct the global group. Note: DF_1_GLOBAL bit of a library is
+  // determined at step 3.
+
+  // Step 4-1: DF_1_GLOBAL bit is force set for LD_PRELOADed libs because they
+  // must be added to the global group
   if (ld_preloads != nullptr) {
     for (auto&& si : *ld_preloads) {
       si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
     }
   }
 
+  // Step 4-2: Gather all DF_1_GLOBAL libs which were newly loaded during this
+  // run. These will be the new member of the global group
+  soinfo_list_t new_global_group_members;
+  for (auto&& task : load_tasks) {
+    soinfo* si = task->get_soinfo();
+    if (!si->is_linked() && (si->get_dt_flags_1() & DF_1_GLOBAL) != 0) {
+      new_global_group_members.push_back(si);
+    }
+  }
 
-  // Step 5: link libraries.
+  // Step 4-3: Add the new global group members to all the linked namespaces
+  for (auto si : new_global_group_members) {
+    for (auto linked_ns : *namespaces) {
+      if (si->get_primary_namespace() != linked_ns) {
+        linked_ns->add_soinfo(si);
+        si->add_secondary_namespace(linked_ns);
+      }
+    }
+  }
+
+  // Step 5: link libraries that are not destined to this namespace.
+  // Do this by recursively calling find_libraries on the namespace where the lib
+  // was found during Step 1.
+  for (auto&& task : load_tasks) {
+    soinfo* si = task->get_soinfo();
+    if (si->get_primary_namespace() != ns) {
+      const char* name = task->get_name();
+      if (find_libraries(si->get_primary_namespace(), task->get_needed_by(), &name, 1,
+                         nullptr /* soinfos */, nullptr /* ld_preloads */, 0 /* ld_preload_count */,
+                         rtld_flags, nullptr /* extinfo */, false /* add_as_children */,
+                         false /* search_linked_namespaces */, readers_map, namespaces)) {
+        // If this lib is directly needed by one of the libs in this namespace,
+        // then increment the count
+        soinfo* needed_by = task->get_needed_by();
+        if (needed_by != nullptr && needed_by->get_primary_namespace() == ns && si->is_linked()) {
+          si->increment_ref_count();
+        }
+      } else {
+        return false;
+      }
+    }
+  }
+
+  // Step 6: link libraries in this namespace
   soinfo_list_t local_group;
   walk_dependencies_tree(
       (start_with != nullptr && add_as_children) ? &start_with : soinfos,
@@ -1654,6 +1665,7 @@
     }
   });
 
+  soinfo_list_t global_group = ns->get_global_group();
   bool linked = local_group.visit([&](soinfo* si) {
     if (!si->is_linked()) {
       if (!si->link_image(global_group, local_group, extinfo) ||
@@ -1684,6 +1696,9 @@
                             soinfo* needed_by) {
   soinfo* si;
 
+  // readers_map is shared across recursive calls to find_libraries.
+  // However, the map is not shared across different threads.
+  std::unordered_map<const soinfo*, ElfReader> readers_map;
   if (name == nullptr) {
     si = solist_get_somain();
   } else if (!find_libraries(ns,
@@ -1696,7 +1711,8 @@
                              rtld_flags,
                              extinfo,
                              false /* add_as_children */,
-                             true /* search_linked_namespaces */)) {
+                             true /* search_linked_namespaces */,
+                             readers_map)) {
     return nullptr;
   }
 
@@ -2208,7 +2224,7 @@
     }
   } else {
     // If not shared - copy only the shared group
-    add_soinfos_to_namespace(get_shared_group(parent_namespace), ns);
+    add_soinfos_to_namespace(parent_namespace->get_shared_group(), ns);
   }
 
   ns->set_ld_library_paths(std::move(ld_library_paths));
@@ -3413,7 +3429,7 @@
   return true;
 }
 
-static void init_default_namespace_no_config(bool is_asan) {
+static std::vector<android_namespace_t*> init_default_namespace_no_config(bool is_asan) {
   g_default_namespace.set_isolated(false);
   auto default_ld_paths = is_asan ? kAsanDefaultLdPaths : kDefaultLdPaths;
 
@@ -3428,9 +3444,13 @@
   }
 
   g_default_namespace.set_default_library_paths(std::move(ld_default_paths));
+
+  std::vector<android_namespace_t*> namespaces;
+  namespaces.push_back(&g_default_namespace);
+  return namespaces;
 }
 
-void init_default_namespace(const char* executable_path) {
+std::vector<android_namespace_t*> init_default_namespaces(const char* executable_path) {
   g_default_namespace.set_name("(default)");
 
   soinfo* somain = solist_get_somain();
@@ -3447,14 +3467,24 @@
 
   std::string error_msg;
 
-  if (!Config::read_binary_config(kLdConfigFilePath,
+  const char* config_file = kLdConfigFilePath;
+#ifdef USE_LD_CONFIG_FILE
+  // This is a debugging/testing only feature. Must not be available on
+  // production builds.
+  const char* ld_config_file = getenv("LD_CONFIG_FILE");
+  if (ld_config_file != nullptr && file_exists(ld_config_file)) {
+    config_file = ld_config_file;
+  }
+#endif
+
+  if (!Config::read_binary_config(config_file,
                                   executable_path,
                                   g_is_asan,
                                   &config,
                                   &error_msg)) {
     if (!error_msg.empty()) {
       DL_WARN("error reading config file \"%s\" for \"%s\" (will use default configuration): %s",
-              kLdConfigFilePath,
+              config_file,
               executable_path,
               error_msg.c_str());
     }
@@ -3462,8 +3492,7 @@
   }
 
   if (config == nullptr) {
-    init_default_namespace_no_config(g_is_asan);
-    return;
+    return init_default_namespace_no_config(g_is_asan);
   }
 
   const auto& namespace_configs = config->namespace_configs();
@@ -3514,10 +3543,17 @@
   soinfo* ld_android_so = solist_get_head();
   for (auto it : namespaces) {
     it.second->add_soinfo(ld_android_so);
-    // TODO (dimitry): somain and ld_preloads should probably be added to all of these namespaces too?
+    // somain and ld_preloads are added to these namespaces after LD_PRELOAD libs are linked
   }
 
   set_application_target_sdk_version(config->target_sdk_version());
+
+  std::vector<android_namespace_t*> created_namespaces;
+  created_namespaces.reserve(namespaces.size());
+  for (auto kv : namespaces) {
+    created_namespaces.push_back(kv.second);
+  }
+  return created_namespaces;
 }
 
 // This function finds a namespace exported in ld.config.txt by its name.
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index bcb2281..54593e4 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -341,12 +341,19 @@
 
   somain = si;
 
-  init_default_namespace(executable_path);
+  std::vector<android_namespace_t*> namespaces = init_default_namespaces(executable_path);
 
   if (!si->prelink_image()) __linker_cannot_link(g_argv[0]);
 
   // add somain to global group
   si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
+  // ... and add it to all other linked namespaces
+  for (auto linked_ns : namespaces) {
+    if (linked_ns != &g_default_namespace) {
+      linked_ns->add_soinfo(somain);
+      somain->add_secondary_namespace(linked_ns);
+    }
+  }
 
   // Load ld_preloads and dependencies.
   std::vector<const char*> needed_library_name_list;
@@ -364,6 +371,9 @@
   const char** needed_library_names = &needed_library_name_list[0];
   size_t needed_libraries_count = needed_library_name_list.size();
 
+  // readers_map is shared across recursive calls to find_libraries so that we
+  // don't need to re-load elf headers.
+  std::unordered_map<const soinfo*, ElfReader> readers_map;
   if (needed_libraries_count > 0 &&
       !find_libraries(&g_default_namespace,
                       si,
@@ -375,7 +385,9 @@
                       RTLD_GLOBAL,
                       nullptr,
                       true /* add_as_children */,
-                      true /* search_linked_namespaces */)) {
+                      true /* search_linked_namespaces */,
+                      readers_map,
+                      &namespaces)) {
     __linker_cannot_link(g_argv[0]);
   } else if (needed_libraries_count == 0) {
     if (!si->link_image(g_empty_list, soinfo_list_t::make_list(si), nullptr)) {
diff --git a/linker/linker_main.h b/linker/linker_main.h
index 8f3f07c..2cf30c2 100644
--- a/linker/linker_main.h
+++ b/linker/linker_main.h
@@ -31,6 +31,9 @@
 
 #include <android/dlext.h>
 
+#include <unordered_map>
+#include <vector>
+
 #include "linker_namespaces.h"
 #include "linker_soinfo.h"
 
@@ -44,7 +47,9 @@
   static size_t ref_count_;
 };
 
-void init_default_namespace(const char* executable_path);
+class ElfReader;
+
+std::vector<android_namespace_t*> init_default_namespaces(const char* executable_path);
 soinfo* soinfo_alloc(android_namespace_t* ns, const char* name,
                      struct stat* file_stat, off64_t file_offset,
                      uint32_t rtld_flags);
@@ -59,7 +64,9 @@
                     int rtld_flags,
                     const android_dlextinfo* extinfo,
                     bool add_as_children,
-                    bool search_linked_namespaces);
+                    bool search_linked_namespaces,
+                    std::unordered_map<const soinfo*, ElfReader>& readers_map,
+                    std::vector<android_namespace_t*>* namespaces = nullptr);
 
 void solist_add_soinfo(soinfo* si);
 bool solist_remove_soinfo(soinfo* si);
diff --git a/linker/linker_namespaces.cpp b/linker/linker_namespaces.cpp
index 3c86f99..9fdf0b5 100644
--- a/linker/linker_namespaces.cpp
+++ b/linker/linker_namespaces.cpp
@@ -31,6 +31,8 @@
 #include "linker_soinfo.h"
 #include "linker_utils.h"
 
+#include <dlfcn.h>
+
 bool android_namespace_t::is_accessible(const std::string& file) {
   if (!is_isolated_) {
     return true;
@@ -86,3 +88,41 @@
     return !is_accessible_ftor(si);
   });
 }
+
+// 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.
+soinfo_list_t android_namespace_t::get_global_group() {
+  soinfo_list_t global_group;
+  soinfo_list().for_each([&](soinfo* si) {
+    if ((si->get_dt_flags_1() & DF_1_GLOBAL) != 0) {
+      global_group.push_back(si);
+    }
+  });
+
+  return global_group;
+}
+
+// This function provides a list of libraries to be shared
+// by the namespace. For the default namespace this is the global
+// group (see get_global_group). For all others this is a group
+// of RTLD_GLOBAL libraries (which includes the global group from
+// the default namespace).
+soinfo_list_t android_namespace_t::get_shared_group() {
+  if (this == &g_default_namespace) {
+    return get_global_group();
+  }
+
+  soinfo_list_t shared_group;
+  soinfo_list().for_each([&](soinfo* si) {
+    if ((si->get_rtld_flags() & RTLD_GLOBAL) != 0) {
+      shared_group.push_back(si);
+    }
+  });
+
+  return shared_group;
+}
diff --git a/linker/linker_namespaces.h b/linker/linker_namespaces.h
index 1099b6b..16906d6 100644
--- a/linker/linker_namespaces.h
+++ b/linker/linker_namespaces.h
@@ -136,6 +136,9 @@
   // or one of it's parent soinfos belongs to this namespace.
   bool is_accessible(soinfo* si);
 
+  soinfo_list_t get_global_group();
+  soinfo_list_t get_shared_group();
+
  private:
   const char* name_;
   bool is_isolated_;
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index 42c29c8..a9873c4 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -147,8 +147,9 @@
 }
 
 bool ElfReader::Read(const char* name, int fd, off64_t file_offset, off64_t file_size) {
-  CHECK(!did_read_);
-  CHECK(!did_load_);
+  if (did_read_) {
+    return true;
+  }
   name_ = name;
   fd_ = fd;
   file_offset_ = file_offset;
@@ -167,7 +168,9 @@
 
 bool ElfReader::Load(const android_dlextinfo* extinfo) {
   CHECK(did_read_);
-  CHECK(!did_load_);
+  if (did_load_) {
+    return true;
+  }
   if (ReserveAddressSpace(extinfo) &&
       LoadSegments() &&
       FindPhdr()) {
diff --git a/tests/Android.bp b/tests/Android.bp
index 8f1c11f..084d393 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -298,7 +298,13 @@
                 "libLLVMSupport",
             ],
         }
-    }
+    },
+
+    product_variables: {
+        debuggable: {
+            cppflags: ["-DUSE_LD_CONFIG_FILE"],
+        },
+    },
 }
 
 // -----------------------------------------------------------------------------
@@ -592,6 +598,12 @@
     sanitize: {
         never: false,
     },
+
+    product_variables: {
+        debuggable: {
+            cppflags: ["-DUSE_LD_CONFIG_FILE"],
+        },
+    },
 }
 
 subdirs = ["libs"]
diff --git a/tests/dl_test.cpp b/tests/dl_test.cpp
index aa8bd57..857640a 100644
--- a/tests/dl_test.cpp
+++ b/tests/dl_test.cpp
@@ -23,6 +23,8 @@
 #include <stdint.h>
 
 #include <string>
+#include <iostream>
+#include <fstream>
 
 #include "gtest_globals.h"
 #include "utils.h"
@@ -109,4 +111,131 @@
 #endif
 }
 
-// TODO: Add tests for LD_PRELOADs
+
+TEST(dl, exec_without_ld_preload) {
+#if defined(__BIONIC__)
+  std::string helper = get_testlib_root() +
+      "/ld_preload_test_helper/ld_preload_test_helper";
+  chmod(helper.c_str(), 0755);
+  ExecTestHelper eth;
+  eth.SetArgs({ helper.c_str(), nullptr });
+  eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "12345");
+#endif
+}
+
+TEST(dl, exec_with_ld_preload) {
+#if defined(__BIONIC__)
+  std::string helper = get_testlib_root() +
+      "/ld_preload_test_helper/ld_preload_test_helper";
+  std::string env = std::string("LD_PRELOAD=") + get_testlib_root() + "/ld_preload_test_helper_lib2.so";
+  chmod(helper.c_str(), 0755);
+  ExecTestHelper eth;
+  eth.SetArgs({ helper.c_str(), nullptr });
+  eth.SetEnv({ env.c_str(), nullptr });
+  // ld_preload_test_helper calls get_value_from_lib() and returns the value.
+  // The symbol is defined by two libs: ld_preload_test_helper_lib.so and
+  // ld_preloaded_lib.so. The former is DT_NEEDED and the latter is LD_PRELOADED
+  // via this execution. The main executable is linked to the LD_PRELOADED lib
+  // and the value given from the lib is returned.
+  eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "54321");
+#endif
+}
+
+
+// ld_config_test_helper must fail because it is depending on a lib which is not
+// in the search path
+//
+// Call sequence is...
+// _helper -- (get_value_from_lib()) -->
+//     _lib1.so -- (get_value_from_another_lib()) -->
+//       _lib2.so (returns 12345)
+// The two libs are in ns2/ subdir.
+TEST(dl, exec_without_ld_config_file) {
+#if defined(__BIONIC__)
+  std::string error_message = "CANNOT LINK EXECUTABLE \"" + get_testlib_root() + "/ld_config_test_helper/ld_config_test_helper\": library \"ld_config_test_helper_lib1.so\" not found\n";
+  std::string helper = get_testlib_root() +
+      "/ld_config_test_helper/ld_config_test_helper";
+  chmod(helper.c_str(), 0755);
+  ExecTestHelper eth;
+  eth.SetArgs({ helper.c_str(), nullptr });
+  eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, -6, error_message.c_str());
+#endif
+}
+
+#if defined(__BIONIC__)
+static void create_ld_config_file(std::string& config_file) {
+  std::ofstream fout(config_file.c_str(), std::ios::out);
+  fout << "dir.test = " << get_testlib_root() << "/ld_config_test_helper/" << std::endl
+       << "[test]" << std::endl
+       << "additional.namespaces = ns2" << std::endl
+       << "namespace.default.search.paths = " << get_testlib_root() << std::endl
+       << "namespace.default.links = ns2" << std::endl
+       << "namespace.default.link.ns2.shared_libs = libc.so:libm.so:libdl.so:ld_config_test_helper_lib1.so" << std::endl
+       << "namespace.ns2.search.paths = /system/${LIB}:" << get_testlib_root() << "/ns2" << std::endl;
+  fout.close();
+}
+#endif
+
+#ifdef USE_LD_CONFIG_FILE
+
+// _lib1.so and _lib2.so are now searchable by having another namespace 'ns2'
+// whose search paths include the 'ns2/' subdir.
+TEST(dl, exec_with_ld_config_file) {
+#if defined(__BIONIC__)
+  std::string helper = get_testlib_root() +
+      "/ld_config_test_helper/ld_config_test_helper";
+  std::string config_file = get_testlib_root() + "/ld.config.txt";
+  create_ld_config_file(config_file);
+  std::string env = std::string("LD_CONFIG_FILE=") + config_file;
+  chmod(helper.c_str(), 0755);
+  ExecTestHelper eth;
+  eth.SetArgs({ helper.c_str(), nullptr });
+  eth.SetEnv({ env.c_str(), nullptr });
+  eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "12345");
+#endif
+}
+
+// _lib3.so has same symbol as lib2.so but returns 54321. _lib3.so is
+// LD_PRELOADed. This test is to ensure LD_PRELOADed libs are available to
+// additional namespaces other than the default namespace.
+TEST(dl, exec_with_ld_config_file_with_ld_preload) {
+#if defined(__BIONIC__)
+  std::string helper = get_testlib_root() +
+      "/ld_config_test_helper/ld_config_test_helper";
+  std::string config_file = get_testlib_root() + "/ld.config.txt";
+  create_ld_config_file(config_file);
+  std::string env = std::string("LD_CONFIG_FILE=") + config_file;
+  std::string env2 = std::string("LD_PRELOAD=") + get_testlib_root() + "/ld_config_test_helper_lib3.so";
+  chmod(helper.c_str(), 0755);
+  ExecTestHelper eth;
+  eth.SetArgs({ helper.c_str(), nullptr });
+  eth.SetEnv({ env.c_str(), env2.c_str(), nullptr });
+  eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "54321");
+#endif
+}
+
+#endif // USE_LD_CONFIG_FILE
+
+// ensures that LD_CONFIG_FILE env var does not work for production builds.
+// The test input is the same as exec_with_ld_config_file, but it must fail in
+// this case.
+TEST(dl, disable_ld_config_file) {
+#if defined(__BIONIC__)
+  if (getuid() == 0) {
+    // when executed from the shell (e.g. not as part of CTS), skip the test.
+    // This test is only for CTS.
+    return;
+  }
+  std::string error_message = "CANNOT LINK EXECUTABLE \"" + get_testlib_root() + "/ld_config_test_helper/ld_config_test_helper\": library \"ld_config_test_helper_lib1.so\" not found\n";
+  std::string helper = get_testlib_root() +
+      "/ld_config_test_helper/ld_config_test_helper";
+  std::string config_file = get_testlib_root() + "/ld.config.txt";
+  create_ld_config_file(config_file);
+  std::string env = std::string("LD_CONFIG_FILE=") + config_file;
+  chmod(helper.c_str(), 0755);
+  ExecTestHelper eth;
+  eth.SetArgs({ helper.c_str(), nullptr });
+  eth.SetEnv({ env.c_str(), nullptr });
+  eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, -6, error_message.c_str());
+#endif
+}
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index 973a8d2..c1600cc 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -617,3 +617,59 @@
     defaults: ["bionic_testlib_defaults"],
     srcs: ["preinit_syscall_test_helper.cpp"],
 }
+
+cc_test {
+    name: "ld_preload_test_helper",
+    host_supported: false,
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["ld_preload_test_helper.cpp"],
+    shared_libs: ["ld_preload_test_helper_lib1"],
+    ldflags: ["-Wl,--rpath,${ORIGIN}/.."],
+}
+
+cc_test_library {
+    name: "ld_preload_test_helper_lib1",
+    host_supported: false,
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["ld_preload_test_helper_lib1.cpp"],
+}
+
+cc_test_library {
+    name: "ld_preload_test_helper_lib2",
+    host_supported: false,
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["ld_preload_test_helper_lib2.cpp"],
+}
+
+cc_test {
+    name: "ld_config_test_helper",
+    host_supported: false,
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["ld_config_test_helper.cpp"],
+    shared_libs: ["ld_config_test_helper_lib1"],
+    ldflags: ["-Wl,--rpath,${ORIGIN}/.."],
+}
+
+cc_test_library {
+    name: "ld_config_test_helper_lib1",
+    host_supported: false,
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["ld_config_test_helper_lib1.cpp"],
+    shared_libs: ["ld_config_test_helper_lib2"],
+    relative_install_path: "/ns2",
+}
+
+cc_test_library {
+    name: "ld_config_test_helper_lib2",
+    host_supported: false,
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["ld_config_test_helper_lib2.cpp"],
+    relative_install_path: "/ns2",
+}
+
+cc_test_library {
+    name: "ld_config_test_helper_lib3",
+    host_supported: false,
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["ld_config_test_helper_lib3.cpp"],
+}
diff --git a/tests/libs/ld_config_test_helper.cpp b/tests/libs/ld_config_test_helper.cpp
new file mode 100644
index 0000000..592e8c0
--- /dev/null
+++ b/tests/libs/ld_config_test_helper.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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 <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+extern int get_value_from_lib();
+
+int main() {
+  printf("%d", get_value_from_lib());
+  return 0;
+}
diff --git a/tests/libs/ld_config_test_helper_lib1.cpp b/tests/libs/ld_config_test_helper_lib1.cpp
new file mode 100644
index 0000000..fc5401a
--- /dev/null
+++ b/tests/libs/ld_config_test_helper_lib1.cpp
@@ -0,0 +1,4 @@
+extern int get_value_from_another_lib();
+int get_value_from_lib() {
+  return get_value_from_another_lib();
+}
diff --git a/tests/libs/ld_config_test_helper_lib2.cpp b/tests/libs/ld_config_test_helper_lib2.cpp
new file mode 100644
index 0000000..a620a6c
--- /dev/null
+++ b/tests/libs/ld_config_test_helper_lib2.cpp
@@ -0,0 +1,3 @@
+int get_value_from_another_lib() {
+  return 12345;
+}
diff --git a/tests/libs/ld_config_test_helper_lib3.cpp b/tests/libs/ld_config_test_helper_lib3.cpp
new file mode 100644
index 0000000..93d1cd8
--- /dev/null
+++ b/tests/libs/ld_config_test_helper_lib3.cpp
@@ -0,0 +1,3 @@
+int get_value_from_another_lib() {
+  return 54321;
+}
diff --git a/tests/libs/ld_preload_test_helper.cpp b/tests/libs/ld_preload_test_helper.cpp
new file mode 100644
index 0000000..592e8c0
--- /dev/null
+++ b/tests/libs/ld_preload_test_helper.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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 <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+extern int get_value_from_lib();
+
+int main() {
+  printf("%d", get_value_from_lib());
+  return 0;
+}
diff --git a/tests/libs/ld_preload_test_helper_lib1.cpp b/tests/libs/ld_preload_test_helper_lib1.cpp
new file mode 100644
index 0000000..74e89db
--- /dev/null
+++ b/tests/libs/ld_preload_test_helper_lib1.cpp
@@ -0,0 +1,3 @@
+int get_value_from_lib() {
+  return 12345;
+}
diff --git a/tests/libs/ld_preload_test_helper_lib2.cpp b/tests/libs/ld_preload_test_helper_lib2.cpp
new file mode 100644
index 0000000..9239891
--- /dev/null
+++ b/tests/libs/ld_preload_test_helper_lib2.cpp
@@ -0,0 +1,3 @@
+int get_value_from_lib() {
+  return 54321;
+}