Merge "Be more strict about using invalid `pthread_t`s."
diff --git a/libc/Android.bp b/libc/Android.bp
index 6aebb3c..f663a97 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1665,6 +1665,8 @@
         keep_symbols: true,
     },
 
+    ldflags: ["-Wl,-z,global"],
+
     // Do not pack libc.so relocations; see http://b/20645321 for details.
     pack_relocations: false,
 
diff --git a/libc/bionic/system_properties.cpp b/libc/bionic/system_properties.cpp
index a62ce14..32d1e31 100644
--- a/libc/bionic/system_properties.cpp
+++ b/libc/bionic/system_properties.cpp
@@ -1102,7 +1102,7 @@
   return fsetxattr_failed ? -2 : 0;
 }
 
-unsigned int __system_property_area_serial() {
+uint32_t __system_property_area_serial() {
   prop_area* pa = __system_property_area__;
   if (!pa) {
     return -1;
@@ -1163,8 +1163,10 @@
 }
 
 void __system_property_read_callback(const prop_info* pi,
-                                     void (*callback)(void* cookie, const char* name,
-                                                      const char* value),
+                                     void (*callback)(void* cookie,
+                                                      const char* name,
+                                                      const char* value,
+                                                      uint32_t serial),
                                      void* cookie) {
   while (true) {
     uint32_t serial = __system_property_serial(pi);  // acquire semantics
@@ -1177,7 +1179,7 @@
     // TODO: see todo in __system_property_read function
     atomic_thread_fence(memory_order_acquire);
     if (serial == load_const_atomic(&(pi->serial), memory_order_relaxed)) {
-      callback(cookie, pi->name, value_buf);
+      callback(cookie, pi->name, value_buf, serial);
       return;
     }
   }
@@ -1329,30 +1331,34 @@
 }
 
 // Wait for non-locked serial, and retrieve it with acquire semantics.
-unsigned int __system_property_serial(const prop_info* pi) {
+uint32_t __system_property_serial(const prop_info* pi) {
   uint32_t serial = load_const_atomic(&pi->serial, memory_order_acquire);
   while (SERIAL_DIRTY(serial)) {
-    __futex_wait(const_cast<volatile void*>(reinterpret_cast<const void*>(&pi->serial)), serial,
-                 nullptr);
+    __futex_wait(const_cast<_Atomic(uint_least32_t)*>(&pi->serial), serial, nullptr);
     serial = load_const_atomic(&pi->serial, memory_order_acquire);
   }
   return serial;
 }
 
-unsigned int __system_property_wait_any(unsigned int serial) {
+uint32_t __system_property_wait_any(uint32_t old_serial) {
   prop_area* pa = __system_property_area__;
-  uint32_t my_serial;
+  if (!pa) return 0;
 
-  if (!pa) {
-    return 0;
-  }
-
+  uint32_t new_serial;
   do {
-    __futex_wait(pa->serial(), serial, nullptr);
-    my_serial = atomic_load_explicit(pa->serial(), memory_order_acquire);
-  } while (my_serial == serial);
+    __futex_wait(pa->serial(), old_serial, nullptr);
+    new_serial = atomic_load_explicit(pa->serial(), memory_order_acquire);
+  } while (new_serial == old_serial);
+  return new_serial;
+}
 
-  return my_serial;
+uint32_t __system_property_wait(const prop_info* pi, uint32_t old_serial) {
+  uint32_t new_serial;
+  do {
+    __futex_wait(const_cast<_Atomic(uint_least32_t)*>(&pi->serial), old_serial, nullptr);
+    new_serial = load_const_atomic(&pi->serial, memory_order_acquire);
+  } while (new_serial == old_serial);
+  return new_serial;
 }
 
 const prop_info* __system_property_find_nth(unsigned n) {
diff --git a/libc/include/stdio.h b/libc/include/stdio.h
index 6a76da6..f1f86a3 100644
--- a/libc/include/stdio.h
+++ b/libc/include/stdio.h
@@ -341,7 +341,7 @@
     __errorattr("size * count is too large");
 
 __BIONIC_FORTIFY_INLINE
-size_t fread(void *__restrict const __pass_object_size buf, size_t size,
+size_t fread(void *__restrict const __pass_object_size0 buf, size_t size,
              size_t count, FILE *__restrict stream) __overloadable {
     size_t bos = __bos0(buf);
 
@@ -366,8 +366,9 @@
     __errorattr("size * count is too large");
 
 __BIONIC_FORTIFY_INLINE
-size_t fwrite(const void * __restrict const __pass_object_size buf, size_t size,
-              size_t count, FILE * __restrict stream) __overloadable {
+size_t fwrite(const void * __restrict const __pass_object_size0 buf,
+              size_t size, size_t count, FILE * __restrict stream)
+        __overloadable {
     size_t bos = __bos0(buf);
 
     if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
diff --git a/libc/include/string.h b/libc/include/string.h
index 38595b2..16fc4b6 100644
--- a/libc/include/string.h
+++ b/libc/include/string.h
@@ -168,13 +168,13 @@
 // trickery...
 #if __ANDROID_API__ >= __ANDROID_API_J_MR1__
 __BIONIC_FORTIFY_INLINE
-void* memcpy(void* _Nonnull __restrict const dst __pass_object_size,
+void* memcpy(void* _Nonnull __restrict const dst __pass_object_size0,
         const void* _Nonnull __restrict src, size_t copy_amount) __overloadable {
     return __builtin___memcpy_chk(dst, src, copy_amount, __bos0(dst));
 }
 
 __BIONIC_FORTIFY_INLINE
-void* memmove(void* const _Nonnull dst __pass_object_size,
+void* memmove(void* const _Nonnull dst __pass_object_size0,
         const void* _Nonnull src, size_t len) __overloadable {
     return __builtin___memmove_chk(dst, src, len, __bos0(dst));
 }
@@ -209,7 +209,7 @@
 }
 
 __BIONIC_FORTIFY_INLINE
-void* memset(void* const _Nonnull s __pass_object_size, int c, size_t n)
+void* memset(void* const _Nonnull s __pass_object_size0, int c, size_t n)
         __overloadable {
     return __builtin___memset_chk(s, c, n, __bos0(s));
 }
@@ -343,7 +343,7 @@
 }
 
 __BIONIC_FORTIFY_INLINE
-size_t strlen(const char* const _Nonnull s __pass_object_size_n(0))
+size_t strlen(const char* const _Nonnull s __pass_object_size0)
         __overloadable {
     size_t bos = __bos0(s);
 
@@ -358,7 +358,7 @@
 
 #if  __ANDROID_API__ >= __ANDROID_API_J_MR2__
 __BIONIC_FORTIFY_INLINE
-char* strchr(const char* const _Nonnull s __pass_object_size_n(0), int c)
+char* strchr(const char* const _Nonnull s __pass_object_size0, int c)
         __overloadable {
     size_t bos = __bos0(s);
 
@@ -395,7 +395,7 @@
         __error_if_overflows_dst(memset, s, n, "size");
 
 __BIONIC_FORTIFY_INLINE
-void* memset(void* const _Nonnull s __pass_object_size, int c, size_t n,
+void* memset(void* const _Nonnull s __pass_object_size0, int c, size_t n,
              struct __bionic_zero_size_is_okay_t ok __attribute__((unused)))
         __overloadable {
     return __builtin___memset_chk(s, c, n, __bos0(s));
diff --git a/libc/include/sys/_system_properties.h b/libc/include/sys/_system_properties.h
index ffa6d2e..fa98d11 100644
--- a/libc/include/sys/_system_properties.h
+++ b/libc/include/sys/_system_properties.h
@@ -30,6 +30,7 @@
 #define _INCLUDE_SYS__SYSTEM_PROPERTIES_H
 
 #include <sys/cdefs.h>
+#include <stdint.h>
 
 #ifndef _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #error you should #include <sys/system_properties.h> instead
@@ -91,7 +92,7 @@
 **
 ** Returns the serial number on success, -1 on error.
 */
-unsigned int __system_property_area_serial();
+uint32_t __system_property_area_serial();
 
 /* Add a new system property.  Can only be done by a single
 ** process that has write access to the property area, and
@@ -118,12 +119,22 @@
 **
 ** Returns the serial number on success, -1 on error.
 */
-unsigned int __system_property_serial(const prop_info *pi);
+uint32_t __system_property_serial(const prop_info* pi);
 
-/* Wait for any system property to be updated.  Caller must pass
-** in 0 the first time, and the previous return value on each
-** successive call. */
-unsigned int __system_property_wait_any(unsigned int serial);
+/*
+ * Waits for any system property to be updated past `old_serial`.
+ * If you don't know the current global serial number, use 0.
+ * Returns the new global serial number.
+ */
+uint32_t __system_property_wait_any(uint32_t old_serial);
+
+/*
+ * Waits for the specific system property identified by `pi` to be updated past `old_serial`.
+ * If you don't know the current serial, use 0.
+ * Returns the serial number for `pi` that caused the wake.
+ */
+uint32_t __system_property_wait(const prop_info* pi, uint32_t old_serial)
+    __INTRODUCED_IN_FUTURE;
 
 /* Initialize the system properties area in read only mode.
  * Should be done by all processes that need to read system
diff --git a/libc/include/sys/cdefs.h b/libc/include/sys/cdefs.h
index 9492d92..94860d9 100644
--- a/libc/include/sys/cdefs.h
+++ b/libc/include/sys/cdefs.h
@@ -298,6 +298,7 @@
 #    define __BIONIC_FORTIFY_INLINE extern __inline__ __always_inline __attribute__((gnu_inline)) __attribute__((__artificial__))
 #  endif
 #  define __pass_object_size __pass_object_size_n(__bos_level)
+#  define __pass_object_size0 __pass_object_size_n(0)
 #endif
 
 /* Used to support clangisms with FORTIFY. This isn't in the FORTIFY section
diff --git a/libc/include/sys/socket.h b/libc/include/sys/socket.h
index 997191e..0813423 100644
--- a/libc/include/sys/socket.h
+++ b/libc/include/sys/socket.h
@@ -329,18 +329,19 @@
 
 #if defined(__BIONIC_FORTIFY)
 
+#define __recvfrom_bad_size "recvfrom called with size bigger than buffer"
 #if defined(__clang__)
 #if __ANDROID_API__ >= __ANDROID_API_N__
 __BIONIC_ERROR_FUNCTION_VISIBILITY
-ssize_t recvfrom(int fd, void* const buf __pass_object_size, size_t len,
+ssize_t recvfrom(int fd, void* const buf __pass_object_size0, size_t len,
                  int flags, struct sockaddr* src_addr, socklen_t* addr_len)
         __overloadable
         __enable_if(__bos(buf) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
                     __bos(buf) < len, "selected when the buffer is too small")
-        __errorattr("size is larger than the destination buffer");
+        __errorattr(__recvfrom_bad_size);
 
 __BIONIC_FORTIFY_INLINE
-ssize_t recvfrom(int fd, void* const buf __pass_object_size, size_t len,
+ssize_t recvfrom(int fd, void* const buf __pass_object_size0, size_t len,
                  int flags, struct sockaddr* src_addr, socklen_t* addr_len)
       __overloadable {
   size_t bos = __bos0(buf);
@@ -354,15 +355,9 @@
 }
 #endif /* __ANDROID_API__ >= __ANDROID_API_N__ */
 
-__BIONIC_FORTIFY_INLINE
-ssize_t recv(int socket, void* const buf __pass_object_size, size_t len,
-             int flags) __overloadable {
-  return recvfrom(socket, buf, len, flags, NULL, 0);
-}
-
 #else /* defined(__clang__) */
 ssize_t __recvfrom_real(int, void*, size_t, int, struct sockaddr*, socklen_t*) __RENAME(recvfrom);
-__errordecl(__recvfrom_error, "recvfrom called with size bigger than buffer");
+__errordecl(__recvfrom_error, __recvfrom_bad_size);
 
 #if __ANDROID_API__ >= __ANDROID_API_N__
 __BIONIC_FORTIFY_INLINE
@@ -385,14 +380,15 @@
   return __recvfrom_chk(fd, buf, len, bos, flags, src_addr, addr_len);
 }
 #endif /* __ANDROID_API__ >= __ANDROID_API_N__ */
+#endif /* defined(__clang__) */
+#undef __recvfrom_bad_size
 
 __BIONIC_FORTIFY_INLINE
-ssize_t recv(int socket, void* buf, size_t len, int flags) {
+ssize_t recv(int socket, void* const buf __pass_object_size0, size_t len,
+             int flags) __overloadable {
   return recvfrom(socket, buf, len, flags, NULL, 0);
 }
 
-#endif /* defined(__clang__) */
-
 #endif /* __BIONIC_FORTIFY */
 
 #undef __socketcall
diff --git a/libc/include/sys/system_properties.h b/libc/include/sys/system_properties.h
index d80b2fe..fb90251 100644
--- a/libc/include/sys/system_properties.h
+++ b/libc/include/sys/system_properties.h
@@ -31,77 +31,52 @@
 
 #include <sys/cdefs.h>
 #include <stddef.h>
+#include <stdint.h>
 
 __BEGIN_DECLS
 
 typedef struct prop_info prop_info;
 
-#define PROP_NAME_MAX   32
 #define PROP_VALUE_MAX  92
 
-/* Look up a system property by name, copying its value and a
-** \0 terminator to the provided pointer.  The total bytes
-** copied will be no greater than PROP_VALUE_MAX.  Returns
-** the string length of the value.  A property that is not
-** defined is identical to a property with a length 0 value.
-*/
-int __system_property_get(const char *name, char *value);
-
-/* Set a system property by name.
-**/
+/*
+ * Sets system property `key` to `value`, creating the system property if it doesn't already exist.
+ */
 int __system_property_set(const char* key, const char* value) __INTRODUCED_IN(12);
 
-/* Return a pointer to the system property named name, if it
-** exists, or NULL if there is no such property.  Use 
-** __system_property_read() to obtain the string value from
-** the returned prop_info pointer.
-**
-** It is safe to cache the prop_info pointer to avoid future
-** lookups.  These returned pointers will remain valid for
-** the lifetime of the system.
-*/
-const prop_info *__system_property_find(const char *name);
+/*
+ * Returns a `prop_info` corresponding system property `name`, or nullptr if it doesn't exist.
+ * Use __system_property_read_callback to query the current value.
+ *
+ * Property lookup is expensive, so it can be useful to cache the result of this function.
+ */
+const prop_info* __system_property_find(const char* name);
 
-/* Read the value of a system property.  Returns the length
-** of the value.  Copies the value and \0 terminator into
-** the provided value pointer.  Total length (including
-** terminator) will be no greater that PROP_VALUE_MAX for
-** __system_property_read.
-**
-** If name is nonzero, up to PROP_NAME_MAX bytes will be
-** copied into the provided name pointer.  The name will
-** be \0 terminated.
-*/
-int __system_property_read(const prop_info *pi, char *name, char *value);
+/*
+ * Calls `callback` with a consistent trio of name, value, and serial number for property `pi`.
+ */
 void __system_property_read_callback(const prop_info *pi,
-                                     void (*)(void* cookie, const char *name, const char *value),
-                                     void* cookie) __INTRODUCED_IN_FUTURE;
+    void (*callback)(void* cookie, const char *name, const char *value, uint32_t serial),
+    void* cookie) __INTRODUCED_IN_FUTURE;
 
-/* Return a prop_info for the nth system property, or NULL if 
-** there is no nth property.  Use __system_property_read() to
-** read the value of this property.
-**
-** Please do not call this method.  It only exists to provide
-** backwards compatibility to NDK apps.  Its implementation
-** is inefficient and order of results may change from call
-** to call.
-*/ 
-const prop_info *__system_property_find_nth(unsigned n)
-  __REMOVED_IN(26);
-
-/* Pass a prop_info for each system property to the provided
-** callback.  Use __system_property_read() to read the value
-** of this property.
-**
-** This method is for inspecting and debugging the property
-** system.  Please use __system_property_find() instead.
-**
-** Order of results may change from call to call.  This is
-** not a bug.
-*/
+/*
+ * Passes a `prop_info` for each system property to the provided
+ * callback.  Use __system_property_read_callback() to read the value.
+ *
+ * This method is for inspecting and debugging the property system, and not generally useful.
+ */
 int __system_property_foreach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie)
   __INTRODUCED_IN(19);
 
+/* Deprecated. In Android O and above, there's no limit on property name length. */
+#define PROP_NAME_MAX   32
+/* Deprecated. Use __system_property_read_callback instead. */
+int __system_property_read(const prop_info *pi, char *name, char *value);
+/* Deprecated. Use __system_property_read_callback instead. */
+int __system_property_get(const char *name, char *value);
+/* Deprecated. Use __system_property_foreach instead. Aborts in Android O and above. */
+const prop_info *__system_property_find_nth(unsigned n) __REMOVED_IN(26);
+
 __END_DECLS
 
 #endif
diff --git a/libc/include/unistd.h b/libc/include/unistd.h
index b3f8c11..7c6125a 100644
--- a/libc/include/unistd.h
+++ b/libc/include/unistd.h
@@ -322,7 +322,7 @@
         __error_if_overflows_objectsize(count, __bos0(buf));
 
 __BIONIC_FORTIFY_INLINE
-ssize_t pread(int fd, void* const __pass_object_size buf, size_t count,
+ssize_t pread(int fd, void* const __pass_object_size0 buf, size_t count,
               off_t offset) __overloadable {
     size_t bos = __bos0(buf);
 
@@ -343,7 +343,7 @@
         __error_if_overflows_objectsize(count, __bos0(buf));
 
 __BIONIC_FORTIFY_INLINE
-ssize_t pread64(int fd, void* const __pass_object_size buf, size_t count,
+ssize_t pread64(int fd, void* const __pass_object_size0 buf, size_t count,
                 off64_t offset) __overloadable {
     size_t bos = __bos0(buf);
 
@@ -368,7 +368,7 @@
         __error_if_overflows_objectsize(count, __bos0(buf));
 
 __BIONIC_FORTIFY_INLINE
-ssize_t pwrite(int fd, const void* const __pass_object_size buf, size_t count,
+ssize_t pwrite(int fd, const void* const __pass_object_size0 buf, size_t count,
                off_t offset) __overloadable {
     size_t bos = __bos0(buf);
 
@@ -391,8 +391,8 @@
         __error_if_overflows_objectsize(count, __bos0(buf));
 
 __BIONIC_FORTIFY_INLINE
-ssize_t pwrite64(int fd, const void* const __pass_object_size buf, size_t count,
-                 off64_t offset) __overloadable {
+ssize_t pwrite64(int fd, const void* const __pass_object_size0 buf,
+                 size_t count, off64_t offset) __overloadable {
     size_t bos = __bos0(buf);
 
     if (bos == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
@@ -413,7 +413,7 @@
         __error_if_overflows_objectsize(count, __bos0(buf));
 
 __BIONIC_FORTIFY_INLINE
-ssize_t read(int fd, void* const __pass_object_size buf, size_t count)
+ssize_t read(int fd, void* const __pass_object_size0 buf, size_t count)
         __overloadable {
     size_t bos = __bos0(buf);
 
@@ -434,7 +434,7 @@
         __error_if_overflows_objectsize(count, __bos0(buf));
 
 __BIONIC_FORTIFY_INLINE
-ssize_t write(int fd, const void* const __pass_object_size buf, size_t count)
+ssize_t write(int fd, const void* const __pass_object_size0 buf, size_t count)
         __overloadable {
     size_t bos = __bos0(buf);
 
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index 4319429..33aedb2 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1266,6 +1266,7 @@
 LIBC_O {
   global:
     __system_property_read_callback; # future
+    __system_property_wait; # future
     bsd_signal; # arm x86 mips versioned=26
     catclose; # future
     catgets; # future
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index f88c284..0d4fc2d 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1189,6 +1189,7 @@
 LIBC_O {
   global:
     __system_property_read_callback; # future
+    __system_property_wait; # future
     catclose; # future
     catgets; # future
     catopen; # future
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 46087e6..4d9ac57 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1291,6 +1291,7 @@
 LIBC_O {
   global:
     __system_property_read_callback; # future
+    __system_property_wait; # future
     bsd_signal; # arm x86 mips versioned=26
     catclose; # future
     catgets; # future
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index 075746c..c526226 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1250,6 +1250,7 @@
 LIBC_O {
   global:
     __system_property_read_callback; # future
+    __system_property_wait; # future
     bsd_signal; # arm x86 mips versioned=26
     catclose; # future
     catgets; # future
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index f88c284..0d4fc2d 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1189,6 +1189,7 @@
 LIBC_O {
   global:
     __system_property_read_callback; # future
+    __system_property_wait; # future
     catclose; # future
     catgets; # future
     catopen; # future
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index 75c7757..130bbed 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1248,6 +1248,7 @@
 LIBC_O {
   global:
     __system_property_read_callback; # future
+    __system_property_wait; # future
     bsd_signal; # arm x86 mips versioned=26
     catclose; # future
     catgets; # future
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index f88c284..0d4fc2d 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1189,6 +1189,7 @@
 LIBC_O {
   global:
     __system_property_read_callback; # future
+    __system_property_wait; # future
     catclose; # future
     catgets; # future
     catopen; # future
diff --git a/libdl/libdl.arm.map b/libdl/libdl.arm.map
index f452641..c0dcd5d 100644
--- a/libdl/libdl.arm.map
+++ b/libdl/libdl.arm.map
@@ -44,6 +44,7 @@
     android_set_application_target_sdk_version;
     android_get_LD_LIBRARY_PATH;
     android_update_LD_LIBRARY_PATH;
-    android_init_namespaces;
+    android_init_anonymous_namespace;
     android_create_namespace;
+    android_link_namespaces;
 } LIBC_N;
diff --git a/libdl/libdl.arm64.map b/libdl/libdl.arm64.map
index 62f5ff4..3b797f7 100644
--- a/libdl/libdl.arm64.map
+++ b/libdl/libdl.arm64.map
@@ -43,6 +43,7 @@
     android_set_application_target_sdk_version;
     android_get_LD_LIBRARY_PATH;
     android_update_LD_LIBRARY_PATH;
-    android_init_namespaces;
+    android_init_anonymous_namespace;
     android_create_namespace;
+    android_link_namespaces;
 } LIBC_N;
diff --git a/libdl/libdl.c b/libdl/libdl.c
index 3fbd7e5..6a95629 100644
--- a/libdl/libdl.c
+++ b/libdl/libdl.c
@@ -72,8 +72,8 @@
 uint32_t __loader_android_get_application_target_sdk_version();
 
 __attribute__((__weak__, visibility("default")))
-bool __loader_android_init_namespaces(const char* public_ns_sonames,
-                                      const char* anon_ns_library_path);
+bool __loader_android_init_anonymous_namespace(const char* shared_libs_sonames,
+                                               const char* library_search_path);
 
 __attribute__((__weak__, visibility("default")))
 struct android_namespace_t* __loader_android_create_namespace(
@@ -86,6 +86,12 @@
                                 const void* caller_addr);
 
 __attribute__((__weak__, visibility("default")))
+bool __loader_android_link_namespaces(
+                                struct android_namespace_t* namespace_from,
+                                struct android_namespace_t* namespace_to,
+                                const char* shared_libs_sonames);
+
+__attribute__((__weak__, visibility("default")))
 void __loader_android_dlwarning(void* obj, void (*f)(void*, const char*));
 
 // Proxy calls to bionic loader
@@ -146,9 +152,9 @@
   return __loader_android_get_application_target_sdk_version();
 }
 
-bool android_init_namespaces(const char* public_ns_sonames,
-                             const char* anon_ns_library_path) {
-  return __loader_android_init_namespaces(public_ns_sonames, anon_ns_library_path);
+bool android_init_anonymous_namespace(const char* shared_libs_sonames,
+                                      const char* library_search_path) {
+  return __loader_android_init_anonymous_namespace(shared_libs_sonames, library_search_path);
 }
 
 struct android_namespace_t* android_create_namespace(const char* name,
@@ -167,6 +173,12 @@
                                            caller_addr);
 }
 
+bool android_link_namespaces(struct android_namespace_t* namespace_from,
+                             struct android_namespace_t* namespace_to,
+                             const char* shared_libs_sonames) {
+  return __loader_android_link_namespaces(namespace_from, namespace_to, shared_libs_sonames);
+}
+
 void android_dlwarning(void* obj, void (*f)(void*, const char*)) {
   __loader_android_dlwarning(obj, f);
 }
diff --git a/libdl/libdl.map.txt b/libdl/libdl.map.txt
index cc044fe..245e016 100644
--- a/libdl/libdl.map.txt
+++ b/libdl/libdl.map.txt
@@ -43,6 +43,7 @@
     android_set_application_target_sdk_version;
     android_get_LD_LIBRARY_PATH;
     android_update_LD_LIBRARY_PATH;
-    android_init_namespaces;
+    android_init_anonymous_namespace;
     android_create_namespace;
+    android_link_namespaces;
 } LIBC_N;
diff --git a/libdl/libdl.mips.map b/libdl/libdl.mips.map
index 62f5ff4..3b797f7 100644
--- a/libdl/libdl.mips.map
+++ b/libdl/libdl.mips.map
@@ -43,6 +43,7 @@
     android_set_application_target_sdk_version;
     android_get_LD_LIBRARY_PATH;
     android_update_LD_LIBRARY_PATH;
-    android_init_namespaces;
+    android_init_anonymous_namespace;
     android_create_namespace;
+    android_link_namespaces;
 } LIBC_N;
diff --git a/libdl/libdl.mips64.map b/libdl/libdl.mips64.map
index 62f5ff4..3b797f7 100644
--- a/libdl/libdl.mips64.map
+++ b/libdl/libdl.mips64.map
@@ -43,6 +43,7 @@
     android_set_application_target_sdk_version;
     android_get_LD_LIBRARY_PATH;
     android_update_LD_LIBRARY_PATH;
-    android_init_namespaces;
+    android_init_anonymous_namespace;
     android_create_namespace;
+    android_link_namespaces;
 } LIBC_N;
diff --git a/libdl/libdl.x86.map b/libdl/libdl.x86.map
index 62f5ff4..3b797f7 100644
--- a/libdl/libdl.x86.map
+++ b/libdl/libdl.x86.map
@@ -43,6 +43,7 @@
     android_set_application_target_sdk_version;
     android_get_LD_LIBRARY_PATH;
     android_update_LD_LIBRARY_PATH;
-    android_init_namespaces;
+    android_init_anonymous_namespace;
     android_create_namespace;
+    android_link_namespaces;
 } LIBC_N;
diff --git a/libdl/libdl.x86_64.map b/libdl/libdl.x86_64.map
index 62f5ff4..3b797f7 100644
--- a/libdl/libdl.x86_64.map
+++ b/libdl/libdl.x86_64.map
@@ -43,6 +43,7 @@
     android_set_application_target_sdk_version;
     android_get_LD_LIBRARY_PATH;
     android_update_LD_LIBRARY_PATH;
-    android_init_namespaces;
+    android_init_anonymous_namespace;
     android_create_namespace;
+    android_link_namespaces;
 } LIBC_N;
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index 0092179..4e8a364 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -30,8 +30,6 @@
 #include "private/ScopedPthreadMutexLocker.h"
 #include "private/ThreadLocalBuffer.h"
 
-/* This file hijacks the symbols stubbed out in libdl.so. */
-
 static pthread_mutex_t g_dl_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
 
 static char* __bionic_set_dlerror(char* new_value) {
@@ -155,12 +153,12 @@
   get_dlwarning(obj, f);
 }
 
-bool __android_init_namespaces(const char* public_ns_sonames,
-                             const char* anon_ns_library_path) {
+bool __android_init_anonymous_namespace(const char* shared_libs_sonames,
+                                         const char* library_search_path) {
   ScopedPthreadMutexLocker locker(&g_dl_mutex);
-  bool success = init_namespaces(public_ns_sonames, anon_ns_library_path);
+  bool success = init_anonymous_namespace(shared_libs_sonames, library_search_path);
   if (!success) {
-    __bionic_format_dlerror("android_init_namespaces failed", linker_get_error_buffer());
+    __bionic_format_dlerror("android_init_anonymous_namespace failed", linker_get_error_buffer());
   }
 
   return success;
@@ -190,6 +188,20 @@
   return result;
 }
 
+bool __android_link_namespaces(android_namespace_t* namespace_from,
+                               android_namespace_t* namespace_to,
+                               const char* shared_libs_sonames) {
+  ScopedPthreadMutexLocker locker(&g_dl_mutex);
+
+  bool success = link_namespaces(namespace_from, namespace_to, shared_libs_sonames);
+
+  if (!success) {
+    __bionic_format_dlerror("android_link_namespaces failed", linker_get_error_buffer());
+  }
+
+  return success;
+}
+
 void __cfi_fail(uint64_t CallSiteTypeId, void* Ptr, void *DiagData, void *CallerPc) {
   CFIShadowWriter::CfiFail(CallSiteTypeId, Ptr, DiagData, CallerPc);
 }
@@ -226,15 +238,15 @@
   // 01234567890 1234567890123456789012345678901234567890123456789012 3456789012345678901234567890123456789
     "dlopen_ext\0__loader_android_set_application_target_sdk_version\0__loader_android_get_application_targ"
   // 3*
-  // 000000000011111 111112222222222333333333344444444 4455555555556666666666777777777788 8888888899999999 99
-  // 012345678901234 567890123456789012345678901234567 8901234567890123456789012345678901 2345678901234567 89
-    "et_sdk_version\0__loader_android_init_namespaces\0__loader_android_create_namespace\0__loader_dlvsym\0__"
+  // 000000000011111 111112222222222333333333344444444445555555 5556666666666777777777788888888889 999999999
+  // 012345678901234 567890123456789012345678901234567890123456 7890123456789012345678901234567890 123456789
+    "et_sdk_version\0__loader_android_init_anonymous_namespace\0__loader_android_create_namespace\0__loader_"
   // 4*
-  // 0000000000111111111122222 222223333333333444 4444444555555555566666666667777 77777788888888889999999999
-  // 0123456789012345678901234 567890123456789012 3456789012345678901234567890123 45678901234567890123456789
-    "loader_android_dlwarning\0__loader_cfi_fail\0"
+  // 0000000 000111111111122222222223333 333333444444444455 555555556666666666777777777788888 888889999999999
+  // 0123456 789012345678901234567890123 456789012345678901 234567890123456789012345678901234 567890123456789
+    "dlvsym\0__loader_android_dlwarning\0__loader_cfi_fail\0__loader_android_link_namespaces\0"
 #if defined(__arm__)
-  // 443
+  // 485
     "__loader_dl_unwind_find_exidx\0"
 #endif
     ;
@@ -256,13 +268,14 @@
   ELFW(SYM_INITIALIZER)(183, &__android_dlopen_ext, 1),
   ELFW(SYM_INITIALIZER)(211, &__android_set_application_target_sdk_version, 1),
   ELFW(SYM_INITIALIZER)(263, &__android_get_application_target_sdk_version, 1),
-  ELFW(SYM_INITIALIZER)(315, &__android_init_namespaces, 1),
-  ELFW(SYM_INITIALIZER)(348, &__android_create_namespace, 1),
-  ELFW(SYM_INITIALIZER)(382, &__dlvsym, 1),
-  ELFW(SYM_INITIALIZER)(398, &__android_dlwarning, 1),
-  ELFW(SYM_INITIALIZER)(425, &__cfi_fail, 1),
+  ELFW(SYM_INITIALIZER)(315, &__android_init_anonymous_namespace, 1),
+  ELFW(SYM_INITIALIZER)(357, &__android_create_namespace, 1),
+  ELFW(SYM_INITIALIZER)(391, &__dlvsym, 1),
+  ELFW(SYM_INITIALIZER)(407, &__android_dlwarning, 1),
+  ELFW(SYM_INITIALIZER)(434, &__cfi_fail, 1),
+  ELFW(SYM_INITIALIZER)(452, &__android_link_namespaces, 1),
 #if defined(__arm__)
-  ELFW(SYM_INITIALIZER)(443, &__dl_unwind_find_exidx, 1),
+  ELFW(SYM_INITIALIZER)(485, &__dl_unwind_find_exidx, 1),
 #endif
 };
 
@@ -279,9 +292,9 @@
 // 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, 13, 14, 15, 16, 17, 0 };
+static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 0 };
 #else
-static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0 };
+static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 0 };
 #endif
 
 static uint8_t __libdl_info_buf[sizeof(soinfo)] __attribute__((aligned(8)));
diff --git a/linker/linked_list.h b/linker/linked_list.h
index 092e831..b0dd4ce 100644
--- a/linker/linked_list.h
+++ b/linker/linked_list.h
@@ -136,6 +136,10 @@
     tail_ = nullptr;
   }
 
+  bool empty() {
+    return (head_ == nullptr);
+  }
+
   template<typename F>
   void for_each(F action) const {
     visit([&] (T* si) {
@@ -179,6 +183,12 @@
     }
   }
 
+  void remove(T* element) {
+    remove_if([&](T* e) {
+      return e == element;
+    });
+  }
+
   template<typename F>
   T* find_if(F predicate) const {
     for (LinkedListEntry<T>* e = head_; e != nullptr; e = e->next) {
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 522d5dc..136c2b6 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -185,10 +185,7 @@
 static const char* const* g_default_ld_paths;
 static std::vector<std::string> g_ld_preload_names;
 
-static bool g_public_namespace_initialized;
-
-// TODO (dimitry): Remove once interface between libnativeloader and the linker is updated
-static std::unordered_set<std::string> g_public_namespace_sonames;
+static bool g_anonymous_namespace_initialized;
 
 #if STATS
 struct linker_stats_t {
@@ -656,11 +653,18 @@
 typedef linked_list_t<const char> StringLinkedList;
 typedef std::vector<LoadTask*> LoadTaskList;
 
+enum walk_action_result_t : uint32_t {
+  kWalkStop = 0,
+  kWalkContinue = 1,
+  kWalkSkip = 2
+};
 
 // This function walks down the tree of soinfo dependencies
 // in breadth-first order and
 //   * calls action(soinfo* si) for each node, and
-//   * terminates walk if action returns false.
+//   * terminates walk if action returns kWalkStop
+//   * skips children of the node if action
+//     return kWalkSkip
 //
 // walk_dependencies_tree returns false if walk was terminated
 // by the action and true otherwise.
@@ -679,23 +683,30 @@
       continue;
     }
 
-    if (!action(si)) {
+    walk_action_result_t result = action(si);
+
+    if (result == kWalkStop) {
       return false;
     }
 
     visited.push_back(si);
 
-    si->get_children().for_each([&](soinfo* child) {
-      visit_list.push_back(child);
-    });
+    if (result != kWalkSkip) {
+      si->get_children().for_each([&](soinfo* child) {
+        visit_list.push_back(child);
+      });
+    }
   }
 
   return true;
 }
 
 
-static const ElfW(Sym)* dlsym_handle_lookup(soinfo* root, soinfo* skip_until,
-                                            soinfo** found, SymbolName& symbol_name,
+static const ElfW(Sym)* dlsym_handle_lookup(android_namespace_t* ns,
+                                            soinfo* root,
+                                            soinfo* skip_until,
+                                            soinfo** found,
+                                            SymbolName& symbol_name,
                                             const version_info* vi) {
   const ElfW(Sym)* result = nullptr;
   bool skip_lookup = skip_until != nullptr;
@@ -703,20 +714,24 @@
   walk_dependencies_tree(&root, 1, [&](soinfo* current_soinfo) {
     if (skip_lookup) {
       skip_lookup = current_soinfo != skip_until;
-      return true;
+      return kWalkContinue;
+    }
+
+    if (!ns->is_accessible(current_soinfo)) {
+      return kWalkSkip;
     }
 
     if (!current_soinfo->find_symbol_by_name(symbol_name, vi, &result)) {
       result = nullptr;
-      return false;
+      return kWalkStop;
     }
 
     if (result != nullptr) {
       *found = current_soinfo;
-      return false;
+      return kWalkStop;
     }
 
-    return true;
+    return kWalkContinue;
   });
 
   return result;
@@ -731,8 +746,10 @@
 
 // This is used by dlsym(3).  It performs symbol lookup only within the
 // specified soinfo object and its dependencies in breadth first order.
-static const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found,
-                                            const char* name, const version_info* vi) {
+static const ElfW(Sym)* dlsym_handle_lookup(soinfo* si,
+                                            soinfo** found,
+                                            const char* name,
+                                            const version_info* vi) {
   // According to man dlopen(3) and posix docs in the case when si is handle
   // of the main executable we need to search not only in the executable and its
   // dependencies but also in all libraries loaded with RTLD_GLOBAL.
@@ -745,7 +762,11 @@
   }
 
   SymbolName symbol_name(name);
-  return dlsym_handle_lookup(si, nullptr, found, symbol_name, vi);
+  // note that the namespace is not the namespace associated with caller_addr
+  // we use ns associated with root si intentionally here. Using caller_ns
+  // causes problems when user uses dlopen_ext to open a library in the separate
+  // namespace and then calls dlsym() on the handle.
+  return dlsym_handle_lookup(si->get_primary_namespace(), si, nullptr, found, symbol_name, vi);
 }
 
 /* This is used by dlsym(3) to performs a global symbol lookup. If the
@@ -800,8 +821,14 @@
   // case we already did it.
   if (s == nullptr && caller != nullptr &&
       (caller->get_rtld_flags() & RTLD_GLOBAL) == 0) {
-    return dlsym_handle_lookup(caller->get_local_group_root(),
-        (handle == RTLD_NEXT) ? caller : nullptr, found, symbol_name, vi);
+    soinfo* local_group_root = caller->get_local_group_root();
+
+    return dlsym_handle_lookup(local_group_root->get_primary_namespace(),
+                               local_group_root,
+                               (handle == RTLD_NEXT) ? caller : nullptr,
+                               found,
+                               symbol_name,
+                               vi);
   }
 
   if (s != nullptr) {
@@ -1542,8 +1569,12 @@
       (start_with != nullptr && add_as_children) ? &start_with : soinfos,
       (start_with != nullptr && add_as_children) ? 1 : soinfos_count,
       [&] (soinfo* si) {
-    local_group.push_back(si);
-    return true;
+    if (ns->is_accessible(si)) {
+      local_group.push_back(si);
+      return kWalkContinue;
+    } else {
+      return kWalkSkip;
+    }
   });
 
   bool linked = local_group.visit([&](soinfo* si) {
@@ -1661,12 +1692,14 @@
         TRACE("%s@%p needs to unload %s@%p", si->get_realpath(), si,
             child->get_realpath(), child);
 
+        child->get_parents().remove(si);
+
         if (local_unload_list.contains(child)) {
           continue;
         } else if (child->is_linked() && child->get_local_group_root() != root) {
           external_unload_list.push_back(child);
-        } else {
-          unload_list.push_front(child);
+        } else if (child->get_parents().empty()) {
+          unload_list.push_back(child);
         }
       }
     } else {
@@ -2001,39 +2034,42 @@
   return 0;
 }
 
-bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_path) {
-  if (g_public_namespace_initialized) {
-    DL_ERR("public namespace has already been initialized.");
+bool init_anonymous_namespace(const char* shared_lib_sonames, const char* library_search_path) {
+  if (g_anonymous_namespace_initialized) {
+    DL_ERR("anonymous namespace has already been initialized.");
     return false;
   }
 
-  if (public_ns_sonames == nullptr || public_ns_sonames[0] == '\0') {
-    DL_ERR("error initializing public namespace: the list of public libraries is empty.");
-    return false;
-  }
-
-  auto sonames = android::base::Split(public_ns_sonames, ":");
-
   ProtectedDataGuard guard;
 
-  g_public_namespace_sonames = std::unordered_set<std::string>(sonames.begin(), sonames.end());
-
-  g_public_namespace_initialized = true;
+  g_anonymous_namespace_initialized = true;
 
   // create anonymous namespace
   // When the caller is nullptr - create_namespace will take global group
   // from the anonymous namespace, which is fine because anonymous namespace
   // is still pointing to the default one.
   android_namespace_t* anon_ns =
-      create_namespace(nullptr, "(anonymous)", nullptr, anon_ns_library_path,
-                       ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, &g_default_namespace);
+      create_namespace(nullptr,
+                       "(anonymous)",
+                       nullptr,
+                       library_search_path,
+                       // TODO (dimitry): change to isolated eventually.
+                       ANDROID_NAMESPACE_TYPE_REGULAR,
+                       nullptr,
+                       &g_default_namespace);
 
   if (anon_ns == nullptr) {
-    g_public_namespace_initialized = false;
+    g_anonymous_namespace_initialized = false;
+    return false;
+  }
+
+  if (!link_namespaces(anon_ns, &g_default_namespace, shared_lib_sonames)) {
+    g_anonymous_namespace_initialized = false;
     return false;
   }
 
   g_anonymous_namespace = anon_ns;
+
   return true;
 }
 
@@ -2051,8 +2087,8 @@
                                       uint64_t type,
                                       const char* permitted_when_isolated_path,
                                       android_namespace_t* parent_namespace) {
-  if (!g_public_namespace_initialized) {
-    DL_ERR("cannot create namespace: public namespace is not initialized.");
+  if (!g_anonymous_namespace_initialized) {
+    DL_ERR("cannot create namespace: anonymous namespace is not initialized.");
     return nullptr;
   }
 
@@ -2089,13 +2125,36 @@
     add_soinfos_to_namespace(get_shared_group(parent_namespace), ns);
   }
 
-  // link it to default namespace
-  // TODO (dimitry): replace this with user-supplied link once interface is updated
-  ns->add_linked_namespace(&g_default_namespace, g_public_namespace_sonames);
-
   return ns;
 }
 
+bool link_namespaces(android_namespace_t* namespace_from,
+                     android_namespace_t* namespace_to,
+                     const char* shared_lib_sonames) {
+  if (namespace_to == nullptr) {
+    namespace_to = &g_default_namespace;
+  }
+
+  if (namespace_from == nullptr) {
+    DL_ERR("error linking namespaces: namespace_from is null.");
+    return false;
+  }
+
+  if (shared_lib_sonames == nullptr || shared_lib_sonames[0] == '\0') {
+    DL_ERR("error linking namespaces \"%s\"->\"%s\": the list of shared libraries is empty.",
+           namespace_from->get_name(), namespace_to->get_name());
+    return false;
+  }
+
+  auto sonames = android::base::Split(shared_lib_sonames, ":");
+  std::unordered_set<std::string> sonames_set(sonames.begin(), sonames.end());
+
+  ProtectedDataGuard guard;
+  namespace_from->add_linked_namespace(namespace_to, sonames_set);
+
+  return true;
+}
+
 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);
diff --git a/linker/linker.h b/linker/linker.h
index 7746982..d5d4980 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -160,7 +160,7 @@
                                            ANDROID_NAMESPACE_TYPE_ISOLATED,
 };
 
-bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_path);
+bool init_anonymous_namespace(const char* shared_lib_sonames, const char* library_search_path);
 android_namespace_t* create_namespace(const void* caller_addr,
                                       const char* name,
                                       const char* ld_library_path,
@@ -169,4 +169,8 @@
                                       const char* permitted_when_isolated_path,
                                       android_namespace_t* parent_namespace);
 
+bool link_namespaces(android_namespace_t* namespace_from,
+                     android_namespace_t* namespace_to,
+                     const char* shared_lib_sonames);
+
 #endif
diff --git a/linker/linker_namespaces.cpp b/linker/linker_namespaces.cpp
index 675f324..273ff9b 100644
--- a/linker/linker_namespaces.cpp
+++ b/linker/linker_namespaces.cpp
@@ -27,10 +27,9 @@
  */
 
 #include "linker_namespaces.h"
+#include "linker_soinfo.h"
 #include "linker_utils.h"
 
-#include <vector>
-
 bool android_namespace_t::is_accessible(const std::string& file) {
   if (!is_isolated_) {
     return true;
@@ -57,3 +56,25 @@
   return false;
 }
 
+bool android_namespace_t::is_accessible(soinfo* s) {
+  auto is_accessible_ftor = [this] (soinfo* si) {
+    if (si->get_primary_namespace() == this) {
+      return true;
+    }
+
+    const android_namespace_list_t& secondary_namespaces = si->get_secondary_namespaces();
+    if (secondary_namespaces.find(this) != secondary_namespaces.end()) {
+      return true;
+    }
+
+    return false;
+  };
+
+  if (is_accessible_ftor(s)) {
+    return true;
+  }
+
+  return !s->get_parents().visit([&](soinfo* si) {
+    return !is_accessible_ftor(si);
+  });
+}
diff --git a/linker/linker_namespaces.h b/linker/linker_namespaces.h
index 17800c6..868b4a6 100644
--- a/linker/linker_namespaces.h
+++ b/linker/linker_namespaces.h
@@ -118,6 +118,11 @@
   // always returns true for not isolated namespace.
   bool is_accessible(const std::string& path);
 
+  // Returns true if si is accessible from this namespace. A soinfo
+  // is considered accessible when it belongs to this namespace
+  // or one of it's parent soinfos belongs to this namespace.
+  bool is_accessible(soinfo* si);
+
  private:
   const char* name_;
   bool is_isolated_;
diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp
index 16e42c2..6601dc1 100644
--- a/linker/linker_soinfo.cpp
+++ b/linker/linker_soinfo.cpp
@@ -634,6 +634,11 @@
   secondary_namespaces_.push_back(secondary_ns);
 }
 
+android_namespace_list_t& soinfo::get_secondary_namespaces() {
+  CHECK(has_min_version(3));
+  return secondary_namespaces_;
+}
+
 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);
@@ -695,7 +700,6 @@
   return local_group_root_;
 }
 
-
 void soinfo::set_mapped_by_caller(bool mapped_by_caller) {
   if (mapped_by_caller) {
     flags_ |= FLAG_MAPPED_BY_CALLER;
diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h
index 7ef5da2..71eb543 100644
--- a/linker/linker_soinfo.h
+++ b/linker/linker_soinfo.h
@@ -263,6 +263,7 @@
   const std::vector<std::string>& get_dt_runpath() const;
   android_namespace_t* get_primary_namespace();
   void add_secondary_namespace(android_namespace_t* secondary_ns);
+  android_namespace_list_t& get_secondary_namespaces();
 
   void set_mapped_by_caller(bool reserved_map);
   bool is_mapped_by_caller() const;
diff --git a/tests/dlext_private.h b/tests/dlext_private.h
index 049db91..6b943ce 100644
--- a/tests/dlext_private.h
+++ b/tests/dlext_private.h
@@ -22,16 +22,15 @@
 __BEGIN_DECLS
 
 /*
- * Initializes public and anonymous namespaces. The public_ns_sonames is the list of sonames
- * to be included into public namespace separated by colon. Example: "libc.so:libm.so:libdl.so".
- * The libraries in this list should be loaded prior to this call.
+ * Initializes anonymous namespaces. The shared_libs_sonames is the list of sonames
+ * to be shared by default namespace separated by colon. Example: "libc.so:libm.so:libdl.so".
  *
- * The anon_ns_library_path is the search path for anonymous namespace. The anonymous namespace
+ * The library_search_path is the search path for anonymous namespace. The anonymous namespace
  * is used in the case when linker cannot identify the caller of dlopen/dlsym. This happens
  * for the code not loaded by dynamic linker; for example calls from the mono-compiled code.
  */
-extern bool android_init_namespaces(const char* public_ns_sonames,
-                                    const char* anon_ns_library_path);
+extern bool android_init_anonymous_namespace(const char* shared_libs_sonames,
+                                             const char* library_search_path);
 
 
 enum {
@@ -86,6 +85,10 @@
                                                             const char* permitted_when_isolated_path,
                                                             android_namespace_t* parent);
 
+extern bool android_link_namespaces(android_namespace_t* from,
+                                    android_namespace_t* to,
+                                    const char* shared_libs_sonames);
+
 extern void android_set_application_target_sdk_version(uint32_t target);
 
 __END_DECLS
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index fdb7365..dcabae8 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -617,41 +617,54 @@
 // Testing namespaces
 static const char* g_public_lib = "libnstest_public.so";
 
+// These are libs shared with default namespace
+static const std::string g_core_shared_libs = "libc.so:libc++.so:libdl.so:libm.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;
+  std::string shared_libs = g_core_shared_libs + ":" + g_public_lib;
 
-  ASSERT_FALSE(android_init_namespaces("", nullptr));
-  ASSERT_STREQ("android_init_namespaces failed: error initializing public namespace: "
-               "the list of public libraries is empty.", dlerror());
+  ASSERT_FALSE(android_init_anonymous_namespace("", nullptr));
+  ASSERT_STREQ("android_init_anonymous_namespace failed: error linking namespaces"
+               " \"(anonymous)\"->\"(default)\": the list of shared libraries is empty.",
+               dlerror());
 
   const std::string lib_public_path = get_testlib_root() + "/public_namespace_libs/" + g_public_lib;
   void* handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
   ASSERT_TRUE(handle_public != nullptr) << dlerror();
 
-  ASSERT_TRUE(android_init_namespaces(path.c_str(), nullptr)) << dlerror();
+  ASSERT_TRUE(android_init_anonymous_namespace(shared_libs.c_str(), nullptr)) << dlerror();
 
   // Check that libraries added to public namespace are not NODELETE
   dlclose(handle_public);
   handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW | RTLD_NOLOAD);
-
   ASSERT_TRUE(handle_public == nullptr);
   ASSERT_EQ(std::string("dlopen failed: library \"") + lib_public_path +
                "\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror());
 
   handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
 
+  // create "public namespace", share limited set of public libraries with
+
   android_namespace_t* ns1 =
-          android_create_namespace("private", nullptr,
+          android_create_namespace("private",
+                                   nullptr,
                                    (get_testlib_root() + "/private_namespace_libs").c_str(),
-                                   ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr);
+                                   ANDROID_NAMESPACE_TYPE_REGULAR,
+                                   nullptr,
+                                   nullptr);
   ASSERT_TRUE(ns1 != nullptr) << dlerror();
+  ASSERT_TRUE(android_link_namespaces(ns1, nullptr, shared_libs.c_str())) << dlerror();
 
   android_namespace_t* ns2 =
-          android_create_namespace("private_isolated", nullptr,
+          android_create_namespace("private_isolated",
+                                   nullptr,
                                    (get_testlib_root() + "/private_namespace_libs").c_str(),
-                                   ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr, nullptr);
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                   nullptr,
+                                   nullptr);
   ASSERT_TRUE(ns2 != nullptr) << dlerror();
+  ASSERT_TRUE(android_link_namespaces(ns2, nullptr, shared_libs.c_str())) << dlerror();
 
   // This should not have affect search path for default namespace:
   ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr);
@@ -725,6 +738,16 @@
 
   ASSERT_TRUE(ns_get_dlopened_string1() != ns_get_dlopened_string2());
 
+  // Check that symbols from non-shared libraries a shared library depends on are not visible
+  // from original namespace.
+
+  fn_t ns_get_internal_extern_string =
+          reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_internal_extern_string"));
+  ASSERT_TRUE(ns_get_internal_extern_string != nullptr) << dlerror();
+  ASSERT_TRUE(ns_get_internal_extern_string() == nullptr) <<
+      "ns_get_internal_extern_string() expected to return null but returns \"" <<
+      ns_get_internal_extern_string() << "\"";
+
   dlclose(handle1);
 
   // Check if handle2 is still alive (and well)
@@ -736,9 +759,187 @@
   dlclose(handle2);
 }
 
+TEST(dlext, ns_symbol_visibilty_one_namespace) {
+  static const char* root_lib = "libnstest_root.so";
+  ASSERT_TRUE(android_init_anonymous_namespace(g_core_shared_libs.c_str(), nullptr));
+
+  const std::string ns_search_path = get_testlib_root() + "/public_namespace_libs:" +
+                                     get_testlib_root() + "/private_namespace_libs";
+
+  android_namespace_t* ns =
+          android_create_namespace("one",
+                                   nullptr,
+                                   ns_search_path.c_str(),
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                   nullptr,
+                                   nullptr);
+
+  ASSERT_TRUE(android_link_namespaces(ns, nullptr, g_core_shared_libs.c_str())) << dlerror();
+
+  android_dlextinfo extinfo;
+  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+  extinfo.library_namespace = ns;
+
+  void* handle = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle != nullptr) << dlerror();
+
+  typedef const char* (*fn_t)();
+
+  // Check that relocation worked correctly
+  fn_t ns_get_internal_extern_string =
+          reinterpret_cast<fn_t>(dlsym(handle, "ns_get_internal_extern_string"));
+  ASSERT_TRUE(ns_get_internal_extern_string != nullptr) << dlerror();
+  ASSERT_STREQ("This string is from a library a shared library depends on", ns_get_internal_extern_string());
+
+  fn_t internal_extern_string_fn =
+          reinterpret_cast<fn_t>(dlsym(handle, "internal_extern_string"));
+  ASSERT_TRUE(internal_extern_string_fn != nullptr) << dlerror();
+  ASSERT_STREQ("This string is from a library a shared library depends on", internal_extern_string_fn());
+}
+
+TEST(dlext, ns_symbol_visibilty_between_namespaces) {
+  static const char* root_lib = "libnstest_root.so";
+  ASSERT_TRUE(android_init_anonymous_namespace(g_core_shared_libs.c_str(), nullptr));
+
+  const std::string public_ns_search_path =  get_testlib_root() + "/public_namespace_libs";
+  const std::string private_ns_search_path = get_testlib_root() + "/private_namespace_libs";
+
+  android_namespace_t* ns_public =
+          android_create_namespace("public",
+                                   nullptr,
+                                   public_ns_search_path.c_str(),
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                   nullptr,
+                                   nullptr);
+
+  ASSERT_TRUE(android_link_namespaces(ns_public, nullptr, g_core_shared_libs.c_str())) << dlerror();
+
+  android_namespace_t* ns_private =
+          android_create_namespace("private",
+                                   nullptr,
+                                   private_ns_search_path.c_str(),
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                   nullptr,
+                                   nullptr);
+
+  ASSERT_TRUE(android_link_namespaces(ns_private, ns_public, g_public_lib)) << dlerror();
+  ASSERT_TRUE(android_link_namespaces(ns_private, nullptr, g_core_shared_libs.c_str())) << dlerror();
+
+  android_dlextinfo extinfo;
+  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+  extinfo.library_namespace = ns_private;
+
+  void* handle = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle != nullptr) << dlerror();
+
+  typedef const char* (*fn_t)();
+
+  // Check that relocation worked correctly
+  fn_t ns_get_internal_extern_string =
+          reinterpret_cast<fn_t>(dlsym(handle, "ns_get_internal_extern_string"));
+  ASSERT_TRUE(ns_get_internal_extern_string != nullptr) << dlerror();
+  ASSERT_TRUE(ns_get_internal_extern_string() == nullptr) <<
+      "ns_get_internal_extern_string() expected to return null but returns \"" <<
+      ns_get_internal_extern_string() << "\"";
+
+  fn_t internal_extern_string_fn =
+          reinterpret_cast<fn_t>(dlsym(handle, "internal_extern_string"));
+  ASSERT_TRUE(internal_extern_string_fn == nullptr);
+  ASSERT_STREQ("undefined symbol: internal_extern_string", dlerror());
+}
+
+TEST(dlext, ns_unload_between_namespaces) {
+  static const char* root_lib = "libnstest_root.so";
+  ASSERT_TRUE(android_init_anonymous_namespace(g_core_shared_libs.c_str(), nullptr));
+
+  const std::string public_ns_search_path =  get_testlib_root() + "/public_namespace_libs";
+  const std::string private_ns_search_path = get_testlib_root() + "/private_namespace_libs";
+
+  android_namespace_t* ns_public =
+          android_create_namespace("public",
+                                   nullptr,
+                                   public_ns_search_path.c_str(),
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                   nullptr,
+                                   nullptr);
+
+  ASSERT_TRUE(android_link_namespaces(ns_public, nullptr, g_core_shared_libs.c_str())) << dlerror();
+
+  android_namespace_t* ns_private =
+          android_create_namespace("private",
+                                   nullptr,
+                                   private_ns_search_path.c_str(),
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                   nullptr,
+                                   nullptr);
+
+  ASSERT_TRUE(android_link_namespaces(ns_private, ns_public, g_public_lib)) << dlerror();
+  ASSERT_TRUE(android_link_namespaces(ns_private, nullptr, g_core_shared_libs.c_str())) << dlerror();
+
+  android_dlextinfo extinfo;
+  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+  extinfo.library_namespace = ns_private;
+
+  void* handle = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle != nullptr) << dlerror();
+
+  dlclose(handle);
+  // Check that root_lib was unloaded
+  handle = android_dlopen_ext(root_lib, RTLD_NOW | RTLD_NOLOAD, &extinfo);
+  ASSERT_TRUE(handle == nullptr);
+  ASSERT_EQ(std::string("dlopen failed: library \"") + root_lib +
+            "\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror());
+
+  // Check that shared library was unloaded in public ns
+  extinfo.library_namespace = ns_public;
+  handle = android_dlopen_ext(g_public_lib, RTLD_NOW | RTLD_NOLOAD, &extinfo);
+  ASSERT_TRUE(handle == nullptr);
+  ASSERT_EQ(std::string("dlopen failed: library \"") + g_public_lib +
+            "\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror());
+}
+
+TEST(dlext, ns_cyclic_namespaces) {
+  // Test that ns1->ns2->ns1 link does not break the loader
+  ASSERT_TRUE(android_init_anonymous_namespace(g_core_shared_libs.c_str(), nullptr));
+  std::string shared_libs = g_core_shared_libs + ":libthatdoesnotexist.so";
+
+  const std::string ns_search_path =  get_testlib_root() + "/public_namespace_libs";
+
+  android_namespace_t* ns1 =
+          android_create_namespace("ns1",
+                                   nullptr,
+                                   ns_search_path.c_str(),
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                   nullptr,
+                                   nullptr);
+
+  ASSERT_TRUE(android_link_namespaces(ns1, nullptr, g_core_shared_libs.c_str())) << dlerror();
+
+  android_namespace_t* ns2 =
+          android_create_namespace("ns1",
+                                   nullptr,
+                                   ns_search_path.c_str(),
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                   nullptr,
+                                   nullptr);
+
+  ASSERT_TRUE(android_link_namespaces(ns2, nullptr, g_core_shared_libs.c_str())) << dlerror();
+
+  ASSERT_TRUE(android_link_namespaces(ns2, ns1, shared_libs.c_str())) << dlerror();
+  ASSERT_TRUE(android_link_namespaces(ns1, ns2, shared_libs.c_str())) << dlerror();
+
+  android_dlextinfo extinfo;
+  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+  extinfo.library_namespace = ns1;
+
+  void* handle = android_dlopen_ext("libthatdoesnotexist.so", RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle == nullptr);
+  ASSERT_STREQ("dlopen failed: library \"libthatdoesnotexist.so\" not found", dlerror());
+}
+
 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;
+  std::string shared_libs = g_core_shared_libs + ":" + g_public_lib;
 
   const std::string lib_public_path = get_testlib_root() + "/public_namespace_libs/" + g_public_lib;
   void* handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
@@ -746,13 +947,17 @@
 
   android_set_application_target_sdk_version(42U); // something > 23
 
-  ASSERT_TRUE(android_init_namespaces(path.c_str(), nullptr)) << dlerror();
+  ASSERT_TRUE(android_init_anonymous_namespace(shared_libs.c_str(), nullptr)) << dlerror();
 
   android_namespace_t* ns_not_isolated =
-          android_create_namespace("private", nullptr,
+          android_create_namespace("private",
+                                   nullptr,
                                    (get_testlib_root() + "/private_namespace_libs").c_str(),
-                                   ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr);
+                                   ANDROID_NAMESPACE_TYPE_REGULAR,
+                                   nullptr,
+                                   nullptr);
   ASSERT_TRUE(ns_not_isolated != nullptr) << dlerror();
+  ASSERT_TRUE(android_link_namespaces(ns_not_isolated, nullptr, shared_libs.c_str())) << dlerror();
 
   android_namespace_t* ns_isolated =
           android_create_namespace("private_isolated1",
@@ -762,6 +967,7 @@
                                    nullptr,
                                    nullptr);
   ASSERT_TRUE(ns_isolated != nullptr) << dlerror();
+  ASSERT_TRUE(android_link_namespaces(ns_isolated, nullptr, shared_libs.c_str())) << dlerror();
 
   android_namespace_t* ns_isolated2 =
           android_create_namespace("private_isolated2",
@@ -771,6 +977,7 @@
                                    get_testlib_root().c_str(),
                                    nullptr);
   ASSERT_TRUE(ns_isolated2 != nullptr) << dlerror();
+  ASSERT_TRUE(android_link_namespaces(ns_isolated2, nullptr, shared_libs.c_str())) << dlerror();
 
   ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr);
   ASSERT_STREQ("dlopen failed: library \"libnstest_root_not_isolated.so\" not found", dlerror());
@@ -844,7 +1051,8 @@
 TEST(dlext, ns_shared) {
   static const char* root_lib = "libnstest_root_not_isolated.so";
   static const char* root_lib_isolated = "libnstest_root.so";
-  std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib;
+
+  std::string shared_libs = g_core_shared_libs + ":" + g_public_lib;
 
   const std::string lib_public_path = get_testlib_root() + "/public_namespace_libs/" + g_public_lib;
   void* handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
@@ -852,7 +1060,7 @@
 
   android_set_application_target_sdk_version(42U); // something > 23
 
-  ASSERT_TRUE(android_init_namespaces(path.c_str(), nullptr)) << dlerror();
+  ASSERT_TRUE(android_init_anonymous_namespace(shared_libs.c_str(), nullptr)) << dlerror();
 
   // preload this library to the default namespace to check if it
   // is shared later on.
@@ -861,17 +1069,24 @@
   ASSERT_TRUE(handle_dlopened != nullptr) << dlerror();
 
   android_namespace_t* ns_not_isolated =
-          android_create_namespace("private", nullptr,
+          android_create_namespace("private",
+                                   nullptr,
                                    (get_testlib_root() + "/private_namespace_libs").c_str(),
-                                   ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr);
+                                   ANDROID_NAMESPACE_TYPE_REGULAR,
+                                   nullptr,
+                                   nullptr);
   ASSERT_TRUE(ns_not_isolated != nullptr) << dlerror();
+  ASSERT_TRUE(android_link_namespaces(ns_not_isolated, nullptr, shared_libs.c_str())) << dlerror();
 
   android_namespace_t* ns_isolated_shared =
-          android_create_namespace("private_isolated_shared", nullptr,
+          android_create_namespace("private_isolated_shared",
+                                   nullptr,
                                    (get_testlib_root() + "/private_namespace_libs").c_str(),
                                    ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED,
-                                   nullptr, nullptr);
+                                   nullptr,
+                                   nullptr);
   ASSERT_TRUE(ns_isolated_shared != nullptr) << dlerror();
+  ASSERT_TRUE(android_link_namespaces(ns_isolated_shared, nullptr, shared_libs.c_str())) << dlerror();
 
   ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr);
   ASSERT_STREQ("dlopen failed: library \"libnstest_root_not_isolated.so\" not found", dlerror());
@@ -959,11 +1174,9 @@
 }
 
 TEST(dlext, ns_shared_dlclose) {
-  std::string path = "libc.so:libc++.so:libdl.so:libm.so";
-
   android_set_application_target_sdk_version(42U); // something > 23
 
-  ASSERT_TRUE(android_init_namespaces(path.c_str(), nullptr)) << dlerror();
+  ASSERT_TRUE(android_init_anonymous_namespace(g_core_shared_libs.c_str(), nullptr)) << dlerror();
 
   // preload this library to the default namespace to check if it
   // is shared later on.
@@ -972,11 +1185,14 @@
   ASSERT_TRUE(handle_dlopened != nullptr) << dlerror();
 
   android_namespace_t* ns_isolated_shared =
-          android_create_namespace("private_isolated_shared", nullptr,
+          android_create_namespace("private_isolated_shared",
+                                   nullptr,
                                    (get_testlib_root() + "/private_namespace_libs").c_str(),
                                    ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED,
-                                   nullptr, nullptr);
+                                   nullptr,
+                                   nullptr);
   ASSERT_TRUE(ns_isolated_shared != nullptr) << dlerror();
+  ASSERT_TRUE(android_link_namespaces(ns_isolated_shared, nullptr, g_core_shared_libs.c_str())) << dlerror();
 
   // Check if "libnstest_dlopened.so" is loaded (and the same)
   android_dlextinfo extinfo;
@@ -1022,9 +1238,7 @@
 
 TEST(dlext, ns_isolated_rtld_global) {
   static const char* root_lib = "libnstest_root.so";
-  std::string path = "libc.so:libc++.so:libdl.so:libm.so";
-
-  ASSERT_TRUE(android_init_namespaces(path.c_str(), nullptr));
+  ASSERT_TRUE(android_init_anonymous_namespace(g_core_shared_libs.c_str(), nullptr));
 
   const std::string lib_public_path = get_testlib_root() + "/public_namespace_libs";
 
@@ -1036,6 +1250,7 @@
                                    lib_public_path.c_str(),
                                    nullptr);
   ASSERT_TRUE(ns1 != nullptr) << dlerror();
+  ASSERT_TRUE(android_link_namespaces(ns1, nullptr, g_core_shared_libs.c_str())) << dlerror();
 
   android_namespace_t* ns2 =
           android_create_namespace("isolated2",
@@ -1045,6 +1260,7 @@
                                    lib_public_path.c_str(),
                                    nullptr);
   ASSERT_TRUE(ns2 != nullptr) << dlerror();
+  ASSERT_TRUE(android_link_namespaces(ns2, nullptr, g_core_shared_libs.c_str())) << dlerror();
 
   android_dlextinfo extinfo;
   extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
@@ -1057,12 +1273,15 @@
   ASSERT_TRUE(handle_global != nullptr) << dlerror();
 
   android_namespace_t* ns1_child =
-        android_create_namespace("isolated1_child",
-                                 nullptr,
-                                 (get_testlib_root() + "/private_namespace_libs").c_str(),
-                                 ANDROID_NAMESPACE_TYPE_ISOLATED,
-                                 nullptr,
-                                 ns1);
+          android_create_namespace("isolated1_child",
+                                   nullptr,
+                                   (get_testlib_root() + "/private_namespace_libs").c_str(),
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                   nullptr,
+                                   ns1);
+
+  ASSERT_TRUE(ns1_child != nullptr) << dlerror();
+  ASSERT_TRUE(android_link_namespaces(ns1_child, nullptr, g_core_shared_libs.c_str())) << dlerror();
 
   // Now - only ns1 and ns1 child should be able to dlopen root_lib
   // attempt to use ns2 should result in dlerror()
@@ -1092,22 +1311,28 @@
 
 TEST(dlext, ns_anonymous) {
   static const char* root_lib = "libnstest_root.so";
-  std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib;
+  std::string shared_libs = g_core_shared_libs + ":" + g_public_lib;
 
   const std::string lib_public_path = get_testlib_root() + "/public_namespace_libs/" + g_public_lib;
   void* handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
 
   ASSERT_TRUE(handle_public != nullptr) << dlerror();
 
-  ASSERT_TRUE(android_init_namespaces(path.c_str(), (get_testlib_root() + "/private_namespace_libs").c_str()))
-      << dlerror();
+  ASSERT_TRUE(
+          android_init_anonymous_namespace(shared_libs.c_str(),
+                                           (get_testlib_root() + "/private_namespace_libs").c_str())
+      ) << dlerror();
 
-  android_namespace_t* ns = android_create_namespace(
-                                "private", nullptr,
-                                (get_testlib_root() + "/private_namespace_libs").c_str(),
-                                ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr);
+  android_namespace_t* ns =
+          android_create_namespace("private",
+                                   nullptr,
+                                   (get_testlib_root() + "/private_namespace_libs").c_str(),
+                                   ANDROID_NAMESPACE_TYPE_REGULAR,
+                                   nullptr,
+                                   nullptr);
 
   ASSERT_TRUE(ns != nullptr) << dlerror();
+  ASSERT_TRUE(android_link_namespaces(ns, nullptr, shared_libs.c_str())) << dlerror();
 
   std::string private_library_absolute_path = get_testlib_root() + "/private_namespace_libs/" + root_lib;
 
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 3e9e85e..48fb6d1 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -1142,8 +1142,9 @@
   g_fini_call_order_str += s;
 }
 
-TEST(dlfcn, init_fini_call_order) {
-  void* handle = dlopen("libtest_init_fini_order_root.so", RTLD_NOW);
+static void test_init_fini_call_order_for(const char* libname) {
+  g_fini_call_order_str.clear();
+  void* handle = dlopen(libname, RTLD_NOW);
   ASSERT_TRUE(handle != nullptr) << dlerror();
   typedef int (*get_init_order_number_t)();
   get_init_order_number_t get_init_order_number =
@@ -1158,6 +1159,11 @@
   ASSERT_EQ("(root)(child)(grandchild)", g_fini_call_order_str);
 }
 
+TEST(dlfcn, init_fini_call_order) {
+  test_init_fini_call_order_for("libtest_init_fini_order_root.so");
+  test_init_fini_call_order_for("libtest_init_fini_order_root2.so");
+}
+
 TEST(dlfcn, symbol_versioning_use_v1) {
   void* handle = dlopen("libtest_versioned_uselibv1.so", RTLD_NOW);
   ASSERT_TRUE(handle != nullptr) << dlerror();
diff --git a/tests/fortify_compilation_test.cpp b/tests/fortify_compilation_test.cpp
index 1b02d4e..ea8e598 100644
--- a/tests/fortify_compilation_test.cpp
+++ b/tests/fortify_compilation_test.cpp
@@ -184,7 +184,7 @@
 
   // NOLINTNEXTLINE(whitespace/line_length)
   // GCC: error: call to '__recvfrom_error' declared with attribute error: recvfrom called with size bigger than buffer
-  // CLANG: error: call to unavailable function 'recvfrom': size is larger than the destination buffer
+  // CLANG: error: call to unavailable function 'recvfrom': recvfrom called with size bigger than buffer
   recvfrom(0, buf, 6, 0, reinterpret_cast<sockaddr*>(&addr), NULL);
 }
 
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index 31a0916..5eb16c5 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -448,13 +448,26 @@
 }
 
 // -----------------------------------------------------------------------------
-// Library used to check init/fini call order
+// Libraries used to check init/fini call order
 // -----------------------------------------------------------------------------
 cc_test_library {
     name: "libtest_init_fini_order_root",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlopen_check_init_fini_root.cpp"],
-    shared_libs: ["libtest_init_fini_order_child"],
+    shared_libs: [
+        "libtest_init_fini_order_child",
+        "libtest_init_fini_order_grand_child",
+    ],
+}
+
+cc_test_library {
+    name: "libtest_init_fini_order_root2",
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["dlopen_check_init_fini_root.cpp"],
+    shared_libs: [
+        "libtest_init_fini_order_grand_child",
+        "libtest_init_fini_order_child",
+    ],
 }
 
 cc_test_library {
diff --git a/tests/libs/Android.build.linker_namespaces.mk b/tests/libs/Android.build.linker_namespaces.mk
index df6428c..cd9d7f1 100644
--- a/tests/libs/Android.build.linker_namespaces.mk
+++ b/tests/libs/Android.build.linker_namespaces.mk
@@ -25,10 +25,13 @@
 # 2. Check that public libraries loaded in different namespaces are shared
 #    between them.
 # 3. Check that namespace sticks on dlopen
+# 4. Check that having access to shared library (libnstest_public.so)
+#    does not expose symbols from dependent library (libnstest_public_internal.so)
 #
 # Dependency tree (visibility)
 # libnstest_root.so (this should be local to the namespace)
 # +-> libnstest_public.so
+#     +-> libnstest_public_internal.so
 # +-> libnstest_private.so
 #
 # libnstest_dlopened.so (library in private namespace dlopened from libnstest_root.so)
@@ -39,7 +42,13 @@
 module := libnstest_root
 include $(LOCAL_PATH)/Android.build.testlib.target.mk
 
+libnstest_public_internal_src_files := namespaces_public_internal.cpp
+module := libnstest_public_internal
+libnstest_public_internal_relative_install_path := public_namespace_libs
+include $(LOCAL_PATH)/Android.build.testlib.target.mk
+
 libnstest_public_src_files := namespaces_public.cpp
+libnstest_public_shared_libraries := libnstest_public_internal
 module := libnstest_public
 libnstest_public_relative_install_path := public_namespace_libs
 include $(LOCAL_PATH)/Android.build.testlib.target.mk
diff --git a/tests/libs/namespaces_public_internal.cpp b/tests/libs/namespaces_public_internal.cpp
new file mode 100644
index 0000000..15ae398
--- /dev/null
+++ b/tests/libs/namespaces_public_internal.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+static const char* g_internal_extern_string = "This string is from a library a shared library depends on";
+
+extern "C" const char* internal_extern_string() {
+  return g_internal_extern_string;
+}
diff --git a/tests/libs/namespaces_root.cpp b/tests/libs/namespaces_root.cpp
index b0006c7..a551673 100644
--- a/tests/libs/namespaces_root.cpp
+++ b/tests/libs/namespaces_root.cpp
@@ -20,6 +20,14 @@
 extern "C" const char* g_private_extern_string;
 extern "C" const char* g_public_extern_string;
 
+// This is resolved only if public library is in the same namespace as
+// the root one. It should remain unresolved if looking up for public library
+// crosses namespace boundary.
+//
+// Defined in libnstest_public_internal.so on which libnstest_public.so
+// depends on
+extern "C" const char* __attribute__((weak)) internal_extern_string();
+
 bool g_dlopened = false;
 
 extern "C" const char* ns_get_local_string() {
@@ -34,6 +42,14 @@
   return g_public_extern_string;
 }
 
+extern "C" const char* ns_get_internal_extern_string() {
+  if (internal_extern_string != nullptr) {
+    return internal_extern_string();
+  } else {
+    return nullptr;
+  }
+}
+
 extern "C" const char* ns_get_dlopened_string() {
   void* handle = dlopen("libnstest_dlopened.so", RTLD_NOW | RTLD_GLOBAL);
   if (handle == nullptr) {
diff --git a/tests/system_properties_test.cpp b/tests/system_properties_test.cpp
index 6b037d8..39734d7 100644
--- a/tests/system_properties_test.cpp
+++ b/tests/system_properties_test.cpp
@@ -20,7 +20,9 @@
 #include <errno.h>
 #include <sys/wait.h>
 #include <unistd.h>
+
 #include <string>
+#include <thread>
 
 #if defined(__BIONIC__)
 
@@ -111,13 +113,11 @@
 
 #endif // __BIONIC__
 
-TEST(properties, add) {
+TEST(properties, __system_property_add) {
 #if defined(__BIONIC__)
     LocalPropertyTestState pa;
     ASSERT_TRUE(pa.valid);
 
-    char propvalue[PROP_VALUE_MAX];
-
     ASSERT_EQ(0, __system_property_add("property", 8, "value1", 6));
     ASSERT_EQ(0, __system_property_add("other_property", 14, "value2", 6));
     ASSERT_EQ(0, __system_property_add("property_other", 14, "value3", 6));
@@ -132,6 +132,7 @@
     name[sizeof(name)-1] = '\0';
     ASSERT_EQ(0, __system_property_add(name, strlen(name), "value", 5));
 
+    char propvalue[PROP_VALUE_MAX];
     ASSERT_EQ(6, __system_property_get("property", propvalue));
     ASSERT_STREQ(propvalue, "value1");
 
@@ -148,30 +149,28 @@
 #endif // __BIONIC__
 }
 
-TEST(properties, update) {
+TEST(properties, __system_property_update) {
 #if defined(__BIONIC__)
     LocalPropertyTestState pa;
     ASSERT_TRUE(pa.valid);
 
-    char propvalue[PROP_VALUE_MAX];
-    prop_info *pi;
-
     ASSERT_EQ(0, __system_property_add("property", 8, "oldvalue1", 9));
     ASSERT_EQ(0, __system_property_add("other_property", 14, "value2", 6));
     ASSERT_EQ(0, __system_property_add("property_other", 14, "value3", 6));
 
-    pi = (prop_info *)__system_property_find("property");
-    ASSERT_NE((prop_info *)NULL, pi);
-    __system_property_update(pi, "value4", 6);
+    const prop_info* pi = __system_property_find("property");
+    ASSERT_TRUE(pi != nullptr);
+    __system_property_update(const_cast<prop_info*>(pi), "value4", 6);
 
-    pi = (prop_info *)__system_property_find("other_property");
-    ASSERT_NE((prop_info *)NULL, pi);
-    __system_property_update(pi, "newvalue5", 9);
+    pi = __system_property_find("other_property");
+    ASSERT_TRUE(pi != nullptr);
+    __system_property_update(const_cast<prop_info*>(pi), "newvalue5", 9);
 
-    pi = (prop_info *)__system_property_find("property_other");
-    ASSERT_NE((prop_info *)NULL, pi);
-    __system_property_update(pi, "value6", 6);
+    pi = __system_property_find("property_other");
+    ASSERT_TRUE(pi != nullptr);
+    __system_property_update(const_cast<prop_info*>(pi), "value6", 6);
 
+    char propvalue[PROP_VALUE_MAX];
     ASSERT_EQ(6, __system_property_get("property", propvalue));
     ASSERT_STREQ(propvalue, "value4");
 
@@ -230,16 +229,16 @@
 #endif // __BIONIC__
 }
 
-TEST(properties, foreach) {
+TEST(properties, __system_property_foreach) {
 #if defined(__BIONIC__)
     LocalPropertyTestState pa;
     ASSERT_TRUE(pa.valid);
-    size_t count = 0;
 
     ASSERT_EQ(0, __system_property_add("property", 8, "value1", 6));
     ASSERT_EQ(0, __system_property_add("other_property", 14, "value2", 6));
     ASSERT_EQ(0, __system_property_add("property_other", 14, "value3", 6));
 
+    size_t count = 0;
     ASSERT_EQ(0, __system_property_foreach(foreach_test_callback, &count));
     ASSERT_EQ(3U, count);
 #else // __BIONIC__
@@ -247,7 +246,7 @@
 #endif // __BIONIC__
 }
 
-TEST(properties, find_nth) {
+TEST(properties, __system_property_find_nth) {
 #if defined(__BIONIC__)
     LocalPropertyTestState pa;
     ASSERT_TRUE(pa.valid);
@@ -342,47 +341,74 @@
 #endif // __BIONIC__
 }
 
-TEST(properties, serial) {
+TEST(properties, __system_property_serial) {
 #if defined(__BIONIC__)
     LocalPropertyTestState pa;
     ASSERT_TRUE(pa.valid);
-    const prop_info *pi;
-    unsigned int serial;
 
     ASSERT_EQ(0, __system_property_add("property", 8, "value1", 6));
-    ASSERT_NE((const prop_info *)NULL, pi = __system_property_find("property"));
-    serial = __system_property_serial(pi);
-    ASSERT_EQ(0, __system_property_update((prop_info *)pi, "value2", 6));
+    const prop_info* pi = __system_property_find("property");
+    ASSERT_TRUE(pi != nullptr);
+    unsigned serial = __system_property_serial(pi);
+    ASSERT_EQ(0, __system_property_update(const_cast<prop_info*>(pi), "value2", 6));
     ASSERT_NE(serial, __system_property_serial(pi));
 #else // __BIONIC__
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif // __BIONIC__
 }
 
-TEST(properties, wait) {
+TEST(properties, __system_property_wait_any) {
 #if defined(__BIONIC__)
     LocalPropertyTestState pa;
     ASSERT_TRUE(pa.valid);
-    unsigned int serial;
-    prop_info *pi;
-    pthread_t t;
-    int flag = 0;
 
     ASSERT_EQ(0, __system_property_add("property", 8, "value1", 6));
-    serial = __system_property_wait_any(0);
+    unsigned serial = __system_property_wait_any(0);
 
-    pi = const_cast<prop_info*>(__system_property_find("property"));
+    prop_info* pi = const_cast<prop_info*>(__system_property_find("property"));
     ASSERT_TRUE(pi != nullptr);
     __system_property_update(pi, "value2", 6);
     serial = __system_property_wait_any(serial);
 
+    int flag = 0;
+    pthread_t t;
     ASSERT_EQ(0, pthread_create(&t, nullptr, PropertyWaitHelperFn, &flag));
     ASSERT_EQ(flag, 0);
     serial = __system_property_wait_any(serial);
     ASSERT_EQ(flag, 1);
 
-    void* result;
-    ASSERT_EQ(0, pthread_join(t, &result));
+    ASSERT_EQ(0, pthread_join(t, nullptr));
+#else // __BIONIC__
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif // __BIONIC__
+}
+
+TEST(properties, __system_property_wait) {
+#if defined(__BIONIC__)
+    LocalPropertyTestState pa;
+    ASSERT_TRUE(pa.valid);
+
+    ASSERT_EQ(0, __system_property_add("property", 8, "value1", 6));
+
+    prop_info* pi = const_cast<prop_info*>(__system_property_find("property"));
+    ASSERT_TRUE(pi != nullptr);
+
+    unsigned serial = __system_property_serial(pi);
+
+    std::thread thread([]() {
+        prop_info* pi = const_cast<prop_info*>(__system_property_find("property"));
+        ASSERT_TRUE(pi != nullptr);
+
+        __system_property_update(pi, "value2", 6);
+    });
+
+    __system_property_wait(pi, serial);
+
+    char value[PROP_VALUE_MAX];
+    ASSERT_EQ(6, __system_property_get("property", value));
+    ASSERT_STREQ("value2", value);
+
+    thread.join();
 #else // __BIONIC__
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif // __BIONIC__
diff --git a/tests/system_properties_test2.cpp b/tests/system_properties_test2.cpp
index 0560960..e6e7ef2 100644
--- a/tests/system_properties_test2.cpp
+++ b/tests/system_properties_test2.cpp
@@ -90,20 +90,22 @@
     ASSERT_TRUE(pi != nullptr);
 
     std::string expected_name = property_name;
-    __system_property_read_callback(pi, [](void* cookie, const char *name, const char *value) {
-      const std::string* expected_name = static_cast<const std::string*>(cookie);
-      ASSERT_EQ(*expected_name, name);
-      ASSERT_STREQ("value1-1", value);
+    __system_property_read_callback(pi,
+      [](void* cookie, const char* name, const char* value, unsigned /*serial*/) {
+        const std::string* expected_name = static_cast<const std::string*>(cookie);
+        ASSERT_EQ(*expected_name, name);
+        ASSERT_STREQ("value1-1", value);
     }, &expected_name);
 
     pi = __system_property_find(long_property_name.c_str());
     ASSERT_TRUE(pi != nullptr);
 
     expected_name = long_property_name;
-    __system_property_read_callback(pi, [](void* cookie, const char *name, const char *value) {
-      const std::string* expected_name = static_cast<const std::string*>(cookie);
-      ASSERT_EQ(*expected_name, name);
-      ASSERT_STREQ("value2", value);
+    __system_property_read_callback(pi,
+      [](void* cookie, const char* name, const char* value, unsigned /*serial*/) {
+        const std::string* expected_name = static_cast<const std::string*>(cookie);
+        ASSERT_EQ(*expected_name, name);
+        ASSERT_STREQ("value2", value);
     }, &expected_name);
 
     // Check that read() for long names still works but returns truncated version of the name