Merge "Keep .debug_frame on arm 32."
diff --git a/linker/linker.cpp b/linker/linker.cpp
index f68775c..32dce38 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -1162,6 +1162,13 @@
     }
   }
 
+#if !defined(__ANDROID_APEX__)
+  if (fd == -1) {
+    std::vector<std::string> bootstrap_paths = { std::string(kSystemLibDir) + "/bootstrap" };
+    fd = open_library_on_paths(zip_archive_cache, name, file_offset, bootstrap_paths, realpath);
+  }
+#endif
+
   if (fd == -1) {
     fd = open_library_on_paths(zip_archive_cache, name, file_offset, ns->get_default_library_paths(), realpath);
   }
diff --git a/linker/linker_config.cpp b/linker/linker_config.cpp
index 7741904..46c91a3 100644
--- a/linker/linker_config.cpp
+++ b/linker/linker_config.cpp
@@ -417,9 +417,22 @@
 
     if (resolve) {
       std::vector<std::string> resolved_paths;
-
-      // do not remove paths that do not exist
-      resolve_paths(paths, &resolved_paths);
+      for (const auto& path : paths) {
+        if (path.empty()) {
+          continue;
+        }
+        // this is single threaded. no need to lock
+        auto cached = resolved_paths_.find(path);
+        if (cached == resolved_paths_.end()) {
+          resolved_paths_[path] = resolve_path(path);
+          cached = resolved_paths_.find(path);
+        }
+        CHECK(cached != resolved_paths_.end());
+        if (cached->second.empty()) {
+          continue;
+        }
+        resolved_paths.push_back(cached->second);
+      }
 
       return resolved_paths;
     } else {
@@ -442,6 +455,7 @@
     return it;
   }
   std::unordered_map<std::string, PropertyValue> properties_;
+  std::unordered_map<std::string, std::string> resolved_paths_;
   int target_sdk_version_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(Properties);
diff --git a/linker/linker_utils.cpp b/linker/linker_utils.cpp
index e926671..29110ed 100644
--- a/linker/linker_utils.cpp
+++ b/linker/linker_utils.cpp
@@ -204,47 +204,54 @@
     if (path.empty()) {
       continue;
     }
+    std::string resolved = resolve_path(path);
+    if (!resolved.empty()) {
+      resolved_paths->push_back(std::move(resolved));
+    }
+  }
+}
 
-    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) == -1) {
-        DL_WARN("Warning: cannot stat file \"%s\": %s (ignoring)", resolved_path, strerror(errno));
-        continue;
+std::string resolve_path(const std::string& path) {
+  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) == -1) {
+      DL_WARN("Warning: cannot stat file \"%s\": %s (ignoring)", resolved_path, strerror(errno));
+      return "";
+    }
+    if (!S_ISDIR(s.st_mode)) {
+      DL_WARN("Warning: \"%s\" is not a directory (ignoring)", resolved_path);
+      return "";
+    }
+    return resolved_path;
+  } else {
+    std::string normalized_path;
+    if (!normalize_path(original_path, &normalized_path)) {
+      DL_WARN("Warning: unable to normalize \"%s\" (ignoring)", original_path);
+      return "";
+    }
+
+    std::string zip_path;
+    std::string entry_path;
+    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 (ignoring)",
+                zip_path.c_str(), strerror(errno));
+        return "";
       }
-      if (!S_ISDIR(s.st_mode)) {
-        DL_WARN("Warning: \"%s\" is not a directory (ignoring)", resolved_path);
-        continue;
-      }
-      resolved_paths->push_back(resolved_path);
+
+      return std::string(resolved_path) + kZipFileSeparator + entry_path;
     } else {
-      std::string normalized_path;
-      if (!normalize_path(original_path, &normalized_path)) {
-        DL_WARN("Warning: unable to normalize \"%s\" (ignoring)", original_path);
-        continue;
-      }
-
-      std::string zip_path;
-      std::string entry_path;
-      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 (ignoring)",
-                  zip_path.c_str(), strerror(errno));
-          continue;
-        }
-
-        resolved_paths->push_back(std::string(resolved_path) + kZipFileSeparator + entry_path);
-      } else {
-        struct stat s;
-        if (stat(normalized_path.c_str(), &s) == 0 && S_ISDIR(s.st_mode)) {
-          // Path is not a zip path, but an existing directory. Then add it
-          // although we failed to resolve it. b/119656753
-          resolved_paths->push_back(normalized_path);
-        }
+      struct stat s;
+      if (stat(normalized_path.c_str(), &s) == 0 && S_ISDIR(s.st_mode)) {
+        // Path is not a zip path, but an existing directory. Then add it
+        // although we failed to resolve it. b/119656753
+        return normalized_path;
       }
     }
   }
+  return "";
 }
 
 bool is_first_stage_init() {
diff --git a/linker/linker_utils.h b/linker/linker_utils.h
index 34a597b..5073b10 100644
--- a/linker/linker_utils.h
+++ b/linker/linker_utils.h
@@ -47,6 +47,9 @@
 //    normalizes entry name by calling normalize_path function.
 void resolve_paths(std::vector<std::string>& paths,
                    std::vector<std::string>* resolved_paths);
+// Resolve a single path. Return empty string when the path is invalid or can't
+// be resolved.
+std::string resolve_path(const std::string& path);
 
 void split_path(const char* path, const char* delimiters, std::vector<std::string>* paths);
 
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index c6d7ddb..d815dba 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -999,6 +999,7 @@
 #error "Unknown architecture"
 #endif
 #define PATH_TO_LIBC PATH_TO_SYSTEM_LIB "libc.so"
+#define PATH_TO_BOOTSTRAP_LIBC PATH_TO_SYSTEM_LIB "bootstrap/libc.so"
 #define ALTERNATE_PATH_TO_LIBC ALTERNATE_PATH_TO_SYSTEM_LIB "libc.so"
 
 TEST(dlfcn, dladdr_libc) {
@@ -1018,6 +1019,9 @@
               sizeof(ALTERNATE_PATH_TO_SYSTEM_LIB) - 1) == 0) {
     // Platform with emulated architecture.  Symlink on ARC++.
     ASSERT_TRUE(realpath(ALTERNATE_PATH_TO_LIBC, libc_realpath) == libc_realpath);
+  } else if (strncmp(PATH_TO_BOOTSTRAP_LIBC, info.dli_fname,
+                     sizeof(PATH_TO_BOOTSTRAP_LIBC) - 1) == 0) {
+    ASSERT_TRUE(realpath(PATH_TO_BOOTSTRAP_LIBC, libc_realpath) == libc_realpath);
   } else {
     // /system/lib is symlink when this test is executed on host.
     ASSERT_TRUE(realpath(PATH_TO_LIBC, libc_realpath) == libc_realpath);
diff --git a/tests/leak_test.cpp b/tests/leak_test.cpp
index 1fa9e56..6005209 100644
--- a/tests/leak_test.cpp
+++ b/tests/leak_test.cpp
@@ -124,17 +124,22 @@
 // http://b/36045112
 TEST(pthread_leak, detach) {
   LeakChecker lc;
+  constexpr int kThreadCount = 100;
 
-  for (size_t pass = 0; pass < 2; ++pass) {
-    constexpr int kThreadCount = 100;
+  // Devices with low power cores/low number of cores can not finish test in time hence decreasing
+  // threads count to 90.
+  // http://b/129924384.
+  int threads_count = (sysconf(_SC_NPROCESSORS_CONF) > 2) ? kThreadCount : (kThreadCount - 10);
+
+  for (size_t pass = 0; pass < 1; ++pass) {
     struct thread_data { pthread_barrier_t* barrier; pid_t* tid; } threads[kThreadCount] = {};
 
     pthread_barrier_t barrier;
-    ASSERT_EQ(pthread_barrier_init(&barrier, nullptr, kThreadCount + 1), 0);
+    ASSERT_EQ(pthread_barrier_init(&barrier, nullptr, threads_count + 1), 0);
 
     // Start child threads.
     pid_t tids[kThreadCount];
-    for (int i = 0; i < kThreadCount; ++i) {
+    for (int i = 0; i < threads_count; ++i) {
       threads[i] = {&barrier, &tids[i]};
       const auto thread_function = +[](void* ptr) -> void* {
         thread_data* data = static_cast<thread_data*>(ptr);
diff --git a/tests/stack_unwinding_test.cpp b/tests/stack_unwinding_test.cpp
index 0ff6f30..e620ecd 100644
--- a/tests/stack_unwinding_test.cpp
+++ b/tests/stack_unwinding_test.cpp
@@ -112,6 +112,10 @@
 }
 
 TEST(stack_unwinding, unwind_through_signal_frame) {
+#if defined(__i386__)
+  GTEST_SKIP() << "Temporarily skip test since it fails on x86 see b/132763120.";
+#endif
+
   ScopedSignalHandler ssh(SIGUSR1, UnwindSignalHandler);
 
   UnwindTest();
@@ -119,6 +123,10 @@
 
 // On LP32, the SA_SIGINFO flag gets you __restore_rt instead of __restore.
 TEST(stack_unwinding, unwind_through_signal_frame_SA_SIGINFO) {
+#if defined(__i386__)
+  GTEST_SKIP() << "Temporarily skip test since it fails on x86 see b/132763120.";
+#endif
+
   ScopedSignalHandler ssh(SIGUSR1, UnwindSignalHandler, SA_SIGINFO);
 
   UnwindTest();