Merge "Revert "fortify: allow diagnostics without run-time checks""
diff --git a/.clang-format b/.clang-format
index b8c6428..7630d16 100644
--- a/.clang-format
+++ b/.clang-format
@@ -12,3 +12,4 @@
 PenaltyExcessCharacter: 32
 
 Cpp11BracedListStyle: false
+IncludeBlocks: Preserve
\ No newline at end of file
diff --git a/apex/Android.bp b/apex/Android.bp
index 276541e..4fbbec1 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -29,6 +29,7 @@
         "libc",
         "libm",
         "libdl",
+        "libdl_android",
         "libc_malloc_debug",
         "libc_malloc_hooks",
     ],
diff --git a/libc/Android.bp b/libc/Android.bp
index 45c4569..c5ea4c5 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -83,6 +83,12 @@
     // lld complains about duplicate symbols in libcrt and libgcc. Suppress the
     // warning since this is intended right now.
     ldflags: ["-Wl,-z,muldefs"],
+
+    product_variables: {
+        experimental_mte: {
+            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+        },
+    },
 }
 
 // Defaults for native allocator libs/includes to make it
@@ -136,19 +142,21 @@
 }
 
 // ========================================================
-// libc_stack_protector.a - stack protector code
+// libc_bootstrap.a - -fno-stack-protector and -ffreestanding
 // ========================================================
 //
-// Code that implements the stack protector (or that runs
-// before TLS has been set up) needs to be compiled with
-// -fno-stack-protector, since it accesses the stack canary
-// TLS slot.
+// Code that implements the stack protector (or that runs before TLS has been set up) needs to be
+// compiled with -fno-stack-protector, since it accesses the stack canary TLS slot. In the linker,
+// some of this code runs before ifunc resolvers have made string.h functions work, so compile with
+// -ffreestanding.
 
 cc_library_static {
 
     srcs: [
         "bionic/__libc_init_main_thread.cpp",
         "bionic/__stack_chk_fail.cpp",
+        "bionic/bionic_call_ifunc_resolver.cpp",
+        "bionic/getauxval.cpp",
     ],
     arch: {
         arm64: {
@@ -166,20 +174,25 @@
     },
 
     defaults: ["libc_defaults"],
-    cflags: ["-fno-stack-protector"],
-    name: "libc_stack_protector",
+    cflags: ["-fno-stack-protector", "-ffreestanding"],
+    name: "libc_bootstrap",
 }
 
-// libc_init_static.cpp also needs to be built without stack protector,
-// because it's responsible for setting up TLS for static executables.
-// This isn't the case for dynamic executables because the dynamic linker
-// has already set up the main thread's TLS.
+// libc_init_static.cpp and libc_init_dynamic.cpp need to be built without stack protector.
+// libc_init_static.cpp sets up TLS for static executables, and libc_init_dynamic.cpp initializes
+// the stack protector global variable.
 
 cc_library_static {
     name: "libc_init_static",
     defaults: ["libc_defaults"],
     srcs: ["bionic/libc_init_static.cpp"],
-    cflags: ["-fno-stack-protector"],
+    cflags: [
+        "-fno-stack-protector",
+
+        // Compile libc_init_static.cpp with -ffreestanding, because some of its code is called
+        // from the linker before ifunc resolvers have made string.h functions available.
+        "-ffreestanding",
+    ],
 }
 
 cc_library_static {
@@ -778,12 +791,6 @@
 cc_library_static {
     defaults: ["libc_defaults"],
     srcs: [
-        // The data that backs getauxval is initialized in the libc init
-        // functions which are invoked by the linker. If this file is included
-        // in libc_ndk.a, only one of the copies of the global data will be
-        // initialized, resulting in nullptr dereferences.
-        "bionic/getauxval.cpp",
-
         // These require getauxval, which isn't available on older platforms.
         "bionic/sysconf.cpp",
         "bionic/vdso.cpp",
@@ -854,20 +861,28 @@
         },
         arm64: {
             srcs: [
-                "arch-arm64/generic/bionic/memchr.S",
                 "arch-arm64/generic/bionic/memcmp.S",
                 "arch-arm64/generic/bionic/memcpy.S",
                 "arch-arm64/generic/bionic/memmove.S",
                 "arch-arm64/generic/bionic/memset.S",
                 "arch-arm64/generic/bionic/stpcpy.S",
-                "arch-arm64/generic/bionic/strchr.S",
-                "arch-arm64/generic/bionic/strcmp.S",
                 "arch-arm64/generic/bionic/strcpy.S",
-                "arch-arm64/generic/bionic/strlen.S",
-                "arch-arm64/generic/bionic/strncmp.S",
-                "arch-arm64/generic/bionic/strnlen.S",
                 "arch-arm64/generic/bionic/wmemmove.S",
 
+                "arch-arm64/default/bionic/memchr.S",
+                "arch-arm64/default/bionic/strchr.S",
+                "arch-arm64/default/bionic/strcmp.S",
+                "arch-arm64/default/bionic/strlen.S",
+                "arch-arm64/default/bionic/strncmp.S",
+                "arch-arm64/default/bionic/strnlen.S",
+
+                "arch-arm64/mte/bionic/memchr.c",
+                "arch-arm64/mte/bionic/strchr.cpp",
+                "arch-arm64/mte/bionic/strcmp.c",
+                "arch-arm64/mte/bionic/strlen.c",
+                "arch-arm64/mte/bionic/strncmp.c",
+                "arch-arm64/mte/bionic/strnlen.c",
+
                 "arch-arm64/bionic/__bionic_clone.S",
                 "arch-arm64/bionic/_exit_with_stack_teardown.S",
                 "arch-arm64/bionic/setjmp.S",
@@ -1070,7 +1085,6 @@
         "bionic/atof.cpp",
         "bionic/bionic_allocator.cpp",
         "bionic/bionic_arc4random.cpp",
-        "bionic/bionic_call_ifunc_resolver.cpp",
         "bionic/bionic_futex.cpp",
         "bionic/bionic_netlink.cpp",
         "bionic/bionic_systrace.cpp",
@@ -1413,6 +1427,7 @@
 
     whole_static_libs: [
         "libc_bionic_ndk",
+        "libc_bootstrap",
         "libc_fortify",
         "libc_freebsd",
         "libc_freebsd_large_stack",
@@ -1420,7 +1435,6 @@
         "libc_netbsd",
         "libc_openbsd_large_stack",
         "libc_openbsd_ndk",
-        "libc_stack_protector",
         "libc_syscalls",
         "libc_tzcode",
         "libm",
@@ -1444,6 +1458,7 @@
     whole_static_libs: [
         "libc_bionic",
         "libc_bionic_ndk",
+        "libc_bootstrap",
         "libc_dns",
         "libc_fortify",
         "libc_freebsd",
@@ -1453,7 +1468,6 @@
         "libc_openbsd",
         "libc_openbsd_large_stack",
         "libc_openbsd_ndk",
-        "libc_stack_protector",
         "libc_syscalls",
         "libc_tzcode",
         "libstdc++",
@@ -1481,11 +1495,11 @@
 }
 
 // ========================================================
-// libc_common_static.a For static binaries.
+// libc_static_dispatch.a
 // ========================================================
 cc_library_static {
     defaults: ["libc_defaults"],
-    name: "libc_common_static",
+    name: "libc_static_dispatch",
 
     arch: {
         x86: {
@@ -1494,21 +1508,21 @@
         arm: {
             srcs: ["arch-arm/static_function_dispatch.S"],
         },
+        arm64: {
+            srcs: ["arch-arm64/static_function_dispatch.S"],
+        },
     },
-
-    whole_static_libs: [
-        "libc_common",
-    ],
 }
 
 // ========================================================
-// libc_common_shared.a For shared libraries.
+// libc_dynamic_dispatch.a
 // ========================================================
 cc_library_static {
     defaults: ["libc_defaults"],
-    name: "libc_common_shared",
+    name: "libc_dynamic_dispatch",
 
     cflags: [
+        "-ffreestanding",
         "-fno-stack-protector",
         "-fno-jump-tables",
     ],
@@ -1519,10 +1533,35 @@
         arm: {
             srcs: ["arch-arm/dynamic_function_dispatch.cpp"],
         },
+        arm64: {
+            srcs: ["arch-arm64/dynamic_function_dispatch.cpp"],
+        },
     },
+}
+
+// ========================================================
+// libc_common_static.a For static binaries.
+// ========================================================
+cc_library_static {
+    defaults: ["libc_defaults"],
+    name: "libc_common_static",
 
     whole_static_libs: [
         "libc_common",
+        "libc_static_dispatch",
+    ],
+}
+
+// ========================================================
+// libc_common_shared.a For shared libraries.
+// ========================================================
+cc_library_static {
+    defaults: ["libc_defaults"],
+    name: "libc_common_shared",
+
+    whole_static_libs: [
+        "libc_common",
+        "libc_dynamic_dispatch",
     ],
 }
 
@@ -1546,19 +1585,16 @@
 // libc_nomalloc.a
 // ========================================================
 //
-// This is a version of the static C library that does not
-// include malloc. It's useful in situations when the user wants
-// to provide their own malloc implementation, or wants to
-// explicitly disallow the use of malloc, such as in the
-// dynamic linker.
+// This is a version of the static C library used by the dynamic linker that exclude malloc. It also
+// excludes functions selected using ifunc's (e.g. for string.h). Link in either
+// libc_static_dispatch or libc_dynamic_dispatch to provide those functions.
 
 cc_library_static {
     name: "libc_nomalloc",
     defaults: ["libc_defaults"],
-    cflags: ["-DLIBC_STATIC"],
 
     whole_static_libs: [
-        "libc_common_static",
+        "libc_common",
         "libc_init_static",
         "libc_unwind_static",
     ],
diff --git a/libc/arch-arm/dynamic_function_dispatch.cpp b/libc/arch-arm/dynamic_function_dispatch.cpp
index 75000a2..1d2f38f 100644
--- a/libc/arch-arm/dynamic_function_dispatch.cpp
+++ b/libc/arch-arm/dynamic_function_dispatch.cpp
@@ -149,7 +149,7 @@
 
 typedef void* memcpy_func(void*, const void*, size_t);
 DEFINE_IFUNC_FOR(memcpy) {
-    return memmove_resolver();
+    return memmove_resolver(hwcap);
 }
 
 typedef void* __memcpy_func(void*, const void*, size_t);
diff --git a/libc/arch-arm64/generic/bionic/memchr.S b/libc/arch-arm64/default/bionic/memchr.S
similarity index 98%
rename from libc/arch-arm64/generic/bionic/memchr.S
rename to libc/arch-arm64/default/bionic/memchr.S
index 7b7e699..7fbcc8f 100644
--- a/libc/arch-arm64/generic/bionic/memchr.S
+++ b/libc/arch-arm64/default/bionic/memchr.S
@@ -69,7 +69,7 @@
  * identify exactly which byte has matched.
  */
 
-ENTRY(memchr)
+ENTRY(memchr_default)
 	/*
 	 * Magic constant 0x40100401 allows us to identify which lane matches
 	 * the requested byte.
@@ -161,4 +161,4 @@
 .Lzero_length:
 	mov	result, xzr
 	ret
-END(memchr)
+END(memchr_default)
diff --git a/libc/arch-arm64/generic/bionic/strchr.S b/libc/arch-arm64/default/bionic/strchr.S
similarity index 98%
rename from libc/arch-arm64/generic/bionic/strchr.S
rename to libc/arch-arm64/default/bionic/strchr.S
index 2db0c76..f8cb724 100644
--- a/libc/arch-arm64/generic/bionic/strchr.S
+++ b/libc/arch-arm64/default/bionic/strchr.S
@@ -73,7 +73,7 @@
 
 /* Locals and temporaries.  */
 
-ENTRY(strchr)
+ENTRY(strchr_default)
 	/* Magic constant 0x40100401 to allow us to identify which lane
 	   matches the requested byte.  Magic constant 0x80200802 used
 	   similarly for NUL termination.  */
@@ -150,4 +150,4 @@
 	add	result, src, tmp1, lsr #1
 	csel	result, result, xzr, eq
 	ret
-END(strchr)
+END(strchr_default)
diff --git a/libc/arch-arm64/generic/bionic/strcmp.S b/libc/arch-arm64/default/bionic/strcmp.S
similarity index 99%
rename from libc/arch-arm64/generic/bionic/strcmp.S
rename to libc/arch-arm64/default/bionic/strcmp.S
index fbc215e..dfac7c4 100644
--- a/libc/arch-arm64/generic/bionic/strcmp.S
+++ b/libc/arch-arm64/default/bionic/strcmp.S
@@ -58,7 +58,7 @@
 #define pos		x11
 
 	/* Start of performance-critical section  -- one 64B cache line.  */
-ENTRY(strcmp)
+ENTRY(strcmp_default)
 .p2align  6
 	eor	tmp1, src1, src2
 	mov	zeroones, #REP8_01
@@ -189,4 +189,4 @@
 L(done):
 	sub	result, data1, data2
 	ret
-END(strcmp)
+END(strcmp_default)
diff --git a/libc/arch-arm64/generic/bionic/strlen.S b/libc/arch-arm64/default/bionic/strlen.S
similarity index 99%
rename from libc/arch-arm64/generic/bionic/strlen.S
rename to libc/arch-arm64/default/bionic/strlen.S
index 6e540fc..07c5294 100644
--- a/libc/arch-arm64/generic/bionic/strlen.S
+++ b/libc/arch-arm64/default/bionic/strlen.S
@@ -94,7 +94,7 @@
 	   whether the first fetch, which may be misaligned, crosses a page
 	   boundary.  */
 
-ENTRY(strlen)
+ENTRY(strlen_default)
 	and	tmp1, srcin, MIN_PAGE_SIZE - 1
 	mov	zeroones, REP8_01
 	cmp	tmp1, MIN_PAGE_SIZE - 16
@@ -224,4 +224,4 @@
 	csel	data2, data2, tmp2, eq
 	b	L(page_cross_entry)
 
-END(strlen)
+END(strlen_default)
diff --git a/libc/arch-arm64/generic/bionic/strncmp.S b/libc/arch-arm64/default/bionic/strncmp.S
similarity index 99%
rename from libc/arch-arm64/generic/bionic/strncmp.S
rename to libc/arch-arm64/default/bionic/strncmp.S
index b81f43a..5432b73 100644
--- a/libc/arch-arm64/generic/bionic/strncmp.S
+++ b/libc/arch-arm64/default/bionic/strncmp.S
@@ -65,7 +65,7 @@
 	.rep 7
 	nop	/* Pad so that the loop below fits a cache line.  */
 	.endr
-ENTRY(strncmp)
+ENTRY(strncmp_default)
 	cbz	limit, .Lret0
 	eor	tmp1, src1, src2
 	mov	zeroones, #REP8_01
@@ -277,4 +277,4 @@
 .Lret0:
 	mov	result, #0
 	ret
-END(strncmp)
+END(strncmp_default)
diff --git a/libc/arch-arm64/generic/bionic/strnlen.S b/libc/arch-arm64/default/bionic/strnlen.S
similarity index 98%
rename from libc/arch-arm64/generic/bionic/strnlen.S
rename to libc/arch-arm64/default/bionic/strnlen.S
index 0ad446e..1694532 100644
--- a/libc/arch-arm64/generic/bionic/strnlen.S
+++ b/libc/arch-arm64/default/bionic/strnlen.S
@@ -68,7 +68,7 @@
 	mov	len, limit
 	ret
 
-ENTRY(strnlen)
+ENTRY(strnlen_default)
 	cbz	limit, .Lhit_limit
 	mov	zeroones, #REP8_01
 	bic	src, srcin, #15
@@ -171,4 +171,4 @@
 	csinv	data1, data1, xzr, le
 	csel	data2, data2, data2a, le
 	b	.Lrealigned
-END(strnlen)
+END(strnlen_default)
diff --git a/libc/arch-arm64/dynamic_function_dispatch.cpp b/libc/arch-arm64/dynamic_function_dispatch.cpp
new file mode 100644
index 0000000..37abea4
--- /dev/null
+++ b/libc/arch-arm64/dynamic_function_dispatch.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <platform/bionic/mte_kernel.h>
+#include <private/bionic_ifuncs.h>
+#include <stddef.h>
+#include <sys/auxv.h>
+
+extern "C" {
+
+static bool supports_mte(unsigned long hwcap2) {
+#ifdef ANDROID_EXPERIMENTAL_MTE
+    return hwcap2 & HWCAP2_MTE;
+#else
+    (void)hwcap2;
+    return false;
+#endif
+}
+
+typedef void* memchr_func(const void*, int, size_t);
+DEFINE_IFUNC_FOR(memchr) {
+    if (supports_mte(arg->_hwcap2)) {
+        RETURN_FUNC(memchr_func, memchr_mte);
+    } else {
+        RETURN_FUNC(memchr_func, memchr_default);
+    }
+}
+
+typedef char* strchr_func(const char*, int);
+DEFINE_IFUNC_FOR(strchr) {
+    if (supports_mte(arg->_hwcap2)) {
+        RETURN_FUNC(strchr_func, strchr_mte);
+    } else {
+        RETURN_FUNC(strchr_func, strchr_default);
+    }
+}
+
+typedef int strcmp_func(const char*, const char*);
+DEFINE_IFUNC_FOR(strcmp) {
+    if (supports_mte(arg->_hwcap2)) {
+        RETURN_FUNC(strcmp_func, strcmp_mte);
+    } else {
+        RETURN_FUNC(strcmp_func, strcmp_default);
+    }
+}
+
+typedef size_t strlen_func(const char*);
+DEFINE_IFUNC_FOR(strlen) {
+    if (supports_mte(arg->_hwcap2)) {
+        RETURN_FUNC(strlen_func, strlen_mte);
+    } else {
+        RETURN_FUNC(strlen_func, strlen_default);
+    }
+}
+
+typedef int strncmp_func(const char*, const char*, int);
+DEFINE_IFUNC_FOR(strncmp) {
+    if (supports_mte(arg->_hwcap2)) {
+        RETURN_FUNC(strncmp_func, strncmp_mte);
+    } else {
+        RETURN_FUNC(strncmp_func, strncmp_default);
+    }
+}
+
+typedef size_t strnlen_func(const char*, int);
+DEFINE_IFUNC_FOR(strnlen) {
+    if (supports_mte(arg->_hwcap2)) {
+        RETURN_FUNC(strnlen_func, strnlen_mte);
+    } else {
+        RETURN_FUNC(strnlen_func, strnlen_default);
+    }
+}
+
+}  // extern "C"
diff --git a/libc/arch-arm64/mte/bionic/memchr.c b/libc/arch-arm64/mte/bionic/memchr.c
new file mode 100644
index 0000000..33b2fc2
--- /dev/null
+++ b/libc/arch-arm64/mte/bionic/memchr.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <upstream-openbsd/android/include/openbsd-compat.h>
+
+#define memchr memchr_mte
+#include <upstream-openbsd/lib/libc/string/memchr.c>
diff --git a/libc/arch-arm64/mte/bionic/strchr.cpp b/libc/arch-arm64/mte/bionic/strchr.cpp
new file mode 100644
index 0000000..7d394df
--- /dev/null
+++ b/libc/arch-arm64/mte/bionic/strchr.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define strchr strchr_mte
+#include <bionic/strchr.cpp>
diff --git a/libc/arch-arm64/mte/bionic/strcmp.c b/libc/arch-arm64/mte/bionic/strcmp.c
new file mode 100644
index 0000000..0e134f0
--- /dev/null
+++ b/libc/arch-arm64/mte/bionic/strcmp.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <upstream-openbsd/android/include/openbsd-compat.h>
+
+#define strcmp strcmp_mte
+#include <upstream-openbsd/lib/libc/string/strcmp.c>
diff --git a/libc/arch-arm64/mte/bionic/strlen.c b/libc/arch-arm64/mte/bionic/strlen.c
new file mode 100644
index 0000000..de88320
--- /dev/null
+++ b/libc/arch-arm64/mte/bionic/strlen.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <upstream-openbsd/android/include/openbsd-compat.h>
+
+#define strlen strlen_mte
+#include <upstream-openbsd/lib/libc/string/strlen.c>
diff --git a/libc/arch-arm64/mte/bionic/strncmp.c b/libc/arch-arm64/mte/bionic/strncmp.c
new file mode 100644
index 0000000..54d08e9
--- /dev/null
+++ b/libc/arch-arm64/mte/bionic/strncmp.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <upstream-openbsd/android/include/openbsd-compat.h>
+
+#define strncmp strncmp_mte
+#include <upstream-openbsd/lib/libc/string/strncmp.c>
diff --git a/libc/arch-arm64/mte/bionic/strnlen.c b/libc/arch-arm64/mte/bionic/strnlen.c
new file mode 100644
index 0000000..3dc251d
--- /dev/null
+++ b/libc/arch-arm64/mte/bionic/strnlen.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define strnlen strnlen_mte
+#include <bionic/strnlen.c>
diff --git a/libc/arch-arm64/static_function_dispatch.S b/libc/arch-arm64/static_function_dispatch.S
new file mode 100644
index 0000000..8e3a4c1
--- /dev/null
+++ b/libc/arch-arm64/static_function_dispatch.S
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <private/bionic_asm.h>
+
+#define FUNCTION_DELEGATE(name, impl) \
+ENTRY(name); \
+    b impl; \
+END(name)
+
+FUNCTION_DELEGATE(memchr, memchr_mte)
+FUNCTION_DELEGATE(strchr, strchr_mte)
+FUNCTION_DELEGATE(strcmp, strcmp_mte)
+FUNCTION_DELEGATE(strlen, strlen_mte)
+FUNCTION_DELEGATE(strncmp, strncmp_mte)
+FUNCTION_DELEGATE(strnlen, strnlen_mte)
diff --git a/libc/bionic/__libc_init_main_thread.cpp b/libc/bionic/__libc_init_main_thread.cpp
index 6e1b0de..94cf1f8 100644
--- a/libc/bionic/__libc_init_main_thread.cpp
+++ b/libc/bionic/__libc_init_main_thread.cpp
@@ -57,7 +57,9 @@
 //
 // This is in a file by itself because it needs to be built with
 // -fno-stack-protector because it's responsible for setting up the main
-// thread's TLS (which stack protector relies on).
+// thread's TLS (which stack protector relies on). It's also built with
+// -ffreestanding because the early init function runs in the linker before
+// ifunc resolvers have run.
 
 // Do enough setup to:
 //  - Let the dynamic linker invoke system calls (and access errno)
@@ -65,7 +67,8 @@
 //  - Allow the stack protector to work (with a zero cookie)
 // Avoid doing much more because, when this code is called within the dynamic
 // linker, the linker binary hasn't been relocated yet, so certain kinds of code
-// are hazardous, such as accessing non-hidden global variables.
+// are hazardous, such as accessing non-hidden global variables or calling
+// string.h functions.
 __BIONIC_WEAK_FOR_NATIVE_BRIDGE
 extern "C" void __libc_init_main_thread_early(const KernelArgumentBlock& args,
                                               bionic_tcb* temp_tcb) {
@@ -80,6 +83,23 @@
   main_thread.set_cached_pid(main_thread.tid);
 }
 
+// This code is used both by each new pthread and the code that initializes the main thread.
+void __init_tcb(bionic_tcb* tcb, pthread_internal_t* thread) {
+#ifdef TLS_SLOT_SELF
+  // On x86, slot 0 must point to itself so code can read the thread pointer by
+  // loading %fs:0 or %gs:0.
+  tcb->tls_slot(TLS_SLOT_SELF) = &tcb->tls_slot(TLS_SLOT_SELF);
+#endif
+  tcb->tls_slot(TLS_SLOT_THREAD_ID) = thread;
+}
+
+void __init_tcb_dtv(bionic_tcb* tcb) {
+  // Initialize the DTV slot to a statically-allocated empty DTV. The first
+  // access to a dynamic TLS variable allocates a new DTV.
+  static const TlsDtv zero_dtv = {};
+  __set_tcb_dtv(tcb, const_cast<TlsDtv*>(&zero_dtv));
+}
+
 // Finish initializing the main thread.
 __BIONIC_WEAK_FOR_NATIVE_BRIDGE
 extern "C" void __libc_init_main_thread_late() {
diff --git a/libc/bionic/bionic_call_ifunc_resolver.cpp b/libc/bionic/bionic_call_ifunc_resolver.cpp
index 8522835..437de78 100644
--- a/libc/bionic/bionic_call_ifunc_resolver.cpp
+++ b/libc/bionic/bionic_call_ifunc_resolver.cpp
@@ -30,14 +30,32 @@
 #include <sys/auxv.h>
 #include <sys/ifunc.h>
 
+#include "private/bionic_auxv.h"
+
+// This code is called in the linker before it has been relocated, so minimize calls into other
+// parts of Bionic. In particular, we won't ever have two ifunc resolvers called concurrently, so
+// initializing the ifunc resolver argument doesn't need to be thread-safe.
+
 ElfW(Addr) __bionic_call_ifunc_resolver(ElfW(Addr) resolver_addr) {
 #if defined(__aarch64__)
   typedef ElfW(Addr) (*ifunc_resolver_t)(uint64_t, __ifunc_arg_t*);
-  static __ifunc_arg_t arg = { sizeof(__ifunc_arg_t), getauxval(AT_HWCAP), getauxval(AT_HWCAP2) };
+  static __ifunc_arg_t arg;
+  static bool initialized = false;
+  if (!initialized) {
+    initialized = true;
+    arg._size = sizeof(__ifunc_arg_t);
+    arg._hwcap = getauxval(AT_HWCAP);
+    arg._hwcap2 = getauxval(AT_HWCAP2);
+  }
   return reinterpret_cast<ifunc_resolver_t>(resolver_addr)(arg._hwcap | _IFUNC_ARG_HWCAP, &arg);
 #elif defined(__arm__)
   typedef ElfW(Addr) (*ifunc_resolver_t)(unsigned long);
-  static unsigned long hwcap = getauxval(AT_HWCAP);
+  static unsigned long hwcap;
+  static bool initialized = false;
+  if (!initialized) {
+    initialized = true;
+    hwcap = getauxval(AT_HWCAP);
+  }
   return reinterpret_cast<ifunc_resolver_t>(resolver_addr)(hwcap);
 #else
   typedef ElfW(Addr) (*ifunc_resolver_t)(void);
diff --git a/libc/bionic/bionic_netlink.cpp b/libc/bionic/bionic_netlink.cpp
index f2449dc..5d5a026 100644
--- a/libc/bionic/bionic_netlink.cpp
+++ b/libc/bionic/bionic_netlink.cpp
@@ -39,8 +39,6 @@
 #include "private/ErrnoRestorer.h"
 
 NetlinkConnection::NetlinkConnection() {
-  fd_ = -1;
-
   // The kernel keeps packets under 8KiB (NLMSG_GOODSIZE),
   // but that's a bit too large to go on the stack.
   size_ = 8192;
@@ -48,8 +46,6 @@
 }
 
 NetlinkConnection::~NetlinkConnection() {
-  ErrnoRestorer errno_restorer;
-  if (fd_ != -1) close(fd_);
   delete[] data_;
 }
 
@@ -59,9 +55,9 @@
   if (data_ == nullptr) return false;
 
   // Did we open a netlink socket yet?
-  if (fd_ == -1) {
-    fd_ = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
-    if (fd_ == -1) return false;
+  if (fd_.get() == -1) {
+    fd_.reset(socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE));
+    if (fd_.get() == -1) return false;
   }
 
   // Construct and send the message.
@@ -74,13 +70,13 @@
   request.hdr.nlmsg_type = type;
   request.hdr.nlmsg_len = sizeof(request);
   request.msg.rtgen_family = AF_UNSPEC; // All families.
-  return (TEMP_FAILURE_RETRY(send(fd_, &request, sizeof(request), 0)) == sizeof(request));
+  return (TEMP_FAILURE_RETRY(send(fd_.get(), &request, sizeof(request), 0)) == sizeof(request));
 }
 
 bool NetlinkConnection::ReadResponses(void callback(void*, nlmsghdr*), void* context) {
   // Read through all the responses, handing interesting ones to the callback.
   ssize_t bytes_read;
-  while ((bytes_read = TEMP_FAILURE_RETRY(recv(fd_, data_, size_, 0))) > 0) {
+  while ((bytes_read = TEMP_FAILURE_RETRY(recv(fd_.get(), data_, size_, 0))) > 0) {
     nlmsghdr* hdr = reinterpret_cast<nlmsghdr*>(data_);
     for (; NLMSG_OK(hdr, static_cast<size_t>(bytes_read)); hdr = NLMSG_NEXT(hdr, bytes_read)) {
       if (hdr->nlmsg_type == NLMSG_DONE) return true;
diff --git a/libc/bionic/bionic_netlink.h b/libc/bionic/bionic_netlink.h
index a21200e..fc1bd0f 100644
--- a/libc/bionic/bionic_netlink.h
+++ b/libc/bionic/bionic_netlink.h
@@ -33,6 +33,8 @@
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 
+#include "private/ScopedFd.h"
+
 struct nlmsghdr;
 
 class NetlinkConnection {
@@ -44,7 +46,7 @@
   bool ReadResponses(void callback(void*, nlmsghdr*), void* context);
 
  private:
-  int fd_;
+  ScopedFd fd_;
   char* data_;
   size_t size_;
 };
diff --git a/libc/bionic/fchmodat.cpp b/libc/bionic/fchmodat.cpp
index c28e15a..632d2ac 100644
--- a/libc/bionic/fchmodat.cpp
+++ b/libc/bionic/fchmodat.cpp
@@ -32,7 +32,7 @@
 #include <errno.h>
 #include <unistd.h>
 
-#include "private/ErrnoRestorer.h"
+#include "private/ScopedFd.h"
 
 extern "C" int __fchmodat(int, const char*, mode_t);
 
@@ -47,20 +47,15 @@
     // at https://sourceware.org/bugzilla/show_bug.cgi?id=14578
     // comment #10
 
-    int fd = openat(dirfd, pathname, O_PATH | O_NOFOLLOW | O_CLOEXEC);
-    if (fd == -1) {
-      return -1; // returns errno from openat
-    }
+    ScopedFd fd(openat(dirfd, pathname, O_PATH | O_NOFOLLOW | O_CLOEXEC));
+    if (fd.get() == -1) return -1;
 
     // POSIX requires that ENOTSUP be returned when the system
     // doesn't support setting the mode of a symbolic link.
     // This is true for all Linux kernels.
     // We rely on the O_PATH compatibility layer added in the
     // fchmod() function to get errno correct.
-    int result = fchmod(fd, mode);
-    ErrnoRestorer errno_restorer; // don't let close() clobber errno
-    close(fd);
-    return result;
+    return fchmod(fd.get(), mode);
   }
 
   return __fchmodat(dirfd, pathname, mode);
diff --git a/libc/bionic/getauxval.cpp b/libc/bionic/getauxval.cpp
index c8f867b..f865f97 100644
--- a/libc/bionic/getauxval.cpp
+++ b/libc/bionic/getauxval.cpp
@@ -36,7 +36,6 @@
 
 // This function needs to be safe to call before TLS is set up, so it can't
 // access errno or the stack protector.
-__attribute__((no_stack_protector))
 __LIBC_HIDDEN__ unsigned long __bionic_getauxval(unsigned long type, bool& exists) {
   for (ElfW(auxv_t)* v = __libc_shared_globals()->auxv; v->a_type != AT_NULL; ++v) {
     if (v->a_type == type) {
diff --git a/libc/bionic/getentropy.cpp b/libc/bionic/getentropy.cpp
index 2c6e417..9c93e71 100644
--- a/libc/bionic/getentropy.cpp
+++ b/libc/bionic/getentropy.cpp
@@ -31,22 +31,20 @@
 #include <sys/random.h>
 #include <unistd.h>
 
+#include "private/ScopedFd.h"
+
 static int getentropy_urandom(void* buffer, size_t buffer_size, int saved_errno) {
-  int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_NOFOLLOW | O_CLOEXEC, 0));
-  if (fd == -1) return -1;
+  ScopedFd fd(TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_NOFOLLOW | O_CLOEXEC, 0)));
+  if (fd.get() == -1) return -1;
 
   size_t collected = 0;
   while (collected < buffer_size) {
-    ssize_t count = TEMP_FAILURE_RETRY(read(fd, static_cast<char*>(buffer) + collected,
+    ssize_t count = TEMP_FAILURE_RETRY(read(fd.get(), static_cast<char*>(buffer) + collected,
                                             buffer_size - collected));
-    if (count == -1) {
-      close(fd);
-      return -1;
-    }
+    if (count == -1) return -1;
     collected += count;
   }
 
-  close(fd);
   errno = saved_errno;
   return 0;
 }
diff --git a/libc/bionic/grp_pwd_file.cpp b/libc/bionic/grp_pwd_file.cpp
index e13604e..81cf893 100644
--- a/libc/bionic/grp_pwd_file.cpp
+++ b/libc/bionic/grp_pwd_file.cpp
@@ -37,6 +37,7 @@
 #include <async_safe/log.h>
 
 #include "private/ErrnoRestorer.h"
+#include "private/ScopedFd.h"
 
 // This file mmap's /*/etc/passwd and /*/etc/group in order to return their contents without any
 // allocations.  Note that these files and the strings contained within them are explicitly not
@@ -230,19 +231,16 @@
 }
 
 bool MmapFile::DoMmap() {
-  int fd = open(filename_, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
+  ScopedFd fd(open(filename_, O_CLOEXEC | O_NOFOLLOW | O_RDONLY));
 
   struct stat fd_stat;
-  if (fstat(fd, &fd_stat) == -1) {
-    close(fd);
+  if (fstat(fd.get(), &fd_stat) == -1) {
     return false;
   }
 
   auto mmap_size = fd_stat.st_size;
 
-  void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0);
-  close(fd);
-
+  void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd.get(), 0);
   if (map_result == MAP_FAILED) {
     return false;
   }
diff --git a/libc/bionic/jemalloc_wrapper.cpp b/libc/bionic/jemalloc_wrapper.cpp
index 7d04457..ef488ee 100644
--- a/libc/bionic/jemalloc_wrapper.cpp
+++ b/libc/bionic/jemalloc_wrapper.cpp
@@ -140,30 +140,32 @@
     return -1;
   }
 
-  MallocXmlElem root(fp, "malloc", "version=\"jemalloc-1\"");
+  fflush(fp);
+  int fd = fileno(fp);
+  MallocXmlElem root(fd, "malloc", "version=\"jemalloc-1\"");
 
   // Dump all of the large allocations in the arenas.
   for (size_t i = 0; i < je_mallinfo_narenas(); i++) {
     struct mallinfo mi = je_mallinfo_arena_info(i);
     if (mi.hblkhd != 0) {
-      MallocXmlElem arena_elem(fp, "heap", "nr=\"%d\"", i);
+      MallocXmlElem arena_elem(fd, "heap", "nr=\"%d\"", i);
       {
-        MallocXmlElem(fp, "allocated-large").Contents("%zu", mi.ordblks);
-        MallocXmlElem(fp, "allocated-huge").Contents("%zu", mi.uordblks);
-        MallocXmlElem(fp, "allocated-bins").Contents("%zu", mi.fsmblks);
+        MallocXmlElem(fd, "allocated-large").Contents("%zu", mi.ordblks);
+        MallocXmlElem(fd, "allocated-huge").Contents("%zu", mi.uordblks);
+        MallocXmlElem(fd, "allocated-bins").Contents("%zu", mi.fsmblks);
 
         size_t total = 0;
         for (size_t j = 0; j < je_mallinfo_nbins(); j++) {
           struct mallinfo mi = je_mallinfo_bin_info(i, j);
           if (mi.ordblks != 0) {
-            MallocXmlElem bin_elem(fp, "bin", "nr=\"%d\"", j);
-            MallocXmlElem(fp, "allocated").Contents("%zu", mi.ordblks);
-            MallocXmlElem(fp, "nmalloc").Contents("%zu", mi.uordblks);
-            MallocXmlElem(fp, "ndalloc").Contents("%zu", mi.fordblks);
+            MallocXmlElem bin_elem(fd, "bin", "nr=\"%d\"", j);
+            MallocXmlElem(fd, "allocated").Contents("%zu", mi.ordblks);
+            MallocXmlElem(fd, "nmalloc").Contents("%zu", mi.uordblks);
+            MallocXmlElem(fd, "ndalloc").Contents("%zu", mi.fordblks);
             total += mi.ordblks;
           }
         }
-        MallocXmlElem(fp, "bins-total").Contents("%zu", total);
+        MallocXmlElem(fd, "bins-total").Contents("%zu", total);
       }
     }
   }
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index 0b74023..28c0b0c 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -231,6 +231,9 @@
   g_target_sdk_version = target;
 }
 
+// This function is called in the dynamic linker before ifunc resolvers have run, so this file is
+// compiled with -ffreestanding to avoid implicit string.h function calls. (It shouldn't strictly
+// be necessary, though.)
 __LIBC_HIDDEN__ libc_shared_globals* __libc_shared_globals() {
   static libc_shared_globals globals;
   return &globals;
diff --git a/libc/bionic/net_if.cpp b/libc/bionic/net_if.cpp
index db9c9ea2..ad53364 100644
--- a/libc/bionic/net_if.cpp
+++ b/libc/bionic/net_if.cpp
@@ -40,37 +40,27 @@
 #include <sys/socket.h>
 #include <unistd.h>
 
-#include "private/ErrnoRestorer.h"
+#include "private/ScopedFd.h"
 
 #include "bionic_netlink.h"
 
 char* if_indextoname(unsigned ifindex, char* ifname) {
-  int s = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0);
-  if (s == -1) return nullptr;
+  ScopedFd s(socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0));
+  if (s.get() == -1) return nullptr;
 
-  struct ifreq ifr;
-  memset(&ifr, 0, sizeof(ifr));
-  ifr.ifr_ifindex = ifindex;
-
-  int rc = ioctl(s, SIOCGIFNAME, &ifr);
-  ErrnoRestorer errno_restorer;
-  close(s);
-  return (rc == -1) ? nullptr : strncpy(ifname, ifr.ifr_name, IFNAMSIZ);
+  ifreq ifr = {.ifr_ifindex = static_cast<int>(ifindex)};
+  return (ioctl(s.get(), SIOCGIFNAME, &ifr) == -1) ? nullptr
+                                                   : strncpy(ifname, ifr.ifr_name, IFNAMSIZ);
 }
 
 unsigned if_nametoindex(const char* ifname) {
-  int s = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0);
-  if (s == -1) return 0;
+  ScopedFd s(socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0));
+  if (s.get() == -1) return 0;
 
-  struct ifreq ifr;
-  memset(&ifr, 0, sizeof(ifr));
+  ifreq ifr = {};
   strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
   ifr.ifr_name[IFNAMSIZ - 1] = 0;
-
-  int rc = ioctl(s, SIOCGIFINDEX, &ifr);
-  ErrnoRestorer errno_restorer;
-  close(s);
-  return (rc == -1) ? 0 : ifr.ifr_ifindex;
+  return (ioctl(s.get(), SIOCGIFINDEX, &ifr) == -1) ? 0 : ifr.ifr_ifindex;
 }
 
 struct if_list {
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index 1dc1066..03af2d9 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -54,31 +54,12 @@
 void __init_user_desc(struct user_desc*, bool, void*);
 #endif
 
-// This code is used both by each new pthread and the code that initializes the main thread.
-__attribute__((no_stack_protector))
-void __init_tcb(bionic_tcb* tcb, pthread_internal_t* thread) {
-#ifdef TLS_SLOT_SELF
-  // On x86, slot 0 must point to itself so code can read the thread pointer by
-  // loading %fs:0 or %gs:0.
-  tcb->tls_slot(TLS_SLOT_SELF) = &tcb->tls_slot(TLS_SLOT_SELF);
-#endif
-  tcb->tls_slot(TLS_SLOT_THREAD_ID) = thread;
-}
-
 __attribute__((no_stack_protector))
 void __init_tcb_stack_guard(bionic_tcb* tcb) {
   // GCC looks in the TLS for the stack guard on x86, so copy it there from our global.
   tcb->tls_slot(TLS_SLOT_STACK_GUARD) = reinterpret_cast<void*>(__stack_chk_guard);
 }
 
-__attribute__((no_stack_protector))
-void __init_tcb_dtv(bionic_tcb* tcb) {
-  // Initialize the DTV slot to a statically-allocated empty DTV. The first
-  // access to a dynamic TLS variable allocates a new DTV.
-  static const TlsDtv zero_dtv = {};
-  __set_tcb_dtv(tcb, const_cast<TlsDtv*>(&zero_dtv));
-}
-
 void __init_bionic_tls_ptrs(bionic_tcb* tcb, bionic_tls* tls) {
   tcb->thread()->bionic_tls = tls;
   tcb->tls_slot(TLS_SLOT_BIONIC_TLS) = tls;
diff --git a/libc/bionic/system_property_set.cpp b/libc/bionic/system_property_set.cpp
index c508db1..e981a58 100644
--- a/libc/bionic/system_property_set.cpp
+++ b/libc/bionic/system_property_set.cpp
@@ -46,6 +46,7 @@
 
 #include "private/bionic_defs.h"
 #include "private/bionic_macros.h"
+#include "private/ScopedFd.h"
 
 static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
 static const char* kServiceVersionPropertyName = "ro.property_service.version";
@@ -53,8 +54,8 @@
 class PropertyServiceConnection {
  public:
   PropertyServiceConnection() : last_error_(0) {
-    socket_ = ::socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
-    if (socket_ == -1) {
+    socket_.reset(::socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0));
+    if (socket_.get() == -1) {
       last_error_ = errno;
       return;
     }
@@ -66,15 +67,15 @@
     addr.sun_family = AF_LOCAL;
     socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
 
-    if (TEMP_FAILURE_RETRY(connect(socket_, reinterpret_cast<sockaddr*>(&addr), alen)) == -1) {
+    if (TEMP_FAILURE_RETRY(connect(socket_.get(),
+                                   reinterpret_cast<sockaddr*>(&addr), alen)) == -1) {
       last_error_ = errno;
-      close(socket_);
-      socket_ = -1;
+      socket_.reset();
     }
   }
 
   bool IsValid() {
-    return socket_ != -1;
+    return socket_.get() != -1;
   }
 
   int GetLastError() {
@@ -82,18 +83,12 @@
   }
 
   bool RecvInt32(int32_t* value) {
-    int result = TEMP_FAILURE_RETRY(recv(socket_, value, sizeof(*value), MSG_WAITALL));
+    int result = TEMP_FAILURE_RETRY(recv(socket_.get(), value, sizeof(*value), MSG_WAITALL));
     return CheckSendRecvResult(result, sizeof(*value));
   }
 
   int socket() {
-    return socket_;
-  }
-
-  ~PropertyServiceConnection() {
-    if (socket_ != -1) {
-      close(socket_);
-    }
+    return socket_.get();
   }
 
  private:
@@ -109,7 +104,7 @@
     return last_error_ == 0;
   }
 
-  int socket_;
+  ScopedFd socket_;
   int last_error_;
 
   friend class SocketWriter;
diff --git a/libc/dns/net/getaddrinfo.c b/libc/dns/net/getaddrinfo.c
index 4e1aa61..d0c11d2 100644
--- a/libc/dns/net/getaddrinfo.c
+++ b/libc/dns/net/getaddrinfo.c
@@ -470,7 +470,7 @@
 			break;
 		}
 
-		struct addrinfo* ai = calloc(1, sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
+		ai = calloc(1, sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
 		if (ai == NULL) {
 			break;
 		}
diff --git a/libc/include/fcntl.h b/libc/include/fcntl.h
index 1c5d64c..c45c91f 100644
--- a/libc/include/fcntl.h
+++ b/libc/include/fcntl.h
@@ -26,9 +26,14 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _FCNTL_H
+#pragma once
 #define _FCNTL_H
 
+/**
+ * @file fcntl.h
+ * @brief File control operations.
+ */
+
 #include <sys/cdefs.h>
 #include <sys/types.h>
 #include <linux/fadvise.h>
@@ -48,47 +53,180 @@
 
 #ifdef __LP64__
 /* LP64 kernels don't have F_*64 defines because their flock is 64-bit. */
+/** Flag for flock(). */
 #define F_GETLK64  F_GETLK
+/** Flag for flock(). */
 #define F_SETLK64  F_SETLK
+/** Flag for flock(). */
 #define F_SETLKW64 F_SETLKW
 #endif
 
+/** Flag for open(). */
 #define O_ASYNC FASYNC
+/** Flag for open(). */
 #define O_RSYNC O_SYNC
 
 #if __ANDROID_API__ >= __ANDROID_API_L__
+/** Flag for splice(). */
 #define SPLICE_F_MOVE 1
+/** Flag for splice(). */
 #define SPLICE_F_NONBLOCK 2
+/** Flag for splice(). */
 #define SPLICE_F_MORE 4
+/** Flag for splice(). */
 #define SPLICE_F_GIFT 8
 #endif
 
 #if __ANDROID_API__ >= __ANDROID_API_O__
+/** Flag for sync_file_range(). */
 #define SYNC_FILE_RANGE_WAIT_BEFORE 1
+/** Flag for sync_file_range(). */
 #define SYNC_FILE_RANGE_WRITE 2
+/** Flag for sync_file_range(). */
 #define SYNC_FILE_RANGE_WAIT_AFTER 4
 #endif
 
+/**
+ * [creat(2)](http://man7.org/linux/man-pages/man2/creat.2.html)
+ * creates a file.
+ *
+ * Returns a new file descriptor on success and returns -1 and sets `errno` on
+ * failure.
+ */
 int creat(const char* __path, mode_t __mode);
+/** See creat(). */
 int creat64(const char* __path, mode_t __mode) __INTRODUCED_IN(21);
+
+/**
+ * [openat(2)](http://man7.org/linux/man-pages/man2/openat.2.html)
+ * opens (and possibly creates) a file.
+ *
+ * Returns a new file descriptor on success and returns -1 and sets `errno` on
+ * failure.
+ */
 int openat(int __dir_fd, const char* __path, int __flags, ...);
+/** See openat(). */
 int openat64(int __dir_fd, const char* __path, int __flags, ...) __INTRODUCED_IN(21);
+
+/**
+ * [open(2)](http://man7.org/linux/man-pages/man2/open.2.html)
+ * opens (and possibly creates) a file.
+ *
+ * Returns a new file descriptor on success and returns -1 and sets `errno` on
+ * failure.
+ */
 int open(const char* __path, int __flags, ...);
+/** See open(). */
 int open64(const char* __path, int __flags, ...) __INTRODUCED_IN(21);
+
+/**
+ * [splice(2)](http://man7.org/linux/man-pages/man2/splice.2.html)
+ * splices data to/from a pipe.
+ *
+ * Valid flags are `SPLICE_F_MOVE`, `SPLICE_F_NONBLOCK`, `SPLICE_F_MORE`, and
+ * `SPLICE_F_GIFT`.
+ *
+ * Returns the number of bytes spliced on success and returns -1 and sets
+ * `errno` on failure.
+ *
+ * Available since API level 21.
+ */
 ssize_t splice(int __in_fd, off64_t* __in_offset, int __out_fd, off64_t* __out_offset, size_t __length, unsigned int __flags) __INTRODUCED_IN(21);
+
+/**
+ * [tee(2)](http://man7.org/linux/man-pages/man2/tee.2.html)
+ * duplicates data from one pipe to another.
+ *
+ * Valid flags are `SPLICE_F_MOVE`, `SPLICE_F_NONBLOCK`, `SPLICE_F_MORE`, and
+ * `SPLICE_F_GIFT`.
+ *
+ * Returns the number of bytes duplicated on success and returns -1 and sets
+ * `errno` on failure.
+ *
+ * Available since API level 21.
+ */
 ssize_t tee(int __in_fd, int __out_fd, size_t __length, unsigned int __flags) __INTRODUCED_IN(21);
+
+/**
+ * [vmsplice(2)](http://man7.org/linux/man-pages/man2/vmsplice.2.html)
+ * splices data to/from a pipe.
+ *
+ * Valid flags are `SPLICE_F_MOVE`, `SPLICE_F_NONBLOCK`, `SPLICE_F_MORE`, and
+ * `SPLICE_F_GIFT`.
+ *
+ * Returns the number of bytes spliced on success and returns -1 and sets
+ * `errno` on failure.
+ *
+ * Available since API level 21.
+ */
 ssize_t vmsplice(int __fd, const struct iovec* __iov, size_t __count, unsigned int __flags) __INTRODUCED_IN(21);
 
+/**
+ * [fallocate(2)](http://man7.org/linux/man-pages/man2/fallocate.2.html)
+ * is a Linux-specific extension of posix_fallocate().
+ *
+ * Valid flags are `FALLOC_FL_KEEP_SIZE`, `FALLOC_FL_PUNCH_HOLE`,
+ * `FALLOC_FL_NO_HIDE_STALE`, `FALLOC_FL_COLLAPSE_RANGE`,
+ * `FALLOC_FL_ZERO_RANGE`, `FALLOC_FL_INSERT_RANGE`, and
+ * `FALLOC_FL_UNSHARE_RANGE`.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ *
+ * Available since API level 21.
+ */
 int fallocate(int __fd, int __mode, off_t __offset, off_t __length) __RENAME_IF_FILE_OFFSET64(fallocate64) __INTRODUCED_IN(21);
+/** See fallocate(). */
 int fallocate64(int __fd, int __mode, off64_t __offset, off64_t __length) __INTRODUCED_IN(21);
+
+/**
+ * [posix_fadvise(2)](http://man7.org/linux/man-pages/man2/posix_fadvise.2.html)
+ * declares an expected access pattern for file data.
+ *
+ * Valid flags are `POSIX_FADV_NORMAL`, `POSIX_FADV_RANDOM`,
+ * `POSIX_FADV_SEQUENTIAL`, `POSIX_FADV_WILLNEED`, `POSIX_FADV_DONTNEED`,
+ * and `POSIX_FADV_NOREUSE`.
+ *
+ * Returns 0 on success and returns an error number on failure.
+ *
+ * Available since API level 21.
+ */
 int posix_fadvise(int __fd, off_t __offset, off_t __length, int __advice) __RENAME_IF_FILE_OFFSET64(posix_fadvise64) __INTRODUCED_IN(21);
+/** See posix_fadvise(). */
 int posix_fadvise64(int __fd, off64_t __offset, off64_t __length, int __advice) __INTRODUCED_IN(21);
+
+/**
+ * [posix_fallocate(2)](http://man7.org/linux/man-pages/man2/posix_fallocate.2.html)
+ * allocates file space.
+ *
+ * Returns 0 on success and returns an error number on failure.
+ *
+ * Available since API level 21.
+ */
 int posix_fallocate(int __fd, off_t __offset, off_t __length) __RENAME_IF_FILE_OFFSET64(posix_fallocate64) __INTRODUCED_IN(21);
+/** See posix_fallocate(). */
 int posix_fallocate64(int __fd, off64_t __offset, off64_t __length) __INTRODUCED_IN(21);
 
 #if defined(__USE_GNU)
-ssize_t readahead(int __fd, off64_t __offset, size_t __length) __INTRODUCED_IN(16);
+
+/**
+ * [readahead(2)](http://man7.org/linux/man-pages/man2/readahead.2.html)
+ * initiates readahead for the given file.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+ssize_t readahead(int __fd, off64_t __offset, size_t __length);
+
+/**
+ * [sync_file_range(2)](http://man7.org/linux/man-pages/man2/sync_file_range.2.html)
+ * syncs part of a file with disk.
+ *
+ * Valid flags are `SYNC_FILE_RANGE_WAIT_BEFORE`, `SYNC_FILE_RANGE_WRITE`, and
+ * `SYNC_FILE_RANGE_WAIT_AFTER`.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
 int sync_file_range(int __fd, off64_t __offset, off64_t __length, unsigned int __flags) __INTRODUCED_IN(26);
+
 #endif
 
 #if defined(__BIONIC_INCLUDE_FORTIFY_HEADERS)
@@ -96,5 +234,3 @@
 #endif
 
 __END_DECLS
-
-#endif
diff --git a/libc/include/search.h b/libc/include/search.h
index 97fdeda..7a75404 100644
--- a/libc/include/search.h
+++ b/libc/include/search.h
@@ -6,30 +6,56 @@
  * $FreeBSD: release/9.0.0/include/search.h 105250 2002-10-16 14:29:23Z robert $
  */
 
-#ifndef _SEARCH_H_
-#define _SEARCH_H_
+#pragma once
+
+/**
+ * @file search.h
+ * @brief Queues, hash tables, trees, and linear array searches.
+ */
 
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
+/** See hsearch()/hsearch_r(). */
 typedef enum {
   FIND,
   ENTER
 } ACTION;
 
+/** See hsearch()/hsearch_r(). */
 typedef struct entry {
+  /** The string key. */
   char* key;
+  /** The associated data. */
   void* data;
 } ENTRY;
 
+/**
+ * Constants given to the twalk() visitor.
+ * Note that the constant names are misleading.
+ */
 typedef enum {
+  /**
+   * If this is the first visit to a non-leaf node.
+   * Use this for *preorder* traversal.
+   */
   preorder,
+  /**
+   * If this is the second visit to a non-leaf node.
+   * Use this for *inorder* traversal.
+   */
   postorder,
+  /**
+   * If this is the third visit to a non-leaf node.
+   * Use this for *postorder* traversal.
+   */
   endorder,
+  /** If this is the first and only visit to a leaf node. */
   leaf
 } VISIT;
 
 #if defined(__USE_BSD) || defined(__USE_GNU)
+/** The hash table type for hcreate_r()/hdestroy_r()/hsearch_r(). */
 struct hsearch_data {
   struct __hsearch* __hsearch;
 };
@@ -37,30 +63,157 @@
 
 __BEGIN_DECLS
 
+/**
+ * [insque(3)](http://man7.org/linux/man-pages/man3/insque.3.html) inserts
+ * an item in a queue (an intrusive doubly-linked list).
+ *
+ * Available since API level 21.
+ */
 void insque(void* __element, void* __previous) __INTRODUCED_IN(21);
+
+/**
+ * [remque(3)](http://man7.org/linux/man-pages/man3/remque.3.html) removes
+ * an item from a queue (an intrusive doubly-linked list).
+ *
+ * Available since API level 21.
+ */
 void remque(void* __element) __INTRODUCED_IN(21);
 
-int hcreate(size_t) __INTRODUCED_IN(28);
+/**
+ * [hcreate(3)](http://man7.org/linux/man-pages/man3/hcreate.3.html)
+ * initializes the global hash table, with space for at least `__n` elements.
+ *
+ * See hcreate_r() if you need more than one hash table.
+ *
+ * Returns *non-zero* on success and returns 0 and sets `errno` on failure.
+ *
+ * Available since API level 28.
+ */
+int hcreate(size_t __n) __INTRODUCED_IN(28);
+
+/**
+ * [hdestroy(3)](http://man7.org/linux/man-pages/man3/hdestroy.3.html) destroys
+ * the global hash table.
+ *
+ * See hdestroy_r() if you need more than one hash table.
+ *
+ * Available since API level 28.
+ */
 void hdestroy(void) __INTRODUCED_IN(28);
-ENTRY* hsearch(ENTRY, ACTION) __INTRODUCED_IN(28);
+
+/**
+ * [hsearch(3)](http://man7.org/linux/man-pages/man3/hsearch.3.html) finds or
+ * inserts `__entry` in the global hash table, based on `__action`.
+ *
+ * See hsearch_r() if you need more than one hash table.
+ *
+ * Returns a pointer to the entry on success, and returns NULL and sets
+ * `errno` on failure.
+ *
+ * Available since API level 28.
+ */
+ENTRY* hsearch(ENTRY __entry, ACTION __action) __INTRODUCED_IN(28);
 
 #if defined(__USE_BSD) || defined(__USE_GNU)
-int hcreate_r(size_t, struct hsearch_data*) __INTRODUCED_IN(28);
-void hdestroy_r(struct hsearch_data*) __INTRODUCED_IN(28);
-int hsearch_r(ENTRY, ACTION, ENTRY**, struct hsearch_data*) __INTRODUCED_IN(28);
+
+/**
+ * [hcreate_r(3)](http://man7.org/linux/man-pages/man3/hcreate_r.3.html)
+ * initializes a hash table `__table` with space for at least `__n` elements.
+ *
+ * Returns *non-zero* on success and returns 0 and sets `errno` on failure.
+ *
+ * Available since API level 28.
+ */
+int hcreate_r(size_t __n, struct hsearch_data* __table) __INTRODUCED_IN(28);
+
+/**
+ * [hdestroy_r(3)](http://man7.org/linux/man-pages/man3/hdestroy_r.3.html) destroys
+ * the hash table `__table`.
+ *
+ * Available since API level 28.
+ */
+void hdestroy_r(struct hsearch_data* __table) __INTRODUCED_IN(28);
+
+/**
+ * [hsearch_r(3)](http://man7.org/linux/man-pages/man3/hsearch_r.3.html) finds or
+ * inserts `__entry` in the hash table `__table`, based on `__action`.
+ *
+ * Returns *non-zero* on success and returns 0 and sets `errno` on failure.
+ * A pointer to the entry is returned in `*__result`.
+ *
+ * Available since API level 28.
+ */
+int hsearch_r(ENTRY __entry, ACTION __action, ENTRY** __result, struct hsearch_data* __table) __INTRODUCED_IN(28);
+
 #endif
 
-void* lfind(const void* __key, const void* __base, size_t* __count, size_t __size, int (*__comparator)(const void*, const void*))
-  __INTRODUCED_IN(21);
-void* lsearch(const void* __key, void* __base, size_t* __count, size_t __size, int (*__comparator)(const void*, const void*))
-  __INTRODUCED_IN(21);
+/**
+ * [lfind(3)](http://man7.org/linux/man-pages/man3/lfind.3.html) brute-force
+ * searches the unsorted array `__array` (of `__count` items each of size `__size`)
+ * for `__key`, using `__comparator`.
+ *
+ * See bsearch() if you have a sorted array.
+ *
+ * Returns a pointer to the matching element on success, or NULL on failure.
+ *
+ * Available since API level 21.
+ */
+void* lfind(const void* __key, const void* __array, size_t* __count, size_t __size, int (*__comparator)(const void*, const void*)) __INTRODUCED_IN(21);
 
-void* tdelete(const void* __key, void** __root_ptr, int (*__comparator)(const void*, const void*)) __INTRODUCED_IN(16);
-void tdestroy(void* __root, void (*__free_fn)(void*)) __INTRODUCED_IN(16);
-void* tfind(const void* __key, void* const* __root_ptr, int (*__comparator)(const void*, const void*)) __INTRODUCED_IN(16);
-void* tsearch(const void* __key, void** __root_ptr, int (*__comparator)(const void*, const void*)) __INTRODUCED_IN(16);
-void twalk(const void* __root, void (*__visitor)(const void*, VISIT, int)) __INTRODUCED_IN(21);
+/**
+ * [lsearch(3)](http://man7.org/linux/man-pages/man3/lsearch.3.html) brute-force
+ * searches the unsorted array `__array` (of `__count` items each of size `__size`)
+ * for `__key`, using `__comparator`.
+ *
+ * Unlike lfind(), on failure lsearch() will *insert* `__key` at the end of
+ * `__array` and increment `*__count`.
+ *
+ * Returns a pointer to the matching element on success, or to the newly-added
+ * element on failure.
+ *
+ * Available since API level 21.
+ */
+void* lsearch(const void* __key, void* __array, size_t* __count, size_t __size, int (*__comparator)(const void*, const void*)) __INTRODUCED_IN(21);
+
+/**
+ * [tdelete(3)](http://man7.org/linux/man-pages/man3/tdelete.3.html) searches
+ * for and removes an element in the tree `*__root_ptr`. The search is performed
+ * using `__comparator`.
+ *
+ * Returns a pointer to the parent of the deleted node, or NULL on failure.
+ */
+void* tdelete(const void* __key, void** __root_ptr, int (*__comparator)(const void*, const void*));
+
+/**
+ * [tdestroy(3)](http://man7.org/linux/man-pages/man3/tdestroy.3.html) destroys
+ * the hash table `__root` using `__free_fn` on each node.
+ */
+void tdestroy(void* __root, void (*__free_fn)(void*));
+
+/**
+ * [tfind(3)](http://man7.org/linux/man-pages/man3/tfind.3.html) searches
+ * for an element in the tree `*__root_ptr`. The search is performed using
+ * `__comparator`.
+ *
+ * Returns a pointer to the matching node, or NULL on failure.
+ */
+void* tfind(const void* __key, void* const* __root_ptr, int (*__comparator)(const void*, const void*));
+
+/**
+ * [tsearch(3)](http://man7.org/linux/man-pages/man3/tsearch.3.html) searches
+ * for an element in the tree `*__root_ptr`. The search is performed using
+ * `__comparator`.
+ *
+ * Unlike tfind(), on failure tsearch() will *insert* `__key` into the tree.
+ *
+ * Returns a pointer to the matching node, or to the newly-added node.
+ */
+void* tsearch(const void* __key, void** __root_ptr, int (*__comparator)(const void*, const void*));
+
+/**
+ * [twalk(3)](http://man7.org/linux/man-pages/man3/twalk.3.html) calls
+ * `__visitor` on every node in the tree.
+ */
+void twalk(const void* __root, void (*__visitor)(const void*, VISIT, int));
 
 __END_DECLS
-
-#endif
diff --git a/libc/include/sys/xattr.h b/libc/include/sys/xattr.h
index d15b3fc..dc58026 100644
--- a/libc/include/sys/xattr.h
+++ b/libc/include/sys/xattr.h
@@ -26,8 +26,12 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _SYS_XATTR_H_
-#define _SYS_XATTR_H_
+#pragma once
+
+/**
+ * @file sys/xattr.h
+ * @brief Extended attribute functions.
+ */
 
 #include <linux/xattr.h>
 #include <sys/cdefs.h>
@@ -35,25 +39,120 @@
 
 __BEGIN_DECLS
 
-int fsetxattr(int __fd, const char* __name, const void* __value, size_t __size, int __flags)
-  __INTRODUCED_IN(16);
-int setxattr(const char* __path, const char* __name, const void* __value, size_t __size, int __flags)
-  __INTRODUCED_IN(16);
-int lsetxattr(const char* __path, const char* __name, const void* __value, size_t __size, int __flags)
-  __INTRODUCED_IN(16);
+/**
+ * [fsetxattr(2)](http://man7.org/linux/man-pages/man2/fsetxattr.2.html)
+ * sets an extended attribute on the file referred to by the given file
+ * descriptor.
+ *
+ * Valid flags are `XATTR_CREATE` and `XATTR_REPLACE`.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int fsetxattr(int __fd, const char* __name, const void* __value, size_t __size, int __flags);
 
-ssize_t fgetxattr(int __fd, const char* __name, void* __value, size_t __size) __INTRODUCED_IN(16);
-ssize_t getxattr(const char* __path, const char* __name, void* __value, size_t __size) __INTRODUCED_IN(16);
-ssize_t lgetxattr(const char* __path, const char* __name, void* __value, size_t __size) __INTRODUCED_IN(16);
+/**
+ * [setxattr(2)](http://man7.org/linux/man-pages/man2/setxattr.2.html)
+ * sets an extended attribute on the file referred to by the given path.
+ *
+ * Valid flags are `XATTR_CREATE` and `XATTR_REPLACE`.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int setxattr(const char* __path, const char* __name, const void* __value, size_t __size, int __flags);
 
-ssize_t listxattr(const char* __path, char* __list, size_t __size) __INTRODUCED_IN(16);
-ssize_t llistxattr(const char* __path, char* __list, size_t __size) __INTRODUCED_IN(16);
-ssize_t flistxattr(int __fd, char* __list, size_t __size) __INTRODUCED_IN(16);
+/**
+ * [lsetxattr(2)](http://man7.org/linux/man-pages/man2/lsetxattr.2.html)
+ * sets an extended attribute on the file referred to by the given path, which
+ * is the link itself rather than its target in the case of a symbolic link.
+ *
+ * Valid flags are `XATTR_CREATE` and `XATTR_REPLACE`.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int lsetxattr(const char* __path, const char* __name, const void* __value, size_t __size, int __flags);
 
-int removexattr(const char* __path, const char* __name) __INTRODUCED_IN(16);
-int lremovexattr(const char* __path, const char* __name) __INTRODUCED_IN(16);
-int fremovexattr(int __fd, const char* __name) __INTRODUCED_IN(16);
+/**
+ * [fgetxattr(2)](http://man7.org/linux/man-pages/man2/fgetxattr.2.html)
+ * gets an extended attribute on the file referred to by the given file
+ * descriptor.
+ *
+ * Returns the non-negative length of the value on success, or
+ * returns -1 and sets `errno` on failure.
+ */
+ssize_t fgetxattr(int __fd, const char* __name, void* __value, size_t __size);
+
+/**
+ * [getxattr(2)](http://man7.org/linux/man-pages/man2/getxattr.2.html)
+ * gets an extended attribute on the file referred to by the given path.
+ *
+ * Returns the non-negative length of the value on success, or
+ * returns -1 and sets `errno` on failure.
+ */
+ssize_t getxattr(const char* __path, const char* __name, void* __value, size_t __size);
+
+/**
+ * [lgetxattr(2)](http://man7.org/linux/man-pages/man2/lgetxattr.2.html)
+ * gets an extended attribute on the file referred to by the given path, which
+ * is the link itself rather than its target in the case of a symbolic link.
+ *
+ * Returns the non-negative length of the value on success, or
+ * returns -1 and sets `errno` on failure.
+ */
+ssize_t lgetxattr(const char* __path, const char* __name, void* __value, size_t __size);
+
+/**
+ * [flistxattr(2)](http://man7.org/linux/man-pages/man2/flistxattr.2.html)
+ * lists the extended attributes on the file referred to by the given file
+ * descriptor.
+ *
+ * Returns the non-negative length of the list on success, or
+ * returns -1 and sets `errno` on failure.
+ */
+ssize_t flistxattr(int __fd, char* __list, size_t __size);
+
+/**
+ * [listxattr(2)](http://man7.org/linux/man-pages/man2/listxattr.2.html)
+ * lists the extended attributes on the file referred to by the given path.
+ *
+ * Returns the non-negative length of the list on success, or
+ * returns -1 and sets `errno` on failure.
+ */
+ssize_t listxattr(const char* __path, char* __list, size_t __size);
+
+/**
+ * [llistxattr(2)](http://man7.org/linux/man-pages/man2/llistxattr.2.html)
+ * lists the extended attributes on the file referred to by the given path, which
+ * is the link itself rather than its target in the case of a symbolic link.
+ *
+ * Returns the non-negative length of the list on success, or
+ * returns -1 and sets `errno` on failure.
+ */
+ssize_t llistxattr(const char* __path, char* __list, size_t __size);
+
+/**
+ * [fremovexattr(2)](http://man7.org/linux/man-pages/man2/fremovexattr.2.html)
+ * removes an extended attribute on the file referred to by the given file
+ * descriptor.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int fremovexattr(int __fd, const char* __name);
+
+/**
+ * [lremovexattr(2)](http://man7.org/linux/man-pages/man2/lremovexattr.2.html)
+ * removes an extended attribute on the file referred to by the given path, which
+ * is the link itself rather than its target in the case of a symbolic link.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int lremovexattr(const char* __path, const char* __name);
+
+/**
+ * [removexattr(2)](http://man7.org/linux/man-pages/man2/removexattr.2.html)
+ * removes an extended attribute on the file referred to by the given path.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int removexattr(const char* __path, const char* __name);
 
 __END_DECLS
-
-#endif
diff --git a/libc/malloc_debug/PointerData.cpp b/libc/malloc_debug/PointerData.cpp
index ec7e42d..b1e28b7 100644
--- a/libc/malloc_debug/PointerData.cpp
+++ b/libc/malloc_debug/PointerData.cpp
@@ -554,7 +554,7 @@
   return pointers_.count(pointer) != 0;
 }
 
-void PointerData::DumpLiveToFile(FILE* fp) {
+void PointerData::DumpLiveToFile(int fd) {
   std::vector<ListInfoType> list;
 
   std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
@@ -566,13 +566,13 @@
     total_memory += info.size * info.num_allocations;
   }
 
-  fprintf(fp, "Total memory: %zu\n", total_memory);
-  fprintf(fp, "Allocation records: %zd\n", list.size());
-  fprintf(fp, "Backtrace size: %zu\n", g_debug->config().backtrace_frames());
-  fprintf(fp, "\n");
+  dprintf(fd, "Total memory: %zu\n", total_memory);
+  dprintf(fd, "Allocation records: %zd\n", list.size());
+  dprintf(fd, "Backtrace size: %zu\n", g_debug->config().backtrace_frames());
+  dprintf(fd, "\n");
 
   for (const auto& info : list) {
-    fprintf(fp, "z %d  sz %8zu  num    %zu  bt", (info.zygote_child_alloc) ? 1 : 0, info.size,
+    dprintf(fd, "z %d  sz %8zu  num    %zu  bt", (info.zygote_child_alloc) ? 1 : 0, info.size,
             info.num_allocations);
     FrameInfoType* frame_info = info.frame_info;
     if (frame_info != nullptr) {
@@ -580,22 +580,22 @@
         if (frame_info->frames[i] == 0) {
           break;
         }
-        fprintf(fp, " %" PRIxPTR, frame_info->frames[i]);
+        dprintf(fd, " %" PRIxPTR, frame_info->frames[i]);
       }
     }
-    fprintf(fp, "\n");
+    dprintf(fd, "\n");
     if (info.backtrace_info != nullptr) {
-      fprintf(fp, "  bt_info");
+      dprintf(fd, "  bt_info");
       for (const auto& frame : *info.backtrace_info) {
-        fprintf(fp, " {");
+        dprintf(fd, " {");
         if (frame.map_info != nullptr && !frame.map_info->name.empty()) {
-          fprintf(fp, "\"%s\"", frame.map_info->name.c_str());
+          dprintf(fd, "\"%s\"", frame.map_info->name.c_str());
         } else {
-          fprintf(fp, "\"\"");
+          dprintf(fd, "\"\"");
         }
-        fprintf(fp, " %" PRIx64, frame.rel_pc);
+        dprintf(fd, " %" PRIx64, frame.rel_pc);
         if (frame.function_name.empty()) {
-          fprintf(fp, " \"\" 0}");
+          dprintf(fd, " \"\" 0}");
         } else {
           char* demangled_name = __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr,
                                                 nullptr);
@@ -605,11 +605,11 @@
           } else {
             name = frame.function_name.c_str();
           }
-          fprintf(fp, " \"%s\" %" PRIx64 "}", name, frame.function_offset);
+          dprintf(fd, " \"%s\" %" PRIx64 "}", name, frame.function_offset);
           free(demangled_name);
         }
       }
-      fprintf(fp, "\n");
+      dprintf(fd, "\n");
     }
   }
 }
diff --git a/libc/malloc_debug/PointerData.h b/libc/malloc_debug/PointerData.h
index c7958f3..78f0ed8 100644
--- a/libc/malloc_debug/PointerData.h
+++ b/libc/malloc_debug/PointerData.h
@@ -152,7 +152,7 @@
 
   static void GetAllocList(std::vector<ListInfoType>* list);
   static void LogLeaks();
-  static void DumpLiveToFile(FILE* fp);
+  static void DumpLiveToFile(int fd);
 
   static void GetInfo(uint8_t** info, size_t* overall_size, size_t* info_size, size_t* total_memory,
                       size_t* backtrace_size);
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index c030d54..3c0e630 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -785,16 +785,23 @@
   if (DebugCallsDisabled() || !g_debug->TrackPointers()) {
     return g_dispatch->malloc_info(options, fp);
   }
+
+  // Make sure any pending output is written to the file.
+  fflush(fp);
+
   ScopedConcurrentLock lock;
   ScopedDisableDebugCalls disable;
 
-  MallocXmlElem root(fp, "malloc", "version=\"debug-malloc-1\"");
+  // Avoid any issues where allocations are made that will be freed
+  // in the fclose.
+  int fd = fileno(fp);
+  MallocXmlElem root(fd, "malloc", "version=\"debug-malloc-1\"");
   std::vector<ListInfoType> list;
   PointerData::GetAllocList(&list);
 
   size_t alloc_num = 0;
   for (size_t i = 0; i < list.size(); i++) {
-    MallocXmlElem alloc(fp, "allocation", "nr=\"%zu\"", alloc_num);
+    MallocXmlElem alloc(fd, "allocation", "nr=\"%zu\"", alloc_num);
 
     size_t total = 1;
     size_t size = list[i].size;
@@ -802,8 +809,8 @@
       i++;
       total++;
     }
-    MallocXmlElem(fp, "size").Contents("%zu", list[i].size);
-    MallocXmlElem(fp, "total").Contents("%zu", total);
+    MallocXmlElem(fd, "size").Contents("%zu", list[i].size);
+    MallocXmlElem(fd, "total").Contents("%zu", total);
     alloc_num++;
   }
   return 0;
@@ -905,25 +912,28 @@
 
 static std::mutex g_dump_lock;
 
-static void write_dump(FILE* fp) {
-  fprintf(fp, "Android Native Heap Dump v1.2\n\n");
+static void write_dump(int fd) {
+  dprintf(fd, "Android Native Heap Dump v1.2\n\n");
 
   std::string fingerprint = android::base::GetProperty("ro.build.fingerprint", "unknown");
-  fprintf(fp, "Build fingerprint: '%s'\n\n", fingerprint.c_str());
+  dprintf(fd, "Build fingerprint: '%s'\n\n", fingerprint.c_str());
 
-  PointerData::DumpLiveToFile(fp);
+  PointerData::DumpLiveToFile(fd);
 
-  fprintf(fp, "MAPS\n");
+  dprintf(fd, "MAPS\n");
   std::string content;
   if (!android::base::ReadFileToString("/proc/self/maps", &content)) {
-    fprintf(fp, "Could not open /proc/self/maps\n");
+    dprintf(fd, "Could not open /proc/self/maps\n");
   } else {
-    fprintf(fp, "%s", content.c_str());
+    dprintf(fd, "%s", content.c_str());
   }
-  fprintf(fp, "END\n");
+  dprintf(fd, "END\n");
 }
 
 bool debug_write_malloc_leak_info(FILE* fp) {
+  // Make sure any pending output is written to the file.
+  fflush(fp);
+
   ScopedConcurrentLock lock;
   ScopedDisableDebugCalls disable;
 
@@ -933,7 +943,8 @@
     return false;
   }
 
-  write_dump(fp);
+  write_dump(fileno(fp));
+
   return true;
 }
 
@@ -943,13 +954,13 @@
 
   std::lock_guard<std::mutex> guard(g_dump_lock);
 
-  FILE* fp = fopen(file_name, "w+e");
-  if (fp == nullptr) {
+  int fd = open(file_name, O_RDWR | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0644);
+  if (fd == -1) {
     error_log("Unable to create file: %s", file_name);
     return;
   }
 
   error_log("Dumping to file: %s\n", file_name);
-  write_dump(fp);
-  fclose(fp);
+  write_dump(fd);
+  close(fd);
 }
diff --git a/libc/malloc_debug/tests/malloc_debug_system_tests.cpp b/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
index 0716758..67bb8d9 100644
--- a/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
@@ -37,6 +37,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
@@ -174,6 +175,7 @@
 }
 
 static void FindStrings(pid_t pid, std::vector<const char*> match_strings,
+                        std::vector<const char*> no_match_strings = std::vector<const char*>{},
                         time_t timeout_seconds = kTimeoutSeconds) {
   std::string log_str;
   time_t start = time(nullptr);
@@ -181,12 +183,18 @@
   while (true) {
     GetLogStr(pid, &log_str);
     found_all = true;
+    // Look for the expected strings.
     for (auto str : match_strings) {
       if (log_str.find(str) == std::string::npos) {
         found_all = false;
         break;
       }
     }
+
+    // Verify the unexpected strings are not present.
+    for (auto str : no_match_strings) {
+      ASSERT_TRUE(log_str.find(str) == std::string::npos) << "Unexpectedly found '" << str << "' in log output:\n" << log_str;
+    }
     if (found_all) {
       return;
     }
@@ -194,7 +202,7 @@
       break;
     }
   }
-  ASSERT_TRUE(found_all) << "Didn't find expected log output:\n" + log_str;
+  ASSERT_TRUE(found_all) << "Didn't find expected log output:\n" << log_str;
 }
 
 TEST(MallocTests, DISABLED_smoke) {}
@@ -464,3 +472,45 @@
         << "Found crash in log.\nLog message: " << log_str;
   }
 }
+
+TEST(MallocTests, DISABLED_write_leak_info) {
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+
+  FILE* fp = fdopen(tf.fd, "w+");
+  if (fp == nullptr) {
+    printf("Unable to create %s\n", tf.path);
+    _exit(1);
+  }
+  tf.release();
+
+  void* ptr = malloc(1000);
+  if (ptr == nullptr) {
+    printf("malloc failed\n");
+    _exit(1);
+  }
+  memset(ptr, 0, 1000);
+
+  android_mallopt(M_WRITE_MALLOC_LEAK_INFO_TO_FILE, fp, sizeof(fp));
+
+  fclose(fp);
+
+  free(ptr);
+}
+
+TEST(MallocDebugSystemTest, write_leak_info_no_header) {
+  pid_t pid;
+  ASSERT_NO_FATAL_FAILURE(Exec("MallocTests.DISABLED_write_leak_info", "verbose backtrace", &pid, 0));
+
+  ASSERT_NO_FATAL_FAILURE(FindStrings(pid, std::vector<const char*>{"malloc debug enabled"},
+
+                          std::vector<const char*>{" HAS INVALID TAG ", "USED AFTER FREE ", "UNKNOWN POINTER "}));
+}
+
+TEST(MallocDebugSystemTest, write_leak_info_header) {
+  pid_t pid;
+  ASSERT_NO_FATAL_FAILURE(Exec("MallocTests.DISABLED_write_leak_info", "verbose backtrace guard", &pid, 0));
+
+  ASSERT_NO_FATAL_FAILURE(FindStrings(pid, std::vector<const char*>{"malloc debug enabled"},
+                          std::vector<const char*>{" HAS INVALID TAG ", "USED AFTER FREE ", "UNKNOWN POINTER "}));
+}
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index 0238d10..70457b9 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -27,6 +27,8 @@
 
 #include <algorithm>
 #include <memory>
+#include <string>
+#include <string_view>
 #include <thread>
 #include <vector>
 #include <utility>
@@ -73,6 +75,9 @@
 void* debug_valloc(size_t);
 #endif
 
+bool debug_write_malloc_leak_info(FILE*);
+void debug_dump_heap(const char*);
+
 __END_DECLS
 
 constexpr char DIVIDER[] =
@@ -1302,6 +1307,10 @@
 }
 
 static std::string SanitizeHeapData(const std::string& data) {
+  if (data.empty()) {
+    return data;
+  }
+
   // Remove the map data since it's not consistent.
   std::string sanitized;
   bool skip_map_data = false;
@@ -1329,7 +1338,7 @@
       sanitized += line + '\n';
     }
   }
-  return sanitized;
+  return android::base::Trim(sanitized);
 }
 
 void MallocDebugTest::BacktraceDumpOnSignal(bool trigger_with_alloc) {
@@ -1402,9 +1411,7 @@
 z 1  sz       40  num    1  bt 300 400
 MAPS
 MAP_DATA
-END
-
-)";
+END)";
   ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -1463,9 +1470,7 @@
 z 0  sz      300  num    1  bt 100 200
 MAPS
 MAP_DATA
-END
-
-)";
+END)";
   ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -1513,9 +1518,7 @@
 z 0  sz      300  num    2  bt 100 200
 MAPS
 MAP_DATA
-END
-
-)";
+END)";
   ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -1575,9 +1578,7 @@
   bt_info {"" 100 "fake1" a} {"" 200 "fake2" 14}
 MAPS
 MAP_DATA
-END
-
-)";
+END)";
   ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -2472,15 +2473,19 @@
 TEST_F(MallocDebugTest, malloc_info_no_pointer_tracking) {
   Init("fill");
 
-  char* buffer;
-  size_t size;
-  FILE* memstream = open_memstream(&buffer, &size);
-  ASSERT_TRUE(memstream != nullptr);
-  ASSERT_EQ(0, debug_malloc_info(0, memstream));
-  ASSERT_EQ(0, fclose(memstream));
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+  FILE* fp = fdopen(tf.fd, "w+");
+  tf.release();
+  ASSERT_TRUE(fp != nullptr);
+  ASSERT_EQ(0, debug_malloc_info(0, fp));
+  ASSERT_EQ(0, fclose(fp));
+
+  std::string contents;
+  ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
 
   tinyxml2::XMLDocument doc;
-  ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buffer));
+  ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(contents.c_str()));
   auto root = doc.FirstChildElement();
   ASSERT_TRUE(root != nullptr);
   ASSERT_STREQ("malloc", root->Name());
@@ -2501,17 +2506,21 @@
   std::unique_ptr<void, decltype(debug_free)*> ptr4(debug_malloc(1200), debug_free);
   ASSERT_TRUE(ptr4.get() != nullptr);
 
-  char* buffer;
-  size_t size;
-  FILE* memstream = open_memstream(&buffer, &size);
-  ASSERT_TRUE(memstream != nullptr);
-  ASSERT_EQ(0, debug_malloc_info(0, memstream));
-  ASSERT_EQ(0, fclose(memstream));
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+  FILE* fp = fdopen(tf.fd, "w+");
+  tf.release();
+  ASSERT_TRUE(fp != nullptr);
+  ASSERT_EQ(0, debug_malloc_info(0, fp));
+  ASSERT_EQ(0, fclose(fp));
 
-  SCOPED_TRACE(testing::Message() << "Output:\n" << buffer);
+  std::string contents;
+  ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
+
+  SCOPED_TRACE(testing::Message() << "Output:\n" << contents);
 
   tinyxml2::XMLDocument doc;
-  ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buffer));
+  ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(contents.c_str()));
   auto root = doc.FirstChildElement();
   ASSERT_TRUE(root != nullptr);
   ASSERT_STREQ("malloc", root->Name());
@@ -2548,3 +2557,134 @@
   ASSERT_EQ(tinyxml2::XML_SUCCESS, alloc->FirstChildElement("total")->QueryIntText(&val));
   ASSERT_EQ(1, val);
 }
+
+static void AllocPtrsWithBacktrace(std::vector<void*>* ptrs) {
+  backtrace_fake_add(std::vector<uintptr_t> {0xf, 0xe, 0xd, 0xc});
+  void* ptr = debug_malloc(1024);
+  ASSERT_TRUE(ptr != nullptr);
+  memset(ptr, 0, 1024);
+  ptrs->push_back(ptr);
+
+  backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xbc001, 0xbc002});
+  ptr = debug_malloc(500);
+  ASSERT_TRUE(ptr != nullptr);
+  memset(ptr, 0, 500);
+  ptrs->push_back(ptr);
+
+  backtrace_fake_add(std::vector<uintptr_t> {0x104});
+  ptr = debug_malloc(100);
+  ASSERT_TRUE(ptr != nullptr);
+  memset(ptr, 0, 100);
+  ptrs->push_back(ptr);
+}
+
+static constexpr std::string_view kDumpInfo = R"(Android Native Heap Dump v1.2
+
+Build fingerprint: ''
+
+Total memory: 1624
+Allocation records: 3
+Backtrace size: 16
+
+z 0  sz     1024  num    1  bt f e d c
+z 0  sz      500  num    1  bt bc000 bc001 bc002
+z 0  sz      100  num    1  bt 104
+MAPS
+MAP_DATA
+END)";
+
+TEST_F(MallocDebugTest, debug_write_malloc_leak_info) {
+  Init("backtrace=16");
+
+  std::vector<void*> ptrs;
+  AllocPtrsWithBacktrace(&ptrs);
+
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+  close(tf.fd);
+  tf.release();
+  FILE* fp = fopen(tf.path, "w+");
+  ASSERT_TRUE(fp != nullptr);
+
+  ASSERT_TRUE(debug_write_malloc_leak_info(fp));
+
+  fclose(fp);
+
+  for (auto ptr : ptrs) {
+    debug_free(ptr);
+  }
+  ptrs.clear();
+
+  std::string expected(kDumpInfo);
+
+  std::string contents;
+  ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
+  contents = SanitizeHeapData(contents);
+  ASSERT_EQ(expected, contents);
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, debug_write_malloc_leak_info_extra_data) {
+  Init("backtrace=16");
+
+  std::vector<void*> ptrs;
+  AllocPtrsWithBacktrace(&ptrs);
+
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+  close(tf.fd);
+  tf.release();
+  FILE* fp = fopen(tf.path, "w+");
+  ASSERT_TRUE(fp != nullptr);
+
+  fprintf(fp, "This message should appear before the output.\n");
+  ASSERT_TRUE(debug_write_malloc_leak_info(fp));
+  fprintf(fp, "This message should appear after the output.\n");
+
+  fclose(fp);
+
+  for (auto ptr : ptrs) {
+    debug_free(ptr);
+  }
+  ptrs.clear();
+
+  std::string expected = "This message should appear before the output.\n"
+                         + std::string(kDumpInfo)
+                         + "\nThis message should appear after the output.";
+
+  std::string contents;
+  ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
+  contents = SanitizeHeapData(contents);
+  ASSERT_EQ(expected, contents);
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, dump_heap) {
+  Init("backtrace=16");
+
+  std::vector<void*> ptrs;
+  AllocPtrsWithBacktrace(&ptrs);
+
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+  close(tf.fd);
+  tf.release();
+  debug_dump_heap(tf.path);
+
+  for (auto ptr : ptrs) {
+    debug_free(ptr);
+  }
+  ptrs.clear();
+
+  std::string expected(kDumpInfo);
+
+  std::string contents;
+  ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
+  contents = SanitizeHeapData(contents);
+  ASSERT_EQ(expected, contents);
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string expected_log = std::string("6 malloc_debug Dumping to file: ") + tf.path + "\n\n";
+  ASSERT_EQ(expected_log, getFakeLogPrint());
+}
diff --git a/libc/platform/bionic/mte_kernel.h b/libc/platform/bionic/mte_kernel.h
new file mode 100644
index 0000000..04f2bb6
--- /dev/null
+++ b/libc/platform/bionic/mte_kernel.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#pragma once
+
+// Defines constants used as part of the interface in an experimental MTE branch
+// of the Linux kernel, which may be found at:
+//
+// https://github.com/pcc/linux/tree/android-experimental-mte
+//
+// This interface should not be considered to be stable.
+
+#ifdef ANDROID_EXPERIMENTAL_MTE
+#define HWCAP2_MTE (1UL << 31)
+#define PROT_MTE 0x10
+#endif
diff --git a/libc/private/MallocXmlElem.h b/libc/private/MallocXmlElem.h
index 04d3eee..a367972 100644
--- a/libc/private/MallocXmlElem.h
+++ b/libc/private/MallocXmlElem.h
@@ -18,38 +18,39 @@
 
 #include <stdarg.h>
 #include <stdio.h>
+#include <unistd.h>
 
 #include <private/bionic_macros.h>
 
 class MallocXmlElem {
  public:
   // Name must be valid throughout lifetime of the object.
-  explicit MallocXmlElem(FILE* fp, const char* name,
-                         const char* attr_fmt = nullptr, ...) : fp_(fp), name_(name) {
-    fprintf(fp, "<%s", name_);
+  explicit MallocXmlElem(int fd, const char* name,
+                         const char* attr_fmt = nullptr, ...) : fd_(fd), name_(name) {
+    dprintf(fd_, "<%s", name_);
     if (attr_fmt != nullptr) {
       va_list args;
       va_start(args, attr_fmt);
-      fputc(' ', fp_);
-      vfprintf(fp_, attr_fmt, args);
+      write(fd_, " ", 1);
+      vdprintf(fd_, attr_fmt, args);
       va_end(args);
     }
-    fputc('>', fp_);
+    write(fd_, ">", 1);
   }
 
   ~MallocXmlElem() noexcept {
-    fprintf(fp_, "</%s>", name_);
+    dprintf(fd_, "</%s>", name_);
   }
 
   void Contents(const char* fmt, ...) {
     va_list args;
     va_start(args, fmt);
-    vfprintf(fp_, fmt, args);
+    vdprintf(fd_, fmt, args);
     va_end(args);
   }
 
 private:
-  FILE* fp_;
+  int fd_;
   const char* name_;
 
   BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(MallocXmlElem);
diff --git a/libc/private/ScopedFd.h b/libc/private/ScopedFd.h
new file mode 100644
index 0000000..1cec916
--- /dev/null
+++ b/libc/private/ScopedFd.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <unistd.h>
+
+#include "private/bionic_macros.h"
+#include "private/ErrnoRestorer.h"
+
+class ScopedFd final {
+ public:
+  explicit ScopedFd(int fd) : fd_(fd) {
+  }
+
+  ScopedFd() : fd_(-1) {
+  }
+
+  ~ScopedFd() {
+    reset(-1);
+  }
+
+  void reset(int fd = -1) {
+    if (fd_ != -1) {
+      ErrnoRestorer e;
+      close(fd_);
+    }
+    fd_ = fd;
+  }
+
+  int get() const {
+    return fd_;
+  }
+
+ private:
+  int fd_;
+
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(ScopedFd);
+};
diff --git a/libc/private/bionic_ifuncs.h b/libc/private/bionic_ifuncs.h
index 35961fb..f175bec 100644
--- a/libc/private/bionic_ifuncs.h
+++ b/libc/private/bionic_ifuncs.h
@@ -28,10 +28,22 @@
 
 #pragma once
 
+#include <stdint.h>
+#include <sys/ifunc.h>
+
+#if defined(__aarch64__)
+#define IFUNC_ARGS (uint64_t hwcap __attribute__((unused)), \
+                    __ifunc_arg_t* arg __attribute__((unused)))
+#elif defined(__arm__)
+#define IFUNC_ARGS (unsigned long hwcap __attribute__((unused)))
+#else
+#define IFUNC_ARGS ()
+#endif
+
 #define DEFINE_IFUNC_FOR(name) \
     name##_func name __attribute__((ifunc(#name "_resolver"))); \
     __attribute__((visibility("hidden"))) \
-    name##_func* name##_resolver()
+    name##_func* name##_resolver IFUNC_ARGS
 
 #define DECLARE_FUNC(type, name) \
     __attribute__((visibility("hidden"))) \
diff --git a/libc/upstream-freebsd/android/include/freebsd-compat.h b/libc/upstream-freebsd/android/include/freebsd-compat.h
index 3228701..b16a688 100644
--- a/libc/upstream-freebsd/android/include/freebsd-compat.h
+++ b/libc/upstream-freebsd/android/include/freebsd-compat.h
@@ -14,39 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef _BIONIC_FREEBSD_COMPAT_H_included
-#define _BIONIC_FREEBSD_COMPAT_H_included
+#pragma once
 
 #define _BSD_SOURCE
 
-#include <sys/cdefs.h>
-#include <stddef.h> // For size_t.
-
 #define REPLACE_GETOPT
 
-/*
- * FreeBSD's libc has three symbols for every symbol:
- *
- *     __f will be the actual implementation.
- *     _f will be a weak reference to __f (used for calls to f from within the library).
- *     f will be a weak reference to __f (used for calls to f from outside the library).
- *
- * We collapse this into just the one symbol, f.
- */
-
-/* Prevent weak reference generation. */
-#define __weak_reference(sym,alias)
-
-/* Ensure that the implementation itself gets the underscore-free name. */
-#define __sleep sleep
-#define __usleep usleep
-
-/* Redirect internal C library calls to the public function. */
-#define _nanosleep nanosleep
-
 /* FreeBSD has this, but we can't really implement it correctly on Linux. */
 #define issetugid() 0
 
 #define __compiler_membar() __asm __volatile(" " : : : "memory")
-
-#endif
diff --git a/libc/upstream-freebsd/android/include/namespace.h b/libc/upstream-freebsd/android/include/namespace.h
index a3f850e..e69de29 100644
--- a/libc/upstream-freebsd/android/include/namespace.h
+++ b/libc/upstream-freebsd/android/include/namespace.h
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _BIONIC_FREEBSD_NAMESPACE_H_included
-#define _BIONIC_FREEBSD_NAMESPACE_H_included
-
-#endif
diff --git a/libc/upstream-freebsd/android/include/un-namespace.h b/libc/upstream-freebsd/android/include/un-namespace.h
index a3f850e..e69de29 100644
--- a/libc/upstream-freebsd/android/include/un-namespace.h
+++ b/libc/upstream-freebsd/android/include/un-namespace.h
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _BIONIC_FREEBSD_NAMESPACE_H_included
-#define _BIONIC_FREEBSD_NAMESPACE_H_included
-
-#endif
diff --git a/libc/upstream-freebsd/lib/libc/stdlib/getopt_long.c b/libc/upstream-freebsd/lib/libc/stdlib/getopt_long.c
index 9534a2a..6a3067c 100644
--- a/libc/upstream-freebsd/lib/libc/stdlib/getopt_long.c
+++ b/libc/upstream-freebsd/lib/libc/stdlib/getopt_long.c
@@ -55,7 +55,7 @@
 #endif /* LIBC_SCCS and not lint */
 #endif
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/lib/libc/stdlib/getopt_long.c 342757 2019-01-04 03:13:24Z kevans $");
 
 #include <err.h>
 #include <errno.h>
@@ -481,6 +481,8 @@
 #endif
 		if (*place == '-') {
 			place++;		/* --foo long option */
+			if (*place == '\0')
+				return (BADARG);	/* malformed option */
 #ifdef GNU_COMPATIBLE
 			dash_prefix = DD_PREFIX;
 #endif
diff --git a/libc/upstream-freebsd/lib/libc/stdlib/qsort.c b/libc/upstream-freebsd/lib/libc/stdlib/qsort.c
index 1ccc518..e0db4f3 100644
--- a/libc/upstream-freebsd/lib/libc/stdlib/qsort.c
+++ b/libc/upstream-freebsd/lib/libc/stdlib/qsort.c
@@ -1,4 +1,6 @@
 /*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
  * Copyright (c) 1992, 1993
  *	The Regents of the University of California.  All rights reserved.
  *
@@ -31,7 +33,7 @@
 static char sccsid[] = "@(#)qsort.c	8.1 (Berkeley) 6/4/93";
 #endif /* LIBC_SCCS and not lint */
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/lib/libc/stdlib/qsort.c 334928 2018-06-10 17:54:44Z kib $");
 
 #include <stdlib.h>
 
@@ -41,53 +43,27 @@
 typedef int		 cmp_t(const void *, const void *);
 #endif
 static inline char	*med3(char *, char *, char *, cmp_t *, void *);
-static inline void	 swapfunc(char *, char *, size_t, int, int);
 
 #define	MIN(a, b)	((a) < (b) ? a : b)
 
 /*
  * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
  */
-#define	swapcode(TYPE, parmi, parmj, n) {		\
-	size_t i = (n) / sizeof (TYPE);			\
-	TYPE *pi = (TYPE *) (parmi);		\
-	TYPE *pj = (TYPE *) (parmj);		\
-	do { 						\
-		TYPE	t = *pi;		\
-		*pi++ = *pj;				\
-		*pj++ = t;				\
-	} while (--i > 0);				\
-}
-
-#define	SWAPINIT(TYPE, a, es) swaptype_ ## TYPE =	\
-	((char *)a - (char *)0) % sizeof(TYPE) ||	\
-	es % sizeof(TYPE) ? 2 : es == sizeof(TYPE) ? 0 : 1;
 
 static inline void
-swapfunc(char *a, char *b, size_t n, int swaptype_long, int swaptype_int)
+swapfunc(char *a, char *b, size_t es)
 {
-	if (swaptype_long <= 1)
-		swapcode(long, a, b, n)
-	else if (swaptype_int <= 1)
-		swapcode(int, a, b, n)
-	else
-		swapcode(char, a, b, n)
+	char t;
+
+	do {
+		t = *a;
+		*a++ = *b;
+		*b++ = t;
+	} while (--es > 0);
 }
 
-#define	swap(a, b)					\
-	if (swaptype_long == 0) {			\
-		long t = *(long *)(a);			\
-		*(long *)(a) = *(long *)(b);		\
-		*(long *)(b) = t;			\
-	} else if (swaptype_int == 0) {			\
-		int t = *(int *)(a);			\
-		*(int *)(a) = *(int *)(b);		\
-		*(int *)(b) = t;			\
-	} else						\
-		swapfunc(a, b, es, swaptype_long, swaptype_int)
-
 #define	vecswap(a, b, n)				\
-	if ((n) > 0) swapfunc(a, b, n, swaptype_long, swaptype_int)
+	if ((n) > 0) swapfunc(a, b, n)
 
 #ifdef I_AM_QSORT_R
 #define	CMP(t, x, y) (cmp((t), (x), (y)))
@@ -119,17 +95,16 @@
 	char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
 	size_t d1, d2;
 	int cmp_result;
-	int swaptype_long, swaptype_int, swap_cnt;
+	int swap_cnt;
 
-loop:	SWAPINIT(long, a, es);
-	SWAPINIT(int, a, es);
+loop:
 	swap_cnt = 0;
 	if (n < 7) {
 		for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es)
 			for (pl = pm; 
 			     pl > (char *)a && CMP(thunk, pl - es, pl) > 0;
 			     pl -= es)
-				swap(pl, pl - es);
+				swapfunc(pl, pl - es, es);
 		return;
 	}
 	pm = (char *)a + (n / 2) * es;
@@ -145,7 +120,7 @@
 		}
 		pm = med3(pl, pm, pn, cmp, thunk);
 	}
-	swap(a, pm);
+	swapfunc(a, pm, es);
 	pa = pb = (char *)a + es;
 
 	pc = pd = (char *)a + (n - 1) * es;
@@ -153,7 +128,7 @@
 		while (pb <= pc && (cmp_result = CMP(thunk, pb, a)) <= 0) {
 			if (cmp_result == 0) {
 				swap_cnt = 1;
-				swap(pa, pb);
+				swapfunc(pa, pb, es);
 				pa += es;
 			}
 			pb += es;
@@ -161,14 +136,14 @@
 		while (pb <= pc && (cmp_result = CMP(thunk, pc, a)) >= 0) {
 			if (cmp_result == 0) {
 				swap_cnt = 1;
-				swap(pc, pd);
+				swapfunc(pc, pd, es);
 				pd -= es;
 			}
 			pc -= es;
 		}
 		if (pb > pc)
 			break;
-		swap(pb, pc);
+		swapfunc(pb, pc, es);
 		swap_cnt = 1;
 		pb += es;
 		pc -= es;
@@ -178,7 +153,7 @@
 			for (pl = pm; 
 			     pl > (char *)a && CMP(thunk, pl - es, pl) > 0;
 			     pl -= es)
-				swap(pl, pl - es);
+				swapfunc(pl, pl - es, es);
 		return;
 	}
 
diff --git a/libc/upstream-freebsd/lib/libc/string/wcslcat.c b/libc/upstream-freebsd/lib/libc/string/wcslcat.c
index f5f1e1e..9706fa6 100644
--- a/libc/upstream-freebsd/lib/libc/string/wcslcat.c
+++ b/libc/upstream-freebsd/lib/libc/string/wcslcat.c
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
  * All rights reserved.
  *
@@ -33,7 +35,7 @@
 __RCSID("$NetBSD: wcslcat.c,v 1.1 2000/12/23 23:14:36 itojun Exp $");
 #endif /* LIBC_SCCS and not lint */
 #endif
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/lib/libc/string/wcslcat.c 326193 2017-11-25 17:12:48Z pfg $");
 
 #include <sys/types.h>
 #include <wchar.h>
@@ -54,7 +56,7 @@
 	size_t dlen;
 
 	/* Find the end of dst and adjust bytes left but don't go past end */
-	while (*d != '\0' && n-- != 0)
+	while (n-- != 0 && *d != '\0')
 		d++;
 	dlen = d - dst;
 	n = siz - dlen;
diff --git a/libc/upstream-freebsd/lib/libc/string/wcsncat.c b/libc/upstream-freebsd/lib/libc/string/wcsncat.c
index 44f1ff9..0214af4 100644
--- a/libc/upstream-freebsd/lib/libc/string/wcsncat.c
+++ b/libc/upstream-freebsd/lib/libc/string/wcsncat.c
@@ -1,4 +1,6 @@
 /*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (c)1999 Citrus Project,
  * All rights reserved.
  *
@@ -32,7 +34,7 @@
 __RCSID("$NetBSD: wcsncat.c,v 1.1 2000/12/23 23:14:36 itojun Exp $");
 #endif /* LIBC_SCCS and not lint */
 #endif
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/lib/libc/string/wcsncat.c 326193 2017-11-25 17:12:48Z pfg $");
 
 #include <wchar.h>
 
@@ -48,7 +50,7 @@
 		p++;
 	q = p;
 	r = s2;
-	while (*r && n) {
+	while (n && *r) {
 		*q++ = *r++;
 		n--;
 	}
diff --git a/libc/upstream-netbsd/android/include/env.h b/libc/upstream-netbsd/android/include/env.h
index 8f99f9d..e69de29 100644
--- a/libc/upstream-netbsd/android/include/env.h
+++ b/libc/upstream-netbsd/android/include/env.h
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _BIONIC_NETBSD_ENV_H_included
-#define _BIONIC_NETBSD_ENV_H_included
-
-/* Placeholder. */
-
-#endif
diff --git a/libc/upstream-netbsd/android/include/extern.h b/libc/upstream-netbsd/android/include/extern.h
index 616becd..b8e6151 100644
--- a/libc/upstream-netbsd/android/include/extern.h
+++ b/libc/upstream-netbsd/android/include/extern.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _BIONIC_NETBSD_EXTERN_H_included
-#define _BIONIC_NETBSD_EXTERN_H_included
+#pragma once
 
 #include <sys/cdefs.h>
 
@@ -24,5 +23,3 @@
 const char* __strsignal(int, char*, size_t);
 
 __END_DECLS
-
-#endif
diff --git a/libc/upstream-netbsd/android/include/namespace.h b/libc/upstream-netbsd/android/include/namespace.h
index 630ea9b..ae7585c 100644
--- a/libc/upstream-netbsd/android/include/namespace.h
+++ b/libc/upstream-netbsd/android/include/namespace.h
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _BIONIC_NETBSD_NAMESPACE_H_included
-#define _BIONIC_NETBSD_NAMESPACE_H_included
+#pragma once
 
 __LIBC_HIDDEN__ int __res_enable_mt(void);
 __LIBC_HIDDEN__ int __res_disable_mt(void);
-
-#endif
diff --git a/libc/upstream-netbsd/android/include/netbsd-compat.h b/libc/upstream-netbsd/android/include/netbsd-compat.h
index af5ae29..ea630b2 100644
--- a/libc/upstream-netbsd/android/include/netbsd-compat.h
+++ b/libc/upstream-netbsd/android/include/netbsd-compat.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _BIONIC_NETBSD_COMPAT_H_included
-#define _BIONIC_NETBSD_COMPAT_H_included
+#pragma once
 
 #define _BSD_SOURCE
 #define _GNU_SOURCE
@@ -47,5 +46,3 @@
 /* Use appropriate shell depending on process's executable. */
 __LIBC_HIDDEN__ extern const char* __bionic_get_shell_path();
 #define _PATH_BSHELL __bionic_get_shell_path()
-
-#endif
diff --git a/libc/upstream-netbsd/android/include/port_after.h b/libc/upstream-netbsd/android/include/port_after.h
index 3f8b6f8..e69de29 100644
--- a/libc/upstream-netbsd/android/include/port_after.h
+++ b/libc/upstream-netbsd/android/include/port_after.h
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _BIONIC_NETBSD_PORT_BEFORE_H_included
-#define _BIONIC_NETBSD_PORT_BEFORE_H_included
-
-#endif
diff --git a/libc/upstream-netbsd/android/include/port_before.h b/libc/upstream-netbsd/android/include/port_before.h
index 9fa9487..8266f81 100644
--- a/libc/upstream-netbsd/android/include/port_before.h
+++ b/libc/upstream-netbsd/android/include/port_before.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _BIONIC_NETBSD_PORT_BEFORE_H_included
-#define _BIONIC_NETBSD_PORT_BEFORE_H_included
+#pragma once
 
 #include "namespace.h"
 #include <sys/cdefs.h>
@@ -24,5 +23,3 @@
 
 #define ISC_FORMAT_PRINTF(a,b) __printflike(a,b)
 #define ISC_SOCKLEN_T socklen_t
-
-#endif
diff --git a/libc/upstream-openbsd/android/include/arith.h b/libc/upstream-openbsd/android/include/arith.h
index cb116d4..554aa85 100644
--- a/libc/upstream-openbsd/android/include/arith.h
+++ b/libc/upstream-openbsd/android/include/arith.h
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#pragma once
+
 #define IEEE_8087
 
 #if defined(__LP64__)
diff --git a/libc/upstream-openbsd/android/include/openbsd-compat.h b/libc/upstream-openbsd/android/include/openbsd-compat.h
index 2c8c735..afd1077 100644
--- a/libc/upstream-openbsd/android/include/openbsd-compat.h
+++ b/libc/upstream-openbsd/android/include/openbsd-compat.h
@@ -14,34 +14,20 @@
  * limitations under the License.
  */
 
-#ifndef _BIONIC_OPENBSD_COMPAT_H_included
-#define _BIONIC_OPENBSD_COMPAT_H_included
+#pragma once
 
 #define _BSD_SOURCE
 #include <sys/cdefs.h>
 
 #include <stddef.h> // For size_t.
 
-// TODO: libandroid_support uses this file, so we need to wait for
-// <sys/random.h> to be in the NDK headers before we can lose this declaration.
-//#include <sys/random.h> // For getentropy.
-int getentropy(void*, size_t);
+#include <sys/random.h> // For getentropy.
 
 #define __BEGIN_HIDDEN_DECLS _Pragma("GCC visibility push(hidden)")
 #define __END_HIDDEN_DECLS _Pragma("GCC visibility pop")
 
 extern const char* __progname;
 
-/* Redirect internal C library calls to the public function. */
-#define _err err
-#define _errx errx
-#define _verr verr
-#define _verrx verrx
-#define _vwarn vwarn
-#define _vwarnx vwarnx
-#define _warn warn
-#define _warnx warnx
-
 /* Ignore all DEF_STRONG/DEF_WEAK in OpenBSD. */
 #define DEF_STRONG(sym)
 #define DEF_WEAK(sym)
@@ -82,5 +68,3 @@
 
 __LIBC_HIDDEN__ extern char* __findenv(const char*, int, int*);
 __LIBC_HIDDEN__ extern char* _mktemp(char*);
-
-#endif
diff --git a/libc/upstream-openbsd/lib/libc/string/strlen.c b/libc/upstream-openbsd/lib/libc/string/strlen.c
new file mode 100644
index 0000000..a5721d3
--- /dev/null
+++ b/libc/upstream-openbsd/lib/libc/string/strlen.c
@@ -0,0 +1,44 @@
+/*	$OpenBSD: strlen.c,v 1.9 2015/08/31 02:53:57 guenther Exp $	*/
+
+/*-
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+size_t
+strlen(const char *str)
+{
+	const char *s;
+
+	for (s = str; *s; ++s)
+		;
+	return (s - str);
+}
+
+DEF_STRONG(strlen);
diff --git a/libdl/Android.bp b/libdl/Android.bp
index 15263e9..9daa9c4 100644
--- a/libdl/Android.bp
+++ b/libdl/Android.bp
@@ -185,6 +185,11 @@
         symbol_file: "libdl_android.map.txt",
         versions: ["10000"],
     },
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.runtime",
+    ],
 }
 
 ndk_library {
diff --git a/linker/Android.bp b/linker/Android.bp
index bb9d26d..1800bdb 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -1,22 +1,6 @@
-cc_library_static {
-    name: "liblinker_malloc",
-    defaults: ["linux_bionic_supported"],
-    recovery_available: true,
-    native_bridge_supported: true,
-
-    srcs: [
-        "linker_memory.cpp",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-
-    // We need to access Bionic private headers in the linker.
-    include_dirs: ["bionic/libc"],
-
-    static_libs: ["libasync_safe", "libbase"],
-}
+// ========================================================
+// linker_wrapper - Linux Bionic (on the host)
+// ========================================================
 
 // This is used for bionic on (host) Linux to bootstrap our linker embedded into
 // a binary.
@@ -66,6 +50,103 @@
     include_dirs: ["bionic/libc"],
 }
 
+// ========================================================
+// linker default configuration
+// ========================================================
+
+// Configuration for the linker binary and any of its static libraries.
+cc_defaults {
+    name: "linker_defaults",
+    arch: {
+        arm: {
+            cflags: ["-D__work_around_b_24465209__"],
+        },
+        x86: {
+            cflags: ["-D__work_around_b_24465209__"],
+        },
+    },
+
+    cflags: [
+        "-fno-stack-protector",
+        "-Wstrict-overflow=5",
+        "-fvisibility=hidden",
+        "-Wall",
+        "-Wextra",
+        "-Wunused",
+        "-Werror",
+    ],
+
+    // TODO: split out the asflags.
+    asflags: [
+        "-fno-stack-protector",
+        "-Wstrict-overflow=5",
+        "-fvisibility=hidden",
+        "-Wall",
+        "-Wextra",
+        "-Wunused",
+        "-Werror",
+    ],
+
+    product_variables: {
+        debuggable: {
+            cppflags: ["-DUSE_LD_CONFIG_FILE"],
+        },
+    },
+
+    cppflags: ["-Wold-style-cast"],
+
+    static_libs: [
+        "libziparchive",
+        "libbase",
+        "libz",
+
+        "libasync_safe",
+
+        "liblog",
+    ],
+
+    // We need to access Bionic private headers in the linker.
+    include_dirs: ["bionic/libc"],
+}
+
+// ========================================================
+// linker components
+// ========================================================
+
+// Enable a module on all targets the linker runs on (ordinary Android targets, Linux Bionic, and
+// native bridge implementations).
+cc_defaults {
+    name: "linker_all_targets",
+    defaults: ["linux_bionic_supported"],
+    recovery_available: true,
+    native_bridge_supported: true,
+}
+
+cc_library_static {
+    name: "liblinker_main",
+    defaults: ["linker_defaults", "linker_all_targets"],
+    srcs: ["linker_main.cpp"],
+
+    // Ensure that the compiler won't insert string function calls before ifuncs are resolved.
+    cflags: ["-ffreestanding"],
+}
+
+cc_library_static {
+    name: "liblinker_malloc",
+    defaults: ["linker_defaults", "linker_all_targets"],
+    srcs: ["linker_memory.cpp"],
+}
+
+cc_library_static {
+    name: "liblinker_debuggerd_stub",
+    defaults: ["linker_defaults", "linker_all_targets"],
+    srcs: ["linker_debuggerd_stub.cpp"],
+}
+
+// ========================================================
+// template for the linker binary
+// ========================================================
+
 filegroup {
     name: "linker_sources",
     srcs: [
@@ -79,7 +160,6 @@
         "linker_globals.cpp",
         "linker_libc_support.c",
         "linker_libcxx_support.cpp",
-        "linker_main.cpp",
         "linker_namespaces.cpp",
         "linker_logger.cpp",
         "linker_mapped_file_fragment.cpp",
@@ -137,30 +217,50 @@
     ],
 }
 
-filegroup {
-    name: "linker_version_script",
-    srcs: ["linker.generic.map"],
-}
-
-filegroup {
-    name: "linker_version_script_arm",
-    srcs: ["linker.arm.map"],
-}
-
 cc_defaults {
-    name: "linker_defaults",
+    name: "linker_version_script_overlay",
+    arch: {
+        arm:    { version_script: "linker.arm.map"      },
+        arm64:  { version_script: "linker.generic.map"  },
+        x86:    { version_script: "linker.generic.map"  },
+        x86_64: { version_script: "linker.generic.map"  },
+        mips:   { version_script: "linker.generic.map"  },
+        mips64: { version_script: "linker.generic.map"  },
+    },
+}
+
+// A template for the linker binary. May be inherited by native bridge implementations.
+cc_defaults {
+    name: "linker_bin_template",
+    defaults: ["linker_defaults"],
+
+    srcs: [":linker_sources"],
+
     arch: {
         arm: {
-            cflags: ["-D__work_around_b_24465209__"],
+            srcs: [":linker_sources_arm"],
+            static_libs: ["libunwind_llvm"],
+        },
+        arm64: {
+            srcs: [":linker_sources_arm64"],
         },
         x86: {
-            cflags: ["-D__work_around_b_24465209__"],
+            srcs: [":linker_sources_x86"],
+        },
+        x86_64: {
+            srcs: [":linker_sources_x86_64"],
+        },
+        mips: {
+            srcs: [":linker_sources_mips"],
+        },
+        mips64: {
+            srcs: [":linker_sources_mips64"],
         },
     },
 
-    // -shared is used to overwrite the -Bstatic and -static
-    // flags triggered by LOCAL_FORCE_STATIC_EXECUTABLE.
-    // This dynamic linker is actually a shared object linked with static libraries.
+    // -shared is used to overwrite the -Bstatic and -static flags triggered by enabling
+    // static_executable. This dynamic linker is actually a shared object linked with static
+    // libraries.
     ldflags: [
         "-shared",
         "-Wl,-Bsymbolic",
@@ -168,35 +268,6 @@
         "-Wl,-soname,ld-android.so",
     ],
 
-    cflags: [
-        "-fno-stack-protector",
-        "-Wstrict-overflow=5",
-        "-fvisibility=hidden",
-        "-Wall",
-        "-Wextra",
-        "-Wunused",
-        "-Werror",
-    ],
-
-    // TODO: split out the asflags.
-    asflags: [
-        "-fno-stack-protector",
-        "-Wstrict-overflow=5",
-        "-fvisibility=hidden",
-        "-Wall",
-        "-Wextra",
-        "-Wunused",
-        "-Werror",
-    ],
-
-    product_variables: {
-        debuggable: {
-            cppflags: ["-DUSE_LD_CONFIG_FILE"],
-        },
-    },
-
-    cppflags: ["-Wold-style-cast"],
-
     // we are going to link libc++_static manually because
     // when stl is not set to "none" build system adds libdl
     // to the list of static libraries which needs to be
@@ -222,58 +293,15 @@
     sanitize: {
         hwaddress: false,
     },
-}
-
-cc_binary {
-    defaults: ["linux_bionic_supported", "linker_defaults"],
-    srcs: [ ":linker_sources" ],
-
-    arch: {
-        arm: {
-            srcs: [ ":linker_sources_arm" ],
-            version_script: ":linker_version_script_arm",
-            static_libs: ["libunwind_llvm"],
-        },
-        arm64: {
-            srcs: [":linker_sources_arm64"],
-            version_script: ":linker_version_script",
-        },
-        x86: {
-            srcs: [":linker_sources_x86"],
-            version_script: ":linker_version_script",
-        },
-        x86_64: {
-            srcs: [":linker_sources_x86_64"],
-            version_script: ":linker_version_script",
-        },
-        mips: {
-            srcs: [":linker_sources_mips"],
-            version_script: ":linker_version_script",
-        },
-        mips64: {
-            srcs: [":linker_sources_mips64"],
-            version_script: ":linker_version_script",
-        },
-    },
-
-    // We need to access Bionic private headers in the linker.
-    include_dirs: ["bionic/libc"],
 
     static_libs: [
-        "libc_nomalloc",
-        "libm",
-        "libziparchive",
-        "libbase",
-        "libz",
-
-        "libasync_safe",
-
-        "liblog",
-        "libc++_static",
-
-        // Important: The liblinker_malloc should be the last library in the list
-        // to overwrite any other malloc implementations by other static libraries.
+        "liblinker_main",
         "liblinker_malloc",
+
+        "libc++_static",
+        "libc_nomalloc",
+        "libc_dynamic_dispatch",
+        "libm",
     ],
 
     // Ensure that if the linker needs __gnu_Unwind_Find_exidx, then the linker will have a
@@ -287,9 +315,25 @@
     // linker stops linking against libgcc.a's arm32 unwinder.
     whole_static_libs: ["libc_unwind_static"],
 
+    system_shared_libs: [],
+
+    // Opt out of native_coverage when opting out of system_shared_libs
+    native_coverage: false,
+}
+
+// ========================================================
+// linker[_asan][64] binary
+// ========================================================
+
+cc_binary {
     name: "linker",
+    defaults: [
+        "linker_bin_template",
+        "linux_bionic_supported",
+        "linker_version_script_overlay",
+    ],
+
     symlinks: ["linker_asan"],
-    recovery_available: true,
     multilib: {
         lib32: {
             cflags: ["-DLIB_PATH=\"lib\""],
@@ -299,28 +343,38 @@
             suffix: "64",
         },
     },
-    system_shared_libs: [],
 
-    // Opt out of native_coverage when opting out of system_shared_libs
-    native_coverage: false,
+    compile_multilib: "both",
+    xom: false,
+
+    recovery_available: true,
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.runtime",
+    ],
 
     target: {
         android: {
+            srcs: [
+                "linker_debuggerd_android.cpp",
+            ],
             static_libs: [
                 "libc++demangle",
                 "libdebuggerd_handler_fallback",
             ],
         },
+        linux_bionic: {
+            static_libs: [
+                "liblinker_debuggerd_stub",
+            ],
+        }
     },
-    compile_multilib: "both",
-    xom: false,
-
-    apex_available: [
-        "//apex_available:platform",
-        "com.android.runtime",
-    ],
 }
 
+// ========================================================
+// assorted modules
+// ========================================================
+
 sh_binary {
     name: "ldd",
     src: "ldd",
@@ -347,25 +401,11 @@
 
     // for x86, exclude libgcc_eh.a for the same reasons as above
     arch: {
-        arm: {
-            version_script: "linker.arm.map",
-        },
-        arm64: {
-            version_script: "linker.generic.map",
-        },
         x86: {
             ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
-            version_script: "linker.generic.map",
         },
         x86_64: {
             ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
-            version_script: "linker.generic.map",
-        },
-        mips: {
-            version_script: "linker.generic.map",
-        },
-        mips64: {
-            version_script: "linker.generic.map",
         },
     },
 
@@ -379,7 +419,7 @@
     stl: "none",
 
     name: "ld-android",
-    defaults: ["linux_bionic_supported"],
+    defaults: ["linux_bionic_supported", "linker_version_script_overlay"],
     recovery_available: true,
     native_bridge_supported: true,
 
diff --git a/linker/linker.cpp b/linker/linker.cpp
index c1bccfe..1393eb5 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -301,10 +301,6 @@
 }
 #endif
 
-#if COUNT_PAGES
-uint32_t bitmask[4096];
-#endif
-
 static void notify_gdb_of_load(soinfo* info) {
   if (info->is_linker() || info->is_main_executable()) {
     // gdb already knows about the linker and the main executable.
@@ -3140,7 +3136,6 @@
     switch (type) {
       case R_GENERIC_JUMP_SLOT:
         count_relocation(kRelocAbsolute);
-        MARK(rel->r_offset);
         TRACE_TYPE(RELO, "RELO JMP_SLOT %16p <- %16p %s\n",
                    reinterpret_cast<void*>(reloc),
                    reinterpret_cast<void*>(sym_addr + addend), sym_name);
@@ -3150,7 +3145,6 @@
       case R_GENERIC_ABSOLUTE:
       case R_GENERIC_GLOB_DAT:
         count_relocation(kRelocAbsolute);
-        MARK(rel->r_offset);
         TRACE_TYPE(RELO, "RELO ABSOLUTE/GLOB_DAT %16p <- %16p %s\n",
                    reinterpret_cast<void*>(reloc),
                    reinterpret_cast<void*>(sym_addr + addend), sym_name);
@@ -3158,7 +3152,6 @@
         break;
       case R_GENERIC_RELATIVE:
         count_relocation(kRelocRelative);
-        MARK(rel->r_offset);
         TRACE_TYPE(RELO, "RELO RELATIVE %16p <- %16p\n",
                    reinterpret_cast<void*>(reloc),
                    reinterpret_cast<void*>(load_bias + addend));
@@ -3166,11 +3159,13 @@
         break;
       case R_GENERIC_IRELATIVE:
         count_relocation(kRelocRelative);
-        MARK(rel->r_offset);
         TRACE_TYPE(RELO, "RELO IRELATIVE %16p <- %16p\n",
                     reinterpret_cast<void*>(reloc),
                     reinterpret_cast<void*>(load_bias + addend));
-        {
+        // In the linker, ifuncs are called as soon as possible so that string functions work.
+        // We must not call them again. (e.g. On arm32, resolving an ifunc changes the meaning of
+        // the addend from a resolver function to the implementation.)
+        if (!is_linker()) {
 #if !defined(__LP64__)
           // When relocating dso with text_relocation .text segment is
           // not executable. We need to restore elf flags for this
@@ -3210,7 +3205,6 @@
         return false;
       case R_GENERIC_TLS_TPREL:
         count_relocation(kRelocRelative);
-        MARK(rel->r_offset);
         {
           ElfW(Addr) tpoff = 0;
           if (lsi == nullptr) {
@@ -3236,7 +3230,6 @@
         break;
       case R_GENERIC_TLS_DTPMOD:
         count_relocation(kRelocRelative);
-        MARK(rel->r_offset);
         {
           size_t module_id = 0;
           if (lsi == nullptr) {
@@ -3252,7 +3245,6 @@
         break;
       case R_GENERIC_TLS_DTPREL:
         count_relocation(kRelocRelative);
-        MARK(rel->r_offset);
         TRACE_TYPE(RELO, "RELO TLS_DTPREL %16p <- %16p %s\n",
                    reinterpret_cast<void*>(reloc),
                    reinterpret_cast<void*>(sym_addr + addend), sym_name);
@@ -3264,7 +3256,6 @@
       // other architectures, as long as the resolver functions are implemented.
       case R_GENERIC_TLSDESC:
         count_relocation(kRelocRelative);
-        MARK(rel->r_offset);
         {
           TlsDescriptor* desc = reinterpret_cast<TlsDescriptor*>(reloc);
           if (lsi == nullptr) {
@@ -3305,14 +3296,12 @@
 #if defined(__x86_64__)
       case R_X86_64_32:
         count_relocation(kRelocAbsolute);
-        MARK(rel->r_offset);
         TRACE_TYPE(RELO, "RELO R_X86_64_32 %08zx <- +%08zx %s", static_cast<size_t>(reloc),
                    static_cast<size_t>(sym_addr), sym_name);
         *reinterpret_cast<Elf32_Addr*>(reloc) = sym_addr + addend;
         break;
       case R_X86_64_PC32:
         count_relocation(kRelocRelative);
-        MARK(rel->r_offset);
         TRACE_TYPE(RELO, "RELO R_X86_64_PC32 %08zx <- +%08zx (%08zx - %08zx) %s",
                    static_cast<size_t>(reloc), static_cast<size_t>(sym_addr - reloc),
                    static_cast<size_t>(sym_addr), static_cast<size_t>(reloc), sym_name);
@@ -3321,7 +3310,6 @@
 #elif defined(__i386__)
       case R_386_PC32:
         count_relocation(kRelocRelative);
-        MARK(rel->r_offset);
         TRACE_TYPE(RELO, "RELO R_386_PC32 %08x <- +%08x (%08x - %08x) %s",
                    reloc, (sym_addr - reloc), sym_addr, reloc, sym_name);
         *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr - reloc);
diff --git a/linker/linker_config.cpp b/linker/linker_config.cpp
index 46c91a3..85ea8d1 100644
--- a/linker/linker_config.cpp
+++ b/linker/linker_config.cpp
@@ -408,8 +408,10 @@
       params.push_back({ "SDK_VER", buf });
     }
 
-    static std::string vndk = Config::get_vndk_version_string('-');
-    params.push_back({ "VNDK_VER", vndk });
+    static std::string vndk_ver = Config::get_vndk_version_string('-');
+    params.push_back({ "VNDK_VER", vndk_ver });
+    static std::string vndk_apex_ver = Config::get_vndk_version_string('v');
+    params.push_back({ "VNDK_APEX_VER", vndk_apex_ver });
 
     for (auto& path : paths) {
       format_string(&path, params);
diff --git a/linker/linker_debug.h b/linker/linker_debug.h
index 7a1cb3c..6031850 100644
--- a/linker/linker_debug.h
+++ b/linker/linker_debug.h
@@ -44,7 +44,6 @@
 #define DO_TRACE_IFUNC       1
 #define TIMING               0
 #define STATS                0
-#define COUNT_PAGES          0
 
 /*********************************************************************
  * You shouldn't need to modify anything below unless you are adding
@@ -83,22 +82,3 @@
 #endif /* TRACE_DEBUG */
 
 #define TRACE_TYPE(t, x...)   do { if (DO_TRACE_##t) { TRACE(x); } } while (0)
-
-#if COUNT_PAGES
-extern uint32_t bitmask[];
-#if defined(__LP64__)
-#define MARK(offset) \
-    do { \
-      if ((((offset) >> 12) >> 5) < 4096) \
-          bitmask[((offset) >> 12) >> 5] |= (1 << (((offset) >> 12) & 31)); \
-    } while (0)
-#else
-#define MARK(offset) \
-    do { \
-      bitmask[((offset) >> 12) >> 3] |= (1 << (((offset) >> 12) & 7)); \
-    } while (0)
-#endif
-#else
-#define MARK(x) do {} while (0)
-
-#endif
diff --git a/linker/linker_debuggerd.h b/linker/linker_debuggerd.h
new file mode 100644
index 0000000..d701879
--- /dev/null
+++ b/linker/linker_debuggerd.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#pragma once
+
+void linker_debuggerd_init();
diff --git a/linker/linker_debuggerd_android.cpp b/linker/linker_debuggerd_android.cpp
new file mode 100644
index 0000000..b8c82f9
--- /dev/null
+++ b/linker/linker_debuggerd_android.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "linker_debuggerd.h"
+
+#include "debuggerd/handler.h"
+#include "private/bionic_globals.h"
+
+#include "linker_gdb_support.h"
+
+void linker_debuggerd_init() {
+  debuggerd_callbacks_t callbacks = {
+    .get_abort_message = []() {
+      return __libc_shared_globals()->abort_msg;
+    },
+    .post_dump = &notify_gdb_of_libraries,
+  };
+  debuggerd_init(&callbacks);
+}
diff --git a/linker/linker_debuggerd_stub.cpp b/linker/linker_debuggerd_stub.cpp
new file mode 100644
index 0000000..631e6e4
--- /dev/null
+++ b/linker/linker_debuggerd_stub.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "linker_debuggerd.h"
+
+void linker_debuggerd_init() {
+}
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index fd1592d..8ba947f 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -32,6 +32,7 @@
 #include <sys/auxv.h>
 
 #include "linker_debug.h"
+#include "linker_debuggerd.h"
 #include "linker_cfi.h"
 #include "linker_gdb_support.h"
 #include "linker_globals.h"
@@ -39,6 +40,8 @@
 #include "linker_tls.h"
 #include "linker_utils.h"
 
+#include "private/bionic_auxv.h"
+#include "private/bionic_call_ifunc_resolver.h"
 #include "private/bionic_globals.h"
 #include "private/bionic_tls.h"
 #include "private/KernelArgumentBlock.h"
@@ -46,9 +49,6 @@
 #include "android-base/unique_fd.h"
 #include "android-base/strings.h"
 #include "android-base/stringprintf.h"
-#ifdef __ANDROID__
-#include "debuggerd/handler.h"
-#endif
 
 #include <async_safe/log.h>
 #include <bionic/libc_init_common.h>
@@ -311,15 +311,7 @@
   __system_properties_init(); // may use 'environ'
 
   // Register the debuggerd signal handler.
-#ifdef __ANDROID__
-  debuggerd_callbacks_t callbacks = {
-    .get_abort_message = []() {
-      return __libc_shared_globals()->abort_msg;
-    },
-    .post_dump = &notify_gdb_of_libraries,
-  };
-  debuggerd_init(&callbacks);
-#endif
+  linker_debuggerd_init();
 
   g_linker_logger.ResetState();
 
@@ -496,31 +488,7 @@
 #if STATS
   print_linker_stats();
 #endif
-#if COUNT_PAGES
-  {
-    unsigned n;
-    unsigned i;
-    unsigned count = 0;
-    for (n = 0; n < 4096; n++) {
-      if (bitmask[n]) {
-        unsigned x = bitmask[n];
-#if defined(__LP64__)
-        for (i = 0; i < 32; i++) {
-#else
-        for (i = 0; i < 8; i++) {
-#endif
-          if (x & 1) {
-            count++;
-          }
-          x >>= 1;
-        }
-      }
-    }
-    PRINT("PAGES MODIFIED: %s: %d (%dKB)", g_argv[0], count, count * 4);
-  }
-#endif
-
-#if TIMING || STATS || COUNT_PAGES
+#if TIMING || STATS
   fflush(stdout);
 #endif
 
@@ -599,6 +567,39 @@
   }
 }
 
+// TODO: There is a similar ifunc resolver calling loop in libc_init_static.cpp, but that version
+// uses weak symbols, which don't work in the linker prior to its relocation. This version also
+// supports a load bias. When we stop supporting the gold linker in the NDK, then maybe we can use
+// non-weak definitions and merge the two loops.
+#if defined(USE_RELA)
+extern __LIBC_HIDDEN__ ElfW(Rela) __rela_iplt_start[], __rela_iplt_end[];
+
+static void call_ifunc_resolvers(ElfW(Addr) load_bias) {
+  for (ElfW(Rela) *r = __rela_iplt_start; r != __rela_iplt_end; ++r) {
+    ElfW(Addr)* offset = reinterpret_cast<ElfW(Addr)*>(r->r_offset + load_bias);
+    ElfW(Addr) resolver = r->r_addend + load_bias;
+    *offset = __bionic_call_ifunc_resolver(resolver);
+  }
+}
+#else
+extern __LIBC_HIDDEN__ ElfW(Rel) __rel_iplt_start[], __rel_iplt_end[];
+
+static void call_ifunc_resolvers(ElfW(Addr) load_bias) {
+  for (ElfW(Rel) *r = __rel_iplt_start; r != __rel_iplt_end; ++r) {
+    ElfW(Addr)* offset = reinterpret_cast<ElfW(Addr)*>(r->r_offset + load_bias);
+    ElfW(Addr) resolver = *offset + load_bias;
+    *offset = __bionic_call_ifunc_resolver(resolver);
+  }
+}
+#endif
+
+// Usable before ifunc resolvers have been called. This function is compiled with -ffreestanding.
+static void linker_memclr(void* dst, size_t cnt) {
+  for (size_t i = 0; i < cnt; ++i) {
+    reinterpret_cast<char*>(dst)[i] = '\0';
+  }
+}
+
 // Detect an attempt to run the linker on itself. e.g.:
 //   /system/bin/linker64 /system/bin/linker64
 // Use priority-1 to run this constructor before other constructors.
@@ -632,7 +633,8 @@
 extern "C" ElfW(Addr) __linker_init(void* raw_args) {
   // Initialize TLS early so system calls and errno work.
   KernelArgumentBlock args(raw_args);
-  bionic_tcb temp_tcb = {};
+  bionic_tcb temp_tcb __attribute__((uninitialized));
+  linker_memclr(&temp_tcb, sizeof(temp_tcb));
   __libc_init_main_thread_early(args, &temp_tcb);
 
   // When the linker is run by itself (rather than as an interpreter for
@@ -650,11 +652,15 @@
   ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_addr);
   ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_addr + elf_hdr->e_phoff);
 
+  // string.h functions must not be used prior to calling the linker's ifunc resolvers.
+  const ElfW(Addr) load_bias = get_elf_exec_load_bias(elf_hdr);
+  call_ifunc_resolvers(load_bias);
+
   soinfo tmp_linker_so(nullptr, nullptr, nullptr, 0, 0);
 
   tmp_linker_so.base = linker_addr;
   tmp_linker_so.size = phdr_table_get_load_size(phdr, elf_hdr->e_phnum);
-  tmp_linker_so.load_bias = get_elf_exec_load_bias(elf_hdr);
+  tmp_linker_so.load_bias = load_bias;
   tmp_linker_so.dynamic = nullptr;
   tmp_linker_so.phdr = phdr;
   tmp_linker_so.phnum = elf_hdr->e_phnum;
diff --git a/tests/leak_test.cpp b/tests/leak_test.cpp
index 3cc1a0a..e0a3d57 100644
--- a/tests/leak_test.cpp
+++ b/tests/leak_test.cpp
@@ -127,22 +127,20 @@
 // http://b/36045112
 TEST(pthread_leak, detach) {
   LeakChecker lc;
-  constexpr int kThreadCount = 100;
 
-  // Devices with low power cores/low number of cores can not finish test in time hence decreasing
-  // threads count to 90.
-  // http://b/129924384.
-  int threads_count = (sysconf(_SC_NPROCESSORS_CONF) > 2) ? kThreadCount : (kThreadCount - 10);
+  // Ancient devices with only 2 cores need a lower limit.
+  // http://b/129924384 and https://issuetracker.google.com/142210680.
+  const int thread_count = (sysconf(_SC_NPROCESSORS_CONF) > 2) ? 100 : 50;
 
   for (size_t pass = 0; pass < 1; ++pass) {
-    struct thread_data { pthread_barrier_t* barrier; pid_t* tid; } threads[kThreadCount] = {};
+    struct thread_data { pthread_barrier_t* barrier; pid_t* tid; } threads[thread_count];
 
     pthread_barrier_t barrier;
-    ASSERT_EQ(pthread_barrier_init(&barrier, nullptr, threads_count + 1), 0);
+    ASSERT_EQ(pthread_barrier_init(&barrier, nullptr, thread_count + 1), 0);
 
     // Start child threads.
-    pid_t tids[kThreadCount];
-    for (int i = 0; i < threads_count; ++i) {
+    pid_t tids[thread_count];
+    for (int i = 0; i < thread_count; ++i) {
       threads[i] = {&barrier, &tids[i]};
       const auto thread_function = +[](void* ptr) -> void* {
         thread_data* data = static_cast<thread_data*>(ptr);
@@ -158,7 +156,7 @@
     pthread_barrier_wait(&barrier);
     ASSERT_EQ(pthread_barrier_destroy(&barrier), 0);
 
-    WaitUntilAllThreadsExited(tids, threads_count);
+    WaitUntilAllThreadsExited(tids, thread_count);
 
     // A native bridge implementation might need a warm up pass to reach a steady state.
     // http://b/37920774.
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index 0407553..ebbd247 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -358,15 +358,20 @@
 TEST(malloc, malloc_info) {
 #ifdef __BIONIC__
   SKIP_WITH_HWASAN; // hwasan does not implement malloc_info
-  char* buf;
-  size_t bufsize;
-  FILE* memstream = open_memstream(&buf, &bufsize);
-  ASSERT_NE(nullptr, memstream);
-  ASSERT_EQ(0, malloc_info(0, memstream));
-  ASSERT_EQ(0, fclose(memstream));
+
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+  FILE* fp = fdopen(tf.fd, "w+");
+  tf.release();
+  ASSERT_TRUE(fp != nullptr);
+  ASSERT_EQ(0, malloc_info(0, fp));
+  ASSERT_EQ(0, fclose(fp));
+
+  std::string contents;
+  ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
 
   tinyxml2::XMLDocument doc;
-  ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buf));
+  ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(contents.c_str()));
 
   auto root = doc.FirstChildElement();
   ASSERT_NE(nullptr, root);
@@ -416,17 +421,21 @@
 #ifdef __BIONIC__
   SKIP_WITH_HWASAN; // hwasan does not implement malloc_info
 
-  char* buf;
-  size_t bufsize;
-  FILE* memstream = open_memstream(&buf, &bufsize);
-  ASSERT_NE(nullptr, memstream);
+  TemporaryFile tf;
+  ASSERT_TRUE(tf.fd != -1);
+  FILE* fp = fdopen(tf.fd, "w+");
+  tf.release();
+  ASSERT_TRUE(fp != nullptr);
   size_t mallinfo_before_allocated_bytes = mallinfo().uordblks;
-  ASSERT_EQ(0, malloc_info(0, memstream));
+  ASSERT_EQ(0, malloc_info(0, fp));
   size_t mallinfo_after_allocated_bytes = mallinfo().uordblks;
-  ASSERT_EQ(0, fclose(memstream));
+  ASSERT_EQ(0, fclose(fp));
+
+  std::string contents;
+  ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
 
   tinyxml2::XMLDocument doc;
-  ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buf));
+  ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(contents.c_str()));
 
   size_t total_allocated_bytes = 0;
   auto root = doc.FirstChildElement();
diff --git a/tests/system_properties_test2.cpp b/tests/system_properties_test2.cpp
index b9936dd..0953bde 100644
--- a/tests/system_properties_test2.cpp
+++ b/tests/system_properties_test2.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#include <gtest/gtest.h>
-
 #include <errno.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -24,6 +22,10 @@
 #include <sstream>
 #include <string>
 
+#include <gtest/gtest.h>
+
+#include "utils.h"
+
 #if defined(__BIONIC__)
 #include <sys/system_properties.h>
 int64_t NanoTime() {
@@ -128,6 +130,29 @@
 #endif // __BIONIC__
 }
 
+TEST(properties, no_fd_leaks) {
+#if defined(__BIONIC__)
+  FdLeakChecker leak_checker;
+  std::stringstream ss;
+  ss << "debug.test." << getpid() << "." << NanoTime() << ".";
+  const std::string property_prefix = ss.str();
+  const std::string property_name = property_prefix + "property1";
+
+  for (size_t i = 0; i < 100; ++i) {
+    char propvalue[PROP_VALUE_MAX];
+    ASSERT_EQ(0, __system_property_set(property_name.c_str(), "value1"));
+    ASSERT_EQ(6, __system_property_get(property_name.c_str(), propvalue));
+    ASSERT_STREQ("value1", propvalue);
+
+    ASSERT_EQ(0, __system_property_set(property_name.c_str(), "value2"));
+    ASSERT_EQ(6, __system_property_get(property_name.c_str(), propvalue));
+    ASSERT_STREQ("value2", propvalue);
+  }
+#else   // __BIONIC__
+  GTEST_SKIP() << "bionic-only test";
+#endif  // __BIONIC__
+}
+
 TEST(properties, empty_value) {
 #if defined(__BIONIC__)
     char propvalue[PROP_VALUE_MAX];
@@ -136,13 +161,13 @@
     ss << "debug.test." << getpid() << "." << NanoTime() << "." << "property_empty";
     const std::string property_name = ss.str();
 
-    for (size_t i=0; i<1000; ++i) {
+    for (size_t i = 0; i < 1000; ++i) {
       ASSERT_EQ(0, __system_property_set(property_name.c_str(), ""));
       ASSERT_EQ(0, __system_property_get(property_name.c_str(), propvalue));
       ASSERT_STREQ("", propvalue);
     }
 
-#else // __BIONIC__
-    GTEST_SKIP() << "bionic-only test";
+#else  // __BIONIC__
+  GTEST_SKIP() << "bionic-only test";
 #endif // __BIONIC__
 }
diff --git a/tests/utils.h b/tests/utils.h
index 1f4cece..cfc68c9 100644
--- a/tests/utils.h
+++ b/tests/utils.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <dirent.h>
 #include <dlfcn.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -253,3 +254,29 @@
   std::string output_;
 };
 #endif
+
+class FdLeakChecker {
+ public:
+  FdLeakChecker() {
+  }
+
+  ~FdLeakChecker() {
+    size_t end_count = CountOpenFds();
+    EXPECT_EQ(start_count_, end_count);
+  }
+
+ private:
+  static size_t CountOpenFds() {
+    auto fd_dir = std::unique_ptr<DIR, decltype(&closedir)>{ opendir("/proc/self/fd"), closedir };
+    size_t count = 0;
+    dirent* de = nullptr;
+    while ((de = readdir(fd_dir.get())) != nullptr) {
+      if (de->d_type == DT_LNK) {
+        ++count;
+      }
+    }
+    return count;
+  }
+
+  size_t start_count_ = CountOpenFds();
+};
diff --git a/tools/versioner/src/Driver.cpp b/tools/versioner/src/Driver.cpp
index d2c50a9..bad63ad 100644
--- a/tools/versioner/src/Driver.cpp
+++ b/tools/versioner/src/Driver.cpp
@@ -126,14 +126,7 @@
 
   cmd.push_back("-DANDROID");
   cmd.push_back("-D__ANDROID_API__="s + std::to_string(type.api_level));
-  // FIXME: Re-enable FORTIFY properly once our clang in external/ is new enough
-  // to support diagnose_if without giving us syntax errors.
-#if 0
   cmd.push_back("-D_FORTIFY_SOURCE=2");
-#else
-  cmd.push_back("-D_FORTIFY_SOURCE=0");
-  cmd.push_back("-D__BIONIC_DECLARE_FORTIFY_HELPERS");
-#endif
   cmd.push_back("-D_GNU_SOURCE");
   cmd.push_back("-D_FILE_OFFSET_BITS="s + std::to_string(type.file_offset_bits));