Load namespace configuration from ld.config.txt

This change allows customization of default namespace
configuration for different executables. It also enables
target_sdk_version setup for binaries (note that this
option should explicitly be enabled in ld.config.txt).

Bug: http://b/30706810
Bug: http://b/30435785
Test: run linker-unit-tests/bionic-unit-tests, boot angler
Change-Id: Ibbe87209acf1538fc9cec04944f3d22a190c38f1
diff --git a/linker/Android.bp b/linker/Android.bp
index a43f8b3..2351e71 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -19,6 +19,7 @@
         "linker_block_allocator.cpp",
         "linker_dlwarning.cpp",
         "linker_cfi.cpp",
+        "linker_config.cpp",
         "linker_gdb_support.cpp",
         "linker_globals.cpp",
         "linker_libc_support.c",
diff --git a/linker/ld.config.format.md b/linker/ld.config.format.md
new file mode 100644
index 0000000..686d6be
--- /dev/null
+++ b/linker/ld.config.format.md
@@ -0,0 +1,83 @@
+# Linker config file format
+
+This document describes format of /system/etc/ld.config.txt file. This file can be used to customize
+linker-namespace setup for dynamic executables.
+
+## Overview
+
+The configuration consists of 2 parts
+1. Mappings - maps executable locations to sections
+2. Sections - contains linker-namespace configuration
+
+## Mappings
+
+This part of the document maps location of an executable to a section. Here is an example
+
+The format is `dir.<section_name>=<directory>`
+
+The mappings should be defined between start of ld.config.txt and the first section.
+
+## Section
+
+Every section starts with `[section_name]` (which is used in mappings) and it defines namespaces
+configuration using set of properties described in example below.
+
+## Example
+
+```
+# The following line maps section to a dir. Binraies ran from this location will use namespaces
+# configuration specified in [example_section] below
+dir.example_section=/system/bin/example
+
+# Section starts
+[example_section]
+
+# When this flag is set to true linker will set target_sdk_version for this binary to
+# the version specified in <dirname>/.version file, where <dirname> = dirname(executable_path)
+#
+# default value is false
+enable.target.sdk.version = true
+
+# This property can be used to declare additional namespaces.Note that there is always the default
+# namespace. The default namespace is the namespace for the main executable. This list is
+# comma-separated.
+additional.namespaces = ns1
+
+# Each namespace property starts with "namespace.<namespace-name>" The following is configuration
+# for the default namespace
+
+# Is namespace isolated - the default value is false
+namespace.default.isolated = true
+
+# Default namespace search path. Note that ${LIB} here is substituted with "lib" for 32bit targets
+# and with "lib64" for 64bit ones.
+namespace.default.search.paths = /system/${LIB}:/system/other/${LIB}
+
+# ... same for asan
+namespace.default.asan.search.paths = /data/${LIB}:/data/other/${LIB}
+
+# Permitted path
+namespace.default.permitted.paths = /system/${LIB}
+
+# ... asan
+namespace.default.asan.permitted.paths = /data/${LIB}
+
+# This declares linked namespaces - comma separated list.
+namespace.default.links = ns1
+
+# For every link define list of shared libraries. This is list of the libraries accessilbe from
+# default namespace but loaded in the linked namespace.
+namespace.default.link.ns1.shared_libs = libexternal.so:libother.so
+
+# This part defines config for ns1
+namespace.ns1.isolated = true
+namespace.ns1.search.paths = /vendor/${LIB}
+namespace.ns1.asan.search.paths = /data/vendor/${LIB}
+namespace.ns1.permitted.paths = /vendor/${LIB}
+namespace.ns1.asan.permitted.paths = /data/vendor/${LIB}
+
+# and links it to default namespace
+namespace.ns.links = default
+namespace.ns.link.default.shared_libs = libc.so:libdl.so:libm.so:libstdc++.so
+```
+
diff --git a/linker/linker.cpp b/linker/linker.cpp
index a05cd3b..27b812d 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -49,6 +49,7 @@
 #include "linker.h"
 #include "linker_block_allocator.h"
 #include "linker_cfi.h"
+#include "linker_config.h"
 #include "linker_gdb_support.h"
 #include "linker_globals.h"
 #include "linker_debug.h"
@@ -77,6 +78,8 @@
 static LinkerTypeAllocator<android_namespace_t> g_namespace_allocator;
 static LinkerTypeAllocator<LinkedListEntry<android_namespace_t>> g_namespace_list_allocator;
 
+static const char* const kLdConfigFilePath = "/system/etc/ld.config.txt";
+
 #if defined(__LP64__)
 static const char* const kSystemLibDir           = "/system/lib64";
 static const char* const kSystemNdkLibDir       = "/system/lib64/ndk";
@@ -3363,21 +3366,9 @@
   return true;
 }
 
-void init_default_namespace() {
-  g_default_namespace.set_name("(default)");
+static void init_default_namespace_no_config(bool is_asan) {
   g_default_namespace.set_isolated(false);
-
-  soinfo* somain = solist_get_somain();
-
-  const char *interp = phdr_table_get_interpreter_name(somain->phdr, somain->phnum,
-                                                       somain->load_bias);
-  const char* bname = basename(interp);
-
-  bool is_asan = bname != nullptr &&
-                 (strcmp(bname, "linker_asan") == 0 ||
-                  strcmp(bname, "linker_asan64") == 0);
   auto default_ld_paths = is_asan ? kAsanDefaultLdPaths : kDefaultLdPaths;
-  g_is_asan = is_asan;
 
   char real_path[PATH_MAX];
   std::vector<std::string> ld_default_paths;
@@ -3390,4 +3381,91 @@
   }
 
   g_default_namespace.set_default_library_paths(std::move(ld_default_paths));
-};
+}
+
+void init_default_namespace(const char* executable_path) {
+  g_default_namespace.set_name("(default)");
+
+  soinfo* somain = solist_get_somain();
+
+  const char *interp = phdr_table_get_interpreter_name(somain->phdr, somain->phnum,
+                                                       somain->load_bias);
+  const char* bname = basename(interp);
+
+  g_is_asan = bname != nullptr &&
+              (strcmp(bname, "linker_asan") == 0 ||
+               strcmp(bname, "linker_asan64") == 0);
+
+  const Config* config = nullptr;
+
+  std::string error_msg;
+
+  if (!Config::read_binary_config(kLdConfigFilePath,
+                                  executable_path,
+                                  g_is_asan,
+                                  &config,
+                                  &error_msg)) {
+    if (!error_msg.empty()) {
+      DL_WARN("error reading config file \"%s\" for \"%s\" (will use default configuration): %s",
+              kLdConfigFilePath,
+              executable_path,
+              error_msg.c_str());
+    }
+    config = nullptr;
+  }
+
+  if (config == nullptr) {
+    init_default_namespace_no_config(g_is_asan);
+    return;
+  }
+
+  const auto& namespace_configs = config->namespace_configs();
+  std::unordered_map<std::string, android_namespace_t*> namespaces;
+
+  // 1. Initialize default namespace
+  const NamespaceConfig* default_ns_config = config->default_namespace_config();
+
+  g_default_namespace.set_isolated(default_ns_config->isolated());
+  g_default_namespace.set_default_library_paths(default_ns_config->search_paths());
+  g_default_namespace.set_permitted_paths(default_ns_config->permitted_paths());
+
+  namespaces[default_ns_config->name()] = &g_default_namespace;
+
+  // 2. Initialize other namespaces
+
+  for (auto& ns_config : namespace_configs) {
+    if (namespaces.find(ns_config->name()) != namespaces.end()) {
+      continue;
+    }
+
+    android_namespace_t* ns = new (g_namespace_allocator.alloc()) android_namespace_t();
+    ns->set_name(ns_config->name());
+    ns->set_isolated(ns_config->isolated());
+    ns->set_default_library_paths(ns_config->search_paths());
+    ns->set_permitted_paths(ns_config->permitted_paths());
+
+    namespaces[ns_config->name()] = ns;
+  }
+
+  // 3. Establish links between namespaces
+  for (auto& ns_config : namespace_configs) {
+    auto it_from = namespaces.find(ns_config->name());
+    CHECK(it_from != namespaces.end());
+    android_namespace_t* namespace_from = it_from->second;
+    for (const NamespaceLinkConfig& ns_link : ns_config->links()) {
+      auto it_to = namespaces.find(ns_link.ns_name());
+      CHECK(it_to != namespaces.end());
+      android_namespace_t* namespace_to = it_to->second;
+      link_namespaces(namespace_from, namespace_to, ns_link.shared_libs().c_str());
+    }
+  }
+  // we can no longer rely on the fact that libdl.so is part of default namespace
+  // this is why we want to add ld-android.so to all namespaces from ld.config.txt
+  soinfo* ld_android_so = solist_get_head();
+  for (auto it : namespaces) {
+    it.second->add_soinfo(ld_android_so);
+    // TODO (dimitry): somain and ld_preloads should probably be added to all of these namespaces too?
+  }
+
+  set_application_target_sdk_version(config->target_sdk_version());
+}
diff --git a/linker/linker_config.cpp b/linker/linker_config.cpp
new file mode 100644
index 0000000..33616f7
--- /dev/null
+++ b/linker/linker_config.cpp
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2017 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_config.h"
+
+#include "linker_globals.h"
+#include "linker_debug.h"
+#include "linker_utils.h"
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+
+#include <private/ScopeGuard.h>
+
+#include <stdlib.h>
+
+#include <string>
+#include <unordered_map>
+
+class ConfigParser {
+ public:
+  enum {
+    kProperty,
+    kSection,
+    kEndOfFile,
+    kError,
+  };
+
+  explicit ConfigParser(std::string&& content)
+      : content_(content), p_(0), lineno_(0), was_end_of_file_(false) {}
+
+  /*
+   * Possible return values
+   * kProperty: name is set to property name and value is set to property value
+   * kSection: name is set to section name.
+   * kEndOfFile: reached end of file.
+   * kError: error_msg is set.
+   */
+  int next_token(std::string* name, std::string* value, std::string* error_msg) {
+    std::string line;
+    while(NextLine(&line)) {
+      size_t found = line.find('#');
+      line = android::base::Trim(line.substr(0, found));
+
+      if (line.empty()) {
+        continue;
+      }
+
+      if (line[0] == '[' && line[line.size() - 1] == ']') {
+        *name = line.substr(1, line.size() - 2);
+        return kSection;
+      }
+
+      found = line.find('=');
+      if (found == std::string::npos) {
+        *error_msg = std::string("invalid format: ") +
+                    line +
+                    ", expected \"name = property\" or \"[section]\"";
+        return kError;
+      }
+
+      *name = android::base::Trim(line.substr(0, found));
+      *value = android::base::Trim(line.substr(found + 1));
+      return kProperty;
+    }
+
+    // to avoid infinite cycles when programmer makes a mistake
+    CHECK(!was_end_of_file_);
+    was_end_of_file_ = true;
+    return kEndOfFile;
+  }
+
+  size_t lineno() const {
+    return lineno_;
+  }
+
+ private:
+  bool NextLine(std::string* line) {
+    if (p_ == std::string::npos) {
+      return false;
+    }
+
+    size_t found = content_.find('\n', p_);
+    if (found != std::string::npos) {
+      *line = content_.substr(p_, found - p_);
+      p_ = found + 1;
+    } else {
+      *line = content_.substr(p_);
+      p_ = std::string::npos;
+    }
+
+    lineno_++;
+    return true;
+  }
+
+  std::string content_;
+  size_t p_;
+  size_t lineno_;
+  bool was_end_of_file_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ConfigParser);
+};
+
+class PropertyValue {
+ public:
+  PropertyValue() = default;
+
+  PropertyValue(std::string&& value, size_t lineno)
+    : value_(value), lineno_(lineno) {}
+
+  const std::string& value() const {
+    return value_;
+  }
+
+  size_t lineno() const {
+    return lineno_;
+  }
+
+ private:
+  std::string value_;
+  size_t lineno_;
+};
+
+static std::string create_error_msg(const char* file,
+                                    size_t lineno,
+                                    const std::string& msg) {
+  char buf[1024];
+  __libc_format_buffer(buf, sizeof(buf), "%s:%zu: error: %s", file, lineno, msg.c_str());
+
+  return std::string(buf);
+}
+
+static bool parse_config_file(const char* ld_config_file_path,
+                              const char* binary_realpath,
+                              std::unordered_map<std::string, PropertyValue>* properties,
+                              std::string* error_msg) {
+  std::string content;
+  if (!android::base::ReadFileToString(ld_config_file_path, &content)) {
+    if (errno != ENOENT) {
+      *error_msg = std::string("error reading file \"") +
+                   ld_config_file_path + "\": " + strerror(errno);
+    }
+    return false;
+  }
+
+  ConfigParser cp(std::move(content));
+
+  std::string section_name;
+
+  while(true) {
+    std::string name;
+    std::string value;
+    std::string error;
+
+    int result = cp.next_token(&name, &value, &error);
+    if (result == ConfigParser::kError) {
+      DL_WARN("error parsing %s:%zd: %s (ignoring this line)",
+              ld_config_file_path,
+              cp.lineno(),
+              error.c_str());
+      continue;
+    }
+
+    if (result == ConfigParser::kSection || result == ConfigParser::kEndOfFile) {
+      return false;
+    }
+
+    if (result == ConfigParser::kProperty) {
+      if (!android::base::StartsWith(name, "dir.")) {
+        DL_WARN("error parsing %s:%zd: unexpected property name \"%s\", "
+                "expected format dir.<section_name> (ignoring this line)",
+                ld_config_file_path,
+                cp.lineno(),
+                name.c_str());
+        continue;
+      }
+
+      // remove trailing '/'
+      while (value[value.size() - 1] == '/') {
+        value = value.substr(0, value.size() - 1);
+      }
+
+      if (value.empty()) {
+        DL_WARN("error parsing %s:%zd: property value is empty (ignoring this line)",
+                ld_config_file_path,
+                cp.lineno());
+        continue;
+      }
+
+      if (file_is_under_dir(binary_realpath, value)) {
+        section_name = name.substr(4);
+        break;
+      }
+    }
+  }
+
+  // skip everything until we meet a correct section
+  while (true) {
+    std::string name;
+    std::string value;
+    std::string error;
+
+    int result = cp.next_token(&name, &value, &error);
+
+    if (result == ConfigParser::kSection && name == section_name) {
+      break;
+    }
+
+    if (result == ConfigParser::kEndOfFile) {
+      *error_msg = create_error_msg(ld_config_file_path,
+                                    cp.lineno(),
+                                    std::string("section \"") + section_name + "\" not found");
+      return false;
+    }
+  }
+
+  // found the section - parse it
+  while (true) {
+    std::string name;
+    std::string value;
+    std::string error;
+
+    int result = cp.next_token(&name, &value, &error);
+
+    if (result == ConfigParser::kEndOfFile || result == ConfigParser::kSection) {
+      break;
+    }
+
+    if (result == ConfigParser::kProperty) {
+      if (properties->find(name) != properties->end()) {
+        DL_WARN("%s:%zd: warning: property \"%s\" redefinition",
+                ld_config_file_path,
+                cp.lineno(),
+                name.c_str());
+      }
+
+      (*properties)[name] = PropertyValue(std::move(value), cp.lineno());
+    }
+
+    if (result == ConfigParser::kError) {
+      DL_WARN("error parsing %s:%zd: %s (ignoring this line)",
+              ld_config_file_path,
+              cp.lineno(),
+              error.c_str());
+      continue;
+    }
+  }
+
+  return true;
+}
+
+static Config g_config;
+
+static constexpr const char* kDefaultConfigName = "default";
+static constexpr const char* kPropertyAdditionalNamespaces = "additional.namespaces";
+#if defined(__LP64__)
+static constexpr const char* kLibParamValue = "lib64";
+#else
+static constexpr const char* kLibParamValue = "lib";
+#endif
+
+class Properties {
+ public:
+  explicit Properties(std::unordered_map<std::string, PropertyValue>&& properties)
+      : properties_(properties), target_sdk_version_(__ANDROID_API__) {}
+
+  std::vector<std::string> get_strings(const std::string& name, size_t* lineno = nullptr) const {
+    auto it = find_property(name, lineno);
+    if (it == properties_.end()) {
+      // return empty vector
+      return std::vector<std::string>();
+    }
+
+    std::vector<std::string> strings = android::base::Split(it->second.value(), ",");
+
+    for (size_t i = 0; i < strings.size(); ++i) {
+      strings[i] = android::base::Trim(strings[i]);
+    }
+
+    return strings;
+  }
+
+  bool get_bool(const std::string& name, size_t* lineno = nullptr) const {
+    auto it = find_property(name, lineno);
+    if (it == properties_.end()) {
+      return false;
+    }
+
+    return it->second.value() == "true";
+  }
+
+  std::string get_string(const std::string& name, size_t* lineno = nullptr) const {
+    auto it = find_property(name, lineno);
+    return (it == properties_.end()) ? "" : it->second.value();
+  }
+
+  std::vector<std::string> get_paths(const std::string& name, size_t* lineno = nullptr) {
+    std::string paths_str = get_string(name, lineno);
+
+    std::vector<std::string> paths;
+    split_path(paths_str.c_str(), ":", &paths);
+
+    std::vector<std::pair<std::string, std::string>> params;
+    params.push_back({ "LIB", kLibParamValue });
+    if (target_sdk_version_ != 0) {
+      char buf[16];
+      __libc_format_buffer(buf, sizeof(buf), "%d", target_sdk_version_);
+      params.push_back({ "SDK_VER", buf });
+    }
+
+    for (auto&& path : paths) {
+      format_string(&path, params);
+    }
+
+    std::vector<std::string> resolved_paths;
+
+    // do not remove paths that do not exist
+    resolve_paths(paths, &resolved_paths);
+
+    return resolved_paths;
+  }
+
+  void set_target_sdk_version(int target_sdk_version) {
+    target_sdk_version_ = target_sdk_version;
+  }
+
+ private:
+  std::unordered_map<std::string, PropertyValue>::const_iterator
+  find_property(const std::string& name, size_t* lineno) const {
+    auto it = properties_.find(name);
+    if (it != properties_.end() && lineno != nullptr) {
+      *lineno = it->second.lineno();
+    }
+
+    return it;
+  }
+  std::unordered_map<std::string, PropertyValue> properties_;
+  int target_sdk_version_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(Properties);
+};
+
+bool Config::read_binary_config(const char* ld_config_file_path,
+                                      const char* binary_realpath,
+                                      bool is_asan,
+                                      const Config** config,
+                                      std::string* error_msg) {
+  g_config.clear();
+
+  std::unordered_map<std::string, PropertyValue> property_map;
+  if (!parse_config_file(ld_config_file_path, binary_realpath, &property_map, error_msg)) {
+    return false;
+  }
+
+  Properties properties(std::move(property_map));
+
+  auto failure_guard = make_scope_guard([] {
+    g_config.clear();
+  });
+
+  std::unordered_map<std::string, NamespaceConfig*> namespace_configs;
+
+  namespace_configs[kDefaultConfigName] = g_config.create_namespace_config(kDefaultConfigName);
+
+  std::vector<std::string> additional_namespaces = properties.get_strings(kPropertyAdditionalNamespaces);
+  for (const auto& name : additional_namespaces) {
+    namespace_configs[name] = g_config.create_namespace_config(name);
+  }
+
+  bool versioning_enabled = properties.get_bool("enable.target.sdk.version");
+  int target_sdk_version = __ANDROID_API__;
+  if (versioning_enabled) {
+    std::string version_file = dirname(binary_realpath) + "/.version";
+    std::string content;
+    if (!android::base::ReadFileToString(version_file, &content)) {
+      if (errno != ENOENT) {
+        *error_msg = std::string("error reading version file \"") +
+                     version_file + "\": " + strerror(errno);
+        return false;
+      }
+    } else {
+      content = android::base::Trim(content);
+      errno = 0;
+      char* end = nullptr;
+      const char* content_str = content.c_str();
+      int result = strtol(content_str, &end, 10);
+      if (errno == 0 && *end == '\0' && result > 0) {
+        target_sdk_version = result;
+        properties.set_target_sdk_version(target_sdk_version);
+      } else {
+        *error_msg = std::string("invalid version \"") + version_file + "\": \"" + content +"\"";
+        return false;
+      }
+    }
+  }
+
+  g_config.set_target_sdk_version(target_sdk_version);
+
+  for (auto ns_config_it : namespace_configs) {
+    auto& name = ns_config_it.first;
+    NamespaceConfig* ns_config = ns_config_it.second;
+
+    std::string property_name_prefix = std::string("namespace.") + name;
+
+    size_t lineno = 0;
+    std::vector<std::string> linked_namespaces =
+        properties.get_strings(property_name_prefix + ".links", &lineno);
+
+    for (const auto& linked_ns_name : linked_namespaces) {
+      if (namespace_configs.find(linked_ns_name) == namespace_configs.end()) {
+        *error_msg = create_error_msg(ld_config_file_path,
+                                      lineno,
+                                      std::string("undefined namespace: ") + linked_ns_name);
+        return false;
+      }
+
+      std::string shared_libs = properties.get_string(property_name_prefix +
+                                                      ".link." +
+                                                      linked_ns_name +
+                                                      ".shared_libs", &lineno);
+
+      if (shared_libs.empty()) {
+        *error_msg = create_error_msg(ld_config_file_path,
+                                      lineno,
+                                      std::string("list of shared_libs for ") +
+                                      name +
+                                      "->" +
+                                      linked_ns_name +
+                                      " link is not specified or is empty.");
+        return false;
+      }
+
+      ns_config->add_namespace_link(linked_ns_name, shared_libs);
+    }
+
+    ns_config->set_isolated(properties.get_bool(property_name_prefix + ".isolated"));
+
+    // these are affected by is_asan flag
+    if (is_asan) {
+      property_name_prefix += ".asan";
+    }
+
+    ns_config->set_search_paths(properties.get_paths(property_name_prefix + ".search.paths"));
+    ns_config->set_permitted_paths(properties.get_paths(property_name_prefix + ".permitted.paths"));
+  }
+
+  failure_guard.disable();
+  *config = &g_config;
+  return true;
+}
+
+NamespaceConfig* Config::create_namespace_config(const std::string& name) {
+  namespace_configs_.push_back(std::unique_ptr<NamespaceConfig>(new NamespaceConfig(name)));
+  NamespaceConfig* ns_config_ptr = namespace_configs_.back().get();
+  namespace_configs_map_[name] = ns_config_ptr;
+  return ns_config_ptr;
+}
+
+void Config::clear() {
+  namespace_configs_.clear();
+  namespace_configs_map_.clear();
+}
diff --git a/linker/linker_config.h b/linker/linker_config.h
new file mode 100644
index 0000000..4ec8b26
--- /dev/null
+++ b/linker/linker_config.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 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_CONFIG_H_
+#define _LINKER_CONFIG_H_
+
+#include <android/api-level.h>
+
+#include <stdlib.h>
+#include <limits.h>
+#include "private/bionic_macros.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+class NamespaceLinkConfig {
+ public:
+  NamespaceLinkConfig() = default;
+  NamespaceLinkConfig(const std::string& ns_name, const std::string& shared_libs)
+      : ns_name_(ns_name), shared_libs_(shared_libs)  {}
+
+  const std::string& ns_name() const {
+    return ns_name_;
+  }
+
+  const std::string& shared_libs() const {
+    return shared_libs_;
+  }
+
+ private:
+  std::string ns_name_;
+  std::string shared_libs_;
+};
+
+class NamespaceConfig {
+ public:
+  explicit NamespaceConfig(const std::string& name)
+      : name_(name), isolated_(false)
+  {}
+
+  const char* name() const {
+    return name_.c_str();
+  }
+
+  bool isolated() const {
+    return isolated_;
+  }
+
+  const std::vector<std::string>& search_paths() const {
+    return search_paths_;
+  }
+
+  const std::vector<std::string>& permitted_paths() const {
+    return permitted_paths_;
+  }
+
+  const std::vector<NamespaceLinkConfig>& links() const {
+    return namespace_links_;
+  }
+
+  void add_namespace_link(const std::string& ns_name, const std::string& shared_libs) {
+    namespace_links_.push_back(NamespaceLinkConfig(ns_name, shared_libs));
+  }
+
+  void set_isolated(bool isolated) {
+    isolated_ = isolated;
+  }
+
+  void set_search_paths(std::vector<std::string>&& search_paths) {
+    search_paths_ = search_paths;
+  }
+
+  void set_permitted_paths(std::vector<std::string>&& permitted_paths) {
+    permitted_paths_ = permitted_paths;
+  }
+ private:
+  const std::string name_;
+  bool isolated_;
+  std::vector<std::string> search_paths_;
+  std::vector<std::string> permitted_paths_;
+  std::vector<NamespaceLinkConfig> namespace_links_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(NamespaceConfig);
+};
+
+class Config {
+ public:
+  Config() : target_sdk_version_(__ANDROID_API__) {}
+
+  const std::vector<std::unique_ptr<NamespaceConfig>>& namespace_configs() const {
+    return namespace_configs_;
+  }
+
+  const NamespaceConfig* default_namespace_config() const {
+    auto it = namespace_configs_map_.find("default");
+    return it == namespace_configs_map_.end() ? nullptr : it->second;
+  }
+
+  uint32_t target_sdk_version() const {
+    return target_sdk_version_;
+  }
+
+  // note that this is one time event and therefore there is no need to
+  // read every section of the config. Every linker instance needs at
+  // most one configuration.
+  // Returns false in case of an error. If binary config was not found
+  // sets *config = nullptr.
+  static bool read_binary_config(const char* ld_config_file_path,
+                                 const char* binary_realpath,
+                                 bool is_asan,
+                                 const Config** config,
+                                 std::string* error_msg);
+ private:
+  void clear();
+
+  void set_target_sdk_version(uint32_t target_sdk_version) {
+    target_sdk_version_ = target_sdk_version;
+  }
+
+  NamespaceConfig* create_namespace_config(const std::string& name);
+
+  std::vector<std::unique_ptr<NamespaceConfig>> namespace_configs_;
+  std::unordered_map<std::string, NamespaceConfig*> namespace_configs_map_;
+  uint32_t target_sdk_version_;
+
+  DISALLOW_COPY_AND_ASSIGN(Config);
+};
+
+#endif /* _LINKER_CONFIG_H_ */
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index d037a18..6870c03 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -209,6 +209,8 @@
  * and other non-local data at this point.
  */
 static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW(Addr) linker_base) {
+  ProtectedDataGuard guard;
+
 #if TIMING
   struct timeval t0, t1;
   gettimeofday(&t0, 0);
@@ -330,7 +332,7 @@
 
   somain = si;
 
-  init_default_namespace();
+  init_default_namespace(executable_path);
 
   if (!si->prelink_image()) {
     __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer());
@@ -381,19 +383,15 @@
     __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer());
   }
 
-  {
-    ProtectedDataGuard guard;
+  si->call_pre_init_constructors();
 
-    si->call_pre_init_constructors();
-
-    /* After the prelink_image, the si->load_bias is initialized.
-     * For so lib, the map->l_addr will be updated in notify_gdb_of_load.
-     * We need to update this value for so exe here. So Unwind_Backtrace
-     * for some arch like x86 could work correctly within so exe.
-     */
-    map->l_addr = si->load_bias;
-    si->call_constructors();
-  }
+  /* After the prelink_image, the si->load_bias is initialized.
+   * For so lib, the map->l_addr will be updated in notify_gdb_of_load.
+   * We need to update this value for so exe here. So Unwind_Backtrace
+   * for some arch like x86 could work correctly within so exe.
+   */
+  map->l_addr = si->load_bias;
+  si->call_constructors();
 
 #if TIMING
   gettimeofday(&t1, nullptr);
diff --git a/linker/linker_main.h b/linker/linker_main.h
index b68035b..8f3f07c 100644
--- a/linker/linker_main.h
+++ b/linker/linker_main.h
@@ -44,7 +44,7 @@
   static size_t ref_count_;
 };
 
-void init_default_namespace();
+void init_default_namespace(const char* executable_path);
 soinfo* soinfo_alloc(android_namespace_t* ns, const char* name,
                      struct stat* file_stat, off64_t file_offset,
                      uint32_t rtld_flags);
diff --git a/linker/linker_namespaces.h b/linker/linker_namespaces.h
index 868b4a6..e7d9b2e 100644
--- a/linker/linker_namespaces.h
+++ b/linker/linker_namespaces.h
@@ -80,6 +80,9 @@
   void set_default_library_paths(std::vector<std::string>&& library_paths) {
     default_library_paths_ = library_paths;
   }
+  void set_default_library_paths(const std::vector<std::string>& library_paths) {
+    default_library_paths_ = library_paths;
+  }
 
   const std::vector<std::string>& get_permitted_paths() const {
     return permitted_paths_;
@@ -87,6 +90,9 @@
   void set_permitted_paths(std::vector<std::string>&& permitted_paths) {
     permitted_paths_ = permitted_paths;
   }
+  void set_permitted_paths(const std::vector<std::string>& permitted_paths) {
+    permitted_paths_ = permitted_paths;
+  }
 
   const std::vector<android_namespace_link_t>& linked_namespaces() const {
     return linked_namespaces_;
diff --git a/linker/linker_utils.h b/linker/linker_utils.h
index 740d04b..e104a25 100644
--- a/linker/linker_utils.h
+++ b/linker/linker_utils.h
@@ -46,7 +46,9 @@
 // 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 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);
diff --git a/linker/tests/Android.mk b/linker/tests/Android.mk
index f3810c1..61c43c9 100644
--- a/linker/tests/Android.mk
+++ b/linker/tests/Android.mk
@@ -40,6 +40,7 @@
 
 LOCAL_SRC_FILES := \
   linker_block_allocator_test.cpp \
+  linker_config_test.cpp \
   linker_globals.cpp \
   linked_list_test.cpp \
   linker_memory_allocator_test.cpp \
@@ -47,7 +48,8 @@
   linker_utils_test.cpp \
   ../linker_allocator.cpp \
   ../linker_block_allocator.cpp \
-  ../linker_utils.cpp
+  ../linker_config.cpp \
+  ../linker_utils.cpp \
 
 # for __libc_fatal
 LOCAL_SRC_FILES += ../../libc/bionic/libc_logging.cpp
diff --git a/linker/tests/linker_config_test.cpp b/linker/tests/linker_config_test.cpp
new file mode 100644
index 0000000..64ab00f
--- /dev/null
+++ b/linker/tests/linker_config_test.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2017 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 <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include "../linker_config.h"
+
+#include <unistd.h>
+
+#include <android-base/stringprintf.h>
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+
+#include "private/ScopeGuard.h"
+
+
+static const char* config_str =
+  "# comment \n"
+  "dir.test = /data/local/tmp\n"
+  "\n"
+  "[test]\n"
+  "\n"
+  "enable.target.sdk.version = true\n"
+  "additional.namespaces=system\n"
+  "namespace.default.isolated = true\n"
+  "namespace.default.search.paths = /vendor/${LIB}\n"
+  "namespace.default.permitted.paths = /vendor/${LIB}\n"
+  "namespace.default.asan.search.paths = /data:/vendor/${LIB}\n"
+  "namespace.default.asan.permitted.paths = /data:/vendor\n"
+  "namespace.default.links = system\n"
+  "namespace.default.link.system.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so\n"
+  "namespace.system.isolated = true\n"
+  "namespace.system.search.paths = /system/${LIB}\n"
+  "namespace.system.permitted.paths = /system/${LIB}\n"
+  "namespace.system.asan.search.paths = /data:/system/${LIB}\n"
+  "namespace.system.asan.permitted.paths = /data:/system\n"
+  "\n";
+
+static bool write_version(const std::string& path, uint32_t version) {
+  std::string content = android::base::StringPrintf("%d", version);
+  return android::base::WriteStringToFile(content, path);
+}
+
+static void run_linker_config_smoke_test(bool is_asan) {
+#if defined(__LP64__)
+  const std::vector<std::string> kExpectedDefaultSearchPath = is_asan ?
+        std::vector<std::string>({ "/data", "/vendor/lib64"}) :
+        std::vector<std::string>({ "/vendor/lib64" });
+
+  const std::vector<std::string> kExpectedDefaultPermittedPath = is_asan ?
+        std::vector<std::string>({ "/data", "/vendor" }) :
+        std::vector<std::string>({ "/vendor/lib64" });
+
+  const std::vector<std::string> kExpectedSystemSearchPath = is_asan ?
+        std::vector<std::string>({ "/data", "/system/lib64" }) :
+        std::vector<std::string>({ "/system/lib64" });
+
+  const std::vector<std::string> kExpectedSystemPermittedPath = is_asan ?
+        std::vector<std::string>({ "/data", "/system" }) :
+        std::vector<std::string>({ "/system/lib64" });
+#else
+  const std::vector<std::string> kExpectedDefaultSearchPath = is_asan ?
+        std::vector<std::string>({ "/data", "/vendor/lib"}) :
+        std::vector<std::string>({ "/vendor/lib" });
+
+  const std::vector<std::string> kExpectedDefaultPermittedPath = is_asan ?
+        std::vector<std::string>({ "/data", "/vendor" }) :
+        std::vector<std::string>({ "/vendor/lib" });
+
+  const std::vector<std::string> kExpectedSystemSearchPath = is_asan ?
+        std::vector<std::string>({ "/data", "/system/lib" }) :
+        std::vector<std::string>({ "/system/lib" });
+
+  const std::vector<std::string> kExpectedSystemPermittedPath = is_asan ?
+        std::vector<std::string>({ "/data", "/system" }) :
+        std::vector<std::string>({ "/system/lib" });
+#endif
+
+  TemporaryFile tmp_file;
+  close(tmp_file.fd);
+  tmp_file.fd = -1;
+
+  android::base::WriteStringToFile(config_str, tmp_file.path);
+
+  TemporaryDir tmp_dir;
+
+  std::string executable_path = std::string(tmp_dir.path) + "/some-binary";
+  std::string version_file = std::string(tmp_dir.path) + "/.version";
+
+  auto file_guard = make_scope_guard([&version_file] {
+    unlink(version_file.c_str());
+  });
+
+  ASSERT_TRUE(write_version(version_file, 113U)) << strerror(errno);
+
+  // read config
+  const Config* config = nullptr;
+  std::string error_msg;
+  ASSERT_TRUE(Config::read_binary_config(tmp_file.path,
+                                         executable_path.c_str(),
+                                         is_asan,
+                                         &config,
+                                         &error_msg)) << error_msg;
+  ASSERT_TRUE(config != nullptr);
+  ASSERT_TRUE(error_msg.empty());
+
+  ASSERT_EQ(113U, config->target_sdk_version());
+
+  const NamespaceConfig* default_ns_config = config->default_namespace_config();
+  ASSERT_TRUE(default_ns_config != nullptr);
+
+  ASSERT_TRUE(default_ns_config->isolated());
+  ASSERT_EQ(kExpectedDefaultSearchPath, default_ns_config->search_paths());
+  ASSERT_EQ(kExpectedDefaultPermittedPath, default_ns_config->permitted_paths());
+
+  const auto& default_ns_links = default_ns_config->links();
+  ASSERT_EQ(1U, default_ns_links.size());
+  ASSERT_EQ("system", default_ns_links[0].ns_name());
+  ASSERT_EQ("libc.so:libm.so:libdl.so:libstdc++.so", default_ns_links[0].shared_libs());
+
+  auto& ns_configs = config->namespace_configs();
+  ASSERT_EQ(2U, ns_configs.size());
+
+  // find second namespace
+  const NamespaceConfig* ns_system = nullptr;
+  for (auto& ns : ns_configs) {
+    std::string ns_name = ns->name();
+    ASSERT_TRUE(ns_name == "system" || ns_name == "default")
+        << "unexpected ns name: " << ns->name();
+
+    if (ns_name == "system") {
+      ns_system = ns.get();
+    }
+  }
+
+  ASSERT_TRUE(ns_system != nullptr) << "system namespace was not found";
+
+  ASSERT_TRUE(ns_system->isolated());
+  ASSERT_EQ(kExpectedSystemSearchPath, ns_system->search_paths());
+  ASSERT_EQ(kExpectedSystemPermittedPath, ns_system->permitted_paths());
+}
+
+TEST(linker_config, smoke) {
+  run_linker_config_smoke_test(false);
+}
+
+TEST(linker_config, asan_smoke) {
+  run_linker_config_smoke_test(true);
+}