Merge "Update res_debug.c to take into account changes to the nameser.h"
diff --git a/libc/Android.mk b/libc/Android.mk
index d2b67c1..0ad5af2 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -165,6 +165,7 @@
     bionic/pipe.cpp \
     bionic/poll.cpp \
     bionic/posix_fallocate.cpp \
+    bionic/posix_timers.cpp \
     bionic/pthread_atfork.cpp \
     bionic/pthread_attr.cpp \
     bionic/pthread_cond.cpp \
@@ -224,7 +225,6 @@
     bionic/sys_signame.c \
     bionic/tdestroy.cpp \
     bionic/thread_atexit.cpp \
-    bionic/timer.cpp \
     bionic/tmpfile.cpp \
     bionic/unlink.cpp \
     bionic/utimes.cpp \
diff --git a/libc/NOTICE b/libc/NOTICE
index a737001..898b5db 100644
--- a/libc/NOTICE
+++ b/libc/NOTICE
@@ -390,6 +390,50 @@
 
 -------------------------------------------------------------------
 
+Copyright (C) 2014 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.
+
+-------------------------------------------------------------------
+
+Copyright (C) 2014 The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the
+   distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
 Copyright (c) 1980, 1983, 1988, 1993
    The Regents of the University of California.  All rights reserved.
 
@@ -1393,43 +1437,6 @@
 
 -------------------------------------------------------------------
 
-Copyright (c) 1989, 1993
-   The Regents of the University of California.  All rights reserved.
-(c) UNIX System Laboratories, Inc.
-All or some portions of this file are derived from material licensed
-to the University of California by American Telephone and Telegraph
-Co. or Unix System Laboratories, Inc. and are reproduced herein with
-the permission of UNIX System Laboratories, Inc.
-
-This code is derived from software contributed to Berkeley by
-Paul Borman at Krystal Technologies.
-
-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.
-4. 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.
-
--------------------------------------------------------------------
-
 Copyright (c) 1989, 1993, 1994
    The Regents of the University of California.  All rights reserved.
 
@@ -1800,6 +1807,35 @@
 
 -------------------------------------------------------------------
 
+Copyright (c) 1991 The Regents of the University of California.
+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.
+
+-------------------------------------------------------------------
+
 Copyright (c) 1991, 1993
    The Regents of the University of California.  All rights reserved.
 
@@ -2365,12 +2401,9 @@
 
 -------------------------------------------------------------------
 
-Copyright (c) 1994 The NetBSD Foundation, Inc.
+Copyright (c) 1994 SigmaSoft, Th. Lockert <tholo@sigmasoft.com>
 All rights reserved.
 
-This code is derived from software contributed to The NetBSD Foundation
-by Christos Zoulas.
-
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:
@@ -2379,18 +2412,19 @@
 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. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
 
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 
 -------------------------------------------------------------------
 
@@ -2427,33 +2461,6 @@
 
 -------------------------------------------------------------------
 
-Copyright (c) 1995 Alex Tatmanjants <alex@elvisti.kiev.ua>
-       at Electronni Visti IA, Kiev, Ukraine.
-           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.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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) 1995, 1996 Carnegie-Mellon University.
 All rights reserved.
 
@@ -3913,6 +3920,22 @@
 
 -------------------------------------------------------------------
 
+Copyright (c) 2010 Todd C. Miller <Todd.Miller@courtesan.com>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+-------------------------------------------------------------------
+
 Copyright (c) 2010, 2011, 2012, 2013 Intel Corporation
 All rights reserved.
 
@@ -4119,6 +4142,34 @@
 
 -------------------------------------------------------------------
 
+Copyright (c) 2012, Linaro Limited
+   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 the Linaro 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
+   HOLDER 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) 2013 ARM Ltd
 All rights reserved.
 
@@ -4147,6 +4198,22 @@
 
 -------------------------------------------------------------------
 
+Copyright (c) 2013 Antoine Jacoutot <ajacoutot@openbsd.org>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+-------------------------------------------------------------------
+
 Copyright (c) 2013 Intel Corporation
 All rights reserved.
 
@@ -4239,6 +4306,34 @@
 
 -------------------------------------------------------------------
 
+Copyright (c) 2014, Linaro Limited
+   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 the Linaro 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
+   HOLDER 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.
 
@@ -4291,43 +4386,6 @@
 
 -------------------------------------------------------------------
 
-Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
-Copyright 2008 Damien Miller <djm@openbsd.org>
-Copyright 2008 Android Open Source Project (thread-safety)
-All rights reserved.
-
-Theo de Raadt <deraadt@openbsd.org> came up with the idea of using
-such a mathematical system to generate more random (yet non-repeating)
-ids to solve the resolver/named problem.  But Niels designed the
-actual system based on the constraints.
-
-Later modified by Damien Miller to wrap the LCG output in a 15-bit
-permutation generator based on a Luby-Rackoff block cipher. This
-ensures the output is non-repeating and preserves the MSB twiddle
-trick, but makes it more resistant to LCG prediction.
-
-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.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 2000 David E. O'Brien, John D. Polstra.
 All rights reserved.
 
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index 1928168..8d4b258 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -213,11 +213,11 @@
 int           clock_nanosleep(clockid_t clock_id, int flags, const struct timespec* req, struct timespec* rem)  all
 int           getitimer(int, const struct itimerval*)   all
 int           setitimer(int, const struct itimerval*, struct itimerval*)  all
-int           __timer_create:timer_create(clockid_t clockid, struct sigevent* evp, timer_t* timerid)    all
-int           __timer_settime:timer_settime(timer_t, int, const struct itimerspec*, struct itimerspec*) all
-int           __timer_gettime:timer_gettime(timer_t, struct itimerspec*)                                all
-int           __timer_getoverrun:timer_getoverrun(timer_t)                                              all
-int           __timer_delete:timer_delete(timer_t)                                                      all
+int           __timer_create:timer_create(clockid_t clockid, struct sigevent* evp, __kernel_timer_t* timerid)    all
+int           __timer_settime:timer_settime(__kernel_timer_t, int, const struct itimerspec*, struct itimerspec*) all
+int           __timer_gettime:timer_gettime(__kernel_timer_t, struct itimerspec*)                                all
+int           __timer_getoverrun:timer_getoverrun(__kernel_timer_t)                                              all
+int           __timer_delete:timer_delete(__kernel_timer_t)                                                      all
 int           timerfd_create(clockid_t, int)   all
 int           timerfd_settime(int, int, const struct itimerspec*, struct itimerspec*)   all
 int           timerfd_gettime(int, struct itimerspec*)   all
diff --git a/libc/arch-arm64/arm64.mk b/libc/arch-arm64/arm64.mk
index 76c610a..88da1f3 100644
--- a/libc/arch-arm64/arm64.mk
+++ b/libc/arch-arm64/arm64.mk
@@ -4,13 +4,8 @@
     bionic/index.cpp \
     bionic/memchr.c \
     bionic/__memcmp16.cpp \
-    bionic/memcmp.c \
-    bionic/memcpy.cpp \
-    bionic/memmove.c \
     bionic/memrchr.c \
-    bionic/memset.c \
     bionic/strchr.cpp \
-    bionic/strnlen.c \
     bionic/strrchr.cpp \
     upstream-freebsd/lib/libc/string/wcscat.c \
     upstream-freebsd/lib/libc/string/wcschr.c \
@@ -21,13 +16,10 @@
     upstream-freebsd/lib/libc/string/wmemcmp.c \
     upstream-openbsd/lib/libc/string/bcopy.c \
     upstream-openbsd/lib/libc/string/strcat.c \
-    upstream-openbsd/lib/libc/string/strcmp.c \
     upstream-openbsd/lib/libc/string/strcpy.c \
     upstream-openbsd/lib/libc/string/strlcat.c \
     upstream-openbsd/lib/libc/string/strlcpy.c \
-    upstream-openbsd/lib/libc/string/strlen.c \
     upstream-openbsd/lib/libc/string/strncat.c \
-    upstream-openbsd/lib/libc/string/strncmp.c \
     upstream-openbsd/lib/libc/string/strncpy.c \
 
 # Fortify implementations of libc functions.
@@ -62,3 +54,5 @@
 
 libc_crt_target_crtbegin_so_file_arm64 := \
     $(LOCAL_PATH)/arch-common/bionic/crtbegin_so.c
+
+include $(LOCAL_PATH)/arch-arm64/generic/generic.mk
diff --git a/libc/arch-arm64/generic/bionic/memcmp.S b/libc/arch-arm64/generic/bionic/memcmp.S
new file mode 100644
index 0000000..3d08ecd
--- /dev/null
+++ b/libc/arch-arm64/generic/bionic/memcmp.S
@@ -0,0 +1,155 @@
+/* Copyright (c) 2014, Linaro Limited
+   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 the Linaro 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
+   HOLDER 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.
+*/
+
+/* Assumptions:
+ *
+ * ARMv8-a, AArch64
+ */
+
+#include <private/bionic_asm.h>
+
+/* Parameters and result.  */
+#define src1		x0
+#define src2		x1
+#define limit		x2
+#define result		x0
+
+/* Internal variables.  */
+#define data1		x3
+#define data1w		w3
+#define data2		x4
+#define data2w		w4
+#define has_nul		x5
+#define diff		x6
+#define endloop		x7
+#define tmp1		x8
+#define tmp2		x9
+#define tmp3		x10
+#define pos		x11
+#define limit_wd	x12
+#define mask		x13
+
+ENTRY(memcmp)
+	cbz	limit, .Lret0
+	eor	tmp1, src1, src2
+	tst	tmp1, #7
+	b.ne	.Lmisaligned8
+	ands	tmp1, src1, #7
+	b.ne	.Lmutual_align
+	add	limit_wd, limit, #7
+	lsr	limit_wd, limit_wd, #3
+	/* Start of performance-critical section  -- one 64B cache line.  */
+.Lloop_aligned:
+	ldr	data1, [src1], #8
+	ldr	data2, [src2], #8
+.Lstart_realigned:
+	subs	limit_wd, limit_wd, #1
+	eor	diff, data1, data2	/* Non-zero if differences found.  */
+	csinv	endloop, diff, xzr, ne	/* Last Dword or differences.  */
+	cbz	endloop, .Lloop_aligned
+	/* End of performance-critical section  -- one 64B cache line.  */
+
+	/* Not reached the limit, must have found a diff.  */
+	cbnz	limit_wd, .Lnot_limit
+
+	/* Limit % 8 == 0 => all bytes significant.  */
+	ands	limit, limit, #7
+	b.eq	.Lnot_limit
+
+	lsl	limit, limit, #3	/* Bits -> bytes.  */
+	mov	mask, #~0
+#ifdef __AARCH64EB__
+	lsr	mask, mask, limit
+#else
+	lsl	mask, mask, limit
+#endif
+	bic	data1, data1, mask
+	bic	data2, data2, mask
+
+	orr	diff, diff, mask
+.Lnot_limit:
+
+#ifndef	__AARCH64EB__
+	rev	diff, diff
+	rev	data1, data1
+	rev	data2, data2
+#endif
+	/* The MS-non-zero bit of DIFF marks either the first bit
+	   that is different, or the end of the significant data.
+	   Shifting left now will bring the critical information into the
+	   top bits.  */
+	clz	pos, diff
+	lsl	data1, data1, pos
+	lsl	data2, data2, pos
+	/* But we need to zero-extend (char is unsigned) the value and then
+	   perform a signed 32-bit subtraction.  */
+	lsr	data1, data1, #56
+	sub	result, data1, data2, lsr #56
+	ret
+
+.Lmutual_align:
+	/* Sources are mutually aligned, but are not currently at an
+	   alignment boundary.  Round down the addresses and then mask off
+	   the bytes that precede the start point.  */
+	bic	src1, src1, #7
+	bic	src2, src2, #7
+	add	limit, limit, tmp1	/* Adjust the limit for the extra.  */
+	lsl	tmp1, tmp1, #3		/* Bytes beyond alignment -> bits.  */
+	ldr	data1, [src1], #8
+	neg	tmp1, tmp1		/* Bits to alignment -64.  */
+	ldr	data2, [src2], #8
+	mov	tmp2, #~0
+#ifdef __AARCH64EB__
+	/* Big-endian.  Early bytes are at MSB.  */
+	lsl	tmp2, tmp2, tmp1	/* Shift (tmp1 & 63).  */
+#else
+	/* Little-endian.  Early bytes are at LSB.  */
+	lsr	tmp2, tmp2, tmp1	/* Shift (tmp1 & 63).  */
+#endif
+	add	limit_wd, limit, #7
+	orr	data1, data1, tmp2
+	orr	data2, data2, tmp2
+	lsr	limit_wd, limit_wd, #3
+	b	.Lstart_realigned
+
+.Lret0:
+	mov	result, #0
+	ret
+
+	.p2align 6
+.Lmisaligned8:
+	sub	limit, limit, #1
+1:
+	/* Perhaps we can do better than this.  */
+	ldrb	data1w, [src1], #1
+	ldrb	data2w, [src2], #1
+	subs	limit, limit, #1
+	ccmp	data1w, data2w, #0, cs	/* NZCV = 0b0000.  */
+	b.eq	1b
+	sub	result, data1, data2
+	ret
+END(memcmp)
diff --git a/libc/arch-arm64/generic/bionic/memcpy.S b/libc/arch-arm64/generic/bionic/memcpy.S
new file mode 100644
index 0000000..e1b1a72
--- /dev/null
+++ b/libc/arch-arm64/generic/bionic/memcpy.S
@@ -0,0 +1,184 @@
+/* Copyright (c) 2012, Linaro Limited
+   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 the Linaro 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
+   HOLDER 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.
+*/
+
+/* Assumptions:
+ *
+ * ARMv8-a, AArch64
+ * Unaligned accesses
+ *
+ */
+
+#include <private/bionic_asm.h>
+
+#define dstin	x0
+#define src	x1
+#define count	x2
+#define tmp1	x3
+#define tmp1w	w3
+#define tmp2	x4
+#define tmp2w	w4
+#define tmp3	x5
+#define tmp3w	w5
+#define dst	x6
+
+#define A_l	x7
+#define A_h	x8
+#define B_l	x9
+#define B_h	x10
+#define C_l	x11
+#define C_h	x12
+#define D_l	x13
+#define D_h	x14
+
+ENTRY(memcpy)
+
+	mov	dst, dstin
+	cmp	count, #64
+	b.ge	.Lcpy_not_short
+	cmp	count, #15
+	b.le	.Ltail15tiny
+
+	/* Deal with small copies quickly by dropping straight into the
+	 * exit block.  */
+.Ltail63:
+	/* Copy up to 48 bytes of data.  At this point we only need the
+	 * bottom 6 bits of count to be accurate.  */
+	ands	tmp1, count, #0x30
+	b.eq	.Ltail15
+	add	dst, dst, tmp1
+	add	src, src, tmp1
+	cmp	tmp1w, #0x20
+	b.eq	1f
+	b.lt	2f
+	ldp	A_l, A_h, [src, #-48]
+	stp	A_l, A_h, [dst, #-48]
+1:
+	ldp	A_l, A_h, [src, #-32]
+	stp	A_l, A_h, [dst, #-32]
+2:
+	ldp	A_l, A_h, [src, #-16]
+	stp	A_l, A_h, [dst, #-16]
+
+.Ltail15:
+	ands	count, count, #15
+	beq	1f
+	add	src, src, count
+	ldp	A_l, A_h, [src, #-16]
+	add	dst, dst, count
+	stp	A_l, A_h, [dst, #-16]
+1:
+	ret
+
+.Ltail15tiny:
+	/* Copy up to 15 bytes of data.  Does not assume additional data
+	   being copied.  */
+	tbz	count, #3, 1f
+	ldr	tmp1, [src], #8
+	str	tmp1, [dst], #8
+1:
+	tbz	count, #2, 1f
+	ldr	tmp1w, [src], #4
+	str	tmp1w, [dst], #4
+1:
+	tbz	count, #1, 1f
+	ldrh	tmp1w, [src], #2
+	strh	tmp1w, [dst], #2
+1:
+	tbz	count, #0, 1f
+	ldrb	tmp1w, [src]
+	strb	tmp1w, [dst]
+1:
+	ret
+
+.Lcpy_not_short:
+	/* We don't much care about the alignment of DST, but we want SRC
+	 * to be 128-bit (16 byte) aligned so that we don't cross cache line
+	 * boundaries on both loads and stores.  */
+	neg	tmp2, src
+	ands	tmp2, tmp2, #15		/* Bytes to reach alignment.  */
+	b.eq	2f
+	sub	count, count, tmp2
+	/* Copy more data than needed; it's faster than jumping
+	 * around copying sub-Quadword quantities.  We know that
+	 * it can't overrun.  */
+	ldp	A_l, A_h, [src]
+	add	src, src, tmp2
+	stp	A_l, A_h, [dst]
+	add	dst, dst, tmp2
+	/* There may be less than 63 bytes to go now.  */
+	cmp	count, #63
+	b.le	.Ltail63
+2:
+	subs	count, count, #128
+	b.ge	.Lcpy_body_large
+	/* Less than 128 bytes to copy, so handle 64 here and then jump
+	 * to the tail.  */
+	ldp	A_l, A_h, [src]
+	ldp	B_l, B_h, [src, #16]
+	ldp	C_l, C_h, [src, #32]
+	ldp	D_l, D_h, [src, #48]
+	stp	A_l, A_h, [dst]
+	stp	B_l, B_h, [dst, #16]
+	stp	C_l, C_h, [dst, #32]
+	stp	D_l, D_h, [dst, #48]
+	tst	count, #0x3f
+	add	src, src, #64
+	add	dst, dst, #64
+	b.ne	.Ltail63
+	ret
+
+	/* Critical loop.  Start at a new cache line boundary.  Assuming
+	 * 64 bytes per line this ensures the entire loop is in one line.  */
+	.p2align 6
+.Lcpy_body_large:
+	/* There are at least 128 bytes to copy.  */
+	ldp	A_l, A_h, [src, #0]
+	sub	dst, dst, #16		/* Pre-bias.  */
+	ldp	B_l, B_h, [src, #16]
+	ldp	C_l, C_h, [src, #32]
+	ldp	D_l, D_h, [src, #48]!	/* src += 64 - Pre-bias.  */
+1:
+	stp	A_l, A_h, [dst, #16]
+	ldp	A_l, A_h, [src, #16]
+	stp	B_l, B_h, [dst, #32]
+	ldp	B_l, B_h, [src, #32]
+	stp	C_l, C_h, [dst, #48]
+	ldp	C_l, C_h, [src, #48]
+	stp	D_l, D_h, [dst, #64]!
+	ldp	D_l, D_h, [src, #64]!
+	subs	count, count, #64
+	b.ge	1b
+	stp	A_l, A_h, [dst, #16]
+	stp	B_l, B_h, [dst, #32]
+	stp	C_l, C_h, [dst, #48]
+	stp	D_l, D_h, [dst, #64]
+	add	src, src, #16
+	add	dst, dst, #64 + 16
+	tst	count, #0x3f
+	b.ne	.Ltail63
+	ret
+END(memcpy)
diff --git a/libc/arch-arm64/generic/bionic/memmove.S b/libc/arch-arm64/generic/bionic/memmove.S
new file mode 100644
index 0000000..d6ecb86
--- /dev/null
+++ b/libc/arch-arm64/generic/bionic/memmove.S
@@ -0,0 +1,319 @@
+/* Copyright (c) 2014, Linaro Limited
+   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 the Linaro 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
+   HOLDER 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.
+*/
+
+/* Assumptions:
+ *
+ * ARMv8-a, AArch64
+ * Unaligned accesses
+ */
+
+#include <private/bionic_asm.h>
+
+/* Parameters and result.  */
+#define dstin	x0
+#define src	x1
+#define count	x2
+#define tmp1	x3
+#define tmp1w	w3
+#define tmp2	x4
+#define tmp2w	w4
+#define tmp3	x5
+#define tmp3w	w5
+#define dst	x6
+
+#define A_l	x7
+#define A_h	x8
+#define B_l	x9
+#define B_h	x10
+#define C_l	x11
+#define C_h	x12
+#define D_l	x13
+#define D_h	x14
+
+ENTRY(memmove)
+	cmp	dstin, src
+	b.lo	.Ldownwards
+	add	tmp1, src, count
+	cmp	dstin, tmp1
+	b.hs	memcpy		/* No overlap.  */
+
+	/* Upwards move with potential overlap.
+	 * Need to move from the tail backwards.  SRC and DST point one
+	 * byte beyond the remaining data to move.  */
+	add	dst, dstin, count
+	add	src, src, count
+	cmp	count, #64
+	b.ge	.Lmov_not_short_up
+
+	/* Deal with small moves quickly by dropping straight into the
+	 * exit block.  */
+.Ltail63up:
+	/* Move up to 48 bytes of data.  At this point we only need the
+	 * bottom 6 bits of count to be accurate.  */
+	ands	tmp1, count, #0x30
+	b.eq	.Ltail15up
+	sub	dst, dst, tmp1
+	sub	src, src, tmp1
+	cmp	tmp1w, #0x20
+	b.eq	1f
+	b.lt	2f
+	ldp	A_l, A_h, [src, #32]
+	stp	A_l, A_h, [dst, #32]
+1:
+	ldp	A_l, A_h, [src, #16]
+	stp	A_l, A_h, [dst, #16]
+2:
+	ldp	A_l, A_h, [src]
+	stp	A_l, A_h, [dst]
+.Ltail15up:
+	/* Move up to 15 bytes of data.  Does not assume additional data
+	 * being moved.  */
+	tbz	count, #3, 1f
+	ldr	tmp1, [src, #-8]!
+	str	tmp1, [dst, #-8]!
+1:
+	tbz	count, #2, 1f
+	ldr	tmp1w, [src, #-4]!
+	str	tmp1w, [dst, #-4]!
+1:
+	tbz	count, #1, 1f
+	ldrh	tmp1w, [src, #-2]!
+	strh	tmp1w, [dst, #-2]!
+1:
+	tbz	count, #0, 1f
+	ldrb	tmp1w, [src, #-1]
+	strb	tmp1w, [dst, #-1]
+1:
+	ret
+
+.Lmov_not_short_up:
+	/* We don't much care about the alignment of DST, but we want SRC
+	 * to be 128-bit (16 byte) aligned so that we don't cross cache line
+	 * boundaries on both loads and stores.  */
+	ands	tmp2, src, #15		/* Bytes to reach alignment.  */
+	b.eq	2f
+	sub	count, count, tmp2
+	/* Move enough data to reach alignment; unlike memcpy, we have to
+	 * be aware of the overlap, which means we can't move data twice.  */
+	tbz	tmp2, #3, 1f
+	ldr	tmp1, [src, #-8]!
+	str	tmp1, [dst, #-8]!
+1:
+	tbz	tmp2, #2, 1f
+	ldr	tmp1w, [src, #-4]!
+	str	tmp1w, [dst, #-4]!
+1:
+	tbz	tmp2, #1, 1f
+	ldrh	tmp1w, [src, #-2]!
+	strh	tmp1w, [dst, #-2]!
+1:
+	tbz	tmp2, #0, 1f
+	ldrb	tmp1w, [src, #-1]!
+	strb	tmp1w, [dst, #-1]!
+1:
+
+	/* There may be less than 63 bytes to go now.  */
+	cmp	count, #63
+	b.le	.Ltail63up
+2:
+	subs	count, count, #128
+	b.ge	.Lmov_body_large_up
+	/* Less than 128 bytes to move, so handle 64 here and then jump
+	 * to the tail.  */
+	ldp	A_l, A_h, [src, #-64]!
+	ldp	B_l, B_h, [src, #16]
+	ldp	C_l, C_h, [src, #32]
+	ldp	D_l, D_h, [src, #48]
+	stp	A_l, A_h, [dst, #-64]!
+	stp	B_l, B_h, [dst, #16]
+	stp	C_l, C_h, [dst, #32]
+	stp	D_l, D_h, [dst, #48]
+	tst	count, #0x3f
+	b.ne	.Ltail63up
+	ret
+
+	/* Critical loop.  Start at a new Icache line boundary.  Assuming
+	 * 64 bytes per line this ensures the entire loop is in one line.  */
+	.p2align 6
+.Lmov_body_large_up:
+	/* There are at least 128 bytes to move.  */
+	ldp	A_l, A_h, [src, #-16]
+	ldp	B_l, B_h, [src, #-32]
+	ldp	C_l, C_h, [src, #-48]
+	ldp	D_l, D_h, [src, #-64]!
+1:
+	stp	A_l, A_h, [dst, #-16]
+	ldp	A_l, A_h, [src, #-16]
+	stp	B_l, B_h, [dst, #-32]
+	ldp	B_l, B_h, [src, #-32]
+	stp	C_l, C_h, [dst, #-48]
+	ldp	C_l, C_h, [src, #-48]
+	stp	D_l, D_h, [dst, #-64]!
+	ldp	D_l, D_h, [src, #-64]!
+	subs	count, count, #64
+	b.ge	1b
+	stp	A_l, A_h, [dst, #-16]
+	stp	B_l, B_h, [dst, #-32]
+	stp	C_l, C_h, [dst, #-48]
+	stp	D_l, D_h, [dst, #-64]!
+	tst	count, #0x3f
+	b.ne	.Ltail63up
+	ret
+
+
+.Ldownwards:
+	/* For a downwards move we can safely use memcpy provided that
+	 * DST is more than 16 bytes away from SRC.  */
+	sub	tmp1, src, #16
+	cmp	dstin, tmp1
+	b.ls	memcpy		/* May overlap, but not critically.  */
+
+	mov	dst, dstin	/* Preserve DSTIN for return value.  */
+	cmp	count, #64
+	b.ge	.Lmov_not_short_down
+
+	/* Deal with small moves quickly by dropping straight into the
+	 * exit block.  */
+.Ltail63down:
+	/* Move up to 48 bytes of data.  At this point we only need the
+	 * bottom 6 bits of count to be accurate.  */
+	ands	tmp1, count, #0x30
+	b.eq	.Ltail15down
+	add	dst, dst, tmp1
+	add	src, src, tmp1
+	cmp	tmp1w, #0x20
+	b.eq	1f
+	b.lt	2f
+	ldp	A_l, A_h, [src, #-48]
+	stp	A_l, A_h, [dst, #-48]
+1:
+	ldp	A_l, A_h, [src, #-32]
+	stp	A_l, A_h, [dst, #-32]
+2:
+	ldp	A_l, A_h, [src, #-16]
+	stp	A_l, A_h, [dst, #-16]
+.Ltail15down:
+	/* Move up to 15 bytes of data.  Does not assume additional data
+	   being moved.  */
+	tbz	count, #3, 1f
+	ldr	tmp1, [src], #8
+	str	tmp1, [dst], #8
+1:
+	tbz	count, #2, 1f
+	ldr	tmp1w, [src], #4
+	str	tmp1w, [dst], #4
+1:
+	tbz	count, #1, 1f
+	ldrh	tmp1w, [src], #2
+	strh	tmp1w, [dst], #2
+1:
+	tbz	count, #0, 1f
+	ldrb	tmp1w, [src]
+	strb	tmp1w, [dst]
+1:
+	ret
+
+.Lmov_not_short_down:
+	/* We don't much care about the alignment of DST, but we want SRC
+	 * to be 128-bit (16 byte) aligned so that we don't cross cache line
+	 * boundaries on both loads and stores.  */
+	neg	tmp2, src
+	ands	tmp2, tmp2, #15		/* Bytes to reach alignment.  */
+	b.eq	2f
+	sub	count, count, tmp2
+	/* Move enough data to reach alignment; unlike memcpy, we have to
+	 * be aware of the overlap, which means we can't move data twice.  */
+	tbz	tmp2, #3, 1f
+	ldr	tmp1, [src], #8
+	str	tmp1, [dst], #8
+1:
+	tbz	tmp2, #2, 1f
+	ldr	tmp1w, [src], #4
+	str	tmp1w, [dst], #4
+1:
+	tbz	tmp2, #1, 1f
+	ldrh	tmp1w, [src], #2
+	strh	tmp1w, [dst], #2
+1:
+	tbz	tmp2, #0, 1f
+	ldrb	tmp1w, [src], #1
+	strb	tmp1w, [dst], #1
+1:
+
+	/* There may be less than 63 bytes to go now.  */
+	cmp	count, #63
+	b.le	.Ltail63down
+2:
+	subs	count, count, #128
+	b.ge	.Lmov_body_large_down
+	/* Less than 128 bytes to move, so handle 64 here and then jump
+	 * to the tail.  */
+	ldp	A_l, A_h, [src]
+	ldp	B_l, B_h, [src, #16]
+	ldp	C_l, C_h, [src, #32]
+	ldp	D_l, D_h, [src, #48]
+	stp	A_l, A_h, [dst]
+	stp	B_l, B_h, [dst, #16]
+	stp	C_l, C_h, [dst, #32]
+	stp	D_l, D_h, [dst, #48]
+	tst	count, #0x3f
+	add	src, src, #64
+	add	dst, dst, #64
+	b.ne	.Ltail63down
+	ret
+
+	/* Critical loop.  Start at a new cache line boundary.  Assuming
+	 * 64 bytes per line this ensures the entire loop is in one line.  */
+	.p2align 6
+.Lmov_body_large_down:
+	/* There are at least 128 bytes to move.  */
+	ldp	A_l, A_h, [src, #0]
+	sub	dst, dst, #16		/* Pre-bias.  */
+	ldp	B_l, B_h, [src, #16]
+	ldp	C_l, C_h, [src, #32]
+	ldp	D_l, D_h, [src, #48]!	/* src += 64 - Pre-bias.  */
+1:
+	stp	A_l, A_h, [dst, #16]
+	ldp	A_l, A_h, [src, #16]
+	stp	B_l, B_h, [dst, #32]
+	ldp	B_l, B_h, [src, #32]
+	stp	C_l, C_h, [dst, #48]
+	ldp	C_l, C_h, [src, #48]
+	stp	D_l, D_h, [dst, #64]!
+	ldp	D_l, D_h, [src, #64]!
+	subs	count, count, #64
+	b.ge	1b
+	stp	A_l, A_h, [dst, #16]
+	stp	B_l, B_h, [dst, #32]
+	stp	C_l, C_h, [dst, #48]
+	stp	D_l, D_h, [dst, #64]
+	add	src, src, #16
+	add	dst, dst, #64 + 16
+	tst	count, #0x3f
+	b.ne	.Ltail63down
+	ret
+END(memmove)
diff --git a/libc/arch-arm64/generic/bionic/memset.S b/libc/arch-arm64/generic/bionic/memset.S
new file mode 100644
index 0000000..4750fe3
--- /dev/null
+++ b/libc/arch-arm64/generic/bionic/memset.S
@@ -0,0 +1,236 @@
+/* Copyright (c) 2012, Linaro Limited
+   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 the Linaro 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
+   HOLDER 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.
+*/
+
+/* Assumptions:
+ *
+ * ARMv8-a, AArch64
+ * Unaligned accesses
+ *
+ */
+
+#include <private/bionic_asm.h>
+
+/* By default we assume that the DC instruction can be used to zero
+   data blocks more efficiently.  In some circumstances this might be
+   unsafe, for example in an asymmetric multiprocessor environment with
+   different DC clear lengths (neither the upper nor lower lengths are
+   safe to use).  The feature can be disabled by defining DONT_USE_DC.
+
+   If code may be run in a virtualized environment, then define
+   MAYBE_VIRT.  This will cause the code to cache the system register
+   values rather than re-reading them each call.  */
+
+#define dstin		x0
+#define val		w1
+#define count		x2
+#define tmp1		x3
+#define tmp1w		w3
+#define tmp2		x4
+#define tmp2w		w4
+#define zva_len_x	x5
+#define zva_len		w5
+#define zva_bits_x	x6
+
+#define A_l		x7
+#define A_lw		w7
+#define dst		x8
+#define tmp3w		w9
+
+ENTRY(memset)
+
+	mov	dst, dstin		/* Preserve return value.  */
+	ands	A_lw, val, #255
+#ifndef DONT_USE_DC
+	b.eq	.Lzero_mem
+#endif
+	orr	A_lw, A_lw, A_lw, lsl #8
+	orr	A_lw, A_lw, A_lw, lsl #16
+	orr	A_l, A_l, A_l, lsl #32
+.Ltail_maybe_long:
+	cmp	count, #64
+	b.ge	.Lnot_short
+.Ltail_maybe_tiny:
+	cmp	count, #15
+	b.le	.Ltail15tiny
+.Ltail63:
+	ands	tmp1, count, #0x30
+	b.eq	.Ltail15
+	add	dst, dst, tmp1
+	cmp	tmp1w, #0x20
+	b.eq	1f
+	b.lt	2f
+	stp	A_l, A_l, [dst, #-48]
+1:
+	stp	A_l, A_l, [dst, #-32]
+2:
+	stp	A_l, A_l, [dst, #-16]
+
+.Ltail15:
+	and	count, count, #15
+	add	dst, dst, count
+	stp	A_l, A_l, [dst, #-16]	/* Repeat some/all of last store. */
+	ret
+
+.Ltail15tiny:
+	/* Set up to 15 bytes.  Does not assume earlier memory
+	   being set.  */
+	tbz	count, #3, 1f
+	str	A_l, [dst], #8
+1:
+	tbz	count, #2, 1f
+	str	A_lw, [dst], #4
+1:
+	tbz	count, #1, 1f
+	strh	A_lw, [dst], #2
+1:
+	tbz	count, #0, 1f
+	strb	A_lw, [dst]
+1:
+	ret
+
+	/* Critical loop.  Start at a new cache line boundary.  Assuming
+	 * 64 bytes per line, this ensures the entire loop is in one line.  */
+	.p2align 6
+.Lnot_short:
+	neg	tmp2, dst
+	ands	tmp2, tmp2, #15
+	b.eq	2f
+	/* Bring DST to 128-bit (16-byte) alignment.  We know that there's
+	 * more than that to set, so we simply store 16 bytes and advance by
+	 * the amount required to reach alignment.  */
+	sub	count, count, tmp2
+	stp	A_l, A_l, [dst]
+	add	dst, dst, tmp2
+	/* There may be less than 63 bytes to go now.  */
+	cmp	count, #63
+	b.le	.Ltail63
+2:
+	sub	dst, dst, #16		/* Pre-bias.  */
+	sub	count, count, #64
+1:
+	stp	A_l, A_l, [dst, #16]
+	stp	A_l, A_l, [dst, #32]
+	stp	A_l, A_l, [dst, #48]
+	stp	A_l, A_l, [dst, #64]!
+	subs	count, count, #64
+	b.ge	1b
+	tst	count, #0x3f
+	add	dst, dst, #16
+	b.ne	.Ltail63
+	ret
+
+#ifndef DONT_USE_DC
+	/* For zeroing memory, check to see if we can use the ZVA feature to
+	 * zero entire 'cache' lines.  */
+.Lzero_mem:
+	mov	A_l, #0
+	cmp	count, #63
+	b.le	.Ltail_maybe_tiny
+	neg	tmp2, dst
+	ands	tmp2, tmp2, #15
+	b.eq	1f
+	sub	count, count, tmp2
+	stp	A_l, A_l, [dst]
+	add	dst, dst, tmp2
+	cmp	count, #63
+	b.le	.Ltail63
+1:
+	/* For zeroing small amounts of memory, it's not worth setting up
+	 * the line-clear code.  */
+	cmp	count, #128
+	b.lt	.Lnot_short
+#ifdef MAYBE_VIRT
+	/* For efficiency when virtualized, we cache the ZVA capability.  */
+	adrp	tmp2, .Lcache_clear
+	ldr	zva_len, [tmp2, #:lo12:.Lcache_clear]
+	tbnz	zva_len, #31, .Lnot_short
+	cbnz	zva_len, .Lzero_by_line
+	mrs	tmp1, dczid_el0
+	tbz	tmp1, #4, 1f
+	/* ZVA not available.  Remember this for next time.  */
+	mov	zva_len, #~0
+	str	zva_len, [tmp2, #:lo12:.Lcache_clear]
+	b	.Lnot_short
+1:
+	mov	tmp3w, #4
+	and	zva_len, tmp1w, #15	/* Safety: other bits reserved.  */
+	lsl	zva_len, tmp3w, zva_len
+	str	zva_len, [tmp2, #:lo12:.Lcache_clear]
+#else
+	mrs	tmp1, dczid_el0
+	tbnz	tmp1, #4, .Lnot_short
+	mov	tmp3w, #4
+	and	zva_len, tmp1w, #15	/* Safety: other bits reserved.  */
+	lsl	zva_len, tmp3w, zva_len
+#endif
+
+.Lzero_by_line:
+	/* Compute how far we need to go to become suitably aligned.  We're
+	 * already at quad-word alignment.  */
+	cmp	count, zva_len_x
+	b.lt	.Lnot_short		/* Not enough to reach alignment.  */
+	sub	zva_bits_x, zva_len_x, #1
+	neg	tmp2, dst
+	ands	tmp2, tmp2, zva_bits_x
+	b.eq	1f			/* Already aligned.  */
+	/* Not aligned, check that there's enough to copy after alignment.  */
+	sub	tmp1, count, tmp2
+	cmp	tmp1, #64
+	ccmp	tmp1, zva_len_x, #8, ge	/* NZCV=0b1000 */
+	b.lt	.Lnot_short
+	/* We know that there's at least 64 bytes to zero and that it's safe
+	 * to overrun by 64 bytes.  */
+	mov	count, tmp1
+2:
+	stp	A_l, A_l, [dst]
+	stp	A_l, A_l, [dst, #16]
+	stp	A_l, A_l, [dst, #32]
+	subs	tmp2, tmp2, #64
+	stp	A_l, A_l, [dst, #48]
+	add	dst, dst, #64
+	b.ge	2b
+	/* We've overrun a bit, so adjust dst downwards.  */
+	add	dst, dst, tmp2
+1:
+	sub	count, count, zva_len_x
+3:
+	dc	zva, dst
+	add	dst, dst, zva_len_x
+	subs	count, count, zva_len_x
+	b.ge	3b
+	ands	count, count, zva_bits_x
+	b.ne	.Ltail_maybe_long
+	ret
+END(memset)
+
+#ifdef MAYBE_VIRT
+	.bss
+	.p2align 2
+.Lcache_clear:
+	.space 4
+#endif
+#endif /* DONT_USE_DC */
diff --git a/libc/arch-arm64/generic/bionic/strcmp.S b/libc/arch-arm64/generic/bionic/strcmp.S
new file mode 100644
index 0000000..3cce478
--- /dev/null
+++ b/libc/arch-arm64/generic/bionic/strcmp.S
@@ -0,0 +1,162 @@
+/* Copyright (c) 2012, Linaro Limited
+   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 the Linaro 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
+   HOLDER 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.
+*/
+
+/* Assumptions:
+ *
+ * ARMv8-a, AArch64
+ */
+
+#include <private/bionic_asm.h>
+
+#define REP8_01 0x0101010101010101
+#define REP8_7f 0x7f7f7f7f7f7f7f7f
+#define REP8_80 0x8080808080808080
+
+/* Parameters and result.  */
+#define src1		x0
+#define src2		x1
+#define result		x0
+
+/* Internal variables.  */
+#define data1		x2
+#define data1w		w2
+#define data2		x3
+#define data2w		w3
+#define has_nul		x4
+#define diff		x5
+#define syndrome	x6
+#define tmp1		x7
+#define tmp2		x8
+#define tmp3		x9
+#define zeroones	x10
+#define pos		x11
+
+	/* Start of performance-critical section  -- one 64B cache line.  */
+ENTRY(strcmp)
+	eor	tmp1, src1, src2
+	mov	zeroones, #REP8_01
+	tst	tmp1, #7
+	b.ne	.Lmisaligned8
+	ands	tmp1, src1, #7
+	b.ne	.Lmutual_align
+	/* NUL detection works on the principle that (X - 1) & (~X) & 0x80
+	   (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and
+	   can be done in parallel across the entire word.  */
+.Lloop_aligned:
+	ldr	data1, [src1], #8
+	ldr	data2, [src2], #8
+.Lstart_realigned:
+	sub	tmp1, data1, zeroones
+	orr	tmp2, data1, #REP8_7f
+	eor	diff, data1, data2	/* Non-zero if differences found.  */
+	bic	has_nul, tmp1, tmp2	/* Non-zero if NUL terminator.  */
+	orr	syndrome, diff, has_nul
+	cbz	syndrome, .Lloop_aligned
+	/* End of performance-critical section  -- one 64B cache line.  */
+
+#ifndef	__AARCH64EB__
+	rev	syndrome, syndrome
+	rev	data1, data1
+	/* The MS-non-zero bit of the syndrome marks either the first bit
+	   that is different, or the top bit of the first zero byte.
+	   Shifting left now will bring the critical information into the
+	   top bits.  */
+	clz	pos, syndrome
+	rev	data2, data2
+	lsl	data1, data1, pos
+	lsl	data2, data2, pos
+	/* But we need to zero-extend (char is unsigned) the value and then
+	   perform a signed 32-bit subtraction.  */
+	lsr	data1, data1, #56
+	sub	result, data1, data2, lsr #56
+	ret
+#else
+	/* For big-endian we cannot use the trick with the syndrome value
+	   as carry-propagation can corrupt the upper bits if the trailing
+	   bytes in the string contain 0x01.  */
+	/* However, if there is no NUL byte in the dword, we can generate
+	   the result directly.  We can't just subtract the bytes as the
+	   MSB might be significant.  */
+	cbnz	has_nul, 1f
+	cmp	data1, data2
+	cset	result, ne
+	cneg	result, result, lo
+	ret
+1:
+	/* Re-compute the NUL-byte detection, using a byte-reversed value.  */
+	rev	tmp3, data1
+	sub	tmp1, tmp3, zeroones
+	orr	tmp2, tmp3, #REP8_7f
+	bic	has_nul, tmp1, tmp2
+	rev	has_nul, has_nul
+	orr	syndrome, diff, has_nul
+	clz	pos, syndrome
+	/* The MS-non-zero bit of the syndrome marks either the first bit
+	   that is different, or the top bit of the first zero byte.
+	   Shifting left now will bring the critical information into the
+	   top bits.  */
+	lsl	data1, data1, pos
+	lsl	data2, data2, pos
+	/* But we need to zero-extend (char is unsigned) the value and then
+	   perform a signed 32-bit subtraction.  */
+	lsr	data1, data1, #56
+	sub	result, data1, data2, lsr #56
+	ret
+#endif
+
+.Lmutual_align:
+	/* Sources are mutually aligned, but are not currently at an
+	   alignment boundary.  Round down the addresses and then mask off
+	   the bytes that preceed the start point.  */
+	bic	src1, src1, #7
+	bic	src2, src2, #7
+	lsl	tmp1, tmp1, #3		/* Bytes beyond alignment -> bits.  */
+	ldr	data1, [src1], #8
+	neg	tmp1, tmp1		/* Bits to alignment -64.  */
+	ldr	data2, [src2], #8
+	mov	tmp2, #~0
+#ifdef __AARCH64EB__
+	/* Big-endian.  Early bytes are at MSB.  */
+	lsl	tmp2, tmp2, tmp1	/* Shift (tmp1 & 63).  */
+#else
+	/* Little-endian.  Early bytes are at LSB.  */
+	lsr	tmp2, tmp2, tmp1	/* Shift (tmp1 & 63).  */
+#endif
+	orr	data1, data1, tmp2
+	orr	data2, data2, tmp2
+	b	.Lstart_realigned
+
+.Lmisaligned8:
+	/* We can do better than this.  */
+	ldrb	data1w, [src1], #1
+	ldrb	data2w, [src2], #1
+	cmp	data1w, #1
+	ccmp	data1w, data2w, #0, cs	/* NZCV = 0b0000.  */
+	b.eq	.Lmisaligned8
+	sub	result, data1, data2
+	ret
+END(strcmp)
diff --git a/libc/arch-arm64/generic/bionic/strlen.S b/libc/arch-arm64/generic/bionic/strlen.S
new file mode 100644
index 0000000..3bd9809
--- /dev/null
+++ b/libc/arch-arm64/generic/bionic/strlen.S
@@ -0,0 +1,126 @@
+/* Copyright (c) 2014, Linaro Limited
+   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 the Linaro 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
+   HOLDER 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.
+*/
+
+/* Assumptions:
+ *
+ * ARMv8-a, AArch64
+ */
+
+#include <private/bionic_asm.h>
+
+/* Arguments and results.  */
+#define srcin		x0
+#define len		x0
+
+/* Locals and temporaries.  */
+#define src		x1
+#define data1		x2
+#define data2		x3
+#define data2a		x4
+#define has_nul1	x5
+#define has_nul2	x6
+#define tmp1		x7
+#define tmp2		x8
+#define tmp3		x9
+#define tmp4		x10
+#define zeroones	x11
+#define pos		x12
+
+#define REP8_01 0x0101010101010101
+#define REP8_7f 0x7f7f7f7f7f7f7f7f
+#define REP8_80 0x8080808080808080
+
+	/* Start of critial section -- keep to one 64Byte cache line.  */
+ENTRY(strlen)
+	mov	zeroones, #REP8_01
+	bic	src, srcin, #15
+	ands	tmp1, srcin, #15
+	b.ne	.Lmisaligned
+	/* NUL detection works on the principle that (X - 1) & (~X) & 0x80
+	   (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and
+	   can be done in parallel across the entire word.  */
+	/* The inner loop deals with two Dwords at a time.  This has a
+	   slightly higher start-up cost, but we should win quite quickly,
+	   especially on cores with a high number of issue slots per
+	   cycle, as we get much better parallelism out of the operations.  */
+.Lloop:
+	ldp	data1, data2, [src], #16
+.Lrealigned:
+	sub	tmp1, data1, zeroones
+	orr	tmp2, data1, #REP8_7f
+	sub	tmp3, data2, zeroones
+	orr	tmp4, data2, #REP8_7f
+	bic	has_nul1, tmp1, tmp2
+	bics	has_nul2, tmp3, tmp4
+	ccmp	has_nul1, #0, #0, eq	/* NZCV = 0000  */
+	b.eq	.Lloop
+	/* End of critical section -- keep to one 64Byte cache line.  */
+
+	sub	len, src, srcin
+	cbz	has_nul1, .Lnul_in_data2
+#ifdef __AARCH64EB__
+	mov	data2, data1
+#endif
+	sub	len, len, #8
+	mov	has_nul2, has_nul1
+.Lnul_in_data2:
+#ifdef __AARCH64EB__
+	/* For big-endian, carry propagation (if the final byte in the
+	   string is 0x01) means we cannot use has_nul directly.  The
+	   easiest way to get the correct byte is to byte-swap the data
+	   and calculate the syndrome a second time.  */
+	rev	data2, data2
+	sub	tmp1, data2, zeroones
+	orr	tmp2, data2, #REP8_7f
+	bic	has_nul2, tmp1, tmp2
+#endif
+	sub	len, len, #8
+	rev	has_nul2, has_nul2
+	clz	pos, has_nul2
+	add	len, len, pos, lsr #3		/* Bits to bytes.  */
+	ret
+
+.Lmisaligned:
+	cmp	tmp1, #8
+	neg	tmp1, tmp1
+	ldp	data1, data2, [src], #16
+	lsl	tmp1, tmp1, #3		/* Bytes beyond alignment -> bits.  */
+	mov	tmp2, #~0
+#ifdef __AARCH64EB__
+	/* Big-endian.  Early bytes are at MSB.  */
+	lsl	tmp2, tmp2, tmp1	/* Shift (tmp1 & 63).  */
+#else
+	/* Little-endian.  Early bytes are at LSB.  */
+	lsr	tmp2, tmp2, tmp1	/* Shift (tmp1 & 63).  */
+#endif
+	orr	data1, data1, tmp2
+	orr	data2a, data2, tmp2
+	csinv	data1, data1, xzr, le
+	csel	data2, data2, data2a, le
+	b	.Lrealigned
+
+END(strlen)
diff --git a/libc/arch-arm64/generic/bionic/strncmp.S b/libc/arch-arm64/generic/bionic/strncmp.S
new file mode 100644
index 0000000..267f663
--- /dev/null
+++ b/libc/arch-arm64/generic/bionic/strncmp.S
@@ -0,0 +1,217 @@
+/* Copyright (c) 2014, Linaro Limited
+   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 the Linaro 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
+   HOLDER 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.
+*/
+
+/* Assumptions:
+ *
+ * ARMv8-a, AArch64
+ */
+
+#include <private/bionic_asm.h>
+
+#define REP8_01 0x0101010101010101
+#define REP8_7f 0x7f7f7f7f7f7f7f7f
+#define REP8_80 0x8080808080808080
+
+/* Parameters and result.  */
+#define src1		x0
+#define src2		x1
+#define limit		x2
+#define result		x0
+
+/* Internal variables.  */
+#define data1		x3
+#define data1w		w3
+#define data2		x4
+#define data2w		w4
+#define has_nul		x5
+#define diff		x6
+#define syndrome	x7
+#define tmp1		x8
+#define tmp2		x9
+#define tmp3		x10
+#define zeroones	x11
+#define pos		x12
+#define limit_wd	x13
+#define mask		x14
+#define endloop		x15
+
+	.text
+	.p2align 6
+	.rep 7
+	nop	/* Pad so that the loop below fits a cache line.  */
+	.endr
+ENTRY(strncmp)
+	cbz	limit, .Lret0
+	eor	tmp1, src1, src2
+	mov	zeroones, #REP8_01
+	tst	tmp1, #7
+	b.ne	.Lmisaligned8
+	ands	tmp1, src1, #7
+	b.ne	.Lmutual_align
+	/* Calculate the number of full and partial words -1.  */
+	sub	limit_wd, limit, #1	/* limit != 0, so no underflow.  */
+	lsr	limit_wd, limit_wd, #3	/* Convert to Dwords.  */
+
+	/* NUL detection works on the principle that (X - 1) & (~X) & 0x80
+	   (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and
+	   can be done in parallel across the entire word.  */
+	/* Start of performance-critical section  -- one 64B cache line.  */
+.Lloop_aligned:
+	ldr	data1, [src1], #8
+	ldr	data2, [src2], #8
+.Lstart_realigned:
+	subs	limit_wd, limit_wd, #1
+	sub	tmp1, data1, zeroones
+	orr	tmp2, data1, #REP8_7f
+	eor	diff, data1, data2	/* Non-zero if differences found.  */
+	csinv	endloop, diff, xzr, pl	/* Last Dword or differences.  */
+	bics	has_nul, tmp1, tmp2	/* Non-zero if NUL terminator.  */
+	ccmp	endloop, #0, #0, eq
+	b.eq	.Lloop_aligned
+	/* End of performance-critical section  -- one 64B cache line.  */
+
+	/* Not reached the limit, must have found the end or a diff.  */
+	tbz	limit_wd, #63, .Lnot_limit
+
+	/* Limit % 8 == 0 => all bytes significant.  */
+	ands	limit, limit, #7
+	b.eq	.Lnot_limit
+
+	lsl	limit, limit, #3	/* Bits -> bytes.  */
+	mov	mask, #~0
+#ifdef __AARCH64EB__
+	lsr	mask, mask, limit
+#else
+	lsl	mask, mask, limit
+#endif
+	bic	data1, data1, mask
+	bic	data2, data2, mask
+
+	/* Make sure that the NUL byte is marked in the syndrome.  */
+	orr	has_nul, has_nul, mask
+
+.Lnot_limit:
+	orr	syndrome, diff, has_nul
+
+#ifndef	__AARCH64EB__
+	rev	syndrome, syndrome
+	rev	data1, data1
+	/* The MS-non-zero bit of the syndrome marks either the first bit
+	   that is different, or the top bit of the first zero byte.
+	   Shifting left now will bring the critical information into the
+	   top bits.  */
+	clz	pos, syndrome
+	rev	data2, data2
+	lsl	data1, data1, pos
+	lsl	data2, data2, pos
+	/* But we need to zero-extend (char is unsigned) the value and then
+	   perform a signed 32-bit subtraction.  */
+	lsr	data1, data1, #56
+	sub	result, data1, data2, lsr #56
+	ret
+#else
+	/* For big-endian we cannot use the trick with the syndrome value
+	   as carry-propagation can corrupt the upper bits if the trailing
+	   bytes in the string contain 0x01.  */
+	/* However, if there is no NUL byte in the dword, we can generate
+	   the result directly.  We can't just subtract the bytes as the
+	   MSB might be significant.  */
+	cbnz	has_nul, 1f
+	cmp	data1, data2
+	cset	result, ne
+	cneg	result, result, lo
+	ret
+1:
+	/* Re-compute the NUL-byte detection, using a byte-reversed value.  */
+	rev	tmp3, data1
+	sub	tmp1, tmp3, zeroones
+	orr	tmp2, tmp3, #REP8_7f
+	bic	has_nul, tmp1, tmp2
+	rev	has_nul, has_nul
+	orr	syndrome, diff, has_nul
+	clz	pos, syndrome
+	/* The MS-non-zero bit of the syndrome marks either the first bit
+	   that is different, or the top bit of the first zero byte.
+	   Shifting left now will bring the critical information into the
+	   top bits.  */
+	lsl	data1, data1, pos
+	lsl	data2, data2, pos
+	/* But we need to zero-extend (char is unsigned) the value and then
+	   perform a signed 32-bit subtraction.  */
+	lsr	data1, data1, #56
+	sub	result, data1, data2, lsr #56
+	ret
+#endif
+
+.Lmutual_align:
+	/* Sources are mutually aligned, but are not currently at an
+	   alignment boundary.  Round down the addresses and then mask off
+	   the bytes that precede the start point.
+	   We also need to adjust the limit calculations, but without
+	   overflowing if the limit is near ULONG_MAX.  */
+	bic	src1, src1, #7
+	bic	src2, src2, #7
+	ldr	data1, [src1], #8
+	neg	tmp3, tmp1, lsl #3	/* 64 - bits(bytes beyond align). */
+	ldr	data2, [src2], #8
+	mov	tmp2, #~0
+	sub	limit_wd, limit, #1	/* limit != 0, so no underflow.  */
+#ifdef __AARCH64EB__
+	/* Big-endian.  Early bytes are at MSB.  */
+	lsl	tmp2, tmp2, tmp3	/* Shift (tmp1 & 63).  */
+#else
+	/* Little-endian.  Early bytes are at LSB.  */
+	lsr	tmp2, tmp2, tmp3	/* Shift (tmp1 & 63).  */
+#endif
+	and	tmp3, limit_wd, #7
+	lsr	limit_wd, limit_wd, #3
+	/* Adjust the limit. Only low 3 bits used, so overflow irrelevant.  */
+	add	limit, limit, tmp1
+	add	tmp3, tmp3, tmp1
+	orr	data1, data1, tmp2
+	orr	data2, data2, tmp2
+	add	limit_wd, limit_wd, tmp3, lsr #3
+	b	.Lstart_realigned
+
+.Lret0:
+	mov	result, #0
+	ret
+
+	.p2align 6
+.Lmisaligned8:
+	sub	limit, limit, #1
+1:
+	/* Perhaps we can do better than this.  */
+	ldrb	data1w, [src1], #1
+	ldrb	data2w, [src2], #1
+	subs	limit, limit, #1
+	ccmp	data1w, #1, #0, cs	/* NZCV = 0b0000.  */
+	ccmp	data1w, data2w, #0, cs	/* NZCV = 0b0000.  */
+	b.eq	1b
+	sub	result, data1, data2
+	ret
+END(strncmp)
diff --git a/libc/arch-arm64/generic/bionic/strnlen.S b/libc/arch-arm64/generic/bionic/strnlen.S
new file mode 100644
index 0000000..0ad446e
--- /dev/null
+++ b/libc/arch-arm64/generic/bionic/strnlen.S
@@ -0,0 +1,174 @@
+/* Copyright (c) 2014, Linaro Limited
+   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 the Linaro 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
+   HOLDER 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.
+*/
+
+/* Assumptions:
+ *
+ * ARMv8-a, AArch64
+ */
+
+#include <private/bionic_asm.h>
+
+/* Arguments and results.  */
+#define srcin		x0
+#define len		x0
+#define limit		x1
+
+/* Locals and temporaries.  */
+#define src		x2
+#define data1		x3
+#define data2		x4
+#define data2a		x5
+#define has_nul1	x6
+#define has_nul2	x7
+#define tmp1		x8
+#define tmp2		x9
+#define tmp3		x10
+#define tmp4		x11
+#define zeroones	x12
+#define pos		x13
+#define limit_wd	x14
+
+#define REP8_01 0x0101010101010101
+#define REP8_7f 0x7f7f7f7f7f7f7f7f
+#define REP8_80 0x8080808080808080
+
+	.text
+	.p2align	6
+.Lstart:
+	/* Pre-pad to ensure critical loop begins an icache line.  */
+	.rep 7
+	nop
+	.endr
+	/* Put this code here to avoid wasting more space with pre-padding.  */
+.Lhit_limit:
+	mov	len, limit
+	ret
+
+ENTRY(strnlen)
+	cbz	limit, .Lhit_limit
+	mov	zeroones, #REP8_01
+	bic	src, srcin, #15
+	ands	tmp1, srcin, #15
+	b.ne	.Lmisaligned
+	/* Calculate the number of full and partial words -1.  */
+	sub	limit_wd, limit, #1	/* Limit != 0, so no underflow.  */
+	lsr	limit_wd, limit_wd, #4	/* Convert to Qwords.  */
+
+	/* NUL detection works on the principle that (X - 1) & (~X) & 0x80
+	   (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and
+	   can be done in parallel across the entire word.  */
+	/* The inner loop deals with two Dwords at a time.  This has a
+	   slightly higher start-up cost, but we should win quite quickly,
+	   especially on cores with a high number of issue slots per
+	   cycle, as we get much better parallelism out of the operations.  */
+
+	/* Start of critial section -- keep to one 64Byte cache line.  */
+.Lloop:
+	ldp	data1, data2, [src], #16
+.Lrealigned:
+	sub	tmp1, data1, zeroones
+	orr	tmp2, data1, #REP8_7f
+	sub	tmp3, data2, zeroones
+	orr	tmp4, data2, #REP8_7f
+	bic	has_nul1, tmp1, tmp2
+	bic	has_nul2, tmp3, tmp4
+	subs	limit_wd, limit_wd, #1
+	orr	tmp1, has_nul1, has_nul2
+	ccmp	tmp1, #0, #0, pl	/* NZCV = 0000  */
+	b.eq	.Lloop
+	/* End of critical section -- keep to one 64Byte cache line.  */
+
+	orr	tmp1, has_nul1, has_nul2
+	cbz	tmp1, .Lhit_limit	/* No null in final Qword.  */
+
+	/* We know there's a null in the final Qword.  The easiest thing
+	   to do now is work out the length of the string and return
+	   MIN (len, limit).  */
+
+	sub	len, src, srcin
+	cbz	has_nul1, .Lnul_in_data2
+#ifdef __AARCH64EB__
+	mov	data2, data1
+#endif
+	sub	len, len, #8
+	mov	has_nul2, has_nul1
+.Lnul_in_data2:
+#ifdef __AARCH64EB__
+	/* For big-endian, carry propagation (if the final byte in the
+	   string is 0x01) means we cannot use has_nul directly.  The
+	   easiest way to get the correct byte is to byte-swap the data
+	   and calculate the syndrome a second time.  */
+	rev	data2, data2
+	sub	tmp1, data2, zeroones
+	orr	tmp2, data2, #REP8_7f
+	bic	has_nul2, tmp1, tmp2
+#endif
+	sub	len, len, #8
+	rev	has_nul2, has_nul2
+	clz	pos, has_nul2
+	add	len, len, pos, lsr #3		/* Bits to bytes.  */
+	cmp	len, limit
+	csel	len, len, limit, ls		/* Return the lower value.  */
+	ret
+
+.Lmisaligned:
+	/* Deal with a partial first word.
+	   We're doing two things in parallel here;
+	   1) Calculate the number of words (but avoiding overflow if
+	      limit is near ULONG_MAX) - to do this we need to work out
+	      limit + tmp1 - 1 as a 65-bit value before shifting it;
+	   2) Load and mask the initial data words - we force the bytes
+	      before the ones we are interested in to 0xff - this ensures
+	      early bytes will not hit any zero detection.  */
+	sub	limit_wd, limit, #1
+	neg	tmp4, tmp1
+	cmp	tmp1, #8
+
+	and	tmp3, limit_wd, #15
+	lsr	limit_wd, limit_wd, #4
+	mov	tmp2, #~0
+
+	ldp	data1, data2, [src], #16
+	lsl	tmp4, tmp4, #3		/* Bytes beyond alignment -> bits.  */
+	add	tmp3, tmp3, tmp1
+
+#ifdef __AARCH64EB__
+	/* Big-endian.  Early bytes are at MSB.  */
+	lsl	tmp2, tmp2, tmp4	/* Shift (tmp1 & 63).  */
+#else
+	/* Little-endian.  Early bytes are at LSB.  */
+	lsr	tmp2, tmp2, tmp4	/* Shift (tmp1 & 63).  */
+#endif
+	add	limit_wd, limit_wd, tmp3, lsr #4
+
+	orr	data1, data1, tmp2
+	orr	data2a, data2, tmp2
+
+	csinv	data1, data1, xzr, le
+	csel	data2, data2, data2a, le
+	b	.Lrealigned
+END(strnlen)
diff --git a/libc/arch-arm64/generic/generic.mk b/libc/arch-arm64/generic/generic.mk
new file mode 100644
index 0000000..14920a6
--- /dev/null
+++ b/libc/arch-arm64/generic/generic.mk
@@ -0,0 +1,9 @@
+libc_bionic_src_files_arm64 += \
+    arch-arm64/generic/bionic/memcmp.S \
+    arch-arm64/generic/bionic/memcpy.S \
+    arch-arm64/generic/bionic/memmove.S \
+    arch-arm64/generic/bionic/memset.S \
+    arch-arm64/generic/bionic/strcmp.S \
+    arch-arm64/generic/bionic/strlen.S \
+    arch-arm64/generic/bionic/strncmp.S \
+    arch-arm64/generic/bionic/strnlen.S \
diff --git a/libc/bionic/fork.cpp b/libc/bionic/fork.cpp
index 9fa5fcf..a0f98e4 100644
--- a/libc/bionic/fork.cpp
+++ b/libc/bionic/fork.cpp
@@ -29,15 +29,9 @@
 #include <unistd.h>
 #include <sys/syscall.h>
 
-#include "private/libc_logging.h"
 #include "pthread_internal.h"
 
 int fork() {
-  // POSIX mandates that the timers of a fork child process be
-  // disarmed, but not destroyed. To avoid a race condition, we're
-  // going to stop all timers now, and only re-start them in case
-  // of error, or in the parent process
-  __timer_table_start_stop(1);
   __bionic_atfork_run_prepare();
 
   pthread_internal_t* self = __get_thread();
@@ -50,7 +44,6 @@
   if (result == 0) {
     __bionic_atfork_run_child();
   } else {
-    __timer_table_start_stop(0);
     __bionic_atfork_run_parent();
   }
   return result;
diff --git a/libc/bionic/libc_logging.cpp b/libc/bionic/libc_logging.cpp
index e8a7597..a4aeced 100644
--- a/libc/bionic/libc_logging.cpp
+++ b/libc/bionic/libc_logging.cpp
@@ -478,6 +478,11 @@
 
   return log_fd;
 }
+
+struct log_time { // Wire format
+  uint32_t tv_sec;
+  uint32_t tv_nsec;
+};
 #endif
 
 static int __libc_write_log(int priority, const char* tag, const char* msg) {
@@ -493,8 +498,11 @@
   char log_id = LOG_ID_MAIN;
   vec[0].iov_base = &log_id;
   vec[0].iov_len = sizeof(log_id);
-  timespec realtime_ts;
-  clock_gettime(CLOCK_REALTIME, &realtime_ts);
+  timespec ts;
+  clock_gettime(CLOCK_REALTIME, &ts);
+  log_time realtime_ts;
+  realtime_ts.tv_sec = ts.tv_sec;
+  realtime_ts.tv_nsec = ts.tv_nsec;
   vec[1].iov_base = &realtime_ts;
   vec[1].iov_len = sizeof(realtime_ts);
 
@@ -549,8 +557,11 @@
   char log_id = LOG_ID_EVENTS;
   vec[0].iov_base = &log_id;
   vec[0].iov_len = sizeof(log_id);
-  timespec realtime_ts;
-  clock_gettime(CLOCK_REALTIME, &realtime_ts);
+  timespec ts;
+  clock_gettime(CLOCK_REALTIME, &ts);
+  log_time realtime_ts;
+  realtime_ts.tv_sec = ts.tv_sec;
+  realtime_ts.tv_nsec = ts.tv_nsec;
   vec[1].iov_base = &realtime_ts;
   vec[1].iov_len = sizeof(realtime_ts);
 
diff --git a/libc/bionic/posix_timers.cpp b/libc/bionic/posix_timers.cpp
new file mode 100644
index 0000000..ffe213c
--- /dev/null
+++ b/libc/bionic/posix_timers.cpp
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "pthread_internal.h"
+#include "private/bionic_futex.h"
+#include "private/bionic_pthread.h"
+#include "private/kernel_sigset_t.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+// System calls.
+extern "C" int __rt_sigtimedwait(const sigset_t*, siginfo_t*, const struct timespec*, size_t);
+extern "C" int __timer_create(clockid_t, sigevent*, __kernel_timer_t*);
+extern "C" int __timer_delete(__kernel_timer_t);
+extern "C" int __timer_getoverrun(__kernel_timer_t);
+extern "C" int __timer_gettime(__kernel_timer_t, itimerspec*);
+extern "C" int __timer_settime(__kernel_timer_t, int, const itimerspec*, itimerspec*);
+
+// Most POSIX timers are handled directly by the kernel. We translate SIGEV_THREAD timers
+// into SIGEV_THREAD_ID timers so the kernel handles all the time-related stuff and we just
+// need to worry about running user code on a thread.
+
+// We can't use SIGALRM because too many other C library functions throw that around, and since
+// they don't send to a specific thread, all threads are eligible to handle the signal and we can
+// end up with one of our POSIX timer threads handling it (meaning that the intended recipient
+// doesn't). glibc uses SIGRTMIN for its POSIX timer implementation, so in the absence of any
+// reason to use anything else, we use that too.
+static const int TIMER_SIGNAL = SIGRTMIN;
+
+struct PosixTimer {
+  __kernel_timer_t kernel_timer_id;
+
+  int sigev_notify;
+
+  // These fields are only needed for a SIGEV_THREAD timer.
+  pthread_t callback_thread;
+  void (*callback)(sigval_t);
+  sigval_t callback_argument;
+  volatile int exiting;
+};
+
+static __kernel_timer_t to_kernel_timer_id(timer_t timer) {
+  return reinterpret_cast<PosixTimer*>(timer)->kernel_timer_id;
+}
+
+static void* __timer_thread_start(void* arg) {
+  PosixTimer* timer = reinterpret_cast<PosixTimer*>(arg);
+
+  kernel_sigset_t sigset;
+  sigaddset(sigset.get(), TIMER_SIGNAL);
+
+  while (true) {
+    // Wait for a signal...
+    siginfo_t si;
+    memset(&si, 0, sizeof(si));
+    int rc = __rt_sigtimedwait(sigset.get(), &si, NULL, sizeof(sigset));
+    if (rc == -1) {
+      continue;
+    }
+
+    if (si.si_code == SI_TIMER) {
+      // This signal was sent because a timer fired, so call the callback.
+      timer->callback(timer->callback_argument);
+    } else if (si.si_code == SI_TKILL) {
+      // This signal was sent because someone wants us to exit.
+      timer->exiting = 1;
+      __futex_wake(&timer->exiting, INT32_MAX);
+      return NULL;
+    }
+  }
+}
+
+static void __timer_thread_stop(PosixTimer* timer) {
+  pthread_kill(timer->callback_thread, TIMER_SIGNAL);
+
+  // We can't pthread_join because POSIX says "the threads created in response to a timer
+  // expiration are created detached, or in an unspecified way if the thread attribute's
+  // detachstate is PTHREAD_CREATE_JOINABLE".
+  while (timer->exiting == 0) {
+    __futex_wait(&timer->exiting, 0, NULL);
+  }
+}
+
+// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_create.html
+int timer_create(clockid_t clock_id, sigevent* evp, timer_t* timer_id) {
+  PosixTimer* new_timer = reinterpret_cast<PosixTimer*>(malloc(sizeof(PosixTimer)));
+  if (new_timer == NULL) {
+    return -1;
+  }
+
+  new_timer->sigev_notify = (evp == NULL) ? SIGEV_SIGNAL : evp->sigev_notify;
+
+  // If not a SIGEV_THREAD timer, the kernel can handle it without our help.
+  if (new_timer->sigev_notify != SIGEV_THREAD) {
+    if (__timer_create(clock_id, evp, &new_timer->kernel_timer_id) == -1) {
+      free(new_timer);
+      return -1;
+    }
+
+    *timer_id = new_timer;
+    return 0;
+  }
+
+  // Otherwise, this must be SIGEV_THREAD timer...
+  new_timer->callback = evp->sigev_notify_function;
+  new_timer->callback_argument = evp->sigev_value;
+  new_timer->exiting = 0;
+
+  // Check arguments that the kernel doesn't care about but we do.
+  if (new_timer->callback == NULL) {
+    free(new_timer);
+    errno = EINVAL;
+    return -1;
+  }
+
+  // Create this timer's thread.
+  pthread_attr_t thread_attributes;
+  if (evp->sigev_notify_attributes == NULL) {
+    pthread_attr_init(&thread_attributes);
+  } else {
+    thread_attributes = *reinterpret_cast<pthread_attr_t*>(evp->sigev_notify_attributes);
+  }
+  pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);
+
+  // We start the thread with TIMER_SIGNAL blocked by blocking the signal here and letting it
+  // inherit. If it tried to block the signal itself, there would be a race.
+  kernel_sigset_t sigset;
+  sigaddset(sigset.get(), TIMER_SIGNAL);
+  kernel_sigset_t old_sigset;
+  pthread_sigmask(SIG_BLOCK, sigset.get(), old_sigset.get());
+
+  int rc = pthread_create(&new_timer->callback_thread, &thread_attributes, __timer_thread_start, new_timer);
+
+  pthread_sigmask(SIG_SETMASK, old_sigset.get(), NULL);
+
+  if (rc != 0) {
+    free(new_timer);
+    errno = rc;
+    return -1;
+  }
+
+  sigevent se = *evp;
+  se.sigev_signo = TIMER_SIGNAL;
+  se.sigev_notify = SIGEV_THREAD_ID;
+  se.sigev_notify_thread_id = __pthread_gettid(new_timer->callback_thread);
+  if (__timer_create(clock_id, &se, &new_timer->kernel_timer_id) == -1) {
+    __timer_thread_stop(new_timer);
+    free(new_timer);
+    return -1;
+  }
+
+  // Give the thread a meaningful name.
+  // It can't do this itself because the kernel timer isn't created until after it's running.
+  char name[32];
+  snprintf(name, sizeof(name), "POSIX interval timer %d", to_kernel_timer_id(new_timer));
+  pthread_setname_np(new_timer->callback_thread, name);
+
+  *timer_id = new_timer;
+  return 0;
+}
+
+// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_delete.html
+int timer_delete(timer_t id) {
+  int rc = __timer_delete(to_kernel_timer_id(id));
+  if (rc == -1) {
+    return -1;
+  }
+
+  PosixTimer* timer = reinterpret_cast<PosixTimer*>(id);
+
+  // Make sure the timer's thread has exited before we free the timer data.
+  if (timer->sigev_notify == SIGEV_THREAD) {
+    __timer_thread_stop(timer);
+  }
+
+  free(timer);
+
+  return 0;
+}
+
+// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_getoverrun.html
+int timer_gettime(timer_t id, itimerspec* ts) {
+  return __timer_gettime(to_kernel_timer_id(id), ts);
+}
+
+// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_getoverrun.html
+int timer_settime(timer_t id, int flags, const itimerspec* ts, itimerspec* ots) {
+  return __timer_settime(to_kernel_timer_id(id), flags, ts, ots);
+}
+
+// http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_getoverrun.html
+int timer_getoverrun(timer_t id) {
+  return __timer_getoverrun(to_kernel_timer_id(id));
+}
diff --git a/libc/bionic/pthread_atfork.cpp b/libc/bionic/pthread_atfork.cpp
index 5bf63fb..c0664a9 100644
--- a/libc/bionic/pthread_atfork.cpp
+++ b/libc/bionic/pthread_atfork.cpp
@@ -48,14 +48,12 @@
 static atfork_list_t gAtForkList = { NULL, NULL };
 
 void __bionic_atfork_run_prepare() {
-  // We will lock this here, and unlock it in the parent and child functions.
+  // We lock the atfork list here, unlock it in the parent, and reset it in the child.
   // This ensures that nobody can modify the handler array between the calls
   // to the prepare and parent/child handlers.
   //
-  // TODO: If a handler mucks with the list, it could cause problems.  Right
-  //       now it's ok because all they can do is add new items to the end
-  //       of the list, but if/when we implement cleanup in dlclose() things
-  //       will get more interesting...
+  // TODO: If a handler tries to mutate the list, they'll block. We should probably copy
+  // the list before forking, and have prepare, parent, and child all work on the consistent copy.
   pthread_mutex_lock(&gAtForkListMutex);
 
   // Call pthread_atfork() prepare handlers. POSIX states that the prepare
@@ -75,10 +73,7 @@
     }
   }
 
-  pthread_mutexattr_t attr;
-  pthread_mutexattr_init(&attr);
-  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
-  pthread_mutex_init(&gAtForkListMutex, &attr);
+  gAtForkListMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
 }
 
 void __bionic_atfork_run_parent() {
diff --git a/libc/bionic/pthread_attr.cpp b/libc/bionic/pthread_attr.cpp
index fdf2965..d597c7e 100644
--- a/libc/bionic/pthread_attr.cpp
+++ b/libc/bionic/pthread_attr.cpp
@@ -94,18 +94,21 @@
   return 0;
 }
 
-int pthread_attr_setstackaddr(pthread_attr_t*, void*) {
+#if !defined(__LP64__)
+// TODO: this exists only for backward binary compatibility on 32 bit platforms.
+extern "C" int pthread_attr_setstackaddr(pthread_attr_t*, void*) {
   // This was removed from POSIX.1-2008, and is not implemented on bionic.
   // Needed for ABI compatibility with the NDK.
   return ENOSYS;
 }
 
-int pthread_attr_getstackaddr(const pthread_attr_t* attr, void** stack_addr) {
+extern "C" int pthread_attr_getstackaddr(const pthread_attr_t* attr, void** stack_addr) {
   // This was removed from POSIX.1-2008.
   // Needed for ABI compatibility with the NDK.
   *stack_addr = (char*)attr->stack_base + attr->stack_size;
   return 0;
 }
+#endif // !defined(__LP64__)
 
 int pthread_attr_setstack(pthread_attr_t* attr, void* stack_base, size_t stack_size) {
   if ((stack_size & (PAGE_SIZE - 1) || stack_size < PTHREAD_STACK_MIN)) {
diff --git a/libc/bionic/pthread_cond.cpp b/libc/bionic/pthread_cond.cpp
index e67afba..c09e972 100644
--- a/libc/bionic/pthread_cond.cpp
+++ b/libc/bionic/pthread_cond.cpp
@@ -96,9 +96,6 @@
 }
 
 int pthread_condattr_destroy(pthread_condattr_t* attr) {
-  if (attr == NULL) {
-    return EINVAL;
-  }
   *attr = 0xdeada11d;
   return 0;
 }
@@ -112,10 +109,6 @@
 // XXX then the signal will be lost.
 
 int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* attr) {
-  if (cond == NULL) {
-    return EINVAL;
-  }
-
   if (attr != NULL) {
     cond->value = (*attr & COND_FLAGS_MASK);
   } else {
@@ -126,10 +119,6 @@
 }
 
 int pthread_cond_destroy(pthread_cond_t* cond) {
-  if (cond == NULL) {
-    return EINVAL;
-  }
-
   cond->value = 0xdeadc04d;
   return 0;
 }
@@ -138,10 +127,6 @@
 // pthread_cond_signal to atomically decrement the counter
 // then wake up 'counter' threads.
 static int __pthread_cond_pulse(pthread_cond_t* cond, int counter) {
-  if (__predict_false(cond == NULL)) {
-    return EINVAL;
-  }
-
   int flags = (cond->value & COND_FLAGS_MASK);
   while (true) {
     int old_value = cond->value;
@@ -185,7 +170,7 @@
   timespec* tsp;
 
   if (abstime != NULL) {
-    if (__timespec_to_absolute(&ts, abstime, clock) < 0) {
+    if (__timespec_from_absolute(&ts, abstime, clock) < 0) {
       return ETIMEDOUT;
     }
     tsp = &ts;
@@ -221,14 +206,14 @@
 extern "C" int pthread_cond_timedwait_monotonic_np(pthread_cond_t* cond, pthread_mutex_t* mutex, const timespec* abstime) {
   return __pthread_cond_timedwait(cond, mutex, abstime, CLOCK_MONOTONIC);
 }
-#endif // !defined(__LP64__)
 
-int pthread_cond_timedwait_relative_np(pthread_cond_t* cond, pthread_mutex_t* mutex, const timespec* reltime) {
+extern "C" int pthread_cond_timedwait_relative_np(pthread_cond_t* cond, pthread_mutex_t* mutex, const timespec* reltime) {
   return __pthread_cond_timedwait_relative(cond, mutex, reltime);
 }
 
-int pthread_cond_timeout_np(pthread_cond_t* cond, pthread_mutex_t* mutex, unsigned ms) {
+extern "C" int pthread_cond_timeout_np(pthread_cond_t* cond, pthread_mutex_t* mutex, unsigned ms) {
   timespec ts;
   timespec_from_ms(ts, ms);
   return __pthread_cond_timedwait_relative(cond, mutex, &ts);
 }
+#endif // !defined(__LP64__)
diff --git a/libc/bionic/pthread_detach.cpp b/libc/bionic/pthread_detach.cpp
index 95f11ac..a8608e3 100644
--- a/libc/bionic/pthread_detach.cpp
+++ b/libc/bionic/pthread_detach.cpp
@@ -44,6 +44,12 @@
     return 0; // Already being joined; silently do nothing, like glibc.
   }
 
+  if (thread->tid == 0) {
+    // Already exited; clean up.
+    _pthread_internal_remove_locked(thread.get());
+    return 0;
+  }
+
   thread->attr.flags |= PTHREAD_ATTR_FLAG_DETACHED;
   return 0;
 }
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index 31ed07c..41f4636 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -89,10 +89,9 @@
 __LIBC_HIDDEN__ extern pthread_internal_t* gThreadList;
 __LIBC_HIDDEN__ extern pthread_mutex_t gThreadListLock;
 
-__LIBC_HIDDEN__ int __timespec_to_absolute(timespec*, const timespec*, clockid_t);
+__LIBC_HIDDEN__ int __timespec_from_absolute(timespec*, const timespec*, clockid_t);
 
-/* needed by fork.c */
-__LIBC_HIDDEN__ extern void __timer_table_start_stop(int);
+/* Needed by fork. */
 __LIBC_HIDDEN__ extern void __bionic_atfork_run_prepare();
 __LIBC_HIDDEN__ extern void __bionic_atfork_run_child();
 __LIBC_HIDDEN__ extern void __bionic_atfork_run_parent();
diff --git a/libc/bionic/pthread_internals.cpp b/libc/bionic/pthread_internals.cpp
index 09c48dc..d4d6099 100644
--- a/libc/bionic/pthread_internals.cpp
+++ b/libc/bionic/pthread_internals.cpp
@@ -75,7 +75,7 @@
 
 // Initialize 'ts' with the difference between 'abstime' and the current time
 // according to 'clock'. Returns -1 if abstime already expired, or 0 otherwise.
-int __timespec_to_absolute(timespec* ts, const timespec* abstime, clockid_t clock) {
+int __timespec_from_absolute(timespec* ts, const timespec* abstime, clockid_t clock) {
   clock_gettime(clock, ts);
   ts->tv_sec  = abstime->tv_sec - ts->tv_sec;
   ts->tv_nsec = abstime->tv_nsec - ts->tv_nsec;
diff --git a/libc/bionic/pthread_mutex.cpp b/libc/bionic/pthread_mutex.cpp
index 0d992b3..a2e7b25 100644
--- a/libc/bionic/pthread_mutex.cpp
+++ b/libc/bionic/pthread_mutex.cpp
@@ -207,55 +207,42 @@
 
 int pthread_mutexattr_init(pthread_mutexattr_t *attr)
 {
-    if (attr) {
-        *attr = PTHREAD_MUTEX_DEFAULT;
-        return 0;
-    } else {
-        return EINVAL;
-    }
+    *attr = PTHREAD_MUTEX_DEFAULT;
+    return 0;
 }
 
 int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
 {
-    if (attr) {
-        *attr = -1;
-        return 0;
-    } else {
-        return EINVAL;
-    }
+    *attr = -1;
+    return 0;
 }
 
-int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type)
+int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type_p)
 {
-    if (attr) {
-        int  atype = (*attr & MUTEXATTR_TYPE_MASK);
+    int type = (*attr & MUTEXATTR_TYPE_MASK);
 
-         if (atype >= PTHREAD_MUTEX_NORMAL &&
-             atype <= PTHREAD_MUTEX_ERRORCHECK) {
-            *type = atype;
-            return 0;
-        }
+    if (type < PTHREAD_MUTEX_NORMAL || type > PTHREAD_MUTEX_ERRORCHECK) {
+        return EINVAL;
     }
-    return EINVAL;
+
+    *type_p = type;
+    return 0;
 }
 
 int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
 {
-    if (attr && type >= PTHREAD_MUTEX_NORMAL &&
-                type <= PTHREAD_MUTEX_ERRORCHECK ) {
-        *attr = (*attr & ~MUTEXATTR_TYPE_MASK) | type;
-        return 0;
+    if (type < PTHREAD_MUTEX_NORMAL || type > PTHREAD_MUTEX_ERRORCHECK ) {
+        return EINVAL;
     }
-    return EINVAL;
+
+    *attr = (*attr & ~MUTEXATTR_TYPE_MASK) | type;
+    return 0;
 }
 
 /* process-shared mutexes are not supported at the moment */
 
 int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int  pshared)
 {
-    if (!attr)
-        return EINVAL;
-
     switch (pshared) {
     case PTHREAD_PROCESS_PRIVATE:
         *attr &= ~MUTEXATTR_SHARED_MASK;
@@ -274,11 +261,7 @@
 }
 
 int pthread_mutexattr_getpshared(const pthread_mutexattr_t* attr, int* pshared) {
-    if (!attr || !pshared)
-        return EINVAL;
-
-    *pshared = (*attr & MUTEXATTR_SHARED_MASK) ? PTHREAD_PROCESS_SHARED
-                                               : PTHREAD_PROCESS_PRIVATE;
+    *pshared = (*attr & MUTEXATTR_SHARED_MASK) ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE;
     return 0;
 }
 
@@ -667,140 +650,133 @@
     return err;
 }
 
-/* initialize 'abstime' to the current time according to 'clock' plus 'msecs'
- * milliseconds.
- */
-static void __timespec_to_relative_msec(timespec* abstime, unsigned msecs, clockid_t clock) {
-    clock_gettime(clock, abstime);
-    abstime->tv_sec  += msecs/1000;
-    abstime->tv_nsec += (msecs%1000)*1000000;
-    if (abstime->tv_nsec >= 1000000000) {
-        abstime->tv_sec++;
-        abstime->tv_nsec -= 1000000000;
+static int __pthread_mutex_timedlock(pthread_mutex_t* mutex, const timespec* abs_timeout, clockid_t clock) {
+  timespec ts;
+
+  int mvalue = mutex->value;
+  int mtype  = (mvalue & MUTEX_TYPE_MASK);
+  int shared = (mvalue & MUTEX_SHARED_MASK);
+
+  // Handle common case first.
+  if (__predict_true(mtype == MUTEX_TYPE_BITS_NORMAL)) {
+    const int unlocked           = shared | MUTEX_STATE_BITS_UNLOCKED;
+    const int locked_uncontended = shared | MUTEX_STATE_BITS_LOCKED_UNCONTENDED;
+    const int locked_contended   = shared | MUTEX_STATE_BITS_LOCKED_CONTENDED;
+
+    // Fast path for uncontended lock. Note: MUTEX_TYPE_BITS_NORMAL is 0.
+    if (__bionic_cmpxchg(unlocked, locked_uncontended, &mutex->value) == 0) {
+      ANDROID_MEMBAR_FULL();
+      return 0;
     }
-}
 
-__LIBC_HIDDEN__
-int pthread_mutex_lock_timeout_np_impl(pthread_mutex_t *mutex, unsigned msecs)
-{
-    clockid_t        clock = CLOCK_MONOTONIC;
-    timespec  abstime;
-    timespec  ts;
-    int               mvalue, mtype, tid, shared;
+    // Loop while needed.
+    while (__bionic_swap(locked_contended, &mutex->value) != unlocked) {
+      if (__timespec_from_absolute(&ts, abs_timeout, clock) < 0) {
+        return ETIMEDOUT;
+      }
+      __futex_wait_ex(&mutex->value, shared, locked_contended, &ts);
+    }
+    ANDROID_MEMBAR_FULL();
+    return 0;
+  }
 
-    /* compute absolute expiration time */
-    __timespec_to_relative_msec(&abstime, msecs, clock);
+  // Do we already own this recursive or error-check mutex?
+  pid_t tid = __get_thread()->tid;
+  if (tid == MUTEX_OWNER_FROM_BITS(mvalue)) {
+    return _recursive_increment(mutex, mvalue, mtype);
+  }
 
+  // The following implements the same loop as pthread_mutex_lock_impl
+  // but adds checks to ensure that the operation never exceeds the
+  // absolute expiration time.
+  mtype |= shared;
+
+  // First try a quick lock.
+  if (mvalue == mtype) {
+    mvalue = MUTEX_OWNER_TO_BITS(tid) | mtype | MUTEX_STATE_BITS_LOCKED_UNCONTENDED;
+    if (__predict_true(__bionic_cmpxchg(mtype, mvalue, &mutex->value) == 0)) {
+      ANDROID_MEMBAR_FULL();
+      return 0;
+    }
     mvalue = mutex->value;
-    mtype  = (mvalue & MUTEX_TYPE_MASK);
-    shared = (mvalue & MUTEX_SHARED_MASK);
+  }
 
-    /* Handle common case first */
-    if ( __predict_true(mtype == MUTEX_TYPE_BITS_NORMAL) )
-    {
-        const int unlocked           = shared | MUTEX_STATE_BITS_UNLOCKED;
-        const int locked_uncontended = shared | MUTEX_STATE_BITS_LOCKED_UNCONTENDED;
-        const int locked_contended   = shared | MUTEX_STATE_BITS_LOCKED_CONTENDED;
-
-        /* fast path for uncontended lock. Note: MUTEX_TYPE_BITS_NORMAL is 0 */
-        if (__bionic_cmpxchg(unlocked, locked_uncontended, &mutex->value) == 0) {
-            ANDROID_MEMBAR_FULL();
-            return 0;
-        }
-
-        /* loop while needed */
-        while (__bionic_swap(locked_contended, &mutex->value) != unlocked) {
-            if (__timespec_to_absolute(&ts, &abstime, clock) < 0)
-                return EBUSY;
-
-            __futex_wait_ex(&mutex->value, shared, locked_contended, &ts);
-        }
+  while (true) {
+    // If the value is 'unlocked', try to acquire it directly.
+    // NOTE: put state to 2 since we know there is contention.
+    if (mvalue == mtype) { // Unlocked.
+      mvalue = MUTEX_OWNER_TO_BITS(tid) | mtype | MUTEX_STATE_BITS_LOCKED_CONTENDED;
+      if (__bionic_cmpxchg(mtype, mvalue, &mutex->value) == 0) {
         ANDROID_MEMBAR_FULL();
         return 0;
+      }
+      // The value changed before we could lock it. We need to check
+      // the time to avoid livelocks, reload the value, then loop again.
+      if (__timespec_from_absolute(&ts, abs_timeout, clock) < 0) {
+        return ETIMEDOUT;
+      }
+
+      mvalue = mutex->value;
+      continue;
     }
 
-    /* Do we already own this recursive or error-check mutex ? */
-    tid = __get_thread()->tid;
-    if ( tid == MUTEX_OWNER_FROM_BITS(mvalue) )
-        return _recursive_increment(mutex, mvalue, mtype);
-
-    /* the following implements the same loop than pthread_mutex_lock_impl
-     * but adds checks to ensure that the operation never exceeds the
-     * absolute expiration time.
-     */
-    mtype |= shared;
-
-    /* first try a quick lock */
-    if (mvalue == mtype) {
-        mvalue = MUTEX_OWNER_TO_BITS(tid) | mtype | MUTEX_STATE_BITS_LOCKED_UNCONTENDED;
-        if (__predict_true(__bionic_cmpxchg(mtype, mvalue, &mutex->value) == 0)) {
-            ANDROID_MEMBAR_FULL();
-            return 0;
-        }
+    // The value is locked. If 'uncontended', try to switch its state
+    // to 'contented' to ensure we get woken up later.
+    if (MUTEX_STATE_BITS_IS_LOCKED_UNCONTENDED(mvalue)) {
+      int newval = MUTEX_STATE_BITS_FLIP_CONTENTION(mvalue);
+      if (__bionic_cmpxchg(mvalue, newval, &mutex->value) != 0) {
+        // This failed because the value changed, reload it.
         mvalue = mutex->value;
+      } else {
+        // This succeeded, update mvalue.
+        mvalue = newval;
+      }
     }
 
-    for (;;) {
-        timespec ts;
-
-        /* if the value is 'unlocked', try to acquire it directly */
-        /* NOTE: put state to 2 since we know there is contention */
-        if (mvalue == mtype) /* unlocked */ {
-            mvalue = MUTEX_OWNER_TO_BITS(tid) | mtype | MUTEX_STATE_BITS_LOCKED_CONTENDED;
-            if (__bionic_cmpxchg(mtype, mvalue, &mutex->value) == 0) {
-                ANDROID_MEMBAR_FULL();
-                return 0;
-            }
-            /* the value changed before we could lock it. We need to check
-             * the time to avoid livelocks, reload the value, then loop again. */
-            if (__timespec_to_absolute(&ts, &abstime, clock) < 0)
-                return EBUSY;
-
-            mvalue = mutex->value;
-            continue;
-        }
-
-        /* The value is locked. If 'uncontended', try to switch its state
-         * to 'contented' to ensure we get woken up later. */
-        if (MUTEX_STATE_BITS_IS_LOCKED_UNCONTENDED(mvalue)) {
-            int newval = MUTEX_STATE_BITS_FLIP_CONTENTION(mvalue);
-            if (__bionic_cmpxchg(mvalue, newval, &mutex->value) != 0) {
-                /* this failed because the value changed, reload it */
-                mvalue = mutex->value;
-            } else {
-                /* this succeeded, update mvalue */
-                mvalue = newval;
-            }
-        }
-
-        /* check time and update 'ts' */
-        if (__timespec_to_absolute(&ts, &abstime, clock) < 0)
-            return EBUSY;
-
-        /* Only wait to be woken up if the state is '2', otherwise we'll
-         * simply loop right now. This can happen when the second cmpxchg
-         * in our loop failed because the mutex was unlocked by another
-         * thread.
-         */
-        if (MUTEX_STATE_BITS_IS_LOCKED_CONTENDED(mvalue)) {
-            if (__futex_wait_ex(&mutex->value, shared, mvalue, &ts) == -ETIMEDOUT) {
-                return EBUSY;
-            }
-            mvalue = mutex->value;
-        }
+    // Check time and update 'ts'.
+    if (__timespec_from_absolute(&ts, abs_timeout, clock) < 0) {
+      return ETIMEDOUT;
     }
-    /* NOTREACHED */
+
+    // Only wait to be woken up if the state is '2', otherwise we'll
+    // simply loop right now. This can happen when the second cmpxchg
+    // in our loop failed because the mutex was unlocked by another thread.
+    if (MUTEX_STATE_BITS_IS_LOCKED_CONTENDED(mvalue)) {
+      if (__futex_wait_ex(&mutex->value, shared, mvalue, &ts) == -ETIMEDOUT) {
+        return ETIMEDOUT;
+      }
+      mvalue = mutex->value;
+    }
+  }
+  /* NOTREACHED */
 }
 
-int pthread_mutex_lock_timeout_np(pthread_mutex_t *mutex, unsigned msecs)
-{
-    int err = pthread_mutex_lock_timeout_np_impl(mutex, msecs);
-    if (PTHREAD_DEBUG_ENABLED) {
-        if (!err) {
-            pthread_debug_mutex_lock_check(mutex);
-        }
+#if !defined(__LP64__)
+extern "C" int pthread_mutex_lock_timeout_np(pthread_mutex_t* mutex, unsigned ms) {
+  timespec abs_timeout;
+  clock_gettime(CLOCK_MONOTONIC, &abs_timeout);
+  abs_timeout.tv_sec  += ms / 1000;
+  abs_timeout.tv_nsec += (ms % 1000) * 1000000;
+  if (abs_timeout.tv_nsec >= 1000000000) {
+    abs_timeout.tv_sec++;
+    abs_timeout.tv_nsec -= 1000000000;
+  }
+
+  int err = __pthread_mutex_timedlock(mutex, &abs_timeout, CLOCK_MONOTONIC);
+  if (err == ETIMEDOUT) {
+    err = EBUSY;
+  }
+  if (PTHREAD_DEBUG_ENABLED) {
+    if (!err) {
+      pthread_debug_mutex_lock_check(mutex);
     }
-    return err;
+  }
+  return err;
+}
+#endif
+
+int pthread_mutex_timedlock(pthread_mutex_t* mutex, const timespec* abs_timeout) {
+  return __pthread_mutex_timedlock(mutex, abs_timeout, CLOCK_REALTIME);
 }
 
 int pthread_mutex_destroy(pthread_mutex_t *mutex)
diff --git a/libc/bionic/pthread_rwlock.cpp b/libc/bionic/pthread_rwlock.cpp
index 0182ef3..dfb4315 100644
--- a/libc/bionic/pthread_rwlock.cpp
+++ b/libc/bionic/pthread_rwlock.cpp
@@ -60,27 +60,18 @@
 
 int pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
 {
-    if (!attr)
-        return EINVAL;
-
     *attr = PTHREAD_PROCESS_PRIVATE;
     return 0;
 }
 
 int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
 {
-    if (!attr)
-        return EINVAL;
-
     *attr = -1;
     return 0;
 }
 
 int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int  pshared)
 {
-    if (!attr)
-        return EINVAL;
-
     switch (pshared) {
     case PTHREAD_PROCESS_PRIVATE:
     case PTHREAD_PROCESS_SHARED:
@@ -92,9 +83,6 @@
 }
 
 int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t* attr, int* pshared) {
-    if (!attr || !pshared)
-        return EINVAL;
-
     *pshared = *attr;
     return 0;
 }
@@ -107,9 +95,6 @@
     pthread_condattr_t    cond_attr0;
     int                   ret;
 
-    if (rwlock == NULL)
-        return EINVAL;
-
     if (attr && *attr == PTHREAD_PROCESS_SHARED) {
         lock_attr = &lock_attr0;
         pthread_mutexattr_init(lock_attr);
@@ -140,9 +125,6 @@
 
 int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
 {
-    if (rwlock == NULL)
-        return EINVAL;
-
     if (rwlock->numLocks > 0)
         return EBUSY;
 
@@ -197,10 +179,6 @@
 static int __pthread_rwlock_timedrdlock(pthread_rwlock_t* rwlock, const timespec* abs_timeout) {
   int ret = 0;
 
-  if (rwlock == NULL) {
-    return EINVAL;
-  }
-
   pthread_mutex_lock(&rwlock->lock);
   int tid = __get_thread()->tid;
   if (__predict_false(!read_precondition(rwlock, tid))) {
@@ -222,10 +200,6 @@
 static int __pthread_rwlock_timedwrlock(pthread_rwlock_t* rwlock, const timespec* abs_timeout) {
   int ret = 0;
 
-  if (rwlock == NULL) {
-    return EINVAL;
-  }
-
   pthread_mutex_lock(&rwlock->lock);
   int tid = __get_thread()->tid;
   if (__predict_false(!write_precondition(rwlock, tid))) {
@@ -256,9 +230,6 @@
 {
     int ret = 0;
 
-    if (rwlock == NULL)
-        return EINVAL;
-
     pthread_mutex_lock(&rwlock->lock);
     if (__predict_false(!read_precondition(rwlock, __get_thread()->tid)))
         ret = EBUSY;
@@ -281,9 +252,6 @@
 {
     int ret = 0;
 
-    if (rwlock == NULL)
-        return EINVAL;
-
     pthread_mutex_lock(&rwlock->lock);
     int tid = __get_thread()->tid;
     if (__predict_false(!write_precondition(rwlock, tid))) {
@@ -304,9 +272,6 @@
 {
     int  ret = 0;
 
-    if (rwlock == NULL)
-        return EINVAL;
-
     pthread_mutex_lock(&rwlock->lock);
 
     /* The lock must be held */
diff --git a/libc/bionic/pthread_setname_np.cpp b/libc/bionic/pthread_setname_np.cpp
index e0ecace..1ddf810 100644
--- a/libc/bionic/pthread_setname_np.cpp
+++ b/libc/bionic/pthread_setname_np.cpp
@@ -46,10 +46,6 @@
 int pthread_setname_np(pthread_t t, const char* thread_name) {
   ErrnoRestorer errno_restorer;
 
-  if (thread_name == NULL) {
-    return EINVAL;
-  }
-
   size_t thread_name_len = strlen(thread_name);
   if (thread_name_len >= MAX_TASK_COMM_LEN) {
     return ERANGE;
diff --git a/libc/bionic/timer.cpp b/libc/bionic/timer.cpp
deleted file mode 100644
index ed73821..0000000
--- a/libc/bionic/timer.cpp
+++ /dev/null
@@ -1,636 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include "pthread_internal.h"
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-
-extern int __pthread_cond_timedwait(pthread_cond_t*, pthread_mutex_t*, const timespec*, clockid_t);
-extern int __pthread_cond_timedwait_relative(pthread_cond_t*, pthread_mutex_t*, const timespec*);
-
-// Normal (i.e. non-SIGEV_THREAD) timers are created directly by the kernel
-// and are passed as is to/from the caller.
-//
-// This file also implements the support required for SIGEV_THREAD ("POSIX interval")
-// timers. See the following pages for additional details:
-//
-// www.opengroup.org/onlinepubs/000095399/functions/timer_create.html
-// www.opengroup.org/onlinepubs/000095399/functions/timer_settime.html
-// www.opengroup.org/onlinepubs/000095399/functions/xsh_chap02_04.html#tag_02_04_01
-//
-// The Linux kernel doesn't support these, so we need to implement them in the
-// C library. We use a very basic scheme where each timer is associated to a
-// thread that will loop, waiting for timeouts or messages from the program
-// corresponding to calls to timer_settime() and timer_delete().
-//
-// Note also an important thing: Posix mandates that in the case of fork(),
-// the timers of the child process should be disarmed, but not deleted.
-// this is implemented by providing a fork() wrapper (see bionic/fork.c) which
-// stops all timers before the fork, and only re-start them in case of error
-// or in the parent process.
-//
-// This stop/start is implemented by the __timer_table_start_stop() function
-// below.
-//
-// A SIGEV_THREAD timer ID will always have its TIMER_ID_WRAP_BIT
-// set to 1. In this implementation, this is always bit 31, which is
-// guaranteed to never be used by kernel-provided timer ids
-//
-// (See code in <kernel>/lib/idr.c, used to manage IDs, to see why.)
-
-#define  TIMER_ID_WRAP_BIT        0x80000000
-#define  TIMER_ID_WRAP(id)        ((timer_t)((id) |  TIMER_ID_WRAP_BIT))
-#define  TIMER_ID_UNWRAP(id)      ((timer_t)((id) & ~TIMER_ID_WRAP_BIT))
-#define  TIMER_ID_IS_WRAPPED(id)  (((id) & TIMER_ID_WRAP_BIT) != 0)
-
-/* this value is used internally to indicate a 'free' or 'zombie'
- * thr_timer structure. Here, 'zombie' means that timer_delete()
- * has been called, but that the corresponding thread hasn't
- * exited yet.
- */
-#define  TIMER_ID_NONE            ((timer_t)0xffffffff)
-
-/* True iff a timer id is valid */
-#define  TIMER_ID_IS_VALID(id)    ((id) != TIMER_ID_NONE)
-
-/* the maximum value of overrun counters */
-#define  DELAYTIMER_MAX    0x7fffffff
-
-typedef struct thr_timer          thr_timer_t;
-typedef struct thr_timer_table    thr_timer_table_t;
-
-/* The Posix spec says the function receives an unsigned parameter, but
- * it's really a 'union sigval' a.k.a. sigval_t */
-typedef void (*thr_timer_func_t)( sigval_t );
-
-struct thr_timer {
-    thr_timer_t*       next;     /* next in free list */
-    timer_t            id;       /* TIMER_ID_NONE iff free or dying */
-    clockid_t          clock;
-    pthread_t          thread;
-    pthread_attr_t     attributes;
-    thr_timer_func_t   callback;
-    sigval_t           value;
-
-    /* the following are used to communicate between
-     * the timer thread and the timer_XXX() functions
-     */
-    pthread_mutex_t           mutex;     /* lock */
-    pthread_cond_t            cond;      /* signal a state change to thread */
-    int volatile              done;      /* set by timer_delete */
-    int volatile              stopped;   /* set by _start_stop() */
-    timespec volatile  expires;   /* next expiration time, or 0 */
-    timespec volatile  period;    /* reload value, or 0 */
-    int volatile              overruns;  /* current number of overruns */
-};
-
-#define  MAX_THREAD_TIMERS  32
-
-struct thr_timer_table {
-    pthread_mutex_t  lock;
-    thr_timer_t*     free_timer;
-    thr_timer_t      timers[ MAX_THREAD_TIMERS ];
-};
-
-/** GLOBAL TABLE OF THREAD TIMERS
- **/
-
-static void
-thr_timer_table_init( thr_timer_table_t*  t )
-{
-    int  nn;
-
-    memset(t, 0, sizeof *t);
-    pthread_mutex_init( &t->lock, NULL );
-
-    for (nn = 0; nn < MAX_THREAD_TIMERS; nn++)
-        t->timers[nn].id = TIMER_ID_NONE;
-
-    t->free_timer = &t->timers[0];
-    for (nn = 1; nn < MAX_THREAD_TIMERS; nn++)
-        t->timers[nn-1].next = &t->timers[nn];
-}
-
-
-static thr_timer_t*
-thr_timer_table_alloc( thr_timer_table_t*  t )
-{
-    thr_timer_t*  timer;
-
-    if (t == NULL)
-        return NULL;
-
-    pthread_mutex_lock(&t->lock);
-    timer = t->free_timer;
-    if (timer != NULL) {
-        t->free_timer = timer->next;
-        timer->next   = NULL;
-        timer->id     = TIMER_ID_WRAP((timer - t->timers));
-    }
-    pthread_mutex_unlock(&t->lock);
-    return timer;
-}
-
-
-static void
-thr_timer_table_free( thr_timer_table_t*  t, thr_timer_t*  timer )
-{
-    pthread_mutex_lock( &t->lock );
-    timer->id     = TIMER_ID_NONE;
-    timer->thread = 0;
-    timer->next   = t->free_timer;
-    t->free_timer = timer;
-    pthread_mutex_unlock( &t->lock );
-}
-
-
-static void thr_timer_table_start_stop(thr_timer_table_t* t, int stop) {
-  if (t == NULL) {
-    return;
-  }
-
-  pthread_mutex_lock(&t->lock);
-  for (int nn = 0; nn < MAX_THREAD_TIMERS; ++nn) {
-    thr_timer_t*  timer  = &t->timers[nn];
-    if (TIMER_ID_IS_VALID(timer->id)) {
-      // Tell the thread to start/stop.
-      pthread_mutex_lock(&timer->mutex);
-      timer->stopped = stop;
-      pthread_cond_signal( &timer->cond );
-      pthread_mutex_unlock(&timer->mutex);
-    }
-  }
-  pthread_mutex_unlock(&t->lock);
-}
-
-
-/* convert a timer_id into the corresponding thr_timer_t* pointer
- * returns NULL if the id is not wrapped or is invalid/free
- */
-static thr_timer_t*
-thr_timer_table_from_id( thr_timer_table_t*  t,
-                         timer_t             id,
-                         int                 remove )
-{
-    unsigned      index;
-    thr_timer_t*  timer;
-
-    if (t == NULL || !TIMER_ID_IS_WRAPPED(id))
-        return NULL;
-
-    index = (unsigned) TIMER_ID_UNWRAP(id);
-    if (index >= MAX_THREAD_TIMERS)
-        return NULL;
-
-    pthread_mutex_lock(&t->lock);
-
-    timer = &t->timers[index];
-
-    if (!TIMER_ID_IS_VALID(timer->id)) {
-        timer = NULL;
-    } else {
-        /* if we're removing this timer, clear the id
-         * right now to prevent another thread to
-         * use the same id after the unlock */
-        if (remove)
-            timer->id = TIMER_ID_NONE;
-    }
-    pthread_mutex_unlock(&t->lock);
-
-    return timer;
-}
-
-/* the static timer table - we only create it if the process
- * really wants to use SIGEV_THREAD timers, which should be
- * pretty infrequent
- */
-
-static pthread_once_t __timer_table_once = PTHREAD_ONCE_INIT;
-static thr_timer_table_t* __timer_table;
-
-static void __timer_table_init(void) {
-  __timer_table = reinterpret_cast<thr_timer_table_t*>(calloc(1, sizeof(*__timer_table)));
-  if (__timer_table != NULL) {
-    thr_timer_table_init(__timer_table);
-  }
-}
-
-static thr_timer_table_t* __timer_table_get(void) {
-  pthread_once(&__timer_table_once, __timer_table_init);
-  return __timer_table;
-}
-
-/** POSIX THREAD TIMERS CLEANUP ON FORK
- **
- ** this should be called from the 'fork()' wrapper to stop/start
- ** all active thread timers. this is used to implement a Posix
- ** requirements: the timers of fork child processes must be
- ** disarmed but not deleted.
- **/
-void __timer_table_start_stop(int stop) {
-  // We access __timer_table directly so we don't create it if it doesn't yet exist.
-  thr_timer_table_start_stop(__timer_table, stop);
-}
-
-static thr_timer_t*
-thr_timer_from_id( timer_t   id )
-{
-    thr_timer_table_t*  table = __timer_table_get();
-    thr_timer_t*        timer = thr_timer_table_from_id( table, id, 0 );
-
-    return timer;
-}
-
-
-static __inline__ void
-thr_timer_lock( thr_timer_t*  t )
-{
-    pthread_mutex_lock(&t->mutex);
-}
-
-static __inline__ void
-thr_timer_unlock( thr_timer_t*  t )
-{
-    pthread_mutex_unlock(&t->mutex);
-}
-
-
-static __inline__ void timespec_add(timespec* a, const timespec* b) {
-  a->tv_sec  += b->tv_sec;
-  a->tv_nsec += b->tv_nsec;
-  if (a->tv_nsec >= 1000000000) {
-    a->tv_nsec -= 1000000000;
-    a->tv_sec  += 1;
-  }
-}
-
-static __inline__ void timespec_sub(timespec* a, const timespec* b) {
-  a->tv_sec  -= b->tv_sec;
-  a->tv_nsec -= b->tv_nsec;
-  if (a->tv_nsec < 0) {
-    a->tv_nsec += 1000000000;
-    a->tv_sec  -= 1;
-  }
-}
-
-static __inline__ void timespec_zero(timespec* a) {
-  a->tv_sec = a->tv_nsec = 0;
-}
-
-static __inline__ int timespec_is_zero(const timespec* a) {
-  return (a->tv_sec == 0 && a->tv_nsec == 0);
-}
-
-static __inline__ int timespec_cmp(const timespec* a, const timespec* b) {
-  if (a->tv_sec  < b->tv_sec)  return -1;
-  if (a->tv_sec  > b->tv_sec)  return +1;
-  if (a->tv_nsec < b->tv_nsec) return -1;
-  if (a->tv_nsec > b->tv_nsec) return +1;
-  return 0;
-}
-
-static __inline__ int timespec_cmp0(const timespec* a) {
-  if (a->tv_sec < 0) return -1;
-  if (a->tv_sec > 0) return +1;
-  if (a->tv_nsec < 0) return -1;
-  if (a->tv_nsec > 0) return +1;
-  return 0;
-}
-
-/** POSIX TIMERS APIs */
-
-extern "C" int __timer_create(clockid_t, sigevent*, timer_t*);
-extern "C" int __timer_delete(timer_t);
-extern "C" int __timer_gettime(timer_t, itimerspec*);
-extern "C" int __timer_settime(timer_t, int, const itimerspec*, itimerspec*);
-extern "C" int __timer_getoverrun(timer_t);
-
-static void* timer_thread_start(void*);
-
-int timer_create(clockid_t clock_id, sigevent* evp, timer_t* timer_id) {
-  // If not a SIGEV_THREAD timer, the kernel can handle it without our help.
-  if (__predict_true(evp == NULL || evp->sigev_notify != SIGEV_THREAD)) {
-    return __timer_create(clock_id, evp, timer_id);
-  }
-
-  // Check arguments.
-  if (evp->sigev_notify_function == NULL) {
-    errno = EINVAL;
-    return -1;
-  }
-
-  // Check that the clock id is supported by the kernel.
-  timespec dummy;
-  if (clock_gettime(clock_id, &dummy) < 0 && errno == EINVAL) {
-    return -1;
-  }
-
-  // Create a new timer and its thread.
-  // TODO: use a single global thread for all timers.
-  thr_timer_table_t* table = __timer_table_get();
-  thr_timer_t* timer = thr_timer_table_alloc(table);
-  if (timer == NULL) {
-    errno = ENOMEM;
-    return -1;
-  }
-
-  // Copy the thread attributes.
-  if (evp->sigev_notify_attributes == NULL) {
-    pthread_attr_init(&timer->attributes);
-  } else {
-    timer->attributes = ((pthread_attr_t*) evp->sigev_notify_attributes)[0];
-  }
-
-  // Posix says that the default is PTHREAD_CREATE_DETACHED and
-  // that PTHREAD_CREATE_JOINABLE has undefined behavior.
-  // So simply always use DETACHED :-)
-  pthread_attr_setdetachstate(&timer->attributes, PTHREAD_CREATE_DETACHED);
-
-  timer->callback = evp->sigev_notify_function;
-  timer->value = evp->sigev_value;
-  timer->clock = clock_id;
-
-  pthread_mutex_init(&timer->mutex, NULL);
-  pthread_cond_init(&timer->cond, NULL);
-
-  timer->done = 0;
-  timer->stopped = 0;
-  timer->expires.tv_sec = timer->expires.tv_nsec = 0;
-  timer->period.tv_sec = timer->period.tv_nsec  = 0;
-  timer->overruns = 0;
-
-  // Create the thread.
-  int rc = pthread_create(&timer->thread, &timer->attributes, timer_thread_start, timer);
-  if (rc != 0) {
-    thr_timer_table_free(table, timer);
-    errno = rc;
-    return -1;
-  }
-
-  *timer_id = timer->id;
-  return 0;
-}
-
-
-int
-timer_delete( timer_t  id )
-{
-    if ( __predict_true(!TIMER_ID_IS_WRAPPED(id)) )
-        return __timer_delete( id );
-    else
-    {
-        thr_timer_table_t*  table = __timer_table_get();
-        thr_timer_t*        timer = thr_timer_table_from_id(table, id, 1);
-
-        if (timer == NULL) {
-            errno = EINVAL;
-            return -1;
-        }
-
-        /* tell the timer's thread to stop */
-        thr_timer_lock(timer);
-        timer->done = 1;
-        pthread_cond_signal( &timer->cond );
-        thr_timer_unlock(timer);
-
-        /* NOTE: the thread will call __timer_table_free() to free the
-         * timer object. the '1' parameter to thr_timer_table_from_id
-         * above ensured that the object and its timer_id cannot be
-         * reused before that.
-         */
-        return 0;
-    }
-}
-
-/* return the relative time until the next expiration, or 0 if
- * the timer is disarmed */
-static void timer_gettime_internal(thr_timer_t* timer, itimerspec* spec) {
-  timespec diff = const_cast<timespec&>(timer->expires);
-  if (!timespec_is_zero(&diff)) {
-    timespec now;
-
-    clock_gettime(timer->clock, &now);
-    timespec_sub(&diff, &now);
-
-    /* in case of overrun, return 0 */
-    if (timespec_cmp0(&diff) < 0) {
-      timespec_zero(&diff);
-    }
-  }
-
-  spec->it_value = diff;
-  spec->it_interval = const_cast<timespec&>(timer->period);
-}
-
-
-int timer_gettime(timer_t id, itimerspec* ospec) {
-    if (ospec == NULL) {
-        errno = EINVAL;
-        return -1;
-    }
-
-    if ( __predict_true(!TIMER_ID_IS_WRAPPED(id)) ) {
-        return __timer_gettime( id, ospec );
-    } else {
-        thr_timer_t*  timer = thr_timer_from_id(id);
-
-        if (timer == NULL) {
-            errno = EINVAL;
-            return -1;
-        }
-        thr_timer_lock(timer);
-        timer_gettime_internal( timer, ospec );
-        thr_timer_unlock(timer);
-    }
-    return 0;
-}
-
-
-int
-timer_settime(timer_t id, int flags, const itimerspec* spec, itimerspec* ospec) {
-    if (spec == NULL) {
-        errno = EINVAL;
-        return -1;
-    }
-
-    if ( __predict_true(!TIMER_ID_IS_WRAPPED(id)) ) {
-        return __timer_settime( id, flags, spec, ospec );
-    } else {
-        thr_timer_t*        timer = thr_timer_from_id(id);
-        timespec     expires, now;
-
-        if (timer == NULL) {
-            errno = EINVAL;
-            return -1;
-        }
-        thr_timer_lock(timer);
-
-        /* return current timer value if ospec isn't NULL */
-        if (ospec != NULL) {
-            timer_gettime_internal(timer, ospec );
-        }
-
-        /* compute next expiration time. note that if the
-         * new it_interval is 0, we should disarm the timer
-         */
-        expires = spec->it_value;
-        if (!timespec_is_zero(&expires)) {
-            clock_gettime( timer->clock, &now );
-            if (!(flags & TIMER_ABSTIME)) {
-                timespec_add(&expires, &now);
-            } else {
-                if (timespec_cmp(&expires, &now) < 0)
-                    expires = now;
-            }
-        }
-        const_cast<timespec&>(timer->expires) = expires;
-        const_cast<timespec&>(timer->period) = spec->it_interval;
-        thr_timer_unlock( timer );
-
-        /* signal the change to the thread */
-        pthread_cond_signal( &timer->cond );
-    }
-    return 0;
-}
-
-
-int
-timer_getoverrun(timer_t  id)
-{
-    if ( __predict_true(!TIMER_ID_IS_WRAPPED(id)) ) {
-        return __timer_getoverrun( id );
-    } else {
-        thr_timer_t*  timer = thr_timer_from_id(id);
-        int           result;
-
-        if (timer == NULL) {
-            errno = EINVAL;
-            return -1;
-        }
-
-        thr_timer_lock(timer);
-        result = timer->overruns;
-        thr_timer_unlock(timer);
-
-        return result;
-    }
-}
-
-
-static void* timer_thread_start(void* arg) {
-  thr_timer_t* timer = reinterpret_cast<thr_timer_t*>(arg);
-
-  thr_timer_lock(timer);
-
-  // Give this thread a meaningful name.
-  char name[32];
-  snprintf(name, sizeof(name), "POSIX interval timer 0x%08x", timer->id);
-  pthread_setname_np(pthread_self(), name);
-
-  // We loop until timer->done is set in timer_delete().
-  while (!timer->done) {
-    timespec expires = const_cast<timespec&>(timer->expires);
-    timespec period = const_cast<timespec&>(timer->period);
-
-    // If the timer is stopped or disarmed, wait indefinitely
-    // for a state change from timer_settime/_delete/_start_stop.
-    if (timer->stopped || timespec_is_zero(&expires)) {
-      pthread_cond_wait(&timer->cond, &timer->mutex);
-      continue;
-    }
-
-    // Otherwise, we need to do a timed wait until either a
-    // state change of the timer expiration time.
-    timespec now;
-    clock_gettime(timer->clock, &now);
-
-    if (timespec_cmp(&expires, &now) > 0) {
-      // Cool, there was no overrun, so compute the
-      // relative timeout as 'expires - now', then wait.
-      timespec diff = expires;
-      timespec_sub(&diff, &now);
-
-      int ret = __pthread_cond_timedwait_relative(&timer->cond, &timer->mutex, &diff);
-
-      // If we didn't time out, it means that a state change
-      // occurred, so loop to take care of it.
-      if (ret != ETIMEDOUT) {
-        continue;
-      }
-    } else {
-      // Overrun was detected before we could wait!
-      if (!timespec_is_zero(&period)) {
-        // For periodic timers, compute total overrun count.
-        do {
-          timespec_add(&expires, &period);
-          if (timer->overruns < DELAYTIMER_MAX) {
-            timer->overruns += 1;
-          }
-        } while (timespec_cmp(&expires, &now) < 0);
-
-        // Backtrack the last one, because we're going to
-        // add the same value just a bit later.
-        timespec_sub(&expires, &period);
-      } else {
-        // For non-periodic timers, things are simple.
-        timer->overruns = 1;
-      }
-    }
-
-    // If we get here, a timeout was detected.
-    // First reload/disarm the timer as needed.
-    if (!timespec_is_zero(&period)) {
-      timespec_add(&expires, &period);
-    } else {
-      timespec_zero(&expires);
-    }
-    const_cast<timespec&>(timer->expires) = expires;
-
-    // Now call the timer callback function. Release the
-    // lock to allow the function to modify the timer setting
-    // or call timer_getoverrun().
-    // NOTE: at this point we trust the callback not to be a
-    //      total moron and pthread_kill() the timer thread
-    thr_timer_unlock(timer);
-    timer->callback(timer->value);
-    thr_timer_lock(timer);
-
-    // Now clear the overruns counter. it only makes sense
-    // within the callback.
-    timer->overruns = 0;
-  }
-
-  thr_timer_unlock(timer);
-
-  // Free the timer object.
-  thr_timer_table_free(__timer_table_get(), timer);
-
-  return NULL;
-}
diff --git a/libc/include/pthread.h b/libc/include/pthread.h
index 6330a6f..a2a6789 100644
--- a/libc/include/pthread.h
+++ b/libc/include/pthread.h
@@ -174,7 +174,7 @@
 int pthread_mutex_destroy(pthread_mutex_t*) __nonnull((1));
 int pthread_mutex_init(pthread_mutex_t*, const pthread_mutexattr_t*) __nonnull((1));
 int pthread_mutex_lock(pthread_mutex_t*) __nonnull((1));
-int pthread_mutex_timedlock(pthread_mutex_t*, struct timespec*) __nonnull((1, 2));
+int pthread_mutex_timedlock(pthread_mutex_t*, const struct timespec*) __nonnull((1, 2));
 int pthread_mutex_trylock(pthread_mutex_t*) __nonnull((1));
 int pthread_mutex_unlock(pthread_mutex_t*) __nonnull((1));
 
@@ -243,25 +243,12 @@
 // TODO: Remove them once chromium_org / NFC have switched over.
 int pthread_cond_timedwait_monotonic_np(pthread_cond_t*, pthread_mutex_t*, const struct timespec*);
 int pthread_cond_timedwait_monotonic(pthread_cond_t*, pthread_mutex_t*, const struct timespec*);
-#define HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC 1
 
-/*
- * Like pthread_cond_timedwait except 'reltime' is relative to the current time.
- * TODO: not like glibc; include in LP64?
- */
-int pthread_cond_timedwait_relative_np(pthread_cond_t*, pthread_mutex_t*, const struct timespec*);
-#define HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE 1
+int pthread_cond_timedwait_relative_np(pthread_cond_t*, pthread_mutex_t*, const struct timespec*) /* TODO: __attribute__((deprecated("use pthread_cond_timedwait instead")))*/;
+#define HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE 1 /* TODO: stop defining this to push LP32 off this API sooner. */
+int pthread_cond_timeout_np(pthread_cond_t*, pthread_mutex_t*, unsigned) /* TODO: __attribute__((deprecated("use pthread_cond_timedwait instead")))*/;
 
-/* TODO: not like glibc; include in LP64? */
-int pthread_cond_timeout_np(pthread_cond_t*, pthread_mutex_t*, unsigned);
-
-/* Like pthread_mutex_lock(), but will wait up to 'msecs' milli-seconds
- * before returning. Same return values as pthread_mutex_trylock though, i.e.
- * returns EBUSY if the lock could not be acquired after the timeout expired.
- *
- * TODO: replace with pthread_mutex_timedlock_np for LP64.
- */
-int pthread_mutex_lock_timeout_np(pthread_mutex_t*, unsigned);
+int pthread_mutex_lock_timeout_np(pthread_mutex_t*, unsigned) __attribute__((deprecated("use pthread_mutex_timedlock instead")));
 
 #endif /* !defined(__LP64__) */
 
diff --git a/libc/include/signal.h b/libc/include/signal.h
index 29bef57..267f3e6 100644
--- a/libc/include/signal.h
+++ b/libc/include/signal.h
@@ -66,6 +66,8 @@
 typedef __sighandler_t sig_t; /* BSD compatibility. */
 typedef __sighandler_t sighandler_t; /* glibc compatibility. */
 
+#define si_timerid si_tid /* glibc compatibility. */
+
 #if defined(__LP64__)
 
 struct sigaction {
diff --git a/libc/include/sys/types.h b/libc/include/sys/types.h
index 5340585..f8261f4 100644
--- a/libc/include/sys/types.h
+++ b/libc/include/sys/types.h
@@ -69,7 +69,7 @@
 typedef __uint32_t __nlink_t;
 typedef __nlink_t nlink_t;
 
-typedef int __timer_t;
+typedef void* __timer_t;
 typedef __timer_t timer_t;
 
 typedef __int32_t __suseconds_t;
diff --git a/libc/tools/generate-NOTICE.py b/libc/tools/generate-NOTICE.py
index 5115317..3fad656 100755
--- a/libc/tools/generate-NOTICE.py
+++ b/libc/tools/generate-NOTICE.py
@@ -25,7 +25,7 @@
     return False
 
 def IsAutoGenerated(content):
-    if "generated by gensyscalls.py" in content or "generated by genserv.py" in content:
+    if "Generated by gensyscalls.py" in content or "generated by genserv.py" in content:
         return True
     if "This header was automatically generated from a Linux kernel header" in content:
         return True
diff --git a/libm/NOTICE b/libm/NOTICE
index 709563c..1229447 100644
--- a/libm/NOTICE
+++ b/libm/NOTICE
@@ -836,6 +836,32 @@
 
 -------------------------------------------------------------------
 
+Copyright (c) 2010 The NetBSD Foundation, 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:
+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.
+
+THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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) 2011 David Schultz
 All rights reserved.
 
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index d481a1d..9e7a9bc 100644
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -19,12 +19,15 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <limits.h>
+#include <malloc.h>
 #include <pthread.h>
 #include <signal.h>
 #include <sys/mman.h>
 #include <time.h>
 #include <unistd.h>
 
+#include "ScopedSignalHandler.h"
+
 TEST(pthread, pthread_key_create) {
   pthread_key_t key;
   ASSERT_EQ(0, pthread_key_create(&key, NULL));
@@ -337,14 +340,8 @@
 }
 
 TEST(pthread, pthread_kill__in_signal_handler) {
-  struct sigaction action;
-  struct sigaction original_action;
-  sigemptyset(&action.sa_mask);
-  action.sa_flags = 0;
-  action.sa_handler = pthread_kill__in_signal_handler_helper;
-  ASSERT_EQ(0, sigaction(SIGALRM, &action, &original_action));
+  ScopedSignalHandler ssh(SIGALRM, pthread_kill__in_signal_handler_helper);
   ASSERT_EQ(0, pthread_kill(pthread_self(), SIGALRM));
-  ASSERT_EQ(0, sigaction(SIGALRM, &original_action, NULL));
 }
 
 TEST(pthread, pthread_detach__no_such_thread) {
@@ -354,6 +351,35 @@
   ASSERT_EQ(ESRCH, pthread_detach(dead_thread));
 }
 
+TEST(pthread, pthread_detach__leak) {
+  size_t initial_bytes = mallinfo().uordblks;
+
+  pthread_attr_t attr;
+  ASSERT_EQ(0, pthread_attr_init(&attr));
+  ASSERT_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE));
+
+  std::vector<pthread_t> threads;
+  for (size_t i = 0; i < 32; ++i) {
+    pthread_t t;
+    ASSERT_EQ(0, pthread_create(&t, &attr, IdFn, NULL));
+    threads.push_back(t);
+  }
+
+  sleep(1);
+
+  for (size_t i = 0; i < 32; ++i) {
+    ASSERT_EQ(0, pthread_detach(threads[i])) << i;
+  }
+
+  size_t final_bytes = mallinfo().uordblks;
+
+  int leaked_bytes = (final_bytes - initial_bytes);
+
+  // User code (like this test) doesn't know how large pthread_internal_t is.
+  // We can be pretty sure it's more than 128 bytes.
+  ASSERT_LT(leaked_bytes, 32 /*threads*/ * 128 /*bytes*/);
+}
+
 TEST(pthread, pthread_getcpuclockid__clock_gettime) {
   pthread_t t;
   ASSERT_EQ(0, pthread_create(&t, NULL, SleepFn, reinterpret_cast<void*>(5)));
@@ -653,3 +679,26 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif // __BIONIC__
 }
+
+TEST(pthread, pthread_mutex_timedlock) {
+  pthread_mutex_t m;
+  ASSERT_EQ(0, pthread_mutex_init(&m, NULL));
+
+  // If the mutex is already locked, pthread_mutex_timedlock should time out.
+  ASSERT_EQ(0, pthread_mutex_lock(&m));
+
+  timespec ts;
+  ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
+  ts.tv_nsec += 1;
+  ASSERT_EQ(ETIMEDOUT, pthread_mutex_timedlock(&m, &ts));
+
+  // If the mutex is unlocked, pthread_mutex_timedlock should succeed.
+  ASSERT_EQ(0, pthread_mutex_unlock(&m));
+
+  ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
+  ts.tv_nsec += 1;
+  ASSERT_EQ(0, pthread_mutex_timedlock(&m, &ts));
+
+  ASSERT_EQ(0, pthread_mutex_unlock(&m));
+  ASSERT_EQ(0, pthread_mutex_destroy(&m));
+}
diff --git a/tests/time_test.cpp b/tests/time_test.cpp
index 869ee4b..7a2116b 100644
--- a/tests/time_test.cpp
+++ b/tests/time_test.cpp
@@ -14,11 +14,16 @@
  * limitations under the License.
  */
 
-#include <sys/cdefs.h>
+#include <time.h>
+
+#include <errno.h>
 #include <features.h>
 #include <gtest/gtest.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
 
-#include <time.h>
+#include "ScopedSignalHandler.h"
 
 #if defined(__BIONIC__) // mktime_tz is a bionic extension.
 #include <libc/private/bionic_time.h>
@@ -92,3 +97,208 @@
 #endif
 #endif
 }
+
+void SetTime(timer_t t, time_t value_s, time_t value_ns, time_t interval_s, time_t interval_ns) {
+  itimerspec ts;
+  ts.it_value.tv_sec = value_s;
+  ts.it_value.tv_nsec = value_ns;
+  ts.it_interval.tv_sec = interval_s;
+  ts.it_interval.tv_nsec = interval_ns;
+  ASSERT_EQ(0, timer_settime(t, TIMER_ABSTIME, &ts, NULL));
+}
+
+static void NoOpNotifyFunction(sigval_t) {
+}
+
+TEST(time, timer_create) {
+  sigevent_t se;
+  memset(&se, 0, sizeof(se));
+  se.sigev_notify = SIGEV_THREAD;
+  se.sigev_notify_function = NoOpNotifyFunction;
+  timer_t timer_id;
+  ASSERT_EQ(0, timer_create(CLOCK_MONOTONIC, &se, &timer_id));
+
+  int pid = fork();
+  ASSERT_NE(-1, pid) << strerror(errno);
+
+  if (pid == 0) {
+    // Timers are not inherited by the child.
+    ASSERT_EQ(-1, timer_delete(timer_id));
+    ASSERT_EQ(EINVAL, errno);
+    _exit(0);
+  }
+
+  int status;
+  ASSERT_EQ(pid, waitpid(pid, &status, 0));
+  ASSERT_TRUE(WIFEXITED(status));
+  ASSERT_EQ(0, WEXITSTATUS(status));
+
+  ASSERT_EQ(0, timer_delete(timer_id));
+}
+
+static int timer_create_SIGEV_SIGNAL_signal_handler_invocation_count = 0;
+static void timer_create_SIGEV_SIGNAL_signal_handler(int signal_number) {
+  ++timer_create_SIGEV_SIGNAL_signal_handler_invocation_count;
+  ASSERT_EQ(SIGUSR1, signal_number);
+}
+
+TEST(time, timer_create_SIGEV_SIGNAL) {
+  sigevent_t se;
+  memset(&se, 0, sizeof(se));
+  se.sigev_notify = SIGEV_SIGNAL;
+  se.sigev_signo = SIGUSR1;
+
+  timer_t timer_id;
+  ASSERT_EQ(0, timer_create(CLOCK_MONOTONIC, &se, &timer_id));
+
+  ScopedSignalHandler ssh(SIGUSR1, timer_create_SIGEV_SIGNAL_signal_handler);
+
+  ASSERT_EQ(0, timer_create_SIGEV_SIGNAL_signal_handler_invocation_count);
+
+  itimerspec ts;
+  ts.it_value.tv_sec =  0;
+  ts.it_value.tv_nsec = 1;
+  ts.it_interval.tv_sec = 0;
+  ts.it_interval.tv_nsec = 0;
+  ASSERT_EQ(0, timer_settime(timer_id, TIMER_ABSTIME, &ts, NULL));
+
+  usleep(500000);
+  ASSERT_EQ(1, timer_create_SIGEV_SIGNAL_signal_handler_invocation_count);
+}
+
+struct Counter {
+  volatile int value;
+  timer_t timer_id;
+  sigevent_t se;
+
+  Counter(void (*fn)(sigval_t)) : value(0) {
+    memset(&se, 0, sizeof(se));
+    se.sigev_notify = SIGEV_THREAD;
+    se.sigev_notify_function = fn;
+    se.sigev_value.sival_ptr = this;
+  }
+
+  void Create() {
+    ASSERT_EQ(0, timer_create(CLOCK_REALTIME, &se, &timer_id));
+  }
+
+  ~Counter() {
+    if (timer_delete(timer_id) != 0) {
+      abort();
+    }
+  }
+
+  static void CountNotifyFunction(sigval_t value) {
+    Counter* cd = reinterpret_cast<Counter*>(value.sival_ptr);
+    ++cd->value;
+  }
+
+  static void CountAndDisarmNotifyFunction(sigval_t value) {
+    Counter* cd = reinterpret_cast<Counter*>(value.sival_ptr);
+    ++cd->value;
+
+    // Setting the initial expiration time to 0 disarms the timer.
+    SetTime(cd->timer_id, 0, 0, 1, 0);
+  }
+};
+
+TEST(time, timer_settime_0) {
+  Counter counter(Counter::CountAndDisarmNotifyFunction);
+  counter.Create();
+
+  ASSERT_EQ(0, counter.value);
+
+  SetTime(counter.timer_id, 0, 1, 1, 0);
+  usleep(500000);
+
+  // The count should just be 1 because we disarmed the timer the first time it fired.
+  ASSERT_EQ(1, counter.value);
+}
+
+TEST(time, timer_settime_repeats) {
+  Counter counter(Counter::CountNotifyFunction);
+  counter.Create();
+
+  ASSERT_EQ(0, counter.value);
+
+  SetTime(counter.timer_id, 0, 1, 0, 10);
+  usleep(500000);
+
+  // The count should just be > 1 because we let the timer repeat.
+  ASSERT_GT(counter.value, 1);
+}
+
+static int timer_create_NULL_signal_handler_invocation_count = 0;
+static void timer_create_NULL_signal_handler(int signal_number) {
+  ++timer_create_NULL_signal_handler_invocation_count;
+  ASSERT_EQ(SIGALRM, signal_number);
+}
+
+TEST(time, timer_create_NULL) {
+  // A NULL sigevent* is equivalent to asking for SIGEV_SIGNAL for SIGALRM.
+  timer_t timer_id;
+  ASSERT_EQ(0, timer_create(CLOCK_MONOTONIC, NULL, &timer_id));
+
+  ScopedSignalHandler ssh(SIGALRM, timer_create_NULL_signal_handler);
+
+  ASSERT_EQ(0, timer_create_NULL_signal_handler_invocation_count);
+
+  SetTime(timer_id, 0, 1, 0, 0);
+  usleep(500000);
+
+  ASSERT_EQ(1, timer_create_NULL_signal_handler_invocation_count);
+}
+
+TEST(time, timer_create_EINVAL) {
+  clockid_t invalid_clock = 16;
+
+  // A SIGEV_SIGNAL timer is easy; the kernel does all that.
+  timer_t timer_id;
+  ASSERT_EQ(-1, timer_create(invalid_clock, NULL, &timer_id));
+  ASSERT_EQ(EINVAL, errno);
+
+  // A SIGEV_THREAD timer is more interesting because we have stuff to clean up.
+  sigevent_t se;
+  memset(&se, 0, sizeof(se));
+  se.sigev_notify = SIGEV_THREAD;
+  se.sigev_notify_function = NoOpNotifyFunction;
+  ASSERT_EQ(-1, timer_create(invalid_clock, &se, &timer_id));
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(time, timer_delete_multiple) {
+  timer_t timer_id;
+  ASSERT_EQ(0, timer_create(CLOCK_MONOTONIC, NULL, &timer_id));
+  ASSERT_EQ(0, timer_delete(timer_id));
+  ASSERT_EQ(-1, timer_delete(timer_id));
+  ASSERT_EQ(EINVAL, errno);
+
+  sigevent_t se;
+  memset(&se, 0, sizeof(se));
+  se.sigev_notify = SIGEV_THREAD;
+  se.sigev_notify_function = NoOpNotifyFunction;
+  ASSERT_EQ(0, timer_create(CLOCK_MONOTONIC, &se, &timer_id));
+  ASSERT_EQ(0, timer_delete(timer_id));
+  ASSERT_EQ(-1, timer_delete(timer_id));
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(time, timer_create_multiple) {
+  Counter counter1(Counter::CountNotifyFunction);
+  counter1.Create();
+  Counter counter2(Counter::CountNotifyFunction);
+  counter2.Create();
+  Counter counter3(Counter::CountNotifyFunction);
+  counter3.Create();
+
+  ASSERT_EQ(0, counter1.value);
+  ASSERT_EQ(0, counter2.value);
+  ASSERT_EQ(0, counter3.value);
+
+  SetTime(counter2.timer_id, 0, 1, 0, 0);
+  usleep(500000);
+
+  EXPECT_EQ(0, counter1.value);
+  EXPECT_EQ(1, counter2.value);
+  EXPECT_EQ(0, counter3.value);
+}