Merge "Don't use the same declaration to declare multiple symbol names."
diff --git a/libc/NOTICE b/libc/NOTICE
index 5e0d7b2..735b6c4 100644
--- a/libc/NOTICE
+++ b/libc/NOTICE
@@ -1457,23 +1457,6 @@
 Copyright (c) 1989 The Regents of the University of California.
 All rights reserved.
 
-Redistribution and use in source and binary forms are permitted
-provided that the above copyright notice and this paragraph are
-duplicated in all such forms and that any documentation,
-advertising materials, and other materials related to such
-distribution and use acknowledge that the software was developed
-by the University of California, Berkeley. The name of the
-University may not be used to endorse or promote products derived
-from this software without specific prior written permission.
-THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1989 The Regents of the University of California.
-All rights reserved.
-
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:
@@ -4815,6 +4798,34 @@
 
 -------------------------------------------------------------------
 
+Copyright 1989 The Regents of the University of California.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+   1. Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+   2. Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+   3. Neither the name of the University nor the names of its contributors
+      may be used to endorse or promote products derived from this software
+      without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
+   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+
+-------------------------------------------------------------------
+
 Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
 Copyright 2008 Damien Miller <djm@openbsd.org>
 All rights reserved.
diff --git a/libdl/NOTICE b/libdl/NOTICE
index 77b5743..7940d04 100644
--- a/libdl/NOTICE
+++ b/libdl/NOTICE
@@ -14,3 +14,19 @@
 
 -------------------------------------------------------------------
 
+Copyright (C) 2015 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.
+
+-------------------------------------------------------------------
+
diff --git a/libstdc++/NOTICE b/libstdc++/NOTICE
index 492770d..e69de29 100644
--- a/libstdc++/NOTICE
+++ b/libstdc++/NOTICE
@@ -1,81 +0,0 @@
-Copyright (C) 2008 The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (C) 2009 The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
-Copyright (c) 1994
-Hewlett-Packard Company
-
-Permission to use, copy, modify, distribute and sell this software
-and its documentation for any purpose is hereby granted without fee,
-provided that the above copyright notice appear in all copies and
-that both that copyright notice and this permission notice appear
-in supporting documentation.  Hewlett-Packard Company makes no
-representations about the suitability of this software for any
-purpose.  It is provided "as is" without express or implied warranty.
-
-
-Copyright (c) 1996,1997
-Silicon Graphics Computer Systems, Inc.
-
-Permission to use, copy, modify, distribute and sell this software
-and its documentation for any purpose is hereby granted without fee,
-provided that the above copyright notice appear in all copies and
-that both that copyright notice and this permission notice appear
-in supporting documentation.  Silicon Graphics makes no
-representations about the suitability of this software for any
-purpose.  It is provided "as is" without express or implied warranty.
-
--------------------------------------------------------------------
-
diff --git a/linker/Android.bp b/linker/Android.bp
index a35eb45..39f1da9 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -20,11 +20,14 @@
         "linker_block_allocator.cpp",
         "linker_dlwarning.cpp",
         "linker_gdb_support.cpp",
+        "linker_globals.cpp",
         "linker_libc_support.c",
+        "linker_namespaces.cpp",
         "linker_logger.cpp",
         "linker_mapped_file_fragment.cpp",
         "linker_phdr.cpp",
         "linker_sdk_versions.cpp",
+        "linker_soinfo.cpp",
         "linker_utils.cpp",
         "rt.cpp",
     ],
diff --git a/linker/NOTICE b/linker/NOTICE
index 4bbd741..3ba0ff9 100644
--- a/linker/NOTICE
+++ b/linker/NOTICE
@@ -190,3 +190,31 @@
 
 -------------------------------------------------------------------
 
+Copyright (C) 2016 The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the
+   distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index c03ffa8..4d9a218 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "linker.h"
+#include "linker_globals.h"
 #include "linker_dlwarning.h"
 
 #include <pthread.h>
@@ -261,8 +262,6 @@
 static uint8_t __libdl_info_buf[sizeof(soinfo)] __attribute__((aligned(8)));
 static soinfo* __libdl_info = nullptr;
 
-extern android_namespace_t g_default_namespace;
-
 // This is used by the dynamic linker. Every process gets these symbols for free.
 soinfo* get_libdl_info() {
   if (__libdl_info == nullptr) {
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 68b3a53..ca8ffbc 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -53,8 +53,10 @@
 #include "linker.h"
 #include "linker_block_allocator.h"
 #include "linker_gdb_support.h"
+#include "linker_globals.h"
 #include "linker_debug.h"
 #include "linker_dlwarning.h"
+#include "linker_namespaces.h"
 #include "linker_sleb128.h"
 #include "linker_phdr.h"
 #include "linker_relocs.h"
@@ -75,74 +77,6 @@
 #undef ELF_ST_TYPE
 #define ELF_ST_TYPE(x) (static_cast<uint32_t>(x) & 0xf)
 
-struct android_namespace_t {
- public:
-  android_namespace_t() : name_(nullptr), is_isolated_(false) {}
-
-  const char* get_name() const { return name_; }
-  void set_name(const char* name) { name_ = name; }
-
-  bool is_isolated() const { return is_isolated_; }
-  void set_isolated(bool isolated) { is_isolated_ = isolated; }
-
-  const std::vector<std::string>& get_ld_library_paths() const {
-    return ld_library_paths_;
-  }
-  void set_ld_library_paths(std::vector<std::string>&& library_paths) {
-    ld_library_paths_ = library_paths;
-  }
-
-  const std::vector<std::string>& get_default_library_paths() const {
-    return default_library_paths_;
-  }
-  void set_default_library_paths(std::vector<std::string>&& library_paths) {
-    default_library_paths_ = library_paths;
-  }
-
-  const std::vector<std::string>& get_permitted_paths() const {
-    return permitted_paths_;
-  }
-  void set_permitted_paths(std::vector<std::string>&& permitted_paths) {
-    permitted_paths_ = permitted_paths;
-  }
-
-  void add_soinfo(soinfo* si) {
-    soinfo_list_.push_back(si);
-  }
-
-  void add_soinfos(const soinfo::soinfo_list_t& soinfos) {
-    for (auto si : soinfos) {
-      add_soinfo(si);
-      si->add_secondary_namespace(this);
-    }
-  }
-
-  void remove_soinfo(soinfo* si) {
-    soinfo_list_.remove_if([&](soinfo* candidate) {
-      return si == candidate;
-    });
-  }
-
-  const soinfo::soinfo_list_t& soinfo_list() const { return soinfo_list_; }
-
-  // For isolated namespaces - checks if the file is on the search path;
-  // always returns true for not isolated namespace.
-  bool is_accessible(const std::string& path);
-
- private:
-  const char* name_;
-  bool is_isolated_;
-  std::vector<std::string> ld_library_paths_;
-  std::vector<std::string> default_library_paths_;
-  std::vector<std::string> permitted_paths_;
-  soinfo::soinfo_list_t soinfo_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(android_namespace_t);
-};
-
-android_namespace_t g_default_namespace;
-
-static std::unordered_map<uintptr_t, soinfo*> g_soinfo_handles_map;
 static android_namespace_t* g_anonymous_namespace = &g_default_namespace;
 
 static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf);
@@ -198,8 +132,6 @@
 static const char* const kSystemLibDir = "/system/lib";
 #endif
 
-static std::string dirname(const char *path);
-
 // TODO(dimitry): The grey-list is a workaround for http://b/26394120 ---
 // gradually remove libraries from this list until it is gone.
 static bool is_greylisted(const char* name, const soinfo* needed_by) {
@@ -250,33 +182,17 @@
 }
 // END OF WORKAROUND
 
-static const ElfW(Versym) kVersymNotNeeded = 0;
-static const ElfW(Versym) kVersymGlobal = 1;
-
 static const char* const* g_default_ld_paths;
 static std::vector<std::string> g_ld_preload_names;
 
 static std::vector<soinfo*> g_ld_preloads;
 
 static bool g_public_namespace_initialized;
-static soinfo::soinfo_list_t g_public_namespace;
+static soinfo_list_t g_public_namespace;
 
 int g_ld_debug_verbosity;
 abort_msg_t* g_abort_message = nullptr; // For debuggerd.
 
-// These values are used to call constructors for .init_array && .preinit_array
-int g_argc = 0;
-char** g_argv = nullptr;
-char** g_envp = nullptr;
-
-static std::string dirname(const char *path) {
-  const char* last_slash = strrchr(path, '/');
-  if (last_slash == path) return "/";
-  else if (last_slash == nullptr) return ".";
-  else
-    return std::string(path, last_slash - path);
-}
-
 #if STATS
 struct linker_stats_t {
   int count[kRelocMax];
@@ -296,16 +212,6 @@
 uint32_t bitmask[4096];
 #endif
 
-static char __linker_dl_err_buf[768];
-
-char* linker_get_error_buffer() {
-  return &__linker_dl_err_buf[0];
-}
-
-size_t linker_get_error_buffer_size() {
-  return sizeof(__linker_dl_err_buf);
-}
-
 static void notify_gdb_of_load(soinfo* info) {
   if (info->is_linker() || info->is_main_executable()) {
     // gdb already knows about the linker and the main executable.
@@ -329,32 +235,6 @@
   notify_gdb_of_unload(&(info->link_map_head));
 }
 
-bool android_namespace_t::is_accessible(const std::string& file) {
-  if (!is_isolated_) {
-    return true;
-  }
-
-  for (const auto& dir : ld_library_paths_) {
-    if (file_is_in_dir(file, dir)) {
-      return true;
-    }
-  }
-
-  for (const auto& dir : default_library_paths_) {
-    if (file_is_in_dir(file, dir)) {
-      return true;
-    }
-  }
-
-  for (const auto& dir : permitted_paths_) {
-    if (file_is_under_dir(file, dir)) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
 LinkedListEntry<soinfo>* SoinfoListAllocator::alloc() {
   return g_soinfo_links_allocator.alloc();
 }
@@ -438,60 +318,6 @@
   g_soinfo_allocator.free(si);
 }
 
-// For every path element this function checks of it exists, and is a directory,
-// and normalizes it:
-// 1. For regular path it converts it to realpath()
-// 2. For path in a zip file it uses realpath on the zipfile
-//    normalizes entry name by calling normalize_path function.
-static void resolve_paths(std::vector<std::string>& paths,
-                          std::vector<std::string>* resolved_paths) {
-  resolved_paths->clear();
-  for (const auto& path : paths) {
-    char resolved_path[PATH_MAX];
-    const char* original_path = path.c_str();
-    if (realpath(original_path, resolved_path) != nullptr) {
-      struct stat s;
-      if (stat(resolved_path, &s) == 0) {
-        if (S_ISDIR(s.st_mode)) {
-          resolved_paths->push_back(resolved_path);
-        } else {
-          DL_WARN("Warning: \"%s\" is not a directory (excluding from path)", resolved_path);
-          continue;
-        }
-      } else {
-        DL_WARN("Warning: cannot stat file \"%s\": %s", resolved_path, strerror(errno));
-        continue;
-      }
-    } else {
-      std::string zip_path;
-      std::string entry_path;
-
-      std::string normalized_path;
-
-      if (!normalize_path(original_path, &normalized_path)) {
-        DL_WARN("Warning: unable to normalize \"%s\"", original_path);
-        continue;
-      }
-
-      if (parse_zip_path(normalized_path.c_str(), &zip_path, &entry_path)) {
-        if (realpath(zip_path.c_str(), resolved_path) == nullptr) {
-          DL_WARN("Warning: unable to resolve \"%s\": %s", zip_path.c_str(), strerror(errno));
-          continue;
-        }
-
-        resolved_paths->push_back(std::string(resolved_path) + kZipFileSeparator + entry_path);
-      }
-    }
-  }
-}
-
-static void split_path(const char* path, const char* delimiters,
-                       std::vector<std::string>* paths) {
-  if (path != nullptr && path[0] != 0) {
-    *paths = android::base::Split(path, delimiters);
-  }
-}
-
 static void parse_path(const char* path, const char* delimiters,
                        std::vector<std::string>* resolved_paths) {
   std::vector<std::string> paths;
@@ -505,45 +331,6 @@
   g_default_namespace.set_ld_library_paths(std::move(ld_libary_paths));
 }
 
-void soinfo::set_dt_runpath(const char* path) {
-  if (!has_min_version(3)) {
-    return;
-  }
-
-  std::vector<std::string> runpaths;
-
-  split_path(path, ":", &runpaths);
-
-  std::string origin = dirname(get_realpath());
-  // FIXME: add $LIB and $PLATFORM.
-  std::pair<std::string, std::string> substs[] = {{"ORIGIN", origin}};
-  for (auto&& s : runpaths) {
-    size_t pos = 0;
-    while (pos < s.size()) {
-      pos = s.find("$", pos);
-      if (pos == std::string::npos) break;
-      for (const auto& subst : substs) {
-        const std::string& token = subst.first;
-        const std::string& replacement = subst.second;
-        if (s.substr(pos + 1, token.size()) == token) {
-          s.replace(pos, token.size() + 1, replacement);
-          // -1 to compensate for the ++pos below.
-          pos += replacement.size() - 1;
-          break;
-        } else if (s.substr(pos + 1, token.size() + 2) == "{" + token + "}") {
-          s.replace(pos, token.size() + 3, replacement);
-          pos += replacement.size() - 1;
-          break;
-        }
-      }
-      // Skip $ in case it did not match any of the known substitutions.
-      ++pos;
-    }
-  }
-
-  resolve_paths(runpaths, &dt_runpath_);
-}
-
 static void parse_LD_PRELOAD(const char* path) {
   g_ld_preload_names.clear();
   if (path != nullptr) {
@@ -610,336 +397,10 @@
   return rv;
 }
 
-const ElfW(Versym)* soinfo::get_versym(size_t n) const {
-  if (has_min_version(2) && versym_ != nullptr) {
-    return versym_ + n;
-  }
-
-  return nullptr;
-}
-
-ElfW(Addr) soinfo::get_verneed_ptr() const {
-  if (has_min_version(2)) {
-    return verneed_ptr_;
-  }
-
-  return 0;
-}
-
-size_t soinfo::get_verneed_cnt() const {
-  if (has_min_version(2)) {
-    return verneed_cnt_;
-  }
-
-  return 0;
-}
-
-ElfW(Addr) soinfo::get_verdef_ptr() const {
-  if (has_min_version(2)) {
-    return verdef_ptr_;
-  }
-
-  return 0;
-}
-
-size_t soinfo::get_verdef_cnt() const {
-  if (has_min_version(2)) {
-    return verdef_cnt_;
-  }
-
-  return 0;
-}
-
-template<typename F>
-static bool for_each_verdef(const soinfo* si, F functor) {
-  if (!si->has_min_version(2)) {
-    return true;
-  }
-
-  uintptr_t verdef_ptr = si->get_verdef_ptr();
-  if (verdef_ptr == 0) {
-    return true;
-  }
-
-  size_t offset = 0;
-
-  size_t verdef_cnt = si->get_verdef_cnt();
-  for (size_t i = 0; i<verdef_cnt; ++i) {
-    const ElfW(Verdef)* verdef = reinterpret_cast<ElfW(Verdef)*>(verdef_ptr + offset);
-    size_t verdaux_offset = offset + verdef->vd_aux;
-    offset += verdef->vd_next;
-
-    if (verdef->vd_version != 1) {
-      DL_ERR("unsupported verdef[%zd] vd_version: %d (expected 1) library: %s",
-          i, verdef->vd_version, si->get_realpath());
-      return false;
-    }
-
-    if ((verdef->vd_flags & VER_FLG_BASE) != 0) {
-      // "this is the version of the file itself.  It must not be used for
-      //  matching a symbol. It can be used to match references."
-      //
-      // http://www.akkadia.org/drepper/symbol-versioning
-      continue;
-    }
-
-    if (verdef->vd_cnt == 0) {
-      DL_ERR("invalid verdef[%zd] vd_cnt == 0 (version without a name)", i);
-      return false;
-    }
-
-    const ElfW(Verdaux)* verdaux = reinterpret_cast<ElfW(Verdaux)*>(verdef_ptr + verdaux_offset);
-
-    if (functor(i, verdef, verdaux) == true) {
-      break;
-    }
-  }
-
-  return true;
-}
-
-bool soinfo::find_verdef_version_index(const version_info* vi, ElfW(Versym)* versym) const {
-  if (vi == nullptr) {
-    *versym = kVersymNotNeeded;
-    return true;
-  }
-
-  *versym = kVersymGlobal;
-
-  return for_each_verdef(this,
-    [&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) {
-      if (verdef->vd_hash == vi->elf_hash &&
-          strcmp(vi->name, get_string(verdaux->vda_name)) == 0) {
-        *versym = verdef->vd_ndx;
-        return true;
-      }
-
-      return false;
-    }
-  );
-}
-
-bool soinfo::find_symbol_by_name(SymbolName& symbol_name,
-                                 const version_info* vi,
-                                 const ElfW(Sym)** symbol) const {
-  uint32_t symbol_index;
-  bool success =
-      is_gnu_hash() ?
-      gnu_lookup(symbol_name, vi, &symbol_index) :
-      elf_lookup(symbol_name, vi, &symbol_index);
-
-  if (success) {
-    *symbol = symbol_index == 0 ? nullptr : symtab_ + symbol_index;
-  }
-
-  return success;
-}
-
-static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) {
-  if (ELF_ST_BIND(s->st_info) == STB_GLOBAL ||
-      ELF_ST_BIND(s->st_info) == STB_WEAK) {
-    return s->st_shndx != SHN_UNDEF;
-  } else if (ELF_ST_BIND(s->st_info) != STB_LOCAL) {
-    DL_WARN("unexpected ST_BIND value: %d for \"%s\" in \"%s\"",
-            ELF_ST_BIND(s->st_info), si->get_string(s->st_name), si->get_realpath());
-  }
-
-  return false;
-}
-
-static const ElfW(Versym) kVersymHiddenBit = 0x8000;
-
-static inline bool is_versym_hidden(const ElfW(Versym)* versym) {
-  // the symbol is hidden if bit 15 of versym is set.
-  return versym != nullptr && (*versym & kVersymHiddenBit) != 0;
-}
-
-static inline bool check_symbol_version(const ElfW(Versym) verneed,
-                                        const ElfW(Versym)* verdef) {
-  return verneed == kVersymNotNeeded ||
-      verdef == nullptr ||
-      verneed == (*verdef & ~kVersymHiddenBit);
-}
-
-bool soinfo::gnu_lookup(SymbolName& symbol_name,
-                        const version_info* vi,
-                        uint32_t* symbol_index) const {
-  uint32_t hash = symbol_name.gnu_hash();
-  uint32_t h2 = hash >> gnu_shift2_;
-
-  uint32_t bloom_mask_bits = sizeof(ElfW(Addr))*8;
-  uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_;
-  ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];
-
-  *symbol_index = 0;
-
-  TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)",
-      symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
-
-  // test against bloom filter
-  if ((1 & (bloom_word >> (hash % bloom_mask_bits)) & (bloom_word >> (h2 % bloom_mask_bits))) == 0) {
-    TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
-        symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
-
-    return true;
-  }
-
-  // bloom test says "probably yes"...
-  uint32_t n = gnu_bucket_[hash % gnu_nbucket_];
-
-  if (n == 0) {
-    TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
-        symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
-
-    return true;
-  }
-
-  // lookup versym for the version definition in this library
-  // note the difference between "version is not requested" (vi == nullptr)
-  // and "version not found". In the first case verneed is kVersymNotNeeded
-  // which implies that the default version can be accepted; the second case results in
-  // verneed = 1 (kVersymGlobal) and implies that we should ignore versioned symbols
-  // for this library and consider only *global* ones.
-  ElfW(Versym) verneed = 0;
-  if (!find_verdef_version_index(vi, &verneed)) {
-    return false;
-  }
-
-  do {
-    ElfW(Sym)* s = symtab_ + n;
-    const ElfW(Versym)* verdef = get_versym(n);
-    // skip hidden versions when verneed == kVersymNotNeeded (0)
-    if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) {
-        continue;
-    }
-    if (((gnu_chain_[n] ^ hash) >> 1) == 0 &&
-        check_symbol_version(verneed, verdef) &&
-        strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
-        is_symbol_global_and_defined(this, s)) {
-      TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
-          symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(s->st_value),
-          static_cast<size_t>(s->st_size));
-      *symbol_index = n;
-      return true;
-    }
-  } while ((gnu_chain_[n++] & 1) == 0);
-
-  TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
-             symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
-
-  return true;
-}
-
-bool soinfo::elf_lookup(SymbolName& symbol_name,
-                        const version_info* vi,
-                        uint32_t* symbol_index) const {
-  uint32_t hash = symbol_name.elf_hash();
-
-  TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd",
-             symbol_name.get_name(), get_realpath(),
-             reinterpret_cast<void*>(base), hash, hash % nbucket_);
-
-  ElfW(Versym) verneed = 0;
-  if (!find_verdef_version_index(vi, &verneed)) {
-    return false;
-  }
-
-  for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) {
-    ElfW(Sym)* s = symtab_ + n;
-    const ElfW(Versym)* verdef = get_versym(n);
-
-    // skip hidden versions when verneed == 0
-    if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) {
-        continue;
-    }
-
-    if (check_symbol_version(verneed, verdef) &&
-        strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
-        is_symbol_global_and_defined(this, s)) {
-      TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
-                 symbol_name.get_name(), get_realpath(),
-                 reinterpret_cast<void*>(s->st_value),
-                 static_cast<size_t>(s->st_size));
-      *symbol_index = n;
-      return true;
-    }
-  }
-
-  TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p %x %zd",
-             symbol_name.get_name(), get_realpath(),
-             reinterpret_cast<void*>(base), hash, hash % nbucket_);
-
-  *symbol_index = 0;
-  return true;
-}
-
-soinfo::soinfo(android_namespace_t* ns, const char* realpath,
-               const struct stat* file_stat, off64_t file_offset,
-               int rtld_flags) {
-  memset(this, 0, sizeof(*this));
-
-  if (realpath != nullptr) {
-    realpath_ = realpath;
-  }
-
-  flags_ = FLAG_NEW_SOINFO;
-  version_ = SOINFO_VERSION;
-
-  if (file_stat != nullptr) {
-    this->st_dev_ = file_stat->st_dev;
-    this->st_ino_ = file_stat->st_ino;
-    this->file_offset_ = file_offset;
-  }
-
-  this->rtld_flags_ = rtld_flags;
-  this->primary_namespace_ = ns;
-}
-
-soinfo::~soinfo() {
-  g_soinfo_handles_map.erase(handle_);
-}
-
-static uint32_t calculate_elf_hash(const char* name) {
-  const uint8_t* name_bytes = reinterpret_cast<const uint8_t*>(name);
-  uint32_t h = 0, g;
-
-  while (*name_bytes) {
-    h = (h << 4) + *name_bytes++;
-    g = h & 0xf0000000;
-    h ^= g;
-    h ^= g >> 24;
-  }
-
-  return h;
-}
-
-uint32_t SymbolName::elf_hash() {
-  if (!has_elf_hash_) {
-    elf_hash_ = calculate_elf_hash(name_);
-    has_elf_hash_ = true;
-  }
-
-  return elf_hash_;
-}
-
-uint32_t SymbolName::gnu_hash() {
-  if (!has_gnu_hash_) {
-    uint32_t h = 5381;
-    const uint8_t* name = reinterpret_cast<const uint8_t*>(name_);
-    while (*name != 0) {
-      h += (h << 5) + *name++; // h*33 + c = h + h * 32 + c = h + h << 5 + c
-    }
-
-    gnu_hash_ =  h;
-    has_gnu_hash_ = true;
-  }
-
-  return gnu_hash_;
-}
 
 bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi,
-                      soinfo** si_found_in, const soinfo::soinfo_list_t& global_group,
-                      const soinfo::soinfo_list_t& local_group, const ElfW(Sym)** symbol) {
+                      soinfo** si_found_in, const soinfo_list_t& global_group,
+                      const soinfo_list_t& local_group, const ElfW(Sym)** symbol) {
   SymbolName symbol_name(name);
   const ElfW(Sym)* s = nullptr;
 
@@ -1394,52 +855,6 @@
   return nullptr;
 }
 
-ElfW(Sym)* soinfo::find_symbol_by_address(const void* addr) {
-  return is_gnu_hash() ? gnu_addr_lookup(addr) : elf_addr_lookup(addr);
-}
-
-static bool symbol_matches_soaddr(const ElfW(Sym)* sym, ElfW(Addr) soaddr) {
-  return sym->st_shndx != SHN_UNDEF &&
-      soaddr >= sym->st_value &&
-      soaddr < sym->st_value + sym->st_size;
-}
-
-ElfW(Sym)* soinfo::gnu_addr_lookup(const void* addr) {
-  ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - load_bias;
-
-  for (size_t i = 0; i < gnu_nbucket_; ++i) {
-    uint32_t n = gnu_bucket_[i];
-
-    if (n == 0) {
-      continue;
-    }
-
-    do {
-      ElfW(Sym)* sym = symtab_ + n;
-      if (symbol_matches_soaddr(sym, soaddr)) {
-        return sym;
-      }
-    } while ((gnu_chain_[n++] & 1) == 0);
-  }
-
-  return nullptr;
-}
-
-ElfW(Sym)* soinfo::elf_addr_lookup(const void* addr) {
-  ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - load_bias;
-
-  // Search the library's symbol table for any defined symbol which
-  // contains this address.
-  for (size_t i = 0; i < nchain_; ++i) {
-    ElfW(Sym)* sym = symtab_ + i;
-    if (symbol_matches_soaddr(sym, soaddr)) {
-      return sym;
-    }
-  }
-
-  return nullptr;
-}
-
 class ZipArchiveCache {
  public:
   ZipArchiveCache() {}
@@ -1954,8 +1369,8 @@
 //
 // This group consists of the main executable, LD_PRELOADs
 // and libraries with the DF_1_GLOBAL flag set.
-static soinfo::soinfo_list_t make_global_group(android_namespace_t* ns) {
-  soinfo::soinfo_list_t global_group;
+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);
@@ -1970,12 +1385,12 @@
 // 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::soinfo_list_t get_shared_group(android_namespace_t* ns) {
+static soinfo_list_t get_shared_group(android_namespace_t* ns) {
   if (ns == &g_default_namespace) {
     return make_global_group(ns);
   }
 
-  soinfo::soinfo_list_t shared_group;
+  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);
@@ -2015,7 +1430,7 @@
   }
 
   // Construct global_group.
-  soinfo::soinfo_list_t global_group = make_global_group(ns);
+  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
@@ -2122,7 +1537,7 @@
 
 
   // Step 5: link libraries.
-  soinfo::soinfo_list_t local_group;
+  soinfo_list_t local_group;
   walk_dependencies_tree(
       (start_with != nullptr && add_as_children) ? &start_with : soinfos,
       (start_with != nullptr && add_as_children) ? 1 : soinfos_count,
@@ -2202,7 +1617,7 @@
     return;
   }
 
-  soinfo::soinfo_list_t unload_list;
+  soinfo_list_t unload_list;
   for (size_t i = 0; i < count; ++i) {
     soinfo* si = soinfos[i];
 
@@ -2225,8 +1640,8 @@
   // linked. This is why we can safely use the first one.
   soinfo* root = soinfos[0];
 
-  soinfo::soinfo_list_t local_unload_list;
-  soinfo::soinfo_list_t external_unload_list;
+  soinfo_list_t local_unload_list;
+  soinfo_list_t external_unload_list;
   soinfo* si = nullptr;
 
   while ((si = unload_list.pop_front()) != nullptr) {
@@ -2584,6 +1999,13 @@
   return true;
 }
 
+static void add_soinfos_to_namespace(const soinfo_list_t& soinfos, android_namespace_t* ns) {
+  ns->add_soinfos(soinfos);
+  for (auto si : soinfos) {
+    si->add_secondary_namespace(ns);
+  }
+}
+
 android_namespace_t* create_namespace(const void* caller_addr,
                                       const char* name,
                                       const char* ld_library_path,
@@ -2623,16 +2045,16 @@
 
   if ((type & ANDROID_NAMESPACE_TYPE_SHARED) != 0) {
     // If shared - clone the parent namespace
-    ns->add_soinfos(parent_namespace->soinfo_list());
+    add_soinfos_to_namespace(parent_namespace->soinfo_list(), ns);
   } else {
     // If not shared - copy only the shared group
-    ns->add_soinfos(get_shared_group(parent_namespace));
+    add_soinfos_to_namespace(get_shared_group(parent_namespace), ns);
   }
 
   return ns;
 }
 
-static ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) {
+ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) {
   typedef ElfW(Addr) (*ifunc_resolver_t)(void);
   ifunc_resolver_t ifunc_resolver = reinterpret_cast<ifunc_resolver_t>(resolver_addr);
   ElfW(Addr) ifunc_addr = ifunc_resolver();
@@ -2711,6 +2133,75 @@
   return true;
 }
 
+template <typename F>
+static bool for_each_verdef(const soinfo* si, F functor) {
+  if (!si->has_min_version(2)) {
+    return true;
+  }
+
+  uintptr_t verdef_ptr = si->get_verdef_ptr();
+  if (verdef_ptr == 0) {
+    return true;
+  }
+
+  size_t offset = 0;
+
+  size_t verdef_cnt = si->get_verdef_cnt();
+  for (size_t i = 0; i<verdef_cnt; ++i) {
+    const ElfW(Verdef)* verdef = reinterpret_cast<ElfW(Verdef)*>(verdef_ptr + offset);
+    size_t verdaux_offset = offset + verdef->vd_aux;
+    offset += verdef->vd_next;
+
+    if (verdef->vd_version != 1) {
+      DL_ERR("unsupported verdef[%zd] vd_version: %d (expected 1) library: %s",
+          i, verdef->vd_version, si->get_realpath());
+      return false;
+    }
+
+    if ((verdef->vd_flags & VER_FLG_BASE) != 0) {
+      // "this is the version of the file itself.  It must not be used for
+      //  matching a symbol. It can be used to match references."
+      //
+      // http://www.akkadia.org/drepper/symbol-versioning
+      continue;
+    }
+
+    if (verdef->vd_cnt == 0) {
+      DL_ERR("invalid verdef[%zd] vd_cnt == 0 (version without a name)", i);
+      return false;
+    }
+
+    const ElfW(Verdaux)* verdaux = reinterpret_cast<ElfW(Verdaux)*>(verdef_ptr + verdaux_offset);
+
+    if (functor(i, verdef, verdaux) == true) {
+      break;
+    }
+  }
+
+  return true;
+}
+
+bool find_verdef_version_index(const soinfo* si, const version_info* vi, ElfW(Versym)* versym) {
+  if (vi == nullptr) {
+    *versym = kVersymNotNeeded;
+    return true;
+  }
+
+  *versym = kVersymGlobal;
+
+  return for_each_verdef(si,
+    [&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) {
+      if (verdef->vd_hash == vi->elf_hash &&
+          strcmp(vi->name, si->get_string(verdaux->vda_name)) == 0) {
+        *versym = verdef->vd_ndx;
+        return true;
+      }
+
+      return false;
+    }
+  );
+}
+
 bool VersionTracker::init_verdef(const soinfo* si_from) {
   return for_each_verdef(si_from,
     [&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) {
@@ -2729,6 +2220,25 @@
   return init_verneed(si_from) && init_verdef(si_from);
 }
 
+#if !defined(__mips__)
+#if defined(USE_RELA)
+static ElfW(Addr) get_addend(ElfW(Rela)* rela, ElfW(Addr) reloc_addr __unused) {
+  return rela->r_addend;
+}
+#else
+static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) {
+  if (ELFW(R_TYPE)(rel->r_info) == R_GENERIC_RELATIVE ||
+      ELFW(R_TYPE)(rel->r_info) == R_GENERIC_IRELATIVE) {
+    return *reinterpret_cast<ElfW(Addr)*>(reloc_addr);
+  }
+  return 0;
+}
+#endif
+
+// TODO (dimitry): Methods below need to be moved out of soinfo
+// and in more isolated file in order minimize dependencies on
+// unnecessary object in the linker binary. Consider making them
+// independent from soinfo (?).
 bool soinfo::lookup_version_info(const VersionTracker& version_tracker, ElfW(Word) sym,
                                  const char* sym_name, const version_info** vi) {
   const ElfW(Versym)* sym_ver_ptr = get_versym(sym);
@@ -2750,21 +2260,6 @@
   return true;
 }
 
-#if !defined(__mips__)
-#if defined(USE_RELA)
-static ElfW(Addr) get_addend(ElfW(Rela)* rela, ElfW(Addr) reloc_addr __unused) {
-  return rela->r_addend;
-}
-#else
-static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) {
-  if (ELFW(R_TYPE)(rel->r_info) == R_GENERIC_RELATIVE ||
-      ELFW(R_TYPE)(rel->r_info) == R_GENERIC_IRELATIVE) {
-    return *reinterpret_cast<ElfW(Addr)*>(reloc_addr);
-  }
-  return 0;
-}
-#endif
-
 template<typename ElfRelIteratorT>
 bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& rel_iterator,
                       const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
@@ -3129,408 +2624,8 @@
 }
 #endif  // !defined(__mips__)
 
-static void call_function(const char* function_name __unused,
-                          linker_ctor_function_t function,
-                          const char* realpath __unused) {
-  if (function == nullptr || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) {
-    return;
-  }
-
-  TRACE("[ Calling c-tor %s @ %p for '%s' ]", function_name, function, realpath);
-  function(g_argc, g_argv, g_envp);
-  TRACE("[ Done calling c-tor %s @ %p for '%s' ]", function_name, function, realpath);
-}
-
-static void call_function(const char* function_name __unused,
-                          linker_dtor_function_t function,
-                          const char* realpath __unused) {
-  if (function == nullptr || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) {
-    return;
-  }
-
-  TRACE("[ Calling d-tor %s @ %p for '%s' ]", function_name, function, realpath);
-  function();
-  TRACE("[ Done calling d-tor %s @ %p for '%s' ]", function_name, function, realpath);
-}
-
-template <typename F>
-static void call_array(const char* array_name __unused,
-                       F* functions,
-                       size_t count,
-                       bool reverse,
-                       const char* realpath) {
-  if (functions == nullptr) {
-    return;
-  }
-
-  TRACE("[ Calling %s (size %zd) @ %p for '%s' ]", array_name, count, functions, realpath);
-
-  int begin = reverse ? (count - 1) : 0;
-  int end = reverse ? -1 : count;
-  int step = reverse ? -1 : 1;
-
-  for (int i = begin; i != end; i += step) {
-    TRACE("[ %s[%d] == %p ]", array_name, i, functions[i]);
-    call_function("function", functions[i], realpath);
-  }
-
-  TRACE("[ Done calling %s for '%s' ]", array_name, realpath);
-}
-
-void soinfo::call_pre_init_constructors() {
-  // DT_PREINIT_ARRAY functions are called before any other constructors for executables,
-  // but ignored in a shared library.
-  call_array("DT_PREINIT_ARRAY", preinit_array_, preinit_array_count_, false, get_realpath());
-}
-
-void soinfo::call_constructors() {
-  if (constructors_called) {
-    return;
-  }
-
-  // We set constructors_called before actually calling the constructors, otherwise it doesn't
-  // protect against recursive constructor calls. One simple example of constructor recursion
-  // is the libc debug malloc, which is implemented in libc_malloc_debug_leak.so:
-  // 1. The program depends on libc, so libc's constructor is called here.
-  // 2. The libc constructor calls dlopen() to load libc_malloc_debug_leak.so.
-  // 3. dlopen() calls the constructors on the newly created
-  //    soinfo for libc_malloc_debug_leak.so.
-  // 4. The debug .so depends on libc, so CallConstructors is
-  //    called again with the libc soinfo. If it doesn't trigger the early-
-  //    out above, the libc constructor will be called again (recursively!).
-  constructors_called = true;
-
-  if (!is_main_executable() && preinit_array_ != nullptr) {
-    // The GNU dynamic linker silently ignores these, but we warn the developer.
-    PRINT("\"%s\": ignoring DT_PREINIT_ARRAY in shared library!", get_realpath());
-  }
-
-  get_children().for_each([] (soinfo* si) {
-    si->call_constructors();
-  });
-
-  TRACE("\"%s\": calling constructors", get_realpath());
-
-  // DT_INIT should be called before DT_INIT_ARRAY if both are present.
-  call_function("DT_INIT", init_func_, get_realpath());
-  call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false, get_realpath());
-}
-
-void soinfo::call_destructors() {
-  if (!constructors_called) {
-    return;
-  }
-  TRACE("\"%s\": calling destructors", get_realpath());
-
-  // DT_FINI_ARRAY must be parsed in reverse order.
-  call_array("DT_FINI_ARRAY", fini_array_, fini_array_count_, true, get_realpath());
-
-  // DT_FINI should be called after DT_FINI_ARRAY if both are present.
-  call_function("DT_FINI", fini_func_, get_realpath());
-}
-
-void soinfo::add_child(soinfo* child) {
-  if (has_min_version(0)) {
-    child->parents_.push_back(this);
-    this->children_.push_back(child);
-  }
-}
-
-void soinfo::remove_all_links() {
-  if (!has_min_version(0)) {
-    return;
-  }
-
-  // 1. Untie connected soinfos from 'this'.
-  children_.for_each([&] (soinfo* child) {
-    child->parents_.remove_if([&] (const soinfo* parent) {
-      return parent == this;
-    });
-  });
-
-  parents_.for_each([&] (soinfo* parent) {
-    parent->children_.remove_if([&] (const soinfo* child) {
-      return child == this;
-    });
-  });
-
-  // 2. Remove from the primary namespace
-  primary_namespace_->remove_soinfo(this);
-  primary_namespace_ = nullptr;
-
-  // 3. Remove from secondary namespaces
-  secondary_namespaces_.for_each([&](android_namespace_t* ns) {
-    ns->remove_soinfo(this);
-  });
-
-
-  // 4. Once everything untied - clear local lists.
-  parents_.clear();
-  children_.clear();
-  secondary_namespaces_.clear();
-}
-
-dev_t soinfo::get_st_dev() const {
-  if (has_min_version(0)) {
-    return st_dev_;
-  }
-
-  return 0;
-};
-
-ino_t soinfo::get_st_ino() const {
-  if (has_min_version(0)) {
-    return st_ino_;
-  }
-
-  return 0;
-}
-
-off64_t soinfo::get_file_offset() const {
-  if (has_min_version(1)) {
-    return file_offset_;
-  }
-
-  return 0;
-}
-
-uint32_t soinfo::get_rtld_flags() const {
-  if (has_min_version(1)) {
-    return rtld_flags_;
-  }
-
-  return 0;
-}
-
-uint32_t soinfo::get_dt_flags_1() const {
-  if (has_min_version(1)) {
-    return dt_flags_1_;
-  }
-
-  return 0;
-}
-
-void soinfo::set_dt_flags_1(uint32_t dt_flags_1) {
-  if (has_min_version(1)) {
-    if ((dt_flags_1 & DF_1_GLOBAL) != 0) {
-      rtld_flags_ |= RTLD_GLOBAL;
-    }
-
-    if ((dt_flags_1 & DF_1_NODELETE) != 0) {
-      rtld_flags_ |= RTLD_NODELETE;
-    }
-
-    dt_flags_1_ = dt_flags_1;
-  }
-}
-
-void soinfo::set_nodelete() {
-  rtld_flags_ |= RTLD_NODELETE;
-}
-
-const char* soinfo::get_realpath() const {
-#if defined(__work_around_b_24465209__)
-  if (has_min_version(2)) {
-    return realpath_.c_str();
-  } else {
-    return old_name_;
-  }
-#else
-  return realpath_.c_str();
-#endif
-}
-
-void soinfo::set_soname(const char* soname) {
-#if defined(__work_around_b_24465209__)
-  if (has_min_version(2)) {
-    soname_ = soname;
-  }
-  strlcpy(old_name_, soname_, sizeof(old_name_));
-#else
-  soname_ = soname;
-#endif
-}
-
-const char* soinfo::get_soname() const {
-#if defined(__work_around_b_24465209__)
-  if (has_min_version(2)) {
-    return soname_;
-  } else {
-    return old_name_;
-  }
-#else
-  return soname_;
-#endif
-}
-
-// This is a return on get_children()/get_parents() if
-// 'this->flags' does not have FLAG_NEW_SOINFO set.
-static soinfo::soinfo_list_t g_empty_list;
-
-soinfo::soinfo_list_t& soinfo::get_children() {
-  if (has_min_version(0)) {
-    return children_;
-  }
-
-  return g_empty_list;
-}
-
-const soinfo::soinfo_list_t& soinfo::get_children() const {
-  if (has_min_version(0)) {
-    return children_;
-  }
-
-  return g_empty_list;
-}
-
-soinfo::soinfo_list_t& soinfo::get_parents() {
-  if (has_min_version(0)) {
-    return parents_;
-  }
-
-  return g_empty_list;
-}
-
-static std::vector<std::string> g_empty_runpath;
-
-const std::vector<std::string>& soinfo::get_dt_runpath() const {
-  if (has_min_version(3)) {
-    return dt_runpath_;
-  }
-
-  return g_empty_runpath;
-}
-
-android_namespace_t* soinfo::get_primary_namespace() {
-  if (has_min_version(3)) {
-    return primary_namespace_;
-  }
-
-  return &g_default_namespace;
-}
-
-void soinfo::add_secondary_namespace(android_namespace_t* secondary_ns) {
-  CHECK(has_min_version(3));
-  secondary_namespaces_.push_back(secondary_ns);
-}
-
-ElfW(Addr) soinfo::resolve_symbol_address(const ElfW(Sym)* s) const {
-  if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) {
-    return call_ifunc_resolver(s->st_value + load_bias);
-  }
-
-  return static_cast<ElfW(Addr)>(s->st_value + load_bias);
-}
-
-const char* soinfo::get_string(ElfW(Word) index) const {
-  if (has_min_version(1) && (index >= strtab_size_)) {
-    __libc_fatal("%s: strtab out of bounds error; STRSZ=%zd, name=%d",
-        get_realpath(), strtab_size_, index);
-  }
-
-  return strtab_ + index;
-}
-
-bool soinfo::is_gnu_hash() const {
-  return (flags_ & FLAG_GNU_HASH) != 0;
-}
-
-bool soinfo::can_unload() const {
-  return !is_linked() || ((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;
-}
-
-bool soinfo::is_linker() const {
-  return (flags_ & FLAG_LINKER) != 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_;
-}
-
-
-void soinfo::set_mapped_by_caller(bool mapped_by_caller) {
-  if (mapped_by_caller) {
-    flags_ |= FLAG_MAPPED_BY_CALLER;
-  } else {
-    flags_ &= ~FLAG_MAPPED_BY_CALLER;
-  }
-}
-
-bool soinfo::is_mapped_by_caller() const {
-  return (flags_ & FLAG_MAPPED_BY_CALLER) != 0;
-}
-
-// This function returns api-level at the time of
-// dlopen/load. Note that libraries opened by system
-// will always have 'current' api level.
-uint32_t soinfo::get_target_sdk_version() const {
-  if (!has_min_version(2)) {
-    return __ANDROID_API__;
-  }
-
-  return local_group_root_->target_sdk_version_;
-}
-
-uintptr_t soinfo::get_handle() const {
-  CHECK(has_min_version(3));
-  CHECK(handle_ != 0);
-  return handle_;
-}
-
-void* soinfo::to_handle() {
-  if (get_application_target_sdk_version() <= 23 || !has_min_version(3)) {
-    return this;
-  }
-
-  return reinterpret_cast<void*>(get_handle());
-}
-
-void soinfo::generate_handle() {
-  CHECK(has_min_version(3));
-  CHECK(handle_ == 0); // Make sure this is the first call
-
-  // Make sure the handle is unique and does not collide
-  // with special values which are RTLD_DEFAULT and RTLD_NEXT.
-  do {
-    arc4random_buf(&handle_, sizeof(handle_));
-    // the least significant bit for the handle is always 1
-    // making it easy to test the type of handle passed to
-    // dl* functions.
-    handle_ = handle_ | 1;
-  } while (handle_ == reinterpret_cast<uintptr_t>(RTLD_DEFAULT) ||
-           handle_ == reinterpret_cast<uintptr_t>(RTLD_NEXT) ||
-           g_soinfo_handles_map.find(handle_) != g_soinfo_handles_map.end());
-
-  g_soinfo_handles_map[handle_] = this;
-}
+// An empty list of soinfos
+static soinfo_list_t g_empty_list;
 
 bool soinfo::prelink_image() {
   /* Extract dynamic section */
@@ -4144,7 +3239,7 @@
   si->load_bias = get_elf_exec_load_bias(ehdr_vdso);
 
   si->prelink_image();
-  si->link_image(g_empty_list, soinfo::soinfo_list_t::make_list(si), nullptr);
+  si->link_image(g_empty_list, soinfo_list_t::make_list(si), nullptr);
 #endif
 }
 
@@ -4362,7 +3457,7 @@
                       /* add_as_children */ true)) {
     __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer());
   } else if (needed_libraries_count == 0) {
-    if (!si->link_image(g_empty_list, soinfo::soinfo_list_t::make_list(si), nullptr)) {
+    if (!si->link_image(g_empty_list, soinfo_list_t::make_list(si), nullptr)) {
       __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer());
     }
     si->increment_ref_count();
@@ -4454,8 +3549,8 @@
   return 0;
 }
 
-static void __linker_cannot_link() {
-  __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer());
+static void __linker_cannot_link(const char* argv0) {
+  __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", argv0, linker_get_error_buffer());
 }
 
 /*
@@ -4470,10 +3565,6 @@
 extern "C" ElfW(Addr) __linker_init(void* raw_args) {
   KernelArgumentBlock args(raw_args);
 
-  g_argc = args.argc;
-  g_argv = args.argv;
-  g_envp = args.envp;
-
   ElfW(Addr) linker_addr = args.getauxval(AT_BASE);
   ElfW(Addr) entry_point = args.getauxval(AT_ENTRY);
   ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_addr);
@@ -4490,7 +3581,7 @@
   if (reinterpret_cast<ElfW(Addr)>(&_start) == entry_point) {
     __libc_format_fd(STDOUT_FILENO,
                      "This is %s, the helper program for shared library executables.\n",
-                     g_argv[0]);
+                     args.argv[0]);
     exit(0);
   }
 
@@ -4503,7 +3594,7 @@
   linker_so.set_linker_flag();
 
   // Prelink the linker so we can access linker globals.
-  if (!linker_so.prelink_image()) __linker_cannot_link();
+  if (!linker_so.prelink_image()) __linker_cannot_link(args.argv[0]);
 
   // 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
@@ -4511,7 +3602,7 @@
   // itself without having to look into local_group and (2) allocators
   // are not yet initialized, and therefore we cannot use linked_list.push_*
   // functions at this point.
-  if (!linker_so.link_image(g_empty_list, g_empty_list, nullptr)) __linker_cannot_link();
+  if (!linker_so.link_image(g_empty_list, g_empty_list, nullptr)) __linker_cannot_link(args.argv[0]);
 
 #if defined(__i386__)
   // On x86, we can't make system calls before this point.
@@ -4527,11 +3618,16 @@
 
   // We didn't protect the linker's RELRO pages in link_image because we
   // couldn't make system calls on x86 at that point, but we can now...
-  if (!linker_so.protect_relro()) __linker_cannot_link();
+  if (!linker_so.protect_relro()) __linker_cannot_link(args.argv[0]);
 
   // Initialize the linker's static libc's globals
   __libc_init_globals(args);
 
+  // store argc/argv/envp to use them for calling constructors
+  g_argc = args.argc;
+  g_argv = args.argv;
+  g_envp = args.envp;
+
   // Initialize the linker's own global variables
   linker_so.call_constructors();
 
diff --git a/linker/linker.h b/linker/linker.h
index d8c0e19..aa6d00b 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -40,26 +40,13 @@
 #include "private/bionic_page.h"
 #include "private/libc_logging.h"
 #include "linked_list.h"
+#include "linker_common_types.h"
 #include "linker_logger.h"
+#include "linker_soinfo.h"
 
 #include <string>
 #include <vector>
 
-#define DL_ERR(fmt, x...) \
-    do { \
-      __libc_format_buffer(linker_get_error_buffer(), linker_get_error_buffer_size(), fmt, ##x); \
-      /* If LD_DEBUG is set high enough, log every dlerror(3) message. */ \
-      LD_LOG(kLogErrors, "%s\n", linker_get_error_buffer()); \
-    } while (false)
-
-#define DL_WARN(fmt, x...) \
-    do { \
-      __libc_format_log(ANDROID_LOG_WARN, "linker", fmt, ##x); \
-      __libc_format_fd(2, "WARNING: linker: "); \
-      __libc_format_fd(2, fmt, ##x); \
-      __libc_format_fd(2, "\n"); \
-    } while (false)
-
 #if defined(__LP64__)
 #define ELFW(what) ELF64_ ## what
 #else
@@ -80,83 +67,8 @@
 #define ELF64_R_TYPE(info)  (((info) >> 56) & 0xff)
 #endif
 
-#define FLAG_LINKED           0x00000001
-#define FLAG_EXE              0x00000004 // The main executable
-#define FLAG_LINKER           0x00000010 // The linker itself
-#define FLAG_GNU_HASH         0x00000040 // uses gnu hash
-#define FLAG_MAPPED_BY_CALLER 0x00000080 // the map is reserved by the caller
-                                         // and should not be unmapped
-#define FLAG_NEW_SOINFO       0x40000000 // new soinfo format
-
 #define SUPPORTED_DT_FLAGS_1 (DF_1_NOW | DF_1_GLOBAL | DF_1_NODELETE)
 
-#define SOINFO_VERSION 3
-
-#if defined(__work_around_b_24465209__)
-#define SOINFO_NAME_LEN 128
-#endif
-
-typedef void (*linker_dtor_function_t)();
-typedef void (*linker_ctor_function_t)(int, char**, char**);
-
-// Android uses RELA for aarch64 and x86_64. mips64 still uses REL.
-#if defined(__aarch64__) || defined(__x86_64__)
-#define USE_RELA 1
-#endif
-
-struct soinfo;
-
-class SoinfoListAllocator {
- public:
-  static LinkedListEntry<soinfo>* alloc();
-  static void free(LinkedListEntry<soinfo>* entry);
-
- private:
-  // unconstructable
-  DISALLOW_IMPLICIT_CONSTRUCTORS(SoinfoListAllocator);
-};
-
-class NamespaceListAllocator {
- public:
-  static LinkedListEntry<android_namespace_t>* alloc();
-  static void free(LinkedListEntry<android_namespace_t>* entry);
-
- private:
-  // unconstructable
-  DISALLOW_IMPLICIT_CONSTRUCTORS(NamespaceListAllocator);
-};
-
-class SymbolName {
- public:
-  explicit SymbolName(const char* name)
-      : name_(name), has_elf_hash_(false), has_gnu_hash_(false),
-        elf_hash_(0), gnu_hash_(0) { }
-
-  const char* get_name() {
-    return name_;
-  }
-
-  uint32_t elf_hash();
-  uint32_t gnu_hash();
-
- private:
-  const char* name_;
-  bool has_elf_hash_;
-  bool has_gnu_hash_;
-  uint32_t elf_hash_;
-  uint32_t gnu_hash_;
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(SymbolName);
-};
-
-struct version_info {
-  constexpr version_info() : elf_hash(0), name(nullptr), target_si(nullptr) {}
-
-  uint32_t elf_hash;
-  const char* name;
-  const soinfo* target_si;
-};
-
 // Class used construct version dependency graph.
 class VersionTracker {
  public:
@@ -175,267 +87,9 @@
   DISALLOW_COPY_AND_ASSIGN(VersionTracker);
 };
 
-struct soinfo {
- public:
-  typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t;
-  typedef LinkedList<android_namespace_t, NamespaceListAllocator> android_namespace_list_t;
-#if defined(__work_around_b_24465209__)
- private:
-  char old_name_[SOINFO_NAME_LEN];
-#endif
- public:
-  const ElfW(Phdr)* phdr;
-  size_t phnum;
-#if defined(__work_around_b_24465209__)
-  ElfW(Addr) unused0; // DO NOT USE, maintained for compatibility.
-#endif
-  ElfW(Addr) base;
-  size_t size;
-
-#if defined(__work_around_b_24465209__)
-  uint32_t unused1;  // DO NOT USE, maintained for compatibility.
-#endif
-
-  ElfW(Dyn)* dynamic;
-
-#if defined(__work_around_b_24465209__)
-  uint32_t unused2; // DO NOT USE, maintained for compatibility
-  uint32_t unused3; // DO NOT USE, maintained for compatibility
-#endif
-
-  soinfo* next;
- private:
-  uint32_t flags_;
-
-  const char* strtab_;
-  ElfW(Sym)* symtab_;
-
-  size_t nbucket_;
-  size_t nchain_;
-  uint32_t* bucket_;
-  uint32_t* chain_;
-
-#if defined(__mips__) || !defined(__LP64__)
-  // This is only used by mips and mips64, but needs to be here for
-  // all 32-bit architectures to preserve binary compatibility.
-  ElfW(Addr)** plt_got_;
-#endif
-
-#if defined(USE_RELA)
-  ElfW(Rela)* plt_rela_;
-  size_t plt_rela_count_;
-
-  ElfW(Rela)* rela_;
-  size_t rela_count_;
-#else
-  ElfW(Rel)* plt_rel_;
-  size_t plt_rel_count_;
-
-  ElfW(Rel)* rel_;
-  size_t rel_count_;
-#endif
-
-  linker_ctor_function_t* preinit_array_;
-  size_t preinit_array_count_;
-
-  linker_ctor_function_t* init_array_;
-  size_t init_array_count_;
-  linker_dtor_function_t* fini_array_;
-  size_t fini_array_count_;
-
-  linker_ctor_function_t init_func_;
-  linker_dtor_function_t fini_func_;
-
-#if defined(__arm__)
- public:
-  // ARM EABI section used for stack unwinding.
-  uint32_t* ARM_exidx;
-  size_t ARM_exidx_count;
- private:
-#elif defined(__mips__)
-  uint32_t mips_symtabno_;
-  uint32_t mips_local_gotno_;
-  uint32_t mips_gotsym_;
-  bool mips_relocate_got(const VersionTracker& version_tracker,
-                         const soinfo_list_t& global_group,
-                         const soinfo_list_t& local_group);
-#if !defined(__LP64__)
-  bool mips_check_and_adjust_fp_modes();
-#endif
-#endif
-  size_t ref_count_;
- public:
-  link_map link_map_head;
-
-  bool constructors_called;
-
-  // When you read a virtual address from the ELF file, add this
-  // value to get the corresponding address in the process' address space.
-  ElfW(Addr) load_bias;
-
-#if !defined(__LP64__)
-  bool has_text_relocations;
-#endif
-  bool has_DT_SYMBOLIC;
-
- public:
-  soinfo(android_namespace_t* ns, const char* name, const struct stat* file_stat,
-         off64_t file_offset, int rtld_flags);
-  ~soinfo();
-
-  void call_constructors();
-  void call_destructors();
-  void call_pre_init_constructors();
-  bool prelink_image();
-  bool link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group,
-                  const android_dlextinfo* extinfo);
-  bool protect_relro();
-
-  void add_child(soinfo* child);
-  void remove_all_links();
-
-  ino_t get_st_ino() const;
-  dev_t get_st_dev() const;
-  off64_t get_file_offset() const;
-
-  uint32_t get_rtld_flags() const;
-  uint32_t get_dt_flags_1() const;
-  void set_dt_flags_1(uint32_t dt_flags_1);
-
-  soinfo_list_t& get_children();
-  const soinfo_list_t& get_children() const;
-
-  soinfo_list_t& get_parents();
-
-  bool find_symbol_by_name(SymbolName& symbol_name,
-                           const version_info* vi,
-                           const ElfW(Sym)** symbol) const;
-
-  ElfW(Sym)* find_symbol_by_address(const void* addr);
-  ElfW(Addr) resolve_symbol_address(const ElfW(Sym)* s) const;
-
-  const char* get_string(ElfW(Word) index) const;
-  bool can_unload() const;
-  bool is_gnu_hash() const;
-
-  bool inline has_min_version(uint32_t min_version __unused) const {
-#if defined(__work_around_b_24465209__)
-    return (flags_ & FLAG_NEW_SOINFO) != 0 && version_ >= min_version;
-#else
-    return true;
-#endif
-  }
-
-  bool is_linked() const;
-  bool is_linker() const;
-  bool is_main_executable() const;
-
-  void set_linked();
-  void set_linker_flag();
-  void set_main_executable();
-  void set_nodelete();
-
-  void increment_ref_count();
-  size_t decrement_ref_count();
-
-  soinfo* get_local_group_root() const;
-
-  void set_soname(const char* soname);
-  const char* get_soname() const;
-  const char* get_realpath() const;
-  const ElfW(Versym)* get_versym(size_t n) const;
-  ElfW(Addr) get_verneed_ptr() const;
-  size_t get_verneed_cnt() const;
-  ElfW(Addr) get_verdef_ptr() const;
-  size_t get_verdef_cnt() const;
-
-  bool find_verdef_version_index(const version_info* vi, ElfW(Versym)* versym) const;
-
-  uint32_t get_target_sdk_version() const;
-
-  void set_dt_runpath(const char *);
-  const std::vector<std::string>& get_dt_runpath() const;
-  android_namespace_t* get_primary_namespace();
-  void add_secondary_namespace(android_namespace_t* secondary_ns);
-
-  void set_mapped_by_caller(bool reserved_map);
-  bool is_mapped_by_caller() const;
-
-  uintptr_t get_handle() const;
-  void generate_handle();
-  void* to_handle();
-
- private:
-  bool elf_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
-  ElfW(Sym)* elf_addr_lookup(const void* addr);
-  bool gnu_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
-  ElfW(Sym)* gnu_addr_lookup(const void* addr);
-
-  bool lookup_version_info(const VersionTracker& version_tracker, ElfW(Word) sym,
-                           const char* sym_name, const version_info** vi);
-
-  template<typename ElfRelIteratorT>
-  bool relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& rel_iterator,
-                const soinfo_list_t& global_group, const soinfo_list_t& local_group);
-
- private:
-  // This part of the structure is only available
-  // when FLAG_NEW_SOINFO is set in this->flags.
-  uint32_t version_;
-
-  // version >= 0
-  dev_t st_dev_;
-  ino_t st_ino_;
-
-  // dependency graph
-  soinfo_list_t children_;
-  soinfo_list_t parents_;
-
-  // version >= 1
-  off64_t file_offset_;
-  uint32_t rtld_flags_;
-  uint32_t dt_flags_1_;
-  size_t strtab_size_;
-
-  // version >= 2
-
-  size_t gnu_nbucket_;
-  uint32_t* gnu_bucket_;
-  uint32_t* gnu_chain_;
-  uint32_t gnu_maskwords_;
-  uint32_t gnu_shift2_;
-  ElfW(Addr)* gnu_bloom_filter_;
-
-  soinfo* local_group_root_;
-
-  uint8_t* android_relocs_;
-  size_t android_relocs_size_;
-
-  const char* soname_;
-  std::string realpath_;
-
-  const ElfW(Versym)* versym_;
-
-  ElfW(Addr) verdef_ptr_;
-  size_t verdef_cnt_;
-
-  ElfW(Addr) verneed_ptr_;
-  size_t verneed_cnt_;
-
-  uint32_t target_sdk_version_;
-
-  // version >= 3
-  std::vector<std::string> dt_runpath_;
-  android_namespace_t* primary_namespace_;
-  android_namespace_list_t secondary_namespaces_;
-  uintptr_t handle_;
-
-  friend soinfo* get_libdl_info();
-};
-
 bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi,
-                      soinfo** si_found_in, const soinfo::soinfo_list_t& global_group,
-                      const soinfo::soinfo_list_t& local_group, const ElfW(Sym)** symbol);
+                      soinfo** si_found_in, const soinfo_list_t& global_group,
+                      const soinfo_list_t& local_group, const ElfW(Sym)** symbol);
 
 enum RelocationKind {
   kRelocAbsolute = 0,
@@ -461,9 +115,6 @@
 
 int do_dladdr(const void* addr, Dl_info* info);
 
-char* linker_get_error_buffer();
-size_t linker_get_error_buffer_size();
-
 void set_application_target_sdk_version(uint32_t target);
 uint32_t get_application_target_sdk_version();
 
diff --git a/linker/linker_common_types.h b/linker/linker_common_types.h
new file mode 100644
index 0000000..6afd950
--- /dev/null
+++ b/linker/linker_common_types.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __LINKER_COMMON_TYPES_H
+#define __LINKER_COMMON_TYPES_H
+
+#include <android/dlext.h>
+#include "linked_list.h"
+
+// TODO(dimitry): move this to linker_defines.h? Unless it is removed by
+// consequent refactoring steps.
+
+// Android uses RELA for aarch64 and x86_64. mips64 still uses REL.
+#if defined(__aarch64__) || defined(__x86_64__)
+#define USE_RELA 1
+#endif
+
+
+struct soinfo;
+
+class SoinfoListAllocator {
+ public:
+  static LinkedListEntry<soinfo>* alloc();
+  static void free(LinkedListEntry<soinfo>* entry);
+
+ private:
+  // unconstructable
+  DISALLOW_IMPLICIT_CONSTRUCTORS(SoinfoListAllocator);
+};
+
+class NamespaceListAllocator {
+ public:
+  static LinkedListEntry<android_namespace_t>* alloc();
+  static void free(LinkedListEntry<android_namespace_t>* entry);
+
+ private:
+  // unconstructable
+  DISALLOW_IMPLICIT_CONSTRUCTORS(NamespaceListAllocator);
+};
+
+typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t;
+typedef LinkedList<android_namespace_t, NamespaceListAllocator> android_namespace_list_t;
+
+#endif  /* __LINKER_COMMON_TYPES_H */
diff --git a/linker/linker_globals.cpp b/linker/linker_globals.cpp
new file mode 100644
index 0000000..155ebf4
--- /dev/null
+++ b/linker/linker_globals.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "linker_globals.h"
+#include "linker_namespaces.h"
+
+int g_argc = 0;
+char** g_argv = nullptr;
+char** g_envp = nullptr;
+
+android_namespace_t g_default_namespace;
+
+std::unordered_map<uintptr_t, soinfo*> g_soinfo_handles_map;
+
+static char __linker_dl_err_buf[768];
+
+char* linker_get_error_buffer() {
+  return &__linker_dl_err_buf[0];
+}
+
+size_t linker_get_error_buffer_size() {
+  return sizeof(__linker_dl_err_buf);
+}
+
diff --git a/linker/linker_globals.h b/linker/linker_globals.h
new file mode 100644
index 0000000..265af27
--- /dev/null
+++ b/linker/linker_globals.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __LINKER_GLOBALS_H
+#define __LINKER_GLOBALS_H
+
+#include <link.h>
+#include <stddef.h>
+
+#include <unordered_map>
+
+#define DL_ERR(fmt, x...) \
+    do { \
+      __libc_format_buffer(linker_get_error_buffer(), linker_get_error_buffer_size(), fmt, ##x); \
+      /* If LD_DEBUG is set high enough, log every dlerror(3) message. */ \
+      LD_LOG(kLogErrors, "%s\n", linker_get_error_buffer()); \
+    } while (false)
+
+#define DL_WARN(fmt, x...) \
+    do { \
+      __libc_format_log(ANDROID_LOG_WARN, "linker", fmt, ##x); \
+      __libc_format_fd(2, "WARNING: linker: "); \
+      __libc_format_fd(2, fmt, ##x); \
+      __libc_format_fd(2, "\n"); \
+    } while (false)
+
+constexpr ElfW(Versym) kVersymNotNeeded = 0;
+constexpr ElfW(Versym) kVersymGlobal = 1;
+
+// These values are used to call constructors for .init_array && .preinit_array
+extern int g_argc;
+extern char** g_argv;
+extern char** g_envp;
+
+struct soinfo;
+struct android_namespace_t;
+
+extern android_namespace_t g_default_namespace;
+
+extern std::unordered_map<uintptr_t, soinfo*> g_soinfo_handles_map;
+
+// Error buffer "variable"
+char* linker_get_error_buffer();
+size_t linker_get_error_buffer_size();
+
+#endif  /* __LINKER_GLOBALS_H */
diff --git a/linker/linker_namespaces.cpp b/linker/linker_namespaces.cpp
new file mode 100644
index 0000000..675f324
--- /dev/null
+++ b/linker/linker_namespaces.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "linker_namespaces.h"
+#include "linker_utils.h"
+
+#include <vector>
+
+bool android_namespace_t::is_accessible(const std::string& file) {
+  if (!is_isolated_) {
+    return true;
+  }
+
+  for (const auto& dir : ld_library_paths_) {
+    if (file_is_in_dir(file, dir)) {
+      return true;
+    }
+  }
+
+  for (const auto& dir : default_library_paths_) {
+    if (file_is_in_dir(file, dir)) {
+      return true;
+    }
+  }
+
+  for (const auto& dir : permitted_paths_) {
+    if (file_is_under_dir(file, dir)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
diff --git a/linker/linker_namespaces.h b/linker/linker_namespaces.h
new file mode 100644
index 0000000..c1cee8e
--- /dev/null
+++ b/linker/linker_namespaces.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __LINKER_NAMESPACES_H
+#define __LINKER_NAMESPACES_H
+
+#include "linker_common_types.h"
+
+#include <string>
+#include <vector>
+
+struct android_namespace_t {
+ public:
+  android_namespace_t() : name_(nullptr), is_isolated_(false) {}
+
+  const char* get_name() const { return name_; }
+  void set_name(const char* name) { name_ = name; }
+
+  bool is_isolated() const { return is_isolated_; }
+  void set_isolated(bool isolated) { is_isolated_ = isolated; }
+
+  const std::vector<std::string>& get_ld_library_paths() const {
+    return ld_library_paths_;
+  }
+  void set_ld_library_paths(std::vector<std::string>&& library_paths) {
+    ld_library_paths_ = library_paths;
+  }
+
+  const std::vector<std::string>& get_default_library_paths() const {
+    return default_library_paths_;
+  }
+  void set_default_library_paths(std::vector<std::string>&& library_paths) {
+    default_library_paths_ = library_paths;
+  }
+
+  const std::vector<std::string>& get_permitted_paths() const {
+    return permitted_paths_;
+  }
+  void set_permitted_paths(std::vector<std::string>&& permitted_paths) {
+    permitted_paths_ = permitted_paths;
+  }
+
+  void add_soinfo(soinfo* si) {
+    soinfo_list_.push_back(si);
+  }
+
+  void add_soinfos(const soinfo_list_t& soinfos) {
+    for (auto si : soinfos) {
+      add_soinfo(si);
+    }
+  }
+
+  void remove_soinfo(soinfo* si) {
+    soinfo_list_.remove_if([&](soinfo* candidate) {
+      return si == candidate;
+    });
+  }
+
+  const soinfo_list_t& soinfo_list() const { return soinfo_list_; }
+
+  // For isolated namespaces - checks if the file is on the search path;
+  // always returns true for not isolated namespace.
+  bool is_accessible(const std::string& path);
+
+ private:
+  const char* name_;
+  bool is_isolated_;
+  std::vector<std::string> ld_library_paths_;
+  std::vector<std::string> default_library_paths_;
+  std::vector<std::string> permitted_paths_;
+  soinfo_list_t soinfo_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(android_namespace_t);
+};
+
+#endif  /* __LINKER_NAMESPACES_H */
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index ebc3166..5f1d280 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -36,6 +36,7 @@
 #include <unistd.h>
 
 #include "linker.h"
+#include "linker_globals.h"
 #include "linker_debug.h"
 #include "linker_utils.h"
 
diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp
new file mode 100644
index 0000000..59bdc4d
--- /dev/null
+++ b/linker/linker_soinfo.cpp
@@ -0,0 +1,795 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "linker_soinfo.h"
+
+#include <dlfcn.h>
+#include <elf.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "linker_debug.h"
+#include "linker_globals.h"
+#include "linker_logger.h"
+#include "linker_utils.h"
+
+// TODO(dimitry): These functions are currently located in linker.cpp - find a better place for it
+bool find_verdef_version_index(const soinfo* si, const version_info* vi, ElfW(Versym)* versym);
+ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr);
+uint32_t get_application_target_sdk_version();
+
+soinfo::soinfo(android_namespace_t* ns, const char* realpath,
+               const struct stat* file_stat, off64_t file_offset,
+               int rtld_flags) {
+  memset(this, 0, sizeof(*this));
+
+  if (realpath != nullptr) {
+    realpath_ = realpath;
+  }
+
+  flags_ = FLAG_NEW_SOINFO;
+  version_ = SOINFO_VERSION;
+
+  if (file_stat != nullptr) {
+    this->st_dev_ = file_stat->st_dev;
+    this->st_ino_ = file_stat->st_ino;
+    this->file_offset_ = file_offset;
+  }
+
+  this->rtld_flags_ = rtld_flags;
+  this->primary_namespace_ = ns;
+}
+
+soinfo::~soinfo() {
+  g_soinfo_handles_map.erase(handle_);
+}
+
+void soinfo::set_dt_runpath(const char* path) {
+  if (!has_min_version(3)) {
+    return;
+  }
+
+  std::vector<std::string> runpaths;
+
+  split_path(path, ":", &runpaths);
+
+  std::string origin = dirname(get_realpath());
+  // FIXME: add $LIB and $PLATFORM.
+  std::pair<std::string, std::string> substs[] = {{"ORIGIN", origin}};
+  for (auto&& s : runpaths) {
+    size_t pos = 0;
+    while (pos < s.size()) {
+      pos = s.find("$", pos);
+      if (pos == std::string::npos) break;
+      for (const auto& subst : substs) {
+        const std::string& token = subst.first;
+        const std::string& replacement = subst.second;
+        if (s.substr(pos + 1, token.size()) == token) {
+          s.replace(pos, token.size() + 1, replacement);
+          // -1 to compensate for the ++pos below.
+          pos += replacement.size() - 1;
+          break;
+        } else if (s.substr(pos + 1, token.size() + 2) == "{" + token + "}") {
+          s.replace(pos, token.size() + 3, replacement);
+          pos += replacement.size() - 1;
+          break;
+        }
+      }
+      // Skip $ in case it did not match any of the known substitutions.
+      ++pos;
+    }
+  }
+
+  resolve_paths(runpaths, &dt_runpath_);
+}
+
+const ElfW(Versym)* soinfo::get_versym(size_t n) const {
+  if (has_min_version(2) && versym_ != nullptr) {
+    return versym_ + n;
+  }
+
+  return nullptr;
+}
+
+ElfW(Addr) soinfo::get_verneed_ptr() const {
+  if (has_min_version(2)) {
+    return verneed_ptr_;
+  }
+
+  return 0;
+}
+
+size_t soinfo::get_verneed_cnt() const {
+  if (has_min_version(2)) {
+    return verneed_cnt_;
+  }
+
+  return 0;
+}
+
+ElfW(Addr) soinfo::get_verdef_ptr() const {
+  if (has_min_version(2)) {
+    return verdef_ptr_;
+  }
+
+  return 0;
+}
+
+size_t soinfo::get_verdef_cnt() const {
+  if (has_min_version(2)) {
+    return verdef_cnt_;
+  }
+
+  return 0;
+}
+
+bool soinfo::find_symbol_by_name(SymbolName& symbol_name,
+                                 const version_info* vi,
+                                 const ElfW(Sym)** symbol) const {
+  uint32_t symbol_index;
+  bool success =
+      is_gnu_hash() ?
+      gnu_lookup(symbol_name, vi, &symbol_index) :
+      elf_lookup(symbol_name, vi, &symbol_index);
+
+  if (success) {
+    *symbol = symbol_index == 0 ? nullptr : symtab_ + symbol_index;
+  }
+
+  return success;
+}
+
+static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) {
+  if (ELF_ST_BIND(s->st_info) == STB_GLOBAL ||
+      ELF_ST_BIND(s->st_info) == STB_WEAK) {
+    return s->st_shndx != SHN_UNDEF;
+  } else if (ELF_ST_BIND(s->st_info) != STB_LOCAL) {
+    DL_WARN("unexpected ST_BIND value: %d for \"%s\" in \"%s\"",
+            ELF_ST_BIND(s->st_info), si->get_string(s->st_name), si->get_realpath());
+  }
+
+  return false;
+}
+
+static const ElfW(Versym) kVersymHiddenBit = 0x8000;
+
+static inline bool is_versym_hidden(const ElfW(Versym)* versym) {
+  // the symbol is hidden if bit 15 of versym is set.
+  return versym != nullptr && (*versym & kVersymHiddenBit) != 0;
+}
+
+static inline bool check_symbol_version(const ElfW(Versym) verneed,
+                                        const ElfW(Versym)* verdef) {
+  return verneed == kVersymNotNeeded ||
+      verdef == nullptr ||
+      verneed == (*verdef & ~kVersymHiddenBit);
+}
+
+bool soinfo::gnu_lookup(SymbolName& symbol_name,
+                        const version_info* vi,
+                        uint32_t* symbol_index) const {
+  uint32_t hash = symbol_name.gnu_hash();
+  uint32_t h2 = hash >> gnu_shift2_;
+
+  uint32_t bloom_mask_bits = sizeof(ElfW(Addr))*8;
+  uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_;
+  ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];
+
+  *symbol_index = 0;
+
+  TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)",
+      symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
+
+  // test against bloom filter
+  if ((1 & (bloom_word >> (hash % bloom_mask_bits)) & (bloom_word >> (h2 % bloom_mask_bits))) == 0) {
+    TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
+        symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
+
+    return true;
+  }
+
+  // bloom test says "probably yes"...
+  uint32_t n = gnu_bucket_[hash % gnu_nbucket_];
+
+  if (n == 0) {
+    TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
+        symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
+
+    return true;
+  }
+
+  // lookup versym for the version definition in this library
+  // note the difference between "version is not requested" (vi == nullptr)
+  // and "version not found". In the first case verneed is kVersymNotNeeded
+  // which implies that the default version can be accepted; the second case results in
+  // verneed = 1 (kVersymGlobal) and implies that we should ignore versioned symbols
+  // for this library and consider only *global* ones.
+  ElfW(Versym) verneed = 0;
+  if (!find_verdef_version_index(this, vi, &verneed)) {
+    return false;
+  }
+
+  do {
+    ElfW(Sym)* s = symtab_ + n;
+    const ElfW(Versym)* verdef = get_versym(n);
+    // skip hidden versions when verneed == kVersymNotNeeded (0)
+    if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) {
+        continue;
+    }
+    if (((gnu_chain_[n] ^ hash) >> 1) == 0 &&
+        check_symbol_version(verneed, verdef) &&
+        strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
+        is_symbol_global_and_defined(this, s)) {
+      TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
+          symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(s->st_value),
+          static_cast<size_t>(s->st_size));
+      *symbol_index = n;
+      return true;
+    }
+  } while ((gnu_chain_[n++] & 1) == 0);
+
+  TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
+             symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
+
+  return true;
+}
+
+bool soinfo::elf_lookup(SymbolName& symbol_name,
+                        const version_info* vi,
+                        uint32_t* symbol_index) const {
+  uint32_t hash = symbol_name.elf_hash();
+
+  TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd",
+             symbol_name.get_name(), get_realpath(),
+             reinterpret_cast<void*>(base), hash, hash % nbucket_);
+
+  ElfW(Versym) verneed = 0;
+  if (!find_verdef_version_index(this, vi, &verneed)) {
+    return false;
+  }
+
+  for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) {
+    ElfW(Sym)* s = symtab_ + n;
+    const ElfW(Versym)* verdef = get_versym(n);
+
+    // skip hidden versions when verneed == 0
+    if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) {
+        continue;
+    }
+
+    if (check_symbol_version(verneed, verdef) &&
+        strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
+        is_symbol_global_and_defined(this, s)) {
+      TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
+                 symbol_name.get_name(), get_realpath(),
+                 reinterpret_cast<void*>(s->st_value),
+                 static_cast<size_t>(s->st_size));
+      *symbol_index = n;
+      return true;
+    }
+  }
+
+  TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p %x %zd",
+             symbol_name.get_name(), get_realpath(),
+             reinterpret_cast<void*>(base), hash, hash % nbucket_);
+
+  *symbol_index = 0;
+  return true;
+}
+
+ElfW(Sym)* soinfo::find_symbol_by_address(const void* addr) {
+  return is_gnu_hash() ? gnu_addr_lookup(addr) : elf_addr_lookup(addr);
+}
+
+static bool symbol_matches_soaddr(const ElfW(Sym)* sym, ElfW(Addr) soaddr) {
+  return sym->st_shndx != SHN_UNDEF &&
+      soaddr >= sym->st_value &&
+      soaddr < sym->st_value + sym->st_size;
+}
+
+ElfW(Sym)* soinfo::gnu_addr_lookup(const void* addr) {
+  ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - load_bias;
+
+  for (size_t i = 0; i < gnu_nbucket_; ++i) {
+    uint32_t n = gnu_bucket_[i];
+
+    if (n == 0) {
+      continue;
+    }
+
+    do {
+      ElfW(Sym)* sym = symtab_ + n;
+      if (symbol_matches_soaddr(sym, soaddr)) {
+        return sym;
+      }
+    } while ((gnu_chain_[n++] & 1) == 0);
+  }
+
+  return nullptr;
+}
+
+ElfW(Sym)* soinfo::elf_addr_lookup(const void* addr) {
+  ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - load_bias;
+
+  // Search the library's symbol table for any defined symbol which
+  // contains this address.
+  for (size_t i = 0; i < nchain_; ++i) {
+    ElfW(Sym)* sym = symtab_ + i;
+    if (symbol_matches_soaddr(sym, soaddr)) {
+      return sym;
+    }
+  }
+
+  return nullptr;
+}
+
+static void call_function(const char* function_name __unused,
+                          linker_ctor_function_t function,
+                          const char* realpath __unused) {
+  if (function == nullptr || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) {
+    return;
+  }
+
+  TRACE("[ Calling c-tor %s @ %p for '%s' ]", function_name, function, realpath);
+  function(g_argc, g_argv, g_envp);
+  TRACE("[ Done calling c-tor %s @ %p for '%s' ]", function_name, function, realpath);
+}
+
+static void call_function(const char* function_name __unused,
+                          linker_dtor_function_t function,
+                          const char* realpath __unused) {
+  if (function == nullptr || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) {
+    return;
+  }
+
+  TRACE("[ Calling d-tor %s @ %p for '%s' ]", function_name, function, realpath);
+  function();
+  TRACE("[ Done calling d-tor %s @ %p for '%s' ]", function_name, function, realpath);
+}
+
+template <typename F>
+static void call_array(const char* array_name __unused,
+                       F* functions,
+                       size_t count,
+                       bool reverse,
+                       const char* realpath) {
+  if (functions == nullptr) {
+    return;
+  }
+
+  TRACE("[ Calling %s (size %zd) @ %p for '%s' ]", array_name, count, functions, realpath);
+
+  int begin = reverse ? (count - 1) : 0;
+  int end = reverse ? -1 : count;
+  int step = reverse ? -1 : 1;
+
+  for (int i = begin; i != end; i += step) {
+    TRACE("[ %s[%d] == %p ]", array_name, i, functions[i]);
+    call_function("function", functions[i], realpath);
+  }
+
+  TRACE("[ Done calling %s for '%s' ]", array_name, realpath);
+}
+
+void soinfo::call_pre_init_constructors() {
+  // DT_PREINIT_ARRAY functions are called before any other constructors for executables,
+  // but ignored in a shared library.
+  call_array("DT_PREINIT_ARRAY", preinit_array_, preinit_array_count_, false, get_realpath());
+}
+
+void soinfo::call_constructors() {
+  if (constructors_called) {
+    return;
+  }
+
+  // We set constructors_called before actually calling the constructors, otherwise it doesn't
+  // protect against recursive constructor calls. One simple example of constructor recursion
+  // is the libc debug malloc, which is implemented in libc_malloc_debug_leak.so:
+  // 1. The program depends on libc, so libc's constructor is called here.
+  // 2. The libc constructor calls dlopen() to load libc_malloc_debug_leak.so.
+  // 3. dlopen() calls the constructors on the newly created
+  //    soinfo for libc_malloc_debug_leak.so.
+  // 4. The debug .so depends on libc, so CallConstructors is
+  //    called again with the libc soinfo. If it doesn't trigger the early-
+  //    out above, the libc constructor will be called again (recursively!).
+  constructors_called = true;
+
+  if (!is_main_executable() && preinit_array_ != nullptr) {
+    // The GNU dynamic linker silently ignores these, but we warn the developer.
+    PRINT("\"%s\": ignoring DT_PREINIT_ARRAY in shared library!", get_realpath());
+  }
+
+  get_children().for_each([] (soinfo* si) {
+    si->call_constructors();
+  });
+
+  TRACE("\"%s\": calling constructors", get_realpath());
+
+  // DT_INIT should be called before DT_INIT_ARRAY if both are present.
+  call_function("DT_INIT", init_func_, get_realpath());
+  call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false, get_realpath());
+}
+
+void soinfo::call_destructors() {
+  if (!constructors_called) {
+    return;
+  }
+  TRACE("\"%s\": calling destructors", get_realpath());
+
+  // DT_FINI_ARRAY must be parsed in reverse order.
+  call_array("DT_FINI_ARRAY", fini_array_, fini_array_count_, true, get_realpath());
+
+  // DT_FINI should be called after DT_FINI_ARRAY if both are present.
+  call_function("DT_FINI", fini_func_, get_realpath());
+}
+
+void soinfo::add_child(soinfo* child) {
+  if (has_min_version(0)) {
+    child->parents_.push_back(this);
+    this->children_.push_back(child);
+  }
+}
+
+void soinfo::remove_all_links() {
+  if (!has_min_version(0)) {
+    return;
+  }
+
+  // 1. Untie connected soinfos from 'this'.
+  children_.for_each([&] (soinfo* child) {
+    child->parents_.remove_if([&] (const soinfo* parent) {
+      return parent == this;
+    });
+  });
+
+  parents_.for_each([&] (soinfo* parent) {
+    parent->children_.remove_if([&] (const soinfo* child) {
+      return child == this;
+    });
+  });
+
+  // 2. Remove from the primary namespace
+  primary_namespace_->remove_soinfo(this);
+  primary_namespace_ = nullptr;
+
+  // 3. Remove from secondary namespaces
+  secondary_namespaces_.for_each([&](android_namespace_t* ns) {
+    ns->remove_soinfo(this);
+  });
+
+
+  // 4. Once everything untied - clear local lists.
+  parents_.clear();
+  children_.clear();
+  secondary_namespaces_.clear();
+}
+
+dev_t soinfo::get_st_dev() const {
+  if (has_min_version(0)) {
+    return st_dev_;
+  }
+
+  return 0;
+};
+
+ino_t soinfo::get_st_ino() const {
+  if (has_min_version(0)) {
+    return st_ino_;
+  }
+
+  return 0;
+}
+
+off64_t soinfo::get_file_offset() const {
+  if (has_min_version(1)) {
+    return file_offset_;
+  }
+
+  return 0;
+}
+
+uint32_t soinfo::get_rtld_flags() const {
+  if (has_min_version(1)) {
+    return rtld_flags_;
+  }
+
+  return 0;
+}
+
+uint32_t soinfo::get_dt_flags_1() const {
+  if (has_min_version(1)) {
+    return dt_flags_1_;
+  }
+
+  return 0;
+}
+
+void soinfo::set_dt_flags_1(uint32_t dt_flags_1) {
+  if (has_min_version(1)) {
+    if ((dt_flags_1 & DF_1_GLOBAL) != 0) {
+      rtld_flags_ |= RTLD_GLOBAL;
+    }
+
+    if ((dt_flags_1 & DF_1_NODELETE) != 0) {
+      rtld_flags_ |= RTLD_NODELETE;
+    }
+
+    dt_flags_1_ = dt_flags_1;
+  }
+}
+
+void soinfo::set_nodelete() {
+  rtld_flags_ |= RTLD_NODELETE;
+}
+
+const char* soinfo::get_realpath() const {
+#if defined(__work_around_b_24465209__)
+  if (has_min_version(2)) {
+    return realpath_.c_str();
+  } else {
+    return old_name_;
+  }
+#else
+  return realpath_.c_str();
+#endif
+}
+
+void soinfo::set_soname(const char* soname) {
+#if defined(__work_around_b_24465209__)
+  if (has_min_version(2)) {
+    soname_ = soname;
+  }
+  strlcpy(old_name_, soname_, sizeof(old_name_));
+#else
+  soname_ = soname;
+#endif
+}
+
+const char* soinfo::get_soname() const {
+#if defined(__work_around_b_24465209__)
+  if (has_min_version(2)) {
+    return soname_;
+  } else {
+    return old_name_;
+  }
+#else
+  return soname_;
+#endif
+}
+
+// This is a return on get_children()/get_parents() if
+// 'this->flags' does not have FLAG_NEW_SOINFO set.
+static soinfo_list_t g_empty_list;
+
+soinfo_list_t& soinfo::get_children() {
+  if (has_min_version(0)) {
+    return children_;
+  }
+
+  return g_empty_list;
+}
+
+const soinfo_list_t& soinfo::get_children() const {
+  if (has_min_version(0)) {
+    return children_;
+  }
+
+  return g_empty_list;
+}
+
+soinfo_list_t& soinfo::get_parents() {
+  if (has_min_version(0)) {
+    return parents_;
+  }
+
+  return g_empty_list;
+}
+
+static std::vector<std::string> g_empty_runpath;
+
+const std::vector<std::string>& soinfo::get_dt_runpath() const {
+  if (has_min_version(3)) {
+    return dt_runpath_;
+  }
+
+  return g_empty_runpath;
+}
+
+android_namespace_t* soinfo::get_primary_namespace() {
+  if (has_min_version(3)) {
+    return primary_namespace_;
+  }
+
+  return &g_default_namespace;
+}
+
+void soinfo::add_secondary_namespace(android_namespace_t* secondary_ns) {
+  CHECK(has_min_version(3));
+  secondary_namespaces_.push_back(secondary_ns);
+}
+
+ElfW(Addr) soinfo::resolve_symbol_address(const ElfW(Sym)* s) const {
+  if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) {
+    return call_ifunc_resolver(s->st_value + load_bias);
+  }
+
+  return static_cast<ElfW(Addr)>(s->st_value + load_bias);
+}
+
+const char* soinfo::get_string(ElfW(Word) index) const {
+  if (has_min_version(1) && (index >= strtab_size_)) {
+    __libc_fatal("%s: strtab out of bounds error; STRSZ=%zd, name=%d",
+        get_realpath(), strtab_size_, index);
+  }
+
+  return strtab_ + index;
+}
+
+bool soinfo::is_gnu_hash() const {
+  return (flags_ & FLAG_GNU_HASH) != 0;
+}
+
+bool soinfo::can_unload() const {
+  return !is_linked() || ((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;
+}
+
+bool soinfo::is_linker() const {
+  return (flags_ & FLAG_LINKER) != 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_;
+}
+
+
+void soinfo::set_mapped_by_caller(bool mapped_by_caller) {
+  if (mapped_by_caller) {
+    flags_ |= FLAG_MAPPED_BY_CALLER;
+  } else {
+    flags_ &= ~FLAG_MAPPED_BY_CALLER;
+  }
+}
+
+bool soinfo::is_mapped_by_caller() const {
+  return (flags_ & FLAG_MAPPED_BY_CALLER) != 0;
+}
+
+// This function returns api-level at the time of
+// dlopen/load. Note that libraries opened by system
+// will always have 'current' api level.
+uint32_t soinfo::get_target_sdk_version() const {
+  if (!has_min_version(2)) {
+    return __ANDROID_API__;
+  }
+
+  return local_group_root_->target_sdk_version_;
+}
+
+uintptr_t soinfo::get_handle() const {
+  CHECK(has_min_version(3));
+  CHECK(handle_ != 0);
+  return handle_;
+}
+
+void* soinfo::to_handle() {
+  if (get_application_target_sdk_version() <= 23 || !has_min_version(3)) {
+    return this;
+  }
+
+  return reinterpret_cast<void*>(get_handle());
+}
+
+void soinfo::generate_handle() {
+  CHECK(has_min_version(3));
+  CHECK(handle_ == 0); // Make sure this is the first call
+
+  // Make sure the handle is unique and does not collide
+  // with special values which are RTLD_DEFAULT and RTLD_NEXT.
+  do {
+    arc4random_buf(&handle_, sizeof(handle_));
+    // the least significant bit for the handle is always 1
+    // making it easy to test the type of handle passed to
+    // dl* functions.
+    handle_ = handle_ | 1;
+  } while (handle_ == reinterpret_cast<uintptr_t>(RTLD_DEFAULT) ||
+           handle_ == reinterpret_cast<uintptr_t>(RTLD_NEXT) ||
+           g_soinfo_handles_map.find(handle_) != g_soinfo_handles_map.end());
+
+  g_soinfo_handles_map[handle_] = this;
+}
+
+// TODO(dimitry): Move SymbolName methods to a separate file.
+
+uint32_t calculate_elf_hash(const char* name) {
+  const uint8_t* name_bytes = reinterpret_cast<const uint8_t*>(name);
+  uint32_t h = 0, g;
+
+  while (*name_bytes) {
+    h = (h << 4) + *name_bytes++;
+    g = h & 0xf0000000;
+    h ^= g;
+    h ^= g >> 24;
+  }
+
+  return h;
+}
+
+uint32_t SymbolName::elf_hash() {
+  if (!has_elf_hash_) {
+    elf_hash_ = calculate_elf_hash(name_);
+    has_elf_hash_ = true;
+  }
+
+  return elf_hash_;
+}
+
+uint32_t SymbolName::gnu_hash() {
+  if (!has_gnu_hash_) {
+    uint32_t h = 5381;
+    const uint8_t* name = reinterpret_cast<const uint8_t*>(name_);
+    while (*name != 0) {
+      h += (h << 5) + *name++; // h*33 + c = h + h * 32 + c = h + h << 5 + c
+    }
+
+    gnu_hash_ =  h;
+    has_gnu_hash_ = true;
+  }
+
+  return gnu_hash_;
+}
+
+
diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h
new file mode 100644
index 0000000..d7b584e
--- /dev/null
+++ b/linker/linker_soinfo.h
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __LINKER_SOINFO_H
+#define __LINKER_SOINFO_H
+
+#include <link.h>
+
+#include <string>
+
+#include "linker_namespaces.h"
+
+#define FLAG_LINKED           0x00000001
+#define FLAG_EXE              0x00000004 // The main executable
+#define FLAG_LINKER           0x00000010 // The linker itself
+#define FLAG_GNU_HASH         0x00000040 // uses gnu hash
+#define FLAG_MAPPED_BY_CALLER 0x00000080 // the map is reserved by the caller
+                                         // and should not be unmapped
+#define FLAG_NEW_SOINFO       0x40000000 // new soinfo format
+
+#define SOINFO_VERSION 3
+
+typedef void (*linker_dtor_function_t)();
+typedef void (*linker_ctor_function_t)(int, char**, char**);
+
+class SymbolName {
+ public:
+  explicit SymbolName(const char* name)
+      : name_(name), has_elf_hash_(false), has_gnu_hash_(false),
+        elf_hash_(0), gnu_hash_(0) { }
+
+  const char* get_name() {
+    return name_;
+  }
+
+  uint32_t elf_hash();
+  uint32_t gnu_hash();
+
+ private:
+  const char* name_;
+  bool has_elf_hash_;
+  bool has_gnu_hash_;
+  uint32_t elf_hash_;
+  uint32_t gnu_hash_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(SymbolName);
+};
+
+struct version_info {
+  constexpr version_info() : elf_hash(0), name(nullptr), target_si(nullptr) {}
+
+  uint32_t elf_hash;
+  const char* name;
+  const soinfo* target_si;
+};
+
+// TODO(dimitry): remove reference from soinfo member functions to this class.
+class VersionTracker;
+
+#if defined(__work_around_b_24465209__)
+#define SOINFO_NAME_LEN 128
+#endif
+
+struct soinfo {
+#if defined(__work_around_b_24465209__)
+ private:
+  char old_name_[SOINFO_NAME_LEN];
+#endif
+ public:
+  const ElfW(Phdr)* phdr;
+  size_t phnum;
+#if defined(__work_around_b_24465209__)
+  ElfW(Addr) unused0; // DO NOT USE, maintained for compatibility.
+#endif
+  ElfW(Addr) base;
+  size_t size;
+
+#if defined(__work_around_b_24465209__)
+  uint32_t unused1;  // DO NOT USE, maintained for compatibility.
+#endif
+
+  ElfW(Dyn)* dynamic;
+
+#if defined(__work_around_b_24465209__)
+  uint32_t unused2; // DO NOT USE, maintained for compatibility
+  uint32_t unused3; // DO NOT USE, maintained for compatibility
+#endif
+
+  soinfo* next;
+ private:
+  uint32_t flags_;
+
+  const char* strtab_;
+  ElfW(Sym)* symtab_;
+
+  size_t nbucket_;
+  size_t nchain_;
+  uint32_t* bucket_;
+  uint32_t* chain_;
+
+#if defined(__mips__) || !defined(__LP64__)
+  // This is only used by mips and mips64, but needs to be here for
+  // all 32-bit architectures to preserve binary compatibility.
+  ElfW(Addr)** plt_got_;
+#endif
+
+#if defined(USE_RELA)
+  ElfW(Rela)* plt_rela_;
+  size_t plt_rela_count_;
+
+  ElfW(Rela)* rela_;
+  size_t rela_count_;
+#else
+  ElfW(Rel)* plt_rel_;
+  size_t plt_rel_count_;
+
+  ElfW(Rel)* rel_;
+  size_t rel_count_;
+#endif
+
+  linker_ctor_function_t* preinit_array_;
+  size_t preinit_array_count_;
+
+  linker_ctor_function_t* init_array_;
+  size_t init_array_count_;
+  linker_dtor_function_t* fini_array_;
+  size_t fini_array_count_;
+
+  linker_ctor_function_t init_func_;
+  linker_dtor_function_t fini_func_;
+
+#if defined(__arm__)
+ public:
+  // ARM EABI section used for stack unwinding.
+  uint32_t* ARM_exidx;
+  size_t ARM_exidx_count;
+ private:
+#elif defined(__mips__)
+  uint32_t mips_symtabno_;
+  uint32_t mips_local_gotno_;
+  uint32_t mips_gotsym_;
+  bool mips_relocate_got(const VersionTracker& version_tracker,
+                         const soinfo_list_t& global_group,
+                         const soinfo_list_t& local_group);
+#if !defined(__LP64__)
+  bool mips_check_and_adjust_fp_modes();
+#endif
+#endif
+  size_t ref_count_;
+ public:
+  link_map link_map_head;
+
+  bool constructors_called;
+
+  // When you read a virtual address from the ELF file, add this
+  // value to get the corresponding address in the process' address space.
+  ElfW(Addr) load_bias;
+
+#if !defined(__LP64__)
+  bool has_text_relocations;
+#endif
+  bool has_DT_SYMBOLIC;
+
+ public:
+  soinfo(android_namespace_t* ns, const char* name, const struct stat* file_stat,
+         off64_t file_offset, int rtld_flags);
+  ~soinfo();
+
+  void call_constructors();
+  void call_destructors();
+  void call_pre_init_constructors();
+  bool prelink_image();
+  bool link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group,
+                  const android_dlextinfo* extinfo);
+  bool protect_relro();
+
+  void add_child(soinfo* child);
+  void remove_all_links();
+
+  ino_t get_st_ino() const;
+  dev_t get_st_dev() const;
+  off64_t get_file_offset() const;
+
+  uint32_t get_rtld_flags() const;
+  uint32_t get_dt_flags_1() const;
+  void set_dt_flags_1(uint32_t dt_flags_1);
+
+  soinfo_list_t& get_children();
+  const soinfo_list_t& get_children() const;
+
+  soinfo_list_t& get_parents();
+
+  bool find_symbol_by_name(SymbolName& symbol_name,
+                           const version_info* vi,
+                           const ElfW(Sym)** symbol) const;
+
+  ElfW(Sym)* find_symbol_by_address(const void* addr);
+  ElfW(Addr) resolve_symbol_address(const ElfW(Sym)* s) const;
+
+  const char* get_string(ElfW(Word) index) const;
+  bool can_unload() const;
+  bool is_gnu_hash() const;
+
+  bool inline has_min_version(uint32_t min_version __unused) const {
+#if defined(__work_around_b_24465209__)
+    return (flags_ & FLAG_NEW_SOINFO) != 0 && version_ >= min_version;
+#else
+    return true;
+#endif
+  }
+
+  bool is_linked() const;
+  bool is_linker() const;
+  bool is_main_executable() const;
+
+  void set_linked();
+  void set_linker_flag();
+  void set_main_executable();
+  void set_nodelete();
+
+  void increment_ref_count();
+  size_t decrement_ref_count();
+
+  soinfo* get_local_group_root() const;
+
+  void set_soname(const char* soname);
+  const char* get_soname() const;
+  const char* get_realpath() const;
+  const ElfW(Versym)* get_versym(size_t n) const;
+  ElfW(Addr) get_verneed_ptr() const;
+  size_t get_verneed_cnt() const;
+  ElfW(Addr) get_verdef_ptr() const;
+  size_t get_verdef_cnt() const;
+
+  uint32_t get_target_sdk_version() const;
+
+  void set_dt_runpath(const char *);
+  const std::vector<std::string>& get_dt_runpath() const;
+  android_namespace_t* get_primary_namespace();
+  void add_secondary_namespace(android_namespace_t* secondary_ns);
+
+  void set_mapped_by_caller(bool reserved_map);
+  bool is_mapped_by_caller() const;
+
+  uintptr_t get_handle() const;
+  void generate_handle();
+  void* to_handle();
+
+ private:
+  bool elf_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
+  ElfW(Sym)* elf_addr_lookup(const void* addr);
+  bool gnu_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
+  ElfW(Sym)* gnu_addr_lookup(const void* addr);
+
+  bool lookup_version_info(const VersionTracker& version_tracker, ElfW(Word) sym,
+                           const char* sym_name, const version_info** vi);
+
+  template<typename ElfRelIteratorT>
+  bool relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& rel_iterator,
+                const soinfo_list_t& global_group, const soinfo_list_t& local_group);
+
+ private:
+  // This part of the structure is only available
+  // when FLAG_NEW_SOINFO is set in this->flags.
+  uint32_t version_;
+
+  // version >= 0
+  dev_t st_dev_;
+  ino_t st_ino_;
+
+  // dependency graph
+  soinfo_list_t children_;
+  soinfo_list_t parents_;
+
+  // version >= 1
+  off64_t file_offset_;
+  uint32_t rtld_flags_;
+  uint32_t dt_flags_1_;
+  size_t strtab_size_;
+
+  // version >= 2
+
+  size_t gnu_nbucket_;
+  uint32_t* gnu_bucket_;
+  uint32_t* gnu_chain_;
+  uint32_t gnu_maskwords_;
+  uint32_t gnu_shift2_;
+  ElfW(Addr)* gnu_bloom_filter_;
+
+  soinfo* local_group_root_;
+
+  uint8_t* android_relocs_;
+  size_t android_relocs_size_;
+
+  const char* soname_;
+  std::string realpath_;
+
+  const ElfW(Versym)* versym_;
+
+  ElfW(Addr) verdef_ptr_;
+  size_t verdef_cnt_;
+
+  ElfW(Addr) verneed_ptr_;
+  size_t verneed_cnt_;
+
+  uint32_t target_sdk_version_;
+
+  // version >= 3
+  std::vector<std::string> dt_runpath_;
+  android_namespace_t* primary_namespace_;
+  android_namespace_list_t secondary_namespaces_;
+  uintptr_t handle_;
+
+  friend soinfo* get_libdl_info();
+};
+
+// This function is used by dlvsym() to calculate hash of sym_ver
+uint32_t calculate_elf_hash(const char* name);
+
+#endif  /* __LINKER_SOINFO_H */
diff --git a/linker/linker_utils.cpp b/linker/linker_utils.cpp
index fb070ee..e7447e4 100644
--- a/linker/linker_utils.cpp
+++ b/linker/linker_utils.cpp
@@ -15,7 +15,26 @@
  */
 
 #include "linker_utils.h"
+
 #include "linker_debug.h"
+#include "linker_globals.h"
+
+#include "android-base/strings.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+std::string dirname(const char* path) {
+  const char* last_slash = strrchr(path, '/');
+
+  if (last_slash == path) {
+    return "/";
+  } else if (last_slash == nullptr) {
+    return ".";
+  } else {
+    return std::string(path, last_slash - path);
+  }
+}
 
 bool normalize_path(const char* path, std::string* normalized_path) {
   // Input should be an absolute path
@@ -134,3 +153,52 @@
   return static_cast<size_t>(offset & (PAGE_SIZE-1));
 }
 
+void split_path(const char* path, const char* delimiters,
+                std::vector<std::string>* paths) {
+  if (path != nullptr && path[0] != 0) {
+    *paths = android::base::Split(path, delimiters);
+  }
+}
+
+void resolve_paths(std::vector<std::string>& paths,
+                   std::vector<std::string>* resolved_paths) {
+  resolved_paths->clear();
+  for (const auto& path : paths) {
+    char resolved_path[PATH_MAX];
+    const char* original_path = path.c_str();
+    if (realpath(original_path, resolved_path) != nullptr) {
+      struct stat s;
+      if (stat(resolved_path, &s) == 0) {
+        if (S_ISDIR(s.st_mode)) {
+          resolved_paths->push_back(resolved_path);
+        } else {
+          DL_WARN("Warning: \"%s\" is not a directory (excluding from path)", resolved_path);
+          continue;
+        }
+      } else {
+        DL_WARN("Warning: cannot stat file \"%s\": %s", resolved_path, strerror(errno));
+        continue;
+      }
+    } else {
+      std::string zip_path;
+      std::string entry_path;
+
+      std::string normalized_path;
+
+      if (!normalize_path(original_path, &normalized_path)) {
+        DL_WARN("Warning: unable to normalize \"%s\"", original_path);
+        continue;
+      }
+
+      if (parse_zip_path(normalized_path.c_str(), &zip_path, &entry_path)) {
+        if (realpath(zip_path.c_str(), resolved_path) == nullptr) {
+          DL_WARN("Warning: unable to resolve \"%s\": %s", zip_path.c_str(), strerror(errno));
+          continue;
+        }
+
+        resolved_paths->push_back(std::string(resolved_path) + kZipFileSeparator + entry_path);
+      }
+    }
+  }
+}
+
diff --git a/linker/linker_utils.h b/linker/linker_utils.h
index 987eabd..2e015a5 100644
--- a/linker/linker_utils.h
+++ b/linker/linker_utils.h
@@ -17,14 +17,25 @@
 #define __LINKER_UTILS_H
 
 #include <string>
+#include <vector>
 
 extern const char* const kZipFileSeparator;
 
-bool normalize_path(const char* path, std::string* normalized_path);
 bool file_is_in_dir(const std::string& file, const std::string& dir);
 bool file_is_under_dir(const std::string& file, const std::string& dir);
+bool normalize_path(const char* path, std::string* normalized_path);
 bool parse_zip_path(const char* input_path, std::string* zip_path, std::string* entry_path);
 
+// For every path element this function checks of it exists, and is a directory,
+// and normalizes it:
+// 1. For regular path it converts it to realpath()
+// 2. For path in a zip file it uses realpath on the zipfile
+//    normalizes entry name by calling normalize_path function.
+void resolve_paths(std::vector<std::string>& paths, std::vector<std::string>* resolved_paths);
+void split_path(const char* path, const char* delimiters, std::vector<std::string>* paths);
+
+std::string dirname(const char* path);
+
 off64_t page_start(off64_t offset);
 size_t page_offset(off64_t offset);
 bool safe_add(off64_t* out, off64_t a, size_t b);
diff --git a/linker/tests/Android.mk b/linker/tests/Android.mk
index 48c6374..d5b57f1 100644
--- a/linker/tests/Android.mk
+++ b/linker/tests/Android.mk
@@ -40,4 +40,6 @@
 # for __libc_fatal
 LOCAL_SRC_FILES += ../../libc/bionic/libc_logging.cpp
 
+LOCAL_STATIC_LIBRARIES += libbase
+
 include $(BUILD_NATIVE_TEST)