Merge "Implement pthread barrier."
diff --git a/.clang-format b/.clang-format
index ea19538..9b7478c 100644
--- a/.clang-format
+++ b/.clang-format
@@ -2,6 +2,7 @@
 AllowShortBlocksOnASingleLine: false
 AllowShortFunctionsOnASingleLine: false
 
+ColumnLimit: 100
 CommentPragmas: NOLINT:.*
 DerivePointerAlignment: false
 IndentWidth: 2
diff --git a/libc/Android.bp b/libc/Android.bp
index 528dd47..d1228af 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -9,7 +9,6 @@
     "bionic/if_indextoname.c",
     "bionic/if_nametoindex.c",
     "bionic/initgroups.c",
-    "bionic/ioctl.c",
     "bionic/isatty.c",
     "bionic/memmem.c",
     "bionic/pututline.c",
@@ -1348,6 +1347,7 @@
         "bionic/gettid.cpp",
         "bionic/__gnu_basename.cpp",
         "bionic/inotify_init.cpp",
+        "bionic/ioctl.cpp",
         "bionic/lchown.cpp",
         "bionic/lfs64_support.cpp",
         "bionic/__libc_current_sigrtmax.cpp",
@@ -1367,6 +1367,7 @@
         "bionic/mkfifo.cpp",
         "bionic/mknod.cpp",
         "bionic/mntent.cpp",
+        "bionic/mremap.cpp",
         "bionic/NetdClientDispatch.cpp",
         "bionic/open.cpp",
         "bionic/pathconf.cpp",
diff --git a/libc/Android.mk b/libc/Android.mk
index a7fe15c..2849591 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -47,7 +47,6 @@
     bionic/if_indextoname.c \
     bionic/if_nametoindex.c \
     bionic/initgroups.c \
-    bionic/ioctl.c \
     bionic/isatty.c \
     bionic/memmem.c \
     bionic/pututline.c \
@@ -151,6 +150,7 @@
     bionic/gettid.cpp \
     bionic/__gnu_basename.cpp \
     bionic/inotify_init.cpp \
+    bionic/ioctl.cpp \
     bionic/lchown.cpp \
     bionic/lfs64_support.cpp \
     bionic/__libc_current_sigrtmax.cpp \
@@ -170,6 +170,7 @@
     bionic/mkfifo.cpp \
     bionic/mknod.cpp \
     bionic/mntent.cpp \
+    bionic/mremap.cpp \
     bionic/NetdClientDispatch.cpp \
     bionic/open.cpp \
     bionic/pathconf.cpp \
@@ -725,6 +726,7 @@
 LOCAL_CFLAGS += -DALL_STATE
 # Include tzsetwall, timelocal, timegm, time2posix, and posix2time.
 LOCAL_CFLAGS += -DSTD_INSPIRED
+# Obviously, we want to be thread-safe.
 LOCAL_CFLAGS += -DTHREAD_SAFE
 # The name of the tm_gmtoff field in our struct tm.
 LOCAL_CFLAGS += -DTM_GMTOFF=tm_gmtoff
@@ -732,6 +734,8 @@
 LOCAL_CFLAGS += -DTZDIR=\"/system/usr/share/zoneinfo\"
 # Include timezone and daylight globals.
 LOCAL_CFLAGS += -DUSG_COMPAT=1
+# Use the empty string (instead of "   ") as the timezone abbreviation fallback.
+LOCAL_CFLAGS += -DWILDABBR=\"\"
 LOCAL_CFLAGS += -DNO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
 LOCAL_CFLAGS += -Dlint
 
@@ -1043,15 +1047,15 @@
 LOCAL_CPPFLAGS := $(libc_common_cppflags) -Wold-style-cast
 LOCAL_C_INCLUDES := $(libc_common_c_includes)
 LOCAL_MODULE := libc_thread_atexit_impl
-# TODO: Clang tries to use __tls_get_addr which is not supported yet
-# remove after it is implemented.
-LOCAL_CLANG := false
 LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies)
 LOCAL_CXX_STL := none
 LOCAL_SYSTEM_SHARED_LIBRARIES :=
 LOCAL_SANITIZE := never
 LOCAL_NATIVE_COVERAGE := $(bionic_coverage)
 
+# b/25662915, clang compiled __cxa_thread_atexit_impl.cpp still failed.
+LOCAL_CLANG_arm64 := false
+
 include $(BUILD_STATIC_LIBRARY)
 
 # ========================================================
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index 4188c15..526b6d0 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -107,7 +107,7 @@
 int         ___close:close(int)  all
 pid_t       __getpid:getpid()  all
 int         munmap(void*, size_t)  all
-void*       mremap(void*, size_t, size_t, unsigned long)  all
+void*       ___mremap:mremap(void*, size_t, size_t, int, void*)  all
 int         msync(const void*, size_t, int)    all
 int         mprotect(const void*, size_t, int)  all
 int         madvise(void*, size_t, int)  all
diff --git a/libc/arch-arm/syscalls/___mremap.S b/libc/arch-arm/syscalls/___mremap.S
new file mode 100644
index 0000000..ade8d78
--- /dev/null
+++ b/libc/arch-arm/syscalls/___mremap.S
@@ -0,0 +1,23 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(___mremap)
+    mov     ip, sp
+    stmfd   sp!, {r4, r5, r6, r7}
+    .cfi_def_cfa_offset 16
+    .cfi_rel_offset r4, 0
+    .cfi_rel_offset r5, 4
+    .cfi_rel_offset r6, 8
+    .cfi_rel_offset r7, 12
+    ldmfd   ip, {r4, r5, r6}
+    ldr     r7, =__NR_mremap
+    swi     #0
+    ldmfd   sp!, {r4, r5, r6, r7}
+    .cfi_def_cfa_offset 0
+    cmn     r0, #(MAX_ERRNO + 1)
+    bxls    lr
+    neg     r0, r0
+    b       __set_errno_internal
+END(___mremap)
+.hidden ___mremap
diff --git a/libc/arch-arm/syscalls/mremap.S b/libc/arch-arm/syscalls/mremap.S
deleted file mode 100644
index 505ea3c..0000000
--- a/libc/arch-arm/syscalls/mremap.S
+++ /dev/null
@@ -1,14 +0,0 @@
-/* Generated by gensyscalls.py. Do not edit. */
-
-#include <private/bionic_asm.h>
-
-ENTRY(mremap)
-    mov     ip, r7
-    ldr     r7, =__NR_mremap
-    swi     #0
-    mov     r7, ip
-    cmn     r0, #(MAX_ERRNO + 1)
-    bxls    lr
-    neg     r0, r0
-    b       __set_errno_internal
-END(mremap)
diff --git a/libc/arch-arm64/syscalls/mremap.S b/libc/arch-arm64/syscalls/___mremap.S
similarity index 81%
rename from libc/arch-arm64/syscalls/mremap.S
rename to libc/arch-arm64/syscalls/___mremap.S
index 69b91d6..aeb93bc 100644
--- a/libc/arch-arm64/syscalls/mremap.S
+++ b/libc/arch-arm64/syscalls/___mremap.S
@@ -2,7 +2,7 @@
 
 #include <private/bionic_asm.h>
 
-ENTRY(mremap)
+ENTRY(___mremap)
     mov     x8, __NR_mremap
     svc     #0
 
@@ -11,4 +11,5 @@
     b.hi    __set_errno_internal
 
     ret
-END(mremap)
+END(___mremap)
+.hidden ___mremap
diff --git a/libc/arch-mips/syscalls/mremap.S b/libc/arch-mips/syscalls/___mremap.S
similarity index 84%
rename from libc/arch-mips/syscalls/mremap.S
rename to libc/arch-mips/syscalls/___mremap.S
index 7cbb94e..82e2eb3 100644
--- a/libc/arch-mips/syscalls/mremap.S
+++ b/libc/arch-mips/syscalls/___mremap.S
@@ -2,7 +2,7 @@
 
 #include <private/bionic_asm.h>
 
-ENTRY(mremap)
+ENTRY(___mremap)
     .set noreorder
     .cpload t9
     li v0, __NR_mremap
@@ -16,4 +16,5 @@
     j t9
     nop
     .set reorder
-END(mremap)
+END(___mremap)
+.hidden ___mremap
diff --git a/libc/arch-mips64/syscalls/mremap.S b/libc/arch-mips64/syscalls/___mremap.S
similarity index 87%
rename from libc/arch-mips64/syscalls/mremap.S
rename to libc/arch-mips64/syscalls/___mremap.S
index cf7f1de..5004d50 100644
--- a/libc/arch-mips64/syscalls/mremap.S
+++ b/libc/arch-mips64/syscalls/___mremap.S
@@ -2,7 +2,7 @@
 
 #include <private/bionic_asm.h>
 
-ENTRY(mremap)
+ENTRY(___mremap)
     .set push
     .set noreorder
     li v0, __NR_mremap
@@ -22,4 +22,5 @@
     j t9
     move ra, t0
     .set pop
-END(mremap)
+END(___mremap)
+.hidden ___mremap
diff --git a/libc/arch-x86/syscalls/mremap.S b/libc/arch-x86/syscalls/___mremap.S
similarity index 69%
rename from libc/arch-x86/syscalls/mremap.S
rename to libc/arch-x86/syscalls/___mremap.S
index 869ef5d..e5e9c54 100644
--- a/libc/arch-x86/syscalls/mremap.S
+++ b/libc/arch-x86/syscalls/___mremap.S
@@ -2,7 +2,7 @@
 
 #include <private/bionic_asm.h>
 
-ENTRY(mremap)
+ENTRY(___mremap)
     pushl   %ebx
     .cfi_def_cfa_offset 8
     .cfi_rel_offset ebx, 0
@@ -15,10 +15,14 @@
     pushl   %esi
     .cfi_adjust_cfa_offset 4
     .cfi_rel_offset esi, 0
-    mov     20(%esp), %ebx
-    mov     24(%esp), %ecx
-    mov     28(%esp), %edx
-    mov     32(%esp), %esi
+    pushl   %edi
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset edi, 0
+    mov     24(%esp), %ebx
+    mov     28(%esp), %ecx
+    mov     32(%esp), %edx
+    mov     36(%esp), %esi
+    mov     40(%esp), %edi
     movl    $__NR_mremap, %eax
     int     $0x80
     cmpl    $-MAX_ERRNO, %eax
@@ -28,9 +32,11 @@
     call    __set_errno_internal
     addl    $4, %esp
 1:
+    popl    %edi
     popl    %esi
     popl    %edx
     popl    %ecx
     popl    %ebx
     ret
-END(mremap)
+END(___mremap)
+.hidden ___mremap
diff --git a/libc/arch-x86_64/syscalls/mremap.S b/libc/arch-x86_64/syscalls/___mremap.S
similarity index 84%
rename from libc/arch-x86_64/syscalls/mremap.S
rename to libc/arch-x86_64/syscalls/___mremap.S
index a6042cb..cc6dee9 100644
--- a/libc/arch-x86_64/syscalls/mremap.S
+++ b/libc/arch-x86_64/syscalls/___mremap.S
@@ -2,7 +2,7 @@
 
 #include <private/bionic_asm.h>
 
-ENTRY(mremap)
+ENTRY(___mremap)
     movq    %rcx, %r10
     movl    $__NR_mremap, %eax
     syscall
@@ -13,4 +13,5 @@
     call    __set_errno_internal
 1:
     ret
-END(mremap)
+END(___mremap)
+.hidden ___mremap
diff --git a/libc/bionic/ioctl.c b/libc/bionic/ioctl.cpp
similarity index 85%
rename from libc/bionic/ioctl.c
rename to libc/bionic/ioctl.cpp
index 6dd95d0..db85132 100644
--- a/libc/bionic/ioctl.c
+++ b/libc/bionic/ioctl.cpp
@@ -25,19 +25,16 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
+#include <sys/ioctl.h>
 #include <stdarg.h>
 
-extern int __ioctl(int, int, void *);
+extern "C" int __ioctl(int, int, void *);
 
-int ioctl(int fd, int request, ...)
-{
-    va_list ap;
-    void * arg;
-
-    va_start(ap, request);
-    arg = va_arg(ap, void *);
-    va_end(ap);
-
-    return __ioctl(fd, request, arg);
+int ioctl(int fd, int request, ...) {
+  va_list ap;
+  va_start(ap, request);
+  void* arg = va_arg(ap, void*);
+  va_end(ap);
+  return __ioctl(fd, request, arg);
 }
-
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index 4995414..a683748 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -245,7 +245,11 @@
 }
 
 static bool __is_unsafe_environment_variable(const char* name) {
-  // None of these should be allowed in setuid programs.
+  // None of these should be allowed when the AT_SECURE auxv
+  // flag is set. This flag is set to inform userspace that a
+  // security transition has occurred, for example, as a result
+  // of executing a setuid program or the result of an SELinux
+  // security transition.
   static constexpr const char* UNSAFE_VARIABLE_NAMES[] = {
     "GCONV_PATH",
     "GETCONF_DIR",
diff --git a/libc/bionic/ioctl.c b/libc/bionic/mremap.cpp
similarity index 71%
copy from libc/bionic/ioctl.c
copy to libc/bionic/mremap.cpp
index 6dd95d0..4892b1d 100644
--- a/libc/bionic/ioctl.c
+++ b/libc/bionic/mremap.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -25,19 +25,21 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
+#include <sys/mman.h>
 #include <stdarg.h>
 
-extern int __ioctl(int, int, void *);
+extern "C" void* ___mremap(void*, size_t, size_t, int, void*);
 
-int ioctl(int fd, int request, ...)
-{
+void* mremap(void* old_address, size_t old_size, size_t new_size, int flags, ...) {
+  void* new_address = nullptr;
+  // The optional argument is only valid if the MREMAP_FIXED flag is set,
+  // so we assume it's not present otherwise.
+  if ((flags & MREMAP_FIXED) != 0) {
     va_list ap;
-    void * arg;
-
-    va_start(ap, request);
-    arg = va_arg(ap, void *);
+    va_start(ap, flags);
+    new_address = va_arg(ap, void*);
     va_end(ap);
-
-    return __ioctl(fd, request, arg);
+  }
+  return ___mremap(old_address, old_size, new_size, flags, new_address);
 }
-
diff --git a/libc/bionic/system_properties.cpp b/libc/bionic/system_properties.cpp
index c436a16..9fe982a 100644
--- a/libc/bionic/system_properties.cpp
+++ b/libc/bionic/system_properties.cpp
@@ -112,23 +112,57 @@
     DISALLOW_COPY_AND_ASSIGN(prop_bt);
 };
 
-struct prop_area {
-    uint32_t bytes_used;
-    atomic_uint_least32_t serial;
-    uint32_t magic;
-    uint32_t version;
-    uint32_t reserved[28];
-    char data[0];
+class prop_area {
+public:
 
     prop_area(const uint32_t magic, const uint32_t version) :
-        magic(magic), version(version) {
-        atomic_init(&serial, 0);
-        memset(reserved, 0, sizeof(reserved));
+        magic_(magic), version_(version) {
+        atomic_init(&serial_, 0);
+        memset(reserved_, 0, sizeof(reserved_));
         // Allocate enough space for the root node.
-        bytes_used = sizeof(prop_bt);
+        bytes_used_ = sizeof(prop_bt);
     }
 
+    const prop_info *find(const char *name);
+    bool add(const char *name, unsigned int namelen,
+             const char *value, unsigned int valuelen);
+
+    bool foreach(void (*propfn)(const prop_info *pi, void *cookie), void *cookie);
+
+    atomic_uint_least32_t *serial() { return &serial_; }
+    uint32_t magic() const { return magic_; }
+    uint32_t version() const { return version_; }
+
 private:
+    void *allocate_obj(const size_t size, uint_least32_t *const off);
+    prop_bt *new_prop_bt(const char *name, uint8_t namelen, uint_least32_t *const off);
+    prop_info *new_prop_info(const char *name, uint8_t namelen,
+                             const char *value, uint8_t valuelen,
+                             uint_least32_t *const off);
+    void *to_prop_obj(uint_least32_t off);
+    prop_bt *to_prop_bt(atomic_uint_least32_t *off_p);
+    prop_info *to_prop_info(atomic_uint_least32_t *off_p);
+
+    prop_bt *root_node();
+
+    prop_bt *find_prop_bt(prop_bt *const bt, const char *name,
+                          uint8_t namelen, bool alloc_if_needed);
+
+    const prop_info *find_property(prop_bt *const trie, const char *name,
+                                   uint8_t namelen, const char *value,
+                                   uint8_t valuelen, bool alloc_if_needed);
+
+    bool foreach_property(prop_bt *const trie,
+                          void (*propfn)(const prop_info *pi, void *cookie),
+                          void *cookie);
+
+    uint32_t bytes_used_;
+    atomic_uint_least32_t serial_;
+    uint32_t magic_;
+    uint32_t version_;
+    uint32_t reserved_[28];
+    char data_[0];
+
     DISALLOW_COPY_AND_ASSIGN(prop_area);
 };
 
@@ -246,13 +280,14 @@
     }
 
     prop_area* pa = reinterpret_cast<prop_area*>(map_result);
-    if ((pa->magic != PROP_AREA_MAGIC) || (pa->version != PROP_AREA_VERSION &&
-                pa->version != PROP_AREA_VERSION_COMPAT)) {
+    if ((pa->magic() != PROP_AREA_MAGIC) ||
+        (pa->version() != PROP_AREA_VERSION &&
+         pa->version() != PROP_AREA_VERSION_COMPAT)) {
         munmap(pa, pa_size);
         return -1;
     }
 
-    if (pa->version == PROP_AREA_VERSION_COMPAT) {
+    if (pa->version() == PROP_AREA_VERSION_COMPAT) {
         compat_mode = true;
     }
 
@@ -290,20 +325,19 @@
     return map_result;
 }
 
-static void *allocate_obj(const size_t size, uint_least32_t *const off)
+void *prop_area::allocate_obj(const size_t size, uint_least32_t *const off)
 {
-    prop_area *pa = __system_property_area__;
     const size_t aligned = BIONIC_ALIGN(size, sizeof(uint_least32_t));
-    if (pa->bytes_used + aligned > pa_data_size) {
+    if (bytes_used_ + aligned > pa_data_size) {
         return NULL;
     }
 
-    *off = pa->bytes_used;
-    pa->bytes_used += aligned;
-    return pa->data + *off;
+    *off = bytes_used_;
+    bytes_used_ += aligned;
+    return data_ + *off;
 }
 
-static prop_bt *new_prop_bt(const char *name, uint8_t namelen, uint_least32_t *const off)
+prop_bt *prop_area::new_prop_bt(const char *name, uint8_t namelen, uint_least32_t *const off)
 {
     uint_least32_t new_offset;
     void *const p = allocate_obj(sizeof(prop_bt) + namelen + 1, &new_offset);
@@ -316,7 +350,7 @@
     return NULL;
 }
 
-static prop_info *new_prop_info(const char *name, uint8_t namelen,
+prop_info *prop_area::new_prop_info(const char *name, uint8_t namelen,
         const char *value, uint8_t valuelen, uint_least32_t *const off)
 {
     uint_least32_t new_offset;
@@ -330,27 +364,25 @@
     return NULL;
 }
 
-static void *to_prop_obj(uint_least32_t off)
+void *prop_area::to_prop_obj(uint_least32_t off)
 {
     if (off > pa_data_size)
         return NULL;
-    if (!__system_property_area__)
-        return NULL;
 
-    return (__system_property_area__->data + off);
+    return (data_ + off);
 }
 
-static inline prop_bt *to_prop_bt(atomic_uint_least32_t* off_p) {
+inline prop_bt *prop_area::to_prop_bt(atomic_uint_least32_t* off_p) {
   uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume);
   return reinterpret_cast<prop_bt*>(to_prop_obj(off));
 }
 
-static inline prop_info *to_prop_info(atomic_uint_least32_t* off_p) {
+inline prop_info *prop_area::to_prop_info(atomic_uint_least32_t* off_p) {
   uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume);
   return reinterpret_cast<prop_info*>(to_prop_obj(off));
 }
 
-static inline prop_bt *root_node()
+inline prop_bt *prop_area::root_node()
 {
     return reinterpret_cast<prop_bt*>(to_prop_obj(0));
 }
@@ -366,8 +398,8 @@
         return strncmp(one, two, one_len);
 }
 
-static prop_bt *find_prop_bt(prop_bt *const bt, const char *name,
-                             uint8_t namelen, bool alloc_if_needed)
+prop_bt *prop_area::find_prop_bt(prop_bt *const bt, const char *name,
+                                 uint8_t namelen, bool alloc_if_needed)
 {
 
     prop_bt* current = bt;
@@ -417,7 +449,7 @@
     }
 }
 
-static const prop_info *find_property(prop_bt *const trie, const char *name,
+const prop_info *prop_area::find_property(prop_bt *const trie, const char *name,
         uint8_t namelen, const char *value, uint8_t valuelen,
         bool alloc_if_needed)
 {
@@ -543,39 +575,52 @@
     cookie->count++;
 }
 
-static int foreach_property(prop_bt *const trie,
+bool prop_area::foreach_property(prop_bt *const trie,
         void (*propfn)(const prop_info *pi, void *cookie), void *cookie)
 {
     if (!trie)
-        return -1;
+        return false;
 
     uint_least32_t left_offset = atomic_load_explicit(&trie->left, memory_order_relaxed);
     if (left_offset != 0) {
         const int err = foreach_property(to_prop_bt(&trie->left), propfn, cookie);
         if (err < 0)
-            return -1;
+            return false;
     }
     uint_least32_t prop_offset = atomic_load_explicit(&trie->prop, memory_order_relaxed);
     if (prop_offset != 0) {
         prop_info *info = to_prop_info(&trie->prop);
         if (!info)
-            return -1;
+            return false;
         propfn(info, cookie);
     }
     uint_least32_t children_offset = atomic_load_explicit(&trie->children, memory_order_relaxed);
     if (children_offset != 0) {
         const int err = foreach_property(to_prop_bt(&trie->children), propfn, cookie);
         if (err < 0)
-            return -1;
+            return false;
     }
     uint_least32_t right_offset = atomic_load_explicit(&trie->right, memory_order_relaxed);
     if (right_offset != 0) {
         const int err = foreach_property(to_prop_bt(&trie->right), propfn, cookie);
         if (err < 0)
-            return -1;
+            return false;
     }
 
-    return 0;
+    return true;
+}
+
+const prop_info *prop_area::find(const char *name) {
+    return find_property(root_node(), name, strlen(name), nullptr, 0, false);
+}
+
+bool prop_area::add(const char *name, unsigned int namelen,
+                    const char *value, unsigned int valuelen) {
+    return find_property(root_node(), name, namelen, value, valuelen, true);
+}
+
+bool prop_area::foreach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) {
+    return foreach_property(root_node(), propfn, cookie);
 }
 
 int __system_properties_init()
@@ -605,7 +650,7 @@
         return -1;
     }
     // Make sure this read fulfilled before __system_property_serial
-    return atomic_load_explicit(&(pa->serial), memory_order_acquire);
+    return atomic_load_explicit(pa->serial(), memory_order_acquire);
 }
 
 const prop_info *__system_property_find(const char *name)
@@ -613,7 +658,12 @@
     if (__predict_false(compat_mode)) {
         return __system_property_find_compat(name);
     }
-    return find_property(root_node(), name, strlen(name), NULL, 0, false);
+
+    if (!__system_property_area__) {
+        return nullptr;
+    }
+
+    return __system_property_area__->find(name);
 }
 
 // The C11 standard doesn't allow atomic loads from const fields,
@@ -708,10 +758,10 @@
     __futex_wake(&pi->serial, INT32_MAX);
 
     atomic_store_explicit(
-        &pa->serial,
-        atomic_load_explicit(&pa->serial, memory_order_relaxed) + 1,
+        pa->serial(),
+        atomic_load_explicit(pa->serial(), memory_order_relaxed) + 1,
         memory_order_release);
-    __futex_wake(&pa->serial, INT32_MAX);
+    __futex_wake(pa->serial(), INT32_MAX);
 
     return 0;
 }
@@ -720,7 +770,6 @@
             const char *value, unsigned int valuelen)
 {
     prop_area *pa = __system_property_area__;
-    const prop_info *pi;
 
     if (namelen >= PROP_NAME_MAX)
         return -1;
@@ -729,17 +778,21 @@
     if (namelen < 1)
         return -1;
 
-    pi = find_property(root_node(), name, namelen, value, valuelen, true);
-    if (!pi)
+    if (!__system_property_area__) {
+        return -1;
+    }
+
+    bool ret = __system_property_area__->add(name, namelen, value, valuelen);
+    if (!ret)
         return -1;
 
     // There is only a single mutator, but we want to make sure that
     // updates are visible to a reader waiting for the update.
     atomic_store_explicit(
-        &pa->serial,
-        atomic_load_explicit(&pa->serial, memory_order_relaxed) + 1,
+        pa->serial(),
+        atomic_load_explicit(pa->serial(), memory_order_relaxed) + 1,
         memory_order_release);
-    __futex_wake(&pa->serial, INT32_MAX);
+    __futex_wake(pa->serial(), INT32_MAX);
     return 0;
 }
 
@@ -762,8 +815,8 @@
     uint32_t my_serial;
 
     do {
-        __futex_wait(&pa->serial, serial, NULL);
-        my_serial = atomic_load_explicit(&pa->serial, memory_order_acquire);
+        __futex_wait(pa->serial(), serial, NULL);
+        my_serial = atomic_load_explicit(pa->serial(), memory_order_acquire);
     } while (my_serial == serial);
 
     return my_serial;
@@ -788,5 +841,9 @@
         return __system_property_foreach_compat(propfn, cookie);
     }
 
-    return foreach_property(root_node(), propfn, cookie);
+    if (!__system_property_area__) {
+        return -1;
+    }
+
+    return __system_property_area__->foreach(propfn, cookie) ? 0 : -1;
 }
diff --git a/libc/include/android/dlext.h b/libc/include/android/dlext.h
index 4af5de3..d59de29 100644
--- a/libc/include/android/dlext.h
+++ b/libc/include/android/dlext.h
@@ -98,6 +98,11 @@
    */
   ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS = 0x100,
 
+  /* This flag used to load library in a different namespace. The namespace is
+   * specified in library_namespace.
+   */
+  ANDROID_DLEXT_USE_NAMESPACE = 0x200,
+
   /* Mask of valid bits */
   ANDROID_DLEXT_VALID_FLAG_BITS       = ANDROID_DLEXT_RESERVED_ADDRESS |
                                         ANDROID_DLEXT_RESERVED_ADDRESS_HINT |
@@ -107,9 +112,12 @@
                                         ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET |
                                         ANDROID_DLEXT_FORCE_LOAD |
                                         ANDROID_DLEXT_FORCE_FIXED_VADDR |
-                                        ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS,
+                                        ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS |
+                                        ANDROID_DLEXT_USE_NAMESPACE,
 };
 
+struct android_namespace_t;
+
 typedef struct {
   uint64_t flags;
   void*   reserved_addr;
@@ -117,10 +125,40 @@
   int     relro_fd;
   int     library_fd;
   off64_t library_fd_offset;
+  struct android_namespace_t* library_namespace;
 } android_dlextinfo;
 
 extern void* android_dlopen_ext(const char* filename, int flag, const android_dlextinfo* extinfo);
 
+/*
+ * Initializes public namespace. The path is the list of sonames
+ * separated by colon. Example: "libc.so:libm.so:libdl.so".
+ *
+ * The libraries in this list should be loaded prior to this call.
+ */
+extern bool android_init_public_namespace(const char* path);
+
+/*
+ * Creates new linker namespace.
+ * ld_library_path and default_library_path represent the search path
+ * for the libraries in the namespace.
+ *
+ * The libraries in the namespace are searched by folowing order:
+ * 1. ld_library_path (Think of this as namespace-local LD_LIBRARY_PATH)
+ * 2. In directories specified by DT_RUNPATH of the "needed by" binary.
+ * 3. deault_library_path (This of this as namespace-local default library path)
+ *
+ * When is_isolated is true the resulted namespace requires all of the libraries
+ * to be on the search path; the search_path is ld_library_path:default_library_path.
+ *
+ * If a library or any of its dependencies are outside of the search path and not
+ * part of the public namespace dlopen will fail.
+ */
+extern struct android_namespace_t* android_create_namespace(const char* name,
+                                                            const char* ld_library_path,
+                                                            const char* default_library_path,
+                                                            bool is_isolated);
+
 __END_DECLS
 
 #endif /* __ANDROID_DLEXT_H__ */
diff --git a/libc/include/stdio.h b/libc/include/stdio.h
index 1df4b54..fd653d9 100644
--- a/libc/include/stdio.h
+++ b/libc/include/stdio.h
@@ -270,6 +270,7 @@
 void clearerr_unlocked(FILE*);
 int feof_unlocked(FILE*);
 int ferror_unlocked(FILE*);
+int fileno_unlocked(FILE*);
 
 /*
  * Stdio function-access interface.
diff --git a/libc/include/sys/mman.h b/libc/include/sys/mman.h
index 6857f60..a19ceb5 100644
--- a/libc/include/sys/mman.h
+++ b/libc/include/sys/mman.h
@@ -59,7 +59,7 @@
 extern int munmap(void*, size_t);
 extern int msync(const void*, size_t, int);
 extern int mprotect(const void*, size_t, int);
-extern void* mremap(void*, size_t, size_t, unsigned long);
+extern void* mremap(void*, size_t, size_t, int, ...);
 
 extern int mlockall(int);
 extern int munlockall(void);
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index 489a368..f7805fe 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1310,6 +1310,7 @@
     __pwrite_chk;
     __pwrite64_chk;
     __write_chk;
+    fileno_unlocked;
     getgrgid_r;
     getgrnam_r;
     preadv;
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index 6b8b55b..d5fe262 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1156,6 +1156,7 @@
     __pwrite_chk;
     __pwrite64_chk;
     __write_chk;
+    fileno_unlocked;
     getgrgid_r;
     getgrnam_r;
     preadv;
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 83965bd..738836c 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1337,6 +1337,7 @@
     __pwrite_chk;
     __pwrite64_chk;
     __write_chk;
+    fileno_unlocked;
     getgrgid_r;
     getgrnam_r;
     preadv;
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index d74c2c8..2121ba7 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1273,6 +1273,7 @@
     __pwrite_chk;
     __pwrite64_chk;
     __write_chk;
+    fileno_unlocked;
     getgrgid_r;
     getgrnam_r;
     preadv;
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index 6b8b55b..d5fe262 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1156,6 +1156,7 @@
     __pwrite_chk;
     __pwrite64_chk;
     __write_chk;
+    fileno_unlocked;
     getgrgid_r;
     getgrnam_r;
     preadv;
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index 7dc85cc..fdee2c6 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1271,6 +1271,7 @@
     __pwrite_chk;
     __pwrite64_chk;
     __write_chk;
+    fileno_unlocked;
     getgrgid_r;
     getgrnam_r;
     preadv;
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index 6b8b55b..d5fe262 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1156,6 +1156,7 @@
     __pwrite_chk;
     __pwrite64_chk;
     __write_chk;
+    fileno_unlocked;
     getgrgid_r;
     getgrnam_r;
     preadv;
diff --git a/libc/stdio/stdio_ext.cpp b/libc/stdio/stdio_ext.cpp
index fea44f6..310076a 100644
--- a/libc/stdio/stdio_ext.cpp
+++ b/libc/stdio/stdio_ext.cpp
@@ -99,3 +99,7 @@
 int ferror_unlocked(FILE* fp) {
   return __sferror(fp);
 }
+
+int fileno_unlocked(FILE* fp) {
+  return __sfileno(fp);
+}
diff --git a/libc/tzcode/localtime.c b/libc/tzcode/localtime.c
index cb8aeed..b1ebb24 100644
--- a/libc/tzcode/localtime.c
+++ b/libc/tzcode/localtime.c
@@ -1316,9 +1316,10 @@
 tzset_unlocked(void)
 {
 #if defined(__ANDROID__)
+  // The TZ environment variable is meant to override the system-wide setting.
   const char * name = getenv("TZ");
 
-  // Try the "persist.sys.timezone" system property.
+  // If that's not set, look at the "persist.sys.timezone" system property.
   if (name == NULL) {
     static const prop_info *pi;
 
@@ -1340,6 +1341,10 @@
     }
   }
 
+  // If that's not available (because you're running AOSP on a WiFi-only
+  // device, say), fall back to GMT.
+  if (name == NULL) name = gmt;
+
   tzsetlcl(name);
 #else
   tzsetlcl(getenv("TZ"));
diff --git a/libc/tzcode/strftime.c b/libc/tzcode/strftime.c
index 10dfb4b..4349cf6 100644
--- a/libc/tzcode/strftime.c
+++ b/libc/tzcode/strftime.c
@@ -502,7 +502,23 @@
                 continue;
             case 'Z':
 #ifdef TM_ZONE
-                pt = _add(t->TM_ZONE, pt, ptlim, modifier);
+                // BEGIN: Android-changed.
+                {
+                    const char* zone = t->TM_ZONE;
+                    if (!zone || !*zone) {
+                        // "The value of tm_isdst shall be positive if Daylight Savings Time is
+                        // in effect, 0 if Daylight Savings Time is not in effect, and negative
+                        // if the information is not available."
+                        if (t->tm_isdst == 0) zone = tzname[0];
+                        else if (t->tm_isdst > 0) zone = tzname[1];
+
+                        // "Replaced by the timezone name or abbreviation, or by no bytes if no
+                        // timezone information exists."
+                        if (!zone || !*zone) zone = "";
+                    }
+                    pt = _add(zone, pt, ptlim, modifier);
+                }
+                // END: Android-changed.
 #else
                 if (t->tm_isdst >= 0)
                     pt = _add(tzname[t->tm_isdst != 0],
diff --git a/libdl/libdl.arm.map b/libdl/libdl.arm.map
index 9e58704..3417f99 100644
--- a/libdl/libdl.arm.map
+++ b/libdl/libdl.arm.map
@@ -14,10 +14,16 @@
     *;
 };
 
+LIBC_N {
+  global:
+    android_init_public_namespace;
+    android_create_namespace;
+} LIBC;
+
 LIBC_PRIVATE {
   global:
     android_get_application_target_sdk_version;
     android_set_application_target_sdk_version;
     android_get_LD_LIBRARY_PATH;
     android_update_LD_LIBRARY_PATH;
-} LIBC;
+} LIBC_N;
diff --git a/libdl/libdl.arm64.map b/libdl/libdl.arm64.map
index e83b501..b7e9aec 100644
--- a/libdl/libdl.arm64.map
+++ b/libdl/libdl.arm64.map
@@ -13,10 +13,16 @@
     *;
 };
 
+LIBC_N {
+  global:
+    android_init_public_namespace;
+    android_create_namespace;
+} LIBC;
+
 LIBC_PRIVATE {
   global:
     android_get_application_target_sdk_version;
     android_set_application_target_sdk_version;
     android_get_LD_LIBRARY_PATH;
     android_update_LD_LIBRARY_PATH;
-} LIBC;
+} LIBC_N;
diff --git a/libdl/libdl.c b/libdl/libdl.c
index 9a858a3..3cde5eb 100644
--- a/libdl/libdl.c
+++ b/libdl/libdl.c
@@ -33,12 +33,26 @@
 _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc __unused, int* pcount __unused) { return 0; }
 #endif
 
-int dl_iterate_phdr(int (*cb)(struct dl_phdr_info* info, size_t size, void* data) __unused, void* data __unused) { return 0; }
+int dl_iterate_phdr(int (*cb)(struct dl_phdr_info* info, size_t size, void* data) __unused,
+                    void* data __unused) {
+  return 0;
+}
 
 void android_get_LD_LIBRARY_PATH(char* buffer __unused, size_t buffer_size __unused) { }
 void android_update_LD_LIBRARY_PATH(const char* ld_library_path __unused) { }
 
-void* android_dlopen_ext(const char* filename __unused, int flag __unused, const android_dlextinfo* extinfo __unused) { return 0; }
+void* android_dlopen_ext(const char* filename __unused, int flag __unused,
+                         const android_dlextinfo* extinfo __unused) {
+  return 0;
+}
 
 void android_set_application_target_sdk_version(uint32_t target __unused) { }
 uint32_t android_get_application_target_sdk_version() { return 0; }
+
+bool android_init_public_namespace(const char* paths __unused) { return false; }
+struct android_namespace_t* android_create_namespace(const char* name __unused,
+                                                     const char* ld_library_path __unused,
+                                                     const char* default_library_path __unused,
+                                                     bool isolated __unused) {
+  return 0;
+}
diff --git a/libdl/libdl.map.txt b/libdl/libdl.map.txt
index f71f40b..421d825 100644
--- a/libdl/libdl.map.txt
+++ b/libdl/libdl.map.txt
@@ -28,10 +28,16 @@
     *;
 };
 
+LIBC_N {
+  global:
+    android_init_public_namespace;
+    android_create_namespace;
+} LIBC;
+
 LIBC_PRIVATE {
   global:
     android_get_application_target_sdk_version;
     android_set_application_target_sdk_version;
     android_get_LD_LIBRARY_PATH;
     android_update_LD_LIBRARY_PATH;
-} LIBC;
+} LIBC_N;
diff --git a/libdl/libdl.mips.map b/libdl/libdl.mips.map
index e83b501..b7e9aec 100644
--- a/libdl/libdl.mips.map
+++ b/libdl/libdl.mips.map
@@ -13,10 +13,16 @@
     *;
 };
 
+LIBC_N {
+  global:
+    android_init_public_namespace;
+    android_create_namespace;
+} LIBC;
+
 LIBC_PRIVATE {
   global:
     android_get_application_target_sdk_version;
     android_set_application_target_sdk_version;
     android_get_LD_LIBRARY_PATH;
     android_update_LD_LIBRARY_PATH;
-} LIBC;
+} LIBC_N;
diff --git a/libdl/libdl.mips64.map b/libdl/libdl.mips64.map
index e83b501..b7e9aec 100644
--- a/libdl/libdl.mips64.map
+++ b/libdl/libdl.mips64.map
@@ -13,10 +13,16 @@
     *;
 };
 
+LIBC_N {
+  global:
+    android_init_public_namespace;
+    android_create_namespace;
+} LIBC;
+
 LIBC_PRIVATE {
   global:
     android_get_application_target_sdk_version;
     android_set_application_target_sdk_version;
     android_get_LD_LIBRARY_PATH;
     android_update_LD_LIBRARY_PATH;
-} LIBC;
+} LIBC_N;
diff --git a/libdl/libdl.x86.map b/libdl/libdl.x86.map
index e83b501..b7e9aec 100644
--- a/libdl/libdl.x86.map
+++ b/libdl/libdl.x86.map
@@ -13,10 +13,16 @@
     *;
 };
 
+LIBC_N {
+  global:
+    android_init_public_namespace;
+    android_create_namespace;
+} LIBC;
+
 LIBC_PRIVATE {
   global:
     android_get_application_target_sdk_version;
     android_set_application_target_sdk_version;
     android_get_LD_LIBRARY_PATH;
     android_update_LD_LIBRARY_PATH;
-} LIBC;
+} LIBC_N;
diff --git a/libdl/libdl.x86_64.map b/libdl/libdl.x86_64.map
index e83b501..b7e9aec 100644
--- a/libdl/libdl.x86_64.map
+++ b/libdl/libdl.x86_64.map
@@ -13,10 +13,16 @@
     *;
 };
 
+LIBC_N {
+  global:
+    android_init_public_namespace;
+    android_create_namespace;
+} LIBC;
+
 LIBC_PRIVATE {
   global:
     android_get_application_target_sdk_version;
     android_set_application_target_sdk_version;
     android_get_LD_LIBRARY_PATH;
     android_update_LD_LIBRARY_PATH;
-} LIBC;
+} LIBC_N;
diff --git a/linker/Android.mk b/linker/Android.mk
index 0188a49..85ac0ca 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -9,12 +9,12 @@
     dlfcn.cpp \
     linker.cpp \
     linker_allocator.cpp \
-    linker_sdk_versions.cpp \
     linker_block_allocator.cpp \
     linker_libc_support.c \
     linker_mapped_file_fragment.cpp \
     linker_memory.cpp \
     linker_phdr.cpp \
+    linker_sdk_versions.cpp \
     linker_utils.cpp \
     rt.cpp \
 
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index 93189df..7957921 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -108,9 +108,16 @@
   const ElfW(Sym)* sym = nullptr;
   void* caller_addr = __builtin_return_address(0);
   soinfo* caller = find_containing_library(caller_addr);
+  if (caller == nullptr) {
+    char buf[256];
+    __libc_format_buffer(buf, sizeof(buf), "dlsym couldn't locate its caller address=%p; "
+                         "the caller was code not loaded by the dynamic linker", caller_addr);
+    __bionic_format_dlerror(buf, nullptr);
+    return nullptr;
+  }
 
   if (handle == RTLD_DEFAULT || handle == RTLD_NEXT) {
-    sym = dlsym_linear_lookup(symbol, &found, caller, handle);
+    sym = dlsym_linear_lookup(caller->get_namespace(), symbol, &found, caller, handle);
   } else {
     sym = dlsym_handle_lookup(reinterpret_cast<soinfo*>(handle), &found, symbol);
   }
@@ -177,6 +184,30 @@
   return get_application_target_sdk_version();
 }
 
+bool android_init_public_namespace(const char* path) {
+  ScopedPthreadMutexLocker locker(&g_dl_mutex);
+  bool success = init_public_namespace(path);
+  if (!success) {
+    __bionic_format_dlerror("android_init_public_namespace failed", linker_get_error_buffer());
+  }
+
+  return success;
+}
+
+android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path,
+                                              const char* default_library_path, bool is_isolated) {
+  ScopedPthreadMutexLocker locker(&g_dl_mutex);
+
+  android_namespace_t* result = create_namespace(name, ld_library_path,
+                                                 default_library_path, is_isolated);
+
+  if (result == nullptr) {
+    __bionic_format_dlerror("android_create_namespace failed", linker_get_error_buffer());
+  }
+
+  return result;
+}
+
 // name_offset: starting index of the name in libdl_info.strtab
 #define ELF32_SYM_INITIALIZER(name_offset, value, shndx) \
     { name_offset, \
@@ -203,11 +234,11 @@
   // 00000000001 1111111112222222222 3333333333444444444455555555556666666666777 777777788888888889999999999
   // 01234567890 1234567890123456789 0123456789012345678901234567890123456789012 345678901234567890123456789
     "erate_phdr\0android_dlopen_ext\0android_set_application_target_sdk_version\0android_get_application_tar"
-  // 0000000000111111
-  // 0123456789012345
-    "get_sdk_version\0"
+  // 0000000000111111 111122222222223333333333444444 4444555555555566666666667
+  // 0123456789012345 678901234567890123456789012345 6789012345678901234567890
+    "get_sdk_version\0android_init_public_namespace\0android_create_namespace\0"
 #if defined(__arm__)
-  // 216
+  // 271
     "dl_unwind_find_exidx\0"
 #endif
     ;
@@ -229,8 +260,10 @@
   ELFW(SYM_INITIALIZER)(111, &android_dlopen_ext, 1),
   ELFW(SYM_INITIALIZER)(130, &android_set_application_target_sdk_version, 1),
   ELFW(SYM_INITIALIZER)(173, &android_get_application_target_sdk_version, 1),
+  ELFW(SYM_INITIALIZER)(216, &android_init_public_namespace, 1),
+  ELFW(SYM_INITIALIZER)(246, &android_create_namespace, 1),
 #if defined(__arm__)
-  ELFW(SYM_INITIALIZER)(216, &dl_unwind_find_exidx, 1),
+  ELFW(SYM_INITIALIZER)(271, &dl_unwind_find_exidx, 1),
 #endif
 };
 
@@ -247,18 +280,20 @@
 // Note that adding any new symbols here requires stubbing them out in libdl.
 static unsigned g_libdl_buckets[1] = { 1 };
 #if defined(__arm__)
-static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0 };
+static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0 };
 #else
-static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0 };
+static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0 };
 #endif
 
 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) {
-    __libdl_info = new (__libdl_info_buf) soinfo("libdl.so", nullptr, 0, RTLD_GLOBAL);
+    __libdl_info = new (__libdl_info_buf) soinfo(&g_default_namespace, "libdl.so", nullptr, 0, RTLD_GLOBAL);
     __libdl_info->flags_ |= FLAG_LINKED;
     __libdl_info->strtab_ = ANDROID_LIBDL_STRTAB;
     __libdl_info->symtab_ = g_libdl_symtab;
diff --git a/linker/linked_list.h b/linker/linked_list.h
index eb3ecd4..88386b0 100644
--- a/linker/linked_list.h
+++ b/linker/linked_list.h
@@ -25,12 +25,49 @@
   T* element;
 };
 
+// ForwardInputIterator
+template<typename T>
+class LinkedListIterator {
+ public:
+  LinkedListIterator() : entry_(nullptr) {}
+  LinkedListIterator(const LinkedListIterator<T>& that) : entry_(that.entry_) {}
+  explicit LinkedListIterator(LinkedListEntry<T>* entry) : entry_(entry) {}
+
+  LinkedListIterator<T>& operator=(const LinkedListIterator<T>& that) {
+    entry_ = that.entry_;
+    return *this;
+  }
+
+  LinkedListIterator<T>& operator++() {
+    entry_ = entry_->next;
+    return *this;
+  }
+
+  T* operator*() {
+    return entry_->element;
+  }
+
+  bool operator==(const LinkedListIterator<T>& that) const {
+    return entry_ == that.entry_;
+  }
+
+  bool operator!=(const LinkedListIterator<T>& that) const {
+    return entry_ != that.entry_;
+  }
+
+ private:
+  LinkedListEntry<T> *entry_;
+};
+
 /*
  * Represents linked list of objects of type T
  */
 template<typename T, typename Allocator>
 class LinkedList {
  public:
+  typedef LinkedListIterator<T> iterator;
+  typedef T* value_type;
+
   LinkedList() : head_(nullptr), tail_(nullptr) {}
   ~LinkedList() {
     clear();
@@ -133,6 +170,7 @@
         }
 
         Allocator::free(e);
+
         e = next;
       } else {
         p = e;
@@ -152,6 +190,24 @@
     return nullptr;
   }
 
+  iterator begin() {
+    return iterator(head_);
+  }
+
+  iterator end() {
+    return iterator(nullptr);
+  }
+
+  iterator find(T* value) {
+    for (LinkedListEntry<T>* e = head_; e != nullptr; e = e->next) {
+      if (e->element == value) {
+        return iterator(e);
+      }
+    }
+
+    return end();
+  }
+
   size_t copy_to_array(T* array[], size_t array_length) const {
     size_t sz = 0;
     for (LinkedListEntry<T>* e = head_; sz < array_length && e != nullptr; e = e->next) {
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 9f0b559..27bcb21 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -70,11 +70,55 @@
 #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;
+  }
+
+  soinfo::soinfo_list_t& soinfo_list() { 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_;
+  soinfo::soinfo_list_t soinfo_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(android_namespace_t);
+};
+
+android_namespace_t g_default_namespace;
+
 static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf);
 
 static LinkerTypeAllocator<soinfo> g_soinfo_allocator;
 static LinkerTypeAllocator<LinkedListEntry<soinfo>> g_soinfo_links_allocator;
 
+static LinkerTypeAllocator<android_namespace_t> g_namespace_allocator;
+
 static soinfo* solist;
 static soinfo* sonext;
 static soinfo* somain; // main process, always the one after libdl_info
@@ -107,14 +151,15 @@
 
 static const ElfW(Versym) kVersymNotNeeded = 0;
 static const ElfW(Versym) kVersymGlobal = 1;
-static const char* const kZipFileSeparator = "!/";
 
 static const char* const* g_default_ld_paths;
-static std::vector<std::string> g_ld_library_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;
+
 __LIBC_HIDDEN__ int g_ld_debug_verbosity;
 
 __LIBC_HIDDEN__ abort_msg_t* g_abort_message = nullptr; // For debuggerd.
@@ -247,6 +292,26 @@
   rtld_db_dlactivity();
 }
 
+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;
+    }
+  }
+
+  return false;
+}
+
 LinkedListEntry<soinfo>* SoinfoListAllocator::alloc() {
   return g_soinfo_links_allocator.alloc();
 }
@@ -255,18 +320,22 @@
   g_soinfo_links_allocator.free(entry);
 }
 
-static soinfo* soinfo_alloc(const char* name, struct stat* file_stat,
-                            off64_t file_offset, uint32_t rtld_flags) {
+static soinfo* soinfo_alloc(android_namespace_t* ns, const char* name,
+                            struct stat* file_stat, off64_t file_offset,
+                            uint32_t rtld_flags) {
   if (strlen(name) >= PATH_MAX) {
     DL_ERR("library name \"%s\" too long", name);
     return nullptr;
   }
 
-  soinfo* si = new (g_soinfo_allocator.alloc()) soinfo(name, file_stat, file_offset, rtld_flags);
+  soinfo* si = new (g_soinfo_allocator.alloc()) soinfo(ns, name, file_stat,
+                                                       file_offset, rtld_flags);
 
   sonext->next = si;
   sonext = si;
 
+  ns->soinfo_list().push_back(si);
+
   TRACE("name %s: allocated soinfo @ %p", name, si);
   return si;
 }
@@ -307,33 +376,130 @@
     sonext = prev;
   }
 
+  // remove from the namespace
+  si->get_namespace()->soinfo_list().remove_if([&](soinfo* candidate) {
+    return si == candidate;
+  });
+
   si->~soinfo();
   g_soinfo_allocator.free(si);
 }
 
-static void parse_path(const char* path, const char* delimiters,
+// 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;
+        }
+
+        ZipArchiveHandle handle = nullptr;
+        if (OpenArchive(resolved_path, &handle) != 0) {
+          DL_WARN("Warning: unable to open zip archive: %s", resolved_path);
+          continue;
+        }
+
+        // Check if zip-file has a dir with entry_path name
+        void* cookie = nullptr;
+        std::string prefix_str = entry_path + "/";
+        ZipString prefix(prefix_str.c_str());
+
+        ZipEntry out_data;
+        ZipString out_name;
+
+        int32_t error_code;
+
+        if ((error_code = StartIteration(handle, &cookie, &prefix, nullptr)) != 0) {
+          DL_WARN("Unable to iterate over zip-archive entries \"%s\";"
+                  " error code: %d", zip_path.c_str(), error_code);
+          continue;
+        }
+
+        if (Next(cookie, &out_data, &out_name) != 0) {
+          DL_WARN("Unable to find entries starting with \"%s\" in \"%s\"",
+                  prefix_str.c_str(), zip_path.c_str());
+          continue;
+        }
+
+        auto zip_guard = make_scope_guard([&]() {
+          if (cookie != nullptr) {
+            EndIteration(cookie);
+          }
+          CloseArchive(handle);
+        });
+
+        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) {
-  paths->clear();
-  if (path != nullptr) {
+  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;
+  split_path(path, delimiters, &paths);
+  resolve_paths(paths, resolved_paths);
+}
+
 static void parse_LD_LIBRARY_PATH(const char* path) {
-  parse_path(path, ":", &g_ld_library_paths);
+  std::vector<std::string> ld_libary_paths;
+  parse_path(path, ":", &ld_libary_paths);
+  g_default_namespace.set_ld_library_paths(std::move(ld_libary_paths));
 }
 
 void soinfo::set_dt_runpath(const char* path) {
-  if (!has_min_version(2)) {
+  if (!has_min_version(3)) {
     return;
   }
 
-  parse_path(path, ":", &dt_runpath_);
+  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 : dt_runpath_) {
+  for (auto&& s : runpaths) {
     size_t pos = 0;
     while (pos < s.size()) {
       pos = s.find("$", pos);
@@ -356,11 +522,16 @@
       ++pos;
     }
   }
+
+  resolve_paths(runpaths, &dt_runpath_);
 }
 
 static void parse_LD_PRELOAD(const char* path) {
-  // We have historically supported ':' as well as ' ' in LD_PRELOAD.
-  parse_path(path, " :", &g_ld_preload_names);
+  g_ld_preload_names.clear();
+  if (path != nullptr) {
+    // We have historically supported ':' as well as ' ' in LD_PRELOAD.
+    g_ld_preload_names = android::base::Split(path, " :");
+  }
 }
 
 static bool realpath_fd(int fd, std::string* realpath) {
@@ -688,8 +859,9 @@
   return true;
 }
 
-soinfo::soinfo(const char* realpath, const struct stat* file_stat,
-               off64_t file_offset, int rtld_flags) {
+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) {
@@ -706,6 +878,7 @@
   }
 
   this->rtld_flags_ = rtld_flags;
+  this->namespace_ = ns;
 }
 
 
@@ -857,6 +1030,7 @@
   void protect_data(int protection) {
     g_soinfo_allocator.protect_all(protection);
     g_soinfo_links_allocator.protect_all(protection);
+    g_namespace_allocator.protect_all(protection);
   }
 
   static size_t ref_count_;
@@ -1096,7 +1270,7 @@
   // libraries and they are loaded in breath-first (correct) order we can just execute
   // dlsym(RTLD_DEFAULT, ...); instead of doing two stage lookup.
   if (si == somain) {
-    return dlsym_linear_lookup(name, found, nullptr, RTLD_DEFAULT);
+    return dlsym_linear_lookup(&g_default_namespace, name, found, nullptr, RTLD_DEFAULT);
   }
 
   SymbolName symbol_name(name);
@@ -1108,24 +1282,29 @@
    beginning of the global solist. Otherwise the search starts at the
    specified soinfo (for RTLD_NEXT).
  */
-const ElfW(Sym)* dlsym_linear_lookup(const char* name,
+const ElfW(Sym)* dlsym_linear_lookup(android_namespace_t* ns,
+                                     const char* name,
                                      soinfo** found,
                                      soinfo* caller,
                                      void* handle) {
   SymbolName symbol_name(name);
 
-  soinfo* start = solist;
+  soinfo::soinfo_list_t& soinfo_list = ns->soinfo_list();
+  soinfo::soinfo_list_t::iterator start = soinfo_list.begin();
 
   if (handle == RTLD_NEXT) {
     if (caller == nullptr) {
       return nullptr;
     } else {
-      start = caller->next;
+      soinfo::soinfo_list_t::iterator it = soinfo_list.find(caller);
+      CHECK (it != soinfo_list.end());
+      start = ++it;
     }
   }
 
   const ElfW(Sym)* s = nullptr;
-  for (soinfo* si = start; si != nullptr; si = si->next) {
+  for (soinfo::soinfo_list_t::iterator it = start, end = soinfo_list.end(); it != end; ++it) {
+    soinfo* si = *it;
     // Do not skip RTLD_LOCAL libraries in dlsym(RTLD_DEFAULT, ...)
     // if the library is opened by application with target api level <= 22
     // See http://b/21565766
@@ -1337,35 +1516,13 @@
   return true;
 }
 
-static int open_library_on_default_path(const char* name, off64_t* file_offset, std::string* realpath) {
-  for (size_t i = 0; g_default_ld_paths[i] != nullptr; ++i) {
-    char buf[512];
-    if (!format_path(buf, sizeof(buf), g_default_ld_paths[i], name)) {
-      continue;
-    }
-
-    int fd = TEMP_FAILURE_RETRY(open(buf, O_RDONLY | O_CLOEXEC));
-    if (fd != -1) {
-      *file_offset = 0;
-      if (!realpath_fd(fd, realpath)) {
-        PRINT("warning: unable to get realpath for the library \"%s\". Will use given path.", buf);
-        *realpath = buf;
-      }
-      return fd;
-    }
-  }
-
-  return -1;
-}
-
 static int open_library_on_paths(ZipArchiveCache* zip_archive_cache,
                                  const char* name, off64_t* file_offset,
                                  const std::vector<std::string>& paths,
                                  std::string* realpath) {
-  for (const auto& path_str : paths) {
+  for (const auto& path : paths) {
     char buf[512];
-    const char* const path = path_str.c_str();
-    if (!format_path(buf, sizeof(buf), path, name)) {
+    if (!format_path(buf, sizeof(buf), path.c_str(), name)) {
       continue;
     }
 
@@ -1393,39 +1550,49 @@
   return -1;
 }
 
-static int open_library(ZipArchiveCache* zip_archive_cache,
+static int open_library(android_namespace_t* ns,
+                        ZipArchiveCache* zip_archive_cache,
                         const char* name, soinfo *needed_by,
                         off64_t* file_offset, std::string* realpath) {
   TRACE("[ opening %s ]", name);
 
   // If the name contains a slash, we should attempt to open it directly and not search the paths.
   if (strchr(name, '/') != nullptr) {
+    int fd = -1;
+
     if (strstr(name, kZipFileSeparator) != nullptr) {
-      int fd = open_library_in_zipfile(zip_archive_cache, name, file_offset, realpath);
+      fd = open_library_in_zipfile(zip_archive_cache, name, file_offset, realpath);
+    }
+
+    if (fd == -1) {
+      fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_CLOEXEC));
       if (fd != -1) {
-        return fd;
+        *file_offset = 0;
+        if (!realpath_fd(fd, realpath)) {
+          PRINT("warning: unable to get realpath for the library \"%s\". Will use given path.", name);
+          *realpath = name;
+        }
       }
     }
 
-    int fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_CLOEXEC));
-    if (fd != -1) {
-      *file_offset = 0;
-      if (!realpath_fd(fd, realpath)) {
-        PRINT("warning: unable to get realpath for the library \"%s\". Will use given path.", name);
-        *realpath = name;
-      }
+    if (fd != -1 && !ns->is_accessible(*realpath)) {
+      fd = -1;
     }
     return fd;
   }
 
-  // Otherwise we try LD_LIBRARY_PATH first, and fall back to the built-in well known paths.
-  int fd = open_library_on_paths(zip_archive_cache, name, file_offset, g_ld_library_paths, realpath);
+  // Otherwise we try LD_LIBRARY_PATH first, and fall back to the default library path
+  int fd = open_library_on_paths(zip_archive_cache, name, file_offset, ns->get_ld_library_paths(), realpath);
   if (fd == -1 && needed_by != nullptr) {
     fd = open_library_on_paths(zip_archive_cache, name, file_offset, needed_by->get_dt_runpath(), realpath);
+    // Check if the library is accessible
+    if (fd != -1 && !ns->is_accessible(*realpath)) {
+      fd = -1;
+    }
   }
 
   if (fd == -1) {
-    fd = open_library_on_default_path(name, file_offset, realpath);
+    fd = open_library_on_paths(zip_archive_cache, name, file_offset, ns->get_default_library_paths(), realpath);
   }
 
   return fd;
@@ -1464,7 +1631,8 @@
   }
 }
 
-static bool load_library(LoadTask* task,
+static bool load_library(android_namespace_t* ns,
+                         LoadTask* task,
                          LoadTaskList* load_tasks,
                          int rtld_flags,
                          const std::string& realpath) {
@@ -1495,18 +1663,30 @@
   // Check for symlink and other situations where
   // file can have different names, unless ANDROID_DLEXT_FORCE_LOAD is set
   if (extinfo == nullptr || (extinfo->flags & ANDROID_DLEXT_FORCE_LOAD) == 0) {
-    for (soinfo* si = solist; si != nullptr; si = si->next) {
-      if (si->get_st_dev() != 0 &&
-          si->get_st_ino() != 0 &&
-          si->get_st_dev() == file_stat.st_dev &&
-          si->get_st_ino() == file_stat.st_ino &&
-          si->get_file_offset() == file_offset) {
-        TRACE("library \"%s\" is already loaded under different name/path \"%s\" - "
-            "will return existing soinfo", name, si->get_realpath());
-        task->set_soinfo(si);
-        return true;
+    auto predicate = [&](soinfo* si) {
+      return si->get_st_dev() != 0 &&
+             si->get_st_ino() != 0 &&
+             si->get_st_dev() == file_stat.st_dev &&
+             si->get_st_ino() == file_stat.st_ino &&
+             si->get_file_offset() == file_offset;
+    };
+
+    soinfo* si = ns->soinfo_list().find_if(predicate);
+
+    // check public namespace
+    if (si == nullptr) {
+      si = g_public_namespace.find_if(predicate);
+      if (si != nullptr) {
+        ns->soinfo_list().push_back(si);
       }
     }
+
+    if (si != nullptr) {
+      TRACE("library \"%s\" is already loaded under different name/path \"%s\" - "
+            "will return existing soinfo", name, si->get_realpath());
+      task->set_soinfo(si);
+      return true;
+    }
   }
 
   if ((rtld_flags & RTLD_NOLOAD) != 0) {
@@ -1514,7 +1694,7 @@
     return false;
   }
 
-  soinfo* si = soinfo_alloc(realpath.c_str(), &file_stat, file_offset, rtld_flags);
+  soinfo* si = soinfo_alloc(ns, realpath.c_str(), &file_stat, file_offset, rtld_flags);
   if (si == nullptr) {
     return false;
   }
@@ -1523,6 +1703,8 @@
 
   // Read the ELF header and some of the segments.
   if (!task->read(realpath.c_str(), file_stat.st_size)) {
+    soinfo_free(si);
+    task->set_soinfo(nullptr);
     return false;
   }
 
@@ -1548,7 +1730,8 @@
 
 }
 
-static bool load_library(LoadTask* task,
+static bool load_library(android_namespace_t* ns,
+                         LoadTask* task,
                          ZipArchiveCache* zip_archive_cache,
                          LoadTaskList* load_tasks,
                          int rtld_flags) {
@@ -1572,11 +1755,11 @@
 
     task->set_fd(extinfo->library_fd, false);
     task->set_file_offset(file_offset);
-    return load_library(task, load_tasks, rtld_flags, realpath);
+    return load_library(ns, task, load_tasks, rtld_flags, realpath);
   }
 
   // Open the file.
-  int fd = open_library(zip_archive_cache, name, needed_by, &file_offset, &realpath);
+  int fd = open_library(ns, zip_archive_cache, name, needed_by, &file_offset, &realpath);
   if (fd == -1) {
     DL_ERR("library \"%s\" not found", name);
     return false;
@@ -1585,14 +1768,15 @@
   task->set_fd(fd, true);
   task->set_file_offset(file_offset);
 
-  return load_library(task, load_tasks, rtld_flags, realpath);
+  return load_library(ns, task, load_tasks, rtld_flags, realpath);
 }
 
 // Returns true if library was found and false in 2 cases
-// 1. The library was found but loaded under different target_sdk_version
-//    (*candidate != nullptr)
+// 1. (for default namespace only) The library was found but loaded under different
+//    target_sdk_version (*candidate != nullptr)
 // 2. The library was not found by soname (*candidate is nullptr)
-static bool find_loaded_library_by_soname(const char* name, soinfo** candidate) {
+static bool find_loaded_library_by_soname(android_namespace_t* ns,
+                                          const char* name, soinfo** candidate) {
   *candidate = nullptr;
 
   // Ignore filename with path.
@@ -1602,7 +1786,7 @@
 
   uint32_t target_sdk_version = get_application_target_sdk_version();
 
-  for (soinfo* si = solist; si != nullptr; si = si->next) {
+  return !ns->soinfo_list().visit([&](soinfo* si) {
     const char* soname = si->get_soname();
     if (soname != nullptr && (strcmp(name, soname) == 0)) {
       // If the library was opened under different target sdk version
@@ -1612,36 +1796,52 @@
       // in any case.
       bool is_libdl = si == solist;
       if (is_libdl || (si->get_dt_flags_1() & DF_1_GLOBAL) != 0 ||
-          !si->is_linked() || si->get_target_sdk_version() == target_sdk_version) {
+          !si->is_linked() || si->get_target_sdk_version() == target_sdk_version ||
+          ns != &g_default_namespace) {
         *candidate = si;
-        return true;
+        return false;
       } else if (*candidate == nullptr) {
-        // for the different sdk version - remember the first library.
+        // for the different sdk version in the default namespace
+        // remember the first library.
         *candidate = si;
       }
     }
-  }
 
-  return false;
+    return true;
+  });
 }
 
-static bool find_library_internal(LoadTask* task,
+static bool find_library_internal(android_namespace_t* ns,
+                                  LoadTask* task,
                                   ZipArchiveCache* zip_archive_cache,
                                   LoadTaskList* load_tasks,
                                   int rtld_flags) {
   soinfo* candidate;
 
-  if (find_loaded_library_by_soname(task->get_name(), &candidate)) {
+  if (find_loaded_library_by_soname(ns, task->get_name(), &candidate)) {
     task->set_soinfo(candidate);
     return true;
   }
 
+  if (ns != &g_default_namespace) {
+    // check public namespace
+    candidate = g_public_namespace.find_if([&](soinfo* si) {
+      return strcmp(task->get_name(), si->get_soname()) == 0;
+    });
+
+    if (candidate != nullptr) {
+      ns->soinfo_list().push_back(candidate);
+      task->set_soinfo(candidate);
+      return true;
+    }
+  }
+
   // Library might still be loaded, the accurate detection
   // of this fact is done by load_library.
   TRACE("[ '%s' find_loaded_library_by_soname returned false (*candidate=%s@%p). Trying harder...]",
       task->get_name(), candidate == nullptr ? "n/a" : candidate->get_realpath(), candidate);
 
-  if (load_library(task, zip_archive_cache, load_tasks, rtld_flags)) {
+  if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags)) {
     return true;
   } else {
     // In case we were unable to load the library but there
@@ -1665,13 +1865,13 @@
 //
 // 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() {
+static soinfo::soinfo_list_t make_global_group(android_namespace_t* ns) {
   soinfo::soinfo_list_t global_group;
-  for (soinfo* si = somain; si != nullptr; si = si->next) {
+  ns->soinfo_list().for_each([&](soinfo* si) {
     if ((si->get_dt_flags_1() & DF_1_GLOBAL) != 0) {
       global_group.push_back(si);
     }
-  }
+  });
 
   return global_group;
 }
@@ -1688,7 +1888,8 @@
 // not their transitive dependencies) as children of the start_with library.
 // This is false when find_libraries is called for dlopen(), when newly loaded
 // libraries must form a disjoint tree.
-static bool find_libraries(soinfo* start_with,
+static bool find_libraries(android_namespace_t* ns,
+                           soinfo* start_with,
                            const char* const library_names[],
                            size_t library_names_count, soinfo* soinfos[],
                            std::vector<soinfo*>* ld_preloads,
@@ -1705,7 +1906,7 @@
   }
 
   // Construct global_group.
-  soinfo::soinfo_list_t global_group = make_global_group();
+  soinfo::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
@@ -1747,7 +1948,7 @@
     bool is_dt_needed = needed_by != nullptr && (needed_by != start_with || add_as_children);
     task->set_extinfo(is_dt_needed ? nullptr : extinfo);
 
-    if(!find_library_internal(task, &zip_archive_cache, &load_tasks, rtld_flags)) {
+    if(!find_library_internal(ns, task, &zip_archive_cache, &load_tasks, rtld_flags)) {
       return false;
     }
 
@@ -1848,14 +2049,15 @@
   return linked;
 }
 
-static soinfo* find_library(const char* name, int rtld_flags,
+static soinfo* find_library(android_namespace_t* ns,
+                            const char* name, int rtld_flags,
                             const android_dlextinfo* extinfo,
                             soinfo* needed_by) {
   soinfo* si;
 
   if (name == nullptr) {
     si = somain;
-  } else if (!find_libraries(needed_by, &name, 1, &si, nullptr, 0, rtld_flags,
+  } else if (!find_libraries(ns, needed_by, &name, 1, &si, nullptr, 0, rtld_flags,
                              extinfo, /* add_as_children */ false)) {
     return nullptr;
   }
@@ -1918,7 +2120,9 @@
           TRACE("deprecated (old format of soinfo): %s needs to unload %s",
               si->get_realpath(), library_name);
 
-          soinfo* needed = find_library(library_name, RTLD_NOLOAD, nullptr, nullptr);
+          soinfo* needed = find_library(si->get_namespace(),
+                                        library_name, RTLD_NOLOAD, nullptr, nullptr);
+
           if (needed != nullptr) {
             // Not found: for example if symlink was deleted between dlopen and dlclose
             // Since we cannot really handle errors at this point - print and continue.
@@ -1990,6 +2194,9 @@
     DL_ERR("invalid flags to dlopen: %x", flags);
     return nullptr;
   }
+
+  android_namespace_t* ns = caller->get_namespace();
+
   if (extinfo != nullptr) {
     if ((extinfo->flags & ~(ANDROID_DLEXT_VALID_FLAG_BITS)) != 0) {
       DL_ERR("invalid extended flags to android_dlopen_ext: 0x%" PRIx64, extinfo->flags);
@@ -2009,13 +2216,22 @@
              "compatible with ANDROID_DLEXT_RESERVED_ADDRESS/ANDROID_DLEXT_RESERVED_ADDRESS_HINT");
       return nullptr;
     }
+
+    if ((extinfo->flags & ANDROID_DLEXT_USE_NAMESPACE) != 0) {
+      if (extinfo->library_namespace == nullptr) {
+        DL_ERR("ANDROID_DLEXT_USE_NAMESPACE is set but extinfo->library_namespace is null");
+        return nullptr;
+      }
+      ns = extinfo->library_namespace;
+    }
   }
 
   ProtectedDataGuard guard;
-  soinfo* si = find_library(name, flags, extinfo, caller);
+  soinfo* si = find_library(ns, name, flags, extinfo, caller);
   if (si != nullptr) {
     si->call_constructors();
   }
+
   return si;
 }
 
@@ -2024,6 +2240,67 @@
   soinfo_unload(si);
 }
 
+bool init_public_namespace(const char* libs) {
+  CHECK(libs != nullptr);
+  if (g_public_namespace_initialized) {
+    DL_ERR("Public namespace has already been initialized.");
+    return false;
+  }
+
+  std::vector<std::string> sonames = android::base::Split(libs, ":");
+
+  ProtectedDataGuard guard;
+
+  auto failure_guard = make_scope_guard([&]() {
+    g_public_namespace.clear();
+  });
+
+  soinfo* candidate;
+  for (const auto& soname : sonames) {
+    if (!find_loaded_library_by_soname(&g_default_namespace, soname.c_str(), &candidate)) {
+      DL_ERR("Error initializing public namespace: \"%s\" was not found"
+             " in the default namespace", soname.c_str());
+      return false;
+    }
+
+    candidate->set_nodelete();
+    g_public_namespace.push_back(candidate);
+  }
+
+  failure_guard.disable();
+  g_public_namespace_initialized = true;
+  return true;
+}
+
+android_namespace_t* create_namespace(const char* name,
+                                      const char* ld_library_path,
+                                      const char* default_library_path,
+                                      bool is_isolated) {
+  if (!g_public_namespace_initialized) {
+    DL_ERR("Cannot create namespace: public namespace is not initialized.");
+    return nullptr;
+  }
+
+  ProtectedDataGuard guard;
+  std::vector<std::string> ld_library_paths;
+  std::vector<std::string> default_library_paths;
+
+  parse_path(ld_library_path, ":", &ld_library_paths);
+  parse_path(default_library_path, ":", &default_library_paths);
+
+  android_namespace_t* ns = new (g_namespace_allocator.alloc()) android_namespace_t();
+  ns->set_name(name);
+  ns->set_isolated(is_isolated);
+  ns->set_ld_library_paths(std::move(ld_library_paths));
+  ns->set_default_library_paths(std::move(default_library_paths));
+
+  // TODO(dimtiry): Should this be global group of caller's namespace?
+  auto global_group = make_global_group(&g_default_namespace);
+  std::copy(global_group.begin(), global_group.end(), std::back_inserter(ns->soinfo_list()));
+
+  return ns;
+}
+
 static 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);
@@ -2692,6 +2969,10 @@
   }
 }
 
+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)) {
@@ -2758,13 +3039,21 @@
 static std::vector<std::string> g_empty_runpath;
 
 const std::vector<std::string>& soinfo::get_dt_runpath() const {
-  if (has_min_version(2)) {
+  if (has_min_version(3)) {
     return dt_runpath_;
   }
 
   return g_empty_runpath;
 }
 
+android_namespace_t* soinfo::get_namespace() {
+  if (has_min_version(3)) {
+    return namespace_;
+  }
+
+  return &g_default_namespace;
+}
+
 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);
@@ -3426,7 +3715,7 @@
     return;
   }
 
-  soinfo* si = soinfo_alloc("[vdso]", nullptr, 0, 0);
+  soinfo* si = soinfo_alloc(&g_default_namespace, "[vdso]", nullptr, 0, 0);
 
   si->phdr = reinterpret_cast<ElfW(Phdr)*>(reinterpret_cast<char*>(ehdr_vdso) + ehdr_vdso->e_phoff);
   si->phnum = ehdr_vdso->e_phnum;
@@ -3462,7 +3751,8 @@
  * be on the soinfo list.
  */
 static void init_linker_info_for_gdb(ElfW(Addr) linker_base) {
-  linker_soinfo_for_gdb = new (linker_soinfo_for_gdb_buf) soinfo(LINKER_PATH, nullptr, 0, 0);
+  linker_soinfo_for_gdb = new (linker_soinfo_for_gdb_buf) soinfo(nullptr, LINKER_PATH,
+                                                                 nullptr, 0, 0);
 
   linker_soinfo_for_gdb->load_bias = linker_base;
 
@@ -3479,14 +3769,25 @@
   insert_soinfo_into_debug_map(linker_soinfo_for_gdb);
 }
 
-static void init_default_ld_library_path() {
+static void init_default_namespace() {
+  g_default_namespace.set_name("(default)");
+  g_default_namespace.set_isolated(false);
+
   const char *interp = phdr_table_get_interpreter_name(somain->phdr, somain->phnum,
                                                        somain->load_bias);
   const char* bname = basename(interp);
-  if (bname && (strcmp(bname, "linker_asan") == 0 || strcmp(bname, "linker_asan64") == 0))
+  if (bname && (strcmp(bname, "linker_asan") == 0 || strcmp(bname, "linker_asan64") == 0)) {
     g_default_ld_paths = kAsanDefaultLdPaths;
-  else
+  } else {
     g_default_ld_paths = kDefaultLdPaths;
+  }
+
+  std::vector<std::string> ld_default_paths;
+  for (size_t i = 0; g_default_ld_paths[i] != nullptr; ++i) {
+    ld_default_paths.push_back(g_default_ld_paths[i]);
+  }
+
+  g_default_namespace.set_default_library_paths(std::move(ld_default_paths));
 };
 
 extern "C" int __system_properties_init(void);
@@ -3527,7 +3828,7 @@
 
   INFO("[ android linker & debugger ]");
 
-  soinfo* si = soinfo_alloc(args.argv[0], nullptr, 0, RTLD_GLOBAL);
+  soinfo* si = soinfo_alloc(&g_default_namespace, args.argv[0], nullptr, 0, RTLD_GLOBAL);
   if (si == nullptr) {
     exit(EXIT_FAILURE);
   }
@@ -3579,11 +3880,10 @@
 
   somain = si;
 
-  init_default_ld_library_path();
+  init_default_namespace();
 
   if (!si->prelink_image()) {
-    __libc_format_fd(2, "CANNOT LINK EXECUTABLE: %s\n", linker_get_error_buffer());
-    exit(EXIT_FAILURE);
+    __libc_fatal("CANNOT LINK EXECUTABLE: %s", linker_get_error_buffer());
   }
 
   // add somain to global group
@@ -3611,15 +3911,13 @@
   needed_library_name_list.copy_to_array(needed_library_names, needed_libraries_count);
 
   if (needed_libraries_count > 0 &&
-      !find_libraries(si, needed_library_names, needed_libraries_count, nullptr,
-                      &g_ld_preloads, ld_preloads_count, RTLD_GLOBAL, nullptr,
+      !find_libraries(&g_default_namespace, si, needed_library_names, needed_libraries_count,
+                      nullptr, &g_ld_preloads, ld_preloads_count, RTLD_GLOBAL, nullptr,
                       /* add_as_children */ true)) {
-    __libc_format_fd(2, "CANNOT LINK EXECUTABLE: %s\n", linker_get_error_buffer());
-    exit(EXIT_FAILURE);
+    __libc_fatal("CANNOT LINK EXECUTABLE: %s", 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)) {
-      __libc_format_fd(2, "CANNOT LINK EXECUTABLE: %s\n", linker_get_error_buffer());
-      exit(EXIT_FAILURE);
+      __libc_fatal("CANNOT LINK EXECUTABLE: %s", linker_get_error_buffer());
     }
     si->increment_ref_count();
   }
@@ -3728,7 +4026,7 @@
   ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_addr);
   ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_addr + elf_hdr->e_phoff);
 
-  soinfo linker_so(nullptr, nullptr, 0, 0);
+  soinfo linker_so(nullptr, nullptr, nullptr, 0, 0);
 
   // If the linker is not acting as PT_INTERP entry_point is equal to
   // _start. Which means that the linker is running as an executable and
@@ -3737,7 +4035,7 @@
   // This happens when user tries to run 'adb shell /system/bin/linker'
   // see also https://code.google.com/p/android/issues/detail?id=63174
   if (reinterpret_cast<ElfW(Addr)>(&_start) == entry_point) {
-    __libc_fatal("This is %s, the helper program for shared library executables.\n", args.argv[0]);
+    __libc_fatal("This is %s, the helper program for shared library executables.", args.argv[0]);
   }
 
   linker_so.base = linker_addr;
@@ -3755,15 +4053,7 @@
   // are not yet initialized, and therefore we cannot use linked_list.push_*
   // functions at this point.
   if (!(linker_so.prelink_image() && linker_so.link_image(g_empty_list, g_empty_list, nullptr))) {
-    // It would be nice to print an error message, but if the linker
-    // can't link itself, there's no guarantee that we'll be able to
-    // call write() (because it involves a GOT reference). We may as
-    // well try though...
-    const char* msg = "CANNOT LINK EXECUTABLE: ";
-    write(2, msg, strlen(msg));
-    write(2, __linker_dl_err_buf, strlen(__linker_dl_err_buf));
-    write(2, "\n", 1);
-    _exit(EXIT_FAILURE);
+    __libc_fatal("CANNOT LINK EXECUTABLE: %s", linker_get_error_buffer());
   }
 
   __libc_init_main_thread(args);
@@ -3779,6 +4069,7 @@
   // before get_libdl_info().
   solist = get_libdl_info();
   sonext = get_libdl_info();
+  g_default_namespace.soinfo_list().push_back(get_libdl_info());
 
   // We have successfully fixed our own relocations. It's safe to run
   // the main part of the linker now.
diff --git a/linker/linker.h b/linker/linker.h
index 2c98869..c46b4e1 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -86,7 +86,7 @@
 
 #define SUPPORTED_DT_FLAGS_1 (DF_1_NOW | DF_1_GLOBAL | DF_1_NODELETE)
 
-#define SOINFO_VERSION 2
+#define SOINFO_VERSION 3
 
 #if defined(__work_around_b_24465209__)
 #define SOINFO_NAME_LEN 128
@@ -261,7 +261,8 @@
   bool has_DT_SYMBOLIC;
 
  public:
-  soinfo(const char* name, const struct stat* file_stat, off64_t file_offset, int rtld_flags);
+  soinfo(android_namespace_t* ns, const char* name, const struct stat* file_stat,
+         off64_t file_offset, int rtld_flags);
 
   void call_constructors();
   void call_destructors();
@@ -311,6 +312,7 @@
   void set_linked();
   void set_linker_flag();
   void set_main_executable();
+  void set_nodelete();
 
   void increment_ref_count();
   size_t decrement_ref_count();
@@ -332,6 +334,7 @@
 
   void set_dt_runpath(const char *);
   const std::vector<std::string>& get_dt_runpath() const;
+  android_namespace_t* get_namespace();
 
  private:
   bool elf_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
@@ -394,7 +397,9 @@
 
   uint32_t target_sdk_version_;
 
+  // version >= 3
   std::vector<std::string> dt_runpath_;
+  android_namespace_t* namespace_;
 
   friend soinfo* get_libdl_info();
 };
@@ -422,7 +427,9 @@
 
 int do_dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void* data);
 
-const ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, void* handle);
+const ElfW(Sym)* dlsym_linear_lookup(android_namespace_t* ns, const char* name, soinfo** found,
+                                     soinfo* caller, void* handle);
+
 soinfo* find_containing_library(const void* addr);
 
 const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name);
@@ -437,4 +444,8 @@
 void set_application_target_sdk_version(uint32_t target);
 uint32_t get_application_target_sdk_version();
 
+bool init_public_namespace(const char* path);
+android_namespace_t* create_namespace(const char* name, const char* ld_library_path,
+                                      const char* default_library_path, bool is_isolated);
+
 #endif
diff --git a/linker/linker_utils.cpp b/linker/linker_utils.cpp
index 5d39d83..f81b77b 100644
--- a/linker/linker_utils.cpp
+++ b/linker/linker_utils.cpp
@@ -20,7 +20,7 @@
 bool normalize_path(const char* path, std::string* normalized_path) {
   // Input should be an absolute path
   if (path[0] != '/') {
-    PRINT("canonize_path - invalid input: '%s', the input path should be absolute", path);
+    PRINT("normalize_path - invalid input: '%s', the input path should be absolute", path);
     return false;
   }
 
@@ -61,3 +61,47 @@
   return true;
 }
 
+bool file_is_in_dir(const std::string& file, const std::string& dir) {
+  const char* needle = dir.c_str();
+  const char* haystack = file.c_str();
+  size_t needle_len = strlen(needle);
+
+  return (strncmp(haystack, needle, needle_len) == 0 &&
+          haystack[needle_len] == '/' &&
+          strchr(haystack + needle_len + 1, '/') == nullptr);
+}
+
+const char* const kZipFileSeparator = "!/";
+
+bool parse_zip_path(const char* input_path, std::string* zip_path, std::string* entry_path) {
+  std::string normalized_path;
+  if (!normalize_path(input_path, &normalized_path)) {
+    return false;
+  }
+
+  const char* const path = normalized_path.c_str();
+  TRACE("Trying zip file open from path '%s' -> normalized '%s'", input_path, path);
+
+  // Treat an '!/' separator inside a path as the separator between the name
+  // of the zip file on disk and the subdirectory to search within it.
+  // For example, if path is "foo.zip!/bar/bas/x.so", then we search for
+  // "bar/bas/x.so" within "foo.zip".
+  const char* const separator = strstr(path, kZipFileSeparator);
+  if (separator == nullptr) {
+    return false;
+  }
+
+  char buf[512];
+  if (strlcpy(buf, path, sizeof(buf)) >= sizeof(buf)) {
+    PRINT("Warning: ignoring very long library path: %s", path);
+    return false;
+  }
+
+  buf[separator - path] = '\0';
+
+  *zip_path = buf;
+  *entry_path = &buf[separator - path + 2];
+
+  return true;
+}
+
diff --git a/linker/linker_utils.h b/linker/linker_utils.h
index fc79fd1..b998fb5 100644
--- a/linker/linker_utils.h
+++ b/linker/linker_utils.h
@@ -18,6 +18,10 @@
 
 #include <string>
 
+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 parse_zip_path(const char* input_path, std::string* zip_path, std::string* entry_path);
 
 #endif
diff --git a/linker/tests/linker_utils_test.cpp b/linker/tests/linker_utils_test.cpp
index 458474e..d9b290c 100644
--- a/linker/tests/linker_utils_test.cpp
+++ b/linker/tests/linker_utils_test.cpp
@@ -43,3 +43,29 @@
   ASSERT_FALSE(normalize_path("root///dir/.///dir2/somedir/../zipfile!/dir/dir9//..///afile", &output));
   ASSERT_EQ("unchanged", output);
 }
+
+TEST(linker_utils, file_is_in_dir_smoke) {
+  ASSERT_TRUE(file_is_in_dir("/foo/bar/file", "/foo/bar"));
+  ASSERT_FALSE(file_is_in_dir("/foo/bar/file", "/foo"));
+
+  ASSERT_FALSE(file_is_in_dir("/foo/bar/file", "/bar/foo"));
+
+  ASSERT_TRUE(file_is_in_dir("/file", ""));
+  ASSERT_FALSE(file_is_in_dir("/file", "/"));
+}
+
+TEST(linker_utils, parse_zip_path_smoke) {
+  std::string zip_path;
+  std::string entry_path;
+
+  ASSERT_FALSE(parse_zip_path("/not/a/zip/path/file.zip", &zip_path, &entry_path));
+  ASSERT_FALSE(parse_zip_path("/not/a/zip/path/file.zip!path/in/zip", &zip_path, &entry_path));
+  ASSERT_TRUE(parse_zip_path("/zip/path/file.zip!/path/in/zip", &zip_path, &entry_path));
+  ASSERT_EQ("/zip/path/file.zip", zip_path);
+  ASSERT_EQ("path/in/zip", entry_path);
+
+  ASSERT_TRUE(parse_zip_path("/zip/path/file2.zip!/", &zip_path, &entry_path));
+  ASSERT_EQ("/zip/path/file2.zip", zip_path);
+  ASSERT_EQ("", entry_path);
+}
+
diff --git a/tests/Android.build.mk b/tests/Android.build.mk
index 7cac349..740c2f4 100644
--- a/tests/Android.build.mk
+++ b/tests/Android.build.mk
@@ -28,9 +28,17 @@
     LOCAL_MODULE_STEM_32 := $(module)32
     LOCAL_MODULE_STEM_64 := $(module)64
 else
+
+ifneq ($($(module)_install_to_out_data_dir),)
+  $(module)_install_to_out_data := true
+endif
+
 ifeq ($($(module)_install_to_out_data),true)
-    LOCAL_MODULE_PATH_32 := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS)/$(module)
-    LOCAL_MODULE_PATH_64 := $(TARGET_OUT_DATA_NATIVE_TESTS)/$(module)
+    ifeq ($($(module)_install_to_out_data_dir),)
+      $(module)_install_to_out_data_dir := $(module)
+    endif
+    LOCAL_MODULE_PATH_32 := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS)/$($(module)_install_to_out_data_dir)
+    LOCAL_MODULE_PATH_64 := $(TARGET_OUT_DATA_NATIVE_TESTS)/$($(module)_install_to_out_data_dir)
 endif
 endif
 
diff --git a/tests/Android.mk b/tests/Android.mk
index 8de3b22..e2fae52 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -142,7 +142,9 @@
 # Clang/llvm has incompatible long double (fp128) for x86_64.
 # https://llvm.org/bugs/show_bug.cgi?id=23897
 # This affects most of math_test.cpp.
+ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),x86_64))
 libBionicStandardTests_clang_target := false
+endif
 
 module := libBionicStandardTests
 module_tag := optional
@@ -169,6 +171,7 @@
   ) \
 )
 
+fortify1-tests-gcc_clang_target := false
 module := fortify1-tests-gcc
 module_tag := optional
 build_type := target
@@ -177,6 +180,7 @@
 build_type := host
 include $(LOCAL_PATH)/Android.build.mk
 
+fortify2-tests-gcc_clang_target := false
 module := fortify2-tests-gcc
 module_tag := optional
 build_type := target
@@ -309,10 +313,6 @@
     libdl_preempt_test_1 \
     libdl_preempt_test_2
 
-# TODO: clang support for thread_local on arm is done via __aeabi_read_tp()
-# which bionic does not support. Reenable this once this question is resolved.
-bionic-unit-tests_clang_target := false
-
 bionic-unit-tests_shared_libraries_target += libdl_test_df_1_global
 
 module := bionic-unit-tests
diff --git a/tests/atexit_test.cpp b/tests/atexit_test.cpp
index e92889d..67fbfd2 100644
--- a/tests/atexit_test.cpp
+++ b/tests/atexit_test.cpp
@@ -14,7 +14,17 @@
  * limitations under the License.
  */
 
+// To work around b/25643775, we disable clang optimization so that
+//   VTT for std::__1::basic_stringstream<char, std::__1::char_traits<char>,
+//   std::__1::allocator<char> >
+// will be correctly kept for other module's references.
+#if defined(__clang__) && (defined(__arm__) || defined(__aarch64__))
+#pragma clang optimize off
+#endif
 #include <gtest/gtest.h>
+#if defined(__clang__) && (defined(__arm__) || defined(__aarch64__))
+#pragma clang optimize on
+#endif
 
 #include <dlfcn.h>
 #include <libgen.h>
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index baf7eb5..83bd5cc 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -52,14 +52,14 @@
 #define LIBSIZE 1024*1024 // how much address space to reserve for it
 
 #if defined(__LP64__)
-#define LIBPATH_PREFIX "/nativetest64/"
+#define NATIVE_TESTS_PATH "/nativetest64"
 #else
-#define LIBPATH_PREFIX "/nativetest/"
+#define NATIVE_TESTS_PATH "/nativetest"
 #endif
 
-#define LIBPATH LIBPATH_PREFIX "libdlext_test_fd/libdlext_test_fd.so"
-#define LIBZIPPATH LIBPATH_PREFIX "libdlext_test_zip/libdlext_test_zip_zipaligned.zip"
-#define LIBZIPPATH_WITH_RUNPATH LIBPATH_PREFIX "libdlext_test_runpath_zip/libdlext_test_runpath_zip_zipaligned.zip"
+#define LIBPATH NATIVE_TESTS_PATH "/libdlext_test_fd/libdlext_test_fd.so"
+#define LIBZIPPATH NATIVE_TESTS_PATH "/libdlext_test_zip/libdlext_test_zip_zipaligned.zip"
+#define LIBZIPPATH_WITH_RUNPATH NATIVE_TESTS_PATH "/libdlext_test_runpath_zip/libdlext_test_runpath_zip_zipaligned.zip"
 
 #define LIBZIP_OFFSET PAGE_SIZE
 
@@ -169,6 +169,11 @@
   ASSERT_TRUE(handle_ == nullptr);
   ASSERT_EQ("dlopen failed: \"" + lib_realpath + "\" has bad ELF magic", dlerror());
 
+  // Check if dlsym works after unsuccessful dlopen().
+  // Supply non-exiting one to make linker visit every soinfo.
+  void* sym = dlsym(RTLD_DEFAULT, "this_symbol_does_not_exist___");
+  ASSERT_TRUE(sym == nullptr);
+
   close(extinfo.library_fd);
 }
 
@@ -597,3 +602,192 @@
     ASSERT_EQ(0, WEXITSTATUS(status));
   }
 }
+
+// Testing namespaces
+static const char* g_public_lib = "libnstest_public.so";
+
+TEST(dlext, ns_smoke) {
+  static const char* root_lib = "libnstest_root.so";
+  std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib;
+
+  ASSERT_FALSE(android_init_public_namespace(path.c_str()));
+  ASSERT_STREQ("android_init_public_namespace failed: Error initializing public namespace: "
+               "\"libnstest_public.so\" was not found in the default namespace", dlerror());
+
+  const std::string lib_path = std::string(getenv("ANDROID_DATA")) + NATIVE_TESTS_PATH;
+
+  void* handle_public = dlopen((lib_path + "/public_namespace_libs/" + g_public_lib).c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle_public != nullptr) << dlerror();
+
+  ASSERT_TRUE(android_init_public_namespace(path.c_str())) << dlerror();
+
+  // Check that libraries added to public namespace are NODELETE
+  dlclose(handle_public);
+  handle_public = dlopen((lib_path + "/public_namespace_libs/" + g_public_lib).c_str(), RTLD_NOW | RTLD_NOLOAD);
+  ASSERT_TRUE(handle_public != nullptr) << dlerror();
+
+  android_namespace_t* ns1 = android_create_namespace("private", nullptr, (lib_path + "/private_namespace_libs").c_str(), false);
+  ASSERT_TRUE(ns1 != nullptr) << dlerror();
+
+  android_namespace_t* ns2 = android_create_namespace("private_isolated", nullptr, (lib_path + "/private_namespace_libs").c_str(), true);
+  ASSERT_TRUE(ns2 != nullptr) << dlerror();
+
+  // This should not have affect search path for default namespace:
+  ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr);
+  void* handle = dlopen(g_public_lib, RTLD_NOW);
+  ASSERT_TRUE(handle != nullptr) << dlerror();
+  dlclose(handle);
+
+  android_dlextinfo extinfo;
+  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+  extinfo.library_namespace = ns1;
+
+  void* handle1 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle1 != nullptr) << dlerror();
+
+  extinfo.library_namespace = ns2;
+  void* handle2 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle2 != nullptr) << dlerror();
+
+  ASSERT_TRUE(handle1 != handle2);
+
+  typedef const char* (*fn_t)();
+
+  fn_t ns_get_local_string1 = reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_local_string"));
+  ASSERT_TRUE(ns_get_local_string1 != nullptr) << dlerror();
+  fn_t ns_get_local_string2 = reinterpret_cast<fn_t>(dlsym(handle2, "ns_get_local_string"));
+  ASSERT_TRUE(ns_get_local_string2 != nullptr) << dlerror();
+
+  EXPECT_STREQ("This string is local to root library", ns_get_local_string1());
+  EXPECT_STREQ("This string is local to root library", ns_get_local_string2());
+
+  ASSERT_TRUE(ns_get_local_string1() != ns_get_local_string2());
+
+  fn_t ns_get_private_extern_string1 =
+          reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_private_extern_string"));
+  ASSERT_TRUE(ns_get_private_extern_string1 != nullptr) << dlerror();
+  fn_t ns_get_private_extern_string2 =
+          reinterpret_cast<fn_t>(dlsym(handle2, "ns_get_private_extern_string"));
+  ASSERT_TRUE(ns_get_private_extern_string2 != nullptr) << dlerror();
+
+  EXPECT_STREQ("This string is from private namespace", ns_get_private_extern_string1());
+  EXPECT_STREQ("This string is from private namespace", ns_get_private_extern_string2());
+
+  ASSERT_TRUE(ns_get_private_extern_string1() != ns_get_private_extern_string2());
+
+  fn_t ns_get_public_extern_string1 =
+          reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_public_extern_string"));
+  ASSERT_TRUE(ns_get_public_extern_string1 != nullptr) << dlerror();
+  fn_t ns_get_public_extern_string2 =
+          reinterpret_cast<fn_t>(dlsym(handle2, "ns_get_public_extern_string"));
+  ASSERT_TRUE(ns_get_public_extern_string2 != nullptr) << dlerror();
+
+  EXPECT_STREQ("This string is from public namespace", ns_get_public_extern_string1());
+  ASSERT_TRUE(ns_get_public_extern_string1() == ns_get_public_extern_string2());
+
+  // and now check that dlopen() does the right thing in terms of preserving namespace
+  fn_t ns_get_dlopened_string1 = reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_dlopened_string"));
+  ASSERT_TRUE(ns_get_dlopened_string1 != nullptr) << dlerror();
+  fn_t ns_get_dlopened_string2 = reinterpret_cast<fn_t>(dlsym(handle2, "ns_get_dlopened_string"));
+  ASSERT_TRUE(ns_get_dlopened_string2 != nullptr) << dlerror();
+
+  EXPECT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string1());
+  EXPECT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string2());
+
+  ASSERT_TRUE(ns_get_dlopened_string1() != ns_get_dlopened_string2());
+
+  dlclose(handle1);
+
+  // Check if handle2 is still alive (and well)
+  ASSERT_STREQ("This string is local to root library", ns_get_local_string2());
+  ASSERT_STREQ("This string is from private namespace", ns_get_private_extern_string2());
+  ASSERT_STREQ("This string is from public namespace", ns_get_public_extern_string2());
+  ASSERT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string2());
+
+  dlclose(handle2);
+}
+
+TEST(dlext, ns_isolated) {
+  static const char* root_lib = "libnstest_root_not_isolated.so";
+  std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib;
+
+  const std::string lib_path = std::string(getenv("ANDROID_DATA")) + NATIVE_TESTS_PATH;
+  void* handle_public = dlopen((lib_path + "/public_namespace_libs/" + g_public_lib).c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle_public != nullptr) << dlerror();
+
+  ASSERT_TRUE(android_init_public_namespace(path.c_str())) << dlerror();
+
+  android_namespace_t* ns_not_isolated = android_create_namespace("private", nullptr, (lib_path + "/private_namespace_libs").c_str(), false);
+  ASSERT_TRUE(ns_not_isolated != nullptr) << dlerror();
+
+  android_namespace_t* ns_isolated = android_create_namespace("private_isolated1", nullptr, (lib_path + "/private_namespace_libs").c_str(), true);
+  ASSERT_TRUE(ns_isolated != nullptr) << dlerror();
+
+  android_namespace_t* ns_isolated2 = android_create_namespace("private_isolated2", (lib_path + "/private_namespace_libs").c_str(), nullptr, true);
+  ASSERT_TRUE(ns_isolated2 != nullptr) << dlerror();
+
+  ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr);
+  ASSERT_STREQ("dlopen failed: library \"libnstest_root_not_isolated.so\" not found", dlerror());
+
+  std::string lib_private_external_path =
+      lib_path + "/private_namespace_libs_external/libnstest_private_external.so";
+
+  // Load lib_private_external_path to default namespace
+  // (it should remain invisible for the isolated namespaces after this)
+  void* handle = dlopen(lib_private_external_path.c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle != nullptr) << dlerror();
+
+  android_dlextinfo extinfo;
+  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+  extinfo.library_namespace = ns_not_isolated;
+
+  void* handle1 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle1 != nullptr) << dlerror();
+
+  extinfo.library_namespace = ns_isolated;
+
+  void* handle2 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle2 == nullptr);
+  ASSERT_STREQ("dlopen failed: library \"libnstest_private_external.so\" not found", dlerror());
+
+  // Check dlopen by absolute path
+  handle2 = android_dlopen_ext(lib_private_external_path.c_str(), RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle2 == nullptr);
+  ASSERT_EQ("dlopen failed: library \"" + lib_private_external_path + "\" not found", dlerror());
+
+  extinfo.library_namespace = ns_isolated2;
+
+  handle2 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle2 == nullptr);
+  ASSERT_STREQ("dlopen failed: library \"libnstest_private_external.so\" not found", dlerror());
+
+  // Check dlopen by absolute path
+  handle2 = android_dlopen_ext(lib_private_external_path.c_str(), RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle2 == nullptr);
+  ASSERT_EQ("dlopen failed: library \"" + lib_private_external_path + "\" not found", dlerror());
+
+  typedef const char* (*fn_t)();
+  fn_t ns_get_local_string = reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_local_string"));
+  ASSERT_TRUE(ns_get_local_string != nullptr) << dlerror();
+
+  ASSERT_STREQ("This string is local to root library", ns_get_local_string());
+
+  fn_t ns_get_private_extern_string =
+          reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_private_extern_string"));
+  ASSERT_TRUE(ns_get_private_extern_string != nullptr) << dlerror();
+
+  ASSERT_STREQ("This string is from private namespace", ns_get_private_extern_string());
+
+  fn_t ns_get_public_extern_string =
+          reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_public_extern_string"));
+  ASSERT_TRUE(ns_get_public_extern_string != nullptr) << dlerror();
+
+  ASSERT_STREQ("This string is from public namespace", ns_get_public_extern_string());
+
+  fn_t ns_get_dlopened_string = reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_dlopened_string"));
+  ASSERT_TRUE(ns_get_dlopened_string != nullptr) << dlerror();
+
+  ASSERT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string());
+
+  dlclose(handle1);
+}
diff --git a/tests/libgen_basename_test.cpp b/tests/libgen_basename_test.cpp
index 55939d1..d97e0da 100644
--- a/tests/libgen_basename_test.cpp
+++ b/tests/libgen_basename_test.cpp
@@ -42,6 +42,7 @@
 #include <gtest/gtest.h>
 
 static void __TestGnuBasename(const char* in, const char* expected_out, int line) {
+  errno = 0;
   const char* out = gnu_basename(in);
   ASSERT_STREQ(expected_out, out) << "(" << line << "): " << in << std::endl;
   ASSERT_EQ(0, errno) << "(" << line << "): " << in << std::endl;
diff --git a/tests/libs/Android.build.linker_namespaces.mk b/tests/libs/Android.build.linker_namespaces.mk
new file mode 100644
index 0000000..f913780
--- /dev/null
+++ b/tests/libs/Android.build.linker_namespaces.mk
@@ -0,0 +1,84 @@
+#
+# 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.
+#
+
+# -----------------------------------------------------------------------------
+# This set of libraries are used to verify linker namespaces.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# Test cases
+# 1. Check that private libraries loaded in different namespaces are
+#    different. Check that dlsym does not confuse them.
+# 2. Check that public libraries loaded in different namespaces are shared
+#    between them.
+# 3. Check that namespace sticks on dlopen
+#
+# Dependency tree (visibility)
+# libnstest_root.so (this should be local to the namespace)
+# +-> libnstest_public.so
+# +-> libnstest_private.so
+#
+# libnstest_dlopened.so (library in private namespace dlopened from libnstest_root.so)
+# -----------------------------------------------------------------------------
+libnstest_root_src_files := namespaces_root.cpp
+libnstest_root_shared_libraries := libnstest_public libnstest_private
+libnstest_root_install_to_out_data_dir := private_namespace_libs
+module := libnstest_root
+include $(LOCAL_PATH)/Android.build.target.testlib.mk
+
+libnstest_public_src_files := namespaces_public.cpp
+module := libnstest_public
+libnstest_public_install_to_out_data_dir := public_namespace_libs
+include $(LOCAL_PATH)/Android.build.target.testlib.mk
+
+libnstest_private_src_files := namespaces_private.cpp
+libnstest_private_install_to_out_data_dir := private_namespace_libs
+module := libnstest_private
+include $(LOCAL_PATH)/Android.build.target.testlib.mk
+
+libnstest_dlopened_src_files := namespaces_dlopened.cpp
+libnstest_dlopened_install_to_out_data_dir := private_namespace_libs
+module := libnstest_dlopened
+include $(LOCAL_PATH)/Android.build.target.testlib.mk
+
+# -----------------------------------------------------------------------------
+# This set of libraries is to test isolated namespaces
+#
+# Isolated namespaces do not allow loading of the library outside of
+# the search paths.
+#
+# This library cannot be loaded in isolated namespace because one of DT_NEEDED
+# libraries is outside of the search paths.
+#
+# libnstest_root_not_isolated.so (DT_RUNPATH = $ORIGIN/../private_namespace_libs_external/)
+# +-> libnstest_public.so
+# +-> libnstest_private_external.so (located in $ORIGIN/../private_namespace_libs_external/)
+#
+# Search path: $NATIVE_TESTS/private_namespace_libs/
+# -----------------------------------------------------------------------------
+libnstest_root_not_isolated_src_files := namespaces_root.cpp
+libnstest_root_not_isolated_shared_libraries := libnstest_public libnstest_private_external
+libnstest_root_not_isolated_install_to_out_data_dir := private_namespace_libs
+libnstest_root_not_isolated_ldflags := -Wl,--rpath,\$$ORIGIN/../private_namespace_libs_external \
+                                       -Wl,--enable-new-dtags
+
+module := libnstest_root_not_isolated
+include $(LOCAL_PATH)/Android.build.target.testlib.mk
+
+libnstest_private_external_src_files := namespaces_private.cpp
+libnstest_private_external_install_to_out_data_dir := private_namespace_libs_external
+module := libnstest_private_external
+include $(LOCAL_PATH)/Android.build.target.testlib.mk
diff --git a/tests/libs/Android.build.target.testlib.mk b/tests/libs/Android.build.target.testlib.mk
new file mode 100644
index 0000000..1e767c2
--- /dev/null
+++ b/tests/libs/Android.build.target.testlib.mk
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+build_target := SHARED_LIBRARY
+build_type := target
+include $(TEST_PATH)/Android.build.mk
+
diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk
index 3391d79..93d95ee 100644
--- a/tests/libs/Android.mk
+++ b/tests/libs/Android.mk
@@ -26,6 +26,7 @@
     $(LOCAL_PATH)/Android.build.dlopen_check_order_dlsym.mk \
     $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk \
     $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk \
+    $(LOCAL_PATH)/Android.build.linker_namespaces.mk \
     $(LOCAL_PATH)/Android.build.pthread_atfork.mk \
     $(LOCAL_PATH)/Android.build.testlib.mk \
     $(LOCAL_PATH)/Android.build.versioned_lib.mk \
@@ -213,6 +214,11 @@
 include $(LOCAL_PATH)/Android.build.testlib.mk
 
 # -----------------------------------------------------------------------------
+# Build test helper libraries for linker namespaces
+# -----------------------------------------------------------------------------
+include $(LOCAL_PATH)/Android.build.linker_namespaces.mk
+
+# -----------------------------------------------------------------------------
 # Build DT_RUNPATH test helper libraries
 # -----------------------------------------------------------------------------
 include $(LOCAL_PATH)/Android.build.dt_runpath.mk
diff --git a/tests/libs/namespaces_dlopened.cpp b/tests/libs/namespaces_dlopened.cpp
new file mode 100644
index 0000000..9d11689
--- /dev/null
+++ b/tests/libs/namespaces_dlopened.cpp
@@ -0,0 +1,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.
+ */
+
+const char* g_private_dlopened_string = "This string is from private namespace "
+                                        "(dlopened library)";
+
diff --git a/tests/libs/namespaces_private.cpp b/tests/libs/namespaces_private.cpp
new file mode 100644
index 0000000..07cab70
--- /dev/null
+++ b/tests/libs/namespaces_private.cpp
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+const char* g_private_extern_string = "This string is from private namespace";
+
diff --git a/tests/libs/namespaces_public.cpp b/tests/libs/namespaces_public.cpp
new file mode 100644
index 0000000..bb2a8de
--- /dev/null
+++ b/tests/libs/namespaces_public.cpp
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+const char* g_public_extern_string = "This string is from public namespace";
+
diff --git a/tests/libs/namespaces_root.cpp b/tests/libs/namespaces_root.cpp
new file mode 100644
index 0000000..0bb4611
--- /dev/null
+++ b/tests/libs/namespaces_root.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#include <dlfcn.h>
+
+static const char* g_local_string = "This string is local to root library";
+extern "C" const char* g_private_extern_string;
+extern "C" const char* g_public_extern_string;
+
+bool g_dlopened = false;
+
+extern "C" const char* ns_get_local_string() {
+  return g_local_string;
+}
+
+extern "C" const char* ns_get_private_extern_string() {
+  return g_private_extern_string;
+}
+
+extern "C" const char* ns_get_public_extern_string() {
+  return g_public_extern_string;
+}
+
+extern "C" const char* ns_get_dlopened_string() {
+  void* handle = dlopen("libnstest_dlopened.so", RTLD_NOW | RTLD_GLOBAL);
+  if (handle == nullptr) {
+    return nullptr;
+  }
+
+  const char* result = *static_cast<const char**>(dlsym(handle, "g_private_dlopened_string"));
+  if (result != nullptr) {
+    g_dlopened = true;
+  }
+
+  return result;
+}
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
old mode 100644
new mode 100755
index 88b8a5c..e62518a
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -1303,8 +1303,14 @@
 }
 
 #if defined(__BIONIC__)
+static pthread_mutex_t pthread_gettid_np_mutex = PTHREAD_MUTEX_INITIALIZER;
+
 static void* pthread_gettid_np_helper(void* arg) {
   *reinterpret_cast<pid_t*>(arg) = gettid();
+
+  // Wait for our parent to call pthread_gettid_np on us before exiting.
+  pthread_mutex_lock(&pthread_gettid_np_mutex);
+  pthread_mutex_unlock(&pthread_gettid_np_mutex);
   return NULL;
 }
 #endif
@@ -1313,12 +1319,18 @@
 #if defined(__BIONIC__)
   ASSERT_EQ(gettid(), pthread_gettid_np(pthread_self()));
 
+  // Ensure the other thread doesn't exit until after we've called
+  // pthread_gettid_np on it.
+  pthread_mutex_lock(&pthread_gettid_np_mutex);
+
   pid_t t_gettid_result;
   pthread_t t;
   pthread_create(&t, NULL, pthread_gettid_np_helper, &t_gettid_result);
 
   pid_t t_pthread_gettid_np_result = pthread_gettid_np(t);
 
+  // Release the other thread and wait for it to exit.
+  pthread_mutex_unlock(&pthread_gettid_np_mutex);
   pthread_join(t, NULL);
 
   ASSERT_EQ(t_gettid_result, t_pthread_gettid_np_result);
diff --git a/tests/sys_mman_test.cpp b/tests/sys_mman_test.cpp
index b0e40fd..dffb646 100644
--- a/tests/sys_mman_test.cpp
+++ b/tests/sys_mman_test.cpp
@@ -215,3 +215,7 @@
 
   ASSERT_EQ(0, munmap(map, pagesize));
 }
+
+TEST(sys_mman, mremap) {
+  ASSERT_EQ(MAP_FAILED, mremap(nullptr, 0, 0, 0));
+}
diff --git a/tests/thread_local_test.cpp b/tests/thread_local_test.cpp
index aeba2ba..1422ed2 100644
--- a/tests/thread_local_test.cpp
+++ b/tests/thread_local_test.cpp
@@ -18,7 +18,8 @@
 #include <stdint.h>
 #include <string.h>
 
-#ifdef __GNUC__
+#if defined(__GNUC__) && !defined(__clang__) && \
+    (defined(__arm__) || defined(__aarch64__))
 // Gcc has a bug with -O -fdata-section for the arm target: http://b/22772147.
 // Until that bug is fixed, disable optimization since
 // it is not essential for this test.
diff --git a/tests/time_test.cpp b/tests/time_test.cpp
index c685e94..a04c449 100644
--- a/tests/time_test.cpp
+++ b/tests/time_test.cpp
@@ -143,6 +143,44 @@
   EXPECT_STREQ("Sun Mar 10 00:00:00 2100", buf);
 }
 
+TEST(time, strftime_null_tm_zone) {
+  // Netflix on Nexus Player wouldn't start (http://b/25170306).
+  struct tm t;
+  memset(&t, 0, sizeof(tm));
+
+  char buf[64];
+
+  setenv("TZ", "America/Los_Angeles", 1);
+  tzset();
+
+  t.tm_isdst = 0; // "0 if Daylight Savings Time is not in effect".
+  EXPECT_EQ(5U, strftime(buf, sizeof(buf), "<%Z>", &t));
+  EXPECT_STREQ("<PST>", buf);
+
+#if defined(__BIONIC__) // glibc 2.19 only copes with tm_isdst being 0 and 1.
+  t.tm_isdst = 2; // "positive if Daylight Savings Time is in effect"
+  EXPECT_EQ(5U, strftime(buf, sizeof(buf), "<%Z>", &t));
+  EXPECT_STREQ("<PDT>", buf);
+
+  t.tm_isdst = -123; // "and negative if the information is not available".
+  EXPECT_EQ(2U, strftime(buf, sizeof(buf), "<%Z>", &t));
+  EXPECT_STREQ("<>", buf);
+#endif
+
+  setenv("TZ", "UTC", 1);
+  tzset();
+
+  t.tm_isdst = 0;
+  EXPECT_EQ(5U, strftime(buf, sizeof(buf), "<%Z>", &t));
+  EXPECT_STREQ("<UTC>", buf);
+
+#if defined(__BIONIC__) // glibc 2.19 thinks UTC DST is "UTC".
+  t.tm_isdst = 1; // UTC has no DST.
+  EXPECT_EQ(2U, strftime(buf, sizeof(buf), "<%Z>", &t));
+  EXPECT_STREQ("<>", buf);
+#endif
+}
+
 TEST(time, strptime) {
   setenv("TZ", "UTC", 1);