Merge "Revert "Ensure that readlink has access to /proc/self/fd""
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/benchmarks/Benchmark.cpp b/benchmarks/Benchmark.cpp
index ea6000f..a7ab682 100644
--- a/benchmarks/Benchmark.cpp
+++ b/benchmarks/Benchmark.cpp
@@ -24,7 +24,7 @@
 #include <string>
 #include <vector>
 
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
 
 #include <benchmark/Benchmark.h>
 
diff --git a/benchmarks/utils.cpp b/benchmarks/utils.cpp
index cf17edb..8bbd20a 100644
--- a/benchmarks/utils.cpp
+++ b/benchmarks/utils.cpp
@@ -23,7 +23,7 @@
 
 #include <string>
 
-#include <base/stringprintf.h>
+#include <android-base/stringprintf.h>
 
 int Round(int n) {
   int base = 1;
diff --git a/libc/Android.bp b/libc/Android.bp
index 2429634..74fd22b 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1,5 +1,3 @@
-bionic_coverage = false
-
 // Define the common source files for all the libc instances
 // =========================================================
 libc_common_src_files = [
@@ -11,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",
@@ -23,6 +20,7 @@
     "bionic/system_properties_compat.c",
     "stdio/findfp.c",
     "stdio/fread.c",
+    "stdio/refill.c",
     "stdio/snprintf.c",
     "stdio/sprintf.c",
     "stdio/stdio.c",
@@ -76,51 +74,70 @@
 
 // Define some common cflags
 // ========================================================
-libc_common_cflags = [
-    "-D_LIBC=1",
-    "-Wall",
-    "-Wextra",
-    "-Wunused",
+cc_defaults {
+    name: "libc_defaults",
+    cflags: [
+        "-D_LIBC=1",
+        "-Wall",
+        "-Wextra",
+        "-Wunused",
 
-// Try to catch typical 32-bit assumptions that break with 64-bit pointers.
-    "-Werror=pointer-to-int-cast",
-    "-Werror=int-to-pointer-cast",
-    "-Werror=type-limits",
-    "-Werror",
-]
+        // Try to catch typical 32-bit assumptions that break with 64-bit pointers.
+        "-Werror=pointer-to-int-cast",
+        "-Werror=int-to-pointer-cast",
+        "-Werror=type-limits",
+        "-Werror",
+    ],
+    // TODO: split out the asflags.
+    asflags: [
+        "-D_LIBC=1",
+        "-Wall",
+        "-Wextra",
+        "-Wunused",
 
-libc_common_product_variables = {
-    device_uses_jemalloc: {
-        cflags: ["-DUSE_JEMALLOC"],
-        include_dirs: ["external/jemalloc/include"],
+        // Try to catch typical 32-bit assumptions that break with 64-bit pointers.
+        "-Werror=pointer-to-int-cast",
+        "-Werror=int-to-pointer-cast",
+        "-Werror=type-limits",
+        "-Werror",
+    ],
+    conlyflags: ["-std=gnu99"],
+    cppflags: [],
+
+    product_variables: {
+        device_uses_jemalloc: {
+            cflags: ["-DUSE_JEMALLOC"],
+            include_dirs: ["external/jemalloc/include"],
+        },
+        device_uses_dlmalloc: {
+            cflags: ["-DUSE_DLMALLOC"],
+        },
+        // To customize dlmalloc's alignment, set BOARD_MALLOC_ALIGNMENT in
+        // the appropriate BoardConfig.mk file.
+        dlmalloc_alignment: {
+            cflags: ["-DMALLOC_ALIGNMENT=%d"],
+        },
     },
-    device_uses_dlmalloc: {
-        cflags: ["-DUSE_DLMALLOC"],
+    // Clang/llvm has incompatible long double (fp128) for x86_64.
+    // https://llvm.org/bugs/show_bug.cgi?id=23897
+    arch: {
+        x86_64: {
+            clang: false,
+        },
     },
-    // To customize dlmalloc's alignment, set BOARD_MALLOC_ALIGNMENT in
-    // the appropriate BoardConfig.mk file.
-    dlmalloc_alignment: {
-        cflags: ["-DMALLOC_ALIGNMENT=%d"],
-    },
+
+    stl: "none",
+    system_shared_libs: [],
+    sanitize: ["never"],
+    native_coverage: false,
 }
 
-// Clang/llvm has incompatible long double (fp128) for x86_64.
-// https://llvm.org/bugs/show_bug.cgi?id=23897
-use_clang_x86_64 = false
-
 // ANDROIDMK TRANSLATION ERROR: unsupported directive
 // ifeq ($(strip $(DEBUG_BIONIC_LIBC)),true)
 //libc_common_cflags += ["-DDEBUG"]
 // ANDROIDMK TRANSLATION ERROR: unsupported directive
 // endif
 
-//
-// Define some common conlyflags
-libc_common_conlyflags = ["-std=gnu99"]
-
-// Define some common cppflags
-libc_common_cppflags = []
-
 // ========================================================
 // libc_stack_protector.a - stack protector code
 // ========================================================
@@ -132,23 +149,9 @@
 cc_library_static {
 
     srcs: ["bionic/__stack_chk_fail.cpp"],
-    cflags: libc_common_cflags + ["-fno-stack-protector"],
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags,
-    product_variables: libc_common_product_variables,
+    defaults: ["libc_defaults"],
+    cflags: ["-fno-stack-protector"],
     name: "libc_stack_protector",
-
-    stl: "none",
-    system_shared_libs: [],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
-
-    arch: {
-        x86_64: {
-            clang: use_clang_x86_64,
-        },
-    },
-
 }
 
 // ========================================================
@@ -157,6 +160,7 @@
 
 cc_library_static {
 
+    defaults: ["libc_defaults"],
     srcs: [
         "tzcode/asctime.c",
         "tzcode/difftime.c",
@@ -166,38 +170,30 @@
         "upstream-openbsd/lib/libc/time/wcsftime.c", // tzcode doesn't include wcsftime, so we use the OpenBSD one.
     ],
 
-    cflags: libc_common_cflags + [
+    cflags: [
         "-fvisibility=hidden",
+        "-Wno-unused-parameter",
         // Don't use ridiculous amounts of stack.
         "-DALL_STATE",
         // Include tzsetwall, timelocal, timegm, time2posix, and posix2time.
         "-DSTD_INSPIRED",
+        // Obviously, we want to be thread-safe.
+        "-DTHREAD_SAFE",
         // The name of the tm_gmtoff field in our struct tm.
         "-DTM_GMTOFF=tm_gmtoff",
         // Where we store our tzdata.
         "-DTZDIR=\\\"/system/usr/share/zoneinfo\\\"",
         // Include timezone and daylight globals.
         "-DUSG_COMPAT=1",
+        // Use the empty string (instead of "   ") as the timezone abbreviation
+        // fallback.
+        "-DWILDABBR=\\\"\\\"",
         "-DNO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU",
         "-Dlint",
     ],
 
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags,
-    product_variables: libc_common_product_variables,
     local_include_dirs: ["tzcode/"],
     name: "libc_tzcode",
-
-    stl: "none",
-    system_shared_libs: [],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
-
-    arch: {
-        x86_64: {
-            clang: use_clang_x86_64,
-        },
-    },
 }
 
 // ========================================================
@@ -206,6 +202,7 @@
 
 cc_library_static {
 
+    defaults: ["libc_defaults"],
     srcs: [
         "dns/net/gethnamaddr.c",
         "dns/net/getservbyname.c",
@@ -240,8 +237,8 @@
         "upstream-openbsd/lib/libc/net/res_random.c",
     ],
 
-    cflags: ["-Dres_randomid=__res_randomid"] +
-    libc_common_cflags + [
+    cflags: [
+        "-Dres_randomid=__res_randomid",
         "-DANDROID_CHANGES",
         "-DINET6",
         "-fvisibility=hidden",
@@ -249,9 +246,6 @@
         "-include netbsd-compat.h",
     ],
 
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags,
-    product_variables: libc_common_product_variables,
     local_include_dirs: [
         "dns/include",
         "private",
@@ -260,17 +254,6 @@
     ],
 
     name: "libc_dns",
-
-    stl: "none",
-    system_shared_libs: [],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
-
-    arch: {
-        x86_64: {
-            clang: use_clang_x86_64,
-        },
-    },
 }
 
 // ========================================================
@@ -281,6 +264,7 @@
 // automatically included.
 
 cc_library_static {
+    defaults: ["libc_defaults"],
     srcs: [
         "upstream-freebsd/lib/libc/gen/ldexp.c",
         "upstream-freebsd/lib/libc/gen/sleep.c",
@@ -292,28 +276,26 @@
         "upstream-freebsd/lib/libc/string/wcpcpy.c",
         "upstream-freebsd/lib/libc/string/wcpncpy.c",
         "upstream-freebsd/lib/libc/string/wcscasecmp.c",
+        "upstream-freebsd/lib/libc/string/wcscat.c",
+        "upstream-freebsd/lib/libc/string/wcschr.c",
+        "upstream-freebsd/lib/libc/string/wcscmp.c",
+        "upstream-freebsd/lib/libc/string/wcscpy.c",
         "upstream-freebsd/lib/libc/string/wcscspn.c",
         "upstream-freebsd/lib/libc/string/wcsdup.c",
         "upstream-freebsd/lib/libc/string/wcslcat.c",
+        "upstream-freebsd/lib/libc/string/wcslen.c",
         "upstream-freebsd/lib/libc/string/wcsncasecmp.c",
         "upstream-freebsd/lib/libc/string/wcsncat.c",
         "upstream-freebsd/lib/libc/string/wcsncmp.c",
         "upstream-freebsd/lib/libc/string/wcsncpy.c",
         "upstream-freebsd/lib/libc/string/wcsnlen.c",
         "upstream-freebsd/lib/libc/string/wcspbrk.c",
+        "upstream-freebsd/lib/libc/string/wcsrchr.c",
         "upstream-freebsd/lib/libc/string/wcsspn.c",
         "upstream-freebsd/lib/libc/string/wcstok.c",
         "upstream-freebsd/lib/libc/string/wmemchr.c",
-        "upstream-freebsd/lib/libc/string/wmemset.c",
-
-        // May be overriden by per-arch optimized versions
-        "upstream-freebsd/lib/libc/string/wcscat.c",
-        "upstream-freebsd/lib/libc/string/wcschr.c",
-        "upstream-freebsd/lib/libc/string/wcscmp.c",
-        "upstream-freebsd/lib/libc/string/wcscpy.c",
-        "upstream-freebsd/lib/libc/string/wcslen.c",
-        "upstream-freebsd/lib/libc/string/wcsrchr.c",
         "upstream-freebsd/lib/libc/string/wmemcmp.c",
+        "upstream-freebsd/lib/libc/string/wmemset.c",
         "upstream-freebsd/lib/libc/string/wmemmove.c",
     ],
     arch: {
@@ -329,43 +311,36 @@
                 "upstream-freebsd/lib/libc/string/wcslen.c",
                 "upstream-freebsd/lib/libc/string/wcsrchr.c",
             ],
-        },
-        x86_sse3: {
-            exclude_srcs: [
-                "upstream-freebsd/lib/libc/string/wcscpy.c",
-                "upstream-freebsd/lib/libc/string/wcscat.c",
-            ],
-        },
-        x86_sse4: {
-            exclude_srcs: [
-                "upstream-freebsd/lib/libc/string/wmemcmp.c",
-            ],
-        },
-        x86_64: {
-            clang: use_clang_x86_64,
+            atom: {
+                exclude_srcs: [
+                    "upstream-freebsd/lib/libc/string/wmemcmp.c",
+                ],
+            },
+            ssse3: {
+                exclude_srcs: [
+                    "upstream-freebsd/lib/libc/string/wcscat.c",
+                    "upstream-freebsd/lib/libc/string/wcscpy.c",
+                ],
+            },
+            sse4: {
+                exclude_srcs: [
+                    "upstream-freebsd/lib/libc/string/wmemcmp.c",
+                ],
+            },
         },
     },
 
-    cflags: libc_common_cflags + [
+    cflags: [
         "-Wno-sign-compare",
         "-Wno-uninitialized",
         "-include freebsd-compat.h",
     ],
 
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags,
-    product_variables: libc_common_product_variables,
     local_include_dirs: [
         "upstream-freebsd/android/include",
     ],
 
     name: "libc_freebsd",
-
-    stl: "none",
-    system_shared_libs: [],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
-
 }
 
 // ========================================================
@@ -377,6 +352,7 @@
 
 cc_library_static {
 
+    defaults: ["libc_defaults"],
     srcs: [
         "upstream-netbsd/common/lib/libc/stdlib/random.c",
         "upstream-netbsd/lib/libc/gen/ftw.c",
@@ -418,33 +394,19 @@
             srcs: ["upstream-netbsd/common/lib/libc/hash/sha1/sha1.c"],
         },
     },
-    cflags: libc_common_cflags + [
+    cflags: [
         "-Wno-sign-compare",
         "-Wno-uninitialized",
         "-DPOSIX_MISTAKE",
         "-include netbsd-compat.h",
     ],
 
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags,
-    product_variables: libc_common_product_variables,
     local_include_dirs: [
         "upstream-netbsd/android/include",
         "upstream-netbsd/lib/libc/include",
     ],
 
     name: "libc_netbsd",
-
-    stl: "none",
-    system_shared_libs: [],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
-
-    arch: {
-        x86_64: {
-            clang: use_clang_x86_64,
-        },
-    },
 }
 
 // ========================================================
@@ -458,6 +420,7 @@
 
 cc_library_static {
     name: "libc_openbsd_ndk",
+    defaults: ["libc_defaults"],
     srcs: [
         "upstream-openbsd/lib/libc/compat-43/killpg.c",
         "upstream-openbsd/lib/libc/gen/alarm.c",
@@ -500,11 +463,9 @@
         "upstream-openbsd/lib/libc/locale/wctomb.c",
         "upstream-openbsd/lib/libc/net/htonl.c",
         "upstream-openbsd/lib/libc/net/htons.c",
-        "upstream-openbsd/lib/libc/net/inet_addr.c",
         "upstream-openbsd/lib/libc/net/inet_lnaof.c",
         "upstream-openbsd/lib/libc/net/inet_makeaddr.c",
         "upstream-openbsd/lib/libc/net/inet_netof.c",
-        "upstream-openbsd/lib/libc/net/inet_network.c",
         "upstream-openbsd/lib/libc/net/inet_ntoa.c",
         "upstream-openbsd/lib/libc/net/inet_ntop.c",
         "upstream-openbsd/lib/libc/net/inet_pton.c",
@@ -564,7 +525,6 @@
         "upstream-openbsd/lib/libc/stdio/puts.c",
         "upstream-openbsd/lib/libc/stdio/putwc.c",
         "upstream-openbsd/lib/libc/stdio/putwchar.c",
-        "upstream-openbsd/lib/libc/stdio/refill.c",
         "upstream-openbsd/lib/libc/stdio/remove.c",
         "upstream-openbsd/lib/libc/stdio/rewind.c",
         "upstream-openbsd/lib/libc/stdio/rget.c",
@@ -636,16 +596,13 @@
         "upstream-openbsd/lib/libc/string/wcswidth.c",
     ],
 
-    cflags: libc_common_cflags + [
+    cflags: [
         "-Wno-sign-compare",
         "-Wno-uninitialized",
         "-Wno-unused-parameter",
         "-include openbsd-compat.h",
     ],
 
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags,
-    product_variables: libc_common_product_variables,
     local_include_dirs: [
         "private",
         "stdio",
@@ -653,17 +610,6 @@
         "upstream-openbsd/lib/libc/include",
         "upstream-openbsd/lib/libc/gdtoa/",
     ],
-
-    stl: "none",
-    system_shared_libs: [],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
-
-    arch: {
-        x86_64: {
-            clang: use_clang_x86_64,
-        },
-    },
 }
 
 // ========================================================
@@ -673,6 +619,7 @@
 // These files are built with the openbsd-compat.h header file
 // automatically included.
 cc_library_static {
+    defaults: ["libc_defaults"],
     srcs: [
         // These two depend on getentropy_linux.c, which isn't in libc_ndk.a.
         "upstream-openbsd/lib/libc/crypt/arc4random.c",
@@ -685,7 +632,6 @@
         "upstream-openbsd/lib/libc/string/stpcpy.c",
         "upstream-openbsd/lib/libc/string/stpncpy.c",
         "upstream-openbsd/lib/libc/string/strcat.c",
-        "upstream-openbsd/lib/libc/string/strcmp.c",
         "upstream-openbsd/lib/libc/string/strcpy.c",
         "upstream-openbsd/lib/libc/string/strlcat.c",
         "upstream-openbsd/lib/libc/string/strlcpy.c",
@@ -703,133 +649,119 @@
     arch: {
         arm: {
             exclude_srcs: [
-                "upstream-openbsd/lib/libc/string/strcmp.c",
                 "upstream-openbsd/lib/libc/string/strcpy.c",
             ],
+            cortex_a7: {
+                exclude_srcs: [
+                    "upstream-openbsd/lib/libc/string/memmove.c",
+                    "upstream-openbsd/lib/libc/string/stpcpy.c",
+                    "upstream-openbsd/lib/libc/string/strcat.c",
+                ],
+            },
+            cortex_a53: {
+                exclude_srcs: [
+                    "upstream-openbsd/lib/libc/string/memmove.c",
+                    "upstream-openbsd/lib/libc/string/stpcpy.c",
+                    "upstream-openbsd/lib/libc/string/strcat.c",
+                ],
+            },
+            cortex_a53_a57: {
+                exclude_srcs: [
+                    "upstream-openbsd/lib/libc/string/memmove.c",
+                    "upstream-openbsd/lib/libc/string/stpcpy.c",
+                    "upstream-openbsd/lib/libc/string/strcat.c",
+                ],
+            },
+            cortex_a8: {
+                exclude_srcs: [
+                    "upstream-openbsd/lib/libc/string/memmove.c",
+                    "upstream-openbsd/lib/libc/string/stpcpy.c",
+                    "upstream-openbsd/lib/libc/string/strcat.c",
+                ],
+            },
+            cortex_a9: {
+                exclude_srcs: [
+                    "upstream-openbsd/lib/libc/string/memmove.c",
+                    "upstream-openbsd/lib/libc/string/stpcpy.c",
+                    "upstream-openbsd/lib/libc/string/strcat.c",
+                ],
+            },
+            cortex_a15: {
+                exclude_srcs: [
+                    "upstream-openbsd/lib/libc/string/memmove.c",
+                    "upstream-openbsd/lib/libc/string/stpcpy.c",
+                    "upstream-openbsd/lib/libc/string/strcat.c",
+                ],
+            },
+            denver: {
+                exclude_srcs: [
+                    "upstream-openbsd/lib/libc/string/memmove.c",
+                    "upstream-openbsd/lib/libc/string/stpcpy.c",
+                    "upstream-openbsd/lib/libc/string/strcat.c",
+                ],
+            },
+            krait: {
+                exclude_srcs: [
+                    "upstream-openbsd/lib/libc/string/memmove.c",
+                    "upstream-openbsd/lib/libc/string/stpcpy.c",
+                    "upstream-openbsd/lib/libc/string/strcat.c",
+                ],
+            },
         },
-        cortex_a7: {
-            exclude_srcs: [
-                "upstream-openbsd/lib/libc/string/memmove.c",
-                "upstream-openbsd/lib/libc/string/stpcpy.c",
-                "upstream-openbsd/lib/libc/string/strcat.c",
-            ],
-        },
-        cortex_a53: {
-            exclude_srcs: [
-                "upstream-openbsd/lib/libc/string/memmove.c",
-                "upstream-openbsd/lib/libc/string/stpcpy.c",
-                "upstream-openbsd/lib/libc/string/strcat.c",
-            ],
-        },
-        cortex_a8: {
-            exclude_srcs: [
-                "upstream-openbsd/lib/libc/string/memmove.c",
-                "upstream-openbsd/lib/libc/string/stpcpy.c",
-                "upstream-openbsd/lib/libc/string/strcat.c",
-            ],
-        },
-        cortex_a9: {
-            exclude_srcs: [
-                "upstream-openbsd/lib/libc/string/memmove.c",
-                "upstream-openbsd/lib/libc/string/stpcpy.c",
-                "upstream-openbsd/lib/libc/string/strcat.c",
-            ],
-        },
-        cortex_a15: {
-            exclude_srcs: [
-                "upstream-openbsd/lib/libc/string/memmove.c",
-                "upstream-openbsd/lib/libc/string/stpcpy.c",
-                "upstream-openbsd/lib/libc/string/strcat.c",
-            ],
-        },
-        denver: {
-            exclude_srcs: [
-                "upstream-openbsd/lib/libc/string/memmove.c",
-                "upstream-openbsd/lib/libc/string/stpcpy.c",
-                "upstream-openbsd/lib/libc/string/strcat.c",
-            ],
-        },
-        krait: {
-            exclude_srcs: [
-                "upstream-openbsd/lib/libc/string/memmove.c",
-                "upstream-openbsd/lib/libc/string/stpcpy.c",
-                "upstream-openbsd/lib/libc/string/strcat.c",
-            ],
-        },
-
         arm64: {
             exclude_srcs: [
                 "upstream-openbsd/lib/libc/string/memchr.c",
                 "upstream-openbsd/lib/libc/string/memmove.c",
                 "upstream-openbsd/lib/libc/string/stpcpy.c",
-                "upstream-openbsd/lib/libc/string/strcmp.c",
                 "upstream-openbsd/lib/libc/string/strcpy.c",
                 "upstream-openbsd/lib/libc/string/strncmp.c",
             ],
         },
 
-        mips: {
-            exclude_srcs: [
-                "upstream-openbsd/lib/libc/string/strcmp.c",
-            ],
-        },
-
-        mips64: {
-            exclude_srcs: [
-                "upstream-openbsd/lib/libc/string/strcmp.c",
-            ],
-        },
-
         x86: {
-             exclude_srcs: [
-                 "upstream-openbsd/lib/libc/string/memchr.c",
-                 "upstream-openbsd/lib/libc/string/memmove.c",
-                 "upstream-openbsd/lib/libc/string/memrchr.c",
-                 "upstream-openbsd/lib/libc/string/stpcpy.c",
-                 "upstream-openbsd/lib/libc/string/stpncpy.c",
-                 "upstream-openbsd/lib/libc/string/strcat.c",
-                 "upstream-openbsd/lib/libc/string/strcmp.c",
-                 "upstream-openbsd/lib/libc/string/strcpy.c",
-                 "upstream-openbsd/lib/libc/string/strncmp.c",
-                 "upstream-openbsd/lib/libc/string/strncpy.c",
-                 ],
-        },
-        x86_sse3: {
-             exclude_srcs: [
-                 "upstream-openbsd/lib/libc/string/strlcat.c",
-                 "upstream-openbsd/lib/libc/string/strlcpy.c",
-                 "upstream-openbsd/lib/libc/string/strncat.c",
-             ],
+            exclude_srcs: [
+                "upstream-openbsd/lib/libc/string/memchr.c",
+                "upstream-openbsd/lib/libc/string/memmove.c",
+                "upstream-openbsd/lib/libc/string/memrchr.c",
+                "upstream-openbsd/lib/libc/string/stpcpy.c",
+                "upstream-openbsd/lib/libc/string/stpncpy.c",
+                "upstream-openbsd/lib/libc/string/strcat.c",
+                "upstream-openbsd/lib/libc/string/strcpy.c",
+                "upstream-openbsd/lib/libc/string/strncmp.c",
+                "upstream-openbsd/lib/libc/string/strncpy.c",
+            ],
+            ssse3: {
+                exclude_srcs: [
+                    "upstream-openbsd/lib/libc/string/strlcat.c",
+                    "upstream-openbsd/lib/libc/string/strlcpy.c",
+                    "upstream-openbsd/lib/libc/string/strncat.c",
+                ],
+            },
         },
 
         x86_64: {
-             exclude_srcs: [
-                 "upstream-openbsd/lib/libc/string/memmove.c",
-                 "upstream-openbsd/lib/libc/string/stpcpy.c",
-                 "upstream-openbsd/lib/libc/string/stpncpy.c",
-                 "upstream-openbsd/lib/libc/string/strcat.c",
-                 "upstream-openbsd/lib/libc/string/strcmp.c",
-                 "upstream-openbsd/lib/libc/string/strcpy.c",
-                 "upstream-openbsd/lib/libc/string/strlcat.c",
-                 "upstream-openbsd/lib/libc/string/strlcpy.c",
-                 "upstream-openbsd/lib/libc/string/strncat.c",
-                 "upstream-openbsd/lib/libc/string/strncmp.c",
-                 "upstream-openbsd/lib/libc/string/strncpy.c",
-             ],
-            clang: use_clang_x86_64,
+            exclude_srcs: [
+                "upstream-openbsd/lib/libc/string/memmove.c",
+                "upstream-openbsd/lib/libc/string/stpcpy.c",
+                "upstream-openbsd/lib/libc/string/stpncpy.c",
+                "upstream-openbsd/lib/libc/string/strcat.c",
+                "upstream-openbsd/lib/libc/string/strcpy.c",
+                "upstream-openbsd/lib/libc/string/strlcat.c",
+                "upstream-openbsd/lib/libc/string/strlcpy.c",
+                "upstream-openbsd/lib/libc/string/strncat.c",
+                "upstream-openbsd/lib/libc/string/strncmp.c",
+                "upstream-openbsd/lib/libc/string/strncpy.c",
+            ],
         },
     },
 
-    cflags: libc_common_cflags + [
+    cflags: [
         "-Wno-sign-compare",
         "-Wno-uninitialized",
         "-Wno-unused-parameter",
         "-include openbsd-compat.h",
     ],
 
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags,
-    product_variables: libc_common_product_variables,
     local_include_dirs: [
         "private",
         "stdio",
@@ -839,12 +771,6 @@
     ],
 
     name: "libc_openbsd",
-
-    stl: "none",
-    system_shared_libs: [],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
-
 }
 
 // ========================================================
@@ -855,6 +781,7 @@
 // automatically included.
 
 cc_library_static {
+    defaults: ["libc_defaults"],
     srcs: [
         "upstream-openbsd/android/gdtoa_support.cpp",
         "upstream-openbsd/lib/libc/gdtoa/dmisc.c",
@@ -877,20 +804,17 @@
     ],
     multilib: {
         lib64: {
-            srcs: ["upstream-openbsd/lib/libc/gdtoa/strtorQ.c"]
+            srcs: ["upstream-openbsd/lib/libc/gdtoa/strtorQ.c"],
         },
     },
 
-    cflags: libc_common_cflags + [
+    cflags: [
         "-Wno-sign-compare",
         "-Wno-uninitialized",
         "-fvisibility=hidden",
         "-include openbsd-compat.h",
     ],
 
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags,
-    product_variables: libc_common_product_variables,
     local_include_dirs: [
         "private",
         "upstream-openbsd/android/include",
@@ -898,17 +822,6 @@
     ],
 
     name: "libc_gdtoa",
-
-    stl: "none",
-    system_shared_libs: [],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
-
-    arch: {
-        x86_64: {
-            clang: use_clang_x86_64,
-        },
-    },
 }
 
 // ========================================================
@@ -916,9 +829,11 @@
 // ========================================================
 
 cc_library_static {
+    defaults: ["libc_defaults"],
     srcs: [
-        // The fork implementation depends on pthread data, so we can't include
-        // it in libc_ndk.a.
+        // The following implementations depend on pthread data, so we can't
+        // include them in libc_ndk.a.
+        "bionic/__cxa_thread_atexit_impl.cpp",
         "bionic/fork.cpp",
 
         // The data that backs getauxval is initialized in the libc init
@@ -933,26 +848,447 @@
         "bionic/sysconf.cpp",
         "bionic/vdso.cpp",
         "bionic/setjmp_cookie.cpp",
+
+        "bionic/__memcpy_chk.cpp",
+        "bionic/__memset_chk.cpp",
+        "bionic/__strcat_chk.cpp",
+        "bionic/__strcpy_chk.cpp",
+        "bionic/strchr.cpp",
+        "bionic/strnlen.c",
+        "bionic/strrchr.cpp",
     ],
-    cflags: libc_common_cflags + ["-Wframe-larger-than=2048"],
+    cflags: ["-Wframe-larger-than=2048"],
 
     arch: {
+        arm: {
+            srcs: [
+                "arch-arm/bionic/abort_arm.S",
+                "arch-arm/bionic/atomics_arm.c",
+                "arch-arm/bionic/__bionic_clone.S",
+                "arch-arm/bionic/_exit_with_stack_teardown.S",
+                "arch-arm/bionic/libgcc_compat.c",
+                "arch-arm/bionic/popcount_tab.c",
+                "arch-arm/bionic/__restore.S",
+                "arch-arm/bionic/setjmp.S",
+                "arch-arm/bionic/syscall.S",
+                "arch-arm/bionic/vfork.S",
+
+                "arch-arm/generic/bionic/memcmp.S",
+                "arch-arm/generic/bionic/memcpy.S",
+                "arch-arm/generic/bionic/memset.S",
+                "arch-arm/generic/bionic/strcmp.S",
+                "arch-arm/generic/bionic/strcpy.S",
+                "arch-arm/generic/bionic/strlen.c",
+            ],
+            exclude_srcs: [
+                "bionic/__memcpy_chk.cpp",
+                "bionic/__memset_chk.cpp",
+            ],
+            cortex_a7: {
+                srcs: [
+                    "arch-arm/cortex-a7/bionic/memset.S",
+
+                    "arch-arm/cortex-a15/bionic/memcpy.S",
+                    "arch-arm/cortex-a15/bionic/stpcpy.S",
+                    "arch-arm/cortex-a15/bionic/strcat.S",
+                    "arch-arm/cortex-a15/bionic/__strcat_chk.S",
+                    "arch-arm/cortex-a15/bionic/strcmp.S",
+                    "arch-arm/cortex-a15/bionic/strcpy.S",
+                    "arch-arm/cortex-a15/bionic/__strcpy_chk.S",
+                    "arch-arm/cortex-a15/bionic/strlen.S",
+
+                    "arch-arm/denver/bionic/memmove.S",
+                ],
+                exclude_srcs: [
+                    "arch-arm/generic/bionic/memcpy.S",
+                    "arch-arm/generic/bionic/memset.S",
+                    "arch-arm/generic/bionic/strcmp.S",
+                    "arch-arm/generic/bionic/strcpy.S",
+                    "arch-arm/generic/bionic/strlen.c",
+                    "bionic/__strcat_chk.cpp",
+                    "bionic/__strcpy_chk.cpp",
+                ],
+            },
+            cortex_a53: {
+                srcs: [
+                    "arch-arm/cortex-a53/bionic/memcpy.S",
+                    "arch-arm/cortex-a53/bionic/__strcat_chk.S",
+                    "arch-arm/cortex-a53/bionic/__strcpy_chk.S",
+
+                    "arch-arm/cortex-a7/bionic/memset.S",
+
+                    "arch-arm/cortex-a15/bionic/stpcpy.S",
+                    "arch-arm/cortex-a15/bionic/strcat.S",
+                    "arch-arm/cortex-a15/bionic/strcmp.S",
+                    "arch-arm/cortex-a15/bionic/strcpy.S",
+                    "arch-arm/cortex-a15/bionic/strlen.S",
+
+                    "arch-arm/denver/bionic/memmove.S",
+                ],
+                exclude_srcs: [
+                    "arch-arm/generic/bionic/memcpy.S",
+                    "arch-arm/generic/bionic/memset.S",
+                    "arch-arm/generic/bionic/strcmp.S",
+                    "arch-arm/generic/bionic/strcpy.S",
+                    "arch-arm/generic/bionic/strlen.c",
+                    "bionic/__strcat_chk.cpp",
+                    "bionic/__strcpy_chk.cpp",
+                ],
+            },
+            cortex_a53_a57: {
+                srcs: [
+                    "arch-arm/cortex-a15/bionic/memcpy.S",
+                    "arch-arm/cortex-a15/bionic/memset.S",
+                    "arch-arm/cortex-a15/bionic/stpcpy.S",
+                    "arch-arm/cortex-a15/bionic/strcat.S",
+                    "arch-arm/cortex-a15/bionic/__strcat_chk.S",
+                    "arch-arm/cortex-a15/bionic/strcmp.S",
+                    "arch-arm/cortex-a15/bionic/strcpy.S",
+                    "arch-arm/cortex-a15/bionic/__strcpy_chk.S",
+                    "arch-arm/cortex-a15/bionic/strlen.S",
+
+                    "arch-arm/denver/bionic/memmove.S",
+                ],
+                exclude_srcs: [
+                    "arch-arm/generic/bionic/memcpy.S",
+                    "arch-arm/generic/bionic/memset.S",
+                    "arch-arm/generic/bionic/strcmp.S",
+                    "arch-arm/generic/bionic/strcpy.S",
+                    "arch-arm/generic/bionic/strlen.c",
+                    "bionic/__strcat_chk.cpp",
+                    "bionic/__strcpy_chk.cpp",
+                ],
+            },
+            cortex_a8: {
+                srcs: [
+                    "arch-arm/cortex-a15/bionic/memcpy.S",
+                    "arch-arm/cortex-a15/bionic/memset.S",
+                    "arch-arm/cortex-a15/bionic/stpcpy.S",
+                    "arch-arm/cortex-a15/bionic/strcat.S",
+                    "arch-arm/cortex-a15/bionic/__strcat_chk.S",
+                    "arch-arm/cortex-a15/bionic/strcmp.S",
+                    "arch-arm/cortex-a15/bionic/strcpy.S",
+                    "arch-arm/cortex-a15/bionic/__strcpy_chk.S",
+                    "arch-arm/cortex-a15/bionic/strlen.S",
+
+                    "arch-arm/denver/bionic/memmove.S",
+                ],
+                exclude_srcs: [
+                    "arch-arm/generic/bionic/memcpy.S",
+                    "arch-arm/generic/bionic/memset.S",
+                    "arch-arm/generic/bionic/strcmp.S",
+                    "arch-arm/generic/bionic/strcpy.S",
+                    "arch-arm/generic/bionic/strlen.c",
+                    "bionic/__strcat_chk.cpp",
+                    "bionic/__strcpy_chk.cpp",
+                ],
+            },
+            cortex_a9: {
+                srcs: [
+                    "arch-arm/cortex-a9/bionic/memcpy.S",
+                    "arch-arm/cortex-a9/bionic/memset.S",
+                    "arch-arm/cortex-a9/bionic/stpcpy.S",
+                    "arch-arm/cortex-a9/bionic/strcat.S",
+                    "arch-arm/cortex-a9/bionic/__strcat_chk.S",
+                    "arch-arm/cortex-a9/bionic/strcmp.S",
+                    "arch-arm/cortex-a9/bionic/strcpy.S",
+                    "arch-arm/cortex-a9/bionic/__strcpy_chk.S",
+                    "arch-arm/cortex-a9/bionic/strlen.S",
+
+                    "arch-arm/denver/bionic/memmove.S",
+                ],
+                exclude_srcs: [
+                    "arch-arm/generic/bionic/memcpy.S",
+                    "arch-arm/generic/bionic/memset.S",
+                    "arch-arm/generic/bionic/strcmp.S",
+                    "arch-arm/generic/bionic/strcpy.S",
+                    "arch-arm/generic/bionic/strlen.c",
+                    "bionic/__strcat_chk.cpp",
+                    "bionic/__strcpy_chk.cpp",
+                ],
+            },
+            cortex_a15: {
+                srcs: [
+                    "arch-arm/cortex-a15/bionic/memcpy.S",
+                    "arch-arm/cortex-a15/bionic/memset.S",
+                    "arch-arm/cortex-a15/bionic/stpcpy.S",
+                    "arch-arm/cortex-a15/bionic/strcat.S",
+                    "arch-arm/cortex-a15/bionic/__strcat_chk.S",
+                    "arch-arm/cortex-a15/bionic/strcmp.S",
+                    "arch-arm/cortex-a15/bionic/strcpy.S",
+                    "arch-arm/cortex-a15/bionic/__strcpy_chk.S",
+                    "arch-arm/cortex-a15/bionic/strlen.S",
+
+                    "arch-arm/denver/bionic/memmove.S",
+                ],
+                exclude_srcs: [
+                    "arch-arm/generic/bionic/memcpy.S",
+                    "arch-arm/generic/bionic/memset.S",
+                    "arch-arm/generic/bionic/strcmp.S",
+                    "arch-arm/generic/bionic/strcpy.S",
+                    "arch-arm/generic/bionic/strlen.c",
+                    "bionic/__strcat_chk.cpp",
+                    "bionic/__strcpy_chk.cpp",
+                ],
+            },
+            denver: {
+                srcs: [
+                    "arch-arm/denver/bionic/memcpy.S",
+                    "arch-arm/denver/bionic/memmove.S",
+                    "arch-arm/denver/bionic/memset.S",
+                    "arch-arm/denver/bionic/__strcat_chk.S",
+                    "arch-arm/denver/bionic/__strcpy_chk.S",
+
+                    // Use cortex-a15 versions of strcat/strcpy/strlen.
+                    "arch-arm/cortex-a15/bionic/stpcpy.S",
+                    "arch-arm/cortex-a15/bionic/strcat.S",
+                    "arch-arm/cortex-a15/bionic/strcmp.S",
+                    "arch-arm/cortex-a15/bionic/strcpy.S",
+                    "arch-arm/cortex-a15/bionic/strlen.S",
+                ],
+                exclude_srcs: [
+                    "arch-arm/generic/bionic/memcpy.S",
+                    "arch-arm/generic/bionic/memset.S",
+                    "arch-arm/generic/bionic/strcmp.S",
+                    "arch-arm/generic/bionic/strcpy.S",
+                    "arch-arm/generic/bionic/strlen.c",
+                    "bionic/__strcat_chk.cpp",
+                    "bionic/__strcpy_chk.cpp",
+                ],
+            },
+            krait: {
+                srcs: [
+                    "arch-arm/krait/bionic/memcpy.S",
+                    "arch-arm/krait/bionic/memset.S",
+                    "arch-arm/krait/bionic/strcmp.S",
+                    "arch-arm/krait/bionic/__strcat_chk.S",
+                    "arch-arm/krait/bionic/__strcpy_chk.S",
+
+                    // Use cortex-a15 versions of strcat/strcpy/strlen.
+                    "arch-arm/cortex-a15/bionic/stpcpy.S",
+                    "arch-arm/cortex-a15/bionic/strcat.S",
+                    "arch-arm/cortex-a15/bionic/strcpy.S",
+                    "arch-arm/cortex-a15/bionic/strlen.S",
+
+                    "arch-arm/denver/bionic/memmove.S",
+                ],
+                exclude_srcs: [
+                    "arch-arm/generic/bionic/memcpy.S",
+                    "arch-arm/generic/bionic/memset.S",
+                    "arch-arm/generic/bionic/strcmp.S",
+                    "arch-arm/generic/bionic/strcpy.S",
+                    "arch-arm/generic/bionic/strlen.c",
+                    "bionic/__strcat_chk.cpp",
+                    "bionic/__strcpy_chk.cpp",
+                ],
+            },
+
+        },
+        arm64: {
+            srcs: [
+                "arch-arm64/bionic/__bionic_clone.S",
+                "arch-arm64/bionic/_exit_with_stack_teardown.S",
+                "arch-arm64/bionic/setjmp.S",
+                "arch-arm64/bionic/__set_tls.c",
+                "arch-arm64/bionic/syscall.S",
+                "arch-arm64/bionic/vfork.S",
+
+                "arch-arm64/generic/bionic/memchr.S",
+                "arch-arm64/generic/bionic/memcmp.S",
+                "arch-arm64/generic/bionic/memcpy.S",
+                "arch-arm64/generic/bionic/memmove.S",
+                "arch-arm64/generic/bionic/memset.S",
+                "arch-arm64/generic/bionic/stpcpy.S",
+                "arch-arm64/generic/bionic/strchr.S",
+                "arch-arm64/generic/bionic/strcmp.S",
+                "arch-arm64/generic/bionic/strcpy.S",
+                "arch-arm64/generic/bionic/strlen.S",
+                "arch-arm64/generic/bionic/strncmp.S",
+                "arch-arm64/generic/bionic/strnlen.S",
+                "arch-arm64/generic/bionic/wmemmove.S",
+            ],
+            exclude_srcs: [
+                "bionic/__memcpy_chk.cpp",
+                "bionic/strchr.cpp",
+                "bionic/strnlen.c",
+            ],
+            denver64: {
+                srcs: [
+                    "arch-arm64/denver64/bionic/memcpy.S",
+                    "arch-arm64/denver64/bionic/memset.S",
+                ],
+                exclude_srcs: [
+                    "arch-arm64/generic/bionic/memcpy.S",
+                    "arch-arm64/generic/bionic/memset.S",
+                ],
+            },
+        },
+
+        mips: {
+            srcs: [
+                "arch-mips/bionic/__bionic_clone.S",
+                "arch-mips/bionic/bzero.S",
+                "arch-mips/bionic/cacheflush.cpp",
+                "arch-mips/bionic/_exit_with_stack_teardown.S",
+                "arch-mips/bionic/setjmp.S",
+                "arch-mips/bionic/syscall.S",
+                "arch-mips/bionic/vfork.S",
+
+                "arch-mips/string/memcmp.c",
+                "arch-mips/string/memcpy.S",
+                "arch-mips/string/memset.S",
+                "arch-mips/string/strcmp.S",
+                "arch-mips/string/strlen.c",
+            ],
+            rev6: {
+                srcs: [
+                    "arch-mips/string/mips_strlen.c",
+                ],
+                exclude_srcs: [
+                    "arch-mips/string/strlen.c",
+                ],
+            },
+        },
+        mips64: {
+            srcs: [
+                "arch-mips64/bionic/__bionic_clone.S",
+                "arch-mips64/bionic/_exit_with_stack_teardown.S",
+                "arch-mips64/bionic/setjmp.S",
+                "arch-mips64/bionic/syscall.S",
+                "arch-mips64/bionic/vfork.S",
+                "arch-mips64/bionic/stat.cpp",
+
+                "arch-mips/string/memcmp.c",
+                "arch-mips/string/memcpy.S",
+                "arch-mips/string/memset.S",
+                "arch-mips/string/strcmp.S",
+                "arch-mips/string/strlen.c",
+            ],
+        },
+
+        x86: {
+            srcs: [
+                "arch-x86/bionic/__bionic_clone.S",
+                "arch-x86/bionic/_exit_with_stack_teardown.S",
+                "arch-x86/bionic/libgcc_compat.c",
+                "arch-x86/bionic/__restore.S",
+                "arch-x86/bionic/setjmp.S",
+                "arch-x86/bionic/__set_tls.c",
+                "arch-x86/bionic/syscall.S",
+                "arch-x86/bionic/vfork.S",
+
+                "arch-x86/generic/string/memcmp.S",
+                "arch-x86/generic/string/strcmp.S",
+                "arch-x86/generic/string/strncmp.S",
+                "arch-x86/generic/string/strcat.S",
+                "arch-x86/atom/string/sse2-memchr-atom.S",
+                "arch-x86/atom/string/sse2-memrchr-atom.S",
+                "arch-x86/atom/string/sse2-strchr-atom.S",
+                "arch-x86/atom/string/sse2-strnlen-atom.S",
+                "arch-x86/atom/string/sse2-strrchr-atom.S",
+                "arch-x86/atom/string/sse2-wcschr-atom.S",
+                "arch-x86/atom/string/sse2-wcsrchr-atom.S",
+                "arch-x86/atom/string/sse2-wcslen-atom.S",
+                "arch-x86/atom/string/sse2-wcscmp-atom.S",
+                "arch-x86/silvermont/string/sse2-bcopy-slm.S",
+                "arch-x86/silvermont/string/sse2-bzero-slm.S",
+                "arch-x86/silvermont/string/sse2-memcpy-slm.S",
+                "arch-x86/silvermont/string/sse2-memmove-slm.S",
+                "arch-x86/silvermont/string/sse2-memset-slm.S",
+                "arch-x86/silvermont/string/sse2-stpcpy-slm.S",
+                "arch-x86/silvermont/string/sse2-stpncpy-slm.S",
+                "arch-x86/silvermont/string/sse2-strcpy-slm.S",
+                "arch-x86/silvermont/string/sse2-strlen-slm.S",
+                "arch-x86/silvermont/string/sse2-strncpy-slm.S",
+            ],
+
+            exclude_srcs: [
+                "bionic/strchr.cpp",
+                "bionic/strnlen.c",
+                "bionic/strrchr.cpp",
+            ],
+            atom: {
+                srcs: [
+                    "arch-x86/atom/string/sse2-bzero-atom.S",
+                    "arch-x86/atom/string/sse2-memset-atom.S",
+                    "arch-x86/atom/string/sse2-strlen-atom.S",
+                    "arch-x86/atom/string/ssse3-bcopy-atom.S",
+                    "arch-x86/atom/string/ssse3-memcmp-atom.S",
+                    "arch-x86/atom/string/ssse3-memmove-atom.S",
+                    "arch-x86/atom/string/ssse3-strncpy-atom.S",
+                    "arch-x86/atom/string/ssse3-wmemcmp-atom.S",
+                ],
+                exclude_srcs: [
+                    "arch-x86/generic/string/memcmp.S",
+                    "arch-x86/silvermont/string/sse2-bcopy-slm.S",
+                    "arch-x86/silvermont/string/sse2-bzero-slm.S",
+                    "arch-x86/silvermont/string/sse2-memcpy-slm.S",
+                    "arch-x86/silvermont/string/sse2-memmove-slm.S",
+                    "arch-x86/silvermont/string/sse2-memset-slm.S",
+                    "arch-x86/silvermont/string/sse2-strcpy-slm.S",
+                    "arch-x86/silvermont/string/sse2-strlen-slm.S",
+                    "arch-x86/silvermont/string/sse2-strncpy-slm.S",
+                ],
+            },
+            ssse3: {
+                srcs: [
+                    "arch-x86/atom/string/ssse3-strncat-atom.S",
+                    "arch-x86/atom/string/ssse3-strlcat-atom.S",
+                    "arch-x86/atom/string/ssse3-strlcpy-atom.S",
+                    "arch-x86/atom/string/ssse3-strcat-atom.S",
+                    "arch-x86/atom/string/ssse3-strcmp-atom.S",
+                    "arch-x86/atom/string/ssse3-strncmp-atom.S",
+                    "arch-x86/atom/string/ssse3-wcscat-atom.S",
+                    "arch-x86/atom/string/ssse3-wcscpy-atom.S",
+                ],
+                exclude_srcs: [
+                    "arch-x86/generic/string/strcmp.S",
+                    "arch-x86/generic/string/strncmp.S",
+                    "arch-x86/generic/string/strcat.S",
+                ],
+            },
+            sse4: {
+                srcs: [
+                    "arch-x86/silvermont/string/sse4-memcmp-slm.S",
+                    "arch-x86/silvermont/string/sse4-wmemcmp-slm.S",
+                ],
+                exclude_srcs: [
+                    "arch-x86/generic/string/memcmp.S",
+                ],
+            },
+        },
         x86_64: {
-            clang: use_clang_x86_64,
+            srcs: [
+                "arch-x86_64/bionic/__bionic_clone.S",
+                "arch-x86_64/bionic/_exit_with_stack_teardown.S",
+                "arch-x86_64/bionic/__restore_rt.S",
+                "arch-x86_64/bionic/setjmp.S",
+                "arch-x86_64/bionic/__set_tls.c",
+                "arch-x86_64/bionic/syscall.S",
+                "arch-x86_64/bionic/vfork.S",
+
+                "arch-x86_64/string/sse2-memcpy-slm.S",
+                "arch-x86_64/string/sse2-memmove-slm.S",
+                "arch-x86_64/string/sse2-memset-slm.S",
+                "arch-x86_64/string/sse2-stpcpy-slm.S",
+                "arch-x86_64/string/sse2-stpncpy-slm.S",
+                "arch-x86_64/string/sse2-strcat-slm.S",
+                "arch-x86_64/string/sse2-strcpy-slm.S",
+                "arch-x86_64/string/sse2-strlcat-slm.S",
+                "arch-x86_64/string/sse2-strlcpy-slm.S",
+                "arch-x86_64/string/sse2-strlen-slm.S",
+                "arch-x86_64/string/sse2-strncat-slm.S",
+                "arch-x86_64/string/sse2-strncpy-slm.S",
+                "arch-x86_64/string/sse4-memcmp-slm.S",
+                "arch-x86_64/string/ssse3-strcmp-slm.S",
+                "arch-x86_64/string/ssse3-strncmp-slm.S",
+            ],
         },
     },
 
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags + ["-Wold-style-cast"],
-    product_variables: libc_common_product_variables,
+    cppflags: ["-Wold-style-cast"],
     include_dirs: ["bionic/libstdc++/include"],
     name: "libc_bionic",
-
-    stl: "none",
-    system_shared_libs: [],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
-
 }
 
 // ========================================================
@@ -961,11 +1297,13 @@
 // or constructors).
 // ========================================================
 cc_library_static {
+    defaults: ["libc_defaults"],
     srcs: [
         "bionic/abort.cpp",
         "bionic/accept.cpp",
         "bionic/accept4.cpp",
         "bionic/access.cpp",
+        "bionic/arpa_inet.cpp",
         "bionic/assert.cpp",
         "bionic/atof.cpp",
         "bionic/bionic_systrace.cpp",
@@ -1011,6 +1349,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",
@@ -1030,6 +1369,7 @@
         "bionic/mkfifo.cpp",
         "bionic/mknod.cpp",
         "bionic/mntent.cpp",
+        "bionic/mremap.cpp",
         "bionic/NetdClientDispatch.cpp",
         "bionic/open.cpp",
         "bionic/pathconf.cpp",
@@ -1099,433 +1439,20 @@
         "bionic/wchar.cpp",
         "bionic/wctype.cpp",
         "bionic/wmempcpy.cpp",
-
-        // May be overriden by per-arch optimized versions
-        "bionic/__memcpy_chk.cpp",
-        "bionic/__memset_chk.cpp",
-        "bionic/__strcat_chk.cpp",
-        "bionic/__strcpy_chk.cpp",
-        "bionic/strchr.cpp",
-        "bionic/strnlen.c",
-        "bionic/strrchr.cpp",
     ],
-    cflags: libc_common_cflags + ["-Wframe-larger-than=2048"],
+    cflags: ["-Wframe-larger-than=2048"],
 
-    arch: {
-
-        arm: {
-            srcs: [
-                "arch-arm/bionic/abort_arm.S",
-                "arch-arm/bionic/atomics_arm.c",
-                "arch-arm/bionic/__bionic_clone.S",
-                "arch-arm/bionic/_exit_with_stack_teardown.S",
-                "arch-arm/bionic/libgcc_compat.c",
-                "arch-arm/bionic/popcount_tab.c",
-                "arch-arm/bionic/__restore.S",
-                "arch-arm/bionic/setjmp.S",
-                "arch-arm/bionic/syscall.S",
-                "arch-arm/bionic/vfork.S",
-
-                "arch-arm/generic/bionic/memcmp.S",
-                "arch-arm/generic/bionic/memcpy.S",
-                "arch-arm/generic/bionic/memset.S",
-                "arch-arm/generic/bionic/strcmp.S",
-                "arch-arm/generic/bionic/strcpy.S",
-                "arch-arm/generic/bionic/strlen.c",
-            ],
-            exclude_srcs: [
-                "bionic/__memcpy_chk.cpp",
-                "bionic/__memset_chk.cpp",
-            ],
-        },
-        cortex_a7: {
-            srcs: [
-                "arch-arm/cortex-a7/bionic/memset.S",
-
-                "arch-arm/cortex-a15/bionic/memcpy.S",
-                "arch-arm/cortex-a15/bionic/stpcpy.S",
-                "arch-arm/cortex-a15/bionic/strcat.S",
-                "arch-arm/cortex-a15/bionic/__strcat_chk.S",
-                "arch-arm/cortex-a15/bionic/strcmp.S",
-                "arch-arm/cortex-a15/bionic/strcpy.S",
-                "arch-arm/cortex-a15/bionic/__strcpy_chk.S",
-                "arch-arm/cortex-a15/bionic/strlen.S",
-
-                "arch-arm/denver/bionic/memmove.S",
-            ],
-            exclude_srcs: [
-                "arch-arm/generic/bionic/memcpy.S",
-                "arch-arm/generic/bionic/memset.S",
-                "arch-arm/generic/bionic/strcmp.S",
-                "arch-arm/generic/bionic/strcpy.S",
-                "arch-arm/generic/bionic/strlen.c",
-                "bionic/__strcat_chk.cpp",
-                "bionic/__strcpy_chk.cpp",
-            ],
-        },
-        cortex_a53: {
-            srcs: [
-                "arch-arm/cortex-a53/bionic/memcpy.S",
-                "arch-arm/cortex-a53/bionic/__strcat_chk.S",
-                "arch-arm/cortex-a53/bionic/__strcpy_chk.S",
-
-                "arch-arm/cortex-a7/bionic/memset.S",
-
-                "arch-arm/cortex-a15/bionic/stpcpy.S",
-                "arch-arm/cortex-a15/bionic/strcat.S",
-                "arch-arm/cortex-a15/bionic/strcmp.S",
-                "arch-arm/cortex-a15/bionic/strcpy.S",
-                "arch-arm/cortex-a15/bionic/strlen.S",
-
-                "arch-arm/denver/bionic/memmove.S",
-            ],
-            exclude_srcs: [
-                "arch-arm/generic/bionic/memcpy.S",
-                "arch-arm/generic/bionic/memset.S",
-                "arch-arm/generic/bionic/strcmp.S",
-                "arch-arm/generic/bionic/strcpy.S",
-                "arch-arm/generic/bionic/strlen.c",
-                "bionic/__strcat_chk.cpp",
-                "bionic/__strcpy_chk.cpp",
-            ],
-        },
-        cortex_a8: {
-            srcs: [
-                "arch-arm/cortex-a15/bionic/memcpy.S",
-                "arch-arm/cortex-a15/bionic/memset.S",
-                "arch-arm/cortex-a15/bionic/stpcpy.S",
-                "arch-arm/cortex-a15/bionic/strcat.S",
-                "arch-arm/cortex-a15/bionic/__strcat_chk.S",
-                "arch-arm/cortex-a15/bionic/strcmp.S",
-                "arch-arm/cortex-a15/bionic/strcpy.S",
-                "arch-arm/cortex-a15/bionic/__strcpy_chk.S",
-                "arch-arm/cortex-a15/bionic/strlen.S",
-
-                "arch-arm/denver/bionic/memmove.S",
-            ],
-            exclude_srcs: [
-                "arch-arm/generic/bionic/memcpy.S",
-                "arch-arm/generic/bionic/memset.S",
-                "arch-arm/generic/bionic/strcmp.S",
-                "arch-arm/generic/bionic/strcpy.S",
-                "arch-arm/generic/bionic/strlen.c",
-                "bionic/__strcat_chk.cpp",
-                "bionic/__strcpy_chk.cpp",
-            ],
-        },
-        cortex_a9: {
-            srcs: [
-                "arch-arm/cortex-a9/bionic/memcpy.S",
-                "arch-arm/cortex-a9/bionic/memset.S",
-                "arch-arm/cortex-a9/bionic/stpcpy.S",
-                "arch-arm/cortex-a9/bionic/strcat.S",
-                "arch-arm/cortex-a9/bionic/__strcat_chk.S",
-                "arch-arm/cortex-a9/bionic/strcmp.S",
-                "arch-arm/cortex-a9/bionic/strcpy.S",
-                "arch-arm/cortex-a9/bionic/__strcpy_chk.S",
-                "arch-arm/cortex-a9/bionic/strlen.S",
-
-                "arch-arm/denver/bionic/memmove.S",
-            ],
-            exclude_srcs: [
-                "arch-arm/generic/bionic/memcpy.S",
-                "arch-arm/generic/bionic/memset.S",
-                "arch-arm/generic/bionic/strcmp.S",
-                "arch-arm/generic/bionic/strcpy.S",
-                "arch-arm/generic/bionic/strlen.c",
-                "bionic/__strcat_chk.cpp",
-                "bionic/__strcpy_chk.cpp",
-            ],
-        },
-        cortex_a15: {
-            srcs: [
-                "arch-arm/cortex-a15/bionic/memcpy.S",
-                "arch-arm/cortex-a15/bionic/memset.S",
-                "arch-arm/cortex-a15/bionic/stpcpy.S",
-                "arch-arm/cortex-a15/bionic/strcat.S",
-                "arch-arm/cortex-a15/bionic/__strcat_chk.S",
-                "arch-arm/cortex-a15/bionic/strcmp.S",
-                "arch-arm/cortex-a15/bionic/strcpy.S",
-                "arch-arm/cortex-a15/bionic/__strcpy_chk.S",
-                "arch-arm/cortex-a15/bionic/strlen.S",
-
-                "arch-arm/denver/bionic/memmove.S",
-            ],
-            exclude_srcs: [
-                "arch-arm/generic/bionic/memcpy.S",
-                "arch-arm/generic/bionic/memset.S",
-                "arch-arm/generic/bionic/strcmp.S",
-                "arch-arm/generic/bionic/strcpy.S",
-                "arch-arm/generic/bionic/strlen.c",
-                "bionic/__strcat_chk.cpp",
-                "bionic/__strcpy_chk.cpp",
-            ],
-        },
-        denver: {
-            srcs: [
-                "arch-arm/denver/bionic/memcpy.S",
-                "arch-arm/denver/bionic/memmove.S",
-                "arch-arm/denver/bionic/memset.S",
-                "arch-arm/denver/bionic/__strcat_chk.S",
-                "arch-arm/denver/bionic/__strcpy_chk.S",
-
-                // Use cortex-a15 versions of strcat/strcpy/strlen.
-                "arch-arm/cortex-a15/bionic/stpcpy.S",
-                "arch-arm/cortex-a15/bionic/strcat.S",
-                "arch-arm/cortex-a15/bionic/strcmp.S",
-                "arch-arm/cortex-a15/bionic/strcpy.S",
-                "arch-arm/cortex-a15/bionic/strlen.S",
-            ],
-            exclude_srcs: [
-                "arch-arm/generic/bionic/memcpy.S",
-                "arch-arm/generic/bionic/memset.S",
-                "arch-arm/generic/bionic/strcmp.S",
-                "arch-arm/generic/bionic/strcpy.S",
-                "arch-arm/generic/bionic/strlen.c",
-                "bionic/__strcat_chk.cpp",
-                "bionic/__strcpy_chk.cpp",
-            ],
-        },
-        krait: {
-            srcs: [
-                "arch-arm/krait/bionic/memcpy.S",
-                "arch-arm/krait/bionic/memset.S",
-                "arch-arm/krait/bionic/strcmp.S",
-                "arch-arm/krait/bionic/__strcat_chk.S",
-                "arch-arm/krait/bionic/__strcpy_chk.S",
-
-                // Use cortex-a15 versions of strcat/strcpy/strlen.
-                "arch-arm/cortex-a15/bionic/stpcpy.S",
-                "arch-arm/cortex-a15/bionic/strcat.S",
-                "arch-arm/cortex-a15/bionic/strcpy.S",
-                "arch-arm/cortex-a15/bionic/strlen.S",
-
-                "arch-arm/denver/bionic/memmove.S",
-            ],
-            exclude_srcs: [
-                "arch-arm/generic/bionic/memcpy.S",
-                "arch-arm/generic/bionic/memset.S",
-                "arch-arm/generic/bionic/strcmp.S",
-                "arch-arm/generic/bionic/strcpy.S",
-                "arch-arm/generic/bionic/strlen.c",
-                "bionic/__strcat_chk.cpp",
-                "bionic/__strcpy_chk.cpp",
-            ],
-        },
-
-        arm64: {
-            srcs: [
-                "arch-arm64/bionic/__bionic_clone.S",
-                "arch-arm64/bionic/_exit_with_stack_teardown.S",
-                "arch-arm64/bionic/setjmp.S",
-                "arch-arm64/bionic/__set_tls.c",
-                "arch-arm64/bionic/syscall.S",
-                "arch-arm64/bionic/vfork.S",
-
-                "arch-arm64/generic/bionic/memchr.S",
-                "arch-arm64/generic/bionic/memcmp.S",
-                "arch-arm64/generic/bionic/memcpy.S",
-                "arch-arm64/generic/bionic/memmove.S",
-                "arch-arm64/generic/bionic/memset.S",
-                "arch-arm64/generic/bionic/stpcpy.S",
-                "arch-arm64/generic/bionic/strchr.S",
-                "arch-arm64/generic/bionic/strcmp.S",
-                "arch-arm64/generic/bionic/strcpy.S",
-                "arch-arm64/generic/bionic/strlen.S",
-                "arch-arm64/generic/bionic/strncmp.S",
-                "arch-arm64/generic/bionic/strnlen.S",
-                "arch-arm64/generic/bionic/wmemmove.S",
-            ],
-            exclude_srcs: [
-                "bionic/__memcpy_chk.cpp",
-                "bionic/strchr.cpp",
-                "bionic/strnlen.c",
-            ],
-        },
-        denver64: {
-            srcs: [
-                "arch-arm64/denver64/bionic/memcpy.S",
-                "arch-arm64/denver64/bionic/memset.S",
-            ],
-            exclude_srcs: [
-                "arch-arm64/generic/bionic/memcpy.S",
-                "arch-arm64/generic/bionic/memset.S",
-            ],
-        },
-
-        mips: {
-            srcs: [
-                "arch-mips/bionic/__bionic_clone.S",
-                "arch-mips/bionic/bzero.S",
-                "arch-mips/bionic/cacheflush.cpp",
-                "arch-mips/bionic/_exit_with_stack_teardown.S",
-                "arch-mips/bionic/setjmp.S",
-                "arch-mips/bionic/syscall.S",
-                "arch-mips/bionic/vfork.S",
-
-                "arch-mips/string/memcmp.c",
-                "arch-mips/string/memcpy.S",
-                "arch-mips/string/memset.S",
-                "arch-mips/string/strcmp.S",
-                "arch-mips/string/strlen.c",
-            ],
-        },
-        mips_rev6: {
-            srcs: [
-                "arch-mips/string/mips_strlen.c"
-            ],
-            exclude_srcs: [
-                "arch-mips/string/strlen.c"
-            ],
-        },
-
-        mips64: {
-            srcs: [
-                "arch-mips64/bionic/__bionic_clone.S",
-                "arch-mips64/bionic/_exit_with_stack_teardown.S",
-                "arch-mips64/bionic/setjmp.S",
-                "arch-mips64/bionic/syscall.S",
-                "arch-mips64/bionic/vfork.S",
-                "arch-mips64/bionic/stat.cpp",
-
-                "arch-mips/string/memcmp.c",
-                "arch-mips/string/memcpy.S",
-                "arch-mips/string/memset.S",
-                "arch-mips/string/strcmp.S",
-                "arch-mips/string/strlen.c",
-            ],
-        },
-
-        x86: {
-            srcs: [
-                "arch-x86/bionic/__bionic_clone.S",
-                "arch-x86/bionic/_exit_with_stack_teardown.S",
-                "arch-x86/bionic/libgcc_compat.c",
-                "arch-x86/bionic/__restore.S",
-                "arch-x86/bionic/setjmp.S",
-                "arch-x86/bionic/__set_tls.c",
-                "arch-x86/bionic/syscall.S",
-                "arch-x86/bionic/vfork.S",
-
-                "arch-x86/generic/string/memcmp.S",
-                "arch-x86/generic/string/strcmp.S",
-                "arch-x86/generic/string/strncmp.S",
-                "arch-x86/generic/string/strcat.S",
-                "arch-x86/atom/string/sse2-memchr-atom.S",
-                "arch-x86/atom/string/sse2-memrchr-atom.S",
-                "arch-x86/atom/string/sse2-strchr-atom.S",
-                "arch-x86/atom/string/sse2-strnlen-atom.S",
-                "arch-x86/atom/string/sse2-strrchr-atom.S",
-                "arch-x86/atom/string/sse2-wcschr-atom.S",
-                "arch-x86/atom/string/sse2-wcsrchr-atom.S",
-                "arch-x86/atom/string/sse2-wcslen-atom.S",
-                "arch-x86/atom/string/sse2-wcscmp-atom.S",
-                "arch-x86/silvermont/string/sse2-bcopy-slm.S",
-                "arch-x86/silvermont/string/sse2-bzero-slm.S",
-                "arch-x86/silvermont/string/sse2-memcpy-slm.S",
-                "arch-x86/silvermont/string/sse2-memmove-slm.S",
-                "arch-x86/silvermont/string/sse2-memset-slm.S",
-                "arch-x86/silvermont/string/sse2-stpcpy-slm.S",
-                "arch-x86/silvermont/string/sse2-stpncpy-slm.S",
-                "arch-x86/silvermont/string/sse2-strcpy-slm.S",
-                "arch-x86/silvermont/string/sse2-strlen-slm.S",
-                "arch-x86/silvermont/string/sse2-strncpy-slm.S",
-            ],
-        },
-        x86_sse3: {
-            srcs: [
-                "arch-x86/atom/string/ssse3-strncat-atom.S",
-                "arch-x86/atom/string/ssse3-strlcat-atom.S",
-                "arch-x86/atom/string/ssse3-strlcpy-atom.S",
-                "arch-x86/atom/string/ssse3-strcmp-atom.S",
-                "arch-x86/atom/string/ssse3-strncmp-atom.S",
-                "arch-x86/atom/string/ssse3-strcat-atom.S",
-                "arch-x86/atom/string/ssse3-wcscat-atom.S",
-                "arch-x86/atom/string/ssse3-wcscpy-atom.S",
-            ],
-            exclude_srcs: [
-                "arch-x86/generic/string/strcmp.S",
-                "arch-x86/generic/string/strncmp.S",
-                "arch-x86/generic/string/strcat.S",
-            ],
-        },
-        x86_sse4: {
-            srcs: [
-                "arch-x86/silvermont/string/sse4-memcmp-slm.S",
-                "arch-x86/silvermont/string/sse4-wmemcmp-slm.S",
-            ],
-            exclude_srcs: [
-                "arch-x86/generic/string/memcmp.S",
-            ],
-        },
-
-
-        x86_64: {
-            clang: use_clang_x86_64,
-            srcs: [
-                "arch-x86_64/bionic/__bionic_clone.S",
-                "arch-x86_64/bionic/_exit_with_stack_teardown.S",
-                "arch-x86_64/bionic/__restore_rt.S",
-                "arch-x86_64/bionic/setjmp.S",
-                "arch-x86_64/bionic/__set_tls.c",
-                "arch-x86_64/bionic/syscall.S",
-                "arch-x86_64/bionic/vfork.S",
-
-                "arch-x86_64/string/sse2-memcpy-slm.S",
-                "arch-x86_64/string/sse2-memmove-slm.S",
-                "arch-x86_64/string/sse2-memset-slm.S",
-                "arch-x86_64/string/sse2-stpcpy-slm.S",
-                "arch-x86_64/string/sse2-stpncpy-slm.S",
-                "arch-x86_64/string/sse2-strcat-slm.S",
-                "arch-x86_64/string/sse2-strcpy-slm.S",
-                "arch-x86_64/string/sse2-strlcat-slm.S",
-                "arch-x86_64/string/sse2-strlcpy-slm.S",
-                "arch-x86_64/string/sse2-strlen-slm.S",
-                "arch-x86_64/string/sse2-strncat-slm.S",
-                "arch-x86_64/string/sse2-strncpy-slm.S",
-                "arch-x86_64/string/sse4-memcmp-slm.S",
-                "arch-x86_64/string/ssse3-strcmp-slm.S",
-                "arch-x86_64/string/ssse3-strncmp-slm.S",
-            ],
-        },
-
-    },
     multilib: {
         lib32: {
             // LP32 cruft
-            srcs: ["bionic/mmap.cpp"]
+            srcs: ["bionic/mmap.cpp"],
         },
     },
 
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags + ["-Wold-style-cast"],
-    product_variables: libc_common_product_variables,
+    cppflags: ["-Wold-style-cast"],
     local_include_dirs: ["stdio"],
     include_dirs: ["bionic/libstdc++/include"],
     name: "libc_bionic_ndk",
-
-    stl: "none",
-    system_shared_libs: [],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
-
-}
-
-cc_library_static {
-    name: "libc_thread_atexit_impl",
-    srcs: ["bionic/__cxa_thread_atexit_impl.cpp"],
-    cflags: libc_common_cflags + ["-Wframe-larger-than=2048"],
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags + ["-Wold-style-cast"],
-    product_variables: libc_common_product_variables,
-    include_dirs: ["bionic/libstdc++/include"],
-    // TODO: Clang tries to use __tls_get_addr which is not supported yet
-    // remove after it is implemented.
-    clang: false,
-    stl: "none",
-    system_shared_libs: [],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
 }
 
 // ========================================================
@@ -1537,9 +1464,11 @@
 // ========================================================
 
 cc_library_static {
+    defaults: ["libc_defaults"],
     srcs: [
         "bionic/pthread_atfork.cpp",
         "bionic/pthread_attr.cpp",
+        "bionic/pthread_barrier.cpp",
         "bionic/pthread_cond.cpp",
         "bionic/pthread_create.cpp",
         "bionic/pthread_detach.cpp",
@@ -1559,25 +1488,13 @@
         "bionic/pthread_setname_np.cpp",
         "bionic/pthread_setschedparam.cpp",
         "bionic/pthread_sigmask.cpp",
+        "bionic/pthread_spinlock.cpp",
     ],
-    cflags: libc_common_cflags + ["-Wframe-larger-than=2048"],
+    cflags: ["-Wframe-larger-than=2048"],
 
-    arch: {
-        x86_64: {
-            clang: use_clang_x86_64,
-        },
-    },
-
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags + ["-Wold-style-cast"],
-    product_variables: libc_common_product_variables,
+    cppflags: ["-Wold-style-cast"],
     include_dirs: ["bionic/libstdc++/include"],
     name: "libc_pthread",
-
-    stl: "none",
-    system_shared_libs: [],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
 }
 
 // ========================================================
@@ -1585,21 +1502,17 @@
 // ========================================================
 
 cc_library_static {
+    defaults: ["libc_defaults"],
     srcs: [
         "bionic/__cxa_guard.cpp",
         "bionic/__cxa_pure_virtual.cpp",
         "bionic/new.cpp",
     ],
-    cflags: libc_common_cflags + ["-fvisibility=hidden"],
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags,
+    cflags: ["-fvisibility=hidden"],
     include_dirs: ["bionic/libstdc++/include"],
     name: "libc_cxa",
     clang: true, // GCC refuses to hide new/delete
 
-    stl: "none",
-    system_shared_libs: [],
-    sanitize: ["never"],
     // b/17574078: Need to disable coverage until we have a prebuilt libprofile_rt.
     // Since this is a static library built with clang, it needs to link
     // libprofile_rt when it is linked into the final binary. Since the final binary
@@ -1632,16 +1545,10 @@
             srcs: ["arch-x86/syscalls/**/*.S"],
         },
         x86_64: {
-            clang: use_clang_x86_64,
             srcs: ["arch-x86_64/syscalls/**/*.S"],
         },
     },
     name: "libc_syscalls",
-
-    stl: "none",
-    system_shared_libs: [],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
 }
 
 // ========================================================
@@ -1652,22 +1559,14 @@
 // ========================================================
 
 cc_library_static {
+    defaults: ["libc_defaults"],
     arch: {
         arm: {
             srcs: ["arch-arm/bionic/__aeabi.c"],
         },
-        x86_64: {
-            clang: use_clang_x86_64,
-        },
     },
     name: "libc_aeabi",
-    cflags: libc_common_cflags + ["-fno-builtin"],
-    product_variables: libc_common_product_variables,
-
-    stl: "none",
-    system_shared_libs: [],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
+    cflags: ["-fno-builtin"],
 }
 
 // ========================================================
@@ -1684,6 +1583,7 @@
 
 cc_library_static {
     name: "libc_ndk",
+    defaults: ["libc_defaults"],
     srcs: libc_common_src_files + ["bionic/malloc_debug_common.cpp"],
     multilib: {
         lib32: {
@@ -1700,18 +1600,12 @@
             ],
             whole_static_libs: ["libc_aeabi"],
         },
-        x86_64: {
-            clang: use_clang_x86_64,
-        },
     },
 
-    cflags: libc_common_cflags + [
+    cflags: [
         "-fvisibility=hidden",
         "-DLIBC_STATIC",
     ],
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags,
-    product_variables: libc_common_product_variables,
 
     whole_static_libs: [
         "libc_bionic_ndk",
@@ -1726,14 +1620,6 @@
         "libc_tzcode",
         "libm",
     ],
-
-    stl: "none",
-    system_shared_libs: [],
-
-    // TODO: split out the asflags.
-    asflags: libc_common_cflags,
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
 }
 
 // ========================================================
@@ -1741,16 +1627,13 @@
 // ========================================================
 
 cc_library_static {
+    defaults: ["libc_defaults"],
     srcs: libc_common_src_files,
     multilib: {
         lib32: {
             srcs: libc_common_src_files_32,
         },
     },
-    cflags: libc_common_cflags,
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags,
-    product_variables: libc_common_product_variables,
     name: "libc_common",
 
     whole_static_libs: [
@@ -1767,7 +1650,6 @@
         "libc_pthread",
         "libc_stack_protector",
         "libc_syscalls",
-        "libc_thread_atexit_impl",
         "libc_tzcode",
     ],
 
@@ -1775,18 +1657,7 @@
         arm: {
             whole_static_libs: ["libc_aeabi"],
         },
-        x86_64: {
-            clang: use_clang_x86_64,
-        },
     },
-
-    stl: "none",
-    system_shared_libs: [],
-
-    // TODO: split out the asflags.
-    asflags: libc_common_cflags,
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
 }
 
 // ========================================================
@@ -1800,6 +1671,7 @@
 // dynamic linker.
 
 cc_library_static {
+    defaults: ["libc_defaults"],
     srcs: [
         "bionic/dl_iterate_phdr_static.cpp",
         "bionic/libc_init_static.cpp",
@@ -1809,32 +1681,21 @@
         arm: {
             srcs: ["arch-arm/bionic/exidx_static.c"],
         },
-        x86_64: {
-            clang: use_clang_x86_64,
-        },
     },
 
-    cflags: libc_common_cflags + ["-DLIBC_STATIC"],
-
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags,
-    product_variables: libc_common_product_variables,
+    cflags: ["-DLIBC_STATIC"],
 
     name: "libc_nomalloc",
 
     whole_static_libs: ["libc_common"],
-    stl: "none",
-    system_shared_libs: [],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
-
 }
 
 // ========================================================
 // libc_malloc.a: the _prefixed_ malloc functions (like dlcalloc).
 // ========================================================
 cc_library_static {
-    product_variables: libc_common_product_variables + {
+    defaults: ["libc_defaults"],
+    product_variables: {
         device_uses_jemalloc: {
             srcs: ["bionic/jemalloc_wrapper.cpp"],
             whole_static_libs: ["libjemalloc"],
@@ -1843,31 +1704,18 @@
             srcs: ["bionic/dlmalloc.c"],
         },
     },
-    cflags: libc_common_cflags + ["-fvisibility=hidden"],
+    cflags: ["-fvisibility=hidden"],
 
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags,
     name: "libc_malloc",
-    stl: "none",
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
-
-    arch: {
-        x86_64: {
-            clang: use_clang_x86_64,
-        },
-    },
 }
 
 // ========================================================
 // libc.a + libc.so
 // ========================================================
 cc_library {
+    defaults: ["libc_defaults"],
     name: "libc",
-    cflags: libc_common_cflags,
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags,
-    product_variables: libc_common_product_variables + {
+    product_variables: {
         platform_sdk_version: {
             asflags: ["-DPLATFORM_SDK_VERSION=%d"],
         },
@@ -1906,11 +1754,6 @@
 
     shared_libs: ["libdl"],
     whole_static_libs: ["libc_common"],
-    stl: "none",
-    system_shared_libs: [],
-
-    // Don't re-export new/delete and friends, even if the compiler really wants to.
-    version_script: "libc.map",
 
     // We'd really like to do this for all architectures, but since this wasn't done
     // before, these symbols must continue to be exported on LP32 for binary
@@ -1929,6 +1772,9 @@
             //TODO: This is to work around b/24465209. Remove after root cause is fixed
             ldflags: ["-Wl,--hash-style=both"],
 
+            // Don't re-export new/delete and friends, even if the compiler really wants to.
+            version_script: "libc.arm.map",
+
             shared: {
                 srcs: ["arch-arm/bionic/exidx_dynamic.c"],
             },
@@ -1940,16 +1786,30 @@
                 "arch-arm/bionic/atexit_legacy.c",
             ],
         },
+        arm64: {
+            // Don't re-export new/delete and friends, even if the compiler really wants to.
+            version_script: "libc.arm64.map",
+        },
+        mips: {
+            // Don't re-export new/delete and friends, even if the compiler really wants to.
+            version_script: "libc.mips.map",
+        },
+        mips64: {
+            // Don't re-export new/delete and friends, even if the compiler really wants to.
+            version_script: "libc.mips64.map",
+        },
         x86: {
             //TODO: This is to work around b/24465209. Remove after root cause is fixed
             ldflags: ["-Wl,--hash-style=both"],
+
+            // Don't re-export new/delete and friends, even if the compiler really wants to.
+            version_script: "libc.x86.map",
         },
         x86_64: {
-            clang: use_clang_x86_64,
+            // Don't re-export new/delete and friends, even if the compiler really wants to.
+            version_script: "libc.x86_64.map",
         },
     },
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
 }
 
 // For all builds, except for the -user build we will enable memory
@@ -1964,10 +1824,7 @@
 // libc_malloc_debug_leak.so
 // ========================================================
 cc_library_shared {
-    cflags: libc_common_cflags,
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags,
-    product_variables: libc_common_product_variables,
+    defaults: ["libc_defaults"],
 
     srcs: [
         "bionic/debug_backtrace.cpp",
@@ -1983,8 +1840,6 @@
         "libc",
         "libdl",
     ],
-    stl: "none",
-    system_shared_libs: [],
     // Only need this for arm since libc++ uses its own unwind code that
     // doesn't mix with the other default unwind code.
     arch: {
@@ -1995,9 +1850,6 @@
             ],
             ldflags: ["-Wl,--exclude-libs,libunwind_llvm.a"],
         },
-        x86_64: {
-            clang: use_clang_x86_64,
-        },
     },
     allow_undefined_symbols: true,
 
@@ -2009,19 +1861,14 @@
         "eng",
         "debug",
     ],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
 }
 
 // ========================================================
 // libc_malloc_debug_qemu.so
 // ========================================================
 cc_library_shared {
-    cflags: libc_common_cflags + ["-DMALLOC_QEMU_INSTRUMENT"],
-
-    conlyflags: libc_common_conlyflags,
-    cppflags: libc_common_cppflags,
-    product_variables: libc_common_product_variables,
+    defaults: ["libc_defaults"],
+    cflags: ["-DMALLOC_QEMU_INSTRUMENT"],
 
     srcs: [
         "bionic/libc_logging.cpp",
@@ -2034,8 +1881,6 @@
         "libc",
         "libdl",
     ],
-    stl: "none",
-    system_shared_libs: [],
 
     // Don't re-export new/delete and friends, even if the compiler really wants to.
     version_script: "version_script.txt",
@@ -2045,14 +1890,6 @@
         "eng",
         "debug",
     ],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
-
-    arch: {
-        x86_64: {
-            clang: use_clang_x86_64,
-        },
-    },
 }
 
 // ANDROIDMK TRANSLATION ERROR: unsupported directive
@@ -2063,10 +1900,8 @@
 // libstdc++.so + libstdc++.a
 // ========================================================
 cc_library {
+    defaults: ["libc_defaults"],
     include_dirs: ["bionic/libstdc++/include"],
-    cflags: libc_common_cflags,
-    cppflags: libc_common_cppflags,
-    product_variables: libc_common_product_variables,
     srcs: [
         "bionic/__cxa_guard.cpp",
         "bionic/__cxa_pure_virtual.cpp",
@@ -2074,11 +1909,7 @@
         "bionic/libc_logging.cpp",
     ],
     name: "libstdc++",
-
-    stl: "none",
     system_shared_libs: ["libc"],
-    sanitize: ["never"],
-    native_coverage: bionic_coverage,
 
     //TODO: This is to work around b/24465209. Remove after root cause is fixed
     arch: {
@@ -2091,46 +1922,50 @@
     },
 }
 
-crt_arch_flags = {
-    arm: {
-        local_include_dirs: ["arch-arm/include"],
-        cflags: ["-mthumb-interwork"],
+cc_defaults {
+    name: "crt_defaults",
+
+    no_default_compiler_flags: true,
+
+    arch: {
+        arm: {
+            local_include_dirs: ["arch-arm/include"],
+        },
+        arm64: {
+            local_include_dirs: ["arch-arm64/include"],
+        },
+        mips: {
+            local_include_dirs: ["arch-mips/include"],
+        },
+        mips64: {
+            local_include_dirs: ["arch-mips64/include"],
+        },
+        x86: {
+            local_include_dirs: ["arch-x86/include"],
+        },
+        x86_64: {
+            local_include_dirs: ["arch-x86_64/include"],
+        },
     },
-    arm64: {
-        local_include_dirs: ["arch-arm64/include"],
-    },
-    mips: {
-        local_include_dirs: ["arch-mips/include"],
-        ldflags: ["-melf32ltsmip"],
-    },
-    mips64: {
-        local_include_dirs: ["arch-mips64/include"],
-        ldflags: ["-melf64ltsmip"],
-    },
-    x86: {
-        cflags: ["-m32"],
-        ldflags: ["-melf_i386"],
-        local_include_dirs: ["arch-x86/include"],
-    },
-    x86_64: {
-        cflags: ["-m64"],
-        ldflags: ["-melf_x86_64"],
-        local_include_dirs: ["arch-x86_64/include"],
-    },
+    clang: false,
 }
 
-crt_arch_so_flags = crt_arch_flags + {
-    mips: {
-        cflags: ["-fPIC"],
-    },
-    mips64: {
-        cflags: ["-fPIC"],
-    },
-    x86: {
-        cflags: ["-fPIC"],
-    },
-    x86_64: {
-        cflags: ["-fPIC"],
+cc_defaults {
+    name: "crt_so_defaults",
+
+    arch: {
+        mips: {
+            cflags: ["-fPIC"],
+        },
+        mips64: {
+            cflags: ["-fPIC"],
+        },
+        x86: {
+            cflags: ["-fPIC"],
+        },
+        x86_64: {
+            cflags: ["-fPIC"],
+        },
     },
 }
 
@@ -2163,9 +1998,11 @@
         },
     },
     srcs: ["arch-common/bionic/crtbrand.S"],
-    no_default_compiler_flags: true,
 
-    arch: crt_arch_so_flags,
+    defaults: [
+        "crt_defaults",
+        "crt_so_defaults",
+    ],
 }
 
 // Android.mk:ignore
@@ -2173,17 +2010,21 @@
     name: "crtbegin_so1",
     local_include_dirs: ["include"],
     srcs: ["arch-common/bionic/crtbegin_so.c"],
-    no_default_compiler_flags: true,
 
-    arch: crt_arch_so_flags,
+    defaults: [
+        "crt_defaults",
+        "crt_so_defaults",
+    ],
 }
 
 // Android.mk:ignore
 cc_object {
     name: "crtbegin_so",
-    no_default_compiler_flags: true,
 
-    arch: crt_arch_so_flags,
+    defaults: [
+        "crt_defaults",
+        "crt_so_defaults",
+    ],
     deps: [
         "crtbegin_so1",
         "crtbrand",
@@ -2195,9 +2036,11 @@
     name: "crtend_so",
     local_include_dirs: ["include"],
     srcs: ["arch-common/bionic/crtend_so.S"],
-    no_default_compiler_flags: true,
 
-    arch: crt_arch_so_flags,
+    defaults: [
+        "crt_defaults",
+        "crt_so_defaults",
+    ],
 }
 
 // Android.mk:ignore
@@ -2205,9 +2048,8 @@
     name: "crtbegin_static1",
     local_include_dirs: ["include"],
     srcs: ["arch-common/bionic/crtbegin.c"],
-    no_default_compiler_flags: true,
 
-    arch: crt_arch_flags + {
+    arch: {
         arm64: {
             srcs: [
                 "arch-arm64/bionic/crtbegin.c",
@@ -2233,18 +2075,19 @@
             ],
         },
     },
+
+    defaults: ["crt_defaults"],
 }
 
 // Android.mk:ignore
 cc_object {
     name: "crtbegin_static",
-    no_default_compiler_flags: true,
 
-    arch: crt_arch_flags,
     deps: [
         "crtbegin_static1",
         "crtbrand",
     ],
+    defaults: ["crt_defaults"],
 }
 
 // Android.mk:ignore
@@ -2252,9 +2095,8 @@
     name: "crtbegin_dynamic1",
     local_include_dirs: ["include"],
     srcs: ["arch-common/bionic/crtbegin.c"],
-    no_default_compiler_flags: true,
 
-    arch: crt_arch_flags + {
+    arch: {
         arm64: {
             srcs: [
                 "arch-arm64/bionic/crtbegin.c",
@@ -2280,18 +2122,18 @@
             ],
         },
     },
+    defaults: ["crt_defaults"],
 }
 
 // Android.mk:ignore
 cc_object {
     name: "crtbegin_dynamic",
-    no_default_compiler_flags: true,
 
-    arch: crt_arch_flags,
     deps: [
         "crtbegin_dynamic1",
         "crtbrand",
     ],
+    defaults: ["crt_defaults"],
 }
 
 // Android.mk:ignore
@@ -2301,7 +2143,6 @@
     name: "crtend_android",
     local_include_dirs: ["include"],
     srcs: ["arch-common/bionic/crtend.S"],
-    no_default_compiler_flags: true,
 
-    arch: crt_arch_flags,
+    defaults: ["crt_defaults"],
 }
diff --git a/libc/Android.mk b/libc/Android.mk
index 140ec82..ba3e5aa 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 \
@@ -242,9 +243,11 @@
 
 libc_bionic_src_files :=
 
-# The fork implementation depends on pthread data, so we can't include it in
-# libc_ndk.a.
-libc_bionic_src_files += bionic/fork.cpp
+# The following implementations depend on pthread data, so we can't include
+# them in libc_ndk.a.
+libc_bionic_src_files += \
+    bionic/__cxa_thread_atexit_impl.cpp \
+    bionic/fork.cpp \
 
 # The data that backs getauxval is initialized in the libc init functions which
 # are invoked by the linker. If this file is included in libc_ndk.a, only one of
@@ -258,6 +261,15 @@
 libc_bionic_src_files += bionic/vdso.cpp
 libc_bionic_src_files += bionic/setjmp_cookie.cpp
 
+libc_bionic_src_files += \
+    bionic/__memcpy_chk.cpp \
+    bionic/__memset_chk.cpp \
+    bionic/__strcat_chk.cpp \
+    bionic/__strcpy_chk.cpp \
+    bionic/strchr.cpp \
+    bionic/strnlen.c \
+    bionic/strrchr.cpp \
+
 libc_cxa_src_files := \
     bionic/__cxa_guard.cpp \
     bionic/__cxa_pure_virtual.cpp \
@@ -274,18 +286,26 @@
     upstream-freebsd/lib/libc/string/wcpcpy.c \
     upstream-freebsd/lib/libc/string/wcpncpy.c \
     upstream-freebsd/lib/libc/string/wcscasecmp.c \
+    upstream-freebsd/lib/libc/string/wcscat.c \
+    upstream-freebsd/lib/libc/string/wcschr.c \
+    upstream-freebsd/lib/libc/string/wcscmp.c \
+    upstream-freebsd/lib/libc/string/wcscpy.c \
     upstream-freebsd/lib/libc/string/wcscspn.c \
     upstream-freebsd/lib/libc/string/wcsdup.c \
     upstream-freebsd/lib/libc/string/wcslcat.c \
+    upstream-freebsd/lib/libc/string/wcslen.c \
     upstream-freebsd/lib/libc/string/wcsncasecmp.c \
     upstream-freebsd/lib/libc/string/wcsncat.c \
     upstream-freebsd/lib/libc/string/wcsncmp.c \
     upstream-freebsd/lib/libc/string/wcsncpy.c \
     upstream-freebsd/lib/libc/string/wcsnlen.c \
     upstream-freebsd/lib/libc/string/wcspbrk.c \
+    upstream-freebsd/lib/libc/string/wcsrchr.c \
     upstream-freebsd/lib/libc/string/wcsspn.c \
     upstream-freebsd/lib/libc/string/wcstok.c \
     upstream-freebsd/lib/libc/string/wmemchr.c \
+    upstream-freebsd/lib/libc/string/wmemcmp.c \
+    upstream-freebsd/lib/libc/string/wmemmove.c \
     upstream-freebsd/lib/libc/string/wmemset.c \
 
 libc_upstream_netbsd_src_files := \
@@ -355,6 +375,20 @@
     upstream-openbsd/lib/libc/crypt/arc4random.c \
     upstream-openbsd/lib/libc/crypt/arc4random_uniform.c \
 
+libc_upstream_openbsd_src_files += \
+    upstream-openbsd/lib/libc/string/memchr.c \
+    upstream-openbsd/lib/libc/string/memmove.c \
+    upstream-openbsd/lib/libc/string/memrchr.c \
+    upstream-openbsd/lib/libc/string/stpcpy.c \
+    upstream-openbsd/lib/libc/string/stpncpy.c \
+    upstream-openbsd/lib/libc/string/strcat.c \
+    upstream-openbsd/lib/libc/string/strcpy.c \
+    upstream-openbsd/lib/libc/string/strlcat.c \
+    upstream-openbsd/lib/libc/string/strlcpy.c \
+    upstream-openbsd/lib/libc/string/strncat.c \
+    upstream-openbsd/lib/libc/string/strncmp.c \
+    upstream-openbsd/lib/libc/string/strncpy.c \
+
 libc_upstream_openbsd_ndk_src_files := \
     upstream-openbsd/lib/libc/compat-43/killpg.c \
     upstream-openbsd/lib/libc/gen/alarm.c \
@@ -532,6 +566,7 @@
 libc_pthread_src_files := \
     bionic/pthread_atfork.cpp \
     bionic/pthread_attr.cpp \
+    bionic/pthread_barrier.cpp \
     bionic/pthread_cond.cpp \
     bionic/pthread_create.cpp \
     bionic/pthread_detach.cpp \
@@ -551,9 +586,7 @@
     bionic/pthread_setname_np.cpp \
     bionic/pthread_setschedparam.cpp \
     bionic/pthread_sigmask.cpp \
-
-libc_thread_atexit_impl_src_files := \
-    bionic/__cxa_thread_atexit_impl.cpp \
+    bionic/pthread_spinlock.cpp \
 
 libc_arch_static_src_files := \
     bionic/dl_iterate_phdr_static.cpp \
@@ -591,6 +624,11 @@
   use_clang := false
 endif
 
+# b/25291096, Clang/llvm compiled libc.so for mips/mips64 failed to boot.
+ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),mips mips64))
+  use_clang := false
+endif
+
 ifeq ($(use_clang),)
   use_clang := false
 endif
@@ -693,6 +731,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
@@ -700,6 +739,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
 
@@ -794,7 +835,7 @@
 LOCAL_NATIVE_COVERAGE := $(bionic_coverage)
 
 $(eval $(call patch-up-arch-specific-flags,LOCAL_CFLAGS,libc_common_cflags))
-$(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES,libc_freebsd_src_files))
+$(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES_EXCLUDE,libc_freebsd_src_files_exclude))
 include $(BUILD_STATIC_LIBRARY)
 
 
@@ -909,6 +950,7 @@
 
 $(eval $(call patch-up-arch-specific-flags,LOCAL_CFLAGS,libc_common_cflags))
 $(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES,libc_openbsd_src_files))
+$(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES_EXCLUDE,libc_openbsd_src_files_exclude))
 include $(BUILD_STATIC_LIBRARY)
 
 
@@ -972,6 +1014,7 @@
 
 $(eval $(call patch-up-arch-specific-flags,LOCAL_CFLAGS,libc_common_cflags))
 $(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES,libc_bionic_src_files))
+$(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES_EXCLUDE,libc_bionic_src_files_exclude))
 include $(BUILD_STATIC_LIBRARY)
 
 
@@ -1002,24 +1045,6 @@
 $(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES,libc_bionic_ndk_src_files))
 include $(BUILD_STATIC_LIBRARY)
 
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(libc_thread_atexit_impl_src_files)
-LOCAL_CFLAGS := $(libc_common_cflags) -Wframe-larger-than=2048
-
-LOCAL_CONLYFLAGS := $(libc_common_conlyflags)
-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)
-
-include $(BUILD_STATIC_LIBRARY)
 
 # ========================================================
 # libc_pthread.a - pthreads parts that previously lived in
@@ -1217,7 +1242,6 @@
     libc_pthread \
     libc_stack_protector \
     libc_syscalls \
-    libc_thread_atexit_impl \
     libc_tzcode \
 
 LOCAL_WHOLE_STATIC_LIBRARIES_arm := libc_aeabi
@@ -1353,7 +1377,12 @@
 LOCAL_REQUIRED_MODULES := tzdata
 LOCAL_ADDITIONAL_DEPENDENCIES := \
     $(libc_common_additional_dependencies) \
-    $(LOCAL_PATH)/libc.map \
+    $(LOCAL_PATH)/libc.arm.map \
+    $(LOCAL_PATH)/libc.arm64.map \
+    $(LOCAL_PATH)/libc.mips.map \
+    $(LOCAL_PATH)/libc.mips64.map \
+    $(LOCAL_PATH)/libc.x86.map \
+    $(LOCAL_PATH)/libc.x86_64.map \
 
 # Leave the symbols in the shared library so that stack unwinders can produce
 # meaningful name resolution.
@@ -1380,8 +1409,17 @@
 LOCAL_CXX_STL := none
 LOCAL_SYSTEM_SHARED_LIBRARIES :=
 
+# TODO: This is to work around b/24465209. Remove after root cause is fixed
+LOCAL_LDFLAGS_arm := -Wl,--hash-style=both
+LOCAL_LDFLAGS_x86 := -Wl,--hash-style=both
+
 # Don't re-export new/delete and friends, even if the compiler really wants to.
-LOCAL_LDFLAGS := -Wl,--version-script,$(LOCAL_PATH)/libc.map
+LOCAL_LDFLAGS_arm    += -Wl,--version-script,$(LOCAL_PATH)/libc.arm.map
+LOCAL_LDFLAGS_arm64  += -Wl,--version-script,$(LOCAL_PATH)/libc.arm64.map
+LOCAL_LDFLAGS_mips   += -Wl,--version-script,$(LOCAL_PATH)/libc.mips.map
+LOCAL_LDFLAGS_mips64 += -Wl,--version-script,$(LOCAL_PATH)/libc.mips64.map
+LOCAL_LDFLAGS_x86    += -Wl,--version-script,$(LOCAL_PATH)/libc.x86.map
+LOCAL_LDFLAGS_x86_64 += -Wl,--version-script,$(LOCAL_PATH)/libc.x86_64.map
 
 # We'd really like to do this for all architectures, but since this wasn't done
 # before, these symbols must continue to be exported on LP32 for binary
@@ -1392,10 +1430,6 @@
 # prevent the build system from using this flag.
 LOCAL_NO_EXCLUDE_LIBS := true
 
-# TODO: This is to work around b/24465209. Remove after root cause is fixed
-LOCAL_LDFLAGS_arm := -Wl,--hash-style=both
-LOCAL_LDFLAGS_x86 := -Wl,--hash-style=both
-
 $(eval $(call patch-up-arch-specific-flags,LOCAL_CFLAGS,libc_common_cflags))
 $(eval $(call patch-up-arch-specific-flags,LOCAL_SRC_FILES,libc_arch_dynamic_src_files))
 
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/arm.mk b/libc/arch-arm/arm.mk
index 58c5eb6..76f465e 100644
--- a/libc/arch-arm/arm.mk
+++ b/libc/arch-arm/arm.mk
@@ -1,33 +1,19 @@
 # 32-bit arm.
 
-#
-# Default implementations of functions that are commonly optimized.
-#
-
 libc_bionic_src_files_arm += \
-    bionic/strchr.cpp \
-    bionic/strnlen.c \
-    bionic/strrchr.cpp \
+    arch-arm/generic/bionic/memcmp.S \
+    arch-arm/generic/bionic/memcpy.S \
+    arch-arm/generic/bionic/memset.S \
+    arch-arm/generic/bionic/strcmp.S \
+    arch-arm/generic/bionic/strcpy.S \
+    arch-arm/generic/bionic/strlen.c \
 
-libc_freebsd_src_files_arm += \
-    upstream-freebsd/lib/libc/string/wcscat.c \
-    upstream-freebsd/lib/libc/string/wcschr.c \
-    upstream-freebsd/lib/libc/string/wcscmp.c \
-    upstream-freebsd/lib/libc/string/wcscpy.c \
-    upstream-freebsd/lib/libc/string/wcslen.c \
-    upstream-freebsd/lib/libc/string/wcsrchr.c \
-    upstream-freebsd/lib/libc/string/wmemcmp.c \
-    upstream-freebsd/lib/libc/string/wmemmove.c \
+libc_bionic_src_files_exclude_arm += \
+    bionic/__memcpy_chk.cpp \
+    bionic/__memset_chk.cpp \
 
-libc_openbsd_src_files_arm += \
-    upstream-openbsd/lib/libc/string/memchr.c \
-    upstream-openbsd/lib/libc/string/memrchr.c \
-    upstream-openbsd/lib/libc/string/stpncpy.c \
-    upstream-openbsd/lib/libc/string/strlcat.c \
-    upstream-openbsd/lib/libc/string/strlcpy.c \
-    upstream-openbsd/lib/libc/string/strncat.c \
-    upstream-openbsd/lib/libc/string/strncmp.c \
-    upstream-openbsd/lib/libc/string/strncpy.c \
+libc_openbsd_src_files_exclude_arm += \
+    upstream-openbsd/lib/libc/string/strcpy.c \
 
 #
 # Inherently architecture-specific code.
@@ -52,6 +38,7 @@
 ifeq ($(strip $(TARGET_$(my_2nd_arch_prefix)CPU_VARIANT)),)
   $(warning TARGET_$(my_2nd_arch_prefix)ARCH is arm, but TARGET_$(my_2nd_arch_prefix)CPU_VARIANT is not defined)
 endif
+ifneq ($(TARGET_$(my_2nd_arch_prefix)CPU_VARIANT),generic)
 cpu_variant_mk := $(LOCAL_PATH)/arch-arm/$(TARGET_$(my_2nd_arch_prefix)CPU_VARIANT)/$(TARGET_$(my_2nd_arch_prefix)CPU_VARIANT).mk
 ifeq ($(wildcard $(cpu_variant_mk)),)
 $(error "TARGET_$(my_2nd_arch_prefix)CPU_VARIANT not set or set to an unknown value. Possible values are cortex-a7, cortex-a8, cortex-a9, cortex-a15, krait, denver. Use generic for devices that do not have a CPU similar to any of the supported cpu variants.")
@@ -60,6 +47,7 @@
 libc_common_additional_dependencies += $(cpu_variant_mk)
 
 cpu_variant_mk :=
+endif
 
 
 libc_crt_target_cflags_arm := \
diff --git a/libc/arch-arm/bionic/setjmp.S b/libc/arch-arm/bionic/setjmp.S
index a119529..464f7d8 100644
--- a/libc/arch-arm/bionic/setjmp.S
+++ b/libc/arch-arm/bionic/setjmp.S
@@ -151,7 +151,10 @@
   // Save core registers.
   add r1, r0, #(_JB_CORE_BASE * 4)
   m_mangle_registers r2
-  stmia r1, {r4-r14}
+
+  // ARM deprecates using sp in the register list for stmia.
+  stmia r1, {r4-r12, lr}
+  str sp, [r1, #(10 * 4)]
   m_unmangle_registers r2
 
   // Save floating-point registers.
@@ -204,7 +207,10 @@
   ldr r3, [r0, #(_JB_SIGFLAG * 4)]
   bic r3, r3, #1
   add r2, r0, #(_JB_CORE_BASE * 4)
-  ldmia r2, {r4-r14}
+
+  // ARM deprecates using sp in the register list for ldmia.
+  ldmia r2, {r4-r12, lr}
+  ldr sp, [r2, #(10 * 4)]
   m_unmangle_registers r3
 
   // Save the return value/address and check the setjmp cookie.
diff --git a/libc/arch-arm/cortex-a15/cortex-a15.mk b/libc/arch-arm/cortex-a15/cortex-a15.mk
index 6fa3270..20202a7 100644
--- a/libc/arch-arm/cortex-a15/cortex-a15.mk
+++ b/libc/arch-arm/cortex-a15/cortex-a15.mk
@@ -1,3 +1,17 @@
+libc_openbsd_src_files_exclude_arm += \
+    upstream-openbsd/lib/libc/string/memmove.c \
+    upstream-openbsd/lib/libc/string/stpcpy.c \
+    upstream-openbsd/lib/libc/string/strcat.c \
+
+libc_bionic_src_files_exclude_arm += \
+    arch-arm/generic/bionic/memcpy.S \
+    arch-arm/generic/bionic/memset.S \
+    arch-arm/generic/bionic/strcmp.S \
+    arch-arm/generic/bionic/strcpy.S \
+    arch-arm/generic/bionic/strlen.c \
+    bionic/__strcat_chk.cpp \
+    bionic/__strcpy_chk.cpp \
+
 libc_bionic_src_files_arm += \
     arch-arm/cortex-a15/bionic/memcpy.S \
     arch-arm/cortex-a15/bionic/memset.S \
@@ -10,7 +24,4 @@
     arch-arm/cortex-a15/bionic/strlen.S \
 
 libc_bionic_src_files_arm += \
-    arch-arm/generic/bionic/memcmp.S \
-
-libc_bionic_src_files_arm += \
     arch-arm/denver/bionic/memmove.S \
diff --git a/libc/arch-arm/cortex-a53.a57/cortex-a53.a57.mk b/libc/arch-arm/cortex-a53.a57/cortex-a53.a57.mk
index 0eec165..6455d04 100644
--- a/libc/arch-arm/cortex-a53.a57/cortex-a53.a57.mk
+++ b/libc/arch-arm/cortex-a53.a57/cortex-a53.a57.mk
@@ -3,6 +3,20 @@
 # The cortex-a7 optimized routines, and the cortex-a53 optimized routines
 # decrease performance on cortex-a57 processors by as much as 20%.
 
+libc_openbsd_src_files_exclude_arm += \
+    upstream-openbsd/lib/libc/string/memmove.c \
+    upstream-openbsd/lib/libc/string/stpcpy.c \
+    upstream-openbsd/lib/libc/string/strcat.c \
+
+libc_bionic_src_files_exclude_arm += \
+    arch-arm/generic/bionic/memcpy.S \
+    arch-arm/generic/bionic/memset.S \
+    arch-arm/generic/bionic/strcmp.S \
+    arch-arm/generic/bionic/strcpy.S \
+    arch-arm/generic/bionic/strlen.c \
+    bionic/__strcat_chk.cpp \
+    bionic/__strcpy_chk.cpp \
+
 libc_bionic_src_files_arm += \
     arch-arm/cortex-a15/bionic/memcpy.S \
     arch-arm/cortex-a15/bionic/memset.S \
@@ -15,7 +29,4 @@
     arch-arm/cortex-a15/bionic/strlen.S \
 
 libc_bionic_src_files_arm += \
-    arch-arm/generic/bionic/memcmp.S \
-
-libc_bionic_src_files_arm += \
     arch-arm/denver/bionic/memmove.S \
diff --git a/libc/arch-arm/cortex-a53/cortex-a53.mk b/libc/arch-arm/cortex-a53/cortex-a53.mk
index bb00433..9b431ae 100644
--- a/libc/arch-arm/cortex-a53/cortex-a53.mk
+++ b/libc/arch-arm/cortex-a53/cortex-a53.mk
@@ -1,3 +1,17 @@
+libc_openbsd_src_files_exclude_arm += \
+    upstream-openbsd/lib/libc/string/memmove.c \
+    upstream-openbsd/lib/libc/string/stpcpy.c \
+    upstream-openbsd/lib/libc/string/strcat.c \
+
+libc_bionic_src_files_exclude_arm += \
+    arch-arm/generic/bionic/memcpy.S \
+    arch-arm/generic/bionic/memset.S \
+    arch-arm/generic/bionic/strcmp.S \
+    arch-arm/generic/bionic/strcpy.S \
+    arch-arm/generic/bionic/strlen.c \
+    bionic/__strcat_chk.cpp \
+    bionic/__strcpy_chk.cpp \
+
 libc_bionic_src_files_arm += \
     arch-arm/cortex-a53/bionic/memcpy.S \
     arch-arm/cortex-a53/bionic/__strcat_chk.S \
@@ -14,7 +28,4 @@
     arch-arm/cortex-a15/bionic/strlen.S \
 
 libc_bionic_src_files_arm += \
-    arch-arm/generic/bionic/memcmp.S \
-
-libc_bionic_src_files_arm += \
     arch-arm/denver/bionic/memmove.S \
diff --git a/libc/arch-arm/cortex-a7/cortex-a7.mk b/libc/arch-arm/cortex-a7/cortex-a7.mk
index b6af4da..f570d0f 100644
--- a/libc/arch-arm/cortex-a7/cortex-a7.mk
+++ b/libc/arch-arm/cortex-a7/cortex-a7.mk
@@ -1,3 +1,17 @@
+libc_openbsd_src_files_exclude_arm += \
+    upstream-openbsd/lib/libc/string/memmove.c \
+    upstream-openbsd/lib/libc/string/stpcpy.c \
+    upstream-openbsd/lib/libc/string/strcat.c \
+
+libc_bionic_src_files_exclude_arm += \
+    arch-arm/generic/bionic/memcpy.S \
+    arch-arm/generic/bionic/memset.S \
+    arch-arm/generic/bionic/strcmp.S \
+    arch-arm/generic/bionic/strcpy.S \
+    arch-arm/generic/bionic/strlen.c \
+    bionic/__strcat_chk.cpp \
+    bionic/__strcpy_chk.cpp \
+
 libc_bionic_src_files_arm += \
     arch-arm/cortex-a7/bionic/memset.S \
 
@@ -12,7 +26,4 @@
     arch-arm/cortex-a15/bionic/strlen.S \
 
 libc_bionic_src_files_arm += \
-    arch-arm/generic/bionic/memcmp.S \
-
-libc_bionic_src_files_arm += \
     arch-arm/denver/bionic/memmove.S \
diff --git a/libc/arch-arm/cortex-a9/cortex-a9.mk b/libc/arch-arm/cortex-a9/cortex-a9.mk
index 7b38de1..8a26d6b 100644
--- a/libc/arch-arm/cortex-a9/cortex-a9.mk
+++ b/libc/arch-arm/cortex-a9/cortex-a9.mk
@@ -1,3 +1,18 @@
+libc_openbsd_src_files_exclude_arm += \
+    upstream-openbsd/lib/libc/string/memmove.c \
+    upstream-openbsd/lib/libc/string/stpcpy.c \
+    upstream-openbsd/lib/libc/string/strcat.c \
+    upstream-openbsd/lib/libc/string/strcpy.c \
+
+libc_bionic_src_files_exclude_arm += \
+    arch-arm/generic/bionic/memcpy.S \
+    arch-arm/generic/bionic/memset.S \
+    arch-arm/generic/bionic/strcmp.S \
+    arch-arm/generic/bionic/strcpy.S \
+    arch-arm/generic/bionic/strlen.c \
+    bionic/__strcat_chk.cpp \
+    bionic/__strcpy_chk.cpp \
+
 libc_bionic_src_files_arm += \
     arch-arm/cortex-a9/bionic/memcpy.S \
     arch-arm/cortex-a9/bionic/memset.S \
@@ -10,7 +25,4 @@
     arch-arm/cortex-a9/bionic/strlen.S \
 
 libc_bionic_src_files_arm += \
-    arch-arm/generic/bionic/memcmp.S \
-
-libc_bionic_src_files_arm += \
     arch-arm/denver/bionic/memmove.S \
diff --git a/libc/arch-arm/denver/denver.mk b/libc/arch-arm/denver/denver.mk
index 5fddf95..f167991 100644
--- a/libc/arch-arm/denver/denver.mk
+++ b/libc/arch-arm/denver/denver.mk
@@ -1,5 +1,18 @@
+libc_openbsd_src_files_exclude_arm += \
+    upstream-openbsd/lib/libc/string/memmove.c \
+    upstream-openbsd/lib/libc/string/stpcpy.c \
+    upstream-openbsd/lib/libc/string/strcat.c \
+
+libc_bionic_src_files_exclude_arm += \
+    arch-arm/generic/bionic/memcpy.S \
+    arch-arm/generic/bionic/memset.S \
+    arch-arm/generic/bionic/strcmp.S \
+    arch-arm/generic/bionic/strcpy.S \
+    arch-arm/generic/bionic/strlen.c \
+    bionic/__strcat_chk.cpp \
+    bionic/__strcpy_chk.cpp \
+
 libc_bionic_src_files_arm += \
-    arch-arm/generic/bionic/memcmp.S \
     arch-arm/denver/bionic/memcpy.S \
     arch-arm/denver/bionic/memmove.S \
     arch-arm/denver/bionic/memset.S \
diff --git a/libc/arch-arm/generic/generic.mk b/libc/arch-arm/generic/generic.mk
deleted file mode 100644
index e49d6d2..0000000
--- a/libc/arch-arm/generic/generic.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-libc_bionic_src_files_arm += \
-    arch-arm/generic/bionic/memcmp.S \
-    arch-arm/generic/bionic/memcpy.S \
-    arch-arm/generic/bionic/memset.S \
-    arch-arm/generic/bionic/strcmp.S \
-    arch-arm/generic/bionic/strcpy.S \
-    arch-arm/generic/bionic/strlen.c \
-    bionic/__strcat_chk.cpp \
-    bionic/__strcpy_chk.cpp \
-
-libc_openbsd_src_files_arm += \
-    upstream-openbsd/lib/libc/string/memmove.c \
-    upstream-openbsd/lib/libc/string/stpcpy.c \
-    upstream-openbsd/lib/libc/string/strcat.c \
diff --git a/libc/arch-arm/krait/krait.mk b/libc/arch-arm/krait/krait.mk
index 88b4d66..f8e3452 100644
--- a/libc/arch-arm/krait/krait.mk
+++ b/libc/arch-arm/krait/krait.mk
@@ -1,3 +1,18 @@
+libc_openbsd_src_files_exclude_arm += \
+    upstream-openbsd/lib/libc/string/memmove.c \
+    upstream-openbsd/lib/libc/string/stpcpy.c \
+    upstream-openbsd/lib/libc/string/stpcpy.c \
+    upstream-openbsd/lib/libc/string/strcat.c \
+
+libc_bionic_src_files_exclude_arm += \
+    arch-arm/generic/bionic/memcpy.S \
+    arch-arm/generic/bionic/memset.S \
+    arch-arm/generic/bionic/strcmp.S \
+    arch-arm/generic/bionic/strcpy.S \
+    arch-arm/generic/bionic/strlen.c \
+    bionic/__strcat_chk.cpp \
+    bionic/__strcpy_chk.cpp \
+
 libc_bionic_src_files_arm += \
     arch-arm/krait/bionic/memcpy.S \
     arch-arm/krait/bionic/memset.S \
@@ -13,7 +28,4 @@
     arch-arm/cortex-a15/bionic/strlen.S \
 
 libc_bionic_src_files_arm += \
-    arch-arm/generic/bionic/memcmp.S \
-
-libc_bionic_src_files_arm += \
     arch-arm/denver/bionic/memmove.S \
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/arm64.mk b/libc/arch-arm64/arm64.mk
index 470a038..0811c96 100644
--- a/libc/arch-arm64/arm64.mk
+++ b/libc/arch-arm64/arm64.mk
@@ -1,32 +1,38 @@
 # 64-bit arm.
 
 #
-# Default implementations of functions that are commonly optimized.
+# Generic arm64 optimizations, may be overriden by CPU variants.
 #
 
 libc_bionic_src_files_arm64 += \
-    bionic/__memset_chk.cpp \
-    bionic/__strcpy_chk.cpp \
-    bionic/__strcat_chk.cpp \
-    bionic/strrchr.cpp \
+    arch-arm64/generic/bionic/memchr.S \
+    arch-arm64/generic/bionic/memcmp.S \
+    arch-arm64/generic/bionic/memcpy.S \
+    arch-arm64/generic/bionic/memmove.S \
+    arch-arm64/generic/bionic/memset.S \
+    arch-arm64/generic/bionic/stpcpy.S \
+    arch-arm64/generic/bionic/strchr.S \
+    arch-arm64/generic/bionic/strcmp.S \
+    arch-arm64/generic/bionic/strcpy.S \
+    arch-arm64/generic/bionic/strlen.S \
+    arch-arm64/generic/bionic/strncmp.S \
+    arch-arm64/generic/bionic/strnlen.S \
+    arch-arm64/generic/bionic/wmemmove.S \
 
-libc_freebsd_src_files_arm64 += \
-    upstream-freebsd/lib/libc/string/wcscat.c \
-    upstream-freebsd/lib/libc/string/wcschr.c \
-    upstream-freebsd/lib/libc/string/wcscmp.c \
-    upstream-freebsd/lib/libc/string/wcscpy.c \
-    upstream-freebsd/lib/libc/string/wcslen.c \
-    upstream-freebsd/lib/libc/string/wcsrchr.c \
-    upstream-freebsd/lib/libc/string/wmemcmp.c \
+libc_bionic_src_files_exclude_arm64 += \
+    bionic/__memcpy_chk.cpp \
+    bionic/strchr.cpp \
+    bionic/strnlen.c \
 
-libc_openbsd_src_files_arm64 += \
-    upstream-openbsd/lib/libc/string/memrchr.c \
-    upstream-openbsd/lib/libc/string/stpncpy.c \
-    upstream-openbsd/lib/libc/string/strcat.c \
-    upstream-openbsd/lib/libc/string/strlcat.c \
-    upstream-openbsd/lib/libc/string/strlcpy.c \
-    upstream-openbsd/lib/libc/string/strncat.c \
-    upstream-openbsd/lib/libc/string/strncpy.c \
+libc_freebsd_src_files_exclude_arm64 += \
+    upstream-freebsd/lib/libc/string/wmemmove.c \
+
+libc_openbsd_src_files_exclude_arm64 += \
+    upstream-openbsd/lib/libc/string/memchr.c \
+    upstream-openbsd/lib/libc/string/memmove.c \
+    upstream-openbsd/lib/libc/string/stpcpy.c \
+    upstream-openbsd/lib/libc/string/strcpy.c \
+    upstream-openbsd/lib/libc/string/strncmp.c \
 
 #
 # Inherently architecture-specific code.
@@ -54,6 +60,7 @@
 ifeq ($(strip $(TARGET_CPU_VARIANT)),)
   $(warning TARGET_ARCH is arm64, but TARGET_CPU_VARIANT is not defined)
 endif
+ifneq ($(TARGET_CPU_VARIANT),generic)
 cpu_variant_mk := $(LOCAL_PATH)/arch-arm64/$(TARGET_CPU_VARIANT)/$(TARGET_CPU_VARIANT).mk
 ifeq ($(wildcard $(cpu_variant_mk)),)
 $(error "TARGET_CPU_VARIANT not set or set to an unknown value. Possible values are generic, denver64. Use generic for devices that do not have a CPU similar to any of the supported cpu variants.")
@@ -62,3 +69,4 @@
 libc_common_additional_dependencies += $(cpu_variant_mk)
 
 cpu_variant_mk :=
+endif
diff --git a/libc/arch-arm64/cortex-a53/cortex-a53.mk b/libc/arch-arm64/cortex-a53/cortex-a53.mk
index 676e886..e69de29 100644
--- a/libc/arch-arm64/cortex-a53/cortex-a53.mk
+++ b/libc/arch-arm64/cortex-a53/cortex-a53.mk
@@ -1 +0,0 @@
-include bionic/libc/arch-arm64/generic/generic.mk
diff --git a/libc/arch-arm64/denver64/denver64.mk b/libc/arch-arm64/denver64/denver64.mk
index d619c11..703af45 100644
--- a/libc/arch-arm64/denver64/denver64.mk
+++ b/libc/arch-arm64/denver64/denver64.mk
@@ -1,14 +1,7 @@
 libc_bionic_src_files_arm64 += \
-    arch-arm64/generic/bionic/memchr.S \
-    arch-arm64/generic/bionic/memcmp.S \
     arch-arm64/denver64/bionic/memcpy.S \
-    arch-arm64/generic/bionic/memmove.S \
     arch-arm64/denver64/bionic/memset.S \
-    arch-arm64/generic/bionic/stpcpy.S \
-    arch-arm64/generic/bionic/strchr.S \
-    arch-arm64/generic/bionic/strcmp.S \
-    arch-arm64/generic/bionic/strcpy.S \
-    arch-arm64/generic/bionic/strlen.S \
-    arch-arm64/generic/bionic/strncmp.S \
-    arch-arm64/generic/bionic/strnlen.S \
-    arch-arm64/generic/bionic/wmemmove.S
+
+libc_bionic_src_files_exclude_arm64 += \
+    arch-arm64/generic/bionic/memcpy.S \
+    arch-arm64/generic/bionic/memset.S \
diff --git a/libc/arch-arm64/generic/generic.mk b/libc/arch-arm64/generic/generic.mk
deleted file mode 100644
index 1b595aa..0000000
--- a/libc/arch-arm64/generic/generic.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-libc_bionic_src_files_arm64 += \
-    arch-arm64/generic/bionic/memchr.S \
-    arch-arm64/generic/bionic/memcmp.S \
-    arch-arm64/generic/bionic/memcpy.S \
-    arch-arm64/generic/bionic/memmove.S \
-    arch-arm64/generic/bionic/memset.S \
-    arch-arm64/generic/bionic/stpcpy.S \
-    arch-arm64/generic/bionic/strchr.S \
-    arch-arm64/generic/bionic/strcmp.S \
-    arch-arm64/generic/bionic/strcpy.S \
-    arch-arm64/generic/bionic/strlen.S \
-    arch-arm64/generic/bionic/strncmp.S \
-    arch-arm64/generic/bionic/strnlen.S \
-    arch-arm64/generic/bionic/wmemmove.S
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/mips.mk b/libc/arch-mips/mips.mk
index 05e7198..b184abb 100644
--- a/libc/arch-mips/mips.mk
+++ b/libc/arch-mips/mips.mk
@@ -1,45 +1,10 @@
 # 32-bit mips.
 
-#
-# Default implementations of functions that are commonly optimized.
-#
-
 libc_bionic_src_files_mips += \
     arch-mips/string/memcmp.c \
     arch-mips/string/memcpy.S \
     arch-mips/string/memset.S \
     arch-mips/string/strcmp.S \
-    bionic/__memcpy_chk.cpp \
-    bionic/__memset_chk.cpp \
-    bionic/__strcpy_chk.cpp \
-    bionic/__strcat_chk.cpp \
-    bionic/strchr.cpp \
-    bionic/strnlen.c \
-    bionic/strrchr.cpp \
-
-libc_freebsd_src_files_mips += \
-    upstream-freebsd/lib/libc/string/wcscat.c \
-    upstream-freebsd/lib/libc/string/wcschr.c \
-    upstream-freebsd/lib/libc/string/wcscmp.c \
-    upstream-freebsd/lib/libc/string/wcscpy.c \
-    upstream-freebsd/lib/libc/string/wcslen.c \
-    upstream-freebsd/lib/libc/string/wcsrchr.c \
-    upstream-freebsd/lib/libc/string/wmemcmp.c \
-    upstream-freebsd/lib/libc/string/wmemmove.c \
-
-libc_openbsd_src_files_mips += \
-    upstream-openbsd/lib/libc/string/memchr.c \
-    upstream-openbsd/lib/libc/string/memmove.c \
-    upstream-openbsd/lib/libc/string/memrchr.c \
-    upstream-openbsd/lib/libc/string/stpcpy.c \
-    upstream-openbsd/lib/libc/string/stpncpy.c \
-    upstream-openbsd/lib/libc/string/strcat.c \
-    upstream-openbsd/lib/libc/string/strcpy.c \
-    upstream-openbsd/lib/libc/string/strlcat.c \
-    upstream-openbsd/lib/libc/string/strlcpy.c \
-    upstream-openbsd/lib/libc/string/strncat.c \
-    upstream-openbsd/lib/libc/string/strncmp.c \
-    upstream-openbsd/lib/libc/string/strncpy.c \
 
 #
 # Inherently architecture-specific code.
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/mips64.mk b/libc/arch-mips64/mips64.mk
index 7757385..20ee639 100644
--- a/libc/arch-mips64/mips64.mk
+++ b/libc/arch-mips64/mips64.mk
@@ -1,46 +1,11 @@
 # 64-bit mips.
 
-#
-# Default implementations of functions that are commonly optimized.
-#
-
 libc_bionic_src_files_mips64 += \
     arch-mips/string/memcmp.c \
     arch-mips/string/memcpy.S \
     arch-mips/string/memset.S \
     arch-mips/string/strcmp.S \
     arch-mips/string/strlen.c \
-    bionic/__memcpy_chk.cpp \
-    bionic/__memset_chk.cpp \
-    bionic/__strcpy_chk.cpp \
-    bionic/__strcat_chk.cpp \
-    bionic/strchr.cpp \
-    bionic/strnlen.c \
-    bionic/strrchr.cpp \
-
-libc_freebsd_src_files_mips64 += \
-    upstream-freebsd/lib/libc/string/wcscat.c \
-    upstream-freebsd/lib/libc/string/wcschr.c \
-    upstream-freebsd/lib/libc/string/wcscmp.c \
-    upstream-freebsd/lib/libc/string/wcscpy.c \
-    upstream-freebsd/lib/libc/string/wcslen.c \
-    upstream-freebsd/lib/libc/string/wcsrchr.c \
-    upstream-freebsd/lib/libc/string/wmemcmp.c \
-    upstream-freebsd/lib/libc/string/wmemmove.c \
-
-libc_openbsd_src_files_mips64 += \
-    upstream-openbsd/lib/libc/string/memchr.c \
-    upstream-openbsd/lib/libc/string/memmove.c \
-    upstream-openbsd/lib/libc/string/memrchr.c \
-    upstream-openbsd/lib/libc/string/stpcpy.c \
-    upstream-openbsd/lib/libc/string/stpncpy.c \
-    upstream-openbsd/lib/libc/string/strcat.c \
-    upstream-openbsd/lib/libc/string/strcpy.c \
-    upstream-openbsd/lib/libc/string/strlcat.c \
-    upstream-openbsd/lib/libc/string/strlcpy.c \
-    upstream-openbsd/lib/libc/string/strncat.c \
-    upstream-openbsd/lib/libc/string/strncmp.c \
-    upstream-openbsd/lib/libc/string/strncpy.c \
 
 #
 # Inherently architecture-specific code.
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/atom/atom.mk b/libc/arch-x86/atom/atom.mk
index 3f28fb2..1afabac 100644
--- a/libc/arch-x86/atom/atom.mk
+++ b/libc/arch-x86/atom/atom.mk
@@ -1,32 +1,27 @@
 libc_bionic_src_files_x86 += \
     arch-x86/atom/string/sse2-bzero-atom.S \
-    arch-x86/atom/string/sse2-memchr-atom.S \
-    arch-x86/atom/string/sse2-memrchr-atom.S \
     arch-x86/atom/string/sse2-memset-atom.S \
-    arch-x86/atom/string/sse2-strchr-atom.S \
     arch-x86/atom/string/sse2-strlen-atom.S \
-    arch-x86/atom/string/sse2-strnlen-atom.S \
-    arch-x86/atom/string/sse2-strrchr-atom.S \
-    arch-x86/atom/string/sse2-wcschr-atom.S \
-    arch-x86/atom/string/sse2-wcsrchr-atom.S \
-    arch-x86/atom/string/sse2-wcslen-atom.S \
-    arch-x86/atom/string/sse2-wcscmp-atom.S \
     arch-x86/atom/string/ssse3-bcopy-atom.S \
     arch-x86/atom/string/ssse3-memcmp-atom.S \
     arch-x86/atom/string/ssse3-memcpy-atom.S \
     arch-x86/atom/string/ssse3-memmove-atom.S \
-    arch-x86/atom/string/ssse3-strcat-atom.S \
-    arch-x86/atom/string/ssse3-strcmp-atom.S \
     arch-x86/atom/string/ssse3-strcpy-atom.S \
-    arch-x86/atom/string/ssse3-strlcat-atom.S \
-    arch-x86/atom/string/ssse3-strlcpy-atom.S \
-    arch-x86/atom/string/ssse3-strncat-atom.S \
-    arch-x86/atom/string/ssse3-strncmp-atom.S \
     arch-x86/atom/string/ssse3-strncpy-atom.S \
-    arch-x86/atom/string/ssse3-wcscat-atom.S \
-    arch-x86/atom/string/ssse3-wcscpy-atom.S \
     arch-x86/atom/string/ssse3-wmemcmp-atom.S
 
-libc_bionic_src_files_x86 += \
-    arch-x86/silvermont/string/sse2-stpcpy-slm.S \
-    arch-x86/silvermont/string/sse2-stpncpy-slm.S
+libc_bionic_src_files_exclude_x86 += \
+    arch-x86/generic/string/memcmp.S \
+
+libc_bionic_src_files_exclude_x86 += \
+    arch-x86/silvermont/string/sse2-bcopy-slm.S \
+    arch-x86/silvermont/string/sse2-bzero-slm.S \
+    arch-x86/silvermont/string/sse2-memcpy-slm.S \
+    arch-x86/silvermont/string/sse2-memmove-slm.S \
+    arch-x86/silvermont/string/sse2-memset-slm.S \
+    arch-x86/silvermont/string/sse2-strcpy-slm.S \
+    arch-x86/silvermont/string/sse2-strlen-slm.S \
+    arch-x86/silvermont/string/sse2-strncpy-slm.S \
+
+libc_freebsd_src_files_exclude_x86 += \
+    upstream-freebsd/lib/libc/string/wmemcmp.c \
diff --git a/libc/arch-x86/generic/generic.mk b/libc/arch-x86/generic/generic.mk
deleted file mode 100644
index 4aee5dc..0000000
--- a/libc/arch-x86/generic/generic.mk
+++ /dev/null
@@ -1,52 +0,0 @@
-libc_bionic_src_files_x86 += \
-    arch-x86/atom/string/sse2-memchr-atom.S \
-    arch-x86/atom/string/sse2-memrchr-atom.S \
-    arch-x86/atom/string/sse2-strchr-atom.S \
-    arch-x86/atom/string/sse2-strnlen-atom.S \
-    arch-x86/atom/string/sse2-strrchr-atom.S \
-    arch-x86/atom/string/sse2-wcschr-atom.S \
-    arch-x86/atom/string/sse2-wcsrchr-atom.S \
-    arch-x86/atom/string/sse2-wcslen-atom.S \
-    arch-x86/atom/string/sse2-wcscmp-atom.S \
-    arch-x86/silvermont/string/sse2-bcopy-slm.S \
-    arch-x86/silvermont/string/sse2-bzero-slm.S \
-    arch-x86/silvermont/string/sse2-memcpy-slm.S \
-    arch-x86/silvermont/string/sse2-memmove-slm.S \
-    arch-x86/silvermont/string/sse2-memset-slm.S \
-    arch-x86/silvermont/string/sse2-stpcpy-slm.S \
-    arch-x86/silvermont/string/sse2-stpncpy-slm.S \
-    arch-x86/silvermont/string/sse2-strcpy-slm.S \
-    arch-x86/silvermont/string/sse2-strlen-slm.S \
-    arch-x86/silvermont/string/sse2-strncpy-slm.S
-
-ifeq ($(ARCH_X86_HAVE_SSSE3),true)
-libc_bionic_src_files_x86 += \
-    arch-x86/atom/string/ssse3-strncat-atom.S \
-    arch-x86/atom/string/ssse3-strlcat-atom.S \
-    arch-x86/atom/string/ssse3-strlcpy-atom.S \
-    arch-x86/atom/string/ssse3-strcmp-atom.S \
-    arch-x86/atom/string/ssse3-strncmp-atom.S \
-    arch-x86/atom/string/ssse3-strcat-atom.S \
-    arch-x86/atom/string/ssse3-wcscat-atom.S \
-    arch-x86/atom/string/ssse3-wcscpy-atom.S
-else
-libc_bionic_src_files_x86 += \
-    arch-x86/generic/string/strcmp.S \
-    arch-x86/generic/string/strncmp.S \
-    arch-x86/generic/string/strcat.S \
-    upstream-freebsd/lib/libc/string/wcscpy.c \
-    upstream-freebsd/lib/libc/string/wcscat.c \
-    upstream-openbsd/lib/libc/string/strlcat.c \
-    upstream-openbsd/lib/libc/string/strlcpy.c \
-    upstream-openbsd/lib/libc/string/strncat.c
-endif
-
-ifeq ($(ARCH_X86_HAVE_SSE4),true)
- libc_bionic_src_files_x86 += \
-    arch-x86/silvermont/string/sse4-memcmp-slm.S \
-    arch-x86/silvermont/string/sse4-wmemcmp-slm.S
-else
-libc_bionic_src_files_x86 += \
-    arch-x86/generic/string/memcmp.S \
-    upstream-freebsd/lib/libc/string/wmemcmp.c
-endif
diff --git a/libc/arch-x86/silvermont/silvermont.mk b/libc/arch-x86/silvermont/silvermont.mk
index 176bee3..e69de29 100644
--- a/libc/arch-x86/silvermont/silvermont.mk
+++ b/libc/arch-x86/silvermont/silvermont.mk
@@ -1,32 +0,0 @@
-libc_bionic_src_files_x86 += \
-    arch-x86/silvermont/string/sse2-bcopy-slm.S \
-    arch-x86/silvermont/string/sse2-bzero-slm.S \
-    arch-x86/silvermont/string/sse2-memcpy-slm.S \
-    arch-x86/silvermont/string/sse2-memmove-slm.S \
-    arch-x86/silvermont/string/sse2-memset-slm.S \
-    arch-x86/silvermont/string/sse2-stpcpy-slm.S \
-    arch-x86/silvermont/string/sse2-stpncpy-slm.S \
-    arch-x86/silvermont/string/sse2-strcpy-slm.S \
-    arch-x86/silvermont/string/sse2-strlen-slm.S \
-    arch-x86/silvermont/string/sse2-strncpy-slm.S \
-    arch-x86/silvermont/string/sse4-memcmp-slm.S \
-    arch-x86/silvermont/string/sse4-wmemcmp-slm.S
-
-libc_bionic_src_files_x86 += \
-    arch-x86/atom/string/sse2-memchr-atom.S \
-    arch-x86/atom/string/sse2-memrchr-atom.S \
-    arch-x86/atom/string/sse2-strchr-atom.S \
-    arch-x86/atom/string/sse2-strrchr-atom.S \
-    arch-x86/atom/string/sse2-strnlen-atom.S \
-    arch-x86/atom/string/sse2-wcschr-atom.S \
-    arch-x86/atom/string/sse2-wcsrchr-atom.S \
-    arch-x86/atom/string/sse2-wcslen-atom.S \
-    arch-x86/atom/string/sse2-wcscmp-atom.S \
-    arch-x86/atom/string/ssse3-strncat-atom.S \
-    arch-x86/atom/string/ssse3-strlcat-atom.S \
-    arch-x86/atom/string/ssse3-strlcpy-atom.S \
-    arch-x86/atom/string/ssse3-strcmp-atom.S \
-    arch-x86/atom/string/ssse3-strncmp-atom.S \
-    arch-x86/atom/string/ssse3-strcat-atom.S \
-    arch-x86/atom/string/ssse3-wcscat-atom.S \
-    arch-x86/atom/string/ssse3-wcscpy-atom.S
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/x86.mk b/libc/arch-x86/x86.mk
index e5d70a9..b4056bb 100644
--- a/libc/arch-x86/x86.mk
+++ b/libc/arch-x86/x86.mk
@@ -1,17 +1,103 @@
 # 32-bit x86.
 
 #
-# Default implementations of functions that are commonly optimized.
+# Generic x86 optimizations, may be overriden by CPU variants.
 #
 
-libc_common_src_files_x86 += \
-    bionic/__memcpy_chk.cpp \
-    bionic/__memset_chk.cpp \
-    bionic/__strcpy_chk.cpp \
-    bionic/__strcat_chk.cpp \
+libc_bionic_src_files_x86 += \
+    arch-x86/atom/string/sse2-memchr-atom.S \
+    arch-x86/atom/string/sse2-memrchr-atom.S \
+    arch-x86/atom/string/sse2-strchr-atom.S \
+    arch-x86/atom/string/sse2-strnlen-atom.S \
+    arch-x86/atom/string/sse2-strrchr-atom.S \
+    arch-x86/atom/string/sse2-wcschr-atom.S \
+    arch-x86/atom/string/sse2-wcsrchr-atom.S \
+    arch-x86/atom/string/sse2-wcslen-atom.S \
+    arch-x86/atom/string/sse2-wcscmp-atom.S \
+    arch-x86/silvermont/string/sse2-bcopy-slm.S \
+    arch-x86/silvermont/string/sse2-bzero-slm.S \
+    arch-x86/silvermont/string/sse2-memcpy-slm.S \
+    arch-x86/silvermont/string/sse2-memmove-slm.S \
+    arch-x86/silvermont/string/sse2-memset-slm.S \
+    arch-x86/silvermont/string/sse2-stpcpy-slm.S \
+    arch-x86/silvermont/string/sse2-stpncpy-slm.S \
+    arch-x86/silvermont/string/sse2-strcpy-slm.S \
+    arch-x86/silvermont/string/sse2-strlen-slm.S \
+    arch-x86/silvermont/string/sse2-strncpy-slm.S
 
-libc_freebsd_src_files_x86 += \
-    upstream-freebsd/lib/libc/string/wmemmove.c \
+libc_bionic_src_files_x86 += \
+    arch-x86/generic/string/memcmp.S \
+    arch-x86/generic/string/strcmp.S \
+    arch-x86/generic/string/strncmp.S \
+    arch-x86/generic/string/strcat.S
+
+ifeq ($(ARCH_X86_HAVE_SSSE3),true)
+libc_bionic_src_files_x86 += \
+    arch-x86/atom/string/ssse3-strncat-atom.S \
+    arch-x86/atom/string/ssse3-strlcat-atom.S \
+    arch-x86/atom/string/ssse3-strlcpy-atom.S \
+    arch-x86/atom/string/ssse3-strcmp-atom.S \
+    arch-x86/atom/string/ssse3-strncmp-atom.S \
+    arch-x86/atom/string/ssse3-strcat-atom.S \
+    arch-x86/atom/string/ssse3-wcscat-atom.S \
+    arch-x86/atom/string/ssse3-wcscpy-atom.S
+libc_bionic_src_files_exclude_x86 += \
+    arch-x86/generic/string/strcmp.S \
+    arch-x86/generic/string/strncmp.S \
+    arch-x86/generic/string/strcat.S
+endif
+
+ifeq ($(ARCH_X86_HAVE_SSE4),true)
+libc_bionic_src_files_x86 += \
+    arch-x86/silvermont/string/sse4-memcmp-slm.S \
+    arch-x86/silvermont/string/sse4-wmemcmp-slm.S
+libc_bionic_src_files_exclude_x86 += \
+    arch-x86/generic/string/memcmp.S
+endif
+
+#
+# Remove default implementations that we have optimized versions of.
+#
+
+libc_freebsd_src_files_exclude_x86 += \
+    upstream-freebsd/lib/libc/string/wcschr.c \
+    upstream-freebsd/lib/libc/string/wcscmp.c \
+    upstream-freebsd/lib/libc/string/wcslen.c \
+    upstream-freebsd/lib/libc/string/wcsrchr.c \
+
+ifeq ($(ARCH_X86_HAVE_SSSE3),true)
+libc_freebsd_src_files_exclude_x86 += \
+    upstream-freebsd/lib/libc/string/wcscat.c \
+    upstream-freebsd/lib/libc/string/wcscpy.c
+endif
+
+ifeq ($(ARCH_X86_HAVE_SSE4),true)
+libc_freebsd_src_files_exclude_x86 += \
+    upstream-freebsd/lib/libc/string/wmemcmp.c
+endif
+
+libc_openbsd_src_files_exclude_x86 += \
+    upstream-openbsd/lib/libc/string/memchr.c \
+    upstream-openbsd/lib/libc/string/memmove.c \
+    upstream-openbsd/lib/libc/string/memrchr.c \
+    upstream-openbsd/lib/libc/string/stpcpy.c \
+    upstream-openbsd/lib/libc/string/stpncpy.c \
+    upstream-openbsd/lib/libc/string/strcat.c \
+    upstream-openbsd/lib/libc/string/strcpy.c \
+    upstream-openbsd/lib/libc/string/strncmp.c \
+    upstream-openbsd/lib/libc/string/strncpy.c \
+
+ifeq ($(ARCH_X86_HAVE_SSSE3),true)
+libc_openbsd_src_files_exclude_x86 += \
+    upstream-openbsd/lib/libc/string/strlcat.c \
+    upstream-openbsd/lib/libc/string/strlcpy.c \
+    upstream-openbsd/lib/libc/string/strncat.c
+endif
+
+libc_bionic_src_files_exclude_x86 += \
+    bionic/strchr.cpp \
+    bionic/strnlen.c \
+    bionic/strrchr.cpp \
 
 #
 # Inherently architecture-specific functions.
@@ -30,12 +116,14 @@
 ## ARCH variant specific source files
 arch_variant_mk := $(LOCAL_PATH)/arch-x86/$(TARGET_ARCH_VARIANT)/$(TARGET_ARCH_VARIANT).mk
 ifeq ($(wildcard $(arch_variant_mk)),)
-    arch_variant_mk := $(LOCAL_PATH)/arch-x86/generic/generic.mk
+    arch_variant_mk :=
 endif
+ifneq ($(arch_variant_mk),)
 include $(arch_variant_mk)
 libc_common_additional_dependencies += $(arch_variant_mk)
 
 arch_variant_mk :=
+endif
 
 libc_crt_target_cflags_x86 := \
     -m32 \
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/arch-x86_64/x86_64.mk b/libc/arch-x86_64/x86_64.mk
index 06d3185..bbf5c8c 100644
--- a/libc/arch-x86_64/x86_64.mk
+++ b/libc/arch-x86_64/x86_64.mk
@@ -1,31 +1,20 @@
 # 64-bit x86.
 
 #
-# Default implementations of functions that are commonly optimized.
+# Remove default implementations that we have optimized versions of.
 #
 
-libc_bionic_src_files_x86_64 += \
-    bionic/__memcpy_chk.cpp \
-    bionic/__memset_chk.cpp \
-    bionic/__strcpy_chk.cpp \
-    bionic/__strcat_chk.cpp \
-    bionic/strchr.cpp \
-    bionic/strnlen.c \
-    bionic/strrchr.cpp \
-
-libc_freebsd_src_files_x86_64 += \
-    upstream-freebsd/lib/libc/string/wcscat.c \
-    upstream-freebsd/lib/libc/string/wcschr.c \
-    upstream-freebsd/lib/libc/string/wcscmp.c \
-    upstream-freebsd/lib/libc/string/wcscpy.c \
-    upstream-freebsd/lib/libc/string/wcslen.c \
-    upstream-freebsd/lib/libc/string/wcsrchr.c \
-    upstream-freebsd/lib/libc/string/wmemcmp.c \
-    upstream-freebsd/lib/libc/string/wmemmove.c \
-
-libc_openbsd_src_files_x86_64 += \
-    upstream-openbsd/lib/libc/string/memchr.c \
-    upstream-openbsd/lib/libc/string/memrchr.c \
+libc_openbsd_src_files_exclude_x86_64 += \
+    upstream-openbsd/lib/libc/string/memmove.c \
+    upstream-openbsd/lib/libc/string/stpcpy.c \
+    upstream-openbsd/lib/libc/string/stpncpy.c \
+    upstream-openbsd/lib/libc/string/strcat.c \
+    upstream-openbsd/lib/libc/string/strcpy.c \
+    upstream-openbsd/lib/libc/string/strlcat.c \
+    upstream-openbsd/lib/libc/string/strlcpy.c \
+    upstream-openbsd/lib/libc/string/strncat.c \
+    upstream-openbsd/lib/libc/string/strncmp.c \
+    upstream-openbsd/lib/libc/string/strncpy.c \
 
 #
 # Inherently architecture-specific code.
diff --git a/libc/bionic/__cxa_guard.cpp b/libc/bionic/__cxa_guard.cpp
index 5b34b58..97284d5 100644
--- a/libc/bionic/__cxa_guard.cpp
+++ b/libc/bionic/__cxa_guard.cpp
@@ -109,7 +109,7 @@
       }
     }
 
-    __futex_wait_ex(&gv->state, false, CONSTRUCTION_UNDERWAY_WITH_WAITER, NULL);
+    __futex_wait_ex(&gv->state, false, CONSTRUCTION_UNDERWAY_WITH_WAITER, false, nullptr);
     old_value = atomic_load_explicit(&gv->state, memory_order_relaxed);
   }
 }
diff --git a/libc/bionic/__cxa_thread_atexit_impl.cpp b/libc/bionic/__cxa_thread_atexit_impl.cpp
index 0e427d3..0e903b9 100644
--- a/libc/bionic/__cxa_thread_atexit_impl.cpp
+++ b/libc/bionic/__cxa_thread_atexit_impl.cpp
@@ -15,6 +15,8 @@
  */
 #include <sys/cdefs.h>
 
+#include "pthread_internal.h"
+
 struct thread_local_dtor {
   void (*func) (void *);
   void *arg;
@@ -22,25 +24,24 @@
   thread_local_dtor* next;
 };
 
-static __thread thread_local_dtor* thread_local_dtors = nullptr;
-
 extern "C" int __cxa_thread_atexit_impl(void (*func) (void *), void *arg, void *dso_handle) {
   thread_local_dtor* dtor = new thread_local_dtor();
 
   dtor->func = func;
   dtor->arg = arg;
   dtor->dso_handle = dso_handle;
-  dtor->next = thread_local_dtors;
 
-  thread_local_dtors = dtor;
-
+  pthread_internal_t* thread = __get_thread();
+  dtor->next = thread->thread_local_dtors;
+  thread->thread_local_dtors = dtor;
   return 0;
 }
 
 extern "C" __LIBC_HIDDEN__ void __cxa_thread_finalize() {
-  while (thread_local_dtors != nullptr) {
-    thread_local_dtor* current = thread_local_dtors;
-    thread_local_dtors = current->next;
+  pthread_internal_t* thread = __get_thread();
+  while (thread->thread_local_dtors != nullptr) {
+    thread_local_dtor* current = thread->thread_local_dtors;
+    thread->thread_local_dtors = current->next;
 
     current->func(current->arg);
     delete current;
diff --git a/libc/bionic/__set_errno.cpp b/libc/bionic/__set_errno.cpp
index 30df350..9ef0047 100644
--- a/libc/bionic/__set_errno.cpp
+++ b/libc/bionic/__set_errno.cpp
@@ -36,20 +36,13 @@
 // system these are the same size, but on a 64-bit system they're not.
 // 'long' gives us 32-bit on 32-bit systems, 64-bit on 64-bit systems.
 
-// __set_errno was mistakenly exposed in <errno.h> in the 32-bit NDK.
-// We need the extra level of indirection so that the .hidden directives
-// in the system call stubs don't cause __set_errno to be hidden, breaking
-// old NDK apps.
+// Since __set_errno was mistakenly exposed in <errno.h> in the 32-bit
+// NDK, use a differently named internal function for the system call
+// stubs. This avoids having the stubs .hidden directives accidentally
+// hide __set_errno for old NDK apps.
 
 // This one is for internal use only and used by both LP32 and LP64 assembler.
 extern "C" __LIBC_HIDDEN__ long __set_errno_internal(int n) {
   errno = n;
   return -1;
 }
-
-// This one exists for the LP32 NDK and is not present at all in LP64.
-#if !defined(__LP64__)
-extern "C" long __set_errno(int n) {
-  return __set_errno_internal(n);
-}
-#endif
diff --git a/libc/bionic/bionic_time_conversions.cpp b/libc/bionic/bionic_time_conversions.cpp
index 75e8d49..f3ca46a 100644
--- a/libc/bionic/bionic_time_conversions.cpp
+++ b/libc/bionic/bionic_time_conversions.cpp
@@ -52,18 +52,12 @@
   tv.tv_usec = ts.tv_nsec / 1000;
 }
 
-// Initializes 'ts' with the difference between 'abs_ts' and the current time
-// according to 'clock'. Returns false if abstime already expired, true otherwise.
-bool timespec_from_absolute_timespec(timespec& ts, const timespec& abs_ts, clockid_t clock) {
-  clock_gettime(clock, &ts);
-  ts.tv_sec = abs_ts.tv_sec - ts.tv_sec;
-  ts.tv_nsec = abs_ts.tv_nsec - ts.tv_nsec;
-  if (ts.tv_nsec < 0) {
-    ts.tv_sec--;
-    ts.tv_nsec += NS_PER_S;
+void absolute_timespec_from_timespec(timespec& abs_ts, const timespec& ts, clockid_t clock) {
+  clock_gettime(clock, &abs_ts);
+  abs_ts.tv_sec += ts.tv_sec;
+  abs_ts.tv_nsec += ts.tv_nsec;
+  if (abs_ts.tv_nsec >= NS_PER_S) {
+    abs_ts.tv_nsec -= NS_PER_S;
+    abs_ts.tv_sec++;
   }
-  if (ts.tv_nsec < 0 || ts.tv_sec < 0) {
-    return false;
-  }
-  return true;
 }
diff --git a/libc/bionic/flockfile.cpp b/libc/bionic/flockfile.cpp
index b73907cb..db53828 100644
--- a/libc/bionic/flockfile.cpp
+++ b/libc/bionic/flockfile.cpp
@@ -36,23 +36,15 @@
 // struct __sfileext (see fileext.h).
 
 void flockfile(FILE* fp) {
-  if (!__sdidinit) {
-    __sinit();
-  }
-
-  if (fp != NULL) {
+  if (fp != nullptr) {
     pthread_mutex_lock(&_FLOCK(fp));
   }
 }
 
 int ftrylockfile(FILE* fp) {
-  if (!__sdidinit) {
-    __sinit();
-  }
-
   // The specification for ftrylockfile() says it returns 0 on success,
   // or non-zero on error. So return an errno code directly on error.
-  if (fp == NULL) {
+  if (fp == nullptr) {
     return EINVAL;
   }
 
@@ -60,11 +52,7 @@
 }
 
 void funlockfile(FILE* fp) {
-  if (!__sdidinit) {
-    __sinit();
-  }
-
-  if (fp != NULL) {
+  if (fp != nullptr) {
     pthread_mutex_unlock(&_FLOCK(fp));
   }
 }
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/legacy_32_bit_support.cpp b/libc/bionic/legacy_32_bit_support.cpp
index 20d258d..f2bb37d 100644
--- a/libc/bionic/legacy_32_bit_support.cpp
+++ b/libc/bionic/legacy_32_bit_support.cpp
@@ -111,3 +111,22 @@
 int setrlimit64(int resource, const rlimit64* limits64) {
   return prlimit64(0, resource, limits64, NULL);
 }
+
+// There is no prlimit system call, so we need to use prlimit64.
+int prlimit(pid_t pid, int resource, const rlimit* n32, rlimit* o32) {
+  rlimit64 n64;
+  if (n32 != nullptr) {
+    n64.rlim_cur = (n32->rlim_cur == RLIM_INFINITY) ? RLIM64_INFINITY : n32->rlim_cur;
+    n64.rlim_max = (n32->rlim_max == RLIM_INFINITY) ? RLIM64_INFINITY : n32->rlim_max;
+  }
+
+  rlimit64 o64;
+  int result = prlimit64(pid, resource,
+                         (n32 != nullptr) ? &n64 : nullptr,
+                         (o32 != nullptr) ? &o64 : nullptr);
+  if (result != -1 && o32 != nullptr) {
+    o32->rlim_cur = (o64.rlim_cur == RLIM64_INFINITY) ? RLIM_INFINITY : o64.rlim_cur;
+    o32->rlim_max = (o64.rlim_max == RLIM64_INFINITY) ? RLIM_INFINITY : o64.rlim_max;
+  }
+  return result;
+}
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index 4995414..8f1ee95 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -110,6 +110,7 @@
   // Initialize libc globals that are needed in both the linker and in libc.
   // In dynamic binaries, this is run at least twice for different copies of the
   // globals, once for the linker's copy and once for the one in libc.so.
+  __libc_auxv = args.auxv;
   __libc_globals.initialize();
   __libc_globals.mutate([&args](libc_globals* globals) {
     __libc_init_vdso(globals, args);
@@ -121,7 +122,6 @@
   // Initialize various globals.
   environ = args.envp;
   errno = 0;
-  __libc_auxv = args.auxv;
   __progname = args.argv[0] ? args.argv[0] : "<unknown>";
   __abort_message_ptr = args.abort_message_ptr;
 
@@ -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/libc_init_dynamic.cpp b/libc/bionic/libc_init_dynamic.cpp
index 3bb6e89..edf6a44 100644
--- a/libc/bionic/libc_init_dynamic.cpp
+++ b/libc/bionic/libc_init_dynamic.cpp
@@ -50,11 +50,11 @@
 #include <elf.h>
 #include "libc_init_common.h"
 
+#include "private/bionic_globals.h"
 #include "private/bionic_tls.h"
 #include "private/KernelArgumentBlock.h"
 
 extern "C" {
-  extern void malloc_debug_init(void);
   extern void malloc_debug_fini(void);
   extern void netdClientInit(void);
   extern int __cxa_atexit(void (*)(void *), void *, void *);
@@ -78,7 +78,7 @@
   __libc_init_common(*args);
 
   // Hooks for various libraries to let them know that we're starting up.
-  malloc_debug_init();
+  __libc_globals.mutate(__libc_init_malloc);
   netdClientInit();
 }
 
diff --git a/libc/bionic/libc_logging.cpp b/libc/bionic/libc_logging.cpp
index 2a987b9..67bb052 100644
--- a/libc/bionic/libc_logging.cpp
+++ b/libc/bionic/libc_logging.cpp
@@ -31,6 +31,7 @@
 
 #include <android/set_abort_message.h>
 #include <assert.h>
+#include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <pthread.h>
@@ -46,6 +47,9 @@
 #include <time.h>
 #include <unistd.h>
 
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
 static pthread_mutex_t g_abort_msg_lock = PTHREAD_MUTEX_INITIALIZER;
 
 __LIBC_HIDDEN__ abort_msg_t** __abort_message_ptr; // Accessible to __libc_init_common.
@@ -481,6 +485,64 @@
   return log_fd;
 }
 
+struct cache {
+  const prop_info* pinfo;
+  uint32_t serial;
+  char c;
+};
+
+static void refresh_cache(struct cache *cache, const char *key)
+{
+  if (!cache->pinfo) {
+    cache->pinfo = __system_property_find(key);
+    if (!cache->pinfo) {
+      return;
+    }
+  }
+  uint32_t serial = __system_property_serial(cache->pinfo);
+  if (serial == cache->serial) {
+    return;
+  }
+  cache->serial = serial;
+
+  char buf[PROP_VALUE_MAX];
+  __system_property_read(cache->pinfo, 0, buf);
+  cache->c = buf[0];
+}
+
+// Timestamp state generally remains constant, since a change is
+// rare, we can accept a trylock failure gracefully.
+static pthread_mutex_t lock_clockid = PTHREAD_MUTEX_INITIALIZER;
+
+static clockid_t __android_log_clockid()
+{
+  static struct cache r_time_cache = { NULL, static_cast<uint32_t>(-1), 0 };
+  static struct cache p_time_cache = { NULL, static_cast<uint32_t>(-1), 0 };
+  char c;
+
+  if (pthread_mutex_trylock(&lock_clockid)) {
+    // We are willing to accept some race in this context
+    if (!(c = p_time_cache.c)) {
+      c = r_time_cache.c;
+    }
+  } else {
+    static uint32_t serial;
+    uint32_t current_serial = __system_property_area_serial();
+    if (current_serial != serial) {
+      refresh_cache(&r_time_cache, "ro.logd.timestamp");
+      refresh_cache(&p_time_cache, "persist.logd.timestamp");
+      serial = current_serial;
+    }
+    if (!(c = p_time_cache.c)) {
+      c = r_time_cache.c;
+    }
+
+    pthread_mutex_unlock(&lock_clockid);
+  }
+
+  return (tolower(c) == 'm') ? CLOCK_MONOTONIC : CLOCK_REALTIME;
+}
+
 struct log_time { // Wire format
   uint32_t tv_sec;
   uint32_t tv_nsec;
@@ -501,7 +563,7 @@
   vec[1].iov_base = &tid;
   vec[1].iov_len = sizeof(tid);
   timespec ts;
-  clock_gettime(CLOCK_REALTIME, &ts);
+  clock_gettime(__android_log_clockid(), &ts);
   log_time realtime_ts;
   realtime_ts.tv_sec = ts.tv_sec;
   realtime_ts.tv_nsec = ts.tv_nsec;
@@ -544,7 +606,7 @@
   vec[1].iov_base = &tid;
   vec[1].iov_len = sizeof(tid);
   timespec ts;
-  clock_gettime(CLOCK_REALTIME, &ts);
+  clock_gettime(__android_log_clockid(), &ts);
   log_time realtime_ts;
   realtime_ts.tv_sec = ts.tv_sec;
   realtime_ts.tv_nsec = ts.tv_nsec;
diff --git a/libc/bionic/libgen.cpp b/libc/bionic/libgen.cpp
index 5c27bb5..c415c0f 100644
--- a/libc/bionic/libgen.cpp
+++ b/libc/bionic/libgen.cpp
@@ -92,7 +92,7 @@
 }
 
 // Since this is a non-standard symbol, it might be hijacked by a basename_r in the executable.
-__LIBC64_HIDDEN__ int basename_r(const char* path, char* buffer, size_t buffer_size) {
+__LIBC32_LEGACY_PUBLIC__ int basename_r(const char* path, char* buffer, size_t buffer_size) {
   return __basename_r(path, buffer, buffer_size);
 }
 
@@ -156,7 +156,7 @@
 }
 
 // Since this is a non-standard symbol, it might be hijacked by a basename_r in the executable.
-__LIBC64_HIDDEN__ int dirname_r(const char* path, char* buffer, size_t buffer_size) {
+__LIBC32_LEGACY_PUBLIC__ int dirname_r(const char* path, char* buffer, size_t buffer_size) {
   return __dirname_r(path, buffer, buffer_size);
 }
 
diff --git a/libc/bionic/malloc_debug_common.cpp b/libc/bionic/malloc_debug_common.cpp
index 5b7c42c..9f7f3ba 100644
--- a/libc/bionic/malloc_debug_common.cpp
+++ b/libc/bionic/malloc_debug_common.cpp
@@ -44,6 +44,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "private/bionic_globals.h"
 #include "private/ScopedPthreadMutexLocker.h"
 
 #if defined(USE_JEMALLOC)
@@ -56,33 +57,29 @@
 #error "Either one of USE_DLMALLOC or USE_JEMALLOC must be defined."
 #endif
 
+static constexpr MallocDebug __libc_malloc_default_dispatch
+  __attribute__((unused)) = {
+    Malloc(calloc),
+    Malloc(free),
+    Malloc(mallinfo),
+    Malloc(malloc),
+    Malloc(malloc_usable_size),
+    Malloc(memalign),
+    Malloc(posix_memalign),
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+    Malloc(pvalloc),
+#endif
+    Malloc(realloc),
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+    Malloc(valloc),
+#endif
+  };
+
 // In a VM process, this is set to 1 after fork()ing out of zygote.
 int gMallocLeakZygoteChild = 0;
 
 static HashTable g_hash_table;
 
-// Support for malloc debugging.
-// Table for dispatching malloc calls, initialized with default dispatchers.
-static const MallocDebug __libc_malloc_default_dispatch __attribute__((aligned(32))) = {
-  Malloc(calloc),
-  Malloc(free),
-  Malloc(mallinfo),
-  Malloc(malloc),
-  Malloc(malloc_usable_size),
-  Malloc(memalign),
-  Malloc(posix_memalign),
-#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-  Malloc(pvalloc),
-#endif
-  Malloc(realloc),
-#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-  Malloc(valloc),
-#endif
-};
-
-// Selector of dispatch table to use for dispatching malloc calls.
-static const MallocDebug* __libc_malloc_dispatch = &__libc_malloc_default_dispatch;
-
 // Handle to shared library where actual memory allocation is implemented.
 // This library is loaded and memory allocation calls are redirected there
 // when libc.debug.malloc environment variable contains value other than
@@ -244,46 +241,87 @@
 // Allocation functions
 // =============================================================================
 extern "C" void* calloc(size_t n_elements, size_t elem_size) {
-  return __libc_malloc_dispatch->calloc(n_elements, elem_size);
+  auto _calloc = __libc_globals->malloc_dispatch.calloc;
+  if (__predict_false(_calloc != nullptr)) {
+    return _calloc(n_elements, elem_size);
+  }
+  return Malloc(calloc)(n_elements, elem_size);
 }
 
 extern "C" void free(void* mem) {
-  __libc_malloc_dispatch->free(mem);
+  auto _free = __libc_globals->malloc_dispatch.free;
+  if (__predict_false(_free != nullptr)) {
+    _free(mem);
+  } else {
+    Malloc(free)(mem);
+  }
 }
 
 extern "C" struct mallinfo mallinfo() {
-  return __libc_malloc_dispatch->mallinfo();
+  auto _mallinfo = __libc_globals->malloc_dispatch.mallinfo;
+  if (__predict_false(_mallinfo != nullptr)) {
+    return _mallinfo();
+  }
+  return Malloc(mallinfo)();
 }
 
 extern "C" void* malloc(size_t bytes) {
-  return __libc_malloc_dispatch->malloc(bytes);
+  auto _malloc = __libc_globals->malloc_dispatch.malloc;
+  if (__predict_false(_malloc != nullptr)) {
+    return _malloc(bytes);
+  }
+  return Malloc(malloc)(bytes);
 }
 
 extern "C" size_t malloc_usable_size(const void* mem) {
-  return __libc_malloc_dispatch->malloc_usable_size(mem);
+  auto _malloc_usable_size = __libc_globals->malloc_dispatch.malloc_usable_size;
+  if (__predict_false(_malloc_usable_size != nullptr)) {
+    return _malloc_usable_size(mem);
+  }
+  return Malloc(malloc_usable_size)(mem);
 }
 
 extern "C" void* memalign(size_t alignment, size_t bytes) {
-  return __libc_malloc_dispatch->memalign(alignment, bytes);
+  auto _memalign = __libc_globals->malloc_dispatch.memalign;
+  if (__predict_false(_memalign != nullptr)) {
+    return _memalign(alignment, bytes);
+  }
+  return Malloc(memalign)(alignment, bytes);
 }
 
 extern "C" int posix_memalign(void** memptr, size_t alignment, size_t size) {
-  return __libc_malloc_dispatch->posix_memalign(memptr, alignment, size);
+  auto _posix_memalign = __libc_globals->malloc_dispatch.posix_memalign;
+  if (__predict_false(_posix_memalign != nullptr)) {
+    return _posix_memalign(memptr, alignment, size);
+  }
+  return Malloc(posix_memalign)(memptr, alignment, size);
 }
 
 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
 extern "C" void* pvalloc(size_t bytes) {
-  return __libc_malloc_dispatch->pvalloc(bytes);
+  auto _pvalloc = __libc_globals->malloc_dispatch.pvalloc;
+  if (__predict_false(_pvalloc != nullptr)) {
+    return _pvalloc(bytes);
+  }
+  return Malloc(pvalloc)(bytes);
 }
 #endif
 
-extern "C" void* realloc(void* oldMem, size_t bytes) {
-  return __libc_malloc_dispatch->realloc(oldMem, bytes);
+extern "C" void* realloc(void* old_mem, size_t bytes) {
+  auto _realloc = __libc_globals->malloc_dispatch.realloc;
+  if (__predict_false(_realloc != nullptr)) {
+    return _realloc(old_mem, bytes);
+  }
+  return Malloc(realloc)(old_mem, bytes);
 }
 
 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
 extern "C" void* valloc(size_t bytes) {
-  return __libc_malloc_dispatch->valloc(bytes);
+  auto _valloc = __libc_globals->malloc_dispatch.valloc;
+  if (__predict_false(_valloc != nullptr)) {
+    return _valloc(bytes);
+  }
+  return Malloc(valloc)(bytes);
 }
 #endif
 
@@ -326,7 +364,7 @@
 }
 
 // Initializes memory allocation framework once per process.
-static void malloc_init_impl() {
+static void malloc_init_impl(libc_globals* globals) {
   const char* so_name = NULL;
   MallocDebugInit malloc_debug_initialize = NULL;
   unsigned int qemu_running = 0;
@@ -442,7 +480,7 @@
 
   // No need to init the dispatch table because we can only get
   // here if debug level is 1, 5, 10, or 20.
-  static MallocDebug malloc_dispatch_table __attribute__((aligned(32)));
+  MallocDebug malloc_dispatch_table;
   switch (g_malloc_debug_level) {
     case 1:
       InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "leak");
@@ -480,7 +518,7 @@
               getprogname(), g_malloc_debug_level);
     dlclose(malloc_impl_handle);
   } else {
-    __libc_malloc_dispatch = &malloc_dispatch_table;
+    globals->malloc_dispatch = malloc_dispatch_table;
     libc_malloc_impl_handle = malloc_impl_handle;
   }
 }
@@ -507,15 +545,12 @@
 #endif  // !LIBC_STATIC
 
 // Initializes memory allocation framework.
-// This routine is called from __libc_init routines implemented
-// in libc_init_static.c and libc_init_dynamic.c files.
-extern "C" __LIBC_HIDDEN__ void malloc_debug_init() {
+// This routine is called from __libc_init routines in libc_init_dynamic.cpp.
+__LIBC_HIDDEN__ void __libc_init_malloc(libc_globals* globals) {
+  (void)globals;
 #if !defined(LIBC_STATIC)
-  static pthread_once_t malloc_init_once_ctl = PTHREAD_ONCE_INIT;
-  if (pthread_once(&malloc_init_once_ctl, malloc_init_impl)) {
-    error_log("Unable to initialize malloc_debug component.");
-  }
-#endif  // !LIBC_STATIC
+  malloc_init_impl(globals);
+#endif
 }
 
 extern "C" __LIBC_HIDDEN__ void malloc_debug_fini() {
diff --git a/libc/bionic/malloc_debug_common.h b/libc/bionic/malloc_debug_common.h
index 5c73da3..f8745da 100644
--- a/libc/bionic/malloc_debug_common.h
+++ b/libc/bionic/malloc_debug_common.h
@@ -38,6 +38,7 @@
 #include <stdlib.h>
 
 #include "private/bionic_config.h"
+#include "private/bionic_malloc_dispatch.h"
 #include "private/libc_logging.h"
 
 #define HASHTABLE_SIZE      1543
@@ -72,39 +73,6 @@
     HashEntry* slots[HASHTABLE_SIZE];
 };
 
-/* Entry in malloc dispatch table. */
-typedef void* (*MallocDebugCalloc)(size_t, size_t);
-typedef void (*MallocDebugFree)(void*);
-typedef struct mallinfo (*MallocDebugMallinfo)();
-typedef void* (*MallocDebugMalloc)(size_t);
-typedef size_t (*MallocDebugMallocUsableSize)(const void*);
-typedef void* (*MallocDebugMemalign)(size_t, size_t);
-typedef int (*MallocDebugPosixMemalign)(void**, size_t, size_t);
-#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-typedef void* (*MallocDebugPvalloc)(size_t);
-#endif
-typedef void* (*MallocDebugRealloc)(void*, size_t);
-#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-typedef void* (*MallocDebugValloc)(size_t);
-#endif
-
-struct MallocDebug {
-  MallocDebugCalloc calloc;
-  MallocDebugFree free;
-  MallocDebugMallinfo mallinfo;
-  MallocDebugMalloc malloc;
-  MallocDebugMallocUsableSize malloc_usable_size;
-  MallocDebugMemalign memalign;
-  MallocDebugPosixMemalign posix_memalign;
-#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-  MallocDebugPvalloc pvalloc;
-#endif
-  MallocDebugRealloc realloc;
-#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-  MallocDebugValloc valloc;
-#endif
-};
-
 typedef bool (*MallocDebugInit)(HashTable*, const MallocDebug*);
 typedef void (*MallocDebugFini)(int);
 
diff --git a/libc/bionic/mmap.cpp b/libc/bionic/mmap.cpp
index 794f50f..57a8cdf 100644
--- a/libc/bionic/mmap.cpp
+++ b/libc/bionic/mmap.cpp
@@ -27,6 +27,7 @@
  */
 
 #include <errno.h>
+#include <stdint.h>
 #include <sys/mman.h>
 #include <unistd.h>
 
@@ -48,15 +49,19 @@
 
   // prevent allocations large enough for `end - start` to overflow
   size_t rounded = BIONIC_ALIGN(size, PAGE_SIZE);
-  if (rounded < size || size > PTRDIFF_MAX) {
+  if (rounded < size || rounded > PTRDIFF_MAX) {
     errno = ENOMEM;
     return MAP_FAILED;
   }
 
-  bool is_private_anonymous = (flags & (MAP_PRIVATE | MAP_ANONYMOUS)) != 0;
+  bool is_private_anonymous =
+      (flags & (MAP_PRIVATE | MAP_ANONYMOUS)) == (MAP_PRIVATE | MAP_ANONYMOUS);
+  bool is_stack_or_grows_down = (flags & (MAP_STACK | MAP_GROWSDOWN)) != 0;
+
   void* result = __mmap2(addr, size, prot, flags, fd, offset >> MMAP2_SHIFT);
 
-  if (result != MAP_FAILED && kernel_has_MADV_MERGEABLE && is_private_anonymous) {
+  if (result != MAP_FAILED && kernel_has_MADV_MERGEABLE &&
+      is_private_anonymous && !is_stack_or_grows_down) {
     ErrnoRestorer errno_restorer;
     int rc = madvise(result, size, MADV_MERGEABLE);
     if (rc == -1 && errno == EINVAL) {
diff --git a/libc/bionic/ioctl.c b/libc/bionic/mremap.cpp
similarity index 61%
copy from libc/bionic/ioctl.c
copy to libc/bionic/mremap.cpp
index 6dd95d0..6653d43 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,33 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
+#include <errno.h>
+#include <sys/mman.h>
 #include <stdarg.h>
+#include <stdint.h>
+#include <unistd.h>
 
-extern int __ioctl(int, int, void *);
+#include "private/bionic_macros.h"
 
-int ioctl(int fd, int request, ...)
-{
+extern "C" void* ___mremap(void*, size_t, size_t, int, void*);
+
+void* mremap(void* old_address, size_t old_size, size_t new_size, int flags, ...) {
+  // prevent allocations large enough for `end - start` to overflow
+  size_t rounded = BIONIC_ALIGN(new_size, PAGE_SIZE);
+  if (rounded < new_size || rounded > PTRDIFF_MAX) {
+    errno = ENOMEM;
+    return MAP_FAILED;
+  }
+
+  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/ndk_cruft.cpp b/libc/bionic/ndk_cruft.cpp
index b299684..0429430 100644
--- a/libc/bionic/ndk_cruft.cpp
+++ b/libc/bionic/ndk_cruft.cpp
@@ -47,34 +47,37 @@
 
 #include "private/libc_logging.h"
 
-// The part is only for 32-bit targets.
+extern "C" {
+
+// Brillo doesn't need to support any legacy cruft.
+#if !defined(__BRILLO__)
+
+// Most of the cruft is only for 32-bit Android targets.
 #if !defined(__LP64__)
 
 // These were accidentally declared in <unistd.h> because we stupidly used to inline
 // getpagesize() and __getpageshift(). Needed for backwards compatibility with old NDK apps.
-extern "C" {
-  unsigned int __page_size = PAGE_SIZE;
-  unsigned int __page_shift = 12;
-}
+unsigned int __page_size = PAGE_SIZE;
+unsigned int __page_shift = 12;
 
 // TODO: remove this backward compatibility hack (for jb-mr1 strace binaries).
-extern "C" pid_t __wait4(pid_t pid, int* status, int options, struct rusage* rusage) {
+pid_t __wait4(pid_t pid, int* status, int options, struct rusage* rusage) {
   return wait4(pid, status, options, rusage);
 }
 
 // TODO: does anything still need this?
-extern "C" int __open() {
+int __open() {
   abort();
 }
 
 // TODO: does anything still need this?
-extern "C" void** __get_tls() {
+void** __get_tls() {
 #include "private/__get_tls.h"
   return __get_tls();
 }
 
 // This non-standard function was in our <string.h> for some reason.
-extern "C" void memswap(void* m1, void* m2, size_t n) {
+void memswap(void* m1, void* m2, size_t n) {
   char* p = reinterpret_cast<char*>(m1);
   char* p_end = p + n;
   char* q = reinterpret_cast<char*>(m2);
@@ -87,13 +90,13 @@
   }
 }
 
-extern "C" int pthread_attr_setstackaddr(pthread_attr_t*, void*) {
+int pthread_attr_setstackaddr(pthread_attr_t*, void*) {
   // This was removed from POSIX.1-2008, and is not implemented on bionic.
   // Needed for ABI compatibility with the NDK.
   return ENOSYS;
 }
 
-extern "C" int pthread_attr_getstackaddr(const pthread_attr_t* attr, void** stack_addr) {
+int pthread_attr_getstackaddr(const pthread_attr_t* attr, void** stack_addr) {
   // This was removed from POSIX.1-2008.
   // Needed for ABI compatibility with the NDK.
   *stack_addr = (char*)attr->stack_base + attr->stack_size;
@@ -101,7 +104,7 @@
 }
 
 // Non-standard cruft that should only ever have been in system/core/toolbox.
-extern "C" char* strtotimeval(const char* str, struct timeval* ts) {
+char* strtotimeval(const char* str, struct timeval* ts) {
   char* s;
   ts->tv_sec = strtoumax(str, &s, 10);
 
@@ -143,7 +146,7 @@
 }
 
 // This non-standard function was in our <inttypes.h> for some reason.
-extern "C" uintmax_t strntoumax(const char *nptr, char **endptr, int base, size_t n) {
+uintmax_t strntoumax(const char *nptr, char **endptr, int base, size_t n) {
   const unsigned char*  p   = (const unsigned char *)nptr;
   const unsigned char*  end = p + n;
   int                   minus = 0;
@@ -191,12 +194,12 @@
 }
 
 // This non-standard function was in our <inttypes.h> for some reason.
-extern "C" intmax_t strntoimax(const char* nptr, char** endptr, int base, size_t n) {
+intmax_t strntoimax(const char* nptr, char** endptr, int base, size_t n) {
   return (intmax_t) strntoumax(nptr, endptr, base, n);
 }
 
 // POSIX calls this dprintf, but LP32 Android had fdprintf instead.
-extern "C" int fdprintf(int fd, const char* fmt, ...) {
+int fdprintf(int fd, const char* fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
   int rc = vdprintf(fd, fmt, ap);
@@ -205,7 +208,7 @@
 }
 
 // POSIX calls this vdprintf, but LP32 Android had fdprintf instead.
-extern "C" int vfdprintf(int fd, const char* fmt, va_list ap) {
+int vfdprintf(int fd, const char* fmt, va_list ap) {
   return vdprintf(fd, fmt, ap);
 }
 
@@ -216,64 +219,64 @@
 #undef __futex_wait
 
 // This used to be in <sys/atomics.h>.
-extern "C" int __futex_wake(volatile void* ftx, int count) {
+int __futex_wake(volatile void* ftx, int count) {
   return __real_futex_wake(ftx, count);
 }
 
 // This used to be in <sys/atomics.h>.
-extern "C" int __futex_wait(volatile void* ftx, int value, const struct timespec* timeout) {
+int __futex_wait(volatile void* ftx, int value, const struct timespec* timeout) {
   return __real_futex_wait(ftx, value, timeout);
 }
 
 // Unity's libmono uses this.
-extern "C" int tkill(pid_t tid, int sig) {
+int tkill(pid_t tid, int sig) {
   return syscall(__NR_tkill, tid, sig);
 }
 
 // This was removed from POSIX 2008.
-extern "C" wchar_t* wcswcs(wchar_t* haystack, wchar_t* needle) {
+wchar_t* wcswcs(wchar_t* haystack, wchar_t* needle) {
   return wcsstr(haystack, needle);
 }
 
 // This was removed from POSIX 2008.
-extern "C" sighandler_t bsd_signal(int signum, sighandler_t handler) {
+sighandler_t bsd_signal(int signum, sighandler_t handler) {
   return signal(signum, handler);
 }
 
 #if !defined(__i386__)
 // This was removed from POSIX 2008.
 #undef bcopy
-extern "C" void bcopy(const void* src, void* dst, size_t n) {
-  memcpy(dst, src, n);
+void bcopy(const void* src, void* dst, size_t n) {
+  memmove(dst, src, n);
 }
 #else
 // x86 has an assembler implementation.
 #endif
 
 // sysv_signal() was never in POSIX.
-extern sighandler_t _signal(int signum, sighandler_t handler, int flags);
-extern "C" sighandler_t sysv_signal(int signum, sighandler_t handler) {
+extern "C++" sighandler_t _signal(int signum, sighandler_t handler, int flags);
+sighandler_t sysv_signal(int signum, sighandler_t handler) {
   return _signal(signum, handler, SA_RESETHAND);
 }
 
 // This is a system call that was never in POSIX. Use readdir(3) instead.
-extern "C" int __getdents64(unsigned int, dirent*, unsigned int);
-extern "C" int getdents(unsigned int fd, dirent* dirp, unsigned int count) {
+int __getdents64(unsigned int, dirent*, unsigned int);
+int getdents(unsigned int fd, dirent* dirp, unsigned int count) {
   return __getdents64(fd, dirp, count);
 }
 
 // This is a BSDism that we never implemented correctly. Used by Firefox.
-extern "C" int issetugid() {
+int issetugid() {
   return 0;
 }
 
 // This was removed from POSIX 2004.
-extern "C" pid_t wait3(int* status, int options, struct rusage* rusage) {
+pid_t wait3(int* status, int options, struct rusage* rusage) {
   return wait4(-1, status, options, rusage);
 }
 
 // This was removed from POSIX 2004.
-extern "C" int getdtablesize() {
+int getdtablesize() {
   struct rlimit r;
 
   if (getrlimit(RLIMIT_NOFILE, &r) < 0) {
@@ -283,6 +286,10 @@
   return r.rlim_cur;
 }
 
+// A leaked BSD stdio implementation detail that's now a no-op.
+void __sinit() {}
+int __sdidinit = 1;
+
 // Only used by ftime, which was removed from POSIX 2008.
 struct timeb {
   time_t          time;
@@ -292,7 +299,7 @@
 };
 
 // This was removed from POSIX 2008.
-extern "C" int ftime(struct timeb* tb) {
+int ftime(struct timeb* tb) {
   struct timeval  tv;
   struct timezone tz;
 
@@ -314,35 +321,35 @@
 }
 
 // This was removed from POSIX 2008.
-extern "C" char* index(const char* str, int ch) {
+char* index(const char* str, int ch) {
   return strchr(str, ch);
 }
 
 // This was removed from BSD.
-extern "C" void arc4random_stir(void) {
+void arc4random_stir(void) {
   // The current implementation stirs itself as needed.
 }
 
 // This was removed from BSD.
-extern "C" void arc4random_addrandom(unsigned char*, int) {
+void arc4random_addrandom(unsigned char*, int) {
   // The current implementation adds randomness as needed.
 }
 
 // Old versions of the NDK did not export malloc_usable_size, but did
 // export dlmalloc_usable_size. We are moving away from dlmalloc in L
 // so make this call malloc_usable_size.
-extern "C" size_t dlmalloc_usable_size(void* ptr) {
+size_t dlmalloc_usable_size(void* ptr) {
   return malloc_usable_size(ptr);
 }
 
 // In L we added a public pthread_gettid_np, but some apps were using the private API.
-extern "C" pid_t __pthread_gettid(pthread_t t) {
+pid_t __pthread_gettid(pthread_t t) {
   return pthread_gettid_np(t);
 }
 
 // Older versions of apportable used dlmalloc directly instead of malloc,
 // so export this compatibility shim that simply calls malloc.
-extern "C" void* dlmalloc(size_t size) {
+void* dlmalloc(size_t size) {
   return malloc(size);
 }
 
@@ -350,45 +357,41 @@
 #include "pthread_internal.h"
 #undef __get_thread
 // Various third-party apps contain a backport of our pthread_rwlock implementation that uses this.
-extern "C" pthread_internal_t* __get_thread() {
+pthread_internal_t* __get_thread() {
   return __real_get_thread();
 }
 
+// This one exists only for the LP32 NDK and is not present anywhere else.
+extern long __set_errno_internal(int);
+long __set_errno(int n) {
+  return __set_errno_internal(n);
+}
+
 #endif // !defined(__LP64__)
 
-// This is never implemented in bionic, only needed for ABI compatibility with the NDK.
-extern "C" char* getusershell() {
-  return NULL;
-}
-
-// This is never implemented in bionic, only needed for ABI compatibility with the NDK.
-extern "C" void setusershell() { }
-
-// This is never implemented in bionic, only needed for ABI compatibility with the NDK.
-extern "C" void endusershell() { }
-
-// This is never implemented in bionic, only needed for ABI compatibility with the NDK.
-extern "C" void endpwent() { }
+// This was never implemented in bionic, only needed for ABI compatibility with the NDK.
+// In the M time frame, over 1000 apps have a reference to this!
+void endpwent() { }
 
 // Since dlmalloc_inspect_all and dlmalloc_trim are exported for systems
 // that use dlmalloc, be consistent and export them everywhere.
 #if defined(USE_JEMALLOC)
-extern "C" void dlmalloc_inspect_all(void (*)(void*, void*, size_t, void*), void*) {
+void dlmalloc_inspect_all(void (*)(void*, void*, size_t, void*), void*) {
+}
+int dlmalloc_trim(size_t) {
+    return 0;
 }
 #else
-extern "C" void dlmalloc_inspect_all_real(void (*)(void*, void*, size_t, void*), void*);
-extern "C" void dlmalloc_inspect_all(void (*handler)(void*, void*, size_t, void*), void* arg) {
+void dlmalloc_inspect_all_real(void (*)(void*, void*, size_t, void*), void*);
+void dlmalloc_inspect_all(void (*handler)(void*, void*, size_t, void*), void* arg) {
   dlmalloc_inspect_all_real(handler, arg);
 }
-#endif
-
-#if defined(USE_JEMALLOC)
-extern "C" int dlmalloc_trim(size_t) {
-  return 0;
-}
-#else
-extern "C" int dlmalloc_trim_real(size_t);
-extern "C" int dlmalloc_trim(size_t pad) {
+int dlmalloc_trim_real(size_t);
+int dlmalloc_trim(size_t pad) {
   return dlmalloc_trim_real(pad);
 }
 #endif
+
+#endif // !defined(__BRILLO__)
+
+} // extern "C"
diff --git a/libc/bionic/pthread_barrier.cpp b/libc/bionic/pthread_barrier.cpp
new file mode 100644
index 0000000..1bcd12a
--- /dev/null
+++ b/libc/bionic/pthread_barrier.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <pthread.h>
+#include <stdatomic.h>
+#include <stdint.h>
+
+#include "private/bionic_futex.h"
+
+int pthread_barrierattr_init(pthread_barrierattr_t* attr) {
+  *attr = 0;
+  return 0;
+}
+
+int pthread_barrierattr_destroy(pthread_barrierattr_t* attr) {
+  *attr = 0;
+  return 0;
+}
+
+int pthread_barrierattr_getpshared(pthread_barrierattr_t* attr, int* pshared) {
+  *pshared = (*attr & 1) ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE;
+  return 0;
+}
+
+int pthread_barrierattr_setpshared(pthread_barrierattr_t* attr, int pshared) {
+  if (pshared == PTHREAD_PROCESS_SHARED) {
+    *attr |= 1;
+  } else {
+    *attr &= ~1;
+  }
+  return 0;
+}
+
+enum BarrierState {
+  WAIT,
+  RELEASE,
+};
+
+struct pthread_barrier_internal_t {
+  // One barrier can be used for unlimited number of cycles. In each cycle, [init_count]
+  // threads must call pthread_barrier_wait() before any of them successfully return from
+  // the call. It is undefined behavior if there are more than [init_count] threads call
+  // pthread_barrier_wait() in one cycle.
+  uint32_t init_count;
+  // Barrier state. It is WAIT if waiting for more threads to enter the barrier in this cycle,
+  // otherwise threads are leaving the barrier.
+  _Atomic(BarrierState) state;
+  // Number of threads having entered but not left the barrier in this cycle.
+  atomic_uint wait_count;
+  // Whether the barrier is shared across processes.
+  bool pshared;
+  uint32_t __reserved[4];
+};
+
+static_assert(sizeof(pthread_barrier_t) == sizeof(pthread_barrier_internal_t),
+              "pthread_barrier_t should actually be pthread_barrier_internal_t in implementation."
+              );
+
+static_assert(alignof(pthread_barrier_t) >= 4,
+              "pthread_barrier_t should fulfill the alignment of pthread_barrier_internal_t.");
+
+static inline pthread_barrier_internal_t* __get_internal_barrier(pthread_barrier_t* barrier) {
+  return reinterpret_cast<pthread_barrier_internal_t*>(barrier);
+}
+
+int pthread_barrier_init(pthread_barrier_t* barrier_interface, const pthread_barrierattr_t* attr,
+                         unsigned count) {
+  pthread_barrier_internal_t* barrier = __get_internal_barrier(barrier_interface);
+  if (count == 0) {
+    return EINVAL;
+  }
+  barrier->init_count = count;
+  atomic_init(&barrier->state, WAIT);
+  atomic_init(&barrier->wait_count, 0);
+  barrier->pshared = false;
+  if (attr != nullptr && (*attr & 1)) {
+    barrier->pshared = true;
+  }
+  return 0;
+}
+
+// According to POSIX standard, pthread_barrier_wait() synchronizes memory between participating
+// threads. It means all memory operations made by participating threads before calling
+// pthread_barrier_wait() can be seen by all participating threads after the function call.
+// We establish this by making a happens-before relation between all threads entering the barrier
+// with the last thread entering the barrier, and a happens-before relation between the last
+// thread entering the barrier with all threads leaving the barrier.
+int pthread_barrier_wait(pthread_barrier_t* barrier_interface) {
+  pthread_barrier_internal_t* barrier = __get_internal_barrier(barrier_interface);
+
+  // Wait until all threads for the previous cycle have left the barrier. This is needed
+  // as a participating thread can call pthread_barrier_wait() again before other
+  // threads have left the barrier. Use acquire operation here to synchronize with
+  // the last thread leaving the previous cycle, so we can read correct wait_count below.
+  while(atomic_load_explicit(&barrier->state, memory_order_acquire) == RELEASE) {
+    __futex_wait_ex(&barrier->state, barrier->pshared, RELEASE, false, nullptr);
+  }
+
+  uint32_t prev_wait_count = atomic_load_explicit(&barrier->wait_count, memory_order_relaxed);
+  while (true) {
+    // It happens when there are more than [init_count] threads trying to enter the barrier
+    // at one cycle. We read the POSIX standard as disallowing this, since additional arriving
+    // threads are not synchronized with respect to the barrier reset. We also don't know of
+    // any reasonable cases in which this would be intentional.
+    if (prev_wait_count >= barrier->init_count) {
+      return EINVAL;
+    }
+    // Use memory_order_acq_rel operation here to synchronize between all threads entering
+    // the barrier with the last thread entering the barrier.
+    if (atomic_compare_exchange_weak_explicit(&barrier->wait_count, &prev_wait_count,
+                                              prev_wait_count + 1u, memory_order_acq_rel,
+                                              memory_order_relaxed)) {
+      break;
+    }
+  }
+
+  int result = 0;
+  if (prev_wait_count + 1 == barrier->init_count) {
+    result = PTHREAD_BARRIER_SERIAL_THREAD;
+    if (prev_wait_count != 0) {
+      // Use release operation here to synchronize between the last thread entering the
+      // barrier with all threads leaving the barrier.
+      atomic_store_explicit(&barrier->state, RELEASE, memory_order_release);
+      __futex_wake_ex(&barrier->state, barrier->pshared, prev_wait_count);
+    }
+  } else {
+    // Use acquire operation here to synchronize between the last thread entering the
+    // barrier with all threads leaving the barrier.
+    while (atomic_load_explicit(&barrier->state, memory_order_acquire) == WAIT) {
+      __futex_wait_ex(&barrier->state, barrier->pshared, WAIT, false, nullptr);
+    }
+  }
+  // Use release operation here to make it not reordered with previous operations.
+  if (atomic_fetch_sub_explicit(&barrier->wait_count, 1, memory_order_release) == 1) {
+    // Use release operation here to synchronize with threads entering the barrier for
+    // the next cycle, or the thread calling pthread_barrier_destroy().
+    atomic_store_explicit(&barrier->state, WAIT, memory_order_release);
+    __futex_wake_ex(&barrier->state, barrier->pshared, barrier->init_count);
+  }
+  return result;
+}
+
+int pthread_barrier_destroy(pthread_barrier_t* barrier_interface) {
+  pthread_barrier_internal_t* barrier = __get_internal_barrier(barrier_interface);
+  if (barrier->init_count == 0) {
+    return EINVAL;
+  }
+  // Use acquire operation here to synchronize with the last thread leaving the barrier.
+  // So we can read correct wait_count below.
+  while (atomic_load_explicit(&barrier->state, memory_order_acquire) == RELEASE) {
+    __futex_wait_ex(&barrier->state, barrier->pshared, RELEASE, false, nullptr);
+  }
+  if (atomic_load_explicit(&barrier->wait_count, memory_order_relaxed) != 0) {
+    return EBUSY;
+  }
+  barrier->init_count = 0;
+  return 0;
+}
diff --git a/libc/bionic/pthread_cond.cpp b/libc/bionic/pthread_cond.cpp
index 4a69da5..adbce07 100644
--- a/libc/bionic/pthread_cond.cpp
+++ b/libc/bionic/pthread_cond.cpp
@@ -111,8 +111,8 @@
     return COND_IS_SHARED(atomic_load_explicit(&state, memory_order_relaxed));
   }
 
-  int get_clock() {
-    return COND_GET_CLOCK(atomic_load_explicit(&state, memory_order_relaxed));
+  bool use_realtime_clock() {
+    return COND_GET_CLOCK(atomic_load_explicit(&state, memory_order_relaxed)) == CLOCK_REALTIME;
   }
 
 #if defined(__LP64__)
@@ -170,12 +170,17 @@
   return 0;
 }
 
-static int __pthread_cond_timedwait_relative(pthread_cond_internal_t* cond, pthread_mutex_t* mutex,
-                                             const timespec* rel_timeout_or_null) {
-  unsigned int old_state = atomic_load_explicit(&cond->state, memory_order_relaxed);
+static int __pthread_cond_timedwait(pthread_cond_internal_t* cond, pthread_mutex_t* mutex,
+                                    bool use_realtime_clock, const timespec* abs_timeout_or_null) {
+  int result = check_timespec(abs_timeout_or_null);
+  if (result != 0) {
+    return result;
+  }
 
+  unsigned int old_state = atomic_load_explicit(&cond->state, memory_order_relaxed);
   pthread_mutex_unlock(mutex);
-  int status = __futex_wait_ex(&cond->state, cond->process_shared(), old_state, rel_timeout_or_null);
+  int status = __futex_wait_ex(&cond->state, cond->process_shared(), old_state,
+                               use_realtime_clock, abs_timeout_or_null);
   pthread_mutex_lock(mutex);
 
   if (status == -ETIMEDOUT) {
@@ -184,21 +189,6 @@
   return 0;
 }
 
-static int __pthread_cond_timedwait(pthread_cond_internal_t* cond, pthread_mutex_t* mutex,
-                                    const timespec* abs_timeout_or_null, clockid_t clock) {
-  timespec ts;
-  timespec* rel_timeout = NULL;
-
-  if (abs_timeout_or_null != NULL) {
-    rel_timeout = &ts;
-    if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, clock)) {
-      return ETIMEDOUT;
-    }
-  }
-
-  return __pthread_cond_timedwait_relative(cond, mutex, rel_timeout);
-}
-
 int pthread_cond_broadcast(pthread_cond_t* cond_interface) {
   return __pthread_cond_pulse(__get_internal_cond(cond_interface), INT_MAX);
 }
@@ -209,14 +199,14 @@
 
 int pthread_cond_wait(pthread_cond_t* cond_interface, pthread_mutex_t* mutex) {
   pthread_cond_internal_t* cond = __get_internal_cond(cond_interface);
-  return __pthread_cond_timedwait(cond, mutex, NULL, cond->get_clock());
+  return __pthread_cond_timedwait(cond, mutex, false, nullptr);
 }
 
 int pthread_cond_timedwait(pthread_cond_t *cond_interface, pthread_mutex_t * mutex,
                            const timespec *abstime) {
 
   pthread_cond_internal_t* cond = __get_internal_cond(cond_interface);
-  return __pthread_cond_timedwait(cond, mutex, abstime, cond->get_clock());
+  return __pthread_cond_timedwait(cond, mutex, cond->use_realtime_clock(), abstime);
 }
 
 #if !defined(__LP64__)
@@ -225,8 +215,7 @@
                                                 pthread_mutex_t* mutex,
                                                 const timespec* abs_timeout) {
 
-  return __pthread_cond_timedwait(__get_internal_cond(cond_interface), mutex, abs_timeout,
-                                  CLOCK_MONOTONIC);
+  return __pthread_cond_timedwait(__get_internal_cond(cond_interface), mutex, false, abs_timeout);
 }
 
 extern "C" int pthread_cond_timedwait_monotonic_np(pthread_cond_t* cond_interface,
@@ -238,8 +227,13 @@
 extern "C" int pthread_cond_timedwait_relative_np(pthread_cond_t* cond_interface,
                                                   pthread_mutex_t* mutex,
                                                   const timespec* rel_timeout) {
-
-  return __pthread_cond_timedwait_relative(__get_internal_cond(cond_interface), mutex, rel_timeout);
+  timespec ts;
+  timespec* abs_timeout = nullptr;
+  if (rel_timeout != nullptr) {
+    absolute_timespec_from_timespec(ts, *rel_timeout, CLOCK_REALTIME);
+    abs_timeout = &ts;
+  }
+  return __pthread_cond_timedwait(__get_internal_cond(cond_interface), mutex, true, abs_timeout);
 }
 
 extern "C" int pthread_cond_timeout_np(pthread_cond_t* cond_interface,
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index e260e97..34826db 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -53,13 +53,6 @@
 
 // This code is used both by each new pthread and the code that initializes the main thread.
 void __init_tls(pthread_internal_t* thread) {
-  if (thread->mmap_size == 0) {
-    // If the TLS area was not allocated by mmap(), it may not have been cleared to zero.
-    // So assume the worst and zero the TLS area.
-    memset(thread->tls, 0, sizeof(thread->tls));
-    memset(thread->key_data, 0, sizeof(thread->key_data));
-  }
-
   // Slot 0 must point to itself. The x86 Linux kernel reads the TLS from %fs:0.
   thread->tls[TLS_SLOT_SELF] = thread->tls;
   thread->tls[TLS_SLOT_THREAD_ID] = thread;
@@ -175,6 +168,11 @@
                 (reinterpret_cast<uintptr_t>(stack_top) - sizeof(pthread_internal_t)) & ~0xf);
 
   pthread_internal_t* thread = reinterpret_cast<pthread_internal_t*>(stack_top);
+  if (mmap_size == 0) {
+    // If thread was not allocated by mmap(), it may not have been cleared to zero.
+    // So assume the worst and zero it.
+    memset(thread, 0, sizeof(pthread_internal_t));
+  }
   attr->stack_size = stack_top - reinterpret_cast<uint8_t*>(attr->stack_base);
 
   thread->mmap_size = mmap_size;
@@ -193,8 +191,7 @@
   // notify gdb about this thread before we start doing anything.
   // This also provides the memory barrier needed to ensure that all memory
   // accesses previously made by the creating thread are visible to us.
-  pthread_mutex_lock(&thread->startup_handshake_mutex);
-  pthread_mutex_destroy(&thread->startup_handshake_mutex);
+  thread->startup_handshake_lock.lock();
 
   __init_alternate_signal_stack(thread);
 
@@ -233,14 +230,14 @@
     return result;
   }
 
-  // Create a mutex for the thread in TLS to wait on once it starts so we can keep
+  // Create a lock for the thread to wait on once it starts so we can keep
   // it from doing anything until after we notify the debugger about it
   //
   // This also provides the memory barrier we need to ensure that all
   // memory accesses previously performed by this thread are visible to
   // the new thread.
-  pthread_mutex_init(&thread->startup_handshake_mutex, NULL);
-  pthread_mutex_lock(&thread->startup_handshake_mutex);
+  thread->startup_handshake_lock.init(false);
+  thread->startup_handshake_lock.lock();
 
   thread->start_routine = start_routine;
   thread->start_routine_arg = arg;
@@ -263,7 +260,7 @@
     // We don't have to unlock the mutex at all because clone(2) failed so there's no child waiting to
     // be unblocked, but we're about to unmap the memory the mutex is stored in, so this serves as a
     // reminder that you can't rewrite this function to use a ScopedPthreadMutexLocker.
-    pthread_mutex_unlock(&thread->startup_handshake_mutex);
+    thread->startup_handshake_lock.unlock();
     if (thread->mmap_size != 0) {
       munmap(thread->attr.stack_base, thread->mmap_size);
     }
@@ -278,13 +275,13 @@
     atomic_store(&thread->join_state, THREAD_DETACHED);
     __pthread_internal_add(thread);
     thread->start_routine = __do_nothing;
-    pthread_mutex_unlock(&thread->startup_handshake_mutex);
+    thread->startup_handshake_lock.unlock();
     return init_errno;
   }
 
   // Publish the pthread_t and unlock the mutex to let the new thread start running.
   *thread_out = __pthread_internal_add(thread);
-  pthread_mutex_unlock(&thread->startup_handshake_mutex);
+  thread->startup_handshake_lock.unlock();
 
   return 0;
 }
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index 6a39a21..f96e9d2 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -31,6 +31,7 @@
 #include <pthread.h>
 #include <stdatomic.h>
 
+#include "private/bionic_lock.h"
 #include "private/bionic_tls.h"
 
 /* Has the thread been detached by a pthread_join or pthread_detach call? */
@@ -51,6 +52,8 @@
   THREAD_DETACHED
 };
 
+struct thread_local_dtor;
+
 struct pthread_internal_t {
   struct pthread_internal_t* next;
   struct pthread_internal_t* prev;
@@ -89,10 +92,12 @@
 
   void* alternate_signal_stack;
 
-  pthread_mutex_t startup_handshake_mutex;
+  Lock startup_handshake_lock;
 
   size_t mmap_size;
 
+  thread_local_dtor* thread_local_dtors;
+
   void* tls[BIONIC_TLS_SLOTS];
 
   pthread_key_data_t key_data[BIONIC_PTHREAD_KEY_COUNT];
diff --git a/libc/bionic/pthread_mutex.cpp b/libc/bionic/pthread_mutex.cpp
index 851fc3d..cad138c 100644
--- a/libc/bionic/pthread_mutex.cpp
+++ b/libc/bionic/pthread_mutex.cpp
@@ -166,11 +166,14 @@
 #define  MUTEX_STATE_BITS_LOCKED_UNCONTENDED  MUTEX_STATE_TO_BITS(MUTEX_STATE_LOCKED_UNCONTENDED)
 #define  MUTEX_STATE_BITS_LOCKED_CONTENDED    MUTEX_STATE_TO_BITS(MUTEX_STATE_LOCKED_CONTENDED)
 
-/* return true iff the mutex if locked with no waiters */
-#define  MUTEX_STATE_BITS_IS_LOCKED_UNCONTENDED(v)  (((v) & MUTEX_STATE_MASK) == MUTEX_STATE_BITS_LOCKED_UNCONTENDED)
+// Return true iff the mutex is unlocked.
+#define MUTEX_STATE_BITS_IS_UNLOCKED(v) (((v) & MUTEX_STATE_MASK) == MUTEX_STATE_BITS_UNLOCKED)
 
-/* return true iff the mutex if locked with maybe waiters */
-#define  MUTEX_STATE_BITS_IS_LOCKED_CONTENDED(v)   (((v) & MUTEX_STATE_MASK) == MUTEX_STATE_BITS_LOCKED_CONTENDED)
+// Return true iff the mutex is locked with no waiters.
+#define MUTEX_STATE_BITS_IS_LOCKED_UNCONTENDED(v)  (((v) & MUTEX_STATE_MASK) == MUTEX_STATE_BITS_LOCKED_UNCONTENDED)
+
+// return true iff the mutex is locked with maybe waiters.
+#define MUTEX_STATE_BITS_IS_LOCKED_CONTENDED(v)   (((v) & MUTEX_STATE_MASK) == MUTEX_STATE_BITS_LOCKED_CONTENDED)
 
 /* used to flip from LOCKED_UNCONTENDED to LOCKED_CONTENDED */
 #define  MUTEX_STATE_BITS_FLIP_CONTENTION(v)      ((v) ^ (MUTEX_STATE_BITS_LOCKED_CONTENDED ^ MUTEX_STATE_BITS_LOCKED_UNCONTENDED))
@@ -296,11 +299,15 @@
  */
 static inline __always_inline int __pthread_normal_mutex_lock(pthread_mutex_internal_t* mutex,
                                                               uint16_t shared,
-                                                              const timespec* abs_timeout_or_null,
-                                                              clockid_t clock) {
+                                                              bool use_realtime_clock,
+                                                              const timespec* abs_timeout_or_null) {
     if (__predict_true(__pthread_normal_mutex_trylock(mutex, shared) == 0)) {
         return 0;
     }
+    int result = check_timespec(abs_timeout_or_null);
+    if (result != 0) {
+        return result;
+    }
 
     ScopedTrace trace("Contending for pthread mutex");
 
@@ -317,15 +324,8 @@
     // made by other threads visible to the current CPU.
     while (atomic_exchange_explicit(&mutex->state, locked_contended,
                                     memory_order_acquire) != unlocked) {
-        timespec ts;
-        timespec* rel_timeout = NULL;
-        if (abs_timeout_or_null != NULL) {
-            rel_timeout = &ts;
-            if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, clock)) {
-                return ETIMEDOUT;
-            }
-        }
-        if (__futex_wait_ex(&mutex->state, shared, locked_contended, rel_timeout) == -ETIMEDOUT) {
+        if (__futex_wait_ex(&mutex->state, shared, locked_contended, use_realtime_clock,
+                            abs_timeout_or_null) == -ETIMEDOUT) {
             return ETIMEDOUT;
         }
     }
@@ -396,14 +396,15 @@
                                                       pthread_mutex_internal_t* mutex,
                                                       uint16_t shared,
                                                       uint16_t old_state,
-                                                      const timespec* rel_timeout) {
+                                                      bool use_realtime_clock,
+                                                      const timespec* abs_timeout) {
 // __futex_wait always waits on a 32-bit value. But state is 16-bit. For a normal mutex, the owner_tid
 // field in mutex is not used. On 64-bit devices, the __pad field in mutex is not used.
 // But when a recursive or errorcheck mutex is used on 32-bit devices, we need to add the
 // owner_tid value in the value argument for __futex_wait, otherwise we may always get EAGAIN error.
 
 #if defined(__LP64__)
-  return __futex_wait_ex(&mutex->state, shared, old_state, rel_timeout);
+  return __futex_wait_ex(&mutex->state, shared, old_state, use_realtime_clock, abs_timeout);
 
 #else
   // This implementation works only when the layout of pthread_mutex_internal_t matches below expectation.
@@ -412,19 +413,21 @@
   static_assert(offsetof(pthread_mutex_internal_t, owner_tid) == 2, "");
 
   uint32_t owner_tid = atomic_load_explicit(&mutex->owner_tid, memory_order_relaxed);
-  return __futex_wait_ex(&mutex->state, shared, (owner_tid << 16) | old_state, rel_timeout);
+  return __futex_wait_ex(&mutex->state, shared, (owner_tid << 16) | old_state,
+                         use_realtime_clock, abs_timeout);
 #endif
 }
 
 static int __pthread_mutex_lock_with_timeout(pthread_mutex_internal_t* mutex,
-                                           const timespec* abs_timeout_or_null, clockid_t clock) {
+                                             bool use_realtime_clock,
+                                             const timespec* abs_timeout_or_null) {
     uint16_t old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed);
     uint16_t mtype = (old_state & MUTEX_TYPE_MASK);
     uint16_t shared = (old_state & MUTEX_SHARED_MASK);
 
     // Handle common case first.
     if ( __predict_true(mtype == MUTEX_TYPE_BITS_NORMAL) ) {
-        return __pthread_normal_mutex_lock(mutex, shared, abs_timeout_or_null, clock);
+        return __pthread_normal_mutex_lock(mutex, shared, use_realtime_clock, abs_timeout_or_null);
     }
 
     // Do we already own this recursive or error-check mutex?
@@ -484,16 +487,13 @@
             old_state = new_state;
         }
 
-        // We are in locked_contended state, sleep until someone wakes us up.
-        timespec ts;
-        timespec* rel_timeout = NULL;
-        if (abs_timeout_or_null != NULL) {
-            rel_timeout = &ts;
-            if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, clock)) {
-                return ETIMEDOUT;
-            }
+        int result = check_timespec(abs_timeout_or_null);
+        if (result != 0) {
+            return result;
         }
-        if (__recursive_or_errorcheck_mutex_wait(mutex, shared, old_state, rel_timeout) == -ETIMEDOUT) {
+        // We are in locked_contended state, sleep until someone wakes us up.
+        if (__recursive_or_errorcheck_mutex_wait(mutex, shared, old_state, use_realtime_clock,
+                                                 abs_timeout_or_null) == -ETIMEDOUT) {
             return ETIMEDOUT;
         }
         old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed);
@@ -518,7 +518,7 @@
         return 0;
       }
     }
-    return __pthread_mutex_lock_with_timeout(mutex, NULL, 0);
+    return __pthread_mutex_lock_with_timeout(mutex, false, nullptr);
 }
 
 int pthread_mutex_unlock(pthread_mutex_t* mutex_interface) {
@@ -613,17 +613,12 @@
 
 #if !defined(__LP64__)
 extern "C" int pthread_mutex_lock_timeout_np(pthread_mutex_t* mutex_interface, unsigned ms) {
+    timespec ts;
+    timespec_from_ms(ts, ms);
     timespec abs_timeout;
-    clock_gettime(CLOCK_MONOTONIC, &abs_timeout);
-    abs_timeout.tv_sec  += ms / 1000;
-    abs_timeout.tv_nsec += (ms % 1000) * 1000000;
-    if (abs_timeout.tv_nsec >= NS_PER_S) {
-        abs_timeout.tv_sec++;
-        abs_timeout.tv_nsec -= NS_PER_S;
-    }
-
+    absolute_timespec_from_timespec(abs_timeout, ts, CLOCK_MONOTONIC);
     int error = __pthread_mutex_lock_with_timeout(__get_internal_mutex(mutex_interface),
-                                                  &abs_timeout, CLOCK_MONOTONIC);
+                                                  false, &abs_timeout);
     if (error == ETIMEDOUT) {
         error = EBUSY;
     }
@@ -633,14 +628,18 @@
 
 int pthread_mutex_timedlock(pthread_mutex_t* mutex_interface, const timespec* abs_timeout) {
     return __pthread_mutex_lock_with_timeout(__get_internal_mutex(mutex_interface),
-                                             abs_timeout, CLOCK_REALTIME);
+                                             true, abs_timeout);
 }
 
 int pthread_mutex_destroy(pthread_mutex_t* mutex_interface) {
-    // Use trylock to ensure that the mutex is valid and not already locked.
-    int error = pthread_mutex_trylock(mutex_interface);
-    if (error != 0) {
-        return error;
+    pthread_mutex_internal_t* mutex = __get_internal_mutex(mutex_interface);
+    uint16_t old_state = atomic_load_explicit(&mutex->state, memory_order_relaxed);
+    // Store 0xffff to make the mutex unusable. Although POSIX standard says it is undefined
+    // behavior to destroy a locked mutex, we prefer not to change mutex->state in that situation.
+    if (MUTEX_STATE_BITS_IS_UNLOCKED(old_state) &&
+        atomic_compare_exchange_strong_explicit(&mutex->state, &old_state, 0xffff,
+                                                memory_order_relaxed, memory_order_relaxed)) {
+      return 0;
     }
-    return 0;
+    return EBUSY;
 }
diff --git a/libc/bionic/pthread_once.cpp b/libc/bionic/pthread_once.cpp
index 7688a23..f48eadc 100644
--- a/libc/bionic/pthread_once.cpp
+++ b/libc/bionic/pthread_once.cpp
@@ -79,7 +79,7 @@
     }
 
     // The initialization is underway, wait for its finish.
-    __futex_wait_ex(once_control_ptr, 0, old_value, NULL);
+    __futex_wait_ex(once_control_ptr, 0, old_value, false, nullptr);
     old_value = atomic_load_explicit(once_control_ptr, memory_order_acquire);
   }
 }
diff --git a/libc/bionic/pthread_rwlock.cpp b/libc/bionic/pthread_rwlock.cpp
index 934210e..b1c48c8 100644
--- a/libc/bionic/pthread_rwlock.cpp
+++ b/libc/bionic/pthread_rwlock.cpp
@@ -294,9 +294,13 @@
   }
 
   while (true) {
-    int ret = __pthread_rwlock_tryrdlock(rwlock);
-    if (ret == 0 || ret == EAGAIN) {
-      return ret;
+    int result = __pthread_rwlock_tryrdlock(rwlock);
+    if (result == 0 || result == EAGAIN) {
+      return result;
+    }
+    result = check_timespec(abs_timeout_or_null);
+    if (result != 0) {
+      return result;
     }
 
     int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
@@ -304,16 +308,6 @@
       continue;
     }
 
-    timespec ts;
-    timespec* rel_timeout = NULL;
-
-    if (abs_timeout_or_null != NULL) {
-      rel_timeout = &ts;
-      if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) {
-        return ETIMEDOUT;
-      }
-    }
-
     rwlock->pending_lock.lock();
     rwlock->pending_reader_count++;
 
@@ -327,10 +321,10 @@
     int old_serial = rwlock->pending_reader_wakeup_serial;
     rwlock->pending_lock.unlock();
 
-    int futex_ret = 0;
+    int futex_result = 0;
     if (!__can_acquire_read_lock(old_state, rwlock->writer_nonrecursive_preferred)) {
-      futex_ret = __futex_wait_ex(&rwlock->pending_reader_wakeup_serial, rwlock->pshared,
-                                  old_serial, rel_timeout);
+      futex_result = __futex_wait_ex(&rwlock->pending_reader_wakeup_serial, rwlock->pshared,
+                                  old_serial, true, abs_timeout_or_null);
     }
 
     rwlock->pending_lock.lock();
@@ -341,7 +335,7 @@
     }
     rwlock->pending_lock.unlock();
 
-    if (futex_ret == -ETIMEDOUT) {
+    if (futex_result == -ETIMEDOUT) {
       return ETIMEDOUT;
     }
   }
@@ -372,9 +366,13 @@
     return EDEADLK;
   }
   while (true) {
-    int ret = __pthread_rwlock_trywrlock(rwlock);
-    if (ret == 0) {
-      return ret;
+    int result = __pthread_rwlock_trywrlock(rwlock);
+    if (result == 0) {
+      return result;
+    }
+    result = check_timespec(abs_timeout_or_null);
+    if (result != 0) {
+      return result;
     }
 
     int old_state = atomic_load_explicit(&rwlock->state, memory_order_relaxed);
@@ -382,16 +380,6 @@
       continue;
     }
 
-    timespec ts;
-    timespec* rel_timeout = NULL;
-
-    if (abs_timeout_or_null != NULL) {
-      rel_timeout = &ts;
-      if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout_or_null, CLOCK_REALTIME)) {
-        return ETIMEDOUT;
-      }
-    }
-
     rwlock->pending_lock.lock();
     rwlock->pending_writer_count++;
 
@@ -401,10 +389,10 @@
     int old_serial = rwlock->pending_writer_wakeup_serial;
     rwlock->pending_lock.unlock();
 
-    int futex_ret = 0;
+    int futex_result = 0;
     if (!__can_acquire_write_lock(old_state)) {
-      futex_ret = __futex_wait_ex(&rwlock->pending_writer_wakeup_serial, rwlock->pshared,
-                                  old_serial, rel_timeout);
+      futex_result = __futex_wait_ex(&rwlock->pending_writer_wakeup_serial, rwlock->pshared,
+                                  old_serial, true, abs_timeout_or_null);
     }
 
     rwlock->pending_lock.lock();
@@ -415,7 +403,7 @@
     }
     rwlock->pending_lock.unlock();
 
-    if (futex_ret == -ETIMEDOUT) {
+    if (futex_result == -ETIMEDOUT) {
       return ETIMEDOUT;
     }
   }
@@ -427,7 +415,7 @@
   if (__predict_true(__pthread_rwlock_tryrdlock(rwlock) == 0)) {
     return 0;
   }
-  return __pthread_rwlock_timedrdlock(rwlock, NULL);
+  return __pthread_rwlock_timedrdlock(rwlock, nullptr);
 }
 
 int pthread_rwlock_timedrdlock(pthread_rwlock_t* rwlock_interface, const timespec* abs_timeout) {
@@ -446,7 +434,7 @@
   if (__predict_true(__pthread_rwlock_trywrlock(rwlock) == 0)) {
     return 0;
   }
-  return __pthread_rwlock_timedwrlock(rwlock, NULL);
+  return __pthread_rwlock_timedwrlock(rwlock, nullptr);
 }
 
 int pthread_rwlock_timedwrlock(pthread_rwlock_t* rwlock_interface, const timespec* abs_timeout) {
diff --git a/libc/bionic/pthread_spinlock.cpp b/libc/bionic/pthread_spinlock.cpp
new file mode 100644
index 0000000..f26f283
--- /dev/null
+++ b/libc/bionic/pthread_spinlock.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <pthread.h>
+
+#include "private/bionic_lock.h"
+
+// User-level spinlocks can be hazardous to battery life on Android.
+// We implement a simple compromise that behaves mostly like a spinlock,
+// but prevents excessively long spinning.
+
+struct pthread_spinlock_internal_t {
+  Lock lock;
+};
+
+static_assert(sizeof(pthread_spinlock_t) == sizeof(pthread_spinlock_internal_t),
+              "pthread_spinlock_t should actually be pthread_spinlock_internal_t.");
+
+static_assert(alignof(pthread_spinlock_t) >= 4,
+              "pthread_spinlock_t should fulfill the alignment of pthread_spinlock_internal_t.");
+
+static inline pthread_spinlock_internal_t* __get_internal_spinlock(pthread_spinlock_t* lock) {
+  return reinterpret_cast<pthread_spinlock_internal_t*>(lock);
+}
+
+int pthread_spin_init(pthread_spinlock_t* lock_interface, int pshared) {
+  pthread_spinlock_internal_t* lock = __get_internal_spinlock(lock_interface);
+  lock->lock.init(pshared);
+  return 0;
+}
+
+int pthread_spin_destroy(pthread_spinlock_t* lock_interface) {
+  pthread_spinlock_internal_t* lock = __get_internal_spinlock(lock_interface);
+  return lock->lock.trylock() ? 0 : EBUSY;
+}
+
+int pthread_spin_trylock(pthread_spinlock_t* lock_interface) {
+  pthread_spinlock_internal_t* lock = __get_internal_spinlock(lock_interface);
+  return lock->lock.trylock() ? 0 : EBUSY;
+}
+
+int pthread_spin_lock(pthread_spinlock_t* lock_interface) {
+  pthread_spinlock_internal_t* lock = __get_internal_spinlock(lock_interface);
+  for (int i = 0; i < 10000; ++i) {
+    if (lock->lock.trylock()) {
+      return 0;
+    }
+  }
+  lock->lock.lock();
+  return 0;
+}
+
+int pthread_spin_unlock(pthread_spinlock_t* lock_interface) {
+  pthread_spinlock_internal_t* lock = __get_internal_spinlock(lock_interface);
+  lock->lock.unlock();
+  return 0;
+}
diff --git a/libc/bionic/scandir.cpp b/libc/bionic/scandir.cpp
index ee62fee..e55be42 100644
--- a/libc/bionic/scandir.cpp
+++ b/libc/bionic/scandir.cpp
@@ -16,9 +16,11 @@
 
 #include <dirent.h>
 
+#include <fcntl.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "private/bionic_macros.h"
 #include "private/ScopedReaddir.h"
@@ -26,7 +28,7 @@
 // A smart pointer to the scandir dirent**.
 class ScandirResult {
  public:
-  ScandirResult() : names_(NULL), size_(0), capacity_(0) {
+  ScandirResult() : names_(nullptr), size_(0), capacity_(0) {
   }
 
   ~ScandirResult() {
@@ -42,7 +44,7 @@
 
   dirent** release() {
     dirent** result = names_;
-    names_ = NULL;
+    names_ = nullptr;
     size_ = capacity_ = 0;
     return result;
   }
@@ -52,7 +54,7 @@
       size_t new_capacity = capacity_ + 32;
       dirent** new_names =
           reinterpret_cast<dirent**>(realloc(names_, new_capacity * sizeof(dirent*)));
-      if (new_names == NULL) {
+      if (new_names == nullptr) {
         return false;
       }
       names_ = new_names;
@@ -60,7 +62,7 @@
     }
 
     dirent* copy = CopyDirent(entry);
-    if (copy == NULL) {
+    if (copy == nullptr) {
       return false;
     }
     names_[size_++] = copy;
@@ -69,7 +71,7 @@
 
   void Sort(int (*comparator)(const dirent**, const dirent**)) {
     // If we have entries and a comparator, sort them.
-    if (size_ > 0 && comparator != NULL) {
+    if (size_ > 0 && comparator != nullptr) {
       qsort(names_, size_, sizeof(dirent*),
             reinterpret_cast<int (*)(const void*, const void*)>(comparator));
     }
@@ -91,19 +93,29 @@
   DISALLOW_COPY_AND_ASSIGN(ScandirResult);
 };
 
-int scandir(const char* dirname, dirent*** name_list,
-            int (*filter)(const dirent*),
-            int (*comparator)(const dirent**, const dirent**)) {
-  ScopedReaddir reader(dirname);
+int scandirat(int parent_fd, const char* dir_name, dirent*** name_list,
+              int (*filter)(const dirent*),
+              int (*comparator)(const dirent**, const dirent**)) {
+  DIR* dir = nullptr;
+  if (parent_fd == AT_FDCWD) {
+    dir = opendir(dir_name);
+  } else {
+    int dir_fd = openat(parent_fd, dir_name, O_CLOEXEC | O_DIRECTORY | O_RDONLY);
+    if (dir_fd != -1) {
+      dir = fdopendir(dir_fd);
+    }
+  }
+
+  ScopedReaddir reader(dir);
   if (reader.IsBad()) {
     return -1;
   }
 
   ScandirResult names;
   dirent* entry;
-  while ((entry = reader.ReadEntry()) != NULL) {
+  while ((entry = reader.ReadEntry()) != nullptr) {
     // If we have a filter, skip names that don't match.
-    if (filter != NULL && !(*filter)(entry)) {
+    if (filter != nullptr && !(*filter)(entry)) {
       continue;
     }
     names.Add(entry);
@@ -115,4 +127,11 @@
   *name_list = names.release();
   return size;
 }
+__strong_alias(scandirat64, scandirat);
+
+int scandir(const char* dir_path, dirent*** name_list,
+            int (*filter)(const dirent*),
+            int (*comparator)(const dirent**, const dirent**)) {
+  return scandirat(AT_FDCWD, dir_path, name_list, filter, comparator);
+}
 __strong_alias(scandir64, scandir);
diff --git a/libc/bionic/semaphore.cpp b/libc/bionic/semaphore.cpp
index ff84443..79b5d63 100644
--- a/libc/bionic/semaphore.cpp
+++ b/libc/bionic/semaphore.cpp
@@ -220,7 +220,7 @@
       return 0;
     }
 
-    __futex_wait_ex(sem_count_ptr, shared, shared | SEMCOUNT_MINUS_ONE, NULL);
+    __futex_wait_ex(sem_count_ptr, shared, shared | SEMCOUNT_MINUS_ONE, false, nullptr);
   }
 }
 
@@ -235,36 +235,29 @@
   }
 
   // Check it as per POSIX.
-  if (abs_timeout == NULL || abs_timeout->tv_sec < 0 || abs_timeout->tv_nsec < 0 || abs_timeout->tv_nsec >= NS_PER_S) {
-    errno = EINVAL;
+  int result = check_timespec(abs_timeout);
+  if (result != 0) {
+    errno = result;
     return -1;
   }
 
   unsigned int shared = SEM_GET_SHARED(sem_count_ptr);
 
   while (true) {
-    // POSIX mandates CLOCK_REALTIME here.
-    timespec ts;
-    if (!timespec_from_absolute_timespec(ts, *abs_timeout, CLOCK_REALTIME)) {
-      errno = ETIMEDOUT;
-      return -1;
-    }
-
     // Try to grab the semaphore. If the value was 0, this will also change it to -1.
     if (__sem_dec(sem_count_ptr) > 0) {
-      break;
+      return 0;
     }
 
     // Contention detected. Wait for a wakeup event.
-    int ret = __futex_wait_ex(sem_count_ptr, shared, shared | SEMCOUNT_MINUS_ONE, &ts);
+    int result = __futex_wait_ex(sem_count_ptr, shared, shared | SEMCOUNT_MINUS_ONE, true, abs_timeout);
 
     // Return in case of timeout or interrupt.
-    if (ret == -ETIMEDOUT || ret == -EINTR) {
-      errno = -ret;
+    if (result == -ETIMEDOUT || result == -EINTR) {
+      errno = -result;
       return -1;
     }
   }
-  return 0;
 }
 
 int sem_post(sem_t* sem) {
diff --git a/libc/bionic/signal.cpp b/libc/bionic/signal.cpp
index 74a2f65..13d1882 100644
--- a/libc/bionic/signal.cpp
+++ b/libc/bionic/signal.cpp
@@ -28,12 +28,7 @@
 
 #include <signal.h>
 
-#ifdef __LP64__
-static
-#else
-__LIBC_HIDDEN__
-#endif
-sighandler_t _signal(int signum, sighandler_t handler, int flags) {
+__LIBC_HIDDEN__ sighandler_t _signal(int signum, sighandler_t handler, int flags) {
   struct sigaction sa;
   sigemptyset(&sa.sa_mask);
   sa.sa_handler = handler;
diff --git a/libc/bionic/system_properties.cpp b/libc/bionic/system_properties.cpp
index c436a16..b9a373e 100644
--- a/libc/bionic/system_properties.cpp
+++ b/libc/bionic/system_properties.cpp
@@ -25,34 +25,38 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#include <new>
-#include <stdatomic.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <stddef.h>
+#include <ctype.h>
 #include <errno.h>
-#include <poll.h>
 #include <fcntl.h>
+#include <poll.h>
+#include <stdatomic.h>
 #include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
+#include <new>
 
+#include <linux/xattr.h>
+#include <netinet/in.h>
 #include <sys/mman.h>
-
-#include <sys/socket.h>
-#include <sys/un.h>
 #include <sys/select.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <netinet/in.h>
+#include <sys/un.h>
+#include <sys/xattr.h>
 
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 #include <sys/system_properties.h>
 
 #include "private/bionic_futex.h"
+#include "private/bionic_lock.h"
 #include "private/bionic_macros.h"
+#include "private/libc_logging.h"
 
 static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
 
@@ -112,23 +116,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);
 };
 
@@ -158,7 +196,7 @@
     }
 };
 
-static char property_filename[PATH_MAX] = PROP_FILENAME;
+static char property_filename[PROP_FILENAME_MAX] = PROP_FILENAME;
 static bool compat_mode = false;
 static size_t pa_data_size;
 static size_t pa_size;
@@ -182,13 +220,12 @@
     return atoi(env);
 }
 
-static int map_prop_area_rw()
-{
+static prop_area* map_prop_area_rw(const char* filename, const char* context,
+                                   bool* fsetxattr_failed) {
     /* dev is a tmpfs that we can use to carve a shared workspace
      * out of, so let's do that...
      */
-    const int fd = open(property_filename,
-                        O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);
+    const int fd = open(filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);
 
     if (fd < 0) {
         if (errno == EACCES) {
@@ -197,12 +234,31 @@
              */
             abort();
         }
-        return -1;
+        return nullptr;
+    }
+
+    if (context) {
+        if (fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0) != 0) {
+            __libc_format_log(ANDROID_LOG_ERROR, "libc",
+                              "fsetxattr failed to set context (%s) for \"%s\"", context, filename);
+            /*
+             * fsetxattr() will fail during system properties tests due to selinux policy.
+             * We do not want to create a custom policy for the tester, so we will continue in
+             * this function but set a flag that an error has occurred.
+             * Init, which is the only daemon that should ever call this function will abort
+             * when this error occurs.
+             * Otherwise, the tester will ignore it and continue, albeit without any selinux
+             * property separation.
+             */
+            if (fsetxattr_failed) {
+                *fsetxattr_failed = true;
+            }
+        }
     }
 
     if (ftruncate(fd, PA_SIZE) < 0) {
         close(fd);
-        return -1;
+        return nullptr;
     }
 
     pa_size = PA_SIZE;
@@ -212,29 +268,26 @@
     void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
     if (memory_area == MAP_FAILED) {
         close(fd);
-        return -1;
+        return nullptr;
     }
 
     prop_area *pa = new(memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION);
 
-    /* plug into the lib property services */
-    __system_property_area__ = pa;
-
     close(fd);
-    return 0;
+    return pa;
 }
 
-static int map_fd_ro(const int fd) {
+static prop_area* map_fd_ro(const int fd) {
     struct stat fd_stat;
     if (fstat(fd, &fd_stat) < 0) {
-        return -1;
+        return nullptr;
     }
 
     if ((fd_stat.st_uid != 0)
             || (fd_stat.st_gid != 0)
             || ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0)
             || (fd_stat.st_size < static_cast<off_t>(sizeof(prop_area))) ) {
-        return -1;
+        return nullptr;
     }
 
     pa_size = fd_stat.st_size;
@@ -242,29 +295,28 @@
 
     void* const map_result = mmap(NULL, pa_size, PROT_READ, MAP_SHARED, fd, 0);
     if (map_result == MAP_FAILED) {
-        return -1;
+        return nullptr;
     }
 
     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;
+        return nullptr;
     }
 
-    if (pa->version == PROP_AREA_VERSION_COMPAT) {
+    if (pa->version() == PROP_AREA_VERSION_COMPAT) {
         compat_mode = true;
     }
 
-    __system_property_area__ = pa;
-    return 0;
+    return pa;
 }
 
-static int map_prop_area()
-{
-    int fd = open(property_filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
+static prop_area* map_prop_area(const char* filename, bool is_legacy) {
+    int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
     bool close_fd = true;
-    if (fd == -1 && errno == ENOENT) {
+    if (fd == -1 && errno == ENOENT && is_legacy) {
         /*
          * For backwards compatibility, if the file doesn't
          * exist, we use the environment to get the file descriptor.
@@ -273,16 +325,18 @@
          * returns other errors such as ENOMEM or ENFILE, since it
          * might be possible for an external program to trigger this
          * condition.
+         * Only do this for the legacy prop file, secured prop files
+         * do not have a backup
          */
         fd = get_fd_from_env();
         close_fd = false;
     }
 
     if (fd < 0) {
-        return -1;
+        return nullptr;
     }
 
-    const int map_result = map_fd_ro(fd);
+    prop_area* map_result = map_fd_ro(fd);
     if (close_fd) {
         close(fd);
     }
@@ -290,20 +344,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 +369,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 +383,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 +417,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 +468,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,44 +594,383 @@
     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 false;
+    }
+
+    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);
+}
+
+struct context_node {
+    context_node(struct context_node* next, const char* context, prop_area* pa)
+        : context(strdup(context)), pa(pa), checked_access(false), next(next) {
+        lock.init(false);
+    }
+    ~context_node() {
+        if (pa) {
+            munmap(pa, pa_size);
+        }
+        free(context);
+    }
+    Lock lock;
+    char* context;
+    prop_area* pa;
+    bool checked_access;
+    struct context_node* next;
+};
+
+struct prefix_node {
+    prefix_node(struct prefix_node* next, const char* prefix, context_node* context)
+        : prefix(strdup(prefix)), prefix_len(strlen(prefix)), context(context), next(next) {
+    }
+    ~prefix_node() {
+        free(prefix);
+    }
+    char* prefix;
+    const size_t prefix_len;
+    context_node* context;
+    struct prefix_node* next;
+};
+
+template <typename List, typename... Args>
+static inline void list_add(List** list, Args... args) {
+    *list = new List(*list, args...);
+}
+
+static void list_add_after_len(prefix_node** list, const char* prefix, context_node* context) {
+    size_t prefix_len = strlen(prefix);
+
+    auto next_list = list;
+
+    while (*next_list) {
+        if ((*next_list)->prefix_len < prefix_len || (*next_list)->prefix[0] == '*') {
+            list_add(next_list, prefix, context);
+            return;
+        }
+        next_list = &(*next_list)->next;
+    }
+    list_add(next_list, prefix, context);
+}
+
+template <typename List, typename Func>
+static void list_foreach(List* list, Func func) {
+    while (list) {
+        func(list);
+        list = list->next;
+    }
+}
+
+template <typename List, typename Func>
+static List* list_find(List* list, Func func) {
+    while (list) {
+        if (func(list)) {
+            return list;
+        }
+        list = list->next;
+    }
+    return nullptr;
+}
+
+template <typename List>
+static void list_free(List** list) {
+    while (*list) {
+        auto old_list = *list;
+        *list = old_list->next;
+        delete old_list;
+    }
+}
+
+static prefix_node* prefixes = nullptr;
+static context_node* contexts = nullptr;
+
+/*
+ * pthread_mutex_lock() calls into system_properties in the case of contention.
+ * This creates a risk of dead lock if any system_properties functions
+ * use pthread locks after system_property initialization.
+ *
+ * For this reason, the below three functions use a bionic Lock and static
+ * allocation of memory for each filename.
+ */
+
+static bool open_prop_file(context_node* cnode, bool access_rw, bool* fsetxattr_failed) {
+    cnode->lock.lock();
+    if (cnode->pa) {
+        cnode->lock.unlock();
+        return true;
+    }
+
+    char filename[PROP_FILENAME_MAX];
+    int len = snprintf(filename, sizeof(filename), "%s/%s", property_filename, cnode->context);
+    if (len < 0 || len > PROP_FILENAME_MAX) {
+        cnode->lock.unlock();
+        return false;
+    }
+
+    if (access_rw) {
+        cnode->pa = map_prop_area_rw(filename, cnode->context, fsetxattr_failed);
+    } else {
+        cnode->pa = map_prop_area(filename, false);
+    }
+    cnode->lock.unlock();
+    return cnode->pa;
+}
+
+static bool check_access(context_node* cnode) {
+    char filename[PROP_FILENAME_MAX];
+    int len = snprintf(filename, sizeof(filename), "%s/%s", property_filename, cnode->context);
+    if (len < 0 || len > PROP_FILENAME_MAX) {
+        return false;
+    }
+
+    return access(filename, R_OK) == 0;
+}
+
+static bool map_system_property_area(bool access_rw, bool* fsetxattr_failed) {
+    char filename[PROP_FILENAME_MAX];
+    int len = snprintf(filename, sizeof(filename), "%s/properties_serial", property_filename);
+    if (len < 0 || len > PROP_FILENAME_MAX) {
+        __system_property_area__ = nullptr;
+        return false;
+    }
+
+    if (access_rw) {
+        __system_property_area__ =
+            map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed);
+    } else {
+        __system_property_area__ = map_prop_area(filename, false);
+    }
+    return __system_property_area__;
+}
+
+static prop_area* get_prop_area_for_name(const char* name) {
+    auto entry = list_find(prefixes, [name](prefix_node* l) {
+        return l->prefix[0] == '*' || !strncmp(l->prefix, name, l->prefix_len);
+    });
+    if (!entry) {
+        return nullptr;
+    }
+
+    auto cnode = entry->context;
+    if (!cnode->pa) {
+        open_prop_file(cnode, false, nullptr);
+    }
+    return cnode->pa;
+}
+
+/*
+ * The below two functions are duplicated from label_support.c in libselinux.
+ * TODO: Find a location suitable for these functions such that both libc and
+ * libselinux can share a common source file.
+ */
+
+/*
+ * The read_spec_entries and read_spec_entry functions may be used to
+ * replace sscanf to read entries from spec files. The file and
+ * property services now use these.
+ */
+
+/* Read an entry from a spec file (e.g. file_contexts) */
+static inline int read_spec_entry(char **entry, char **ptr, int *len)
+{
+    *entry = NULL;
+    char *tmp_buf = NULL;
+
+    while (isspace(**ptr) && **ptr != '\0')
+        (*ptr)++;
+
+    tmp_buf = *ptr;
+    *len = 0;
+
+    while (!isspace(**ptr) && **ptr != '\0') {
+        (*ptr)++;
+        (*len)++;
+    }
+
+    if (*len) {
+        *entry = strndup(tmp_buf, *len);
+        if (!*entry)
             return -1;
     }
 
     return 0;
 }
 
+/*
+ * line_buf - Buffer containing the spec entries .
+ * num_args - The number of spec parameter entries to process.
+ * ...      - A 'char **spec_entry' for each parameter.
+ * returns  - The number of items processed.
+ *
+ * This function calls read_spec_entry() to do the actual string processing.
+ */
+static int read_spec_entries(char *line_buf, int num_args, ...)
+{
+    char **spec_entry, *buf_p;
+    int len, rc, items, entry_len = 0;
+    va_list ap;
+
+    len = strlen(line_buf);
+    if (line_buf[len - 1] == '\n')
+        line_buf[len - 1] = '\0';
+    else
+        /* Handle case if line not \n terminated by bumping
+         * the len for the check below (as the line is NUL
+         * terminated by getline(3)) */
+        len++;
+
+    buf_p = line_buf;
+    while (isspace(*buf_p))
+        buf_p++;
+
+    /* Skip comment lines and empty lines. */
+    if (*buf_p == '#' || *buf_p == '\0')
+        return 0;
+
+    /* Process the spec file entries */
+    va_start(ap, num_args);
+
+    items = 0;
+    while (items < num_args) {
+        spec_entry = va_arg(ap, char **);
+
+        if (len - 1 == buf_p - line_buf) {
+            va_end(ap);
+            return items;
+        }
+
+        rc = read_spec_entry(spec_entry, &buf_p, &entry_len);
+        if (rc < 0) {
+            va_end(ap);
+            return rc;
+        }
+        if (entry_len)
+            items++;
+    }
+    va_end(ap);
+    return items;
+}
+
+static bool initialize_properties() {
+    list_free(&prefixes);
+    list_free(&contexts);
+
+    FILE* file = fopen("/property_contexts", "re");
+
+    if (!file) {
+        return false;
+    }
+
+    char* buffer = nullptr;
+    size_t line_len;
+    char* prop_prefix = nullptr;
+    char* context = nullptr;
+
+    while (getline(&buffer, &line_len, file) > 0) {
+        int items = read_spec_entries(buffer, 2, &prop_prefix, &context);
+        if (items <= 0) {
+            continue;
+        }
+        if (items == 1) {
+            free(prop_prefix);
+            continue;
+        }
+        /*
+         * init uses ctl.* properties as an IPC mechanism and does not write them
+         * to a property file, therefore we do not need to create property files
+         * to store them.
+         */
+        if (!strncmp(prop_prefix, "ctl.", 4)) {
+            free(prop_prefix);
+            free(context);
+            continue;
+        }
+
+        auto old_context = list_find(
+            contexts, [context](context_node* l) { return !strcmp(l->context, context); });
+        if (old_context) {
+            list_add_after_len(&prefixes, prop_prefix, old_context);
+        } else {
+            list_add(&contexts, context, nullptr);
+            list_add_after_len(&prefixes, prop_prefix, contexts);
+        }
+        free(prop_prefix);
+        free(context);
+    }
+
+    free(buffer);
+    fclose(file);
+    return true;
+}
+
+static bool is_dir(const char* pathname) {
+    struct stat info;
+    if (stat(pathname, &info) == -1) {
+        return false;
+    }
+    return S_ISDIR(info.st_mode);
+}
+
 int __system_properties_init()
 {
-    return map_prop_area();
+    if (is_dir(property_filename)) {
+        if (!initialize_properties()) {
+            return -1;
+        }
+        if (!map_system_property_area(false, nullptr)) {
+            list_free(&prefixes);
+            list_free(&contexts);
+            return -1;
+        }
+    } else {
+        __system_property_area__ = map_prop_area(property_filename, true);
+        if (!__system_property_area__) {
+            return -1;
+        }
+        list_add(&contexts, "legacy_system_prop_area", __system_property_area__);
+        list_add_after_len(&prefixes, "*", contexts);
+    }
+    return 0;
 }
 
 int __system_property_set_filename(const char *filename)
@@ -595,7 +985,23 @@
 
 int __system_property_area_init()
 {
-    return map_prop_area_rw();
+    mkdir(property_filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+    if (!initialize_properties()) {
+        return -1;
+    }
+    bool open_prop_file_failed = false;
+    bool fsetxattr_failed = false;
+    list_foreach(contexts, [&fsetxattr_failed, &open_prop_file_failed](context_node* l) {
+        if (!open_prop_file(l, true, &fsetxattr_failed)) {
+            open_prop_file_failed = true;
+        }
+    });
+    if (open_prop_file_failed || !map_system_property_area(true, &fsetxattr_failed)) {
+        list_free(&prefixes);
+        list_free(&contexts);
+        return -1;
+    }
+    return fsetxattr_failed ? -2 : 0;
 }
 
 unsigned int __system_property_area_serial()
@@ -605,15 +1011,26 @@
         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)
 {
+    if (!__system_property_area__) {
+        return nullptr;
+    }
+
     if (__predict_false(compat_mode)) {
         return __system_property_find_compat(name);
     }
-    return find_property(root_node(), name, strlen(name), NULL, 0, false);
+
+    prop_area* pa = get_prop_area_for_name(name);
+    if (!pa) {
+        __libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied finding property \"%s\"", name);
+        return nullptr;
+    }
+
+    return pa->find(name);
 }
 
 // The C11 standard doesn't allow atomic loads from const fields,
@@ -688,11 +1105,15 @@
 
 int __system_property_update(prop_info *pi, const char *value, unsigned int len)
 {
-    prop_area *pa = __system_property_area__;
-
     if (len >= PROP_VALUE_MAX)
         return -1;
 
+    prop_area* pa = __system_property_area__;
+
+    if (!pa) {
+        return -1;
+    }
+
     uint32_t serial = atomic_load_explicit(&pi->serial, memory_order_relaxed);
     serial |= 1;
     atomic_store_explicit(&pi->serial, serial, memory_order_relaxed);
@@ -708,10 +1129,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;
 }
@@ -719,9 +1140,6 @@
 int __system_property_add(const char *name, unsigned int namelen,
             const char *value, unsigned int valuelen)
 {
-    prop_area *pa = __system_property_area__;
-    const prop_info *pi;
-
     if (namelen >= PROP_NAME_MAX)
         return -1;
     if (valuelen >= PROP_VALUE_MAX)
@@ -729,17 +1147,28 @@
     if (namelen < 1)
         return -1;
 
-    pi = find_property(root_node(), name, namelen, value, valuelen, true);
-    if (!pi)
+    if (!__system_property_area__) {
+        return -1;
+    }
+
+    prop_area* pa = get_prop_area_for_name(name);
+
+    if (!pa) {
+        __libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied adding property \"%s\"", name);
+        return -1;
+    }
+
+    bool ret = pa->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,
+        __system_property_area__->serial(),
+        atomic_load_explicit(__system_property_area__->serial(), memory_order_relaxed) + 1,
         memory_order_release);
-    __futex_wake(&pa->serial, INT32_MAX);
+    __futex_wake(__system_property_area__->serial(), INT32_MAX);
     return 0;
 }
 
@@ -761,9 +1190,13 @@
     prop_area *pa = __system_property_area__;
     uint32_t my_serial;
 
+    if (!pa) {
+        return 0;
+    }
+
     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;
@@ -784,9 +1217,24 @@
 int __system_property_foreach(void (*propfn)(const prop_info *pi, void *cookie),
         void *cookie)
 {
+    if (!__system_property_area__) {
+        return -1;
+    }
+
     if (__predict_false(compat_mode)) {
         return __system_property_foreach_compat(propfn, cookie);
     }
 
-    return foreach_property(root_node(), propfn, cookie);
+    list_foreach(contexts, [propfn, cookie](context_node* l) {
+        if (!l->pa && !l->checked_access) {
+            if (check_access(l)) {
+                open_prop_file(l, false, nullptr);
+            }
+            l->checked_access = true;
+        }
+        if (l->pa) {
+            l->pa->foreach(propfn, cookie);
+        }
+    });
+    return 0;
 }
diff --git a/libc/include/android/dlext.h b/libc/include/android/dlext.h
index 40f610f..ed9a3b9 100644
--- a/libc/include/android/dlext.h
+++ b/libc/include/android/dlext.h
@@ -80,6 +80,29 @@
    */
   ANDROID_DLEXT_FORCE_FIXED_VADDR = 0x80,
 
+  /* Instructs dlopen to load the library at the address specified by reserved_addr.
+   *
+   * The difference between ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS and ANDROID_DLEXT_RESERVED_ADDRESS
+   * is that for ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS the linker reserves memory at reserved_addr
+   * whereas for ANDROID_DLEXT_RESERVED_ADDRESS the linker relies on the caller to reserve the memory.
+   *
+   * This flag can be used with ANDROID_DLEXT_FORCE_FIXED_VADDR; when ANDROID_DLEXT_FORCE_FIXED_VADDR
+   * is set and load_bias is not 0 (load_bias is min(p_vaddr) of PT_LOAD segments) this flag is ignored.
+   * This is implemented this way because the linker has to pick one address over the other and this
+   * way is more convenient for art. Note that ANDROID_DLEXT_FORCE_FIXED_VADDR does not generate
+   * an error when min(p_vaddr) is 0.
+   *
+   * Cannot be used with ANDROID_DLEXT_RESERVED_ADDRESS or ANDROID_DLEXT_RESERVED_ADDRESS_HINT.
+   *
+   * This flag is for ART internal use only.
+   */
+  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 |
@@ -88,9 +111,13 @@
                                         ANDROID_DLEXT_USE_LIBRARY_FD |
                                         ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET |
                                         ANDROID_DLEXT_FORCE_LOAD |
-                                        ANDROID_DLEXT_FORCE_FIXED_VADDR,
+                                        ANDROID_DLEXT_FORCE_FIXED_VADDR |
+                                        ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS |
+                                        ANDROID_DLEXT_USE_NAMESPACE,
 };
 
+struct android_namespace_t;
+
 typedef struct {
   uint64_t flags;
   void*   reserved_addr;
@@ -98,10 +125,44 @@
   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 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.
+ *
+ * The anon_ns_library_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);
+
+/*
+ * 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/byteswap.h b/libc/include/byteswap.h
index 74b0e91..628fb7f 100644
--- a/libc/include/byteswap.h
+++ b/libc/include/byteswap.h
@@ -28,11 +28,10 @@
 #ifndef _BYTESWAP_H_
 #define _BYTESWAP_H_
 
-/* endian.h rather than sys/endian.h so we get the machine-specific file. */
-#include <endian.h>
+#include <sys/endian.h>
 
-#define  bswap_16(x)   swap16(x)
-#define  bswap_32(x)   swap32(x)
-#define  bswap_64(x)   swap64(x)
+#define bswap_16(x) __swap16(x)
+#define bswap_32(x) __swap32(x)
+#define bswap_64(x) __swap64(x)
 
 #endif /* _BYTESWAP_H_ */
diff --git a/libc/include/dirent.h b/libc/include/dirent.h
index 63716a4..3cdfa68 100644
--- a/libc/include/dirent.h
+++ b/libc/include/dirent.h
@@ -81,8 +81,13 @@
 extern int dirfd(DIR*);
 extern int alphasort(const struct dirent**, const struct dirent**);
 extern int alphasort64(const struct dirent64**, const struct dirent64**);
-extern int scandir(const char*, struct dirent***, int (*)(const struct dirent*), int (*)(const struct dirent**, const struct dirent**));
 extern int scandir64(const char*, struct dirent64***, int (*)(const struct dirent64*), int (*)(const struct dirent64**, const struct dirent64**));
+extern int scandir(const char*, struct dirent***, int (*)(const struct dirent*), int (*)(const struct dirent**, const struct dirent**));
+
+#if defined(__USE_GNU)
+int scandirat64(int, const char*, struct dirent64***, int (*)(const struct dirent64*), int (*)(const struct dirent64**, const struct dirent64**));
+int scandirat(int, const char*, struct dirent***, int (*)(const struct dirent*), int (*)(const struct dirent**, const struct dirent**));
+#endif
 
 __END_DECLS
 
diff --git a/libc/include/libgen.h b/libc/include/libgen.h
index e89328e..4d22d15 100644
--- a/libc/include/libgen.h
+++ b/libc/include/libgen.h
@@ -32,18 +32,19 @@
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
+
 __BEGIN_DECLS
 
-#if !defined(__bionic_using_gnu_basename)
 /*
- * <string.h> gets you the GNU basename.
- * <libgen.h> the POSIX one.
- * Note that our "POSIX" one has the wrong argument cv-qualifiers, but doesn't
- * modify its input and uses thread-local storage for the result if necessary.
+ * Including <string.h> will get you the GNU basename, unless <libgen.h> is
+ * included, either before or after including <string.h>.
+ *
+ * Note that this has the wrong argument cv-qualifiers, but doesn't modify its
+ * input and uses thread-local storage for the result if necessary.
  */
-extern char* basename(const char*);
-#define __bionic_using_posix_basename
-#endif
+extern char* __posix_basename(const char*) __RENAME(basename);
+
+#define basename __posix_basename
 
 /* This has the wrong argument cv-qualifiers, but doesn't modify its input and uses thread-local storage for the result if necessary. */
 extern char* dirname(const char*);
diff --git a/libc/include/pthread.h b/libc/include/pthread.h
index 260ae5b..21d34fb 100644
--- a/libc/include/pthread.h
+++ b/libc/include/pthread.h
@@ -96,6 +96,26 @@
 
 #define PTHREAD_ONCE_INIT 0
 
+typedef struct {
+#if defined(__LP64__)
+  int64_t __private[4];
+#else
+  int32_t __private[8];
+#endif
+} pthread_barrier_t;
+
+typedef int pthread_barrierattr_t;
+
+#define PTHREAD_BARRIER_SERIAL_THREAD -1
+
+typedef struct {
+#if defined(__LP64__)
+  int64_t __private;
+#else
+  int32_t __private[2];
+#endif
+} pthread_spinlock_t;
+
 #if defined(__LP64__)
 #define PTHREAD_STACK_MIN (4 * PAGE_SIZE)
 #else
@@ -130,7 +150,7 @@
 int pthread_attr_setschedpolicy(pthread_attr_t*, int) __nonnull((1));
 int pthread_attr_setscope(pthread_attr_t*, int) __nonnull((1));
 int pthread_attr_setstack(pthread_attr_t*, void*, size_t) __nonnull((1));
-int pthread_attr_setstacksize(pthread_attr_t*, size_t stack_size) __nonnull((1));
+int pthread_attr_setstacksize(pthread_attr_t*, size_t) __nonnull((1));
 
 int pthread_condattr_destroy(pthread_condattr_t*) __nonnull((1));
 int pthread_condattr_getclock(const pthread_condattr_t*, clockid_t*) __nonnull((1, 2));
@@ -205,9 +225,24 @@
 int pthread_rwlock_timedwrlock(pthread_rwlock_t*, const struct timespec*) __nonnull((1, 2));
 int pthread_rwlock_tryrdlock(pthread_rwlock_t*) __nonnull((1));
 int pthread_rwlock_trywrlock(pthread_rwlock_t*) __nonnull((1));
-int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) __nonnull((1));
+int pthread_rwlock_unlock(pthread_rwlock_t *) __nonnull((1));
 int pthread_rwlock_wrlock(pthread_rwlock_t*) __nonnull((1));
 
+int pthread_barrierattr_init(pthread_barrierattr_t* attr) __nonnull((1));
+int pthread_barrierattr_destroy(pthread_barrierattr_t* attr) __nonnull((1));
+int pthread_barrierattr_getpshared(pthread_barrierattr_t* attr, int* pshared) __nonnull((1, 2));
+int pthread_barrierattr_setpshared(pthread_barrierattr_t* attr, int pshared) __nonnull((1));
+
+int pthread_barrier_init(pthread_barrier_t*, const pthread_barrierattr_t*, unsigned) __nonnull((1));
+int pthread_barrier_destroy(pthread_barrier_t*) __nonnull((1));
+int pthread_barrier_wait(pthread_barrier_t*) __nonnull((1));
+
+int pthread_spin_destroy(pthread_spinlock_t*) __nonnull((1));
+int pthread_spin_init(pthread_spinlock_t*, int) __nonnull((1));
+int pthread_spin_lock(pthread_spinlock_t*) __nonnull((1));
+int pthread_spin_trylock(pthread_spinlock_t*) __nonnull((1));
+int pthread_spin_unlock(pthread_spinlock_t*) __nonnull((1));
+
 pthread_t pthread_self(void) __pure2;
 
 int pthread_setname_np(pthread_t, const char*) __nonnull((2));
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/string.h b/libc/include/string.h
index 2c50cfa..32d4a18 100644
--- a/libc/include/string.h
+++ b/libc/include/string.h
@@ -115,18 +115,18 @@
 extern int    strcoll_l(const char *, const char *, locale_t) __purefunc;
 extern size_t strxfrm_l(char* __restrict, const char* __restrict, size_t, locale_t);
 
-#if defined(__USE_GNU) && !defined(__bionic_using_posix_basename)
+#if defined(__USE_GNU) && !defined(basename)
 /*
  * glibc has a basename in <string.h> that's different to the POSIX one in <libgen.h>.
  * It doesn't modify its argument, and in C++ it's const-correct.
  */
+
 #if defined(__cplusplus)
 extern "C++" char* basename(char*) __RENAME(__gnu_basename) __nonnull((1));
 extern "C++" const char* basename(const char*) __RENAME(__gnu_basename) __nonnull((1));
 #else
 extern char* basename(const char*) __RENAME(__gnu_basename) __nonnull((1));
 #endif
-#define __bionic_using_gnu_basename
 #endif
 
 extern void* __memchr_chk(const void*, int, size_t, size_t);
diff --git a/libc/include/sys/_system_properties.h b/libc/include/sys/_system_properties.h
index a0315b5..3b1f7d0 100644
--- a/libc/include/sys/_system_properties.h
+++ b/libc/include/sys/_system_properties.h
@@ -41,6 +41,7 @@
 #define PROP_AREA_VERSION_COMPAT 0x45434f76
 
 #define PROP_SERVICE_NAME "property_service"
+#define PROP_FILENAME_MAX 1024
 #define PROP_FILENAME "/dev/__properties__"
 
 #define PA_SIZE         (128 * 1024)
diff --git a/libc/include/sys/cdefs.h b/libc/include/sys/cdefs.h
index 58038cd..342cfad 100644
--- a/libc/include/sys/cdefs.h
+++ b/libc/include/sys/cdefs.h
@@ -432,9 +432,9 @@
 
 /* Like __LIBC_HIDDEN__, but preserves binary compatibility for LP32. */
 #ifdef __LP64__
-#define __LIBC64_HIDDEN__ __LIBC_HIDDEN__
+#define __LIBC32_LEGACY_PUBLIC__ __LIBC_HIDDEN__
 #else
-#define __LIBC64_HIDDEN__ __LIBC_ABI_PUBLIC__
+#define __LIBC32_LEGACY_PUBLIC__ __LIBC_ABI_PUBLIC__
 #endif
 
 /* Used to tag non-static symbols that are public and exposed by the shared library. */
diff --git a/libc/include/sys/endian.h b/libc/include/sys/endian.h
index 2fd480d..60cc030 100644
--- a/libc/include/sys/endian.h
+++ b/libc/include/sys/endian.h
@@ -75,10 +75,6 @@
 #define	HTONL(x) (x) = htonl((u_int32_t)(x))
 #define	HTONS(x) (x) = htons((u_int16_t)(x))
 
-#define swap16 __swap16
-#define swap32 __swap32
-#define swap64 __swap64
-
 #define htobe16 __swap16
 #define htobe32 __swap32
 #define htobe64 __swap64
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/include/sys/resource.h b/libc/include/sys/resource.h
index 3f8dd45..8209dfb 100644
--- a/libc/include/sys/resource.h
+++ b/libc/include/sys/resource.h
@@ -53,10 +53,7 @@
 
 extern int getrusage(int, struct rusage*);
 
-#if __LP64__
-/* Implementing prlimit for 32-bit isn't worth the effort. */
 extern int prlimit(pid_t, int, const struct rlimit*, struct rlimit*);
-#endif
 extern int prlimit64(pid_t, int, const struct rlimit64*, struct rlimit64*);
 
 __END_DECLS
diff --git a/libc/include/sys/shm.h b/libc/include/sys/shm.h
deleted file mode 100644
index c691c29..0000000
--- a/libc/include/sys/shm.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef _SYS_SHM_H_
-#define _SYS_SHM_H_
-
-#include <linux/shm.h>
-
-#endif /* _SYS_SHM_H_ */
diff --git a/libc/include/utmp.h b/libc/include/utmp.h
index 7eeea41..c6f22a5 100644
--- a/libc/include/utmp.h
+++ b/libc/include/utmp.h
@@ -97,8 +97,9 @@
 __BEGIN_DECLS
 
 int utmpname(const char*);
-void setutent();
-struct utmp* getutent();
+void setutent(void);
+struct utmp* getutent(void);
+void endutent(void);
 
 int login_tty(int);
 
diff --git a/libc/libc.map b/libc/libc.arm.map
similarity index 95%
copy from libc/libc.map
copy to libc/libc.arm.map
index fbceb83..69aa272 100644
--- a/libc/libc.map
+++ b/libc/libc.arm.map
@@ -1,3 +1,4 @@
+# Generated by genversionscripts.py. Do not edit.
 LIBC {
   global:
     __assert;
@@ -28,7 +29,6 @@
     __errno;
     __exit; # arm x86 mips
     __extendsfdf2; # arm
-    __fadvise64; # x86 mips
     __fbufsize;
     __fcntl64; # arm x86 mips
     __FD_CLR_chk;
@@ -105,7 +105,6 @@
     __memrchr_chk;
     __memset_chk;
     __mmap2; # arm x86 mips
-    __moddi3; # x86 mips
     __muldf3; # arm
     __muldi3; # arm
     __mulsf3; # arm
@@ -205,7 +204,6 @@
     __sclose; # arm x86 mips
     __sdidinit; # arm x86 mips
     __set_errno; # arm x86 mips
-    __set_thread_area; # x86
     __set_tid_address; # arm x86 mips
     __set_tls; # arm mips
     __sF;
@@ -272,7 +270,6 @@
     __udivdi3; # arm x86 mips
     __udivsi3; # arm
     __umask_chk;
-    __umoddi3; # x86 mips
     __unorddf2; # arm
     __unordsf2; # arm
     __vsnprintf_chk;
@@ -282,7 +279,6 @@
     _ctype_;
     _Exit;
     _exit;
-    _flush_cache; # mips
     _flushlbf;
     _fwalk; # arm x86 mips
     _getlong;
@@ -389,7 +385,6 @@
     endmntent;
     endpwent;
     endservent;
-    endusershell;
     endutent;
     environ;
     epoll_create;
@@ -580,7 +575,6 @@
     gettid;
     gettimeofday;
     getuid;
-    getusershell;
     getutent;
     getwc;
     getwchar;
@@ -780,28 +774,6 @@
     nftw64;
     nice;
     nrand48;
-    ns_format_ttl; # arm64 x86_64 mips64
-    ns_get16; # arm64 x86_64 mips64
-    ns_get32; # arm64 x86_64 mips64
-    ns_initparse; # arm64 x86_64 mips64
-    ns_makecanon; # arm64 x86_64 mips64
-    ns_msg_getflag; # arm64 x86_64 mips64
-    ns_name_compress; # arm64 x86_64 mips64
-    ns_name_ntol; # arm64 x86_64 mips64
-    ns_name_ntop; # arm64 x86_64 mips64
-    ns_name_pack; # arm64 x86_64 mips64
-    ns_name_pton; # arm64 x86_64 mips64
-    ns_name_rollback; # arm64 x86_64 mips64
-    ns_name_skip; # arm64 x86_64 mips64
-    ns_name_uncompress; # arm64 x86_64 mips64
-    ns_name_unpack; # arm64 x86_64 mips64
-    ns_parserr; # arm64 x86_64 mips64
-    ns_put16; # arm64 x86_64 mips64
-    ns_put32; # arm64 x86_64 mips64
-    ns_samename; # arm64 x86_64 mips64
-    ns_skiprr; # arm64 x86_64 mips64
-    ns_sprintrr; # arm64 x86_64 mips64
-    ns_sprintrrf; # arm64 x86_64 mips64
     nsdispatch;
     ntohl;
     ntohs;
@@ -840,7 +812,6 @@
     pread;
     pread64;
     printf;
-    prlimit; # arm64 x86_64 mips64
     prlimit64;
     process_vm_readv;
     process_vm_writev;
@@ -1051,7 +1022,6 @@
     setstate;
     settimeofday;
     setuid;
-    setusershell;
     setutent;
     setvbuf;
     setxattr;
@@ -1340,12 +1310,28 @@
     __pwrite_chk;
     __pwrite64_chk;
     __write_chk;
+    fileno_unlocked;
     getgrgid_r;
     getgrnam_r;
     preadv;
     preadv64;
+    prlimit; # arm mips x86
+    pthread_barrierattr_destroy;
+    pthread_barrierattr_getpshared;
+    pthread_barrierattr_init;
+    pthread_barrierattr_setpshared;
+    pthread_barrier_destroy;
+    pthread_barrier_init;
+    pthread_barrier_wait;
+    pthread_spin_destroy;
+    pthread_spin_init;
+    pthread_spin_lock;
+    pthread_spin_trylock;
+    pthread_spin_unlock;
     pwritev;
     pwritev64;
+    scandirat;
+    scandirat64;
     strchrnul;
 } LIBC;
 
diff --git a/libc/libc.map b/libc/libc.arm64.map
similarity index 68%
copy from libc/libc.map
copy to libc/libc.arm64.map
index fbceb83..763f77e 100644
--- a/libc/libc.map
+++ b/libc/libc.arm64.map
@@ -1,52 +1,25 @@
+# Generated by genversionscripts.py. Do not edit.
 LIBC {
   global:
     __assert;
     __assert2;
-    __atomic_cmpxchg; # arm
-    __atomic_dec; # arm
-    __atomic_inc; # arm
-    __atomic_swap; # arm
     __b64_ntop;
     __b64_pton;
-    __brk; # arm x86 mips
-    __cmpdf2; # arm
     __cmsg_nxthdr;
-    __connect; # arm x86 mips
     __ctype_get_mb_cur_max;
     __cxa_atexit;
     __cxa_finalize;
     __cxa_thread_atexit_impl;
-    __divdf3; # arm
-    __divdi3; # arm x86 mips
-    __divsf3; # arm
-    __divsi3; # arm
     __dn_comp;
     __dn_count_labels;
     __dn_skipname;
-    __epoll_pwait; # arm x86 mips
-    __eqdf2; # arm
     __errno;
-    __exit; # arm x86 mips
-    __extendsfdf2; # arm
-    __fadvise64; # x86 mips
     __fbufsize;
-    __fcntl64; # arm x86 mips
     __FD_CLR_chk;
     __FD_ISSET_chk;
     __FD_SET_chk;
     __fgets_chk;
-    __fixdfsi; # arm
-    __fixsfsi; # arm
-    __fixunssfsi; # arm
     __flbf;
-    __floatdidf; # arm
-    __floatdisf; # arm
-    __floatsidf; # arm
-    __floatsisf; # arm
-    __floatundidf; # arm
-    __floatundisf; # arm
-    __floatunsidf; # arm
-    __floatunsisf; # arm
     __fp_nquery;
     __fp_query;
     __fpclassify;
@@ -57,26 +30,11 @@
     __fpurge;
     __freadable;
     __fsetlocking;
-    __fstatfs64; # arm x86 mips
-    __futex_wait; # arm x86 mips
-    __futex_wake; # arm x86 mips
     __fwritable;
-    __gedf2; # arm
     __get_h_errno;
-    __get_thread; # arm x86 mips
-    __get_tls; # arm x86 mips
-    __getcpu; # arm x86 mips
-    __getcwd; # arm x86 mips
-    __getdents64; # arm x86 mips
-    __getpid; # arm x86 mips
-    __getpriority; # arm x86 mips
     __gnu_basename;
-    __gnu_ldivmod_helper; # arm
     __gnu_strerror_r;
-    __gnu_uldivmod_helper; # arm
-    __gtdf2; # arm
     __hostalias;
-    __ioctl; # arm x86 mips
     __isfinite;
     __isfinitef;
     __isfinitel;
@@ -90,51 +48,17 @@
     __isnormalf;
     __isnormall;
     __isthreaded;
-    __ledf2; # arm
     __libc_current_sigrtmax;
     __libc_current_sigrtmin;
     __libc_init;
-    __llseek; # arm x86 mips
     __loc_aton;
     __loc_ntoa;
-    __lshrdi3; # arm
-    __ltdf2; # arm
     __memchr_chk;
     __memcpy_chk;
     __memmove_chk;
     __memrchr_chk;
     __memset_chk;
-    __mmap2; # arm x86 mips
-    __moddi3; # x86 mips
-    __muldf3; # arm
-    __muldi3; # arm
-    __mulsf3; # arm
-    __nedf2; # arm
-    __ns_format_ttl; # arm x86 mips
-    __ns_get16; # arm x86 mips
-    __ns_get32; # arm x86 mips
-    __ns_initparse; # arm x86 mips
-    __ns_makecanon; # arm x86 mips
-    __ns_msg_getflag; # arm x86 mips
-    __ns_name_compress; # arm x86 mips
-    __ns_name_ntol; # arm x86 mips
-    __ns_name_ntop; # arm x86 mips
-    __ns_name_pack; # arm x86 mips
-    __ns_name_pton; # arm x86 mips
-    __ns_name_rollback; # arm x86 mips
-    __ns_name_skip; # arm x86 mips
-    __ns_name_uncompress; # arm x86 mips
-    __ns_name_unpack; # arm x86 mips
-    __ns_parserr; # arm x86 mips
-    __ns_put16; # arm x86 mips
-    __ns_put32; # arm x86 mips
-    __ns_samename; # arm x86 mips
-    __ns_skiprr; # arm x86 mips
-    __ns_sprintrr; # arm x86 mips
-    __ns_sprintrrf; # arm x86 mips
-    __open; # arm x86 mips
     __open_2;
-    __openat; # arm x86 mips
     __openat_2;
     __p_cdname;
     __p_cdnname;
@@ -149,27 +73,18 @@
     __p_time;
     __p_type;
     __p_type_syms;
-    __page_shift; # arm x86 mips
-    __page_size; # arm x86 mips
     __poll_chk;
-    __popcount_tab; # arm
-    __popcountsi2; # arm x86 mips
-    __ppoll; # arm x86 mips
     __ppoll_chk;
     __pread64_chk;
     __pread_chk;
     __progname;
-    __pselect6; # arm x86 mips
     __pthread_cleanup_pop;
     __pthread_cleanup_push;
-    __pthread_gettid; # arm x86 mips
-    __ptrace; # arm x86 mips
     __putlong;
     __putshort;
     __read_chk;
     __readlink_chk;
     __readlinkat_chk;
-    __reboot; # arm x86 mips
     __recvfrom_chk;
     __register_atfork;
     __res_close;
@@ -192,41 +107,14 @@
     __res_send;
     __res_send_setqhook;
     __res_send_setrhook;
-    __restore_core_regs; # arm
-    __rt_sigaction; # arm x86 mips
-    __rt_sigpending; # arm x86 mips
-    __rt_sigprocmask; # arm x86 mips
-    __rt_sigsuspend; # arm x86 mips
-    __rt_sigtimedwait; # arm x86 mips
     __sched_cpualloc;
     __sched_cpucount;
     __sched_cpufree;
-    __sched_getaffinity; # arm x86 mips
-    __sclose; # arm x86 mips
-    __sdidinit; # arm x86 mips
-    __set_errno; # arm x86 mips
-    __set_thread_area; # x86
-    __set_tid_address; # arm x86 mips
-    __set_tls; # arm mips
     __sF;
-    __sflags; # arm x86 mips
-    __sflush; # arm x86 mips
-    __sfp; # arm x86 mips
-    __sglue; # arm x86 mips
-    __sigaction; # arm x86 mips
-    __signalfd4; # arm x86 mips
-    __sinit; # arm x86 mips
-    __smakebuf; # arm x86 mips
     __snprintf_chk;
-    __socket; # arm x86 mips
     __sprintf_chk;
-    __sread; # arm x86 mips
-    __srefill; # arm x86 mips
-    __srget; # arm x86 mips
-    __sseek; # arm x86 mips
     __stack_chk_fail;
     __stack_chk_guard;
-    __statfs64; # arm x86 mips
     __stpcpy_chk;
     __stpncpy_chk;
     __stpncpy_chk2;
@@ -240,11 +128,6 @@
     __strncpy_chk;
     __strncpy_chk2;
     __strrchr_chk;
-    __subdf3; # arm
-    __subsf3; # arm
-    __swbuf; # arm x86 mips
-    __swrite; # arm x86 mips
-    __swsetup; # arm x86 mips
     __sym_ntop;
     __sym_ntos;
     __sym_ston;
@@ -263,28 +146,13 @@
     __system_property_set_filename;
     __system_property_update;
     __system_property_wait_any;
-    __timer_create; # arm x86 mips
-    __timer_delete; # arm x86 mips
-    __timer_getoverrun; # arm x86 mips
-    __timer_gettime; # arm x86 mips
-    __timer_settime; # arm x86 mips
-    __truncdfsf2; # arm
-    __udivdi3; # arm x86 mips
-    __udivsi3; # arm
     __umask_chk;
-    __umoddi3; # x86 mips
-    __unorddf2; # arm
-    __unordsf2; # arm
     __vsnprintf_chk;
     __vsprintf_chk;
-    __wait4; # arm x86 mips
-    __waitid; # arm x86 mips
     _ctype_;
     _Exit;
     _exit;
-    _flush_cache; # mips
     _flushlbf;
-    _fwalk; # arm x86 mips
     _getlong;
     _getshort;
     _longjmp;
@@ -293,9 +161,7 @@
     _resolv_set_nameservers_for_net;
     _setjmp;
     _tolower;
-    _tolower_tab_; # arm x86 mips
     _toupper;
-    _toupper_tab_; # arm x86 mips
     abort;
     abs;
     accept;
@@ -311,13 +177,9 @@
     android_gethostbynamefornet;
     android_set_abort_message;
     arc4random;
-    arc4random_addrandom; # arm x86 mips
     arc4random_buf;
-    arc4random_stir; # arm x86 mips
     arc4random_uniform;
     asctime;
-    asctime64; # arm x86 mips
-    asctime64_r; # arm x86 mips
     asctime_r;
     asprintf;
     at_quick_exit;
@@ -326,18 +188,13 @@
     atol;
     atoll;
     basename;
-    basename_r; # arm x86 mips
-    bcopy; # arm x86 mips
     bind;
     bindresvport;
     brk;
-    bsd_signal; # arm x86 mips
     bsearch;
     btowc;
-    bzero; # arm x86 mips
     c16rtomb;
     c32rtomb;
-    cacheflush; # arm mips
     calloc;
     capget;
     capset;
@@ -368,8 +225,6 @@
     creat;
     creat64;
     ctime;
-    ctime64; # arm x86 mips
-    ctime64_r; # arm x86 mips
     ctime_r;
     daemon;
     daylight;
@@ -377,7 +232,6 @@
     difftime;
     dirfd;
     dirname;
-    dirname_r; # arm x86 mips
     div;
     dn_expand;
     dprintf;
@@ -389,7 +243,6 @@
     endmntent;
     endpwent;
     endservent;
-    endusershell;
     endutent;
     environ;
     epoll_create;
@@ -421,8 +274,6 @@
     execvpe;
     exit;
     faccessat;
-    fake_gmtime_r; # arm x86 mips
-    fake_localtime_r; # arm x86 mips
     fallocate;
     fallocate64;
     fchdir;
@@ -435,7 +286,6 @@
     fdatasync;
     fdopen;
     fdopendir;
-    fdprintf; # arm x86 mips
     feof;
     feof_unlocked;
     ferror;
@@ -488,7 +338,6 @@
     fsync;
     ftell;
     ftello;
-    ftime; # arm x86 mips
     ftok;
     ftruncate;
     ftruncate64;
@@ -521,8 +370,6 @@
     getchar_unlocked;
     getcwd;
     getdelim;
-    getdents; # arm x86 mips
-    getdtablesize; # arm x86 mips
     getegid;
     getenv;
     geteuid;
@@ -580,14 +427,11 @@
     gettid;
     gettimeofday;
     getuid;
-    getusershell;
     getutent;
     getwc;
     getwchar;
     getxattr;
     gmtime;
-    gmtime64; # arm x86 mips
-    gmtime64_r; # arm x86 mips
     gmtime_r;
     grantpt;
     herror;
@@ -598,7 +442,6 @@
     if_nametoindex;
     imaxabs;
     imaxdiv;
-    index; # arm x86 mips
     inet_addr;
     inet_aton;
     inet_lnaof;
@@ -651,7 +494,6 @@
     isprint_l;
     ispunct;
     ispunct_l;
-    issetugid; # arm x86 mips
     isspace;
     isspace_l;
     isupper;
@@ -704,8 +546,6 @@
     llistxattr;
     localeconv;
     localtime;
-    localtime64; # arm x86 mips
-    localtime64_r; # arm x86 mips
     localtime_r;
     login_tty;
     longjmp;
@@ -741,7 +581,6 @@
     mempcpy;
     memrchr;
     memset;
-    memswap; # arm x86 mips
     mincore;
     mkdir;
     mkdirat;
@@ -760,7 +599,6 @@
     mkstemps64;
     mktemp;
     mktime;
-    mktime64; # arm x86 mips
     mktime_tz;
     mlock;
     mlockall;
@@ -855,7 +693,6 @@
     pthread_attr_getschedpolicy;
     pthread_attr_getscope;
     pthread_attr_getstack;
-    pthread_attr_getstackaddr; # arm x86 mips
     pthread_attr_getstacksize;
     pthread_attr_init;
     pthread_attr_setdetachstate;
@@ -864,17 +701,12 @@
     pthread_attr_setschedpolicy;
     pthread_attr_setscope;
     pthread_attr_setstack;
-    pthread_attr_setstackaddr; # arm x86 mips
     pthread_attr_setstacksize;
     pthread_cond_broadcast;
     pthread_cond_destroy;
     pthread_cond_init;
     pthread_cond_signal;
     pthread_cond_timedwait;
-    pthread_cond_timedwait_monotonic; # arm x86 mips
-    pthread_cond_timedwait_monotonic_np; # arm x86 mips
-    pthread_cond_timedwait_relative_np; # arm x86 mips
-    pthread_cond_timeout_np; # arm x86 mips
     pthread_cond_wait;
     pthread_condattr_destroy;
     pthread_condattr_getclock;
@@ -898,7 +730,6 @@
     pthread_mutex_destroy;
     pthread_mutex_init;
     pthread_mutex_lock;
-    pthread_mutex_lock_timeout_np; # arm x86 mips
     pthread_mutex_timedlock;
     pthread_mutex_trylock;
     pthread_mutex_unlock;
@@ -939,10 +770,8 @@
     putenv;
     puts;
     pututline;
-    putw; # arm x86 mips
     putwc;
     putwchar;
-    pvalloc; # arm x86 mips
     pwrite;
     pwrite64;
     qsort;
@@ -980,7 +809,6 @@
     res_mkquery;
     res_query;
     res_search;
-    restore_core_regs; # arm
     rewind;
     rewinddir;
     rmdir;
@@ -1051,7 +879,6 @@
     setstate;
     settimeofday;
     setuid;
-    setusershell;
     setutent;
     setvbuf;
     setxattr;
@@ -1124,8 +951,6 @@
     strncpy;
     strndup;
     strnlen;
-    strntoimax; # arm x86 mips
-    strntoumax; # arm x86 mips
     strpbrk;
     strptime;
     strrchr;
@@ -1144,7 +969,6 @@
     strtoll;
     strtoll_l;
     strtoq;
-    strtotimeval; # arm x86 mips
     strtoul;
     strtoull;
     strtoull_l;
@@ -1166,7 +990,6 @@
     sysinfo;
     syslog;
     system;
-    sysv_signal; # arm x86 mips
     tcdrain;
     tcflow;
     tcflush;
@@ -1185,9 +1008,7 @@
     tgkill;
     time;
     timegm;
-    timegm64; # arm x86 mips
     timelocal;
-    timelocal64; # arm x86 mips
     timer_create;
     timer_delete;
     timer_getoverrun;
@@ -1198,7 +1019,6 @@
     timerfd_settime;
     times;
     timezone;
-    tkill; # arm x86 mips
     tmpfile;
     tmpnam;
     toascii;
@@ -1235,12 +1055,10 @@
     utimensat;
     utimes;
     utmpname;
-    valloc; # arm x86 mips
     vasprintf;
     vdprintf;
     verr;
     verrx;
-    vfdprintf; # arm x86 mips
     vfork;
     vfprintf;
     vfscanf;
@@ -1260,7 +1078,6 @@
     vwprintf;
     vwscanf;
     wait;
-    wait3; # arm x86 mips
     wait4;
     waitid;
     waitpid;
@@ -1309,7 +1126,6 @@
     wcstoull;
     wcstoull_l;
     wcstoumax;
-    wcswcs; # arm x86 mips
     wcswidth;
     wcsxfrm;
     wcsxfrm_l;
@@ -1340,134 +1156,33 @@
     __pwrite_chk;
     __pwrite64_chk;
     __write_chk;
+    fileno_unlocked;
     getgrgid_r;
     getgrnam_r;
     preadv;
     preadv64;
+    pthread_barrierattr_destroy;
+    pthread_barrierattr_getpshared;
+    pthread_barrierattr_init;
+    pthread_barrierattr_setpshared;
+    pthread_barrier_destroy;
+    pthread_barrier_init;
+    pthread_barrier_wait;
+    pthread_spin_destroy;
+    pthread_spin_init;
+    pthread_spin_lock;
+    pthread_spin_trylock;
+    pthread_spin_unlock;
     pwritev;
     pwritev64;
+    scandirat;
+    scandirat64;
     strchrnul;
 } LIBC;
 
 LIBC_PRIVATE {
   global:
-    ___Unwind_Backtrace; # arm
-    ___Unwind_ForcedUnwind; # arm
-    ___Unwind_RaiseException; # arm
-    ___Unwind_Resume; # arm
-    ___Unwind_Resume_or_Rethrow; # arm
-    __accept4; # arm x86 mips
-    __adddf3; # arm
-    __addsf3; # arm
-    __aeabi_atexit; # arm
-    __aeabi_cdcmpeq; # arm
-    __aeabi_cdcmple; # arm
-    __aeabi_cdrcmple; # arm
-    __aeabi_d2f; # arm
-    __aeabi_d2iz; # arm
-    __aeabi_dadd; # arm
-    __aeabi_dcmpeq; # arm
-    __aeabi_dcmpge; # arm
-    __aeabi_dcmpgt; # arm
-    __aeabi_dcmple; # arm
-    __aeabi_dcmplt; # arm
-    __aeabi_dcmpun; # arm
-    __aeabi_ddiv; # arm
-    __aeabi_dmul; # arm
-    __aeabi_drsub; # arm
-    __aeabi_dsub; # arm
-    __aeabi_f2d; # arm
-    __aeabi_f2iz; # arm
-    __aeabi_f2uiz; # arm
-    __aeabi_fadd; # arm
-    __aeabi_fcmpun; # arm
-    __aeabi_fdiv; # arm
-    __aeabi_fmul; # arm
-    __aeabi_frsub; # arm
-    __aeabi_fsub; # arm
-    __aeabi_i2d; # arm
-    __aeabi_i2f; # arm
-    __aeabi_idiv; # arm
-    __aeabi_idiv0; # arm
-    __aeabi_idivmod; # arm
-    __aeabi_l2d; # arm
-    __aeabi_l2f; # arm
-    __aeabi_lasr; # arm
-    __aeabi_ldiv0; # arm
-    __aeabi_ldivmod; # arm
-    __aeabi_llsl; # arm
-    __aeabi_llsr; # arm
-    __aeabi_lmul; # arm
-    __aeabi_memclr; # arm
-    __aeabi_memclr4; # arm
-    __aeabi_memclr8; # arm
-    __aeabi_memcpy; # arm
-    __aeabi_memcpy4; # arm
-    __aeabi_memcpy8; # arm
-    __aeabi_memmove; # arm
-    __aeabi_memmove4; # arm
-    __aeabi_memmove8; # arm
-    __aeabi_memset; # arm
-    __aeabi_memset4; # arm
-    __aeabi_memset8; # arm
-    __aeabi_ui2d; # arm
-    __aeabi_ui2f; # arm
-    __aeabi_uidiv; # arm
-    __aeabi_uidivmod; # arm
-    __aeabi_ul2d; # arm
-    __aeabi_ul2f; # arm
-    __aeabi_uldivmod; # arm
-    __aeabi_unwind_cpp_pr0; # arm
-    __aeabi_unwind_cpp_pr1; # arm
-    __aeabi_unwind_cpp_pr2; # arm
-    __arm_fadvise64_64; # arm
-    __ashldi3; # arm
-    __ashrdi3; # arm
-    __bionic_brk; # arm x86 mips
-    __bionic_libgcc_compat_symbols; # arm x86
-    __bionic_libgcc_unwind_symbols; # arm
-    __dso_handle; # arm
-    __gnu_Unwind_Backtrace; # arm
-    __gnu_unwind_execute; # arm
-    __gnu_Unwind_Find_exidx; # arm
-    __gnu_Unwind_ForcedUnwind; # arm
-    __gnu_unwind_frame; # arm
-    __gnu_Unwind_RaiseException; # arm
-    __gnu_Unwind_Restore_VFP; # arm
-    __gnu_Unwind_Restore_VFP_D; # arm
-    __gnu_Unwind_Restore_VFP_D_16_to_31; # arm
-    __gnu_Unwind_Restore_WMMXC; # arm
-    __gnu_Unwind_Restore_WMMXD; # arm
-    __gnu_Unwind_Resume; # arm
-    __gnu_Unwind_Resume_or_Rethrow; # arm
-    __gnu_Unwind_Save_VFP; # arm
-    __gnu_Unwind_Save_VFP_D; # arm
-    __gnu_Unwind_Save_VFP_D_16_to_31; # arm
-    __gnu_Unwind_Save_WMMXC; # arm
-    __gnu_Unwind_Save_WMMXD; # arm
-    _Unwind_Backtrace; # arm
-    _Unwind_Complete; # arm
-    _Unwind_DeleteException; # arm
-    _Unwind_ForcedUnwind; # arm
-    _Unwind_GetCFA; # arm
-    _Unwind_GetDataRelBase; # arm
-    _Unwind_GetLanguageSpecificData; # arm
-    _Unwind_GetRegionStart; # arm
-    _Unwind_GetTextRelBase; # arm
-    _Unwind_RaiseException; # arm
-    _Unwind_Resume; # arm
-    _Unwind_Resume_or_Rethrow; # arm
-    _Unwind_VRS_Get; # arm
-    _Unwind_VRS_Pop; # arm
-    _Unwind_VRS_Set; # arm
-    atexit; # arm
-    dlmalloc; # arm x86 mips
     dlmalloc_inspect_all;
     dlmalloc_trim;
-    dlmalloc_usable_size; # arm x86 mips
     gMallocLeakZygoteChild;
-    SHA1Final; # arm x86 mips
-    SHA1Init; # arm x86 mips
-    SHA1Transform; # arm x86 mips
-    SHA1Update; # arm x86 mips
 } LIBC_N;
diff --git a/libc/libc.map b/libc/libc.map.txt
similarity index 98%
rename from libc/libc.map
rename to libc/libc.map.txt
index fbceb83..76cb168 100644
--- a/libc/libc.map
+++ b/libc/libc.map.txt
@@ -389,7 +389,6 @@
     endmntent;
     endpwent;
     endservent;
-    endusershell;
     endutent;
     environ;
     epoll_create;
@@ -580,7 +579,6 @@
     gettid;
     gettimeofday;
     getuid;
-    getusershell;
     getutent;
     getwc;
     getwchar;
@@ -1051,7 +1049,6 @@
     setstate;
     settimeofday;
     setuid;
-    setusershell;
     setutent;
     setvbuf;
     setxattr;
@@ -1340,12 +1337,28 @@
     __pwrite_chk;
     __pwrite64_chk;
     __write_chk;
+    fileno_unlocked;
     getgrgid_r;
     getgrnam_r;
     preadv;
     preadv64;
+    prlimit; # arm mips x86
+    pthread_barrierattr_destroy;
+    pthread_barrierattr_getpshared;
+    pthread_barrierattr_init;
+    pthread_barrierattr_setpshared;
+    pthread_barrier_destroy;
+    pthread_barrier_init;
+    pthread_barrier_wait;
+    pthread_spin_destroy;
+    pthread_spin_init;
+    pthread_spin_lock;
+    pthread_spin_trylock;
+    pthread_spin_unlock;
     pwritev;
     pwritev64;
+    scandirat;
+    scandirat64;
     strchrnul;
 } LIBC;
 
diff --git a/libc/libc.map b/libc/libc.mips.map
similarity index 81%
copy from libc/libc.map
copy to libc/libc.mips.map
index fbceb83..e268bad 100644
--- a/libc/libc.map
+++ b/libc/libc.mips.map
@@ -1,33 +1,24 @@
+# Generated by genversionscripts.py. Do not edit.
 LIBC {
   global:
     __assert;
     __assert2;
-    __atomic_cmpxchg; # arm
-    __atomic_dec; # arm
-    __atomic_inc; # arm
-    __atomic_swap; # arm
     __b64_ntop;
     __b64_pton;
     __brk; # arm x86 mips
-    __cmpdf2; # arm
     __cmsg_nxthdr;
     __connect; # arm x86 mips
     __ctype_get_mb_cur_max;
     __cxa_atexit;
     __cxa_finalize;
     __cxa_thread_atexit_impl;
-    __divdf3; # arm
     __divdi3; # arm x86 mips
-    __divsf3; # arm
-    __divsi3; # arm
     __dn_comp;
     __dn_count_labels;
     __dn_skipname;
     __epoll_pwait; # arm x86 mips
-    __eqdf2; # arm
     __errno;
     __exit; # arm x86 mips
-    __extendsfdf2; # arm
     __fadvise64; # x86 mips
     __fbufsize;
     __fcntl64; # arm x86 mips
@@ -35,18 +26,7 @@
     __FD_ISSET_chk;
     __FD_SET_chk;
     __fgets_chk;
-    __fixdfsi; # arm
-    __fixsfsi; # arm
-    __fixunssfsi; # arm
     __flbf;
-    __floatdidf; # arm
-    __floatdisf; # arm
-    __floatsidf; # arm
-    __floatsisf; # arm
-    __floatundidf; # arm
-    __floatundisf; # arm
-    __floatunsidf; # arm
-    __floatunsisf; # arm
     __fp_nquery;
     __fp_query;
     __fpclassify;
@@ -61,7 +41,6 @@
     __futex_wait; # arm x86 mips
     __futex_wake; # arm x86 mips
     __fwritable;
-    __gedf2; # arm
     __get_h_errno;
     __get_thread; # arm x86 mips
     __get_tls; # arm x86 mips
@@ -71,10 +50,7 @@
     __getpid; # arm x86 mips
     __getpriority; # arm x86 mips
     __gnu_basename;
-    __gnu_ldivmod_helper; # arm
     __gnu_strerror_r;
-    __gnu_uldivmod_helper; # arm
-    __gtdf2; # arm
     __hostalias;
     __ioctl; # arm x86 mips
     __isfinite;
@@ -90,15 +66,12 @@
     __isnormalf;
     __isnormall;
     __isthreaded;
-    __ledf2; # arm
     __libc_current_sigrtmax;
     __libc_current_sigrtmin;
     __libc_init;
     __llseek; # arm x86 mips
     __loc_aton;
     __loc_ntoa;
-    __lshrdi3; # arm
-    __ltdf2; # arm
     __memchr_chk;
     __memcpy_chk;
     __memmove_chk;
@@ -106,10 +79,6 @@
     __memset_chk;
     __mmap2; # arm x86 mips
     __moddi3; # x86 mips
-    __muldf3; # arm
-    __muldi3; # arm
-    __mulsf3; # arm
-    __nedf2; # arm
     __ns_format_ttl; # arm x86 mips
     __ns_get16; # arm x86 mips
     __ns_get32; # arm x86 mips
@@ -152,7 +121,6 @@
     __page_shift; # arm x86 mips
     __page_size; # arm x86 mips
     __poll_chk;
-    __popcount_tab; # arm
     __popcountsi2; # arm x86 mips
     __ppoll; # arm x86 mips
     __ppoll_chk;
@@ -192,7 +160,6 @@
     __res_send;
     __res_send_setqhook;
     __res_send_setrhook;
-    __restore_core_regs; # arm
     __rt_sigaction; # arm x86 mips
     __rt_sigpending; # arm x86 mips
     __rt_sigprocmask; # arm x86 mips
@@ -205,7 +172,6 @@
     __sclose; # arm x86 mips
     __sdidinit; # arm x86 mips
     __set_errno; # arm x86 mips
-    __set_thread_area; # x86
     __set_tid_address; # arm x86 mips
     __set_tls; # arm mips
     __sF;
@@ -240,8 +206,6 @@
     __strncpy_chk;
     __strncpy_chk2;
     __strrchr_chk;
-    __subdf3; # arm
-    __subsf3; # arm
     __swbuf; # arm x86 mips
     __swrite; # arm x86 mips
     __swsetup; # arm x86 mips
@@ -268,13 +232,9 @@
     __timer_getoverrun; # arm x86 mips
     __timer_gettime; # arm x86 mips
     __timer_settime; # arm x86 mips
-    __truncdfsf2; # arm
     __udivdi3; # arm x86 mips
-    __udivsi3; # arm
     __umask_chk;
     __umoddi3; # x86 mips
-    __unorddf2; # arm
-    __unordsf2; # arm
     __vsnprintf_chk;
     __vsprintf_chk;
     __wait4; # arm x86 mips
@@ -389,7 +349,6 @@
     endmntent;
     endpwent;
     endservent;
-    endusershell;
     endutent;
     environ;
     epoll_create;
@@ -580,7 +539,6 @@
     gettid;
     gettimeofday;
     getuid;
-    getusershell;
     getutent;
     getwc;
     getwchar;
@@ -780,28 +738,6 @@
     nftw64;
     nice;
     nrand48;
-    ns_format_ttl; # arm64 x86_64 mips64
-    ns_get16; # arm64 x86_64 mips64
-    ns_get32; # arm64 x86_64 mips64
-    ns_initparse; # arm64 x86_64 mips64
-    ns_makecanon; # arm64 x86_64 mips64
-    ns_msg_getflag; # arm64 x86_64 mips64
-    ns_name_compress; # arm64 x86_64 mips64
-    ns_name_ntol; # arm64 x86_64 mips64
-    ns_name_ntop; # arm64 x86_64 mips64
-    ns_name_pack; # arm64 x86_64 mips64
-    ns_name_pton; # arm64 x86_64 mips64
-    ns_name_rollback; # arm64 x86_64 mips64
-    ns_name_skip; # arm64 x86_64 mips64
-    ns_name_uncompress; # arm64 x86_64 mips64
-    ns_name_unpack; # arm64 x86_64 mips64
-    ns_parserr; # arm64 x86_64 mips64
-    ns_put16; # arm64 x86_64 mips64
-    ns_put32; # arm64 x86_64 mips64
-    ns_samename; # arm64 x86_64 mips64
-    ns_skiprr; # arm64 x86_64 mips64
-    ns_sprintrr; # arm64 x86_64 mips64
-    ns_sprintrrf; # arm64 x86_64 mips64
     nsdispatch;
     ntohl;
     ntohs;
@@ -840,7 +776,6 @@
     pread;
     pread64;
     printf;
-    prlimit; # arm64 x86_64 mips64
     prlimit64;
     process_vm_readv;
     process_vm_writev;
@@ -980,7 +915,6 @@
     res_mkquery;
     res_query;
     res_search;
-    restore_core_regs; # arm
     rewind;
     rewinddir;
     rmdir;
@@ -1051,7 +985,6 @@
     setstate;
     settimeofday;
     setuid;
-    setusershell;
     setutent;
     setvbuf;
     setxattr;
@@ -1340,127 +1273,35 @@
     __pwrite_chk;
     __pwrite64_chk;
     __write_chk;
+    fileno_unlocked;
     getgrgid_r;
     getgrnam_r;
     preadv;
     preadv64;
+    prlimit; # arm mips x86
+    pthread_barrierattr_destroy;
+    pthread_barrierattr_getpshared;
+    pthread_barrierattr_init;
+    pthread_barrierattr_setpshared;
+    pthread_barrier_destroy;
+    pthread_barrier_init;
+    pthread_barrier_wait;
+    pthread_spin_destroy;
+    pthread_spin_init;
+    pthread_spin_lock;
+    pthread_spin_trylock;
+    pthread_spin_unlock;
     pwritev;
     pwritev64;
+    scandirat;
+    scandirat64;
     strchrnul;
 } LIBC;
 
 LIBC_PRIVATE {
   global:
-    ___Unwind_Backtrace; # arm
-    ___Unwind_ForcedUnwind; # arm
-    ___Unwind_RaiseException; # arm
-    ___Unwind_Resume; # arm
-    ___Unwind_Resume_or_Rethrow; # arm
     __accept4; # arm x86 mips
-    __adddf3; # arm
-    __addsf3; # arm
-    __aeabi_atexit; # arm
-    __aeabi_cdcmpeq; # arm
-    __aeabi_cdcmple; # arm
-    __aeabi_cdrcmple; # arm
-    __aeabi_d2f; # arm
-    __aeabi_d2iz; # arm
-    __aeabi_dadd; # arm
-    __aeabi_dcmpeq; # arm
-    __aeabi_dcmpge; # arm
-    __aeabi_dcmpgt; # arm
-    __aeabi_dcmple; # arm
-    __aeabi_dcmplt; # arm
-    __aeabi_dcmpun; # arm
-    __aeabi_ddiv; # arm
-    __aeabi_dmul; # arm
-    __aeabi_drsub; # arm
-    __aeabi_dsub; # arm
-    __aeabi_f2d; # arm
-    __aeabi_f2iz; # arm
-    __aeabi_f2uiz; # arm
-    __aeabi_fadd; # arm
-    __aeabi_fcmpun; # arm
-    __aeabi_fdiv; # arm
-    __aeabi_fmul; # arm
-    __aeabi_frsub; # arm
-    __aeabi_fsub; # arm
-    __aeabi_i2d; # arm
-    __aeabi_i2f; # arm
-    __aeabi_idiv; # arm
-    __aeabi_idiv0; # arm
-    __aeabi_idivmod; # arm
-    __aeabi_l2d; # arm
-    __aeabi_l2f; # arm
-    __aeabi_lasr; # arm
-    __aeabi_ldiv0; # arm
-    __aeabi_ldivmod; # arm
-    __aeabi_llsl; # arm
-    __aeabi_llsr; # arm
-    __aeabi_lmul; # arm
-    __aeabi_memclr; # arm
-    __aeabi_memclr4; # arm
-    __aeabi_memclr8; # arm
-    __aeabi_memcpy; # arm
-    __aeabi_memcpy4; # arm
-    __aeabi_memcpy8; # arm
-    __aeabi_memmove; # arm
-    __aeabi_memmove4; # arm
-    __aeabi_memmove8; # arm
-    __aeabi_memset; # arm
-    __aeabi_memset4; # arm
-    __aeabi_memset8; # arm
-    __aeabi_ui2d; # arm
-    __aeabi_ui2f; # arm
-    __aeabi_uidiv; # arm
-    __aeabi_uidivmod; # arm
-    __aeabi_ul2d; # arm
-    __aeabi_ul2f; # arm
-    __aeabi_uldivmod; # arm
-    __aeabi_unwind_cpp_pr0; # arm
-    __aeabi_unwind_cpp_pr1; # arm
-    __aeabi_unwind_cpp_pr2; # arm
-    __arm_fadvise64_64; # arm
-    __ashldi3; # arm
-    __ashrdi3; # arm
     __bionic_brk; # arm x86 mips
-    __bionic_libgcc_compat_symbols; # arm x86
-    __bionic_libgcc_unwind_symbols; # arm
-    __dso_handle; # arm
-    __gnu_Unwind_Backtrace; # arm
-    __gnu_unwind_execute; # arm
-    __gnu_Unwind_Find_exidx; # arm
-    __gnu_Unwind_ForcedUnwind; # arm
-    __gnu_unwind_frame; # arm
-    __gnu_Unwind_RaiseException; # arm
-    __gnu_Unwind_Restore_VFP; # arm
-    __gnu_Unwind_Restore_VFP_D; # arm
-    __gnu_Unwind_Restore_VFP_D_16_to_31; # arm
-    __gnu_Unwind_Restore_WMMXC; # arm
-    __gnu_Unwind_Restore_WMMXD; # arm
-    __gnu_Unwind_Resume; # arm
-    __gnu_Unwind_Resume_or_Rethrow; # arm
-    __gnu_Unwind_Save_VFP; # arm
-    __gnu_Unwind_Save_VFP_D; # arm
-    __gnu_Unwind_Save_VFP_D_16_to_31; # arm
-    __gnu_Unwind_Save_WMMXC; # arm
-    __gnu_Unwind_Save_WMMXD; # arm
-    _Unwind_Backtrace; # arm
-    _Unwind_Complete; # arm
-    _Unwind_DeleteException; # arm
-    _Unwind_ForcedUnwind; # arm
-    _Unwind_GetCFA; # arm
-    _Unwind_GetDataRelBase; # arm
-    _Unwind_GetLanguageSpecificData; # arm
-    _Unwind_GetRegionStart; # arm
-    _Unwind_GetTextRelBase; # arm
-    _Unwind_RaiseException; # arm
-    _Unwind_Resume; # arm
-    _Unwind_Resume_or_Rethrow; # arm
-    _Unwind_VRS_Get; # arm
-    _Unwind_VRS_Pop; # arm
-    _Unwind_VRS_Set; # arm
-    atexit; # arm
     dlmalloc; # arm x86 mips
     dlmalloc_inspect_all;
     dlmalloc_trim;
diff --git a/libc/libc.map b/libc/libc.mips64.map
similarity index 68%
copy from libc/libc.map
copy to libc/libc.mips64.map
index fbceb83..763f77e 100644
--- a/libc/libc.map
+++ b/libc/libc.mips64.map
@@ -1,52 +1,25 @@
+# Generated by genversionscripts.py. Do not edit.
 LIBC {
   global:
     __assert;
     __assert2;
-    __atomic_cmpxchg; # arm
-    __atomic_dec; # arm
-    __atomic_inc; # arm
-    __atomic_swap; # arm
     __b64_ntop;
     __b64_pton;
-    __brk; # arm x86 mips
-    __cmpdf2; # arm
     __cmsg_nxthdr;
-    __connect; # arm x86 mips
     __ctype_get_mb_cur_max;
     __cxa_atexit;
     __cxa_finalize;
     __cxa_thread_atexit_impl;
-    __divdf3; # arm
-    __divdi3; # arm x86 mips
-    __divsf3; # arm
-    __divsi3; # arm
     __dn_comp;
     __dn_count_labels;
     __dn_skipname;
-    __epoll_pwait; # arm x86 mips
-    __eqdf2; # arm
     __errno;
-    __exit; # arm x86 mips
-    __extendsfdf2; # arm
-    __fadvise64; # x86 mips
     __fbufsize;
-    __fcntl64; # arm x86 mips
     __FD_CLR_chk;
     __FD_ISSET_chk;
     __FD_SET_chk;
     __fgets_chk;
-    __fixdfsi; # arm
-    __fixsfsi; # arm
-    __fixunssfsi; # arm
     __flbf;
-    __floatdidf; # arm
-    __floatdisf; # arm
-    __floatsidf; # arm
-    __floatsisf; # arm
-    __floatundidf; # arm
-    __floatundisf; # arm
-    __floatunsidf; # arm
-    __floatunsisf; # arm
     __fp_nquery;
     __fp_query;
     __fpclassify;
@@ -57,26 +30,11 @@
     __fpurge;
     __freadable;
     __fsetlocking;
-    __fstatfs64; # arm x86 mips
-    __futex_wait; # arm x86 mips
-    __futex_wake; # arm x86 mips
     __fwritable;
-    __gedf2; # arm
     __get_h_errno;
-    __get_thread; # arm x86 mips
-    __get_tls; # arm x86 mips
-    __getcpu; # arm x86 mips
-    __getcwd; # arm x86 mips
-    __getdents64; # arm x86 mips
-    __getpid; # arm x86 mips
-    __getpriority; # arm x86 mips
     __gnu_basename;
-    __gnu_ldivmod_helper; # arm
     __gnu_strerror_r;
-    __gnu_uldivmod_helper; # arm
-    __gtdf2; # arm
     __hostalias;
-    __ioctl; # arm x86 mips
     __isfinite;
     __isfinitef;
     __isfinitel;
@@ -90,51 +48,17 @@
     __isnormalf;
     __isnormall;
     __isthreaded;
-    __ledf2; # arm
     __libc_current_sigrtmax;
     __libc_current_sigrtmin;
     __libc_init;
-    __llseek; # arm x86 mips
     __loc_aton;
     __loc_ntoa;
-    __lshrdi3; # arm
-    __ltdf2; # arm
     __memchr_chk;
     __memcpy_chk;
     __memmove_chk;
     __memrchr_chk;
     __memset_chk;
-    __mmap2; # arm x86 mips
-    __moddi3; # x86 mips
-    __muldf3; # arm
-    __muldi3; # arm
-    __mulsf3; # arm
-    __nedf2; # arm
-    __ns_format_ttl; # arm x86 mips
-    __ns_get16; # arm x86 mips
-    __ns_get32; # arm x86 mips
-    __ns_initparse; # arm x86 mips
-    __ns_makecanon; # arm x86 mips
-    __ns_msg_getflag; # arm x86 mips
-    __ns_name_compress; # arm x86 mips
-    __ns_name_ntol; # arm x86 mips
-    __ns_name_ntop; # arm x86 mips
-    __ns_name_pack; # arm x86 mips
-    __ns_name_pton; # arm x86 mips
-    __ns_name_rollback; # arm x86 mips
-    __ns_name_skip; # arm x86 mips
-    __ns_name_uncompress; # arm x86 mips
-    __ns_name_unpack; # arm x86 mips
-    __ns_parserr; # arm x86 mips
-    __ns_put16; # arm x86 mips
-    __ns_put32; # arm x86 mips
-    __ns_samename; # arm x86 mips
-    __ns_skiprr; # arm x86 mips
-    __ns_sprintrr; # arm x86 mips
-    __ns_sprintrrf; # arm x86 mips
-    __open; # arm x86 mips
     __open_2;
-    __openat; # arm x86 mips
     __openat_2;
     __p_cdname;
     __p_cdnname;
@@ -149,27 +73,18 @@
     __p_time;
     __p_type;
     __p_type_syms;
-    __page_shift; # arm x86 mips
-    __page_size; # arm x86 mips
     __poll_chk;
-    __popcount_tab; # arm
-    __popcountsi2; # arm x86 mips
-    __ppoll; # arm x86 mips
     __ppoll_chk;
     __pread64_chk;
     __pread_chk;
     __progname;
-    __pselect6; # arm x86 mips
     __pthread_cleanup_pop;
     __pthread_cleanup_push;
-    __pthread_gettid; # arm x86 mips
-    __ptrace; # arm x86 mips
     __putlong;
     __putshort;
     __read_chk;
     __readlink_chk;
     __readlinkat_chk;
-    __reboot; # arm x86 mips
     __recvfrom_chk;
     __register_atfork;
     __res_close;
@@ -192,41 +107,14 @@
     __res_send;
     __res_send_setqhook;
     __res_send_setrhook;
-    __restore_core_regs; # arm
-    __rt_sigaction; # arm x86 mips
-    __rt_sigpending; # arm x86 mips
-    __rt_sigprocmask; # arm x86 mips
-    __rt_sigsuspend; # arm x86 mips
-    __rt_sigtimedwait; # arm x86 mips
     __sched_cpualloc;
     __sched_cpucount;
     __sched_cpufree;
-    __sched_getaffinity; # arm x86 mips
-    __sclose; # arm x86 mips
-    __sdidinit; # arm x86 mips
-    __set_errno; # arm x86 mips
-    __set_thread_area; # x86
-    __set_tid_address; # arm x86 mips
-    __set_tls; # arm mips
     __sF;
-    __sflags; # arm x86 mips
-    __sflush; # arm x86 mips
-    __sfp; # arm x86 mips
-    __sglue; # arm x86 mips
-    __sigaction; # arm x86 mips
-    __signalfd4; # arm x86 mips
-    __sinit; # arm x86 mips
-    __smakebuf; # arm x86 mips
     __snprintf_chk;
-    __socket; # arm x86 mips
     __sprintf_chk;
-    __sread; # arm x86 mips
-    __srefill; # arm x86 mips
-    __srget; # arm x86 mips
-    __sseek; # arm x86 mips
     __stack_chk_fail;
     __stack_chk_guard;
-    __statfs64; # arm x86 mips
     __stpcpy_chk;
     __stpncpy_chk;
     __stpncpy_chk2;
@@ -240,11 +128,6 @@
     __strncpy_chk;
     __strncpy_chk2;
     __strrchr_chk;
-    __subdf3; # arm
-    __subsf3; # arm
-    __swbuf; # arm x86 mips
-    __swrite; # arm x86 mips
-    __swsetup; # arm x86 mips
     __sym_ntop;
     __sym_ntos;
     __sym_ston;
@@ -263,28 +146,13 @@
     __system_property_set_filename;
     __system_property_update;
     __system_property_wait_any;
-    __timer_create; # arm x86 mips
-    __timer_delete; # arm x86 mips
-    __timer_getoverrun; # arm x86 mips
-    __timer_gettime; # arm x86 mips
-    __timer_settime; # arm x86 mips
-    __truncdfsf2; # arm
-    __udivdi3; # arm x86 mips
-    __udivsi3; # arm
     __umask_chk;
-    __umoddi3; # x86 mips
-    __unorddf2; # arm
-    __unordsf2; # arm
     __vsnprintf_chk;
     __vsprintf_chk;
-    __wait4; # arm x86 mips
-    __waitid; # arm x86 mips
     _ctype_;
     _Exit;
     _exit;
-    _flush_cache; # mips
     _flushlbf;
-    _fwalk; # arm x86 mips
     _getlong;
     _getshort;
     _longjmp;
@@ -293,9 +161,7 @@
     _resolv_set_nameservers_for_net;
     _setjmp;
     _tolower;
-    _tolower_tab_; # arm x86 mips
     _toupper;
-    _toupper_tab_; # arm x86 mips
     abort;
     abs;
     accept;
@@ -311,13 +177,9 @@
     android_gethostbynamefornet;
     android_set_abort_message;
     arc4random;
-    arc4random_addrandom; # arm x86 mips
     arc4random_buf;
-    arc4random_stir; # arm x86 mips
     arc4random_uniform;
     asctime;
-    asctime64; # arm x86 mips
-    asctime64_r; # arm x86 mips
     asctime_r;
     asprintf;
     at_quick_exit;
@@ -326,18 +188,13 @@
     atol;
     atoll;
     basename;
-    basename_r; # arm x86 mips
-    bcopy; # arm x86 mips
     bind;
     bindresvport;
     brk;
-    bsd_signal; # arm x86 mips
     bsearch;
     btowc;
-    bzero; # arm x86 mips
     c16rtomb;
     c32rtomb;
-    cacheflush; # arm mips
     calloc;
     capget;
     capset;
@@ -368,8 +225,6 @@
     creat;
     creat64;
     ctime;
-    ctime64; # arm x86 mips
-    ctime64_r; # arm x86 mips
     ctime_r;
     daemon;
     daylight;
@@ -377,7 +232,6 @@
     difftime;
     dirfd;
     dirname;
-    dirname_r; # arm x86 mips
     div;
     dn_expand;
     dprintf;
@@ -389,7 +243,6 @@
     endmntent;
     endpwent;
     endservent;
-    endusershell;
     endutent;
     environ;
     epoll_create;
@@ -421,8 +274,6 @@
     execvpe;
     exit;
     faccessat;
-    fake_gmtime_r; # arm x86 mips
-    fake_localtime_r; # arm x86 mips
     fallocate;
     fallocate64;
     fchdir;
@@ -435,7 +286,6 @@
     fdatasync;
     fdopen;
     fdopendir;
-    fdprintf; # arm x86 mips
     feof;
     feof_unlocked;
     ferror;
@@ -488,7 +338,6 @@
     fsync;
     ftell;
     ftello;
-    ftime; # arm x86 mips
     ftok;
     ftruncate;
     ftruncate64;
@@ -521,8 +370,6 @@
     getchar_unlocked;
     getcwd;
     getdelim;
-    getdents; # arm x86 mips
-    getdtablesize; # arm x86 mips
     getegid;
     getenv;
     geteuid;
@@ -580,14 +427,11 @@
     gettid;
     gettimeofday;
     getuid;
-    getusershell;
     getutent;
     getwc;
     getwchar;
     getxattr;
     gmtime;
-    gmtime64; # arm x86 mips
-    gmtime64_r; # arm x86 mips
     gmtime_r;
     grantpt;
     herror;
@@ -598,7 +442,6 @@
     if_nametoindex;
     imaxabs;
     imaxdiv;
-    index; # arm x86 mips
     inet_addr;
     inet_aton;
     inet_lnaof;
@@ -651,7 +494,6 @@
     isprint_l;
     ispunct;
     ispunct_l;
-    issetugid; # arm x86 mips
     isspace;
     isspace_l;
     isupper;
@@ -704,8 +546,6 @@
     llistxattr;
     localeconv;
     localtime;
-    localtime64; # arm x86 mips
-    localtime64_r; # arm x86 mips
     localtime_r;
     login_tty;
     longjmp;
@@ -741,7 +581,6 @@
     mempcpy;
     memrchr;
     memset;
-    memswap; # arm x86 mips
     mincore;
     mkdir;
     mkdirat;
@@ -760,7 +599,6 @@
     mkstemps64;
     mktemp;
     mktime;
-    mktime64; # arm x86 mips
     mktime_tz;
     mlock;
     mlockall;
@@ -855,7 +693,6 @@
     pthread_attr_getschedpolicy;
     pthread_attr_getscope;
     pthread_attr_getstack;
-    pthread_attr_getstackaddr; # arm x86 mips
     pthread_attr_getstacksize;
     pthread_attr_init;
     pthread_attr_setdetachstate;
@@ -864,17 +701,12 @@
     pthread_attr_setschedpolicy;
     pthread_attr_setscope;
     pthread_attr_setstack;
-    pthread_attr_setstackaddr; # arm x86 mips
     pthread_attr_setstacksize;
     pthread_cond_broadcast;
     pthread_cond_destroy;
     pthread_cond_init;
     pthread_cond_signal;
     pthread_cond_timedwait;
-    pthread_cond_timedwait_monotonic; # arm x86 mips
-    pthread_cond_timedwait_monotonic_np; # arm x86 mips
-    pthread_cond_timedwait_relative_np; # arm x86 mips
-    pthread_cond_timeout_np; # arm x86 mips
     pthread_cond_wait;
     pthread_condattr_destroy;
     pthread_condattr_getclock;
@@ -898,7 +730,6 @@
     pthread_mutex_destroy;
     pthread_mutex_init;
     pthread_mutex_lock;
-    pthread_mutex_lock_timeout_np; # arm x86 mips
     pthread_mutex_timedlock;
     pthread_mutex_trylock;
     pthread_mutex_unlock;
@@ -939,10 +770,8 @@
     putenv;
     puts;
     pututline;
-    putw; # arm x86 mips
     putwc;
     putwchar;
-    pvalloc; # arm x86 mips
     pwrite;
     pwrite64;
     qsort;
@@ -980,7 +809,6 @@
     res_mkquery;
     res_query;
     res_search;
-    restore_core_regs; # arm
     rewind;
     rewinddir;
     rmdir;
@@ -1051,7 +879,6 @@
     setstate;
     settimeofday;
     setuid;
-    setusershell;
     setutent;
     setvbuf;
     setxattr;
@@ -1124,8 +951,6 @@
     strncpy;
     strndup;
     strnlen;
-    strntoimax; # arm x86 mips
-    strntoumax; # arm x86 mips
     strpbrk;
     strptime;
     strrchr;
@@ -1144,7 +969,6 @@
     strtoll;
     strtoll_l;
     strtoq;
-    strtotimeval; # arm x86 mips
     strtoul;
     strtoull;
     strtoull_l;
@@ -1166,7 +990,6 @@
     sysinfo;
     syslog;
     system;
-    sysv_signal; # arm x86 mips
     tcdrain;
     tcflow;
     tcflush;
@@ -1185,9 +1008,7 @@
     tgkill;
     time;
     timegm;
-    timegm64; # arm x86 mips
     timelocal;
-    timelocal64; # arm x86 mips
     timer_create;
     timer_delete;
     timer_getoverrun;
@@ -1198,7 +1019,6 @@
     timerfd_settime;
     times;
     timezone;
-    tkill; # arm x86 mips
     tmpfile;
     tmpnam;
     toascii;
@@ -1235,12 +1055,10 @@
     utimensat;
     utimes;
     utmpname;
-    valloc; # arm x86 mips
     vasprintf;
     vdprintf;
     verr;
     verrx;
-    vfdprintf; # arm x86 mips
     vfork;
     vfprintf;
     vfscanf;
@@ -1260,7 +1078,6 @@
     vwprintf;
     vwscanf;
     wait;
-    wait3; # arm x86 mips
     wait4;
     waitid;
     waitpid;
@@ -1309,7 +1126,6 @@
     wcstoull;
     wcstoull_l;
     wcstoumax;
-    wcswcs; # arm x86 mips
     wcswidth;
     wcsxfrm;
     wcsxfrm_l;
@@ -1340,134 +1156,33 @@
     __pwrite_chk;
     __pwrite64_chk;
     __write_chk;
+    fileno_unlocked;
     getgrgid_r;
     getgrnam_r;
     preadv;
     preadv64;
+    pthread_barrierattr_destroy;
+    pthread_barrierattr_getpshared;
+    pthread_barrierattr_init;
+    pthread_barrierattr_setpshared;
+    pthread_barrier_destroy;
+    pthread_barrier_init;
+    pthread_barrier_wait;
+    pthread_spin_destroy;
+    pthread_spin_init;
+    pthread_spin_lock;
+    pthread_spin_trylock;
+    pthread_spin_unlock;
     pwritev;
     pwritev64;
+    scandirat;
+    scandirat64;
     strchrnul;
 } LIBC;
 
 LIBC_PRIVATE {
   global:
-    ___Unwind_Backtrace; # arm
-    ___Unwind_ForcedUnwind; # arm
-    ___Unwind_RaiseException; # arm
-    ___Unwind_Resume; # arm
-    ___Unwind_Resume_or_Rethrow; # arm
-    __accept4; # arm x86 mips
-    __adddf3; # arm
-    __addsf3; # arm
-    __aeabi_atexit; # arm
-    __aeabi_cdcmpeq; # arm
-    __aeabi_cdcmple; # arm
-    __aeabi_cdrcmple; # arm
-    __aeabi_d2f; # arm
-    __aeabi_d2iz; # arm
-    __aeabi_dadd; # arm
-    __aeabi_dcmpeq; # arm
-    __aeabi_dcmpge; # arm
-    __aeabi_dcmpgt; # arm
-    __aeabi_dcmple; # arm
-    __aeabi_dcmplt; # arm
-    __aeabi_dcmpun; # arm
-    __aeabi_ddiv; # arm
-    __aeabi_dmul; # arm
-    __aeabi_drsub; # arm
-    __aeabi_dsub; # arm
-    __aeabi_f2d; # arm
-    __aeabi_f2iz; # arm
-    __aeabi_f2uiz; # arm
-    __aeabi_fadd; # arm
-    __aeabi_fcmpun; # arm
-    __aeabi_fdiv; # arm
-    __aeabi_fmul; # arm
-    __aeabi_frsub; # arm
-    __aeabi_fsub; # arm
-    __aeabi_i2d; # arm
-    __aeabi_i2f; # arm
-    __aeabi_idiv; # arm
-    __aeabi_idiv0; # arm
-    __aeabi_idivmod; # arm
-    __aeabi_l2d; # arm
-    __aeabi_l2f; # arm
-    __aeabi_lasr; # arm
-    __aeabi_ldiv0; # arm
-    __aeabi_ldivmod; # arm
-    __aeabi_llsl; # arm
-    __aeabi_llsr; # arm
-    __aeabi_lmul; # arm
-    __aeabi_memclr; # arm
-    __aeabi_memclr4; # arm
-    __aeabi_memclr8; # arm
-    __aeabi_memcpy; # arm
-    __aeabi_memcpy4; # arm
-    __aeabi_memcpy8; # arm
-    __aeabi_memmove; # arm
-    __aeabi_memmove4; # arm
-    __aeabi_memmove8; # arm
-    __aeabi_memset; # arm
-    __aeabi_memset4; # arm
-    __aeabi_memset8; # arm
-    __aeabi_ui2d; # arm
-    __aeabi_ui2f; # arm
-    __aeabi_uidiv; # arm
-    __aeabi_uidivmod; # arm
-    __aeabi_ul2d; # arm
-    __aeabi_ul2f; # arm
-    __aeabi_uldivmod; # arm
-    __aeabi_unwind_cpp_pr0; # arm
-    __aeabi_unwind_cpp_pr1; # arm
-    __aeabi_unwind_cpp_pr2; # arm
-    __arm_fadvise64_64; # arm
-    __ashldi3; # arm
-    __ashrdi3; # arm
-    __bionic_brk; # arm x86 mips
-    __bionic_libgcc_compat_symbols; # arm x86
-    __bionic_libgcc_unwind_symbols; # arm
-    __dso_handle; # arm
-    __gnu_Unwind_Backtrace; # arm
-    __gnu_unwind_execute; # arm
-    __gnu_Unwind_Find_exidx; # arm
-    __gnu_Unwind_ForcedUnwind; # arm
-    __gnu_unwind_frame; # arm
-    __gnu_Unwind_RaiseException; # arm
-    __gnu_Unwind_Restore_VFP; # arm
-    __gnu_Unwind_Restore_VFP_D; # arm
-    __gnu_Unwind_Restore_VFP_D_16_to_31; # arm
-    __gnu_Unwind_Restore_WMMXC; # arm
-    __gnu_Unwind_Restore_WMMXD; # arm
-    __gnu_Unwind_Resume; # arm
-    __gnu_Unwind_Resume_or_Rethrow; # arm
-    __gnu_Unwind_Save_VFP; # arm
-    __gnu_Unwind_Save_VFP_D; # arm
-    __gnu_Unwind_Save_VFP_D_16_to_31; # arm
-    __gnu_Unwind_Save_WMMXC; # arm
-    __gnu_Unwind_Save_WMMXD; # arm
-    _Unwind_Backtrace; # arm
-    _Unwind_Complete; # arm
-    _Unwind_DeleteException; # arm
-    _Unwind_ForcedUnwind; # arm
-    _Unwind_GetCFA; # arm
-    _Unwind_GetDataRelBase; # arm
-    _Unwind_GetLanguageSpecificData; # arm
-    _Unwind_GetRegionStart; # arm
-    _Unwind_GetTextRelBase; # arm
-    _Unwind_RaiseException; # arm
-    _Unwind_Resume; # arm
-    _Unwind_Resume_or_Rethrow; # arm
-    _Unwind_VRS_Get; # arm
-    _Unwind_VRS_Pop; # arm
-    _Unwind_VRS_Set; # arm
-    atexit; # arm
-    dlmalloc; # arm x86 mips
     dlmalloc_inspect_all;
     dlmalloc_trim;
-    dlmalloc_usable_size; # arm x86 mips
     gMallocLeakZygoteChild;
-    SHA1Final; # arm x86 mips
-    SHA1Init; # arm x86 mips
-    SHA1Transform; # arm x86 mips
-    SHA1Update; # arm x86 mips
 } LIBC_N;
diff --git a/libc/libc.map b/libc/libc.x86.map
similarity index 81%
copy from libc/libc.map
copy to libc/libc.x86.map
index fbceb83..6578370 100644
--- a/libc/libc.map
+++ b/libc/libc.x86.map
@@ -1,33 +1,24 @@
+# Generated by genversionscripts.py. Do not edit.
 LIBC {
   global:
     __assert;
     __assert2;
-    __atomic_cmpxchg; # arm
-    __atomic_dec; # arm
-    __atomic_inc; # arm
-    __atomic_swap; # arm
     __b64_ntop;
     __b64_pton;
     __brk; # arm x86 mips
-    __cmpdf2; # arm
     __cmsg_nxthdr;
     __connect; # arm x86 mips
     __ctype_get_mb_cur_max;
     __cxa_atexit;
     __cxa_finalize;
     __cxa_thread_atexit_impl;
-    __divdf3; # arm
     __divdi3; # arm x86 mips
-    __divsf3; # arm
-    __divsi3; # arm
     __dn_comp;
     __dn_count_labels;
     __dn_skipname;
     __epoll_pwait; # arm x86 mips
-    __eqdf2; # arm
     __errno;
     __exit; # arm x86 mips
-    __extendsfdf2; # arm
     __fadvise64; # x86 mips
     __fbufsize;
     __fcntl64; # arm x86 mips
@@ -35,18 +26,7 @@
     __FD_ISSET_chk;
     __FD_SET_chk;
     __fgets_chk;
-    __fixdfsi; # arm
-    __fixsfsi; # arm
-    __fixunssfsi; # arm
     __flbf;
-    __floatdidf; # arm
-    __floatdisf; # arm
-    __floatsidf; # arm
-    __floatsisf; # arm
-    __floatundidf; # arm
-    __floatundisf; # arm
-    __floatunsidf; # arm
-    __floatunsisf; # arm
     __fp_nquery;
     __fp_query;
     __fpclassify;
@@ -61,7 +41,6 @@
     __futex_wait; # arm x86 mips
     __futex_wake; # arm x86 mips
     __fwritable;
-    __gedf2; # arm
     __get_h_errno;
     __get_thread; # arm x86 mips
     __get_tls; # arm x86 mips
@@ -71,10 +50,7 @@
     __getpid; # arm x86 mips
     __getpriority; # arm x86 mips
     __gnu_basename;
-    __gnu_ldivmod_helper; # arm
     __gnu_strerror_r;
-    __gnu_uldivmod_helper; # arm
-    __gtdf2; # arm
     __hostalias;
     __ioctl; # arm x86 mips
     __isfinite;
@@ -90,15 +66,12 @@
     __isnormalf;
     __isnormall;
     __isthreaded;
-    __ledf2; # arm
     __libc_current_sigrtmax;
     __libc_current_sigrtmin;
     __libc_init;
     __llseek; # arm x86 mips
     __loc_aton;
     __loc_ntoa;
-    __lshrdi3; # arm
-    __ltdf2; # arm
     __memchr_chk;
     __memcpy_chk;
     __memmove_chk;
@@ -106,10 +79,6 @@
     __memset_chk;
     __mmap2; # arm x86 mips
     __moddi3; # x86 mips
-    __muldf3; # arm
-    __muldi3; # arm
-    __mulsf3; # arm
-    __nedf2; # arm
     __ns_format_ttl; # arm x86 mips
     __ns_get16; # arm x86 mips
     __ns_get32; # arm x86 mips
@@ -152,7 +121,6 @@
     __page_shift; # arm x86 mips
     __page_size; # arm x86 mips
     __poll_chk;
-    __popcount_tab; # arm
     __popcountsi2; # arm x86 mips
     __ppoll; # arm x86 mips
     __ppoll_chk;
@@ -192,7 +160,6 @@
     __res_send;
     __res_send_setqhook;
     __res_send_setrhook;
-    __restore_core_regs; # arm
     __rt_sigaction; # arm x86 mips
     __rt_sigpending; # arm x86 mips
     __rt_sigprocmask; # arm x86 mips
@@ -207,7 +174,6 @@
     __set_errno; # arm x86 mips
     __set_thread_area; # x86
     __set_tid_address; # arm x86 mips
-    __set_tls; # arm mips
     __sF;
     __sflags; # arm x86 mips
     __sflush; # arm x86 mips
@@ -240,8 +206,6 @@
     __strncpy_chk;
     __strncpy_chk2;
     __strrchr_chk;
-    __subdf3; # arm
-    __subsf3; # arm
     __swbuf; # arm x86 mips
     __swrite; # arm x86 mips
     __swsetup; # arm x86 mips
@@ -268,13 +232,9 @@
     __timer_getoverrun; # arm x86 mips
     __timer_gettime; # arm x86 mips
     __timer_settime; # arm x86 mips
-    __truncdfsf2; # arm
     __udivdi3; # arm x86 mips
-    __udivsi3; # arm
     __umask_chk;
     __umoddi3; # x86 mips
-    __unorddf2; # arm
-    __unordsf2; # arm
     __vsnprintf_chk;
     __vsprintf_chk;
     __wait4; # arm x86 mips
@@ -282,7 +242,6 @@
     _ctype_;
     _Exit;
     _exit;
-    _flush_cache; # mips
     _flushlbf;
     _fwalk; # arm x86 mips
     _getlong;
@@ -337,7 +296,6 @@
     bzero; # arm x86 mips
     c16rtomb;
     c32rtomb;
-    cacheflush; # arm mips
     calloc;
     capget;
     capset;
@@ -389,7 +347,6 @@
     endmntent;
     endpwent;
     endservent;
-    endusershell;
     endutent;
     environ;
     epoll_create;
@@ -580,7 +537,6 @@
     gettid;
     gettimeofday;
     getuid;
-    getusershell;
     getutent;
     getwc;
     getwchar;
@@ -780,28 +736,6 @@
     nftw64;
     nice;
     nrand48;
-    ns_format_ttl; # arm64 x86_64 mips64
-    ns_get16; # arm64 x86_64 mips64
-    ns_get32; # arm64 x86_64 mips64
-    ns_initparse; # arm64 x86_64 mips64
-    ns_makecanon; # arm64 x86_64 mips64
-    ns_msg_getflag; # arm64 x86_64 mips64
-    ns_name_compress; # arm64 x86_64 mips64
-    ns_name_ntol; # arm64 x86_64 mips64
-    ns_name_ntop; # arm64 x86_64 mips64
-    ns_name_pack; # arm64 x86_64 mips64
-    ns_name_pton; # arm64 x86_64 mips64
-    ns_name_rollback; # arm64 x86_64 mips64
-    ns_name_skip; # arm64 x86_64 mips64
-    ns_name_uncompress; # arm64 x86_64 mips64
-    ns_name_unpack; # arm64 x86_64 mips64
-    ns_parserr; # arm64 x86_64 mips64
-    ns_put16; # arm64 x86_64 mips64
-    ns_put32; # arm64 x86_64 mips64
-    ns_samename; # arm64 x86_64 mips64
-    ns_skiprr; # arm64 x86_64 mips64
-    ns_sprintrr; # arm64 x86_64 mips64
-    ns_sprintrrf; # arm64 x86_64 mips64
     nsdispatch;
     ntohl;
     ntohs;
@@ -840,7 +774,6 @@
     pread;
     pread64;
     printf;
-    prlimit; # arm64 x86_64 mips64
     prlimit64;
     process_vm_readv;
     process_vm_writev;
@@ -980,7 +913,6 @@
     res_mkquery;
     res_query;
     res_search;
-    restore_core_regs; # arm
     rewind;
     rewinddir;
     rmdir;
@@ -1051,7 +983,6 @@
     setstate;
     settimeofday;
     setuid;
-    setusershell;
     setutent;
     setvbuf;
     setxattr;
@@ -1340,127 +1271,36 @@
     __pwrite_chk;
     __pwrite64_chk;
     __write_chk;
+    fileno_unlocked;
     getgrgid_r;
     getgrnam_r;
     preadv;
     preadv64;
+    prlimit; # arm mips x86
+    pthread_barrierattr_destroy;
+    pthread_barrierattr_getpshared;
+    pthread_barrierattr_init;
+    pthread_barrierattr_setpshared;
+    pthread_barrier_destroy;
+    pthread_barrier_init;
+    pthread_barrier_wait;
+    pthread_spin_destroy;
+    pthread_spin_init;
+    pthread_spin_lock;
+    pthread_spin_trylock;
+    pthread_spin_unlock;
     pwritev;
     pwritev64;
+    scandirat;
+    scandirat64;
     strchrnul;
 } LIBC;
 
 LIBC_PRIVATE {
   global:
-    ___Unwind_Backtrace; # arm
-    ___Unwind_ForcedUnwind; # arm
-    ___Unwind_RaiseException; # arm
-    ___Unwind_Resume; # arm
-    ___Unwind_Resume_or_Rethrow; # arm
     __accept4; # arm x86 mips
-    __adddf3; # arm
-    __addsf3; # arm
-    __aeabi_atexit; # arm
-    __aeabi_cdcmpeq; # arm
-    __aeabi_cdcmple; # arm
-    __aeabi_cdrcmple; # arm
-    __aeabi_d2f; # arm
-    __aeabi_d2iz; # arm
-    __aeabi_dadd; # arm
-    __aeabi_dcmpeq; # arm
-    __aeabi_dcmpge; # arm
-    __aeabi_dcmpgt; # arm
-    __aeabi_dcmple; # arm
-    __aeabi_dcmplt; # arm
-    __aeabi_dcmpun; # arm
-    __aeabi_ddiv; # arm
-    __aeabi_dmul; # arm
-    __aeabi_drsub; # arm
-    __aeabi_dsub; # arm
-    __aeabi_f2d; # arm
-    __aeabi_f2iz; # arm
-    __aeabi_f2uiz; # arm
-    __aeabi_fadd; # arm
-    __aeabi_fcmpun; # arm
-    __aeabi_fdiv; # arm
-    __aeabi_fmul; # arm
-    __aeabi_frsub; # arm
-    __aeabi_fsub; # arm
-    __aeabi_i2d; # arm
-    __aeabi_i2f; # arm
-    __aeabi_idiv; # arm
-    __aeabi_idiv0; # arm
-    __aeabi_idivmod; # arm
-    __aeabi_l2d; # arm
-    __aeabi_l2f; # arm
-    __aeabi_lasr; # arm
-    __aeabi_ldiv0; # arm
-    __aeabi_ldivmod; # arm
-    __aeabi_llsl; # arm
-    __aeabi_llsr; # arm
-    __aeabi_lmul; # arm
-    __aeabi_memclr; # arm
-    __aeabi_memclr4; # arm
-    __aeabi_memclr8; # arm
-    __aeabi_memcpy; # arm
-    __aeabi_memcpy4; # arm
-    __aeabi_memcpy8; # arm
-    __aeabi_memmove; # arm
-    __aeabi_memmove4; # arm
-    __aeabi_memmove8; # arm
-    __aeabi_memset; # arm
-    __aeabi_memset4; # arm
-    __aeabi_memset8; # arm
-    __aeabi_ui2d; # arm
-    __aeabi_ui2f; # arm
-    __aeabi_uidiv; # arm
-    __aeabi_uidivmod; # arm
-    __aeabi_ul2d; # arm
-    __aeabi_ul2f; # arm
-    __aeabi_uldivmod; # arm
-    __aeabi_unwind_cpp_pr0; # arm
-    __aeabi_unwind_cpp_pr1; # arm
-    __aeabi_unwind_cpp_pr2; # arm
-    __arm_fadvise64_64; # arm
-    __ashldi3; # arm
-    __ashrdi3; # arm
     __bionic_brk; # arm x86 mips
     __bionic_libgcc_compat_symbols; # arm x86
-    __bionic_libgcc_unwind_symbols; # arm
-    __dso_handle; # arm
-    __gnu_Unwind_Backtrace; # arm
-    __gnu_unwind_execute; # arm
-    __gnu_Unwind_Find_exidx; # arm
-    __gnu_Unwind_ForcedUnwind; # arm
-    __gnu_unwind_frame; # arm
-    __gnu_Unwind_RaiseException; # arm
-    __gnu_Unwind_Restore_VFP; # arm
-    __gnu_Unwind_Restore_VFP_D; # arm
-    __gnu_Unwind_Restore_VFP_D_16_to_31; # arm
-    __gnu_Unwind_Restore_WMMXC; # arm
-    __gnu_Unwind_Restore_WMMXD; # arm
-    __gnu_Unwind_Resume; # arm
-    __gnu_Unwind_Resume_or_Rethrow; # arm
-    __gnu_Unwind_Save_VFP; # arm
-    __gnu_Unwind_Save_VFP_D; # arm
-    __gnu_Unwind_Save_VFP_D_16_to_31; # arm
-    __gnu_Unwind_Save_WMMXC; # arm
-    __gnu_Unwind_Save_WMMXD; # arm
-    _Unwind_Backtrace; # arm
-    _Unwind_Complete; # arm
-    _Unwind_DeleteException; # arm
-    _Unwind_ForcedUnwind; # arm
-    _Unwind_GetCFA; # arm
-    _Unwind_GetDataRelBase; # arm
-    _Unwind_GetLanguageSpecificData; # arm
-    _Unwind_GetRegionStart; # arm
-    _Unwind_GetTextRelBase; # arm
-    _Unwind_RaiseException; # arm
-    _Unwind_Resume; # arm
-    _Unwind_Resume_or_Rethrow; # arm
-    _Unwind_VRS_Get; # arm
-    _Unwind_VRS_Pop; # arm
-    _Unwind_VRS_Set; # arm
-    atexit; # arm
     dlmalloc; # arm x86 mips
     dlmalloc_inspect_all;
     dlmalloc_trim;
diff --git a/libc/libc.map b/libc/libc.x86_64.map
similarity index 68%
copy from libc/libc.map
copy to libc/libc.x86_64.map
index fbceb83..763f77e 100644
--- a/libc/libc.map
+++ b/libc/libc.x86_64.map
@@ -1,52 +1,25 @@
+# Generated by genversionscripts.py. Do not edit.
 LIBC {
   global:
     __assert;
     __assert2;
-    __atomic_cmpxchg; # arm
-    __atomic_dec; # arm
-    __atomic_inc; # arm
-    __atomic_swap; # arm
     __b64_ntop;
     __b64_pton;
-    __brk; # arm x86 mips
-    __cmpdf2; # arm
     __cmsg_nxthdr;
-    __connect; # arm x86 mips
     __ctype_get_mb_cur_max;
     __cxa_atexit;
     __cxa_finalize;
     __cxa_thread_atexit_impl;
-    __divdf3; # arm
-    __divdi3; # arm x86 mips
-    __divsf3; # arm
-    __divsi3; # arm
     __dn_comp;
     __dn_count_labels;
     __dn_skipname;
-    __epoll_pwait; # arm x86 mips
-    __eqdf2; # arm
     __errno;
-    __exit; # arm x86 mips
-    __extendsfdf2; # arm
-    __fadvise64; # x86 mips
     __fbufsize;
-    __fcntl64; # arm x86 mips
     __FD_CLR_chk;
     __FD_ISSET_chk;
     __FD_SET_chk;
     __fgets_chk;
-    __fixdfsi; # arm
-    __fixsfsi; # arm
-    __fixunssfsi; # arm
     __flbf;
-    __floatdidf; # arm
-    __floatdisf; # arm
-    __floatsidf; # arm
-    __floatsisf; # arm
-    __floatundidf; # arm
-    __floatundisf; # arm
-    __floatunsidf; # arm
-    __floatunsisf; # arm
     __fp_nquery;
     __fp_query;
     __fpclassify;
@@ -57,26 +30,11 @@
     __fpurge;
     __freadable;
     __fsetlocking;
-    __fstatfs64; # arm x86 mips
-    __futex_wait; # arm x86 mips
-    __futex_wake; # arm x86 mips
     __fwritable;
-    __gedf2; # arm
     __get_h_errno;
-    __get_thread; # arm x86 mips
-    __get_tls; # arm x86 mips
-    __getcpu; # arm x86 mips
-    __getcwd; # arm x86 mips
-    __getdents64; # arm x86 mips
-    __getpid; # arm x86 mips
-    __getpriority; # arm x86 mips
     __gnu_basename;
-    __gnu_ldivmod_helper; # arm
     __gnu_strerror_r;
-    __gnu_uldivmod_helper; # arm
-    __gtdf2; # arm
     __hostalias;
-    __ioctl; # arm x86 mips
     __isfinite;
     __isfinitef;
     __isfinitel;
@@ -90,51 +48,17 @@
     __isnormalf;
     __isnormall;
     __isthreaded;
-    __ledf2; # arm
     __libc_current_sigrtmax;
     __libc_current_sigrtmin;
     __libc_init;
-    __llseek; # arm x86 mips
     __loc_aton;
     __loc_ntoa;
-    __lshrdi3; # arm
-    __ltdf2; # arm
     __memchr_chk;
     __memcpy_chk;
     __memmove_chk;
     __memrchr_chk;
     __memset_chk;
-    __mmap2; # arm x86 mips
-    __moddi3; # x86 mips
-    __muldf3; # arm
-    __muldi3; # arm
-    __mulsf3; # arm
-    __nedf2; # arm
-    __ns_format_ttl; # arm x86 mips
-    __ns_get16; # arm x86 mips
-    __ns_get32; # arm x86 mips
-    __ns_initparse; # arm x86 mips
-    __ns_makecanon; # arm x86 mips
-    __ns_msg_getflag; # arm x86 mips
-    __ns_name_compress; # arm x86 mips
-    __ns_name_ntol; # arm x86 mips
-    __ns_name_ntop; # arm x86 mips
-    __ns_name_pack; # arm x86 mips
-    __ns_name_pton; # arm x86 mips
-    __ns_name_rollback; # arm x86 mips
-    __ns_name_skip; # arm x86 mips
-    __ns_name_uncompress; # arm x86 mips
-    __ns_name_unpack; # arm x86 mips
-    __ns_parserr; # arm x86 mips
-    __ns_put16; # arm x86 mips
-    __ns_put32; # arm x86 mips
-    __ns_samename; # arm x86 mips
-    __ns_skiprr; # arm x86 mips
-    __ns_sprintrr; # arm x86 mips
-    __ns_sprintrrf; # arm x86 mips
-    __open; # arm x86 mips
     __open_2;
-    __openat; # arm x86 mips
     __openat_2;
     __p_cdname;
     __p_cdnname;
@@ -149,27 +73,18 @@
     __p_time;
     __p_type;
     __p_type_syms;
-    __page_shift; # arm x86 mips
-    __page_size; # arm x86 mips
     __poll_chk;
-    __popcount_tab; # arm
-    __popcountsi2; # arm x86 mips
-    __ppoll; # arm x86 mips
     __ppoll_chk;
     __pread64_chk;
     __pread_chk;
     __progname;
-    __pselect6; # arm x86 mips
     __pthread_cleanup_pop;
     __pthread_cleanup_push;
-    __pthread_gettid; # arm x86 mips
-    __ptrace; # arm x86 mips
     __putlong;
     __putshort;
     __read_chk;
     __readlink_chk;
     __readlinkat_chk;
-    __reboot; # arm x86 mips
     __recvfrom_chk;
     __register_atfork;
     __res_close;
@@ -192,41 +107,14 @@
     __res_send;
     __res_send_setqhook;
     __res_send_setrhook;
-    __restore_core_regs; # arm
-    __rt_sigaction; # arm x86 mips
-    __rt_sigpending; # arm x86 mips
-    __rt_sigprocmask; # arm x86 mips
-    __rt_sigsuspend; # arm x86 mips
-    __rt_sigtimedwait; # arm x86 mips
     __sched_cpualloc;
     __sched_cpucount;
     __sched_cpufree;
-    __sched_getaffinity; # arm x86 mips
-    __sclose; # arm x86 mips
-    __sdidinit; # arm x86 mips
-    __set_errno; # arm x86 mips
-    __set_thread_area; # x86
-    __set_tid_address; # arm x86 mips
-    __set_tls; # arm mips
     __sF;
-    __sflags; # arm x86 mips
-    __sflush; # arm x86 mips
-    __sfp; # arm x86 mips
-    __sglue; # arm x86 mips
-    __sigaction; # arm x86 mips
-    __signalfd4; # arm x86 mips
-    __sinit; # arm x86 mips
-    __smakebuf; # arm x86 mips
     __snprintf_chk;
-    __socket; # arm x86 mips
     __sprintf_chk;
-    __sread; # arm x86 mips
-    __srefill; # arm x86 mips
-    __srget; # arm x86 mips
-    __sseek; # arm x86 mips
     __stack_chk_fail;
     __stack_chk_guard;
-    __statfs64; # arm x86 mips
     __stpcpy_chk;
     __stpncpy_chk;
     __stpncpy_chk2;
@@ -240,11 +128,6 @@
     __strncpy_chk;
     __strncpy_chk2;
     __strrchr_chk;
-    __subdf3; # arm
-    __subsf3; # arm
-    __swbuf; # arm x86 mips
-    __swrite; # arm x86 mips
-    __swsetup; # arm x86 mips
     __sym_ntop;
     __sym_ntos;
     __sym_ston;
@@ -263,28 +146,13 @@
     __system_property_set_filename;
     __system_property_update;
     __system_property_wait_any;
-    __timer_create; # arm x86 mips
-    __timer_delete; # arm x86 mips
-    __timer_getoverrun; # arm x86 mips
-    __timer_gettime; # arm x86 mips
-    __timer_settime; # arm x86 mips
-    __truncdfsf2; # arm
-    __udivdi3; # arm x86 mips
-    __udivsi3; # arm
     __umask_chk;
-    __umoddi3; # x86 mips
-    __unorddf2; # arm
-    __unordsf2; # arm
     __vsnprintf_chk;
     __vsprintf_chk;
-    __wait4; # arm x86 mips
-    __waitid; # arm x86 mips
     _ctype_;
     _Exit;
     _exit;
-    _flush_cache; # mips
     _flushlbf;
-    _fwalk; # arm x86 mips
     _getlong;
     _getshort;
     _longjmp;
@@ -293,9 +161,7 @@
     _resolv_set_nameservers_for_net;
     _setjmp;
     _tolower;
-    _tolower_tab_; # arm x86 mips
     _toupper;
-    _toupper_tab_; # arm x86 mips
     abort;
     abs;
     accept;
@@ -311,13 +177,9 @@
     android_gethostbynamefornet;
     android_set_abort_message;
     arc4random;
-    arc4random_addrandom; # arm x86 mips
     arc4random_buf;
-    arc4random_stir; # arm x86 mips
     arc4random_uniform;
     asctime;
-    asctime64; # arm x86 mips
-    asctime64_r; # arm x86 mips
     asctime_r;
     asprintf;
     at_quick_exit;
@@ -326,18 +188,13 @@
     atol;
     atoll;
     basename;
-    basename_r; # arm x86 mips
-    bcopy; # arm x86 mips
     bind;
     bindresvport;
     brk;
-    bsd_signal; # arm x86 mips
     bsearch;
     btowc;
-    bzero; # arm x86 mips
     c16rtomb;
     c32rtomb;
-    cacheflush; # arm mips
     calloc;
     capget;
     capset;
@@ -368,8 +225,6 @@
     creat;
     creat64;
     ctime;
-    ctime64; # arm x86 mips
-    ctime64_r; # arm x86 mips
     ctime_r;
     daemon;
     daylight;
@@ -377,7 +232,6 @@
     difftime;
     dirfd;
     dirname;
-    dirname_r; # arm x86 mips
     div;
     dn_expand;
     dprintf;
@@ -389,7 +243,6 @@
     endmntent;
     endpwent;
     endservent;
-    endusershell;
     endutent;
     environ;
     epoll_create;
@@ -421,8 +274,6 @@
     execvpe;
     exit;
     faccessat;
-    fake_gmtime_r; # arm x86 mips
-    fake_localtime_r; # arm x86 mips
     fallocate;
     fallocate64;
     fchdir;
@@ -435,7 +286,6 @@
     fdatasync;
     fdopen;
     fdopendir;
-    fdprintf; # arm x86 mips
     feof;
     feof_unlocked;
     ferror;
@@ -488,7 +338,6 @@
     fsync;
     ftell;
     ftello;
-    ftime; # arm x86 mips
     ftok;
     ftruncate;
     ftruncate64;
@@ -521,8 +370,6 @@
     getchar_unlocked;
     getcwd;
     getdelim;
-    getdents; # arm x86 mips
-    getdtablesize; # arm x86 mips
     getegid;
     getenv;
     geteuid;
@@ -580,14 +427,11 @@
     gettid;
     gettimeofday;
     getuid;
-    getusershell;
     getutent;
     getwc;
     getwchar;
     getxattr;
     gmtime;
-    gmtime64; # arm x86 mips
-    gmtime64_r; # arm x86 mips
     gmtime_r;
     grantpt;
     herror;
@@ -598,7 +442,6 @@
     if_nametoindex;
     imaxabs;
     imaxdiv;
-    index; # arm x86 mips
     inet_addr;
     inet_aton;
     inet_lnaof;
@@ -651,7 +494,6 @@
     isprint_l;
     ispunct;
     ispunct_l;
-    issetugid; # arm x86 mips
     isspace;
     isspace_l;
     isupper;
@@ -704,8 +546,6 @@
     llistxattr;
     localeconv;
     localtime;
-    localtime64; # arm x86 mips
-    localtime64_r; # arm x86 mips
     localtime_r;
     login_tty;
     longjmp;
@@ -741,7 +581,6 @@
     mempcpy;
     memrchr;
     memset;
-    memswap; # arm x86 mips
     mincore;
     mkdir;
     mkdirat;
@@ -760,7 +599,6 @@
     mkstemps64;
     mktemp;
     mktime;
-    mktime64; # arm x86 mips
     mktime_tz;
     mlock;
     mlockall;
@@ -855,7 +693,6 @@
     pthread_attr_getschedpolicy;
     pthread_attr_getscope;
     pthread_attr_getstack;
-    pthread_attr_getstackaddr; # arm x86 mips
     pthread_attr_getstacksize;
     pthread_attr_init;
     pthread_attr_setdetachstate;
@@ -864,17 +701,12 @@
     pthread_attr_setschedpolicy;
     pthread_attr_setscope;
     pthread_attr_setstack;
-    pthread_attr_setstackaddr; # arm x86 mips
     pthread_attr_setstacksize;
     pthread_cond_broadcast;
     pthread_cond_destroy;
     pthread_cond_init;
     pthread_cond_signal;
     pthread_cond_timedwait;
-    pthread_cond_timedwait_monotonic; # arm x86 mips
-    pthread_cond_timedwait_monotonic_np; # arm x86 mips
-    pthread_cond_timedwait_relative_np; # arm x86 mips
-    pthread_cond_timeout_np; # arm x86 mips
     pthread_cond_wait;
     pthread_condattr_destroy;
     pthread_condattr_getclock;
@@ -898,7 +730,6 @@
     pthread_mutex_destroy;
     pthread_mutex_init;
     pthread_mutex_lock;
-    pthread_mutex_lock_timeout_np; # arm x86 mips
     pthread_mutex_timedlock;
     pthread_mutex_trylock;
     pthread_mutex_unlock;
@@ -939,10 +770,8 @@
     putenv;
     puts;
     pututline;
-    putw; # arm x86 mips
     putwc;
     putwchar;
-    pvalloc; # arm x86 mips
     pwrite;
     pwrite64;
     qsort;
@@ -980,7 +809,6 @@
     res_mkquery;
     res_query;
     res_search;
-    restore_core_regs; # arm
     rewind;
     rewinddir;
     rmdir;
@@ -1051,7 +879,6 @@
     setstate;
     settimeofday;
     setuid;
-    setusershell;
     setutent;
     setvbuf;
     setxattr;
@@ -1124,8 +951,6 @@
     strncpy;
     strndup;
     strnlen;
-    strntoimax; # arm x86 mips
-    strntoumax; # arm x86 mips
     strpbrk;
     strptime;
     strrchr;
@@ -1144,7 +969,6 @@
     strtoll;
     strtoll_l;
     strtoq;
-    strtotimeval; # arm x86 mips
     strtoul;
     strtoull;
     strtoull_l;
@@ -1166,7 +990,6 @@
     sysinfo;
     syslog;
     system;
-    sysv_signal; # arm x86 mips
     tcdrain;
     tcflow;
     tcflush;
@@ -1185,9 +1008,7 @@
     tgkill;
     time;
     timegm;
-    timegm64; # arm x86 mips
     timelocal;
-    timelocal64; # arm x86 mips
     timer_create;
     timer_delete;
     timer_getoverrun;
@@ -1198,7 +1019,6 @@
     timerfd_settime;
     times;
     timezone;
-    tkill; # arm x86 mips
     tmpfile;
     tmpnam;
     toascii;
@@ -1235,12 +1055,10 @@
     utimensat;
     utimes;
     utmpname;
-    valloc; # arm x86 mips
     vasprintf;
     vdprintf;
     verr;
     verrx;
-    vfdprintf; # arm x86 mips
     vfork;
     vfprintf;
     vfscanf;
@@ -1260,7 +1078,6 @@
     vwprintf;
     vwscanf;
     wait;
-    wait3; # arm x86 mips
     wait4;
     waitid;
     waitpid;
@@ -1309,7 +1126,6 @@
     wcstoull;
     wcstoull_l;
     wcstoumax;
-    wcswcs; # arm x86 mips
     wcswidth;
     wcsxfrm;
     wcsxfrm_l;
@@ -1340,134 +1156,33 @@
     __pwrite_chk;
     __pwrite64_chk;
     __write_chk;
+    fileno_unlocked;
     getgrgid_r;
     getgrnam_r;
     preadv;
     preadv64;
+    pthread_barrierattr_destroy;
+    pthread_barrierattr_getpshared;
+    pthread_barrierattr_init;
+    pthread_barrierattr_setpshared;
+    pthread_barrier_destroy;
+    pthread_barrier_init;
+    pthread_barrier_wait;
+    pthread_spin_destroy;
+    pthread_spin_init;
+    pthread_spin_lock;
+    pthread_spin_trylock;
+    pthread_spin_unlock;
     pwritev;
     pwritev64;
+    scandirat;
+    scandirat64;
     strchrnul;
 } LIBC;
 
 LIBC_PRIVATE {
   global:
-    ___Unwind_Backtrace; # arm
-    ___Unwind_ForcedUnwind; # arm
-    ___Unwind_RaiseException; # arm
-    ___Unwind_Resume; # arm
-    ___Unwind_Resume_or_Rethrow; # arm
-    __accept4; # arm x86 mips
-    __adddf3; # arm
-    __addsf3; # arm
-    __aeabi_atexit; # arm
-    __aeabi_cdcmpeq; # arm
-    __aeabi_cdcmple; # arm
-    __aeabi_cdrcmple; # arm
-    __aeabi_d2f; # arm
-    __aeabi_d2iz; # arm
-    __aeabi_dadd; # arm
-    __aeabi_dcmpeq; # arm
-    __aeabi_dcmpge; # arm
-    __aeabi_dcmpgt; # arm
-    __aeabi_dcmple; # arm
-    __aeabi_dcmplt; # arm
-    __aeabi_dcmpun; # arm
-    __aeabi_ddiv; # arm
-    __aeabi_dmul; # arm
-    __aeabi_drsub; # arm
-    __aeabi_dsub; # arm
-    __aeabi_f2d; # arm
-    __aeabi_f2iz; # arm
-    __aeabi_f2uiz; # arm
-    __aeabi_fadd; # arm
-    __aeabi_fcmpun; # arm
-    __aeabi_fdiv; # arm
-    __aeabi_fmul; # arm
-    __aeabi_frsub; # arm
-    __aeabi_fsub; # arm
-    __aeabi_i2d; # arm
-    __aeabi_i2f; # arm
-    __aeabi_idiv; # arm
-    __aeabi_idiv0; # arm
-    __aeabi_idivmod; # arm
-    __aeabi_l2d; # arm
-    __aeabi_l2f; # arm
-    __aeabi_lasr; # arm
-    __aeabi_ldiv0; # arm
-    __aeabi_ldivmod; # arm
-    __aeabi_llsl; # arm
-    __aeabi_llsr; # arm
-    __aeabi_lmul; # arm
-    __aeabi_memclr; # arm
-    __aeabi_memclr4; # arm
-    __aeabi_memclr8; # arm
-    __aeabi_memcpy; # arm
-    __aeabi_memcpy4; # arm
-    __aeabi_memcpy8; # arm
-    __aeabi_memmove; # arm
-    __aeabi_memmove4; # arm
-    __aeabi_memmove8; # arm
-    __aeabi_memset; # arm
-    __aeabi_memset4; # arm
-    __aeabi_memset8; # arm
-    __aeabi_ui2d; # arm
-    __aeabi_ui2f; # arm
-    __aeabi_uidiv; # arm
-    __aeabi_uidivmod; # arm
-    __aeabi_ul2d; # arm
-    __aeabi_ul2f; # arm
-    __aeabi_uldivmod; # arm
-    __aeabi_unwind_cpp_pr0; # arm
-    __aeabi_unwind_cpp_pr1; # arm
-    __aeabi_unwind_cpp_pr2; # arm
-    __arm_fadvise64_64; # arm
-    __ashldi3; # arm
-    __ashrdi3; # arm
-    __bionic_brk; # arm x86 mips
-    __bionic_libgcc_compat_symbols; # arm x86
-    __bionic_libgcc_unwind_symbols; # arm
-    __dso_handle; # arm
-    __gnu_Unwind_Backtrace; # arm
-    __gnu_unwind_execute; # arm
-    __gnu_Unwind_Find_exidx; # arm
-    __gnu_Unwind_ForcedUnwind; # arm
-    __gnu_unwind_frame; # arm
-    __gnu_Unwind_RaiseException; # arm
-    __gnu_Unwind_Restore_VFP; # arm
-    __gnu_Unwind_Restore_VFP_D; # arm
-    __gnu_Unwind_Restore_VFP_D_16_to_31; # arm
-    __gnu_Unwind_Restore_WMMXC; # arm
-    __gnu_Unwind_Restore_WMMXD; # arm
-    __gnu_Unwind_Resume; # arm
-    __gnu_Unwind_Resume_or_Rethrow; # arm
-    __gnu_Unwind_Save_VFP; # arm
-    __gnu_Unwind_Save_VFP_D; # arm
-    __gnu_Unwind_Save_VFP_D_16_to_31; # arm
-    __gnu_Unwind_Save_WMMXC; # arm
-    __gnu_Unwind_Save_WMMXD; # arm
-    _Unwind_Backtrace; # arm
-    _Unwind_Complete; # arm
-    _Unwind_DeleteException; # arm
-    _Unwind_ForcedUnwind; # arm
-    _Unwind_GetCFA; # arm
-    _Unwind_GetDataRelBase; # arm
-    _Unwind_GetLanguageSpecificData; # arm
-    _Unwind_GetRegionStart; # arm
-    _Unwind_GetTextRelBase; # arm
-    _Unwind_RaiseException; # arm
-    _Unwind_Resume; # arm
-    _Unwind_Resume_or_Rethrow; # arm
-    _Unwind_VRS_Get; # arm
-    _Unwind_VRS_Pop; # arm
-    _Unwind_VRS_Set; # arm
-    atexit; # arm
-    dlmalloc; # arm x86 mips
     dlmalloc_inspect_all;
     dlmalloc_trim;
-    dlmalloc_usable_size; # arm x86 mips
     gMallocLeakZygoteChild;
-    SHA1Final; # arm x86 mips
-    SHA1Init; # arm x86 mips
-    SHA1Transform; # arm x86 mips
-    SHA1Update; # arm x86 mips
 } LIBC_N;
diff --git a/libc/private/ScopedPthreadMutexLocker.h b/libc/private/ScopedPthreadMutexLocker.h
index 43dbdc1..58462e3 100644
--- a/libc/private/ScopedPthreadMutexLocker.h
+++ b/libc/private/ScopedPthreadMutexLocker.h
@@ -34,7 +34,7 @@
  private:
   pthread_mutex_t* mu_;
 
-  DISALLOW_COPY_AND_ASSIGN(ScopedPthreadMutexLocker);
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedPthreadMutexLocker);
 };
 
 #endif // SCOPED_PTHREAD_MUTEX_LOCKER_H
diff --git a/libc/private/ScopedReaddir.h b/libc/private/ScopedReaddir.h
index 84c1b93..3d77a40 100644
--- a/libc/private/ScopedReaddir.h
+++ b/libc/private/ScopedReaddir.h
@@ -23,8 +23,11 @@
 
 class ScopedReaddir {
  public:
-  ScopedReaddir(const char* path) {
-    dir_ = opendir(path);
+  ScopedReaddir(const char* path) : ScopedReaddir(opendir(path)) {
+  }
+
+  ScopedReaddir(DIR* dir) {
+    dir_ = dir;
   }
 
   ~ScopedReaddir() {
diff --git a/libc/private/bionic_futex.h b/libc/private/bionic_futex.h
index 401577a..946d9dd 100644
--- a/libc/private/bionic_futex.h
+++ b/libc/private/bionic_futex.h
@@ -40,10 +40,12 @@
 
 struct timespec;
 
-static inline __always_inline int __futex(volatile void* ftx, int op, int value, const struct timespec* timeout) {
+static inline __always_inline int __futex(volatile void* ftx, int op, int value,
+                                          const struct timespec* timeout,
+                                          int bitset) {
   // Our generated syscall assembler sets errno, but our callers (pthread functions) don't want to.
   int saved_errno = errno;
-  int result = syscall(__NR_futex, ftx, op, value, timeout);
+  int result = syscall(__NR_futex, ftx, op, value, timeout, NULL, bitset);
   if (__predict_false(result == -1)) {
     result = -errno;
     errno = saved_errno;
@@ -52,19 +54,22 @@
 }
 
 static inline int __futex_wake(volatile void* ftx, int count) {
-  return __futex(ftx, FUTEX_WAKE, count, NULL);
+  return __futex(ftx, FUTEX_WAKE, count, NULL, 0);
 }
 
 static inline int __futex_wake_ex(volatile void* ftx, bool shared, int count) {
-  return __futex(ftx, shared ? FUTEX_WAKE : FUTEX_WAKE_PRIVATE, count, NULL);
+  return __futex(ftx, shared ? FUTEX_WAKE : FUTEX_WAKE_PRIVATE, count, NULL, 0);
 }
 
 static inline int __futex_wait(volatile void* ftx, int value, const struct timespec* timeout) {
-  return __futex(ftx, FUTEX_WAIT, value, timeout);
+  return __futex(ftx, FUTEX_WAIT, value, timeout, 0);
 }
 
-static inline int __futex_wait_ex(volatile void* ftx, bool shared, int value, const struct timespec* timeout) {
-  return __futex(ftx, shared ? FUTEX_WAIT : FUTEX_WAIT_PRIVATE, value, timeout);
+static inline int __futex_wait_ex(volatile void* ftx, bool shared, int value,
+                                  bool use_realtime_clock, const struct timespec* abs_timeout) {
+  return __futex(ftx, (shared ? FUTEX_WAIT_BITSET : FUTEX_WAIT_BITSET_PRIVATE) |
+                 (use_realtime_clock ? FUTEX_CLOCK_REALTIME : 0), value, abs_timeout,
+                 FUTEX_BITSET_MATCH_ANY);
 }
 
 __END_DECLS
diff --git a/libc/private/bionic_globals.h b/libc/private/bionic_globals.h
index 644b5a4..a671d77 100644
--- a/libc/private/bionic_globals.h
+++ b/libc/private/bionic_globals.h
@@ -29,12 +29,14 @@
 #define _PRIVATE_BIONIC_GLOBALS_H
 
 #include <sys/cdefs.h>
+#include "private/bionic_malloc_dispatch.h"
 #include "private/bionic_vdso.h"
 #include "private/WriteProtected.h"
 
 struct libc_globals {
   vdso_entry vdso[VDSO_END];
   long setjmp_cookie;
+  MallocDebug malloc_dispatch;
 };
 
 __LIBC_HIDDEN__ extern WriteProtected<libc_globals> __libc_globals;
@@ -44,5 +46,5 @@
                                       KernelArgumentBlock& args);
 __LIBC_HIDDEN__ void __libc_init_setjmp_cookie(libc_globals* globals,
                                                KernelArgumentBlock& args);
-
+__LIBC_HIDDEN__ void __libc_init_malloc(libc_globals* globals);
 #endif
diff --git a/libc/private/bionic_lock.h b/libc/private/bionic_lock.h
index 6a0fd06..3dbafe0 100644
--- a/libc/private/bionic_lock.h
+++ b/libc/private/bionic_lock.h
@@ -30,7 +30,10 @@
 
 #include <stdatomic.h>
 #include "private/bionic_futex.h"
+#include "private/bionic_macros.h"
 
+// Lock is used in places like pthread_rwlock_t, which can be initialized without calling
+// an initialization function. So make sure Lock can be initialized by setting its memory to 0.
 class Lock {
  private:
   enum LockState {
@@ -42,15 +45,17 @@
   bool process_shared;
 
  public:
-  Lock(bool process_shared = false) {
-    init(process_shared);
-  }
-
   void init(bool process_shared) {
     atomic_init(&state, Unlocked);
     this->process_shared = process_shared;
   }
 
+  bool trylock() {
+    LockState old_state = Unlocked;
+    return __predict_true(atomic_compare_exchange_strong_explicit(&state, &old_state,
+                        LockedWithoutWaiter, memory_order_acquire, memory_order_relaxed));
+  }
+
   void lock() {
     LockState old_state = Unlocked;
     if (__predict_true(atomic_compare_exchange_strong_explicit(&state, &old_state,
@@ -59,7 +64,7 @@
     }
     while (atomic_exchange_explicit(&state, LockedWithWaiter, memory_order_acquire) != Unlocked) {
       // TODO: As the critical section is brief, it is a better choice to spin a few times befor sleeping.
-      __futex_wait_ex(&state, process_shared, LockedWithWaiter, NULL);
+      __futex_wait_ex(&state, process_shared, LockedWithWaiter, false, nullptr);
     }
     return;
   }
diff --git a/libc/private/bionic_malloc_dispatch.h b/libc/private/bionic_malloc_dispatch.h
new file mode 100644
index 0000000..34fb898
--- /dev/null
+++ b/libc/private/bionic_malloc_dispatch.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _PRIVATE_BIONIC_MALLOC_DISPATCH_H
+#define _PRIVATE_BIONIC_MALLOC_DISPATCH_H
+
+#include <stddef.h>
+#include "private/bionic_config.h"
+
+/* Entry in malloc dispatch table. */
+typedef void* (*MallocDebugCalloc)(size_t, size_t);
+typedef void (*MallocDebugFree)(void*);
+typedef struct mallinfo (*MallocDebugMallinfo)();
+typedef void* (*MallocDebugMalloc)(size_t);
+typedef size_t (*MallocDebugMallocUsableSize)(const void*);
+typedef void* (*MallocDebugMemalign)(size_t, size_t);
+typedef int (*MallocDebugPosixMemalign)(void**, size_t, size_t);
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+typedef void* (*MallocDebugPvalloc)(size_t);
+#endif
+typedef void* (*MallocDebugRealloc)(void*, size_t);
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+typedef void* (*MallocDebugValloc)(size_t);
+#endif
+
+struct MallocDebug {
+  MallocDebugCalloc calloc;
+  MallocDebugFree free;
+  MallocDebugMallinfo mallinfo;
+  MallocDebugMalloc malloc;
+  MallocDebugMallocUsableSize malloc_usable_size;
+  MallocDebugMemalign memalign;
+  MallocDebugPosixMemalign posix_memalign;
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+  MallocDebugPvalloc pvalloc;
+#endif
+  MallocDebugRealloc realloc;
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+  MallocDebugValloc valloc;
+#endif
+} __attribute__((aligned(32)));
+
+#endif
diff --git a/libc/private/bionic_time_conversions.h b/libc/private/bionic_time_conversions.h
index cf0046a..294c29a 100644
--- a/libc/private/bionic_time_conversions.h
+++ b/libc/private/bionic_time_conversions.h
@@ -29,9 +29,12 @@
 #ifndef _BIONIC_TIME_CONVERSIONS_H
 #define _BIONIC_TIME_CONVERSIONS_H
 
+#include <errno.h>
 #include <time.h>
 #include <sys/cdefs.h>
 
+#include "private/bionic_constants.h"
+
 __BEGIN_DECLS
 
 __LIBC_HIDDEN__ bool timespec_from_timeval(timespec& ts, const timeval& tv);
@@ -39,8 +42,21 @@
 
 __LIBC_HIDDEN__ void timeval_from_timespec(timeval& tv, const timespec& ts);
 
-__LIBC_HIDDEN__ bool timespec_from_absolute_timespec(timespec& ts, const timespec& abs_ts, clockid_t clock);
+__LIBC_HIDDEN__ void absolute_timespec_from_timespec(timespec& abs_ts, const timespec& ts,
+                                                     clockid_t clock);
 
 __END_DECLS
 
+static inline int check_timespec(const timespec* ts) {
+  if (ts != nullptr) {
+    if (ts->tv_nsec < 0 || ts->tv_nsec >= NS_PER_S) {
+      return EINVAL;
+    }
+    if (ts->tv_sec < 0) {
+      return ETIMEDOUT;
+    }
+  }
+  return 0;
+}
+
 #endif
diff --git a/libc/stdio/findfp.c b/libc/stdio/findfp.c
index 2696cfd..6e20562 100644
--- a/libc/stdio/findfp.c
+++ b/libc/stdio/findfp.c
@@ -44,37 +44,44 @@
 #define ALIGNBYTES (sizeof(uintptr_t) - 1)
 #define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) &~ ALIGNBYTES)
 
-int	__sdidinit;
-
 #define	NDYNAMIC 10		/* add ten more whenever necessary */
 
 #define	std(flags, file) \
 	{0,0,0,flags,file,{0,0},0,__sF+file,__sclose,__sread,__sseek,__swrite, \
 	    {(unsigned char *)(__sFext+file), 0},NULL,0,{0},{0},{0,0},0,0}
 
-				/* the usual - (stdin + stdout + stderr) */
-static FILE usual[FOPEN_MAX - 3];
-static struct __sfileext usualext[FOPEN_MAX - 3];
-static struct glue uglue = { 0, FOPEN_MAX - 3, usual };
-static struct glue *lastglue = &uglue;
 _THREAD_PRIVATE_MUTEX(__sfp_mutex);
 
-static struct __sfileext __sFext[3];
+// TODO: when we no longer have to support both clang and GCC, we can simplify all this.
+#define SBUF_INIT {0,0}
+#if defined(__LP64__)
+#define MBSTATE_T_INIT {{0},{0}}
+#else
+#define MBSTATE_T_INIT {{0}}
+#endif
+#define WCHAR_IO_DATA_INIT {MBSTATE_T_INIT,MBSTATE_T_INIT,{0},0,0}
+
+static struct __sfileext __sFext[3] = {
+  { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false },
+  { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false },
+  { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false },
+};
 
 // __sF is exported for backwards compatibility. Until M, we didn't have symbols
 // for stdin/stdout/stderr; they were macros accessing __sF.
 FILE __sF[3] = {
-	std(__SRD, STDIN_FILENO),		/* stdin */
-	std(__SWR, STDOUT_FILENO),		/* stdout */
-	std(__SWR|__SNBF, STDERR_FILENO)	/* stderr */
+  std(__SRD, STDIN_FILENO),
+  std(__SWR, STDOUT_FILENO),
+  std(__SWR|__SNBF, STDERR_FILENO),
 };
 
-struct glue __sglue = { &uglue, 3, __sF };
-
 FILE* stdin = &__sF[0];
 FILE* stdout = &__sF[1];
 FILE* stderr = &__sF[2];
 
+struct glue __sglue = { NULL, 3, __sF };
+static struct glue* lastglue = &__sglue;
+
 static struct glue *
 moreglue(int n)
 {
@@ -114,9 +121,6 @@
 	int n;
 	struct glue *g;
 
-	if (!__sdidinit)
-		__sinit();
-
 	_THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
 	for (g = &__sglue; g != NULL; g = g->next) {
 		for (fp = g->iobs, n = g->niobs; --n >= 0; fp++)
@@ -149,48 +153,7 @@
 	return (fp);
 }
 
-/*
- * exit() and abort() call _cleanup() through the callback registered
- * with __atexit_register_cleanup(), set whenever we open or buffer a
- * file. This chicanery is done so that programs that do not use stdio
- * need not link it all in.
- *
- * The name `_cleanup' is, alas, fairly well known outside stdio.
- */
-void
-_cleanup(void)
-{
+__LIBC_HIDDEN__ void __libc_stdio_cleanup(void) {
 	/* (void) _fwalk(fclose); */
 	(void) _fwalk(__sflush);		/* `cheating' */
 }
-
-/*
- * __sinit() is called whenever stdio's internal variables must be set up.
- */
-void
-__sinit(void)
-{
-	_THREAD_PRIVATE_MUTEX(__sinit_mutex);
-
-	_THREAD_PRIVATE_MUTEX_LOCK(__sinit_mutex);
-	if (__sdidinit) {
-		/* bail out if caller lost the race */
-		_THREAD_PRIVATE_MUTEX_UNLOCK(__sinit_mutex);
-		return;
-	}
-
-	/* Initialize stdin/stdout/stderr (for the recursive mutex). http://b/18208568. */
-	for (size_t i = 0; i < 3; ++i) {
-		_FILEEXT_SETUP(__sF+i, __sFext+i);
-	}
-	/* Initialize the pre-allocated (but initially unused) streams. */
-	for (size_t i = 0; i < FOPEN_MAX - 3; ++i) {
-		_FILEEXT_SETUP(usual+i, usualext+i);
-	}
-
-	/* make sure we clean up on exit */
-	__atexit_register_cleanup(_cleanup); /* conservative */
-	__sdidinit = 1;
-
-	_THREAD_PRIVATE_MUTEX_UNLOCK(__sinit_mutex);
-}
diff --git a/libc/stdio/glue.h b/libc/stdio/glue.h
index a9e5d10..cb1d182 100644
--- a/libc/stdio/glue.h
+++ b/libc/stdio/glue.h
@@ -47,6 +47,6 @@
 };
 
 /* This was referenced by a couple of different pieces of middleware and the Crystax NDK. */
-__LIBC64_HIDDEN__ extern struct glue __sglue;
+__LIBC32_LEGACY_PUBLIC__ extern struct glue __sglue;
 
 __END_DECLS
diff --git a/libc/stdio/local.h b/libc/stdio/local.h
index 749de7b..6dcd3ae 100644
--- a/libc/stdio/local.h
+++ b/libc/stdio/local.h
@@ -109,7 +109,7 @@
   pthread_mutex_t _lock;
 
   /* __fsetlocking support */
-  bool _stdio_handles_locking;
+  bool _caller_handles_locking;
 };
 
 #if defined(__cplusplus)
@@ -131,7 +131,7 @@
 	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
 	pthread_mutex_init(&_FLOCK(fp), &attr); \
 	pthread_mutexattr_destroy(&attr); \
-	_EXT(fp)->_stdio_handles_locking = true; \
+	_EXT(fp)->_caller_handles_locking = false; \
 } while (0)
 
 #define _FILEEXT_SETUP(f, fext) \
@@ -145,32 +145,29 @@
  * to __srget/__swbuf, so those symbols need to be public for LP32
  * but can be hidden for LP64.
  */
-__LIBC64_HIDDEN__ int __srget(FILE*);
-__LIBC64_HIDDEN__ int __swbuf(int, FILE*);
-__LIBC64_HIDDEN__ int __srefill(FILE*);
+__LIBC32_LEGACY_PUBLIC__ int __srget(FILE*);
+__LIBC32_LEGACY_PUBLIC__ int __swbuf(int, FILE*);
+__LIBC32_LEGACY_PUBLIC__ int __srefill(FILE*);
 
 /* This was referenced by the apportable middleware for LP32. */
-__LIBC64_HIDDEN__ int __swsetup(FILE*);
+__LIBC32_LEGACY_PUBLIC__ int __swsetup(FILE*);
 
 /* These were referenced by a couple of different pieces of middleware and the Crystax NDK. */
-__LIBC64_HIDDEN__ extern int __sdidinit;
-__LIBC64_HIDDEN__ int __sflags(const char*, int*);
-__LIBC64_HIDDEN__ FILE* __sfp(void);
-__LIBC64_HIDDEN__ void __sinit(void);
-__LIBC64_HIDDEN__ void __smakebuf(FILE*);
+__LIBC32_LEGACY_PUBLIC__ int __sflags(const char*, int*);
+__LIBC32_LEGACY_PUBLIC__ FILE* __sfp(void);
+__LIBC32_LEGACY_PUBLIC__ void __smakebuf(FILE*);
 
 /* These are referenced by the Greed for Glory franchise. */
-__LIBC64_HIDDEN__ int __sflush(FILE *);
-__LIBC64_HIDDEN__ int __sread(void *, char *, int);
-__LIBC64_HIDDEN__ int __swrite(void *, const char *, int);
-__LIBC64_HIDDEN__ fpos_t __sseek(void *, fpos_t, int);
-__LIBC64_HIDDEN__ int __sclose(void *);
-__LIBC64_HIDDEN__ int _fwalk(int (*)(FILE *));
+__LIBC32_LEGACY_PUBLIC__ int __sflush(FILE *);
+__LIBC32_LEGACY_PUBLIC__ int __sread(void *, char *, int);
+__LIBC32_LEGACY_PUBLIC__ int __swrite(void *, const char *, int);
+__LIBC32_LEGACY_PUBLIC__ fpos_t __sseek(void *, fpos_t, int);
+__LIBC32_LEGACY_PUBLIC__ int __sclose(void *);
+__LIBC32_LEGACY_PUBLIC__ int _fwalk(int (*)(FILE *));
 
 #pragma GCC visibility push(hidden)
 
 int	__sflush_locked(FILE *);
-void	_cleanup(void);
 int	__swhatbuf(FILE *, size_t *, int *);
 wint_t __fgetwc_unlock(FILE *);
 wint_t	__ungetwc(wint_t, FILE *);
@@ -179,8 +176,6 @@
 int	__vfwprintf(FILE * __restrict, const wchar_t * __restrict, __va_list);
 int	__vfwscanf(FILE * __restrict, const wchar_t * __restrict, __va_list);
 
-extern void __atexit_register_cleanup(void (*)(void));
-
 /*
  * Return true if the given FILE cannot be written now.
  */
@@ -208,8 +203,8 @@
 	(fp)->_lb._base = NULL; \
 }
 
-#define FLOCKFILE(fp)   if (_EXT(fp)->_stdio_handles_locking) flockfile(fp)
-#define FUNLOCKFILE(fp) if (_EXT(fp)->_stdio_handles_locking) funlockfile(fp)
+#define FLOCKFILE(fp)   if (!_EXT(fp)->_caller_handles_locking) flockfile(fp)
+#define FUNLOCKFILE(fp) if (!_EXT(fp)->_caller_handles_locking) funlockfile(fp)
 
 #define FLOATING_POINT
 #define PRINTF_WIDE_CHAR
@@ -237,6 +232,10 @@
 extern int __sfvwrite(FILE *, struct __suio *);
 wint_t __fputwc_unlock(wchar_t wc, FILE *fp);
 
+/* Remove the if (!__sdidinit) __sinit() idiom from untouched upstream stdio code. */
+extern void __sinit(void); // Not actually implemented.
+#define __sdidinit 1
+
 #pragma GCC visibility pop
 
 __END_DECLS
diff --git a/libc/stdio/refill.c b/libc/stdio/refill.c
index e87c7b9..5b0811f 100644
--- a/libc/stdio/refill.c
+++ b/libc/stdio/refill.c
@@ -51,11 +51,6 @@
 int
 __srefill(FILE *fp)
 {
-
-	/* make sure stdio is set up */
-	if (!__sdidinit)
-		__sinit();
-
 	fp->_r = 0;		/* largely a convenience for callers */
 
 #if !defined(__ANDROID__)
diff --git a/libc/stdio/stdio_ext.cpp b/libc/stdio/stdio_ext.cpp
index fea44f6..f273d45 100644
--- a/libc/stdio/stdio_ext.cpp
+++ b/libc/stdio/stdio_ext.cpp
@@ -74,7 +74,7 @@
 }
 
 int __fsetlocking(FILE* fp, int type) {
-  int old_state = _EXT(fp)->_stdio_handles_locking ? FSETLOCKING_INTERNAL : FSETLOCKING_BYCALLER;
+  int old_state = _EXT(fp)->_caller_handles_locking ? FSETLOCKING_BYCALLER : FSETLOCKING_INTERNAL;
   if (type == FSETLOCKING_QUERY) {
     return old_state;
   }
@@ -84,7 +84,7 @@
     __libc_fatal("Bad type (%d) passed to __fsetlocking", type);
   }
 
-  _EXT(fp)->_stdio_handles_locking = (type == FSETLOCKING_INTERNAL);
+  _EXT(fp)->_caller_handles_locking = (type == FSETLOCKING_BYCALLER);
   return old_state;
 }
 
@@ -99,3 +99,7 @@
 int ferror_unlocked(FILE* fp) {
   return __sferror(fp);
 }
+
+int fileno_unlocked(FILE* fp) {
+  return __sfileno(fp);
+}
diff --git a/libc/stdlib/atexit.c b/libc/stdlib/atexit.c
index 34a4db1..c817b63 100644
--- a/libc/stdlib/atexit.c
+++ b/libc/stdlib/atexit.c
@@ -185,51 +185,12 @@
 	}
 	_ATEXIT_UNLOCK();
 
+  extern void __libc_stdio_cleanup(void);
+  __libc_stdio_cleanup();
+
   /* BEGIN android-changed: call __unregister_atfork if dso is not null */
   if (dso != NULL) {
     __unregister_atfork(dso);
   }
   /* END android-changed */
 }
-
-/*
- * Register the cleanup function
- */
-void
-__atexit_register_cleanup(void (*func)(void))
-{
-	struct atexit *p;
-	size_t pgsize = getpagesize();
-
-	if (pgsize < sizeof(*p))
-		return;
-	_ATEXIT_LOCK();
-	p = __atexit;
-	while (p != NULL && p->next != NULL)
-		p = p->next;
-	if (p == NULL) {
-		p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE,
-		    MAP_ANON | MAP_PRIVATE, -1, 0);
-		if (p == MAP_FAILED)
-			goto unlock;
-/* BEGIN android-changed */
-		prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, p, pgsize,
-		    "atexit handlers");
-/* END android-changed */
-		p->ind = 1;
-		p->max = (pgsize - ((char *)&p->fns[0] - (char *)p)) /
-		    sizeof(p->fns[0]);
-		p->next = NULL;
-		__atexit = p;
-	} else {
-		if (mprotect(p, pgsize, PROT_READ | PROT_WRITE))
-			goto unlock;
-	}
-	p->fns[0].fn_ptr = (void (*)(void *))func;
-	p->fns[0].fn_arg = NULL;
-	p->fns[0].fn_dso = NULL;
-	mprotect(p, pgsize, PROT_READ);
-	restartloop = 1;
-unlock:
-	_ATEXIT_UNLOCK();
-}
diff --git a/libc/tools/genversion-scripts.py b/libc/tools/genversion-scripts.py
new file mode 100755
index 0000000..37fb5e9
--- /dev/null
+++ b/libc/tools/genversion-scripts.py
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+
+# This tool is used to generate the version scripts for libc and libm
+# for every architecture.
+
+import atexit
+import os.path
+import shutil
+import tempfile
+
+
+all_arches = ["arm", "arm64", "mips", "mips64", "x86", "x86_64"]
+bionic_libc_root = os.path.join(os.environ["ANDROID_BUILD_TOP"], "bionic/libc")
+bionic_libm_root = os.path.join(os.environ["ANDROID_BUILD_TOP"], "bionic/libm")
+bionic_libdl_root = os.path.join(os.environ["ANDROID_BUILD_TOP"], "bionic/libdl")
+libc_script = os.path.join(bionic_libc_root, "libc.map.txt")
+libm_script = os.path.join(bionic_libm_root, "libm.map.txt")
+libdl_script = os.path.join(bionic_libdl_root, "libdl.map.txt")
+
+# TODO (dimity): generate architecture-specific version scripts as part of build
+
+# temp directory where we store all intermediate files
+bionic_temp = tempfile.mkdtemp(prefix="bionic_genversionscripts")
+# Make sure the directory is deleted when the script exits.
+atexit.register(shutil.rmtree, bionic_temp)
+
+bionic_libc_root = os.path.join(os.environ["ANDROID_BUILD_TOP"], "bionic/libc")
+
+warning = "Generated by genversionscripts.py. Do not edit."
+
+
+class VersionScriptGenerator(object):
+
+  def run(self):
+    for script in [libc_script, libm_script, libdl_script]:
+      basename = os.path.basename(script)
+      dirname = os.path.dirname(script)
+      for arch in all_arches:
+        name = basename.split(".")[0] + "." + arch + ".map"
+        tmp_path = os.path.join(bionic_temp, name)
+        dest_path = os.path.join(dirname, name)
+        with open(tmp_path, "w") as fout:
+          with open(script, "r") as fin:
+            fout.write("# %s\n" % warning)
+            for line in fin:
+              index = line.find("#")
+              if index != -1:
+                arches = line[index+1:].split()
+                if arch not in arches:
+                  continue
+              fout.write(line)
+        shutil.copyfile(tmp_path, dest_path)
+
+
+generator = VersionScriptGenerator()
+generator.run()
+
diff --git a/libc/tzcode/localtime.c b/libc/tzcode/localtime.c
index 79c4a9a..b1ebb24 100644
--- a/libc/tzcode/localtime.c
+++ b/libc/tzcode/localtime.c
@@ -338,7 +338,7 @@
 {
     if (TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS)
         return 0;
-#if defined(__LP64__) // 32-bit Android only has a signed 32-bit time_t; 64-bit Android is fixed.
+#if defined(__LP64__) // 32-bit Android/glibc has a signed 32-bit time_t; 64-bit doesn't.
     return t1 - t0 == SECSPERREPEAT;
 #endif
 }
@@ -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/libc/upstream-netbsd/android/include/netbsd-compat.h b/libc/upstream-netbsd/android/include/netbsd-compat.h
index 0212d16..8d1c46b 100644
--- a/libc/upstream-netbsd/android/include/netbsd-compat.h
+++ b/libc/upstream-netbsd/android/include/netbsd-compat.h
@@ -32,6 +32,6 @@
 #define __unlockenv() 0
 
 #include <stddef.h>
-int reallocarr(void*, size_t, size_t);
+__LIBC_HIDDEN__ int reallocarr(void*, size_t, size_t);
 
 #endif
diff --git a/libc/upstream-openbsd/android/include/openbsd-compat.h b/libc/upstream-openbsd/android/include/openbsd-compat.h
index 47bacc3..b07f55d 100644
--- a/libc/upstream-openbsd/android/include/openbsd-compat.h
+++ b/libc/upstream-openbsd/android/include/openbsd-compat.h
@@ -71,8 +71,8 @@
 __LIBC_HIDDEN__ void* reallocarray(void*, size_t, size_t);
 
 /* LP32 NDK ctype.h contained references to these. */
-__LIBC64_HIDDEN__ extern const short* _tolower_tab_;
-__LIBC64_HIDDEN__ extern const short* _toupper_tab_;
+__LIBC32_LEGACY_PUBLIC__ extern const short* _tolower_tab_;
+__LIBC32_LEGACY_PUBLIC__ extern const short* _toupper_tab_;
 
 __LIBC_HIDDEN__ extern const char _C_ctype_[];
 __LIBC_HIDDEN__ extern const short _C_toupper_[];
diff --git a/libdl/Android.bp b/libdl/Android.bp
index 46dd0eb..2aa9b68 100644
--- a/libdl/Android.bp
+++ b/libdl/Android.bp
@@ -13,15 +13,28 @@
     // DO NOT REMOVE --exclude-libs!
 
     ldflags: ["-Wl,--exclude-libs=libgcc.a"],
-    version_script: "libdl.map",
 
     // for x86, exclude libgcc_eh.a for the same reasons as above
     arch: {
+        arm: {
+            version_script: "libdl.arm.map",
+        },
+        arm64: {
+            version_script: "libdl.arm64.map",
+        },
+        mips: {
+            version_script: "libdl.mips.map",
+        },
+        mips64: {
+            version_script: "libdl.mips64.map",
+        },
         x86: {
             ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
+            version_script: "libdl.x86.map",
         },
         x86_64: {
             ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
+            version_script: "libdl.x86_64.map",
         },
     },
     srcs: ["libdl.c"],
diff --git a/libdl/Android.mk b/libdl/Android.mk
index 4ab32e0..1ea5dc7 100644
--- a/libdl/Android.mk
+++ b/libdl/Android.mk
@@ -15,18 +15,31 @@
 #
 # DO NOT REMOVE --exclude-libs!
 
-LOCAL_LDFLAGS := -Wl,--exclude-libs=libgcc.a -Wl,--version-script=$(LOCAL_PATH)/libdl.map
+LOCAL_LDFLAGS := -Wl,--exclude-libs=libgcc.a
 
 # for x86, exclude libgcc_eh.a for the same reasons as above
 LOCAL_LDFLAGS_x86 := -Wl,--exclude-libs=libgcc_eh.a
 LOCAL_LDFLAGS_x86_64 := $(LOCAL_LDFLAGS_x86)
 
+LOCAL_LDFLAGS_arm    += -Wl,--version-script=$(LOCAL_PATH)/libdl.arm.map
+LOCAL_LDFLAGS_arm64  += -Wl,--version-script=$(LOCAL_PATH)/libdl.arm64.map
+LOCAL_LDFLAGS_mips   += -Wl,--version-script=$(LOCAL_PATH)/libdl.mips.map
+LOCAL_LDFLAGS_mips64 += -Wl,--version-script=$(LOCAL_PATH)/libdl.mips64.map
+LOCAL_LDFLAGS_x86    += -Wl,--version-script=$(LOCAL_PATH)/libdl.x86.map
+LOCAL_LDFLAGS_x86_64 += -Wl,--version-script=$(LOCAL_PATH)/libdl.x86_64.map
+
 LOCAL_SRC_FILES:= libdl.c
 LOCAL_CFLAGS := -Wall -Wextra -Wunused -Werror
 LOCAL_CXX_STL := none
 
 LOCAL_MODULE := libdl
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk \
+                                 $(LOCAL_PATH)/libdl.arm.map \
+                                 $(LOCAL_PATH)/libdl.arm64.map \
+                                 $(LOCAL_PATH)/libdl.mips.map \
+                                 $(LOCAL_PATH)/libdl.mips64.map \
+                                 $(LOCAL_PATH)/libdl.x86.map \
+                                 $(LOCAL_PATH)/libdl.x86_64.map \
 
 # NOTE: libdl needs __aeabi_unwind_cpp_pr0 from libgcc.a but libgcc.a needs a
 # few symbols from libc. Using --no-undefined here results in having to link
diff --git a/libdl/libdl.arm.map b/libdl/libdl.arm.map
new file mode 100644
index 0000000..5ad9f9d
--- /dev/null
+++ b/libdl/libdl.arm.map
@@ -0,0 +1,29 @@
+# Generated by genversionscripts.py. Do not edit.
+
+LIBC {
+  global:
+    android_dlopen_ext;
+    dl_iterate_phdr;
+    dl_unwind_find_exidx; # arm
+    dladdr;
+    dlclose;
+    dlerror;
+    dlopen;
+    dlsym;
+  local:
+    *;
+};
+
+LIBC_N {
+  global:
+    android_init_namespaces;
+    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_N;
diff --git a/libdl/libdl.arm64.map b/libdl/libdl.arm64.map
new file mode 100644
index 0000000..3535774
--- /dev/null
+++ b/libdl/libdl.arm64.map
@@ -0,0 +1,28 @@
+# Generated by genversionscripts.py. Do not edit.
+
+LIBC {
+  global:
+    android_dlopen_ext;
+    dl_iterate_phdr;
+    dladdr;
+    dlclose;
+    dlerror;
+    dlopen;
+    dlsym;
+  local:
+    *;
+};
+
+LIBC_N {
+  global:
+    android_init_namespaces;
+    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_N;
diff --git a/libdl/libdl.c b/libdl/libdl.c
index 9a858a3..3928ba2 100644
--- a/libdl/libdl.c
+++ b/libdl/libdl.c
@@ -33,12 +33,30 @@
 _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_namespaces(const char* public_ns_sonames __unused,
+                             const char* anon_ns_library_path __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 b/libdl/libdl.map.txt
similarity index 88%
rename from libdl/libdl.map
rename to libdl/libdl.map.txt
index a911cb6..8d123fe 100644
--- a/libdl/libdl.map
+++ b/libdl/libdl.map.txt
@@ -18,9 +18,7 @@
   global:
     android_dlopen_ext;
     dl_iterate_phdr;
-# begin arm-only
-    dl_unwind_find_exidx;
-# end arm-only
+    dl_unwind_find_exidx; # arm
     dladdr;
     dlclose;
     dlerror;
@@ -30,10 +28,16 @@
     *;
 };
 
+LIBC_N {
+  global:
+    android_init_namespaces;
+    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
new file mode 100644
index 0000000..3535774
--- /dev/null
+++ b/libdl/libdl.mips.map
@@ -0,0 +1,28 @@
+# Generated by genversionscripts.py. Do not edit.
+
+LIBC {
+  global:
+    android_dlopen_ext;
+    dl_iterate_phdr;
+    dladdr;
+    dlclose;
+    dlerror;
+    dlopen;
+    dlsym;
+  local:
+    *;
+};
+
+LIBC_N {
+  global:
+    android_init_namespaces;
+    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_N;
diff --git a/libdl/libdl.mips64.map b/libdl/libdl.mips64.map
new file mode 100644
index 0000000..3535774
--- /dev/null
+++ b/libdl/libdl.mips64.map
@@ -0,0 +1,28 @@
+# Generated by genversionscripts.py. Do not edit.
+
+LIBC {
+  global:
+    android_dlopen_ext;
+    dl_iterate_phdr;
+    dladdr;
+    dlclose;
+    dlerror;
+    dlopen;
+    dlsym;
+  local:
+    *;
+};
+
+LIBC_N {
+  global:
+    android_init_namespaces;
+    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_N;
diff --git a/libdl/libdl.x86.map b/libdl/libdl.x86.map
new file mode 100644
index 0000000..3535774
--- /dev/null
+++ b/libdl/libdl.x86.map
@@ -0,0 +1,28 @@
+# Generated by genversionscripts.py. Do not edit.
+
+LIBC {
+  global:
+    android_dlopen_ext;
+    dl_iterate_phdr;
+    dladdr;
+    dlclose;
+    dlerror;
+    dlopen;
+    dlsym;
+  local:
+    *;
+};
+
+LIBC_N {
+  global:
+    android_init_namespaces;
+    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_N;
diff --git a/libdl/libdl.x86_64.map b/libdl/libdl.x86_64.map
new file mode 100644
index 0000000..3535774
--- /dev/null
+++ b/libdl/libdl.x86_64.map
@@ -0,0 +1,28 @@
+# Generated by genversionscripts.py. Do not edit.
+
+LIBC {
+  global:
+    android_dlopen_ext;
+    dl_iterate_phdr;
+    dladdr;
+    dlclose;
+    dlerror;
+    dlopen;
+    dlsym;
+  local:
+    *;
+};
+
+LIBC_N {
+  global:
+    android_init_namespaces;
+    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_N;
diff --git a/libm/Android.bp b/libm/Android.bp
index 3ae086f..081a139 100644
--- a/libm/Android.bp
+++ b/libm/Android.bp
@@ -3,18 +3,7 @@
 
 bionic_coverage = false
 
-// TODO: this comes from from upstream's libc, not libm, but it's an
-// implementation detail that should have hidden visibility, so it needs
-// to be in whatever library the math code is in.
-libm_common_src_files = ["digittoint.c"]
-
-// TODO: this is not in the BSDs.
-libm_common_src_files += [
-    "significandl.c",
-    "sincos.c",
-]
-
-libm_common_src_files += [
+libm_common_src_files = [
     "upstream-freebsd/lib/msun/bsdsrc/b_exp.c",
     "upstream-freebsd/lib/msun/bsdsrc/b_log.c",
     "upstream-freebsd/lib/msun/bsdsrc/b_tgamma.c",
@@ -186,8 +175,19 @@
 ]
 
 libm_common_src_files += [
-    "fake_long_double.c",
+    // TODO: this comes from from upstream's libc, not libm, but it's an
+    // implementation detail that should have hidden visibility, so it needs
+    // to be in whatever library the math code is in.
+    "digittoint.c",
+
+    // Functionality not in the BSDs.
+    "significandl.c",
+    "sincos.c",
+
+    // Modified versions of BSD code.
     "signbit.c",
+
+    // Home-grown stuff.
     "fabs.cpp",
 ]
 
@@ -254,6 +254,7 @@
     "-D__BIONIC_NO_MATH_INLINES",
     "-DFLT_EVAL_METHOD=0",
     "-include freebsd-compat.h",
+    "-Werror",
     "-Wno-missing-braces",
     "-Wno-parentheses",
     "-Wno-sign-compare",
@@ -290,9 +291,10 @@
     native_coverage: bionic_coverage,
     sanitize: ["never"],
 
-    version_script: "libm.map",
-
     multilib: {
+        lib32: {
+            srcs: ["fake_long_double.c"],
+        },
         lib64: {
             srcs: libm_ld128_src_files,
             local_include_dirs: libm_ld_local_includes,
@@ -308,23 +310,27 @@
         arm: {
             srcs: [
                 "arm/fenv.c",
-                "arm/sqrt.S",
-                "arm/floor.S",
             ],
-            exclude_srcs: [
-                // TODO: these require neon not available in arm
-                "upstream-freebsd/lib/msun/src/e_sqrt.c",
-                "upstream-freebsd/lib/msun/src/e_sqrtf.c",
-                "upstream-freebsd/lib/msun/src/s_floor.c",
-            ],
+            armv7_a_neon: {
+                srcs: [
+                    "arm/sqrt.S",
+                    "arm/floor.S",
+                ],
+                exclude_srcs: [
+                    "upstream-freebsd/lib/msun/src/e_sqrt.c",
+                    "upstream-freebsd/lib/msun/src/e_sqrtf.c",
+                    "upstream-freebsd/lib/msun/src/s_floor.c",
+                ],
+            },
             instruction_set: "arm",
             ldflags: ["-Wl,--hash-style=both"],
+            version_script: "libm.arm.map",
         },
 
         arm64: {
             srcs: [
-                "arm64/fenv.c",
                 "arm64/ceil.S",
+                "arm64/fenv.c",
                 "arm64/fma.S",
                 "arm64/floor.S",
                 "arm64/lrint.S",
@@ -333,6 +339,8 @@
                 "arm64/trunc.S",
             ],
             exclude_srcs: [
+                "upstream-freebsd/lib/msun/src/e_sqrt.c",
+                "upstream-freebsd/lib/msun/src/e_sqrtf.c",
                 "upstream-freebsd/lib/msun/src/s_ceil.c",
                 "upstream-freebsd/lib/msun/src/s_ceilf.c",
                 "upstream-freebsd/lib/msun/src/s_fma.c",
@@ -345,33 +353,163 @@
                 "upstream-freebsd/lib/msun/src/s_lrintf.c",
                 "upstream-freebsd/lib/msun/src/s_rint.c",
                 "upstream-freebsd/lib/msun/src/s_rintf.c",
-                "upstream-freebsd/lib/msun/src/e_sqrt.c",
-                "upstream-freebsd/lib/msun/src/e_sqrtf.c",
                 "upstream-freebsd/lib/msun/src/s_trunc.c",
                 "upstream-freebsd/lib/msun/src/s_truncf.c",
             ],
+            version_script: "libm.arm64.map",
         },
 
         mips: {
             srcs: ["mips/fenv.c"],
+            version_script: "libm.mips.map",
         },
 
         mips64: {
             srcs: ["mips/fenv.c"],
+            version_script: "libm.mips64.map",
         },
 
         x86: {
+            srcs: [
+                "i387/fenv.c",
+                "x86/sqrt.S",
+                "x86/sqrtf.S",
+                "x86/e_acos.S",
+                "x86/e_asin.S",
+                "x86/e_atan2.S",
+                "x86/e_cosh.S",
+                "x86/e_exp.S",
+                "x86/e_hypot.S",
+                "x86/e_log10.S",
+                "x86/e_log.S",
+                "x86/e_pow.S",
+                "x86/e_sinh.S",
+                "x86/libm_reduce_pi04l.S",
+                "x86/libm_sincos_huge.S",
+                "x86/libm_tancot_huge.S",
+                "x86/s_atan.S",
+                "x86/s_cbrt.S",
+                "x86/s_cos.S",
+                "x86/s_expm1.S",
+                "x86/s_log1p.S",
+                "x86/s_sin.S",
+                "x86/s_tanh.S",
+                "x86/s_tan.S",
+            ],
+            exclude_srcs: [
+                "upstream-freebsd/lib/msun/src/e_acos.c",
+                "upstream-freebsd/lib/msun/src/e_asin.c",
+                "upstream-freebsd/lib/msun/src/e_atan2.c",
+                "upstream-freebsd/lib/msun/src/e_cosh.c",
+                "upstream-freebsd/lib/msun/src/e_exp.c",
+                "upstream-freebsd/lib/msun/src/e_hypot.c",
+                "upstream-freebsd/lib/msun/src/e_log.c",
+                "upstream-freebsd/lib/msun/src/e_log10.c",
+                "upstream-freebsd/lib/msun/src/e_pow.c",
+                "upstream-freebsd/lib/msun/src/e_sinh.c",
+                "upstream-freebsd/lib/msun/src/e_sqrt.c",
+                "upstream-freebsd/lib/msun/src/e_sqrtf.c",
+                "upstream-freebsd/lib/msun/src/s_atan.c",
+                "upstream-freebsd/lib/msun/src/s_cbrt.c",
+                "upstream-freebsd/lib/msun/src/s_cos.c",
+                "upstream-freebsd/lib/msun/src/s_expm1.c",
+                "upstream-freebsd/lib/msun/src/s_log1p.c",
+                "upstream-freebsd/lib/msun/src/s_sin.c",
+                "upstream-freebsd/lib/msun/src/s_tan.c",
+                "upstream-freebsd/lib/msun/src/s_tanh.c",
+            ],
+            sse4_1: {
+                srcs: [
+                    "x86/ceil.S",
+                    "x86/ceilf.S",
+                    "x86/floor.S",
+                    "x86/floorf.S",
+                    "x86/trunc.S",
+                    "x86/truncf.S",
+                ],
+                exclude_srcs: [
+                    "upstream-freebsd/lib/msun/src/s_ceil.c",
+                    "upstream-freebsd/lib/msun/src/s_ceilf.c",
+                    "upstream-freebsd/lib/msun/src/s_floor.c",
+                    "upstream-freebsd/lib/msun/src/s_floorf.c",
+                    "upstream-freebsd/lib/msun/src/s_trunc.c",
+                    "upstream-freebsd/lib/msun/src/s_truncf.c",
+                ],
+            },
             local_include_dirs: ["i387"],
-            srcs: ["i387/fenv.c"],
             // Clang has wrong long double sizes for x86.
             clang: false,
             ldflags: ["-Wl,--hash-style=both"],
+            version_script: "libm.x86.map",
         },
 
         x86_64: {
-            srcs: ["amd64/fenv.c"],
+            srcs: [
+                "amd64/fenv.c",
+                "x86_64/sqrt.S",
+                "x86_64/sqrtf.S",
+                "x86_64/e_acos.S",
+                "x86_64/e_asin.S",
+                "x86_64/e_atan2.S",
+                "x86_64/e_cosh.S",
+                "x86_64/e_exp.S",
+                "x86_64/e_hypot.S",
+                "x86_64/e_log10.S",
+                "x86_64/e_log.S",
+                "x86_64/e_pow.S",
+                "x86_64/e_sinh.S",
+                "x86_64/s_atan.S",
+                "x86_64/s_cbrt.S",
+                "x86_64/s_cos.S",
+                "x86_64/s_expm1.S",
+                "x86_64/s_log1p.S",
+                "x86_64/s_sin.S",
+                "x86_64/s_tanh.S",
+                "x86_64/s_tan.S",
+            ],
+            exclude_srcs: [
+                "upstream-freebsd/lib/msun/src/e_acos.c",
+                "upstream-freebsd/lib/msun/src/e_asin.c",
+                "upstream-freebsd/lib/msun/src/e_atan2.c",
+                "upstream-freebsd/lib/msun/src/e_cosh.c",
+                "upstream-freebsd/lib/msun/src/e_exp.c",
+                "upstream-freebsd/lib/msun/src/e_hypot.c",
+                "upstream-freebsd/lib/msun/src/e_log.c",
+                "upstream-freebsd/lib/msun/src/e_log10.c",
+                "upstream-freebsd/lib/msun/src/e_pow.c",
+                "upstream-freebsd/lib/msun/src/e_sinh.c",
+                "upstream-freebsd/lib/msun/src/e_sqrt.c",
+                "upstream-freebsd/lib/msun/src/e_sqrtf.c",
+                "upstream-freebsd/lib/msun/src/s_atan.c",
+                "upstream-freebsd/lib/msun/src/s_cbrt.c",
+                "upstream-freebsd/lib/msun/src/s_cos.c",
+                "upstream-freebsd/lib/msun/src/s_expm1.c",
+                "upstream-freebsd/lib/msun/src/s_log1p.c",
+                "upstream-freebsd/lib/msun/src/s_sin.c",
+                "upstream-freebsd/lib/msun/src/s_tan.c",
+                "upstream-freebsd/lib/msun/src/s_tanh.c",
+            ],
+            sse4_1: {
+                srcs: [
+                    "x86_64/ceil.S",
+                    "x86_64/ceilf.S",
+                    "x86_64/floor.S",
+                    "x86_64/floorf.S",
+                    "x86_64/trunc.S",
+                    "x86_64/truncf.S",
+                ],
+                exclude_srcs: [
+                    "upstream-freebsd/lib/msun/src/s_ceil.c",
+                    "upstream-freebsd/lib/msun/src/s_ceilf.c",
+                    "upstream-freebsd/lib/msun/src/s_floor.c",
+                    "upstream-freebsd/lib/msun/src/s_floorf.c",
+                    "upstream-freebsd/lib/msun/src/s_trunc.c",
+                    "upstream-freebsd/lib/msun/src/s_truncf.c",
+                ],
+            },
             // Clang has wrong long double sizes for x86.
             clang: false,
+            version_script: "libm.x86_64.map",
         },
     },
 
diff --git a/libm/Android.mk b/libm/Android.mk
index fb8df07..faf3c50 100644
--- a/libm/Android.mk
+++ b/libm/Android.mk
@@ -22,14 +22,19 @@
     upstream-freebsd/lib/msun/bsdsrc/b_tgamma.c \
     upstream-freebsd/lib/msun/src/catrig.c \
     upstream-freebsd/lib/msun/src/catrigf.c \
+    upstream-freebsd/lib/msun/src/e_acos.c \
     upstream-freebsd/lib/msun/src/e_acosf.c \
     upstream-freebsd/lib/msun/src/e_acosh.c \
     upstream-freebsd/lib/msun/src/e_acoshf.c \
+    upstream-freebsd/lib/msun/src/e_asin.c \
     upstream-freebsd/lib/msun/src/e_asinf.c \
+    upstream-freebsd/lib/msun/src/e_atan2.c \
     upstream-freebsd/lib/msun/src/e_atan2f.c \
     upstream-freebsd/lib/msun/src/e_atanh.c \
     upstream-freebsd/lib/msun/src/e_atanhf.c \
+    upstream-freebsd/lib/msun/src/e_cosh.c \
     upstream-freebsd/lib/msun/src/e_coshf.c \
+    upstream-freebsd/lib/msun/src/e_exp.c \
     upstream-freebsd/lib/msun/src/e_expf.c \
     upstream-freebsd/lib/msun/src/e_fmod.c \
     upstream-freebsd/lib/msun/src/e_fmodf.c \
@@ -37,6 +42,7 @@
     upstream-freebsd/lib/msun/src/e_gammaf.c \
     upstream-freebsd/lib/msun/src/e_gammaf_r.c \
     upstream-freebsd/lib/msun/src/e_gamma_r.c \
+    upstream-freebsd/lib/msun/src/e_hypot.c \
     upstream-freebsd/lib/msun/src/e_hypotf.c \
     upstream-freebsd/lib/msun/src/e_j0.c \
     upstream-freebsd/lib/msun/src/e_j0f.c \
@@ -48,10 +54,13 @@
     upstream-freebsd/lib/msun/src/e_lgammaf.c \
     upstream-freebsd/lib/msun/src/e_lgammaf_r.c \
     upstream-freebsd/lib/msun/src/e_lgamma_r.c \
+    upstream-freebsd/lib/msun/src/e_log10.c \
     upstream-freebsd/lib/msun/src/e_log10f.c \
     upstream-freebsd/lib/msun/src/e_log2.c \
     upstream-freebsd/lib/msun/src/e_log2f.c \
+    upstream-freebsd/lib/msun/src/e_log.c \
     upstream-freebsd/lib/msun/src/e_logf.c \
+    upstream-freebsd/lib/msun/src/e_pow.c \
     upstream-freebsd/lib/msun/src/e_powf.c \
     upstream-freebsd/lib/msun/src/e_remainder.c \
     upstream-freebsd/lib/msun/src/e_remainderf.c \
@@ -59,7 +68,10 @@
     upstream-freebsd/lib/msun/src/e_rem_pio2f.c \
     upstream-freebsd/lib/msun/src/e_scalb.c \
     upstream-freebsd/lib/msun/src/e_scalbf.c \
+    upstream-freebsd/lib/msun/src/e_sinh.c \
     upstream-freebsd/lib/msun/src/e_sinhf.c \
+    upstream-freebsd/lib/msun/src/e_sqrt.c \
+    upstream-freebsd/lib/msun/src/e_sqrtf.c \
     upstream-freebsd/lib/msun/src/imprecise.c \
     upstream-freebsd/lib/msun/src/k_cos.c \
     upstream-freebsd/lib/msun/src/k_cosf.c \
@@ -72,13 +84,17 @@
     upstream-freebsd/lib/msun/src/k_tanf.c \
     upstream-freebsd/lib/msun/src/s_asinh.c \
     upstream-freebsd/lib/msun/src/s_asinhf.c \
+    upstream-freebsd/lib/msun/src/s_atan.c \
     upstream-freebsd/lib/msun/src/s_atanf.c \
     upstream-freebsd/lib/msun/src/s_carg.c \
     upstream-freebsd/lib/msun/src/s_cargf.c \
     upstream-freebsd/lib/msun/src/s_cargl.c \
+    upstream-freebsd/lib/msun/src/s_cbrt.c \
     upstream-freebsd/lib/msun/src/s_cbrtf.c \
     upstream-freebsd/lib/msun/src/s_ccosh.c \
     upstream-freebsd/lib/msun/src/s_ccoshf.c \
+    upstream-freebsd/lib/msun/src/s_ceil.c \
+    upstream-freebsd/lib/msun/src/s_ceilf.c \
     upstream-freebsd/lib/msun/src/s_cexp.c \
     upstream-freebsd/lib/msun/src/s_cexpf.c \
     upstream-freebsd/lib/msun/src/s_cimag.c \
@@ -89,6 +105,7 @@
     upstream-freebsd/lib/msun/src/s_conjl.c \
     upstream-freebsd/lib/msun/src/s_copysign.c \
     upstream-freebsd/lib/msun/src/s_copysignf.c \
+    upstream-freebsd/lib/msun/src/s_cos.c \
     upstream-freebsd/lib/msun/src/s_cosf.c \
     upstream-freebsd/lib/msun/src/s_cproj.c \
     upstream-freebsd/lib/msun/src/s_cprojf.c \
@@ -107,10 +124,15 @@
     upstream-freebsd/lib/msun/src/s_erff.c \
     upstream-freebsd/lib/msun/src/s_exp2.c \
     upstream-freebsd/lib/msun/src/s_exp2f.c \
+    upstream-freebsd/lib/msun/src/s_expm1.c \
     upstream-freebsd/lib/msun/src/s_expm1f.c \
     upstream-freebsd/lib/msun/src/s_fdim.c \
     upstream-freebsd/lib/msun/src/s_finite.c \
     upstream-freebsd/lib/msun/src/s_finitef.c \
+    upstream-freebsd/lib/msun/src/s_floor.c \
+    upstream-freebsd/lib/msun/src/s_floorf.c \
+    upstream-freebsd/lib/msun/src/s_fma.c \
+    upstream-freebsd/lib/msun/src/s_fmaf.c \
     upstream-freebsd/lib/msun/src/s_fmax.c \
     upstream-freebsd/lib/msun/src/s_fmaxf.c \
     upstream-freebsd/lib/msun/src/s_fmin.c \
@@ -119,11 +141,16 @@
     upstream-freebsd/lib/msun/src/s_frexpf.c \
     upstream-freebsd/lib/msun/src/s_ilogb.c \
     upstream-freebsd/lib/msun/src/s_ilogbf.c \
+    upstream-freebsd/lib/msun/src/s_llrint.c \
+    upstream-freebsd/lib/msun/src/s_llrintf.c \
     upstream-freebsd/lib/msun/src/s_llround.c \
     upstream-freebsd/lib/msun/src/s_llroundf.c \
+    upstream-freebsd/lib/msun/src/s_log1p.c \
     upstream-freebsd/lib/msun/src/s_log1pf.c \
     upstream-freebsd/lib/msun/src/s_logb.c \
     upstream-freebsd/lib/msun/src/s_logbf.c \
+    upstream-freebsd/lib/msun/src/s_lrint.c \
+    upstream-freebsd/lib/msun/src/s_lrintf.c \
     upstream-freebsd/lib/msun/src/s_lround.c \
     upstream-freebsd/lib/msun/src/s_lroundf.c \
     upstream-freebsd/lib/msun/src/s_modf.c \
@@ -134,6 +161,8 @@
     upstream-freebsd/lib/msun/src/s_nextafterf.c \
     upstream-freebsd/lib/msun/src/s_remquo.c \
     upstream-freebsd/lib/msun/src/s_remquof.c \
+    upstream-freebsd/lib/msun/src/s_rint.c \
+    upstream-freebsd/lib/msun/src/s_rintf.c \
     upstream-freebsd/lib/msun/src/s_round.c \
     upstream-freebsd/lib/msun/src/s_roundf.c \
     upstream-freebsd/lib/msun/src/s_scalbln.c \
@@ -142,10 +171,15 @@
     upstream-freebsd/lib/msun/src/s_signgam.c \
     upstream-freebsd/lib/msun/src/s_significand.c \
     upstream-freebsd/lib/msun/src/s_significandf.c \
+    upstream-freebsd/lib/msun/src/s_sin.c \
     upstream-freebsd/lib/msun/src/s_sinf.c \
+    upstream-freebsd/lib/msun/src/s_tan.c \
     upstream-freebsd/lib/msun/src/s_tanf.c \
+    upstream-freebsd/lib/msun/src/s_tanh.c \
     upstream-freebsd/lib/msun/src/s_tanhf.c \
     upstream-freebsd/lib/msun/src/s_tgammaf.c \
+    upstream-freebsd/lib/msun/src/s_trunc.c \
+    upstream-freebsd/lib/msun/src/s_truncf.c \
     upstream-freebsd/lib/msun/src/w_cabs.c \
     upstream-freebsd/lib/msun/src/w_cabsf.c \
     upstream-freebsd/lib/msun/src/w_cabsl.c \
@@ -236,37 +270,6 @@
 # -----------------------------------------------------------------------------
 LOCAL_SRC_FILES_arm += \
     arm/fenv.c \
-    upstream-freebsd/lib/msun/src/e_acos.c \
-    upstream-freebsd/lib/msun/src/e_asin.c \
-    upstream-freebsd/lib/msun/src/e_atan2.c \
-    upstream-freebsd/lib/msun/src/e_cosh.c \
-    upstream-freebsd/lib/msun/src/e_exp.c \
-    upstream-freebsd/lib/msun/src/e_hypot.c \
-    upstream-freebsd/lib/msun/src/e_log.c \
-    upstream-freebsd/lib/msun/src/e_log10.c \
-    upstream-freebsd/lib/msun/src/e_pow.c \
-    upstream-freebsd/lib/msun/src/e_sinh.c \
-    upstream-freebsd/lib/msun/src/s_atan.c \
-    upstream-freebsd/lib/msun/src/s_cbrt.c \
-    upstream-freebsd/lib/msun/src/s_ceil.c \
-    upstream-freebsd/lib/msun/src/s_ceilf.c \
-    upstream-freebsd/lib/msun/src/s_cos.c \
-    upstream-freebsd/lib/msun/src/s_fma.c \
-    upstream-freebsd/lib/msun/src/s_fmaf.c \
-    upstream-freebsd/lib/msun/src/s_floorf.c \
-    upstream-freebsd/lib/msun/src/s_expm1.c \
-    upstream-freebsd/lib/msun/src/s_llrint.c \
-    upstream-freebsd/lib/msun/src/s_llrintf.c \
-    upstream-freebsd/lib/msun/src/s_log1p.c \
-    upstream-freebsd/lib/msun/src/s_lrint.c \
-    upstream-freebsd/lib/msun/src/s_lrintf.c \
-    upstream-freebsd/lib/msun/src/s_rint.c \
-    upstream-freebsd/lib/msun/src/s_rintf.c \
-    upstream-freebsd/lib/msun/src/s_sin.c \
-    upstream-freebsd/lib/msun/src/s_tan.c \
-    upstream-freebsd/lib/msun/src/s_tanh.c \
-    upstream-freebsd/lib/msun/src/s_trunc.c \
-    upstream-freebsd/lib/msun/src/s_truncf.c \
 
 # s_floor.S requires neon instructions.
 ifdef TARGET_2ND_ARCH
@@ -276,17 +279,16 @@
 endif
 
 # Use the C version on armv7-a since it doesn't support neon instructions.
-ifeq ($(arch_variant),armv7-a)
-LOCAL_SRC_FILES_arm += \
-    upstream-freebsd/lib/msun/src/e_sqrt.c \
-    upstream-freebsd/lib/msun/src/e_sqrtf.c \
-    upstream-freebsd/lib/msun/src/s_floor.c \
-
-else
+ifneq ($(arch_variant),armv7-a)
 LOCAL_SRC_FILES_arm += \
     arm/sqrt.S \
     arm/floor.S \
 
+LOCAL_SRC_FILES_EXCLUDE_arm += \
+    upstream-freebsd/lib/msun/src/e_sqrt.c \
+    upstream-freebsd/lib/msun/src/e_sqrtf.c \
+    upstream-freebsd/lib/msun/src/s_floor.c \
+
 endif
 
 # -----------------------------------------------------------------------------
@@ -301,64 +303,30 @@
     arm64/rint.S \
     arm64/sqrt.S \
     arm64/trunc.S \
-    upstream-freebsd/lib/msun/src/e_acos.c \
-    upstream-freebsd/lib/msun/src/e_asin.c \
-    upstream-freebsd/lib/msun/src/e_atan2.c \
-    upstream-freebsd/lib/msun/src/e_cosh.c \
-    upstream-freebsd/lib/msun/src/e_exp.c \
-    upstream-freebsd/lib/msun/src/e_hypot.c \
-    upstream-freebsd/lib/msun/src/e_log.c \
-    upstream-freebsd/lib/msun/src/e_log10.c \
-    upstream-freebsd/lib/msun/src/e_pow.c \
-    upstream-freebsd/lib/msun/src/e_sinh.c \
-    upstream-freebsd/lib/msun/src/s_atan.c \
-    upstream-freebsd/lib/msun/src/s_cbrt.c \
-    upstream-freebsd/lib/msun/src/s_cos.c \
-    upstream-freebsd/lib/msun/src/s_expm1.c \
-    upstream-freebsd/lib/msun/src/s_log1p.c \
-    upstream-freebsd/lib/msun/src/s_sin.c \
-    upstream-freebsd/lib/msun/src/s_tan.c \
-    upstream-freebsd/lib/msun/src/s_tanh.c \
+
+LOCAL_SRC_FILES_EXCLUDE_arm64 += \
+    upstream-freebsd/lib/msun/src/e_sqrt.c \
+    upstream-freebsd/lib/msun/src/e_sqrtf.c \
+    upstream-freebsd/lib/msun/src/s_ceil.c \
+    upstream-freebsd/lib/msun/src/s_ceilf.c \
+    upstream-freebsd/lib/msun/src/s_fma.c \
+    upstream-freebsd/lib/msun/src/s_fmaf.c \
+    upstream-freebsd/lib/msun/src/s_floor.c \
+    upstream-freebsd/lib/msun/src/s_floorf.c \
+    upstream-freebsd/lib/msun/src/s_llrint.c \
+    upstream-freebsd/lib/msun/src/s_llrintf.c \
+    upstream-freebsd/lib/msun/src/s_lrint.c \
+    upstream-freebsd/lib/msun/src/s_lrintf.c \
+    upstream-freebsd/lib/msun/src/s_rint.c \
+    upstream-freebsd/lib/msun/src/s_rintf.c \
+    upstream-freebsd/lib/msun/src/s_trunc.c \
+    upstream-freebsd/lib/msun/src/s_truncf.c \
 
 # -----------------------------------------------------------------------------
 # mips
 # -----------------------------------------------------------------------------
 libm_mips_arch_files := \
     mips/fenv.c \
-    upstream-freebsd/lib/msun/src/e_acos.c \
-    upstream-freebsd/lib/msun/src/e_asin.c \
-    upstream-freebsd/lib/msun/src/e_atan2.c \
-    upstream-freebsd/lib/msun/src/e_cosh.c \
-    upstream-freebsd/lib/msun/src/e_exp.c \
-    upstream-freebsd/lib/msun/src/e_hypot.c \
-    upstream-freebsd/lib/msun/src/e_log.c \
-    upstream-freebsd/lib/msun/src/e_log10.c \
-    upstream-freebsd/lib/msun/src/e_pow.c \
-    upstream-freebsd/lib/msun/src/e_sinh.c \
-    upstream-freebsd/lib/msun/src/e_sqrt.c \
-    upstream-freebsd/lib/msun/src/e_sqrtf.c \
-    upstream-freebsd/lib/msun/src/s_atan.c \
-    upstream-freebsd/lib/msun/src/s_cbrt.c \
-    upstream-freebsd/lib/msun/src/s_ceil.c \
-    upstream-freebsd/lib/msun/src/s_ceilf.c \
-    upstream-freebsd/lib/msun/src/s_cos.c \
-    upstream-freebsd/lib/msun/src/s_fma.c \
-    upstream-freebsd/lib/msun/src/s_fmaf.c \
-    upstream-freebsd/lib/msun/src/s_floor.c \
-    upstream-freebsd/lib/msun/src/s_floorf.c \
-    upstream-freebsd/lib/msun/src/s_expm1.c \
-    upstream-freebsd/lib/msun/src/s_llrint.c \
-    upstream-freebsd/lib/msun/src/s_llrintf.c \
-    upstream-freebsd/lib/msun/src/s_log1p.c \
-    upstream-freebsd/lib/msun/src/s_lrint.c \
-    upstream-freebsd/lib/msun/src/s_lrintf.c \
-    upstream-freebsd/lib/msun/src/s_rint.c \
-    upstream-freebsd/lib/msun/src/s_rintf.c \
-    upstream-freebsd/lib/msun/src/s_sin.c \
-    upstream-freebsd/lib/msun/src/s_tan.c \
-    upstream-freebsd/lib/msun/src/s_tanh.c \
-    upstream-freebsd/lib/msun/src/s_trunc.c \
-    upstream-freebsd/lib/msun/src/s_truncf.c \
 
 LOCAL_SRC_FILES_mips += $(libm_mips_arch_files)
 LOCAL_SRC_FILES_mips64 += $(libm_mips_arch_files)
@@ -368,14 +336,6 @@
 # -----------------------------------------------------------------------------
 LOCAL_SRC_FILES_x86 += \
     i387/fenv.c \
-    upstream-freebsd/lib/msun/src/s_fma.c \
-    upstream-freebsd/lib/msun/src/s_fmaf.c \
-    upstream-freebsd/lib/msun/src/s_llrint.c \
-    upstream-freebsd/lib/msun/src/s_llrintf.c \
-    upstream-freebsd/lib/msun/src/s_lrint.c \
-    upstream-freebsd/lib/msun/src/s_lrintf.c \
-    upstream-freebsd/lib/msun/src/s_rint.c \
-    upstream-freebsd/lib/msun/src/s_rintf.c \
     x86/sqrt.S \
     x86/sqrtf.S \
     x86/e_acos.S \
@@ -400,6 +360,28 @@
     x86/s_tanh.S \
     x86/s_tan.S \
 
+LOCAL_SRC_FILES_EXCLUDE_x86 += \
+    upstream-freebsd/lib/msun/src/e_acos.c \
+    upstream-freebsd/lib/msun/src/e_asin.c \
+    upstream-freebsd/lib/msun/src/e_atan2.c \
+    upstream-freebsd/lib/msun/src/e_cosh.c \
+    upstream-freebsd/lib/msun/src/e_exp.c \
+    upstream-freebsd/lib/msun/src/e_hypot.c \
+    upstream-freebsd/lib/msun/src/e_log.c \
+    upstream-freebsd/lib/msun/src/e_log10.c \
+    upstream-freebsd/lib/msun/src/e_pow.c \
+    upstream-freebsd/lib/msun/src/e_sinh.c \
+    upstream-freebsd/lib/msun/src/e_sqrt.c \
+    upstream-freebsd/lib/msun/src/e_sqrtf.c \
+    upstream-freebsd/lib/msun/src/s_atan.c \
+    upstream-freebsd/lib/msun/src/s_cbrt.c \
+    upstream-freebsd/lib/msun/src/s_cos.c \
+    upstream-freebsd/lib/msun/src/s_expm1.c \
+    upstream-freebsd/lib/msun/src/s_log1p.c \
+    upstream-freebsd/lib/msun/src/s_sin.c \
+    upstream-freebsd/lib/msun/src/s_tan.c \
+    upstream-freebsd/lib/msun/src/s_tanh.c \
+
 ifeq ($(ARCH_X86_HAVE_SSE4_1),true)
 LOCAL_SRC_FILES_x86 += \
     x86/ceil.S \
@@ -409,8 +391,7 @@
     x86/trunc.S \
     x86/truncf.S \
 
-else
-LOCAL_SRC_FILES_x86 += \
+LOCAL_SRC_FILES_EXCLUDE_x86 += \
     upstream-freebsd/lib/msun/src/s_ceil.c \
     upstream-freebsd/lib/msun/src/s_ceilf.c \
     upstream-freebsd/lib/msun/src/s_floor.c \
@@ -425,14 +406,6 @@
 # -----------------------------------------------------------------------------
 LOCAL_SRC_FILES_x86_64 += \
     amd64/fenv.c \
-    upstream-freebsd/lib/msun/src/s_fma.c \
-    upstream-freebsd/lib/msun/src/s_fmaf.c \
-    upstream-freebsd/lib/msun/src/s_llrint.c \
-    upstream-freebsd/lib/msun/src/s_llrintf.c \
-    upstream-freebsd/lib/msun/src/s_lrint.c \
-    upstream-freebsd/lib/msun/src/s_lrintf.c \
-    upstream-freebsd/lib/msun/src/s_rint.c \
-    upstream-freebsd/lib/msun/src/s_rintf.c \
     x86_64/sqrt.S \
     x86_64/sqrtf.S \
     x86_64/e_acos.S \
@@ -454,6 +427,28 @@
     x86_64/s_tanh.S \
     x86_64/s_tan.S \
 
+LOCAL_SRC_FILES_EXCLUDE_x86_64 += \
+    upstream-freebsd/lib/msun/src/e_acos.c \
+    upstream-freebsd/lib/msun/src/e_asin.c \
+    upstream-freebsd/lib/msun/src/e_atan2.c \
+    upstream-freebsd/lib/msun/src/e_cosh.c \
+    upstream-freebsd/lib/msun/src/e_exp.c \
+    upstream-freebsd/lib/msun/src/e_hypot.c \
+    upstream-freebsd/lib/msun/src/e_log.c \
+    upstream-freebsd/lib/msun/src/e_log10.c \
+    upstream-freebsd/lib/msun/src/e_pow.c \
+    upstream-freebsd/lib/msun/src/e_sinh.c \
+    upstream-freebsd/lib/msun/src/e_sqrt.c \
+    upstream-freebsd/lib/msun/src/e_sqrtf.c \
+    upstream-freebsd/lib/msun/src/s_atan.c \
+    upstream-freebsd/lib/msun/src/s_cbrt.c \
+    upstream-freebsd/lib/msun/src/s_cos.c \
+    upstream-freebsd/lib/msun/src/s_expm1.c \
+    upstream-freebsd/lib/msun/src/s_log1p.c \
+    upstream-freebsd/lib/msun/src/s_sin.c \
+    upstream-freebsd/lib/msun/src/s_tan.c \
+    upstream-freebsd/lib/msun/src/s_tanh.c \
+
 ifeq ($(ARCH_X86_HAVE_SSE4_1),true)
 LOCAL_SRC_FILES_x86_64 += \
     x86_64/ceil.S \
@@ -463,8 +458,7 @@
     x86_64/trunc.S \
     x86_64/truncf.S \
 
-else
-LOCAL_SRC_FILES_x86_64 += \
+LOCAL_SRC_FILES_EXCLUDE_x86_64 += \
     upstream-freebsd/lib/msun/src/s_ceil.c \
     upstream-freebsd/lib/msun/src/s_ceilf.c \
     upstream-freebsd/lib/msun/src/s_floor.c \
@@ -485,6 +479,7 @@
     -D__BIONIC_NO_MATH_INLINES \
     -DFLT_EVAL_METHOD=0 \
     -include $(LOCAL_PATH)/freebsd-compat.h \
+    -Werror \
     -Wno-missing-braces \
     -Wno-parentheses \
     -Wno-sign-compare \
@@ -512,13 +507,25 @@
 # -----------------------------------------------------------------------------
 include $(CLEAR_VARS)
 
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/libm.map
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+    $(LOCAL_PATH)/libm.arm.map \
+    $(LOCAL_PATH)/libm.arm64.map \
+    $(LOCAL_PATH)/libm.mips.map \
+    $(LOCAL_PATH)/libm.mips64.map \
+    $(LOCAL_PATH)/libm.x86.map \
+    $(LOCAL_PATH)/libm.x86_64.map \
 
 # TODO: This is to work around b/24465209. Remove after root cause is fixed
 LOCAL_LDFLAGS_arm := -Wl,--hash-style=both
 LOCAL_LDFLAGS_x86 := -Wl,--hash-style=both
 
-LOCAL_LDFLAGS := -Wl,--version-script,$(LOCAL_PATH)/libm.map
+LOCAL_LDFLAGS_arm    += -Wl,--version-script,$(LOCAL_PATH)/libm.arm.map
+LOCAL_LDFLAGS_arm64  += -Wl,--version-script,$(LOCAL_PATH)/libm.arm64.map
+LOCAL_LDFLAGS_mips   += -Wl,--version-script,$(LOCAL_PATH)/libm.mips.map
+LOCAL_LDFLAGS_mips64 += -Wl,--version-script,$(LOCAL_PATH)/libm.mips64.map
+LOCAL_LDFLAGS_x86    += -Wl,--version-script,$(LOCAL_PATH)/libm.x86.map
+LOCAL_LDFLAGS_x86_64 += -Wl,--version-script,$(LOCAL_PATH)/libm.x86_64.map
+
 
 LOCAL_MODULE := libm
 LOCAL_CLANG := $(libm_clang)
diff --git a/libm/arm64/fenv.c b/libm/arm64/fenv.c
index ce560a7..19a2393 100644
--- a/libm/arm64/fenv.c
+++ b/libm/arm64/fenv.c
@@ -26,6 +26,7 @@
  * $FreeBSD: libm/aarch64/fenv.c $
  */
 
+#include <stdint.h>
 #include <fenv.h>
 
 #define FPCR_EXCEPT_SHIFT 8
@@ -38,10 +39,20 @@
 typedef __uint32_t fpu_control_t;   // FPCR, Floating-point Control Register.
 typedef __uint32_t fpu_status_t;    // FPSR, Floating-point Status Register.
 
-#define __get_fpcr(__fpcr) __asm__ __volatile__("mrs %0,fpcr" : "=r" (__fpcr))
-#define __get_fpsr(__fpsr) __asm__ __volatile__("mrs %0,fpsr" : "=r" (__fpsr))
-#define __set_fpcr(__fpcr) __asm__ __volatile__("msr fpcr,%0" : :"ri" (__fpcr))
-#define __set_fpsr(__fpsr) __asm__ __volatile__("msr fpsr,%0" : :"ri" (__fpsr))
+#define __get(REGISTER, __value) { \
+  uint64_t __value64; \
+  __asm__ __volatile__("mrs %0," REGISTER : "=r" (__value64)); \
+  __value = (__uint32_t) __value64; \
+}
+#define __get_fpcr(__fpcr) __get("fpcr", __fpcr)
+#define __get_fpsr(__fpsr) __get("fpsr", __fpsr)
+
+#define __set(REGISTER, __value) { \
+  uint64_t __value64 = __value; \
+  __asm__ __volatile__("msr " REGISTER ",%0" : : "ri" (__value64)); \
+}
+#define __set_fpcr(__fpcr) __set("fpcr", __fpcr)
+#define __set_fpsr(__fpsr) __set("fpsr", __fpsr)
 
 int fegetenv(fenv_t* envp) {
   __get_fpcr(envp->__control);
diff --git a/libm/libm.map b/libm/libm.arm.map
similarity index 97%
copy from libm/libm.map
copy to libm/libm.arm.map
index 7f02f42..e781f2d 100644
--- a/libm/libm.map
+++ b/libm/libm.arm.map
@@ -1,3 +1,4 @@
+# Generated by genversionscripts.py. Do not edit.
 LIBC {
   global:
     __fe_dfl_env;
@@ -271,8 +272,8 @@
     *;
 };
 
-LIBC_PRIVATE {
-  global:
+LIBC_PRIVATE { # arm mips
+  global: # arm mips
     ___Unwind_Backtrace; # arm
     ___Unwind_ForcedUnwind; # arm
     ___Unwind_RaiseException; # arm
@@ -353,7 +354,6 @@
     __lesf2; # arm
     __ltdf2; # arm
     __ltsf2; # arm
-    __muldc3; # arm x86 mips
     __muldf3; # arm
     __nedf2; # arm
     __nesf2; # arm
@@ -375,4 +375,4 @@
     _Unwind_VRS_Pop; # arm
     _Unwind_VRS_Set; # arm
     restore_core_regs; # arm
-} LIBC;
+} LIBC; # arm mips
diff --git a/libm/libm.arm64.map b/libm/libm.arm64.map
new file mode 100644
index 0000000..1623ea0
--- /dev/null
+++ b/libm/libm.arm64.map
@@ -0,0 +1,274 @@
+# Generated by genversionscripts.py. Do not edit.
+LIBC {
+  global:
+    __fe_dfl_env;
+    __signbit;
+    __signbitf;
+    __signbitl;
+    acos;
+    acosf;
+    acosh;
+    acoshf;
+    acoshl;
+    acosl;
+    asin;
+    asinf;
+    asinh;
+    asinhf;
+    asinhl;
+    asinl;
+    atan;
+    atan2;
+    atan2f;
+    atan2l;
+    atanf;
+    atanh;
+    atanhf;
+    atanhl;
+    atanl;
+    cabs;
+    cabsf;
+    cabsl;
+    cacos;
+    cacosf;
+    cacosh;
+    cacoshf;
+    carg;
+    cargf;
+    cargl;
+    casin;
+    casinf;
+    casinh;
+    casinhf;
+    catan;
+    catanf;
+    catanh;
+    catanhf;
+    cbrt;
+    cbrtf;
+    cbrtl;
+    ccos;
+    ccosf;
+    ccosh;
+    ccoshf;
+    ceil;
+    ceilf;
+    ceill;
+    cexp;
+    cexpf;
+    cimag;
+    cimagf;
+    cimagl;
+    conj;
+    conjf;
+    conjl;
+    copysign;
+    copysignf;
+    copysignl;
+    cos;
+    cosf;
+    cosh;
+    coshf;
+    coshl;
+    cosl;
+    cproj;
+    cprojf;
+    cprojl;
+    creal;
+    crealf;
+    creall;
+    csin;
+    csinf;
+    csinh;
+    csinhf;
+    csqrt;
+    csqrtf;
+    csqrtl;
+    ctan;
+    ctanf;
+    ctanh;
+    ctanhf;
+    drem;
+    dremf;
+    erf;
+    erfc;
+    erfcf;
+    erfcl;
+    erff;
+    erfl;
+    exp;
+    exp2;
+    exp2f;
+    exp2l;
+    expf;
+    expl;
+    expm1;
+    expm1f;
+    expm1l;
+    fabs;
+    fabsf;
+    fabsl;
+    fdim;
+    fdimf;
+    fdiml;
+    feclearexcept;
+    fedisableexcept;
+    feenableexcept;
+    fegetenv;
+    fegetexcept;
+    fegetexceptflag;
+    fegetround;
+    feholdexcept;
+    feraiseexcept;
+    fesetenv;
+    fesetexceptflag;
+    fesetround;
+    fetestexcept;
+    feupdateenv;
+    finite;
+    finitef;
+    floor;
+    floorf;
+    floorl;
+    fma;
+    fmaf;
+    fmal;
+    fmax;
+    fmaxf;
+    fmaxl;
+    fmin;
+    fminf;
+    fminl;
+    fmod;
+    fmodf;
+    fmodl;
+    frexp;
+    frexpf;
+    frexpl;
+    gamma;
+    gamma_r;
+    gammaf;
+    gammaf_r;
+    hypot;
+    hypotf;
+    hypotl;
+    ilogb;
+    ilogbf;
+    ilogbl;
+    j0;
+    j0f;
+    j1;
+    j1f;
+    jn;
+    jnf;
+    ldexpf;
+    ldexpl;
+    lgamma;
+    lgamma_r;
+    lgammaf;
+    lgammaf_r;
+    lgammal;
+    lgammal_r;
+    llrint;
+    llrintf;
+    llrintl;
+    llround;
+    llroundf;
+    llroundl;
+    log;
+    log10;
+    log10f;
+    log10l;
+    log1p;
+    log1pf;
+    log1pl;
+    log2;
+    log2f;
+    log2l;
+    logb;
+    logbf;
+    logbl;
+    logf;
+    logl;
+    lrint;
+    lrintf;
+    lrintl;
+    lround;
+    lroundf;
+    lroundl;
+    modf;
+    modff;
+    modfl;
+    nan;
+    nanf;
+    nanl;
+    nearbyint;
+    nearbyintf;
+    nearbyintl;
+    nextafter;
+    nextafterf;
+    nextafterl;
+    nexttoward;
+    nexttowardf;
+    nexttowardl;
+    pow;
+    powf;
+    powl;
+    remainder;
+    remainderf;
+    remainderl;
+    remquo;
+    remquof;
+    remquol;
+    rint;
+    rintf;
+    rintl;
+    round;
+    roundf;
+    roundl;
+    scalb;
+    scalbf;
+    scalbln;
+    scalblnf;
+    scalblnl;
+    scalbn;
+    scalbnf;
+    scalbnl;
+    signgam;
+    significand;
+    significandf;
+    significandl;
+    sin;
+    sincos;
+    sincosf;
+    sincosl;
+    sinf;
+    sinh;
+    sinhf;
+    sinhl;
+    sinl;
+    sqrt;
+    sqrtf;
+    sqrtl;
+    tan;
+    tanf;
+    tanh;
+    tanhf;
+    tanhl;
+    tanl;
+    tgamma;
+    tgammaf;
+    tgammal;
+    trunc;
+    truncf;
+    truncl;
+    y0;
+    y0f;
+    y1;
+    y1f;
+    yn;
+    ynf;
+  local:
+    *;
+};
+
diff --git a/libm/libm.map b/libm/libm.map.txt
similarity index 98%
rename from libm/libm.map
rename to libm/libm.map.txt
index 7f02f42..075ebd5 100644
--- a/libm/libm.map
+++ b/libm/libm.map.txt
@@ -271,8 +271,8 @@
     *;
 };
 
-LIBC_PRIVATE {
-  global:
+LIBC_PRIVATE { # arm mips
+  global: # arm mips
     ___Unwind_Backtrace; # arm
     ___Unwind_ForcedUnwind; # arm
     ___Unwind_RaiseException; # arm
@@ -353,7 +353,6 @@
     __lesf2; # arm
     __ltdf2; # arm
     __ltsf2; # arm
-    __muldc3; # arm x86 mips
     __muldf3; # arm
     __nedf2; # arm
     __nesf2; # arm
@@ -375,4 +374,4 @@
     _Unwind_VRS_Pop; # arm
     _Unwind_VRS_Set; # arm
     restore_core_regs; # arm
-} LIBC;
+} LIBC; # arm mips
diff --git a/libm/libm.mips.map b/libm/libm.mips.map
new file mode 100644
index 0000000..476c6ad
--- /dev/null
+++ b/libm/libm.mips.map
@@ -0,0 +1,281 @@
+# Generated by genversionscripts.py. Do not edit.
+LIBC {
+  global:
+    __fe_dfl_env;
+    __signbit;
+    __signbitf;
+    __signbitl;
+    acos;
+    acosf;
+    acosh;
+    acoshf;
+    acoshl;
+    acosl;
+    asin;
+    asinf;
+    asinh;
+    asinhf;
+    asinhl;
+    asinl;
+    atan;
+    atan2;
+    atan2f;
+    atan2l;
+    atanf;
+    atanh;
+    atanhf;
+    atanhl;
+    atanl;
+    cabs;
+    cabsf;
+    cabsl;
+    cacos;
+    cacosf;
+    cacosh;
+    cacoshf;
+    carg;
+    cargf;
+    cargl;
+    casin;
+    casinf;
+    casinh;
+    casinhf;
+    catan;
+    catanf;
+    catanh;
+    catanhf;
+    cbrt;
+    cbrtf;
+    cbrtl;
+    ccos;
+    ccosf;
+    ccosh;
+    ccoshf;
+    ceil;
+    ceilf;
+    ceill;
+    cexp;
+    cexpf;
+    cimag;
+    cimagf;
+    cimagl;
+    conj;
+    conjf;
+    conjl;
+    copysign;
+    copysignf;
+    copysignl;
+    cos;
+    cosf;
+    cosh;
+    coshf;
+    coshl;
+    cosl;
+    cproj;
+    cprojf;
+    cprojl;
+    creal;
+    crealf;
+    creall;
+    csin;
+    csinf;
+    csinh;
+    csinhf;
+    csqrt;
+    csqrtf;
+    csqrtl;
+    ctan;
+    ctanf;
+    ctanh;
+    ctanhf;
+    drem;
+    dremf;
+    erf;
+    erfc;
+    erfcf;
+    erfcl;
+    erff;
+    erfl;
+    exp;
+    exp2;
+    exp2f;
+    exp2l;
+    expf;
+    expl;
+    expm1;
+    expm1f;
+    expm1l;
+    fabs;
+    fabsf;
+    fabsl;
+    fdim;
+    fdimf;
+    fdiml;
+    feclearexcept;
+    fedisableexcept;
+    feenableexcept;
+    fegetenv;
+    fegetexcept;
+    fegetexceptflag;
+    fegetround;
+    feholdexcept;
+    feraiseexcept;
+    fesetenv;
+    fesetexceptflag;
+    fesetround;
+    fetestexcept;
+    feupdateenv;
+    finite;
+    finitef;
+    floor;
+    floorf;
+    floorl;
+    fma;
+    fmaf;
+    fmal;
+    fmax;
+    fmaxf;
+    fmaxl;
+    fmin;
+    fminf;
+    fminl;
+    fmod;
+    fmodf;
+    fmodl;
+    frexp;
+    frexpf;
+    frexpl;
+    gamma;
+    gamma_r;
+    gammaf;
+    gammaf_r;
+    hypot;
+    hypotf;
+    hypotl;
+    ilogb;
+    ilogbf;
+    ilogbl;
+    j0;
+    j0f;
+    j1;
+    j1f;
+    jn;
+    jnf;
+    ldexpf;
+    ldexpl;
+    lgamma;
+    lgamma_r;
+    lgammaf;
+    lgammaf_r;
+    lgammal;
+    lgammal_r;
+    llrint;
+    llrintf;
+    llrintl;
+    llround;
+    llroundf;
+    llroundl;
+    log;
+    log10;
+    log10f;
+    log10l;
+    log1p;
+    log1pf;
+    log1pl;
+    log2;
+    log2f;
+    log2l;
+    logb;
+    logbf;
+    logbl;
+    logf;
+    logl;
+    lrint;
+    lrintf;
+    lrintl;
+    lround;
+    lroundf;
+    lroundl;
+    modf;
+    modff;
+    modfl;
+    nan;
+    nanf;
+    nanl;
+    nearbyint;
+    nearbyintf;
+    nearbyintl;
+    nextafter;
+    nextafterf;
+    nextafterl;
+    nexttoward;
+    nexttowardf;
+    nexttowardl;
+    pow;
+    powf;
+    powl;
+    remainder;
+    remainderf;
+    remainderl;
+    remquo;
+    remquof;
+    remquol;
+    rint;
+    rintf;
+    rintl;
+    round;
+    roundf;
+    roundl;
+    scalb;
+    scalbf;
+    scalbln;
+    scalblnf;
+    scalblnl;
+    scalbn;
+    scalbnf;
+    scalbnl;
+    signgam;
+    significand;
+    significandf;
+    significandl;
+    sin;
+    sincos;
+    sincosf;
+    sincosl;
+    sinf;
+    sinh;
+    sinhf;
+    sinhl;
+    sinl;
+    sqrt;
+    sqrtf;
+    sqrtl;
+    tan;
+    tanf;
+    tanh;
+    tanhf;
+    tanhl;
+    tanl;
+    tgamma;
+    tgammaf;
+    tgammal;
+    trunc;
+    truncf;
+    truncl;
+    y0;
+    y0f;
+    y1;
+    y1f;
+    yn;
+    ynf;
+  local:
+    *;
+};
+
+LIBC_PRIVATE { # arm mips
+  global: # arm mips
+    __fixdfdi; # arm mips
+    __fixsfdi; # arm mips
+    __fixunsdfdi; # arm mips
+    __fixunssfdi; # arm mips
+} LIBC; # arm mips
diff --git a/libm/libm.mips64.map b/libm/libm.mips64.map
new file mode 100644
index 0000000..1623ea0
--- /dev/null
+++ b/libm/libm.mips64.map
@@ -0,0 +1,274 @@
+# Generated by genversionscripts.py. Do not edit.
+LIBC {
+  global:
+    __fe_dfl_env;
+    __signbit;
+    __signbitf;
+    __signbitl;
+    acos;
+    acosf;
+    acosh;
+    acoshf;
+    acoshl;
+    acosl;
+    asin;
+    asinf;
+    asinh;
+    asinhf;
+    asinhl;
+    asinl;
+    atan;
+    atan2;
+    atan2f;
+    atan2l;
+    atanf;
+    atanh;
+    atanhf;
+    atanhl;
+    atanl;
+    cabs;
+    cabsf;
+    cabsl;
+    cacos;
+    cacosf;
+    cacosh;
+    cacoshf;
+    carg;
+    cargf;
+    cargl;
+    casin;
+    casinf;
+    casinh;
+    casinhf;
+    catan;
+    catanf;
+    catanh;
+    catanhf;
+    cbrt;
+    cbrtf;
+    cbrtl;
+    ccos;
+    ccosf;
+    ccosh;
+    ccoshf;
+    ceil;
+    ceilf;
+    ceill;
+    cexp;
+    cexpf;
+    cimag;
+    cimagf;
+    cimagl;
+    conj;
+    conjf;
+    conjl;
+    copysign;
+    copysignf;
+    copysignl;
+    cos;
+    cosf;
+    cosh;
+    coshf;
+    coshl;
+    cosl;
+    cproj;
+    cprojf;
+    cprojl;
+    creal;
+    crealf;
+    creall;
+    csin;
+    csinf;
+    csinh;
+    csinhf;
+    csqrt;
+    csqrtf;
+    csqrtl;
+    ctan;
+    ctanf;
+    ctanh;
+    ctanhf;
+    drem;
+    dremf;
+    erf;
+    erfc;
+    erfcf;
+    erfcl;
+    erff;
+    erfl;
+    exp;
+    exp2;
+    exp2f;
+    exp2l;
+    expf;
+    expl;
+    expm1;
+    expm1f;
+    expm1l;
+    fabs;
+    fabsf;
+    fabsl;
+    fdim;
+    fdimf;
+    fdiml;
+    feclearexcept;
+    fedisableexcept;
+    feenableexcept;
+    fegetenv;
+    fegetexcept;
+    fegetexceptflag;
+    fegetround;
+    feholdexcept;
+    feraiseexcept;
+    fesetenv;
+    fesetexceptflag;
+    fesetround;
+    fetestexcept;
+    feupdateenv;
+    finite;
+    finitef;
+    floor;
+    floorf;
+    floorl;
+    fma;
+    fmaf;
+    fmal;
+    fmax;
+    fmaxf;
+    fmaxl;
+    fmin;
+    fminf;
+    fminl;
+    fmod;
+    fmodf;
+    fmodl;
+    frexp;
+    frexpf;
+    frexpl;
+    gamma;
+    gamma_r;
+    gammaf;
+    gammaf_r;
+    hypot;
+    hypotf;
+    hypotl;
+    ilogb;
+    ilogbf;
+    ilogbl;
+    j0;
+    j0f;
+    j1;
+    j1f;
+    jn;
+    jnf;
+    ldexpf;
+    ldexpl;
+    lgamma;
+    lgamma_r;
+    lgammaf;
+    lgammaf_r;
+    lgammal;
+    lgammal_r;
+    llrint;
+    llrintf;
+    llrintl;
+    llround;
+    llroundf;
+    llroundl;
+    log;
+    log10;
+    log10f;
+    log10l;
+    log1p;
+    log1pf;
+    log1pl;
+    log2;
+    log2f;
+    log2l;
+    logb;
+    logbf;
+    logbl;
+    logf;
+    logl;
+    lrint;
+    lrintf;
+    lrintl;
+    lround;
+    lroundf;
+    lroundl;
+    modf;
+    modff;
+    modfl;
+    nan;
+    nanf;
+    nanl;
+    nearbyint;
+    nearbyintf;
+    nearbyintl;
+    nextafter;
+    nextafterf;
+    nextafterl;
+    nexttoward;
+    nexttowardf;
+    nexttowardl;
+    pow;
+    powf;
+    powl;
+    remainder;
+    remainderf;
+    remainderl;
+    remquo;
+    remquof;
+    remquol;
+    rint;
+    rintf;
+    rintl;
+    round;
+    roundf;
+    roundl;
+    scalb;
+    scalbf;
+    scalbln;
+    scalblnf;
+    scalblnl;
+    scalbn;
+    scalbnf;
+    scalbnl;
+    signgam;
+    significand;
+    significandf;
+    significandl;
+    sin;
+    sincos;
+    sincosf;
+    sincosl;
+    sinf;
+    sinh;
+    sinhf;
+    sinhl;
+    sinl;
+    sqrt;
+    sqrtf;
+    sqrtl;
+    tan;
+    tanf;
+    tanh;
+    tanhf;
+    tanhl;
+    tanl;
+    tgamma;
+    tgammaf;
+    tgammal;
+    trunc;
+    truncf;
+    truncl;
+    y0;
+    y0f;
+    y1;
+    y1f;
+    yn;
+    ynf;
+  local:
+    *;
+};
+
diff --git a/libm/libm.x86.map b/libm/libm.x86.map
new file mode 100644
index 0000000..1623ea0
--- /dev/null
+++ b/libm/libm.x86.map
@@ -0,0 +1,274 @@
+# Generated by genversionscripts.py. Do not edit.
+LIBC {
+  global:
+    __fe_dfl_env;
+    __signbit;
+    __signbitf;
+    __signbitl;
+    acos;
+    acosf;
+    acosh;
+    acoshf;
+    acoshl;
+    acosl;
+    asin;
+    asinf;
+    asinh;
+    asinhf;
+    asinhl;
+    asinl;
+    atan;
+    atan2;
+    atan2f;
+    atan2l;
+    atanf;
+    atanh;
+    atanhf;
+    atanhl;
+    atanl;
+    cabs;
+    cabsf;
+    cabsl;
+    cacos;
+    cacosf;
+    cacosh;
+    cacoshf;
+    carg;
+    cargf;
+    cargl;
+    casin;
+    casinf;
+    casinh;
+    casinhf;
+    catan;
+    catanf;
+    catanh;
+    catanhf;
+    cbrt;
+    cbrtf;
+    cbrtl;
+    ccos;
+    ccosf;
+    ccosh;
+    ccoshf;
+    ceil;
+    ceilf;
+    ceill;
+    cexp;
+    cexpf;
+    cimag;
+    cimagf;
+    cimagl;
+    conj;
+    conjf;
+    conjl;
+    copysign;
+    copysignf;
+    copysignl;
+    cos;
+    cosf;
+    cosh;
+    coshf;
+    coshl;
+    cosl;
+    cproj;
+    cprojf;
+    cprojl;
+    creal;
+    crealf;
+    creall;
+    csin;
+    csinf;
+    csinh;
+    csinhf;
+    csqrt;
+    csqrtf;
+    csqrtl;
+    ctan;
+    ctanf;
+    ctanh;
+    ctanhf;
+    drem;
+    dremf;
+    erf;
+    erfc;
+    erfcf;
+    erfcl;
+    erff;
+    erfl;
+    exp;
+    exp2;
+    exp2f;
+    exp2l;
+    expf;
+    expl;
+    expm1;
+    expm1f;
+    expm1l;
+    fabs;
+    fabsf;
+    fabsl;
+    fdim;
+    fdimf;
+    fdiml;
+    feclearexcept;
+    fedisableexcept;
+    feenableexcept;
+    fegetenv;
+    fegetexcept;
+    fegetexceptflag;
+    fegetround;
+    feholdexcept;
+    feraiseexcept;
+    fesetenv;
+    fesetexceptflag;
+    fesetround;
+    fetestexcept;
+    feupdateenv;
+    finite;
+    finitef;
+    floor;
+    floorf;
+    floorl;
+    fma;
+    fmaf;
+    fmal;
+    fmax;
+    fmaxf;
+    fmaxl;
+    fmin;
+    fminf;
+    fminl;
+    fmod;
+    fmodf;
+    fmodl;
+    frexp;
+    frexpf;
+    frexpl;
+    gamma;
+    gamma_r;
+    gammaf;
+    gammaf_r;
+    hypot;
+    hypotf;
+    hypotl;
+    ilogb;
+    ilogbf;
+    ilogbl;
+    j0;
+    j0f;
+    j1;
+    j1f;
+    jn;
+    jnf;
+    ldexpf;
+    ldexpl;
+    lgamma;
+    lgamma_r;
+    lgammaf;
+    lgammaf_r;
+    lgammal;
+    lgammal_r;
+    llrint;
+    llrintf;
+    llrintl;
+    llround;
+    llroundf;
+    llroundl;
+    log;
+    log10;
+    log10f;
+    log10l;
+    log1p;
+    log1pf;
+    log1pl;
+    log2;
+    log2f;
+    log2l;
+    logb;
+    logbf;
+    logbl;
+    logf;
+    logl;
+    lrint;
+    lrintf;
+    lrintl;
+    lround;
+    lroundf;
+    lroundl;
+    modf;
+    modff;
+    modfl;
+    nan;
+    nanf;
+    nanl;
+    nearbyint;
+    nearbyintf;
+    nearbyintl;
+    nextafter;
+    nextafterf;
+    nextafterl;
+    nexttoward;
+    nexttowardf;
+    nexttowardl;
+    pow;
+    powf;
+    powl;
+    remainder;
+    remainderf;
+    remainderl;
+    remquo;
+    remquof;
+    remquol;
+    rint;
+    rintf;
+    rintl;
+    round;
+    roundf;
+    roundl;
+    scalb;
+    scalbf;
+    scalbln;
+    scalblnf;
+    scalblnl;
+    scalbn;
+    scalbnf;
+    scalbnl;
+    signgam;
+    significand;
+    significandf;
+    significandl;
+    sin;
+    sincos;
+    sincosf;
+    sincosl;
+    sinf;
+    sinh;
+    sinhf;
+    sinhl;
+    sinl;
+    sqrt;
+    sqrtf;
+    sqrtl;
+    tan;
+    tanf;
+    tanh;
+    tanhf;
+    tanhl;
+    tanl;
+    tgamma;
+    tgammaf;
+    tgammal;
+    trunc;
+    truncf;
+    truncl;
+    y0;
+    y0f;
+    y1;
+    y1f;
+    yn;
+    ynf;
+  local:
+    *;
+};
+
diff --git a/libm/libm.x86_64.map b/libm/libm.x86_64.map
new file mode 100644
index 0000000..1623ea0
--- /dev/null
+++ b/libm/libm.x86_64.map
@@ -0,0 +1,274 @@
+# Generated by genversionscripts.py. Do not edit.
+LIBC {
+  global:
+    __fe_dfl_env;
+    __signbit;
+    __signbitf;
+    __signbitl;
+    acos;
+    acosf;
+    acosh;
+    acoshf;
+    acoshl;
+    acosl;
+    asin;
+    asinf;
+    asinh;
+    asinhf;
+    asinhl;
+    asinl;
+    atan;
+    atan2;
+    atan2f;
+    atan2l;
+    atanf;
+    atanh;
+    atanhf;
+    atanhl;
+    atanl;
+    cabs;
+    cabsf;
+    cabsl;
+    cacos;
+    cacosf;
+    cacosh;
+    cacoshf;
+    carg;
+    cargf;
+    cargl;
+    casin;
+    casinf;
+    casinh;
+    casinhf;
+    catan;
+    catanf;
+    catanh;
+    catanhf;
+    cbrt;
+    cbrtf;
+    cbrtl;
+    ccos;
+    ccosf;
+    ccosh;
+    ccoshf;
+    ceil;
+    ceilf;
+    ceill;
+    cexp;
+    cexpf;
+    cimag;
+    cimagf;
+    cimagl;
+    conj;
+    conjf;
+    conjl;
+    copysign;
+    copysignf;
+    copysignl;
+    cos;
+    cosf;
+    cosh;
+    coshf;
+    coshl;
+    cosl;
+    cproj;
+    cprojf;
+    cprojl;
+    creal;
+    crealf;
+    creall;
+    csin;
+    csinf;
+    csinh;
+    csinhf;
+    csqrt;
+    csqrtf;
+    csqrtl;
+    ctan;
+    ctanf;
+    ctanh;
+    ctanhf;
+    drem;
+    dremf;
+    erf;
+    erfc;
+    erfcf;
+    erfcl;
+    erff;
+    erfl;
+    exp;
+    exp2;
+    exp2f;
+    exp2l;
+    expf;
+    expl;
+    expm1;
+    expm1f;
+    expm1l;
+    fabs;
+    fabsf;
+    fabsl;
+    fdim;
+    fdimf;
+    fdiml;
+    feclearexcept;
+    fedisableexcept;
+    feenableexcept;
+    fegetenv;
+    fegetexcept;
+    fegetexceptflag;
+    fegetround;
+    feholdexcept;
+    feraiseexcept;
+    fesetenv;
+    fesetexceptflag;
+    fesetround;
+    fetestexcept;
+    feupdateenv;
+    finite;
+    finitef;
+    floor;
+    floorf;
+    floorl;
+    fma;
+    fmaf;
+    fmal;
+    fmax;
+    fmaxf;
+    fmaxl;
+    fmin;
+    fminf;
+    fminl;
+    fmod;
+    fmodf;
+    fmodl;
+    frexp;
+    frexpf;
+    frexpl;
+    gamma;
+    gamma_r;
+    gammaf;
+    gammaf_r;
+    hypot;
+    hypotf;
+    hypotl;
+    ilogb;
+    ilogbf;
+    ilogbl;
+    j0;
+    j0f;
+    j1;
+    j1f;
+    jn;
+    jnf;
+    ldexpf;
+    ldexpl;
+    lgamma;
+    lgamma_r;
+    lgammaf;
+    lgammaf_r;
+    lgammal;
+    lgammal_r;
+    llrint;
+    llrintf;
+    llrintl;
+    llround;
+    llroundf;
+    llroundl;
+    log;
+    log10;
+    log10f;
+    log10l;
+    log1p;
+    log1pf;
+    log1pl;
+    log2;
+    log2f;
+    log2l;
+    logb;
+    logbf;
+    logbl;
+    logf;
+    logl;
+    lrint;
+    lrintf;
+    lrintl;
+    lround;
+    lroundf;
+    lroundl;
+    modf;
+    modff;
+    modfl;
+    nan;
+    nanf;
+    nanl;
+    nearbyint;
+    nearbyintf;
+    nearbyintl;
+    nextafter;
+    nextafterf;
+    nextafterl;
+    nexttoward;
+    nexttowardf;
+    nexttowardl;
+    pow;
+    powf;
+    powl;
+    remainder;
+    remainderf;
+    remainderl;
+    remquo;
+    remquof;
+    remquol;
+    rint;
+    rintf;
+    rintl;
+    round;
+    roundf;
+    roundl;
+    scalb;
+    scalbf;
+    scalbln;
+    scalblnf;
+    scalblnl;
+    scalbn;
+    scalbnf;
+    scalbnl;
+    signgam;
+    significand;
+    significandf;
+    significandl;
+    sin;
+    sincos;
+    sincosf;
+    sincosl;
+    sinf;
+    sinh;
+    sinhf;
+    sinhl;
+    sinl;
+    sqrt;
+    sqrtf;
+    sqrtl;
+    tan;
+    tanf;
+    tanh;
+    tanhf;
+    tanhl;
+    tanl;
+    tgamma;
+    tgammaf;
+    tgammal;
+    trunc;
+    truncf;
+    truncl;
+    y0;
+    y0f;
+    y1;
+    y1f;
+    yn;
+    ynf;
+  local:
+    *;
+};
+
diff --git a/linker/Android.mk b/linker/Android.mk
index 8be53a1..85ac0ca 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -9,11 +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 f1b26c9..d07ec86 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -89,9 +89,12 @@
   return dlopen_ext(filename, flags, nullptr, caller_addr);
 }
 
+extern android_namespace_t* g_anonymous_namespace;
+
 void* dlsym(void* handle, const char* symbol) {
   ScopedPthreadMutexLocker locker(&g_dl_mutex);
 
+  // TODO(dimitry): move (most of) the code below to linker.cpp
 #if !defined(__LP64__)
   if (handle == nullptr) {
     __bionic_format_dlerror("dlsym library handle is null", nullptr);
@@ -108,9 +111,10 @@
   const ElfW(Sym)* sym = nullptr;
   void* caller_addr = __builtin_return_address(0);
   soinfo* caller = find_containing_library(caller_addr);
+  android_namespace_t* ns = caller != nullptr ? caller->get_namespace() : g_anonymous_namespace;
 
   if (handle == RTLD_DEFAULT || handle == RTLD_NEXT) {
-    sym = dlsym_linear_lookup(symbol, &found, caller, handle);
+    sym = dlsym_linear_lookup(ns, symbol, &found, caller, handle);
   } else {
     sym = dlsym_handle_lookup(reinterpret_cast<soinfo*>(handle), &found, symbol);
   }
@@ -177,6 +181,31 @@
   return get_application_target_sdk_version();
 }
 
+bool android_init_namespaces(const char* public_ns_sonames,
+                             const char* anon_ns_library_path) {
+  ScopedPthreadMutexLocker locker(&g_dl_mutex);
+  bool success = init_namespaces(public_ns_sonames, anon_ns_library_path);
+  if (!success) {
+    __bionic_format_dlerror("android_init_namespaces 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 +232,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 111122222222223333333333 4444444444555555555566666
+  // 0123456789012345 678901234567890123456789 0123456789012345678901234
+    "get_sdk_version\0android_init_namespaces\0android_create_namespace\0"
 #if defined(__arm__)
-  // 216
+  // 265
     "dl_unwind_find_exidx\0"
 #endif
     ;
@@ -229,8 +258,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_namespaces, 1),
+  ELFW(SYM_INITIALIZER)(240, &android_create_namespace, 1),
 #if defined(__arm__)
-  ELFW(SYM_INITIALIZER)(216, &dl_unwind_find_exidx, 1),
+  ELFW(SYM_INITIALIZER)(265, &dl_unwind_find_exidx, 1),
 #endif
 };
 
@@ -247,18 +278,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;
@@ -271,7 +304,7 @@
     __libdl_info->local_group_root_ = __libdl_info;
     __libdl_info->soname_ = "libdl.so";
     __libdl_info->target_sdk_version_ = __ANDROID_API__;
-#if defined(__arm__)
+#if defined(__work_around_b_24465209__)
     strlcpy(__libdl_info->old_name_, __libdl_info->soname_, sizeof(__libdl_info->old_name_));
 #endif
   }
diff --git a/linker/linked_list.h b/linker/linked_list.h
index 8003dbf..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();
@@ -127,7 +164,13 @@
         } else {
           p->next = next;
         }
+
+        if (tail_ == e) {
+          tail_ = p;
+        }
+
         Allocator::free(e);
+
         e = next;
       } else {
         p = e;
@@ -147,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 406cdee..028e490 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -49,7 +49,6 @@
 #include "private/KernelArgumentBlock.h"
 #include "private/ScopedPthreadMutexLocker.h"
 #include "private/ScopeGuard.h"
-#include "private/UniquePtr.h"
 
 #include "linker.h"
 #include "linker_block_allocator.h"
@@ -60,7 +59,7 @@
 #include "linker_reloc_iterators.h"
 #include "linker_utils.h"
 
-#include "base/strings.h"
+#include "android-base/strings.h"
 #include "ziparchive/zip_archive.h"
 
 extern void __libc_init_globals(KernelArgumentBlock&);
@@ -70,20 +69,67 @@
 #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;
+android_namespace_t* g_anonymous_namespace = &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
 
 static const char* const kDefaultLdPaths[] = {
 #if defined(__LP64__)
+  "/odm/lib64",
   "/vendor/lib64",
   "/system/lib64",
 #else
+  "/odm/lib",
   "/vendor/lib",
   "/system/lib",
 #endif
@@ -92,11 +138,15 @@
 
 static const char* const kAsanDefaultLdPaths[] = {
 #if defined(__LP64__)
+  "/data/odm/lib64",
+  "/odm/lib64",
   "/data/vendor/lib64",
   "/vendor/lib64",
   "/data/lib64",
   "/system/lib64",
 #else
+  "/data/odm/lib",
+  "/odm/lib",
   "/data/vendor/lib",
   "/vendor/lib",
   "/data/lib",
@@ -107,14 +157,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 +298,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 +326,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,30 +382,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)) return;
-  parse_path(path, ":", &dt_runpath_);
+  if (!has_min_version(3)) {
+    return;
+  }
+
+  std::vector<std::string> runpaths;
+
+  split_path(path, ":", &runpaths);
 
   std::string origin = dirname(get_realpath());
   // FIXME: add $LIB and $PLATFORM.
   std::pair<std::string, std::string> substs[] = {{"ORIGIN", origin}};
-  for (std::string& s : dt_runpath_) {
+  for (auto&& s : runpaths) {
     size_t pos = 0;
     while (pos < s.size()) {
       pos = s.find("$", pos);
@@ -353,11 +528,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) {
@@ -678,8 +858,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) {
@@ -696,6 +877,7 @@
   }
 
   this->rtld_flags_ = rtld_flags;
+  this->namespace_ = ns;
 }
 
 
@@ -847,6 +1029,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_;
@@ -889,17 +1072,17 @@
  public:
   struct deleter_t {
     void operator()(LoadTask* t) {
+      t->~LoadTask();
       TypeBasedAllocator<LoadTask>::free(t);
     }
   };
 
-  typedef UniquePtr<LoadTask, deleter_t> unique_ptr;
-
   static deleter_t deleter;
 
-  static LoadTask* create(const char* name, soinfo* needed_by) {
+  static LoadTask* create(const char* name, soinfo* needed_by,
+                          std::unordered_map<const soinfo*, ElfReader>* readers_map) {
     LoadTask* ptr = TypeBasedAllocator<LoadTask>::alloc();
-    return new (ptr) LoadTask(name, needed_by);
+    return new (ptr) LoadTask(name, needed_by, readers_map);
   }
 
   const char* get_name() const {
@@ -909,12 +1092,94 @@
   soinfo* get_needed_by() const {
     return needed_by_;
   }
+
+  soinfo* get_soinfo() const {
+    return si_;
+  }
+
+  void set_soinfo(soinfo* si) {
+    si_ = si;
+  }
+
+  off64_t get_file_offset() const {
+    return file_offset_;
+  }
+
+  void set_file_offset(off64_t offset) {
+    file_offset_ = offset;
+  }
+
+  int get_fd() const {
+    return fd_;
+  }
+
+  void set_fd(int fd, bool assume_ownership) {
+    fd_ = fd;
+    close_fd_ = assume_ownership;
+  }
+
+  const android_dlextinfo* get_extinfo() const {
+    return extinfo_;
+  }
+
+  void set_extinfo(const android_dlextinfo* extinfo) {
+    extinfo_ = extinfo;
+  }
+
+  const ElfReader& get_elf_reader() const {
+    CHECK(si_ != nullptr);
+    return (*elf_readers_map_)[si_];
+  }
+
+  ElfReader& get_elf_reader() {
+    CHECK(si_ != nullptr);
+    return (*elf_readers_map_)[si_];
+  }
+
+  std::unordered_map<const soinfo*, ElfReader>* get_readers_map() {
+    return elf_readers_map_;
+  }
+
+  bool read(const char* realpath, off64_t file_size) {
+    ElfReader& elf_reader = get_elf_reader();
+    return elf_reader.Read(realpath, fd_, file_offset_, file_size);
+  }
+
+  bool load() {
+    ElfReader& elf_reader = get_elf_reader();
+    if (!elf_reader.Load(extinfo_)) {
+      return false;
+    }
+
+    si_->base = elf_reader.load_start();
+    si_->size = elf_reader.load_size();
+    si_->load_bias = elf_reader.load_bias();
+    si_->phnum = elf_reader.phdr_count();
+    si_->phdr = elf_reader.loaded_phdr();
+
+    return true;
+  }
+
  private:
-  LoadTask(const char* name, soinfo* needed_by)
-    : name_(name), needed_by_(needed_by) {}
+  LoadTask(const char* name, soinfo* needed_by,
+           std::unordered_map<const soinfo*, ElfReader>* readers_map)
+    : name_(name), needed_by_(needed_by), si_(nullptr),
+      fd_(-1), close_fd_(false), file_offset_(0), elf_readers_map_(readers_map) {}
+
+  ~LoadTask() {
+    if (fd_ != -1 && close_fd_) {
+      close(fd_);
+    }
+  }
 
   const char* name_;
   soinfo* needed_by_;
+  soinfo* si_;
+  const android_dlextinfo* extinfo_;
+  int fd_;
+  bool close_fd_;
+  off64_t file_offset_;
+  std::unordered_map<const soinfo*, ElfReader>* elf_readers_map_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(LoadTask);
 };
@@ -926,7 +1191,7 @@
 
 typedef linked_list_t<soinfo> SoinfoLinkedList;
 typedef linked_list_t<const char> StringLinkedList;
-typedef linked_list_t<LoadTask> LoadTaskList;
+typedef std::vector<LoadTask*> LoadTaskList;
 
 
 // This function walks down the tree of soinfo dependencies
@@ -1004,7 +1269,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);
@@ -1016,24 +1281,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
@@ -1161,7 +1431,7 @@
 }
 
 ZipArchiveCache::~ZipArchiveCache() {
-  for (auto it : cache_) {
+  for (const auto& it : cache_) {
     CloseArchive(it.second);
   }
 }
@@ -1245,35 +1515,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;
     }
 
@@ -1301,39 +1549,46 @@
   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;
-      }
-    }
     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);
-  if (fd == -1 && needed_by) {
+  // 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;
@@ -1356,91 +1611,137 @@
 
 template<typename F>
 static void for_each_dt_needed(const soinfo* si, F action) {
-  for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) {
+  for (const ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) {
     if (d->d_tag == DT_NEEDED) {
       action(fix_dt_needed(si->get_string(d->d_un.d_val), si->get_realpath()));
     }
   }
 }
 
-static soinfo* load_library(int fd, off64_t file_offset,
-                            LoadTaskList& load_tasks,
-                            const char* name, int rtld_flags,
-                            const android_dlextinfo* extinfo,
-                            const std::string& realpath) {
+template<typename F>
+static void for_each_dt_needed(const ElfReader& elf_reader, F action) {
+  for (const ElfW(Dyn)* d = elf_reader.dynamic(); d->d_tag != DT_NULL; ++d) {
+    if (d->d_tag == DT_NEEDED) {
+      action(fix_dt_needed(elf_reader.get_string(d->d_un.d_val), elf_reader.name()));
+    }
+  }
+}
+
+static bool load_library(android_namespace_t* ns,
+                         LoadTask* task,
+                         LoadTaskList* load_tasks,
+                         int rtld_flags,
+                         const std::string& realpath) {
+  off64_t file_offset = task->get_file_offset();
+  const char* name = task->get_name();
+  const android_dlextinfo* extinfo = task->get_extinfo();
+
   if ((file_offset % PAGE_SIZE) != 0) {
     DL_ERR("file offset for the library \"%s\" is not page-aligned: %" PRId64, name, file_offset);
-    return nullptr;
+    return false;
   }
   if (file_offset < 0) {
     DL_ERR("file offset for the library \"%s\" is negative: %" PRId64, name, file_offset);
-    return nullptr;
+    return false;
   }
 
   struct stat file_stat;
-  if (TEMP_FAILURE_RETRY(fstat(fd, &file_stat)) != 0) {
+  if (TEMP_FAILURE_RETRY(fstat(task->get_fd(), &file_stat)) != 0) {
     DL_ERR("unable to stat file for the library \"%s\": %s", name, strerror(errno));
-    return nullptr;
+    return false;
   }
   if (file_offset >= file_stat.st_size) {
     DL_ERR("file offset for the library \"%s\" >= file size: %" PRId64 " >= %" PRId64,
         name, file_offset, file_stat.st_size);
-    return nullptr;
+    return false;
   }
 
   // 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());
-        return si;
+    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) {
     DL_ERR("library \"%s\" wasn't loaded and RTLD_NOLOAD prevented it", name);
-    return nullptr;
+    return false;
   }
 
-  // Read the ELF header and load the segments.
-  ElfReader elf_reader(realpath.c_str(), fd, file_offset, file_stat.st_size);
-  if (!elf_reader.Load(extinfo)) {
-    return nullptr;
+  if (!ns->is_accessible(realpath)) {
+    // do not load libraries if they are not accessible for the specified namespace.
+    DL_ERR("library \"%s\" is not accessible for the namespace \"%s\"",
+           name, ns->get_name());
+    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 nullptr;
+    return false;
   }
-  si->base = elf_reader.load_start();
-  si->size = elf_reader.load_size();
-  si->load_bias = elf_reader.load_bias();
-  si->phnum = elf_reader.phdr_count();
-  si->phdr = elf_reader.loaded_phdr();
 
-  if (!si->prelink_image()) {
+  task->set_soinfo(si);
+
+  // Read the ELF header and some of the segments.
+  if (!task->read(realpath.c_str(), file_stat.st_size)) {
     soinfo_free(si);
-    return nullptr;
+    task->set_soinfo(nullptr);
+    return false;
   }
 
-  for_each_dt_needed(si, [&] (const char* name) {
-    load_tasks.push_back(LoadTask::create(name, si));
+  // find and set DT_RUNPATH and dt_soname
+  // Note that these field values are temporary and are
+  // going to be overwritten on soinfo::prelink_image
+  // with values from PT_LOAD segments.
+  const ElfReader& elf_reader = task->get_elf_reader();
+  for (const ElfW(Dyn)* d = elf_reader.dynamic(); d->d_tag != DT_NULL; ++d) {
+    if (d->d_tag == DT_RUNPATH) {
+      si->set_dt_runpath(elf_reader.get_string(d->d_un.d_val));
+    }
+    if (d->d_tag == DT_SONAME) {
+      si->set_soname(elf_reader.get_string(d->d_un.d_val));
+    }
+  }
+
+  for_each_dt_needed(task->get_elf_reader(), [&](const char* name) {
+    load_tasks->push_back(LoadTask::create(name, si, task->get_readers_map()));
   });
 
-  return si;
+  return true;
+
 }
 
-static soinfo* load_library(ZipArchiveCache* zip_archive_cache,
-                            LoadTaskList& load_tasks, const char* name,
-                            soinfo* needed_by, int rtld_flags,
-                            const android_dlextinfo* extinfo) {
+static bool load_library(android_namespace_t* ns,
+                         LoadTask* task,
+                         ZipArchiveCache* zip_archive_cache,
+                         LoadTaskList* load_tasks,
+                         int rtld_flags) {
+  const char* name = task->get_name();
+  soinfo* needed_by = task->get_needed_by();
+  const android_dlextinfo* extinfo = task->get_extinfo();
+
   off64_t file_offset;
   std::string realpath;
   if (extinfo != nullptr && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) != 0) {
@@ -1454,25 +1755,31 @@
             "Will use given name.", name);
       realpath = name;
     }
-    return load_library(extinfo->library_fd, file_offset, load_tasks, name, rtld_flags, extinfo, realpath);
+
+    task->set_fd(extinfo->library_fd, false);
+    task->set_file_offset(file_offset);
+    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 nullptr;
+    return false;
   }
-  soinfo* result = load_library(fd, file_offset, load_tasks, name, rtld_flags, extinfo, realpath);
-  close(fd);
-  return result;
+
+  task->set_fd(fd, true);
+  task->set_file_offset(file_offset);
+
+  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.
@@ -1482,7 +1789,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
@@ -1492,44 +1799,64 @@
       // 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 soinfo* find_library_internal(ZipArchiveCache* zip_archive_cache,
-                                     LoadTaskList& load_tasks, const char* name,
-                                     soinfo* needed_by, int rtld_flags,
-                                     const android_dlextinfo* extinfo) {
+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(name, &candidate)) {
-    return 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...]",
-      name, candidate == nullptr ? "n/a" : candidate->get_realpath(), candidate);
+      task->get_name(), candidate == nullptr ? "n/a" : candidate->get_realpath(), candidate);
 
-  soinfo* si = load_library(zip_archive_cache, load_tasks, name, needed_by, rtld_flags, extinfo);
-
-  // In case we were unable to load the library but there
-  // is a candidate loaded under the same soname but different
-  // sdk level - return it anyways.
-  if (si == nullptr && candidate != nullptr) {
-    si = candidate;
+  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
+    // is a candidate loaded under the same soname but different
+    // sdk level - return it anyways.
+    if (candidate != nullptr) {
+      task->set_soinfo(candidate);
+      return true;
+    }
   }
 
-  return si;
+  return false;
 }
 
 static void soinfo_unload(soinfo* si);
@@ -1541,22 +1868,31 @@
 //
 // 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;
 }
 
+static void shuffle(std::vector<LoadTask*>* v) {
+  for (size_t i = 0, size = v->size(); i < size; ++i) {
+    size_t n = size - i;
+    size_t r = arc4random_uniform(n);
+    std::swap((*v)[n-1], (*v)[r]);
+  }
+}
+
 // add_as_children - add first-level loaded libraries (i.e. library_names[], but
 // 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,
@@ -1565,13 +1901,15 @@
                            bool add_as_children) {
   // Step 0: prepare.
   LoadTaskList load_tasks;
+  std::unordered_map<const soinfo*, ElfReader> readers_map;
+
   for (size_t i = 0; i < library_names_count; ++i) {
     const char* name = library_names[i];
-    load_tasks.push_back(LoadTask::create(name, start_with));
+    load_tasks.push_back(LoadTask::create(name, start_with, &readers_map));
   }
 
   // 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
@@ -1589,12 +1927,14 @@
   // list of libraries to link - see step 2.
   size_t soinfos_count = 0;
 
+  auto scope_guard = make_scope_guard([&]() {
+    for (LoadTask* t : load_tasks) {
+      LoadTask::deleter(t);
+    }
+  });
+
   auto failure_guard = make_scope_guard([&]() {
     // Housekeeping
-    load_tasks.for_each([] (LoadTask* t) {
-      LoadTask::deleter(t);
-    });
-
     for (size_t i = 0; i<soinfos_count; ++i) {
       soinfo_unload(soinfos[i]);
     }
@@ -1602,20 +1942,21 @@
 
   ZipArchiveCache zip_archive_cache;
 
-  // Step 1: load and pre-link all DT_NEEDED libraries in breadth first order.
-  for (LoadTask::unique_ptr task(load_tasks.pop_front());
-      task.get() != nullptr; task.reset(load_tasks.pop_front())) {
+  // Step 1: expand the list of load_tasks to include
+  // all DT_NEEDED libraries (do not load them just yet)
+  for (size_t i = 0; i<load_tasks.size(); ++i) {
+    LoadTask* task = load_tasks[i];
     soinfo* needed_by = task->get_needed_by();
+
     bool is_dt_needed = needed_by != nullptr && (needed_by != start_with || add_as_children);
+    task->set_extinfo(is_dt_needed ? nullptr : extinfo);
 
-    soinfo* si = find_library_internal(&zip_archive_cache, load_tasks,
-                                       task->get_name(), needed_by, rtld_flags,
-                                       is_dt_needed ? nullptr : extinfo);
-
-    if (si == nullptr) {
+    if(!find_library_internal(ns, task, &zip_archive_cache, &load_tasks, rtld_flags)) {
       return false;
     }
 
+    soinfo* si = task->get_soinfo();
+
     if (is_dt_needed) {
       needed_by->add_child(si);
     }
@@ -1627,11 +1968,6 @@
     // When ld_preloads is not null, the first
     // ld_preloads_count libs are in fact ld_preloads.
     if (ld_preloads != nullptr && soinfos_count < ld_preloads_count) {
-      // Add LD_PRELOADed libraries to the global group for future runs.
-      // There is no need to explicitly add them to the global group
-      // for this run because they are going to appear in the local
-      // group in the correct order.
-      si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
       ld_preloads->push_back(si);
     }
 
@@ -1640,7 +1976,47 @@
     }
   }
 
-  // Step 2: link libraries.
+  // Step 2: Load libraries in random order (see b/24047022)
+  LoadTaskList load_list;
+  for (auto&& task : load_tasks) {
+    soinfo* si = task->get_soinfo();
+    auto pred = [&](const LoadTask* t) {
+      return t->get_soinfo() == si;
+    };
+
+    if (!si->is_linked() &&
+        std::find_if(load_list.begin(), load_list.end(), pred) == load_list.end() ) {
+      load_list.push_back(task);
+    }
+  }
+  shuffle(&load_list);
+
+  for (auto&& task : load_list) {
+    if (!task->load()) {
+      return false;
+    }
+  }
+
+  // Step 3: pre-link all DT_NEEDED libraries in breadth first order.
+  for (auto&& task : load_tasks) {
+    soinfo* si = task->get_soinfo();
+    if (!si->is_linked() && !si->prelink_image()) {
+      return false;
+    }
+  }
+
+  // Step 4: Add LD_PRELOADed libraries to the global group for
+  // future runs. There is no need to explicitly add them to
+  // the global group for this run because they are going to
+  // appear in the local group in the correct order.
+  if (ld_preloads != nullptr) {
+    for (auto&& si : *ld_preloads) {
+      si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
+    }
+  }
+
+
+  // Step 5: link libraries.
   soinfo::soinfo_list_t local_group;
   walk_dependencies_tree(
       (start_with != nullptr && add_as_children) ? &start_with : soinfos,
@@ -1676,14 +2052,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;
   }
@@ -1746,7 +2123,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.
@@ -1818,24 +2197,44 @@
     DL_ERR("invalid flags to dlopen: %x", flags);
     return nullptr;
   }
+
+  android_namespace_t* ns = caller != nullptr ? caller->get_namespace() : g_anonymous_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);
       return nullptr;
     }
+
     if ((extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) == 0 &&
         (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET) != 0) {
       DL_ERR("invalid extended flag combination (ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET without "
           "ANDROID_DLEXT_USE_LIBRARY_FD): 0x%" PRIx64, extinfo->flags);
       return nullptr;
     }
+
+    if ((extinfo->flags & ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS) != 0 &&
+        (extinfo->flags & (ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_RESERVED_ADDRESS_HINT)) != 0) {
+      DL_ERR("invalid extended flag combination: ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS is not "
+             "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;
 }
 
@@ -1844,6 +2243,80 @@
   soinfo_unload(si);
 }
 
+bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_path) {
+  CHECK(public_ns_sonames != nullptr);
+  if (g_public_namespace_initialized) {
+    DL_ERR("public namespace has already been initialized.");
+    return false;
+  }
+
+  std::vector<std::string> sonames = android::base::Split(public_ns_sonames, ":");
+
+  ProtectedDataGuard guard;
+
+  auto failure_guard = make_scope_guard([&]() {
+    g_public_namespace.clear();
+  });
+
+  for (const auto& soname : sonames) {
+    soinfo* candidate = nullptr;
+
+    find_loaded_library_by_soname(&g_default_namespace, soname.c_str(), &candidate);
+
+    if (candidate == nullptr) {
+      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);
+  }
+
+  g_public_namespace_initialized = true;
+
+  // create anonymous namespace
+  android_namespace_t* anon_ns =
+      create_namespace("(anonymous)", nullptr, anon_ns_library_path, false);
+
+  if (anon_ns == nullptr) {
+    g_public_namespace_initialized = false;
+    return false;
+  }
+  g_anonymous_namespace = anon_ns;
+  failure_guard.disable();
+  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);
@@ -2165,24 +2638,23 @@
         count_relocation(kRelocAbsolute);
         MARK(rel->r_offset);
         TRACE_TYPE(RELO, "RELO ABS64 %16llx <- %16llx %s\n",
-                   reloc, (sym_addr + addend), sym_name);
-        *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + addend);
+                   reloc, sym_addr + addend, sym_name);
+        *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend;
         break;
       case R_AARCH64_ABS32:
         count_relocation(kRelocAbsolute);
         MARK(rel->r_offset);
         TRACE_TYPE(RELO, "RELO ABS32 %16llx <- %16llx %s\n",
-                   reloc, (sym_addr + addend), sym_name);
+                   reloc, sym_addr + addend, sym_name);
         {
-          const ElfW(Addr) reloc_value = *reinterpret_cast<ElfW(Addr)*>(reloc);
           const ElfW(Addr) min_value = static_cast<ElfW(Addr)>(INT32_MIN);
           const ElfW(Addr) max_value = static_cast<ElfW(Addr)>(UINT32_MAX);
-          if ((min_value <= (reloc_value + (sym_addr + addend))) &&
-              ((reloc_value + (sym_addr + addend)) <= max_value)) {
-            *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + addend);
+          if ((min_value <= (sym_addr + addend)) &&
+              ((sym_addr + addend) <= max_value)) {
+            *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend;
           } else {
             DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx",
-                   (reloc_value + (sym_addr + addend)), min_value, max_value);
+                   sym_addr + addend, min_value, max_value);
             return false;
           }
         }
@@ -2191,17 +2663,16 @@
         count_relocation(kRelocAbsolute);
         MARK(rel->r_offset);
         TRACE_TYPE(RELO, "RELO ABS16 %16llx <- %16llx %s\n",
-                   reloc, (sym_addr + addend), sym_name);
+                   reloc, sym_addr + addend, sym_name);
         {
-          const ElfW(Addr) reloc_value = *reinterpret_cast<ElfW(Addr)*>(reloc);
           const ElfW(Addr) min_value = static_cast<ElfW(Addr)>(INT16_MIN);
           const ElfW(Addr) max_value = static_cast<ElfW(Addr)>(UINT16_MAX);
-          if ((min_value <= (reloc_value + (sym_addr + addend))) &&
-              ((reloc_value + (sym_addr + addend)) <= max_value)) {
-            *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + addend);
+          if ((min_value <= (sym_addr + addend)) &&
+              ((sym_addr + addend) <= max_value)) {
+            *reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + addend);
           } else {
             DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx",
-                   reloc_value + (sym_addr + addend), min_value, max_value);
+                   sym_addr + addend, min_value, max_value);
             return false;
           }
         }
@@ -2210,24 +2681,23 @@
         count_relocation(kRelocRelative);
         MARK(rel->r_offset);
         TRACE_TYPE(RELO, "RELO REL64 %16llx <- %16llx - %16llx %s\n",
-                   reloc, (sym_addr + addend), rel->r_offset, sym_name);
-        *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + addend) - rel->r_offset;
+                   reloc, sym_addr + addend, rel->r_offset, sym_name);
+        *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend - rel->r_offset;
         break;
       case R_AARCH64_PREL32:
         count_relocation(kRelocRelative);
         MARK(rel->r_offset);
         TRACE_TYPE(RELO, "RELO REL32 %16llx <- %16llx - %16llx %s\n",
-                   reloc, (sym_addr + addend), rel->r_offset, sym_name);
+                   reloc, sym_addr + addend, rel->r_offset, sym_name);
         {
-          const ElfW(Addr) reloc_value = *reinterpret_cast<ElfW(Addr)*>(reloc);
           const ElfW(Addr) min_value = static_cast<ElfW(Addr)>(INT32_MIN);
           const ElfW(Addr) max_value = static_cast<ElfW(Addr)>(UINT32_MAX);
-          if ((min_value <= (reloc_value + ((sym_addr + addend) - rel->r_offset))) &&
-              ((reloc_value + ((sym_addr + addend) - rel->r_offset)) <= max_value)) {
-            *reinterpret_cast<ElfW(Addr)*>(reloc) += ((sym_addr + addend) - rel->r_offset);
+          if ((min_value <= (sym_addr + addend - rel->r_offset)) &&
+              ((sym_addr + addend - rel->r_offset) <= max_value)) {
+            *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend - rel->r_offset;
           } else {
             DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx",
-                   reloc_value + ((sym_addr + addend) - rel->r_offset), min_value, max_value);
+                   sym_addr + addend - rel->r_offset, min_value, max_value);
             return false;
           }
         }
@@ -2236,17 +2706,16 @@
         count_relocation(kRelocRelative);
         MARK(rel->r_offset);
         TRACE_TYPE(RELO, "RELO REL16 %16llx <- %16llx - %16llx %s\n",
-                   reloc, (sym_addr + addend), rel->r_offset, sym_name);
+                   reloc, sym_addr + addend, rel->r_offset, sym_name);
         {
-          const ElfW(Addr) reloc_value = *reinterpret_cast<ElfW(Addr)*>(reloc);
           const ElfW(Addr) min_value = static_cast<ElfW(Addr)>(INT16_MIN);
           const ElfW(Addr) max_value = static_cast<ElfW(Addr)>(UINT16_MAX);
-          if ((min_value <= (reloc_value + ((sym_addr + addend) - rel->r_offset))) &&
-              ((reloc_value + ((sym_addr + addend) - rel->r_offset)) <= max_value)) {
-            *reinterpret_cast<ElfW(Addr)*>(reloc) += ((sym_addr + addend) - rel->r_offset);
+          if ((min_value <= (sym_addr + addend - rel->r_offset)) &&
+              ((sym_addr + addend - rel->r_offset) <= max_value)) {
+            *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend - rel->r_offset;
           } else {
             DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx",
-                   reloc_value + ((sym_addr + addend) - rel->r_offset), min_value, max_value);
+                   sym_addr + addend - rel->r_offset, min_value, max_value);
             return false;
           }
         }
@@ -2278,14 +2747,14 @@
         MARK(rel->r_offset);
         TRACE_TYPE(RELO, "RELO R_X86_64_32 %08zx <- +%08zx %s", static_cast<size_t>(reloc),
                    static_cast<size_t>(sym_addr), sym_name);
-        *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend;
+        *reinterpret_cast<Elf32_Addr*>(reloc) = sym_addr + addend;
         break;
       case R_X86_64_64:
         count_relocation(kRelocRelative);
         MARK(rel->r_offset);
         TRACE_TYPE(RELO, "RELO R_X86_64_64 %08zx <- +%08zx %s", static_cast<size_t>(reloc),
                    static_cast<size_t>(sym_addr), sym_name);
-        *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend;
+        *reinterpret_cast<Elf64_Addr*>(reloc) = sym_addr + addend;
         break;
       case R_X86_64_PC32:
         count_relocation(kRelocRelative);
@@ -2293,7 +2762,7 @@
         TRACE_TYPE(RELO, "RELO R_X86_64_PC32 %08zx <- +%08zx (%08zx - %08zx) %s",
                    static_cast<size_t>(reloc), static_cast<size_t>(sym_addr - reloc),
                    static_cast<size_t>(sym_addr), static_cast<size_t>(reloc), sym_name);
-        *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend - reloc;
+        *reinterpret_cast<Elf32_Addr*>(reloc) = sym_addr + addend - reloc;
         break;
 #elif defined(__arm__)
       case R_ARM_ABS32:
@@ -2516,6 +2985,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)) {
@@ -2528,6 +3001,17 @@
 #endif
 }
 
+void soinfo::set_soname(const char* soname) {
+#if defined(__work_around_b_24465209__)
+  if (has_min_version(2)) {
+    soname_ = soname;
+  }
+  strlcpy(old_name_, soname_, sizeof(old_name_));
+#else
+  soname_ = soname;
+#endif
+}
+
 const char* soinfo::get_soname() const {
 #if defined(__work_around_b_24465209__)
   if (has_min_version(2)) {
@@ -2571,13 +3055,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);
@@ -3059,13 +3551,9 @@
   for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) {
     switch (d->d_tag) {
       case DT_SONAME:
-        soname_ = get_string(d->d_un.d_val);
-#if defined(__work_around_b_24465209__)
-        strlcpy(old_name_, soname_, sizeof(old_name_));
-#endif
+        set_soname(get_string(d->d_un.d_val));
         break;
       case DT_RUNPATH:
-        // FIXME: $LIB, $PLATFORM unsupported.
         set_dt_runpath(get_string(d->d_un.d_val));
         break;
     }
@@ -3243,7 +3731,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;
@@ -3279,7 +3767,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;
 
@@ -3296,14 +3785,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);
@@ -3344,7 +3844,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);
   }
@@ -3396,11 +3896,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
@@ -3428,15 +3927,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();
   }
@@ -3545,7 +4042,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
@@ -3554,7 +4051,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;
@@ -3572,15 +4069,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);
@@ -3596,6 +4085,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 39d3ff1..b391fc3 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,12 +312,14 @@
   void set_linked();
   void set_linker_flag();
   void set_main_executable();
+  void set_nodelete();
 
   void increment_ref_count();
   size_t decrement_ref_count();
 
   soinfo* get_local_group_root() const;
 
+  void set_soname(const char* soname);
   const char* get_soname() const;
   const char* get_realpath() const;
   const ElfW(Versym)* get_versym(size_t n) const;
@@ -329,7 +332,9 @@
 
   uint32_t get_target_sdk_version() const;
 
+  void set_dt_runpath(const char *);
   const std::vector<std::string>& get_dt_runpath() const;
+  android_namespace_t* get_namespace();
 
  private:
   bool elf_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
@@ -392,8 +397,9 @@
 
   uint32_t target_sdk_version_;
 
-  void set_dt_runpath(const char *);
+  // version >= 3
   std::vector<std::string> dt_runpath_;
+  android_namespace_t* namespace_;
 
   friend soinfo* get_libdl_info();
 };
@@ -421,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);
@@ -436,4 +444,8 @@
 void set_application_target_sdk_version(uint32_t target);
 uint32_t get_application_target_sdk_version();
 
+bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_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_debug.h b/linker/linker_debug.h
index 51f8d4c..17c6986 100644
--- a/linker/linker_debug.h
+++ b/linker/linker_debug.h
@@ -58,6 +58,13 @@
 
 __LIBC_HIDDEN__ extern int g_ld_debug_verbosity;
 
+#define CHECK(predicate) { \
+    if (!(predicate)) { \
+      __libc_fatal("%s:%d: %s CHECK '" #predicate "' failed", \
+          __FILE__, __LINE__, __FUNCTION__); \
+    } \
+  }
+
 #if LINKER_DEBUG_TO_LOG
 #define _PRINTVF(v, x...) \
     do { \
diff --git a/linker/linker_libc_support.c b/linker/linker_libc_support.c
index 4c49384..77a0252 100644
--- a/linker/linker_libc_support.c
+++ b/linker/linker_libc_support.c
@@ -15,7 +15,9 @@
  */
 
 #include "../libc/arch-common/bionic/__dso_handle.h"
+#include "../libc/arch-common/bionic/pthread_atfork.h"
 
 int atexit(void (*function)(void) __attribute__((__unused__))) {
   return -1;
 }
+
diff --git a/linker/linker_mapped_file_fragment.cpp b/linker/linker_mapped_file_fragment.cpp
new file mode 100644
index 0000000..27c1c69
--- /dev/null
+++ b/linker/linker_mapped_file_fragment.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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 "linker_mapped_file_fragment.h"
+#include "linker_debug.h"
+#include "linker_utils.h"
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+MappedFileFragment::MappedFileFragment() : map_start_(nullptr), map_size_(0),
+                                           data_(nullptr), size_ (0)
+{ }
+
+MappedFileFragment::~MappedFileFragment() {
+  if (map_start_ != nullptr) {
+    munmap(map_start_, map_size_);
+  }
+}
+
+bool MappedFileFragment::Map(int fd, off64_t base_offset, size_t elf_offset, size_t size) {
+  off64_t offset;
+  CHECK(safe_add(&offset, base_offset, elf_offset));
+
+  off64_t page_min = page_start(offset);
+  off64_t end_offset;
+
+  CHECK(safe_add(&end_offset, offset, size));
+  CHECK(safe_add(&end_offset, end_offset, page_offset(offset)));
+
+  size_t map_size = static_cast<size_t>(end_offset - page_min);
+  CHECK(map_size >= size);
+
+  uint8_t* map_start = static_cast<uint8_t*>(
+                          mmap64(nullptr, map_size, PROT_READ, MAP_PRIVATE, fd, page_min));
+
+  if (map_start == MAP_FAILED) {
+    return false;
+  }
+
+  map_start_ = map_start;
+  map_size_ = map_size;
+
+  data_ = map_start + page_offset(offset);
+  size_ = size;
+
+  return true;
+}
diff --git a/linker/linker_mapped_file_fragment.h b/linker/linker_mapped_file_fragment.h
new file mode 100644
index 0000000..91bd077
--- /dev/null
+++ b/linker/linker_mapped_file_fragment.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+#ifndef LINKER_MAPPED_FILE_FRAGMENT_H
+#define LINKER_MAPPED_FILE_FRAGMENT_H
+
+#include <unistd.h>
+
+#include "private/bionic_macros.h"
+
+class MappedFileFragment {
+ public:
+  MappedFileFragment();
+  ~MappedFileFragment();
+
+  bool Map(int fd, off64_t base_offset, size_t elf_offset, size_t size);
+
+  void* data() const { return data_; }
+  size_t size() const { return size_; }
+ private:
+  void* map_start_;
+  size_t map_size_;
+  void* data_;
+  size_t size_;
+
+  DISALLOW_COPY_AND_ASSIGN(MappedFileFragment);
+};
+
+#endif /* LINKER_MAPPED_FILE_FRAGMENT_H */
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index 30bc6fa..4c4ce17 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -37,6 +37,7 @@
 
 #include "linker.h"
 #include "linker_debug.h"
+#include "linker_utils.h"
 
 static int GetTargetElfMachine() {
 #if defined(__arm__)
@@ -133,37 +134,59 @@
                                       MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \
                                       MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE))
 
-ElfReader::ElfReader(const char* name, int fd, off64_t file_offset, off64_t file_size)
-    : name_(name), fd_(fd), file_offset_(file_offset), file_size_(file_size),
-      phdr_num_(0), phdr_mmap_(nullptr), phdr_table_(nullptr), phdr_size_(0),
-      load_start_(nullptr), load_size_(0), load_bias_(0),
-      loaded_phdr_(nullptr) {
+ElfReader::ElfReader()
+    : did_read_(false), did_load_(false), fd_(-1), file_offset_(0), file_size_(0), phdr_num_(0),
+      phdr_table_(nullptr), shdr_table_(nullptr), shdr_num_(0), dynamic_(nullptr), strtab_(nullptr),
+      strtab_size_(0), load_start_(nullptr), load_size_(0), load_bias_(0), loaded_phdr_(nullptr) {
 }
 
-ElfReader::~ElfReader() {
-  if (phdr_mmap_ != nullptr) {
-    munmap(phdr_mmap_, phdr_size_);
+bool ElfReader::Read(const char* name, int fd, off64_t file_offset, off64_t file_size) {
+  CHECK(!did_read_);
+  CHECK(!did_load_);
+  name_ = name;
+  fd_ = fd;
+  file_offset_ = file_offset;
+  file_size_ = file_size;
+
+  if (ReadElfHeader() &&
+      VerifyElfHeader() &&
+      ReadProgramHeaders() &&
+      ReadSectionHeaders() &&
+      ReadDynamicSection()) {
+    did_read_ = true;
   }
+
+  return did_read_;
 }
 
 bool ElfReader::Load(const android_dlextinfo* extinfo) {
-  return ReadElfHeader() &&
-         VerifyElfHeader() &&
-         ReadProgramHeader() &&
-         ReserveAddressSpace(extinfo) &&
-         LoadSegments() &&
-         FindPhdr();
+  CHECK(did_read_);
+  CHECK(!did_load_);
+  if (ReserveAddressSpace(extinfo) &&
+      LoadSegments() &&
+      FindPhdr()) {
+    did_load_ = true;
+  }
+
+  return did_load_;
+}
+
+const char* ElfReader::get_string(ElfW(Word) index) const {
+  CHECK(strtab_ != nullptr);
+  CHECK(index < strtab_size_);
+
+  return strtab_ + index;
 }
 
 bool ElfReader::ReadElfHeader() {
   ssize_t rc = TEMP_FAILURE_RETRY(pread64(fd_, &header_, sizeof(header_), file_offset_));
   if (rc < 0) {
-    DL_ERR("can't read file \"%s\": %s", name_, strerror(errno));
+    DL_ERR("can't read file \"%s\": %s", name_.c_str(), strerror(errno));
     return false;
   }
 
   if (rc != sizeof(header_)) {
-    DL_ERR("\"%s\" is too small to be an ELF executable: only found %zd bytes", name_,
+    DL_ERR("\"%s\" is too small to be an ELF executable: only found %zd bytes", name_.c_str(),
            static_cast<size_t>(rc));
     return false;
   }
@@ -172,7 +195,7 @@
 
 bool ElfReader::VerifyElfHeader() {
   if (memcmp(header_.e_ident, ELFMAG, SELFMAG) != 0) {
-    DL_ERR("\"%s\" has bad ELF magic", name_);
+    DL_ERR("\"%s\" has bad ELF magic", name_.c_str());
     return false;
   }
 
@@ -182,73 +205,161 @@
 #if defined(__LP64__)
   if (elf_class != ELFCLASS64) {
     if (elf_class == ELFCLASS32) {
-      DL_ERR("\"%s\" is 32-bit instead of 64-bit", name_);
+      DL_ERR("\"%s\" is 32-bit instead of 64-bit", name_.c_str());
     } else {
-      DL_ERR("\"%s\" has unknown ELF class: %d", name_, elf_class);
+      DL_ERR("\"%s\" has unknown ELF class: %d", name_.c_str(), elf_class);
     }
     return false;
   }
 #else
   if (elf_class != ELFCLASS32) {
     if (elf_class == ELFCLASS64) {
-      DL_ERR("\"%s\" is 64-bit instead of 32-bit", name_);
+      DL_ERR("\"%s\" is 64-bit instead of 32-bit", name_.c_str());
     } else {
-      DL_ERR("\"%s\" has unknown ELF class: %d", name_, elf_class);
+      DL_ERR("\"%s\" has unknown ELF class: %d", name_.c_str(), elf_class);
     }
     return false;
   }
 #endif
 
   if (header_.e_ident[EI_DATA] != ELFDATA2LSB) {
-    DL_ERR("\"%s\" not little-endian: %d", name_, header_.e_ident[EI_DATA]);
+    DL_ERR("\"%s\" not little-endian: %d", name_.c_str(), header_.e_ident[EI_DATA]);
     return false;
   }
 
   if (header_.e_type != ET_DYN) {
-    DL_ERR("\"%s\" has unexpected e_type: %d", name_, header_.e_type);
+    DL_ERR("\"%s\" has unexpected e_type: %d", name_.c_str(), header_.e_type);
     return false;
   }
 
   if (header_.e_version != EV_CURRENT) {
-    DL_ERR("\"%s\" has unexpected e_version: %d", name_, header_.e_version);
+    DL_ERR("\"%s\" has unexpected e_version: %d", name_.c_str(), header_.e_version);
     return false;
   }
 
   if (header_.e_machine != GetTargetElfMachine()) {
-    DL_ERR("\"%s\" has unexpected e_machine: %d", name_, header_.e_machine);
+    DL_ERR("\"%s\" has unexpected e_machine: %d", name_.c_str(), header_.e_machine);
     return false;
   }
 
   return true;
 }
 
+bool ElfReader::CheckFileRange(ElfW(Addr) offset, size_t size) {
+  off64_t range_start;
+  off64_t range_end;
+
+  return safe_add(&range_start, file_offset_, offset) &&
+         safe_add(&range_end, range_start, size) &&
+         range_start < file_size_ &&
+         range_end <= file_size_;
+}
+
 // Loads the program header table from an ELF file into a read-only private
 // anonymous mmap-ed block.
-bool ElfReader::ReadProgramHeader() {
+bool ElfReader::ReadProgramHeaders() {
   phdr_num_ = header_.e_phnum;
 
   // Like the kernel, we only accept program header tables that
   // are smaller than 64KiB.
   if (phdr_num_ < 1 || phdr_num_ > 65536/sizeof(ElfW(Phdr))) {
-    DL_ERR("\"%s\" has invalid e_phnum: %zd", name_, phdr_num_);
+    DL_ERR("\"%s\" has invalid e_phnum: %zd", name_.c_str(), phdr_num_);
     return false;
   }
 
-  ElfW(Addr) page_min = PAGE_START(header_.e_phoff);
-  ElfW(Addr) page_max = PAGE_END(header_.e_phoff + (phdr_num_ * sizeof(ElfW(Phdr))));
-  ElfW(Addr) page_offset = PAGE_OFFSET(header_.e_phoff);
-
-  phdr_size_ = page_max - page_min;
-
-  void* mmap_result =
-      mmap64(nullptr, phdr_size_, PROT_READ, MAP_PRIVATE, fd_, file_offset_ + page_min);
-  if (mmap_result == MAP_FAILED) {
-    DL_ERR("\"%s\" phdr mmap failed: %s", name_, strerror(errno));
+  // Boundary checks
+  size_t size = phdr_num_ * sizeof(ElfW(Phdr));
+  if (!CheckFileRange(header_.e_phoff, size)) {
+    DL_ERR("\"%s\" has invalid phdr offset/size", name_.c_str());
     return false;
   }
 
-  phdr_mmap_ = mmap_result;
-  phdr_table_ = reinterpret_cast<ElfW(Phdr)*>(reinterpret_cast<char*>(mmap_result) + page_offset);
+  if (!phdr_fragment_.Map(fd_, file_offset_, header_.e_phoff, size)) {
+    DL_ERR("\"%s\" phdr mmap failed: %s", name_.c_str(), strerror(errno));
+    return false;
+  }
+
+  phdr_table_ = static_cast<ElfW(Phdr)*>(phdr_fragment_.data());
+  return true;
+}
+
+bool ElfReader::ReadSectionHeaders() {
+  shdr_num_ = header_.e_shnum;
+
+  if (shdr_num_ == 0) {
+    DL_ERR("\"%s\" has no section headers", name_.c_str());
+    return false;
+  }
+
+  size_t size = shdr_num_ * sizeof(ElfW(Shdr));
+  if (!CheckFileRange(header_.e_shoff, size)) {
+    DL_ERR("\"%s\" has invalid shdr offset/size", name_.c_str());
+    return false;
+  }
+
+  if (!shdr_fragment_.Map(fd_, file_offset_, header_.e_shoff, size)) {
+    DL_ERR("\"%s\" shdr mmap failed: %s", name_.c_str(), strerror(errno));
+    return false;
+  }
+
+  shdr_table_ = static_cast<const ElfW(Shdr)*>(shdr_fragment_.data());
+  return true;
+}
+
+bool ElfReader::ReadDynamicSection() {
+  // 1. Find .dynamic section (in section headers)
+  const ElfW(Shdr)* dynamic_shdr = nullptr;
+  for (size_t i = 0; i < shdr_num_; ++i) {
+    if (shdr_table_[i].sh_type == SHT_DYNAMIC) {
+      dynamic_shdr = &shdr_table_ [i];
+      break;
+    }
+  }
+
+  if (dynamic_shdr == nullptr) {
+    DL_ERR("\"%s\" .dynamic section header was not found", name_.c_str());
+    return false;
+  }
+
+  if (dynamic_shdr->sh_link >= shdr_num_) {
+    DL_ERR("\"%s\" .dynamic section has invalid sh_link: %d", name_.c_str(), dynamic_shdr->sh_link);
+    return false;
+  }
+
+  const ElfW(Shdr)* strtab_shdr = &shdr_table_[dynamic_shdr->sh_link];
+
+  if (strtab_shdr->sh_type != SHT_STRTAB) {
+    DL_ERR("\"%s\" .dynamic section has invalid link(%d) sh_type: %d (expected SHT_STRTAB)",
+           name_.c_str(), dynamic_shdr->sh_link, strtab_shdr->sh_type);
+    return false;
+  }
+
+  if (!CheckFileRange(dynamic_shdr->sh_offset, dynamic_shdr->sh_size)) {
+    DL_ERR("\"%s\" has invalid offset/size of .dynamic section", name_.c_str());
+    PRINT("\"%s\" has invalid offset/size of .dynamic section", name_.c_str());
+    return false;
+  }
+
+  if (!dynamic_fragment_.Map(fd_, file_offset_, dynamic_shdr->sh_offset, dynamic_shdr->sh_size)) {
+    DL_ERR("\"%s\" dynamic section mmap failed: %s", name_.c_str(), strerror(errno));
+    return false;
+  }
+
+  dynamic_ = static_cast<const ElfW(Dyn)*>(dynamic_fragment_.data());
+
+  if (!CheckFileRange(strtab_shdr->sh_offset, strtab_shdr->sh_size)) {
+    DL_ERR("\"%s\" has invalid offset/size of the .strtab section linked from .dynamic section",
+           name_.c_str());
+    return false;
+  }
+
+  if (!strtab_fragment_.Map(fd_, file_offset_, strtab_shdr->sh_offset, strtab_shdr->sh_size)) {
+    DL_ERR("\"%s\" strtab section mmap failed: %s", name_.c_str(), strerror(errno));
+    return false;
+  }
+
+  strtab_ = static_cast<const char*>(strtab_fragment_.data());
+  strtab_size_ = strtab_fragment_.size();
   return true;
 }
 
@@ -308,7 +419,7 @@
   ElfW(Addr) min_vaddr;
   load_size_ = phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr);
   if (load_size_ == 0) {
-    DL_ERR("\"%s\" has no loadable segments", name_);
+    DL_ERR("\"%s\" has no loadable segments", name_.c_str());
     return false;
   }
 
@@ -316,8 +427,9 @@
   void* start;
   size_t reserved_size = 0;
   bool reserved_hint = true;
+  bool strict_hint = false;
   // Assume position independent executable by default.
-  uint8_t* mmap_hint = nullptr;
+  void* mmap_hint = nullptr;
 
   if (extinfo != nullptr) {
     if (extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS) {
@@ -327,21 +439,30 @@
       reserved_size = extinfo->reserved_size;
     }
 
-    if ((extinfo->flags & ANDROID_DLEXT_FORCE_FIXED_VADDR) != 0) {
+    if (addr != nullptr && (extinfo->flags & ANDROID_DLEXT_FORCE_FIXED_VADDR) != 0) {
       mmap_hint = addr;
+    } else if ((extinfo->flags & ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS) != 0) {
+      mmap_hint = extinfo->reserved_addr;
+      strict_hint = true;
     }
   }
 
   if (load_size_ > reserved_size) {
     if (!reserved_hint) {
       DL_ERR("reserved address space %zd smaller than %zd bytes needed for \"%s\"",
-             reserved_size - load_size_, load_size_, name_);
+             reserved_size - load_size_, load_size_, name_.c_str());
       return false;
     }
     int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
     start = mmap(mmap_hint, load_size_, PROT_NONE, mmap_flags, -1, 0);
     if (start == MAP_FAILED) {
-      DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_);
+      DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_.c_str());
+      return false;
+    }
+    if (strict_hint && (start != mmap_hint)) {
+      munmap(start, load_size_);
+      DL_ERR("couldn't reserve %zd bytes of address space at %p for \"%s\"",
+             load_size_, mmap_hint, name_.c_str());
       return false;
     }
   } else {
@@ -378,14 +499,14 @@
     ElfW(Addr) file_length = file_end - file_page_start;
 
     if (file_size_ <= 0) {
-      DL_ERR("\"%s\" invalid file size: %" PRId64, name_, file_size_);
+      DL_ERR("\"%s\" invalid file size: %" PRId64, name_.c_str(), file_size_);
       return false;
     }
 
     if (file_end > static_cast<size_t>(file_size_)) {
       DL_ERR("invalid ELF file \"%s\" load segment[%zd]:"
           " p_offset (%p) + p_filesz (%p) ( = %p) past end of file (0x%" PRIx64 ")",
-          name_, i, reinterpret_cast<void*>(phdr->p_offset),
+          name_.c_str(), i, reinterpret_cast<void*>(phdr->p_offset),
           reinterpret_cast<void*>(phdr->p_filesz),
           reinterpret_cast<void*>(file_end), file_size_);
       return false;
@@ -399,7 +520,7 @@
                             fd_,
                             file_offset_ + file_page_start);
       if (seg_addr == MAP_FAILED) {
-        DL_ERR("couldn't map \"%s\" segment %zd: %s", name_, i, strerror(errno));
+        DL_ERR("couldn't map \"%s\" segment %zd: %s", name_.c_str(), i, strerror(errno));
         return false;
       }
     }
@@ -424,7 +545,7 @@
                            -1,
                            0);
       if (zeromap == MAP_FAILED) {
-        DL_ERR("couldn't zero fill \"%s\" gap: %s", name_, strerror(errno));
+        DL_ERR("couldn't zero fill \"%s\" gap: %s", name_.c_str(), strerror(errno));
         return false;
       }
     }
@@ -819,7 +940,7 @@
     }
   }
 
-  DL_ERR("can't find loaded phdr for \"%s\"", name_);
+  DL_ERR("can't find loaded phdr for \"%s\"", name_.c_str());
   return false;
 }
 
@@ -829,7 +950,7 @@
 bool ElfReader::CheckPhdr(ElfW(Addr) loaded) {
   const ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_;
   ElfW(Addr) loaded_end = loaded + (phdr_num_ * sizeof(ElfW(Phdr)));
-  for (ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
+  for (const ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
     if (phdr->p_type != PT_LOAD) {
       continue;
     }
@@ -840,6 +961,7 @@
       return true;
     }
   }
-  DL_ERR("\"%s\" loaded phdr %p not in loadable segment", name_, reinterpret_cast<void*>(loaded));
+  DL_ERR("\"%s\" loaded phdr %p not in loadable segment",
+         name_.c_str(), reinterpret_cast<void*>(loaded));
   return false;
 }
diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h
index 55196fd..c359cca 100644
--- a/linker/linker_phdr.h
+++ b/linker/linker_phdr.h
@@ -36,30 +36,39 @@
  */
 
 #include "linker.h"
+#include "linker_mapped_file_fragment.h"
 
 class ElfReader {
  public:
-  ElfReader(const char* name, int fd, off64_t file_offset, off64_t file_size);
-  ~ElfReader();
+  ElfReader();
 
+  bool Read(const char* name, int fd, off64_t file_offset, off64_t file_size);
   bool Load(const android_dlextinfo* extinfo);
 
-  size_t phdr_count() { return phdr_num_; }
-  ElfW(Addr) load_start() { return reinterpret_cast<ElfW(Addr)>(load_start_); }
-  size_t load_size() { return load_size_; }
-  ElfW(Addr) load_bias() { return load_bias_; }
-  const ElfW(Phdr)* loaded_phdr() { return loaded_phdr_; }
+  const char* name() const { return name_.c_str(); }
+  size_t phdr_count() const { return phdr_num_; }
+  ElfW(Addr) load_start() const { return reinterpret_cast<ElfW(Addr)>(load_start_); }
+  size_t load_size() const { return load_size_; }
+  ElfW(Addr) load_bias() const { return load_bias_; }
+  const ElfW(Phdr)* loaded_phdr() const { return loaded_phdr_; }
+  const ElfW(Dyn)* dynamic() const { return dynamic_; }
+  const char* get_string(ElfW(Word) index) const;
 
  private:
   bool ReadElfHeader();
   bool VerifyElfHeader();
-  bool ReadProgramHeader();
+  bool ReadProgramHeaders();
+  bool ReadSectionHeaders();
+  bool ReadDynamicSection();
   bool ReserveAddressSpace(const android_dlextinfo* extinfo);
   bool LoadSegments();
   bool FindPhdr();
   bool CheckPhdr(ElfW(Addr));
+  bool CheckFileRange(ElfW(Addr) offset, size_t size);
 
-  const char* name_;
+  bool did_read_;
+  bool did_load_;
+  std::string name_;
   int fd_;
   off64_t file_offset_;
   off64_t file_size_;
@@ -67,9 +76,19 @@
   ElfW(Ehdr) header_;
   size_t phdr_num_;
 
-  void* phdr_mmap_;
-  ElfW(Phdr)* phdr_table_;
-  ElfW(Addr) phdr_size_;
+  MappedFileFragment phdr_fragment_;
+  const ElfW(Phdr)* phdr_table_;
+
+  MappedFileFragment shdr_fragment_;
+  const ElfW(Shdr)* shdr_table_;
+  size_t shdr_num_;
+
+  MappedFileFragment dynamic_fragment_;
+  const ElfW(Dyn)* dynamic_;
+
+  MappedFileFragment strtab_fragment_;
+  const char* strtab_;
+  size_t strtab_size_;
 
   // First page of reserved address space.
   void* load_start_;
diff --git a/linker/linker_utils.cpp b/linker/linker_utils.cpp
index 5d39d83..db43d38 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,67 @@
   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;
+}
+
+constexpr off64_t kPageMask = ~static_cast<off64_t>(PAGE_SIZE-1);
+
+off64_t page_start(off64_t offset) {
+  return offset & kPageMask;
+}
+
+bool safe_add(off64_t* out, off64_t a, size_t b) {
+  CHECK(a >= 0);
+  if (static_cast<uint64_t>(INT64_MAX - a) < b) {
+    return false;
+  }
+
+  *out = a + b;
+  return true;
+}
+
+size_t page_offset(off64_t offset) {
+  return static_cast<size_t>(offset & (PAGE_SIZE-1));
+}
+
diff --git a/linker/linker_utils.h b/linker/linker_utils.h
index fc79fd1..65ffbdc5 100644
--- a/linker/linker_utils.h
+++ b/linker/linker_utils.h
@@ -18,6 +18,14 @@
 
 #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);
+
+off64_t page_start(off64_t offset);
+size_t page_offset(off64_t offset);
+bool safe_add(off64_t* out, off64_t a, size_t b);
 
 #endif
diff --git a/linker/tests/linked_list_test.cpp b/linker/tests/linked_list_test.cpp
index 09ad687..12348d9 100644
--- a/linker/tests/linked_list_test.cpp
+++ b/linker/tests/linked_list_test.cpp
@@ -133,6 +133,23 @@
   ASSERT_TRUE(list.pop_front() == nullptr);
 }
 
+TEST(linked_list, remove_if_last_then_push_back) {
+  test_list_t list;
+
+  list.push_back("a");
+  list.push_back("b");
+  list.push_back("c");
+  list.push_back("d");
+
+  list.remove_if([](const char* c) {
+    return *c == 'c' || *c == 'd';
+  });
+
+  ASSERT_EQ("ab", test_list_to_string(list));
+  list.push_back("d");
+  ASSERT_EQ("abd", test_list_to_string(list));
+}
+
 TEST(linked_list, copy_to_array) {
   test_list_t list;
   const size_t max_size = 128;
diff --git a/linker/tests/linker_utils_test.cpp b/linker/tests/linker_utils_test.cpp
index 458474e..3be9b3f 100644
--- a/linker/tests/linker_utils_test.cpp
+++ b/linker/tests/linker_utils_test.cpp
@@ -43,3 +43,50 @@
   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);
+}
+
+TEST(linker_utils, page_start) {
+  ASSERT_EQ(0x0001000, page_start(0x0001000));
+  ASSERT_EQ(0x3002000, page_start(0x300222f));
+  ASSERT_EQ(0x6001000, page_start(0x6001fff));
+}
+
+TEST(linker_utils, page_offset) {
+  ASSERT_EQ(0x0U, page_offset(0x0001000));
+  ASSERT_EQ(0x22fU, page_offset(0x300222f));
+  ASSERT_EQ(0xfffU, page_offset(0x6001fff));
+}
+
+TEST(linker_utils, safe_add) {
+  int64_t val = 42;
+  ASSERT_FALSE(safe_add(&val, INT64_MAX-20, 21U));
+  ASSERT_EQ(42, val);
+  ASSERT_TRUE(safe_add(&val, INT64_MAX-42, 42U));
+  ASSERT_EQ(INT64_MAX, val);
+  ASSERT_TRUE(safe_add(&val, 2000, 42U));
+  ASSERT_EQ(2042, val);
+}
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 e2c4fb4..86c141a 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -63,6 +63,7 @@
     getcwd_test.cpp \
     inttypes_test.cpp \
     libc_logging_test.cpp \
+    libgen_basename_test.cpp \
     libgen_test.cpp \
     locale_test.cpp \
     malloc_test.cpp \
@@ -141,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
@@ -168,6 +171,7 @@
   ) \
 )
 
+fortify1-tests-gcc_clang_target := false
 module := fortify1-tests-gcc
 module_tag := optional
 build_type := target
@@ -176,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
@@ -267,18 +272,20 @@
 # Tests for the device using bionic's .so. Run with:
 #   adb shell /data/nativetest/bionic-unit-tests/bionic-unit-tests32
 #   adb shell /data/nativetest/bionic-unit-tests/bionic-unit-tests64
+#   adb shell /data/nativetest/bionic-unit-tests/bionic-unit-tests-gcc32
+#   adb shell /data/nativetest/bionic-unit-tests/bionic-unit-tests-gcc64
 # -----------------------------------------------------------------------------
-bionic-unit-tests_whole_static_libraries := \
+common_bionic-unit-tests_whole_static_libraries := \
     libBionicTests \
     libBionicGtestMain \
 
-bionic-unit-tests_static_libraries := \
+common_bionic-unit-tests_static_libraries := \
     libtinyxml2 \
     liblog \
     libbase \
 
 # TODO: Include __cxa_thread_atexit_test.cpp to glibc tests once it is upgraded (glibc 2.18+)
-bionic-unit-tests_src_files := \
+common_bionic-unit-tests_src_files := \
     atexit_test.cpp \
     dl_test.cpp \
     dlext_test.cpp \
@@ -288,36 +295,56 @@
     pthread_dlfcn_test.cpp \
     thread_local_test.cpp \
 
-bionic-unit-tests_cflags := $(test_cflags)
+common_bionic-unit-tests_cflags := $(test_cflags)
 
-bionic-unit-tests_conlyflags := \
+common_bionic-unit-tests_conlyflags := \
     -fexceptions \
     -fnon-call-exceptions \
 
-bionic-unit-tests_cppflags := $(test_cppflags)
+common_bionic-unit-tests_cppflags := $(test_cppflags)
 
-bionic-unit-tests_ldflags := \
+common_bionic-unit-tests_ldflags := \
     -Wl,--export-dynamic
 
-bionic-unit-tests_c_includes := \
+common_bionic-unit-tests_c_includes := \
     bionic/libc \
 
-bionic-unit-tests_shared_libraries_target := \
+common_bionic-unit-tests_shared_libraries_target := \
     libdl \
     libpagemap \
     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
+common_bionic-unit-tests_shared_libraries_target += libdl_test_df_1_global
 
-bionic-unit-tests_shared_libraries_target += libdl_test_df_1_global
-
-module := bionic-unit-tests
 module_tag := optional
 build_type := target
 build_target := NATIVE_TEST
+
+module := bionic-unit-tests
+bionic-unit-tests_clang_target := true
+bionic-unit-tests_whole_static_libraries := $(common_bionic-unit-tests_whole_static_libraries)
+bionic-unit-tests_static_libraries := $(common_bionic-unit-tests_static_libraries)
+bionic-unit-tests_src_files := $(common_bionic-unit-tests_src_files)
+bionic-unit-tests_cflags := $(common_bionic-unit-tests_cflags)
+bionic-unit-tests_conlyflags := $(common_bionic-unit-tests_conlyflags)
+bionic-unit-tests_cppflags := $(common_bionic-unit-tests_cppflags)
+bionic-unit-tests_ldflags := $(common_bionic-unit-tests_ldflags)
+bionic-unit-tests_c_includes := $(common_bionic-unit-tests_c_includes)
+bionic-unit-tests_shared_libraries_target := $(common_bionic-unit-tests_shared_libraries_target)
+include $(LOCAL_PATH)/Android.build.mk
+
+module := bionic-unit-tests-gcc
+bionic-unit-tests-gcc_clang_target := false
+bionic-unit-tests-gcc_whole_static_libraries := $(common_bionic-unit-tests_whole_static_libraries)
+bionic-unit-tests-gcc_static_libraries := $(common_bionic-unit-tests_static_libraries)
+bionic-unit-tests-gcc_src_files := $(common_bionic-unit-tests_src_files)
+bionic-unit-tests-gcc_cflags := $(common_bionic-unit-tests_cflags)
+bionic-unit-tests-gcc_conlyflags := $(common_bionic-unit-tests_conlyflags)
+bionic-unit-tests-gcc_cppflags := $(common_bionic-unit-tests_cppflags)
+bionic-unit-tests-gcc_ldflags := $(common_bionic-unit-tests_ldflags)
+bionic-unit-tests-gcc_c_includes := $(common_bionic-unit-tests_c_includes)
+bionic-unit-tests-gcc_shared_libraries_target := $(common_bionic-unit-tests_shared_libraries_target)
 include $(LOCAL_PATH)/Android.build.mk
 
 # -----------------------------------------------------------------------------
diff --git a/tests/__cxa_thread_atexit_test.cpp b/tests/__cxa_thread_atexit_test.cpp
index e388f3b..1432968 100644
--- a/tests/__cxa_thread_atexit_test.cpp
+++ b/tests/__cxa_thread_atexit_test.cpp
@@ -35,7 +35,12 @@
   std::string message;
 };
 
+#if defined(__clang__) && defined(__aarch64__)
+// b/25642296, aarch64 clang compiled "thread_local" does not link.
+static ClassWithDtor class_with_dtor;
+#else
 static thread_local ClassWithDtor class_with_dtor;
+#endif
 
 static void* thread_nop(void* arg) {
   class_with_dtor.set_message(*static_cast<std::string*>(arg));
@@ -47,7 +52,12 @@
   pthread_t t;
   ASSERT_EQ(0, pthread_create(&t, nullptr, thread_nop, &msg));
   ASSERT_EQ(0, pthread_join(t, nullptr));
+#if defined(__clang__) && defined(__aarch64__)
+  GTEST_LOG_(INFO) << "Skipping test, b/25642296, "
+                   << "thread_local does not work with aarch64 clang/llvm.\n";
+#else
   ASSERT_EQ("dtor called.", class_with_dtor_output);
+#endif
 }
 
 class ClassWithDtorForMainThread {
@@ -64,7 +74,13 @@
 };
 
 static void thread_atexit_main() {
+#if defined(__clang__) && defined(__aarch64__)
+  static ClassWithDtorForMainThread class_with_dtor_for_main_thread;
+  GTEST_LOG_(INFO) << "Skipping test, b/25642296, "
+                   << "thread_local does not work with aarch64 clang/llvm.\n";
+#else
   static thread_local ClassWithDtorForMainThread class_with_dtor_for_main_thread;
+#endif
   class_with_dtor_for_main_thread.set_message("d-tor for main thread called.");
   exit(0);
 }
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/dirent_test.cpp b/tests/dirent_test.cpp
index 214dd78..fa05ca1 100644
--- a/tests/dirent_test.cpp
+++ b/tests/dirent_test.cpp
@@ -81,6 +81,72 @@
   CheckProcSelf(name_set);
 }
 
+TEST(dirent, scandirat_scandirat64) {
+  // Get everything from /proc/self...
+  dirent** entries;
+  int entry_count = scandir("/proc/self", &entries, NULL, alphasort);
+  ASSERT_GE(entry_count, 0);
+
+  int proc_fd = open("/proc", O_DIRECTORY);
+  ASSERT_NE(-1, proc_fd);
+
+  dirent** entries_at;
+  int entry_count_at = scandirat(proc_fd, "self", &entries_at, NULL, alphasort);
+  ASSERT_EQ(entry_count, entry_count_at);
+
+  dirent64** entries_at64;
+  int entry_count_at64 = scandirat64(proc_fd, "self", &entries_at64, NULL, alphasort64);
+  ASSERT_EQ(entry_count, entry_count_at64);
+
+  close(proc_fd);
+
+  // scandirat and scandirat64 should return the same results as scandir.
+  std::set<std::string> name_set, name_set_at, name_set_at64;
+  std::vector<std::string> unsorted_name_list, unsorted_name_list_at, unsorted_name_list_at64;
+  ScanEntries(entries, entry_count, name_set, unsorted_name_list);
+  ScanEntries(entries_at, entry_count_at, name_set_at, unsorted_name_list_at);
+  ScanEntries(entries_at64, entry_count_at64, name_set_at64, unsorted_name_list_at64);
+
+  ASSERT_EQ(name_set, name_set_at);
+  ASSERT_EQ(name_set, name_set_at64);
+  ASSERT_EQ(unsorted_name_list, unsorted_name_list_at);
+  ASSERT_EQ(unsorted_name_list, unsorted_name_list_at64);
+}
+
+TEST(dirent, scandir_ENOENT) {
+  dirent** entries;
+  errno = 0;
+  ASSERT_EQ(-1, scandir("/does-not-exist", &entries, nullptr, nullptr));
+  ASSERT_EQ(ENOENT, errno);
+}
+
+TEST(dirent, scandir64_ENOENT) {
+  dirent64** entries;
+  errno = 0;
+  ASSERT_EQ(-1, scandir64("/does-not-exist", &entries, nullptr, nullptr));
+  ASSERT_EQ(ENOENT, errno);
+}
+
+TEST(dirent, scandirat_ENOENT) {
+  int root_fd = open("/", O_DIRECTORY | O_RDONLY);
+  ASSERT_NE(-1, root_fd);
+  dirent** entries;
+  errno = 0;
+  ASSERT_EQ(-1, scandirat(root_fd, "does-not-exist", &entries, nullptr, nullptr));
+  ASSERT_EQ(ENOENT, errno);
+  close(root_fd);
+}
+
+TEST(dirent, scandirat64_ENOENT) {
+  int root_fd = open("/", O_DIRECTORY | O_RDONLY);
+  ASSERT_NE(-1, root_fd);
+  dirent64** entries;
+  errno = 0;
+  ASSERT_EQ(-1, scandirat64(root_fd, "does-not-exist", &entries, nullptr, nullptr));
+  ASSERT_EQ(ENOENT, errno);
+  close(root_fd);
+}
+
 TEST(dirent, fdopendir_invalid) {
   ASSERT_TRUE(fdopendir(-1) == NULL);
   ASSERT_EQ(EBADF, errno);
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index d5a5e56..97b5208 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -32,6 +32,7 @@
 #include <pagemap/pagemap.h>
 
 #include "TemporaryFile.h"
+#include "utils.h"
 
 #define ASSERT_DL_NOTNULL(ptr) \
     ASSERT_TRUE(ptr != nullptr) << "dlerror: " << dlerror()
@@ -52,14 +53,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 +170,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);
 }
 
@@ -343,6 +349,43 @@
   EXPECT_EQ(4, f());
 }
 
+TEST_F(DlExtTest, LoadAtFixedAddress) {
+  void* start = mmap(nullptr, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS,
+                     -1, 0);
+  ASSERT_TRUE(start != MAP_FAILED);
+  munmap(start, LIBSIZE);
+
+  android_dlextinfo extinfo;
+  extinfo.flags = ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS;
+  extinfo.reserved_addr = start;
+
+  handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo);
+  ASSERT_DL_NOTNULL(handle_);
+  fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber"));
+  ASSERT_DL_NOTNULL(f);
+  EXPECT_GE(reinterpret_cast<void*>(f), start);
+  EXPECT_LT(reinterpret_cast<void*>(f), reinterpret_cast<char*>(start) + LIBSIZE);
+
+  EXPECT_EQ(4, f());
+}
+
+TEST_F(DlExtTest, LoadAtFixedAddressTooSmall) {
+  void* start = mmap(nullptr, LIBSIZE + PAGE_SIZE, PROT_NONE,
+                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  ASSERT_TRUE(start != MAP_FAILED);
+  munmap(start, LIBSIZE + PAGE_SIZE);
+  void* new_addr = mmap(reinterpret_cast<uint8_t*>(start) + PAGE_SIZE, LIBSIZE, PROT_NONE,
+                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  ASSERT_TRUE(new_addr != MAP_FAILED);
+
+  android_dlextinfo extinfo;
+  extinfo.flags = ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS;
+  extinfo.reserved_addr = start;
+
+  handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle_ == nullptr);
+}
+
 class DlExtRelroSharingTest : public DlExtTest {
 protected:
   virtual void SetUp() {
@@ -560,3 +603,298 @@
     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_namespaces(path.c_str(), nullptr));
+  ASSERT_STREQ("android_init_namespaces 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;
+
+  const std::string lib_public_path = lib_path + "/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();
+
+  // 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);
+
+  // dlopen for a public library using an absolute path should work for isolated namespaces
+  extinfo.library_namespace = ns2;
+  handle = android_dlopen_ext(lib_public_path.c_str(), RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle != nullptr) << dlerror();
+  ASSERT_TRUE(handle == handle_public);
+
+  dlclose(handle);
+
+  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);
+}
+
+extern "C" void android_set_application_target_sdk_version(uint32_t target);
+
+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;
+  const std::string lib_public_path = lib_path + "/public_namespace_libs/" + g_public_lib;
+  void* handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle_public != nullptr) << dlerror();
+
+  android_set_application_target_sdk_version(42U); // something > 23
+
+  ASSERT_TRUE(android_init_namespaces(path.c_str(), nullptr)) << 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 + "\" is not accessible for the namespace \"private_isolated1\"", 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 + "\" is not accessible for the namespace \"private_isolated2\"", 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);
+}
+
+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;
+
+  const std::string lib_path = std::string(getenv("ANDROID_DATA")) + NATIVE_TESTS_PATH;
+
+  const std::string lib_public_path = lib_path + "/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(), (lib_path + "/private_namespace_libs").c_str()))
+      << dlerror();
+
+  android_namespace_t* ns = android_create_namespace(
+                                "private", nullptr,
+                                (lib_path + "/private_namespace_libs").c_str(),
+                                false);
+
+  ASSERT_TRUE(ns != nullptr) << dlerror();
+
+  std::string private_library_absolute_path = lib_path + "/private_namespace_libs/" + root_lib;
+
+  android_dlextinfo extinfo;
+  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+  extinfo.library_namespace = ns;
+
+  // we are going to copy this library to anonymous mmap and call the copy of ns_get_dlopened_string
+  void* handle = android_dlopen_ext(private_library_absolute_path.c_str(), RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle != nullptr) << dlerror();
+
+  uintptr_t ns_get_dlopened_string_addr =
+      reinterpret_cast<uintptr_t>(dlsym(handle, "ns_get_dlopened_string"));
+  ASSERT_TRUE(ns_get_dlopened_string_addr != 0) << dlerror();
+  typedef const char* (*fn_t)();
+  fn_t ns_get_dlopened_string_private = reinterpret_cast<fn_t>(ns_get_dlopened_string_addr);
+
+  std::vector<map_record> maps;
+  Maps::parse_maps(&maps);
+
+  uintptr_t addr_start = 0;
+  uintptr_t addr_end = 0;
+  std::vector<map_record> maps_to_copy;
+
+  for (const auto& rec : maps) {
+    if (rec.pathname == private_library_absolute_path) {
+      if (addr_start == 0) {
+        addr_start = rec.addr_start;
+      }
+      addr_end = rec.addr_end;
+
+      maps_to_copy.push_back(rec);
+    }
+  }
+
+  // some sanity checks..
+  ASSERT_TRUE(addr_start > 0);
+  ASSERT_TRUE(addr_end > 0);
+  ASSERT_EQ(3U, maps_to_copy.size());
+  ASSERT_TRUE(ns_get_dlopened_string_addr > addr_start);
+  ASSERT_TRUE(ns_get_dlopened_string_addr < addr_end);
+
+  // copy
+  uintptr_t reserved_addr = reinterpret_cast<uintptr_t>(mmap(nullptr, addr_end - addr_start,
+                                                             PROT_NONE, MAP_ANON | MAP_PRIVATE,
+                                                             -1, 0));
+  ASSERT_TRUE(reinterpret_cast<void*>(reserved_addr) != MAP_FAILED);
+
+  for (const auto& rec : maps_to_copy) {
+    uintptr_t offset = rec.addr_start - addr_start;
+    size_t size = rec.addr_end - rec.addr_start;
+    void* addr = reinterpret_cast<void*>(reserved_addr + offset);
+    void* map = mmap(addr, size, PROT_READ | PROT_WRITE,
+                     MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
+    ASSERT_TRUE(map != MAP_FAILED);
+    memcpy(map, reinterpret_cast<void*>(rec.addr_start), size);
+    mprotect(map, size, rec.perms);
+  }
+
+  // call the function copy
+  uintptr_t ns_get_dlopened_string_offset  = ns_get_dlopened_string_addr - addr_start;
+  fn_t ns_get_dlopened_string_anon = reinterpret_cast<fn_t>(reserved_addr + ns_get_dlopened_string_offset);
+  ASSERT_STREQ("This string is from private namespace (dlopened library)",
+               ns_get_dlopened_string_anon());
+
+  // They should belong to different namespaces (private and anonymous)
+  ASSERT_STREQ("This string is from private namespace (dlopened library)",
+               ns_get_dlopened_string_private());
+
+  ASSERT_TRUE(ns_get_dlopened_string_anon() != ns_get_dlopened_string_private());
+}
diff --git a/tests/gtest_main.cpp b/tests/gtest_main.cpp
index 3b9f6b9..a662c73 100644
--- a/tests/gtest_main.cpp
+++ b/tests/gtest_main.cpp
@@ -575,27 +575,15 @@
   exit(result);
 }
 
-#if defined(__APPLE__)
-
-static int pipe2(int pipefd[2], int flags) {
-  int ret = pipe(pipefd);
-  if (ret != -1) {
-    ret = fcntl(pipefd[0], F_SETFL, flags);
-  }
-  if (ret != -1) {
-    ret = fcntl(pipefd[1], F_SETFL, flags);
-  }
-  return ret;
-}
-
-#endif
-
 static ChildProcInfo RunChildProcess(const std::string& test_name, int testcase_id, int test_id,
                                      int argc, char** argv) {
   int pipefd[2];
-  int ret = pipe2(pipefd, O_NONBLOCK);
-  if (ret == -1) {
-    perror("pipe2 in RunTestInSeparateProc");
+  if (pipe(pipefd) == -1) {
+    perror("pipe in RunTestInSeparateProc");
+    exit(1);
+  }
+  if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) == -1) {
+    perror("fcntl in RunTestInSeparateProc");
     exit(1);
   }
   pid_t pid = fork();
@@ -685,6 +673,30 @@
   return timeout_child_count;
 }
 
+static void ReadChildProcOutput(std::vector<TestCase>& testcase_list,
+                                std::vector<ChildProcInfo>& child_proc_list) {
+  for (const auto& child_proc : child_proc_list) {
+    TestCase& testcase = testcase_list[child_proc.testcase_id];
+    int test_id = child_proc.test_id;
+    while (true) {
+      char buf[1024];
+      ssize_t bytes_read = TEMP_FAILURE_RETRY(read(child_proc.child_read_fd, buf, sizeof(buf) - 1));
+      if (bytes_read > 0) {
+        buf[bytes_read] = '\0';
+        testcase.GetTest(test_id).AppendTestOutput(buf);
+      } else if (bytes_read == 0) {
+        break; // Read end.
+      } else {
+        if (errno == EAGAIN) {
+          break;
+        }
+        perror("failed to read child_read_fd");
+        exit(1);
+      }
+    }
+  }
+}
+
 static void WaitChildProcs(std::vector<TestCase>& testcase_list,
                            std::vector<ChildProcInfo>& child_proc_list) {
   size_t finished_child_count = 0;
@@ -709,6 +721,7 @@
       finished_child_count += CheckChildProcTimeout(child_proc_list);
     }
 
+    ReadChildProcOutput(testcase_list, child_proc_list);
     if (finished_child_count > 0) {
       return;
     }
@@ -742,26 +755,6 @@
     kill(child_proc.pid, SIGKILL);
     WaitForOneChild(child_proc.pid);
   }
-
-  while (true) {
-    char buf[1024];
-    ssize_t bytes_read = TEMP_FAILURE_RETRY(read(child_proc.child_read_fd, buf, sizeof(buf) - 1));
-    if (bytes_read > 0) {
-      buf[bytes_read] = '\0';
-      testcase.GetTest(test_id).AppendTestOutput(buf);
-    } else if (bytes_read == 0) {
-      break; // Read end.
-    } else {
-      if (errno == EAGAIN) {
-        // No data is available. This rarely happens, only when the child process created other
-        // processes which have not exited so far. But the child process has already exited or
-        // been killed, so the test has finished, and we shouldn't wait further.
-        break;
-      }
-      perror("read child_read_fd in RunTestInSeparateProc");
-      exit(1);
-    }
-  }
   close(child_proc.child_read_fd);
 
   if (child_proc.timed_out) {
@@ -780,8 +773,14 @@
     testcase.GetTest(test_id).AppendTestOutput(buf);
 
   } else {
-    testcase.SetTestResult(test_id, WEXITSTATUS(child_proc.exit_status) == 0 ?
-                           TEST_SUCCESS : TEST_FAILED);
+    int exitcode = WEXITSTATUS(child_proc.exit_status);
+    testcase.SetTestResult(test_id, exitcode == 0 ? TEST_SUCCESS : TEST_FAILED);
+    if (exitcode != 0) {
+      char buf[1024];
+      snprintf(buf, sizeof(buf), "%s exited with exitcode %d.\n",
+               testcase.GetTestName(test_id).c_str(), exitcode);
+      testcase.GetTest(test_id).AppendTestOutput(buf);
+    }
   }
 }
 
diff --git a/tests/libgen_basename_test.cpp b/tests/libgen_basename_test.cpp
new file mode 100644
index 0000000..d97e0da
--- /dev/null
+++ b/tests/libgen_basename_test.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef _GNU_SOURCE
+  #define _GNU_SOURCE 1
+#endif
+
+#include <string.h>
+
+#if defined(basename)
+  #error basename should not be defined at this point
+#endif
+
+static const char* gnu_basename(const char* in) {
+  return basename(in);
+}
+
+#include <libgen.h>
+
+#if !defined(basename)
+  #error basename should be defined at this point
+#endif
+
+static char* posix_basename(char* in) {
+  return basename(in);
+}
+
+#include <errno.h>
+#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;
+}
+
+static void __TestPosixBasename(const char* in, const char* expected_out, int line) {
+  char* writable_in = (in != NULL) ? strdup(in) : NULL;
+  errno = 0;
+  const char* out = posix_basename(&writable_in[0]);
+  ASSERT_STREQ(expected_out, out) << "(" << line << "): " << in << std::endl;
+  ASSERT_EQ(0, errno) << "(" << line << "): " << in << std::endl;
+  free(writable_in);
+}
+
+#define TestGnuBasename(in, expected) __TestGnuBasename(in, expected, __LINE__)
+#define TestPosixBasename(in, expected) __TestPosixBasename(in, expected, __LINE__)
+
+TEST(libgen_basename, gnu_basename) {
+  // GNU's basename doesn't accept NULL
+  // TestGnuBasename(NULL, ".");
+  TestGnuBasename("", "");
+  TestGnuBasename("/usr/lib", "lib");
+  TestGnuBasename("/system/bin/sh/", "");
+  TestGnuBasename("/usr/", "");
+  TestGnuBasename("usr", "usr");
+  TestGnuBasename("/", "");
+  TestGnuBasename(".", ".");
+  TestGnuBasename("..", "..");
+  TestGnuBasename("///", "");
+  TestGnuBasename("//usr//lib//", "");
+}
+
+TEST(libgen_basename, posix_basename) {
+  TestPosixBasename(NULL, ".");
+  TestPosixBasename("", ".");
+  TestPosixBasename("/usr/lib", "lib");
+  TestPosixBasename("/system/bin/sh/", "sh");
+  TestPosixBasename("/usr/", "usr");
+  TestPosixBasename("usr", "usr");
+  TestPosixBasename("/", "/");
+  TestPosixBasename(".", ".");
+  TestPosixBasename("..", "..");
+  TestPosixBasename("///", "/");
+  TestPosixBasename("//usr//lib//", "lib");
+}
diff --git a/tests/libgen_test.cpp b/tests/libgen_test.cpp
index e9a5d5c..8a37a3f 100644
--- a/tests/libgen_test.cpp
+++ b/tests/libgen_test.cpp
@@ -19,15 +19,6 @@
 #include <errno.h>
 #include <gtest/gtest.h>
 
-static void TestBasename(const char* in, const char* expected_out) {
-  char* writable_in = (in != NULL) ? strdup(in) : NULL;
-  errno = 0;
-  const char* out = basename(&writable_in[0]);
-  ASSERT_STREQ(expected_out, out) << in;
-  ASSERT_EQ(0, errno) << in;
-  free(writable_in);
-}
-
 static void TestDirname(const char* in, const char* expected_out) {
   char* writable_in = (in != NULL) ? strdup(in) : NULL;
   errno = 0;
@@ -37,21 +28,6 @@
   free(writable_in);
 }
 
-// Do not use basename as the test name, it's defined to another value in glibc
-// so leads to a differently named test on host versus target architectures.
-TEST(libgen, posix_basename) {
-  TestBasename(NULL, ".");
-  TestBasename("", ".");
-  TestBasename("/usr/lib", "lib");
-  TestBasename("/usr/", "usr");
-  TestBasename("usr", "usr");
-  TestBasename("/", "/");
-  TestBasename(".", ".");
-  TestBasename("..", "..");
-  TestBasename("///", "/");
-  TestBasename("//usr//lib//", "lib");
-}
-
 TEST(libgen, dirname) {
   TestDirname(NULL, ".");
   TestDirname("", ".");
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..b0006c7
--- /dev/null
+++ b/tests/libs/namespaces_root.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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) {
+    return nullptr;
+  } else {
+    g_dlopened = true;
+  }
+
+  return *result;
+}
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
old mode 100644
new mode 100755
index 9f887e3..27d992b
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -30,12 +30,9 @@
 #include <unwind.h>
 
 #include <atomic>
-#include <regex>
 #include <vector>
 
-#include <base/file.h>
-#include <base/stringprintf.h>
-
+#include "private/bionic_constants.h"
 #include "private/bionic_macros.h"
 #include "private/ScopeGuard.h"
 #include "BionicDeathTest.h"
@@ -43,8 +40,6 @@
 
 #include "utils.h"
 
-extern "C" pid_t gettid();
-
 TEST(pthread, pthread_key_create) {
   pthread_key_t key;
   ASSERT_EQ(0, pthread_key_create(&key, NULL));
@@ -164,7 +159,7 @@
   pthread_key_t key;
   ASSERT_EQ(0, pthread_key_create(&key, NULL));
 
-  size_t stack_size = 128 * 1024;
+  size_t stack_size = 640 * 1024;
   void* stack = mmap(NULL, stack_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
   ASSERT_NE(MAP_FAILED, stack);
   memset(stack, 0xff, stack_size);
@@ -222,13 +217,13 @@
     while (spin_flag_) {}
     return NULL;
   }
-  static volatile bool spin_flag_;
+  static std::atomic<bool> spin_flag_;
 };
 
 // It doesn't matter if spin_flag_ is used in several tests,
 // because it is always set to false after each test. Each thread
 // loops on spin_flag_ can find it becomes false at some time.
-volatile bool SpinFunctionHelper::spin_flag_ = false;
+std::atomic<bool> SpinFunctionHelper::spin_flag_;
 
 static void* JoinFn(void* arg) {
   return reinterpret_cast<void*>(pthread_join(reinterpret_cast<pthread_t>(arg), NULL));
@@ -421,6 +416,8 @@
   pthread_t t1;
   ASSERT_EQ(0, pthread_create(&t1, NULL, spinhelper.GetFunction(), NULL));
   ASSERT_EQ(0, pthread_setname_np(t1, "short 2"));
+  spinhelper.UnSpin();
+  ASSERT_EQ(0, pthread_join(t1, nullptr));
 }
 
 TEST(pthread, pthread_setname_np__no_such_thread) {
@@ -471,6 +468,8 @@
   ASSERT_EQ(0, pthread_getcpuclockid(t, &c));
   timespec ts;
   ASSERT_EQ(0, clock_gettime(c, &ts));
+  spinhelper.UnSpin();
+  ASSERT_EQ(0, pthread_join(t, nullptr));
 }
 
 TEST(pthread, pthread_getcpuclockid__no_such_thread) {
@@ -539,7 +538,7 @@
   // http://b/11693195 --- pthread_join could return before the thread had actually exited.
   // If the joiner unmapped the thread's stack, that could lead to SIGSEGV in the thread.
   for (size_t i = 0; i < 1024; ++i) {
-    size_t stack_size = 64*1024;
+    size_t stack_size = 640*1024;
     void* stack = mmap(NULL, stack_size, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
 
     pthread_attr_t a;
@@ -721,58 +720,47 @@
   ASSERT_EQ(0, pthread_rwlock_destroy(&l));
 }
 
-static void WaitUntilThreadSleep(std::atomic<pid_t>& pid) {
-  while (pid == 0) {
-    usleep(1000);
-  }
-  std::string filename = android::base::StringPrintf("/proc/%d/stat", pid.load());
-  std::regex regex {R"(\s+S\s+)"};
-
-  while (true) {
-    std::string content;
-    ASSERT_TRUE(android::base::ReadFileToString(filename, &content));
-    if (std::regex_search(content, regex)) {
-      break;
-    }
-    usleep(1000);
-  }
-}
-
 struct RwlockWakeupHelperArg {
   pthread_rwlock_t lock;
   enum Progress {
     LOCK_INITIALIZED,
     LOCK_WAITING,
     LOCK_RELEASED,
-    LOCK_ACCESSED
+    LOCK_ACCESSED,
+    LOCK_TIMEDOUT,
   };
   std::atomic<Progress> progress;
   std::atomic<pid_t> tid;
+  std::function<int (pthread_rwlock_t*)> trylock_function;
+  std::function<int (pthread_rwlock_t*)> lock_function;
+  std::function<int (pthread_rwlock_t*, const timespec*)> timed_lock_function;
 };
 
-static void pthread_rwlock_reader_wakeup_writer_helper(RwlockWakeupHelperArg* arg) {
+static void pthread_rwlock_wakeup_helper(RwlockWakeupHelperArg* arg) {
   arg->tid = gettid();
   ASSERT_EQ(RwlockWakeupHelperArg::LOCK_INITIALIZED, arg->progress);
   arg->progress = RwlockWakeupHelperArg::LOCK_WAITING;
 
-  ASSERT_EQ(EBUSY, pthread_rwlock_trywrlock(&arg->lock));
-  ASSERT_EQ(0, pthread_rwlock_wrlock(&arg->lock));
+  ASSERT_EQ(EBUSY, arg->trylock_function(&arg->lock));
+  ASSERT_EQ(0, arg->lock_function(&arg->lock));
   ASSERT_EQ(RwlockWakeupHelperArg::LOCK_RELEASED, arg->progress);
   ASSERT_EQ(0, pthread_rwlock_unlock(&arg->lock));
 
   arg->progress = RwlockWakeupHelperArg::LOCK_ACCESSED;
 }
 
-TEST(pthread, pthread_rwlock_reader_wakeup_writer) {
+static void test_pthread_rwlock_reader_wakeup_writer(std::function<int (pthread_rwlock_t*)> lock_function) {
   RwlockWakeupHelperArg wakeup_arg;
   ASSERT_EQ(0, pthread_rwlock_init(&wakeup_arg.lock, NULL));
   ASSERT_EQ(0, pthread_rwlock_rdlock(&wakeup_arg.lock));
   wakeup_arg.progress = RwlockWakeupHelperArg::LOCK_INITIALIZED;
   wakeup_arg.tid = 0;
+  wakeup_arg.trylock_function = pthread_rwlock_trywrlock;
+  wakeup_arg.lock_function = lock_function;
 
   pthread_t thread;
   ASSERT_EQ(0, pthread_create(&thread, NULL,
-    reinterpret_cast<void* (*)(void*)>(pthread_rwlock_reader_wakeup_writer_helper), &wakeup_arg));
+    reinterpret_cast<void* (*)(void*)>(pthread_rwlock_wakeup_helper), &wakeup_arg));
   WaitUntilThreadSleep(wakeup_arg.tid);
   ASSERT_EQ(RwlockWakeupHelperArg::LOCK_WAITING, wakeup_arg.progress);
 
@@ -784,29 +772,31 @@
   ASSERT_EQ(0, pthread_rwlock_destroy(&wakeup_arg.lock));
 }
 
-static void pthread_rwlock_writer_wakeup_reader_helper(RwlockWakeupHelperArg* arg) {
-  arg->tid = gettid();
-  ASSERT_EQ(RwlockWakeupHelperArg::LOCK_INITIALIZED, arg->progress);
-  arg->progress = RwlockWakeupHelperArg::LOCK_WAITING;
-
-  ASSERT_EQ(EBUSY, pthread_rwlock_tryrdlock(&arg->lock));
-  ASSERT_EQ(0, pthread_rwlock_rdlock(&arg->lock));
-  ASSERT_EQ(RwlockWakeupHelperArg::LOCK_RELEASED, arg->progress);
-  ASSERT_EQ(0, pthread_rwlock_unlock(&arg->lock));
-
-  arg->progress = RwlockWakeupHelperArg::LOCK_ACCESSED;
+TEST(pthread, pthread_rwlock_reader_wakeup_writer) {
+  test_pthread_rwlock_reader_wakeup_writer(pthread_rwlock_wrlock);
 }
 
-TEST(pthread, pthread_rwlock_writer_wakeup_reader) {
+TEST(pthread, pthread_rwlock_reader_wakeup_writer_timedwait) {
+  timespec ts;
+  ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
+  ts.tv_sec += 1;
+  test_pthread_rwlock_reader_wakeup_writer([&](pthread_rwlock_t* lock) {
+    return pthread_rwlock_timedwrlock(lock, &ts);
+  });
+}
+
+static void test_pthread_rwlock_writer_wakeup_reader(std::function<int (pthread_rwlock_t*)> lock_function) {
   RwlockWakeupHelperArg wakeup_arg;
   ASSERT_EQ(0, pthread_rwlock_init(&wakeup_arg.lock, NULL));
   ASSERT_EQ(0, pthread_rwlock_wrlock(&wakeup_arg.lock));
   wakeup_arg.progress = RwlockWakeupHelperArg::LOCK_INITIALIZED;
   wakeup_arg.tid = 0;
+  wakeup_arg.trylock_function = pthread_rwlock_tryrdlock;
+  wakeup_arg.lock_function = lock_function;
 
   pthread_t thread;
   ASSERT_EQ(0, pthread_create(&thread, NULL,
-    reinterpret_cast<void* (*)(void*)>(pthread_rwlock_writer_wakeup_reader_helper), &wakeup_arg));
+    reinterpret_cast<void* (*)(void*)>(pthread_rwlock_wakeup_helper), &wakeup_arg));
   WaitUntilThreadSleep(wakeup_arg.tid);
   ASSERT_EQ(RwlockWakeupHelperArg::LOCK_WAITING, wakeup_arg.progress);
 
@@ -818,6 +808,85 @@
   ASSERT_EQ(0, pthread_rwlock_destroy(&wakeup_arg.lock));
 }
 
+TEST(pthread, pthread_rwlock_writer_wakeup_reader) {
+  test_pthread_rwlock_writer_wakeup_reader(pthread_rwlock_rdlock);
+}
+
+TEST(pthread, pthread_rwlock_writer_wakeup_reader_timedwait) {
+  timespec ts;
+  ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
+  ts.tv_sec += 1;
+  test_pthread_rwlock_writer_wakeup_reader([&](pthread_rwlock_t* lock) {
+    return pthread_rwlock_timedrdlock(lock, &ts);
+  });
+}
+
+static void pthread_rwlock_wakeup_timeout_helper(RwlockWakeupHelperArg* arg) {
+  arg->tid = gettid();
+  ASSERT_EQ(RwlockWakeupHelperArg::LOCK_INITIALIZED, arg->progress);
+  arg->progress = RwlockWakeupHelperArg::LOCK_WAITING;
+
+  ASSERT_EQ(EBUSY, arg->trylock_function(&arg->lock));
+
+  timespec ts;
+  ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
+  ASSERT_EQ(ETIMEDOUT, arg->timed_lock_function(&arg->lock, &ts));
+  ts.tv_nsec = -1;
+  ASSERT_EQ(EINVAL, arg->timed_lock_function(&arg->lock, &ts));
+  ts.tv_nsec = NS_PER_S;
+  ASSERT_EQ(EINVAL, arg->timed_lock_function(&arg->lock, &ts));
+  ts.tv_nsec = NS_PER_S - 1;
+  ts.tv_sec = -1;
+  ASSERT_EQ(ETIMEDOUT, arg->timed_lock_function(&arg->lock, &ts));
+  ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
+  ts.tv_sec += 1;
+  ASSERT_EQ(ETIMEDOUT, arg->timed_lock_function(&arg->lock, &ts));
+  ASSERT_EQ(RwlockWakeupHelperArg::LOCK_WAITING, arg->progress);
+  arg->progress = RwlockWakeupHelperArg::LOCK_TIMEDOUT;
+}
+
+TEST(pthread, pthread_rwlock_timedrdlock_timeout) {
+  RwlockWakeupHelperArg wakeup_arg;
+  ASSERT_EQ(0, pthread_rwlock_init(&wakeup_arg.lock, nullptr));
+  ASSERT_EQ(0, pthread_rwlock_wrlock(&wakeup_arg.lock));
+  wakeup_arg.progress = RwlockWakeupHelperArg::LOCK_INITIALIZED;
+  wakeup_arg.tid = 0;
+  wakeup_arg.trylock_function = pthread_rwlock_tryrdlock;
+  wakeup_arg.timed_lock_function = pthread_rwlock_timedrdlock;
+
+  pthread_t thread;
+  ASSERT_EQ(0, pthread_create(&thread, nullptr,
+      reinterpret_cast<void* (*)(void*)>(pthread_rwlock_wakeup_timeout_helper), &wakeup_arg));
+  WaitUntilThreadSleep(wakeup_arg.tid);
+  ASSERT_EQ(RwlockWakeupHelperArg::LOCK_WAITING, wakeup_arg.progress);
+
+  ASSERT_EQ(0, pthread_join(thread, nullptr));
+  ASSERT_EQ(RwlockWakeupHelperArg::LOCK_TIMEDOUT, wakeup_arg.progress);
+  ASSERT_EQ(0, pthread_rwlock_unlock(&wakeup_arg.lock));
+  ASSERT_EQ(0, pthread_rwlock_destroy(&wakeup_arg.lock));
+}
+
+TEST(pthread, pthread_rwlock_timedwrlock_timeout) {
+  RwlockWakeupHelperArg wakeup_arg;
+  ASSERT_EQ(0, pthread_rwlock_init(&wakeup_arg.lock, nullptr));
+  ASSERT_EQ(0, pthread_rwlock_rdlock(&wakeup_arg.lock));
+  wakeup_arg.progress = RwlockWakeupHelperArg::LOCK_INITIALIZED;
+  wakeup_arg.tid = 0;
+  wakeup_arg.trylock_function = pthread_rwlock_trywrlock;
+  wakeup_arg.timed_lock_function = pthread_rwlock_timedwrlock;
+
+  pthread_t thread;
+  ASSERT_EQ(0, pthread_create(&thread, nullptr,
+      reinterpret_cast<void* (*)(void*)>(pthread_rwlock_wakeup_timeout_helper), &wakeup_arg));
+  WaitUntilThreadSleep(wakeup_arg.tid);
+  ASSERT_EQ(RwlockWakeupHelperArg::LOCK_WAITING, wakeup_arg.progress);
+
+  ASSERT_EQ(0, pthread_join(thread, nullptr));
+  ASSERT_EQ(RwlockWakeupHelperArg::LOCK_TIMEDOUT, wakeup_arg.progress);
+  ASSERT_EQ(0, pthread_rwlock_unlock(&wakeup_arg.lock));
+  ASSERT_EQ(0, pthread_rwlock_destroy(&wakeup_arg.lock));
+}
+
 class RwlockKindTestHelper {
  private:
   struct ThreadArg {
@@ -1062,36 +1131,44 @@
   };
   std::atomic<Progress> progress;
   pthread_t thread;
+  std::function<int (pthread_cond_t* cond, pthread_mutex_t* mutex)> wait_function;
 
  protected:
-  virtual void SetUp() {
-    ASSERT_EQ(0, pthread_mutex_init(&mutex, NULL));
-    ASSERT_EQ(0, pthread_cond_init(&cond, NULL));
+  void SetUp() override {
+    ASSERT_EQ(0, pthread_mutex_init(&mutex, nullptr));
+  }
+
+  void InitCond(clockid_t clock=CLOCK_REALTIME) {
+    pthread_condattr_t attr;
+    ASSERT_EQ(0, pthread_condattr_init(&attr));
+    ASSERT_EQ(0, pthread_condattr_setclock(&attr, clock));
+    ASSERT_EQ(0, pthread_cond_init(&cond, &attr));
+    ASSERT_EQ(0, pthread_condattr_destroy(&attr));
+  }
+
+  void StartWaitingThread(std::function<int (pthread_cond_t* cond, pthread_mutex_t* mutex)> wait_function) {
     progress = INITIALIZED;
-    ASSERT_EQ(0,
-      pthread_create(&thread, NULL, reinterpret_cast<void* (*)(void*)>(WaitThreadFn), this));
-  }
-
-  virtual void TearDown() {
-    ASSERT_EQ(0, pthread_join(thread, NULL));
-    ASSERT_EQ(FINISHED, progress);
-    ASSERT_EQ(0, pthread_cond_destroy(&cond));
-    ASSERT_EQ(0, pthread_mutex_destroy(&mutex));
-  }
-
-  void SleepUntilProgress(Progress expected_progress) {
-    while (progress != expected_progress) {
+    this->wait_function = wait_function;
+    ASSERT_EQ(0, pthread_create(&thread, NULL, reinterpret_cast<void* (*)(void*)>(WaitThreadFn), this));
+    while (progress != WAITING) {
       usleep(5000);
     }
     usleep(5000);
   }
 
+  void TearDown() override {
+    ASSERT_EQ(0, pthread_join(thread, nullptr));
+    ASSERT_EQ(FINISHED, progress);
+    ASSERT_EQ(0, pthread_cond_destroy(&cond));
+    ASSERT_EQ(0, pthread_mutex_destroy(&mutex));
+  }
+
  private:
   static void WaitThreadFn(pthread_CondWakeupTest* test) {
     ASSERT_EQ(0, pthread_mutex_lock(&test->mutex));
     test->progress = WAITING;
     while (test->progress == WAITING) {
-      ASSERT_EQ(0, pthread_cond_wait(&test->cond, &test->mutex));
+      ASSERT_EQ(0, test->wait_function(&test->cond, &test->mutex));
     }
     ASSERT_EQ(SIGNALED, test->progress);
     test->progress = FINISHED;
@@ -1099,39 +1176,65 @@
   }
 };
 
-TEST_F(pthread_CondWakeupTest, signal) {
-  SleepUntilProgress(WAITING);
+TEST_F(pthread_CondWakeupTest, signal_wait) {
+  InitCond();
+  StartWaitingThread([](pthread_cond_t* cond, pthread_mutex_t* mutex) {
+    return pthread_cond_wait(cond, mutex);
+  });
   progress = SIGNALED;
-  pthread_cond_signal(&cond);
+  ASSERT_EQ(0, pthread_cond_signal(&cond));
 }
 
-TEST_F(pthread_CondWakeupTest, broadcast) {
-  SleepUntilProgress(WAITING);
+TEST_F(pthread_CondWakeupTest, broadcast_wait) {
+  InitCond();
+  StartWaitingThread([](pthread_cond_t* cond, pthread_mutex_t* mutex) {
+    return pthread_cond_wait(cond, mutex);
+  });
   progress = SIGNALED;
-  pthread_cond_broadcast(&cond);
+  ASSERT_EQ(0, pthread_cond_broadcast(&cond));
 }
 
-TEST(pthread, pthread_mutex_timedlock) {
-  pthread_mutex_t m;
-  ASSERT_EQ(0, pthread_mutex_init(&m, NULL));
-
-  // If the mutex is already locked, pthread_mutex_timedlock should time out.
-  ASSERT_EQ(0, pthread_mutex_lock(&m));
-
+TEST_F(pthread_CondWakeupTest, signal_timedwait_CLOCK_REALTIME) {
+  InitCond(CLOCK_REALTIME);
   timespec ts;
   ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
-  ts.tv_nsec += 1;
-  ASSERT_EQ(ETIMEDOUT, pthread_mutex_timedlock(&m, &ts));
+  ts.tv_sec += 1;
+  StartWaitingThread([&](pthread_cond_t* cond, pthread_mutex_t* mutex) {
+    return pthread_cond_timedwait(cond, mutex, &ts);
+  });
+  progress = SIGNALED;
+  ASSERT_EQ(0, pthread_cond_signal(&cond));
+}
 
-  // If the mutex is unlocked, pthread_mutex_timedlock should succeed.
-  ASSERT_EQ(0, pthread_mutex_unlock(&m));
+TEST_F(pthread_CondWakeupTest, signal_timedwait_CLOCK_MONOTONIC) {
+  InitCond(CLOCK_MONOTONIC);
+  timespec ts;
+  ASSERT_EQ(0, clock_gettime(CLOCK_MONOTONIC, &ts));
+  ts.tv_sec += 1;
+  StartWaitingThread([&](pthread_cond_t* cond, pthread_mutex_t* mutex) {
+    return pthread_cond_timedwait(cond, mutex, &ts);
+  });
+  progress = SIGNALED;
+  ASSERT_EQ(0, pthread_cond_signal(&cond));
+}
 
+TEST(pthread, pthread_cond_timedwait_timeout) {
+  pthread_mutex_t mutex;
+  ASSERT_EQ(0, pthread_mutex_init(&mutex, nullptr));
+  pthread_cond_t cond;
+  ASSERT_EQ(0, pthread_cond_init(&cond, nullptr));
+  ASSERT_EQ(0, pthread_mutex_lock(&mutex));
+  timespec ts;
   ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
-  ts.tv_nsec += 1;
-  ASSERT_EQ(0, pthread_mutex_timedlock(&m, &ts));
-
-  ASSERT_EQ(0, pthread_mutex_unlock(&m));
-  ASSERT_EQ(0, pthread_mutex_destroy(&m));
+  ASSERT_EQ(ETIMEDOUT, pthread_cond_timedwait(&cond, &mutex, &ts));
+  ts.tv_nsec = -1;
+  ASSERT_EQ(EINVAL, pthread_cond_timedwait(&cond, &mutex, &ts));
+  ts.tv_nsec = NS_PER_S;
+  ASSERT_EQ(EINVAL, pthread_cond_timedwait(&cond, &mutex, &ts));
+  ts.tv_nsec = NS_PER_S - 1;
+  ts.tv_sec = -1;
+  ASSERT_EQ(ETIMEDOUT, pthread_cond_timedwait(&cond, &mutex, &ts));
+  ASSERT_EQ(0, pthread_mutex_unlock(&mutex));
 }
 
 TEST(pthread, pthread_attr_getstack__main_thread) {
@@ -1303,8 +1406,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 +1422,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);
@@ -1540,6 +1655,35 @@
 #endif
 }
 
+TEST(pthread, pthread_mutex_timedlock) {
+  pthread_mutex_t m;
+  ASSERT_EQ(0, pthread_mutex_init(&m, nullptr));
+
+  // If the mutex is already locked, pthread_mutex_timedlock should time out.
+  ASSERT_EQ(0, pthread_mutex_lock(&m));
+
+  timespec ts;
+  ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
+  ASSERT_EQ(ETIMEDOUT, pthread_mutex_timedlock(&m, &ts));
+  ts.tv_nsec = -1;
+  ASSERT_EQ(EINVAL, pthread_mutex_timedlock(&m, &ts));
+  ts.tv_nsec = NS_PER_S;
+  ASSERT_EQ(EINVAL, pthread_mutex_timedlock(&m, &ts));
+  ts.tv_nsec = NS_PER_S - 1;
+  ts.tv_sec = -1;
+  ASSERT_EQ(ETIMEDOUT, pthread_mutex_timedlock(&m, &ts));
+
+  // If the mutex is unlocked, pthread_mutex_timedlock should succeed.
+  ASSERT_EQ(0, pthread_mutex_unlock(&m));
+
+  ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
+  ts.tv_sec += 1;
+  ASSERT_EQ(0, pthread_mutex_timedlock(&m, &ts));
+
+  ASSERT_EQ(0, pthread_mutex_unlock(&m));
+  ASSERT_EQ(0, pthread_mutex_destroy(&m));
+}
+
 class StrictAlignmentAllocator {
  public:
   void* allocate(size_t size, size_t alignment) {
@@ -1653,3 +1797,128 @@
   kill(getpid(), SIGUSR1);
   ASSERT_TRUE(signal_handler_on_altstack_done);
 }
+
+TEST(pthread, pthread_barrierattr_smoke) {
+  pthread_barrierattr_t attr;
+  ASSERT_EQ(0, pthread_barrierattr_init(&attr));
+  int pshared;
+  ASSERT_EQ(0, pthread_barrierattr_getpshared(&attr, &pshared));
+  ASSERT_EQ(PTHREAD_PROCESS_PRIVATE, pshared);
+  ASSERT_EQ(0, pthread_barrierattr_setpshared(&attr, PTHREAD_PROCESS_SHARED));
+  ASSERT_EQ(0, pthread_barrierattr_getpshared(&attr, &pshared));
+  ASSERT_EQ(PTHREAD_PROCESS_SHARED, pshared);
+  ASSERT_EQ(0, pthread_barrierattr_destroy(&attr));
+}
+
+struct BarrierTestHelperArg {
+  std::atomic<pid_t> tid;
+  pthread_barrier_t* barrier;
+  size_t iteration_count;
+};
+
+static void BarrierTestHelper(BarrierTestHelperArg* arg) {
+  arg->tid = gettid();
+  for (size_t i = 0; i < arg->iteration_count; ++i) {
+    ASSERT_EQ(0, pthread_barrier_wait(arg->barrier));
+  }
+}
+
+TEST(pthread, pthread_barrier_smoke) {
+  const size_t BARRIER_ITERATION_COUNT = 10;
+  const size_t BARRIER_THREAD_COUNT = 10;
+  pthread_barrier_t barrier;
+  ASSERT_EQ(0, pthread_barrier_init(&barrier, nullptr, BARRIER_THREAD_COUNT + 1));
+  std::vector<pthread_t> threads(BARRIER_THREAD_COUNT);
+  std::vector<BarrierTestHelperArg> args(threads.size());
+  for (size_t i = 0; i < threads.size(); ++i) {
+    args[i].tid = 0;
+    args[i].barrier = &barrier;
+    args[i].iteration_count = BARRIER_ITERATION_COUNT;
+    ASSERT_EQ(0, pthread_create(&threads[i], nullptr,
+                                reinterpret_cast<void* (*)(void*)>(BarrierTestHelper), &args[i]));
+  }
+  for (size_t iteration = 0; iteration < BARRIER_ITERATION_COUNT; ++iteration) {
+    for (size_t i = 0; i < threads.size(); ++i) {
+      WaitUntilThreadSleep(args[i].tid);
+    }
+    ASSERT_EQ(PTHREAD_BARRIER_SERIAL_THREAD, pthread_barrier_wait(&barrier));
+  }
+  for (size_t i = 0; i < threads.size(); ++i) {
+    ASSERT_EQ(0, pthread_join(threads[i], nullptr));
+  }
+  ASSERT_EQ(0, pthread_barrier_destroy(&barrier));
+}
+
+TEST(pthread, pthread_barrier_destroy) {
+  pthread_barrier_t barrier;
+  ASSERT_EQ(0, pthread_barrier_init(&barrier, nullptr, 2));
+  pthread_t thread;
+  BarrierTestHelperArg arg;
+  arg.tid = 0;
+  arg.barrier = &barrier;
+  arg.iteration_count = 1;
+  ASSERT_EQ(0, pthread_create(&thread, nullptr,
+                              reinterpret_cast<void* (*)(void*)>(BarrierTestHelper), &arg));
+  WaitUntilThreadSleep(arg.tid);
+  ASSERT_EQ(EBUSY, pthread_barrier_destroy(&barrier));
+  ASSERT_EQ(PTHREAD_BARRIER_SERIAL_THREAD, pthread_barrier_wait(&barrier));
+  // Verify if the barrier can be destroyed directly after pthread_barrier_wait().
+  ASSERT_EQ(0, pthread_barrier_destroy(&barrier));
+  ASSERT_EQ(0, pthread_join(thread, nullptr));
+#if defined(__BIONIC__)
+  ASSERT_EQ(EINVAL, pthread_barrier_destroy(&barrier));
+#endif
+}
+
+struct BarrierOrderingTestHelperArg {
+  pthread_barrier_t* barrier;
+  size_t* array;
+  size_t array_length;
+  size_t id;
+};
+
+void BarrierOrderingTestHelper(BarrierOrderingTestHelperArg* arg) {
+  const size_t ITERATION_COUNT = 10000;
+  for (size_t i = 1; i <= ITERATION_COUNT; ++i) {
+    arg->array[arg->id] = i;
+    int result = pthread_barrier_wait(arg->barrier);
+    ASSERT_TRUE(result == 0 || result == PTHREAD_BARRIER_SERIAL_THREAD);
+    for (size_t j = 0; j < arg->array_length; ++j) {
+      ASSERT_EQ(i, arg->array[j]);
+    }
+    result = pthread_barrier_wait(arg->barrier);
+    ASSERT_TRUE(result == 0 || result == PTHREAD_BARRIER_SERIAL_THREAD);
+  }
+}
+
+TEST(pthread, pthread_barrier_check_ordering) {
+  const size_t THREAD_COUNT = 4;
+  pthread_barrier_t barrier;
+  ASSERT_EQ(0, pthread_barrier_init(&barrier, nullptr, THREAD_COUNT));
+  size_t array[THREAD_COUNT];
+  std::vector<pthread_t> threads(THREAD_COUNT);
+  std::vector<BarrierOrderingTestHelperArg> args(THREAD_COUNT);
+  for (size_t i = 0; i < THREAD_COUNT; ++i) {
+    args[i].barrier = &barrier;
+    args[i].array = array;
+    args[i].array_length = THREAD_COUNT;
+    args[i].id = i;
+    ASSERT_EQ(0, pthread_create(&threads[i], nullptr,
+                                reinterpret_cast<void* (*)(void*)>(BarrierOrderingTestHelper),
+                                &args[i]));
+  }
+  for (size_t i = 0; i < THREAD_COUNT; ++i) {
+    ASSERT_EQ(0, pthread_join(threads[i], nullptr));
+  }
+}
+
+TEST(pthread, pthread_spinlock_smoke) {
+  pthread_spinlock_t lock;
+  ASSERT_EQ(0, pthread_spin_init(&lock, 0));
+  ASSERT_EQ(0, pthread_spin_trylock(&lock));
+  ASSERT_EQ(0, pthread_spin_unlock(&lock));
+  ASSERT_EQ(0, pthread_spin_lock(&lock));
+  ASSERT_EQ(EBUSY, pthread_spin_trylock(&lock));
+  ASSERT_EQ(0, pthread_spin_unlock(&lock));
+  ASSERT_EQ(0, pthread_spin_destroy(&lock));
+}
diff --git a/tests/semaphore_test.cpp b/tests/semaphore_test.cpp
index e517f81..b65bfb8 100644
--- a/tests/semaphore_test.cpp
+++ b/tests/semaphore_test.cpp
@@ -117,6 +117,16 @@
   ts.tv_nsec = -1;
   ASSERT_EQ(-1, sem_timedwait(&s, &ts));
   ASSERT_EQ(EINVAL, errno);
+  errno = 0;
+  ts.tv_nsec = NS_PER_S;
+  ASSERT_EQ(-1, sem_timedwait(&s, &ts));
+  ASSERT_EQ(EINVAL, errno);
+
+  errno = 0;
+  ts.tv_nsec = NS_PER_S - 1;
+  ts.tv_sec = -1;
+  ASSERT_EQ(-1, sem_timedwait(&s, &ts));
+  ASSERT_EQ(ETIMEDOUT, errno);
 
   ASSERT_EQ(0, sem_destroy(&s));
 }
diff --git a/tests/stdio_ext_test.cpp b/tests/stdio_ext_test.cpp
index c95cbbd..7872567 100644
--- a/tests/stdio_ext_test.cpp
+++ b/tests/stdio_ext_test.cpp
@@ -30,6 +30,7 @@
 #include <locale.h>
 
 #include "TemporaryFile.h"
+#include "utils.h"
 
 TEST(stdio_ext, __fbufsize) {
   FILE* fp = fopen("/proc/version", "r");
@@ -140,3 +141,24 @@
   ASSERT_EQ(FSETLOCKING_INTERNAL, __fsetlocking(fp, FSETLOCKING_QUERY));
   fclose(fp);
 }
+
+static void LockingByCallerHelper(std::atomic<pid_t>* pid) {
+  *pid = gettid();
+  flockfile(stdout);
+  funlockfile(stdout);
+}
+
+TEST(stdio_ext, __fsetlocking_BYCALLER) {
+  // Check if users can use flockfile/funlockfile to protect stdio operations.
+  int old_state = __fsetlocking(stdout, FSETLOCKING_BYCALLER);
+  flockfile(stdout);
+  pthread_t thread;
+  std::atomic<pid_t> pid(0);
+  ASSERT_EQ(0, pthread_create(&thread, nullptr,
+                              reinterpret_cast<void* (*)(void*)>(LockingByCallerHelper), &pid));
+  WaitUntilThreadSleep(pid);
+  funlockfile(stdout);
+
+  ASSERT_EQ(0, pthread_join(thread, nullptr));
+  __fsetlocking(stdout, old_state);
+}
diff --git a/tests/sys_mman_test.cpp b/tests/sys_mman_test.cpp
index b0e40fd..ddb6c77 100644
--- a/tests/sys_mman_test.cpp
+++ b/tests/sys_mman_test.cpp
@@ -17,6 +17,7 @@
 #include <gtest/gtest.h>
 
 #include <sys/mman.h>
+#include <sys/user.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -215,3 +216,19 @@
 
   ASSERT_EQ(0, munmap(map, pagesize));
 }
+
+TEST(sys_mman, mremap) {
+  ASSERT_EQ(MAP_FAILED, mremap(nullptr, 0, 0, 0));
+}
+
+const size_t huge = size_t(PTRDIFF_MAX) + 1;
+
+TEST(sys_mman, mmap_PTRDIFF_MAX) {
+  ASSERT_EQ(MAP_FAILED, mmap(nullptr, huge, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+}
+
+TEST(sys_mman, mremap_PTRDIFF_MAX) {
+  void* map = mmap(nullptr, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  ASSERT_NE(MAP_FAILED, map);
+  ASSERT_EQ(MAP_FAILED, mremap(map, PAGE_SIZE, huge, MREMAP_MAYMOVE));
+}
diff --git a/tests/sys_resource_test.cpp b/tests/sys_resource_test.cpp
index 8cefc65..0b6b6ef 100644
--- a/tests/sys_resource_test.cpp
+++ b/tests/sys_resource_test.cpp
@@ -33,7 +33,8 @@
   virtual void SetUp() {
     ASSERT_EQ(0, getrlimit(RLIMIT_CORE, &l32_));
     ASSERT_EQ(0, getrlimit64(RLIMIT_CORE, &l64_));
-    ASSERT_EQ(0, prlimit64(0, RLIMIT_CORE, NULL, &pr_l64_));
+    ASSERT_EQ(0, prlimit(0, RLIMIT_CORE, nullptr, &pr_l32_));
+    ASSERT_EQ(0, prlimit64(0, RLIMIT_CORE, nullptr, &pr_l64_));
   }
 
   void CheckResourceLimits();
@@ -41,21 +42,28 @@
  protected:
   rlimit l32_;
   rlimit64 l64_;
+  rlimit pr_l32_;
   rlimit64 pr_l64_;
 };
 
 void SysResourceTest::CheckResourceLimits() {
   ASSERT_EQ(0, getrlimit(RLIMIT_CORE, &l32_));
   ASSERT_EQ(0, getrlimit64(RLIMIT_CORE, &l64_));
-  ASSERT_EQ(0, prlimit64(0, RLIMIT_CORE, NULL, &pr_l64_));
+  ASSERT_EQ(0, prlimit(0, RLIMIT_CORE, nullptr, &pr_l32_));
+  ASSERT_EQ(0, prlimit64(0, RLIMIT_CORE, nullptr, &pr_l64_));
+
+  ASSERT_EQ(l32_.rlim_cur, pr_l32_.rlim_cur);
   ASSERT_EQ(l64_.rlim_cur, pr_l64_.rlim_cur);
+
   if (l64_.rlim_cur == RLIM64_INFINITY) {
     ASSERT_EQ(RLIM_INFINITY, l32_.rlim_cur);
   } else {
     ASSERT_EQ(l64_.rlim_cur, l32_.rlim_cur);
   }
 
+  ASSERT_EQ(l32_.rlim_max, pr_l32_.rlim_max);
   ASSERT_EQ(l64_.rlim_max, pr_l64_.rlim_max);
+
   if (l64_.rlim_max == RLIM64_INFINITY) {
     ASSERT_EQ(RLIM_INFINITY, l32_.rlim_max);
   } else {
@@ -88,13 +96,16 @@
   ASSERT_EQ(456U, l64_.rlim_cur);
 }
 
-TEST_F(SysResourceTest, prlimit64) {
-  pr_l64_.rlim_cur = pr_l64_.rlim_max;
-  ASSERT_EQ(0, prlimit64(0, RLIMIT_CORE, &pr_l64_, NULL));
+TEST_F(SysResourceTest, prlimit) {
+  pr_l32_.rlim_cur = pr_l32_.rlim_max;
+  ASSERT_EQ(0, prlimit(0, RLIMIT_CORE, &pr_l32_, nullptr));
   CheckResourceLimits();
-  ASSERT_EQ(pr_l64_.rlim_max, pr_l64_.rlim_cur);
+  ASSERT_EQ(pr_l32_.rlim_max, pr_l32_.rlim_cur);
 }
 
-TEST_F(SysResourceTest, prlimit) {
-  // prlimit is prlimit64 on LP64 and unimplemented on 32-bit. So we only test prlimit64.
+TEST_F(SysResourceTest, prlimit64) {
+  pr_l64_.rlim_cur = pr_l64_.rlim_max;
+  ASSERT_EQ(0, prlimit64(0, RLIMIT_CORE, &pr_l64_, nullptr));
+  CheckResourceLimits();
+  ASSERT_EQ(pr_l64_.rlim_max, pr_l64_.rlim_cur);
 }
diff --git a/tests/system_properties_test.cpp b/tests/system_properties_test.cpp
index c7bfee6..09eac3f 100644
--- a/tests/system_properties_test.cpp
+++ b/tests/system_properties_test.cpp
@@ -41,9 +41,6 @@
             return;
         }
 
-        old_pa = __system_property_area__;
-        __system_property_area__ = NULL;
-
         pa_dirname = dirname;
         pa_filename = pa_dirname + "/__properties__";
 
@@ -57,9 +54,8 @@
             return;
         }
 
-        __system_property_area__ = old_pa;
-
         __system_property_set_filename(PROP_FILENAME);
+        __system_properties_init();
         unlink(pa_filename.c_str());
         rmdir(pa_dirname.c_str());
     }
@@ -68,7 +64,6 @@
 private:
     std::string pa_dirname;
     std::string pa_filename;
-    void *old_pa;
 };
 
 static void foreach_test_callback(const prop_info *pi, void* cookie) {
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);
 
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index 0a97abc..5e06b1f 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -30,8 +30,8 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include <base/file.h>
-#include <base/strings.h>
+#include <android-base/file.h>
+#include <android-base/strings.h>
 
 #include "private/get_cpu_count_from_string.h"
 
diff --git a/tests/utils.h b/tests/utils.h
index 53cf6b6..a8f3441 100644
--- a/tests/utils.h
+++ b/tests/utils.h
@@ -18,6 +18,14 @@
 #define __TEST_UTILS_H
 #include <inttypes.h>
 #include <sys/mman.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <string>
+#include <regex>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
 
 #include "private/ScopeGuard.h"
 
@@ -82,4 +90,23 @@
   }
 };
 
+extern "C" pid_t gettid();
+
+static inline void WaitUntilThreadSleep(std::atomic<pid_t>& tid) {
+  while (tid == 0) {
+    usleep(1000);
+  }
+  std::string filename = android::base::StringPrintf("/proc/%d/stat", tid.load());
+  std::regex regex {R"(\s+S\s+)"};
+
+  while (true) {
+    std::string content;
+    ASSERT_TRUE(android::base::ReadFileToString(filename, &content));
+    if (std::regex_search(content, regex)) {
+      break;
+    }
+    usleep(1000);
+  }
+}
+
 #endif
diff --git a/tests/utmp_test.cpp b/tests/utmp_test.cpp
index b61110d..0fa55c7 100644
--- a/tests/utmp_test.cpp
+++ b/tests/utmp_test.cpp
@@ -23,3 +23,9 @@
   // This test just checks that we're exporting the symbol independently.
   ASSERT_EQ(-1, login_tty(-1));
 }
+
+TEST(utmp, setutent_getutent_endutent) {
+  setutent();
+  getutent();
+  endutent();
+}