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);
+}