Merge "Increase support of pathconf options."
diff --git a/README.md b/README.md
index 300b98f..a031fbc 100644
--- a/README.md
+++ b/README.md
@@ -174,6 +174,18 @@
   1. Run update-tzdata.py.
 
 
+Verifying changes
+-----------------
+
+If you make a change that is likely to have a wide effect on the tree (such as a
+libc header change), you should run `make checkbuild`. A regular `make` will
+_not_ build the entire tree; just the minimum number of projects that are
+required for the device. Tests, additional developer tools, and various other
+modules will not be built. Note that `make checkbuild` will not be complete
+either, as `make tests` covers a few additional modules, but generally speaking
+`make checkbuild` is enough.
+
+
 Running the tests
 -----------------
 
diff --git a/libc/arch-x86_64/string/sse2-strlcat-slm.S b/libc/arch-x86_64/string/sse2-strlcat-slm.S
new file mode 100644
index 0000000..d79e8c1
--- /dev/null
+++ b/libc/arch-x86_64/string/sse2-strlcat-slm.S
@@ -0,0 +1,37 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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 Intel Corporation nor the names of its contributors
+    * may be used to endorse or promote products derived from this software
+    * without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define USE_AS_STRLCAT
+
+#ifndef STRLCPY
+# define STRLCPY	strlcat
+#endif
+
+#include "sse2-strlcpy-slm.S"
diff --git a/libc/arch-x86_64/string/sse2-strlcpy-slm.S b/libc/arch-x86_64/string/sse2-strlcpy-slm.S
new file mode 100755
index 0000000..9d4b52f
--- /dev/null
+++ b/libc/arch-x86_64/string/sse2-strlcpy-slm.S
@@ -0,0 +1,1062 @@
+/*
+Copyright (c) 2014, Intel Corporation
+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 Intel Corporation nor the names of its contributors
+    * may be used to endorse or promote products derived from this software
+    * without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef L
+# define L(label)	.L##label
+#endif
+
+#ifndef cfi_startproc
+# define cfi_startproc	.cfi_startproc
+#endif
+
+#ifndef cfi_endproc
+# define cfi_endproc	.cfi_endproc
+#endif
+
+#ifndef ENTRY
+# define ENTRY(name)	\
+	.type name, @function;	\
+	.globl name;	\
+	.p2align 4;	\
+name:	\
+	cfi_startproc
+#endif
+
+#ifndef END
+# define END(name)	\
+       cfi_endproc;	\
+       .size name, .-name
+#endif
+
+
+#ifndef STRLCPY
+# define STRLCPY	strlcpy
+#endif
+
+#define JMPTBL(I, B)	I - B
+#define BRANCH_TO_JMPTBL_ENTRY(TABLE, INDEX, SCALE)	\
+	lea	TABLE(%rip), %r11;	\
+	movslq	(%r11, INDEX, SCALE), %rcx;	\
+	lea	(%r11, %rcx), %rcx;	\
+	jmp	*%rcx
+
+#define RETURN	\
+	add	%r9, %rax;	\
+	ret
+
+.text
+ENTRY (STRLCPY)
+	xor	%rax, %rax
+	xor	%r9, %r9
+	mov	%rdx, %r8
+	cmp	$0, %r8
+	jz	L(CalculateSrcLen)
+
+#ifdef USE_AS_STRLCAT
+	xor	%rcx, %rcx
+	pxor	%xmm0, %xmm0
+
+	movdqu	(%rdi), %xmm1
+	pcmpeqb %xmm1, %xmm0
+	pmovmskb %xmm0, %rdx
+
+	cmp	$17, %r8
+	jb	L(SizeEndCase1)
+	test	%rdx, %rdx
+	jnz	L(StringEndCase1)
+
+	add	$16, %rax
+	movdqu	16(%rdi), %xmm1
+	pcmpeqb %xmm1, %xmm0
+	pmovmskb %xmm0, %rdx
+
+	cmp	$33, %r8
+	jb	L(SizeEndCase1)
+	test	%rdx, %rdx
+	jnz	L(StringEndCase1)
+
+	mov	%rdi, %rcx
+	and	$15, %rcx
+	and	$-16, %rdi
+
+	add	%rcx, %r8
+	sub	$16, %r8
+
+L(DstLenLoop):
+	movdqa	(%rdi, %rax), %xmm1
+	pcmpeqb %xmm1, %xmm0
+	pmovmskb %xmm0, %rdx
+	sub	$16, %r8
+	jbe	L(SizeEndCase2)
+	test	%rdx, %rdx
+	jnz	L(StringEndCase2)
+	add	$16, %rax
+	jmp	L(DstLenLoop)
+
+L(StringEndCase2):
+	add	$16, %r8
+	bsf	%rdx, %rdx
+	sub	%rdx, %r8
+	add	%rdx, %rax
+	sub	%rcx, %r9
+	add	%rax, %rdi
+	jmp	 L(CopySrcString)
+
+L(SizeEndCase1):
+	test	%rdx, %rdx
+	jz	L(SizeEnd)
+	bsf	%rdx, %rdx
+	add	%rdx, %rax
+	cmp	%r8, %rax
+	jb	L(StringEnd)
+L(SizeEnd):
+	mov	%r8, %r9
+	jmp	L(CalculateSrcLenCase1)
+
+L(SizeEndCase2):
+	add	$16, %r8
+	test	%rdx, %rdx
+	jz	L(StringEndCase4)
+	bsf	%rdx, %rdx
+	cmp	%r8, %rdx
+	jb	L(StringEndCase3)
+L(StringEndCase4):
+	add	%r8, %rax
+	sub	%rcx, %rax
+	mov	%rax, %r9
+	jmp	L(CalculateSrcLenCase1)
+
+L(StringEndCase3):
+	add	%rdx, %rax
+	sub	%rcx, %r9
+	add	%rax, %rdi
+	sub	%rdx, %r8
+	jmp	L(CopySrcString)
+
+L(StringEndCase1):
+	bsf	%rdx, %rdx
+	add	%rdx, %rax
+	sub	%rcx, %rax
+L(StringEnd):
+	add	%rax, %rdi
+	sub	%rax, %r8
+#endif
+
+	mov	%rsi, %rcx
+	and	$63, %rcx
+	cmp	$32, %rcx
+	jbe	L(CopySrcString)
+
+	and	$-16, %rsi
+	and	$15, %rcx
+	pxor	%xmm0, %xmm0
+	pxor	%xmm1, %xmm1
+
+	pcmpeqb	(%rsi), %xmm1
+	pmovmskb %xmm1, %rdx
+	shr	%cl, %rdx
+	mov	$16, %r10
+	sub	%rcx, %r10
+	cmp	%r10, %r8
+	jbe	L(CopyFrom1To16BytesTailCase2OrCase3)
+	test	%rdx, %rdx
+	jnz	L(CopyFrom1To16BytesTail)
+
+	pcmpeqb	16(%rsi), %xmm0
+	pmovmskb %xmm0, %rdx
+	add	$16, %r10
+	cmp	%r10, %r8
+	jbe	L(CopyFrom1To32BytesCase2OrCase3)
+	test	%rdx, %rdx
+	jnz	L(CopyFrom1To32Bytes)
+
+	movdqu	(%rsi, %rcx), %xmm1
+	movdqu	%xmm1, (%rdi)
+#ifdef USE_AS_STRLCAT
+	add	%rax, %r9
+#endif
+	jmp	L(LoopStart)
+
+	.p2align 4
+L(CopySrcString):
+#ifdef USE_AS_STRLCAT
+	add	%rax, %r9
+	xor	%rax, %rax
+#endif
+	pxor	%xmm0, %xmm0
+	movdqu	(%rsi), %xmm1
+	pcmpeqb	%xmm1, %xmm0
+	pmovmskb %xmm0, %rdx
+
+	cmp	$17, %r8
+	jb	L(CopyFrom1To16BytesTail1Case2OrCase3)
+	test	%rdx, %rdx
+	jnz	L(CopyFrom1To16BytesTail1)
+
+	movdqu	16(%rsi), %xmm2
+	pcmpeqb	%xmm2, %xmm0
+	movdqu	%xmm1, (%rdi)
+	pmovmskb %xmm0, %rdx
+	add	$16, %rax
+
+	cmp	$33, %r8
+	jb	L(CopyFrom1To32Bytes1Case2OrCase3)
+	test	%rdx, %rdx
+	jnz	L(CopyFrom1To32Bytes1)
+
+	mov	%rsi, %rcx
+	and	$15, %rcx
+	and	$-16, %rsi
+
+L(LoopStart):
+	sub	%rcx, %rdi
+	add	%rcx, %r8
+	sub	$16, %r8
+	mov	$16, %rax
+
+L(16Loop):
+	movdqa	(%rsi, %rax), %xmm1
+	pcmpeqb	%xmm1, %xmm0
+	pmovmskb %xmm0, %rdx
+	sub	$16, %r8
+	jbe	L(CopyFrom1To16BytesCase2OrCase3)
+	test	%rdx, %rdx
+	jnz	L(CopyFrom1To16BytesXmmExit)
+	movdqu	%xmm1, (%rdi, %rax)
+	add	$16, %rax
+	jmp	L(16Loop)
+
+/*------End of main part with loops---------------------*/
+
+/* Case1 */
+	.p2align 4
+L(CopyFrom1To16Bytes):
+	add	%rcx, %rdi
+	add	%rcx, %rsi
+	bsf	%rdx, %rdx
+	add	%rdx, %rax
+	BRANCH_TO_JMPTBL_ENTRY (L(ExitStringTailTable), %rdx, 4)
+
+	.p2align 4
+L(CopyFrom1To16BytesTail):
+	add	%rcx, %rsi
+	bsf	%rdx, %rdx
+	add	%rdx, %rax
+	BRANCH_TO_JMPTBL_ENTRY (L(ExitStringTailTable), %rdx, 4)
+
+	.p2align 4
+L(CopyFrom1To32Bytes1):
+	add	$16, %rsi
+	add	$16, %rdi
+	sub	$16, %r8
+L(CopyFrom1To16BytesTail1):
+	bsf	%rdx, %rdx
+	add	%rdx, %rax
+	BRANCH_TO_JMPTBL_ENTRY (L(ExitStringTailTable), %rdx, 4)
+
+	.p2align 4
+L(CopyFrom1To32Bytes):
+	bsf	%rdx, %rdx
+	add	%rcx, %rsi
+	add	$16, %rdx
+	sub	%rcx, %rdx
+	add	%rdx, %rax
+	BRANCH_TO_JMPTBL_ENTRY (L(ExitStringTailTable), %rdx, 4)
+
+	.p2align 4
+L(CopyFrom1To16BytesExit):
+	add	%rdx, %rax
+	BRANCH_TO_JMPTBL_ENTRY (L(ExitStringTailTable), %rdx, 4)
+
+/* Case2 */
+
+	.p2align 4
+L(CopyFrom1To16BytesCase2):
+	add	$16, %r8
+	add	%rax, %rdi
+	add	%rax, %rsi
+	bsf	%rdx, %rdx
+	sub	%rcx, %rax
+	cmp	%r8, %rdx
+	jb	L(CopyFrom1To16BytesExit)
+	add	%r8, %rax
+	BRANCH_TO_JMPTBL_ENTRY (L(ExitTable), %r8, 4)
+
+	.p2align 4
+L(CopyFrom1To32BytesCase2):
+	add	%rcx, %rsi
+	bsf	%rdx, %rdx
+	add	$16, %rdx
+	sub	%rcx, %rdx
+	cmp	%r8, %rdx
+	jb	L(CopyFrom1To16BytesExit)
+	add	%r8, %rax
+	BRANCH_TO_JMPTBL_ENTRY (L(ExitTable), %r8, 4)
+
+L(CopyFrom1To16BytesTailCase2):
+	add	%rcx, %rsi
+	bsf	%rdx, %rdx
+	cmp	%r8, %rdx
+	jb	L(CopyFrom1To16BytesExit)
+	add	%r8, %rax
+	BRANCH_TO_JMPTBL_ENTRY (L(ExitTable), %r8, 4)
+
+	.p2align 4
+L(CopyFrom1To16BytesTail1Case2):
+	bsf	%rdx, %rdx
+	cmp	%r8, %rdx
+	jb	L(CopyFrom1To16BytesExit)
+	add	%r8, %rax
+	BRANCH_TO_JMPTBL_ENTRY (L(ExitTable), %r8, 4)
+
+/* Case2 or Case3,  Case3 */
+
+	.p2align 4
+L(CopyFrom1To16BytesCase2OrCase3):
+	test	%rdx, %rdx
+	jnz	L(CopyFrom1To16BytesCase2)
+	add	$16, %r8
+	add	%rax, %rdi
+	add	%rax, %rsi
+	add	%r8, %rax
+	sub	%rcx, %rax
+	BRANCH_TO_JMPTBL_ENTRY (L(ExitTable), %r8, 4)
+
+	.p2align 4
+L(CopyFrom1To32BytesCase2OrCase3):
+	test	%rdx, %rdx
+	jnz	L(CopyFrom1To32BytesCase2)
+	add	%rcx, %rsi
+	add	%r8, %rax
+	BRANCH_TO_JMPTBL_ENTRY (L(ExitTable), %r8, 4)
+
+	.p2align 4
+L(CopyFrom1To16BytesTailCase2OrCase3):
+	test	%rdx, %rdx
+	jnz	L(CopyFrom1To16BytesTailCase2)
+	add	%rcx, %rsi
+	add	%r8, %rax
+	BRANCH_TO_JMPTBL_ENTRY (L(ExitTable), %r8, 4)
+
+	.p2align 4
+L(CopyFrom1To32Bytes1Case2OrCase3):
+	add	$16, %rdi
+	add	$16, %rsi
+	sub	$16, %r8
+L(CopyFrom1To16BytesTail1Case2OrCase3):
+	test	%rdx, %rdx
+	jnz	L(CopyFrom1To16BytesTail1Case2)
+	add	%r8, %rax
+	BRANCH_TO_JMPTBL_ENTRY (L(ExitTable), %r8, 4)
+
+	.p2align 4
+L(CopyFrom1To16BytesXmmExit):
+	bsf	%rdx, %rdx
+	add	%rax, %rdi
+	add	%rax, %rsi
+	add	%rdx, %rax
+	sub	%rcx, %rax
+	BRANCH_TO_JMPTBL_ENTRY (L(ExitStringTailTable), %rdx, 4)
+
+/*------------End labels regarding with copying 1-16 bytes--and 1-32 bytes----*/
+
+
+	.p2align 4
+L(Exit0):
+	RETURN
+
+	.p2align 4
+L(Exit1):
+	movb	$0, (%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit2):
+	movb	(%rsi), %dh
+	movb	%dh, (%rdi)
+	movb	$0, 1(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit3):
+	movw	(%rsi), %dx
+	movw	%dx, (%rdi)
+	movb	$0, 2(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit4):
+	movw	(%rsi), %cx
+	movb	2(%rsi), %dh
+	movw	%cx, (%rdi)
+	movb	%dh, 2(%rdi)
+	movb	$0, 3(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit5):
+	movl	(%rsi), %edx
+	movl	%edx, (%rdi)
+	movb	$0, 4(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit6):
+	movl	(%rsi), %ecx
+	movb	4(%rsi), %dh
+	movl	%ecx, (%rdi)
+	movb	%dh, 4(%rdi)
+	movb	$0, 5(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit7):
+	movl	(%rsi), %ecx
+	movw	4(%rsi), %dx
+	movl	%ecx, (%rdi)
+	movw	%dx, 4(%rdi)
+	movb	$0, 6(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit8):
+	movl	(%rsi), %ecx
+	movl	3(%rsi), %edx
+	movl	%ecx, (%rdi)
+	movl	%edx, 3(%rdi)
+	movb	$0, 7(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit9):
+	movq	(%rsi), %rdx
+	movq	%rdx, (%rdi)
+	movb	$0, 8(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit10):
+	movq	(%rsi), %rcx
+	movb	8(%rsi), %dh
+	movq	%rcx, (%rdi)
+	movb	%dh, 8(%rdi)
+	movb	$0, 9(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit11):
+	movq	(%rsi), %rcx
+	movw	8(%rsi), %dx
+	movq	%rcx, (%rdi)
+	movw	%dx, 8(%rdi)
+	movb	$0, 10(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit12):
+	movq	(%rsi), %rcx
+	movl	7(%rsi), %edx
+	movq	%rcx, (%rdi)
+	movl	%edx, 7(%rdi)
+	movb	$0, 11(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit13):
+	movq	(%rsi), %rcx
+	movl	8(%rsi), %edx
+	movq	%rcx, (%rdi)
+	movl	%edx, 8(%rdi)
+	movb	$0, 12(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit14):
+	movq	(%rsi), %rcx
+	movq	5(%rsi), %rdx
+	movq	%rcx, (%rdi)
+	movq	%rdx, 5(%rdi)
+	movb	$0, 13(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit15):
+	movq	(%rsi), %rcx
+	movq	6(%rsi), %rdx
+	movq	%rcx, (%rdi)
+	movq	%rdx, 6(%rdi)
+	movb	$0, 14(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit16):
+	movq	(%rsi), %rcx
+	movq	7(%rsi), %rdx
+	movq	%rcx, (%rdi)
+	movq	%rdx, 7(%rdi)
+	movb	$0, 15(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit17):
+	movdqu	(%rsi), %xmm0
+	movdqu	%xmm0, (%rdi)
+	movb	$0, 16(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit18):
+	movdqu	(%rsi), %xmm0
+	movb	16(%rsi), %dh
+	movdqu	%xmm0, (%rdi)
+	movb	%dh, 16(%rdi)
+	movb	$0, 17(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit19):
+	movdqu	(%rsi), %xmm0
+	movw	16(%rsi), %cx
+	movdqu	%xmm0, (%rdi)
+	movw	%cx, 16(%rdi)
+	movb	$0, 18(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit20):
+	movdqu	(%rsi), %xmm0
+	movl	15(%rsi), %ecx
+	movdqu	%xmm0, (%rdi)
+	movl	%ecx, 15(%rdi)
+	movb	$0, 19(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit21):
+	movdqu	(%rsi), %xmm0
+	movl	16(%rsi), %ecx
+	movdqu	%xmm0, (%rdi)
+	movl	%ecx, 16(%rdi)
+	movb	$0, 20(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit22):
+	movdqu	(%rsi), %xmm0
+	movl	16(%rsi), %ecx
+	movb	20(%rsi), %dh
+	movdqu	%xmm0, (%rdi)
+	movl	%ecx, 16(%rdi)
+	movb	%dh, 20(%rdi)
+	movb	$0, 21(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit23):
+	movdqu	(%rsi), %xmm0
+	movq	14(%rsi), %rcx
+	movdqu	%xmm0, (%rdi)
+	movq	%rcx, 14(%rdi)
+	movb	$0, 22(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit24):
+	movdqu	(%rsi), %xmm0
+	movq	15(%rsi), %rcx
+	movdqu	%xmm0, (%rdi)
+	movq	%rcx, 15(%rdi)
+	movb	$0, 23(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit25):
+	movdqu	(%rsi), %xmm0
+	movq	16(%rsi), %rcx
+	movdqu	%xmm0, (%rdi)
+	movq	%rcx, 16(%rdi)
+	movb	$0, 24(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit26):
+	movdqu	(%rsi), %xmm0
+	movq	16(%rsi), %rcx
+	movb	24(%rsi), %dh
+	movdqu	%xmm0, (%rdi)
+	movq	%rcx, 16(%rdi)
+	mov	%dh, 24(%rdi)
+	movb	$0, 25(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit27):
+	movdqu	(%rsi), %xmm0
+	movq	16(%rsi), %rdx
+	movw	24(%rsi), %cx
+	movdqu	%xmm0, (%rdi)
+	movq	%rdx, 16(%rdi)
+	movw	%cx, 24(%rdi)
+	movb	$0, 26(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit28):
+	movdqu	(%rsi), %xmm0
+	movq	16(%rsi), %rdx
+	movl	23(%rsi), %ecx
+	movdqu	%xmm0, (%rdi)
+	movq	%rdx, 16(%rdi)
+	movl	%ecx, 23(%rdi)
+	movb	$0, 27(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit29):
+	movdqu	(%rsi), %xmm0
+	movq	16(%rsi), %rdx
+	movl	24(%rsi), %ecx
+	movdqu	%xmm0, (%rdi)
+	movq	%rdx, 16(%rdi)
+	movl	%ecx, 24(%rdi)
+	movb	$0, 28(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit30):
+	movdqu	(%rsi), %xmm0
+	movdqu	13(%rsi), %xmm2
+	movdqu	%xmm0, (%rdi)
+	movdqu	%xmm2, 13(%rdi)
+	movb	$0, 29(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit31):
+	movdqu	(%rsi), %xmm0
+	movdqu	14(%rsi), %xmm2
+	movdqu	%xmm0, (%rdi)
+	movdqu	%xmm2, 14(%rdi)
+	movb	$0, 30(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(Exit32):
+	movdqu	(%rsi), %xmm0
+	movdqu	15(%rsi), %xmm2
+	movdqu	%xmm0, (%rdi)
+	movdqu	%xmm2, 15(%rdi)
+	movb	$0, 31(%rdi)
+	jmp	L(CalculateSrcLen)
+
+	.p2align 4
+L(StringTail0):
+	mov	(%rsi), %dl
+	mov	%dl, (%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail1):
+	mov	(%rsi), %dx
+	mov	%dx, (%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail2):
+	mov	(%rsi), %cx
+	mov	2(%rsi), %dl
+	mov	%cx, (%rdi)
+	mov	%dl, 2(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail3):
+	mov	(%rsi), %edx
+	mov	%edx, (%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail4):
+	mov	(%rsi), %ecx
+	mov	4(%rsi), %dl
+	mov	%ecx, (%rdi)
+	mov	%dl, 4(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail5):
+	mov	(%rsi), %ecx
+	mov	4(%rsi), %dx
+	mov	%ecx, (%rdi)
+	mov	%dx, 4(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail6):
+	mov	(%rsi), %ecx
+	mov	3(%rsi), %edx
+	mov	%ecx, (%rdi)
+	mov	%edx, 3(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail7):
+	mov	(%rsi), %rdx
+	mov	%rdx, (%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail8):
+	mov	(%rsi), %rcx
+	mov	8(%rsi), %dl
+	mov	%rcx, (%rdi)
+	mov	%dl, 8(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail9):
+	mov	(%rsi), %rcx
+	mov	8(%rsi), %dx
+	mov	%rcx, (%rdi)
+	mov	%dx, 8(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail10):
+	mov	(%rsi), %rcx
+	mov	7(%rsi), %edx
+	mov	%rcx, (%rdi)
+	mov	%edx, 7(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail11):
+	mov	(%rsi), %rcx
+	mov	8(%rsi), %edx
+	mov	%rcx, (%rdi)
+	mov	%edx, 8(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail12):
+	mov	(%rsi), %rcx
+	mov	5(%rsi), %rdx
+	mov	%rcx, (%rdi)
+	mov	%rdx, 5(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail13):
+	mov	(%rsi), %rcx
+	mov	6(%rsi), %rdx
+	mov	%rcx, (%rdi)
+	mov	%rdx, 6(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail14):
+	mov	(%rsi), %rcx
+	mov	7(%rsi), %rdx
+	mov	%rcx, (%rdi)
+	mov	%rdx, 7(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail15):
+	movdqu	(%rsi), %xmm0
+	movdqu	%xmm0, (%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail16):
+	movdqu	(%rsi), %xmm0
+	mov	16(%rsi), %cl
+	movdqu	%xmm0, (%rdi)
+	mov	%cl, 16(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail17):
+	movdqu	(%rsi), %xmm0
+	mov	16(%rsi), %cx
+	movdqu	%xmm0, (%rdi)
+	mov	%cx, 16(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail18):
+	movdqu	(%rsi), %xmm0
+	mov	15(%rsi), %ecx
+	movdqu	%xmm0, (%rdi)
+	mov	%ecx, 15(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail19):
+	movdqu	(%rsi), %xmm0
+	mov	16(%rsi), %ecx
+	movdqu	%xmm0, (%rdi)
+	mov	%ecx, 16(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail20):
+	movdqu	(%rsi), %xmm0
+	mov	16(%rsi), %ecx
+	mov	20(%rsi), %dl
+	movdqu	%xmm0, (%rdi)
+	mov	%ecx, 16(%rdi)
+	mov	%dl, 20(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail21):
+	movdqu	(%rsi), %xmm0
+	mov	14(%rsi), %rcx
+	movdqu	%xmm0, (%rdi)
+	mov	%rcx, 14(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail22):
+	movdqu	(%rsi), %xmm0
+	mov	15(%rsi), %rcx
+	movdqu	%xmm0, (%rdi)
+	mov	%rcx, 15(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail23):
+	movdqu	(%rsi), %xmm0
+	mov	16(%rsi), %rcx
+	movdqu	%xmm0, (%rdi)
+	mov	%rcx, 16(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail24):
+	movdqu	(%rsi), %xmm0
+	mov	16(%rsi), %rdx
+	mov	24(%rsi), %cl
+	movdqu	%xmm0, (%rdi)
+	mov	%rdx, 16(%rdi)
+	mov	%cl, 24(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail25):
+	movdqu	(%rsi), %xmm0
+	mov	16(%rsi), %rdx
+	mov	24(%rsi), %cx
+	movdqu	%xmm0, (%rdi)
+	mov	%rdx, 16(%rdi)
+	mov	%cx, 24(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail26):
+	movdqu	(%rsi), %xmm0
+	mov	16(%rsi), %rdx
+	mov	23(%rsi), %ecx
+	movdqu	%xmm0, (%rdi)
+	mov	%rdx, 16(%rdi)
+	mov	%ecx, 23(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail27):
+	movdqu	(%rsi), %xmm0
+	mov	16(%rsi), %rdx
+	mov	24(%rsi), %ecx
+	movdqu	%xmm0, (%rdi)
+	mov	%rdx, 16(%rdi)
+	mov	%ecx, 24(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail28):
+	movdqu	(%rsi), %xmm0
+	movdqu	13(%rsi), %xmm2
+	movdqu	%xmm0, (%rdi)
+	movdqu	%xmm2, 13(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail29):
+	movdqu	(%rsi), %xmm0
+	movdqu	14(%rsi), %xmm2
+	movdqu	%xmm0, (%rdi)
+	movdqu	%xmm2, 14(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail30):
+	movdqu	(%rsi), %xmm0
+	movdqu	15(%rsi), %xmm2
+	movdqu	%xmm0, (%rdi)
+	movdqu	%xmm2, 15(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail31):
+	movdqu	(%rsi), %xmm0
+	movdqu	16(%rsi), %xmm2
+	movdqu	%xmm0, (%rdi)
+	movdqu	%xmm2, 16(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail32):
+	movdqu	(%rsi), %xmm0
+	movdqu	16(%rsi), %xmm2
+	mov	32(%rsi), %cl
+	movdqu	%xmm0, (%rdi)
+	movdqu	%xmm2, 16(%rdi)
+	mov	%cl, 32(%rdi)
+	RETURN
+
+	.p2align 4
+L(StringTail33):
+	movdqu	(%rsi), %xmm0
+	movdqu	16(%rsi), %xmm2
+	mov	32(%rsi), %cl
+	movdqu	%xmm0, (%rdi)
+	movdqu	%xmm2, 16(%rdi)
+	mov	%cl, 32(%rdi)
+	RETURN
+
+	.p2align 4
+L(CalculateSrcLenCase1):
+	xor	%r8, %r8
+	xor	%rax, %rax
+L(CalculateSrcLen):
+	pxor	%xmm0, %xmm0
+	xor	%rcx, %rcx
+	add	%r8, %rsi
+	movdqu	(%rsi), %xmm1
+	pcmpeqb	%xmm1, %xmm0
+	pmovmskb %xmm0, %rdx
+	test	%rdx, %rdx
+	jnz	L(SrcLenLoopEnd)
+
+	add	%rax, %r9
+	mov	$16, %rax
+	mov	%rsi, %rcx
+	and	$15, %rcx
+	and	$-16, %rsi
+L(SrcLenLoop):
+	movdqa	(%rsi, %rax), %xmm1
+	pcmpeqb	%xmm1, %xmm0
+	pmovmskb %xmm0, %rdx
+	test	%rdx, %rdx
+	jnz	L(SrcLenLoopEnd)
+	add	$16, %rax
+	jmp	L(SrcLenLoop)
+
+	.p2align 4
+L(SrcLenLoopEnd):
+	bsf	%rdx, %rdx
+	add	%rdx, %rax
+	sub	%rcx, %rax
+	RETURN
+
+END (STRLCPY)
+
+	.p2align 4
+	.section .rodata
+L(ExitTable):
+	.int	JMPTBL(L(Exit0), L(ExitTable))
+	.int	JMPTBL(L(Exit1), L(ExitTable))
+	.int	JMPTBL(L(Exit2), L(ExitTable))
+	.int	JMPTBL(L(Exit3), L(ExitTable))
+	.int	JMPTBL(L(Exit4), L(ExitTable))
+	.int	JMPTBL(L(Exit5), L(ExitTable))
+	.int	JMPTBL(L(Exit6), L(ExitTable))
+	.int	JMPTBL(L(Exit7), L(ExitTable))
+	.int	JMPTBL(L(Exit8), L(ExitTable))
+	.int	JMPTBL(L(Exit9), L(ExitTable))
+	.int	JMPTBL(L(Exit10), L(ExitTable))
+	.int	JMPTBL(L(Exit11), L(ExitTable))
+	.int	JMPTBL(L(Exit12), L(ExitTable))
+	.int	JMPTBL(L(Exit13), L(ExitTable))
+	.int	JMPTBL(L(Exit14), L(ExitTable))
+	.int	JMPTBL(L(Exit15), L(ExitTable))
+	.int	JMPTBL(L(Exit16), L(ExitTable))
+	.int	JMPTBL(L(Exit17), L(ExitTable))
+	.int	JMPTBL(L(Exit18), L(ExitTable))
+	.int	JMPTBL(L(Exit19), L(ExitTable))
+	.int	JMPTBL(L(Exit20), L(ExitTable))
+	.int	JMPTBL(L(Exit21), L(ExitTable))
+	.int	JMPTBL(L(Exit22), L(ExitTable))
+	.int	JMPTBL(L(Exit23), L(ExitTable))
+	.int	JMPTBL(L(Exit24), L(ExitTable))
+	.int	JMPTBL(L(Exit25), L(ExitTable))
+	.int	JMPTBL(L(Exit26), L(ExitTable))
+	.int	JMPTBL(L(Exit27), L(ExitTable))
+	.int	JMPTBL(L(Exit28), L(ExitTable))
+	.int	JMPTBL(L(Exit29), L(ExitTable))
+	.int	JMPTBL(L(Exit30), L(ExitTable))
+	.int	JMPTBL(L(Exit31), L(ExitTable))
+	.int	JMPTBL(L(Exit32), L(ExitTable))
+L(ExitStringTailTable):
+	.int	JMPTBL(L(StringTail0), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail1), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail2), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail3), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail4), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail5), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail6), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail7), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail8), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail9), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail10), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail11), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail12), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail13), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail14), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail15), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail16), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail17), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail18), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail19), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail20), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail21), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail22), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail23), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail24), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail25), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail26), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail27), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail28), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail29), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail30), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail31), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail32), L(ExitStringTailTable))
+	.int	JMPTBL(L(StringTail33), L(ExitStringTailTable))
diff --git a/libc/arch-x86_64/x86_64.mk b/libc/arch-x86_64/x86_64.mk
index 8675ef4..00a3763 100644
--- a/libc/arch-x86_64/x86_64.mk
+++ b/libc/arch-x86_64/x86_64.mk
@@ -25,10 +25,6 @@
     upstream-freebsd/lib/libc/string/wmemcmp.c \
     upstream-freebsd/lib/libc/string/wmemmove.c \
 
-libc_openbsd_src_files_x86_64 += \
-    upstream-openbsd/lib/libc/string/strlcat.c \
-    upstream-openbsd/lib/libc/string/strlcpy.c \
-
 #
 # Inherently architecture-specific code.
 #
@@ -56,6 +52,8 @@
     arch-x86_64/string/sse2-stpncpy-slm.S \
     arch-x86_64/string/sse2-strcat-slm.S \
     arch-x86_64/string/sse2-strcpy-slm.S \
+    arch-x86_64/string/sse2-strlcat-slm.S \
+    arch-x86_64/string/sse2-strlcpy-slm.S \
     arch-x86_64/string/sse2-strlen-slm.S \
     arch-x86_64/string/sse2-strncat-slm.S \
     arch-x86_64/string/sse2-strncpy-slm.S \
diff --git a/libc/bionic/mntent.cpp b/libc/bionic/mntent.cpp
index 93b6915..f5a8eaa 100644
--- a/libc/bionic/mntent.cpp
+++ b/libc/bionic/mntent.cpp
@@ -28,11 +28,42 @@
 
 #include <mntent.h>
 
-mntent* getmntent(FILE*) {
-  return NULL;
+#include "private/ThreadLocalBuffer.h"
+
+GLOBAL_INIT_THREAD_LOCAL_BUFFER(getmntent_mntent);
+GLOBAL_INIT_THREAD_LOCAL_BUFFER(getmntent_strings);
+
+mntent* getmntent(FILE* fp) {
+  LOCAL_INIT_THREAD_LOCAL_BUFFER(mntent*, getmntent_mntent, sizeof(mntent));
+  LOCAL_INIT_THREAD_LOCAL_BUFFER(char*, getmntent_strings, BUFSIZ);
+  return getmntent_r(fp, getmntent_mntent_tls_buffer,
+                     getmntent_strings_tls_buffer, getmntent_strings_tls_buffer_size);
 }
 
-mntent* getmntent_r(FILE*, struct mntent*, char*, int) {
+mntent* getmntent_r(FILE* fp, struct mntent* e, char* buf, int buf_len) {
+  memset(e, 0, sizeof(*e));
+  while (fgets(buf, buf_len, fp) != NULL) {
+    // Entries look like "proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0".
+    // That is: mnt_fsname mnt_dir mnt_type mnt_opts 0 0.
+    int fsname0, fsname1, dir0, dir1, type0, type1, opts0, opts1;
+    if (sscanf(buf, " %n%*s%n %n%*s%n %n%*s%n %n%*s%n %d %d",
+               &fsname0, &fsname1, &dir0, &dir1, &type0, &type1, &opts0, &opts1,
+               &e->mnt_freq, &e->mnt_passno) == 2) {
+      e->mnt_fsname = &buf[fsname0];
+      buf[fsname1] = '\0';
+
+      e->mnt_dir = &buf[dir0];
+      buf[dir1] = '\0';
+
+      e->mnt_type = &buf[type0];
+      buf[type1] = '\0';
+
+      e->mnt_opts = &buf[opts0];
+      buf[opts1] = '\0';
+
+      return e;
+    }
+  }
   return NULL;
 }
 
diff --git a/libc/bionic/pthread_setname_np.cpp b/libc/bionic/pthread_setname_np.cpp
index 7b2fa6b..93d4b2f 100644
--- a/libc/bionic/pthread_setname_np.cpp
+++ b/libc/bionic/pthread_setname_np.cpp
@@ -61,7 +61,7 @@
   {
     pthread_accessor thread(t);
     if (thread.get() == NULL) {
-      return ESRCH;
+      return ENOENT;
     }
     tid = thread->tid;
   }
diff --git a/libc/bionic/sysconf.cpp b/libc/bionic/sysconf.cpp
index 951860d..fb704ad 100644
--- a/libc/bionic/sysconf.cpp
+++ b/libc/bionic/sysconf.cpp
@@ -149,7 +149,7 @@
   return (rc == -1) ? -1 : _POSIX_VERSION;
 }
 
-int sysconf(int name) {
+long sysconf(int name) {
   switch (name) {
     case _SC_ARG_MAX:           return _POSIX_ARG_MAX;
     case _SC_CHILD_MAX:         return CHILD_MAX;
diff --git a/libc/dns/net/getaddrinfo.c b/libc/dns/net/getaddrinfo.c
index 5443999..132a090 100644
--- a/libc/dns/net/getaddrinfo.c
+++ b/libc/dns/net/getaddrinfo.c
@@ -408,6 +408,15 @@
 	return _test_connect(PF_INET, &addr.generic, sizeof(addr.in), mark);
 }
 
+bool readBE32(FILE* fp, int32_t* result) {
+  int32_t tmp;
+  if (fread(&tmp, sizeof(tmp), 1, fp) != 1) {
+    return false;
+  }
+  *result = ntohl(tmp);
+  return true;
+}
+
 // Returns 0 on success, else returns on error.
 static int
 android_getaddrinfo_proxy(
@@ -486,61 +495,62 @@
 	struct addrinfo* ai = NULL;
 	struct addrinfo** nextres = res;
 	while (1) {
-		uint32_t addrinfo_len;
-		if (fread(&addrinfo_len, sizeof(addrinfo_len),
-			  1, proxy) != 1) {
+		int32_t have_more;
+		if (!readBE32(proxy, &have_more)) {
 			break;
 		}
-		addrinfo_len = ntohl(addrinfo_len);
-		if (addrinfo_len == 0) {
+		if (have_more == 0) {
 			success = 1;
 			break;
 		}
 
-		if (addrinfo_len < sizeof(struct addrinfo)) {
-			break;
-		}
-		struct addrinfo* ai = calloc(1, addrinfo_len +
-					     sizeof(struct sockaddr_storage));
+		struct addrinfo* ai = calloc(1, sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
 		if (ai == NULL) {
 			break;
 		}
+		ai->ai_addr = (struct sockaddr*)(ai + 1);
 
-		if (fread(ai, addrinfo_len, 1, proxy) != 1) {
-			// Error; fall through.
+		// struct addrinfo {
+		//	int	ai_flags;	/* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
+		//	int	ai_family;	/* PF_xxx */
+		//	int	ai_socktype;	/* SOCK_xxx */
+		//	int	ai_protocol;	/* 0 or IPPROTO_xxx for IPv4 and IPv6 */
+		//	socklen_t ai_addrlen;	/* length of ai_addr */
+		//	char	*ai_canonname;	/* canonical name for hostname */
+		//	struct	sockaddr *ai_addr;	/* binary address */
+		//	struct	addrinfo *ai_next;	/* next structure in linked list */
+		// };
+
+		// Read the struct piece by piece because we might be a 32-bit process
+		// talking to a 64-bit netd.
+		int32_t addr_len;
+		bool success =
+				readBE32(proxy, &ai->ai_flags) &&
+				readBE32(proxy, &ai->ai_family) &&
+				readBE32(proxy, &ai->ai_socktype) &&
+				readBE32(proxy, &ai->ai_protocol) &&
+				readBE32(proxy, &addr_len);
+		if (!success) {
 			break;
 		}
 
-		// Zero out the pointer fields we copied which aren't
-		// valid in this address space.
-		ai->ai_addr = NULL;
-		ai->ai_canonname = NULL;
-		ai->ai_next = NULL;
-
-		// struct sockaddr
-		uint32_t addr_len;
-		if (fread(&addr_len, sizeof(addr_len), 1, proxy) != 1) {
-			break;
-		}
-		addr_len = ntohl(addr_len);
+		// Set ai_addrlen and read the ai_addr data.
+		ai->ai_addrlen = addr_len;
 		if (addr_len != 0) {
-			if (addr_len > sizeof(struct sockaddr_storage)) {
+			if ((size_t) addr_len > sizeof(struct sockaddr_storage)) {
 				// Bogus; too big.
 				break;
 			}
-			struct sockaddr* addr = (struct sockaddr*)(ai + 1);
-			if (fread(addr, addr_len, 1, proxy) != 1) {
+			if (fread(ai->ai_addr, addr_len, 1, proxy) != 1) {
 				break;
 			}
-			ai->ai_addr = addr;
 		}
 
-		// cannonname
-		uint32_t name_len;
-		if (fread(&name_len, sizeof(name_len), 1, proxy) != 1) {
+		// The string for ai_cannonname.
+		int32_t name_len;
+		if (!readBE32(proxy, &name_len)) {
 			break;
 		}
-		name_len = ntohl(name_len);
 		if (name_len != 0) {
 			ai->ai_canonname = (char*) malloc(name_len);
 			if (fread(ai->ai_canonname, name_len, 1, proxy) != 1) {
diff --git a/libc/include/elf.h b/libc/include/elf.h
index b1166db..2039cc0 100644
--- a/libc/include/elf.h
+++ b/libc/include/elf.h
@@ -94,6 +94,9 @@
 #define DT_PREINIT_ARRAY 32
 #define DT_PREINIT_ARRAYSZ 33
 
+/* gnu hash entry */
+#define DT_GNU_HASH 0x6ffffef5
+
 #define ELFOSABI_SYSV 0 /* Synonym for ELFOSABI_NONE used by valgrind. */
 
 #define PT_GNU_RELRO 0x6474e552
diff --git a/libc/include/stdatomic.h b/libc/include/stdatomic.h
index 58cb1bc..2c4f1ce 100644
--- a/libc/include/stdatomic.h
+++ b/libc/include/stdatomic.h
@@ -32,6 +32,12 @@
 
 #include <sys/cdefs.h>
 
+#if defined(__GNUC__) && !defined(__GNUC_PREREQ)
+/* Duplicate definition here, since the mingw sys/cdefs.h omits the  */
+/* definition, and this needs to be usable there.                    */
+#define	__GNUC_PREREQ(x, y)    \
+	((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || (__GNUC__ > (x)))
+#endif /* __GNUC__ && ... */
 
 #if defined(__cplusplus) && __cplusplus >= 201103L && defined(_USING_LIBCXX)
 # ifdef __clang__
diff --git a/libc/include/stdio.h b/libc/include/stdio.h
index a7ec615..c0dac1a 100644
--- a/libc/include/stdio.h
+++ b/libc/include/stdio.h
@@ -146,7 +146,16 @@
 	fpos_t	_offset;	/* current lseek offset */
 } FILE;
 
+/* Legacy BSD implementation of stdin/stdout/stderr. */
 extern FILE __sF[];
+/* More obvious implementation. */
+extern FILE* stdin;
+extern FILE* stdout;
+extern FILE* stderr;
+/* C99 and earlier plus current C++ standards say these must be macros. */
+#define stdin stdin
+#define stdout stdout
+#define stderr stderr
 
 #define	__SLBF	0x0001		/* line buffered */
 #define	__SNBF	0x0002		/* unbuffered */
@@ -209,10 +218,6 @@
 #define	SEEK_END	2	/* set file offset to EOF plus offset */
 #endif
 
-#define	stdin	(&__sF[0])
-#define	stdout	(&__sF[1])
-#define	stderr	(&__sF[2])
-
 /*
  * Functions defined in ANSI C standard.
  */
diff --git a/libc/include/sys/sysconf.h b/libc/include/sys/sysconf.h
index 0a46e7a..3d058d7 100644
--- a/libc/include/sys/sysconf.h
+++ b/libc/include/sys/sysconf.h
@@ -129,7 +129,7 @@
 #define _SC_AVPHYS_PAGES                0x0063
 #define _SC_MONOTONIC_CLOCK             0x0064
 
-extern int sysconf(int name);
+long sysconf(int);
 
 __END_DECLS
 
diff --git a/libc/include/unistd.h b/libc/include/unistd.h
index dca9b3d..e94ee66 100644
--- a/libc/include/unistd.h
+++ b/libc/include/unistd.h
@@ -202,7 +202,7 @@
 
 int getpagesize(void);
 
-extern int sysconf(int  name);
+long sysconf(int);
 
 extern int daemon(int, int);
 
diff --git a/libc/include/wchar.h b/libc/include/wchar.h
index 9e279d3..cfd2299 100644
--- a/libc/include/wchar.h
+++ b/libc/include/wchar.h
@@ -111,6 +111,8 @@
 extern int vswscanf(const wchar_t*, const wchar_t*, va_list);
 extern int vwprintf(const wchar_t*, va_list);
 extern int vwscanf(const wchar_t*, va_list);
+extern wchar_t* wcpcpy (wchar_t*, const wchar_t *);
+extern wchar_t* wcpncpy (wchar_t*, const wchar_t *, size_t);
 extern size_t            wcrtomb(char *, wchar_t, mbstate_t *);
 extern int               wcscasecmp(const wchar_t *, const wchar_t *);
 extern int               wcscasecmp_l(const wchar_t *, const wchar_t *, locale_t);
diff --git a/libc/stdio/fileext.h b/libc/stdio/fileext.h
index 7efff8f..dc89fff 100644
--- a/libc/stdio/fileext.h
+++ b/libc/stdio/fileext.h
@@ -43,10 +43,6 @@
 	pthread_mutex_t _lock; /* file lock */
 };
 
-__LIBC_HIDDEN__ extern struct __sfileext __sFext[3];
-
-#define _FILEEXT_INITIALIZER  {{NULL,0},{0},PTHREAD_RECURSIVE_MUTEX_INITIALIZER}
-
 #define _EXT(fp) ((struct __sfileext *)((fp)->_ext._base))
 #define _UB(fp) _EXT(fp)->_ub
 #define _FLOCK(fp)  _EXT(fp)->_lock
diff --git a/libc/stdio/findfp.c b/libc/stdio/findfp.c
index ef8e9e6..5e51198 100644
--- a/libc/stdio/findfp.c
+++ b/libc/stdio/findfp.c
@@ -59,12 +59,15 @@
 static struct glue *lastglue = &uglue;
 _THREAD_PRIVATE_MUTEX(__sfp_mutex);
 
-struct __sfileext __sFext[3];
+static struct __sfileext __sFext[3];
 FILE __sF[3] = {
 	std(__SRD, STDIN_FILENO),		/* stdin */
 	std(__SWR, STDOUT_FILENO),		/* stdout */
 	std(__SWR|__SNBF, STDERR_FILENO)	/* stderr */
 };
+FILE* stdin = &__sF[0];
+FILE* stdout = &__sF[1];
+FILE* stderr = &__sF[2];
 struct glue __sglue = { &uglue, 3, __sF };
 
 static struct glue *
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index 367179d..2486e02 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -145,7 +145,7 @@
   info->dli_fbase = reinterpret_cast<void*>(si->base);
 
   // Determine if any symbol in the library contains the specified address.
-  ElfW(Sym)* sym = dladdr_find_symbol(si, addr);
+  ElfW(Sym)* sym = si->find_symbol_by_address(addr);
   if (sym != nullptr) {
     info->dli_sname = si->get_string(sym->st_name);
     info->dli_saddr = reinterpret_cast<void*>(si->resolve_symbol_address(sym));
@@ -238,14 +238,14 @@
 soinfo* get_libdl_info() {
   if ((__libdl_info.flags & FLAG_LINKED) == 0) {
     __libdl_info.flags |= FLAG_LINKED;
-    __libdl_info.strtab = ANDROID_LIBDL_STRTAB;
-    __libdl_info.symtab = g_libdl_symtab;
-    __libdl_info.nbucket = sizeof(g_libdl_buckets)/sizeof(unsigned);
-    __libdl_info.nchain = sizeof(g_libdl_chains)/sizeof(unsigned);
-    __libdl_info.bucket = g_libdl_buckets;
-    __libdl_info.chain = g_libdl_chains;
+    __libdl_info.strtab_ = ANDROID_LIBDL_STRTAB;
+    __libdl_info.symtab_ = g_libdl_symtab;
+    __libdl_info.nbucket_ = sizeof(g_libdl_buckets)/sizeof(unsigned);
+    __libdl_info.nchain_ = sizeof(g_libdl_chains)/sizeof(unsigned);
+    __libdl_info.bucket_ = g_libdl_buckets;
+    __libdl_info.chain_ = g_libdl_chains;
     __libdl_info.ref_count = 1;
-    __libdl_info.strtab_size = sizeof(ANDROID_LIBDL_STRTAB);
+    __libdl_info.strtab_size_ = sizeof(ANDROID_LIBDL_STRTAB);
   }
 
   return &__libdl_info;
diff --git a/linker/linker.cpp b/linker/linker.cpp
index b2911b8..11d7b94 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -35,6 +35,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
+#include <sys/param.h>
 #include <unistd.h>
 
 #include <new>
@@ -316,6 +317,7 @@
     }
     prev = trav;
   }
+
   if (trav == nullptr) {
     // si was not in solist
     DL_ERR("name \"%s\" is not in solist!", si->name);
@@ -335,7 +337,6 @@
   g_soinfo_allocator.free(si);
 }
 
-
 static void parse_path(const char* path, const char* delimiters,
                        const char** array, char* buf, size_t buf_size, size_t max_count) {
   if (path == nullptr) {
@@ -415,39 +416,72 @@
   return rv;
 }
 
-static ElfW(Sym)* soinfo_elf_lookup(const soinfo* si, unsigned hash, const char* name) {
-  ElfW(Sym)* symtab = si->symtab;
+ElfW(Sym)* soinfo::find_symbol_by_name(SymbolName& symbol_name) {
+  return is_gnu_hash() ? gnu_lookup(symbol_name) : elf_lookup(symbol_name);
+}
 
-  TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p %x %zd",
-             name, si->name, reinterpret_cast<void*>(si->base), hash, hash % si->nbucket);
+static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) {
+  if (ELF_ST_BIND(s->st_info) == STB_GLOBAL ||
+      ELF_ST_BIND(s->st_info) == STB_WEAK) {
+    return s->st_shndx != SHN_UNDEF;
+  } else if (ELF_ST_BIND(s->st_info) != STB_LOCAL) {
+    DL_WARN("unexpected ST_BIND value: %d for '%s' in '%s'",
+        ELF_ST_BIND(s->st_info), si->get_string(s->st_name), si->name);
+  }
 
-  for (unsigned n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n]) {
-    ElfW(Sym)* s = symtab + n;
-    if (strcmp(si->get_string(s->st_name), name)) continue;
+  return false;
+}
 
-    // only concern ourselves with global and weak symbol definitions
-    switch (ELF_ST_BIND(s->st_info)) {
-      case STB_GLOBAL:
-      case STB_WEAK:
-        if (s->st_shndx == SHN_UNDEF) {
-          continue;
-        }
+ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) {
+  uint32_t hash = symbol_name.gnu_hash();
+  uint32_t h2 = hash >> gnu_shift2_;
 
-        TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
-                 name, si->name, reinterpret_cast<void*>(s->st_value),
-                 static_cast<size_t>(s->st_size));
-        return s;
-      case STB_LOCAL:
-        continue;
-      default:
-        __libc_fatal("ERROR: Unexpected ST_BIND value: %d for '%s' in '%s'",
-            ELF_ST_BIND(s->st_info), name, si->name);
+  uint32_t bloom_mask_bits = sizeof(ElfW(Addr))*8;
+  uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_;
+  ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];
+
+  // test against bloom filter
+  if ((1 & (bloom_word >> (hash % bloom_mask_bits)) & (bloom_word >> (h2 % bloom_mask_bits))) == 0) {
+    return nullptr;
+  }
+
+  // bloom test says "probably yes"...
+  uint32_t n = bucket_[hash % nbucket_];
+
+  if (n == 0) {
+    return nullptr;
+  }
+
+  do {
+    ElfW(Sym)* s = symtab_ + n;
+    if (((chain_[n] ^ hash) >> 1) == 0 &&
+        strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
+        is_symbol_global_and_defined(this, s)) {
+      return s;
+    }
+  } while ((chain_[n++] & 1) == 0);
+
+  return nullptr;
+}
+
+ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name) {
+  uint32_t hash = symbol_name.elf_hash();
+
+  TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd",
+             symbol_name.get_name(), name, reinterpret_cast<void*>(base), hash, hash % nbucket_);
+
+  for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) {
+    ElfW(Sym)* s = symtab_ + n;
+    if (strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 && is_symbol_global_and_defined(this, s)) {
+      TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
+               symbol_name.get_name(), name, reinterpret_cast<void*>(s->st_value),
+               static_cast<size_t>(s->st_size));
+      return s;
     }
   }
 
   TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p %x %zd",
-             name, si->name, reinterpret_cast<void*>(si->base), hash, hash % si->nbucket);
-
+             symbol_name.get_name(), name, reinterpret_cast<void*>(base), hash, hash % nbucket_);
 
   return nullptr;
 }
@@ -457,33 +491,55 @@
 
   strlcpy(this->name, name, sizeof(this->name));
   flags = FLAG_NEW_SOINFO;
-  version = SOINFO_VERSION;
+  version_ = SOINFO_VERSION;
 
   if (file_stat != nullptr) {
-    this->st_dev = file_stat->st_dev;
-    this->st_ino = file_stat->st_ino;
-    this->file_offset = file_offset;
+    this->st_dev_ = file_stat->st_dev;
+    this->st_ino_ = file_stat->st_ino;
+    this->file_offset_ = file_offset;
   }
 
-  this->rtld_flags = rtld_flags;
+  this->rtld_flags_ = rtld_flags;
 }
 
-static unsigned elfhash(const char* _name) {
-  const unsigned char* name = reinterpret_cast<const unsigned char*>(_name);
-  unsigned h = 0, g;
 
-  while (*name) {
-    h = (h << 4) + *name++;
-    g = h & 0xf0000000;
-    h ^= g;
-    h ^= g >> 24;
+uint32_t SymbolName::elf_hash() {
+  if (!has_elf_hash_) {
+    const unsigned char* name = reinterpret_cast<const unsigned char*>(name_);
+    uint32_t h = 0, g;
+
+    while (*name) {
+      h = (h << 4) + *name++;
+      g = h & 0xf0000000;
+      h ^= g;
+      h ^= g >> 24;
+    }
+
+    elf_hash_ = h;
+    has_elf_hash_ = true;
   }
-  return h;
+
+  return elf_hash_;
+}
+
+uint32_t SymbolName::gnu_hash() {
+  if (!has_gnu_hash_) {
+    uint32_t h = 5381;
+    const unsigned char* name = reinterpret_cast<const unsigned char*>(name_);
+    while (*name != 0) {
+      h += (h << 5) + *name++; // h*33 + c = h + h * 32 + c = h + h << 5 + c
+    }
+
+    gnu_hash_ =  h;
+    has_gnu_hash_ = true;
+  }
+
+  return gnu_hash_;
 }
 
 static ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
     const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group) {
-  unsigned elf_hash = elfhash(name);
+  SymbolName symbol_name(name);
   ElfW(Sym)* s = nullptr;
 
   /* "This element's presence in a shared object library alters the dynamic linker's
@@ -499,7 +555,7 @@
    */
   if (si_from->has_DT_SYMBOLIC) {
     DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->name, name);
-    s = soinfo_elf_lookup(si_from, elf_hash, name);
+    s = si_from->find_symbol_by_name(symbol_name);
     if (s != nullptr) {
       *si_found_in = si_from;
     }
@@ -509,7 +565,7 @@
   if (s == nullptr) {
     global_group.visit([&](soinfo* global_si) {
       DEBUG("%s: looking up %s in %s (from global group)", si_from->name, name, global_si->name);
-      s = soinfo_elf_lookup(global_si, elf_hash, name);
+      s = global_si->find_symbol_by_name(symbol_name);
       if (s != nullptr) {
         *si_found_in = global_si;
         return false;
@@ -528,7 +584,7 @@
       }
 
       DEBUG("%s: looking up %s in %s (from local group)", si_from->name, name, local_si->name);
-      s = soinfo_elf_lookup(local_si, elf_hash, name);
+      s = local_si->find_symbol_by_name(symbol_name);
       if (s != nullptr) {
         *si_found_in = local_si;
         return false;
@@ -665,11 +721,11 @@
 // specified soinfo object and its dependencies in breadth first order.
 ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) {
   ElfW(Sym)* result = nullptr;
-  uint32_t elf_hash = elfhash(name);
+  SymbolName symbol_name(name);
 
 
   walk_dependencies_tree(&si, 1, [&](soinfo* current_soinfo) {
-    result = soinfo_elf_lookup(current_soinfo, elf_hash, name);
+    result = current_soinfo->find_symbol_by_name(symbol_name);
     if (result != nullptr) {
       *found = current_soinfo;
       return false;
@@ -687,7 +743,7 @@
    specified soinfo (for RTLD_NEXT).
  */
 ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start) {
-  unsigned elf_hash = elfhash(name);
+  SymbolName symbol_name(name);
 
   if (start == nullptr) {
     start = solist;
@@ -699,7 +755,7 @@
       continue;
     }
 
-    s = soinfo_elf_lookup(si, elf_hash, name);
+    s = si->find_symbol_by_name(symbol_name);
     if (s != nullptr) {
       *found = si;
       break;
@@ -724,16 +780,45 @@
   return nullptr;
 }
 
-ElfW(Sym)* dladdr_find_symbol(soinfo* si, const void* addr) {
-  ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - si->base;
+ElfW(Sym)* soinfo::find_symbol_by_address(const void* addr) {
+  return is_gnu_hash() ? gnu_addr_lookup(addr) : elf_addr_lookup(addr);
+}
+
+static bool symbol_matches_soaddr(const ElfW(Sym)* sym, ElfW(Addr) soaddr) {
+  return sym->st_shndx != SHN_UNDEF &&
+      soaddr >= sym->st_value &&
+      soaddr < sym->st_value + sym->st_size;
+}
+
+ElfW(Sym)* soinfo::gnu_addr_lookup(const void* addr) {
+  ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - base;
+
+  for (size_t i = 0; i < nbucket_; ++i) {
+    uint32_t n = bucket_[i];
+
+    if (n == 0) {
+      continue;
+    }
+
+    do {
+      ElfW(Sym)* sym = symtab_ + n;
+      if (symbol_matches_soaddr(sym, soaddr)) {
+        return sym;
+      }
+    } while ((chain_[n++] & 1) == 0);
+  }
+
+  return nullptr;
+}
+
+ElfW(Sym)* soinfo::elf_addr_lookup(const void* addr) {
+  ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - base;
 
   // Search the library's symbol table for any defined symbol which
   // contains this address.
-  for (size_t i = 0; i < si->nchain; ++i) {
-    ElfW(Sym)* sym = &si->symtab[i];
-    if (sym->st_shndx != SHN_UNDEF &&
-        soaddr >= sym->st_value &&
-        soaddr < sym->st_value + sym->st_size) {
+  for (size_t i = 0; i < nchain_; ++i) {
+    ElfW(Sym)* sym = symtab_ + i;
+    if (symbol_matches_soaddr(sym, soaddr)) {
       return sym;
     }
   }
@@ -863,7 +948,7 @@
   si->phnum = elf_reader.phdr_count();
   si->phdr = elf_reader.loaded_phdr();
 
-  if (!si->PrelinkImage()) {
+  if (!si->prelink_image()) {
     soinfo_free(si);
     return nullptr;
   }
@@ -1019,7 +1104,7 @@
 
   bool linked = local_group.visit([&](soinfo* si) {
     if ((si->flags & FLAG_LINKED) == 0) {
-      if (!si->LinkImage(global_group, local_group, extinfo)) {
+      if (!si->link_image(global_group, local_group, extinfo)) {
         return false;
       }
       si->flags |= FLAG_LINKED;
@@ -1058,7 +1143,7 @@
 
   if (si->ref_count == 1) {
     TRACE("unloading '%s'", si->name);
-    si->CallDestructors();
+    si->call_destructors();
 
     if (si->has_min_version(0)) {
       soinfo* child = nullptr;
@@ -1132,7 +1217,7 @@
   protect_data(PROT_READ | PROT_WRITE);
   soinfo* si = find_library(name, flags, extinfo);
   if (si != nullptr) {
-    si->CallConstructors();
+    si->call_constructors();
   }
   protect_data(PROT_READ);
   return si;
@@ -1154,7 +1239,7 @@
 }
 
 #if defined(USE_RELA)
-int soinfo::Relocate(ElfW(Rela)* rela, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
+int soinfo::relocate(ElfW(Rela)* rela, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
   for (size_t idx = 0; idx < count; ++idx, ++rela) {
     unsigned type = ELFW(R_TYPE)(rela->r_info);
     unsigned sym = ELFW(R_SYM)(rela->r_info);
@@ -1171,11 +1256,11 @@
     soinfo* lsi = nullptr;
 
     if (sym != 0) {
-      sym_name = get_string(symtab[sym].st_name);
+      sym_name = get_string(symtab_[sym].st_name);
       s = soinfo_do_lookup(this, sym_name, &lsi, global_group,local_group);
       if (s == nullptr) {
         // We only allow an undefined symbol if this is a weak reference...
-        s = &symtab[sym];
+        s = &symtab_[sym];
         if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
           DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, name);
           return -1;
@@ -1431,7 +1516,7 @@
 }
 
 #else // REL, not RELA.
-int soinfo::Relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
+int soinfo::relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
   for (size_t idx = 0; idx < count; ++idx, ++rel) {
     unsigned type = ELFW(R_TYPE)(rel->r_info);
     // TODO: don't use unsigned for 'sym'. Use uint32_t or ElfW(Addr) instead.
@@ -1449,11 +1534,11 @@
     soinfo* lsi = nullptr;
 
     if (sym != 0) {
-      sym_name = get_string(symtab[sym].st_name);
+      sym_name = get_string(symtab_[sym].st_name);
       s = soinfo_do_lookup(this, sym_name, &lsi, global_group, local_group);
       if (s == nullptr) {
         // We only allow an undefined symbol if this is a weak reference...
-        s = &symtab[sym];
+        s = &symtab_[sym];
         if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
           DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, name);
           return -1;
@@ -1636,43 +1721,39 @@
 #endif
 
 #if defined(__mips__)
-static bool mips_relocate_got(soinfo* si, const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group) {
-  ElfW(Addr)** got = si->plt_got;
+bool soinfo::mips_relocate_got(const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
+  ElfW(Addr)** got = plt_got_;
   if (got == nullptr) {
     return true;
   }
-  unsigned local_gotno = si->mips_local_gotno;
-  unsigned gotsym = si->mips_gotsym;
-  unsigned symtabno = si->mips_symtabno;
-  ElfW(Sym)* symtab = si->symtab;
 
   // got[0] is the address of the lazy resolver function.
   // got[1] may be used for a GNU extension.
   // Set it to a recognizable address in case someone calls it (should be _rtld_bind_start).
   // FIXME: maybe this should be in a separate routine?
-  if ((si->flags & FLAG_LINKER) == 0) {
+  if ((flags & FLAG_LINKER) == 0) {
     size_t g = 0;
     got[g++] = reinterpret_cast<ElfW(Addr)*>(0xdeadbeef);
     if (reinterpret_cast<intptr_t>(got[g]) < 0) {
       got[g++] = reinterpret_cast<ElfW(Addr)*>(0xdeadfeed);
     }
     // Relocate the local GOT entries.
-    for (; g < local_gotno; g++) {
-      got[g] = reinterpret_cast<ElfW(Addr)*>(reinterpret_cast<uintptr_t>(got[g]) + si->load_bias);
+    for (; g < mips_local_gotno_; g++) {
+      got[g] = reinterpret_cast<ElfW(Addr)*>(reinterpret_cast<uintptr_t>(got[g]) + load_bias);
     }
   }
 
   // Now for the global GOT entries...
-  ElfW(Sym)* sym = symtab + gotsym;
-  got = si->plt_got + local_gotno;
-  for (size_t g = gotsym; g < symtabno; g++, sym++, got++) {
+  ElfW(Sym)* sym = symtab_ + mips_gotsym_;
+  got = plt_got_ + mips_local_gotno_;
+  for (size_t g = mips_gotsym_; g < mips_symtabno_; g++, sym++, got++) {
     // This is an undefined reference... try to locate it.
-    const char* sym_name = si->get_string(sym->st_name);
+    const char* sym_name = get_string(sym->st_name);
     soinfo* lsi = nullptr;
-    ElfW(Sym)* s = soinfo_do_lookup(si, sym_name, &lsi, global_group, local_group);
+    ElfW(Sym)* s = soinfo_do_lookup(this, sym_name, &lsi, global_group, local_group);
     if (s == nullptr) {
       // We only allow an undefined symbol if this is a weak reference.
-      s = &symtab[g];
+      s = &symtab_[g];
       if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
         DL_ERR("cannot locate \"%s\"...", sym_name);
         return false;
@@ -1689,7 +1770,7 @@
 }
 #endif
 
-void soinfo::CallArray(const char* array_name __unused, linker_function_t* functions, size_t count, bool reverse) {
+void soinfo::call_array(const char* array_name __unused, linker_function_t* functions, size_t count, bool reverse) {
   if (functions == nullptr) {
     return;
   }
@@ -1702,13 +1783,13 @@
 
   for (int i = begin; i != end; i += step) {
     TRACE("[ %s[%d] == %p ]", array_name, i, functions[i]);
-    CallFunction("function", functions[i]);
+    call_function("function", functions[i]);
   }
 
   TRACE("[ Done calling %s for '%s' ]", array_name, name);
 }
 
-void soinfo::CallFunction(const char* function_name __unused, linker_function_t function) {
+void soinfo::call_function(const char* function_name __unused, linker_function_t function) {
   if (function == nullptr || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) {
     return;
   }
@@ -1722,13 +1803,13 @@
   protect_data(PROT_READ | PROT_WRITE);
 }
 
-void soinfo::CallPreInitConstructors() {
+void soinfo::call_pre_init_constructors() {
   // DT_PREINIT_ARRAY functions are called before any other constructors for executables,
   // but ignored in a shared library.
-  CallArray("DT_PREINIT_ARRAY", preinit_array, preinit_array_count, false);
+  call_array("DT_PREINIT_ARRAY", preinit_array_, preinit_array_count_, false);
 }
 
-void soinfo::CallConstructors() {
+void soinfo::call_constructors() {
   if (constructors_called) {
     return;
   }
@@ -1745,34 +1826,34 @@
   //    out above, the libc constructor will be called again (recursively!).
   constructors_called = true;
 
-  if ((flags & FLAG_EXE) == 0 && preinit_array != nullptr) {
+  if ((flags & FLAG_EXE) == 0 && preinit_array_ != nullptr) {
     // The GNU dynamic linker silently ignores these, but we warn the developer.
     PRINT("\"%s\": ignoring %zd-entry DT_PREINIT_ARRAY in shared library!",
-          name, preinit_array_count);
+          name, preinit_array_count_);
   }
 
   get_children().for_each([] (soinfo* si) {
-    si->CallConstructors();
+    si->call_constructors();
   });
 
   TRACE("\"%s\": calling constructors", name);
 
   // DT_INIT should be called before DT_INIT_ARRAY if both are present.
-  CallFunction("DT_INIT", init_func);
-  CallArray("DT_INIT_ARRAY", init_array, init_array_count, false);
+  call_function("DT_INIT", init_func_);
+  call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false);
 }
 
-void soinfo::CallDestructors() {
+void soinfo::call_destructors() {
   if (!constructors_called) {
     return;
   }
   TRACE("\"%s\": calling destructors", name);
 
   // DT_FINI_ARRAY must be parsed in reverse order.
-  CallArray("DT_FINI_ARRAY", fini_array, fini_array_count, true);
+  call_array("DT_FINI_ARRAY", fini_array_, fini_array_count_, true);
 
   // DT_FINI should be called after DT_FINI_ARRAY if both are present.
-  CallFunction("DT_FINI", fini_func);
+  call_function("DT_FINI", fini_func_);
 
   // This is needed on second call to dlopen
   // after library has been unloaded with RTLD_NODELETE
@@ -1781,8 +1862,8 @@
 
 void soinfo::add_child(soinfo* child) {
   if (has_min_version(0)) {
-    child->parents.push_back(this);
-    this->children.push_back(child);
+    child->parents_.push_back(this);
+    this->children_.push_back(child);
   }
 }
 
@@ -1792,26 +1873,26 @@
   }
 
   // 1. Untie connected soinfos from 'this'.
-  children.for_each([&] (soinfo* child) {
-    child->parents.remove_if([&] (const soinfo* parent) {
+  children_.for_each([&] (soinfo* child) {
+    child->parents_.remove_if([&] (const soinfo* parent) {
       return parent == this;
     });
   });
 
-  parents.for_each([&] (soinfo* parent) {
-    parent->children.remove_if([&] (const soinfo* child) {
+  parents_.for_each([&] (soinfo* parent) {
+    parent->children_.remove_if([&] (const soinfo* child) {
       return child == this;
     });
   });
 
   // 2. Once everything untied - clear local lists.
-  parents.clear();
-  children.clear();
+  parents_.clear();
+  children_.clear();
 }
 
 dev_t soinfo::get_st_dev() const {
   if (has_min_version(0)) {
-    return st_dev;
+    return st_dev_;
   }
 
   return 0;
@@ -1819,7 +1900,7 @@
 
 ino_t soinfo::get_st_ino() const {
   if (has_min_version(0)) {
-    return st_ino;
+    return st_ino_;
   }
 
   return 0;
@@ -1827,7 +1908,7 @@
 
 off64_t soinfo::get_file_offset() const {
   if (has_min_version(1)) {
-    return file_offset;
+    return file_offset_;
   }
 
   return 0;
@@ -1835,7 +1916,7 @@
 
 uint32_t soinfo::get_rtld_flags() const {
   if (has_min_version(1)) {
-    return rtld_flags;
+    return rtld_flags_;
   }
 
   return 0;
@@ -1843,7 +1924,7 @@
 
 uint32_t soinfo::get_dt_flags_1() const {
   if (has_min_version(1)) {
-    return dt_flags_1;
+    return dt_flags_1_;
   }
 
   return 0;
@@ -1851,14 +1932,14 @@
 void soinfo::set_dt_flags_1(uint32_t dt_flags_1) {
   if (has_min_version(1)) {
     if ((dt_flags_1 & DF_1_GLOBAL) != 0) {
-      rtld_flags |= RTLD_GLOBAL;
+      rtld_flags_ |= RTLD_GLOBAL;
     }
 
     if ((dt_flags_1 & DF_1_NODELETE) != 0) {
-      rtld_flags |= RTLD_NODELETE;
+      rtld_flags_ |= RTLD_NODELETE;
     }
 
-    this->dt_flags_1 = dt_flags_1;
+    dt_flags_1_ = dt_flags_1;
   }
 }
 
@@ -1868,18 +1949,18 @@
 
 soinfo::soinfo_list_t& soinfo::get_children() {
   if (has_min_version(0)) {
-    return this->children;
+    return children_;
   }
 
   return g_empty_list;
 }
 
 soinfo::soinfo_list_t& soinfo::get_parents() {
-  if ((this->flags & FLAG_NEW_SOINFO) == 0) {
-    return g_empty_list;
+  if (has_min_version(0)) {
+    return parents_;
   }
 
-  return this->parents;
+  return g_empty_list;
 }
 
 ElfW(Addr) soinfo::resolve_symbol_address(ElfW(Sym)* s) {
@@ -1891,11 +1972,15 @@
 }
 
 const char* soinfo::get_string(ElfW(Word) index) const {
-  if (has_min_version(1) && (index >= strtab_size)) {
-    __libc_fatal("%s: strtab out of bounds error; STRSZ=%zd, name=%d", name, strtab_size, index);
+  if (has_min_version(1) && (index >= strtab_size_)) {
+    __libc_fatal("%s: strtab out of bounds error; STRSZ=%zd, name=%d", name, strtab_size_, index);
   }
 
-  return strtab + index;
+  return strtab_ + index;
+}
+
+bool soinfo::is_gnu_hash() const {
+  return (flags & FLAG_GNU_HASH) != 0;
 }
 
 bool soinfo::can_unload() const {
@@ -1963,7 +2048,7 @@
   return return_value;
 }
 
-bool soinfo::PrelinkImage() {
+bool soinfo::prelink_image() {
   /* Extract dynamic section */
   ElfW(Word) dynamic_flags = 0;
   phdr_table_get_dynamic_section(phdr, phnum, load_bias, &dynamic, &dynamic_flags);
@@ -2003,27 +2088,57 @@
         break;
 
       case DT_HASH:
-        nbucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0];
-        nchain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1];
-        bucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8);
-        chain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8 + nbucket * 4);
+        if (nbucket_ != 0) {
+          // in case of --hash-style=both, we prefer gnu
+          break;
+        }
+
+        nbucket_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0];
+        nchain_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1];
+        bucket_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8);
+        chain_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8 + nbucket_ * 4);
+        break;
+
+      case DT_GNU_HASH:
+        if (nbucket_ != 0) {
+          // in case of --hash-style=both, we prefer gnu
+          nchain_ = 0;
+        }
+
+        nbucket_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0];
+        // skip symndx
+        gnu_maskwords_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[2];
+        gnu_shift2_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[3];
+
+        gnu_bloom_filter_ = reinterpret_cast<ElfW(Addr)*>(load_bias + d->d_un.d_ptr + 16);
+        bucket_ = reinterpret_cast<uint32_t*>(gnu_bloom_filter_ + gnu_maskwords_);
+        // amend chain for symndx = header[1]
+        chain_ = bucket_ + nbucket_ - reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1];
+
+        if (!powerof2(gnu_maskwords_)) {
+          DL_ERR("invalid maskwords for gnu_hash = 0x%x, in \"%s\" expecting power to two", gnu_maskwords_, name);
+          return false;
+        }
+        --gnu_maskwords_;
+
+        flags |= FLAG_GNU_HASH;
         break;
 
       case DT_STRTAB:
-        strtab = reinterpret_cast<const char*>(load_bias + d->d_un.d_ptr);
+        strtab_ = reinterpret_cast<const char*>(load_bias + d->d_un.d_ptr);
         break;
 
       case DT_STRSZ:
-        strtab_size = d->d_un.d_val;
+        strtab_size_ = d->d_un.d_val;
         break;
 
       case DT_SYMTAB:
-        symtab = reinterpret_cast<ElfW(Sym)*>(load_bias + d->d_un.d_ptr);
+        symtab_ = reinterpret_cast<ElfW(Sym)*>(load_bias + d->d_un.d_ptr);
         break;
 
       case DT_SYMENT:
         if (d->d_un.d_val != sizeof(ElfW(Sym))) {
-          DL_ERR("invalid DT_SYMENT: %zd", static_cast<size_t>(d->d_un.d_val));
+          DL_ERR("invalid DT_SYMENT: %zd in \"%s\"", static_cast<size_t>(d->d_un.d_val), name);
           return false;
         }
         break;
@@ -2044,24 +2159,24 @@
 
       case DT_JMPREL:
 #if defined(USE_RELA)
-        plt_rela = reinterpret_cast<ElfW(Rela)*>(load_bias + d->d_un.d_ptr);
+        plt_rela_ = reinterpret_cast<ElfW(Rela)*>(load_bias + d->d_un.d_ptr);
 #else
-        plt_rel = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr);
+        plt_rel_ = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr);
 #endif
         break;
 
       case DT_PLTRELSZ:
 #if defined(USE_RELA)
-        plt_rela_count = d->d_un.d_val / sizeof(ElfW(Rela));
+        plt_rela_count_ = d->d_un.d_val / sizeof(ElfW(Rela));
 #else
-        plt_rel_count = d->d_un.d_val / sizeof(ElfW(Rel));
+        plt_rel_count_ = d->d_un.d_val / sizeof(ElfW(Rel));
 #endif
         break;
 
       case DT_PLTGOT:
 #if defined(__mips__)
         // Used by mips and mips64.
-        plt_got = reinterpret_cast<ElfW(Addr)**>(load_bias + d->d_un.d_ptr);
+        plt_got_ = reinterpret_cast<ElfW(Addr)**>(load_bias + d->d_un.d_ptr);
 #endif
         // Ignore for other platforms... (because RTLD_LAZY is not supported)
         break;
@@ -2081,11 +2196,11 @@
 #endif
 #if defined(USE_RELA)
       case DT_RELA:
-        rela = reinterpret_cast<ElfW(Rela)*>(load_bias + d->d_un.d_ptr);
+        rela_ = reinterpret_cast<ElfW(Rela)*>(load_bias + d->d_un.d_ptr);
         break;
 
       case DT_RELASZ:
-        rela_count = d->d_un.d_val / sizeof(ElfW(Rela));
+        rela_count_ = d->d_un.d_val / sizeof(ElfW(Rela));
         break;
 
       case DT_RELAENT:
@@ -2108,11 +2223,11 @@
         return false;
 #else
       case DT_REL:
-        rel = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr);
+        rel_ = reinterpret_cast<ElfW(Rel)*>(load_bias + d->d_un.d_ptr);
         break;
 
       case DT_RELSZ:
-        rel_count = d->d_un.d_val / sizeof(ElfW(Rel));
+        rel_count_ = d->d_un.d_val / sizeof(ElfW(Rel));
         break;
 
       case DT_RELENT:
@@ -2134,40 +2249,40 @@
         return false;
 #endif
       case DT_INIT:
-        init_func = reinterpret_cast<linker_function_t>(load_bias + d->d_un.d_ptr);
-        DEBUG("%s constructors (DT_INIT) found at %p", name, init_func);
+        init_func_ = reinterpret_cast<linker_function_t>(load_bias + d->d_un.d_ptr);
+        DEBUG("%s constructors (DT_INIT) found at %p", name, init_func_);
         break;
 
       case DT_FINI:
-        fini_func = reinterpret_cast<linker_function_t>(load_bias + d->d_un.d_ptr);
-        DEBUG("%s destructors (DT_FINI) found at %p", name, fini_func);
+        fini_func_ = reinterpret_cast<linker_function_t>(load_bias + d->d_un.d_ptr);
+        DEBUG("%s destructors (DT_FINI) found at %p", name, fini_func_);
         break;
 
       case DT_INIT_ARRAY:
-        init_array = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
-        DEBUG("%s constructors (DT_INIT_ARRAY) found at %p", name, init_array);
+        init_array_ = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
+        DEBUG("%s constructors (DT_INIT_ARRAY) found at %p", name, init_array_);
         break;
 
       case DT_INIT_ARRAYSZ:
-        init_array_count = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
+        init_array_count_ = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
         break;
 
       case DT_FINI_ARRAY:
-        fini_array = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
-        DEBUG("%s destructors (DT_FINI_ARRAY) found at %p", name, fini_array);
+        fini_array_ = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
+        DEBUG("%s destructors (DT_FINI_ARRAY) found at %p", name, fini_array_);
         break;
 
       case DT_FINI_ARRAYSZ:
-        fini_array_count = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
+        fini_array_count_ = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
         break;
 
       case DT_PREINIT_ARRAY:
-        preinit_array = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
-        DEBUG("%s constructors (DT_PREINIT_ARRAY) found at %p", name, preinit_array);
+        preinit_array_ = reinterpret_cast<linker_function_t*>(load_bias + d->d_un.d_ptr);
+        DEBUG("%s constructors (DT_PREINIT_ARRAY) found at %p", name, preinit_array_);
         break;
 
       case DT_PREINIT_ARRAYSZ:
-        preinit_array_count = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
+        preinit_array_count_ = ((unsigned)d->d_un.d_val) / sizeof(ElfW(Addr));
         break;
 
       case DT_TEXTREL:
@@ -2224,15 +2339,15 @@
         break;
 
       case DT_MIPS_SYMTABNO:
-        mips_symtabno = d->d_un.d_val;
+        mips_symtabno_ = d->d_un.d_val;
         break;
 
       case DT_MIPS_LOCAL_GOTNO:
-        mips_local_gotno = d->d_un.d_val;
+        mips_local_gotno_ = d->d_un.d_val;
         break;
 
       case DT_MIPS_GOTSYM:
-        mips_gotsym = d->d_un.d_val;
+        mips_gotsym_ = d->d_un.d_val;
         break;
 #endif
       // Ignored: "Its use has been superseded by the DF_BIND_NOW flag"
@@ -2255,29 +2370,29 @@
   }
 
   DEBUG("si->base = %p, si->strtab = %p, si->symtab = %p",
-        reinterpret_cast<void*>(base), strtab, symtab);
+        reinterpret_cast<void*>(base), strtab_, symtab_);
 
   // Sanity checks.
   if (relocating_linker && needed_count != 0) {
     DL_ERR("linker cannot have DT_NEEDED dependencies on other libraries");
     return false;
   }
-  if (nbucket == 0) {
-    DL_ERR("empty/missing DT_HASH in \"%s\" (built with --hash-style=gnu?)", name);
+  if (nbucket_ == 0) {
+    DL_ERR("empty/missing DT_HASH/DT_GNU_HASH in \"%s\" (new hash type from the future?)", name);
     return false;
   }
-  if (strtab == 0) {
+  if (strtab_ == 0) {
     DL_ERR("empty/missing DT_STRTAB in \"%s\"", name);
     return false;
   }
-  if (symtab == 0) {
+  if (symtab_ == 0) {
     DL_ERR("empty/missing DT_SYMTAB in \"%s\"", name);
     return false;
   }
   return true;
 }
 
-bool soinfo::LinkImage(const soinfo_list_t& global_group, const soinfo_list_t& local_group, const android_dlextinfo* extinfo) {
+bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group, const android_dlextinfo* extinfo) {
 
 #if !defined(__LP64__)
   if (has_text_relocations) {
@@ -2294,35 +2409,35 @@
 #endif
 
 #if defined(USE_RELA)
-  if (rela != nullptr) {
+  if (rela_ != nullptr) {
     DEBUG("[ relocating %s ]", name);
-    if (Relocate(rela, rela_count, global_group, local_group)) {
+    if (relocate(rela_, rela_count_, global_group, local_group)) {
       return false;
     }
   }
-  if (plt_rela != nullptr) {
+  if (plt_rela_ != nullptr) {
     DEBUG("[ relocating %s plt ]", name);
-    if (Relocate(plt_rela, plt_rela_count, global_group, local_group)) {
+    if (relocate(plt_rela_, plt_rela_count_, global_group, local_group)) {
       return false;
     }
   }
 #else
-  if (rel != nullptr) {
+  if (rel_ != nullptr) {
     DEBUG("[ relocating %s ]", name);
-    if (Relocate(rel, rel_count, global_group, local_group)) {
+    if (relocate(rel_, rel_count_, global_group, local_group)) {
       return false;
     }
   }
-  if (plt_rel != nullptr) {
+  if (plt_rel_ != nullptr) {
     DEBUG("[ relocating %s plt ]", name);
-    if (Relocate(plt_rel, plt_rel_count, global_group, local_group)) {
+    if (relocate(plt_rel_, plt_rel_count_, global_group, local_group)) {
       return false;
     }
   }
 #endif
 
 #if defined(__mips__)
-  if (!mips_relocate_got(this, global_group, local_group)) {
+  if (!mips_relocate_got(global_group, local_group)) {
     return false;
   }
 #endif
@@ -2388,8 +2503,8 @@
   si->size = phdr_table_get_load_size(si->phdr, si->phnum);
   si->load_bias = get_elf_exec_load_bias(ehdr_vdso);
 
-  si->PrelinkImage();
-  si->LinkImage(g_empty_list, soinfo::soinfo_list_t::make_list(si), nullptr);
+  si->prelink_image();
+  si->link_image(g_empty_list, soinfo::soinfo_list_t::make_list(si), nullptr);
 #endif
 }
 
@@ -2518,7 +2633,7 @@
 
   somain = si;
 
-  si->PrelinkImage();
+  si->prelink_image();
 
   // add somain to global group
   si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
@@ -2549,15 +2664,15 @@
 
   add_vdso(args);
 
-  si->CallPreInitConstructors();
+  si->call_pre_init_constructors();
 
-  /* After the PrelinkImage, the si->load_bias is initialized.
+  /* After the prelink_image, the si->load_bias is initialized.
    * For so lib, the map->l_addr will be updated in notify_gdb_of_load.
    * We need to update this value for so exe here. So Unwind_Backtrace
    * for some arch like x86 could work correctly within so exe.
    */
   map->l_addr = si->load_bias;
-  si->CallConstructors();
+  si->call_constructors();
 
 #if TIMING
   gettimeofday(&t1, nullptr);
@@ -2672,7 +2787,7 @@
   // itself without having to look into local_group and (2) allocators
   // are not yet initialized, and therefore we cannot use linked_list.push_*
   // functions at this point.
-  if (!(linker_so.PrelinkImage() && linker_so.LinkImage(g_empty_list, g_empty_list, nullptr))) {
+  if (!(linker_so.prelink_image() && linker_so.link_image(g_empty_list, g_empty_list, nullptr))) {
     // It would be nice to print an error message, but if the linker
     // can't link itself, there's no guarantee that we'll be able to
     // call write() (because it involves a GOT reference). We may as
@@ -2687,7 +2802,7 @@
   __libc_init_tls(args);
 
   // Initialize the linker's own global variables
-  linker_so.CallConstructors();
+  linker_so.call_constructors();
 
   // Initialize static variables. Note that in order to
   // get correct libdl_info we need to call constructors
diff --git a/linker/linker.h b/linker/linker.h
index 0a98b40..d28f70e 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -87,6 +87,7 @@
 #define FLAG_LINKED     0x00000001
 #define FLAG_EXE        0x00000004 // The main executable
 #define FLAG_LINKER     0x00000010 // The linker itself
+#define FLAG_GNU_HASH   0x00000040 // uses gnu hash
 #define FLAG_NEW_SOINFO 0x40000000 // new soinfo format
 
 #define SUPPORTED_DT_FLAGS_1 (DF_1_NOW | DF_1_GLOBAL | DF_1_NODELETE)
@@ -105,14 +106,38 @@
 struct soinfo;
 
 class SoinfoListAllocator {
-public:
+ public:
   static LinkedListEntry<soinfo>* alloc();
   static void free(LinkedListEntry<soinfo>* entry);
-private:
+
+ private:
   // unconstructable
   DISALLOW_IMPLICIT_CONSTRUCTORS(SoinfoListAllocator);
 };
 
+class SymbolName {
+ public:
+  explicit SymbolName(const char* name)
+      : name_(name), has_elf_hash_(false), has_gnu_hash_(false),
+        elf_hash_(0), gnu_hash_(0) { }
+
+  const char* get_name() {
+    return name_;
+  }
+
+  uint32_t elf_hash();
+  uint32_t gnu_hash();
+
+ private:
+  const char* name_;
+  bool has_elf_hash_;
+  bool has_gnu_hash_;
+  uint32_t elf_hash_;
+  uint32_t gnu_hash_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(SymbolName);
+};
+
 struct soinfo {
  public:
   typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t;
@@ -139,56 +164,60 @@
   uint32_t flags;
 
  private:
-  const char* strtab;
- public:
-  ElfW(Sym)* symtab;
+  const char* strtab_;
+  ElfW(Sym)* symtab_;
 
-  size_t nbucket;
-  size_t nchain;
-  uint32_t* bucket;
-  uint32_t* chain;
+  size_t nbucket_;
+  size_t nchain_;
+  uint32_t* bucket_;
+  uint32_t* chain_;
 
 #if defined(__mips__) || !defined(__LP64__)
   // This is only used by mips and mips64, but needs to be here for
   // all 32-bit architectures to preserve binary compatibility.
-  ElfW(Addr)** plt_got;
+  ElfW(Addr)** plt_got_;
 #endif
 
 #if defined(USE_RELA)
-  ElfW(Rela)* plt_rela;
-  size_t plt_rela_count;
+  ElfW(Rela)* plt_rela_;
+  size_t plt_rela_count_;
 
-  ElfW(Rela)* rela;
-  size_t rela_count;
+  ElfW(Rela)* rela_;
+  size_t rela_count_;
 #else
-  ElfW(Rel)* plt_rel;
-  size_t plt_rel_count;
+  ElfW(Rel)* plt_rel_;
+  size_t plt_rel_count_;
 
-  ElfW(Rel)* rel;
-  size_t rel_count;
+  ElfW(Rel)* rel_;
+  size_t rel_count_;
 #endif
 
-  linker_function_t* preinit_array;
-  size_t preinit_array_count;
+  linker_function_t* preinit_array_;
+  size_t preinit_array_count_;
 
-  linker_function_t* init_array;
-  size_t init_array_count;
-  linker_function_t* fini_array;
-  size_t fini_array_count;
+  linker_function_t* init_array_;
+  size_t init_array_count_;
+  linker_function_t* fini_array_;
+  size_t fini_array_count_;
 
-  linker_function_t init_func;
-  linker_function_t fini_func;
+  linker_function_t init_func_;
+  linker_function_t fini_func_;
 
+ public:
 #if defined(__arm__)
   // ARM EABI section used for stack unwinding.
   uint32_t* ARM_exidx;
   size_t ARM_exidx_count;
 #elif defined(__mips__)
-  uint32_t mips_symtabno;
-  uint32_t mips_local_gotno;
-  uint32_t mips_gotsym;
+ private:
+  uint32_t mips_symtabno_;
+  uint32_t mips_local_gotno_;
+  uint32_t mips_gotsym_;
+  bool mips_relocate_got(const soinfo_list_t& global_group, const soinfo_list_t& local_group);
+
 #endif
 
+ public:
   size_t ref_count;
   link_map link_map_head;
 
@@ -203,13 +232,14 @@
 #endif
   bool has_DT_SYMBOLIC;
 
+ public:
   soinfo(const char* name, const struct stat* file_stat, off64_t file_offset, int rtld_flags);
 
-  void CallConstructors();
-  void CallDestructors();
-  void CallPreInitConstructors();
-  bool PrelinkImage();
-  bool LinkImage(const soinfo_list_t& global_group, const soinfo_list_t& local_group, const android_dlextinfo* extinfo);
+  void call_constructors();
+  void call_destructors();
+  void call_pre_init_constructors();
+  bool prelink_image();
+  bool link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group, const android_dlextinfo* extinfo);
 
   void add_child(soinfo* child);
   void remove_all_links();
@@ -225,42 +255,56 @@
   soinfo_list_t& get_children();
   soinfo_list_t& get_parents();
 
+  ElfW(Sym)* find_symbol_by_name(SymbolName& symbol_name);
+  ElfW(Sym)* find_symbol_by_address(const void* addr);
   ElfW(Addr) resolve_symbol_address(ElfW(Sym)* s);
 
   const char* get_string(ElfW(Word) index) const;
   bool can_unload() const;
+  bool is_gnu_hash() const;
 
   bool inline has_min_version(uint32_t min_version) const {
-    return (flags & FLAG_NEW_SOINFO) != 0 && version >= min_version;
+    return (flags & FLAG_NEW_SOINFO) != 0 && version_ >= min_version;
   }
 
  private:
-  void CallArray(const char* array_name, linker_function_t* functions, size_t count, bool reverse);
-  void CallFunction(const char* function_name, linker_function_t function);
+  ElfW(Sym)* elf_lookup(SymbolName& symbol_name);
+  ElfW(Sym)* elf_addr_lookup(const void* addr);
+  ElfW(Sym)* gnu_lookup(SymbolName& symbol_name);
+  ElfW(Sym)* gnu_addr_lookup(const void* addr);
+
+  void call_array(const char* array_name, linker_function_t* functions, size_t count, bool reverse);
+  void call_function(const char* function_name, linker_function_t function);
 #if defined(USE_RELA)
-  int Relocate(ElfW(Rela)* rela, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group);
+  int relocate(ElfW(Rela)* rela, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group);
 #else
-  int Relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group);
+  int relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group);
 #endif
 
  private:
   // This part of the structure is only available
   // when FLAG_NEW_SOINFO is set in this->flags.
-  uint32_t version;
+  uint32_t version_;
 
   // version >= 0
-  dev_t st_dev;
-  ino_t st_ino;
+  dev_t st_dev_;
+  ino_t st_ino_;
 
   // dependency graph
-  soinfo_list_t children;
-  soinfo_list_t parents;
+  soinfo_list_t children_;
+  soinfo_list_t parents_;
 
   // version >= 1
-  off64_t file_offset;
-  uint32_t rtld_flags;
-  uint32_t dt_flags_1;
-  size_t strtab_size;
+  off64_t file_offset_;
+  uint32_t rtld_flags_;
+  uint32_t dt_flags_1_;
+  size_t strtab_size_;
+
+  // version >= 2
+  uint32_t gnu_maskwords_;
+  uint32_t gnu_shift2_;
+
+  ElfW(Addr)* gnu_bloom_filter_;
 
   friend soinfo* get_libdl_info();
 };
@@ -275,7 +319,6 @@
 ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start);
 soinfo* find_containing_library(const void* addr);
 
-ElfW(Sym)* dladdr_find_symbol(soinfo* si, const void* addr);
 ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name);
 
 void debuggerd_init();
diff --git a/tests/Android.mk b/tests/Android.mk
index 5e0c593..14a5e83 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -301,9 +301,7 @@
     libdl_preempt_test_1 \
     libdl_preempt_test_2
 
-ifneq ($(filter $(TARGET_ARCH),arm arm64),$(TARGET_ARCH))
 bionic-unit-tests-glibc_shared_libraries += libdl_test_df_1_global
-endif
 
 bionic-unit-tests-glibc_whole_static_libraries := \
     libBionicStandardTests \
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 3af9e12..88f0b19 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -648,19 +648,52 @@
   ASSERT_TRUE(dlerror() == NULL); // dladdr(3) doesn't set dlerror(3).
 }
 
-// Our dynamic linker doesn't support GNU hash tables.
-#if defined(__BIONIC__)
 // GNU-style ELF hash tables are incompatible with the MIPS ABI.
 // MIPS requires .dynsym to be sorted to match the GOT but GNU-style requires sorting by hash code.
-#if !defined(__mips__)
 TEST(dlfcn, dlopen_library_with_only_gnu_hash) {
+#if !defined(__mips__)
   dlerror(); // Clear any pending errors.
-  void* handle = dlopen("no-elf-hash-table-library.so", RTLD_NOW);
-  ASSERT_TRUE(handle == NULL);
-  ASSERT_STREQ("dlopen failed: empty/missing DT_HASH in \"no-elf-hash-table-library.so\" (built with --hash-style=gnu?)", dlerror());
+  void* handle = dlopen("libgnu-hash-table-library.so", RTLD_NOW);
+  ASSERT_TRUE(handle != nullptr) << dlerror();
+  auto guard = make_scope_guard([&]() {
+    dlclose(handle);
+  });
+  void* sym = dlsym(handle, "getRandomNumber");
+  ASSERT_TRUE(sym != nullptr) << dlerror();
+  int (*fn)(void);
+  fn = reinterpret_cast<int (*)(void)>(sym);
+  EXPECT_EQ(4, fn());
+
+  Dl_info dlinfo;
+  ASSERT_TRUE(0 != dladdr(reinterpret_cast<void*>(fn), &dlinfo));
+
+  ASSERT_TRUE(fn == dlinfo.dli_saddr);
+  ASSERT_STREQ("getRandomNumber", dlinfo.dli_sname);
+  ASSERT_SUBSTR("libgnu-hash-table-library.so", dlinfo.dli_fname);
+#else
+  GTEST_LOG_(INFO) << "This test does nothing for mips/mips64; mips toolchain does not support '--hash-style=gnu'\n";
+#endif
 }
-#endif
-#endif
+
+TEST(dlfcn, dlopen_library_with_only_sysv_hash) {
+  void* handle = dlopen("libsysv-hash-table-library.so", RTLD_NOW);
+  ASSERT_TRUE(handle != nullptr) << dlerror();
+  auto guard = make_scope_guard([&]() {
+    dlclose(handle);
+  });
+  void* sym = dlsym(handle, "getRandomNumber");
+  ASSERT_TRUE(sym != nullptr) << dlerror();
+  int (*fn)(void);
+  fn = reinterpret_cast<int (*)(void)>(sym);
+  EXPECT_EQ(4, fn());
+
+  Dl_info dlinfo;
+  ASSERT_TRUE(0 != dladdr(reinterpret_cast<void*>(fn), &dlinfo));
+
+  ASSERT_TRUE(fn == dlinfo.dli_saddr);
+  ASSERT_STREQ("getRandomNumber", dlinfo.dli_sname);
+  ASSERT_SUBSTR("libsysv-hash-table-library.so", dlinfo.dli_fname);
+}
 
 TEST(dlfcn, dlopen_bad_flags) {
   dlerror(); // Clear any pending errors.
diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk
index 2fb0b12..fafb9e0 100644
--- a/tests/libs/Android.mk
+++ b/tests/libs/Android.mk
@@ -28,21 +28,34 @@
     $(TEST_PATH)/Android.build.mk
 
 # -----------------------------------------------------------------------------
-# Library used by dlfcn tests.
+# Library to test gnu-styled hash
 # -----------------------------------------------------------------------------
 ifneq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),mips mips64))
-no-elf-hash-table-library_src_files := \
-    empty.cpp \
+libgnu-hash-table-library_src_files := \
+    dlext_test_library.cpp \
 
-no-elf-hash-table-library_ldflags := \
+libgnu-hash-table-library_ldflags := \
     -Wl,--hash-style=gnu \
 
-module := no-elf-hash-table-library
+module := libgnu-hash-table-library
 module_tag := optional
 include $(LOCAL_PATH)/Android.build.testlib.mk
 endif
 
 # -----------------------------------------------------------------------------
+# Library to test sysv-styled hash
+# -----------------------------------------------------------------------------
+libsysv-hash-table-library_src_files := \
+    dlext_test_library.cpp \
+
+libsysv-hash-table-library_ldflags := \
+    -Wl,--hash-style=sysv \
+
+module := libsysv-hash-table-library
+module_tag := optional
+include $(LOCAL_PATH)/Android.build.testlib.mk
+
+# -----------------------------------------------------------------------------
 # Library used by dlext tests - with GNU RELRO program header
 # -----------------------------------------------------------------------------
 libdlext_test_src_files := \
@@ -311,12 +324,17 @@
 # -----------------------------------------------------------------------------
 # Library with DF_1_GLOBAL
 # -----------------------------------------------------------------------------
-# TODO: re-enable arm once b/18137520 or b/18130452 are fixed
-ifeq ($(filter $(TARGET_ARCH),arm arm64),)
 libdl_test_df_1_global_src_files := dl_df_1_global.cpp
 libdl_test_df_1_global_ldflags := -fuse-ld=bfd -Wl,-z,global
 module := libdl_test_df_1_global
+# TODO: re-enable arm once b/18137520 or b/18130452 are fixed
+ifeq ($(filter $(TARGET_ARCH),arm arm64),)
 include $(LOCAL_PATH)/Android.build.testlib.mk
+else
+# build it for host only
+build_target := SHARED_LIBRARY
+build_type := host
+include $(TEST_PATH)/Android.build.mk
 endif
 
 # -----------------------------------------------------------------------------
diff --git a/tests/mntent_test.cpp b/tests/mntent_test.cpp
index 637cb52..a102849 100644
--- a/tests/mntent_test.cpp
+++ b/tests/mntent_test.cpp
@@ -19,16 +19,22 @@
 #include <mntent.h>
 
 TEST(mntent, mntent_smoke) {
-  FILE* fp = setmntent("/no/mnt/tab/on/android", "r");
-  ASSERT_TRUE(fp == NULL);
+  FILE* fp = setmntent("/proc/mounts", "r");
+  ASSERT_TRUE(fp != NULL);
 
-#if __BIONIC__ // glibc doesn't let you call getmntent/getmntent_r with a NULL FILE*.
-  ASSERT_TRUE(getmntent(fp) == NULL);
+  ASSERT_TRUE(getmntent(fp) != NULL);
 
-  struct mntent mbuf;
-  char cbuf[32];
-  ASSERT_TRUE(getmntent_r(fp, &mbuf, cbuf, sizeof(cbuf)) == NULL);
-#endif
+  bool saw_proc = false;
+
+  struct mntent entry;
+  char buf[BUFSIZ];
+  while (getmntent_r(fp, &entry, buf, sizeof(buf)) != NULL) {
+    if (strcmp(entry.mnt_fsname, "proc") == 0 && strcmp(entry.mnt_dir, "/proc") == 0) {
+      saw_proc = true;
+    }
+  }
+
+  ASSERT_TRUE(saw_proc);
 
   ASSERT_EQ(1, endmntent(fp));
 }
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index 379e495..b5e12a5 100644
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -359,50 +359,26 @@
   ASSERT_EQ(0, pthread_sigmask(SIG_SETMASK, &original_set, NULL));
 }
 
-#if defined(__BIONIC__)
-#define HAVE_PTHREAD_SETNAME_NP
-#elif defined(__GLIBC__)
-#if __GLIBC_PREREQ(2, 12)
-#define HAVE_PTHREAD_SETNAME_NP
-#endif
-#endif
-
 TEST(pthread, pthread_setname_np__too_long) {
-#if defined(HAVE_PTHREAD_SETNAME_NP)
   ASSERT_EQ(ERANGE, pthread_setname_np(pthread_self(), "this name is far too long for linux"));
-#else
-  GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
 }
 
 TEST(pthread, pthread_setname_np__self) {
-#if defined(HAVE_PTHREAD_SETNAME_NP)
   ASSERT_EQ(0, pthread_setname_np(pthread_self(), "short 1"));
-#else
-  GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
 }
 
 TEST(pthread, pthread_setname_np__other) {
-#if defined(HAVE_PTHREAD_SETNAME_NP)
   pthread_t t1;
   ASSERT_EQ(0, pthread_create(&t1, NULL, SleepFn, reinterpret_cast<void*>(5)));
   ASSERT_EQ(0, pthread_setname_np(t1, "short 2"));
-#else
-  GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
 }
 
 TEST(pthread, pthread_setname_np__no_such_thread) {
-#if defined(HAVE_PTHREAD_SETNAME_NP)
   pthread_t dead_thread;
   MakeDeadThread(dead_thread);
 
   // Call pthread_setname_np after thread has already exited.
-  ASSERT_EQ(ESRCH, pthread_setname_np(dead_thread, "short 3"));
-#else
-  GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
+  ASSERT_EQ(ENOENT, pthread_setname_np(dead_thread, "short 3"));
 }
 
 TEST(pthread, pthread_kill__0) {
diff --git a/tests/string_posix_strerror_r_test.cpp b/tests/string_posix_strerror_r_test.cpp
index 09cebfe..ae3b41a 100644
--- a/tests/string_posix_strerror_r_test.cpp
+++ b/tests/string_posix_strerror_r_test.cpp
@@ -15,13 +15,23 @@
  */
 
 #undef _GNU_SOURCE
+#include <features.h> // Get __BIONIC__ or __GLIBC__ so we can tell what we're using.
 
-// Old versions of glibc (like our current host prebuilt sysroot one) have
-// headers that don't work if you #undef _GNU_SOURCE, which makes it
-// impossible to build this test.
-#include <features.h>
+#if defined(__GLIBC__)
 
-#if !defined(__GLIBC__)
+// At the time of writing, libcxx -- which is dragged in by gtest -- assumes
+// declarations from glibc of things that aren't available without __USE_GNU.
+// This means we can't even build this test (which is a problem because that
+// means it doesn't get included in CTS).
+// For glibc 2.15, the symbols in question are:
+//   at_quick_exit, quick_exit, vasprintf, strtoll_l, strtoull_l, and strtold_l.
+
+# if __GLIBC_PREREQ(2, 19)
+#  error check whether we can build this now...
+# endif
+
+#else
+
 #include <string.h>
 
 #include <errno.h>
@@ -50,8 +60,5 @@
   // The POSIX strerror_r sets errno to ERANGE (the GNU one doesn't).
   ASSERT_EQ(ERANGE, errno);
 }
-#else
-# if __GLIBC_PREREQ(2, 15)
-#  error this test should work now
-# endif
+
 #endif
diff --git a/tests/sys_resource_test.cpp b/tests/sys_resource_test.cpp
index d6d99a0..91d07ab 100644
--- a/tests/sys_resource_test.cpp
+++ b/tests/sys_resource_test.cpp
@@ -18,17 +18,6 @@
 
 #include <sys/resource.h>
 
-#if defined(__GLIBC__)
-/* The host glibc we're currently building with doesn't have prlimit64 yet. */
-static int prlimit64(pid_t, int resource, const struct rlimit64* new_limit, struct rlimit64* old_limit) {
-  if (new_limit != NULL) {
-    return setrlimit64(resource, new_limit);
-  } else {
-    return getrlimit64(resource, old_limit);
-  }
-}
-#endif
-
 TEST(sys_resource, smoke) {
 #if defined(__LP64__) || defined(__GLIBC__)
   ASSERT_EQ(sizeof(rlimit), sizeof(rlimit64));
diff --git a/tests/sys_socket_test.cpp b/tests/sys_socket_test.cpp
index 38ef68a..eb8c33e 100644
--- a/tests/sys_socket_test.cpp
+++ b/tests/sys_socket_test.cpp
@@ -22,18 +22,6 @@
 #include <sys/un.h>
 #include <fcntl.h>
 
-#if defined(__BIONIC__)
-  #define RECVMMSG_SUPPORTED 1
-  #define SENDMMSG_SUPPORTED 1
-#elif defined(__GLIBC_PREREQ)
-  #if __GLIBC_PREREQ(2, 12)
-    #define RECVMMSG_SUPPORTED 1
-  #endif
-  #if __GLIBC_PREREQ(2, 14)
-    #define SENDMMSG_SUPPORTED 1
-  #endif
-#endif
-
 #define SOCK_PATH "test"
 
 static void* ConnectFn(void* data) {
@@ -120,7 +108,6 @@
   RunTest(TestAccept4, NULL);
 }
 
-#if defined(RECVMMSG_SUPPORTED)
 const char* g_RecvMsgs[] = {
   "RECVMMSG_ONE",
   "RECVMMSG_TWO",
@@ -171,26 +158,16 @@
 
   close(fd_acc);
 }
-#endif
 
 TEST(sys_socket, recvmmsg_smoke) {
-#if defined(RECVMMSG_SUPPORTED)
   RunTest(TestRecvMMsg, SendMultiple);
-#else
-  GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
 }
 
 TEST(sys_socket, recvmmsg_error) {
-#if defined(RECVMMSG_SUPPORTED)
   ASSERT_EQ(-1, recvmmsg(-1, NULL, 0, 0, NULL));
   ASSERT_EQ(EBADF, errno);
-#else
-  GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
 }
 
-#if defined(SENDMMSG_SUPPORTED)
 const char* g_SendMsgs[] = {
   "MSG_ONE",
   "MSG_TWO",
@@ -239,21 +216,12 @@
 
   close(fd_acc);
 }
-#endif
 
 TEST(sys_socket, sendmmsg_smoke) {
-#if defined(SENDMMSG_SUPPORTED)
   RunTest(TestSendMMsg, SendMMsg);
-#else
-  GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
 }
 
 TEST(sys_socket, sendmmsg_error) {
-#if defined(SENDMMSG_SUPPORTED)
   ASSERT_EQ(-1, sendmmsg(-1, NULL, 0, 0));
   ASSERT_EQ(EBADF, errno);
-#else
-  GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
 }
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index 4ead057..b5fcf26 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -30,7 +30,6 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-
 TEST(unistd, sysconf_SC_MONOTONIC_CLOCK) {
   ASSERT_GT(sysconf(_SC_MONOTONIC_CLOCK), 0);
 }
@@ -251,8 +250,6 @@
 }
 
 TEST(unistd, unsetenv_EINVAL) {
-  EXPECT_EQ(-1, unsetenv(NULL));
-  EXPECT_EQ(EINVAL, errno);
   EXPECT_EQ(-1, unsetenv(""));
   EXPECT_EQ(EINVAL, errno);
   EXPECT_EQ(-1, unsetenv("a=b"));
diff --git a/tests/wchar_test.cpp b/tests/wchar_test.cpp
index 887266c..63f3760 100644
--- a/tests/wchar_test.cpp
+++ b/tests/wchar_test.cpp
@@ -22,6 +22,8 @@
 #include <stdint.h>
 #include <wchar.h>
 
+#define NUM_WCHARS(num_bytes) (num_bytes/sizeof(wchar_t))
+
 TEST(wchar, sizeof_wchar_t) {
   EXPECT_EQ(4U, sizeof(wchar_t));
   EXPECT_EQ(4U, sizeof(wint_t));
@@ -343,7 +345,7 @@
   // Check that valid has advanced to the next unread character.
   ASSERT_EQ('e', *valid);
 
-  wmemset(out, L'x', sizeof(out) / sizeof(wchar_t));
+  wmemset(out, L'x', NUM_WCHARS(sizeof(out)));
   ASSERT_EQ(2U, mbsrtowcs(out, &valid, 4, ps));
   ASSERT_EQ(L'e', out[0]);
   ASSERT_EQ(L'f', out[1]);
@@ -450,17 +452,85 @@
   EXPECT_STREQ(L"Sun Mar 10 00:00:00 2100", buf);
 }
 
-TEST(wchar, wmemmove) {
+TEST(wchar, wmemmove_smoke) {
   const wchar_t const_wstr[] = L"This is a test of something or other.....";
-  wchar_t* wstr = new wchar_t[sizeof(const_wstr)];
+  wchar_t wstr[NUM_WCHARS(sizeof(const_wstr))];
 
-  wmemmove(wstr, const_wstr, sizeof(const_wstr)/sizeof(wchar_t));
+  EXPECT_EQ(wstr, wmemmove(wstr, const_wstr, NUM_WCHARS(sizeof(const_wstr))));
   EXPECT_STREQ(const_wstr, wstr);
 
-  wmemmove(wstr+5, wstr, sizeof(const_wstr)/sizeof(wchar_t) - 6);
+  EXPECT_EQ(wstr+5, wmemmove(wstr+5, wstr, NUM_WCHARS(sizeof(const_wstr)) - 6));
   EXPECT_STREQ(L"This This is a test of something or other", wstr);
 }
 
+TEST(wchar, wmemcpy_smoke) {
+  const wchar_t src[] = L"Source string";
+  wchar_t dst[NUM_WCHARS(sizeof(src))];
+
+  EXPECT_EQ(dst, wmemcpy(dst, src, NUM_WCHARS(sizeof(src))));
+  EXPECT_STREQ(dst, src);
+}
+
+TEST(wchar, wcpcpy_smoke) {
+  const wchar_t src[] = L"Source string";
+  wchar_t dst[NUM_WCHARS(sizeof(src))];
+
+  EXPECT_EQ(dst + NUM_WCHARS(sizeof(src)) - 1, wcpcpy(dst, src));
+  EXPECT_STREQ(dst, src);
+}
+
+TEST(wchar, wcpncpy_smoke) {
+  const wchar_t src[] = L"Source string";
+  wchar_t dst[NUM_WCHARS(sizeof(src)) + 5];
+
+  size_t src_len = NUM_WCHARS(sizeof(src)) - 1;
+  EXPECT_EQ(dst + src_len, wcpncpy(dst, src, src_len + 1));
+  EXPECT_STREQ(dst, src);
+
+  EXPECT_EQ(dst + 6, wcpncpy(dst, src, 6));
+  dst[6] = L'\0';
+  EXPECT_STREQ(dst, L"Source");
+
+  wmemset(dst, L'x', NUM_WCHARS(sizeof(dst)));
+  EXPECT_EQ(dst + src_len, wcpncpy(dst, src, src_len + 4));
+  EXPECT_STREQ(dst, src);
+  EXPECT_EQ(dst[src_len], L'\0');
+  EXPECT_EQ(dst[src_len+1], L'\0');
+  EXPECT_EQ(dst[src_len+2], L'\0');
+  EXPECT_EQ(dst[src_len+3], L'\0');
+  EXPECT_EQ(dst[src_len+4], L'x');
+}
+
+TEST(wchar, wcscpy_smoke) {
+  const wchar_t src[] = L"Source string";
+  wchar_t dst[NUM_WCHARS(sizeof(src))];
+
+  EXPECT_EQ(dst, wcscpy(dst, src));
+  EXPECT_STREQ(src, dst);
+}
+
+TEST(wchar, wcsncpy_smoke) {
+  const wchar_t src[] = L"Source string";
+  wchar_t dst[NUM_WCHARS(sizeof(src)) + 5];
+
+  size_t src_len = NUM_WCHARS(sizeof(src)) - 1;
+  EXPECT_EQ(dst, wcsncpy(dst, src, src_len + 1));
+  EXPECT_STREQ(dst, src);
+
+  EXPECT_EQ(dst, wcsncpy(dst, src, 6));
+  dst[6] = L'\0';
+  EXPECT_STREQ(dst, L"Source");
+
+  wmemset(dst, L'x', NUM_WCHARS(sizeof(dst)));
+  EXPECT_EQ(dst, wcsncpy(dst, src, src_len + 4));
+  EXPECT_STREQ(dst, src);
+  EXPECT_EQ(dst[src_len], L'\0');
+  EXPECT_EQ(dst[src_len+1], L'\0');
+  EXPECT_EQ(dst[src_len+2], L'\0');
+  EXPECT_EQ(dst[src_len+3], L'\0');
+  EXPECT_EQ(dst[src_len+4], L'x');
+}
+
 TEST(wchar, mbrtowc_15439554) {
   // http://b/15439554
   ASSERT_STREQ("C.UTF-8", setlocale(LC_CTYPE, "C.UTF-8"));