Merge "Add test cases for __emutls_get_address."
diff --git a/benchmarks/math_benchmark.cpp b/benchmarks/math_benchmark.cpp
index 2e0c962..ed5b56c 100644
--- a/benchmarks/math_benchmark.cpp
+++ b/benchmarks/math_benchmark.cpp
@@ -258,3 +258,29 @@
 
   StopBenchmarkTiming();
 }
+
+BENCHMARK_WITH_ARG(BM_math_fabs_macro, double)->AT_COMMON_VALS;
+void BM_math_fabs_macro::Run(int iters, double value) {
+  StartBenchmarkTiming();
+
+  d = 0.0;
+  v = value;
+  for (int i = 0; i < iters; ++i) {
+    d += fabs(v);
+  }
+
+  StopBenchmarkTiming();
+}
+
+BENCHMARK_WITH_ARG(BM_math_fabs, double)->AT_COMMON_VALS;
+void BM_math_fabs::Run(int iters, double value) {
+  StartBenchmarkTiming();
+
+  d = 0.0;
+  v = value;
+  for (int i = 0; i < iters; ++i) {
+    d += (fabs)(v);
+  }
+
+  StopBenchmarkTiming();
+}
diff --git a/libc/Android.mk b/libc/Android.mk
index ca20d3d..437f8b2 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -214,6 +214,7 @@
     bionic/socket.cpp \
     bionic/stat.cpp \
     bionic/statvfs.cpp \
+    bionic/strchrnul.cpp \
     bionic/strerror.cpp \
     bionic/strerror_r.cpp \
     bionic/strsignal.cpp \
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index ac8db62..4188c15 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -94,6 +94,16 @@
 ssize_t     pread64|pread(int, void*, size_t, off_t) arm64,mips64,x86_64
 ssize_t     pwrite64(int, void*, size_t, off64_t) arm,mips,x86
 ssize_t     pwrite64|pwrite(int, void*, size_t, off_t) arm64,mips64,x86_64
+
+# On LP32, preadv/pwritev don't use off64_t --- they use pairs of 32-bit
+# arguments to avoid problems on architectures like ARM where 64-bit arguments
+# must be in a register pair starting with an even-numbered register.
+# See linux/fs/read_write.c and https://lwn.net/Articles/311630/.
+ssize_t     __preadv64:preadv(int, const struct iovec*, int, long, long) arm,mips,x86
+ssize_t     preadv|preadv64(int, const struct iovec*, int, off_t) arm64,mips64,x86_64
+ssize_t     __pwritev64:pwritev(int, const struct iovec*, int, long, long) arm,mips,x86
+ssize_t     pwritev|pwritev64(int, const struct iovec*, int, off_t) arm64,mips64,x86_64
+
 int         ___close:close(int)  all
 pid_t       __getpid:getpid()  all
 int         munmap(void*, size_t)  all
diff --git a/libc/arch-arm/syscalls/__preadv64.S b/libc/arch-arm/syscalls/__preadv64.S
new file mode 100644
index 0000000..19eaaa5
--- /dev/null
+++ b/libc/arch-arm/syscalls/__preadv64.S
@@ -0,0 +1,22 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(__preadv64)
+    mov     ip, sp
+    stmfd   sp!, {r4, r5, r6, r7}
+    .cfi_def_cfa_offset 16
+    .cfi_rel_offset r4, 0
+    .cfi_rel_offset r5, 4
+    .cfi_rel_offset r6, 8
+    .cfi_rel_offset r7, 12
+    ldmfd   ip, {r4, r5, r6}
+    ldr     r7, =__NR_preadv
+    swi     #0
+    ldmfd   sp!, {r4, r5, r6, r7}
+    .cfi_def_cfa_offset 0
+    cmn     r0, #(MAX_ERRNO + 1)
+    bxls    lr
+    neg     r0, r0
+    b       __set_errno_internal
+END(__preadv64)
diff --git a/libc/arch-arm/syscalls/__pwritev64.S b/libc/arch-arm/syscalls/__pwritev64.S
new file mode 100644
index 0000000..afcbe40
--- /dev/null
+++ b/libc/arch-arm/syscalls/__pwritev64.S
@@ -0,0 +1,22 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(__pwritev64)
+    mov     ip, sp
+    stmfd   sp!, {r4, r5, r6, r7}
+    .cfi_def_cfa_offset 16
+    .cfi_rel_offset r4, 0
+    .cfi_rel_offset r5, 4
+    .cfi_rel_offset r6, 8
+    .cfi_rel_offset r7, 12
+    ldmfd   ip, {r4, r5, r6}
+    ldr     r7, =__NR_pwritev
+    swi     #0
+    ldmfd   sp!, {r4, r5, r6, r7}
+    .cfi_def_cfa_offset 0
+    cmn     r0, #(MAX_ERRNO + 1)
+    bxls    lr
+    neg     r0, r0
+    b       __set_errno_internal
+END(__pwritev64)
diff --git a/libc/arch-arm64/syscalls/preadv.S b/libc/arch-arm64/syscalls/preadv.S
new file mode 100644
index 0000000..cb8300d
--- /dev/null
+++ b/libc/arch-arm64/syscalls/preadv.S
@@ -0,0 +1,16 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(preadv)
+    mov     x8, __NR_preadv
+    svc     #0
+
+    cmn     x0, #(MAX_ERRNO + 1)
+    cneg    x0, x0, hi
+    b.hi    __set_errno_internal
+
+    ret
+END(preadv)
+
+ALIAS_SYMBOL(preadv64, preadv)
diff --git a/libc/arch-arm64/syscalls/pwritev.S b/libc/arch-arm64/syscalls/pwritev.S
new file mode 100644
index 0000000..621466a
--- /dev/null
+++ b/libc/arch-arm64/syscalls/pwritev.S
@@ -0,0 +1,16 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(pwritev)
+    mov     x8, __NR_pwritev
+    svc     #0
+
+    cmn     x0, #(MAX_ERRNO + 1)
+    cneg    x0, x0, hi
+    b.hi    __set_errno_internal
+
+    ret
+END(pwritev)
+
+ALIAS_SYMBOL(pwritev64, pwritev)
diff --git a/libc/arch-mips/syscalls/__preadv64.S b/libc/arch-mips/syscalls/__preadv64.S
new file mode 100644
index 0000000..a46b869
--- /dev/null
+++ b/libc/arch-mips/syscalls/__preadv64.S
@@ -0,0 +1,19 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(__preadv64)
+    .set noreorder
+    .cpload t9
+    li v0, __NR_preadv
+    syscall
+    bnez a3, 1f
+    move a0, v0
+    j ra
+    nop
+1:
+    la t9,__set_errno_internal
+    j t9
+    nop
+    .set reorder
+END(__preadv64)
diff --git a/libc/arch-mips/syscalls/__pwritev64.S b/libc/arch-mips/syscalls/__pwritev64.S
new file mode 100644
index 0000000..1222942
--- /dev/null
+++ b/libc/arch-mips/syscalls/__pwritev64.S
@@ -0,0 +1,19 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(__pwritev64)
+    .set noreorder
+    .cpload t9
+    li v0, __NR_pwritev
+    syscall
+    bnez a3, 1f
+    move a0, v0
+    j ra
+    nop
+1:
+    la t9,__set_errno_internal
+    j t9
+    nop
+    .set reorder
+END(__pwritev64)
diff --git a/libc/arch-mips64/syscalls/preadv.S b/libc/arch-mips64/syscalls/preadv.S
new file mode 100644
index 0000000..df43a8d
--- /dev/null
+++ b/libc/arch-mips64/syscalls/preadv.S
@@ -0,0 +1,27 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(preadv)
+    .set push
+    .set noreorder
+    li v0, __NR_preadv
+    syscall
+    bnez a3, 1f
+    move a0, v0
+    j ra
+    nop
+1:
+    move t0, ra
+    bal     2f
+    nop
+2:
+    .cpsetup ra, t1, 2b
+    LA t9,__set_errno_internal
+    .cpreturn
+    j t9
+    move ra, t0
+    .set pop
+END(preadv)
+
+ALIAS_SYMBOL(preadv64, preadv)
diff --git a/libc/arch-mips64/syscalls/pwritev.S b/libc/arch-mips64/syscalls/pwritev.S
new file mode 100644
index 0000000..f55cec0
--- /dev/null
+++ b/libc/arch-mips64/syscalls/pwritev.S
@@ -0,0 +1,27 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(pwritev)
+    .set push
+    .set noreorder
+    li v0, __NR_pwritev
+    syscall
+    bnez a3, 1f
+    move a0, v0
+    j ra
+    nop
+1:
+    move t0, ra
+    bal     2f
+    nop
+2:
+    .cpsetup ra, t1, 2b
+    LA t9,__set_errno_internal
+    .cpreturn
+    j t9
+    move ra, t0
+    .set pop
+END(pwritev)
+
+ALIAS_SYMBOL(pwritev64, pwritev)
diff --git a/libc/arch-x86/syscalls/__preadv64.S b/libc/arch-x86/syscalls/__preadv64.S
new file mode 100644
index 0000000..3ce4fc3
--- /dev/null
+++ b/libc/arch-x86/syscalls/__preadv64.S
@@ -0,0 +1,41 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(__preadv64)
+    pushl   %ebx
+    .cfi_def_cfa_offset 8
+    .cfi_rel_offset ebx, 0
+    pushl   %ecx
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset ecx, 0
+    pushl   %edx
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset edx, 0
+    pushl   %esi
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset esi, 0
+    pushl   %edi
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset edi, 0
+    mov     24(%esp), %ebx
+    mov     28(%esp), %ecx
+    mov     32(%esp), %edx
+    mov     36(%esp), %esi
+    mov     40(%esp), %edi
+    movl    $__NR_preadv, %eax
+    int     $0x80
+    cmpl    $-MAX_ERRNO, %eax
+    jb      1f
+    negl    %eax
+    pushl   %eax
+    call    __set_errno_internal
+    addl    $4, %esp
+1:
+    popl    %edi
+    popl    %esi
+    popl    %edx
+    popl    %ecx
+    popl    %ebx
+    ret
+END(__preadv64)
diff --git a/libc/arch-x86/syscalls/__pwritev64.S b/libc/arch-x86/syscalls/__pwritev64.S
new file mode 100644
index 0000000..2ce5b0e
--- /dev/null
+++ b/libc/arch-x86/syscalls/__pwritev64.S
@@ -0,0 +1,41 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(__pwritev64)
+    pushl   %ebx
+    .cfi_def_cfa_offset 8
+    .cfi_rel_offset ebx, 0
+    pushl   %ecx
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset ecx, 0
+    pushl   %edx
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset edx, 0
+    pushl   %esi
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset esi, 0
+    pushl   %edi
+    .cfi_adjust_cfa_offset 4
+    .cfi_rel_offset edi, 0
+    mov     24(%esp), %ebx
+    mov     28(%esp), %ecx
+    mov     32(%esp), %edx
+    mov     36(%esp), %esi
+    mov     40(%esp), %edi
+    movl    $__NR_pwritev, %eax
+    int     $0x80
+    cmpl    $-MAX_ERRNO, %eax
+    jb      1f
+    negl    %eax
+    pushl   %eax
+    call    __set_errno_internal
+    addl    $4, %esp
+1:
+    popl    %edi
+    popl    %esi
+    popl    %edx
+    popl    %ecx
+    popl    %ebx
+    ret
+END(__pwritev64)
diff --git a/libc/arch-x86_64/syscalls/preadv.S b/libc/arch-x86_64/syscalls/preadv.S
new file mode 100644
index 0000000..7771a44
--- /dev/null
+++ b/libc/arch-x86_64/syscalls/preadv.S
@@ -0,0 +1,18 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(preadv)
+    movq    %rcx, %r10
+    movl    $__NR_preadv, %eax
+    syscall
+    cmpq    $-MAX_ERRNO, %rax
+    jb      1f
+    negl    %eax
+    movl    %eax, %edi
+    call    __set_errno_internal
+1:
+    ret
+END(preadv)
+
+ALIAS_SYMBOL(preadv64, preadv)
diff --git a/libc/arch-x86_64/syscalls/pwritev.S b/libc/arch-x86_64/syscalls/pwritev.S
new file mode 100644
index 0000000..d3bd8fa
--- /dev/null
+++ b/libc/arch-x86_64/syscalls/pwritev.S
@@ -0,0 +1,18 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(pwritev)
+    movq    %rcx, %r10
+    movl    $__NR_pwritev, %eax
+    syscall
+    cmpq    $-MAX_ERRNO, %rax
+    jb      1f
+    negl    %eax
+    movl    %eax, %edi
+    call    __set_errno_internal
+1:
+    ret
+END(pwritev)
+
+ALIAS_SYMBOL(pwritev64, pwritev)
diff --git a/libc/bionic/debug_stacktrace.cpp b/libc/bionic/debug_stacktrace.cpp
index 4100911..b49a42e 100644
--- a/libc/bionic/debug_stacktrace.cpp
+++ b/libc/bionic/debug_stacktrace.cpp
@@ -90,15 +90,22 @@
   // Modify the pc to point at the real function.
   if (ip != 0) {
 #if defined(__arm__)
-    // We need to do a quick check here to find out if the previous
-    // instruction is a Thumb-mode BLX(2). If so subtract 2 otherwise
-    // 4 from PC.
-    short* ptr = reinterpret_cast<short*>(ip);
-    // Thumb BLX(2)
-    if ((*(ptr-1) & 0xff80) == 0x4780) {
-      ip -= 2;
-    } else {
-      ip -= 4;
+    // If the ip is suspiciously low, do nothing to avoid a segfault trying
+    // to access this memory.
+    if (ip >= 4096) {
+      // Check bits [15:11] of the first halfword assuming the instruction
+      // is 32 bits long. If the bits are any of these values, then our
+      // assumption was correct:
+      //  b11101
+      //  b11110
+      //  b11111
+      // Otherwise, this is a 16 bit instruction.
+      uint16_t value = (*reinterpret_cast<uint16_t*>(ip - 2)) >> 11;
+      if (value == 0x1f || value == 0x1e || value == 0x1d) {
+        ip -= 4;
+      } else {
+        ip -= 2;
+      }
     }
 #elif defined(__aarch64__)
     // All instructions are 4 bytes long, skip back one instruction.
diff --git a/libc/bionic/legacy_32_bit_support.cpp b/libc/bionic/legacy_32_bit_support.cpp
index a107664..20d258d 100644
--- a/libc/bionic/legacy_32_bit_support.cpp
+++ b/libc/bionic/legacy_32_bit_support.cpp
@@ -33,6 +33,7 @@
 #include <stdarg.h>
 #include <sys/resource.h>
 #include <sys/types.h>
+#include <sys/uio.h>
 #include <sys/vfs.h>
 #include <unistd.h>
 
@@ -43,6 +44,8 @@
 // System calls we need.
 extern "C" int __fcntl64(int, int, void*);
 extern "C" int __llseek(int, unsigned long, unsigned long, off64_t*, int);
+extern "C" int __preadv64(int, const struct iovec*, int, long, long);
+extern "C" int __pwritev64(int, const struct iovec*, int, long, long);
 
 // For fcntl we use the fcntl64 system call to signal that we're using struct flock64.
 int fcntl(int fd, int cmd, ...) {
@@ -77,6 +80,23 @@
   return pwrite64(fd, buf, byte_count, static_cast<off64_t>(offset));
 }
 
+// On LP32, there is no off_t preadv/pwritev, and even the 64-bit preadv/pwritev
+// don't use off64_t (see SYSCALLS.TXT for more). Here, this means that we need
+// to implement all four functions because the two system calls don't match any
+// of the userspace functions. Unlike llseek, the pair is split lo-hi, not hi-lo.
+ssize_t preadv(int fd, const struct iovec* ios, int count, off_t offset) {
+  return __preadv64(fd, ios, count, offset, 0);
+}
+ssize_t preadv64(int fd, const struct iovec* ios, int count, off64_t offset) {
+  return __preadv64(fd, ios, count, offset, offset >> 32);
+}
+ssize_t pwritev(int fd, const struct iovec* ios, int count, off_t offset) {
+  return __pwritev64(fd, ios, count, offset, 0);
+}
+ssize_t pwritev64(int fd, const struct iovec* ios, int count, off64_t offset) {
+  return __pwritev64(fd, ios, count, offset, offset >> 32);
+}
+
 // There is no fallocate for 32-bit off_t, so we need to widen and call fallocate64.
 int fallocate(int fd, int mode, off_t offset, off_t length) {
   return fallocate64(fd, mode, static_cast<off64_t>(offset), static_cast<off64_t>(length));
diff --git a/libc/bionic/strchrnul.cpp b/libc/bionic/strchrnul.cpp
new file mode 100644
index 0000000..55422e0
--- /dev/null
+++ b/libc/bionic/strchrnul.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+extern "C" const char* strchrnul(const char* s, int ch) {
+  while (*s && *s != ch) {
+    ++s;
+  }
+  return s;
+}
diff --git a/libc/include/string.h b/libc/include/string.h
index d32c164..2c50cfa 100644
--- a/libc/include/string.h
+++ b/libc/include/string.h
@@ -53,6 +53,14 @@
 
 extern char*  strchr(const char *, int) __purefunc;
 extern char* __strchr_chk(const char *, int, size_t);
+#if defined(__USE_GNU)
+#if defined(__cplusplus)
+extern "C++" char* strchrnul(char*, int) __RENAME(strchrnul) __purefunc;
+extern "C++" const char* strchrnul(const char*, int) __RENAME(strchrnul) __purefunc;
+#else
+char* strchrnul(const char*, int) __purefunc;
+#endif
+#endif
 
 extern char*  strrchr(const char *, int) __purefunc;
 extern char* __strrchr_chk(const char *, int, size_t);
diff --git a/libc/include/sys/ucontext.h b/libc/include/sys/ucontext.h
index 399458e..9e3c653 100644
--- a/libc/include/sys/ucontext.h
+++ b/libc/include/sys/ucontext.h
@@ -82,12 +82,6 @@
 #define NGREG 34 /* x0..x30 + sp + pc + pstate */
 typedef unsigned long greg_t;
 typedef greg_t gregset_t[NGREG];
-
-struct user_fpsimd_struct {
-  long double vregs[32];
-  uint32_t fpsr;
-  uint32_t fpcr;
-};
 typedef struct user_fpsimd_struct fpregset_t;
 
 #include <asm/sigcontext.h>
diff --git a/libc/include/sys/uio.h b/libc/include/sys/uio.h
index 187ec22..72675d1 100644
--- a/libc/include/sys/uio.h
+++ b/libc/include/sys/uio.h
@@ -38,6 +38,18 @@
 int writev(int, const struct iovec*, int);
 
 #if defined(__USE_GNU)
+#if defined(__USE_FILE_OFFSET64)
+ssize_t preadv(int, const struct iovec*, int, off_t) __RENAME(preadv64);
+ssize_t pwritev(int, const struct iovec*, int, off_t) __RENAME(pwritev64);
+#else
+ssize_t preadv(int, const struct iovec*, int, off_t);
+ssize_t pwritev(int, const struct iovec*, int, off_t);
+#endif
+ssize_t preadv64(int, const struct iovec*, int, off64_t);
+ssize_t pwritev64(int, const struct iovec*, int, off64_t);
+#endif
+
+#if defined(__USE_GNU)
 ssize_t process_vm_readv(pid_t, const struct iovec*, unsigned long, const struct iovec*, unsigned long, unsigned long);
 ssize_t process_vm_writev(pid_t, const struct iovec*, unsigned long, const struct iovec*, unsigned long, unsigned long);
 #endif
diff --git a/libc/include/sys/user.h b/libc/include/sys/user.h
index ce4c07c..3312981 100644
--- a/libc/include/sys/user.h
+++ b/libc/include/sys/user.h
@@ -232,7 +232,17 @@
 
 #elif defined(__aarch64__)
 
-// There are no user structures for 64 bit arm.
+struct user_regs_struct {
+  uint64_t regs[31];
+  uint64_t sp;
+  uint64_t pc;
+  uint64_t pstate;
+};
+struct user_fpsimd_struct {
+  __uint128_t vregs[32];
+  uint32_t fpsr;
+  uint32_t fpcr;
+};
 
 #else
 
diff --git a/libc/libc.map b/libc/libc.map
index 4670079..fbceb83 100644
--- a/libc/libc.map
+++ b/libc/libc.map
@@ -1342,6 +1342,11 @@
     __write_chk;
     getgrgid_r;
     getgrnam_r;
+    preadv;
+    preadv64;
+    pwritev;
+    pwritev64;
+    strchrnul;
 } LIBC;
 
 LIBC_PRIVATE {
diff --git a/libc/tools/zoneinfo/ZoneCompactor.java b/libc/tools/zoneinfo/ZoneCompactor.java
deleted file mode 100644
index 2d598fe..0000000
--- a/libc/tools/zoneinfo/ZoneCompactor.java
+++ /dev/null
@@ -1,192 +0,0 @@
-
-import java.io.*;
-import java.util.*;
-
-// usage: java ZoneCompiler <setup file> <data directory> <output directory> <tzdata version>
-//
-// Compile a set of tzfile-formatted files into a single file containing an index.
-//
-// The compilation is controlled by a setup file, which is provided as a
-// command-line argument.  The setup file has the form:
-//
-// Link <toName> <fromName>
-// ...
-// <zone filename>
-// ...
-//
-// Note that the links must be declared prior to the zone names.
-// A zone name is a filename relative to the source directory such as
-// 'GMT', 'Africa/Dakar', or 'America/Argentina/Jujuy'.
-//
-// Use the 'zic' command-line tool to convert from flat files
-// (such as 'africa' or 'northamerica') to a directory
-// hierarchy suitable for this tool (containing files such as 'data/Africa/Abidjan').
-//
-
-public class ZoneCompactor {
-  // Maximum number of characters in a zone name, including '\0' terminator.
-  private static final int MAXNAME = 40;
-
-  // Zone name synonyms.
-  private Map<String,String> links = new HashMap<String,String>();
-
-  // File offsets by zone name.
-  private Map<String,Integer> offsets = new HashMap<String,Integer>();
-
-  // File lengths by zone name.
-  private Map<String,Integer> lengths = new HashMap<String,Integer>();
-
-  // Concatenate the contents of 'inFile' onto 'out'.
-  private static void copyFile(File inFile, OutputStream out) throws Exception {
-    byte[] ret = new byte[0];
-
-    InputStream in = new FileInputStream(inFile);
-    byte[] buf = new byte[8192];
-    while (true) {
-      int nbytes = in.read(buf);
-      if (nbytes == -1) {
-        break;
-      }
-      out.write(buf, 0, nbytes);
-
-      byte[] nret = new byte[ret.length + nbytes];
-      System.arraycopy(ret, 0, nret, 0, ret.length);
-      System.arraycopy(buf, 0, nret, ret.length, nbytes);
-      ret = nret;
-    }
-    out.flush();
-  }
-
-  public ZoneCompactor(String setupFile, String dataDirectory, String zoneTabFile, String outputDirectory, String version) throws Exception {
-    // Read the setup file and concatenate all the data.
-    ByteArrayOutputStream allData = new ByteArrayOutputStream();
-    BufferedReader reader = new BufferedReader(new FileReader(setupFile));
-    String s;
-    int offset = 0;
-    while ((s = reader.readLine()) != null) {
-      s = s.trim();
-      if (s.startsWith("Link")) {
-        StringTokenizer st = new StringTokenizer(s);
-        st.nextToken();
-        String to = st.nextToken();
-        String from = st.nextToken();
-        links.put(from, to);
-      } else {
-        String link = links.get(s);
-        if (link == null) {
-          File sourceFile = new File(dataDirectory, s);
-          long length = sourceFile.length();
-          offsets.put(s, offset);
-          lengths.put(s, (int) length);
-
-          offset += length;
-          copyFile(sourceFile, allData);
-        }
-      }
-    }
-    reader.close();
-
-    // Fill in fields for links.
-    Iterator<String> it = links.keySet().iterator();
-    while (it.hasNext()) {
-      String from = it.next();
-      String to = links.get(from);
-
-      offsets.put(from, offsets.get(to));
-      lengths.put(from, lengths.get(to));
-    }
-
-    // Create/truncate the destination file.
-    RandomAccessFile f = new RandomAccessFile(new File(outputDirectory, "tzdata"), "rw");
-    f.setLength(0);
-
-    // Write the header.
-
-    // byte[12] tzdata_version -- 'tzdata2012f\0'
-    // int index_offset -- so we can slip in extra header fields in a backwards-compatible way
-    // int data_offset
-    // int zonetab_offset
-
-    // tzdata_version
-    f.write(toAscii(new byte[12], version));
-
-    // Write dummy values for the three offsets, and remember where we need to seek back to later
-    // when we have the real values.
-    int index_offset_offset = (int) f.getFilePointer();
-    f.writeInt(0);
-    int data_offset_offset = (int) f.getFilePointer();
-    f.writeInt(0);
-    int zonetab_offset_offset = (int) f.getFilePointer();
-    f.writeInt(0);
-
-    int index_offset = (int) f.getFilePointer();
-
-    // Write the index.
-    ArrayList<String> sortedOlsonIds = new ArrayList<String>();
-    sortedOlsonIds.addAll(offsets.keySet());
-    Collections.sort(sortedOlsonIds);
-    it = sortedOlsonIds.iterator();
-    while (it.hasNext()) {
-      String zoneName = it.next();
-      if (zoneName.length() >= MAXNAME) {
-        throw new RuntimeException("zone filename too long: " + zoneName.length());
-      }
-
-      // Follow the chain of links to work out where the real data for this zone lives.
-      String actualZoneName = zoneName;
-      while (links.get(actualZoneName) != null) {
-        actualZoneName = links.get(actualZoneName);
-      }
-
-      f.write(toAscii(new byte[MAXNAME], zoneName));
-      f.writeInt(offsets.get(actualZoneName));
-      f.writeInt(lengths.get(actualZoneName));
-      f.writeInt(0); // Used to be raw GMT offset. No longer used.
-    }
-
-    int data_offset = (int) f.getFilePointer();
-
-    // Write the data.
-    f.write(allData.toByteArray());
-
-    int zonetab_offset = (int) f.getFilePointer();
-
-    // Copy the zone.tab.
-    reader = new BufferedReader(new FileReader(zoneTabFile));
-    while ((s = reader.readLine()) != null) {
-      if (!s.startsWith("#")) {
-        f.writeBytes(s);
-        f.write('\n');
-      }
-    }
-    reader.close();
-
-    // Go back and fix up the offsets in the header.
-    f.seek(index_offset_offset);
-    f.writeInt(index_offset);
-    f.seek(data_offset_offset);
-    f.writeInt(data_offset);
-    f.seek(zonetab_offset_offset);
-    f.writeInt(zonetab_offset);
-
-    f.close();
-  }
-
-  private static byte[] toAscii(byte[] dst, String src) {
-    for (int i = 0; i < src.length(); ++i) {
-      if (src.charAt(i) > '~') {
-        throw new RuntimeException("non-ASCII string: " + src);
-      }
-      dst[i] = (byte) src.charAt(i);
-    }
-    return dst;
-  }
-
-  public static void main(String[] args) throws Exception {
-    if (args.length != 5) {
-      System.err.println("usage: java ZoneCompactor <setup file> <data directory> <zone.tab file> <output directory> <tzdata version>");
-      System.exit(0);
-    }
-    new ZoneCompactor(args[0], args[1], args[2], args[3], args[4]);
-  }
-}
diff --git a/libc/tools/zoneinfo/update-tzdata.py b/libc/tools/zoneinfo/update-tzdata.py
deleted file mode 100755
index 68a5ff5..0000000
--- a/libc/tools/zoneinfo/update-tzdata.py
+++ /dev/null
@@ -1,262 +0,0 @@
-#!/usr/bin/python
-
-"""Updates the timezone data held in bionic and ICU."""
-
-import ftplib
-import glob
-import httplib
-import os
-import re
-import shutil
-import subprocess
-import sys
-import tarfile
-import tempfile
-
-regions = ['africa', 'antarctica', 'asia', 'australasia',
-           'etcetera', 'europe', 'northamerica', 'southamerica',
-           # These two deliberately come last so they override what came
-           # before (and each other).
-           'backward', 'backzone' ]
-
-def CheckDirExists(dir, dirname):
-  if not os.path.isdir(dir):
-    print "Couldn't find %s (%s)!" % (dirname, dir)
-    sys.exit(1)
-
-bionic_libc_tools_zoneinfo_dir = os.path.realpath(os.path.dirname(sys.argv[0]))
-
-# Find the bionic directory, searching upward from this script.
-bionic_dir = os.path.realpath('%s/../../..' % bionic_libc_tools_zoneinfo_dir)
-bionic_libc_zoneinfo_dir = '%s/libc/zoneinfo' % bionic_dir
-CheckDirExists(bionic_libc_zoneinfo_dir, 'bionic/libc/zoneinfo')
-CheckDirExists(bionic_libc_tools_zoneinfo_dir, 'bionic/libc/tools/zoneinfo')
-print 'Found bionic in %s ...' % bionic_dir
-
-# Find the icu directory.
-icu_dir = os.path.realpath('%s/../external/icu' % bionic_dir)
-icu4c_dir = os.path.realpath('%s/icu4c/source' % icu_dir)
-icu4j_dir = os.path.realpath('%s/icu4j' % icu_dir)
-CheckDirExists(icu4c_dir, 'external/icu/icu4c/source')
-CheckDirExists(icu4j_dir, 'external/icu/icu4j')
-print 'Found icu in %s ...' % icu_dir
-
-
-def GetCurrentTzDataVersion():
-  return open('%s/tzdata' % bionic_libc_zoneinfo_dir).read().split('\x00', 1)[0]
-
-
-def WriteSetupFile():
-  """Writes the list of zones that ZoneCompactor should process."""
-  links = []
-  zones = []
-  for region in regions:
-    for line in open('extracted/%s' % region):
-      fields = line.split()
-      if fields:
-        if fields[0] == 'Link':
-          links.append('%s %s %s' % (fields[0], fields[1], fields[2]))
-          zones.append(fields[2])
-        elif fields[0] == 'Zone':
-          zones.append(fields[1])
-  zones.sort()
-
-  setup = open('setup', 'w')
-  for link in sorted(set(links)):
-    setup.write('%s\n' % link)
-  for zone in sorted(set(zones)):
-    setup.write('%s\n' % zone)
-  setup.close()
-
-
-def SwitchToNewTemporaryDirectory():
-  tmp_dir = tempfile.mkdtemp('-tzdata')
-  os.chdir(tmp_dir)
-  print 'Created temporary directory "%s"...' % tmp_dir
-
-
-def FtpRetrieveFile(ftp, filename):
-  ftp.retrbinary('RETR %s' % filename, open(filename, 'wb').write)
-
-
-def FtpRetrieveFileAndSignature(ftp, data_filename):
-  """Downloads and repackages the given data from the given FTP server."""
-  print 'Downloading data...'
-  FtpRetrieveFile(ftp, data_filename)
-
-  print 'Downloading signature...'
-  signature_filename = '%s.asc' % data_filename
-  FtpRetrieveFile(ftp, signature_filename)
-
-
-def HttpRetrieveFile(http, path, output_filename):
-  http.request("GET", path)
-  f = open(output_filename, 'wb')
-  f.write(http.getresponse().read())
-  f.close()
-
-
-def HttpRetrieveFileAndSignature(http, data_filename):
-  """Downloads and repackages the given data from the given HTTP server."""
-  path = "/time-zones/repository/releases/%s" % data_filename
-
-  print 'Downloading data...'
-  HttpRetrieveFile(http, path, data_filename)
-
-  print 'Downloading signature...'
-  signature_filename = '%s.asc' % data_filename
-  HttpRetrievefile(http, "%s.asc" % path, signature_filename)
-
-
-def BuildIcuToolsAndData(data_filename):
-  # Keep track of the original cwd so we can go back to it at the end.
-  original_working_dir = os.getcwd()
-
-  # Create a directory to run 'make' from.
-  icu_working_dir = '%s/icu' % original_working_dir
-  os.mkdir(icu_working_dir)
-  os.chdir(icu_working_dir)
-
-  # Build the ICU tools.
-  print 'Configuring ICU tools...'
-  subprocess.check_call(['%s/runConfigureICU' % icu4c_dir, 'Linux'])
-
-  # Run the ICU tools.
-  os.chdir('tools/tzcode')
-
-  # The tz2icu tool only picks up icuregions and icuzones in they are in the CWD
-  for icu_data_file in [ 'icuregions', 'icuzones']:
-    icu_data_file_source = '%s/tools/tzcode/%s' % (icu4c_dir, icu_data_file)
-    icu_data_file_symlink = './%s' % icu_data_file
-    os.symlink(icu_data_file_source, icu_data_file_symlink)
-
-  shutil.copyfile('%s/%s' % (original_working_dir, data_filename), data_filename)
-  print 'Making ICU data...'
-  # The Makefile assumes the existence of the bin directory.
-  os.mkdir('%s/bin' % icu_working_dir)
-  subprocess.check_call(['make'])
-
-  # Copy the source file to its ultimate destination.
-  icu_txt_data_dir = '%s/data/misc' % icu4c_dir
-  print 'Copying zoneinfo64.txt to %s ...' % icu_txt_data_dir
-  shutil.copy('zoneinfo64.txt', icu_txt_data_dir)
-
-  # Regenerate the .dat file.
-  os.chdir(icu_working_dir)
-  subprocess.check_call(['make', 'INCLUDE_UNI_CORE_DATA=1', '-j32'])
-
-  # Copy the .dat file to its ultimate destination.
-  icu_dat_data_dir = '%s/stubdata' % icu4c_dir
-  datfiles = glob.glob('data/out/tmp/icudt??l.dat')
-  if len(datfiles) != 1:
-    print 'ERROR: Unexpectedly found %d .dat files (%s). Halting.' % (len(datfiles), datfiles)
-    sys.exit(1)
-  datfile = datfiles[0]
-  print 'Copying %s to %s ...' % (datfile, icu_dat_data_dir)
-  shutil.copy(datfile, icu_dat_data_dir)
-
-  # Generate the ICU4J .jar files
-  os.chdir('%s/data' % icu_working_dir)
-  subprocess.check_call(['make', 'icu4j-data'])
-
-  # Copy the ICU4J .jar files to their ultimate destination.
-  icu_jar_data_dir = '%s/main/shared/data' % icu4j_dir
-  jarfiles = glob.glob('out/icu4j/*.jar')
-  if len(jarfiles) != 2:
-    print 'ERROR: Unexpectedly found %d .jar files (%s). Halting.' % (len(jarfiles), jarfiles)
-    sys.exit(1)
-  for jarfile in jarfiles:
-    print 'Copying %s to %s ...' % (jarfile, icu_jar_data_dir)
-    shutil.copy(jarfile, icu_jar_data_dir)
-
-  # Switch back to the original working cwd.
-  os.chdir(original_working_dir)
-
-
-def CheckSignature(data_filename):
-  signature_filename = '%s.asc' % data_filename
-  print 'Verifying signature...'
-  # If this fails for you, you probably need to import Paul Eggert's public key:
-  # gpg --recv-keys ED97E90E62AA7E34
-  subprocess.check_call(['gpg', '--trusted-key=ED97E90E62AA7E34', '--verify',
-                         signature_filename, data_filename])
-
-
-def BuildBionicToolsAndData(data_filename):
-  new_version = re.search('(tzdata.+)\\.tar\\.gz', data_filename).group(1)
-
-  print 'Extracting...'
-  os.mkdir('extracted')
-  tar = tarfile.open(data_filename, 'r')
-  tar.extractall('extracted')
-
-  print 'Calling zic(1)...'
-  os.mkdir('data')
-  zic_inputs = [ 'extracted/%s' % x for x in regions ]
-  zic_cmd = ['zic', '-d', 'data' ]
-  zic_cmd.extend(zic_inputs)
-  subprocess.check_call(zic_cmd)
-
-  WriteSetupFile()
-
-  print 'Calling ZoneCompactor to update bionic to %s...' % new_version
-  subprocess.check_call(['javac', '-d', '.',
-                         '%s/ZoneCompactor.java' % bionic_libc_tools_zoneinfo_dir])
-  subprocess.check_call(['java', 'ZoneCompactor',
-                         'setup', 'data', 'extracted/zone.tab',
-                         bionic_libc_zoneinfo_dir, new_version])
-
-
-# Run with no arguments from any directory, with no special setup required.
-# See http://www.iana.org/time-zones/ for more about the source of this data.
-def main():
-  print 'Looking for new tzdata...'
-
-  tzdata_filenames = []
-
-  # The FTP server lets you download intermediate releases, and also lets you
-  # download the signatures for verification, so it's your best choice.
-  use_ftp = True
-
-  if use_ftp:
-    ftp = ftplib.FTP('ftp.iana.org')
-    ftp.login()
-    ftp.cwd('tz/releases')
-    for filename in ftp.nlst():
-      if filename.startswith('tzdata20') and filename.endswith('.tar.gz'):
-        tzdata_filenames.append(filename)
-    tzdata_filenames.sort()
-  else:
-    http = httplib.HTTPConnection('www.iana.org')
-    http.request("GET", "/time-zones")
-    index_lines = http.getresponse().read().split('\n')
-    for line in index_lines:
-      m = re.compile('.*href="/time-zones/repository/releases/(tzdata20\d\d\c\.tar\.gz)".*').match(line)
-      if m:
-        tzdata_filenames.append(m.group(1))
-
-  # If you're several releases behind, we'll walk you through the upgrades
-  # one by one.
-  current_version = GetCurrentTzDataVersion()
-  current_filename = '%s.tar.gz' % current_version
-  for filename in tzdata_filenames:
-    if filename > current_filename:
-      print 'Found new tzdata: %s' % filename
-      SwitchToNewTemporaryDirectory()
-      if use_ftp:
-        FtpRetrieveFileAndSignature(ftp, filename)
-      else:
-        HttpRetrieveFileAndSignature(http, filename)
-
-      CheckSignature(filename)
-      BuildIcuToolsAndData(filename)
-      BuildBionicToolsAndData(filename)
-      print 'Look in %s and %s for new data files' % (bionic_dir, icu_dir)
-      sys.exit(0)
-
-  print 'You already have the latest tzdata (%s)!' % current_version
-  sys.exit(0)
-
-
-if __name__ == '__main__':
-  main()
diff --git a/libc/zoneinfo/tzdata b/libc/zoneinfo/tzdata
index 4e4f6a7..f22464e 100644
--- a/libc/zoneinfo/tzdata
+++ b/libc/zoneinfo/tzdata
Binary files differ
diff --git a/libm/Android.mk b/libm/Android.mk
index 7b28f70..d447417 100644
--- a/libm/Android.mk
+++ b/libm/Android.mk
@@ -108,8 +108,6 @@
     upstream-freebsd/lib/msun/src/s_exp2.c \
     upstream-freebsd/lib/msun/src/s_exp2f.c \
     upstream-freebsd/lib/msun/src/s_expm1f.c \
-    upstream-freebsd/lib/msun/src/s_fabs.c \
-    upstream-freebsd/lib/msun/src/s_fabsf.c \
     upstream-freebsd/lib/msun/src/s_fdim.c \
     upstream-freebsd/lib/msun/src/s_finite.c \
     upstream-freebsd/lib/msun/src/s_finitef.c \
@@ -175,7 +173,6 @@
     upstream-freebsd/lib/msun/src/s_copysignl.c \
     upstream-freebsd/lib/msun/src/e_coshl.c \
     upstream-freebsd/lib/msun/src/s_cosl.c \
-    upstream-freebsd/lib/msun/src/s_fabsl.c \
     upstream-freebsd/lib/msun/src/s_floorl.c \
     upstream-freebsd/lib/msun/src/s_fmal.c \
     upstream-freebsd/lib/msun/src/s_fmaxl.c \
@@ -228,6 +225,10 @@
 LOCAL_SRC_FILES += \
     signbit.c \
 
+# Home-grown stuff.
+LOCAL_SRC_FILES += \
+    fabs.cpp \
+
 # Arch specific optimizations.
 
 # -----------------------------------------------------------------------------
@@ -481,6 +482,7 @@
 LOCAL_CLANG := $(libm_clang)
 LOCAL_ARM_MODE := arm
 LOCAL_CFLAGS := \
+    -D__BIONIC_NO_MATH_INLINES \
     -DFLT_EVAL_METHOD=0 \
     -include $(LOCAL_PATH)/freebsd-compat.h \
     -Wno-missing-braces \
diff --git a/libm/fabs.cpp b/libm/fabs.cpp
new file mode 100644
index 0000000..add73fe
--- /dev/null
+++ b/libm/fabs.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <math.h>
+
+#include "fpmath.h"
+
+double fabs(double x) {
+#if __arm__
+  // Both Clang and GCC insist on moving r0/r1 into a double register
+  // and using fabs where bit-twiddling would be a better choice.
+  // They get fabsf right, but we need to be careful in fabsl too.
+  IEEEd2bits u;
+  u.d = x;
+  u.bits.sign = 0;
+  return u.d;
+#else
+  return __builtin_fabs(x);
+#endif
+}
+
+float fabsf(float x) {
+  return __builtin_fabsf(x);
+}
+
+#if defined(__LP64__)
+long double fabsl(long double x) { return __builtin_fabsl(x); }
+#else
+long double fabsl(long double x) {
+  // Don't use __builtin_fabs here because of ARM. (See fabs above.)
+  return fabs(x);
+}
+#endif
diff --git a/libm/fake_long_double.c b/libm/fake_long_double.c
index 317a115..5edf839 100644
--- a/libm/fake_long_double.c
+++ b/libm/fake_long_double.c
@@ -25,7 +25,6 @@
  */
 
 long double copysignl(long double a1, long double a2) { return copysign(a1, a2); }
-long double fabsl(long double a1) { return fabs(a1); }
 long double fmaxl(long double a1, long double a2) { return fmax(a1, a2); }
 long double fmodl(long double a1, long double a2) { return fmod(a1, a2); }
 long double fminl(long double a1, long double a2) { return fmin(a1, a2); }
diff --git a/libm/include/math.h b/libm/include/math.h
index bc48b6a..ce8e3b2 100644
--- a/libm/include/math.h
+++ b/libm/include/math.h
@@ -20,6 +20,12 @@
 #include <sys/cdefs.h>
 #include <limits.h>
 
+#if !defined(__BIONIC_NO_MATH_INLINES)
+#define __BIONIC_MATH_INLINE(__def) extern __inline__ __always_inline __attribute__((gnu_inline)) __attribute__((__artificial__)) __def
+#else
+#define __BIONIC_MATH_INLINE(__def)
+#endif
+
 __BEGIN_DECLS
 #pragma GCC visibility push(default)
 
@@ -161,6 +167,7 @@
 
 double	ceil(double);
 double	fabs(double) __pure2;
+__BIONIC_MATH_INLINE(double fabs(double x) { return __builtin_fabs(x); })
 double	floor(double);
 double	fmod(double, double);
 
@@ -279,6 +286,7 @@
 
 float	ceilf(float);
 float	fabsf(float) __pure2;
+__BIONIC_MATH_INLINE(float fabsf(float x) { return __builtin_fabsf(x); })
 float	floorf(float);
 float	fmodf(float, float);
 float	roundf(float);
@@ -366,6 +374,7 @@
 long double	expl(long double);
 long double	expm1l(long double);
 long double	fabsl(long double) __pure2;
+__BIONIC_MATH_INLINE(long double fabsl(long double x) { return __builtin_fabsl(x); })
 long double	fdiml(long double, long double);
 long double	floorl(long double);
 long double	fmal(long double, long double, long double);
diff --git a/libm/upstream-freebsd/lib/msun/src/s_fabs.c b/libm/upstream-freebsd/lib/msun/src/s_fabs.c
deleted file mode 100644
index 15529e5..0000000
--- a/libm/upstream-freebsd/lib/msun/src/s_fabs.c
+++ /dev/null
@@ -1,31 +0,0 @@
-/* @(#)s_fabs.c 5.1 93/09/24 */
-/*
- * ====================================================
- * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
- *
- * Developed at SunPro, a Sun Microsystems, Inc. business.
- * Permission to use, copy, modify, and distribute this
- * software is freely granted, provided that this notice
- * is preserved.
- * ====================================================
- */
-
-#ifndef lint
-static char rcsid[] = "$FreeBSD$";
-#endif
-
-/*
- * fabs(x) returns the absolute value of x.
- */
-
-#include "math.h"
-#include "math_private.h"
-
-double
-fabs(double x)
-{
-	u_int32_t high;
-	GET_HIGH_WORD(high,x);
-	SET_HIGH_WORD(x,high&0x7fffffff);
-        return x;
-}
diff --git a/libm/upstream-freebsd/lib/msun/src/s_fabsf.c b/libm/upstream-freebsd/lib/msun/src/s_fabsf.c
deleted file mode 100644
index e9383d0..0000000
--- a/libm/upstream-freebsd/lib/msun/src/s_fabsf.c
+++ /dev/null
@@ -1,33 +0,0 @@
-/* s_fabsf.c -- float version of s_fabs.c.
- * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
- */
-
-/*
- * ====================================================
- * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
- *
- * Developed at SunPro, a Sun Microsystems, Inc. business.
- * Permission to use, copy, modify, and distribute this
- * software is freely granted, provided that this notice
- * is preserved.
- * ====================================================
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*
- * fabsf(x) returns the absolute value of x.
- */
-
-#include "math.h"
-#include "math_private.h"
-
-float
-fabsf(float x)
-{
-	u_int32_t ix;
-	GET_FLOAT_WORD(ix,x);
-	SET_FLOAT_WORD(x,ix&0x7fffffff);
-        return x;
-}
diff --git a/tests/string_test.cpp b/tests/string_test.cpp
index cf90045..80190d7 100644
--- a/tests/string_test.cpp
+++ b/tests/string_test.cpp
@@ -454,6 +454,13 @@
   }
 }
 
+TEST(string, strchrnul) {
+  const char* s = "01234222";
+  EXPECT_TRUE(strchrnul(s, '2') == &s[2]);
+  EXPECT_TRUE(strchrnul(s, '8') == (s + strlen(s)));
+  EXPECT_TRUE(strchrnul(s, '\0') == (s + strlen(s)));
+}
+
 TEST(string, strcmp) {
   StringTestState<char> state(SMALL);
   for (size_t i = 1; i < state.n; i++) {
diff --git a/tests/sys_uio_test.cpp b/tests/sys_uio_test.cpp
index c7af8a7..569d4fb 100644
--- a/tests/sys_uio_test.cpp
+++ b/tests/sys_uio_test.cpp
@@ -18,10 +18,60 @@
 
 #include <sys/uio.h>
 
-TEST(sys_uio, process_vm_readv_ESRCH) {
+#include "TemporaryFile.h"
+
+TEST(sys_uio, readv_writev) {
+  TemporaryFile tf;
+
+  char buf1[] = "hello";
+  char buf2[] = "world";
+  iovec ios[] = { { buf1, 5 }, { buf2, 5 } };
+
+  ASSERT_EQ(10, writev(tf.fd, ios, 2));
+
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
+
+  memset(buf1, '1', sizeof(buf1));
+  memset(buf2, '2', sizeof(buf2));
+
+  ASSERT_EQ(10, readv(tf.fd, ios, 2));
+  buf1[5] = buf2[5] = '\0';
+  ASSERT_STREQ("hello", buf1);
+  ASSERT_STREQ("world", buf2);
+}
+
+template <typename ReadFn, typename WriteFn>
+void TestPreadVPwriteV(ReadFn read_fn, WriteFn write_fn) {
+  TemporaryFile tf;
+
+  char buf[] = "world";
+  iovec ios[] = { { buf, 5 } };
+
+  ASSERT_EQ(5, write_fn(tf.fd, ios, 1, 5));
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_CUR));
+
+  strcpy(buf, "hello");
+  ASSERT_EQ(5, write_fn(tf.fd, ios, 1, 0));
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_CUR));
+
+  ASSERT_EQ(5, read_fn(tf.fd, ios, 1, 5));
+  ASSERT_STREQ("world", buf);
+  ASSERT_EQ(5, read_fn(tf.fd, ios, 1, 0));
+  ASSERT_STREQ("hello", buf);
+}
+
+TEST(sys_uio, preadv_pwritev) {
+  TestPreadVPwriteV(preadv, pwritev);
+}
+
+TEST(sys_uio, preadv64_pwritev64) {
+  TestPreadVPwriteV(preadv64, pwritev64);
+}
+
+TEST(sys_uio, process_vm_readv) {
   ASSERT_EQ(0, process_vm_readv(0, nullptr, 0, nullptr, 0, 0));
 }
 
-TEST(sys_uio, process_vm_writev_ESRCH) {
+TEST(sys_uio, process_vm_writev) {
   ASSERT_EQ(0, process_vm_writev(0, nullptr, 0, nullptr, 0, 0));
 }