Merge changes I826756d9,I00e2edb2

* changes:
  libc_logging: mention that they're async signal safe.
  linker: update dependencies.
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 125b469..7b533a4 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,3 +1,2 @@
 [Hook Scripts]
-versioner = tools/versioner/preupload.sh
 notice = tools/update_notice.sh
diff --git a/libc/Android.bp b/libc/Android.bp
index 1ecef1f..9bba776 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -632,7 +632,22 @@
                 "upstream-openbsd/lib/libc/string/strncmp.c",
             ],
         },
-
+        mips: {
+            exclude_srcs: [
+                "upstream-openbsd/lib/libc/string/memchr.c",
+                "upstream-openbsd/lib/libc/string/memmove.c",
+                "upstream-openbsd/lib/libc/string/strcpy.c",
+                "upstream-openbsd/lib/libc/string/strncmp.c",
+            ],
+        },
+        mips64: {
+            exclude_srcs: [
+                "upstream-openbsd/lib/libc/string/memchr.c",
+                "upstream-openbsd/lib/libc/string/memmove.c",
+                "upstream-openbsd/lib/libc/string/strcpy.c",
+                "upstream-openbsd/lib/libc/string/strncmp.c",
+            ],
+        },
         x86: {
             exclude_srcs: [
                 "upstream-openbsd/lib/libc/string/memchr.c",
@@ -1041,9 +1056,16 @@
         mips: {
             srcs: [
                 "arch-mips/string/memcmp.c",
-                "arch-mips/string/memcpy.S",
+                "arch-mips/string/memcpy.c",
                 "arch-mips/string/memset.S",
                 "arch-mips/string/strcmp.S",
+                "arch-mips/string/strncmp.S",
+                "arch-mips/string/strlen.c",
+                "arch-mips/string/strnlen.c",
+                "arch-mips/string/strchr.c",
+                "arch-mips/string/strcpy.c",
+                "arch-mips/string/memchr.c",
+                "arch-mips/string/memmove.c",
 
                 "arch-mips/bionic/__bionic_clone.S",
                 "arch-mips/bionic/cacheflush.cpp",
@@ -1052,25 +1074,25 @@
                 "arch-mips/bionic/setjmp.S",
                 "arch-mips/bionic/syscall.S",
                 "arch-mips/bionic/vfork.S",
-
-                "arch-mips/string/mips_strlen.c",
             ],
-            rev6: {
-                srcs: [
-                    "arch-mips/string/strlen.c",
-                ],
-                exclude_srcs: [
-                    "arch-mips/string/mips_strlen.c",
-                ],
-            },
+            exclude_srcs: [
+                "bionic/strchr.cpp",
+                "bionic/strnlen.c",
+            ],
         },
         mips64: {
             srcs: [
                 "arch-mips/string/memcmp.c",
-                "arch-mips/string/memcpy.S",
+                "arch-mips/string/memcpy.c",
                 "arch-mips/string/memset.S",
                 "arch-mips/string/strcmp.S",
+                "arch-mips/string/strncmp.S",
                 "arch-mips/string/strlen.c",
+                "arch-mips/string/strnlen.c",
+                "arch-mips/string/strchr.c",
+                "arch-mips/string/strcpy.c",
+                "arch-mips/string/memchr.c",
+                "arch-mips/string/memmove.c",
 
                 "arch-mips64/bionic/__bionic_clone.S",
                 "arch-mips64/bionic/_exit_with_stack_teardown.S",
@@ -1079,6 +1101,10 @@
                 "arch-mips64/bionic/vfork.S",
                 "arch-mips64/bionic/stat.cpp",
             ],
+            exclude_srcs: [
+                "bionic/strchr.cpp",
+                "bionic/strnlen.c",
+            ],
         },
 
         x86: {
@@ -2010,15 +2036,12 @@
     defaults: ["crt_defaults"],
 }
 
-// The following module lives in prebuilts/ndk because we need to preprocess the
-// headers to include ifdef guards for __ANDROID_API__. Update with
-// bionic/tools/update_headers.sh.
-// ndk_headers {
-//     name: "common_libc",
-//     from: "include",
-//     to: "",
-//     srcs: ["include/**/*.h"],
-// }
+preprocessed_ndk_headers {
+    name: "common_libc",
+    from: "include",
+    to: "",
+    license: "NOTICE",
+}
 
 ndk_headers {
     name: "libc_uapi",
diff --git a/libc/NOTICE b/libc/NOTICE
index 9f0d2c5..2ce293f 100644
--- a/libc/NOTICE
+++ b/libc/NOTICE
@@ -4816,38 +4816,6 @@
 
 -------------------------------------------------------------------
 
-Copyright (c) 2010 MIPS Technologies, Inc.
-
-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.
-     * Neither the name of MIPS Technologies Inc. nor the names of its
-       contributors may be used to endorse or promote products derived
-       from this software without specific prior written permission.
-
-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.
-
--------------------------------------------------------------------
-
 Copyright (c) 2010 The NetBSD Foundation, Inc.
 All rights reserved.
 
@@ -5344,35 +5312,6 @@
 
 -------------------------------------------------------------------
 
-Copyright (c) 2012-2015
-     MIPS Technologies, Inc., California.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. 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.
-3. Neither the name of the MIPS Technologies, Inc., nor the names of its
-   contributors may be used to endorse or promote products derived from
-   this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE MIPS TECHNOLOGIES, INC. ``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 MIPS TECHNOLOGIES, INC. 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.
-
--------------------------------------------------------------------
-
 Copyright (c) 2013
      MIPS Technologies, Inc., California.
 
@@ -5586,35 +5525,6 @@
 
 -------------------------------------------------------------------
 
-Copyright (c) 2014
-     Imagination Technologies Limited.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. 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.
-3. Neither the name of the MIPS Technologies, Inc., nor the names of its
-   contributors may be used to endorse or promote products derived from
-   this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY IMAGINATION TECHNOLOGIES LIMITED ``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 IMAGINATION TECHNOLOGIES LIMITED 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.
-
--------------------------------------------------------------------
-
 Copyright (c) 2014 Theo de Raadt <deraadt@openbsd.org>
 Copyright (c) 2014 Bob Beck <beck@obtuse.com>
 
@@ -5750,6 +5660,38 @@
 
 -------------------------------------------------------------------
 
+Copyright (c) 2017 Imagination Technologies.
+
+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.
+     * Neither the name of Imagination Technologies nor the names of its
+       contributors may be used to endorse or promote products derived
+       from this software without specific prior written permission.
+
+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.
+
+-------------------------------------------------------------------
+
 Copyright (c)1999 Citrus Project,
 All rights reserved.
 
diff --git a/libc/SECCOMP_WHITELIST.TXT b/libc/SECCOMP_WHITELIST.TXT
index 4cb6620..ab6ce0b 100644
--- a/libc/SECCOMP_WHITELIST.TXT
+++ b/libc/SECCOMP_WHITELIST.TXT
@@ -25,73 +25,81 @@
 # This file is processed by a python script named gensyscalls.py.
 
 # syscalls needed to boot android
-int	pivot_root:pivot_root(const char *new_root, const char *put_old)	arm64
-int	ioprio_get:ioprio_get(int which, int who)	arm64
-int	ioprio_set:ioprio_set(int which, int who, int ioprio)	arm64
-pid_t	gettid:gettid()	arm64,arm
-int	futex:futex(int *uaddr, int futex_op, int val, const struct timespec *timeout, int *uaddr2, int val3)	arm64,arm
-int	clone:clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ..) arm64,arm
-int	rt_sigreturn:rt_sigreturn(unsigned long __unused)	arm64,arm
-int	rt_tgsigqueueinfo:int rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, siginfo_t *uinfo)	arm64,arm
-int	restart_syscall:int restart_syscall()	arm64,arm
-int	getrandom:int getrandom(void *buf, size_t buflen, unsigned int flags) arm64,arm
+int	pivot_root:pivot_root(const char *new_root, const char *put_old)	arm64,x86_64,mips64
+int	ioprio_get:ioprio_get(int which, int who)	arm64,x86_64,mips64
+int	ioprio_set:ioprio_set(int which, int who, int ioprio)	arm64,x86_64,mips64
+pid_t	gettid:gettid()	all
+int	futex:futex(int *uaddr, int futex_op, int val, const struct timespec *timeout, int *uaddr2, int val3)	all
+int	clone:clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ..) all
+int	rt_sigreturn:rt_sigreturn(unsigned long __unused)	all
+int	rt_tgsigqueueinfo:int rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, siginfo_t *uinfo)	all
+int	restart_syscall:int restart_syscall()	all
+int	getrandom:int getrandom(void *buf, size_t buflen, unsigned int flags) all
 
 # Needed for performance tools
-int	perf_event_open:perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags)	arm64,arm
+int	perf_event_open:perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags)	all
 
 # Needed for strace
-int	tkill:tkill(int tid, int sig)	arm64,arm
+int	tkill:tkill(int tid, int sig)	all
 
 # b/35034743
-int	syncfs:syncfs(int fd)	arm64,arm
+int	syncfs:syncfs(int fd)	all
 
 # b/34763393
-int	seccomp:seccomp(unsigned int operation, unsigned int flags, void *args)	arm64,arm
+int	seccomp:seccomp(unsigned int operation, unsigned int flags, void *args)	all
 
 # syscalls needed to boot android
-int	sigreturn:sigreturn(unsigned long __unused)	arm
+int	sigreturn:sigreturn(unsigned long __unused)	arm,x86,mips
 
 # Syscalls needed to run GFXBenchmark
-pid_t	vfork:vfork()	arm
+pid_t	vfork:vfork()	arm,x86,x86_64
 
 # Needed for debugging 32-bit Chrome
-int	pipe:pipe(int pipefd[2])	arm
+int	pipe:pipe(int pipefd[2])	arm,x86,mips
 
 # b/34651972
-int	access:access(const char *pathname, int mode)	arm
-int	stat64:stat64(const char *restrict path, struct stat64 *restrict buf)	arm
+int	access:access(const char *pathname, int mode)	arm,x86,mips
+int	stat64:stat64(const char *restrict path, struct stat64 *restrict buf)	arm,x86,mips
 
 # b/34813887
-int	open:open(const char *path, int oflag, ... ) arm
-int	getdents:getdents(unsigned int fd, struct linux_dirent *dirp, unsigned int count) arm
-int	getdents64:getdents64(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count) arm
+int	open:open(const char *path, int oflag, ... ) arm,x86,mips
+int	getdents:getdents(unsigned int fd, struct linux_dirent *dirp, unsigned int count) arm,x86,mips
 
 # b/34719286
-int	eventfd:eventfd(unsigned int initval, int flags)	arm
+int	eventfd:eventfd(unsigned int initval, int flags)	arm,x86,mips
 
 # b/34817266
-int	epoll_wait:epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)	arm
+int	epoll_wait:epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)	arm,x86,mips
 
 # Needed by sanitizers (b/34606909)
 # 5 (__NR_open) and 195 (__NR_stat64) are also required, but they are
 # already allowed.
-ssize_t	readlink:readlink(const char *path, char *buf, size_t bufsiz)	arm
+ssize_t	readlink:readlink(const char *path, char *buf, size_t bufsiz)	arm,x86,mips
 
 # b/34908783
-int	epoll_create:epoll_create(int size)	arm
+int	epoll_create:epoll_create(int size)	arm,x86,mips
 
 # b/34979910
-int	creat:creat(const char *pathname, mode_t mode)	arm
-int	unlink:unlink(const char *pathname)	arm
+int	creat:creat(const char *pathname, mode_t mode)	arm,x86,mips
+int	unlink:unlink(const char *pathname)	arm,x86,mips
 
 # b/35059702
-int	lstat64:lstat64(const char *restrict path, struct stat64 *restrict buf)	arm
+int	lstat64:lstat64(const char *restrict path, struct stat64 *restrict buf)	arm,x86,mips
 
 # b/35217603
-int	fcntl:fcntl(int fd, int cmd, ... /* arg */ )	arm
-pid_t	fork:fork()	arm
-int	poll:poll(struct pollfd *fds, nfds_t nfds, int timeout)	arm
+int	fcntl:fcntl(int fd, int cmd, ... /* arg */ )	arm,x86,mips
+pid_t	fork:fork()	arm,x86,mips
+int	poll:poll(struct pollfd *fds, nfds_t nfds, int timeout)	arm,x86,mips
 
-# b/35906875
-int	inotify_init()	arm
-uid_t	getuid()	arm
+# b/35906875. Note mips already has getuid from SYSCALLS.TXT
+int	inotify_init()	arm,x86,mips
+uid_t	getuid()	arm,x86
+
+# b/36435222
+int	remap_file_pages(void *addr, size_t size, int prot, size_t pgoff, int flags)	arm,x86,mips
+
+# b/36449658
+int	rename(const char *oldpath, const char *newpath)	arm,x86,mips
+
+# b/36726183. Note arm does not support mmap
+void*	mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)	x86,mips
diff --git a/libc/arch-arm64/generic/bionic/strcmp.S b/libc/arch-arm64/generic/bionic/strcmp.S
index 3cce478..271452d 100644
--- a/libc/arch-arm64/generic/bionic/strcmp.S
+++ b/libc/arch-arm64/generic/bionic/strcmp.S
@@ -57,6 +57,7 @@
 
 	/* Start of performance-critical section  -- one 64B cache line.  */
 ENTRY(strcmp)
+.p2align  6
 	eor	tmp1, src1, src2
 	mov	zeroones, #REP8_01
 	tst	tmp1, #7
diff --git a/libc/arch-mips/string/memchr.c b/libc/arch-mips/string/memchr.c
new file mode 100644
index 0000000..6b4c8cc
--- /dev/null
+++ b/libc/arch-mips/string/memchr.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2017 Imagination Technologies.
+ *
+ * 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.
+ *      * Neither the name of Imagination Technologies nor the names of its
+ *        contributors may be used to endorse or promote products derived
+ *        from this software without specific prior written permission.
+ *
+ * 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 <string.h>
+
+#define ENABLE_PREFETCH     1
+#define op_t                unsigned long int
+#define op_size             sizeof (op_t)
+
+#if ENABLE_PREFETCH
+#define PREFETCH(addr)  __builtin_prefetch (addr, 0, 1);
+#else
+#define PREFETCH(addr)
+#endif
+
+#if __mips64 || __mips_isa_rev >= 2
+static inline void * __attribute__ ((always_inline))
+do_bytes (const op_t* w, op_t inval)
+{
+  const unsigned char *p = (const unsigned char *) w;
+  op_t outval = 0;
+#if __mips64
+  __asm__ volatile (
+    "dsbh %1, %0 \n\t"
+    "dshd %0, %1 \n\t"
+    "dclz %1, %0 \n\t"
+    : "+r" (inval), "+r" (outval)
+  );
+#else
+  __asm__ volatile (
+    "wsbh %1, %0 \n\t"
+    "rotr %0, %1, 16 \n\t"
+    "clz %1, %0 \n\t"
+    : "+r" (inval), "+r" (outval)
+  );
+#endif
+  p += (outval >> 3);
+  return (void *) p;
+}
+
+#define DO_WORD(in, val) {                        \
+  op_t tmp = ((val - mask_1) & ~val) & mask_128;  \
+  if (tmp != 0)                                   \
+    return do_bytes(in, tmp);                     \
+}
+#else
+static inline void * __attribute__ ((always_inline))
+do_bytes (const op_t* w, unsigned char ch)
+{
+  const unsigned char *p = (const unsigned char *) w;
+  for (; *p != ch; ++p);
+  return (void *) p;
+}
+
+#define DO_WORD(in, val) {                        \
+  op_t tmp = ((val - mask_1) & ~val) & mask_128;  \
+  if (tmp != 0)                                   \
+    return do_bytes(in, ch);                      \
+}
+#endif
+
+#define DO_WORDS(w) {          \
+  op_t* w1 = (op_t*) w;        \
+  op_t val0 = w1[0] ^ mask_c;  \
+  op_t val1 = w1[1] ^ mask_c;  \
+  op_t val2 = w1[2] ^ mask_c;  \
+  op_t val3 = w1[3] ^ mask_c;  \
+  DO_WORD(w1, val0)            \
+  DO_WORD(w1 + 1, val1)        \
+  DO_WORD(w1 + 2, val2)        \
+  DO_WORD(w1 + 3, val3)        \
+}
+
+void *
+memchr (void const *s, int c_in, size_t n) __overloadable
+{
+  if (n != 0) {
+    const unsigned char *p = (const unsigned char *) s;
+    const op_t *w;
+    op_t mask_1, mask_128, mask_c;
+    unsigned char ch = (unsigned char) c_in;
+
+    /*
+     * Check bytewize till initial alignment
+     */
+    for (; n > 0 && ((size_t) p % op_size) != 0; --n, ++p) {
+      if (*p == ch)
+        return (void *) p;
+    }
+
+    w = (const op_t *) p;
+
+    mask_c = ch | (ch << 8);
+    mask_c |= mask_c << 16;
+    __asm__ volatile (
+      "li %0, 0x01010101 \n\t"
+      : "=r" (mask_1)
+    );
+#if __mips64
+    mask_1 |= mask_1 << 32;
+    mask_c |= mask_c << 32;
+#endif
+    mask_128 = mask_1 << 7;
+
+    /*
+     * Check op_size byteswize after initial alignment
+     */
+#if ((_MIPS_SIM == _ABIO32) || _MIPS_TUNE_I6400)
+    PREFETCH (w);
+    PREFETCH (w + 8);
+    while (n >= 24 * op_size) {
+      PREFETCH(w + 16);
+      DO_WORDS(w);
+      DO_WORDS(w + 4);
+      w += 8;
+      n -= 8 * op_size;
+    }
+    while (n >= 8 * op_size) {
+      DO_WORDS(w);
+      DO_WORDS(w + 4);
+      w += 8;
+      n -= 8 * op_size;
+    }
+#else
+    PREFETCH (w);
+    PREFETCH (w + 4);
+    while (n >= 12 * op_size) {
+      PREFETCH(w + 8);
+      DO_WORDS(w);
+      w += 4;
+      n -= 4 * op_size;
+    }
+    while (n >= 4 * op_size) {
+      DO_WORDS(w);
+      w += 4;
+      n -= 4 * op_size;
+    }
+#endif
+
+    while (n >= op_size) {
+      op_t val = *w ^ mask_c;
+      DO_WORD(w, val);
+      w++;
+      n -= op_size;
+    }
+
+    /*
+     * Check bytewize for remaining bytes
+     */
+    p = (const unsigned char *) w;
+    for (; n > 0; --n, ++p) {
+      if (*p == ch)
+        return (void *) p;
+    }
+  }
+  return NULL;
+}
diff --git a/libc/arch-mips/string/memcmp.c b/libc/arch-mips/string/memcmp.c
index 8640954..eb4ad07 100644
--- a/libc/arch-mips/string/memcmp.c
+++ b/libc/arch-mips/string/memcmp.c
@@ -1,51 +1,352 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (c) 2017 Imagination Technologies.
+ *
  * 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.
+ *
+ *      * 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.
+ *      * Neither the name of Imagination Technologies nor the names of its
+ *        contributors may be used to endorse or promote products derived
+ *        from this software without specific prior written permission.
  *
  * 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.
+ * 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 <string.h>
+#include <stdint.h>
 
-int memcmp(const void *s1, const void *s2, size_t n)
+#define ENABLE_PREFETCH 1
+
+#define STRNG(X) #X
+#define PREFETCH(src_ptr, offset)  \
+  asm("pref 0, " STRNG(offset) "(%[src]) \n\t" : : [src] "r" (src_ptr));
+
+#if !defined(UNALIGNED_INSTR_SUPPORT)
+/* does target have unaligned lw/ld/ualw/uald instructions? */
+#define UNALIGNED_INSTR_SUPPORT 0
+#if __mips_isa_rev < 6 && !__mips1
+#undef UNALIGNED_INSTR_SUPPORT
+#define UNALIGNED_INSTR_SUPPORT 1
+#endif
+#endif
+
+#if !defined(HW_UNALIGNED_SUPPORT)
+/* Does target have hardware support for unaligned accesses?  */
+#define HW_UNALIGNED_SUPPORT 0
+#if __mips_isa_rev >= 6
+#undef HW_UNALIGNED_SUPPORT
+#define HW_UNALIGNED_SUPPORT 1
+#endif
+#endif
+
+#define SIZEOF_reg_t 4
+#if _MIPS_SIM == _ABIO32
+typedef unsigned long reg_t;
+typedef struct bits
 {
-    const unsigned char*  p1   = s1;
-    const unsigned char*  end1 = p1 + n;
-    const unsigned char*  p2   = s2;
-    int                   d = 0;
+  reg_t B0:8, B1:8, B2:8, B3:8;
+} bits_t;
+#else
+#undef SIZEOF_reg_t
+#define SIZEOF_reg_t 8
+typedef unsigned long long reg_t;
+typedef struct bits
+{
+    reg_t B0:8, B1:8, B2:8, B3:8, B4:8, B5:8, B6:8, B7:8;
+} bits_t;
+#endif
 
-    for (;;) {
-        if (d || p1 >= end1) break;
-        d = (int)*p1++ - (int)*p2++;
+/* This union assumes that small structures can be in registers.  If
+   not, then memory accesses will be done - not optimal, but ok.  */
+typedef union
+{
+  reg_t v;
+  bits_t b;
+} bitfields_t;
 
-        if (d || p1 >= end1) break;
-        d = (int)*p1++ - (int)*p2++;
+#define do_bitfield(__i) \
+  if (x.b.B##__i != y.b.B##__i) return x.b.B##__i - y.b.B##__i;
 
-        if (d || p1 >= end1) break;
-        d = (int)*p1++ - (int)*p2++;
+/* pull apart the words to find the first differing unsigned byte.  */
+static int __attribute__ ((noinline)) do_by_bitfields (reg_t a, reg_t b)
+{
+  bitfields_t x, y;
+  x.v = a;
+  y.v = b;
+  do_bitfield (0);
+  do_bitfield (1);
+  do_bitfield (2);
+#if SIZEOF_reg_t == 4
+  return x.b.B3 - y.b.B3;
+#else
+  do_bitfield (3);
+  do_bitfield (4);
+  do_bitfield (5);
+  do_bitfield (6);
+  return x.b.B7 - y.b.B7;
+#endif
+}
 
-        if (d || p1 >= end1) break;
-        d = (int)*p1++ - (int)*p2++;
-    }
-    return d;
+/* This code is called when aligning a pointer, there are remaining bytes
+   after doing word compares, or architecture does not have some form
+   of unaligned support.  */
+static inline int __attribute__ ((always_inline))
+do_bytes (const void *a, const void *b, unsigned long len)
+{
+  unsigned char *x = (unsigned char *) a;
+  unsigned char *y = (unsigned char *) b;
+  unsigned long i;
+
+  /* 'len' might be zero here, so preloading the first two values
+     before the loop may access unallocated memory.  */
+  for (i = 0; i < len; i++) {
+    if (*x != *y)
+      return *x - *y;
+    x++;
+    y++;
+  }
+  return 0;
+}
+
+#if !HW_UNALIGNED_SUPPORT
+#if UNALIGNED_INSTR_SUPPORT
+/* for MIPS GCC, there are no unaligned builtins - so this struct forces
+   the compiler to treat the pointer access as unaligned.  */
+struct ulw
+{
+  reg_t uli;
+} __attribute__ ((packed));
+
+/* first pointer is not aligned while second pointer is.  */
+static int unaligned_words (const struct ulw *a, const reg_t *b,
+                            unsigned long words, unsigned long bytes)
+{
+#if ENABLE_PREFETCH
+  /* prefetch pointer aligned to 32 byte boundary */
+  const reg_t *pref_ptr = (const reg_t *) (((uintptr_t) b + 31) & ~31);
+  const reg_t *pref_ptr_a = (const reg_t *) (((uintptr_t) a + 31) & ~31);
+#endif
+  for (; words >= 16; words -= 8) {
+#if ENABLE_PREFETCH
+    pref_ptr += 8;
+    PREFETCH(pref_ptr, 0);
+    PREFETCH(pref_ptr, 32);
+
+    pref_ptr_a += 8;
+    PREFETCH(pref_ptr_a, 0);
+    PREFETCH(pref_ptr_a, 32);
+#endif
+    reg_t x0 = a[0].uli, x1 = a[1].uli;
+    reg_t x2 = a[2].uli, x3 = a[3].uli;
+    reg_t y0 = b[0], y1 = b[1], y2 = b[2], y3 = b[3];
+    if (x0 != y0)
+      return do_by_bitfields (x0, y0);
+    if (x1 != y1)
+      return do_by_bitfields (x1, y1);
+    if (x2 != y2)
+      return do_by_bitfields (x2, y2);
+    if (x3 != y3)
+      return do_by_bitfields (x3, y3);
+
+    x0 = a[4].uli; x1 = a[5].uli;
+    x2 = a[6].uli; x3 = a[7].uli;
+    y0 = b[4]; y1 = b[5]; y2 = b[6]; y3 = b[7];
+    if (x0 != y0)
+      return do_by_bitfields (x0, y0);
+    if (x1 != y1)
+      return do_by_bitfields (x1, y1);
+    if (x2 != y2)
+      return do_by_bitfields (x2, y2);
+    if (x3 != y3)
+      return do_by_bitfields (x3, y3);
+
+    a += 8;
+    b += 8;
+  }
+
+  for (; words >= 4; words -= 4) {
+    reg_t x0 = a[0].uli, x1 = a[1].uli;
+    reg_t x2 = a[2].uli, x3 = a[3].uli;
+    reg_t y0 = b[0], y1 = b[1], y2 = b[2], y3 = b[3];
+    if (x0 != y0)
+      return do_by_bitfields (x0, y0);
+    if (x1 != y1)
+      return do_by_bitfields (x1, y1);
+    if (x2 != y2)
+      return do_by_bitfields (x2, y2);
+    if (x3 != y3)
+      return do_by_bitfields (x3, y3);
+    a += 4;
+    b += 4;
+  }
+
+  /* do remaining words.  */
+  while (words--) {
+    reg_t x0 = a->uli;
+    reg_t y0 = *b;
+    a += 1;
+    b += 1;
+    if (x0 != y0)
+      return do_by_bitfields (x0, y0);
+  }
+
+  /* mop up any remaining bytes.  */
+  return do_bytes (a, b, bytes);
+}
+#else
+/* no HW support or unaligned lw/ld/ualw/uald instructions.  */
+static int unaligned_words (const reg_t *a, const reg_t *b,
+                            unsigned long words, unsigned long bytes)
+{
+  return do_bytes (a, b, (sizeof (reg_t) * words) + bytes);
+}
+#endif /* UNALIGNED_INSTR_SUPPORT */
+#endif /* HW_UNALIGNED_SUPPORT */
+
+/* both pointers are aligned, or first isn't and HW support for unaligned.  */
+static int aligned_words (const reg_t *a, const reg_t *b,
+                          unsigned long words, unsigned long bytes)
+{
+#if ENABLE_PREFETCH
+  /* prefetch pointer aligned to 32 byte boundary */
+  const reg_t *pref_ptr = (const reg_t *) (((uintptr_t) b + 31) & ~31);
+  const reg_t *pref_ptr_a = (const reg_t *) (((uintptr_t) a + 31) & ~31);
+#endif
+
+  for (; words >= 24; words -= 12) {
+#if ENABLE_PREFETCH
+    pref_ptr += 12;
+    PREFETCH(pref_ptr, 0);
+    PREFETCH(pref_ptr, 32);
+    PREFETCH(pref_ptr, 64);
+
+    pref_ptr_a += 12;
+    PREFETCH(pref_ptr_a, 0);
+    PREFETCH(pref_ptr_a, 32);
+    PREFETCH(pref_ptr_a, 64);
+#endif
+    reg_t x0 = a[0], x1 = a[1], x2 = a[2], x3 = a[3];
+    reg_t y0 = b[0], y1 = b[1], y2 = b[2], y3 = b[3];
+    if (x0 != y0)
+      return do_by_bitfields (x0, y0);
+    if (x1 != y1)
+      return do_by_bitfields (x1, y1);
+    if (x2 != y2)
+      return do_by_bitfields (x2, y2);
+    if (x3 != y3)
+      return do_by_bitfields (x3, y3);
+
+    x0 = a[4]; x1 = a[5]; x2 = a[6]; x3 = a[7];
+    y0 = b[4]; y1 = b[5]; y2 = b[6]; y3 = b[7];
+    if (x0 != y0)
+      return do_by_bitfields (x0, y0);
+    if (x1 != y1)
+      return do_by_bitfields (x1, y1);
+    if (x2 != y2)
+      return do_by_bitfields (x2, y2);
+    if (x3 != y3)
+      return do_by_bitfields (x3, y3);
+
+    x0 = a[8]; x1 = a[9]; x2 = a[10]; x3 = a[11];
+    y0 = b[8]; y1 = b[9]; y2 = b[10]; y3 = b[11];
+    if (x0 != y0)
+      return do_by_bitfields (x0, y0);
+    if (x1 != y1)
+      return do_by_bitfields (x1, y1);
+    if (x2 != y2)
+      return do_by_bitfields (x2, y2);
+    if (x3 != y3)
+      return do_by_bitfields (x3, y3);
+
+    a += 12;
+    b += 12;
+  }
+
+  for (; words >= 4; words -= 4) {
+    reg_t x0 = a[0], x1 = a[1], x2 = a[2], x3 = a[3];
+    reg_t y0 = b[0], y1 = b[1], y2 = b[2], y3 = b[3];
+    if (x0 != y0)
+      return do_by_bitfields (x0, y0);
+    if (x1 != y1)
+      return do_by_bitfields (x1, y1);
+    if (x2 != y2)
+      return do_by_bitfields (x2, y2);
+    if (x3 != y3)
+      return do_by_bitfields (x3, y3);
+    a += 4;
+    b += 4;
+  }
+
+  /* do remaining words.  */
+  while (words--) {
+    reg_t x0 = *a;
+    reg_t y0 = *b;
+    a += 1;
+    b += 1;
+    if (x0 != y0)
+      return do_by_bitfields (x0, y0);
+  }
+
+  /* mop up any remaining bytes.  */
+  return do_bytes (a, b, bytes);
+}
+
+int memcmp (const void *a, const void *b, size_t len)
+{
+  unsigned long bytes, words;
+
+  /* shouldn't hit that often.  */
+  if (len < sizeof (reg_t) * 4) {
+    return do_bytes (a, b, len);
+  }
+
+  /* Align the second pointer to word/dword alignment.
+     Note that the pointer is only 32-bits for o32/n32 ABIs. For
+     n32, loads are done as 64-bit while address remains 32-bit.   */
+  bytes = ((unsigned long) b) % sizeof (reg_t);
+  if (bytes) {
+    int res;
+    bytes = sizeof (reg_t) - bytes;
+    if (bytes > len)
+      bytes = len;
+    res = do_bytes (a, b, bytes);
+    if (res || len == bytes)
+      return res;
+    len -= bytes;
+    a = (const void *) (((unsigned char *) a) + bytes);
+    b = (const void *) (((unsigned char *) b) + bytes);
+  }
+
+  /* Second pointer now aligned.  */
+  words = len / sizeof (reg_t);
+  bytes = len % sizeof (reg_t);
+
+#if HW_UNALIGNED_SUPPORT
+  /* treat possible unaligned first pointer as aligned.  */
+  return aligned_words (a, b, words, bytes);
+#else
+  if (((unsigned long) a) % sizeof (reg_t) == 0) {
+    return aligned_words (a, b, words, bytes);
+  }
+  /* need to use unaligned instructions on first pointer.  */
+  return unaligned_words (a, b, words, bytes);
+#endif
 }
diff --git a/libc/arch-mips/string/memcpy.S b/libc/arch-mips/string/memcpy.S
deleted file mode 100644
index 0b711bd..0000000
--- a/libc/arch-mips/string/memcpy.S
+++ /dev/null
@@ -1,852 +0,0 @@
-/*
- * Copyright (c) 2012-2015
- *      MIPS Technologies, Inc., California.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. Neither the name of the MIPS Technologies, Inc., nor the names of its
- *    contributors may be used to endorse or promote products derived from
- *    this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE MIPS TECHNOLOGIES, INC. ``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 MIPS TECHNOLOGIES, INC. 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.
- */
-
-#ifdef __ANDROID__
-# include <private/bionic_asm.h>
-# define USE_MEMMOVE_FOR_OVERLAP
-# define PREFETCH_LOAD_HINT PREFETCH_HINT_LOAD_STREAMED
-# define PREFETCH_STORE_HINT PREFETCH_HINT_PREPAREFORSTORE
-#elif _LIBC
-# include <sysdep.h>
-# include <regdef.h>
-# include <sys/asm.h>
-# define PREFETCH_LOAD_HINT PREFETCH_HINT_LOAD_STREAMED
-# define PREFETCH_STORE_HINT PREFETCH_HINT_PREPAREFORSTORE
-#elif _COMPILING_NEWLIB
-# include "machine/asm.h"
-# include "machine/regdef.h"
-# define PREFETCH_LOAD_HINT PREFETCH_HINT_LOAD_STREAMED
-# define PREFETCH_STORE_HINT PREFETCH_HINT_PREPAREFORSTORE
-#else
-# include <regdef.h>
-# include <sys/asm.h>
-#endif
-
-/* Check to see if the MIPS architecture we are compiling for supports
- * prefetching.
- */
-
-#if (__mips == 4) || (__mips == 5) || (__mips == 32) || (__mips == 64)
-# ifndef DISABLE_PREFETCH
-#  define USE_PREFETCH
-# endif
-#endif
-
-#if defined(_MIPS_SIM) && ((_MIPS_SIM == _ABI64) || (_MIPS_SIM == _ABIN32))
-# ifndef DISABLE_DOUBLE
-#  define USE_DOUBLE
-# endif
-#endif
-
-
-#if __mips_isa_rev > 5
-# if (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE)
-#  undef PREFETCH_STORE_HINT
-#  define PREFETCH_STORE_HINT PREFETCH_HINT_STORE_STREAMED
-# endif
-# define R6_CODE
-#endif
-
-/* Some asm.h files do not have the L macro definition.  */
-#ifndef L
-# if _MIPS_SIM == _ABIO32
-#  define L(label) $L ## label
-# else
-#  define L(label) .L ## label
-# endif
-#endif
-
-/* Some asm.h files do not have the PTR_ADDIU macro definition.  */
-#ifndef PTR_ADDIU
-# if _MIPS_SIM == _ABIO32
-#  define PTR_ADDIU	addiu
-# else
-#  define PTR_ADDIU	daddiu
-# endif
-#endif
-
-/* Some asm.h files do not have the PTR_SRA macro definition.  */
-#ifndef PTR_SRA
-# if  _MIPS_SIM == _ABIO32
-#  define PTR_SRA	sra
-# else
-#  define PTR_SRA	dsra
-# endif
-#endif
-
-/* New R6 instructions that may not be in asm.h.  */
-#ifndef PTR_LSA
-# if _MIPS_SIM == _ABIO32
-#  define PTR_LSA	lsa
-# else
-#  define PTR_LSA	dlsa
-# endif
-#endif
-
-/*
- * Using PREFETCH_HINT_LOAD_STREAMED instead of PREFETCH_LOAD on load
- * prefetches appears to offer a slight preformance advantage.
- *
- * Using PREFETCH_HINT_PREPAREFORSTORE instead of PREFETCH_STORE
- * or PREFETCH_STORE_STREAMED offers a large performance advantage
- * but PREPAREFORSTORE has some special restrictions to consider.
- *
- * Prefetch with the 'prepare for store' hint does not copy a memory
- * location into the cache, it just allocates a cache line and zeros
- * it out.  This means that if you do not write to the entire cache
- * line before writing it out to memory some data will get zero'ed out
- * when the cache line is written back to memory and data will be lost.
- *
- * Also if you are using this memcpy to copy overlapping buffers it may
- * not behave correctly when using the 'prepare for store' hint.  If you
- * use the 'prepare for store' prefetch on a memory area that is in the
- * memcpy source (as well as the memcpy destination), then you will get
- * some data zero'ed out before you have a chance to read it and data will
- * be lost.
- *
- * If you are going to use this memcpy routine with the 'prepare for store'
- * prefetch you may want to set USE_MEMMOVE_FOR_OVERLAP in order to avoid
- * the problem of running memcpy on overlapping buffers.
- *
- * There are ifdef'ed sections of this memcpy to make sure that it does not
- * do prefetches on cache lines that are not going to be completely written.
- * This code is only needed and only used when PREFETCH_STORE_HINT is set to
- * PREFETCH_HINT_PREPAREFORSTORE.  This code assumes that cache lines are
- * 32 bytes and if the cache line is larger it will not work correctly.
- */
-
-#ifdef USE_PREFETCH
-# define PREFETCH_HINT_LOAD		0
-# define PREFETCH_HINT_STORE		1
-# define PREFETCH_HINT_LOAD_STREAMED	4
-# define PREFETCH_HINT_STORE_STREAMED	5
-# define PREFETCH_HINT_LOAD_RETAINED	6
-# define PREFETCH_HINT_STORE_RETAINED	7
-# define PREFETCH_HINT_WRITEBACK_INVAL	25
-# define PREFETCH_HINT_PREPAREFORSTORE	30
-
-/*
- * If we have not picked out what hints to use at this point use the
- * standard load and store prefetch hints.
- */
-# ifndef PREFETCH_STORE_HINT
-#  define PREFETCH_STORE_HINT PREFETCH_HINT_STORE
-# endif
-# ifndef PREFETCH_LOAD_HINT
-#  define PREFETCH_LOAD_HINT PREFETCH_HINT_LOAD
-# endif
-
-/*
- * We double everything when USE_DOUBLE is true so we do 2 prefetches to
- * get 64 bytes in that case.  The assumption is that each individual
- * prefetch brings in 32 bytes.
- */
-
-# ifdef USE_DOUBLE
-#  define PREFETCH_CHUNK 64
-#  define PREFETCH_FOR_LOAD(chunk, reg) \
- pref PREFETCH_LOAD_HINT, (chunk)*64(reg); \
- pref PREFETCH_LOAD_HINT, ((chunk)*64)+32(reg)
-#  define PREFETCH_FOR_STORE(chunk, reg) \
- pref PREFETCH_STORE_HINT, (chunk)*64(reg); \
- pref PREFETCH_STORE_HINT, ((chunk)*64)+32(reg)
-# else
-#  define PREFETCH_CHUNK 32
-#  define PREFETCH_FOR_LOAD(chunk, reg) \
- pref PREFETCH_LOAD_HINT, (chunk)*32(reg)
-#  define PREFETCH_FOR_STORE(chunk, reg) \
- pref PREFETCH_STORE_HINT, (chunk)*32(reg)
-# endif
-/* MAX_PREFETCH_SIZE is the maximum size of a prefetch, it must not be less
- * than PREFETCH_CHUNK, the assumed size of each prefetch.  If the real size
- * of a prefetch is greater than MAX_PREFETCH_SIZE and the PREPAREFORSTORE
- * hint is used, the code will not work correctly.  If PREPAREFORSTORE is not
- * used then MAX_PREFETCH_SIZE does not matter.  */
-# define MAX_PREFETCH_SIZE 128
-/* PREFETCH_LIMIT is set based on the fact that we never use an offset greater
- * than 5 on a STORE prefetch and that a single prefetch can never be larger
- * than MAX_PREFETCH_SIZE.  We add the extra 32 when USE_DOUBLE is set because
- * we actually do two prefetches in that case, one 32 bytes after the other.  */
-# ifdef USE_DOUBLE
-#  define PREFETCH_LIMIT (5 * PREFETCH_CHUNK) + 32 + MAX_PREFETCH_SIZE
-# else
-#  define PREFETCH_LIMIT (5 * PREFETCH_CHUNK) + MAX_PREFETCH_SIZE
-# endif
-# if (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE) \
-    && ((PREFETCH_CHUNK * 4) < MAX_PREFETCH_SIZE)
-/* We cannot handle this because the initial prefetches may fetch bytes that
- * are before the buffer being copied.  We start copies with an offset
- * of 4 so avoid this situation when using PREPAREFORSTORE.  */
-#error "PREFETCH_CHUNK is too large and/or MAX_PREFETCH_SIZE is too small."
-# endif
-#else /* USE_PREFETCH not defined */
-# define PREFETCH_FOR_LOAD(offset, reg)
-# define PREFETCH_FOR_STORE(offset, reg)
-#endif
-
-/* Allow the routine to be named something else if desired.  */
-#ifndef MEMCPY_NAME
-# define MEMCPY_NAME memcpy
-#endif
-
-/* We use these 32/64 bit registers as temporaries to do the copying.  */
-#define REG0 t0
-#define REG1 t1
-#define REG2 t2
-#define REG3 t3
-#if defined(_MIPS_SIM) && (_MIPS_SIM == _ABIO32 || _MIPS_SIM == _ABIO64)
-# define REG4 t4
-# define REG5 t5
-# define REG6 t6
-# define REG7 t7
-#else
-# define REG4 ta0
-# define REG5 ta1
-# define REG6 ta2
-# define REG7 ta3
-#endif
-
-/* We load/store 64 bits at a time when USE_DOUBLE is true.
- * The C_ prefix stands for CHUNK and is used to avoid macro name
- * conflicts with system header files.  */
-
-#ifdef USE_DOUBLE
-# define C_ST	sd
-# define C_LD	ld
-# if __MIPSEB
-#  define C_LDHI	ldl	/* high part is left in big-endian	*/
-#  define C_STHI	sdl	/* high part is left in big-endian	*/
-#  define C_LDLO	ldr	/* low part is right in big-endian	*/
-#  define C_STLO	sdr	/* low part is right in big-endian	*/
-# else
-#  define C_LDHI	ldr	/* high part is right in little-endian	*/
-#  define C_STHI	sdr	/* high part is right in little-endian	*/
-#  define C_LDLO	ldl	/* low part is left in little-endian	*/
-#  define C_STLO	sdl	/* low part is left in little-endian	*/
-# endif
-# define C_ALIGN	dalign	/* r6 align instruction			*/
-#else
-# define C_ST	sw
-# define C_LD	lw
-# if __MIPSEB
-#  define C_LDHI	lwl	/* high part is left in big-endian	*/
-#  define C_STHI	swl	/* high part is left in big-endian	*/
-#  define C_LDLO	lwr	/* low part is right in big-endian	*/
-#  define C_STLO	swr	/* low part is right in big-endian	*/
-# else
-#  define C_LDHI	lwr	/* high part is right in little-endian	*/
-#  define C_STHI	swr	/* high part is right in little-endian	*/
-#  define C_LDLO	lwl	/* low part is left in little-endian	*/
-#  define C_STLO	swl	/* low part is left in little-endian	*/
-# endif
-# define C_ALIGN	align	/* r6 align instruction			*/
-#endif
-
-/* Bookkeeping values for 32 vs. 64 bit mode.  */
-#ifdef USE_DOUBLE
-# define NSIZE 8
-# define NSIZEMASK 0x3f
-# define NSIZEDMASK 0x7f
-#else
-# define NSIZE 4
-# define NSIZEMASK 0x1f
-# define NSIZEDMASK 0x3f
-#endif
-#define UNIT(unit) ((unit)*NSIZE)
-#define UNITM1(unit) (((unit)*NSIZE)-1)
-
-#ifdef __ANDROID__
-LEAF(MEMCPY_NAME, 0)
-#else
-LEAF(MEMCPY_NAME)
-#endif
-	.set	nomips16
-	.set	noreorder
-/*
- * Below we handle the case where memcpy is called with overlapping src and dst.
- * Although memcpy is not required to handle this case, some parts of Android
- * like Skia rely on such usage. We call memmove to handle such cases.
- */
-#ifdef USE_MEMMOVE_FOR_OVERLAP
-	PTR_SUBU t0,a0,a1
-	PTR_SRA	t2,t0,31
-	xor	t1,t0,t2
-	PTR_SUBU t0,t1,t2
-	sltu	t2,t0,a2
-	beq	t2,zero,L(memcpy)
-	nop
-#if defined(__LP64__)
-	daddiu	sp,sp,-8
-	SETUP_GP64(0,MEMCPY_NAME)
-	LA	t9,memmove
-	RESTORE_GP64
-	jr	t9
-	daddiu	sp,sp,8
-#else
-	LA	t9,memmove
-	jr	t9
-	nop
-#endif
-L(memcpy):
-#endif
-/*
- * If the size is less than 2*NSIZE (8 or 16), go to L(lastb).  Regardless of
- * size, copy dst pointer to v0 for the return value.
- */
-	slti	t2,a2,(2 * NSIZE)
-	bne	t2,zero,L(lastb)
-#if defined(RETURN_FIRST_PREFETCH) || defined(RETURN_LAST_PREFETCH)
-	move	v0,zero
-#else
-	move	v0,a0
-#endif
-
-#ifndef R6_CODE
-
-/*
- * If src and dst have different alignments, go to L(unaligned), if they
- * have the same alignment (but are not actually aligned) do a partial
- * load/store to make them aligned.  If they are both already aligned
- * we can start copying at L(aligned).
- */
-	xor	t8,a1,a0
-	andi	t8,t8,(NSIZE-1)		/* t8 is a0/a1 word-displacement */
-	bne	t8,zero,L(unaligned)
-	PTR_SUBU a3, zero, a0
-
-	andi	a3,a3,(NSIZE-1)		/* copy a3 bytes to align a0/a1	  */
-	beq	a3,zero,L(aligned)	/* if a3=0, it is already aligned */
-	PTR_SUBU a2,a2,a3		/* a2 is the remining bytes count */
-
-	C_LDHI	t8,0(a1)
-	PTR_ADDU a1,a1,a3
-	C_STHI	t8,0(a0)
-	PTR_ADDU a0,a0,a3
-
-#else /* R6_CODE */
-
-/*
- * Align the destination and hope that the source gets aligned too.  If it
- * doesn't we jump to L(r6_unaligned*) to do unaligned copies using the r6
- * align instruction.
- */
-	andi	t8,a0,7
-	lapc	t9,L(atable)
-	PTR_LSA	t9,t8,t9,2
-	jrc	t9
-L(atable):
-	bc	L(lb0)
-	bc	L(lb7)
-	bc	L(lb6)
-	bc	L(lb5)
-	bc	L(lb4)
-	bc	L(lb3)
-	bc	L(lb2)
-	bc	L(lb1)
-L(lb7):
-	lb	a3, 6(a1)
-	sb	a3, 6(a0)
-L(lb6):
-	lb	a3, 5(a1)
-	sb	a3, 5(a0)
-L(lb5):
-	lb	a3, 4(a1)
-	sb	a3, 4(a0)
-L(lb4):
-	lb	a3, 3(a1)
-	sb	a3, 3(a0)
-L(lb3):
-	lb	a3, 2(a1)
-	sb	a3, 2(a0)
-L(lb2):
-	lb	a3, 1(a1)
-	sb	a3, 1(a0)
-L(lb1):
-	lb	a3, 0(a1)
-	sb	a3, 0(a0)
-
-	li	t9,8
-	subu	t8,t9,t8
-	PTR_SUBU a2,a2,t8
-	PTR_ADDU a0,a0,t8
-	PTR_ADDU a1,a1,t8
-L(lb0):
-
-	andi	t8,a1,(NSIZE-1)
-	lapc	t9,L(jtable)
-	PTR_LSA	t9,t8,t9,2
-	jrc	t9
-L(jtable):
-	bc	L(aligned)
-	bc	L(r6_unaligned1)
-	bc	L(r6_unaligned2)
-	bc	L(r6_unaligned3)
-# ifdef USE_DOUBLE
-	bc	L(r6_unaligned4)
-	bc	L(r6_unaligned5)
-	bc	L(r6_unaligned6)
-	bc	L(r6_unaligned7)
-# endif
-#endif /* R6_CODE */
-
-L(aligned):
-
-/*
- * Now dst/src are both aligned to (word or double word) aligned addresses
- * Set a2 to count how many bytes we have to copy after all the 64/128 byte
- * chunks are copied and a3 to the dst pointer after all the 64/128 byte
- * chunks have been copied.  We will loop, incrementing a0 and a1 until a0
- * equals a3.
- */
-
-	andi	t8,a2,NSIZEDMASK /* any whole 64-byte/128-byte chunks? */
-	beq	a2,t8,L(chkw)	 /* if a2==t8, no 64-byte/128-byte chunks */
-	PTR_SUBU a3,a2,t8	 /* subtract from a2 the reminder */
-	PTR_ADDU a3,a0,a3	 /* Now a3 is the final dst after loop */
-
-/* When in the loop we may prefetch with the 'prepare to store' hint,
- * in this case the a0+x should not be past the "t0-32" address.  This
- * means: for x=128 the last "safe" a0 address is "t0-160".  Alternatively,
- * for x=64 the last "safe" a0 address is "t0-96" In the current version we
- * will use "prefetch hint,128(a0)", so "t0-160" is the limit.
- */
-#if defined(USE_PREFETCH) && (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE)
-	PTR_ADDU t0,a0,a2		/* t0 is the "past the end" address */
-	PTR_SUBU t9,t0,PREFETCH_LIMIT	/* t9 is the "last safe pref" address */
-#endif
-	PREFETCH_FOR_LOAD  (0, a1)
-	PREFETCH_FOR_LOAD  (1, a1)
-	PREFETCH_FOR_LOAD  (2, a1)
-	PREFETCH_FOR_LOAD  (3, a1)
-#if defined(USE_PREFETCH) && (PREFETCH_STORE_HINT != PREFETCH_HINT_PREPAREFORSTORE)
-	PREFETCH_FOR_STORE (1, a0)
-	PREFETCH_FOR_STORE (2, a0)
-	PREFETCH_FOR_STORE (3, a0)
-#endif
-#if defined(RETURN_FIRST_PREFETCH) && defined(USE_PREFETCH)
-# if PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE
-	sltu    v1,t9,a0
-	bgtz    v1,L(skip_set)
-	nop
-	PTR_ADDIU v0,a0,(PREFETCH_CHUNK*4)
-L(skip_set):
-# else
-	PTR_ADDIU v0,a0,(PREFETCH_CHUNK*1)
-# endif
-#endif
-#if defined(RETURN_LAST_PREFETCH) && defined(USE_PREFETCH) \
-    && (PREFETCH_STORE_HINT != PREFETCH_HINT_PREPAREFORSTORE)
-	PTR_ADDIU v0,a0,(PREFETCH_CHUNK*3)
-# ifdef USE_DOUBLE
-	PTR_ADDIU v0,v0,32
-# endif
-#endif
-L(loop16w):
-	C_LD	t0,UNIT(0)(a1)
-#if defined(USE_PREFETCH) && (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE)
-	sltu	v1,t9,a0		/* If a0 > t9 don't use next prefetch */
-	bgtz	v1,L(skip_pref)
-#endif
-	C_LD	t1,UNIT(1)(a1)
-#ifndef R6_CODE
-	PREFETCH_FOR_STORE (4, a0)
-	PREFETCH_FOR_STORE (5, a0)
-#else
-	PREFETCH_FOR_STORE (2, a0)
-#endif
-#if defined(RETURN_LAST_PREFETCH) && defined(USE_PREFETCH)
-	PTR_ADDIU v0,a0,(PREFETCH_CHUNK*5)
-# ifdef USE_DOUBLE
-	PTR_ADDIU v0,v0,32
-# endif
-#endif
-L(skip_pref):
-	C_LD	REG2,UNIT(2)(a1)
-	C_LD	REG3,UNIT(3)(a1)
-	C_LD	REG4,UNIT(4)(a1)
-	C_LD	REG5,UNIT(5)(a1)
-	C_LD	REG6,UNIT(6)(a1)
-	C_LD	REG7,UNIT(7)(a1)
-#ifndef R6_CODE
-	PREFETCH_FOR_LOAD (4, a1)
-#else
-	PREFETCH_FOR_LOAD (3, a1)
-#endif
-	C_ST	t0,UNIT(0)(a0)
-	C_ST	t1,UNIT(1)(a0)
-	C_ST	REG2,UNIT(2)(a0)
-	C_ST	REG3,UNIT(3)(a0)
-	C_ST	REG4,UNIT(4)(a0)
-	C_ST	REG5,UNIT(5)(a0)
-	C_ST	REG6,UNIT(6)(a0)
-	C_ST	REG7,UNIT(7)(a0)
-
-	C_LD	t0,UNIT(8)(a1)
-	C_LD	t1,UNIT(9)(a1)
-	C_LD	REG2,UNIT(10)(a1)
-	C_LD	REG3,UNIT(11)(a1)
-	C_LD	REG4,UNIT(12)(a1)
-	C_LD	REG5,UNIT(13)(a1)
-	C_LD	REG6,UNIT(14)(a1)
-	C_LD	REG7,UNIT(15)(a1)
-#ifndef R6_CODE
-	PREFETCH_FOR_LOAD (5, a1)
-#endif
-	C_ST	t0,UNIT(8)(a0)
-	C_ST	t1,UNIT(9)(a0)
-	C_ST	REG2,UNIT(10)(a0)
-	C_ST	REG3,UNIT(11)(a0)
-	C_ST	REG4,UNIT(12)(a0)
-	C_ST	REG5,UNIT(13)(a0)
-	C_ST	REG6,UNIT(14)(a0)
-	C_ST	REG7,UNIT(15)(a0)
-	PTR_ADDIU a0,a0,UNIT(16)	/* adding 64/128 to dest */
-	bne	a0,a3,L(loop16w)
-	PTR_ADDIU a1,a1,UNIT(16)	/* adding 64/128 to src */
-	move	a2,t8
-
-/* Here we have src and dest word-aligned but less than 64-bytes or
- * 128 bytes to go.  Check for a 32(64) byte chunk and copy if if there
- * is one.  Otherwise jump down to L(chk1w) to handle the tail end of
- * the copy.
- */
-
-L(chkw):
-	PREFETCH_FOR_LOAD (0, a1)
-	andi	t8,a2,NSIZEMASK	/* Is there a 32-byte/64-byte chunk.  */
-				/* The t8 is the reminder count past 32-bytes */
-	beq	a2,t8,L(chk1w)	/* When a2=t8, no 32-byte chunk  */
-	nop
-	C_LD	t0,UNIT(0)(a1)
-	C_LD	t1,UNIT(1)(a1)
-	C_LD	REG2,UNIT(2)(a1)
-	C_LD	REG3,UNIT(3)(a1)
-	C_LD	REG4,UNIT(4)(a1)
-	C_LD	REG5,UNIT(5)(a1)
-	C_LD	REG6,UNIT(6)(a1)
-	C_LD	REG7,UNIT(7)(a1)
-	PTR_ADDIU a1,a1,UNIT(8)
-	C_ST	t0,UNIT(0)(a0)
-	C_ST	t1,UNIT(1)(a0)
-	C_ST	REG2,UNIT(2)(a0)
-	C_ST	REG3,UNIT(3)(a0)
-	C_ST	REG4,UNIT(4)(a0)
-	C_ST	REG5,UNIT(5)(a0)
-	C_ST	REG6,UNIT(6)(a0)
-	C_ST	REG7,UNIT(7)(a0)
-	PTR_ADDIU a0,a0,UNIT(8)
-
-/*
- * Here we have less than 32(64) bytes to copy.  Set up for a loop to
- * copy one word (or double word) at a time.  Set a2 to count how many
- * bytes we have to copy after all the word (or double word) chunks are
- * copied and a3 to the dst pointer after all the (d)word chunks have
- * been copied.  We will loop, incrementing a0 and a1 until a0 equals a3.
- */
-L(chk1w):
-	andi	a2,t8,(NSIZE-1)	/* a2 is the reminder past one (d)word chunks */
-	beq	a2,t8,L(lastb)
-	PTR_SUBU a3,t8,a2	/* a3 is count of bytes in one (d)word chunks */
-	PTR_ADDU a3,a0,a3	/* a3 is the dst address after loop */
-
-/* copying in words (4-byte or 8-byte chunks) */
-L(wordCopy_loop):
-	C_LD	REG3,UNIT(0)(a1)
-	PTR_ADDIU a0,a0,UNIT(1)
-	PTR_ADDIU a1,a1,UNIT(1)
-	bne	a0,a3,L(wordCopy_loop)
-	C_ST	REG3,UNIT(-1)(a0)
-
-/* Copy the last 8 (or 16) bytes */
-L(lastb):
-	blez	a2,L(leave)
-	PTR_ADDU a3,a0,a2	/* a3 is the last dst address */
-L(lastbloop):
-	lb	v1,0(a1)
-	PTR_ADDIU a0,a0,1
-	PTR_ADDIU a1,a1,1
-	bne	a0,a3,L(lastbloop)
-	sb	v1,-1(a0)
-L(leave):
-	j	ra
-	nop
-
-#ifndef R6_CODE
-/*
- * UNALIGNED case, got here with a3 = "negu a0"
- * This code is nearly identical to the aligned code above
- * but only the destination (not the source) gets aligned
- * so we need to do partial loads of the source followed
- * by normal stores to the destination (once we have aligned
- * the destination).
- */
-
-L(unaligned):
-	andi	a3,a3,(NSIZE-1)	/* copy a3 bytes to align a0/a1 */
-	beqz	a3,L(ua_chk16w) /* if a3=0, it is already aligned */
-	PTR_SUBU a2,a2,a3	/* a2 is the remining bytes count */
-
-	C_LDHI	v1,UNIT(0)(a1)
-	C_LDLO	v1,UNITM1(1)(a1)
-	PTR_ADDU a1,a1,a3
-	C_STHI	v1,UNIT(0)(a0)
-	PTR_ADDU a0,a0,a3
-
-/*
- *  Now the destination (but not the source) is aligned
- * Set a2 to count how many bytes we have to copy after all the 64/128 byte
- * chunks are copied and a3 to the dst pointer after all the 64/128 byte
- * chunks have been copied.  We will loop, incrementing a0 and a1 until a0
- * equals a3.
- */
-
-L(ua_chk16w):
-	andi	t8,a2,NSIZEDMASK /* any whole 64-byte/128-byte chunks? */
-	beq	a2,t8,L(ua_chkw) /* if a2==t8, no 64-byte/128-byte chunks */
-	PTR_SUBU a3,a2,t8	 /* subtract from a2 the reminder */
-	PTR_ADDU a3,a0,a3	 /* Now a3 is the final dst after loop */
-
-# if defined(USE_PREFETCH) && (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE)
-	PTR_ADDU t0,a0,a2	  /* t0 is the "past the end" address */
-	PTR_SUBU t9,t0,PREFETCH_LIMIT /* t9 is the "last safe pref" address */
-# endif
-	PREFETCH_FOR_LOAD  (0, a1)
-	PREFETCH_FOR_LOAD  (1, a1)
-	PREFETCH_FOR_LOAD  (2, a1)
-# if defined(USE_PREFETCH) && (PREFETCH_STORE_HINT != PREFETCH_HINT_PREPAREFORSTORE)
-	PREFETCH_FOR_STORE (1, a0)
-	PREFETCH_FOR_STORE (2, a0)
-	PREFETCH_FOR_STORE (3, a0)
-# endif
-# if defined(RETURN_FIRST_PREFETCH) && defined(USE_PREFETCH)
-#  if (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE)
-	sltu    v1,t9,a0
-	bgtz    v1,L(ua_skip_set)
-	nop
-	PTR_ADDIU v0,a0,(PREFETCH_CHUNK*4)
-L(ua_skip_set):
-#  else
-	PTR_ADDIU v0,a0,(PREFETCH_CHUNK*1)
-#  endif
-# endif
-L(ua_loop16w):
-	PREFETCH_FOR_LOAD  (3, a1)
-	C_LDHI	t0,UNIT(0)(a1)
-	C_LDHI	t1,UNIT(1)(a1)
-	C_LDHI	REG2,UNIT(2)(a1)
-# if defined(USE_PREFETCH) && (PREFETCH_STORE_HINT == PREFETCH_HINT_PREPAREFORSTORE)
-	sltu	v1,t9,a0
-	bgtz	v1,L(ua_skip_pref)
-# endif
-	C_LDHI	REG3,UNIT(3)(a1)
-	PREFETCH_FOR_STORE (4, a0)
-	PREFETCH_FOR_STORE (5, a0)
-L(ua_skip_pref):
-	C_LDHI	REG4,UNIT(4)(a1)
-	C_LDHI	REG5,UNIT(5)(a1)
-	C_LDHI	REG6,UNIT(6)(a1)
-	C_LDHI	REG7,UNIT(7)(a1)
-	C_LDLO	t0,UNITM1(1)(a1)
-	C_LDLO	t1,UNITM1(2)(a1)
-	C_LDLO	REG2,UNITM1(3)(a1)
-	C_LDLO	REG3,UNITM1(4)(a1)
-	C_LDLO	REG4,UNITM1(5)(a1)
-	C_LDLO	REG5,UNITM1(6)(a1)
-	C_LDLO	REG6,UNITM1(7)(a1)
-	C_LDLO	REG7,UNITM1(8)(a1)
-        PREFETCH_FOR_LOAD (4, a1)
-	C_ST	t0,UNIT(0)(a0)
-	C_ST	t1,UNIT(1)(a0)
-	C_ST	REG2,UNIT(2)(a0)
-	C_ST	REG3,UNIT(3)(a0)
-	C_ST	REG4,UNIT(4)(a0)
-	C_ST	REG5,UNIT(5)(a0)
-	C_ST	REG6,UNIT(6)(a0)
-	C_ST	REG7,UNIT(7)(a0)
-	C_LDHI	t0,UNIT(8)(a1)
-	C_LDHI	t1,UNIT(9)(a1)
-	C_LDHI	REG2,UNIT(10)(a1)
-	C_LDHI	REG3,UNIT(11)(a1)
-	C_LDHI	REG4,UNIT(12)(a1)
-	C_LDHI	REG5,UNIT(13)(a1)
-	C_LDHI	REG6,UNIT(14)(a1)
-	C_LDHI	REG7,UNIT(15)(a1)
-	C_LDLO	t0,UNITM1(9)(a1)
-	C_LDLO	t1,UNITM1(10)(a1)
-	C_LDLO	REG2,UNITM1(11)(a1)
-	C_LDLO	REG3,UNITM1(12)(a1)
-	C_LDLO	REG4,UNITM1(13)(a1)
-	C_LDLO	REG5,UNITM1(14)(a1)
-	C_LDLO	REG6,UNITM1(15)(a1)
-	C_LDLO	REG7,UNITM1(16)(a1)
-        PREFETCH_FOR_LOAD (5, a1)
-	C_ST	t0,UNIT(8)(a0)
-	C_ST	t1,UNIT(9)(a0)
-	C_ST	REG2,UNIT(10)(a0)
-	C_ST	REG3,UNIT(11)(a0)
-	C_ST	REG4,UNIT(12)(a0)
-	C_ST	REG5,UNIT(13)(a0)
-	C_ST	REG6,UNIT(14)(a0)
-	C_ST	REG7,UNIT(15)(a0)
-	PTR_ADDIU a0,a0,UNIT(16)	/* adding 64/128 to dest */
-	bne	a0,a3,L(ua_loop16w)
-	PTR_ADDIU a1,a1,UNIT(16)	/* adding 64/128 to src */
-	move	a2,t8
-
-/* Here we have src and dest word-aligned but less than 64-bytes or
- * 128 bytes to go.  Check for a 32(64) byte chunk and copy if if there
- * is one.  Otherwise jump down to L(ua_chk1w) to handle the tail end of
- * the copy.  */
-
-L(ua_chkw):
-	PREFETCH_FOR_LOAD (0, a1)
-	andi	t8,a2,NSIZEMASK	  /* Is there a 32-byte/64-byte chunk.  */
-				  /* t8 is the reminder count past 32-bytes */
-	beq	a2,t8,L(ua_chk1w) /* When a2=t8, no 32-byte chunk */
-	nop
-	C_LDHI	t0,UNIT(0)(a1)
-	C_LDHI	t1,UNIT(1)(a1)
-	C_LDHI	REG2,UNIT(2)(a1)
-	C_LDHI	REG3,UNIT(3)(a1)
-	C_LDHI	REG4,UNIT(4)(a1)
-	C_LDHI	REG5,UNIT(5)(a1)
-	C_LDHI	REG6,UNIT(6)(a1)
-	C_LDHI	REG7,UNIT(7)(a1)
-	C_LDLO	t0,UNITM1(1)(a1)
-	C_LDLO	t1,UNITM1(2)(a1)
-	C_LDLO	REG2,UNITM1(3)(a1)
-	C_LDLO	REG3,UNITM1(4)(a1)
-	C_LDLO	REG4,UNITM1(5)(a1)
-	C_LDLO	REG5,UNITM1(6)(a1)
-	C_LDLO	REG6,UNITM1(7)(a1)
-	C_LDLO	REG7,UNITM1(8)(a1)
-	PTR_ADDIU a1,a1,UNIT(8)
-	C_ST	t0,UNIT(0)(a0)
-	C_ST	t1,UNIT(1)(a0)
-	C_ST	REG2,UNIT(2)(a0)
-	C_ST	REG3,UNIT(3)(a0)
-	C_ST	REG4,UNIT(4)(a0)
-	C_ST	REG5,UNIT(5)(a0)
-	C_ST	REG6,UNIT(6)(a0)
-	C_ST	REG7,UNIT(7)(a0)
-	PTR_ADDIU a0,a0,UNIT(8)
-/*
- * Here we have less than 32(64) bytes to copy.  Set up for a loop to
- * copy one word (or double word) at a time.
- */
-L(ua_chk1w):
-	andi	a2,t8,(NSIZE-1)	/* a2 is the reminder past one (d)word chunks */
-	beq	a2,t8,L(ua_smallCopy)
-	PTR_SUBU a3,t8,a2	/* a3 is count of bytes in one (d)word chunks */
-	PTR_ADDU a3,a0,a3	/* a3 is the dst address after loop */
-
-/* copying in words (4-byte or 8-byte chunks) */
-L(ua_wordCopy_loop):
-	C_LDHI	v1,UNIT(0)(a1)
-	C_LDLO	v1,UNITM1(1)(a1)
-	PTR_ADDIU a0,a0,UNIT(1)
-	PTR_ADDIU a1,a1,UNIT(1)
-	bne	a0,a3,L(ua_wordCopy_loop)
-	C_ST	v1,UNIT(-1)(a0)
-
-/* Copy the last 8 (or 16) bytes */
-L(ua_smallCopy):
-	beqz	a2,L(leave)
-	PTR_ADDU a3,a0,a2	/* a3 is the last dst address */
-L(ua_smallCopy_loop):
-	lb	v1,0(a1)
-	PTR_ADDIU a0,a0,1
-	PTR_ADDIU a1,a1,1
-	bne	a0,a3,L(ua_smallCopy_loop)
-	sb	v1,-1(a0)
-
-	j	ra
-	nop
-
-#else /* R6_CODE */
-
-# if __MIPSEB
-#  define SWAP_REGS(X,Y) X, Y
-#  define ALIGN_OFFSET(N) (N)
-# else
-#  define SWAP_REGS(X,Y) Y, X
-#  define ALIGN_OFFSET(N) (NSIZE-N)
-# endif
-# define R6_UNALIGNED_WORD_COPY(BYTEOFFSET) \
-	andi	REG7, a2, (NSIZE-1);/* REG7 is # of bytes to by bytes.     */ \
-	beq	REG7, a2, L(lastb); /* Check for bytes to copy by word	   */ \
-	PTR_SUBU a3, a2, REG7;	/* a3 is number of bytes to be copied in   */ \
-				/* (d)word chunks.			   */ \
-	move	a2, REG7;	/* a2 is # of bytes to copy byte by byte   */ \
-				/* after word loop is finished.		   */ \
-	PTR_ADDU REG6, a0, a3;	/* REG6 is the dst address after loop.	   */ \
-	PTR_SUBU REG2, a1, t8;	/* REG2 is the aligned src address.	   */ \
-	PTR_ADDU a1, a1, a3;	/* a1 is addr of source after word loop.   */ \
-	C_LD	t0, UNIT(0)(REG2);  /* Load first part of source.	   */ \
-L(r6_ua_wordcopy##BYTEOFFSET):						      \
-	C_LD	t1, UNIT(1)(REG2);  /* Load second part of source.	   */ \
-	C_ALIGN	REG3, SWAP_REGS(t1,t0), ALIGN_OFFSET(BYTEOFFSET);	      \
-	PTR_ADDIU a0, a0, UNIT(1);  /* Increment destination pointer.	   */ \
-	PTR_ADDIU REG2, REG2, UNIT(1); /* Increment aligned source pointer.*/ \
-	move	t0, t1;		/* Move second part of source to first.	   */ \
-	bne	a0, REG6,L(r6_ua_wordcopy##BYTEOFFSET);			      \
-	C_ST	REG3, UNIT(-1)(a0);					      \
-	j	L(lastb);						      \
-	nop
-
-	/* We are generating R6 code, the destination is 4 byte aligned and
-	   the source is not 4 byte aligned. t8 is 1, 2, or 3 depending on the
-           alignment of the source.  */
-
-L(r6_unaligned1):
-	R6_UNALIGNED_WORD_COPY(1)
-L(r6_unaligned2):
-	R6_UNALIGNED_WORD_COPY(2)
-L(r6_unaligned3):
-	R6_UNALIGNED_WORD_COPY(3)
-# ifdef USE_DOUBLE
-L(r6_unaligned4):
-	R6_UNALIGNED_WORD_COPY(4)
-L(r6_unaligned5):
-	R6_UNALIGNED_WORD_COPY(5)
-L(r6_unaligned6):
-	R6_UNALIGNED_WORD_COPY(6)
-L(r6_unaligned7):
-	R6_UNALIGNED_WORD_COPY(7)
-# endif
-#endif /* R6_CODE */
-
-	.set	at
-	.set	reorder
-END(MEMCPY_NAME)
-#ifndef __ANDROID__
-# ifdef _LIBC
-libc_hidden_builtin_def (MEMCPY_NAME)
-# endif
-#endif
diff --git a/libc/arch-mips/string/memcpy.c b/libc/arch-mips/string/memcpy.c
new file mode 100644
index 0000000..68827b6
--- /dev/null
+++ b/libc/arch-mips/string/memcpy.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 2017 Imagination Technologies.
+ *
+ * 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.
+ *      * Neither the name of Imagination Technologies nor the names of its
+ *        contributors may be used to endorse or promote products derived
+ *        from this software without specific prior written permission.
+ *
+ * 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 <string.h>
+
+#if !defined(UNALIGNED_INSTR_SUPPORT)
+/* does target have unaligned lw/ld/ualw/uald instructions? */
+#define UNALIGNED_INSTR_SUPPORT 0
+#if __mips_isa_rev < 6 && !__mips1
+#undef UNALIGNED_INSTR_SUPPORT
+#define UNALIGNED_INSTR_SUPPORT 1
+#endif
+#endif
+
+#if !defined(HW_UNALIGNED_SUPPORT)
+/* Does target have hardware support for unaligned accesses?  */
+#define HW_UNALIGNED_SUPPORT 0
+#if __mips_isa_rev >= 6
+#undef HW_UNALIGNED_SUPPORT
+#define HW_UNALIGNED_SUPPORT 1
+#endif
+#endif
+
+#define ENABLE_PREFETCH     1
+
+#if ENABLE_PREFETCH
+#define PREFETCH(addr)  __builtin_prefetch (addr, 0, 1);
+#else
+#define PREFETCH(addr)
+#endif
+
+#if _MIPS_SIM == _ABIO32
+typedef unsigned long reg_t;
+typedef struct
+{
+  reg_t B0:8, B1:8, B2:8, B3:8;
+} bits_t;
+#else
+typedef unsigned long long reg_t;
+typedef struct
+{
+  reg_t B0:8, B1:8, B2:8, B3:8, B4:8, B5:8, B6:8, B7:8;
+} bits_t;
+#endif
+
+typedef union
+{
+  reg_t v;
+  bits_t b;
+} bitfields_t;
+
+#define DO_BYTE(a, i)   \
+  a[i] = bw.b.B##i;     \
+  len--;                \
+  if(!len) return ret;  \
+
+/* This code is called when aligning a pointer, there are remaining bytes
+   after doing word compares, or architecture does not have some form
+   of unaligned support.  */
+static inline void * __attribute__ ((always_inline))
+do_bytes (void *a, const void *b, unsigned long len, void *ret)
+{
+  unsigned char *x = (unsigned char *) a;
+  unsigned char *y = (unsigned char *) b;
+  unsigned long i;
+
+  /* 'len' might be zero here, so preloading the first two values
+     before the loop may access unallocated memory.  */
+  for (i = 0; i < len; i++) {
+    *x = *y;
+    x++;
+    y++;
+  }
+  return ret;
+}
+
+/* This code is called to copy only remaining bytes within word or doubleword */
+static inline void * __attribute__ ((always_inline))
+do_bytes_remaining (void *a, const void *b, unsigned long len, void *ret)
+{
+  unsigned char *x = (unsigned char *) a;
+
+  if(len > 0) {
+    bitfields_t bw;
+    bw.v = *((reg_t*) b);
+
+#if __mips64
+    DO_BYTE(x, 0);
+    DO_BYTE(x, 1);
+    DO_BYTE(x, 2);
+    DO_BYTE(x, 3);
+    DO_BYTE(x, 4);
+    DO_BYTE(x, 5);
+    DO_BYTE(x, 6);
+    DO_BYTE(x, 7);
+#else
+    DO_BYTE(x, 0);
+    DO_BYTE(x, 1);
+    DO_BYTE(x, 2);
+    DO_BYTE(x, 3);
+#endif
+  }
+
+    return ret;
+}
+
+#if !HW_UNALIGNED_SUPPORT
+#if UNALIGNED_INSTR_SUPPORT
+/* for MIPS GCC, there are no unaligned builtins - so this struct forces
+   the compiler to treat the pointer access as unaligned.  */
+struct ulw
+{
+  reg_t uli;
+} __attribute__ ((packed));
+
+/* first pointer is not aligned while second pointer is.  */
+static void *
+unaligned_words (struct ulw *a, const reg_t * b,
+                 unsigned long words, unsigned long bytes, void *ret)
+{
+#if ((_MIPS_SIM == _ABIO32) || _MIPS_TUNE_I6400)
+  unsigned long i, words_by_8, words_by_1;
+  words_by_1 = words % 8;
+  words_by_8 = words >> 3;
+  for (; words_by_8 > 0; words_by_8--) {
+    if(words_by_8 != 1)
+      PREFETCH (b + 8);
+    reg_t y0 = b[0], y1 = b[1], y2 = b[2], y3 = b[3];
+    reg_t y4 = b[4], y5 = b[5], y6 = b[6], y7 = b[7];
+    a[0].uli = y0;
+    a[1].uli = y1;
+    a[2].uli = y2;
+    a[3].uli = y3;
+    a[4].uli = y4;
+    a[5].uli = y5;
+    a[6].uli = y6;
+    a[7].uli = y7;
+    a += 8;
+    b += 8;
+  }
+#else
+  unsigned long i, words_by_4, words_by_1;
+  words_by_1 = words % 4;
+  words_by_4 = words >> 2;
+   for (; words_by_4 > 0; words_by_4--) {
+    if(words_by_4 != 1)
+      PREFETCH (b + 4);
+    reg_t y0 = b[0], y1 = b[1], y2 = b[2], y3 = b[3];
+    a[0].uli = y0;
+    a[1].uli = y1;
+    a[2].uli = y2;
+    a[3].uli = y3;
+    a += 4;
+    b += 4;
+  }
+#endif
+
+  /* do remaining words.  */
+  for (i = 0; i < words_by_1; i++) {
+    a->uli = *b;
+    a += 1;
+    b += 1;
+  }
+
+  /* mop up any remaining bytes.  */
+  return do_bytes_remaining (a, b, bytes, ret);
+}
+#else
+/* no HW support or unaligned lw/ld/ualw/uald instructions.  */
+static void *
+unaligned_words (reg_t * a, const reg_t * b,
+                 unsigned long words, unsigned long bytes, void *ret)
+{
+  unsigned long i;
+  unsigned char *x = (unsigned char *) a;
+
+  for (i = 0; i < words; i++) {
+    bitfields_t bw;
+    bw.v = *((reg_t*) b);
+    x = (unsigned char *) a;
+#if __mips64
+    x[0] = bw.b.B0;
+    x[1] = bw.b.B1;
+    x[2] = bw.b.B2;
+    x[3] = bw.b.B3;
+    x[4] = bw.b.B4;
+    x[5] = bw.b.B5;
+    x[6] = bw.b.B6;
+    x[7] = bw.b.B7;
+#else
+    x[0] = bw.b.B0;
+    x[1] = bw.b.B1;
+    x[2] = bw.b.B2;
+    x[3] = bw.b.B3;
+#endif
+    a += 1;
+    b += 1;
+  }
+
+  /* mop up any remaining bytes */
+  return do_bytes_remaining (a, b, bytes, ret);
+}
+#endif /* UNALIGNED_INSTR_SUPPORT */
+#endif /* HW_UNALIGNED_SUPPORT */
+
+/* both pointers are aligned, or first isn't and HW support for unaligned.  */
+static void *
+aligned_words (reg_t * a, const reg_t * b,
+               unsigned long words, unsigned long bytes, void *ret)
+{
+#if ((_MIPS_SIM == _ABIO32) || _MIPS_TUNE_I6400)
+  unsigned long i, words_by_8, words_by_1;
+  words_by_1 = words % 8;
+  words_by_8 = words >> 3;
+  for (; words_by_8 > 0; words_by_8--) {
+    if(words_by_8 != 1)
+      PREFETCH (b + 8);
+    reg_t x0 = b[0], x1 = b[1], x2 = b[2], x3 = b[3];
+    reg_t x4 = b[4], x5 = b[5], x6 = b[6], x7 = b[7];
+    a[0] = x0;
+    a[1] = x1;
+    a[2] = x2;
+    a[3] = x3;
+    a[4] = x4;
+    a[5] = x5;
+    a[6] = x6;
+    a[7] = x7;
+    a += 8;
+    b += 8;
+  }
+#else
+  unsigned long i, words_by_4, words_by_1;
+  words_by_1 = words % 4;
+  words_by_4 = words >> 2;
+  for (; words_by_4 > 0; words_by_4--) {
+    if(words_by_4 != 1)
+      PREFETCH (b + 4);
+    reg_t x0 = b[0], x1 = b[1], x2 = b[2], x3 = b[3];
+    a[0] = x0;
+    a[1] = x1;
+    a[2] = x2;
+    a[3] = x3;
+    a += 4;
+    b += 4;
+  }
+#endif
+
+  /* do remaining words.  */
+  for (i = 0; i < words_by_1; i++) {
+    *a = *b;
+    a += 1;
+    b += 1;
+  }
+
+  /* mop up any remaining bytes.  */
+  return do_bytes_remaining (a, b, bytes, ret);
+}
+
+void *
+memcpy (void *a, const void *b, size_t len) __overloadable
+{
+  unsigned long bytes, words;
+  void *ret = a;
+
+  /* shouldn't hit that often.  */
+  if (len < sizeof (reg_t) * 4) {
+    return do_bytes (a, b, len, a);
+  }
+
+  /* Align the second pointer to word/dword alignment.
+     Note that the pointer is only 32-bits for o32/n32 ABIs. For
+     n32, loads are done as 64-bit while address remains 32-bit.   */
+  bytes = ((unsigned long) b) % sizeof (reg_t);
+  if (bytes) {
+    bytes = sizeof (reg_t) - bytes;
+    if (bytes > len)
+      bytes = len;
+    do_bytes (a, b, bytes, ret);
+    if (len == bytes)
+      return ret;
+    len -= bytes;
+    a = (void *) (((unsigned char *) a) + bytes);
+    b = (const void *) (((unsigned char *) b) + bytes);
+  }
+
+  /* Second pointer now aligned.  */
+  words = len / sizeof (reg_t);
+  bytes = len % sizeof (reg_t);
+#if HW_UNALIGNED_SUPPORT
+  /* treat possible unaligned first pointer as aligned.  */
+  return aligned_words (a, b, words, bytes, ret);
+#else
+  if (((unsigned long) a) % sizeof (reg_t) == 0) {
+    return aligned_words (a, b, words, bytes, ret);
+  }
+  /* need to use unaligned instructions on first pointer.  */
+  return unaligned_words (a, b, words, bytes, ret);
+#endif
+}
diff --git a/libc/arch-mips/string/memmove.c b/libc/arch-mips/string/memmove.c
new file mode 100644
index 0000000..fbff297
--- /dev/null
+++ b/libc/arch-mips/string/memmove.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2017 Imagination Technologies.
+ *
+ * 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.
+ *      * Neither the name of Imagination Technologies nor the names of its
+ *        contributors may be used to endorse or promote products derived
+ *        from this software without specific prior written permission.
+ *
+ * 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 <string.h>
+
+#if !defined(UNALIGNED_INSTR_SUPPORT)
+/* does target have unaligned lw/ld/ualw/uald instructions? */
+#define UNALIGNED_INSTR_SUPPORT 0
+#if __mips_isa_rev < 6 && !__mips1
+#undef UNALIGNED_INSTR_SUPPORT
+#define UNALIGNED_INSTR_SUPPORT 1
+#endif
+#endif
+
+#if !defined(HW_UNALIGNED_SUPPORT)
+/* Does target have hardware support for unaligned accesses?  */
+#define HW_UNALIGNED_SUPPORT 0
+#if __mips_isa_rev >= 6
+#undef HW_UNALIGNED_SUPPORT
+#define HW_UNALIGNED_SUPPORT 1
+#endif
+#endif
+
+#define ENABLE_PREFETCH     1
+
+#if ENABLE_PREFETCH
+#define PREFETCH(addr)  __builtin_prefetch (addr, 0, 1);
+#else
+#define PREFETCH(addr)
+#endif
+
+#if _MIPS_SIM == _ABIO32
+typedef unsigned long reg_t;
+typedef struct
+{
+  reg_t B0:8, B1:8, B2:8, B3:8;
+} bits_t;
+#else
+typedef unsigned long long reg_t;
+typedef struct
+{
+  reg_t B0:8, B1:8, B2:8, B3:8, B4:8, B5:8, B6:8, B7:8;
+} bits_t;
+#endif
+
+typedef union
+{
+  reg_t v;
+  bits_t b;
+} bitfields_t;
+
+#define DO_BYTE(a, i)   \
+  a[i] = bw.b.B##i;     \
+  len--;                \
+  if(!len) return ret;  \
+
+/* This code is called when aligning a pointer, there are remaining bytes
+   after doing word compares, or architecture does not have some form
+   of unaligned support.  */
+static inline void * __attribute__ ((always_inline))
+do_bytes (void *a, const void *b, unsigned long len, void *ret)
+{
+  unsigned char *x = (unsigned char *) a;
+  unsigned char *y = (unsigned char *) b;
+  unsigned long i;
+
+  /* 'len' might be zero here, so preloading the first two values
+     before the loop may access unallocated memory.  */
+  for (i = 0; i < len; i++)
+  {
+    *x = *y;
+    x++;
+    y++;
+  }
+  return ret;
+}
+
+static inline void * __attribute__ ((always_inline))
+do_bytes_backward (void *a, const void *b, unsigned long len, void *ret)
+{
+  unsigned char *x = (unsigned char *) a;
+  unsigned char *y = (unsigned char *) b;
+  unsigned long i;
+
+  /* 'len' might be zero here, so preloading the first two values
+     before the loop may access unallocated memory.  */
+  for (i = 0; i < len; i++) {
+    *--x = *--y;
+  }
+  return ret;
+}
+
+static inline void * __attribute__ ((always_inline))
+do_bytes_aligned (void *a, const void *b, unsigned long len, void *ret)
+{
+  unsigned char *x = (unsigned char *) a;
+
+  if(len > 0) {
+    bitfields_t bw;
+    bw.v = *((reg_t*) b);
+
+#if __mips64
+    DO_BYTE(x, 0);
+    DO_BYTE(x, 1);
+    DO_BYTE(x, 2);
+    DO_BYTE(x, 3);
+    DO_BYTE(x, 4);
+    DO_BYTE(x, 5);
+    DO_BYTE(x, 6);
+    DO_BYTE(x, 7);
+#else
+    DO_BYTE(x, 0);
+    DO_BYTE(x, 1);
+    DO_BYTE(x, 2);
+    DO_BYTE(x, 3);
+#endif
+  }
+
+  return ret;
+}
+
+#if !HW_UNALIGNED_SUPPORT
+#if UNALIGNED_INSTR_SUPPORT
+/* for MIPS GCC, there are no unaligned builtins - so this struct forces
+   the compiler to treat the pointer access as unaligned.  */
+struct ulw
+{
+  reg_t uli;
+} __attribute__ ((packed));
+
+#define STORE_UNALIGNED_8(a, b)                      \
+{                                                    \
+  reg_t y0 = b[0], y1 = b[1], y2 = b[2], y3 = b[3];  \
+  reg_t y4 = b[4], y5 = b[5], y6 = b[6], y7 = b[7];  \
+  a[0].uli = y0;                                     \
+  a[1].uli = y1;                                     \
+  a[2].uli = y2;                                     \
+  a[3].uli = y3;                                     \
+  a[4].uli = y4;                                     \
+  a[5].uli = y5;                                     \
+  a[6].uli = y6;                                     \
+  a[7].uli = y7;                                     \
+}
+
+#define STORE_UNALIGNED_4(a, b)                      \
+{                                                    \
+  reg_t y0 = b[0], y1 = b[1], y2 = b[2], y3 = b[3];  \
+  a[0].uli = y0;                                     \
+  a[1].uli = y1;                                     \
+  a[2].uli = y2;                                     \
+  a[3].uli = y3;                                     \
+}
+
+/* first pointer is not aligned while second pointer is.  */
+static void *
+unaligned_words_forward (struct ulw *a, const reg_t * b,
+                         unsigned long words, unsigned long bytes, void *ret)
+{
+#if ((_MIPS_SIM == _ABIO32) || _MIPS_TUNE_I6400)
+  unsigned long i, words_by_8, words_by_1;
+  words_by_1 = words % 8;
+  words_by_8 = words >> 3;
+  for (; words_by_8 > 0; words_by_8--) {
+    if(words_by_8 != 1)
+      PREFETCH (b + 8);
+    STORE_UNALIGNED_8(a, b);
+    a += 8;
+    b += 8;
+  }
+#else
+  unsigned long i, words_by_4, words_by_1;
+  words_by_1 = words % 4;
+  words_by_4 = words >> 2;
+  for (; words_by_4 > 0; words_by_4--) {
+    if(words_by_4 != 1)
+      PREFETCH (b + 4);
+    STORE_UNALIGNED_4(a, b);
+    a += 4;
+    b += 4;
+  }
+#endif
+
+  /* do remaining words.  */
+  for (i = 0; i < words_by_1; i++) {
+    a->uli = *b;
+    a += 1;
+    b += 1;
+  }
+
+  /* mop up any remaining bytes.  */
+  return do_bytes_aligned (a, b, bytes, ret);
+}
+
+static void *
+unaligned_words_backward (struct ulw *a, const reg_t * b,
+                          unsigned long words, unsigned long bytes, void *ret)
+{
+#if ((_MIPS_SIM == _ABIO32) || _MIPS_TUNE_I6400)
+  unsigned long i, words_by_8, words_by_1;
+  words_by_1 = words % 8;
+  words_by_8 = words >> 3;
+  for (; words_by_8 > 0; words_by_8--) {
+    if(words_by_8 != 1)
+      PREFETCH (b - 16);
+    a -= 8;
+    b -= 8;
+    STORE_UNALIGNED_8(a, b);
+  }
+#else
+  unsigned long i, words_by_4, words_by_1;
+  words_by_1 = words % 4;
+  words_by_4 = words >> 2;
+  for (; words_by_4 > 0; words_by_4--) {
+    if(words_by_4 != 1)
+      PREFETCH (b - 8);
+    a -= 4;
+    b -= 4;
+    STORE_UNALIGNED_4(a, b);
+  }
+#endif
+
+  /* do remaining words.  */
+  for (i = 0; i < words_by_1; i++) {
+    a -= 1;
+    b -= 1;
+    a->uli = *b;
+  }
+
+  /* mop up any remaining bytes.  */
+  return do_bytes_backward (a, b, bytes, ret);
+}
+
+#else
+/* no HW support or unaligned lw/ld/ualw/uald instructions.  */
+static void *
+unaligned_words_forward (reg_t * a, const reg_t * b,
+                         unsigned long words, unsigned long bytes, void *ret)
+{
+  return do_bytes_aligned (a, b, (sizeof (reg_t) * words) + bytes, ret);
+}
+
+static void *
+unaligned_words_backward (reg_t * a, const reg_t * b,
+                          unsigned long words, unsigned long bytes, void *ret)
+{
+  return do_bytes_backward (a, b, (sizeof (reg_t) * words) + bytes, ret);
+}
+
+#endif /* UNALIGNED_INSTR_SUPPORT */
+#endif /* HW_UNALIGNED_SUPPORT */
+
+/* both pointers are aligned, or first isn't and HW support for unaligned.  */
+
+#define STORE_ALIGNED_8(a, b)                        \
+{                                                    \
+  reg_t x0 = b[0], x1 = b[1], x2 = b[2], x3 = b[3];  \
+  reg_t x4 = b[4], x5 = b[5], x6 = b[6], x7 = b[7];  \
+  a[0] = x0;                                         \
+  a[1] = x1;                                         \
+  a[2] = x2;                                         \
+  a[3] = x3;                                         \
+  a[4] = x4;                                         \
+  a[5] = x5;                                         \
+  a[6] = x6;                                         \
+  a[7] = x7;                                         \
+}
+
+#define STORE_ALIGNED_4(a, b)                        \
+{                                                    \
+  reg_t x0 = b[0], x1 = b[1], x2 = b[2], x3 = b[3];  \
+  a[0] = x0;                                         \
+  a[1] = x1;                                         \
+  a[2] = x2;                                         \
+  a[3] = x3;                                         \
+}
+
+static void *
+aligned_words_forward (reg_t * a, const reg_t * b,
+                       unsigned long words, unsigned long bytes, void *ret)
+{
+#if ((_MIPS_SIM == _ABIO32) || _MIPS_TUNE_I6400)
+  unsigned long i, words_by_8, words_by_1;
+  words_by_1 = words % 8;
+  words_by_8 = words >> 3;
+  for (; words_by_8 > 0; words_by_8--) {
+    if(words_by_8 != 1)
+      PREFETCH (b + 8);
+    STORE_ALIGNED_8(a, b);
+    a += 8;
+    b += 8;
+  }
+#else
+  unsigned long i, words_by_4, words_by_1;
+  words_by_1 = words % 4;
+  words_by_4 = words >> 2;
+  for (; words_by_4 > 0; words_by_4--) {
+    if(words_by_4 != 1)
+      PREFETCH (b + 4);
+    STORE_ALIGNED_4(a, b);
+    a += 4;
+    b += 4;
+  }
+#endif
+
+  /* do remaining words.  */
+  for (i = 0; i < words_by_1; i++) {
+    *a = *b;
+    a += 1;
+    b += 1;
+  }
+
+  /* mop up any remaining bytes.  */
+  return do_bytes_aligned (a, b, bytes, ret);
+}
+
+
+static void *
+aligned_words_backward (reg_t * a, const reg_t * b,
+                        unsigned long words, unsigned long bytes, void *ret)
+{
+#if ((_MIPS_SIM == _ABIO32) || _MIPS_TUNE_I6400)
+  unsigned long i, words_by_8, words_by_1;
+  words_by_1 = words % 8;
+  words_by_8 = words >> 3;
+  for (; words_by_8 > 0; words_by_8--) {
+    if(words_by_8 != 1)
+      PREFETCH (b - 16);
+    a -= 8;
+    b -= 8;
+    STORE_ALIGNED_8(a, b);
+  }
+#else
+  unsigned long i, words_by_4, words_by_1;
+  words_by_1 = words % 4;
+  words_by_4 = words >> 2;
+  for (; words_by_4 > 0; words_by_4--) {
+    if(words_by_4 != 1)
+      PREFETCH (b - 8);
+    a -= 4;
+    b -= 4;
+    STORE_ALIGNED_4(a, b);
+  }
+#endif
+
+  /* do remaining words.  */
+  for (i = 0; i < words_by_1; i++) {
+    a -= 1;
+    b -= 1;
+    *a = *b;
+  }
+
+  /* mop up any remaining bytes.  */
+  return do_bytes_backward (a, b, bytes, ret);
+}
+
+void *
+memmove (void *dst0, const void *src0, size_t length) __overloadable
+{
+  unsigned long bytes, words;
+  void *ret = dst0;
+
+  if (length == 0 || dst0 == src0)      /* nothing to do */
+    return dst0;
+
+  if ((unsigned long)dst0 < (unsigned long)src0) {
+    /* Copy forwards. */
+    /* This shouldn't hit that often. */
+    if (length < sizeof (reg_t) * 4) {
+      return do_bytes (dst0, src0, length, ret);
+    }
+
+    /* Align the second pointer to word/dword alignment.
+       Note that the pointer is only 32-bits for o32/n32 ABIs. For
+       n32, loads are done as 64-bit while address remains 32-bit.   */
+    bytes = ((unsigned long) src0) % sizeof (reg_t);
+    if (bytes) {
+      bytes = sizeof (reg_t) - bytes;
+      if (bytes > length)
+        bytes = length;
+      do_bytes (dst0, src0, bytes, ret);
+      if (length == bytes)
+        return ret;
+      length -= bytes;
+      dst0 = (void *) (((unsigned char *) dst0) + bytes);
+      src0 = (const void *) (((unsigned char *) src0) + bytes);
+    }
+
+    /* Second pointer now aligned.  */
+    words = length / sizeof (reg_t);
+    bytes = length % sizeof (reg_t);
+#if HW_UNALIGNED_SUPPORT
+    /* treat possible unaligned first pointer as aligned.  */
+    return aligned_words_forward (dst0, src0, words, bytes, ret);
+#else
+    if (((unsigned long) dst0) % sizeof (reg_t) == 0) {
+      return aligned_words_forward (dst0, src0, words, bytes, ret);
+    }
+    /* need to use unaligned instructions on first pointer.  */
+    return unaligned_words_forward (dst0, src0, words, bytes, ret);
+#endif
+  } else {
+    /* Copy backwards. */
+    dst0 = (void *) (((unsigned char *) dst0) + length);
+    src0 = (const void *) (((unsigned char *) src0) + length);
+
+    /* This shouldn't hit that often. */
+    if (length < sizeof (reg_t) * 4) {
+      return do_bytes_backward (dst0, src0, length, ret);
+    }
+
+    /* Align the second pointer to word/dword alignment.
+       Note that the pointer is only 32-bits for o32/n32 ABIs. For
+       n32, loads are done as 64-bit while address remains 32-bit.   */
+    bytes = ((unsigned long) src0) % sizeof (reg_t);
+    if (bytes) {
+      if (bytes > length)
+        bytes = length;
+      do_bytes_backward (dst0, src0, bytes, ret);
+      if (length == bytes)
+        return ret;
+      length -= bytes;
+      dst0 = (void *) (((unsigned char *) dst0) - bytes);
+      src0 = (const void *) (((unsigned char *) src0) - bytes);
+    }
+
+    words = length / sizeof (reg_t);
+    bytes = length % sizeof (reg_t);
+#if HW_UNALIGNED_SUPPORT
+    /* treat possible unaligned first pointer as aligned.  */
+    return aligned_words_backward ((void *)dst0, (void *)src0, words, bytes, ret);
+#else
+    if (((unsigned long) dst0) % sizeof (reg_t) == 0) {
+      return aligned_words_backward (dst0, src0, words, bytes, ret);
+    }
+    /* need to use unaligned instructions on first pointer.  */
+    return unaligned_words_backward (dst0, src0, words, bytes, ret);
+#endif
+  }
+}
diff --git a/libc/arch-mips/string/mips-string-ops.h b/libc/arch-mips/string/mips-string-ops.h
deleted file mode 100644
index 50f7e3a..0000000
--- a/libc/arch-mips/string/mips-string-ops.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (c) 2010 MIPS Technologies, Inc.
- *
- * 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.
- *      * Neither the name of MIPS Technologies Inc. nor the names of its
- *        contributors may be used to endorse or promote products derived
- *        from this software without specific prior written permission.
- *
- * 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 __MIPS_STRING_OPS_H
-#define __MIPS_STRING_OPS_H
-    /* This definition of the byte bitfields uses the
-       assumption that the layout of the bitfields is
-       equivalent to the layout in memory.  Generally,
-       for the MIPS ABIs, this is true. If you compile
-       the strcmp.c file with -DSMOKE_TEST_NEW_STRCMP,
-       this assumption will be tested.
-
-       Also, regardless of char signedness, ANSI C dictates that
-       strcmp() treats each character as unsigned char.  For
-       strlen and the like, signedness doesn't matter.
-
-       Also, this code assumes that there are 8-bits per 'char'.  */
-
-#if __mips64
-typedef struct bits
-{
-  unsigned B0:8, B1:8, B2:8, B3:8, B4:8, B5:8, B6:8, B7:8;
-} bits_t;
-#else
-typedef struct bits
-{
-  unsigned B0:8, B1:8, B2:8, B3:8;
-} bits_t;
-#endif
-
-#ifndef _ULW
-    /* for MIPS GCC, there is no unaligned builtins - so this code forces
-       the compiler to treat the pointer access as unaligned.  */
-struct ulw
-{
-  unsigned b;
-} __attribute__ ((packed));
-
-#define _ULW(__x) ((struct ulw *) ((char *)(&__x)))->b;
-#endif
-
-/* This union assumes that small structures can be in registers.  If
-   not, then memory accesses will be done - not optimal, but ok.  */
-typedef union
-{
-  unsigned v;
-  bits_t b;
-} bitfields_t;
-
-#ifndef detect_zero
-/* __mips_dsp, __mips_dspr2, and __mips64 are predefined by
-   the compiler, based on command line options.  */
-#if (__mips_dsp || __mips_dspr2) && !__mips64
-#define __mips_using_dsp 1
-
-/* DSP 4-lane (8 unsigned bits per line) subtract and saturate
- * Intrinsic operation. How this works:
- *     Given a 4-byte string of "ABC\0", subtract this as
- *     an unsigned integer from 0x01010101:
- *	   0x01010101
- *       - 0x41424300
- *        -----------
- (         0xbfbebe01 <-- answer without saturation
- *	   0x00000001 <-- answer with saturation
- * When this 4-lane vector is treated as an unsigned int value,
- * a non-zero answer indicates the presence of a zero in the
- * original 4-byte argument.  */
-
-typedef signed char v4i8 __attribute__ ((vector_size (4)));
-
-#define detect_zero(__x,__y,__01s,__80s)\
-       ((unsigned) __builtin_mips_subu_s_qb((v4i8) __01s,(v4i8) __x))
-
-    /* sets all 4 lanes to requested byte.  */
-#define set_byte_lanes(__x) ((unsigned) __builtin_mips_repl_qb(__x))
-
-    /* sets all 4 lanes to 0x01.  */
-#define def_and_set_01(__x) unsigned __x = (unsigned) __builtin_mips_repl_qb(0x01)
-
-    /* sets all 4 lanes to 0x80. Not needed when subu_s.qb used. */
-#define def_and_set_80(__x) /* do nothing */
-
-#else
-    /* this version, originally published in the 80's, uses
-       a reverse-carry-set like determination of the zero byte.
-       The steps are, for __x = 0x31ff0001:
-       __x - _01s = 0x30fdff00
-       ~__x = 0xce00fffe
-       ((__x - _01s) & ~__x) = 0x0000ff00
-       x & _80s = 0x00008000 <- byte 3 was zero
-       Some implementaions naively assume that characters are
-       always 7-bit unsigned ASCII. With that assumption, the
-       "& ~x" is usually discarded. Since character strings
-       are 8-bit, the and is needed to catch the case of
-       a false positive when the byte is 0x80. */
-
-#define detect_zero(__x,__y,_01s,_80s)\
-	((unsigned) (((__x) - _01s) & ~(__x)) & _80s)
-
-#if __mips64
-#define def_and_set_80(__x) unsigned __x =  0x8080808080808080ul
-#define def_and_set_01(__x)  unsigned __x = 0x0101010101010101ul
-#else
-#define def_and_set_80(__x) unsigned __x = 0x80808080ul
-#define def_and_set_01(__x) unsigned __x = 0x01010101ul
-#endif
-
-#endif
-#endif
-
-/* dealing with 'void *' conversions without using extra variables. */
-#define get_byte(__x,__idx) (((unsigned char *) (__x))[__idx])
-#define set_byte(__x,__idx,__fill) ((unsigned char *) (__x))[__idx] = (__fill)
-#define get_word(__x,__idx) (((unsigned *) (__x))[__idx])
-#define set_word(__x,__idx,__fill) ((unsigned *) (__x))[__idx] = (__fill)
-#define inc_ptr_as(__type,__x,__inc) __x = (void *) (((__type) __x) + (__inc))
-#define cvt_ptr_to(__type,__x) ((__type) (__x))
-
-#endif
diff --git a/libc/arch-mips/string/mips_strlen.c b/libc/arch-mips/string/mips_strlen.c
deleted file mode 100644
index f1465f2..0000000
--- a/libc/arch-mips/string/mips_strlen.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (c) 2010 MIPS Technologies, Inc.
- *
- * 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.
- *      * Neither the name of MIPS Technologies Inc. nor the names of its
- *        contributors may be used to endorse or promote products derived
- *        from this software without specific prior written permission.
- *
- * 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 <string.h>
-#include "mips-string-ops.h"
-
-#define do_strlen_word(__av) {\
-    if (detect_zero(x,x,_01s,_80s)) break;\
-    x = __av;\
-    cnt += sizeof (unsigned);\
-    }
-
-#define do_strlen_byte(__x) {\
-  if ((bx.b.B##__x) == 0) break;\
-  ++cnt;\
-  }
-
-#if SMOKE_TEST_MIPS_STRLEN
-#define strlen my_strlen
-#endif
-
-size_t
-strlen (const char *_a) __overloadable
-{
-  int cnt = 0;
-  unsigned x;
-
-  /* align the string to word boundary so we can do word at a time.  */
-  if ((cvt_ptr_to (unsigned, _a) & (sizeof (unsigned) - 1)) != 0)
-    {
-      if ((cvt_ptr_to (unsigned, _a) & 1) != 0)
-	{
-	  if (get_byte (_a, 0) == 0)
-	    return cnt;
-	  /* set bit 1 so 2-bytes are checked and incremented. */
-	  inc_ptr_as (char *, _a, 1);
-	  ++cnt;
-	}
-      if ((cvt_ptr_to (unsigned, _a) & 2) != 0)
-	{
-	  if (get_byte (_a, 0) == 0)
-	    return cnt + 0;
-	  if (get_byte (_a, 1) == 0)
-	    return cnt + 1;
-	  inc_ptr_as (char *, _a, 2);
-	  cnt += 2;
-	}
-    }
-
-#if __mips64
-#error strlen: mips64 check for 4-byte alignment not implemented.
-#endif
-
-  if (1)
-    {
-      def_and_set_01 (_01s);
-      def_and_set_80 (_80s);
-
-      /* as advantagous as it is to performance, this code cannot pre-load
-         the following word, nor can it prefetch the next line at the start
-         of the loop since the string can be at the end of a page with the
-         following page unmapped. There are tests in the suite to catch
-         any attempt to go beyond the current word. */
-      x = get_word (_a, 0);
-      while (1)
-	{
-	  /* doing 8 words should cover most strings.  */
-	  do_strlen_word (get_word (_a, 1));
-	  do_strlen_word (get_word (_a, 2));
-	  do_strlen_word (get_word (_a, 3));
-	  do_strlen_word (get_word (_a, 4));
-	  do_strlen_word (get_word (_a, 5));
-	  do_strlen_word (get_word (_a, 6));
-	  do_strlen_word (get_word (_a, 7));
-	  do_strlen_word (get_word (_a, 8));
-	  inc_ptr_as (unsigned *, _a, 8);
-	}
-    }
-  while (1)
-    {
-      /* pull apart the last word processed and find the zero.  */
-      bitfields_t bx;
-      bx.v = x;
-#if __mips64
-      do_strlen_byte (0);
-      do_strlen_byte (1);
-      do_strlen_byte (2);
-      do_strlen_byte (3);
-      do_strlen_byte (4);
-      do_strlen_byte (5);
-      do_strlen_byte (6);
-#else
-      do_strlen_byte (0);
-      do_strlen_byte (1);
-      do_strlen_byte (2);
-#endif
-      /* last byte is zero */
-      break;
-    }
-  return cnt;
-}
-
-#undef do_strlen_byte
-#undef do_strlen_word
-
-#if SMOKE_TEST_MIPS_STRLEN
-#include <stdio.h>
-char str1[] = "DHRYSTONE PROGRAM, 1'ST STRING";
-char str2[] = "DHRYSTONE PROGRAM, 2'ST STRING";
-
-char str3[] = "another string";
-char str4[] = "another";
-
-char str5[] = "somes tring";
-char str6[] = "somes_tring";
-
-char str7[16], str8[16];
-
-static char *
-chk (unsigned mine, unsigned libs, int *errors)
-{
-  static char answer[1024];
-  char *result = mine == libs ? "PASS" : "FAIL";
-  sprintf (answer, "new_strlen=%d: lib_strlen=%d: %s!", mine, libs, result);
-  if (mine != libs)
-    (*errors)++;
-  return answer;
-}
-
-int
-main (int argc, char **argv)
-{
-  int errors = 0;
-  /* set -1 in one position */
-  str6[5] = 0xff;
-  /* set zero in same position with junk in following 3 */
-  str7[0] = str8[0] = 0;
-  str7[1] = 0xff;
-  str7[2] = 'a';
-  str7[3] = 2;
-  str8[1] = 's';
-  str8[2] = -2;
-  str8[3] = 0;
-
-  fprintf (stderr, "========== mips_strlen%s test...\n",
-	   argv[0] ? argv[0] : "unknown strlen");
-#define P(__x,__y) {\
-    int a = my_strlen(__x + __y);\
-    int b = (strlen)(__x + __y) /* library version */;\
-    fprintf(stderr,"%s+%d: %s\n",#__x,__y,chk(a,b,&errors));\
-    }
-
-  P (str1, 0);
-  P (str1, 1);
-  P (str1, 2);
-  P (str1, 3);
-
-  P (str2, 0);
-  P (str2, 1);
-  P (str2, 2);
-  P (str2, 3);
-
-  P (str3, 0);
-  P (str3, 1);
-  P (str3, 2);
-  P (str3, 3);
-
-  P (str4, 0);
-  P (str4, 1);
-  P (str4, 2);
-  P (str4, 3);
-
-  P (str5, 0);
-  P (str5, 1);
-  P (str5, 2);
-  P (str5, 3);
-
-  P (str6, 0);
-  P (str6, 1);
-  P (str6, 2);
-  P (str6, 3);
-
-  P (str7, 0);
-  P (str7, 1);
-  P (str7, 2);
-  P (str7, 3);
-
-  P (str8, 0);
-  P (str8, 1);
-  P (str8, 2);
-  P (str8, 3);
-
-  return errors;
-}
-#endif
diff --git a/libc/arch-mips/string/strchr.c b/libc/arch-mips/string/strchr.c
new file mode 100644
index 0000000..c9397e7
--- /dev/null
+++ b/libc/arch-mips/string/strchr.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2017 Imagination Technologies.
+ *
+ * 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.
+ *      * Neither the name of Imagination Technologies nor the names of its
+ *        contributors may be used to endorse or promote products derived
+ *        from this software without specific prior written permission.
+ *
+ * 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 <string.h>
+
+#define op_t        unsigned long int
+#define op_size     sizeof (op_t)
+
+#if __mips64
+typedef struct
+{
+  op_t B0:8, B1:8, B2:8, B3:8, B4:8, B5:8, B6:8, B7:8;
+} bits_t;
+#else
+typedef struct
+{
+  op_t B0:8, B1:8, B2:8, B3:8;
+} bits_t;
+#endif
+
+typedef union
+{
+  op_t v;
+  bits_t b;
+} bitfields_t;
+
+#define DO_BYTE(i)                  \
+  if (a.b.B##i != ch) {             \
+    if(a.b.B##i == '\0') return 0;  \
+    p++;                            \
+  } else                            \
+    return (char *)p;
+
+#define DO_WORD(w, cnt) {                            \
+  op_t val = w[cnt] ^ mask_c;                        \
+  if ((((w[cnt] - mask_1) & ~w[cnt]) & mask_128) ||  \
+    (((val - mask_1) & ~val) & mask_128)) {          \
+    return do_bytes(w + cnt, ch);                    \
+  }                                                  \
+}
+
+static inline char * __attribute__ ((always_inline))
+do_bytes (const op_t* w, unsigned char ch)
+{
+  bitfields_t a;
+  unsigned char* p = (unsigned char *) w;
+  a.v = *w;
+#if __mips64
+  DO_BYTE(0)
+  DO_BYTE(1)
+  DO_BYTE(2)
+  DO_BYTE(3)
+  DO_BYTE(4)
+  DO_BYTE(5)
+  DO_BYTE(6)
+  DO_BYTE(7)
+#else
+  DO_BYTE(0)
+  DO_BYTE(1)
+  DO_BYTE(2)
+  DO_BYTE(3)
+#endif
+  return (char *)p;
+}
+
+char* strchr(const char* s, int c) __overloadable
+{
+  const op_t *w;
+  op_t mask_1, mask_128, mask_c;
+  const unsigned char ch = c;
+  unsigned char* p = (unsigned char *) s;
+
+  /*
+   * Check byte by byte till initial alignment
+   */
+  for ( ; *p != ch && ((size_t) p % op_size) != 0; p++)
+    if (*p == '\0')
+      return 0;
+
+  if (*p != ch) {
+    w = (const op_t *) p;
+
+    mask_c = ch | (ch << 8);
+    mask_c |= mask_c << 16;
+    __asm__ volatile (
+      "li %0, 0x01010101 \n\t"
+      : "=r" (mask_1)
+    );
+#if __mips64
+    mask_1 |= mask_1 << 32;
+    mask_c |= mask_c << 32;
+#endif
+    mask_128 = mask_1 << 7;
+
+    /*
+     * Check word/dword wize after initial alignment till character match
+     * or end of string
+     */
+    while (1) {
+      DO_WORD(w, 0)
+      DO_WORD(w, 1)
+      DO_WORD(w, 2)
+      DO_WORD(w, 3)
+      w += 4;
+    }
+  }
+
+  return (char *)p;
+}
diff --git a/libc/arch-mips/string/strcmp.S b/libc/arch-mips/string/strcmp.S
index 2b67f5a..e1faf2d 100644
--- a/libc/arch-mips/string/strcmp.S
+++ b/libc/arch-mips/string/strcmp.S
@@ -1,30 +1,33 @@
 /*
- * Copyright (c) 2014
- *      Imagination Technologies Limited.
+ * Copyright (c) 2017 Imagination Technologies.
+ *
+ * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. Neither the name of the MIPS Technologies, Inc., nor the names of its
- *    contributors may be used to endorse or promote products derived from
- *    this software without specific prior written permission.
  *
- * THIS SOFTWARE IS PROVIDED BY IMAGINATION TECHNOLOGIES LIMITED ``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 IMAGINATION TECHNOLOGIES LIMITED 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.
+ *      * 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.
+ *      * Neither the name of Imagination Technologies nor the names of its
+ *        contributors may be used to endorse or promote products derived
+ *        from this software without specific prior written permission.
+ *
+ * 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.
  */
 
 #ifdef __ANDROID__
@@ -41,6 +44,22 @@
 # include <sys/asm.h>
 #endif
 
+#if __mips64
+# define NSIZE 8
+# define LW ld
+# define EXT dext
+# define SRL dsrl
+# define SLL dsll
+# define SUBU dsubu
+#else
+# define NSIZE 4
+# define LW lw
+# define EXT ext
+# define SRL srl
+# define SLL sll
+# define SUBU subu
+#endif
+
 /* Technically strcmp should not read past the end of the strings being
    compared.  We will read a full word that may contain excess bits beyond
    the NULL string terminator but unless ENABLE_READAHEAD is set, we will not
@@ -77,6 +96,23 @@
 # endif
 #endif
 
+/* It might seem better to do the 'beq' instruction between the two 'lbu'
+   instructions so that the nop is not needed but testing showed that this
+   code is actually faster (based on glibc strcmp test).  */
+#define BYTECMP01(OFFSET) \
+    lbu v0, OFFSET(a0); \
+    lbu v1, OFFSET(a1); \
+    beq v0, zero, L(bexit01); \
+    nop; \
+    bne v0, v1, L(bexit01)
+
+#define BYTECMP89(OFFSET) \
+    lbu t8, OFFSET(a0); \
+    lbu t9, OFFSET(a1); \
+    beq t8, zero, L(bexit89); \
+    nop;    \
+    bne t8, t9, L(bexit89)
+
 /* Allow the routine to be named something else if desired.  */
 #ifndef STRCMP_NAME
 # define STRCMP_NAME strcmp
@@ -87,170 +123,236 @@
 #else
 LEAF(STRCMP_NAME)
 #endif
-	.set	nomips16
-	.set	noreorder
+    .set    nomips16
+    .set    noreorder
 
-	or	t0, a0, a1
-	andi	t0,0x3
-	bne	t0, zero, L(byteloop)
+    andi t1, a1, (NSIZE - 1)
+    beqz t1, L(exitalign)
+    or   t0, zero, NSIZE
+    SUBU t1, t0, t1 #process (NSIZE - 1) bytes at max
 
-/* Both strings are 4 byte aligned at this point.  */
+L(alignloop): #do by bytes until a1 aligned
+    BYTECMP01(0)
+    SUBU t1, t1, 0x1
+    PTR_ADDIU a0, a0, 0x1
+    bnez  t1, L(alignloop)
+    PTR_ADDIU a1, a1, 0x1
 
-	lui	t8, 0x0101
-	ori	t8, t8, 0x0101
-	lui	t9, 0x7f7f
-	ori	t9, 0x7f7f
+L(exitalign):
 
-#define STRCMP32(OFFSET) \
-	lw	v0, OFFSET(a0); \
-	lw	v1, OFFSET(a1); \
-	subu	t0, v0, t8; \
-	bne	v0, v1, L(worddiff); \
-	nor	t1, v0, t9; \
-	and	t0, t0, t1; \
-	bne	t0, zero, L(returnzero)
+/* string a1 is NSIZE byte aligned at this point. */
+
+    lui t8, 0x0101
+    ori t8, 0x0101
+    lui t9, 0x7f7f
+    ori t9, 0x7f7f
+#if __mips64
+    dsll t1, t8, 32
+    or  t8, t1
+    dsll t1, t9, 32
+    or  t9, t1
+#endif
+
+    andi t2, a0, (NSIZE - 1) #check if a0 aligned
+    SUBU t3, t0, t2 #t3 will be used as shifter
+    bnez t2, L(uloopenter)
+    SUBU a2, a0, t2 #bring back a0 to aligned position
+
+#define STRCMPW(OFFSET) \
+    LW   v0, OFFSET(a0); \
+    LW   v1, OFFSET(a1); \
+    SUBU t0, v0, t8; \
+    bne  v0, v1, L(worddiff); \
+    nor  t1, v0, t9; \
+    and  t0, t0, t1; \
+    bne  t0, zero, L(returnzero);\
 
 L(wordloop):
-	STRCMP32(0)
-	DELAY_READ
-	STRCMP32(4)
-	DELAY_READ
-	STRCMP32(8)
-	DELAY_READ
-	STRCMP32(12)
-	DELAY_READ
-	STRCMP32(16)
-	DELAY_READ
-	STRCMP32(20)
-	DELAY_READ
-	STRCMP32(24)
-	DELAY_READ
-	STRCMP32(28)
-	PTR_ADDIU a0, a0, 32
-	b	L(wordloop)
-	PTR_ADDIU a1, a1, 32
+    STRCMPW(0 * NSIZE)
+    DELAY_READ
+    STRCMPW(1 * NSIZE)
+    DELAY_READ
+    STRCMPW(2 * NSIZE)
+    DELAY_READ
+    STRCMPW(3 * NSIZE)
+    DELAY_READ
+    STRCMPW(4 * NSIZE)
+    DELAY_READ
+    STRCMPW(5 * NSIZE)
+    DELAY_READ
+    STRCMPW(6 * NSIZE)
+    DELAY_READ
+    STRCMPW(7 * NSIZE)
+    PTR_ADDIU a0, a0, (8 * NSIZE)
+    b   L(wordloop)
+    PTR_ADDIU a1, a1, (8 * NSIZE)
+
+#define USTRCMPW(OFFSET) \
+    LW  v1, OFFSET(a1); \
+    SUBU    t0, v0, t8; \
+    nor t1, v0, t9; \
+    and t0, t0, t1; \
+    bne t0, zero, L(worddiff); \
+    SRL v0, t2; \
+    LW  a3, (OFFSET + NSIZE)(a2); \
+    SUBU    t0, v1, t8; \
+    SLL t1, a3, t3; \
+    or v0, v0, t1; \
+    bne v0, v1, L(worddiff); \
+    nor t1, v1, t9; \
+    and t0, t0, t1; \
+    bne t0, zero, L(returnzero); \
+    move v0, a3;\
+
+L(uloopenter):
+    LW  v0, 0(a2)
+    SLL t2, 3  #multiply by 8
+    SLL t3, 3  #multiply by 8
+    li  a3, -1 #all 1s
+    SRL a3, t3
+    or v0, a3 #replace with all 1s if zeros in unintented read
+
+L(uwordloop):
+    USTRCMPW(0 * NSIZE)
+    USTRCMPW(1 * NSIZE)
+    USTRCMPW(2 * NSIZE)
+    USTRCMPW(3 * NSIZE)
+    USTRCMPW(4 * NSIZE)
+    USTRCMPW(5 * NSIZE)
+    USTRCMPW(6 * NSIZE)
+    USTRCMPW(7 * NSIZE)
+    PTR_ADDIU a2, a2, (8 * NSIZE)
+    b   L(uwordloop)
+    PTR_ADDIU a1, a1, (8 * NSIZE)
 
 L(returnzero):
-	j	ra
-	move	v0, zero
+    j   ra
+    move    v0, zero
+
+#if __mips_isa_rev > 1
+#define EXT_COMPARE01(POS) \
+    EXT t0, v0, POS, 8; \
+    beq t0, zero, L(wexit01); \
+    EXT t1, v1, POS, 8; \
+    bne t0, t1, L(wexit01)
+#define EXT_COMPARE89(POS) \
+    EXT t8, v0, POS, 8; \
+    beq t8, zero, L(wexit89); \
+    EXT t9, v1, POS, 8; \
+    bne t8, t9, L(wexit89)
+#else
+#define EXT_COMPARE01(POS) \
+    SRL  t0, v0, POS; \
+    SRL  t1, v1, POS; \
+    andi t0, t0, 0xff; \
+    beq  t0, zero, L(wexit01); \
+    andi t1, t1, 0xff; \
+    bne  t0, t1, L(wexit01)
+#define EXT_COMPARE89(POS) \
+    SRL  t8, v0, POS; \
+    SRL  t9, v1, POS; \
+    andi t8, t8, 0xff; \
+    beq  t8, zero, L(wexit89); \
+    andi t9, t9, 0xff; \
+    bne  t8, t9, L(wexit89)
+#endif
 
 L(worddiff):
 #ifdef USE_CLZ
-	subu	t0, v0, t8
-	nor	t1, v0, t9
-	and	t1, t0, t1
-	xor	t0, v0, v1
-	or	t0, t0, t1
+    SUBU    t0, v0, t8
+    nor t1, v0, t9
+    and t1, t0, t1
+    xor t0, v0, v1
+    or  t0, t0, t1
 # if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-	wsbh	t0, t0
-	rotr	t0, t0, 16
+    wsbh    t0, t0
+    rotr    t0, t0, 16
 # endif
-	clz	t1, t0
-	and	t1, 0xf8
+    clz t1, t0
+    and t1, 0xf8
 # if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-	neg	t1
-	addu	t1, 24
+    neg t1
+    addu    t1, 24
 # endif
-	rotrv	v0, v0, t1
-	rotrv	v1, v1, t1
-	and	v0, v0, 0xff
-	and	v1, v1, 0xff
-	j	ra
-	subu	v0, v0, v1
+    rotrv   v0, v0, t1
+    rotrv   v1, v1, t1
+    and v0, v0, 0xff
+    and v1, v1, 0xff
+    j   ra
+    SUBU    v0, v0, v1
 #else /* USE_CLZ */
 # if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-	andi	t0, v0, 0xff
-	beq	t0, zero, L(wexit01)
-	andi	t1, v1, 0xff
-	bne	t0, t1, L(wexit01)
+    andi    t0, v0, 0xff
+    beq t0, zero, L(wexit01)
+    andi    t1, v1, 0xff
+    bne t0, t1, L(wexit01)
+    EXT_COMPARE89(8)
+    EXT_COMPARE01(16)
+#ifndef __mips64
+    SRL t8, v0, 24
+    SRL t9, v1, 24
+#else
+    EXT_COMPARE89(24)
+    EXT_COMPARE01(32)
+    EXT_COMPARE89(40)
+    EXT_COMPARE01(48)
+    SRL t8, v0, 56
+    SRL t9, v1, 56
+#endif
 
-	srl	t8, v0, 8
-	srl	t9, v1, 8
-	andi	t8, t8, 0xff
-	beq	t8, zero, L(wexit89)
-	andi	t9, t9, 0xff
-	bne	t8, t9, L(wexit89)
-
-	srl	t0, v0, 16
-	srl	t1, v1, 16
-	andi	t0, t0, 0xff
-	beq	t0, zero, L(wexit01)
-	andi	t1, t1, 0xff
-	bne	t0, t1, L(wexit01)
-
-	srl	t8, v0, 24
-	srl	t9, v1, 24
 # else /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ */
-	srl	t0, v0, 24
-	beq	t0, zero, L(wexit01)
-	srl	t1, v1, 24
-	bne	t0, t1, L(wexit01)
+#ifdef __mips64
+    SRL t0, v0, 56
+    beq t0, zero, L(wexit01)
+    SRL t1, v1, 56
+    bne t0, t1, L(wexit01)
+    EXT_COMPARE89(48)
+    EXT_COMPARE01(40)
+    EXT_COMPARE89(32)
+    EXT_COMPARE01(24)
+#else
+    SRL t0, v0, 24
+    beq t0, zero, L(wexit01)
+    SRL t1, v1, 24
+    bne t0, t1, L(wexit01)
+#endif
+    EXT_COMPARE89(16)
+    EXT_COMPARE01(8)
 
-	srl	t8, v0, 16
-	srl	t9, v1, 16
-	andi	t8, t8, 0xff
-	beq	t8, zero, L(wexit89)
-	andi	t9, t9, 0xff
-	bne	t8, t9, L(wexit89)
-
-	srl	t0, v0, 8
-	srl	t1, v1, 8
-	andi	t0, t0, 0xff
-	beq	t0, zero, L(wexit01)
-	andi	t1, t1, 0xff
-	bne	t0, t1, L(wexit01)
-
-	andi	t8, v0, 0xff
-	andi	t9, v1, 0xff
+    andi    t8, v0, 0xff
+    andi    t9, v1, 0xff
 # endif /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ */
 
 L(wexit89):
-	j	ra
-	subu	v0, t8, t9
+    j   ra
+    SUBU    v0, t8, t9
 L(wexit01):
-	j	ra
-	subu	v0, t0, t1
+    j   ra
+    SUBU    v0, t0, t1
 #endif /* USE_CLZ */
 
-/* It might seem better to do the 'beq' instruction between the two 'lbu'
-   instructions so that the nop is not needed but testing showed that this
-   code is actually faster (based on glibc strcmp test).  */
-#define BYTECMP01(OFFSET) \
-	lbu	v0, OFFSET(a0); \
-	lbu	v1, OFFSET(a1); \
-	beq	v0, zero, L(bexit01); \
-	nop; \
-	bne	v0, v1, L(bexit01)
-
-#define BYTECMP89(OFFSET) \
-	lbu	t8, OFFSET(a0); \
-	lbu	t9, OFFSET(a1); \
-	beq	t8, zero, L(bexit89); \
-	nop;	\
-	bne	t8, t9, L(bexit89)
-
 L(byteloop):
-	BYTECMP01(0)
-	BYTECMP89(1)
-	BYTECMP01(2)
-	BYTECMP89(3)
-	BYTECMP01(4)
-	BYTECMP89(5)
-	BYTECMP01(6)
-	BYTECMP89(7)
-	PTR_ADDIU a0, a0, 8
-	b	L(byteloop)
-	PTR_ADDIU a1, a1, 8
+    BYTECMP01(0)
+    BYTECMP89(1)
+    BYTECMP01(2)
+    BYTECMP89(3)
+    BYTECMP01(4)
+    BYTECMP89(5)
+    BYTECMP01(6)
+    BYTECMP89(7)
+    PTR_ADDIU a0, a0, 8
+    b   L(byteloop)
+    PTR_ADDIU a1, a1, 8
 
 L(bexit01):
-	j	ra
-	subu	v0, v0, v1
+    j   ra
+    SUBU    v0, v0, v1
 L(bexit89):
-	j	ra
-	subu	v0, t8, t9
+    j   ra
+    SUBU    v0, t8, t9
 
-	.set	at
-	.set	reorder
+    .set    at
+    .set    reorder
 
 END(STRCMP_NAME)
 #ifndef __ANDROID__
diff --git a/libc/arch-mips/string/strcpy.c b/libc/arch-mips/string/strcpy.c
new file mode 100644
index 0000000..7b5dee3
--- /dev/null
+++ b/libc/arch-mips/string/strcpy.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2017 Imagination Technologies.
+ *
+ * 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.
+ *      * Neither the name of Imagination Technologies nor the names of its
+ *        contributors may be used to endorse or promote products derived
+ *        from this software without specific prior written permission.
+ *
+ * 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 <string.h>
+
+#define op_t        unsigned long int
+
+#if !defined(UNALIGNED_INSTR_SUPPORT)
+/* does target have unaligned lw/ld/ualw/uald instructions? */
+#define UNALIGNED_INSTR_SUPPORT 0
+#if __mips_isa_rev < 6 && !__mips1
+#undef UNALIGNED_INSTR_SUPPORT
+#define UNALIGNED_INSTR_SUPPORT 1
+#endif
+#endif
+
+#if !defined(HW_UNALIGNED_SUPPORT)
+/* Does target have hardware support for unaligned accesses?  */
+#define HW_UNALIGNED_SUPPORT 0
+#if __mips_isa_rev >= 6
+#undef HW_UNALIGNED_SUPPORT
+#define HW_UNALIGNED_SUPPORT 1
+#endif
+#endif
+
+#if __mips64
+typedef struct
+{
+  op_t B0:8, B1:8, B2:8, B3:8, B4:8, B5:8, B6:8, B7:8;
+} bits_t;
+#else
+typedef struct
+{
+  op_t B0:8, B1:8, B2:8, B3:8;
+} bits_t;
+#endif
+
+typedef union
+{
+  op_t v;
+  bits_t b;
+} bitfields_t;
+
+#if !HW_UNALIGNED_SUPPORT && UNALIGNED_INSTR_SUPPORT
+/* for MIPS GCC, there are no unaligned builtins - so this struct forces
+   the compiler to treat the pointer access as unaligned.  */
+struct ulw
+{
+  op_t uli;
+} __attribute__ ((packed));
+#endif /* !HW_UNALIGNED_SUPPORT && UNALIGNED_INSTR_SUPPORT */
+
+#define DO_BYTE(i, ptdst) {  \
+  *(ptdst+i) = a.b.B##i;     \
+  if(a.b.B##i == '\0')       \
+    return ret;              \
+}
+
+#if __mips64
+#define DO_BYTES(val, dst) {   \
+  bitfields_t a;               \
+  char *tdst = (char *)(dst);  \
+  a.v = val;                   \
+  DO_BYTE(0, tdst)             \
+  DO_BYTE(1, tdst)             \
+  DO_BYTE(2, tdst)             \
+  DO_BYTE(3, tdst)             \
+  DO_BYTE(4, tdst)             \
+  DO_BYTE(5, tdst)             \
+  DO_BYTE(6, tdst)             \
+  DO_BYTE(7, tdst)             \
+}
+#else
+#define DO_BYTES(val, dst) {   \
+  bitfields_t a;               \
+  char *tdst = (char *)(dst);  \
+  a.v = val;                   \
+  DO_BYTE(0, tdst)             \
+  DO_BYTE(1, tdst)             \
+  DO_BYTE(2, tdst)             \
+  DO_BYTE(3, tdst)             \
+}
+#endif
+
+#define DO_WORD_ALIGNED(dst, src) {                 \
+  op_t val = *(src);                                \
+  if ((((val - mask_1) & ~val) & mask_128) != 0) {  \
+    DO_BYTES(val, dst);                             \
+  } else *(dst) = val;                              \
+}
+
+#if !HW_UNALIGNED_SUPPORT
+#if UNALIGNED_INSTR_SUPPORT
+#define DO_WORD_UNALIGNED(dst, src) {               \
+  op_t val = *(src);                                \
+  if ((((val - mask_1) & ~val) & mask_128) != 0) {  \
+    DO_BYTES(val, dst);                             \
+  } else {                                          \
+    struct ulw *a = (struct ulw *)(dst);            \
+    a->uli = val;                                   \
+  }                                                 \
+}
+#else
+#define DO_WORD_UNALIGNED(dst, src) {                 \
+  op_t val = *(src);                                  \
+  if ((((val - mask_1) & ~val) & mask_128) != 0) {    \
+    DO_BYTES(val, dst);                               \
+  } else {                                            \
+    char *pdst = (char *) dst;                        \
+    const char *psrc = (const char *) src;            \
+    for (; (*pdst = *psrc) != '\0'; ++psrc, ++pdst);  \
+    return ret;                                       \
+  }                                                   \
+}
+#endif /* UNALIGNED_INSTR_SUPPORT */
+
+#define PROCESS_UNALIGNED_WORDS(a, b) { \
+  while (1) {                           \
+    DO_WORD_UNALIGNED(a, b);            \
+    DO_WORD_UNALIGNED(a + 1, b + 1);    \
+    DO_WORD_UNALIGNED(a + 2, b + 2);    \
+    DO_WORD_UNALIGNED(a + 3, b + 3);    \
+    a += 4;                             \
+    b += 4;                             \
+  }                                     \
+}
+#endif /* HW_UNALIGNED_SUPPORT */
+
+#define PROCESS_ALIGNED_WORDS(a, b) {  \
+  while (1) {                          \
+    DO_WORD_ALIGNED(a, b);             \
+    DO_WORD_ALIGNED(a + 1, b + 1);     \
+    DO_WORD_ALIGNED(a + 2, b + 2);     \
+    DO_WORD_ALIGNED(a + 3, b + 3);     \
+    a += 4;                            \
+    b += 4;                            \
+  }                                    \
+}
+
+char *
+strcpy (char *to, const char *from) __overloadable
+{
+  char *ret = to;
+  op_t mask_1, mask_128;
+  const op_t *src;
+  op_t *dst;
+
+  for (; (*to = *from) != '\0' && ((size_t) from % sizeof (op_t)) != 0; ++from, ++to);
+
+  if(*to != '\0') {
+    __asm__ volatile (
+      "li %0, 0x01010101 \n\t"
+      : "=r" (mask_1)
+    );
+#if __mips64
+    mask_1 |= mask_1 << 32;
+#endif
+    mask_128 = mask_1 << 7;
+
+    src = (const op_t *) from;
+    dst = (op_t *) to;
+
+#if HW_UNALIGNED_SUPPORT
+    PROCESS_ALIGNED_WORDS(dst, src);
+#else
+    if (((unsigned long) dst) % sizeof (op_t) == 0) {
+      PROCESS_ALIGNED_WORDS(dst, src);
+    } else {
+      PROCESS_UNALIGNED_WORDS(dst, src);
+    }
+#endif
+  }
+
+  return ret;
+}
diff --git a/libc/arch-mips/string/strlen.c b/libc/arch-mips/string/strlen.c
index 488e3c8..491efae 100644
--- a/libc/arch-mips/string/strlen.c
+++ b/libc/arch-mips/string/strlen.c
@@ -1,43 +1,115 @@
-/*	$OpenBSD: strlen.c,v 1.8 2014/06/10 04:17:37 deraadt Exp $	*/
-
-/*-
- * Copyright (c) 1990, 1993
- *	The Regents of the University of California.  All rights reserved.
+/*
+ * Copyright (c) 2017 Imagination Technologies.
+ *
+ * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
  *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *      * 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.
+ *      * Neither the name of Imagination Technologies nor the names of its
+ *        contributors may be used to endorse or promote products derived
+ *        from this software without specific prior written permission.
+ *
+ * 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 <string.h>
 
-size_t
-strlen(const char *str) __overloadable
-{
-	const char *s;
+#define op_t        unsigned long int
+#define op_size     sizeof (op_t)
 
-	for (s = str; *s; ++s)
-		;
-	return (s - str);
+#if __mips64 || __mips_isa_rev >= 2
+static inline size_t __attribute__ ((always_inline))
+do_bytes (const char *base, const char *p, op_t inval)
+{
+  op_t outval = 0;
+#if __mips64
+  __asm__ volatile (
+    "dsbh %1, %0 \n\t"
+    "dshd %0, %1 \n\t"
+    "dclz %1, %0 \n\t"
+    : "+r" (inval), "+r" (outval)
+  );
+#else
+  __asm__ volatile (
+    "wsbh %1, %0 \n\t"
+    "rotr %0, %1, 16 \n\t"
+    "clz %1, %0 \n\t"
+    : "+r" (inval), "+r" (outval)
+  );
+#endif
+  p += (outval >> 3);
+  return (size_t) (p - base);
 }
 
+#define DO_WORD(w, cnt) {                                \
+  op_t val = ((w[cnt] - mask_1) & ~w[cnt]) & mask_128;   \
+  if (val)                                               \
+    return do_bytes(str, (const char *)(w + cnt), val);  \
+}
+#else
+static inline size_t __attribute__ ((always_inline))
+do_bytes (const char *base, const char *p)
+{
+  for (; *p; ++p);
+  return (size_t) (p - base);
+}
+
+#define DO_WORD(w, cnt) {                           \
+  if (((w[cnt] - mask_1) & ~w[cnt]) & mask_128)     \
+    return do_bytes(str, (const char *)(w + cnt));  \
+}
+#endif
+
+size_t
+strlen (const char *str) __overloadable
+{
+  if (*str) {
+    const char *p = (const char *) str;
+    const op_t *w;
+    op_t mask_1, mask_128;
+
+    while ((size_t) p % sizeof (op_t)) {
+      if (!(*p))
+        return (p - str);
+      p++;
+    }
+
+    __asm__ volatile (
+      "li %0, 0x01010101 \n\t"
+      : "=r" (mask_1)
+    );
+#if __mips64
+    mask_1 |= mask_1 << 32;
+#endif
+    mask_128 = mask_1 << 7;
+
+    w = (const op_t *) p;
+
+    while (1) {
+      DO_WORD(w, 0);
+      DO_WORD(w, 1);
+      DO_WORD(w, 2);
+      DO_WORD(w, 3);
+      w += 4;
+    }
+  }
+  return 0;
+}
diff --git a/libc/arch-mips/string/strncmp.S b/libc/arch-mips/string/strncmp.S
new file mode 100644
index 0000000..4867c44
--- /dev/null
+++ b/libc/arch-mips/string/strncmp.S
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2017 Imagination Technologies.
+ *
+ * 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.
+ *      * Neither the name of Imagination Technologies nor the names of its
+ *        contributors may be used to endorse or promote products derived
+ *        from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+#ifdef __ANDROID__
+# include <private/bionic_asm.h>
+#elif _LIBC
+# include <sysdep.h>
+# include <regdef.h>
+# include <sys/asm.h>
+#elif _COMPILING_NEWLIB
+# include "machine/asm.h"
+# include "machine/regdef.h"
+#else
+# include <regdef.h>
+# include <sys/asm.h>
+#endif
+
+#if __mips64
+# define NSIZE 8
+# define LW ld
+# define LWR ldr
+# define LWL ldl
+# define EXT dext
+# define SRL dsrl
+# define SUBU dsubu
+#else
+# define NSIZE 4
+# define LW lw
+# define LWR lwr
+# define LWL lwl
+# define EXT ext
+# define SRL srl
+# define SUBU subu
+#endif
+
+/* Technically strcmp should not read past the end of the strings being
+   compared.  We will read a full word that may contain excess bits beyond
+   the NULL string terminator but unless ENABLE_READAHEAD is set, we will not
+   read the next word after the end of string.  Setting ENABLE_READAHEAD will
+   improve performance but is technically illegal based on the definition of
+   strcmp.  */
+#ifdef ENABLE_READAHEAD
+# define DELAY_READ
+#else
+# define DELAY_READ nop
+#endif
+
+/* Testing on a little endian machine showed using CLZ was a
+   performance loss, so we are not turning it on by default.  */
+#if defined(ENABLE_CLZ) && (__mips_isa_rev > 1) && (!__mips64)
+# define USE_CLZ
+#endif
+
+/* Some asm.h files do not have the L macro definition.  */
+#ifndef L
+# if _MIPS_SIM == _ABIO32
+#  define L(label) $L ## label
+# else
+#  define L(label) .L ## label
+# endif
+#endif
+
+/* Some asm.h files do not have the PTR_ADDIU macro definition.  */
+#ifndef PTR_ADDIU
+# if _MIPS_SIM == _ABIO32
+#  define PTR_ADDIU       addiu
+# else
+#  define PTR_ADDIU       daddiu
+# endif
+#endif
+
+/* It might seem better to do the 'beq' instruction between the two 'lbu'
+   instructions so that the nop is not needed but testing showed that this
+   code is actually faster (based on glibc strcmp test).  */
+#define BYTECMP01(OFFSET) \
+    lbu v0, OFFSET(a0); \
+    lbu v1, OFFSET(a1); \
+    beq v0, zero, L(bexit01); \
+    nop; \
+    bne v0, v1, L(bexit01)
+
+#define BYTECMP89(OFFSET) \
+    lbu t8, OFFSET(a0); \
+    lbu t9, OFFSET(a1); \
+    beq t8, zero, L(bexit89); \
+    nop;    \
+    bne t8, t9, L(bexit89)
+
+/* Allow the routine to be named something else if desired.  */
+#ifndef STRNCMP_NAME
+# define STRNCMP_NAME strncmp
+#endif
+
+#ifdef __ANDROID__
+LEAF(STRNCMP_NAME, 0)
+#else
+LEAF(STRNCMP_NAME)
+#endif
+    .set    nomips16
+    .set    noreorder
+
+    srl t0, a2, (2 + NSIZE / 4)
+    beqz  t0, L(byteloop) #process by bytes if less than (2 * NSIZE)
+    andi t1, a1, (NSIZE - 1)
+    beqz  t1, L(exitalign)
+    or   t0, zero, NSIZE
+    SUBU t1, t0, t1 #process (NSIZE - 1) bytes at max
+    SUBU a2, a2, t1 #dec count by t1
+
+L(alignloop): #do by bytes until a1 aligned
+    BYTECMP01(0)
+    SUBU t1, t1, 0x1
+    PTR_ADDIU a0, a0, 0x1
+    bne  t1, zero, L(alignloop)
+    PTR_ADDIU a1, a1, 0x1
+
+L(exitalign):
+
+/* string a1 is NSIZE byte aligned at this point. */
+#ifndef __mips1
+    lui t8, 0x0101
+    ori t8, 0x0101
+    lui t9, 0x7f7f
+    ori t9, 0x7f7f
+#if __mips64
+    dsll t0, t8, 32
+    or  t8, t0
+    dsll t1, t9, 32
+    or  t9, t1
+#endif
+#endif
+
+/* hardware or software alignment not supported for mips1
+   rev6 archs have h/w unaligned support
+   remainings archs need to implemented with unaligned instructions */
+
+#if __mips1
+    andi t0, a0, (NSIZE - 1)
+    bne  t0, zero, L(byteloop)
+#elif __mips_isa_rev < 6
+    andi t0, a0, (NSIZE - 1)
+    bne  t0, zero, L(uwordloop)
+#endif
+
+#define STRCMPW(OFFSET) \
+    LW   v0, (OFFSET)(a0); \
+    LW   v1, (OFFSET)(a1); \
+    SUBU t0, v0, t8; \
+    bne  v0, v1, L(worddiff); \
+    nor  t1, v0, t9; \
+    and  t0, t0, t1; \
+    bne  t0, zero, L(returnzero);\
+
+L(wordloop):
+    SUBU t1, a2, (8 * NSIZE)
+    bltz t1, L(onewords)
+    STRCMPW(0 * NSIZE)
+    DELAY_READ
+    STRCMPW(1 * NSIZE)
+    DELAY_READ
+    STRCMPW(2 * NSIZE)
+    DELAY_READ
+    STRCMPW(3 * NSIZE)
+    DELAY_READ
+    STRCMPW(4 * NSIZE)
+    DELAY_READ
+    STRCMPW(5 * NSIZE)
+    DELAY_READ
+    STRCMPW(6 * NSIZE)
+    DELAY_READ
+    STRCMPW(7 * NSIZE)
+    SUBU a2, a2, (8 * NSIZE)
+    PTR_ADDIU a0, a0, (8 * NSIZE)
+    b   L(wordloop)
+    PTR_ADDIU a1, a1, (8 * NSIZE)
+
+L(onewords):
+    SUBU t1, a2, NSIZE
+    bltz t1, L(byteloop)
+    STRCMPW(0)
+    SUBU a2, a2, NSIZE
+    PTR_ADDIU a0, a0, NSIZE
+    b   L(onewords)
+    PTR_ADDIU a1, a1, NSIZE
+
+#if __mips_isa_rev < 6 && !__mips1
+#define USTRCMPW(OFFSET) \
+    LWR v0, (OFFSET)(a0); \
+    LWL v0, (OFFSET + NSIZE - 1)(a0); \
+    LW  v1, (OFFSET)(a1); \
+    SUBU    t0, v0, t8; \
+    bne v0, v1, L(worddiff); \
+    nor t1, v0, t9; \
+    and t0, t0, t1; \
+    bne t0, zero, L(returnzero);\
+
+L(uwordloop):
+    SUBU t1, a2, (8 * NSIZE)
+    bltz t1, L(uonewords)
+    USTRCMPW(0 * NSIZE)
+    DELAY_READ
+    USTRCMPW(1 * NSIZE)
+    DELAY_READ
+    USTRCMPW(2 * NSIZE)
+    DELAY_READ
+    USTRCMPW(3 * NSIZE)
+    DELAY_READ
+    USTRCMPW(4 * NSIZE)
+    DELAY_READ
+    USTRCMPW(5 * NSIZE)
+    DELAY_READ
+    USTRCMPW(6 * NSIZE)
+    DELAY_READ
+    USTRCMPW(7 * NSIZE)
+    SUBU a2, a2, (8 * NSIZE)
+    PTR_ADDIU a0, a0, (8 * NSIZE)
+    b   L(uwordloop)
+    PTR_ADDIU a1, a1, (8 * NSIZE)
+
+L(uonewords):
+    SUBU t1, a2, NSIZE
+    bltz t1, L(byteloop)
+    USTRCMPW(0)
+    SUBU a2, a2, NSIZE
+    PTR_ADDIU a0, a0, NSIZE
+    b   L(uonewords)
+    PTR_ADDIU a1, a1, NSIZE
+
+#endif
+
+L(returnzero):
+    j   ra
+    move    v0, zero
+
+#if __mips_isa_rev > 1
+#define EXT_COMPARE01(POS) \
+    EXT t0, v0, POS, 8; \
+    beq t0, zero, L(wexit01); \
+    EXT t1, v1, POS, 8; \
+    bne t0, t1, L(wexit01)
+#define EXT_COMPARE89(POS) \
+    EXT t8, v0, POS, 8; \
+    beq t8, zero, L(wexit89); \
+    EXT t9, v1, POS, 8; \
+    bne t8, t9, L(wexit89)
+#else
+#define EXT_COMPARE01(POS) \
+    SRL  t0, v0, POS; \
+    SRL  t1, v1, POS; \
+    andi t0, t0, 0xff; \
+    beq  t0, zero, L(wexit01); \
+    andi t1, t1, 0xff; \
+    bne  t0, t1, L(wexit01)
+#define EXT_COMPARE89(POS) \
+    SRL  t8, v0, POS; \
+    SRL  t9, v1, POS; \
+    andi t8, t8, 0xff; \
+    beq  t8, zero, L(wexit89); \
+    andi t9, t9, 0xff; \
+    bne  t8, t9, L(wexit89)
+#endif
+
+L(worddiff):
+#ifdef USE_CLZ
+    SUBU    t0, v0, t8
+    nor t1, v0, t9
+    and t1, t0, t1
+    xor t0, v0, v1
+    or  t0, t0, t1
+# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+    wsbh    t0, t0
+    rotr    t0, t0, 16
+# endif
+    clz t1, t0
+    and t1, 0xf8
+# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+    neg t1
+    addu    t1, 24
+# endif
+    rotrv   v0, v0, t1
+    rotrv   v1, v1, t1
+    and v0, v0, 0xff
+    and v1, v1, 0xff
+    j   ra
+    SUBU    v0, v0, v1
+#else /* USE_CLZ */
+# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+    andi    t0, v0, 0xff
+    beq t0, zero, L(wexit01)
+    andi    t1, v1, 0xff
+    bne t0, t1, L(wexit01)
+    EXT_COMPARE89(8)
+    EXT_COMPARE01(16)
+#ifndef __mips64
+    SRL t8, v0, 24
+    SRL t9, v1, 24
+#else
+    EXT_COMPARE89(24)
+    EXT_COMPARE01(32)
+    EXT_COMPARE89(40)
+    EXT_COMPARE01(48)
+    SRL t8, v0, 56
+    SRL t9, v1, 56
+#endif
+
+# else /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ */
+#ifdef __mips64
+    SRL t0, v0, 56
+    beq t0, zero, L(wexit01)
+    SRL t1, v1, 56
+    bne t0, t1, L(wexit01)
+    EXT_COMPARE89(48)
+    EXT_COMPARE01(40)
+    EXT_COMPARE89(32)
+    EXT_COMPARE01(24)
+#else
+    SRL t0, v0, 24
+    beq t0, zero, L(wexit01)
+    SRL t1, v1, 24
+    bne t0, t1, L(wexit01)
+#endif
+    EXT_COMPARE89(16)
+    EXT_COMPARE01(8)
+
+    andi    t8, v0, 0xff
+    andi    t9, v1, 0xff
+# endif /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ */
+
+L(wexit89):
+    j   ra
+    SUBU    v0, t8, t9
+L(wexit01):
+    j   ra
+    SUBU    v0, t0, t1
+#endif /* USE_CLZ */
+
+L(byteloop):
+    beq a2, zero, L(returnzero)
+    SUBU a2, a2, 1
+    BYTECMP01(0)
+    nop
+    beq a2, zero, L(returnzero)
+    SUBU a2, a2, 1
+    BYTECMP89(1)
+    nop
+    beq a2, zero, L(returnzero)
+    SUBU a2, a2, 1
+    BYTECMP01(2)
+    nop
+    beq a2, zero, L(returnzero)
+    SUBU a2, a2, 1
+    BYTECMP89(3)
+    PTR_ADDIU a0, a0, 4
+    b   L(byteloop)
+    PTR_ADDIU a1, a1, 4
+
+L(bexit01):
+    j   ra
+    SUBU    v0, v0, v1
+L(bexit89):
+    j   ra
+    SUBU    v0, t8, t9
+
+    .set    at
+    .set    reorder
+
+END(STRNCMP_NAME)
+#ifndef __ANDROID__
+# ifdef _LIBC
+libc_hidden_builtin_def (STRNCMP_NAME)
+# endif
+#endif
diff --git a/libc/arch-mips/string/strnlen.c b/libc/arch-mips/string/strnlen.c
new file mode 100644
index 0000000..2011deb
--- /dev/null
+++ b/libc/arch-mips/string/strnlen.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2017 Imagination Technologies.
+ *
+ * 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.
+ *      * Neither the name of Imagination Technologies nor the names of its
+ *        contributors may be used to endorse or promote products derived
+ *        from this software without specific prior written permission.
+ *
+ * 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 <string.h>
+
+#define op_t                unsigned long int
+#define op_size             sizeof (op_t)
+
+#if __mips64 || __mips_isa_rev >= 2
+static inline size_t __attribute__ ((always_inline))
+do_bytes (const char *base, const char *p, op_t inval)
+{
+  op_t outval = 0;
+#if __mips64
+  __asm__ volatile (
+    "dsbh %1, %0 \n\t"
+    "dshd %0, %1 \n\t"
+    "dclz %1, %0 \n\t"
+    : "+r" (inval), "+r" (outval)
+  );
+#else
+  __asm__ volatile (
+    "wsbh %1, %0 \n\t"
+    "rotr %0, %1, 16 \n\t"
+    "clz %1, %0 \n\t"
+    : "+r" (inval), "+r" (outval)
+  );
+#endif
+  p += (outval >> 3);
+  return (size_t) (p - base);
+}
+
+#define DO_WORD(in, val) {                          \
+  op_t tmp = ((val - mask_1) & ~val) & mask_128;    \
+  if (tmp)                                          \
+    return do_bytes(str, (const char *)(in), tmp);  \
+}
+#else
+static inline size_t __attribute__ ((always_inline))
+do_bytes (const char *base, const char *p)
+{
+  for (; *p; ++p);
+  return (size_t) (p - base);
+}
+
+#define DO_WORD(in, val) {                     \
+  if (((val - mask_1) & ~val) & mask_128) {    \
+    return do_bytes(str, (const char *)(in));  \
+  }                                            \
+}
+#endif
+
+size_t strnlen (const char *str, size_t n) {
+  if (n != 0) {
+    const char *p = (const char *) str;
+    const op_t *w;
+    op_t mask_1, mask_128;
+
+    for (; n > 0 && ((size_t) p % op_size) != 0; --n, ++p) {
+      if (!(*p))
+        return (p - str);
+    }
+
+    w = (const op_t *) p;
+
+    __asm__ volatile (
+      "li %0, 0x01010101 \n\t"
+      : "=r" (mask_1)
+    );
+#if __mips64
+    mask_1 |= mask_1 << 32;
+#endif
+    mask_128 = mask_1 << 7;
+
+    /*
+     * Check op_size byteswize after initial alignment
+     */
+    while (n >= 4 * op_size) {
+      const op_t w0 = w[0];
+      const op_t w1 = w[1];
+      const op_t w2 = w[2];
+      const op_t w3 = w[3];
+      DO_WORD(w + 0, w0)
+      DO_WORD(w + 1, w1)
+      DO_WORD(w + 2, w2)
+      DO_WORD(w + 3, w3)
+      w += 4;
+      n -= 4 * op_size;
+    }
+
+    while (n >= op_size) {
+      DO_WORD(w, w[0]);
+      w++;
+      n -= op_size;
+    }
+
+    /*
+     * Check bytewize for remaining bytes
+     */
+    p = (const char *) w;
+    for (; n > 0; --n, ++p) {
+      if (!(*p))
+        return (p - str);
+    }
+
+    return (p - str);
+  }
+
+  return 0;
+}
diff --git a/libc/bionic/bionic_systrace.cpp b/libc/bionic/bionic_systrace.cpp
index b522b10..8e8ff76 100644
--- a/libc/bionic/bionic_systrace.cpp
+++ b/libc/bionic/bionic_systrace.cpp
@@ -48,10 +48,11 @@
   // case an audit will be logged, and during boot before the property server has
   // been started, in which case we store the global property_area serial to prevent
   // the costly find operation until we see a changed property_area.
-  if (!g_pinfo && g_property_area_serial != __system_property_area_serial()) {
+  if (g_pinfo == nullptr && g_property_area_serial != __system_property_area_serial()) {
     g_property_area_serial = __system_property_area_serial();
     g_pinfo = __system_property_find(SYSTRACE_PROPERTY_NAME);
   }
+
   if (g_pinfo) {
     // Find out which tags have been enabled on the command line and set
     // the value of tags accordingly.  If the value of the property changes,
@@ -61,10 +62,11 @@
     // not to move.
     uint32_t cur_serial = __system_property_serial(g_pinfo);
     if (cur_serial != g_property_serial) {
-      g_property_serial = cur_serial;
-      char value[PROP_VALUE_MAX];
-      __system_property_read(g_pinfo, 0, value);
-      g_tags = strtoull(value, nullptr, 0);
+      __system_property_read_callback(g_pinfo,
+              [] (void*, const char*, const char* value, uint32_t serial) {
+                g_property_serial = serial;
+                g_tags = strtoull(value, nullptr, 0);
+              }, nullptr);
     }
     result = ((g_tags & ATRACE_TAG_BIONIC) != 0);
   }
@@ -81,7 +83,7 @@
   return g_trace_marker_fd;
 }
 
-ScopedTrace::ScopedTrace(const char* message) {
+void bionic_trace_begin(const char* message) {
   if (!should_trace()) {
     return;
   }
@@ -102,7 +104,7 @@
   TEMP_FAILURE_RETRY(write(trace_marker_fd, buf, len));
 }
 
-ScopedTrace::~ScopedTrace() {
+void bionic_trace_end() {
   if (!should_trace()) {
     return;
   }
@@ -114,3 +116,18 @@
 
   TEMP_FAILURE_RETRY(write(trace_marker_fd, "E", 1));
 }
+
+ScopedTrace::ScopedTrace(const char* message) : called_end_(false) {
+  bionic_trace_begin(message);
+}
+
+ScopedTrace::~ScopedTrace() {
+  End();
+}
+
+void ScopedTrace::End() {
+  if (!called_end_) {
+    bionic_trace_end();
+    called_end_ = true;
+  }
+}
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index e5654c3..970a49c 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -62,7 +62,11 @@
 char** environ;
 
 #if defined(__i386__)
-__LIBC_HIDDEN__ void* __libc_sysinfo = nullptr;
+__attribute__((__naked__)) static void __libc_int0x80() {
+  __asm__ volatile("int $0x80; ret");
+}
+
+__LIBC_HIDDEN__ void* __libc_sysinfo = reinterpret_cast<void*>(__libc_int0x80);
 
 __LIBC_HIDDEN__ void __libc_init_sysinfo(KernelArgumentBlock& args) {
   __libc_sysinfo = reinterpret_cast<void*>(args.getauxval(AT_SYSINFO));
@@ -240,6 +244,7 @@
   // of executing a setuid program or the result of an SELinux
   // security transition.
   static constexpr const char* UNSAFE_VARIABLE_NAMES[] = {
+    "ANDROID_DNS_MODE",
     "GCONV_PATH",
     "GETCONF_DIR",
     "HOSTALIASES",
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index f591c86..6b3e148 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -62,11 +62,13 @@
   if (allocation == MAP_FAILED) {
     __libc_fatal("failed to allocate TLS");
   }
+  prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, allocation, allocation_size, "bionic TLS guard page");
 
   thread->bionic_tls = reinterpret_cast<bionic_tls*>(static_cast<char*>(allocation) + PAGE_SIZE);
   if (mprotect(thread->bionic_tls, BIONIC_TLS_SIZE, PROT_READ | PROT_WRITE) != 0) {
     __libc_fatal("failed to mprotect TLS");
   }
+  prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, thread->bionic_tls, BIONIC_TLS_SIZE, "bionic TLS");
 }
 
 void __init_thread_stack_guard(pthread_internal_t* thread) {
diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp
index 3401ed7..9adf405 100644
--- a/libc/bionic/pthread_exit.cpp
+++ b/libc/bionic/pthread_exit.cpp
@@ -92,6 +92,10 @@
     thread->alternate_signal_stack = NULL;
   }
 
+  // Unmap the bionic TLS, including guard pages.
+  void* allocation = reinterpret_cast<char*>(thread->bionic_tls) - PAGE_SIZE;
+  munmap(allocation, BIONIC_TLS_SIZE + 2 * PAGE_SIZE);
+
   ThreadJoinState old_state = THREAD_NOT_JOINED;
   while (old_state == THREAD_NOT_JOINED &&
          !atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_EXITED_NOT_JOINED)) {
diff --git a/libc/bionic/pthread_internal.cpp b/libc/bionic/pthread_internal.cpp
index 2bc2bfb..5819bc1 100644
--- a/libc/bionic/pthread_internal.cpp
+++ b/libc/bionic/pthread_internal.cpp
@@ -86,10 +86,6 @@
 }
 
 static void __pthread_internal_free(pthread_internal_t* thread) {
-  // Unmap the TLS, including guard pages.
-  void* allocation = reinterpret_cast<char*>(thread->bionic_tls) - PAGE_SIZE;
-  munmap(allocation, BIONIC_TLS_SIZE + 2 * PAGE_SIZE);
-
   if (thread->mmap_size != 0) {
     // Free mapped space, including thread stack and pthread_internal_t.
     munmap(thread->attr.stack_base, thread->mmap_size);
diff --git a/libc/bionic/system_properties.cpp b/libc/bionic/system_properties.cpp
index 2bbf2d3..a4faf85 100644
--- a/libc/bionic/system_properties.cpp
+++ b/libc/bionic/system_properties.cpp
@@ -1058,15 +1058,23 @@
     return true;
   }
 
-  // TODO: Change path to /system/property_contexts after b/27805372
-  if (!initialize_properties_from_file("/plat_property_contexts")) {
-    return false;
+  // Use property_contexts from /system & /vendor, fall back to those from /
+  if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) {
+    if (!initialize_properties_from_file("/system/etc/selinux/plat_property_contexts")) {
+      return false;
+    }
+    if (!initialize_properties_from_file("/vendor/etc/selinux/nonplat_property_contexts")) {
+      return false;
+    }
+  } else {
+    if (!initialize_properties_from_file("/plat_property_contexts")) {
+      return false;
+    }
+    if (!initialize_properties_from_file("/nonplat_property_contexts")) {
+      return false;
+    }
   }
 
-  // TODO: Change path to /vendor/property_contexts after b/27805372
-  // device-specific property context is optional, so load if it exists.
-  initialize_properties_from_file("/nonplat_property_contexts");
-
   return true;
 }
 
diff --git a/libc/dns/resolv/res_cache.c b/libc/dns/resolv/res_cache.c
index 8b3c76b..82e0ddf 100644
--- a/libc/dns/resolv/res_cache.c
+++ b/libc/dns/resolv/res_cache.c
@@ -98,12 +98,6 @@
  *     is too short to accomodate the cached result.
  */
 
-/* the name of an environment variable that will be checked the first time
- * this code is called if its value is "0", then the resolver cache is
- * disabled.
- */
-#define  CONFIG_ENV  "BIONIC_DNSCACHE"
-
 /* default number of entries kept in the cache. This value has been
  * determined by browsing through various sites and counting the number
  * of corresponding requests. Keep in mind that our framework is currently
@@ -1812,13 +1806,6 @@
 static void
 _res_cache_init(void)
 {
-    const char*  env = getenv(CONFIG_ENV);
-
-    if (env && atoi(env) == 0) {
-        /* the cache is disabled */
-        return;
-    }
-
     memset(&_res_cache_list, 0, sizeof(_res_cache_list));
     pthread_mutex_init(&_res_cache_list_lock, NULL);
 }
diff --git a/libc/include/bits/posix_limits.h b/libc/include/bits/posix_limits.h
index c498c69..db09bd7 100644
--- a/libc/include/bits/posix_limits.h
+++ b/libc/include/bits/posix_limits.h
@@ -31,20 +31,22 @@
 
 #include <sys/cdefs.h>
 
+#define __BIONIC_POSIX_FEATURE_SINCE(level) (((__ANDROID_API__) >= level) ? 200809L : -1)
+
 /* Any constant values here other than -1 or 200809L are explicitly specified by POSIX.1-2008. */
 /* Keep this list sorted by name. */
-#define _POSIX_ADVISORY_INFO        200809L
+#define _POSIX_ADVISORY_INFO __BIONIC_POSIX_FEATURE_SINCE(23) /* posix_memadvise arrived late. */
 #define _POSIX_AIO_LISTIO_MAX       2
 #define _POSIX_AIO_MAX              1
 #define _POSIX_ARG_MAX              4096
 #define _POSIX_ASYNCHRONOUS_IO      -1  /* not implemented */
-#define _POSIX_BARRIERS             200809L
+#define _POSIX_BARRIERS __BIONIC_POSIX_FEATURE_SINCE(24)
 #define _POSIX_CHILD_MAX            25
 #define _POSIX_CHOWN_RESTRICTED     1  /* yes, chown requires appropriate privileges */
 #define _POSIX_CLOCK_SELECTION      200809L
 #define _POSIX_CPUTIME              0  /* Use sysconf to detect support at runtime. */
 #define _POSIX_DELAYTIMER_MAX       32
-#define _POSIX_FSYNC                200809L  /* fdatasync() supported */
+#define _POSIX_FSYNC 200809L
 #define _POSIX_HOST_NAME_MAX        255
 #define _POSIX_IPV6                 200809L
 #define _POSIX_JOB_CONTROL          1  /* job control is a Linux feature */
@@ -53,8 +55,8 @@
 #define _POSIX_MAPPED_FILES         200809L  /* mmap-ed files supported */
 #define _POSIX_MAX_CANON            255
 #define _POSIX_MAX_INPUT            255
-#define _POSIX_MEMLOCK              200809L
-#define _POSIX_MEMLOCK_RANGE        200809L
+#define _POSIX_MEMLOCK __BIONIC_POSIX_FEATURE_SINCE(17) /* mlockall. */
+#define _POSIX_MEMLOCK_RANGE        200809L /* mlock. */
 #define _POSIX_MEMORY_PROTECTION    200809L
 #define _POSIX_MESSAGE_PASSING      -1  /* not implemented */
 #define _POSIX_MONOTONIC_CLOCK      0  /* the monotonic clock may be available; ask sysconf */
@@ -81,7 +83,7 @@
 #define _POSIX_SHELL                1   /* system() supported */
 #define _POSIX_SIGQUEUE_MAX         32
 #define _POSIX_SPAWN                -1  /* not implemented */
-#define _POSIX_SPIN_LOCKS           200809L
+#define _POSIX_SPIN_LOCKS __BIONIC_POSIX_FEATURE_SINCE(24)
 #define _POSIX_SPORADIC_SERVER      -1  /* not implemented */
 #define _POSIX_SSIZE_MAX            32767
 #define _POSIX_STREAM_MAX           8
@@ -103,7 +105,7 @@
 #define _POSIX_THREAD_SAFE_FUNCTIONS 200809L
 #define _POSIX_THREAD_SPORADIC_SERVER -1  /* not implemented */
 #define _POSIX_THREAD_THREADS_MAX   64
-#define _POSIX_TIMEOUTS             200809L
+#define _POSIX_TIMEOUTS __BIONIC_POSIX_FEATURE_SINCE(21) /* pthread_mutex_timedlock arrived late. */
 #define _POSIX_TIMERS               200809L  /* Posix timers are supported */
 #define _POSIX_TIMER_MAX            32
 #define _POSIX_TRACE                -1  /* not implemented */
diff --git a/libc/include/bits/pthread_types.h b/libc/include/bits/pthread_types.h
index 7fc379b..a173e3c 100644
--- a/libc/include/bits/pthread_types.h
+++ b/libc/include/bits/pthread_types.h
@@ -44,6 +44,7 @@
 #endif
 } pthread_attr_t;
 
+#if __ANDROID_API__ >= __ANDROID_API_N__
 typedef struct {
 #if defined(__LP64__)
   int64_t __private[4];
@@ -51,8 +52,11 @@
   int32_t __private[8];
 #endif
 } pthread_barrier_t;
+#endif
 
+#if __ANDROID_API__ >= __ANDROID_API_N__
 typedef int pthread_barrierattr_t;
+#endif
 
 typedef struct {
 #if defined(__LP64__)
@@ -88,6 +92,7 @@
 
 typedef long pthread_rwlockattr_t;
 
+#if __ANDROID_API__ >= __ANDROID_API_N__
 typedef struct {
 #if defined(__LP64__)
   int64_t __private;
@@ -95,6 +100,7 @@
   int32_t __private[2];
 #endif
 } pthread_spinlock_t;
+#endif
 
 typedef long pthread_t;
 
diff --git a/libc/include/math.h b/libc/include/math.h
index 8bf6fb5..7dd1539 100644
--- a/libc/include/math.h
+++ b/libc/include/math.h
@@ -153,6 +153,20 @@
 long	lrint(double);
 long	lround(double);
 
+/*
+ * https://code.google.com/p/android/issues/detail?id=271629
+ * To be fully compliant with C++, we need to not define these (C doesn't
+ * specify them either). Exposing these means that isinf and isnan will have a
+ * return type of int in C++ rather than bool like they're supposed to be.
+ *
+ * GNU libstdc++ 4.9 isn't able to handle a standard compliant C library. Its
+ * <cmath> will `#undef isnan` from math.h and only adds the function overloads
+ * to the std namespace, making it impossible to use both <cmath> (which gets
+ * included by a lot of other standard headers) and ::isnan.
+ */
+int(isinf)(double) __attribute_const__ __INTRODUCED_IN(21);
+int	(isnan)(double) __attribute_const__;
+
 double nan(const char*) __attribute_const__ __INTRODUCED_IN_ARM(13) __INTRODUCED_IN_MIPS(13)
     __INTRODUCED_IN_X86(9);
 
diff --git a/libc/include/pthread.h b/libc/include/pthread.h
index 20fd566..ae4fdce 100644
--- a/libc/include/pthread.h
+++ b/libc/include/pthread.h
@@ -69,7 +69,9 @@
 
 #define PTHREAD_ONCE_INIT 0
 
+#if __ANDROID_API__ >= __ANDROID_API_N__
 #define PTHREAD_BARRIER_SERIAL_THREAD -1
+#endif
 
 #if defined(__LP64__)
 #define PTHREAD_STACK_MIN (4 * PAGE_SIZE)
@@ -178,23 +180,29 @@
 int pthread_rwlock_unlock(pthread_rwlock_t* _Nonnull);
 int pthread_rwlock_wrlock(pthread_rwlock_t* _Nonnull);
 
+#if __ANDROID_API__ >= __ANDROID_API_N__
 int pthread_barrierattr_init(pthread_barrierattr_t* _Nonnull attr) __INTRODUCED_IN(24);
 int pthread_barrierattr_destroy(pthread_barrierattr_t* _Nonnull attr) __INTRODUCED_IN(24);
 int pthread_barrierattr_getpshared(const pthread_barrierattr_t* _Nonnull attr,
                                    int* _Nonnull pshared) __INTRODUCED_IN(24);
 int pthread_barrierattr_setpshared(pthread_barrierattr_t* _Nonnull attr, int pshared)
   __INTRODUCED_IN(24);
+#endif
 
+#if __ANDROID_API__ >= __ANDROID_API_N__
 int pthread_barrier_init(pthread_barrier_t* _Nonnull, const pthread_barrierattr_t*, unsigned)
   __INTRODUCED_IN(24);
 int pthread_barrier_destroy(pthread_barrier_t* _Nonnull) __INTRODUCED_IN(24);
 int pthread_barrier_wait(pthread_barrier_t* _Nonnull) __INTRODUCED_IN(24);
+#endif
 
+#if __ANDROID_API__ >= __ANDROID_API_N__
 int pthread_spin_destroy(pthread_spinlock_t* _Nonnull) __INTRODUCED_IN(24);
 int pthread_spin_init(pthread_spinlock_t* _Nonnull, int) __INTRODUCED_IN(24);
 int pthread_spin_lock(pthread_spinlock_t* _Nonnull) __INTRODUCED_IN(24);
 int pthread_spin_trylock(pthread_spinlock_t* _Nonnull) __INTRODUCED_IN(24);
 int pthread_spin_unlock(pthread_spinlock_t* _Nonnull) __INTRODUCED_IN(24);
+#endif
 
 pthread_t pthread_self(void) __attribute_const__;
 
diff --git a/libc/include/stdio.h b/libc/include/stdio.h
index 7d63fa4..8b0e9df 100644
--- a/libc/include/stdio.h
+++ b/libc/include/stdio.h
@@ -176,7 +176,7 @@
 int fseek(FILE*, long, int);
 long ftell(FILE*);
 
-#if defined(__USE_FILE_OFFSET64)
+#if defined(__USE_FILE_OFFSET64) && __ANDROID_API__ >= __ANDROID_API_N__
 int fgetpos(FILE*, fpos_t*) __RENAME(fgetpos64);
 int fsetpos(FILE*, const fpos_t*) __RENAME(fsetpos64);
 int fseeko(FILE*, off_t, int) __RENAME(fseeko64);
diff --git a/libc/include/sys/mman.h b/libc/include/sys/mman.h
index 79f1faf..a3dc95c 100644
--- a/libc/include/sys/mman.h
+++ b/libc/include/sys/mman.h
@@ -49,7 +49,7 @@
 #define POSIX_MADV_WILLNEED   MADV_WILLNEED
 #define POSIX_MADV_DONTNEED   MADV_DONTNEED
 
-#if defined(__USE_FILE_OFFSET64)
+#if defined(__USE_FILE_OFFSET64) && __ANDROID_API__ >= __ANDROID_API_L__
 void* mmap(void*, size_t, int, int, int, off_t) __RENAME(mmap64) __INTRODUCED_IN(21);
 #else
 void* mmap(void*, size_t, int, int, int, off_t);
diff --git a/libc/include/sys/sendfile.h b/libc/include/sys/sendfile.h
index 3ac8fdf..dccdec5 100644
--- a/libc/include/sys/sendfile.h
+++ b/libc/include/sys/sendfile.h
@@ -34,7 +34,7 @@
 
 __BEGIN_DECLS
 
-#if defined(__USE_FILE_OFFSET64)
+#if defined(__USE_FILE_OFFSET64) && __ANDROID_API__ >= __ANDROID_API_L__
 ssize_t sendfile(int out_fd, int in_fd, off_t* offset, size_t count) __RENAME(sendfile64)
   __INTRODUCED_IN(21);
 #else
diff --git a/libc/malloc_debug/README_api.md b/libc/malloc_debug/README_api.md
index 9d07cb3..b2c46d8 100644
--- a/libc/malloc_debug/README_api.md
+++ b/libc/malloc_debug/README_api.md
@@ -26,7 +26,7 @@
 
 ### Format of info Buffer
     size_t size_of_original_allocation
-    size_t num_backtrace_frames
+    size_t num_allocations
     uintptr_t pc1
     uintptr_t pc2
     uintptr_t pc3
@@ -38,8 +38,12 @@
 *backtrace\_size* as returned by the original call to
 *get\_malloc\_leak\_info*. This value is not variable, it is the same
 for all the returned data. The value
-*num\_backtrace\_frames* contains the real number of frames found. The
-extra frames are set to zero. Each *uintptr\_t* is a pc of the callstack.
+*num\_allocations* contains the total number of allocations with the same
+backtrace and size as this allocation. On Android Nougat, this value was
+incorrectly set to the number of frames in the backtrace.
+Each *uintptr\_t* is a pc of the callstack. If the total number
+of backtrace entries is less than *backtrace\_size*, the rest of the
+entries are zero.
 The calls from within the malloc debug library are automatically removed.
 
 For 32 bit systems, *size\_t* and *uintptr\_t* are both 4 byte values.
diff --git a/libc/malloc_debug/TrackData.cpp b/libc/malloc_debug/TrackData.cpp
index 18f428b..d4064f8 100644
--- a/libc/malloc_debug/TrackData.cpp
+++ b/libc/malloc_debug/TrackData.cpp
@@ -123,11 +123,12 @@
   GetList(&list);
 
   uint8_t* data = *info;
+  size_t num_allocations = 1;
   for (const auto& header : list) {
     BacktraceHeader* back_header = debug_->GetAllocBacktrace(header);
     if (back_header->num_frames > 0) {
       memcpy(data, &header->size, sizeof(size_t));
-      memcpy(&data[sizeof(size_t)], &back_header->num_frames, sizeof(size_t));
+      memcpy(&data[sizeof(size_t)], &num_allocations, sizeof(size_t));
       memcpy(&data[2 * sizeof(size_t)], &back_header->frames[0],
             back_header->num_frames * sizeof(uintptr_t));
 
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index 1b08a39..219c21e 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -999,7 +999,7 @@
 
 struct InfoEntry {
   size_t size;
-  size_t num_frames;
+  size_t num_allocations;
   uintptr_t frames[0];
 } __attribute__((packed));
 
@@ -1033,7 +1033,7 @@
 
   InfoEntry* entry = reinterpret_cast<InfoEntry*>(expected_info.data());
   entry->size = 200;
-  entry->num_frames = 3;
+  entry->num_allocations = 1;
   entry->frames[0] = 0xf;
   entry->frames[1] = 0xe;
   entry->frames[2] = 0xd;
@@ -1082,7 +1082,7 @@
 
   // These values will be in the reverse order that we create.
   entry2->size = 500;
-  entry2->num_frames = 4;
+  entry2->num_allocations = 1;
   entry2->frames[0] = 0xf;
   entry2->frames[1] = 0xe;
   entry2->frames[2] = 0xd;
@@ -1097,7 +1097,7 @@
   memset(pointers[0], 0, entry2->size);
 
   entry1->size = 4100;
-  entry1->num_frames = 16;
+  entry1->num_allocations = 1;
   for (size_t i = 0; i < 16; i++) {
     entry1->frames[i] = 0xbc000 + i;
   }
@@ -1112,7 +1112,7 @@
   memset(pointers[1], 0, entry1->size);
 
   entry0->size = 9000;
-  entry0->num_frames = 1;
+  entry0->num_allocations = 1;
 
   entry0->frames[0] = 0x104;
   backtrace_fake_add(std::vector<uintptr_t> {0x104});
@@ -1159,7 +1159,7 @@
 
   // These values will be in the reverse order that we create.
   entry1->size = 500;
-  entry1->num_frames = 4;
+  entry1->num_allocations = 1;
   entry1->frames[0] = 0xf;
   entry1->frames[1] = 0xe;
   entry1->frames[2] = 0xd;
@@ -1174,7 +1174,7 @@
   memset(pointers[0], 0, entry1->size);
 
   entry0->size = 4100;
-  entry0->num_frames = 16;
+  entry0->num_allocations = 1;
   for (size_t i = 0; i < 16; i++) {
     entry0->frames[i] = 0xbc000 + i;
   }
@@ -1373,7 +1373,7 @@
   memset(expected_info.data(), 0, expected_info_size);
   InfoEntry* entry = reinterpret_cast<InfoEntry*>(expected_info.data());
   entry->size = memory_bytes | (1U << 31);
-  entry->num_frames = 1;
+  entry->num_allocations = 1;
   entry->frames[0] = 0x1;
 
   uint8_t* info;
diff --git a/libc/private/bionic_systrace.h b/libc/private/bionic_systrace.h
index 0b4560f..304fb80 100644
--- a/libc/private/bionic_systrace.h
+++ b/libc/private/bionic_systrace.h
@@ -28,8 +28,13 @@
   explicit ScopedTrace(const char* message);
   ~ScopedTrace();
 
+  void End();
  private:
+  bool called_end_;
   DISALLOW_COPY_AND_ASSIGN(ScopedTrace);
 };
 
+void bionic_trace_begin(const char* message);
+void bionic_trace_end();
+
 #endif
diff --git a/libc/seccomp/Android.bp b/libc/seccomp/Android.bp
index 71b710e..c341781 100644
--- a/libc/seccomp/Android.bp
+++ b/libc/seccomp/Android.bp
@@ -4,6 +4,10 @@
         "seccomp_policy.cpp",
         "arm_policy.cpp",
         "arm64_policy.cpp",
+        "x86_policy.cpp",
+        "x86_64_policy.cpp",
+        "mips_policy.cpp",
+        "mips64_policy.cpp",
     ],
     export_include_dirs: ["include"],
     shared: {
diff --git a/libc/seccomp/arm64_policy.cpp b/libc/seccomp/arm64_policy.cpp
index 0bf85a3..5eee365 100644
--- a/libc/seccomp/arm64_policy.cpp
+++ b/libc/seccomp/arm64_policy.cpp
@@ -5,7 +5,7 @@
 
 #include "seccomp_bpfs.h"
 const sock_filter arm64_filter[] = {
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5, 0, 25),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5, 0, 26),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 203, 13, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 101, 7, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 43, 3, 0),
diff --git a/libc/seccomp/arm_policy.cpp b/libc/seccomp/arm_policy.cpp
index ff5802a..9f8b9fe 100644
--- a/libc/seccomp/arm_policy.cpp
+++ b/libc/seccomp/arm_policy.cpp
@@ -5,88 +5,86 @@
 
 #include "seccomp_bpfs.h"
 const sock_filter arm_filter[] = {
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 0, 0, 125),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 150, 63, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 0, 0, 124),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 143, 61, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 74, 31, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 41, 15, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 24, 7, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 10, 3, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 8, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 7, 119, 118), //restart_syscall|exit|fork|read|write|open|close
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 9, 118, 117), //creat
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 7, 117, 116), //restart_syscall|exit|fork|read|write|open|close
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 9, 116, 115), //creat
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 19, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 13, 116, 115), //unlink|execve|chdir
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 22, 115, 114), //lseek|getpid|mount
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 13, 114, 113), //unlink|execve|chdir
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 22, 113, 112), //lseek|getpid|mount
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 33, 3, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 26, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 25, 112, 111), //getuid
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 27, 111, 110), //ptrace
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 25, 110, 109), //getuid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 27, 109, 108), //ptrace
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 36, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 34, 109, 108), //access
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 38, 108, 107), //sync|kill
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 34, 107, 106), //access
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 39, 106, 105), //sync|kill|rename
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 57, 7, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 51, 3, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 45, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 44, 104, 103), //dup|pipe|times
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 46, 103, 102), //brk
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 44, 102, 101), //dup|pipe|times
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 46, 101, 100), //brk
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 54, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 53, 101, 100), //acct|umount2
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 56, 100, 99), //ioctl|fcntl
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 53, 99, 98), //acct|umount2
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 56, 98, 97), //ioctl|fcntl
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 64, 3, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 60, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 58, 97, 96), //setpgid
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 62, 96, 95), //umask|chroot
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 58, 95, 94), //setpgid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 62, 94, 93), //umask|chroot
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 66, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 65, 94, 93), //getppid
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 68, 93, 92), //setsid|sigaction
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 65, 92, 91), //getppid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 68, 91, 90), //setsid|sigaction
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 114, 15, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 91, 7, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 85, 3, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 77, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 76, 88, 87), //sethostname|setrlimit
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 80, 87, 86), //getrusage|gettimeofday|settimeofday
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 76, 86, 85), //sethostname|setrlimit
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 80, 85, 84), //getrusage|gettimeofday|settimeofday
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 87, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 86, 85, 84), //readlink
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 89, 84, 83), //swapon|reboot
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 86, 83, 82), //readlink
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 89, 82, 81), //swapon|reboot
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 96, 3, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 94, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 93, 81, 80), //munmap|truncate
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 95, 80, 79), //fchmod
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 93, 79, 78), //munmap|truncate
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 95, 78, 77), //fchmod
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 103, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 98, 78, 77), //getpriority|setpriority
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 106, 77, 76), //syslog|setitimer|getitimer
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 98, 76, 75), //getpriority|setpriority
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 106, 75, 74), //syslog|setitimer|getitimer
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 131, 7, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 124, 3, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 118, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 117, 73, 72), //wait4|swapoff|sysinfo
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 123, 72, 71), //fsync|sigreturn|clone|setdomainname|uname
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 117, 71, 70), //wait4|swapoff|sysinfo
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 123, 70, 69), //fsync|sigreturn|clone|setdomainname|uname
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 128, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 126, 70, 69), //adjtimex|mprotect
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 130, 69, 68), //init_module|delete_module
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 126, 68, 67), //adjtimex|mprotect
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 130, 67, 66), //init_module|delete_module
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 138, 3, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 136, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 134, 66, 65), //quotactl|getpgid|fchdir
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 137, 65, 64), //personality
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 143, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 142, 63, 62), //setfsuid|setfsgid|_llseek|getdents
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 149, 62, 61), //flock|msync|readv|writev|getsid|fdatasync
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 134, 64, 63), //quotactl|getpgid|fchdir
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 137, 63, 62), //personality
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 142, 62, 61), //setfsuid|setfsgid|_llseek|getdents
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 286, 31, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 217, 15, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 190, 7, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 172, 3, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 168, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 164, 56, 55), //mlock|munlock|mlockall|munlockall|sched_setparam|sched_getparam|sched_setscheduler|sched_getscheduler|sched_yield|sched_get_priority_max|sched_get_priority_min|sched_rr_get_interval|nanosleep|mremap
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 169, 55, 54), //poll
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 183, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 182, 53, 52), //prctl|rt_sigreturn|rt_sigaction|rt_sigprocmask|rt_sigpending|rt_sigtimedwait|rt_sigqueueinfo|rt_sigsuspend|pread64|pwrite64
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 188, 52, 51), //getcwd|capget|capset|sigaltstack|sendfile
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 213, 3, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 199, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 198, 49, 48), //vfork|ugetrlimit|mmap2|truncate64|ftruncate64|stat64|lstat64|fstat64
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 212, 48, 47), //getuid32|getgid32|geteuid32|getegid32|setreuid32|setregid32|getgroups32|setgroups32|fchown32|setresuid32|getresuid32|setresgid32|getresgid32
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 217, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 215, 46, 45), //setuid32|setgid32
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 218, 45, 44), //getdents64
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 183, 7, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 168, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 150, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 149, 56, 55), //flock|msync|readv|writev|getsid|fdatasync
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 164, 55, 54), //mlock|munlock|mlockall|munlockall|sched_setparam|sched_getparam|sched_setscheduler|sched_getscheduler|sched_yield|sched_get_priority_max|sched_get_priority_min|sched_rr_get_interval|nanosleep|mremap
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 172, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 169, 53, 52), //poll
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 182, 52, 51), //prctl|rt_sigreturn|rt_sigaction|rt_sigprocmask|rt_sigpending|rt_sigtimedwait|rt_sigqueueinfo|rt_sigsuspend|pread64|pwrite64
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 199, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 190, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 188, 49, 48), //getcwd|capget|capset|sigaltstack|sendfile
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 198, 48, 47), //vfork|ugetrlimit|mmap2|truncate64|ftruncate64|stat64|lstat64|fstat64
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 213, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 212, 46, 45), //getuid32|getgid32|geteuid32|getegid32|setreuid32|setregid32|getgroups32|setgroups32|fchown32|setresuid32|getresuid32|setresgid32|getresgid32
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 215, 45, 44), //setuid32|setgid32
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 250, 7, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 224, 3, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 219, 1, 0),
@@ -97,7 +95,7 @@
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 249, 37, 36), //exit_group
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 270, 3, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 256, 1, 0),
-BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 253, 34, 33), //epoll_create|epoll_ctl|epoll_wait
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 254, 34, 33), //epoll_create|epoll_ctl|epoll_wait|remap_file_pages
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 269, 33, 32), //set_tid_address|timer_create|timer_settime|timer_gettime|timer_getoverrun|timer_delete|clock_settime|clock_gettime|clock_getres|clock_nanosleep|statfs64|fstatfs64|tgkill
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 280, 1, 0),
 BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 271, 31, 30), //arm_fadvise64_64
diff --git a/libc/seccomp/include/seccomp_policy.h b/libc/seccomp/include/seccomp_policy.h
index 33b5d0e..397f8e4 100644
--- a/libc/seccomp/include/seccomp_policy.h
+++ b/libc/seccomp/include/seccomp_policy.h
@@ -17,6 +17,10 @@
 #ifndef SECCOMP_POLICY_H
 #define SECCOMP_POLICY_H
 
+#include <stddef.h>
+#include <linux/filter.h>
+
 bool set_seccomp_filter();
+void get_seccomp_filter(const sock_filter*& filter, size_t& filter_size);
 
 #endif
diff --git a/libc/seccomp/mips64_policy.cpp b/libc/seccomp/mips64_policy.cpp
new file mode 100644
index 0000000..92f175a
--- /dev/null
+++ b/libc/seccomp/mips64_policy.cpp
@@ -0,0 +1,89 @@
+// Autogenerated file - edit at your peril!!
+
+#include <linux/filter.h>
+#include <errno.h>
+
+#include "seccomp_bpfs.h"
+const sock_filter mips64_filter[] = {
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5000, 0, 78),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5168, 39, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5077, 19, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5034, 9, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5023, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5008, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5003, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5002, 71, 70), //read|write
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5004, 70, 69), //close
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5020, 69, 68), //lseek|mmap|mprotect|munmap|brk|rt_sigaction|rt_sigprocmask|ioctl|pread64|pwrite64|readv|writev
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5031, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5028, 67, 66), //sched_yield|mremap|msync|mincore|madvise
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5032, 66, 65), //dup
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5057, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5043, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5038, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5037, 62, 61), //nanosleep|getitimer|setitimer
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5042, 61, 60), //getpid|sendfile|socket|connect
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5056, 60, 59), //sendto|recvfrom|sendmsg|recvmsg|shutdown|bind|listen|getsockname|getpeername|socketpair|setsockopt|getsockopt|clone
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5070, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5062, 58, 57), //execve|exit|wait4|kill|uname
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5076, 57, 56), //fcntl|flock|fsync|fdatasync|truncate|ftruncate
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5132, 9, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5093, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5091, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5089, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5080, 52, 51), //getcwd|chdir|fchdir
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5090, 51, 50), //fchmod
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5092, 50, 49), //fchown
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5110, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5109, 48, 47), //umask|gettimeofday|getrlimit|getrusage|sysinfo|times|ptrace|getuid|syslog|getgid|setuid|setgid|geteuid|getegid|setpgid|getppid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5130, 47, 46), //setsid|setreuid|setregid|getgroups|setgroups|setresuid|getresuid|setresgid|getresgid|getpgid|setfsuid|setfsgid|getsid|capget|capset|rt_sigpending|rt_sigtimedwait|rt_sigqueueinfo|rt_sigsuspend|sigaltstack
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5151, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5137, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5134, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5133, 43, 42), //personality
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5136, 42, 41), //statfs|fstatfs
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5150, 41, 40), //getpriority|setpriority|sched_setparam|sched_getparam|sched_setscheduler|sched_getscheduler|sched_get_priority_max|sched_get_priority_min|sched_rr_get_interval|mlock|munlock|mlockall|munlockall
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5153, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5152, 39, 38), //pivot_root
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5167, 38, 37), //prctl|adjtimex|setrlimit|chroot|sync|acct|settimeofday|mount|umount2|swapon|swapoff|reboot|sethostname|setdomainname
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5244, 19, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5208, 9, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5194, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5178, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5172, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5170, 32, 31), //init_module|delete_module
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5173, 31, 30), //quotactl
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5193, 30, 29), //gettid|readahead|setxattr|lsetxattr|fsetxattr|getxattr|lgetxattr|fgetxattr|listxattr|llistxattr|flistxattr|removexattr|lremovexattr|fremovexattr|tkill
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5205, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5197, 28, 27), //futex|sched_setaffinity|sched_getaffinity
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5206, 27, 26), //exit_group
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5237, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5215, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5211, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5209, 23, 22), //epoll_ctl
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5214, 22, 21), //rt_sigreturn|set_tid_address|restart_syscall
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5226, 21, 20), //fadvise64|timer_create|timer_settime|timer_gettime|timer_getoverrun|timer_delete|clock_settime|clock_gettime|clock_getres|clock_nanosleep|tgkill
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5242, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5238, 19, 18), //waitid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5243, 18, 17), //set_thread_area
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5297, 9, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5271, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5253, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5247, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5246, 13, 12), //inotify_add_watch|inotify_rm_watch
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5251, 12, 11), //openat|mkdirat|mknodat|fchownat
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5267, 11, 10), //unlinkat|renameat|linkat|symlinkat|readlinkat|fchmodat|faccessat|pselect6|ppoll|unshare|splice|sync_file_range|tee|vmsplice
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5279, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5276, 9, 8), //getcpu|epoll_pwait|ioprio_set|ioprio_get|utimensat
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5295, 8, 7), //fallocate|timerfd_create|timerfd_gettime|timerfd_settime|signalfd4|eventfd2|epoll_create1|dup3|pipe2|inotify_init1|preadv|pwritev|rt_tgsigqueueinfo|perf_event_open|accept4|recvmmsg
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5308, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5300, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5298, 5, 4), //prlimit64
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5306, 4, 3), //clock_adjtime|syncfs|sendmmsg|setns|process_vm_readv|process_vm_writev
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5312, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5309, 2, 1), //getdents64
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5314, 1, 0), //seccomp|getrandom
+BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+};
+
+const size_t mips64_filter_size = sizeof(mips64_filter) / sizeof(struct sock_filter);
diff --git a/libc/seccomp/mips_policy.cpp b/libc/seccomp/mips_policy.cpp
new file mode 100644
index 0000000..d775b66
--- /dev/null
+++ b/libc/seccomp/mips_policy.cpp
@@ -0,0 +1,119 @@
+// Autogenerated file - edit at your peril!!
+
+#include <linux/filter.h>
+#include <errno.h>
+
+#include "seccomp_bpfs.h"
+const sock_filter mips_filter[] = {
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4001, 0, 108),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4131, 53, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4064, 27, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4036, 13, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4023, 7, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4010, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4008, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4007, 101, 100), //exit|fork|read|write|open|close
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4009, 100, 99), //creat
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4019, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4013, 98, 97), //unlink|execve|chdir
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4022, 97, 96), //lseek|getpid|mount
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4033, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4026, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4025, 94, 93), //setuid|getuid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4027, 93, 92), //ptrace
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4034, 92, 91), //access
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4054, 7, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4045, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4041, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4039, 88, 87), //sync|kill|rename
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4044, 87, 86), //dup|pipe|times
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4049, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4048, 85, 84), //brk|setgid|getgid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4053, 84, 83), //geteuid|getegid|acct|umount2
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4060, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4057, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4056, 81, 80), //ioctl|fcntl
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4058, 80, 79), //setpgid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4062, 79, 78), //umask|chroot
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4094, 13, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4085, 7, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4070, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4066, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4065, 74, 73), //getppid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4068, 73, 72), //setsid|sigaction
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4074, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4072, 71, 70), //setreuid|setregid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4082, 70, 69), //sethostname|setrlimit|getrlimit|getrusage|gettimeofday|settimeofday|getgroups|setgroups
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4090, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4087, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4086, 67, 66), //readlink
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4089, 66, 65), //swapon|reboot
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4093, 65, 64), //mmap|munmap|truncate
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4118, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4114, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4103, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4098, 61, 60), //fchmod|fchown|getpriority|setpriority
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4106, 60, 59), //syslog|setitimer|getitimer
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4117, 59, 58), //wait4|swapoff|sysinfo
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4128, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4124, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4123, 56, 55), //fsync|sigreturn|clone|setdomainname|uname
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4126, 55, 54), //adjtimex|mprotect
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4130, 54, 53), //init_module|delete_module
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4222, 27, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4176, 13, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4151, 7, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4138, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4136, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4134, 48, 47), //quotactl|getpgid|fchdir
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4137, 47, 46), //personality
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4143, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4142, 45, 44), //setfsuid|setfsgid|_llseek|getdents
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4148, 44, 43), //flock|msync|readv|writev|cacheflush
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4169, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4154, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4153, 41, 40), //getsid|fdatasync
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4168, 40, 39), //mlock|munlock|mlockall|munlockall|sched_setparam|sched_getparam|sched_setscheduler|sched_getscheduler|sched_yield|sched_get_priority_max|sched_get_priority_min|sched_rr_get_interval|nanosleep|mremap
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4175, 39, 38), //bind|connect|getpeername|getsockname|getsockopt|listen
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4203, 7, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4188, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4179, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4178, 35, 34), //recvfrom|recvmsg
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4187, 34, 33), //sendmsg|sendto|setsockopt|shutdown|socket|socketpair|setresuid|getresuid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4190, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4189, 32, 31), //poll
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4202, 31, 30), //setresgid|getresgid|prctl|rt_sigreturn|rt_sigaction|rt_sigprocmask|rt_sigpending|rt_sigtimedwait|rt_sigqueueinfo|rt_sigsuspend|pread64|pwrite64
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4217, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4210, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4208, 28, 27), //getcwd|capget|capset|sigaltstack|sendfile
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4216, 27, 26), //mmap2|truncate64|ftruncate64|stat64|lstat64|fstat64
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4221, 26, 25), //mincore|madvise|getdents64|fcntl64
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4312, 13, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4283, 7, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4248, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4246, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4241, 21, 20), //gettid|readahead|setxattr|lsetxattr|fsetxattr|getxattr|lgetxattr|fgetxattr|listxattr|llistxattr|flistxattr|removexattr|lremovexattr|fremovexattr|tkill|sendfile64|futex|sched_setaffinity|sched_getaffinity
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4247, 20, 19), //exit_group
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4278, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4267, 18, 17), //epoll_create|epoll_ctl|epoll_wait|remap_file_pages|set_tid_address|restart_syscall|fadvise64|statfs64|fstatfs64|timer_create|timer_settime|timer_gettime|timer_getoverrun|timer_delete|clock_settime|clock_gettime|clock_getres|clock_nanosleep|tgkill
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4279, 17, 16), //waitid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4293, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4288, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4287, 14, 13), //set_thread_area|inotify_init|inotify_add_watch|inotify_rm_watch
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4292, 13, 12), //openat|mkdirat|mknodat|fchownat
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4308, 12, 11), //fstatat64|unlinkat|renameat|linkat|symlinkat|readlinkat|fchmodat|faccessat|pselect6|ppoll|unshare|splice|sync_file_range|tee|vmsplice
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4338, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4319, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4316, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4314, 8, 7), //getcpu|epoll_pwait
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4317, 7, 6), //utimensat
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4336, 6, 5), //eventfd|fallocate|timerfd_create|timerfd_gettime|timerfd_settime|signalfd4|eventfd2|epoll_create1|dup3|pipe2|inotify_init1|preadv|pwritev|rt_tgsigqueueinfo|perf_event_open|accept4|recvmmsg
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4352, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4341, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4339, 3, 2), //prlimit64
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4347, 2, 1), //clock_adjtime|syncfs|sendmmsg|setns|process_vm_readv|process_vm_writev
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4354, 1, 0), //seccomp|getrandom
+BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+};
+
+const size_t mips_filter_size = sizeof(mips_filter) / sizeof(struct sock_filter);
diff --git a/libc/seccomp/seccomp_bpfs.h b/libc/seccomp/seccomp_bpfs.h
index db67041..96e127e 100644
--- a/libc/seccomp/seccomp_bpfs.h
+++ b/libc/seccomp/seccomp_bpfs.h
@@ -24,5 +24,13 @@
 extern const size_t arm_filter_size;
 extern const struct sock_filter arm64_filter[];
 extern const size_t arm64_filter_size;
+extern const struct sock_filter x86_filter[];
+extern const size_t x86_filter_size;
+extern const struct sock_filter x86_64_filter[];
+extern const size_t x86_64_filter_size;
+extern const struct sock_filter mips_filter[];
+extern const size_t mips_filter_size;
+extern const struct sock_filter mips64_filter[];
+extern const size_t mips64_filter_size;
 
 #endif
diff --git a/libc/seccomp/seccomp_policy.cpp b/libc/seccomp/seccomp_policy.cpp
index 7f2cd91..fd2179b 100644
--- a/libc/seccomp/seccomp_policy.cpp
+++ b/libc/seccomp/seccomp_policy.cpp
@@ -16,8 +16,8 @@
 
 #include "seccomp_policy.h"
 
+#include <assert.h>
 #include <linux/audit.h>
-#include <linux/filter.h>
 #include <linux/seccomp.h>
 #include <sys/prctl.h>
 
@@ -25,69 +25,84 @@
 
 #include <android-base/logging.h>
 
+#include "seccomp_bpfs.h"
+
+
 #if defined __arm__ || defined __aarch64__
 
-extern const struct sock_filter arm_filter[];
-extern const size_t arm_filter_size;
-extern const struct sock_filter arm64_filter[];
-extern const size_t arm64_filter_size;
+#define DUAL_ARCH
+#define PRIMARY_ARCH AUDIT_ARCH_AARCH64
+static const struct sock_filter* primary_filter = arm64_filter;
+static const size_t primary_filter_size = arm64_filter_size;
+#define SECONDARY_ARCH AUDIT_ARCH_ARM
+static const struct sock_filter* secondary_filter = arm_filter;
+static const size_t secondary_filter_size = arm_filter_size;
+
+#elif defined __i386__ || defined __x86_64__
+
+#define DUAL_ARCH
+#define PRIMARY_ARCH AUDIT_ARCH_X86_64
+static const struct sock_filter* primary_filter = x86_64_filter;
+static const size_t primary_filter_size = x86_64_filter_size;
+#define SECONDARY_ARCH AUDIT_ARCH_I386
+static const struct sock_filter* secondary_filter = x86_filter;
+static const size_t secondary_filter_size = x86_filter_size;
+
+#elif defined __mips__ || defined __mips64__
+
+#define DUAL_ARCH
+#define PRIMARY_ARCH AUDIT_ARCH_MIPS64
+static const struct sock_filter* primary_filter = mips64_filter;
+static const size_t primary_filter_size = mips64_filter_size;
+#define SECONDARY_ARCH AUDIT_ARCH_MIPS
+static const struct sock_filter* secondary_filter = mips_filter;
+static const size_t secondary_filter_size = mips_filter_size;
+
+#else
+#error No architecture was defined!
+#endif
+
 
 #define syscall_nr (offsetof(struct seccomp_data, nr))
 #define arch_nr (offsetof(struct seccomp_data, arch))
 
 typedef std::vector<sock_filter> filter;
 
-// We want to keep the below inline functions for debugging and future
-// development even though they are not all sed currently.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-function"
-
-static inline void Kill(filter& f) {
-    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL));
-}
-
-static inline void Trap(filter& f) {
+inline void Disallow(filter& f) {
     f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRAP));
 }
 
-static inline void Error(filter& f, __u16 retcode) {
-    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO + retcode));
-}
-
-inline static void Trace(filter& f) {
-    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE));
-}
-
-inline static void Allow(filter& f) {
-    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW));
-}
-
-#pragma clang diagnostic pop
-
-inline static void ExamineSyscall(filter& f) {
+static void ExamineSyscall(filter& f) {
     f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_nr));
 }
 
-inline static int SetValidateArchitectureJumpTarget(size_t offset, filter& f) {
+#ifdef DUAL_ARCH
+static bool SetValidateArchitectureJumpTarget(size_t offset, filter& f) {
     size_t jump_length = f.size() - offset - 1;
     auto u8_jump_length = (__u8) jump_length;
     if (u8_jump_length != jump_length) {
         LOG(FATAL)
             << "Can't set jump greater than 255 - actual jump is " <<  jump_length;
-        return -1;
+        return false;
     }
-    f[offset] = BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_ARM, u8_jump_length, 0);
-    return 0;
+    f[offset] = BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SECONDARY_ARCH, u8_jump_length, 0);
+    return true;
 }
 
-inline static size_t ValidateArchitectureAndJumpIfNeeded(filter& f) {
+static size_t ValidateArchitectureAndJumpIfNeeded(filter& f) {
     f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, arch_nr));
-
-    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_AARCH64, 2, 0));
-    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_ARM, 1, 0));
-    Trap(f);
+    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, PRIMARY_ARCH, 2, 0));
+    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SECONDARY_ARCH, 1, 0));
+    Disallow(f);
     return f.size() - 2;
 }
+#else
+static void ValidateArchitecture(filter& f) {
+    f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, arch_nr));
+    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, PRIMARY_ARCH, 1, 0));
+    Disallow(f);
+}
+#endif
 
 static bool install_filter(filter const& f) {
     struct sock_fprog prog = {
@@ -107,41 +122,44 @@
 bool set_seccomp_filter() {
     filter f;
 
+#ifdef DUAL_ARCH
     // Note that for mixed 64/32 bit architectures, ValidateArchitecture inserts a
     // jump that must be changed to point to the start of the 32-bit policy
     // 32 bit syscalls will not hit the policy between here and the call to SetJump
-    auto offset_to_32bit_filter = ValidateArchitectureAndJumpIfNeeded(f);
+    auto offset_to_secondary_filter = ValidateArchitectureAndJumpIfNeeded(f);
+#else
+    ValidateArchitecture(f);
+#endif
 
-    // 64-bit filter
     ExamineSyscall(f);
 
-    // arm64-only filter - autogenerated from bionic syscall usage
-    for (size_t i = 0; i < arm64_filter_size; ++i) {
-        f.push_back(arm64_filter[i]);
+    for (size_t i = 0; i < primary_filter_size; ++i) {
+        f.push_back(primary_filter[i]);
     }
-    Trap(f);
+    Disallow(f);
 
-    if (SetValidateArchitectureJumpTarget(offset_to_32bit_filter, f) != 0) {
-        return -1;
+#ifdef DUAL_ARCH
+    if (!SetValidateArchitectureJumpTarget(offset_to_secondary_filter, f)) {
+        return false;
     }
 
-    // 32-bit filter
     ExamineSyscall(f);
 
-    // arm32 filter - autogenerated from bionic syscall usage
-    for (size_t i = 0; i < arm_filter_size; ++i) {
-        f.push_back(arm_filter[i]);
+    for (size_t i = 0; i < secondary_filter_size; ++i) {
+        f.push_back(secondary_filter[i]);
     }
-    Trap(f);
+    Disallow(f);
+#endif
 
     return install_filter(f);
 }
 
-#else // if defined __arm__ || defined __aarch64__
-
-bool set_seccomp_filter() {
-    LOG(INFO) << "Not setting seccomp filter - wrong architecture";
-    return true;
-}
-
+void get_seccomp_filter(const sock_filter*& filter, size_t& filter_size) {
+#if defined __aarch64__ || defined __x86_64__ || defined __mips64__
+    filter = primary_filter;
+    filter_size = primary_filter_size;
+#else
+    filter = secondary_filter;
+    filter_size = secondary_filter_size;
 #endif
+}
diff --git a/libc/seccomp/x86_64_policy.cpp b/libc/seccomp/x86_64_policy.cpp
new file mode 100644
index 0000000..69756c6
--- /dev/null
+++ b/libc/seccomp/x86_64_policy.cpp
@@ -0,0 +1,91 @@
+// Autogenerated file - edit at your peril!!
+
+#include <linux/filter.h>
+#include <errno.h>
+
+#include "seccomp_bpfs.h"
+const sock_filter x86_64_filter[] = {
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 0, 0, 80),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 157, 39, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 72, 19, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 32, 9, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 8, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 5, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 2, 73, 72), //read|write
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4, 72, 71), //close
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 6, 71, 70), //fstat
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 24, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 21, 69, 68), //lseek|mmap|mprotect|munmap|brk|rt_sigaction|rt_sigprocmask|rt_sigreturn|ioctl|pread64|pwrite64|readv|writev
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 29, 68, 67), //sched_yield|mremap|msync|mincore|madvise
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 44, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 38, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 35, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 33, 64, 63), //dup
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 37, 63, 62), //nanosleep|getitimer
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 43, 62, 61), //setitimer|getpid|sendfile|socket|connect
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 58, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 57, 60, 59), //sendto|recvfrom|sendmsg|recvmsg|shutdown|bind|listen|getsockname|getpeername|socketpair|setsockopt|getsockopt|clone
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 64, 59, 58), //vfork|execve|exit|wait4|kill|uname
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 112, 9, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 93, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 91, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 79, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 78, 54, 53), //fcntl|flock|fsync|fdatasync|truncate|ftruncate
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 82, 53, 52), //getcwd|chdir|fchdir
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 92, 52, 51), //fchmod
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 95, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 94, 50, 49), //fchown
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 111, 49, 48), //umask|gettimeofday|getrlimit|getrusage|sysinfo|times|ptrace|getuid|syslog|getgid|setuid|setgid|geteuid|getegid|setpgid|getppid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 140, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 137, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 135, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 132, 45, 44), //setsid|setreuid|setregid|getgroups|setgroups|setresuid|getresuid|setresgid|getresgid|getpgid|setfsuid|setfsgid|getsid|capget|capset|rt_sigpending|rt_sigtimedwait|rt_sigqueueinfo|rt_sigsuspend|sigaltstack
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 136, 44, 43), //personality
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 139, 43, 42), //statfs|fstatfs
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 155, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 153, 41, 40), //getpriority|setpriority|sched_setparam|sched_getparam|sched_setscheduler|sched_getscheduler|sched_get_priority_max|sched_get_priority_min|sched_rr_get_interval|mlock|munlock|mlockall|munlockall
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 156, 40, 39), //pivot_root
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 254, 19, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 217, 9, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 186, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 179, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 175, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 172, 34, 33), //prctl|arch_prctl|adjtimex|setrlimit|chroot|sync|acct|settimeofday|mount|umount2|swapon|swapoff|reboot|sethostname|setdomainname
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 177, 33, 32), //init_module|delete_module
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 180, 32, 31), //quotactl
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 202, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 201, 30, 29), //gettid|readahead|setxattr|lsetxattr|fsetxattr|getxattr|lgetxattr|fgetxattr|listxattr|llistxattr|flistxattr|removexattr|lremovexattr|fremovexattr|tkill
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 205, 29, 28), //futex|sched_setaffinity|sched_getaffinity
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 247, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 233, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 221, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 220, 25, 24), //getdents64|set_tid_address|restart_syscall
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 232, 24, 23), //fadvise64|timer_create|timer_settime|timer_gettime|timer_getoverrun|timer_delete|clock_settime|clock_gettime|clock_getres|clock_nanosleep|exit_group
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 235, 23, 22), //epoll_ctl|tgkill
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 251, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 248, 21, 20), //waitid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 253, 20, 19), //ioprio_set|ioprio_get
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 283, 9, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 275, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 262, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 257, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 256, 15, 14), //inotify_add_watch|inotify_rm_watch
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 261, 14, 13), //openat|mkdirat|mknodat|fchownat
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 273, 13, 12), //newfstatat|unlinkat|renameat|linkat|symlinkat|readlinkat|fchmodat|faccessat|pselect6|ppoll|unshare
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 280, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 279, 11, 10), //splice|tee|sync_file_range|vmsplice
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 282, 10, 9), //utimensat|epoll_pwait
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 305, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 302, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 285, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 284, 6, 5), //timerfd_create
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 300, 5, 4), //fallocate|timerfd_settime|timerfd_gettime|accept4|signalfd4|eventfd2|epoll_create1|dup3|pipe2|inotify_init1|preadv|pwritev|rt_tgsigqueueinfo|perf_event_open|recvmmsg
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 303, 4, 3), //prlimit64
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 317, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 312, 2, 1), //clock_adjtime|syncfs|sendmmsg|setns|getcpu|process_vm_readv|process_vm_writev
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 319, 1, 0), //seccomp|getrandom
+BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+};
+
+const size_t x86_64_filter_size = sizeof(x86_64_filter) / sizeof(struct sock_filter);
diff --git a/libc/seccomp/x86_policy.cpp b/libc/seccomp/x86_policy.cpp
new file mode 100644
index 0000000..e29f8bf
--- /dev/null
+++ b/libc/seccomp/x86_policy.cpp
@@ -0,0 +1,121 @@
+// Autogenerated file - edit at your peril!!
+
+#include <linux/filter.h>
+#include <errno.h>
+
+#include "seccomp_bpfs.h"
+const sock_filter x86_filter[] = {
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 0, 0, 110),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 131, 55, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 64, 27, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 36, 13, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 24, 7, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 10, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 8, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 7, 103, 102), //restart_syscall|exit|fork|read|write|open|close
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 9, 102, 101), //creat
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 19, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 13, 100, 99), //unlink|execve|chdir
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 22, 99, 98), //lseek|getpid|mount
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 33, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 26, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 25, 96, 95), //getuid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 27, 95, 94), //ptrace
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 34, 94, 93), //access
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 54, 7, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 45, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 41, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 39, 90, 89), //sync|kill|rename
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 44, 89, 88), //dup|pipe|times
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 51, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 46, 87, 86), //brk
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 53, 86, 85), //acct|umount2
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 60, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 57, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 56, 83, 82), //ioctl|fcntl
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 58, 82, 81), //setpgid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 62, 81, 80), //umask|chroot
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 94, 13, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 85, 7, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 74, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 66, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 65, 76, 75), //getppid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 68, 75, 74), //setsid|sigaction
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 77, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 76, 73, 72), //sethostname|setrlimit
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 80, 72, 71), //getrusage|gettimeofday|settimeofday
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 90, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 87, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 86, 69, 68), //readlink
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 89, 68, 67), //swapon|reboot
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 93, 67, 66), //mmap|munmap|truncate
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 118, 7, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 102, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 96, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 95, 63, 62), //fchmod
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 98, 62, 61), //getpriority|setpriority
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 114, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 106, 60, 59), //socketcall|syslog|setitimer|getitimer
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 117, 59, 58), //wait4|swapoff|sysinfo
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 128, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 124, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 123, 56, 55), //fsync|sigreturn|clone|setdomainname|uname
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 126, 55, 54), //adjtimex|mprotect
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 130, 54, 53), //init_module|delete_module
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 254, 27, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 183, 13, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 150, 7, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 138, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 136, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 134, 48, 47), //quotactl|getpgid|fchdir
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 137, 47, 46), //personality
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 143, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 142, 45, 44), //setfsuid|setfsgid|_llseek|getdents
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 149, 44, 43), //flock|msync|readv|writev|getsid|fdatasync
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 172, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 168, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 164, 41, 40), //mlock|munlock|mlockall|munlockall|sched_setparam|sched_getparam|sched_setscheduler|sched_getscheduler|sched_yield|sched_get_priority_max|sched_get_priority_min|sched_rr_get_interval|nanosleep|mremap
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 169, 40, 39), //poll
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 182, 39, 38), //prctl|rt_sigreturn|rt_sigaction|rt_sigprocmask|rt_sigpending|rt_sigtimedwait|rt_sigqueueinfo|rt_sigsuspend|pread64|pwrite64
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 218, 7, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 199, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 190, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 188, 35, 34), //getcwd|capget|capset|sigaltstack|sendfile
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 198, 34, 33), //vfork|ugetrlimit|mmap2|truncate64|ftruncate64|stat64|lstat64|fstat64
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 213, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 212, 32, 31), //getuid32|getgid32|geteuid32|getegid32|setreuid32|setregid32|getgroups32|setgroups32|fchown32|setresuid32|getresuid32|setresgid32|getresgid32
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 215, 31, 30), //setuid32|setgid32
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 252, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 224, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 222, 28, 27), //mincore|madvise|getdents64|fcntl64
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 244, 27, 26), //gettid|readahead|setxattr|lsetxattr|fsetxattr|getxattr|lgetxattr|fgetxattr|listxattr|llistxattr|flistxattr|removexattr|lremovexattr|fremovexattr|tkill|sendfile64|futex|sched_setaffinity|sched_getaffinity|set_thread_area
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 253, 26, 25), //exit_group
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 318, 13, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 295, 7, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 284, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 272, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 271, 21, 20), //epoll_create|epoll_ctl|epoll_wait|remap_file_pages|set_tid_address|timer_create|timer_settime|timer_gettime|timer_getoverrun|timer_delete|clock_settime|clock_gettime|clock_getres|clock_nanosleep|statfs64|fstatfs64|tgkill
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 273, 20, 19), //fadvise64_64
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 291, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 285, 18, 17), //waitid
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 294, 17, 16), //inotify_init|inotify_add_watch|inotify_rm_watch
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 313, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 300, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 299, 14, 13), //openat|mkdirat|mknodat|fchownat
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 311, 13, 12), //fstatat64|unlinkat|renameat|linkat|symlinkat|readlinkat|fchmodat|faccessat|pselect6|ppoll|unshare
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 317, 12, 11), //splice|sync_file_range|tee|vmsplice
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 343, 5, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 340, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 322, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 321, 8, 7), //getcpu|epoll_pwait|utimensat
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 337, 7, 6), //timerfd_create|eventfd|fallocate|timerfd_settime|timerfd_gettime|signalfd4|eventfd2|epoll_create1|dup3|pipe2|inotify_init1|preadv|pwritev|rt_tgsigqueueinfo|perf_event_open
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 341, 6, 5), //prlimit64
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 354, 3, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 346, 1, 0),
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 345, 3, 2), //clock_adjtime|syncfs
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 349, 2, 1), //setns|process_vm_readv|process_vm_writev
+BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 356, 1, 0), //seccomp|getrandom
+BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+};
+
+const size_t x86_filter_size = sizeof(x86_filter) / sizeof(struct sock_filter);
diff --git a/libc/tools/genseccomp.py b/libc/tools/genseccomp.py
index 3796683..a8e551e 100755
--- a/libc/tools/genseccomp.py
+++ b/libc/tools/genseccomp.py
@@ -1,8 +1,9 @@
 #!/usr/bin/env python
+import collections
 import os
-from subprocess import Popen, PIPE
 import textwrap
 from gensyscalls import SysCallsTxtParser
+from subprocess import Popen, PIPE
 
 
 BPF_JGE = "BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, {0}, {1}, {2})"
@@ -36,15 +37,31 @@
   syscalls = [x for x in syscalls if architecture in x and x[architecture]]
 
   # We only want the name
-  return [x["name"] for x in syscalls]
+  names = [x["name"] for x in syscalls]
+
+  # Check for duplicates
+  dups = [name for name, count in collections.Counter(names).items() if count > 1]
+
+  # x86 has duplicate socketcall entries, so hard code for this
+  if architecture == "x86":
+    dups.remove("socketcall")
+
+  if len(dups) > 0:
+    print "Duplicate entries found - aborting ", dups
+    exit(-1)
+
+  # Remove remaining duplicates
+  return list(set(names))
 
 
-def convert_names_to_NRs(names, header_dir):
+def convert_names_to_NRs(names, header_dir, extra_switches):
   # Run preprocessor over the __NR_syscall symbols, including unistd.h,
   # to get the actual numbers
   prefix = "__SECCOMP_"  # prefix to ensure no name collisions
   cpp = Popen(["../../prebuilts/clang/host/linux-x86/clang-stable/bin/clang",
-               "-E", "-nostdinc", "-I" + header_dir, "-Ikernel/uapi/", "-"],
+               "-E", "-nostdinc", "-I" + header_dir, "-Ikernel/uapi/"]
+               + extra_switches
+               + ["-"],
               stdin=PIPE, stdout=PIPE)
   cpp.stdin.write("#include <asm/unistd.h>\n")
   for name in names:
@@ -132,13 +149,14 @@
       bpf[i] = statement.format(fail=str(len(bpf) - i),
                                 allow=str(len(bpf) - i - 1))
 
-  # Add check that we aren't off the bottom of the syscalls
-  bpf.insert(0, BPF_JGE.format(ranges[0].begin, 0, str(len(bpf))) + ',')
 
   # Add the allow calls at the end. If the syscall is not matched, we will
   # continue. This allows the user to choose to match further syscalls, and
   # also to choose the action when we want to block
   bpf.append(BPF_ALLOW + ",")
+
+  # Add check that we aren't off the bottom of the syscalls
+  bpf.insert(0, BPF_JGE.format(ranges[0].begin, 0, str(len(bpf))) + ',')
   return bpf
 
 
@@ -162,25 +180,23 @@
   return header + "\n".join(bpf) + footer
 
 
-def construct_bpf(syscall_files, architecture, header_dir):
+def construct_bpf(syscall_files, architecture, header_dir, extra_switches):
   names = get_names(syscall_files, architecture)
-  syscalls = convert_names_to_NRs(names, header_dir)
+  syscalls = convert_names_to_NRs(names, header_dir, extra_switches)
   ranges = convert_NRs_to_ranges(syscalls)
   bpf = convert_ranges_to_bpf(ranges)
   return convert_bpf_to_output(bpf, architecture)
 
 
-android_syscall_files = ["SYSCALLS.TXT", "SECCOMP_WHITELIST.TXT"]
-arm_headers = "kernel/uapi/asm-arm"
-arm64_headers = "kernel/uapi/asm-arm64"
-arm_architecture = "arm"
-arm64_architecture = "arm64"
-
-
 ANDROID_SYSCALL_FILES = ["SYSCALLS.TXT", "SECCOMP_WHITELIST.TXT"]
 
-POLICY_CONFIGS = [("arm", "kernel/uapi/asm-arm"),
-                  ("arm64", "kernel/uapi/asm-arm64")]
+
+POLICY_CONFIGS = [("arm", "kernel/uapi/asm-arm", []),
+                  ("arm64", "kernel/uapi/asm-arm64", []),
+                  ("x86", "kernel/uapi/asm-x86", ["-D__i386__"]),
+                  ("x86_64", "kernel/uapi/asm-x86", []),
+                  ("mips", "kernel/uapi/asm-mips", ["-D_MIPS_SIM=_MIPS_SIM_ABI32"]),
+                  ("mips64", "kernel/uapi/asm-mips", ["-D_MIPS_SIM=_MIPS_SIM_ABI64"])]
 
 
 def set_dir():
@@ -190,9 +206,9 @@
 
 def main():
   set_dir()
-  for arch, header_path in POLICY_CONFIGS:
+  for arch, header_path, switches in POLICY_CONFIGS:
     files = [open(filename) for filename in ANDROID_SYSCALL_FILES]
-    output = construct_bpf(files, arch, header_path)
+    output = construct_bpf(files, arch, header_path, switches)
 
     # And output policy
     existing = ""
@@ -206,6 +222,5 @@
         output_file.write(output)
       print "Generated file " + output_path
 
-
 if __name__ == "__main__":
   main()
diff --git a/libc/tools/test_genseccomp.py b/libc/tools/test_genseccomp.py
index 5b2f14f..73f768d 100755
--- a/libc/tools/test_genseccomp.py
+++ b/libc/tools/test_genseccomp.py
@@ -20,6 +20,9 @@
   def get_headers(self, arch):
     return self.get_config(arch)[1]
 
+  def get_switches(self, arch):
+    return self.get_config(arch)[2]
+
   def test_get_names(self):
     syscalls = cStringIO.StringIO(textwrap.dedent("""\
 int __llseek:_llseek(int, unsigned long, unsigned long, off64_t*, int) arm,mips,x86
@@ -45,17 +48,40 @@
 
   def test_convert_names_to_NRs(self):
     self.assertEquals(genseccomp.convert_names_to_NRs(["open"],
-                                                      self.get_headers("arm")),
+                                                      self.get_headers("arm"),
+                                                      self.get_switches("arm")),
                       [("open", 5)])
 
     self.assertEquals(genseccomp.convert_names_to_NRs(["__ARM_NR_set_tls"],
-                                                      self.get_headers("arm")),
+                                                      self.get_headers("arm"),
+                                                      self.get_switches("arm")),
                       [('__ARM_NR_set_tls', 983045)])
 
     self.assertEquals(genseccomp.convert_names_to_NRs(["openat"],
-                                                      self.get_headers("arm64")),
+                                                      self.get_headers("arm64"),
+                                                      self.get_switches("arm64")),
                       [("openat", 56)])
 
+    self.assertEquals(genseccomp.convert_names_to_NRs(["openat"],
+                                                      self.get_headers("x86"),
+                                                      self.get_switches("x86")),
+                      [("openat", 295)])
+
+    self.assertEquals(genseccomp.convert_names_to_NRs(["openat"],
+                                                      self.get_headers("x86_64"),
+                                                      self.get_switches("x86_64")),
+                      [("openat", 257)])
+
+    self.assertEquals(genseccomp.convert_names_to_NRs(["openat"],
+                                                      self.get_headers("mips"),
+                                                      self.get_switches("mips")),
+                      [("openat", 4288)])
+
+    self.assertEquals(genseccomp.convert_names_to_NRs(["openat"],
+                                                      self.get_headers("mips64"),
+                                                      self.get_switches("mips64")),
+                      [("openat", 5247)])
+
 
   def test_convert_NRs_to_ranges(self):
     ranges = genseccomp.convert_NRs_to_ranges([("b", 2), ("a", 1)])
@@ -87,13 +113,13 @@
   def test_convert_ranges_to_bpf(self):
     ranges = genseccomp.convert_NRs_to_ranges([("b", 2), ("a", 1)])
     bpf = genseccomp.convert_ranges_to_bpf(ranges)
-    self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 1, 0, 1),',
+    self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 1, 0, 2),',
                             'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 1, 0), //a|b',
                             'BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),'])
 
     ranges = genseccomp.convert_NRs_to_ranges([("b", 3), ("a", 1)])
     bpf = genseccomp.convert_ranges_to_bpf(ranges)
-    self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 1, 0, 3),',
+    self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 1, 0, 4),',
                             'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 1, 0),',
                             'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 2, 2, 1), //a',
                             'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4, 1, 0), //b',
@@ -128,7 +154,8 @@
     """))
 
     syscall_files = [syscalls, whitelist]
-    output = genseccomp.construct_bpf(syscall_files, "arm", self.get_headers("arm"))
+    output = genseccomp.construct_bpf(syscall_files, "arm", self.get_headers("arm"),
+                                      self.get_switches("arm"))
 
     expected_output = textwrap.dedent("""\
     // Autogenerated file - edit at your peril!!
@@ -138,7 +165,7 @@
 
     #include "seccomp_bpfs.h"
     const sock_filter arm_filter[] = {
-    BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 0, 3),
+    BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 0, 4),
     BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 140, 1, 0),
     BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4, 2, 1), //read
     BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 141, 1, 0), //_llseek
diff --git a/libc/tzcode/localtime.c b/libc/tzcode/localtime.c
index 723e4f2..8d5cd07 100644
--- a/libc/tzcode/localtime.c
+++ b/libc/tzcode/localtime.c
@@ -2353,9 +2353,9 @@
 #include <stdint.h>
 #include <arpa/inet.h> // For ntohl(3).
 
-static int __bionic_open_tzdata_path(const char* path_prefix_variable, const char* path_suffix,
-                                     const char* olson_id,
-                                     int32_t* entry_length) {
+#if !defined(__ANDROID__)
+static char* make_path(const char* path_prefix_variable,
+                       const char* path_suffix) {
   const char* path_prefix = getenv(path_prefix_variable);
   if (path_prefix == NULL) {
     fprintf(stderr, "%s: %s not set!\n", __FUNCTION__, path_prefix_variable);
@@ -2368,9 +2368,15 @@
     return -1;
   }
   snprintf(path, path_length, "%s/%s", path_prefix, path_suffix);
+  return path;
+}
+#endif
+
+static int __bionic_open_tzdata_path(const char* path,
+                                     const char* olson_id,
+                                     int32_t* entry_length) {
   int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC));
   if (fd == -1) {
-    free(path);
     return -2; // Distinguish failure to find any data from failure to find a specific id.
   }
 
@@ -2389,7 +2395,6 @@
   if (bytes_read != sizeof(header)) {
     fprintf(stderr, "%s: could not read header of \"%s\": %s\n",
             __FUNCTION__, path, (bytes_read == -1) ? strerror(errno) : "short read");
-    free(path);
     close(fd);
     return -1;
   }
@@ -2397,7 +2402,6 @@
   if (strncmp(header.tzdata_version, "tzdata", 6) != 0 || header.tzdata_version[11] != 0) {
     fprintf(stderr, "%s: bad magic in \"%s\": \"%.6s\"\n",
             __FUNCTION__, path, header.tzdata_version);
-    free(path);
     close(fd);
     return -1;
   }
@@ -2412,7 +2416,6 @@
   if (TEMP_FAILURE_RETRY(lseek(fd, ntohl(header.index_offset), SEEK_SET)) == -1) {
     fprintf(stderr, "%s: couldn't seek to index in \"%s\": %s\n",
             __FUNCTION__, path, strerror(errno));
-    free(path);
     close(fd);
     return -1;
   }
@@ -2423,14 +2426,12 @@
   if (index == NULL) {
     fprintf(stderr, "%s: couldn't allocate %zd-byte index for \"%s\"\n",
             __FUNCTION__, index_size, path);
-    free(path);
     close(fd);
     return -1;
   }
   if (TEMP_FAILURE_RETRY(read(fd, index, index_size)) != index_size) {
     fprintf(stderr, "%s: could not read index of \"%s\": %s\n",
             __FUNCTION__, path, (bytes_read == -1) ? strerror(errno) : "short read");
-    free(path);
     free(index);
     close(fd);
     return -1;
@@ -2462,7 +2463,6 @@
   free(index);
 
   if (specific_zone_offset == -1) {
-    free(path);
     close(fd);
     return -1;
   }
@@ -2470,29 +2470,50 @@
   if (TEMP_FAILURE_RETRY(lseek(fd, specific_zone_offset, SEEK_SET)) == -1) {
     fprintf(stderr, "%s: could not seek to %ld in \"%s\": %s\n",
             __FUNCTION__, specific_zone_offset, path, strerror(errno));
-    free(path);
     close(fd);
     return -1;
   }
 
   // TODO: check that there's TZ_MAGIC at this offset, so we can fall back to the other file if not.
 
-  free(path);
   return fd;
 }
 
 static int __bionic_open_tzdata(const char* olson_id, int32_t* entry_length) {
-  int fd = __bionic_open_tzdata_path("ANDROID_DATA", "/misc/zoneinfo/current/tzdata",
-                                     olson_id, entry_length);
-  if (fd < 0) {
-    fd = __bionic_open_tzdata_path("ANDROID_ROOT", "/usr/share/zoneinfo/tzdata",
-                                   olson_id, entry_length);
-    if (fd == -2) {
-      // The first thing that 'recovery' does is try to format the current time. It doesn't have
-      // any tzdata available, so we must not abort here --- doing so breaks the recovery image!
-      fprintf(stderr, "%s: couldn't find any tzdata when looking for %s!\n", __FUNCTION__, olson_id);
-    }
+  int fd;
+
+#if defined(__ANDROID__)
+  // On Android, try the two hard-coded locations.
+  fd = __bionic_open_tzdata_path("/data/misc/zoneinfo/current/tzdata",
+                                 olson_id, entry_length);
+  if (fd >= 0) return fd;
+
+  fd = __bionic_open_tzdata_path("/system/usr/share/zoneinfo/tzdata",
+                                 olson_id, entry_length);
+  if (fd >= 0) return fd;
+#else
+  // On the host, we don't expect those locations to exist, and we're not
+  // worried about security so we trust $ANDROID_DATA and $ANDROID_ROOT to
+  // point us in the right direction.
+  char* path = make_path("ANDROID_DATA", "/misc/zoneinfo/current/tzdata");
+  fd = __bionic_open_tzdata_path(path, olson_id, entry_length);
+  free(path);
+  if (fd >= 0) return fd;
+
+  path = make_path("ANDROID_ROOT", "/usr/share/zoneinfo/tzdata");
+  fd = __bionic_open_tzdata_path(path, olson_id, entry_length);
+  free(path);
+  if (fd >= 0) return fd;
+#endif
+
+  // Not finding any tzdata is more serious that not finding a specific zone,
+  // and worth logging.
+  if (fd == -2) {
+    // The first thing that 'recovery' does is try to format the current time. It doesn't have
+    // any tzdata available, so we must not abort here --- doing so breaks the recovery image!
+    fprintf(stderr, "%s: couldn't find any tzdata when looking for %s!\n", __FUNCTION__, olson_id);
   }
+
   return fd;
 }
 
diff --git a/libc/versioner-dependencies/arm/arch-arm b/libc/versioner-dependencies/arm/arch-arm
new file mode 120000
index 0000000..cc94225
--- /dev/null
+++ b/libc/versioner-dependencies/arm/arch-arm
@@ -0,0 +1 @@
+../../arch-arm/include/
\ No newline at end of file
diff --git a/libc/versioner-dependencies/arm/kernel_uapi_asm-arm b/libc/versioner-dependencies/arm/kernel_uapi_asm-arm
new file mode 120000
index 0000000..3c7584d
--- /dev/null
+++ b/libc/versioner-dependencies/arm/kernel_uapi_asm-arm
@@ -0,0 +1 @@
+../../kernel/uapi/asm-arm
\ No newline at end of file
diff --git a/libc/versioner-dependencies/arm64/arch-arm64 b/libc/versioner-dependencies/arm64/arch-arm64
new file mode 120000
index 0000000..2d9128a
--- /dev/null
+++ b/libc/versioner-dependencies/arm64/arch-arm64
@@ -0,0 +1 @@
+../../arch-arm64/include/
\ No newline at end of file
diff --git a/libc/versioner-dependencies/arm64/kernel_uapi_asm-arm64 b/libc/versioner-dependencies/arm64/kernel_uapi_asm-arm64
new file mode 120000
index 0000000..7ee6fd2
--- /dev/null
+++ b/libc/versioner-dependencies/arm64/kernel_uapi_asm-arm64
@@ -0,0 +1 @@
+../../kernel/uapi/asm-arm64
\ No newline at end of file
diff --git a/libc/versioner-dependencies/common/clang-builtins b/libc/versioner-dependencies/common/clang-builtins
new file mode 120000
index 0000000..7bd481c
--- /dev/null
+++ b/libc/versioner-dependencies/common/clang-builtins
@@ -0,0 +1 @@
+../../../../external/clang/lib/Headers/
\ No newline at end of file
diff --git a/libc/versioner-dependencies/common/kernel_android_uapi b/libc/versioner-dependencies/common/kernel_android_uapi
new file mode 120000
index 0000000..fd78315
--- /dev/null
+++ b/libc/versioner-dependencies/common/kernel_android_uapi
@@ -0,0 +1 @@
+../../kernel/android/uapi/
\ No newline at end of file
diff --git a/libc/versioner-dependencies/common/kernel_uapi b/libc/versioner-dependencies/common/kernel_uapi
new file mode 120000
index 0000000..d5cb8ee
--- /dev/null
+++ b/libc/versioner-dependencies/common/kernel_uapi
@@ -0,0 +1 @@
+../../kernel/uapi/
\ No newline at end of file
diff --git a/libc/versioner-dependencies/mips/arch-mips b/libc/versioner-dependencies/mips/arch-mips
new file mode 120000
index 0000000..56ed021
--- /dev/null
+++ b/libc/versioner-dependencies/mips/arch-mips
@@ -0,0 +1 @@
+../../arch-mips/include/
\ No newline at end of file
diff --git a/libc/versioner-dependencies/mips/kernel_uapi_asm-mips b/libc/versioner-dependencies/mips/kernel_uapi_asm-mips
new file mode 120000
index 0000000..94bb3db
--- /dev/null
+++ b/libc/versioner-dependencies/mips/kernel_uapi_asm-mips
@@ -0,0 +1 @@
+../../kernel/uapi/asm-mips
\ No newline at end of file
diff --git a/libc/versioner-dependencies/mips64/arch-mips64 b/libc/versioner-dependencies/mips64/arch-mips64
new file mode 120000
index 0000000..4893b57
--- /dev/null
+++ b/libc/versioner-dependencies/mips64/arch-mips64
@@ -0,0 +1 @@
+../../arch-mips64/include/
\ No newline at end of file
diff --git a/libc/versioner-dependencies/mips64/kernel_uapi_asm-mips b/libc/versioner-dependencies/mips64/kernel_uapi_asm-mips
new file mode 120000
index 0000000..94bb3db
--- /dev/null
+++ b/libc/versioner-dependencies/mips64/kernel_uapi_asm-mips
@@ -0,0 +1 @@
+../../kernel/uapi/asm-mips
\ No newline at end of file
diff --git a/libc/versioner-dependencies/x86/arch-x86 b/libc/versioner-dependencies/x86/arch-x86
new file mode 120000
index 0000000..6426384
--- /dev/null
+++ b/libc/versioner-dependencies/x86/arch-x86
@@ -0,0 +1 @@
+../../arch-x86/include/
\ No newline at end of file
diff --git a/libc/versioner-dependencies/x86/kernel_uapi_asm-x86 b/libc/versioner-dependencies/x86/kernel_uapi_asm-x86
new file mode 120000
index 0000000..1b7a73d
--- /dev/null
+++ b/libc/versioner-dependencies/x86/kernel_uapi_asm-x86
@@ -0,0 +1 @@
+../../kernel/uapi/asm-x86/
\ No newline at end of file
diff --git a/libc/versioner-dependencies/x86_64/arch-x86_64 b/libc/versioner-dependencies/x86_64/arch-x86_64
new file mode 120000
index 0000000..684d74e
--- /dev/null
+++ b/libc/versioner-dependencies/x86_64/arch-x86_64
@@ -0,0 +1 @@
+../../arch-x86_64/include/
\ No newline at end of file
diff --git a/libc/versioner-dependencies/x86_64/kernel_uapi_asm-x86 b/libc/versioner-dependencies/x86_64/kernel_uapi_asm-x86
new file mode 120000
index 0000000..1b7a73d
--- /dev/null
+++ b/libc/versioner-dependencies/x86_64/kernel_uapi_asm-x86
@@ -0,0 +1 @@
+../../kernel/uapi/asm-x86/
\ No newline at end of file
diff --git a/libc/zoneinfo/tzdata b/libc/zoneinfo/tzdata
index a2e8da6..c5932bc 100644
--- a/libc/zoneinfo/tzdata
+++ b/libc/zoneinfo/tzdata
Binary files differ
diff --git a/libdl/Android.bp b/libdl/Android.bp
index 013554a..5b67e38 100644
--- a/libdl/Android.bp
+++ b/libdl/Android.bp
@@ -104,6 +104,7 @@
     stl: "none",
 
     name: "ld-android",
+    defaults: ["linux_bionic_supported"],
 
     // 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/linker/Android.bp b/linker/Android.bp
index cb3b07b..d617189 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -19,6 +19,7 @@
         "linker_block_allocator.cpp",
         "linker_dlwarning.cpp",
         "linker_cfi.cpp",
+        "linker_config.cpp",
         "linker_gdb_support.cpp",
         "linker_globals.cpp",
         "linker_libc_support.c",
@@ -39,35 +40,29 @@
             srcs: ["arch/arm/begin.S"],
 
             cflags: ["-D__work_around_b_24465209__"],
-            ldflags: ["-Wl,-dynamic-linker,/system/bin/linker"],
         },
         arm64: {
             srcs: ["arch/arm64/begin.S"],
-            ldflags: ["-Wl,-dynamic-linker,/system/bin/linker64"],
         },
         x86: {
             srcs: ["arch/x86/begin.c"],
 
             cflags: ["-D__work_around_b_24465209__"],
-            ldflags: ["-Wl,-dynamic-linker,/system/bin/linker"],
         },
         x86_64: {
             srcs: ["arch/x86_64/begin.S"],
-            ldflags: ["-Wl,-dynamic-linker,/system/bin/linker64"],
         },
         mips: {
             srcs: [
                 "arch/mips/begin.S",
                 "linker_mips.cpp",
             ],
-            ldflags: ["-Wl,-dynamic-linker,/system/bin/linker"],
         },
         mips64: {
             srcs: [
                 "arch/mips64/begin.S",
                 "linker_mips.cpp",
             ],
-            ldflags: ["-Wl,-dynamic-linker,/system/bin/linker64"],
         },
     },
 
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index 5ccd656..96dd477 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -48,6 +48,7 @@
 
   char* old_value = *dlerror_slot;
   *dlerror_slot = new_value;
+  LD_LOG(kLogErrors, "%s\n", new_value);
   return old_value;
 }
 
diff --git a/linker/ld.config.format.md b/linker/ld.config.format.md
new file mode 100644
index 0000000..686d6be
--- /dev/null
+++ b/linker/ld.config.format.md
@@ -0,0 +1,83 @@
+# Linker config file format
+
+This document describes format of /system/etc/ld.config.txt file. This file can be used to customize
+linker-namespace setup for dynamic executables.
+
+## Overview
+
+The configuration consists of 2 parts
+1. Mappings - maps executable locations to sections
+2. Sections - contains linker-namespace configuration
+
+## Mappings
+
+This part of the document maps location of an executable to a section. Here is an example
+
+The format is `dir.<section_name>=<directory>`
+
+The mappings should be defined between start of ld.config.txt and the first section.
+
+## Section
+
+Every section starts with `[section_name]` (which is used in mappings) and it defines namespaces
+configuration using set of properties described in example below.
+
+## Example
+
+```
+# The following line maps section to a dir. Binraies ran from this location will use namespaces
+# configuration specified in [example_section] below
+dir.example_section=/system/bin/example
+
+# Section starts
+[example_section]
+
+# When this flag is set to true linker will set target_sdk_version for this binary to
+# the version specified in <dirname>/.version file, where <dirname> = dirname(executable_path)
+#
+# default value is false
+enable.target.sdk.version = true
+
+# This property can be used to declare additional namespaces.Note that there is always the default
+# namespace. The default namespace is the namespace for the main executable. This list is
+# comma-separated.
+additional.namespaces = ns1
+
+# Each namespace property starts with "namespace.<namespace-name>" The following is configuration
+# for the default namespace
+
+# Is namespace isolated - the default value is false
+namespace.default.isolated = true
+
+# Default namespace search path. Note that ${LIB} here is substituted with "lib" for 32bit targets
+# and with "lib64" for 64bit ones.
+namespace.default.search.paths = /system/${LIB}:/system/other/${LIB}
+
+# ... same for asan
+namespace.default.asan.search.paths = /data/${LIB}:/data/other/${LIB}
+
+# Permitted path
+namespace.default.permitted.paths = /system/${LIB}
+
+# ... asan
+namespace.default.asan.permitted.paths = /data/${LIB}
+
+# This declares linked namespaces - comma separated list.
+namespace.default.links = ns1
+
+# For every link define list of shared libraries. This is list of the libraries accessilbe from
+# default namespace but loaded in the linked namespace.
+namespace.default.link.ns1.shared_libs = libexternal.so:libother.so
+
+# This part defines config for ns1
+namespace.ns1.isolated = true
+namespace.ns1.search.paths = /vendor/${LIB}
+namespace.ns1.asan.search.paths = /data/vendor/${LIB}
+namespace.ns1.permitted.paths = /vendor/${LIB}
+namespace.ns1.asan.permitted.paths = /data/vendor/${LIB}
+
+# and links it to default namespace
+namespace.ns.links = default
+namespace.ns.link.default.shared_libs = libc.so:libdl.so:libm.so:libstdc++.so
+```
+
diff --git a/linker/linker.cpp b/linker/linker.cpp
index d7864b4..1647db7 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -49,6 +49,7 @@
 #include "linker.h"
 #include "linker_block_allocator.h"
 #include "linker_cfi.h"
+#include "linker_config.h"
 #include "linker_gdb_support.h"
 #include "linker_globals.h"
 #include "linker_debug.h"
@@ -77,18 +78,22 @@
 static LinkerTypeAllocator<android_namespace_t> g_namespace_allocator;
 static LinkerTypeAllocator<LinkedListEntry<android_namespace_t>> g_namespace_list_allocator;
 
+static const char* const kLdConfigFilePath = "/system/etc/ld.config.txt";
+
 #if defined(__LP64__)
 static const char* const kSystemLibDir     = "/system/lib64";
 static const char* const kVendorLibDir     = "/vendor/lib64";
-static const char* const kAsanSystemLibDir = "/data/lib64";
-static const char* const kAsanVendorLibDir = "/data/vendor/lib64";
+static const char* const kAsanSystemLibDir = "/data/asan/system/lib64";
+static const char* const kAsanVendorLibDir = "/data/asan/vendor/lib64";
 #else
 static const char* const kSystemLibDir     = "/system/lib";
 static const char* const kVendorLibDir     = "/vendor/lib";
-static const char* const kAsanSystemLibDir = "/data/lib";
-static const char* const kAsanVendorLibDir = "/data/vendor/lib";
+static const char* const kAsanSystemLibDir = "/data/asan/system/lib";
+static const char* const kAsanVendorLibDir = "/data/asan/vendor/lib";
 #endif
 
+static const char* const kAsanLibDirPrefix = "/data/asan";
+
 static const char* const kDefaultLdPaths[] = {
   kSystemLibDir,
   kVendorLibDir,
@@ -1107,11 +1112,43 @@
   }
 }
 
+static bool find_loaded_library_by_inode(android_namespace_t* ns,
+                                         const struct stat& file_stat,
+                                         off64_t file_offset,
+                                         bool search_linked_namespaces,
+                                         soinfo** candidate) {
+
+  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;
+  };
+
+  *candidate = ns->soinfo_list().find_if(predicate);
+
+  if (*candidate == nullptr && search_linked_namespaces) {
+    for (auto& link : ns->linked_namespaces()) {
+      android_namespace_t* linked_ns = link.linked_namespace();
+      soinfo* si = linked_ns->soinfo_list().find_if(predicate);
+
+      if (si != nullptr && link.is_accessible(si->get_soname())) {
+        *candidate = si;
+        return true;
+      }
+    }
+  }
+
+  return *candidate != nullptr;
+}
+
 static bool load_library(android_namespace_t* ns,
                          LoadTask* task,
                          LoadTaskList* load_tasks,
                          int rtld_flags,
-                         const std::string& realpath) {
+                         const std::string& realpath,
+                         bool search_linked_namespaces) {
   off64_t file_offset = task->get_file_offset();
   const char* name = task->get_name();
   const android_dlextinfo* extinfo = task->get_extinfo();
@@ -1139,17 +1176,8 @@
   // 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) {
-    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);
-
-    if (si != nullptr) {
+    soinfo* si = nullptr;
+    if (find_loaded_library_by_inode(ns, file_stat, file_offset, search_linked_namespaces, &si)) {
       TRACE("library \"%s\" is already loaded under different name/path \"%s\" - "
             "will return existing soinfo", name, si->get_realpath());
       task->set_soinfo(si);
@@ -1244,7 +1272,8 @@
                          LoadTask* task,
                          ZipArchiveCache* zip_archive_cache,
                          LoadTaskList* load_tasks,
-                         int rtld_flags) {
+                         int rtld_flags,
+                         bool search_linked_namespaces) {
   const char* name = task->get_name();
   soinfo* needed_by = task->get_needed_by();
   const android_dlextinfo* extinfo = task->get_extinfo();
@@ -1265,7 +1294,7 @@
 
     task->set_fd(extinfo->library_fd, false);
     task->set_file_offset(file_offset);
-    return load_library(ns, task, load_tasks, rtld_flags, realpath);
+    return load_library(ns, task, load_tasks, rtld_flags, realpath, search_linked_namespaces);
   }
 
   // Open the file.
@@ -1278,19 +1307,12 @@
   task->set_fd(fd, true);
   task->set_file_offset(file_offset);
 
-  return load_library(ns, task, load_tasks, rtld_flags, realpath);
+  return load_library(ns, task, load_tasks, rtld_flags, realpath, search_linked_namespaces);
 }
 
-// Returns true if library was found and false otherwise
 static bool find_loaded_library_by_soname(android_namespace_t* ns,
-                                         const char* name, soinfo** candidate) {
-  *candidate = nullptr;
-
-  // Ignore filename with path.
-  if (strchr(name, '/') != nullptr) {
-    return false;
-  }
-
+                                          const char* name,
+                                          soinfo** candidate) {
   return !ns->soinfo_list().visit([&](soinfo* si) {
     const char* soname = si->get_soname();
     if (soname != nullptr && (strcmp(name, soname) == 0)) {
@@ -1302,6 +1324,38 @@
   });
 }
 
+// Returns true if library was found and false otherwise
+static bool find_loaded_library_by_soname(android_namespace_t* ns,
+                                         const char* name,
+                                         bool search_linked_namespaces,
+                                         soinfo** candidate) {
+  *candidate = nullptr;
+
+  // Ignore filename with path.
+  if (strchr(name, '/') != nullptr) {
+    return false;
+  }
+
+  bool found = find_loaded_library_by_soname(ns, name, candidate);
+
+  if (!found && search_linked_namespaces) {
+    // if a library was not found - look into linked namespaces
+    for (auto& link : ns->linked_namespaces()) {
+      if (!link.is_accessible(name)) {
+        continue;
+      }
+
+      android_namespace_t* linked_ns = link.linked_namespace();
+
+      if (find_loaded_library_by_soname(linked_ns, name, candidate)) {
+        return true;
+      }
+    }
+  }
+
+  return found;
+}
+
 static bool find_library_in_linked_namespace(const android_namespace_link_t& namespace_link,
                                              LoadTask* task,
                                              int rtld_flags) {
@@ -1311,7 +1365,7 @@
   bool loaded = false;
 
   std::string soname;
-  if (find_loaded_library_by_soname(ns, task->get_name(), &candidate)) {
+  if (find_loaded_library_by_soname(ns, task->get_name(), false, &candidate)) {
     loaded = true;
     soname = candidate->get_soname();
   } else {
@@ -1362,7 +1416,7 @@
                                   bool search_linked_namespaces) {
   soinfo* candidate;
 
-  if (find_loaded_library_by_soname(ns, task->get_name(), &candidate)) {
+  if (find_loaded_library_by_soname(ns, task->get_name(), search_linked_namespaces, &candidate)) {
     task->set_soinfo(candidate);
     return true;
   }
@@ -1372,7 +1426,7 @@
   TRACE("[ \"%s\" find_loaded_library_by_soname failed (*candidate=%s@%p). Trying harder...]",
       task->get_name(), candidate == nullptr ? "n/a" : candidate->get_realpath(), candidate);
 
-  if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags)) {
+  if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags, search_linked_namespaces)) {
     return true;
   }
 
@@ -1645,6 +1699,8 @@
     root = root->get_local_group_root();
   }
 
+  ScopedTrace trace((std::string("unload ") + root->get_realpath()).c_str());
+
   if (!root->can_unload()) {
     TRACE("not unloading \"%s\" - the binary is flagged with NODELETE", root->get_realpath());
     return;
@@ -1833,6 +1889,9 @@
 void* do_dlopen(const char* name, int flags,
                 const android_dlextinfo* extinfo,
                 const void* caller_addr) {
+  std::string trace_prefix = std::string("dlopen: ") + (name == nullptr ? "(nullptr)" : name);
+  ScopedTrace trace(trace_prefix.c_str());
+  ScopedTrace loading_trace((trace_prefix + " - loading and linking").c_str());
   soinfo* const caller = find_containing_library(caller_addr);
   android_namespace_t* ns = get_caller_namespace(caller);
 
@@ -1889,24 +1948,18 @@
   if (g_is_asan && translated_name != nullptr && translated_name[0] == '/') {
     char translated_path[PATH_MAX];
     if (realpath(translated_name, translated_path) != nullptr) {
-      if (file_is_in_dir(translated_path, kSystemLibDir)) {
-        asan_name_holder = std::string(kAsanSystemLibDir) + "/" + basename(translated_path);
-        if (file_exists(asan_name_holder.c_str())) {
-          translated_name = asan_name_holder.c_str();
-          PRINT("linker_asan dlopen translating \"%s\" -> \"%s\"", name, translated_name);
-        }
-      } else if (file_is_in_dir(translated_path, kVendorLibDir)) {
-        asan_name_holder = std::string(kAsanVendorLibDir) + "/" + basename(translated_path);
-        if (file_exists(asan_name_holder.c_str())) {
-          translated_name = asan_name_holder.c_str();
-          PRINT("linker_asan dlopen translating \"%s\" -> \"%s\"", name, translated_name);
-        }
+      asan_name_holder = std::string(kAsanLibDirPrefix) + translated_path;
+      if (file_exists(asan_name_holder.c_str())) {
+        translated_name = asan_name_holder.c_str();
+        PRINT("linker_asan dlopen translating \"%s\" -> \"%s\"", name, translated_name);
       }
     }
   }
 
   ProtectedDataGuard guard;
   soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);
+  loading_trace.End();
+
   if (si != nullptr) {
     void* handle = si->to_handle();
     LD_LOG(kLogDlopen,
@@ -1964,6 +2017,7 @@
               const char* sym_ver,
               const void* caller_addr,
               void** symbol) {
+  ScopedTrace trace("dlsym");
 #if !defined(__LP64__)
   if (handle == nullptr) {
     DL_ERR("dlsym failed: library handle is null");
@@ -2039,6 +2093,7 @@
 }
 
 int do_dlclose(void* handle) {
+  ScopedTrace trace("dlclose");
   ProtectedDataGuard guard;
   soinfo* si = soinfo_from_handle(handle);
   if (si == nullptr) {
@@ -2067,8 +2122,7 @@
                        "(anonymous)",
                        nullptr,
                        library_search_path,
-                       // TODO (dimitry): change to isolated eventually.
-                       ANDROID_NAMESPACE_TYPE_REGULAR,
+                       ANDROID_NAMESPACE_TYPE_ISOLATED,
                        nullptr,
                        &g_default_namespace);
 
@@ -3328,21 +3382,9 @@
   return true;
 }
 
-void init_default_namespace() {
-  g_default_namespace.set_name("(default)");
+static void init_default_namespace_no_config(bool is_asan) {
   g_default_namespace.set_isolated(false);
-
-  soinfo* somain = solist_get_somain();
-
-  const char *interp = phdr_table_get_interpreter_name(somain->phdr, somain->phnum,
-                                                       somain->load_bias);
-  const char* bname = basename(interp);
-
-  bool is_asan = bname != nullptr &&
-                 (strcmp(bname, "linker_asan") == 0 ||
-                  strcmp(bname, "linker_asan64") == 0);
   auto default_ld_paths = is_asan ? kAsanDefaultLdPaths : kDefaultLdPaths;
-  g_is_asan = is_asan;
 
   char real_path[PATH_MAX];
   std::vector<std::string> ld_default_paths;
@@ -3355,4 +3397,91 @@
   }
 
   g_default_namespace.set_default_library_paths(std::move(ld_default_paths));
-};
+}
+
+void init_default_namespace(const char* executable_path) {
+  g_default_namespace.set_name("(default)");
+
+  soinfo* somain = solist_get_somain();
+
+  const char *interp = phdr_table_get_interpreter_name(somain->phdr, somain->phnum,
+                                                       somain->load_bias);
+  const char* bname = basename(interp);
+
+  g_is_asan = bname != nullptr &&
+              (strcmp(bname, "linker_asan") == 0 ||
+               strcmp(bname, "linker_asan64") == 0);
+
+  const Config* config = nullptr;
+
+  std::string error_msg;
+
+  if (!Config::read_binary_config(kLdConfigFilePath,
+                                  executable_path,
+                                  g_is_asan,
+                                  &config,
+                                  &error_msg)) {
+    if (!error_msg.empty()) {
+      DL_WARN("error reading config file \"%s\" for \"%s\" (will use default configuration): %s",
+              kLdConfigFilePath,
+              executable_path,
+              error_msg.c_str());
+    }
+    config = nullptr;
+  }
+
+  if (config == nullptr) {
+    init_default_namespace_no_config(g_is_asan);
+    return;
+  }
+
+  const auto& namespace_configs = config->namespace_configs();
+  std::unordered_map<std::string, android_namespace_t*> namespaces;
+
+  // 1. Initialize default namespace
+  const NamespaceConfig* default_ns_config = config->default_namespace_config();
+
+  g_default_namespace.set_isolated(default_ns_config->isolated());
+  g_default_namespace.set_default_library_paths(default_ns_config->search_paths());
+  g_default_namespace.set_permitted_paths(default_ns_config->permitted_paths());
+
+  namespaces[default_ns_config->name()] = &g_default_namespace;
+
+  // 2. Initialize other namespaces
+
+  for (auto& ns_config : namespace_configs) {
+    if (namespaces.find(ns_config->name()) != namespaces.end()) {
+      continue;
+    }
+
+    android_namespace_t* ns = new (g_namespace_allocator.alloc()) android_namespace_t();
+    ns->set_name(ns_config->name());
+    ns->set_isolated(ns_config->isolated());
+    ns->set_default_library_paths(ns_config->search_paths());
+    ns->set_permitted_paths(ns_config->permitted_paths());
+
+    namespaces[ns_config->name()] = ns;
+  }
+
+  // 3. Establish links between namespaces
+  for (auto& ns_config : namespace_configs) {
+    auto it_from = namespaces.find(ns_config->name());
+    CHECK(it_from != namespaces.end());
+    android_namespace_t* namespace_from = it_from->second;
+    for (const NamespaceLinkConfig& ns_link : ns_config->links()) {
+      auto it_to = namespaces.find(ns_link.ns_name());
+      CHECK(it_to != namespaces.end());
+      android_namespace_t* namespace_to = it_to->second;
+      link_namespaces(namespace_from, namespace_to, ns_link.shared_libs().c_str());
+    }
+  }
+  // we can no longer rely on the fact that libdl.so is part of default namespace
+  // this is why we want to add ld-android.so to all namespaces from ld.config.txt
+  soinfo* ld_android_so = solist_get_head();
+  for (auto it : namespaces) {
+    it.second->add_soinfo(ld_android_so);
+    // TODO (dimitry): somain and ld_preloads should probably be added to all of these namespaces too?
+  }
+
+  set_application_target_sdk_version(config->target_sdk_version());
+}
diff --git a/linker/linker_config.cpp b/linker/linker_config.cpp
new file mode 100644
index 0000000..33616f7
--- /dev/null
+++ b/linker/linker_config.cpp
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "linker_config.h"
+
+#include "linker_globals.h"
+#include "linker_debug.h"
+#include "linker_utils.h"
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+
+#include <private/ScopeGuard.h>
+
+#include <stdlib.h>
+
+#include <string>
+#include <unordered_map>
+
+class ConfigParser {
+ public:
+  enum {
+    kProperty,
+    kSection,
+    kEndOfFile,
+    kError,
+  };
+
+  explicit ConfigParser(std::string&& content)
+      : content_(content), p_(0), lineno_(0), was_end_of_file_(false) {}
+
+  /*
+   * Possible return values
+   * kProperty: name is set to property name and value is set to property value
+   * kSection: name is set to section name.
+   * kEndOfFile: reached end of file.
+   * kError: error_msg is set.
+   */
+  int next_token(std::string* name, std::string* value, std::string* error_msg) {
+    std::string line;
+    while(NextLine(&line)) {
+      size_t found = line.find('#');
+      line = android::base::Trim(line.substr(0, found));
+
+      if (line.empty()) {
+        continue;
+      }
+
+      if (line[0] == '[' && line[line.size() - 1] == ']') {
+        *name = line.substr(1, line.size() - 2);
+        return kSection;
+      }
+
+      found = line.find('=');
+      if (found == std::string::npos) {
+        *error_msg = std::string("invalid format: ") +
+                    line +
+                    ", expected \"name = property\" or \"[section]\"";
+        return kError;
+      }
+
+      *name = android::base::Trim(line.substr(0, found));
+      *value = android::base::Trim(line.substr(found + 1));
+      return kProperty;
+    }
+
+    // to avoid infinite cycles when programmer makes a mistake
+    CHECK(!was_end_of_file_);
+    was_end_of_file_ = true;
+    return kEndOfFile;
+  }
+
+  size_t lineno() const {
+    return lineno_;
+  }
+
+ private:
+  bool NextLine(std::string* line) {
+    if (p_ == std::string::npos) {
+      return false;
+    }
+
+    size_t found = content_.find('\n', p_);
+    if (found != std::string::npos) {
+      *line = content_.substr(p_, found - p_);
+      p_ = found + 1;
+    } else {
+      *line = content_.substr(p_);
+      p_ = std::string::npos;
+    }
+
+    lineno_++;
+    return true;
+  }
+
+  std::string content_;
+  size_t p_;
+  size_t lineno_;
+  bool was_end_of_file_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ConfigParser);
+};
+
+class PropertyValue {
+ public:
+  PropertyValue() = default;
+
+  PropertyValue(std::string&& value, size_t lineno)
+    : value_(value), lineno_(lineno) {}
+
+  const std::string& value() const {
+    return value_;
+  }
+
+  size_t lineno() const {
+    return lineno_;
+  }
+
+ private:
+  std::string value_;
+  size_t lineno_;
+};
+
+static std::string create_error_msg(const char* file,
+                                    size_t lineno,
+                                    const std::string& msg) {
+  char buf[1024];
+  __libc_format_buffer(buf, sizeof(buf), "%s:%zu: error: %s", file, lineno, msg.c_str());
+
+  return std::string(buf);
+}
+
+static bool parse_config_file(const char* ld_config_file_path,
+                              const char* binary_realpath,
+                              std::unordered_map<std::string, PropertyValue>* properties,
+                              std::string* error_msg) {
+  std::string content;
+  if (!android::base::ReadFileToString(ld_config_file_path, &content)) {
+    if (errno != ENOENT) {
+      *error_msg = std::string("error reading file \"") +
+                   ld_config_file_path + "\": " + strerror(errno);
+    }
+    return false;
+  }
+
+  ConfigParser cp(std::move(content));
+
+  std::string section_name;
+
+  while(true) {
+    std::string name;
+    std::string value;
+    std::string error;
+
+    int result = cp.next_token(&name, &value, &error);
+    if (result == ConfigParser::kError) {
+      DL_WARN("error parsing %s:%zd: %s (ignoring this line)",
+              ld_config_file_path,
+              cp.lineno(),
+              error.c_str());
+      continue;
+    }
+
+    if (result == ConfigParser::kSection || result == ConfigParser::kEndOfFile) {
+      return false;
+    }
+
+    if (result == ConfigParser::kProperty) {
+      if (!android::base::StartsWith(name, "dir.")) {
+        DL_WARN("error parsing %s:%zd: unexpected property name \"%s\", "
+                "expected format dir.<section_name> (ignoring this line)",
+                ld_config_file_path,
+                cp.lineno(),
+                name.c_str());
+        continue;
+      }
+
+      // remove trailing '/'
+      while (value[value.size() - 1] == '/') {
+        value = value.substr(0, value.size() - 1);
+      }
+
+      if (value.empty()) {
+        DL_WARN("error parsing %s:%zd: property value is empty (ignoring this line)",
+                ld_config_file_path,
+                cp.lineno());
+        continue;
+      }
+
+      if (file_is_under_dir(binary_realpath, value)) {
+        section_name = name.substr(4);
+        break;
+      }
+    }
+  }
+
+  // skip everything until we meet a correct section
+  while (true) {
+    std::string name;
+    std::string value;
+    std::string error;
+
+    int result = cp.next_token(&name, &value, &error);
+
+    if (result == ConfigParser::kSection && name == section_name) {
+      break;
+    }
+
+    if (result == ConfigParser::kEndOfFile) {
+      *error_msg = create_error_msg(ld_config_file_path,
+                                    cp.lineno(),
+                                    std::string("section \"") + section_name + "\" not found");
+      return false;
+    }
+  }
+
+  // found the section - parse it
+  while (true) {
+    std::string name;
+    std::string value;
+    std::string error;
+
+    int result = cp.next_token(&name, &value, &error);
+
+    if (result == ConfigParser::kEndOfFile || result == ConfigParser::kSection) {
+      break;
+    }
+
+    if (result == ConfigParser::kProperty) {
+      if (properties->find(name) != properties->end()) {
+        DL_WARN("%s:%zd: warning: property \"%s\" redefinition",
+                ld_config_file_path,
+                cp.lineno(),
+                name.c_str());
+      }
+
+      (*properties)[name] = PropertyValue(std::move(value), cp.lineno());
+    }
+
+    if (result == ConfigParser::kError) {
+      DL_WARN("error parsing %s:%zd: %s (ignoring this line)",
+              ld_config_file_path,
+              cp.lineno(),
+              error.c_str());
+      continue;
+    }
+  }
+
+  return true;
+}
+
+static Config g_config;
+
+static constexpr const char* kDefaultConfigName = "default";
+static constexpr const char* kPropertyAdditionalNamespaces = "additional.namespaces";
+#if defined(__LP64__)
+static constexpr const char* kLibParamValue = "lib64";
+#else
+static constexpr const char* kLibParamValue = "lib";
+#endif
+
+class Properties {
+ public:
+  explicit Properties(std::unordered_map<std::string, PropertyValue>&& properties)
+      : properties_(properties), target_sdk_version_(__ANDROID_API__) {}
+
+  std::vector<std::string> get_strings(const std::string& name, size_t* lineno = nullptr) const {
+    auto it = find_property(name, lineno);
+    if (it == properties_.end()) {
+      // return empty vector
+      return std::vector<std::string>();
+    }
+
+    std::vector<std::string> strings = android::base::Split(it->second.value(), ",");
+
+    for (size_t i = 0; i < strings.size(); ++i) {
+      strings[i] = android::base::Trim(strings[i]);
+    }
+
+    return strings;
+  }
+
+  bool get_bool(const std::string& name, size_t* lineno = nullptr) const {
+    auto it = find_property(name, lineno);
+    if (it == properties_.end()) {
+      return false;
+    }
+
+    return it->second.value() == "true";
+  }
+
+  std::string get_string(const std::string& name, size_t* lineno = nullptr) const {
+    auto it = find_property(name, lineno);
+    return (it == properties_.end()) ? "" : it->second.value();
+  }
+
+  std::vector<std::string> get_paths(const std::string& name, size_t* lineno = nullptr) {
+    std::string paths_str = get_string(name, lineno);
+
+    std::vector<std::string> paths;
+    split_path(paths_str.c_str(), ":", &paths);
+
+    std::vector<std::pair<std::string, std::string>> params;
+    params.push_back({ "LIB", kLibParamValue });
+    if (target_sdk_version_ != 0) {
+      char buf[16];
+      __libc_format_buffer(buf, sizeof(buf), "%d", target_sdk_version_);
+      params.push_back({ "SDK_VER", buf });
+    }
+
+    for (auto&& path : paths) {
+      format_string(&path, params);
+    }
+
+    std::vector<std::string> resolved_paths;
+
+    // do not remove paths that do not exist
+    resolve_paths(paths, &resolved_paths);
+
+    return resolved_paths;
+  }
+
+  void set_target_sdk_version(int target_sdk_version) {
+    target_sdk_version_ = target_sdk_version;
+  }
+
+ private:
+  std::unordered_map<std::string, PropertyValue>::const_iterator
+  find_property(const std::string& name, size_t* lineno) const {
+    auto it = properties_.find(name);
+    if (it != properties_.end() && lineno != nullptr) {
+      *lineno = it->second.lineno();
+    }
+
+    return it;
+  }
+  std::unordered_map<std::string, PropertyValue> properties_;
+  int target_sdk_version_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(Properties);
+};
+
+bool Config::read_binary_config(const char* ld_config_file_path,
+                                      const char* binary_realpath,
+                                      bool is_asan,
+                                      const Config** config,
+                                      std::string* error_msg) {
+  g_config.clear();
+
+  std::unordered_map<std::string, PropertyValue> property_map;
+  if (!parse_config_file(ld_config_file_path, binary_realpath, &property_map, error_msg)) {
+    return false;
+  }
+
+  Properties properties(std::move(property_map));
+
+  auto failure_guard = make_scope_guard([] {
+    g_config.clear();
+  });
+
+  std::unordered_map<std::string, NamespaceConfig*> namespace_configs;
+
+  namespace_configs[kDefaultConfigName] = g_config.create_namespace_config(kDefaultConfigName);
+
+  std::vector<std::string> additional_namespaces = properties.get_strings(kPropertyAdditionalNamespaces);
+  for (const auto& name : additional_namespaces) {
+    namespace_configs[name] = g_config.create_namespace_config(name);
+  }
+
+  bool versioning_enabled = properties.get_bool("enable.target.sdk.version");
+  int target_sdk_version = __ANDROID_API__;
+  if (versioning_enabled) {
+    std::string version_file = dirname(binary_realpath) + "/.version";
+    std::string content;
+    if (!android::base::ReadFileToString(version_file, &content)) {
+      if (errno != ENOENT) {
+        *error_msg = std::string("error reading version file \"") +
+                     version_file + "\": " + strerror(errno);
+        return false;
+      }
+    } else {
+      content = android::base::Trim(content);
+      errno = 0;
+      char* end = nullptr;
+      const char* content_str = content.c_str();
+      int result = strtol(content_str, &end, 10);
+      if (errno == 0 && *end == '\0' && result > 0) {
+        target_sdk_version = result;
+        properties.set_target_sdk_version(target_sdk_version);
+      } else {
+        *error_msg = std::string("invalid version \"") + version_file + "\": \"" + content +"\"";
+        return false;
+      }
+    }
+  }
+
+  g_config.set_target_sdk_version(target_sdk_version);
+
+  for (auto ns_config_it : namespace_configs) {
+    auto& name = ns_config_it.first;
+    NamespaceConfig* ns_config = ns_config_it.second;
+
+    std::string property_name_prefix = std::string("namespace.") + name;
+
+    size_t lineno = 0;
+    std::vector<std::string> linked_namespaces =
+        properties.get_strings(property_name_prefix + ".links", &lineno);
+
+    for (const auto& linked_ns_name : linked_namespaces) {
+      if (namespace_configs.find(linked_ns_name) == namespace_configs.end()) {
+        *error_msg = create_error_msg(ld_config_file_path,
+                                      lineno,
+                                      std::string("undefined namespace: ") + linked_ns_name);
+        return false;
+      }
+
+      std::string shared_libs = properties.get_string(property_name_prefix +
+                                                      ".link." +
+                                                      linked_ns_name +
+                                                      ".shared_libs", &lineno);
+
+      if (shared_libs.empty()) {
+        *error_msg = create_error_msg(ld_config_file_path,
+                                      lineno,
+                                      std::string("list of shared_libs for ") +
+                                      name +
+                                      "->" +
+                                      linked_ns_name +
+                                      " link is not specified or is empty.");
+        return false;
+      }
+
+      ns_config->add_namespace_link(linked_ns_name, shared_libs);
+    }
+
+    ns_config->set_isolated(properties.get_bool(property_name_prefix + ".isolated"));
+
+    // these are affected by is_asan flag
+    if (is_asan) {
+      property_name_prefix += ".asan";
+    }
+
+    ns_config->set_search_paths(properties.get_paths(property_name_prefix + ".search.paths"));
+    ns_config->set_permitted_paths(properties.get_paths(property_name_prefix + ".permitted.paths"));
+  }
+
+  failure_guard.disable();
+  *config = &g_config;
+  return true;
+}
+
+NamespaceConfig* Config::create_namespace_config(const std::string& name) {
+  namespace_configs_.push_back(std::unique_ptr<NamespaceConfig>(new NamespaceConfig(name)));
+  NamespaceConfig* ns_config_ptr = namespace_configs_.back().get();
+  namespace_configs_map_[name] = ns_config_ptr;
+  return ns_config_ptr;
+}
+
+void Config::clear() {
+  namespace_configs_.clear();
+  namespace_configs_map_.clear();
+}
diff --git a/linker/linker_config.h b/linker/linker_config.h
new file mode 100644
index 0000000..4ec8b26
--- /dev/null
+++ b/linker/linker_config.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LINKER_CONFIG_H_
+#define _LINKER_CONFIG_H_
+
+#include <android/api-level.h>
+
+#include <stdlib.h>
+#include <limits.h>
+#include "private/bionic_macros.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+class NamespaceLinkConfig {
+ public:
+  NamespaceLinkConfig() = default;
+  NamespaceLinkConfig(const std::string& ns_name, const std::string& shared_libs)
+      : ns_name_(ns_name), shared_libs_(shared_libs)  {}
+
+  const std::string& ns_name() const {
+    return ns_name_;
+  }
+
+  const std::string& shared_libs() const {
+    return shared_libs_;
+  }
+
+ private:
+  std::string ns_name_;
+  std::string shared_libs_;
+};
+
+class NamespaceConfig {
+ public:
+  explicit NamespaceConfig(const std::string& name)
+      : name_(name), isolated_(false)
+  {}
+
+  const char* name() const {
+    return name_.c_str();
+  }
+
+  bool isolated() const {
+    return isolated_;
+  }
+
+  const std::vector<std::string>& search_paths() const {
+    return search_paths_;
+  }
+
+  const std::vector<std::string>& permitted_paths() const {
+    return permitted_paths_;
+  }
+
+  const std::vector<NamespaceLinkConfig>& links() const {
+    return namespace_links_;
+  }
+
+  void add_namespace_link(const std::string& ns_name, const std::string& shared_libs) {
+    namespace_links_.push_back(NamespaceLinkConfig(ns_name, shared_libs));
+  }
+
+  void set_isolated(bool isolated) {
+    isolated_ = isolated;
+  }
+
+  void set_search_paths(std::vector<std::string>&& search_paths) {
+    search_paths_ = search_paths;
+  }
+
+  void set_permitted_paths(std::vector<std::string>&& permitted_paths) {
+    permitted_paths_ = permitted_paths;
+  }
+ private:
+  const std::string name_;
+  bool isolated_;
+  std::vector<std::string> search_paths_;
+  std::vector<std::string> permitted_paths_;
+  std::vector<NamespaceLinkConfig> namespace_links_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(NamespaceConfig);
+};
+
+class Config {
+ public:
+  Config() : target_sdk_version_(__ANDROID_API__) {}
+
+  const std::vector<std::unique_ptr<NamespaceConfig>>& namespace_configs() const {
+    return namespace_configs_;
+  }
+
+  const NamespaceConfig* default_namespace_config() const {
+    auto it = namespace_configs_map_.find("default");
+    return it == namespace_configs_map_.end() ? nullptr : it->second;
+  }
+
+  uint32_t target_sdk_version() const {
+    return target_sdk_version_;
+  }
+
+  // note that this is one time event and therefore there is no need to
+  // read every section of the config. Every linker instance needs at
+  // most one configuration.
+  // Returns false in case of an error. If binary config was not found
+  // sets *config = nullptr.
+  static bool read_binary_config(const char* ld_config_file_path,
+                                 const char* binary_realpath,
+                                 bool is_asan,
+                                 const Config** config,
+                                 std::string* error_msg);
+ private:
+  void clear();
+
+  void set_target_sdk_version(uint32_t target_sdk_version) {
+    target_sdk_version_ = target_sdk_version;
+  }
+
+  NamespaceConfig* create_namespace_config(const std::string& name);
+
+  std::vector<std::unique_ptr<NamespaceConfig>> namespace_configs_;
+  std::unordered_map<std::string, NamespaceConfig*> namespace_configs_map_;
+  uint32_t target_sdk_version_;
+
+  DISALLOW_COPY_AND_ASSIGN(Config);
+};
+
+#endif /* _LINKER_CONFIG_H_ */
diff --git a/linker/linker_globals.h b/linker/linker_globals.h
index b6f8a04..e4e3d97 100644
--- a/linker/linker_globals.h
+++ b/linker/linker_globals.h
@@ -38,7 +38,6 @@
     do { \
       __libc_format_buffer(linker_get_error_buffer(), linker_get_error_buffer_size(), fmt, ##x); \
       /* If LD_DEBUG is set high enough, log every dlerror(3) message. */ \
-      LD_LOG(kLogErrors, "%s\n", linker_get_error_buffer()); \
     } while (false)
 
 #define DL_WARN(fmt, x...) \
diff --git a/linker/linker_logger.h b/linker/linker_logger.h
index 502f872..f37b974 100644
--- a/linker/linker_logger.h
+++ b/linker/linker_logger.h
@@ -32,6 +32,7 @@
 #include <stdlib.h>
 #include <limits.h>
 #include "private/bionic_macros.h"
+#include "private/bionic_systrace.h"
 
 #define LD_LOG(type, x...) \
   { \
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index d037a18..40f82a1 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -209,6 +209,8 @@
  * and other non-local data at this point.
  */
 static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW(Addr) linker_base) {
+  ProtectedDataGuard guard;
+
 #if TIMING
   struct timeval t0, t1;
   gettimeofday(&t0, 0);
@@ -330,7 +332,7 @@
 
   somain = si;
 
-  init_default_namespace();
+  init_default_namespace(executable_path);
 
   if (!si->prelink_image()) {
     __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer());
@@ -381,19 +383,15 @@
     __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer());
   }
 
-  {
-    ProtectedDataGuard guard;
+  si->call_pre_init_constructors();
 
-    si->call_pre_init_constructors();
-
-    /* After the prelink_image, the si->load_bias is initialized.
-     * For so lib, the map->l_addr will be updated in notify_gdb_of_load.
-     * We need to update this value for so exe here. So Unwind_Backtrace
-     * for some arch like x86 could work correctly within so exe.
-     */
-    map->l_addr = si->load_bias;
-    si->call_constructors();
-  }
+  /* After the prelink_image, the si->load_bias is initialized.
+   * For so lib, the map->l_addr will be updated in notify_gdb_of_load.
+   * We need to update this value for so exe here. So Unwind_Backtrace
+   * for some arch like x86 could work correctly within so exe.
+   */
+  map->l_addr = si->load_bias;
+  si->call_constructors();
 
 #if TIMING
   gettimeofday(&t1, nullptr);
@@ -481,26 +479,20 @@
 extern "C" ElfW(Addr) __linker_init(void* raw_args) {
   KernelArgumentBlock args(raw_args);
 
-  ElfW(Addr) linker_addr = args.getauxval(AT_BASE);
+  // AT_BASE is set to 0 in the case when linker is run by iself
+  // so in order to link the linker it needs to calcuate AT_BASE
+  // using information at hand. The trick below takes advantage
+  // of the fact that the value of linktime_addr before relocations
+  // are run is an offset and this can be used to calculate AT_BASE.
+  static uintptr_t linktime_addr = reinterpret_cast<uintptr_t>(&linktime_addr);
+  ElfW(Addr) linker_addr = reinterpret_cast<uintptr_t>(&linktime_addr) - linktime_addr;
+
   ElfW(Addr) entry_point = args.getauxval(AT_ENTRY);
   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, 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
-  // already linked by PT_INTERP.
-  //
-  // 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_format_fd(STDOUT_FILENO,
-                     "This is %s, the helper program for dynamic executables.\n",
-                     args.argv[0]);
-    exit(0);
-  }
-
   linker_so.base = linker_addr;
   linker_so.size = phdr_table_get_load_size(phdr, elf_hdr->e_phnum);
   linker_so.load_bias = get_elf_exec_load_bias(elf_hdr);
@@ -547,6 +539,19 @@
   // Initialize the linker's own global variables
   linker_so.call_constructors();
 
+  // 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
+  // already linked by PT_INTERP.
+  //
+  // 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_format_fd(STDOUT_FILENO,
+                     "This is %s, the helper program for dynamic executables.\n",
+                     args.argv[0]);
+    exit(0);
+  }
+
   // Initialize static variables. Note that in order to
   // get correct libdl_info we need to call constructors
   // before get_libdl_info().
diff --git a/linker/linker_main.h b/linker/linker_main.h
index b68035b..8f3f07c 100644
--- a/linker/linker_main.h
+++ b/linker/linker_main.h
@@ -44,7 +44,7 @@
   static size_t ref_count_;
 };
 
-void init_default_namespace();
+void init_default_namespace(const char* executable_path);
 soinfo* soinfo_alloc(android_namespace_t* ns, const char* name,
                      struct stat* file_stat, off64_t file_offset,
                      uint32_t rtld_flags);
diff --git a/linker/linker_namespaces.h b/linker/linker_namespaces.h
index 868b4a6..e7d9b2e 100644
--- a/linker/linker_namespaces.h
+++ b/linker/linker_namespaces.h
@@ -80,6 +80,9 @@
   void set_default_library_paths(std::vector<std::string>&& library_paths) {
     default_library_paths_ = library_paths;
   }
+  void set_default_library_paths(const std::vector<std::string>& library_paths) {
+    default_library_paths_ = library_paths;
+  }
 
   const std::vector<std::string>& get_permitted_paths() const {
     return permitted_paths_;
@@ -87,6 +90,9 @@
   void set_permitted_paths(std::vector<std::string>&& permitted_paths) {
     permitted_paths_ = permitted_paths;
   }
+  void set_permitted_paths(const std::vector<std::string>& permitted_paths) {
+    permitted_paths_ = permitted_paths;
+  }
 
   const std::vector<android_namespace_link_t>& linked_namespaces() const {
     return linked_namespaces_;
diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp
index 6601dc1..1d59dbb 100644
--- a/linker/linker_soinfo.cpp
+++ b/linker/linker_soinfo.cpp
@@ -81,29 +81,9 @@
 
   std::string origin = dirname(get_realpath());
   // FIXME: add $LIB and $PLATFORM.
-  std::pair<std::string, std::string> substs[] = {{"ORIGIN", origin}};
+  std::vector<std::pair<std::string, std::string>> params = {{"ORIGIN", origin}};
   for (auto&& s : runpaths) {
-    size_t pos = 0;
-    while (pos < s.size()) {
-      pos = s.find("$", pos);
-      if (pos == std::string::npos) break;
-      for (const auto& subst : substs) {
-        const std::string& token = subst.first;
-        const std::string& replacement = subst.second;
-        if (s.substr(pos + 1, token.size()) == token) {
-          s.replace(pos, token.size() + 1, replacement);
-          // -1 to compensate for the ++pos below.
-          pos += replacement.size() - 1;
-          break;
-        } else if (s.substr(pos + 1, token.size() + 2) == "{" + token + "}") {
-          s.replace(pos, token.size() + 3, replacement);
-          pos += replacement.size() - 1;
-          break;
-        }
-      }
-      // Skip $ in case it did not match any of the known substitutions.
-      ++pos;
-    }
+    format_string(&s, params);
   }
 
   resolve_paths(runpaths, &dt_runpath_);
@@ -429,18 +409,25 @@
     si->call_constructors();
   });
 
-  TRACE("\"%s\": calling constructors", get_realpath());
+  if (!is_linker()) {
+    bionic_trace_begin((std::string("calling constructors: ") + get_realpath()).c_str());
+  }
 
   // DT_INIT should be called before DT_INIT_ARRAY if both are present.
   call_function("DT_INIT", init_func_, get_realpath());
   call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false, get_realpath());
+
+  if (!is_linker()) {
+    bionic_trace_end();
+  }
 }
 
 void soinfo::call_destructors() {
   if (!constructors_called) {
     return;
   }
-  TRACE("\"%s\": calling destructors", get_realpath());
+
+  ScopedTrace trace((std::string("calling destructors: ") + get_realpath()).c_str());
 
   // DT_FINI_ARRAY must be parsed in reverse order.
   call_array("DT_FINI_ARRAY", fini_array_, fini_array_count_, true, get_realpath());
diff --git a/linker/linker_utils.cpp b/linker/linker_utils.cpp
index 6df5f6d..5bf88e7 100644
--- a/linker/linker_utils.cpp
+++ b/linker/linker_utils.cpp
@@ -36,6 +36,30 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+void format_string(std::string* str, const std::vector<std::pair<std::string, std::string>>& params) {
+  size_t pos = 0;
+  while (pos < str->size()) {
+    pos = str->find("$", pos);
+    if (pos == std::string::npos) break;
+    for (const auto& param : params) {
+      const std::string& token = param.first;
+      const std::string& replacement = param.second;
+      if (str->substr(pos + 1, token.size()) == token) {
+        str->replace(pos, token.size() + 1, replacement);
+        // -1 to compensate for the ++pos below.
+        pos += replacement.size() - 1;
+        break;
+      } else if (str->substr(pos + 1, token.size() + 2) == "{" + token + "}") {
+        str->replace(pos, token.size() + 3, replacement);
+        pos += replacement.size() - 1;
+        break;
+      }
+    }
+    // Skip $ in case it did not match any of the known substitutions.
+    ++pos;
+  }
+}
+
 std::string dirname(const char* path) {
   const char* last_slash = strrchr(path, '/');
 
diff --git a/linker/linker_utils.h b/linker/linker_utils.h
index 5881688..e104a25 100644
--- a/linker/linker_utils.h
+++ b/linker/linker_utils.h
@@ -34,6 +34,8 @@
 
 extern const char* const kZipFileSeparator;
 
+void format_string(std::string* str, const std::vector<std::pair<std::string, std::string>>& params);
+
 bool file_is_in_dir(const std::string& file, const std::string& dir);
 bool file_is_under_dir(const std::string& file, const std::string& dir);
 bool normalize_path(const char* path, std::string* normalized_path);
@@ -44,7 +46,9 @@
 // 1. For regular path it converts it to realpath()
 // 2. For path in a zip file it uses realpath on the zipfile
 //    normalizes entry name by calling normalize_path function.
-void resolve_paths(std::vector<std::string>& paths, std::vector<std::string>* resolved_paths);
+void resolve_paths(std::vector<std::string>& paths,
+                   std::vector<std::string>* resolved_paths);
+
 void split_path(const char* path, const char* delimiters, std::vector<std::string>* paths);
 
 std::string dirname(const char* path);
diff --git a/linker/tests/Android.mk b/linker/tests/Android.mk
index f3810c1..61c43c9 100644
--- a/linker/tests/Android.mk
+++ b/linker/tests/Android.mk
@@ -40,6 +40,7 @@
 
 LOCAL_SRC_FILES := \
   linker_block_allocator_test.cpp \
+  linker_config_test.cpp \
   linker_globals.cpp \
   linked_list_test.cpp \
   linker_memory_allocator_test.cpp \
@@ -47,7 +48,8 @@
   linker_utils_test.cpp \
   ../linker_allocator.cpp \
   ../linker_block_allocator.cpp \
-  ../linker_utils.cpp
+  ../linker_config.cpp \
+  ../linker_utils.cpp \
 
 # for __libc_fatal
 LOCAL_SRC_FILES += ../../libc/bionic/libc_logging.cpp
diff --git a/linker/tests/linker_config_test.cpp b/linker/tests/linker_config_test.cpp
new file mode 100644
index 0000000..64ab00f
--- /dev/null
+++ b/linker/tests/linker_config_test.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include "../linker_config.h"
+
+#include <unistd.h>
+
+#include <android-base/stringprintf.h>
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+
+#include "private/ScopeGuard.h"
+
+
+static const char* config_str =
+  "# comment \n"
+  "dir.test = /data/local/tmp\n"
+  "\n"
+  "[test]\n"
+  "\n"
+  "enable.target.sdk.version = true\n"
+  "additional.namespaces=system\n"
+  "namespace.default.isolated = true\n"
+  "namespace.default.search.paths = /vendor/${LIB}\n"
+  "namespace.default.permitted.paths = /vendor/${LIB}\n"
+  "namespace.default.asan.search.paths = /data:/vendor/${LIB}\n"
+  "namespace.default.asan.permitted.paths = /data:/vendor\n"
+  "namespace.default.links = system\n"
+  "namespace.default.link.system.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so\n"
+  "namespace.system.isolated = true\n"
+  "namespace.system.search.paths = /system/${LIB}\n"
+  "namespace.system.permitted.paths = /system/${LIB}\n"
+  "namespace.system.asan.search.paths = /data:/system/${LIB}\n"
+  "namespace.system.asan.permitted.paths = /data:/system\n"
+  "\n";
+
+static bool write_version(const std::string& path, uint32_t version) {
+  std::string content = android::base::StringPrintf("%d", version);
+  return android::base::WriteStringToFile(content, path);
+}
+
+static void run_linker_config_smoke_test(bool is_asan) {
+#if defined(__LP64__)
+  const std::vector<std::string> kExpectedDefaultSearchPath = is_asan ?
+        std::vector<std::string>({ "/data", "/vendor/lib64"}) :
+        std::vector<std::string>({ "/vendor/lib64" });
+
+  const std::vector<std::string> kExpectedDefaultPermittedPath = is_asan ?
+        std::vector<std::string>({ "/data", "/vendor" }) :
+        std::vector<std::string>({ "/vendor/lib64" });
+
+  const std::vector<std::string> kExpectedSystemSearchPath = is_asan ?
+        std::vector<std::string>({ "/data", "/system/lib64" }) :
+        std::vector<std::string>({ "/system/lib64" });
+
+  const std::vector<std::string> kExpectedSystemPermittedPath = is_asan ?
+        std::vector<std::string>({ "/data", "/system" }) :
+        std::vector<std::string>({ "/system/lib64" });
+#else
+  const std::vector<std::string> kExpectedDefaultSearchPath = is_asan ?
+        std::vector<std::string>({ "/data", "/vendor/lib"}) :
+        std::vector<std::string>({ "/vendor/lib" });
+
+  const std::vector<std::string> kExpectedDefaultPermittedPath = is_asan ?
+        std::vector<std::string>({ "/data", "/vendor" }) :
+        std::vector<std::string>({ "/vendor/lib" });
+
+  const std::vector<std::string> kExpectedSystemSearchPath = is_asan ?
+        std::vector<std::string>({ "/data", "/system/lib" }) :
+        std::vector<std::string>({ "/system/lib" });
+
+  const std::vector<std::string> kExpectedSystemPermittedPath = is_asan ?
+        std::vector<std::string>({ "/data", "/system" }) :
+        std::vector<std::string>({ "/system/lib" });
+#endif
+
+  TemporaryFile tmp_file;
+  close(tmp_file.fd);
+  tmp_file.fd = -1;
+
+  android::base::WriteStringToFile(config_str, tmp_file.path);
+
+  TemporaryDir tmp_dir;
+
+  std::string executable_path = std::string(tmp_dir.path) + "/some-binary";
+  std::string version_file = std::string(tmp_dir.path) + "/.version";
+
+  auto file_guard = make_scope_guard([&version_file] {
+    unlink(version_file.c_str());
+  });
+
+  ASSERT_TRUE(write_version(version_file, 113U)) << strerror(errno);
+
+  // read config
+  const Config* config = nullptr;
+  std::string error_msg;
+  ASSERT_TRUE(Config::read_binary_config(tmp_file.path,
+                                         executable_path.c_str(),
+                                         is_asan,
+                                         &config,
+                                         &error_msg)) << error_msg;
+  ASSERT_TRUE(config != nullptr);
+  ASSERT_TRUE(error_msg.empty());
+
+  ASSERT_EQ(113U, config->target_sdk_version());
+
+  const NamespaceConfig* default_ns_config = config->default_namespace_config();
+  ASSERT_TRUE(default_ns_config != nullptr);
+
+  ASSERT_TRUE(default_ns_config->isolated());
+  ASSERT_EQ(kExpectedDefaultSearchPath, default_ns_config->search_paths());
+  ASSERT_EQ(kExpectedDefaultPermittedPath, default_ns_config->permitted_paths());
+
+  const auto& default_ns_links = default_ns_config->links();
+  ASSERT_EQ(1U, default_ns_links.size());
+  ASSERT_EQ("system", default_ns_links[0].ns_name());
+  ASSERT_EQ("libc.so:libm.so:libdl.so:libstdc++.so", default_ns_links[0].shared_libs());
+
+  auto& ns_configs = config->namespace_configs();
+  ASSERT_EQ(2U, ns_configs.size());
+
+  // find second namespace
+  const NamespaceConfig* ns_system = nullptr;
+  for (auto& ns : ns_configs) {
+    std::string ns_name = ns->name();
+    ASSERT_TRUE(ns_name == "system" || ns_name == "default")
+        << "unexpected ns name: " << ns->name();
+
+    if (ns_name == "system") {
+      ns_system = ns.get();
+    }
+  }
+
+  ASSERT_TRUE(ns_system != nullptr) << "system namespace was not found";
+
+  ASSERT_TRUE(ns_system->isolated());
+  ASSERT_EQ(kExpectedSystemSearchPath, ns_system->search_paths());
+  ASSERT_EQ(kExpectedSystemPermittedPath, ns_system->permitted_paths());
+}
+
+TEST(linker_config, smoke) {
+  run_linker_config_smoke_test(false);
+}
+
+TEST(linker_config, asan_smoke) {
+  run_linker_config_smoke_test(true);
+}
diff --git a/linker/tests/linker_utils_test.cpp b/linker/tests/linker_utils_test.cpp
index 0cfdf40..dce223a 100644
--- a/linker/tests/linker_utils_test.cpp
+++ b/linker/tests/linker_utils_test.cpp
@@ -34,6 +34,13 @@
 
 #include "../linker_utils.h"
 
+TEST(linker_utils, format_string) {
+  std::vector<std::pair<std::string, std::string>> params = {{ "LIB", "lib32"}, { "SDKVER", "42"}};
+  std::string str_smoke = "LIB$LIB${LIB${SDKVER}SDKVER$TEST$";
+  format_string(&str_smoke, params);
+  ASSERT_EQ("LIBlib32${LIB42SDKVER$TEST$", str_smoke);
+}
+
 TEST(linker_utils, normalize_path_smoke) {
   std::string output;
   ASSERT_TRUE(normalize_path("/../root///dir/.///dir2/somedir/../zipfile!/dir/dir9//..///afile", &output));
diff --git a/tests/Android.bp b/tests/Android.bp
index 1cca332..2bdbbbc 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -70,6 +70,7 @@
         "ifaddrs_test.cpp",
         "inttypes_test.cpp",
         "langinfo_test.cpp",
+        "leak_test.cpp",
         "libc_logging_test.cpp",
         "libgen_basename_test.cpp",
         "libgen_test.cpp",
diff --git a/tests/dl_test.cpp b/tests/dl_test.cpp
index ee9b2e1..aa8bd57 100644
--- a/tests/dl_test.cpp
+++ b/tests/dl_test.cpp
@@ -24,6 +24,7 @@
 
 #include <string>
 
+#include "gtest_globals.h"
 #include "utils.h"
 
 extern "C" int main_global_default_serial() {
@@ -86,4 +87,26 @@
 #endif
 }
 
+TEST(dl, preinit_system_calls) {
+#if defined(__BIONIC__)
+  std::string helper = get_testlib_root() +
+      "/preinit_syscall_test_helper/preinit_syscall_test_helper";
+  chmod(helper.c_str(), 0755); // TODO: "x" lost in CTS, b/34945607
+  ExecTestHelper eth;
+  eth.SetArgs({ helper.c_str(), nullptr });
+  eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, nullptr);
+#endif
+}
+
+TEST(dl, xfail_preinit_getauxval) {
+#if defined(__BIONIC__)
+  std::string helper = get_testlib_root() +
+      "/preinit_getauxval_test_helper/preinit_getauxval_test_helper";
+  chmod(helper.c_str(), 0755); // TODO: "x" lost in CTS, b/34945607
+  ExecTestHelper eth;
+  eth.SetArgs({ helper.c_str(), nullptr });
+  eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, nullptr);
+#endif
+}
+
 // TODO: Add tests for LD_PRELOADs
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 808b708..a0226a6 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -672,8 +672,59 @@
   ASSERT_TRUE(handle != nullptr) << dlerror();
   dlclose(handle);
 
+  // dlopen for a public library using an absolute path should work
+  // 1. For isolated namespaces
   android_dlextinfo extinfo;
   extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+  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);
+
+  // 1.1 even if it wasn't loaded before
+  dlclose(handle_public);
+
+  handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW | RTLD_NOLOAD);
+  ASSERT_TRUE(handle_public == nullptr);
+  ASSERT_EQ(std::string("dlopen failed: library \"") + lib_public_path +
+               "\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror());
+
+  handle = android_dlopen_ext(lib_public_path.c_str(), RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle != nullptr) << dlerror();
+
+  handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle == handle_public);
+
+  dlclose(handle);
+
+  // 2. And for regular namespaces (make sure it does not load second copy of the library)
+  extinfo.library_namespace = ns1;
+  handle = android_dlopen_ext(lib_public_path.c_str(), RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle != nullptr) << dlerror();
+  ASSERT_TRUE(handle == handle_public);
+
+  dlclose(handle);
+
+  // 2.1 Unless it was not loaded before - in which case it will load a duplicate.
+  // TODO(dimitry): This is broken. Maybe we need to deprecate non-isolated namespaces?
+  dlclose(handle_public);
+
+  handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW | RTLD_NOLOAD);
+  ASSERT_TRUE(handle_public == nullptr);
+  ASSERT_EQ(std::string("dlopen failed: library \"") + lib_public_path +
+               "\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror());
+
+  handle = android_dlopen_ext(lib_public_path.c_str(), RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle != nullptr) << dlerror();
+
+  handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
+
+  ASSERT_TRUE(handle != handle_public);
+
+  dlclose(handle);
+
   extinfo.library_namespace = ns1;
 
   void* handle1 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
@@ -685,14 +736,6 @@
 
   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"));
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 0f24170..ad8444e 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -1256,7 +1256,7 @@
 // Bionic specific tests
 #if defined(__BIONIC__)
 
-#if defined(__arm__) || defined(__i386__)
+#if defined(__arm__)
 const llvm::ELF::Elf32_Dyn* to_dynamic_table(const char* p) {
   return reinterpret_cast<const llvm::ELF::Elf32_Dyn*>(p);
 }
@@ -1320,7 +1320,7 @@
   validate_compatibility_of_native_library(path, elf);
 }
 
-// This is a test for app compatibility workaround for arm and x86 apps
+// This is a test for app compatibility workaround for arm apps
 // affected by http://b/24465209
 TEST(dlext, compat_elf_hash_and_relocation_tables) {
   validate_compatibility_of_native_library("libc.so");
@@ -1332,7 +1332,7 @@
   validate_compatibility_of_native_library("libjnigraphics.so");
 }
 
-#endif //  defined(__arm__) || defined(__i386__)
+#endif //  defined(__arm__)
 
 TEST(dlfcn, dt_runpath_absolute_path) {
   std::string libpath = get_testlib_root() + "/libtest_dt_runpath_d.so";
diff --git a/tests/gtest_globals.cpp b/tests/gtest_globals.cpp
index 75c08b1..538a534 100644
--- a/tests/gtest_globals.cpp
+++ b/tests/gtest_globals.cpp
@@ -19,17 +19,19 @@
 #include <gtest/gtest.h>
 #include "utils.h"
 
+#include <android-base/file.h>
+
 #include <string>
 
 static std::string init_testlib_root() {
   // Calculate ANDROID_DATA assuming the binary is in "$ANDROID_DATA/somedir/binary-dir/binary"
   std::string path = get_executable_path();
 
-  path = get_dirname(path.c_str());
+  path = android::base::Dirname(path);
   path += "/..";
 
   std::string out_path;
-  if (!get_realpath(path.c_str(), &out_path)) {
+  if (!android::base::Realpath(path.c_str(), &out_path)) {
     printf("Failed to get realpath for \"%s\"", path.c_str());
     abort();
   }
@@ -37,7 +39,7 @@
   out_path += "/bionic-loader-test-libs";
 
   std::string real_path;
-  if (!get_realpath(out_path, &real_path)) {
+  if (!android::base::Realpath(out_path, &real_path)) {
     printf("\"%s\": does not exists", out_path.c_str());
     abort();
   }
diff --git a/tests/gtest_main.cpp b/tests/gtest_main.cpp
index b9ee585..5f28321 100644
--- a/tests/gtest_main.cpp
+++ b/tests/gtest_main.cpp
@@ -56,25 +56,6 @@
   return g_executable_path;
 }
 
-bool get_realpath(const std::string& path, std::string* real_path) {
-  char realpath_buf[PATH_MAX];
-  if (realpath(path.c_str(), realpath_buf) != realpath_buf) {
-    return false;
-  }
-
-  *real_path = realpath_buf;
-  return true;
-}
-
-std::string get_dirname(const char* path) {
-#if defined(__BIONIC__)
-  return dirname(path);
-#else
-  // GLIBC does not have const char* dirname
-  return dirname(const_cast<char*>(path));
-#endif
-}
-
 int get_argc() {
   return g_argc;
 }
@@ -171,7 +152,7 @@
 
   TestResult GetResult() const { return result_; }
   TestResult GetExpectedResult() const {
-    return GetName().find("xfail_") == 0 ? TEST_FAILED : TEST_SUCCESS;
+    return GetName().find("xfail") == 0 ? TEST_FAILED : TEST_SUCCESS;
   }
 
   void SetTestTime(int64_t elapsed_time_ns) { elapsed_time_ns_ = elapsed_time_ns; }
diff --git a/tests/leak_test.cpp b/tests/leak_test.cpp
new file mode 100644
index 0000000..9ddb2ff
--- /dev/null
+++ b/tests/leak_test.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <err.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/user.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <thread>
+#include <vector>
+
+#include <android-base/macros.h>
+
+#include "utils.h"
+
+using namespace std::chrono_literals;
+
+static size_t GetMappingSize() {
+  std::vector<map_record> maps;
+  if (!Maps::parse_maps(&maps)) {
+    err(1, "failed to parse maps");
+  }
+
+  size_t result = 0;
+  for (const map_record& map : maps) {
+    result += map.addr_end - map.addr_start;
+  }
+
+  return result;
+}
+
+#define LEAK_TEST(test_case_name, test_name)                                                 \
+  static void __leak_test__##test_case_name##__##test_name();                                \
+  TEST(test_case_name, test_name) {                                                          \
+    auto previous_size = GetMappingSize();                                                   \
+    __leak_test__##test_case_name##__##test_name();                                          \
+    auto current_size = GetMappingSize();                                                    \
+    if (current_size > previous_size) {                                                      \
+      FAIL() << "increase in process map size: " << previous_size << " -> " << current_size; \
+    }                                                                                        \
+  }                                                                                          \
+  static void __leak_test__##test_case_name##__##test_name()
+
+LEAK_TEST(leak, smoke) {
+  // Do nothing.
+}
+
+LEAK_TEST(leak, xfail) {
+  UNUSED(mmap(nullptr, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+}
+
+// http://b/36045112
+LEAK_TEST(pthread_leak, join) {
+  for (int i = 0; i < 100; ++i) {
+    pthread_t thread;
+    ASSERT_EQ(0, pthread_create(&thread, nullptr, [](void*) -> void* { return nullptr; }, nullptr));
+    ASSERT_EQ(0, pthread_join(thread, nullptr));
+  }
+}
+
+// http://b/36045112
+LEAK_TEST(pthread_leak, detach) {
+  pthread_barrier_t barrier;
+  constexpr int thread_count = 100;
+  ASSERT_EQ(0, pthread_barrier_init(&barrier, nullptr, thread_count + 1));
+  for (int i = 0; i < thread_count; ++i) {
+    pthread_t thread;
+    const auto thread_function = +[](void* barrier) -> void* {
+      pthread_barrier_wait(static_cast<pthread_barrier_t*>(barrier));
+      return nullptr;
+    };
+    ASSERT_EQ(0, pthread_create(&thread, nullptr, thread_function, &barrier));
+    ASSERT_EQ(0, pthread_detach(thread));
+  }
+
+  pthread_barrier_wait(&barrier);
+
+  // Give the threads some time to exit.
+  std::this_thread::sleep_for(100ms);
+}
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index 5eb16c5..a031fe9 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -547,3 +547,17 @@
     shared_libs: ["libcfi-test"],
     ldflags: ["-Wl,--rpath,${ORIGIN}/.."],
 }
+
+cc_test {
+    name: "preinit_getauxval_test_helper",
+    host_supported: false,
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["preinit_getauxval_test_helper.cpp"],
+}
+
+cc_test {
+    name: "preinit_syscall_test_helper",
+    host_supported: false,
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["preinit_syscall_test_helper.cpp"],
+}
diff --git a/tests/libs/libs_utils.h b/tests/libs/libs_utils.h
index f11cbe7..7dae241 100644
--- a/tests/libs/libs_utils.h
+++ b/tests/libs/libs_utils.h
@@ -17,13 +17,11 @@
 #ifndef LIBS_UTILS_H
 #define LIBS_UTILS_H
 
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 
-#define CHECK(x)                                                            \
-  do {                                                                      \
-    fprintf(stderr, "CHECK(" #x ") failed at %s:%d\n", __FILE__, __LINE__); \
-    if (!(x)) abort();                                                      \
-  } while (0)
+#define CHECK(e) \
+    ((e) ? static_cast<void>(0) : __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, #e))
 
 #endif  // LIBS_UTILS_H
diff --git a/tests/libs/preinit_getauxval_test_helper.cpp b/tests/libs/preinit_getauxval_test_helper.cpp
new file mode 100644
index 0000000..2a79b97
--- /dev/null
+++ b/tests/libs/preinit_getauxval_test_helper.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/auxv.h>
+
+#include "libs_utils.h"
+
+static unsigned long g_AT_RANDOM;
+static unsigned long g_AT_PAGESZ;
+
+static void preinit_ctor() {
+  g_AT_RANDOM = getauxval(AT_RANDOM);
+  g_AT_PAGESZ = getauxval(AT_PAGESZ);
+}
+
+__attribute__((section(".preinit_array"), used)) void (*preinit_ctor_p)(void) = preinit_ctor;
+
+int main() {
+  // Did getauxval during preinit get the same results as getauxval now?
+  CHECK(getauxval(AT_RANDOM) == g_AT_RANDOM);
+  CHECK(getauxval(AT_PAGESZ) == g_AT_PAGESZ);
+  return 0;
+}
diff --git a/tests/libs/preinit_syscall_test_helper.cpp b/tests/libs/preinit_syscall_test_helper.cpp
new file mode 100644
index 0000000..9b6b6df
--- /dev/null
+++ b/tests/libs/preinit_syscall_test_helper.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/auxv.h>
+
+#include "libs_utils.h"
+
+static ssize_t g_result;
+static int g_errno;
+
+static void preinit_ctor() {
+  // Can we make a system call?
+  g_result = write(-1, "", 1);
+  g_errno = errno;
+}
+
+__attribute__((section(".preinit_array"), used)) void (*preinit_ctor_p)(void) = preinit_ctor;
+
+int main() {
+  // Did we get the expected failure?
+  CHECK(g_result == -1);
+  CHECK(g_errno == EBADF);
+  return 0;
+}
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index 4fb15ad..60fe294 100755
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -1886,25 +1886,34 @@
 
 static volatile bool signal_handler_on_altstack_done;
 
+__attribute__((__noinline__))
+static void signal_handler_backtrace() {
+  // Check if we have enough stack space for unwinding.
+  int count = 0;
+  _Unwind_Backtrace(FrameCounter, &count);
+  ASSERT_GT(count, 0);
+}
+
+__attribute__((__noinline__))
+static void signal_handler_logging() {
+  // Check if we have enough stack space for logging.
+  std::string s(2048, '*');
+  GTEST_LOG_(INFO) << s;
+  signal_handler_on_altstack_done = true;
+}
+
+__attribute__((__noinline__))
+static void signal_handler_snprintf() {
+  // Check if we have enough stack space for snprintf to a PATH_MAX buffer, plus some extra.
+  char buf[PATH_MAX + 2048];
+  ASSERT_GT(snprintf(buf, sizeof(buf), "/proc/%d/status", getpid()), 0);
+}
+
 static void SignalHandlerOnAltStack(int signo, siginfo_t*, void*) {
   ASSERT_EQ(SIGUSR1, signo);
-  {
-    // Check if we have enough stack space for unwinding.
-    int count = 0;
-    _Unwind_Backtrace(FrameCounter, &count);
-    ASSERT_GT(count, 0);
-  }
-  {
-    // Check if we have enough stack space for logging.
-    std::string s(2048, '*');
-    GTEST_LOG_(INFO) << s;
-    signal_handler_on_altstack_done = true;
-  }
-  {
-    // Check if we have enough stack space for snprintf to a PATH_MAX buffer, plus some extra.
-    char buf[PATH_MAX + 2048];
-    ASSERT_GT(snprintf(buf, sizeof(buf), "/proc/%d/status", getpid()), 0);
-  }
+  signal_handler_backtrace();
+  signal_handler_logging();
+  signal_handler_snprintf();
 }
 
 TEST(pthread, big_enough_signal_stack) {
diff --git a/tests/stack_unwinding_test.cpp b/tests/stack_unwinding_test.cpp
index afd9e7f..bb58ae4 100644
--- a/tests/stack_unwinding_test.cpp
+++ b/tests/stack_unwinding_test.cpp
@@ -90,15 +90,20 @@
 }
 
 static void verify_unwind_data(const UnwindData& unwind_data) {
-  EXPECT_GT(unwind_data.handler_frame_count, unwind_data.expected_frame_count);
+  // In order to avoid a false positive, the caller must have at least 2 frames
+  // outside of the signal handler. This avoids a case where the only frame
+  // right after the signal handler winds up being garbage.
+  EXPECT_GT(unwind_data.handler_frame_count, unwind_data.expected_frame_count + 1);
+
   EXPECT_EQ(unwind_data.handler_frame_count + 1, unwind_data.handler_one_deeper_frame_count);
 }
 
-TEST(stack_unwinding, unwind_through_signal_frame) {
+static void noinline UnwindTest() {
   g_unwind_data = {};
-  ScopedSignalHandler ssh(SIGUSR1, UnwindSignalHandler);
 
   _Unwind_Backtrace(FrameCounter, &g_unwind_data.expected_frame_count);
+  ASSERT_LE(2, g_unwind_data.expected_frame_count)
+      << "The current call must contain at least 2 frames for the test to be valid.";
 
   ASSERT_EQ(0, kill(getpid(), SIGUSR1));
   while (!g_unwind_data.signal_handler_complete) {}
@@ -106,14 +111,15 @@
   verify_unwind_data(g_unwind_data);
 }
 
+TEST(stack_unwinding, unwind_through_signal_frame) {
+  ScopedSignalHandler ssh(SIGUSR1, UnwindSignalHandler);
+
+  UnwindTest();
+}
+
 // On LP32, the SA_SIGINFO flag gets you __restore_rt instead of __restore.
 TEST(stack_unwinding, unwind_through_signal_frame_SA_SIGINFO) {
-  g_unwind_data = {};
   ScopedSignalHandler ssh(SIGUSR1, UnwindSignalHandler, SA_SIGINFO);
 
-  _Unwind_Backtrace(FrameCounter, &g_unwind_data.expected_frame_count);
-  ASSERT_EQ(0, kill(getpid(), SIGUSR1));
-  while (!g_unwind_data.signal_handler_complete) {}
-
-  verify_unwind_data(g_unwind_data);
+  UnwindTest();
 }
diff --git a/tests/sys_ptrace_test.cpp b/tests/sys_ptrace_test.cpp
index bce5898..69638be 100644
--- a/tests/sys_ptrace_test.cpp
+++ b/tests/sys_ptrace_test.cpp
@@ -17,6 +17,7 @@
 #include <sys/ptrace.h>
 
 #include <elf.h>
+#include <err.h>
 #include <fcntl.h>
 #include <sched.h>
 #include <sys/prctl.h>
@@ -26,11 +27,16 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <chrono>
+#include <thread>
+
 #include <gtest/gtest.h>
 
 #include <android-base/macros.h>
 #include <android-base/unique_fd.h>
 
+using namespace std::chrono_literals;
+
 using android::base::unique_fd;
 
 // Host libc does not define this.
@@ -367,31 +373,39 @@
 
 class PtraceResumptionTest : public ::testing::Test {
  public:
+  unique_fd worker_pipe_write;
+
   pid_t worker = -1;
+  pid_t tracer = -1;
+
   PtraceResumptionTest() {
+    unique_fd worker_pipe_read;
+    int pipefd[2];
+    if (pipe2(pipefd, O_CLOEXEC) != 0) {
+      err(1, "failed to create pipe");
+    }
+
+    worker_pipe_read.reset(pipefd[0]);
+    worker_pipe_write.reset(pipefd[1]);
+
+    worker = fork();
+    if (worker == -1) {
+      err(1, "failed to fork worker");
+    } else if (worker == 0) {
+      char buf;
+      worker_pipe_write.reset();
+      TEMP_FAILURE_RETRY(read(worker_pipe_read.get(), &buf, sizeof(buf)));
+      exit(0);
+    }
   }
 
   ~PtraceResumptionTest() {
   }
 
   void AssertDeath(int signo);
-  void Start(std::function<void()> f) {
-    unique_fd worker_pipe_read, worker_pipe_write;
-    int pipefd[2];
-    ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
-    worker_pipe_read.reset(pipefd[0]);
-    worker_pipe_write.reset(pipefd[1]);
 
-    worker = fork();
-    ASSERT_NE(-1, worker);
-    if (worker == 0) {
-      char buf;
-      worker_pipe_write.reset();
-      TEMP_FAILURE_RETRY(read(worker_pipe_read.get(), &buf, sizeof(buf)));
-      exit(0);
-    }
-
-    pid_t tracer = fork();
+  void StartTracer(std::function<void()> f) {
+    tracer = fork();
     ASSERT_NE(-1, tracer);
     if (tracer == 0) {
       f();
@@ -400,26 +414,66 @@
       }
       exit(0);
     }
+  }
+
+  bool WaitForTracer() {
+    if (tracer == -1) {
+      errx(1, "tracer not started");
+    }
 
     int result;
     pid_t rc = waitpid(tracer, &result, 0);
-    ASSERT_EQ(tracer, rc);
-    EXPECT_TRUE(WIFEXITED(result) || WIFSIGNALED(result));
+    if (rc != tracer) {
+      printf("waitpid returned %d (%s)\n", rc, strerror(errno));
+      return false;
+    }
+
+    if (!WIFEXITED(result) && !WIFSIGNALED(result)) {
+      printf("!WIFEXITED && !WIFSIGNALED\n");
+      return false;
+    }
+
     if (WIFEXITED(result)) {
       if (WEXITSTATUS(result) != 0) {
-        FAIL() << "tracer failed";
+        printf("tracer failed\n");
+        return false;
       }
     }
 
-    rc = waitpid(worker, &result, WNOHANG);
-    ASSERT_EQ(0, rc);
+    return true;
+  }
+
+  bool WaitForWorker() {
+    if (worker == -1) {
+      errx(1, "worker not started");
+    }
+
+    int result;
+    pid_t rc = waitpid(worker, &result, WNOHANG);
+    if (rc != 0) {
+      printf("worker exited prematurely\n");
+      return false;
+    }
 
     worker_pipe_write.reset();
 
     rc = waitpid(worker, &result, 0);
-    ASSERT_EQ(worker, rc);
-    EXPECT_TRUE(WIFEXITED(result));
-    EXPECT_EQ(WEXITSTATUS(result), 0);
+    if (rc != worker) {
+      printf("waitpid for worker returned %d (%s)\n", rc, strerror(errno));
+      return false;
+    }
+
+    if (!WIFEXITED(result)) {
+      printf("worker didn't exit\n");
+      return false;
+    }
+
+    if (WEXITSTATUS(result) != 0) {
+      printf("worker exited with status %d\n", WEXITSTATUS(result));
+      return false;
+    }
+
+    return true;
   }
 };
 
@@ -436,22 +490,74 @@
   }
 }
 
+TEST_F(PtraceResumptionTest, smoke) {
+  // Make sure that the worker doesn't exit before the tracer stops tracing.
+  StartTracer([this]() {
+    ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
+    ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
+    wait_for_ptrace_stop(worker);
+    std::this_thread::sleep_for(500ms);
+  });
+
+  worker_pipe_write.reset();
+  std::this_thread::sleep_for(250ms);
+
+  int result;
+  ASSERT_EQ(0, waitpid(worker, &result, WNOHANG));
+  ASSERT_TRUE(WaitForTracer());
+  ASSERT_EQ(worker, waitpid(worker, &result, 0));
+}
+
 TEST_F(PtraceResumptionTest, seize) {
-  Start([this]() { ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno); });
+  StartTracer([this]() { ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno); });
+  ASSERT_TRUE(WaitForTracer());
+  ASSERT_TRUE(WaitForWorker());
 }
 
 TEST_F(PtraceResumptionTest, seize_interrupt) {
-  Start([this]() {
+  StartTracer([this]() {
     ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
     ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
+    wait_for_ptrace_stop(worker);
   });
+  ASSERT_TRUE(WaitForTracer());
+  ASSERT_TRUE(WaitForWorker());
 }
 
 TEST_F(PtraceResumptionTest, seize_interrupt_cont) {
-  Start([this]() {
+  StartTracer([this]() {
     ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
     ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
     wait_for_ptrace_stop(worker);
     ASSERT_EQ(0, ptrace(PTRACE_CONT, worker, 0, 0)) << strerror(errno);
   });
+  ASSERT_TRUE(WaitForTracer());
+  ASSERT_TRUE(WaitForWorker());
+}
+
+TEST_F(PtraceResumptionTest, zombie_seize) {
+  StartTracer([this]() { ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno); });
+  ASSERT_TRUE(WaitForWorker());
+  ASSERT_TRUE(WaitForTracer());
+}
+
+TEST_F(PtraceResumptionTest, zombie_seize_interrupt) {
+  StartTracer([this]() {
+    ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
+    ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
+    wait_for_ptrace_stop(worker);
+  });
+  ASSERT_TRUE(WaitForWorker());
+  ASSERT_TRUE(WaitForTracer());
+}
+
+TEST_F(PtraceResumptionTest, zombie_seize_interrupt_cont) {
+  StartTracer([this]() {
+    ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
+    ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
+    wait_for_ptrace_stop(worker);
+    ASSERT_EQ(0, ptrace(PTRACE_CONT, worker, 0, 0)) << strerror(errno);
+  });
+  ASSERT_TRUE(WaitForWorker());
+  ASSERT_TRUE(WaitForTracer());
 }
diff --git a/tests/utils.h b/tests/utils.h
index 31974d0..fa85545 100644
--- a/tests/utils.h
+++ b/tests/utils.h
@@ -143,11 +143,6 @@
 // The absolute path to the executable
 const std::string& get_executable_path();
 
-// Get realpath
-bool get_realpath(const std::string& path, std::string* realpath);
-// Get dirname
-std::string get_dirname(const char* path);
-
 // Access to argc/argv/envp
 int get_argc();
 char** get_argv();
diff --git a/tools/update_headers.sh b/tools/update_headers.sh
deleted file mode 100755
index 0095d50..0000000
--- a/tools/update_headers.sh
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/bin/bash
-DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-cd $DIR
-
-which versioner >/dev/null 2>&1
-if [ $? -ne 0 ]; then
-  >&2 echo "versioner not in path; run mma in $DIR/versioner"
-  exit 1
-fi
-
-VERSION=$(git rev-parse --short HEAD)
-git diff-index --quiet HEAD
-DIRTY=$?
-git branch -r --contains HEAD | grep -q aosp/master
-SUBMITTED=$?
-
-if [ $DIRTY -ne 0 ]; then
-  >&2 echo "Warning: bionic has uncommitted changes"
-  VERSION="${VERSION}-dirty"
-elif [ $SUBMITTED -ne 0 ]; then
-  >&2 echo "Warning: current HEAD does not exist in aosp/master"
-  VERSION="${VERSION}-unsubmitted"
-fi
-
-PREBUILTS_DIR=$ANDROID_BUILD_TOP/prebuilts/ndk
-BRANCH_NAME=$(git -C $PREBUILTS_DIR symbolic-ref --short -q HEAD)
-if [ $? -ne 0 ]; then
-  BRANCH_NAME=update-bionic-headers-$VERSION
-  echo "prebuilts/ndk has detached head; creating branch $BRANCH_NAME"
-  repo start $BRANCH_NAME $PREBUILTS_DIR
-else
-  echo "prebuilts/ndk already on branch $BRANCH_NAME"
-fi
-
-HEADERS_INSTALL=$PREBUILTS_DIR/headers
-if [ -d "$HEADERS_INSTALL" ]; then
-  git -C $PREBUILTS_DIR rm -r --ignore-unmatch $HEADERS_INSTALL
-  if [ -d $HEADERS_INSTALL ]; then
-    rm -r $HEADERS_INSTALL
-  fi
-fi
-
-versioner -p versioner/platforms versioner/current versioner/dependencies \
-  -o $HEADERS_INSTALL
-if [ $? -ne 0 ]; then
-  >&2 echo "Header preprocessing failed"
-  exit 1
-fi
-
-cp ../libc/NOTICE $PREBUILTS_DIR
-
-git -C $PREBUILTS_DIR add $HEADERS_INSTALL $PREBUILTS_DIR/NOTICE
-git -C $PREBUILTS_DIR commit -m "Update bionic headers to $VERSION."
diff --git a/tools/versioner/dependencies/arm/arch-arm b/tools/versioner/dependencies/arm/arch-arm
deleted file mode 120000
index ed69f41..0000000
--- a/tools/versioner/dependencies/arm/arch-arm
+++ /dev/null
@@ -1 +0,0 @@
-../../../../libc/arch-arm/include
\ No newline at end of file
diff --git a/tools/versioner/dependencies/arm/kernel_uapi_asm-arm b/tools/versioner/dependencies/arm/kernel_uapi_asm-arm
deleted file mode 120000
index fabed85..0000000
--- a/tools/versioner/dependencies/arm/kernel_uapi_asm-arm
+++ /dev/null
@@ -1 +0,0 @@
-../../../../libc/kernel/uapi/asm-arm
\ No newline at end of file
diff --git a/tools/versioner/dependencies/arm64/arch-arm64 b/tools/versioner/dependencies/arm64/arch-arm64
deleted file mode 120000
index 21a21d4..0000000
--- a/tools/versioner/dependencies/arm64/arch-arm64
+++ /dev/null
@@ -1 +0,0 @@
-../../../../libc/arch-arm64/include
\ No newline at end of file
diff --git a/tools/versioner/dependencies/arm64/kernel_uapi_asm-arm64 b/tools/versioner/dependencies/arm64/kernel_uapi_asm-arm64
deleted file mode 120000
index 16e74a3..0000000
--- a/tools/versioner/dependencies/arm64/kernel_uapi_asm-arm64
+++ /dev/null
@@ -1 +0,0 @@
-../../../../libc/kernel/uapi/asm-arm64
\ No newline at end of file
diff --git a/tools/versioner/dependencies/common/clang-builtins b/tools/versioner/dependencies/common/clang-builtins
deleted file mode 120000
index fc27e65..0000000
--- a/tools/versioner/dependencies/common/clang-builtins
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../external/clang/lib/Headers
\ No newline at end of file
diff --git a/tools/versioner/dependencies/common/kernel_android_uapi b/tools/versioner/dependencies/common/kernel_android_uapi
deleted file mode 120000
index bcf6daa..0000000
--- a/tools/versioner/dependencies/common/kernel_android_uapi
+++ /dev/null
@@ -1 +0,0 @@
-../../../../libc/kernel/android/uapi
\ No newline at end of file
diff --git a/tools/versioner/dependencies/common/kernel_uapi b/tools/versioner/dependencies/common/kernel_uapi
deleted file mode 120000
index 2a915ef..0000000
--- a/tools/versioner/dependencies/common/kernel_uapi
+++ /dev/null
@@ -1 +0,0 @@
-../../../../libc/kernel/uapi
\ No newline at end of file
diff --git a/tools/versioner/dependencies/mips/arch-mips b/tools/versioner/dependencies/mips/arch-mips
deleted file mode 120000
index 7f2f104..0000000
--- a/tools/versioner/dependencies/mips/arch-mips
+++ /dev/null
@@ -1 +0,0 @@
-../../../../libc/arch-mips/include
\ No newline at end of file
diff --git a/tools/versioner/dependencies/mips/kernel_uapi_asm-mips b/tools/versioner/dependencies/mips/kernel_uapi_asm-mips
deleted file mode 120000
index dcd1955..0000000
--- a/tools/versioner/dependencies/mips/kernel_uapi_asm-mips
+++ /dev/null
@@ -1 +0,0 @@
-../../../../libc/kernel/uapi/asm-mips
\ No newline at end of file
diff --git a/tools/versioner/dependencies/mips64/arch-mips64 b/tools/versioner/dependencies/mips64/arch-mips64
deleted file mode 120000
index 48cea72..0000000
--- a/tools/versioner/dependencies/mips64/arch-mips64
+++ /dev/null
@@ -1 +0,0 @@
-../../../../libc/arch-mips64/include
\ No newline at end of file
diff --git a/tools/versioner/dependencies/mips64/kernel_uapi_asm-mips b/tools/versioner/dependencies/mips64/kernel_uapi_asm-mips
deleted file mode 120000
index dcd1955..0000000
--- a/tools/versioner/dependencies/mips64/kernel_uapi_asm-mips
+++ /dev/null
@@ -1 +0,0 @@
-../../../../libc/kernel/uapi/asm-mips
\ No newline at end of file
diff --git a/tools/versioner/dependencies/x86/arch-x86 b/tools/versioner/dependencies/x86/arch-x86
deleted file mode 120000
index d0f016a..0000000
--- a/tools/versioner/dependencies/x86/arch-x86
+++ /dev/null
@@ -1 +0,0 @@
-../../../../libc/arch-x86/include
\ No newline at end of file
diff --git a/tools/versioner/dependencies/x86/kernel_uapi_asm-x86 b/tools/versioner/dependencies/x86/kernel_uapi_asm-x86
deleted file mode 120000
index 0efae62..0000000
--- a/tools/versioner/dependencies/x86/kernel_uapi_asm-x86
+++ /dev/null
@@ -1 +0,0 @@
-../../../../libc/kernel/uapi/asm-x86
\ No newline at end of file
diff --git a/tools/versioner/dependencies/x86_64/arch-x86_64 b/tools/versioner/dependencies/x86_64/arch-x86_64
deleted file mode 120000
index deb647d..0000000
--- a/tools/versioner/dependencies/x86_64/arch-x86_64
+++ /dev/null
@@ -1 +0,0 @@
-../../../../libc/arch-x86_64/include
\ No newline at end of file
diff --git a/tools/versioner/dependencies/x86_64/kernel_uapi_asm-x86 b/tools/versioner/dependencies/x86_64/kernel_uapi_asm-x86
deleted file mode 120000
index 0efae62..0000000
--- a/tools/versioner/dependencies/x86_64/kernel_uapi_asm-x86
+++ /dev/null
@@ -1 +0,0 @@
-../../../../libc/kernel/uapi/asm-x86
\ No newline at end of file
diff --git a/tools/versioner/preupload.sh b/tools/versioner/preupload.sh
deleted file mode 100755
index 45d6cca..0000000
--- a/tools/versioner/preupload.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-
-if ! which versioner > /dev/null; then
-  echo "versioner not found (lunch and mma in bionic)"
-  exit 1
-fi
-
-versioner -r arm -r arm64
-exit $?
diff --git a/tools/versioner/src/Android.bp b/tools/versioner/src/Android.bp
index d1362d0..c5afa56 100644
--- a/tools/versioner/src/Android.bp
+++ b/tools/versioner/src/Android.bp
@@ -41,18 +41,8 @@
                 "-fno-rtti",
             ],
         },
-        darwin: {
-            enabled: false,
-        },
         windows: {
             enabled: false,
         },
     },
-
-    product_variables: {
-        unbundled_build: {
-            // Only do this when Clang is available.
-            enabled: false,
-        },
-    },
 }