DO NOT MERGE - Merge pi-dev@5234907 into stage-aosp-master

Bug: 120848293
Change-Id: Iecc9b7d92757fde265e26d306fbbd568535be054
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..72ab6c0
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2019 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.
+//
+
+bionic_mountpoint {
+    name: "libc.mountpoint",
+    stem: "libc.so",
+    src: "dummy_mountpoint",
+    library: true,
+    symlinks: ["libc.so"],
+    mountsource: "libc",
+}
+
+bionic_mountpoint {
+    name: "libdl.mountpoint",
+    stem: "libdl.so",
+    src: "dummy_mountpoint",
+    library: true,
+    symlinks: ["libdl.so"],
+    mountsource: "libdl",
+}
+
+bionic_mountpoint {
+    name: "libm.mountpoint",
+    stem: "libm.so",
+    src: "dummy_mountpoint",
+    library: true,
+    symlinks: ["libm.so"],
+    mountsource: "libm",
+}
+
+bionic_mountpoint {
+    name: "linker.mountpoint",
+    stem: "linker",
+    multilib: {
+        lib64: {
+            suffix: "64",
+        },
+    },
+    src: "dummy_mountpoint",
+    binary: true,
+    symlinks: ["linker", "linker_asan"],
+    mountsource: "linker",
+}
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..e4d3d5e
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsBionicTestCases"
+    }
+  ]
+}
diff --git a/build/Android.bp b/build/Android.bp
new file mode 100644
index 0000000..acd0ee2
--- /dev/null
+++ b/build/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2019 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.
+//
+
+
+bootstrap_go_package {
+    name: "soong-bionic",
+    pkgPath: "android/soong/bionic",
+    deps: [
+        "blueprint",
+        "blueprint-pathtools",
+        "blueprint-proptools",
+        "soong",
+        "soong-android",
+        "soong-cc",
+    ],
+    srcs: [
+        "bionic.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/build/bionic.go b/build/bionic.go
new file mode 100644
index 0000000..54ad10b
--- /dev/null
+++ b/build/bionic.go
@@ -0,0 +1,228 @@
+// Copyright (C) 2019 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.
+
+package bionic
+
+import (
+	"fmt"
+	"io"
+	"strings"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+	"android/soong/cc"
+)
+
+// bionic_mountpoint is a module type that is specialized to create
+// mount points for Bionic files (libc, libdl, libm, and linker).
+//
+// With following description,
+//
+// bionic_mountpoint {
+//    name: "libc.mountpoint",
+//    stem: "libc.so",
+//    src: "dummy_mountpoint",
+//    library: true,
+//    symlinks: ["libc.so"],
+// }
+//
+// , the build system does following jobs:
+//
+// A mount point /bionic/lib[64]/libc.so is created. Its content
+// is from the file 'dummy_mountpoint'.
+//
+// Then a symlink is created at /system/lib[64]/libc.so which points to
+// the created mountpoint.
+//
+// At runtime, on the mount point, either bootstrap Bionic or default Bionic
+// (which is from the runtime APEX) is mounted by the init process. The
+// symlink exists to provide consistent legacy path for compatibility
+// reason.
+func init() {
+	android.RegisterModuleType("bionic_mountpoint", bionicMountpointFactory)
+}
+
+type bionicMountpoint struct {
+	android.ModuleBase
+	properties bionicMountpointProperties
+
+	outputFile android.Path
+	pathInPartition string
+	stem string
+	unstrippedOutputFile android.Path
+}
+
+type bionicMountpointProperties struct {
+	// The file that is installed as the mount point
+	Src *string
+
+	// TODO(jiyong) remove these two properties (probably Stem and Suffix
+	// as well, as they can be inteffered from Mountsource
+
+	// True if the mount point is for a Bionic library such libc.so
+	Library *bool
+	// True if the mount point is for a Bionic binary such as linker
+	Binary *bool
+
+	// The module that this module is a mount point for
+	Mountsource *string
+
+	// Base name of the mount point
+	Stem *string `android:"arch_variant"`
+
+	// Append to the name of the output
+	Suffix *string `android:"arch_variant"`
+
+	// Symlinks to the mountpoints from the system and recovery partitions
+	// Symlinks names will have the same suffix as the mount point
+	Symlinks []string
+
+	// List of sanitizer names that this APEX is enabled for
+	SanitizerNames []string `blueprint:"mutated"`
+}
+
+type dependencyTag struct {
+	blueprint.BaseDependencyTag
+	name string
+}
+
+var mountsourceTag = dependencyTag{name: "mountsource"}
+
+
+func (m *bionicMountpoint) EnableSanitizer(sanitizerName string) {
+	if !android.InList(sanitizerName, m.properties.SanitizerNames) {
+		m.properties.SanitizerNames = append(m.properties.SanitizerNames, sanitizerName)
+	}
+}
+
+func (m *bionicMountpoint) IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizerName string) bool {
+	if android.InList(sanitizerName, m.properties.SanitizerNames) {
+		return true
+	}
+
+	// Then follow the global setting
+	globalSanitizerNames := []string{}
+	if m.Host() {
+		globalSanitizerNames = ctx.Config().SanitizeHost()
+	} else {
+		arches := ctx.Config().SanitizeDeviceArch()
+		if len(arches) == 0 || android.InList(m.Arch().ArchType.Name, arches) {
+			globalSanitizerNames = ctx.Config().SanitizeDevice()
+		}
+	}
+	return android.InList(sanitizerName, globalSanitizerNames)
+}
+
+func (m *bionicMountpoint) DepsMutator(ctx android.BottomUpMutatorContext) {
+	if Bool(m.properties.Library) == Bool(m.properties.Binary) {
+		ctx.ModuleErrorf("either binary or library must be set to true")
+		return
+	}
+	if m.properties.Stem == nil {
+		ctx.PropertyErrorf("stem", "stem must be set")
+		return
+	}
+	if m.properties.Src == nil {
+		ctx.PropertyErrorf("src", "src must be set")
+	}
+	android.ExtractSourceDeps(ctx, m.properties.Src)
+
+	if m.properties.Mountsource == nil {
+		ctx.PropertyErrorf("mountsource", "mountsource must be set")
+		return
+	}
+
+	ctx.AddFarVariationDependencies([]blueprint.Variation{
+		{Mutator: "arch", Variation: ctx.Target().String()},
+		{Mutator: "image", Variation: "core"},
+		{Mutator: "link", Variation: "shared"},
+	}, mountsourceTag, String(m.properties.Mountsource))
+}
+
+func (m *bionicMountpoint) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if Bool(m.properties.Library) {
+		m.pathInPartition = "lib"
+		if m.Arch().ArchType.Multilib == "lib64" {
+			m.pathInPartition = "lib64"
+		}
+	} else if Bool(m.properties.Binary) {
+		m.pathInPartition = "bin"
+	}
+
+	m.stem = String(m.properties.Stem) + String(m.properties.Suffix)
+
+	m.outputFile = ctx.ExpandSource(String(m.properties.Src), "src")
+
+	ctx.VisitDirectDepsWithTag(mountsourceTag, func(module android.Module) {
+		if cc, ok := module.(*cc.Module); ok {
+			m.unstrippedOutputFile = cc.UnstrippedOutputFile()
+		}
+	})
+}
+
+func (m *bionicMountpoint) AndroidMk() android.AndroidMkData {
+	return android.AndroidMkData {
+		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+			if !m.Arch().Native {
+				return
+			}
+			fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+			fmt.Fprintln(w, "LOCAL_MODULE :=", name)
+			fmt.Fprintln(w, "LOCAL_USE_CLANG_LLD := false")
+			fmt.Fprintln(w, "LOCAL_STRIP_MODULE := false")
+			if Bool(m.properties.Library) {
+				fmt.Fprintln(w, "LOCAL_MODULE_CLASS := SHARED_LIBRARIES")
+			} else if Bool(m.properties.Binary) {
+				fmt.Fprintln(w, "LOCAL_MODULE_CLASS := EXECUTABLES")
+			}
+			fmt.Fprintln(w, "LOCAL_MODULE_TAGS := optional")
+			fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", m.outputFile.String())
+			fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", m.Arch().ArchType.String())
+			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", "$(TARGET_ROOT_OUT)/bionic/" + m.pathInPartition)
+			fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", m.stem)
+
+			if len(m.properties.Symlinks) > 0 {
+				symlink_dir_in_system := "$(TARGET_OUT)/" + m.pathInPartition + "/"
+				symlink_dir_in_recovery := "$(TARGET_RECOVERY_ROOT_OUT)/system/" + m.pathInPartition + "/"
+				symlink_target := "/bionic/" + m.pathInPartition + "/" + m.stem
+				cmds := []string{}
+				cmds = append(cmds, "$(hide) mkdir -p " + symlink_dir_in_system)
+				cmds = append(cmds, "mkdir -p " + symlink_dir_in_recovery)
+				for _, s := range m.properties.Symlinks {
+					symlink := s + String(m.properties.Suffix)
+					cmds = append(cmds, "ln -sf " + symlink_target + " " + symlink_dir_in_system +  symlink)
+					cmds = append(cmds, "ln -sf " + symlink_target + " " + symlink_dir_in_recovery +  symlink)
+				}
+				fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD := " + strings.Join(cmds, " && "))
+			}
+			if m.unstrippedOutputFile != nil {
+				fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", m.unstrippedOutputFile.String())
+			}
+			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
+		},
+	}
+}
+
+func bionicMountpointFactory() android.Module {
+	m := &bionicMountpoint{}
+        m.AddProperties(&m.properties)
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibBoth)
+	return m
+}
+
+var Bool = proptools.Bool
+var String = proptools.String
diff --git a/dummy_mountpoint b/dummy_mountpoint
new file mode 100644
index 0000000..2d13c55
--- /dev/null
+++ b/dummy_mountpoint
@@ -0,0 +1 @@
+This file serves as a mount point for bionic files either from /system partition or from /apex/com.android.runtime. This file is never meant to be accessed directly.
diff --git a/libc/Android.bp b/libc/Android.bp
index e0d0fee..489918b 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -77,6 +77,14 @@
     native_coverage: false,
     recovery_available: true,
 
+    arch: {
+        x86: {
+            no_libcrt: true,
+        },
+        x86_64: {
+            no_libcrt: true,
+        },
+    },
     // TODO(ivanlozano): Remove after b/118321713
     xom: false,
 }
@@ -328,13 +336,10 @@
         "upstream-netbsd/lib/libc/regex/regexec.c",
         "upstream-netbsd/lib/libc/regex/regfree.c",
         "upstream-netbsd/lib/libc/stdlib/bsearch.c",
-        "upstream-netbsd/lib/libc/stdlib/div.c",
         "upstream-netbsd/lib/libc/stdlib/drand48.c",
         "upstream-netbsd/lib/libc/stdlib/erand48.c",
         "upstream-netbsd/lib/libc/stdlib/jrand48.c",
         "upstream-netbsd/lib/libc/stdlib/lcong48.c",
-        "upstream-netbsd/lib/libc/stdlib/ldiv.c",
-        "upstream-netbsd/lib/libc/stdlib/lldiv.c",
         "upstream-netbsd/lib/libc/stdlib/lrand48.c",
         "upstream-netbsd/lib/libc/stdlib/mrand48.c",
         "upstream-netbsd/lib/libc/stdlib/nrand48.c",
@@ -343,10 +348,6 @@
         "upstream-netbsd/lib/libc/stdlib/reallocarr.c",
         "upstream-netbsd/lib/libc/stdlib/seed48.c",
         "upstream-netbsd/lib/libc/stdlib/srand48.c",
-        "upstream-netbsd/lib/libc/string/memccpy.c",
-        "upstream-netbsd/lib/libc/string/strcasestr.c",
-        "upstream-netbsd/lib/libc/string/strcoll.c",
-        "upstream-netbsd/lib/libc/string/strxfrm.c",
     ],
     multilib: {
         lib32: {
@@ -456,19 +457,25 @@
         "upstream-openbsd/lib/libc/stdio/wbuf.c",
         "upstream-openbsd/lib/libc/stdio/wsetup.c",
         "upstream-openbsd/lib/libc/stdlib/abs.c",
+        "upstream-openbsd/lib/libc/stdlib/div.c",
         "upstream-openbsd/lib/libc/stdlib/getenv.c",
         "upstream-openbsd/lib/libc/stdlib/getsubopt.c",
         "upstream-openbsd/lib/libc/stdlib/insque.c",
         "upstream-openbsd/lib/libc/stdlib/imaxabs.c",
         "upstream-openbsd/lib/libc/stdlib/imaxdiv.c",
         "upstream-openbsd/lib/libc/stdlib/labs.c",
+        "upstream-openbsd/lib/libc/stdlib/ldiv.c",
         "upstream-openbsd/lib/libc/stdlib/llabs.c",
+        "upstream-openbsd/lib/libc/stdlib/lldiv.c",
         "upstream-openbsd/lib/libc/stdlib/lsearch.c",
         "upstream-openbsd/lib/libc/stdlib/remque.c",
         "upstream-openbsd/lib/libc/stdlib/setenv.c",
         "upstream-openbsd/lib/libc/stdlib/tfind.c",
         "upstream-openbsd/lib/libc/stdlib/tsearch.c",
+        "upstream-openbsd/lib/libc/string/memccpy.c",
         "upstream-openbsd/lib/libc/string/strcasecmp.c",
+        "upstream-openbsd/lib/libc/string/strcasestr.c",
+        "upstream-openbsd/lib/libc/string/strcoll.c",
         "upstream-openbsd/lib/libc/string/strcspn.c",
         "upstream-openbsd/lib/libc/string/strdup.c",
         "upstream-openbsd/lib/libc/string/strndup.c",
@@ -477,6 +484,7 @@
         "upstream-openbsd/lib/libc/string/strspn.c",
         "upstream-openbsd/lib/libc/string/strstr.c",
         "upstream-openbsd/lib/libc/string/strtok.c",
+        "upstream-openbsd/lib/libc/string/strxfrm.c",
         "upstream-openbsd/lib/libc/string/wcslcpy.c",
         "upstream-openbsd/lib/libc/string/wcswidth.c",
     ],
@@ -764,7 +772,7 @@
                 "arch-arm/bionic/atomics_arm.c",
                 "arch-arm/bionic/__bionic_clone.S",
                 "arch-arm/bionic/_exit_with_stack_teardown.S",
-                "arch-arm/bionic/libgcc_compat.c",
+                "arch-arm/bionic/libcrt_compat.c",
                 "arch-arm/bionic/popcount_tab.c",
                 "arch-arm/bionic/__restore.S",
                 "arch-arm/bionic/setjmp.S",
@@ -848,7 +856,7 @@
                 "arch-mips/bionic/__bionic_clone.S",
                 "arch-mips/bionic/cacheflush.cpp",
                 "arch-mips/bionic/_exit_with_stack_teardown.S",
-                "arch-mips/bionic/libgcc_compat.c",
+                "arch-mips/bionic/libcrt_compat.c",
                 "arch-mips/bionic/setjmp.S",
                 "arch-mips/bionic/syscall.S",
                 "arch-mips/bionic/vfork.S",
@@ -918,7 +926,7 @@
 
                 "arch-x86/bionic/__bionic_clone.S",
                 "arch-x86/bionic/_exit_with_stack_teardown.S",
-                "arch-x86/bionic/libgcc_compat.c",
+                "arch-x86/bionic/libcrt_compat.c",
                 "arch-x86/bionic/__restore.S",
                 "arch-x86/bionic/setjmp.S",
                 "arch-x86/bionic/syscall.S",
@@ -1014,6 +1022,7 @@
         "bionic/arpa_inet.cpp",
         "bionic/assert.cpp",
         "bionic/atof.cpp",
+        "bionic/bionic_allocator.cpp",
         "bionic/bionic_arc4random.cpp",
         "bionic/bionic_futex.cpp",
         "bionic/bionic_netlink.cpp",
@@ -1299,7 +1308,9 @@
 cc_library_static {
     name: "libc_ndk",
     defaults: ["libc_defaults"],
-    srcs: libc_common_src_files + ["bionic/malloc_common.cpp"],
+    srcs: libc_common_src_files + [
+        "bionic/malloc_common.cpp",
+    ],
     multilib: {
         lib32: {
             srcs: libc_common_src_files_32,
@@ -1492,6 +1503,8 @@
         "arch-common/bionic/crtbrand.S",
         "bionic/icu.cpp",
         "bionic/malloc_common.cpp",
+        "bionic/malloc_common_dynamic.cpp",
+        "bionic/malloc_heapprofd.cpp",
         "bionic/NetdClient.cpp",
         "arch-common/bionic/crtend_so.S",
     ],
@@ -1547,7 +1560,7 @@
         ],
     },
 
-    required: ["tzdata"],
+    required: ["tzdata", "libc.mountpoint"],
 
     // Leave the symbols in the shared library so that stack unwinders can produce
     // meaningful name resolution.
@@ -1612,6 +1625,8 @@
         symbol_file: "libc.map.txt",
         versions: ["10000"],
     },
+
+    symbol_ordering_file: "symbol_ordering",
 }
 
 genrule {
@@ -1646,6 +1661,59 @@
     cmd: "$(location :bionic-generate-version-script) x86_64 $(in) $(out)",
 }
 
+// libc_headers for libasync_safe and libpropertyinfoparser
+cc_library_headers {
+    name: "libc_headers",
+
+    host_supported: true,
+    vendor_available: true,
+    recovery_available: true,
+
+    no_libcrt: true,
+    no_libgcc: true,
+    stl: "none",
+    system_shared_libs: [],
+
+    export_include_dirs: [
+        "include",
+        "kernel/uapi",
+        "kernel/android/uapi",
+    ],
+
+    arch: {
+        arm: {
+            export_include_dirs: [
+                "kernel/uapi/asm-arm",
+            ],
+        },
+        arm64: {
+            export_include_dirs: [
+                "kernel/uapi/asm-arm64",
+            ],
+        },
+        mips: {
+            export_include_dirs: [
+                "kernel/uapi/asm-mips",
+            ],
+        },
+        mips64: {
+            export_include_dirs: [
+                "kernel/uapi/asm-mips",
+            ],
+        },
+        x86: {
+            export_include_dirs: [
+                "kernel/uapi/asm-x86",
+            ],
+        },
+        x86_64: {
+            export_include_dirs: [
+                "kernel/uapi/asm-x86",
+            ],
+        },
+    },
+}
+
 // ========================================================
 // libstdc++.so and libstdc++.a.
 // ========================================================
@@ -1999,47 +2067,39 @@
     symbol_file: "libc.map.txt",
     export_headers_as_system: true,
     export_preprocessed_headers: ["include"],
+    export_include_dirs: [
+        "kernel/android/uapi",
+        "kernel/uapi",
+    ],
     arch: {
         arm: {
             export_include_dirs: [
-                "kernel/uapi",
                 "kernel/uapi/asm-arm",
-                "kernel/android/uapi",
             ],
         },
         arm64: {
             export_include_dirs: [
-                "kernel/uapi",
                 "kernel/uapi/asm-arm64",
-                "kernel/android/uapi",
             ],
         },
         mips: {
             export_include_dirs: [
-                "kernel/uapi",
                 "kernel/uapi/asm-mips",
-                "kernel/android/uapi",
             ],
         },
         mips64: {
             export_include_dirs: [
-                "kernel/uapi",
                 "kernel/uapi/asm-mips",
-                "kernel/android/uapi",
             ],
         },
         x86: {
             export_include_dirs: [
-                "kernel/uapi",
                 "kernel/uapi/asm-x86",
-                "kernel/android/uapi",
             ],
         },
         x86_64: {
             export_include_dirs: [
-                "kernel/uapi",
                 "kernel/uapi/asm-x86",
-                "kernel/android/uapi",
             ],
         },
     },
@@ -2165,6 +2225,91 @@
     },
 }
 
+python_binary_host {
+    name: "genfunctosyscallnrs",
+    main: "tools/genfunctosyscallnrs.py",
+
+    srcs: [
+        "tools/genseccomp.py",
+        "tools/genfunctosyscallnrs.py",
+        "tools/gensyscalls.py",
+    ],
+
+    data: [
+        "kernel/uapi/**/*.h",
+    ],
+
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
+    },
+}
+
+cc_genrule {
+    name: "func_to_syscall_nrs",
+    recovery_available: true,
+    cmd: "$(location genfunctosyscallnrs) --out-dir=$(genDir) $(in)",
+
+    tools: [ "genfunctosyscallnrs" ],
+
+    srcs: [
+        "SYSCALLS.TXT",
+        ":libseccomp_gen_syscall_nrs_arm",
+        ":libseccomp_gen_syscall_nrs_arm64",
+        ":libseccomp_gen_syscall_nrs_mips",
+        ":libseccomp_gen_syscall_nrs_mips64",
+        ":libseccomp_gen_syscall_nrs_x86",
+        ":libseccomp_gen_syscall_nrs_x86_64",
+    ],
+
+    out: [
+        "func_to_syscall_nrs.h",
+    ],
+}
+
+// SECCOMP_BLACKLIST_APP_ZYGOTE.TXT = SECCOMP_BLACKLIST_APP.txt - setresgid*
+genrule {
+    name: "generate_app_zygote_blacklist",
+    out: ["SECCOMP_BLACKLIST_APP_ZYGOTE.TXT"],
+    srcs: ["SECCOMP_BLACKLIST_APP.TXT"],
+    cmd: "grep -v '^int[ \t]*setresgid' $(in) > $(out)",
+}
+
+cc_genrule {
+    name: "libseccomp_policy_app_zygote_sources",
+    recovery_available: true,
+    cmd: "$(location genseccomp) --out-dir=$(genDir) --name-modifier=app_zygote $(in)",
+
+    tools: [ "genseccomp" ],
+
+    srcs: [
+        "SYSCALLS.TXT",
+        "SECCOMP_WHITELIST_COMMON.TXT",
+        "SECCOMP_WHITELIST_APP.TXT",
+        "SECCOMP_BLACKLIST_COMMON.TXT",
+        ":generate_app_zygote_blacklist",
+        ":libseccomp_gen_syscall_nrs_arm",
+        ":libseccomp_gen_syscall_nrs_arm64",
+        ":libseccomp_gen_syscall_nrs_mips",
+        ":libseccomp_gen_syscall_nrs_mips64",
+        ":libseccomp_gen_syscall_nrs_x86",
+        ":libseccomp_gen_syscall_nrs_x86_64",
+    ],
+
+    out: [
+        "arm64_app_zygote_policy.cpp",
+        "arm_app_zygote_policy.cpp",
+        "mips64_app_zygote_policy.cpp",
+        "mips_app_zygote_policy.cpp",
+        "x86_64_app_zygote_policy.cpp",
+        "x86_app_zygote_policy.cpp",
+    ],
+}
+
 cc_genrule {
     name: "libseccomp_policy_app_sources",
     recovery_available: true,
@@ -2261,8 +2406,10 @@
 cc_library {
     name: "libseccomp_policy",
     recovery_available: true,
+    generated_headers: ["func_to_syscall_nrs"],
     generated_sources: [
         "libseccomp_policy_app_sources",
+        "libseccomp_policy_app_zygote_sources",
         "libseccomp_policy_global_sources",
         "libseccomp_policy_system_sources",
     ],
diff --git a/libc/SECCOMP_BLACKLIST_APP.TXT b/libc/SECCOMP_BLACKLIST_APP.TXT
index 66e24cb..b7a05c4 100644
--- a/libc/SECCOMP_BLACKLIST_APP.TXT
+++ b/libc/SECCOMP_BLACKLIST_APP.TXT
@@ -29,17 +29,21 @@
 # This file is processed by a python script named genseccomp.py.
 
 # Note: Some privileged syscalls are still needed in app process after fork before uid change,
-# including capset and setresuid.
+# including capset and setresuid. This is because the seccomp filter must be installed while
+# the process still has CAP_SYS_ADMIN; changing the uid would remove that capability.
 
 # syscalls to modify IDs
 int     setgid:setgid32(gid_t)     arm,x86
 int     setgid:setgid(gid_t)       arm64,mips,mips64,x86_64
 int     setuid:setuid32(uid_t)    arm,x86
 int     setuid:setuid(uid_t)      arm64,mips,mips64,x86_64
+int     setregid:setregid32(gid_t, gid_t)  arm,x86
+int     setregid:setregid(gid_t, gid_t)    arm64,mips,mips64,x86_64
 int     setreuid:setreuid32(uid_t, uid_t)   arm,x86
 int     setreuid:setreuid(uid_t, uid_t)     arm64,mips,mips64,x86_64
 int     setresgid:setresgid32(gid_t, gid_t, gid_t)   arm,x86
 int     setresgid:setresgid(gid_t, gid_t, gid_t)     arm64,mips,mips64,x86_64
+# setresuid is explicitly allowed, see above.
 int     setfsgid(gid_t)  all
 int     setfsuid(uid_t)  all
 int     setgroups:setgroups32(int, const gid_t*)   arm,x86
diff --git a/libc/arch-arm/bionic/libgcc_compat.c b/libc/arch-arm/bionic/libcrt_compat.c
similarity index 95%
rename from libc/arch-arm/bionic/libgcc_compat.c
rename to libc/arch-arm/bionic/libcrt_compat.c
index abd1422..22a3387 100644
--- a/libc/arch-arm/bionic/libgcc_compat.c
+++ b/libc/arch-arm/bionic/libcrt_compat.c
@@ -90,6 +90,8 @@
 extern char __floatunsisf;
 extern char __gedf2;
 extern char __gtdf2;
+extern char __gnu_ldivmod_helper;
+extern char __gnu_uldivmod_helper;
 extern char __ledf2;
 extern char __ltdf2;
 extern char __muldf3;
@@ -101,10 +103,11 @@
 extern char __subdf3;
 extern char __subsf3;
 extern char __truncdfsf2;
+extern char __udivdi3;
 extern char __unorddf2;
 extern char __unordsf2;
 
-void* __bionic_libgcc_compat_symbols[] = {
+void* __bionic_libcrt_compat_symbols[] = {
     &__adddf3,
     &__addsf3,
     &__aeabi_cdcmpeq,
@@ -169,6 +172,8 @@
     &__floatunsisf,
     &__gedf2,
     &__gtdf2,
+    &__gnu_ldivmod_helper,
+    &__gnu_uldivmod_helper,
     &__ledf2,
     &__ltdf2,
     &__muldf3,
@@ -180,6 +185,7 @@
     &__subdf3,
     &__subsf3,
     &__truncdfsf2,
+    &__udivdi3,
     &__unorddf2,
     &__unordsf2,
 };
diff --git a/libc/arch-arm/bionic/vfork.S b/libc/arch-arm/bionic/vfork.S
index 8329111..0b17d64 100644
--- a/libc/arch-arm/bionic/vfork.S
+++ b/libc/arch-arm/bionic/vfork.S
@@ -27,12 +27,13 @@
  */
 
 #include <private/bionic_asm.h>
+#include <private/bionic_asm_tls.h>
 
 ENTRY(vfork)
 __BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
     // __get_tls()[TLS_SLOT_THREAD_ID]->cached_pid_ = 0
     mrc     p15, 0, r3, c13, c0, 3
-    ldr     r3, [r3, #4]
+    ldr     r3, [r3, #(TLS_SLOT_THREAD_ID * 4)]
     mov     r0, #0
     str     r0, [r3, #12]
 
diff --git a/libc/arch-arm64/bionic/vfork.S b/libc/arch-arm64/bionic/vfork.S
index 0a83cc7..6acd64b 100644
--- a/libc/arch-arm64/bionic/vfork.S
+++ b/libc/arch-arm64/bionic/vfork.S
@@ -27,6 +27,7 @@
  */
 
 #include <private/bionic_asm.h>
+#include <private/bionic_asm_tls.h>
 #include <asm/signal.h>
 #include <linux/sched.h>
 
@@ -34,7 +35,7 @@
 __BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
     // __get_tls()[TLS_SLOT_THREAD_ID]->cached_pid_ = 0
     mrs     x0, tpidr_el0
-    ldr     x0, [x0, #8]
+    ldr     x0, [x0, #(TLS_SLOT_THREAD_ID * 8)]
     str     wzr, [x0, #20]
 
     mov     x0, #(CLONE_VM | CLONE_VFORK | SIGCHLD)
diff --git a/libc/arch-mips/bionic/libgcc_compat.c b/libc/arch-mips/bionic/libcrt_compat.c
similarity index 97%
rename from libc/arch-mips/bionic/libgcc_compat.c
rename to libc/arch-mips/bionic/libcrt_compat.c
index 1a0f566..cfa41f2 100644
--- a/libc/arch-mips/bionic/libgcc_compat.c
+++ b/libc/arch-mips/bionic/libcrt_compat.c
@@ -32,7 +32,7 @@
 extern char __udivdi3;
 extern char __umoddi3;
 
-void* __bionic_libgcc_compat_symbols[] = {
+void* __bionic_libcrt_compat_symbols[] = {
     &__divdi3,
     &__moddi3,
     &__popcountsi2,
diff --git a/libc/arch-x86/bionic/__libc_init_sysinfo.cpp b/libc/arch-x86/bionic/__libc_init_sysinfo.cpp
index 849d253..5c44b4e 100644
--- a/libc/arch-x86/bionic/__libc_init_sysinfo.cpp
+++ b/libc/arch-x86/bionic/__libc_init_sysinfo.cpp
@@ -32,6 +32,10 @@
 // This file is compiled without stack protection, because it runs before TLS
 // has been set up.
 
+__LIBC_HIDDEN__ __attribute__((__naked__)) void __libc_int0x80() {
+  __asm__ volatile("int $0x80; ret");
+}
+
 __LIBC_HIDDEN__ void __libc_init_sysinfo() {
   bool dummy;
   __libc_sysinfo = reinterpret_cast<void*>(__bionic_getauxval(AT_SYSINFO, dummy));
diff --git a/libc/arch-x86/bionic/libgcc_compat.c b/libc/arch-x86/bionic/libcrt_compat.c
similarity index 97%
rename from libc/arch-x86/bionic/libgcc_compat.c
rename to libc/arch-x86/bionic/libcrt_compat.c
index 1a0f566..cfa41f2 100644
--- a/libc/arch-x86/bionic/libgcc_compat.c
+++ b/libc/arch-x86/bionic/libcrt_compat.c
@@ -32,7 +32,7 @@
 extern char __udivdi3;
 extern char __umoddi3;
 
-void* __bionic_libgcc_compat_symbols[] = {
+void* __bionic_libcrt_compat_symbols[] = {
     &__divdi3,
     &__moddi3,
     &__popcountsi2,
diff --git a/libc/arch-x86/bionic/vfork.S b/libc/arch-x86/bionic/vfork.S
index 79d7899..24ede3d 100644
--- a/libc/arch-x86/bionic/vfork.S
+++ b/libc/arch-x86/bionic/vfork.S
@@ -27,6 +27,7 @@
  */
 
 #include <private/bionic_asm.h>
+#include <private/bionic_asm_tls.h>
 
 // This custom code preserves the return address across the system call.
 
@@ -38,7 +39,7 @@
 
   // __get_tls()[TLS_SLOT_THREAD_ID]->cached_pid_ = 0
   movl    %gs:0, %eax
-  movl    4(%eax), %eax
+  movl    (TLS_SLOT_THREAD_ID * 4)(%eax), %eax
   movl    $0, 12(%eax)
 
   movl    $__NR_vfork, %eax
diff --git a/libc/arch-x86_64/bionic/vfork.S b/libc/arch-x86_64/bionic/vfork.S
index ce96a8c..e32b517 100644
--- a/libc/arch-x86_64/bionic/vfork.S
+++ b/libc/arch-x86_64/bionic/vfork.S
@@ -27,6 +27,7 @@
  */
 
 #include <private/bionic_asm.h>
+#include <private/bionic_asm_tls.h>
 
 // This custom code preserves the return address across the system call.
 
@@ -36,7 +37,7 @@
 
   // __get_tls()[TLS_SLOT_THREAD_ID]->cached_pid_ = 0
   mov    %fs:0, %rax
-  mov    8(%rax), %rax
+  mov    (TLS_SLOT_THREAD_ID * 8)(%rax), %rax
   movl   $0, 20(%rax)
 
   movl    $__NR_vfork, %eax
diff --git a/libc/async_safe/Android.bp b/libc/async_safe/Android.bp
index 29f90d1..fbaaad1 100644
--- a/libc/async_safe/Android.bp
+++ b/libc/async_safe/Android.bp
@@ -12,6 +12,20 @@
     recovery_available: true,
 
     include_dirs: ["bionic/libc"],
+    header_libs: ["libc_headers", "liblog_headers"],
 
     export_include_dirs: ["include"],
+    export_header_lib_headers: ["liblog_headers"],
+    stl: "none",
+}
+
+cc_library_headers {
+    name: "libasync_safe_headers",
+    recovery_available: true,
+    defaults: ["linux_bionic_supported"],
+
+    export_include_dirs: ["include"],
+
+    system_shared_libs: [],
+    stl: "none",
 }
diff --git a/libc/arch-mips/bionic/libgcc_compat.c b/libc/async_safe/include/async_safe/CHECK.h
similarity index 74%
copy from libc/arch-mips/bionic/libgcc_compat.c
copy to libc/async_safe/include/async_safe/CHECK.h
index 1a0f566..bec89a3 100644
--- a/libc/arch-mips/bionic/libgcc_compat.c
+++ b/libc/async_safe/include/async_safe/CHECK.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,16 +26,22 @@
  * SUCH DAMAGE.
  */
 
-extern char __divdi3;
-extern char __moddi3;
-extern char __popcountsi2;
-extern char __udivdi3;
-extern char __umoddi3;
+#pragma once
 
-void* __bionic_libgcc_compat_symbols[] = {
-    &__divdi3,
-    &__moddi3,
-    &__popcountsi2,
-    &__udivdi3,
-    &__umoddi3,
-};
+#include <sys/cdefs.h>
+
+#include <async_safe/log.h>
+
+__BEGIN_DECLS
+
+// TODO: replace this with something more like <android-base/logging.h>'s family of macros.
+
+#define CHECK(predicate) \
+  do { \
+    if (!(predicate)) { \
+      async_safe_fatal("%s:%d: %s CHECK '" #predicate "' failed", \
+          __FILE__, __LINE__, __FUNCTION__); \
+    } \
+  } while(0)
+
+__END_DECLS
diff --git a/libc/async_safe/include/async_safe/log.h b/libc/async_safe/include/async_safe/log.h
index df68062..0e02ea7 100644
--- a/libc/async_safe/include/async_safe/log.h
+++ b/libc/async_safe/include/async_safe/log.h
@@ -34,36 +34,14 @@
 #include <stdint.h>
 #include <stdlib.h>
 
+// This file is an alternative to <android/log.h>, but reuses
+// `android_LogPriority` and should not have conflicting identifiers.
+#include <android/log.h>
+
 // These functions do not allocate memory to send data to the log.
 
 __BEGIN_DECLS
 
-enum {
-  ANDROID_LOG_UNKNOWN = 0,
-  ANDROID_LOG_DEFAULT,    /* only for SetMinPriority() */
-
-  ANDROID_LOG_VERBOSE,
-  ANDROID_LOG_DEBUG,
-  ANDROID_LOG_INFO,
-  ANDROID_LOG_WARN,
-  ANDROID_LOG_ERROR,
-  ANDROID_LOG_FATAL,
-
-  ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */
-};
-
-enum {
-  LOG_ID_MIN = 0,
-
-  LOG_ID_MAIN = 0,
-  LOG_ID_RADIO = 1,
-  LOG_ID_EVENTS = 2,
-  LOG_ID_SYSTEM = 3,
-  LOG_ID_CRASH = 4,
-
-  LOG_ID_MAX
-};
-
 // Formats a message to the log (priority 'fatal'), then aborts.
 // Implemented as a macro so that async_safe_fatal isn't on the stack when we crash:
 // we appear to go straight from the caller to abort, saving an uninteresting stack
@@ -91,16 +69,8 @@
 
 int async_safe_format_fd(int fd, const char* format , ...) __printflike(2, 3);
 int async_safe_format_fd_va_list(int fd, const char* format, va_list args);
-int async_safe_format_log(int pri, const char* tag, const char* fmt, ...) __printflike(3, 4);
-int async_safe_format_log_va_list(int pri, const char* tag, const char* fmt, va_list ap);
-int async_safe_write_log(int pri, const char* tag, const char* msg);
-
-#define CHECK(predicate) \
-  do { \
-    if (!(predicate)) { \
-      async_safe_fatal("%s:%d: %s CHECK '" #predicate "' failed", \
-          __FILE__, __LINE__, __FUNCTION__); \
-    } \
-  } while(0)
+int async_safe_format_log(int priority, const char* tag, const char* fmt, ...) __printflike(3, 4);
+int async_safe_format_log_va_list(int priority, const char* tag, const char* fmt, va_list ap);
+int async_safe_write_log(int priority, const char* tag, const char* msg);
 
 __END_DECLS
diff --git a/libc/bionic/__libc_init_main_thread.cpp b/libc/bionic/__libc_init_main_thread.cpp
index 2b90c90..4984e38 100644
--- a/libc/bionic/__libc_init_main_thread.cpp
+++ b/libc/bionic/__libc_init_main_thread.cpp
@@ -74,6 +74,7 @@
   __libc_init_sysinfo(); // uses AT_SYSINFO auxv entry
 #endif
   __init_tcb(temp_tcb, &main_thread);
+  __init_tcb_dtv(temp_tcb);
   __set_tls(&temp_tcb->tls_slot(0));
   main_thread.tid = __getpid();
   main_thread.set_cached_pid(main_thread.tid);
@@ -126,6 +127,7 @@
   auto new_tcb = reinterpret_cast<bionic_tcb*>(mapping.static_tls + layout.offset_bionic_tcb());
   auto new_tls = reinterpret_cast<bionic_tls*>(mapping.static_tls + layout.offset_bionic_tls());
 
+  __init_static_tls(mapping.static_tls);
   new_tcb->copy_from_bootstrap(temp_tcb);
   new_tls->copy_from_bootstrap(temp_tls);
   __init_tcb(new_tcb, &main_thread);
diff --git a/linker/linker_allocator.cpp b/libc/bionic/bionic_allocator.cpp
similarity index 77%
rename from linker/linker_allocator.cpp
rename to libc/bionic/bionic_allocator.cpp
index 015768a..da0f7d0 100644
--- a/linker/linker_allocator.cpp
+++ b/libc/bionic/bionic_allocator.cpp
@@ -26,21 +26,26 @@
  * SUCH DAMAGE.
  */
 
-#include "linker_allocator.h"
-#include "linker_debug.h"
-#include "linker.h"
+#include "private/bionic_allocator.h"
 
 #include <stdlib.h>
+#include <string.h>
 #include <sys/mman.h>
+#include <sys/param.h>
 #include <sys/prctl.h>
 #include <unistd.h>
 
+#include <new>
+
 #include <async_safe/log.h>
+#include <async_safe/CHECK.h>
+
+#include "private/bionic_macros.h"
+#include "private/bionic_page.h"
 
 //
-// LinkerMemeoryAllocator is general purpose allocator
-// designed to provide the same functionality as the malloc/free/realloc
-// libc functions.
+// BionicAllocator is a general purpose allocator designed to provide the same
+// functionality as the malloc/free/realloc libc functions.
 //
 // On alloc:
 // If size is >= 1k allocator proxies malloc call directly to mmap
@@ -90,7 +95,7 @@
   return result;
 }
 
-LinkerSmallObjectAllocator::LinkerSmallObjectAllocator(uint32_t type,
+BionicSmallObjectAllocator::BionicSmallObjectAllocator(uint32_t type,
                                                        size_t block_size)
     : type_(type),
       block_size_(block_size),
@@ -99,7 +104,7 @@
       free_pages_cnt_(0),
       page_list_(nullptr) {}
 
-void* LinkerSmallObjectAllocator::alloc() {
+void* BionicSmallObjectAllocator::alloc() {
   CHECK(block_size_ != 0);
 
   if (page_list_ == nullptr) {
@@ -141,7 +146,7 @@
   return block_record;
 }
 
-void LinkerSmallObjectAllocator::free_page(small_object_page_info* page) {
+void BionicSmallObjectAllocator::free_page(small_object_page_info* page) {
   CHECK(page->free_blocks_cnt == blocks_per_page_);
   if (page->prev_page) {
     page->prev_page->next_page = page->next_page;
@@ -156,7 +161,7 @@
   free_pages_cnt_--;
 }
 
-void LinkerSmallObjectAllocator::free(void* ptr) {
+void BionicSmallObjectAllocator::free(void* ptr) {
   small_object_page_info* const page =
       reinterpret_cast<small_object_page_info*>(
           PAGE_START(reinterpret_cast<uintptr_t>(ptr)));
@@ -186,7 +191,7 @@
   }
 }
 
-void LinkerSmallObjectAllocator::alloc_page() {
+void BionicSmallObjectAllocator::alloc_page() {
   void* const map_ptr = mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE,
                              MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
   if (map_ptr == MAP_FAILED) {
@@ -194,7 +199,7 @@
   }
 
   prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_ptr, PAGE_SIZE,
-        "linker_alloc_small_objects");
+        "bionic_alloc_small_objects");
 
   small_object_page_info* const page =
       reinterpret_cast<small_object_page_info*>(map_ptr);
@@ -220,7 +225,7 @@
   free_pages_cnt_++;
 }
 
-void LinkerSmallObjectAllocator::add_to_page_list(small_object_page_info* page) {
+void BionicSmallObjectAllocator::add_to_page_list(small_object_page_info* page) {
   page->next_page = page_list_;
   page->prev_page = nullptr;
   if (page_list_) {
@@ -229,7 +234,7 @@
   page_list_ = page;
 }
 
-void LinkerSmallObjectAllocator::remove_from_page_list(
+void BionicSmallObjectAllocator::remove_from_page_list(
     small_object_page_info* page) {
   if (page->prev_page) {
     page->prev_page->next_page = page->next_page;
@@ -244,24 +249,30 @@
   page->next_page = nullptr;
 }
 
-void LinkerMemoryAllocator::initialize_allocators() {
+void BionicAllocator::initialize_allocators() {
   if (allocators_ != nullptr) {
     return;
   }
 
-  LinkerSmallObjectAllocator* allocators =
-      reinterpret_cast<LinkerSmallObjectAllocator*>(allocators_buf_);
+  BionicSmallObjectAllocator* allocators =
+      reinterpret_cast<BionicSmallObjectAllocator*>(allocators_buf_);
 
   for (size_t i = 0; i < kSmallObjectAllocatorsCount; ++i) {
     uint32_t type = i + kSmallObjectMinSizeLog2;
-    new (allocators + i) LinkerSmallObjectAllocator(type, 1 << type);
+    new (allocators + i) BionicSmallObjectAllocator(type, 1 << type);
   }
 
   allocators_ = allocators;
 }
 
-void* LinkerMemoryAllocator::alloc_mmap(size_t size) {
-  size_t allocated_size = PAGE_END(size + kPageInfoSize);
+void* BionicAllocator::alloc_mmap(size_t align, size_t size) {
+  size_t header_size = __BIONIC_ALIGN(kPageInfoSize, align);
+  size_t allocated_size;
+  if (__builtin_add_overflow(header_size, size, &allocated_size) ||
+      PAGE_END(allocated_size) < allocated_size) {
+    async_safe_fatal("overflow trying to alloc %zu bytes", size);
+  }
+  allocated_size = PAGE_END(allocated_size);
   void* map_ptr = mmap(nullptr, allocated_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,
                        -1, 0);
 
@@ -269,25 +280,21 @@
     async_safe_fatal("mmap failed: %s", strerror(errno));
   }
 
-  prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_ptr, allocated_size, "linker_alloc_lob");
+  prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_ptr, allocated_size, "bionic_alloc_lob");
 
-  page_info* info = reinterpret_cast<page_info*>(map_ptr);
+  void* result = static_cast<char*>(map_ptr) + header_size;
+  page_info* info = get_page_info_unchecked(result);
   memcpy(info->signature, kSignature, sizeof(kSignature));
   info->type = kLargeObject;
   info->allocated_size = allocated_size;
 
-  return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(info) +
-                                 kPageInfoSize);
+  return result;
 }
 
-void* LinkerMemoryAllocator::alloc(size_t size) {
-  // treat alloc(0) as alloc(1)
-  if (size == 0) {
-    size = 1;
-  }
 
+inline void* BionicAllocator::alloc_impl(size_t align, size_t size) {
   if (size > kSmallObjectMaxSize) {
-    return alloc_mmap(size);
+    return alloc_mmap(align, size);
   }
 
   uint16_t log2_size = log2(size);
@@ -299,8 +306,33 @@
   return get_small_object_allocator(log2_size)->alloc();
 }
 
-page_info* LinkerMemoryAllocator::get_page_info(void* ptr) {
-  page_info* info = reinterpret_cast<page_info*>(PAGE_START(reinterpret_cast<size_t>(ptr)));
+void* BionicAllocator::alloc(size_t size) {
+  // treat alloc(0) as alloc(1)
+  if (size == 0) {
+    size = 1;
+  }
+  return alloc_impl(16, size);
+}
+
+void* BionicAllocator::memalign(size_t align, size_t size) {
+  // The Bionic allocator only supports alignment up to one page, which is good
+  // enough for ELF TLS.
+  align = MIN(align, PAGE_SIZE);
+  align = MAX(align, 16);
+  if (!powerof2(align)) {
+    align = BIONIC_ROUND_UP_POWER_OF_2(align);
+  }
+  size = MAX(size, align);
+  return alloc_impl(align, size);
+}
+
+inline page_info* BionicAllocator::get_page_info_unchecked(void* ptr) {
+  uintptr_t header_page = PAGE_START(reinterpret_cast<size_t>(ptr) - kPageInfoSize);
+  return reinterpret_cast<page_info*>(header_page);
+}
+
+inline page_info* BionicAllocator::get_page_info(void* ptr) {
+  page_info* info = get_page_info_unchecked(ptr);
   if (memcmp(info->signature, kSignature, sizeof(kSignature)) != 0) {
     async_safe_fatal("invalid pointer %p (page signature mismatch)", ptr);
   }
@@ -308,7 +340,7 @@
   return info;
 }
 
-void* LinkerMemoryAllocator::realloc(void* ptr, size_t size) {
+void* BionicAllocator::realloc(void* ptr, size_t size) {
   if (ptr == nullptr) {
     return alloc(size);
   }
@@ -323,9 +355,9 @@
   size_t old_size = 0;
 
   if (info->type == kLargeObject) {
-    old_size = info->allocated_size - kPageInfoSize;
+    old_size = info->allocated_size - (static_cast<char*>(ptr) - reinterpret_cast<char*>(info));
   } else {
-    LinkerSmallObjectAllocator* allocator = get_small_object_allocator(info->type);
+    BionicSmallObjectAllocator* allocator = get_small_object_allocator(info->type);
     if (allocator != info->allocator_addr) {
       async_safe_fatal("invalid pointer %p (page signature mismatch)", ptr);
     }
@@ -343,7 +375,7 @@
   return ptr;
 }
 
-void LinkerMemoryAllocator::free(void* ptr) {
+void BionicAllocator::free(void* ptr) {
   if (ptr == nullptr) {
     return;
   }
@@ -353,7 +385,7 @@
   if (info->type == kLargeObject) {
     munmap(info, info->allocated_size);
   } else {
-    LinkerSmallObjectAllocator* allocator = get_small_object_allocator(info->type);
+    BionicSmallObjectAllocator* allocator = get_small_object_allocator(info->type);
     if (allocator != info->allocator_addr) {
       async_safe_fatal("invalid pointer %p (invalid allocator address for the page)", ptr);
     }
@@ -362,7 +394,7 @@
   }
 }
 
-LinkerSmallObjectAllocator* LinkerMemoryAllocator::get_small_object_allocator(uint32_t type) {
+BionicSmallObjectAllocator* BionicAllocator::get_small_object_allocator(uint32_t type) {
   if (type < kSmallObjectMinSizeLog2 || type > kSmallObjectMaxSizeLog2) {
     async_safe_fatal("invalid type: %u", type);
   }
diff --git a/libc/bionic/bionic_elf_tls.cpp b/libc/bionic/bionic_elf_tls.cpp
index 55c2c31..3fa5182 100644
--- a/libc/bionic/bionic_elf_tls.cpp
+++ b/libc/bionic/bionic_elf_tls.cpp
@@ -28,13 +28,117 @@
 
 #include "private/bionic_elf_tls.h"
 
+#include <async_safe/log.h>
+#include <string.h>
 #include <sys/param.h>
+#include <unistd.h>
 
+#include "private/ScopedRWLock.h"
+#include "private/ScopedSignalBlocker.h"
+#include "private/bionic_globals.h"
 #include "private/bionic_macros.h"
 #include "private/bionic_tls.h"
+#include "pthread_internal.h"
 
-void StaticTlsLayout::reserve_tcb() {
-  offset_bionic_tcb_ = reserve_type<bionic_tcb>();
+// Every call to __tls_get_addr needs to check the generation counter, so
+// accesses to the counter need to be as fast as possible. Keep a copy of it in
+// a hidden variable, which can be accessed without using the GOT. The linker
+// will update this variable when it updates its counter.
+//
+// To allow the linker to update this variable, libc.so's constructor passes its
+// address to the linker. To accommodate a possible __tls_get_addr call before
+// libc.so's constructor, this local copy is initialized to SIZE_MAX, forcing
+// __tls_get_addr to initially use the slow path.
+__LIBC_HIDDEN__ _Atomic(size_t) __libc_tls_generation_copy = SIZE_MAX;
+
+// Search for a TLS segment in the given phdr table. Returns true if it has a
+// TLS segment and false otherwise.
+bool __bionic_get_tls_segment(const ElfW(Phdr)* phdr_table, size_t phdr_count,
+                              ElfW(Addr) load_bias, TlsSegment* out) {
+  for (size_t i = 0; i < phdr_count; ++i) {
+    const ElfW(Phdr)& phdr = phdr_table[i];
+    if (phdr.p_type == PT_TLS) {
+      *out = TlsSegment {
+        phdr.p_memsz,
+        phdr.p_align,
+        reinterpret_cast<void*>(load_bias + phdr.p_vaddr),
+        phdr.p_filesz,
+      };
+      return true;
+    }
+  }
+  return false;
+}
+
+// Return true if the alignment of a TLS segment is a valid power-of-two. Also
+// cap the alignment if it's too high.
+bool __bionic_check_tls_alignment(size_t* alignment) {
+  // N.B. The size does not need to be a multiple of the alignment. With
+  // ld.bfd (or after using binutils' strip), the TLS segment's size isn't
+  // rounded up.
+  if (*alignment == 0 || !powerof2(*alignment)) {
+    return false;
+  }
+  // Bionic only respects TLS alignment up to one page.
+  *alignment = MIN(*alignment, PAGE_SIZE);
+  return true;
+}
+
+size_t StaticTlsLayout::offset_thread_pointer() const {
+  return offset_bionic_tcb_ + (-MIN_TLS_SLOT * sizeof(void*));
+}
+
+// Reserves space for the Bionic TCB and the executable's TLS segment. Returns
+// the offset of the executable's TLS segment.
+size_t StaticTlsLayout::reserve_exe_segment_and_tcb(const TlsSegment* exe_segment,
+                                                    const char* progname __attribute__((unused))) {
+  // Special case: if the executable has no TLS segment, then just allocate a
+  // TCB and skip the minimum alignment check on ARM.
+  if (exe_segment == nullptr) {
+    offset_bionic_tcb_ = reserve_type<bionic_tcb>();
+    return 0;
+  }
+
+#if defined(__arm__) || defined(__aarch64__)
+
+  // First reserve enough space for the TCB before the executable segment.
+  reserve(sizeof(bionic_tcb), 1);
+
+  // Then reserve the segment itself.
+  const size_t result = reserve(exe_segment->size, exe_segment->alignment);
+
+  // The variant 1 ABI that ARM linkers follow specifies a 2-word TCB between
+  // the thread pointer and the start of the executable's TLS segment, but both
+  // the thread pointer and the TLS segment are aligned appropriately for the
+  // TLS segment. Calculate the distance between the thread pointer and the
+  // EXE's segment.
+  const size_t exe_tpoff = __BIONIC_ALIGN(sizeof(void*) * 2, exe_segment->alignment);
+
+  const size_t min_bionic_alignment = BIONIC_ROUND_UP_POWER_OF_2(MAX_TLS_SLOT) * sizeof(void*);
+  if (exe_tpoff < min_bionic_alignment) {
+    async_safe_fatal("error: \"%s\": executable's TLS segment is underaligned: "
+                     "alignment is %zu, needs to be at least %zu for %s Bionic",
+                     progname, exe_segment->alignment, min_bionic_alignment,
+                     (sizeof(void*) == 4 ? "ARM" : "ARM64"));
+  }
+
+  offset_bionic_tcb_ = result - exe_tpoff - (-MIN_TLS_SLOT * sizeof(void*));
+  return result;
+
+#elif defined(__i386__) || defined(__x86_64__)
+
+  // x86 uses variant 2 TLS layout. The executable's segment is located just
+  // before the TCB.
+  static_assert(MIN_TLS_SLOT == 0, "First slot of bionic_tcb must be slot #0 on x86");
+  const size_t exe_size = round_up_with_overflow_check(exe_segment->size, exe_segment->alignment);
+  reserve(exe_size, 1);
+  const size_t max_align = MAX(alignof(bionic_tcb), exe_segment->alignment);
+  offset_bionic_tcb_ = reserve(sizeof(bionic_tcb), max_align);
+  return offset_bionic_tcb_ - exe_size;
+
+#else
+#error "Unrecognized architecture"
+#endif
 }
 
 void StaticTlsLayout::reserve_bionic_tls() {
@@ -44,6 +148,10 @@
 void StaticTlsLayout::finish_layout() {
   // Round the offset up to the alignment.
   offset_ = round_up_with_overflow_check(offset_, alignment_);
+
+  if (overflowed_) {
+    async_safe_fatal("error: TLS segments in static TLS overflowed");
+  }
 }
 
 // The size is not required to be a multiple of the alignment. The alignment
@@ -62,3 +170,197 @@
   if (value < old_value) overflowed_ = true;
   return value;
 }
+
+// Copy each TLS module's initialization image into a newly-allocated block of
+// static TLS memory. To reduce dirty pages, this function only writes to pages
+// within the static TLS that need initialization. The memory should already be
+// zero-initialized on entry.
+void __init_static_tls(void* static_tls) {
+  // The part of the table we care about (i.e. static TLS modules) never changes
+  // after startup, but we still need the mutex because the table could grow,
+  // moving the initial part. If this locking is too slow, we can duplicate the
+  // static part of the table.
+  TlsModules& modules = __libc_shared_globals()->tls_modules;
+  ScopedSignalBlocker ssb;
+  ScopedReadLock locker(&modules.rwlock);
+
+  for (size_t i = 0; i < modules.module_count; ++i) {
+    TlsModule& module = modules.module_table[i];
+    if (module.static_offset == SIZE_MAX) {
+      // All of the static modules come before all of the dynamic modules, so
+      // once we see the first dynamic module, we're done.
+      break;
+    }
+    if (module.segment.init_size == 0) {
+      // Skip the memcpy call for TLS segments with no initializer, which is
+      // common.
+      continue;
+    }
+    memcpy(static_cast<char*>(static_tls) + module.static_offset,
+           module.segment.init_ptr,
+           module.segment.init_size);
+  }
+}
+
+static inline size_t dtv_size_in_bytes(size_t module_count) {
+  return sizeof(TlsDtv) + module_count * sizeof(void*);
+}
+
+// Calculates the number of module slots to allocate in a new DTV. For small
+// objects (up to 1KiB), the TLS allocator allocates memory in power-of-2 sizes,
+// so for better space usage, ensure that the DTV size (header + slots) is a
+// power of 2.
+//
+// The lock on TlsModules must be held.
+static size_t calculate_new_dtv_count() {
+  size_t loaded_cnt = __libc_shared_globals()->tls_modules.module_count;
+  size_t bytes = dtv_size_in_bytes(MAX(1, loaded_cnt));
+  if (!powerof2(bytes)) {
+    bytes = BIONIC_ROUND_UP_POWER_OF_2(bytes);
+  }
+  return (bytes - sizeof(TlsDtv)) / sizeof(void*);
+}
+
+// This function must be called with signals blocked and a write lock on
+// TlsModules held.
+static void update_tls_dtv(bionic_tcb* tcb) {
+  const TlsModules& modules = __libc_shared_globals()->tls_modules;
+  BionicAllocator& allocator = __libc_shared_globals()->tls_allocator;
+
+  // Use the generation counter from the shared globals instead of the local
+  // copy, which won't be initialized yet if __tls_get_addr is called before
+  // libc.so's constructor.
+  if (__get_tcb_dtv(tcb)->generation == atomic_load(&modules.generation)) {
+    return;
+  }
+
+  const size_t old_cnt = __get_tcb_dtv(tcb)->count;
+
+  // If the DTV isn't large enough, allocate a larger one. Because a signal
+  // handler could interrupt the fast path of __tls_get_addr, we don't free the
+  // old DTV. Instead, we add the old DTV to a list, then free all of a thread's
+  // DTVs at thread-exit. Each time the DTV is reallocated, its size at least
+  // doubles.
+  if (modules.module_count > old_cnt) {
+    size_t new_cnt = calculate_new_dtv_count();
+    TlsDtv* const old_dtv = __get_tcb_dtv(tcb);
+    TlsDtv* const new_dtv = static_cast<TlsDtv*>(allocator.alloc(dtv_size_in_bytes(new_cnt)));
+    memcpy(new_dtv, old_dtv, dtv_size_in_bytes(old_cnt));
+    new_dtv->count = new_cnt;
+    new_dtv->next = old_dtv;
+    __set_tcb_dtv(tcb, new_dtv);
+  }
+
+  TlsDtv* const dtv = __get_tcb_dtv(tcb);
+
+  const StaticTlsLayout& layout = __libc_shared_globals()->static_tls_layout;
+  char* static_tls = reinterpret_cast<char*>(tcb) - layout.offset_bionic_tcb();
+
+  // Initialize static TLS modules and free unloaded modules.
+  for (size_t i = 0; i < dtv->count; ++i) {
+    if (i < modules.module_count) {
+      const TlsModule& mod = modules.module_table[i];
+      if (mod.static_offset != SIZE_MAX) {
+        dtv->modules[i] = static_tls + mod.static_offset;
+        continue;
+      }
+      if (mod.first_generation != kTlsGenerationNone &&
+          mod.first_generation <= dtv->generation) {
+        continue;
+      }
+    }
+    allocator.free(dtv->modules[i]);
+    dtv->modules[i] = nullptr;
+  }
+
+  dtv->generation = atomic_load(&modules.generation);
+}
+
+__attribute__((noinline)) static void* tls_get_addr_slow_path(const TlsIndex* ti) {
+  TlsModules& modules = __libc_shared_globals()->tls_modules;
+  bionic_tcb* tcb = __get_bionic_tcb();
+
+  // Block signals and lock TlsModules. We may need the allocator, so take
+  // a write lock.
+  ScopedSignalBlocker ssb;
+  ScopedWriteLock locker(&modules.rwlock);
+
+  update_tls_dtv(tcb);
+
+  TlsDtv* dtv = __get_tcb_dtv(tcb);
+  const size_t module_idx = __tls_module_id_to_idx(ti->module_id);
+  void* mod_ptr = dtv->modules[module_idx];
+  if (mod_ptr == nullptr) {
+    const TlsSegment& segment = modules.module_table[module_idx].segment;
+    mod_ptr = __libc_shared_globals()->tls_allocator.memalign(segment.alignment, segment.size);
+    if (segment.init_size > 0) {
+      memcpy(mod_ptr, segment.init_ptr, segment.init_size);
+    }
+    dtv->modules[module_idx] = mod_ptr;
+  }
+
+  return static_cast<char*>(mod_ptr) + ti->offset;
+}
+
+// Returns the address of a thread's TLS memory given a module ID and an offset
+// into that module's TLS segment. This function is called on every access to a
+// dynamic TLS variable on targets that don't use TLSDESC. arm64 uses TLSDESC,
+// so it only calls this function on a thread's first access to a module's TLS
+// segment.
+//
+// On most targets, this accessor function is __tls_get_addr and
+// TLS_GET_ADDR_CCONV is unset. 32-bit x86 uses ___tls_get_addr instead and a
+// regparm() calling convention.
+extern "C" void* TLS_GET_ADDR(const TlsIndex* ti) TLS_GET_ADDR_CCONV {
+  TlsDtv* dtv = __get_tcb_dtv(__get_bionic_tcb());
+
+  // TODO: See if we can use a relaxed memory ordering here instead.
+  size_t generation = atomic_load(&__libc_tls_generation_copy);
+  if (__predict_true(generation == dtv->generation)) {
+    void* mod_ptr = dtv->modules[__tls_module_id_to_idx(ti->module_id)];
+    if (__predict_true(mod_ptr != nullptr)) {
+      return static_cast<char*>(mod_ptr) + ti->offset;
+    }
+  }
+
+  return tls_get_addr_slow_path(ti);
+}
+
+// This function frees:
+//  - TLS modules referenced by the current DTV.
+//  - The list of DTV objects associated with the current thread.
+//
+// The caller must have already blocked signals.
+void __free_dynamic_tls(bionic_tcb* tcb) {
+  TlsModules& modules = __libc_shared_globals()->tls_modules;
+  BionicAllocator& allocator = __libc_shared_globals()->tls_allocator;
+
+  // If we didn't allocate any dynamic memory, skip out early without taking
+  // the lock.
+  TlsDtv* dtv = __get_tcb_dtv(tcb);
+  if (dtv->generation == kTlsGenerationNone) {
+    return;
+  }
+
+  // We need the write lock to use the allocator.
+  ScopedWriteLock locker(&modules.rwlock);
+
+  // First free everything in the current DTV.
+  for (size_t i = 0; i < dtv->count; ++i) {
+    if (i < modules.module_count && modules.module_table[i].static_offset != SIZE_MAX) {
+      // This module's TLS memory is allocated statically, so don't free it here.
+      continue;
+    }
+    allocator.free(dtv->modules[i]);
+  }
+
+  // Now free the thread's list of DTVs.
+  while (dtv->generation != kTlsGenerationNone) {
+    TlsDtv* next = dtv->next;
+    allocator.free(dtv);
+    dtv = next;
+  }
+
+  // Clear the DTV slot. The DTV must not be used again with this thread.
+  tcb->tls_slot(TLS_SLOT_DTV) = nullptr;
+}
diff --git a/libc/bionic/bionic_systrace.cpp b/libc/bionic/bionic_systrace.cpp
index bac3d88..6182ed8 100644
--- a/libc/bionic/bionic_systrace.cpp
+++ b/libc/bionic/bionic_systrace.cpp
@@ -82,7 +82,7 @@
     return;
   }
 
-  TEMP_FAILURE_RETRY(write(trace_marker_fd, "E", 1));
+  TEMP_FAILURE_RETRY(write(trace_marker_fd, "E|", 2));
 }
 
 ScopedTrace::ScopedTrace(const char* message) : called_end_(false) {
diff --git a/libc/bionic/icu.cpp b/libc/bionic/icu.cpp
index 41a0729..72dac9b 100644
--- a/libc/bionic/icu.cpp
+++ b/libc/bionic/icu.cpp
@@ -36,53 +36,12 @@
 
 #include <async_safe/log.h>
 
-// Allowed icu4c version numbers are in the range [44, 999].
-// Gingerbread's icu4c 4.4 is the minimum supported ICU version.
-static constexpr auto ICUDATA_VERSION_MIN_LENGTH = 2;
-static constexpr auto ICUDATA_VERSION_MAX_LENGTH = 3;
-static constexpr auto ICUDATA_VERSION_MIN = 44;
-
-static char g_icudata_version[ICUDATA_VERSION_MAX_LENGTH + 1];
-
 static void* g_libicuuc_handle = nullptr;
 
-static int __icu_dat_file_filter(const dirent* dirp) {
-  const char* name = dirp->d_name;
-
-  // Is the name the right length to match 'icudt(\d\d\d)l.dat'?
-  const size_t len = strlen(name);
-  if (len < 10 + ICUDATA_VERSION_MIN_LENGTH || len > 10 + ICUDATA_VERSION_MAX_LENGTH) return 0;
-
-  return !strncmp(name, "icudt", 5) && !strncmp(&name[len - 5], "l.dat", 5);
-}
-
 static bool __find_icu() {
-  dirent** namelist = nullptr;
-  int n = scandir("/apex/com.android.runtime/etc/icu", &namelist, &__icu_dat_file_filter,
-                  alphasort);
-  if (n < 0) {
-    async_safe_write_log(ANDROID_LOG_ERROR, "bionic-icu", "couldn't find ICU folder");
-    return false;
-  }
-  int max_version = -1;
-  while (n--) {
-    // We prefer the latest version available.
-    int version = atoi(&namelist[n]->d_name[strlen("icudt")]);
-    if (version != 0 && version > max_version) max_version = version;
-    free(namelist[n]);
-  }
-  free(namelist);
-
-  if (max_version < ICUDATA_VERSION_MIN) {
-    async_safe_write_log(ANDROID_LOG_ERROR, "bionic-icu", "couldn't find an ICU .dat file");
-    return false;
-  }
-
-  snprintf(g_icudata_version, sizeof(g_icudata_version), "_%d", max_version);
-
-  g_libicuuc_handle = dlopen("libicuuc.so", RTLD_LOCAL);
+  g_libicuuc_handle = dlopen("libandroidicu.so", RTLD_LOCAL);
   if (g_libicuuc_handle == nullptr) {
-    async_safe_format_log(ANDROID_LOG_ERROR, "bionic-icu", "couldn't open libicuuc.so: %s",
+    async_safe_format_log(ANDROID_LOG_ERROR, "bionic-icu", "couldn't open libandroidicu.so: %s",
                           dlerror());
     return false;
   }
@@ -94,9 +53,9 @@
   static bool found_icu = __find_icu();
   if (!found_icu) return nullptr;
 
-  char versioned_symbol_name[strlen(symbol_name) + sizeof(g_icudata_version)];
-  snprintf(versioned_symbol_name, sizeof(versioned_symbol_name), "%s%s",
-           symbol_name, g_icudata_version);
+  char versioned_symbol_name[strlen(symbol_name) + strlen("_android") + 1];
+  snprintf(versioned_symbol_name, sizeof(versioned_symbol_name), "%s_android",
+           symbol_name);
 
   void* symbol = dlsym(g_libicuuc_handle, versioned_symbol_name);
   if (symbol == nullptr) {
diff --git a/libc/bionic/libc_init_dynamic.cpp b/libc/bionic/libc_init_dynamic.cpp
index af1b847..4f3b4f7 100644
--- a/libc/bionic/libc_init_dynamic.cpp
+++ b/libc/bionic/libc_init_dynamic.cpp
@@ -51,6 +51,7 @@
 #include <elf.h>
 #include "libc_init_common.h"
 
+#include "private/bionic_elf_tls.h"
 #include "private/bionic_globals.h"
 #include "private/bionic_macros.h"
 #include "private/bionic_ssp.h"
@@ -65,9 +66,6 @@
 // Use an initializer so __libc_sysinfo will have a fallback implementation
 // while .preinit_array constructors run.
 #if defined(__i386__)
-static __attribute__((__naked__)) void __libc_int0x80() {
-  __asm__ volatile("int $0x80; ret");
-}
 __LIBC_HIDDEN__ void* __libc_sysinfo = reinterpret_cast<void*>(__libc_int0x80);
 #endif
 
@@ -82,6 +80,12 @@
   __libc_init_sysinfo();
 #endif
 
+  // Register libc.so's copy of the TLS generation variable so the linker can
+  // update it when it loads or unloads a shared object.
+  TlsModules& tls_modules = __libc_shared_globals()->tls_modules;
+  tls_modules.generation_libc_so = &__libc_tls_generation_copy;
+  __libc_tls_generation_copy = tls_modules.generation;
+
   __libc_init_globals();
   __libc_init_common();
 
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index 68650ed..3219239 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -66,6 +66,30 @@
   }
 }
 
+#if defined(__aarch64__) || defined(__x86_64__)
+extern __LIBC_HIDDEN__ ElfW(Rela) __rela_iplt_start[], __rela_iplt_end[];
+
+static void call_ifunc_resolvers() {
+  typedef ElfW(Addr) (*ifunc_resolver_t)(void);
+  for (ElfW(Rela) *r = __rela_iplt_start; r != __rela_iplt_end; ++r) {
+    ElfW(Addr)* offset = reinterpret_cast<ElfW(Addr)*>(r->r_offset);
+    ElfW(Addr) resolver = r->r_addend;
+    *offset = reinterpret_cast<ifunc_resolver_t>(resolver)();
+  }
+}
+#else
+extern __LIBC_HIDDEN__ ElfW(Rel) __rel_iplt_start[], __rel_iplt_end[];
+
+static void call_ifunc_resolvers() {
+  typedef ElfW(Addr) (*ifunc_resolver_t)(void);
+  for (ElfW(Rel) *r = __rel_iplt_start; r != __rel_iplt_end; ++r) {
+    ElfW(Addr)* offset = reinterpret_cast<ElfW(Addr)*>(r->r_offset);
+    ElfW(Addr) resolver = *offset;
+    *offset = reinterpret_cast<ifunc_resolver_t>(resolver)();
+  }
+}
+#endif
+
 static void apply_gnu_relro() {
   ElfW(Phdr)* phdr_start = reinterpret_cast<ElfW(Phdr)*>(getauxval(AT_PHDR));
   unsigned long int phdr_ct = getauxval(AT_PHNUM);
@@ -83,10 +107,32 @@
   }
 }
 
-static void layout_static_tls() {
+static void layout_static_tls(KernelArgumentBlock& args) {
   StaticTlsLayout& layout = __libc_shared_globals()->static_tls_layout;
   layout.reserve_bionic_tls();
-  layout.reserve_tcb();
+
+  const char* progname = args.argv[0];
+  ElfW(Phdr)* phdr_start = reinterpret_cast<ElfW(Phdr)*>(getauxval(AT_PHDR));
+  size_t phdr_ct = getauxval(AT_PHNUM);
+
+  static TlsModule mod;
+  TlsModules& modules = __libc_shared_globals()->tls_modules;
+  if (__bionic_get_tls_segment(phdr_start, phdr_ct, 0, &mod.segment)) {
+    if (!__bionic_check_tls_alignment(&mod.segment.alignment)) {
+      async_safe_fatal("error: TLS segment alignment in \"%s\" is not a power of 2: %zu\n",
+                       progname, mod.segment.alignment);
+    }
+    mod.static_offset = layout.reserve_exe_segment_and_tcb(&mod.segment, progname);
+    mod.first_generation = kTlsGenerationFirst;
+
+    modules.module_count = 1;
+    modules.module_table = &mod;
+  } else {
+    layout.reserve_exe_segment_and_tcb(nullptr, progname);
+  }
+  // Enable the fast path in __tls_get_addr.
+  __libc_tls_generation_copy = modules.generation;
+
   layout.finish_layout();
 }
 
@@ -111,10 +157,11 @@
   __libc_init_globals();
   __libc_shared_globals()->init_progname = args.argv[0];
   __libc_init_AT_SECURE(args.envp);
-  layout_static_tls();
+  layout_static_tls(args);
   __libc_init_main_thread_final();
   __libc_init_common();
 
+  call_ifunc_resolvers();
   apply_gnu_relro();
 
   // Several Linux ABIs don't pass the onexit pointer, and the ones that
diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp
index d530fa4..80e82f7 100644
--- a/libc/bionic/malloc_common.cpp
+++ b/libc/bionic/malloc_common.cpp
@@ -41,70 +41,17 @@
 //                          get_malloc_leak_info.
 //   write_malloc_leak_info: Writes the leak info data to a file.
 
-#include <pthread.h>
-#include <stdatomic.h>
+#include <stdint.h>
 
-#include <private/bionic_defs.h>
 #include <private/bionic_config.h>
-#include <private/bionic_globals.h>
-#include <private/bionic_malloc_dispatch.h>
 
-#if __has_feature(hwaddress_sanitizer)
-// FIXME: implement these in HWASan allocator.
-extern "C" int __sanitizer_iterate(uintptr_t base __unused, size_t size __unused,
-                                   void (*callback)(uintptr_t base, size_t size, void* arg) __unused,
-                                   void* arg __unused) {
-  return 0;
-}
+#include "malloc_common.h"
 
-extern "C" void __sanitizer_malloc_disable() {
-}
+// =============================================================================
+// Global variables instantations.
+// =============================================================================
 
-extern "C" void __sanitizer_malloc_enable() {
-}
-#include <sanitizer/hwasan_interface.h>
-#define Malloc(function)  __sanitizer_ ## function
-
-#else // __has_feature(hwaddress_sanitizer)
-#include "jemalloc.h"
-#define Malloc(function)  je_ ## function
-#endif
-
-template <typename T>
-static T* RemoveConst(const T* x) {
-  return const_cast<T*>(x);
-}
-
-// RemoveConst is a workaround for bug in current libcxx. Fix in
-// https://reviews.llvm.org/D47613
-#define atomic_load_explicit_const(obj, order) atomic_load_explicit(RemoveConst(obj), order)
-
-static constexpr memory_order default_read_memory_order = memory_order_acquire;
-
-static constexpr MallocDispatch __libc_malloc_default_dispatch
-  __attribute__((unused)) = {
-    Malloc(calloc),
-    Malloc(free),
-    Malloc(mallinfo),
-    Malloc(malloc),
-    Malloc(malloc_usable_size),
-    Malloc(memalign),
-    Malloc(posix_memalign),
-#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-    Malloc(pvalloc),
-#endif
-    Malloc(realloc),
-#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-    Malloc(valloc),
-#endif
-    Malloc(iterate),
-    Malloc(malloc_disable),
-    Malloc(malloc_enable),
-    Malloc(mallopt),
-    Malloc(aligned_alloc),
-  };
-
-// Malloc hooks.
+// Malloc hooks globals.
 void* (*volatile __malloc_hook)(size_t, const void*);
 void* (*volatile __realloc_hook)(void*, size_t, const void*);
 void (*volatile __free_hook)(void*, const void*);
@@ -112,107 +59,88 @@
 
 // In a VM process, this is set to 1 after fork()ing out of zygote.
 int gMallocLeakZygoteChild = 0;
+// =============================================================================
 
 // =============================================================================
 // Allocation functions
 // =============================================================================
 extern "C" void* calloc(size_t n_elements, size_t elem_size) {
-  auto _calloc = atomic_load_explicit_const(
-      &__libc_globals->malloc_dispatch.calloc,
-      default_read_memory_order);
-  if (__predict_false(_calloc != nullptr)) {
-    return _calloc(n_elements, elem_size);
+  auto dispatch_table = GetDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->calloc(n_elements, elem_size);
   }
   return Malloc(calloc)(n_elements, elem_size);
 }
 
 extern "C" void free(void* mem) {
-  auto _free = atomic_load_explicit_const(
-      &__libc_globals->malloc_dispatch.free,
-      default_read_memory_order);
-  if (__predict_false(_free != nullptr)) {
-    _free(mem);
+  auto dispatch_table = GetDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    dispatch_table->free(mem);
   } else {
     Malloc(free)(mem);
   }
 }
 
 extern "C" struct mallinfo mallinfo() {
-  auto _mallinfo = atomic_load_explicit_const(
-      &__libc_globals->malloc_dispatch.mallinfo,
-      default_read_memory_order);
-  if (__predict_false(_mallinfo != nullptr)) {
-    return _mallinfo();
+  auto dispatch_table = GetDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->mallinfo();
   }
   return Malloc(mallinfo)();
 }
 
 extern "C" int mallopt(int param, int value) {
-  auto _mallopt = atomic_load_explicit_const(
-      &__libc_globals->malloc_dispatch.mallopt,
-      default_read_memory_order);
-  if (__predict_false(_mallopt != nullptr)) {
-    return _mallopt(param, value);
+  auto dispatch_table = GetDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->mallopt(param, value);
   }
   return Malloc(mallopt)(param, value);
 }
 
 extern "C" void* malloc(size_t bytes) {
-  auto _malloc = atomic_load_explicit_const(
-      &__libc_globals->malloc_dispatch.malloc,
-      default_read_memory_order);
-  if (__predict_false(_malloc != nullptr)) {
-    return _malloc(bytes);
+  auto dispatch_table = GetDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->malloc(bytes);
   }
   return Malloc(malloc)(bytes);
 }
 
 extern "C" size_t malloc_usable_size(const void* mem) {
-  auto _malloc_usable_size = atomic_load_explicit_const(
-      &__libc_globals->malloc_dispatch.malloc_usable_size,
-      default_read_memory_order);
-  if (__predict_false(_malloc_usable_size != nullptr)) {
-    return _malloc_usable_size(mem);
+  auto dispatch_table = GetDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->malloc_usable_size(mem);
   }
   return Malloc(malloc_usable_size)(mem);
 }
 
 extern "C" void* memalign(size_t alignment, size_t bytes) {
-  auto _memalign = atomic_load_explicit_const(
-      &__libc_globals->malloc_dispatch.memalign,
-      default_read_memory_order);
-  if (__predict_false(_memalign != nullptr)) {
-    return _memalign(alignment, bytes);
+  auto dispatch_table = GetDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->memalign(alignment, bytes);
   }
   return Malloc(memalign)(alignment, bytes);
 }
 
 extern "C" int posix_memalign(void** memptr, size_t alignment, size_t size) {
-  auto _posix_memalign = atomic_load_explicit_const(
-      &__libc_globals->malloc_dispatch.posix_memalign,
-      default_read_memory_order);
-  if (__predict_false(_posix_memalign != nullptr)) {
-    return _posix_memalign(memptr, alignment, size);
+  auto dispatch_table = GetDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->posix_memalign(memptr, alignment, size);
   }
   return Malloc(posix_memalign)(memptr, alignment, size);
 }
 
 extern "C" void* aligned_alloc(size_t alignment, size_t size) {
-  auto _aligned_alloc = atomic_load_explicit_const(
-      &__libc_globals->malloc_dispatch.aligned_alloc,
-      default_read_memory_order);
-  if (__predict_false(_aligned_alloc != nullptr)) {
-    return _aligned_alloc(alignment, size);
+  auto dispatch_table = GetDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->aligned_alloc(alignment, size);
   }
   return Malloc(aligned_alloc)(alignment, size);
 }
 
 extern "C" void* realloc(void* old_mem, size_t bytes) {
-  auto _realloc = atomic_load_explicit_const(
-      &__libc_globals->malloc_dispatch.realloc,
-      default_read_memory_order);
-  if (__predict_false(_realloc != nullptr)) {
-    return _realloc(old_mem, bytes);
+  auto dispatch_table = GetDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->realloc(old_mem, bytes);
   }
   return Malloc(realloc)(old_mem, bytes);
 }
@@ -228,519 +156,22 @@
 
 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
 extern "C" void* pvalloc(size_t bytes) {
-  auto _pvalloc = atomic_load_explicit_const(
-      &__libc_globals->malloc_dispatch.pvalloc,
-      default_read_memory_order);
-  if (__predict_false(_pvalloc != nullptr)) {
-    return _pvalloc(bytes);
+  auto dispatch_table = GetDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->pvalloc(bytes);
   }
   return Malloc(pvalloc)(bytes);
 }
 
 extern "C" void* valloc(size_t bytes) {
-  auto _valloc = atomic_load_explicit_const(
-      &__libc_globals->malloc_dispatch.valloc,
-      default_read_memory_order);
-  if (__predict_false(_valloc != nullptr)) {
-    return _valloc(bytes);
+  auto dispatch_table = GetDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->valloc(bytes);
   }
   return Malloc(valloc)(bytes);
 }
 #endif
-
-// We implement malloc debugging only in libc.so, so the code below
-// must be excluded if we compile this file for static libc.a
-#if !defined(LIBC_STATIC)
-
-#include <dlfcn.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <async_safe/log.h>
-#include <sys/system_properties.h>
-
-extern "C" int __cxa_atexit(void (*func)(void *), void *arg, void *dso);
-
-static const char* HOOKS_SHARED_LIB = "libc_malloc_hooks.so";
-static const char* HOOKS_PROPERTY_ENABLE = "libc.debug.hooks.enable";
-static const char* HOOKS_ENV_ENABLE = "LIBC_HOOKS_ENABLE";
-
-static const char* DEBUG_SHARED_LIB = "libc_malloc_debug.so";
-static const char* DEBUG_PROPERTY_OPTIONS = "libc.debug.malloc.options";
-static const char* DEBUG_PROPERTY_PROGRAM = "libc.debug.malloc.program";
-static const char* DEBUG_ENV_OPTIONS = "LIBC_DEBUG_MALLOC_OPTIONS";
-
-static const char* HEAPPROFD_SHARED_LIB = "heapprofd_client.so";
-static const char* HEAPPROFD_PREFIX = "heapprofd";
-static const char* HEAPPROFD_PROPERTY_ENABLE = "heapprofd.enable";
-static const int HEAPPROFD_SIGNAL = __SIGRTMIN + 4;
-
-enum FunctionEnum : uint8_t {
-  FUNC_INITIALIZE,
-  FUNC_FINALIZE,
-  FUNC_GET_MALLOC_LEAK_INFO,
-  FUNC_FREE_MALLOC_LEAK_INFO,
-  FUNC_MALLOC_BACKTRACE,
-  FUNC_WRITE_LEAK_INFO,
-  FUNC_LAST,
-};
-static void* g_functions[FUNC_LAST];
-
-typedef void (*finalize_func_t)();
-typedef bool (*init_func_t)(const MallocDispatch*, int*, const char*);
-typedef void (*get_malloc_leak_info_func_t)(uint8_t**, size_t*, size_t*, size_t*, size_t*);
-typedef void (*free_malloc_leak_info_func_t)(uint8_t*);
-typedef bool (*write_malloc_leak_info_func_t)(FILE*);
-typedef ssize_t (*malloc_backtrace_func_t)(void*, uintptr_t*, size_t);
-
 // =============================================================================
-// Log functions
-// =============================================================================
-#define error_log(format, ...)  \
-    async_safe_format_log(ANDROID_LOG_ERROR, "libc", (format), ##__VA_ARGS__ )
-#define info_log(format, ...)  \
-    async_safe_format_log(ANDROID_LOG_INFO, "libc", (format), ##__VA_ARGS__ )
-// =============================================================================
-
-// =============================================================================
-// Exported for use by ddms.
-// =============================================================================
-
-// Retrieve native heap information.
-//
-// "*info" is set to a buffer we allocate
-// "*overall_size" is set to the size of the "info" buffer
-// "*info_size" is set to the size of a single entry
-// "*total_memory" is set to the sum of all allocations we're tracking; does
-//   not include heap overhead
-// "*backtrace_size" is set to the maximum number of entries in the back trace
-extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overall_size,
-    size_t* info_size, size_t* total_memory, size_t* backtrace_size) {
-  void* func = g_functions[FUNC_GET_MALLOC_LEAK_INFO];
-  if (func == nullptr) {
-    return;
-  }
-  reinterpret_cast<get_malloc_leak_info_func_t>(func)(info, overall_size, info_size, total_memory,
-                                                      backtrace_size);
-}
-
-extern "C" void free_malloc_leak_info(uint8_t* info) {
-  void* func = g_functions[FUNC_FREE_MALLOC_LEAK_INFO];
-  if (func == nullptr) {
-    return;
-  }
-  reinterpret_cast<free_malloc_leak_info_func_t>(func)(info);
-}
-
-extern "C" void write_malloc_leak_info(FILE* fp) {
-  if (fp == nullptr) {
-    error_log("write_malloc_leak_info called with a nullptr");
-    return;
-  }
-
-  void* func = g_functions[FUNC_WRITE_LEAK_INFO];
-  bool written = false;
-  if (func != nullptr) {
-    written = reinterpret_cast<write_malloc_leak_info_func_t>(func)(fp);
-  }
-
-  if (!written) {
-    fprintf(fp, "Native heap dump not available. To enable, run these commands (requires root):\n");
-    fprintf(fp, "# adb shell stop\n");
-    fprintf(fp, "# adb shell setprop libc.debug.malloc.options backtrace\n");
-    fprintf(fp, "# adb shell start\n");
-  }
-}
-
-// =============================================================================
-
-template<typename FunctionType>
-static bool InitMallocFunction(void* malloc_impl_handler, _Atomic(FunctionType)* func, const char* prefix, const char* suffix) {
-  char symbol[128];
-  snprintf(symbol, sizeof(symbol), "%s_%s", prefix, suffix);
-  *func = reinterpret_cast<FunctionType>(dlsym(malloc_impl_handler, symbol));
-  if (*func == nullptr) {
-    error_log("%s: dlsym(\"%s\") failed", getprogname(), symbol);
-    return false;
-  }
-  return true;
-}
-
-static bool InitMallocFunctions(void* impl_handler, MallocDispatch* table, const char* prefix) {
-  if (!InitMallocFunction<MallocFree>(impl_handler, &table->free, prefix, "free")) {
-    return false;
-  }
-  if (!InitMallocFunction<MallocCalloc>(impl_handler, &table->calloc, prefix, "calloc")) {
-    return false;
-  }
-  if (!InitMallocFunction<MallocMallinfo>(impl_handler, &table->mallinfo, prefix, "mallinfo")) {
-    return false;
-  }
-  if (!InitMallocFunction<MallocMallopt>(impl_handler, &table->mallopt, prefix, "mallopt")) {
-    return false;
-  }
-  if (!InitMallocFunction<MallocMalloc>(impl_handler, &table->malloc, prefix, "malloc")) {
-    return false;
-  }
-  if (!InitMallocFunction<MallocMallocUsableSize>(impl_handler, &table->malloc_usable_size, prefix,
-                                                  "malloc_usable_size")) {
-    return false;
-  }
-  if (!InitMallocFunction<MallocMemalign>(impl_handler, &table->memalign, prefix, "memalign")) {
-    return false;
-  }
-  if (!InitMallocFunction<MallocPosixMemalign>(impl_handler, &table->posix_memalign, prefix,
-                                               "posix_memalign")) {
-    return false;
-  }
-  if (!InitMallocFunction<MallocAlignedAlloc>(impl_handler, &table->aligned_alloc,
-                                              prefix, "aligned_alloc")) {
-    return false;
-  }
-  if (!InitMallocFunction<MallocRealloc>(impl_handler, &table->realloc, prefix, "realloc")) {
-    return false;
-  }
-  if (!InitMallocFunction<MallocIterate>(impl_handler, &table->iterate, prefix, "iterate")) {
-    return false;
-  }
-  if (!InitMallocFunction<MallocMallocDisable>(impl_handler, &table->malloc_disable, prefix,
-                                               "malloc_disable")) {
-    return false;
-  }
-  if (!InitMallocFunction<MallocMallocEnable>(impl_handler, &table->malloc_enable, prefix,
-                                              "malloc_enable")) {
-    return false;
-  }
-#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-  if (!InitMallocFunction<MallocPvalloc>(impl_handler, &table->pvalloc, prefix, "pvalloc")) {
-    return false;
-  }
-  if (!InitMallocFunction<MallocValloc>(impl_handler, &table->valloc, prefix, "valloc")) {
-    return false;
-  }
-#endif
-
-  return true;
-}
-
-static void malloc_fini_impl(void*) {
-  // Our BSD stdio implementation doesn't close the standard streams,
-  // it only flushes them. Other unclosed FILE*s will show up as
-  // malloc leaks, but to avoid the standard streams showing up in
-  // leak reports, close them here.
-  fclose(stdin);
-  fclose(stdout);
-  fclose(stderr);
-
-  reinterpret_cast<finalize_func_t>(g_functions[FUNC_FINALIZE])();
-}
-
-static bool CheckLoadMallocHooks(char** options) {
-  char* env = getenv(HOOKS_ENV_ENABLE);
-  if ((env == nullptr || env[0] == '\0' || env[0] == '0') &&
-    (__system_property_get(HOOKS_PROPERTY_ENABLE, *options) == 0 || *options[0] == '\0' || *options[0] == '0')) {
-    return false;
-  }
-  *options = nullptr;
-  return true;
-}
-
-static bool CheckLoadMallocDebug(char** options) {
-  // If DEBUG_MALLOC_ENV_OPTIONS is set then it overrides the system properties.
-  char* env = getenv(DEBUG_ENV_OPTIONS);
-  if (env == nullptr || env[0] == '\0') {
-    if (__system_property_get(DEBUG_PROPERTY_OPTIONS, *options) == 0 || *options[0] == '\0') {
-      return false;
-    }
-
-    // Check to see if only a specific program should have debug malloc enabled.
-    char program[PROP_VALUE_MAX];
-    if (__system_property_get(DEBUG_PROPERTY_PROGRAM, program) != 0 &&
-        strstr(getprogname(), program) == nullptr) {
-      return false;
-    }
-  } else {
-    *options = env;
-  }
-  return true;
-}
-
-static bool GetHeapprofdProgramProperty(char* data, size_t size) {
-  constexpr char prefix[] = "heapprofd.enable.";
-  // - 1 to skip nullbyte, which we will write later.
-  constexpr size_t prefix_size = sizeof(prefix) - 1;
-  if (size < prefix_size) {
-    error_log("%s: Overflow constructing heapprofd property", getprogname());
-    return false;
-  }
-  memcpy(data, prefix, prefix_size);
-
-  int fd = open("/proc/self/cmdline", O_RDONLY | O_CLOEXEC);
-  if (fd == -1) {
-    error_log("%s: Failed to open /proc/self/cmdline", getprogname());
-    return false;
-  }
-  char cmdline[128];
-  ssize_t rd = read(fd, cmdline, sizeof(cmdline) - 1);
-  close(fd);
-  if (rd == -1) {
-    error_log("%s: Failed to read /proc/self/cmdline", getprogname());
-    return false;
-  }
-  cmdline[rd] = '\0';
-  char* first_arg = static_cast<char*>(memchr(cmdline, '\0', rd));
-  if (first_arg == nullptr || first_arg == cmdline + size - 1) {
-    error_log("%s: Overflow reading cmdline", getprogname());
-    return false;
-  }
-  // For consistency with what we do with Java app cmdlines, trim everything
-  // after the @ sign of the first arg.
-  char* first_at = static_cast<char*>(memchr(cmdline, '@', rd));
-  if (first_at != nullptr && first_at < first_arg) {
-    *first_at = '\0';
-    first_arg = first_at;
-  }
-
-  char* start = static_cast<char*>(memrchr(cmdline, '/', first_arg - cmdline));
-  if (start == first_arg) {
-    // The first argument ended in a slash.
-    error_log("%s: cmdline ends in /", getprogname());
-    return false;
-  } else if (start == nullptr) {
-    start = cmdline;
-  } else {
-    // Skip the /.
-    start++;
-  }
-
-  size_t name_size = static_cast<size_t>(first_arg - start);
-  if (name_size >= size - prefix_size) {
-    error_log("%s: overflow constructing heapprofd property.", getprogname());
-    return false;
-  }
-  // + 1 to also copy the trailing null byte.
-  memcpy(data + prefix_size, start, name_size + 1);
-  return true;
-}
-
-static bool CheckLoadHeapprofd() {
-  // First check for heapprofd.enable. If it is set to "all", enable
-  // heapprofd for all processes. Otherwise, check heapprofd.enable.${prog},
-  // if it is set and not 0, enable heap profiling for this process.
-  char property_value[PROP_VALUE_MAX];
-  if (__system_property_get(HEAPPROFD_PROPERTY_ENABLE, property_value) == 0) {
-    return false;
-  }
-  if (strcmp(property_value, "all") == 0) {
-    return true;
-  }
-
-  char program_property[128];
-  if (!GetHeapprofdProgramProperty(program_property,
-                                   sizeof(program_property))) {
-    return false;
-  }
-  if (__system_property_get(program_property, property_value) == 0) {
-    return false;
-  }
-  return program_property[0] != '\0';
-}
-
-static void ClearGlobalFunctions() {
-  for (size_t i = 0; i < FUNC_LAST; i++) {
-    g_functions[i] = nullptr;
-  }
-}
-
-static void* LoadSharedLibrary(const char* shared_lib, const char* prefix, MallocDispatch* dispatch_table) {
-  void* impl_handle = dlopen(shared_lib, RTLD_NOW | RTLD_LOCAL);
-  if (impl_handle == nullptr) {
-    error_log("%s: Unable to open shared library %s: %s", getprogname(), shared_lib, dlerror());
-    return nullptr;
-  }
-
-  static constexpr const char* names[] = {
-    "initialize",
-    "finalize",
-    "get_malloc_leak_info",
-    "free_malloc_leak_info",
-    "malloc_backtrace",
-    "write_malloc_leak_info",
-  };
-  for (size_t i = 0; i < FUNC_LAST; i++) {
-    char symbol[128];
-    snprintf(symbol, sizeof(symbol), "%s_%s", prefix, names[i]);
-    g_functions[i] = dlsym(impl_handle, symbol);
-    if (g_functions[i] == nullptr) {
-      error_log("%s: %s routine not found in %s", getprogname(), symbol, shared_lib);
-      dlclose(impl_handle);
-      ClearGlobalFunctions();
-      return nullptr;
-    }
-  }
-
-  if (!InitMallocFunctions(impl_handle, dispatch_table, prefix)) {
-    dlclose(impl_handle);
-    ClearGlobalFunctions();
-    return nullptr;
-  }
-
-  return impl_handle;
-}
-
-// A function pointer to heapprofds init function. Used to re-initialize
-// heapprofd. This will start a new profiling session and tear down the old
-// one in case it is still active.
-static _Atomic init_func_t g_heapprofd_init_func = nullptr;
-
-static void install_hooks(libc_globals* globals, const char* options,
-                          const char* prefix, const char* shared_lib) {
-  init_func_t init_func = atomic_load(&g_heapprofd_init_func);
-  if (init_func != nullptr) {
-    init_func(&__libc_malloc_default_dispatch, &gMallocLeakZygoteChild, options);
-    info_log("%s: malloc %s re-enabled", getprogname(), prefix);
-    return;
-  }
-
-  MallocDispatch dispatch_table;
-  void* impl_handle = LoadSharedLibrary(shared_lib, prefix, &dispatch_table);
-  if (impl_handle == nullptr) {
-    return;
-  }
-  init_func = reinterpret_cast<init_func_t>(g_functions[FUNC_INITIALIZE]);
-  if (!init_func(&__libc_malloc_default_dispatch, &gMallocLeakZygoteChild, options)) {
-    dlclose(impl_handle);
-    ClearGlobalFunctions();
-    return;
-  }
-
-  atomic_store(&g_heapprofd_init_func, init_func);
-  // We assign free  first explicitly to prevent the case where we observe a
-  // alloc, but miss the corresponding free because of initialization order.
-  //
-  // This is safer than relying on the declaration order inside
-  // MallocDispatch at the cost of an extra atomic pointer write on
-  // initialization.
-  atomic_store(&globals->malloc_dispatch.free, dispatch_table.free);
-  // The struct gets assigned elementwise and each of the elements is an
-  // _Atomic. Assigning to an _Atomic is an atomic_store operation.
-  // The assignment is done in declaration order.
-  globals->malloc_dispatch = dispatch_table;
-
-  info_log("%s: malloc %s enabled", getprogname(), prefix);
-
-  // Use atexit to trigger the cleanup function. This avoids a problem
-  // where another atexit function is used to cleanup allocated memory,
-  // but the finalize function was already called. This particular error
-  // seems to be triggered by a zygote spawned process calling exit.
-  int ret_value = __cxa_atexit(malloc_fini_impl, nullptr, nullptr);
-  if (ret_value != 0) {
-    error_log("failed to set atexit cleanup function: %d", ret_value);
-  }
-}
-
-extern "C" void InstallInitHeapprofdHook(int);
-
-// Initializes memory allocation framework once per process.
-static void malloc_init_impl(libc_globals* globals) {
-  struct sigaction action = {};
-  action.sa_handler = InstallInitHeapprofdHook;
-  sigaction(HEAPPROFD_SIGNAL, &action, nullptr);
-
-  const char* prefix;
-  const char* shared_lib;
-  char prop[PROP_VALUE_MAX];
-  char* options = prop;
-  // Prefer malloc debug since it existed first and is a more complete
-  // malloc interceptor than the hooks.
-  if (CheckLoadMallocDebug(&options)) {
-    prefix = "debug";
-    shared_lib = DEBUG_SHARED_LIB;
-  } else if (CheckLoadMallocHooks(&options)) {
-    prefix = "hooks";
-    shared_lib = HOOKS_SHARED_LIB;
-  } else if (CheckLoadHeapprofd()) {
-    prefix = "heapprofd";
-    shared_lib = HEAPPROFD_SHARED_LIB;
-  } else {
-    return;
-  }
-  install_hooks(globals, options, prefix, shared_lib);
-}
-
-// Initializes memory allocation framework.
-// This routine is called from __libc_init routines in libc_init_dynamic.cpp.
-__BIONIC_WEAK_FOR_NATIVE_BRIDGE
-__LIBC_HIDDEN__ void __libc_init_malloc(libc_globals* globals) {
-  malloc_init_impl(globals);
-}
-
-// The logic for triggering heapprofd below is as following.
-// 1. HEAPPROFD_SIGNAL is received by the process.
-// 2. If neither InitHeapprofd nor InitHeapprofdHook are currently installed
-//    (g_heapprofd_init_hook_installed is false), InitHeapprofdHook is
-//    installed and g_heapprofd_init_in_progress is set to true.
-//
-// On the next subsequent malloc, InitHeapprofdHook is called and
-// 3a. If the signal is currently being handled (g_heapprofd_init_in_progress
-//     is true), no action is taken.
-// 3b. Otherwise, The signal handler (InstallInitHeapprofdHook) installs a
-//     temporary malloc hook (InitHeapprofdHook).
-// 4. When this hook gets run the first time, it uninstalls itself and spawns
-//    a thread running InitHeapprofd that loads heapprofd.so and installs the
-//    hooks within.
-// 5. g_heapprofd_init_in_progress and g_heapprofd_init_hook_installed are
-//    reset to false so heapprofd can be reinitialized. Reinitialization
-//    means that a new profiling session is started and any still active is
-//    torn down.
-//
-// This roundabout way is needed because we are running non AS-safe code, so
-// we cannot run it directly in the signal handler. The other approach of
-// running a standby thread and signalling through write(2) and read(2) would
-// significantly increase the number of active threads in the system.
-
-static _Atomic bool g_heapprofd_init_in_progress = false;
-static _Atomic bool g_heapprofd_init_hook_installed = false;
-
-static void* InitHeapprofd(void*) {
-  __libc_globals.mutate([](libc_globals* globals) {
-    install_hooks(globals, nullptr, HEAPPROFD_PREFIX, HEAPPROFD_SHARED_LIB);
-  });
-  atomic_store(&g_heapprofd_init_in_progress, false);
-  // Allow to install hook again to re-initialize heap profiling after the
-  // current session finished.
-  atomic_store(&g_heapprofd_init_hook_installed, false);
-  return nullptr;
-}
-
-static void* InitHeapprofdHook(size_t bytes) {
-  if (!atomic_exchange(&g_heapprofd_init_hook_installed, true)) {
-    __libc_globals.mutate([](libc_globals* globals) {
-      atomic_store(&globals->malloc_dispatch.malloc, nullptr);
-    });
-
-    pthread_t thread_id;
-    if (pthread_create(&thread_id, nullptr, InitHeapprofd, nullptr) == -1)
-      error_log("%s: heapprofd: failed to pthread_create.", getprogname());
-    else if (pthread_detach(thread_id) == -1)
-      error_log("%s: heapprofd: failed to pthread_detach", getprogname());
-    if (pthread_setname_np(thread_id, "heapprofdinit") == -1)
-      error_log("%s: heapprod: failed to pthread_setname_np", getprogname());
-  }
-  return Malloc(malloc)(bytes);
-}
-
-extern "C" void InstallInitHeapprofdHook(int) {
-  if (!atomic_exchange(&g_heapprofd_init_in_progress, true)) {
-    __libc_globals.mutate([](libc_globals* globals) {
-      atomic_store(&globals->malloc_dispatch.malloc, InitHeapprofdHook);
-    });
-  }
-}
-
-#endif  // !LIBC_STATIC
 
 // =============================================================================
 // Exported for use by libmemunreachable.
@@ -750,11 +181,9 @@
 // [base, base+size).  Must be called between malloc_disable and malloc_enable.
 extern "C" int malloc_iterate(uintptr_t base, size_t size,
     void (*callback)(uintptr_t base, size_t size, void* arg), void* arg) {
-  auto _iterate = atomic_load_explicit_const(
-      &__libc_globals->malloc_dispatch.iterate,
-      default_read_memory_order);
-  if (__predict_false(_iterate != nullptr)) {
-    return _iterate(base, size, callback, arg);
+  auto dispatch_table = GetDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->iterate(base, size, callback, arg);
   }
   return Malloc(iterate)(base, size, callback, arg);
 }
@@ -762,36 +191,52 @@
 // Disable calls to malloc so malloc_iterate gets a consistent view of
 // allocated memory.
 extern "C" void malloc_disable() {
-  auto _malloc_disable = atomic_load_explicit_const(
-     & __libc_globals->malloc_dispatch.malloc_disable,
-      default_read_memory_order);
-  if (__predict_false(_malloc_disable != nullptr)) {
-    return _malloc_disable();
+  auto dispatch_table = GetDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->malloc_disable();
   }
   return Malloc(malloc_disable)();
 }
 
 // Re-enable calls to malloc after a previous call to malloc_disable.
 extern "C" void malloc_enable() {
-  auto _malloc_enable = atomic_load_explicit_const(
-      &__libc_globals->malloc_dispatch.malloc_enable,
-      default_read_memory_order);
-  if (__predict_false(_malloc_enable != nullptr)) {
-    return _malloc_enable();
+  auto dispatch_table = GetDispatchTable();
+  if (__predict_false(dispatch_table != nullptr)) {
+    return dispatch_table->malloc_enable();
   }
   return Malloc(malloc_enable)();
 }
 
-#ifndef LIBC_STATIC
-extern "C" ssize_t malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count) {
-  void* func = g_functions[FUNC_MALLOC_BACKTRACE];
-  if (func == nullptr) {
-    return 0;
-  }
-  return reinterpret_cast<malloc_backtrace_func_t>(func)(pointer, frames, frame_count);
-}
-#else
+#if defined(LIBC_STATIC)
 extern "C" ssize_t malloc_backtrace(void*, uintptr_t*, size_t) {
   return 0;
 }
 #endif
+
+#if __has_feature(hwaddress_sanitizer)
+// FIXME: implement these in HWASan allocator.
+extern "C" int __sanitizer_iterate(uintptr_t base __unused, size_t size __unused,
+                                   void (*callback)(uintptr_t base, size_t size, void* arg) __unused,
+                                   void* arg __unused) {
+  return 0;
+}
+
+extern "C" void __sanitizer_malloc_disable() {
+}
+
+extern "C" void __sanitizer_malloc_enable() {
+}
+#endif
+// =============================================================================
+
+// =============================================================================
+// Platform-internal mallopt variant.
+// =============================================================================
+#if defined(LIBC_STATIC)
+extern "C" bool android_mallopt(int, void*, size_t) {
+  // There are no options supported on static executables.
+  errno = ENOTSUP;
+  return false;
+}
+#endif
+// =============================================================================
diff --git a/libc/bionic/malloc_common.h b/libc/bionic/malloc_common.h
new file mode 100644
index 0000000..a2f338a
--- /dev/null
+++ b/libc/bionic/malloc_common.h
@@ -0,0 +1,74 @@
+/*
+ * 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 <stdatomic.h>
+
+#include <async_safe/log.h>
+#include <private/bionic_globals.h>
+#include <private/bionic_malloc_dispatch.h>
+
+#if __has_feature(hwaddress_sanitizer)
+
+#include <sanitizer/hwasan_interface.h>
+
+__BEGIN_DECLS
+
+// FIXME: implement these in HWASan allocator.
+int __sanitizer_iterate(uintptr_t base, size_t size,
+                        void (*callback)(uintptr_t base, size_t size, void* arg),
+                        void* arg);
+void __sanitizer_malloc_disable();
+void __sanitizer_malloc_enable();
+
+__END_DECLS
+
+#define Malloc(function)  __sanitizer_ ## function
+
+#else // __has_feature(hwaddress_sanitizer)
+
+#include "jemalloc.h"
+#define Malloc(function)  je_ ## function
+
+#endif
+
+extern int gMallocLeakZygoteChild;
+
+static inline const MallocDispatch* GetDispatchTable() {
+  return atomic_load_explicit(&__libc_globals->current_dispatch_table, memory_order_acquire);
+}
+
+// =============================================================================
+// Log functions
+// =============================================================================
+#define error_log(format, ...)  \
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", (format), ##__VA_ARGS__ )
+#define info_log(format, ...)  \
+    async_safe_format_log(ANDROID_LOG_INFO, "libc", (format), ##__VA_ARGS__ )
+// =============================================================================
diff --git a/libc/bionic/malloc_common_dynamic.cpp b/libc/bionic/malloc_common_dynamic.cpp
new file mode 100644
index 0000000..ce3e761
--- /dev/null
+++ b/libc/bionic/malloc_common_dynamic.cpp
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#if defined(LIBC_STATIC)
+#error This file should not be compiled for static targets.
+#endif
+
+// Contains a thin layer that calls whatever real native allocator
+// has been defined. For the libc shared library, this allows the
+// implementation of a debug malloc that can intercept all of the allocation
+// calls and add special debugging code to attempt to catch allocation
+// errors. All of the debugging code is implemented in a separate shared
+// library that is only loaded when the property "libc.debug.malloc.options"
+// is set to a non-zero value. There are three functions exported to
+// allow ddms, or other external users to get information from the debug
+// allocation.
+//   get_malloc_leak_info: Returns information about all of the known native
+//                         allocations that are currently in use.
+//   free_malloc_leak_info: Frees the data allocated by the call to
+//                          get_malloc_leak_info.
+//   write_malloc_leak_info: Writes the leak info data to a file.
+
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdatomic.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <private/bionic_config.h>
+#include <private/bionic_defs.h>
+#include <private/bionic_malloc_dispatch.h>
+
+#include <sys/system_properties.h>
+
+#include "malloc_common.h"
+#include "malloc_common_dynamic.h"
+#include "malloc_heapprofd.h"
+
+static constexpr MallocDispatch __libc_malloc_default_dispatch
+  __attribute__((unused)) = {
+    Malloc(calloc),
+    Malloc(free),
+    Malloc(mallinfo),
+    Malloc(malloc),
+    Malloc(malloc_usable_size),
+    Malloc(memalign),
+    Malloc(posix_memalign),
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+    Malloc(pvalloc),
+#endif
+    Malloc(realloc),
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+    Malloc(valloc),
+#endif
+    Malloc(iterate),
+    Malloc(malloc_disable),
+    Malloc(malloc_enable),
+    Malloc(mallopt),
+    Malloc(aligned_alloc),
+  };
+
+static constexpr char kHooksSharedLib[] = "libc_malloc_hooks.so";
+static constexpr char kHooksPrefix[] = "hooks";
+static constexpr char kHooksPropertyEnable[] = "libc.debug.hooks.enable";
+static constexpr char kHooksEnvEnable[] = "LIBC_HOOKS_ENABLE";
+
+static constexpr char kDebugSharedLib[] = "libc_malloc_debug.so";
+static constexpr char kDebugPrefix[] = "debug";
+static constexpr char kDebugPropertyOptions[] = "libc.debug.malloc.options";
+static constexpr char kDebugPropertyProgram[] = "libc.debug.malloc.program";
+static constexpr char kDebugEnvOptions[] = "LIBC_DEBUG_MALLOC_OPTIONS";
+
+typedef void (*finalize_func_t)();
+typedef bool (*init_func_t)(const MallocDispatch*, int*, const char*);
+typedef void (*get_malloc_leak_info_func_t)(uint8_t**, size_t*, size_t*, size_t*, size_t*);
+typedef void (*free_malloc_leak_info_func_t)(uint8_t*);
+typedef bool (*write_malloc_leak_info_func_t)(FILE*);
+typedef ssize_t (*malloc_backtrace_func_t)(void*, uintptr_t*, size_t);
+
+enum FunctionEnum : uint8_t {
+  FUNC_INITIALIZE,
+  FUNC_FINALIZE,
+  FUNC_GET_MALLOC_LEAK_INFO,
+  FUNC_FREE_MALLOC_LEAK_INFO,
+  FUNC_MALLOC_BACKTRACE,
+  FUNC_WRITE_LEAK_INFO,
+  FUNC_LAST,
+};
+static void* gFunctions[FUNC_LAST];
+
+extern "C" int __cxa_atexit(void (*func)(void *), void *arg, void *dso);
+
+template<typename FunctionType>
+static bool InitMallocFunction(void* malloc_impl_handler, FunctionType* func, const char* prefix, const char* suffix) {
+  char symbol[128];
+  snprintf(symbol, sizeof(symbol), "%s_%s", prefix, suffix);
+  *func = reinterpret_cast<FunctionType>(dlsym(malloc_impl_handler, symbol));
+  if (*func == nullptr) {
+    error_log("%s: dlsym(\"%s\") failed", getprogname(), symbol);
+    return false;
+  }
+  return true;
+}
+
+static bool InitMallocFunctions(void* impl_handler, MallocDispatch* table, const char* prefix) {
+  if (!InitMallocFunction<MallocFree>(impl_handler, &table->free, prefix, "free")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocCalloc>(impl_handler, &table->calloc, prefix, "calloc")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocMallinfo>(impl_handler, &table->mallinfo, prefix, "mallinfo")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocMallopt>(impl_handler, &table->mallopt, prefix, "mallopt")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocMalloc>(impl_handler, &table->malloc, prefix, "malloc")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocMallocUsableSize>(impl_handler, &table->malloc_usable_size, prefix,
+                                                  "malloc_usable_size")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocMemalign>(impl_handler, &table->memalign, prefix, "memalign")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocPosixMemalign>(impl_handler, &table->posix_memalign, prefix,
+                                               "posix_memalign")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocAlignedAlloc>(impl_handler, &table->aligned_alloc,
+                                              prefix, "aligned_alloc")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocRealloc>(impl_handler, &table->realloc, prefix, "realloc")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocIterate>(impl_handler, &table->iterate, prefix, "iterate")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocMallocDisable>(impl_handler, &table->malloc_disable, prefix,
+                                               "malloc_disable")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocMallocEnable>(impl_handler, &table->malloc_enable, prefix,
+                                              "malloc_enable")) {
+    return false;
+  }
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+  if (!InitMallocFunction<MallocPvalloc>(impl_handler, &table->pvalloc, prefix, "pvalloc")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocValloc>(impl_handler, &table->valloc, prefix, "valloc")) {
+    return false;
+  }
+#endif
+
+  return true;
+}
+
+static void MallocFiniImpl(void*) {
+  // Our BSD stdio implementation doesn't close the standard streams,
+  // it only flushes them. Other unclosed FILE*s will show up as
+  // malloc leaks, but to avoid the standard streams showing up in
+  // leak reports, close them here.
+  fclose(stdin);
+  fclose(stdout);
+  fclose(stderr);
+
+  reinterpret_cast<finalize_func_t>(gFunctions[FUNC_FINALIZE])();
+}
+
+static bool CheckLoadMallocHooks(char** options) {
+  char* env = getenv(kHooksEnvEnable);
+  if ((env == nullptr || env[0] == '\0' || env[0] == '0') &&
+    (__system_property_get(kHooksPropertyEnable, *options) == 0 || *options[0] == '\0' || *options[0] == '0')) {
+    return false;
+  }
+  *options = nullptr;
+  return true;
+}
+
+static bool CheckLoadMallocDebug(char** options) {
+  // If kDebugMallocEnvOptions is set then it overrides the system properties.
+  char* env = getenv(kDebugEnvOptions);
+  if (env == nullptr || env[0] == '\0') {
+    if (__system_property_get(kDebugPropertyOptions, *options) == 0 || *options[0] == '\0') {
+      return false;
+    }
+
+    // Check to see if only a specific program should have debug malloc enabled.
+    char program[PROP_VALUE_MAX];
+    if (__system_property_get(kDebugPropertyProgram, program) != 0 &&
+        strstr(getprogname(), program) == nullptr) {
+      return false;
+    }
+  } else {
+    *options = env;
+  }
+  return true;
+}
+
+static void ClearGlobalFunctions() {
+  for (size_t i = 0; i < FUNC_LAST; i++) {
+    gFunctions[i] = nullptr;
+  }
+}
+
+bool InitSharedLibrary(void* impl_handle, const char* shared_lib, const char* prefix, MallocDispatch* dispatch_table) {
+  static constexpr const char* names[] = {
+    "initialize",
+    "finalize",
+    "get_malloc_leak_info",
+    "free_malloc_leak_info",
+    "malloc_backtrace",
+    "write_malloc_leak_info",
+  };
+  for (size_t i = 0; i < FUNC_LAST; i++) {
+    char symbol[128];
+    snprintf(symbol, sizeof(symbol), "%s_%s", prefix, names[i]);
+    gFunctions[i] = dlsym(impl_handle, symbol);
+    if (gFunctions[i] == nullptr) {
+      error_log("%s: %s routine not found in %s", getprogname(), symbol, shared_lib);
+      ClearGlobalFunctions();
+      return false;
+    }
+  }
+
+  if (!InitMallocFunctions(impl_handle, dispatch_table, prefix)) {
+    ClearGlobalFunctions();
+    return false;
+  }
+  return true;
+}
+
+void* LoadSharedLibrary(const char* shared_lib, const char* prefix, MallocDispatch* dispatch_table) {
+  void* impl_handle = dlopen(shared_lib, RTLD_NOW | RTLD_LOCAL);
+  if (impl_handle == nullptr) {
+    error_log("%s: Unable to open shared library %s: %s", getprogname(), shared_lib, dlerror());
+    return nullptr;
+  }
+
+  if (!InitSharedLibrary(impl_handle, shared_lib, prefix, dispatch_table)) {
+    dlclose(impl_handle);
+    impl_handle = nullptr;
+  }
+
+  return impl_handle;
+}
+
+bool FinishInstallHooks(libc_globals* globals, const char* options, const char* prefix) {
+  init_func_t init_func = reinterpret_cast<init_func_t>(gFunctions[FUNC_INITIALIZE]);
+  if (!init_func(&__libc_malloc_default_dispatch, &gMallocLeakZygoteChild, options)) {
+    error_log("%s: failed to enable malloc %s", getprogname(), prefix);
+    ClearGlobalFunctions();
+    return false;
+  }
+
+  // Do a pointer swap so that all of the functions become valid at once to
+  // avoid any initialization order problems.
+  atomic_store(&globals->current_dispatch_table, &globals->malloc_dispatch_table);
+
+  info_log("%s: malloc %s enabled", getprogname(), prefix);
+
+  // Use atexit to trigger the cleanup function. This avoids a problem
+  // where another atexit function is used to cleanup allocated memory,
+  // but the finalize function was already called. This particular error
+  // seems to be triggered by a zygote spawned process calling exit.
+  int ret_value = __cxa_atexit(MallocFiniImpl, nullptr, nullptr);
+  if (ret_value != 0) {
+    // We don't consider this a fatal error.
+    info_log("failed to set atexit cleanup function: %d", ret_value);
+  }
+  return true;
+}
+
+static bool InstallHooks(libc_globals* globals, const char* options, const char* prefix,
+                         const char* shared_lib) {
+  void* impl_handle = LoadSharedLibrary(shared_lib, prefix, &globals->malloc_dispatch_table);
+  if (impl_handle == nullptr) {
+    return false;
+  }
+
+  init_func_t init_func = reinterpret_cast<init_func_t>(gFunctions[FUNC_INITIALIZE]);
+  if (!init_func(&__libc_malloc_default_dispatch, &gMallocLeakZygoteChild, options)) {
+    error_log("%s: failed to enable malloc %s", getprogname(), prefix);
+    ClearGlobalFunctions();
+    return false;
+  }
+
+  if (!FinishInstallHooks(globals, options, prefix)) {
+    dlclose(impl_handle);
+    return false;
+  }
+  return true;
+}
+
+// Initializes memory allocation framework once per process.
+static void MallocInitImpl(libc_globals* globals) {
+  char prop[PROP_VALUE_MAX];
+  char* options = prop;
+
+  // Prefer malloc debug since it existed first and is a more complete
+  // malloc interceptor than the hooks.
+  bool hook_installed = false;
+  if (CheckLoadMallocDebug(&options)) {
+    hook_installed = InstallHooks(globals, options, kDebugPrefix, kDebugSharedLib);
+  } else if (CheckLoadMallocHooks(&options)) {
+    hook_installed = InstallHooks(globals, options, kHooksPrefix, kHooksSharedLib);
+  }
+
+  if (!hook_installed) {
+    if (HeapprofdShouldLoad()) {
+      HeapprofdInstallHooksAtInit(globals);
+    }
+
+    // Install this last to avoid as many race conditions as possible.
+    HeapprofdInstallSignalHandler();
+  } else {
+    // Install a signal handler that prints an error since we don't support
+    // heapprofd and any other hook to be installed at the same time.
+    HeapprofdInstallErrorSignalHandler();
+  }
+}
+
+// Initializes memory allocation framework.
+// This routine is called from __libc_init routines in libc_init_dynamic.cpp.
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
+__LIBC_HIDDEN__ void __libc_init_malloc(libc_globals* globals) {
+  MallocInitImpl(globals);
+}
+
+// =============================================================================
+// Functions to support dumping of native heap allocations using malloc debug.
+// =============================================================================
+
+// Retrieve native heap information.
+//
+// "*info" is set to a buffer we allocate
+// "*overall_size" is set to the size of the "info" buffer
+// "*info_size" is set to the size of a single entry
+// "*total_memory" is set to the sum of all allocations we're tracking; does
+//   not include heap overhead
+// "*backtrace_size" is set to the maximum number of entries in the back trace
+extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overall_size,
+    size_t* info_size, size_t* total_memory, size_t* backtrace_size) {
+  void* func = gFunctions[FUNC_GET_MALLOC_LEAK_INFO];
+  if (func == nullptr) {
+    return;
+  }
+  reinterpret_cast<get_malloc_leak_info_func_t>(func)(info, overall_size, info_size, total_memory,
+                                                      backtrace_size);
+}
+
+extern "C" void free_malloc_leak_info(uint8_t* info) {
+  void* func = gFunctions[FUNC_FREE_MALLOC_LEAK_INFO];
+  if (func == nullptr) {
+    return;
+  }
+  reinterpret_cast<free_malloc_leak_info_func_t>(func)(info);
+}
+
+extern "C" void write_malloc_leak_info(FILE* fp) {
+  if (fp == nullptr) {
+    error_log("write_malloc_leak_info called with a nullptr");
+    return;
+  }
+
+  void* func = gFunctions[FUNC_WRITE_LEAK_INFO];
+  bool written = false;
+  if (func != nullptr) {
+    written = reinterpret_cast<write_malloc_leak_info_func_t>(func)(fp);
+  }
+
+  if (!written) {
+    fprintf(fp, "Native heap dump not available. To enable, run these commands (requires root):\n");
+    fprintf(fp, "# adb shell stop\n");
+    fprintf(fp, "# adb shell setprop libc.debug.malloc.options backtrace\n");
+    fprintf(fp, "# adb shell start\n");
+  }
+}
+// =============================================================================
+
+// =============================================================================
+// Exported for use by libmemunreachable.
+// =============================================================================
+extern "C" ssize_t malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count) {
+  void* func = gFunctions[FUNC_MALLOC_BACKTRACE];
+  if (func == nullptr) {
+    return 0;
+  }
+  return reinterpret_cast<malloc_backtrace_func_t>(func)(pointer, frames, frame_count);
+}
+// =============================================================================
+
+// =============================================================================
+// Platform-internal mallopt variant.
+// =============================================================================
+extern "C" bool android_mallopt(int opcode, void* arg, size_t arg_size) {
+  return HeapprofdMallopt(opcode, arg, arg_size);
+}
+// =============================================================================
diff --git a/libc/arch-mips/bionic/libgcc_compat.c b/libc/bionic/malloc_common_dynamic.h
similarity index 71%
copy from libc/arch-mips/bionic/libgcc_compat.c
copy to libc/bionic/malloc_common_dynamic.h
index 1a0f566..8794ed0 100644
--- a/libc/arch-mips/bionic/libgcc_compat.c
+++ b/libc/bionic/malloc_common_dynamic.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,16 +26,17 @@
  * SUCH DAMAGE.
  */
 
-extern char __divdi3;
-extern char __moddi3;
-extern char __popcountsi2;
-extern char __udivdi3;
-extern char __umoddi3;
+#pragma once
 
-void* __bionic_libgcc_compat_symbols[] = {
-    &__divdi3,
-    &__moddi3,
-    &__popcountsi2,
-    &__udivdi3,
-    &__umoddi3,
-};
+#include <stdbool.h>
+
+#include <private/bionic_globals.h>
+#include <private/bionic_malloc_dispatch.h>
+
+// Function prototypes.
+bool InitSharedLibrary(void* impl_handle, const char* shared_lib, const char* prefix,
+                       MallocDispatch* dispatch_table);
+
+void* LoadSharedLibrary(const char* shared_lib, const char* prefix, MallocDispatch* dispatch_table);
+
+bool FinishInstallHooks(libc_globals* globals, const char* options, const char* prefix);
diff --git a/libc/bionic/malloc_heapprofd.cpp b/libc/bionic/malloc_heapprofd.cpp
new file mode 100644
index 0000000..c492bac
--- /dev/null
+++ b/libc/bionic/malloc_heapprofd.cpp
@@ -0,0 +1,324 @@
+/*
+ * 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.
+ */
+
+#if defined(LIBC_STATIC)
+#error This file should not be compiled for static targets.
+#endif
+
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <private/bionic_config.h>
+#include <private/bionic_malloc.h>
+#include <private/bionic_malloc_dispatch.h>
+#include <sys/system_properties.h>
+
+#include "malloc_common.h"
+#include "malloc_common_dynamic.h"
+#include "malloc_heapprofd.h"
+
+static constexpr char kHeapprofdSharedLib[] = "heapprofd_client.so";
+static constexpr char kHeapprofdPrefix[] = "heapprofd";
+static constexpr char kHeapprofdPropertyEnable[] = "heapprofd.enable";
+static constexpr int kHeapprofdSignal = __SIGRTMIN + 4;
+
+// The logic for triggering heapprofd (at runtime) is as follows:
+// 1. HEAPPROFD_SIGNAL is received by the process, entering the
+//    MaybeInstallInitHeapprofdHook signal handler.
+// 2. If the initialization is not already in flight
+//    (gHeapprofdInitInProgress is false), the malloc hook is set to
+//    point at InitHeapprofdHook, and gHeapprofdInitInProgress is set to
+//    true.
+// 3. The next malloc call enters InitHeapprofdHook, which removes the malloc
+//    hook, and spawns a detached pthread to run the InitHeapprofd task.
+//    (gHeapprofdInitHook_installed atomic is used to perform this once.)
+// 4. InitHeapprofd, on a dedicated pthread, loads the heapprofd client library,
+//    installs the full set of heapprofd hooks, and invokes the client's
+//    initializer. The dedicated pthread then terminates.
+// 5. gHeapprofdInitInProgress and gHeapprofdInitHookInstalled are
+//    reset to false such that heapprofd can be reinitialized. Reinitialization
+//    means that a new profiling session is started, and any still active is
+//    torn down.
+//
+// The incremental hooking and a dedicated task thread are used since we cannot
+// do heavy work within a signal handler, or when blocking a malloc invocation.
+
+// The handle returned by dlopen when previously loading the heapprofd
+// hooks. nullptr if shared library has not been already been loaded.
+static _Atomic (void*) gHeapprofdHandle = nullptr;
+
+static _Atomic bool gHeapprofdInitInProgress = false;
+static _Atomic bool gHeapprofdInitHookInstalled = false;
+
+// In a Zygote child process, this is set to true if profiling of this process
+// is allowed. Note that this is set at a later time than the global
+// gMallocLeakZygoteChild. The latter is set during the fork (while still in
+// zygote's SELinux domain). While this bit is set after the child is
+// specialized (and has transferred SELinux domains if applicable).
+static _Atomic bool gMallocZygoteChildProfileable = false;
+
+extern "C" void* MallocInitHeapprofdHook(size_t);
+
+static constexpr MallocDispatch __heapprofd_init_dispatch
+  __attribute__((unused)) = {
+    Malloc(calloc),
+    Malloc(free),
+    Malloc(mallinfo),
+    MallocInitHeapprofdHook,
+    Malloc(malloc_usable_size),
+    Malloc(memalign),
+    Malloc(posix_memalign),
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+    Malloc(pvalloc),
+#endif
+    Malloc(realloc),
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+    Malloc(valloc),
+#endif
+    Malloc(iterate),
+    Malloc(malloc_disable),
+    Malloc(malloc_enable),
+    Malloc(mallopt),
+    Malloc(aligned_alloc),
+  };
+
+static void MaybeInstallInitHeapprofdHook(int) {
+  // Zygote child processes must be marked profileable.
+  if (gMallocLeakZygoteChild &&
+      !atomic_load_explicit(&gMallocZygoteChildProfileable, memory_order_acquire)) {
+    return;
+  }
+
+  if (!atomic_exchange(&gHeapprofdInitInProgress, true)) {
+    __libc_globals.mutate([](libc_globals* globals) {
+      atomic_store(&globals->current_dispatch_table, &__heapprofd_init_dispatch);
+    });
+  }
+}
+
+static bool GetHeapprofdProgramProperty(char* data, size_t size) {
+  constexpr char prefix[] = "heapprofd.enable.";
+  // - 1 to skip nullbyte, which we will write later.
+  constexpr size_t prefix_size = sizeof(prefix) - 1;
+  if (size < prefix_size) {
+    error_log("%s: Overflow constructing heapprofd property", getprogname());
+    return false;
+  }
+  memcpy(data, prefix, prefix_size);
+
+  int fd = open("/proc/self/cmdline", O_RDONLY | O_CLOEXEC);
+  if (fd == -1) {
+    error_log("%s: Failed to open /proc/self/cmdline", getprogname());
+    return false;
+  }
+  char cmdline[128];
+  ssize_t rd = read(fd, cmdline, sizeof(cmdline) - 1);
+  close(fd);
+  if (rd == -1) {
+    error_log("%s: Failed to read /proc/self/cmdline", getprogname());
+    return false;
+  }
+  cmdline[rd] = '\0';
+  char* first_arg = static_cast<char*>(memchr(cmdline, '\0', rd));
+  if (first_arg == nullptr || first_arg == cmdline + size - 1) {
+    error_log("%s: Overflow reading cmdline", getprogname());
+    return false;
+  }
+  // For consistency with what we do with Java app cmdlines, trim everything
+  // after the @ sign of the first arg.
+  char* first_at = static_cast<char*>(memchr(cmdline, '@', rd));
+  if (first_at != nullptr && first_at < first_arg) {
+    *first_at = '\0';
+    first_arg = first_at;
+  }
+
+  char* start = static_cast<char*>(memrchr(cmdline, '/', first_arg - cmdline));
+  if (start == first_arg) {
+    // The first argument ended in a slash.
+    error_log("%s: cmdline ends in /", getprogname());
+    return false;
+  } else if (start == nullptr) {
+    start = cmdline;
+  } else {
+    // Skip the /.
+    start++;
+  }
+
+  size_t name_size = static_cast<size_t>(first_arg - start);
+  if (name_size >= size - prefix_size) {
+    error_log("%s: overflow constructing heapprofd property.", getprogname());
+    return false;
+  }
+  // + 1 to also copy the trailing null byte.
+  memcpy(data + prefix_size, start, name_size + 1);
+  return true;
+}
+
+bool HeapprofdShouldLoad() {
+  // First check for heapprofd.enable. If it is set to "all", enable
+  // heapprofd for all processes. Otherwise, check heapprofd.enable.${prog},
+  // if it is set and not 0, enable heap profiling for this process.
+  char property_value[PROP_VALUE_MAX];
+  if (__system_property_get(kHeapprofdPropertyEnable, property_value) == 0) {
+    return false;
+  }
+  if (strcmp(property_value, "all") == 0) {
+    return true;
+  }
+
+  char program_property[128];
+  if (!GetHeapprofdProgramProperty(program_property,
+                                   sizeof(program_property))) {
+    return false;
+  }
+  if (__system_property_get(program_property, property_value) == 0) {
+    return false;
+  }
+  return program_property[0] != '\0';
+}
+
+void HeapprofdInstallSignalHandler() {
+  struct sigaction action = {};
+  action.sa_handler = MaybeInstallInitHeapprofdHook;
+  sigaction(kHeapprofdSignal, &action, nullptr);
+}
+
+static void DisplayError(int) {
+  error_log("Cannot install heapprofd while malloc debug/malloc hooks are enabled.");
+}
+
+void HeapprofdInstallErrorSignalHandler() {
+  struct sigaction action = {};
+  action.sa_handler = DisplayError;
+  sigaction(kHeapprofdSignal, &action, nullptr);
+}
+
+static void CommonInstallHooks(libc_globals* globals) {
+  void* impl_handle = atomic_load(&gHeapprofdHandle);
+  bool reusing_handle = impl_handle != nullptr;
+  if (!reusing_handle) {
+    impl_handle = LoadSharedLibrary(kHeapprofdSharedLib, kHeapprofdPrefix, &globals->malloc_dispatch_table);
+    if (impl_handle == nullptr) {
+      return;
+    }
+  } else if (!InitSharedLibrary(impl_handle, kHeapprofdSharedLib, kHeapprofdPrefix, &globals->malloc_dispatch_table)) {
+    return;
+  }
+
+  if (FinishInstallHooks(globals, nullptr, kHeapprofdPrefix)) {
+    atomic_store(&gHeapprofdHandle, impl_handle);
+  } else if (!reusing_handle) {
+    dlclose(impl_handle);
+  }
+
+  atomic_store(&gHeapprofdInitInProgress, false);
+}
+
+void HeapprofdInstallHooksAtInit(libc_globals* globals) {
+  if (atomic_exchange(&gHeapprofdInitInProgress, true)) {
+    return;
+  }
+  CommonInstallHooks(globals);
+}
+
+static void* InitHeapprofd(void*) {
+  __libc_globals.mutate([](libc_globals* globals) {
+    CommonInstallHooks(globals);
+  });
+
+  // Allow to install hook again to re-initialize heap profiling after the
+  // current session finished.
+  atomic_store(&gHeapprofdInitHookInstalled, false);
+  return nullptr;
+}
+
+extern "C" void* MallocInitHeapprofdHook(size_t bytes) {
+  if (!atomic_exchange(&gHeapprofdInitHookInstalled, true)) {
+    __libc_globals.mutate([](libc_globals* globals) {
+      atomic_store(&globals->current_dispatch_table, nullptr);
+    });
+
+    pthread_t thread_id;
+    if (pthread_create(&thread_id, nullptr, InitHeapprofd, nullptr) != 0) {
+      error_log("%s: heapprofd: failed to pthread_create.", getprogname());
+    } else if (pthread_detach(thread_id) != 0) {
+      error_log("%s: heapprofd: failed to pthread_detach", getprogname());
+    }
+    if (pthread_setname_np(thread_id, "heapprofdinit") != 0) {
+      error_log("%s: heapprod: failed to pthread_setname_np", getprogname());
+    }
+  }
+  return Malloc(malloc)(bytes);
+}
+
+// Marks this process as a profileable zygote child.
+static bool HandleInitZygoteChildProfiling() {
+  atomic_store_explicit(&gMallocZygoteChildProfileable, true, memory_order_release);
+
+  // Conditionally start "from startup" profiling.
+  if (HeapprofdShouldLoad()) {
+    // Directly call the signal handler (will correctly guard against
+    // concurrent signal delivery).
+    MaybeInstallInitHeapprofdHook(kHeapprofdSignal);
+  }
+  return true;
+}
+
+static bool DispatchReset() {
+  if (!atomic_exchange(&gHeapprofdInitInProgress, true)) {
+    __libc_globals.mutate([](libc_globals* globals) {
+      atomic_store(&globals->current_dispatch_table, nullptr);
+    });
+    atomic_store(&gHeapprofdInitInProgress, false);
+    return true;
+  }
+  errno = EAGAIN;
+  return false;
+}
+
+bool HeapprofdMallopt(int opcode, void* arg, size_t arg_size) {
+  if (opcode == M_INIT_ZYGOTE_CHILD_PROFILING) {
+    if (arg != nullptr || arg_size != 0) {
+      errno = EINVAL;
+      return false;
+    }
+    return HandleInitZygoteChildProfiling();
+  }
+  if (opcode == M_RESET_HOOKS) {
+    if (arg != nullptr || arg_size != 0) {
+      errno = EINVAL;
+      return false;
+    }
+    return DispatchReset();
+  }
+  errno = ENOTSUP;
+  return false;
+}
diff --git a/libc/arch-mips/bionic/libgcc_compat.c b/libc/bionic/malloc_heapprofd.h
similarity index 78%
copy from libc/arch-mips/bionic/libgcc_compat.c
copy to libc/bionic/malloc_heapprofd.h
index 1a0f566..5a766fc 100644
--- a/libc/arch-mips/bionic/libgcc_compat.c
+++ b/libc/bionic/malloc_heapprofd.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,16 +26,18 @@
  * SUCH DAMAGE.
  */
 
-extern char __divdi3;
-extern char __moddi3;
-extern char __popcountsi2;
-extern char __udivdi3;
-extern char __umoddi3;
+#pragma once
 
-void* __bionic_libgcc_compat_symbols[] = {
-    &__divdi3,
-    &__moddi3,
-    &__popcountsi2,
-    &__udivdi3,
-    &__umoddi3,
-};
+#include <stdint.h>
+
+#include <private/bionic_globals.h>
+
+bool HeapprofdShouldLoad();
+
+void HeapprofdInstallHooksAtInit(libc_globals* globals);
+
+void HeapprofdInstallSignalHandler();
+
+void HeapprofdInstallErrorSignalHandler();
+
+bool HeapprofdMallopt(int optcode, void* arg, size_t arg_size);
diff --git a/libc/bionic/ndk_cruft.cpp b/libc/bionic/ndk_cruft.cpp
index dbacf18..2c3299f 100644
--- a/libc/bionic/ndk_cruft.cpp
+++ b/libc/bionic/ndk_cruft.cpp
@@ -355,9 +355,14 @@
   return malloc(size);
 }
 
+} // extern "C"
+
 #define __get_thread __real_get_thread
 #include "pthread_internal.h"
 #undef __get_thread
+
+extern "C" {
+
 // Various third-party apps contain a backport of our pthread_rwlock implementation that uses this.
 pthread_internal_t* __get_thread() {
   return __real_get_thread();
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index 8676a45..b8784b8 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -44,6 +44,7 @@
 #include "private/bionic_globals.h"
 #include "private/bionic_macros.h"
 #include "private/bionic_ssp.h"
+#include "private/bionic_systrace.h"
 #include "private/bionic_tls.h"
 #include "private/ErrnoRestorer.h"
 
@@ -70,6 +71,14 @@
   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;
@@ -288,8 +297,10 @@
   auto tcb = reinterpret_cast<bionic_tcb*>(mapping.static_tls + layout.offset_bionic_tcb());
   auto tls = reinterpret_cast<bionic_tls*>(mapping.static_tls + layout.offset_bionic_tls());
 
-  // (Re)initialize TLS pointers.
+  // Initialize TLS memory.
+  __init_static_tls(mapping.static_tls);
   __init_tcb(tcb, thread);
+  __init_tcb_dtv(tcb);
   __init_tcb_stack_guard(tcb);
   __init_bionic_tls_ptrs(tcb, tls);
 
@@ -337,6 +348,7 @@
   ErrnoRestorer errno_restorer;
 
   pthread_attr_t thread_attr;
+  ScopedTrace trace("pthread_create");
   if (attr == nullptr) {
     pthread_attr_init(&thread_attr);
   } else {
diff --git a/libc/bionic/pthread_detach.cpp b/libc/bionic/pthread_detach.cpp
index c2e4127..6b22039 100644
--- a/libc/bionic/pthread_detach.cpp
+++ b/libc/bionic/pthread_detach.cpp
@@ -34,7 +34,7 @@
 
 __BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_detach(pthread_t t) {
-  pthread_internal_t* thread = __pthread_internal_find(t);
+  pthread_internal_t* thread = __pthread_internal_find(t, "pthread_detach");
   if (thread == nullptr) {
     return ESRCH;
   }
diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp
index 84ea2e6..3b873b3 100644
--- a/libc/bionic/pthread_exit.cpp
+++ b/libc/bionic/pthread_exit.cpp
@@ -98,15 +98,22 @@
     thread->alternate_signal_stack = nullptr;
   }
 
+  ThreadJoinState old_state = THREAD_NOT_JOINED;
+  while (old_state == THREAD_NOT_JOINED &&
+         !atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_EXITED_NOT_JOINED)) {
+  }
+
+  // We don't want to take a signal after unmapping the stack, the shadow call
+  // stack, or dynamic TLS memory.
+  ScopedSignalBlocker ssb;
+
 #ifdef __aarch64__
   // Free the shadow call stack and guard pages.
   munmap(thread->shadow_call_stack_guard_region, SCS_GUARD_REGION_SIZE);
 #endif
 
-  ThreadJoinState old_state = THREAD_NOT_JOINED;
-  while (old_state == THREAD_NOT_JOINED &&
-         !atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_EXITED_NOT_JOINED)) {
-  }
+  // Free the ELF TLS DTV and all dynamically-allocated ELF TLS memory.
+  __free_dynamic_tls(__get_bionic_tcb());
 
   if (old_state == THREAD_DETACHED) {
     // The thread is detached, no one will use pthread_internal_t after pthread_exit.
@@ -121,10 +128,6 @@
     if (thread->mmap_size != 0) {
       // We need to free mapped space for detached threads when they exit.
       // That's not something we can do in C.
-
-      // We don't want to take a signal after we've unmapped the stack.
-      // That's one last thing we can do before dropping to assembler.
-      ScopedSignalBlocker ssb;
       __hwasan_thread_exit();
       _exit_with_stack_teardown(thread->mmap_base, thread->mmap_size);
     }
diff --git a/libc/bionic/pthread_getcpuclockid.cpp b/libc/bionic/pthread_getcpuclockid.cpp
index f641e4c..0b35998 100644
--- a/libc/bionic/pthread_getcpuclockid.cpp
+++ b/libc/bionic/pthread_getcpuclockid.cpp
@@ -31,7 +31,7 @@
 #include "pthread_internal.h"
 
 int pthread_getcpuclockid(pthread_t t, clockid_t* clockid) {
-  pid_t tid = pthread_gettid_np(t);
+  pid_t tid = __pthread_internal_gettid(t, "pthread_getcpuclockid");
   if (tid == -1) return ESRCH;
 
   // The tid is stored in the top bits, but negated.
diff --git a/libc/bionic/pthread_getschedparam.cpp b/libc/bionic/pthread_getschedparam.cpp
index cc1ece8..ed1853b 100644
--- a/libc/bionic/pthread_getschedparam.cpp
+++ b/libc/bionic/pthread_getschedparam.cpp
@@ -34,7 +34,7 @@
 int pthread_getschedparam(pthread_t t, int* policy, sched_param* param) {
   ErrnoRestorer errno_restorer;
 
-  pid_t tid = pthread_gettid_np(t);
+  pid_t tid = __pthread_internal_gettid(t, "pthread_getschedparam");
   if (tid == -1) return ESRCH;
 
   if (sched_getparam(tid, param) == -1) return errno;
diff --git a/libc/bionic/pthread_gettid_np.cpp b/libc/bionic/pthread_gettid_np.cpp
index 1beddc9..d14900b 100644
--- a/libc/bionic/pthread_gettid_np.cpp
+++ b/libc/bionic/pthread_gettid_np.cpp
@@ -31,6 +31,5 @@
 
 __BIONIC_WEAK_FOR_NATIVE_BRIDGE
 pid_t pthread_gettid_np(pthread_t t) {
-  pthread_internal_t* thread = __pthread_internal_find(t);
-  return thread ? thread->tid : -1;
+  return __pthread_internal_gettid(t, "pthread_gettid_np");
 }
diff --git a/libc/bionic/pthread_internal.cpp b/libc/bionic/pthread_internal.cpp
index 870a526..6fddefe 100644
--- a/libc/bionic/pthread_internal.cpp
+++ b/libc/bionic/pthread_internal.cpp
@@ -35,30 +35,13 @@
 
 #include <async_safe/log.h>
 
+#include "private/ScopedRWLock.h"
 #include "private/bionic_futex.h"
 #include "private/bionic_tls.h"
 
 static pthread_internal_t* g_thread_list = nullptr;
 static pthread_rwlock_t g_thread_list_lock = PTHREAD_RWLOCK_INITIALIZER;
 
-template <bool write> class ScopedRWLock {
- public:
-  explicit ScopedRWLock(pthread_rwlock_t* rwlock) : rwlock_(rwlock) {
-    (write ? pthread_rwlock_wrlock : pthread_rwlock_rdlock)(rwlock_);
-  }
-
-  ~ScopedRWLock() {
-    pthread_rwlock_unlock(rwlock_);
-  }
-
- private:
-  pthread_rwlock_t* rwlock_;
-  BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedRWLock);
-};
-
-typedef ScopedRWLock<true> ScopedWriteLock;
-typedef ScopedRWLock<false> ScopedReadLock;
-
 pthread_t __pthread_internal_add(pthread_internal_t* thread) {
   ScopedWriteLock locker(&g_thread_list_lock);
 
@@ -97,7 +80,12 @@
   __pthread_internal_free(thread);
 }
 
-pthread_internal_t* __pthread_internal_find(pthread_t thread_id) {
+pid_t __pthread_internal_gettid(pthread_t thread_id, const char* caller) {
+  pthread_internal_t* thread = __pthread_internal_find(thread_id, caller);
+  return thread ? thread->tid : -1;
+}
+
+pthread_internal_t* __pthread_internal_find(pthread_t thread_id, const char* caller) {
   pthread_internal_t* thread = reinterpret_cast<pthread_internal_t*>(thread_id);
 
   // Check if we're looking for ourselves before acquiring the lock.
@@ -120,9 +108,9 @@
       // addresses might sometimes contain threads or things that look enough like
       // threads for us to do some real damage by continuing.
       // TODO: try getting rid of this when Treble lets us keep vendor blobs on an old API level.
-      async_safe_format_log(ANDROID_LOG_WARN, "libc", "invalid pthread_t (0) passed to libc");
+      async_safe_format_log(ANDROID_LOG_WARN, "libc", "invalid pthread_t (0) passed to %s", caller);
     } else {
-      async_safe_fatal("invalid pthread_t %p passed to libc", thread);
+      async_safe_fatal("invalid pthread_t %p passed to %s", thread, caller);
     }
   }
   return nullptr;
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index 27ab3df..a1e0c45 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -38,6 +38,7 @@
 #define __hwasan_thread_exit()
 #endif
 
+#include "private/bionic_elf_tls.h"
 #include "private/bionic_lock.h"
 #include "private/bionic_tls.h"
 
@@ -154,6 +155,7 @@
 
 __LIBC_HIDDEN__ void __init_tcb(bionic_tcb* tcb, pthread_internal_t* thread);
 __LIBC_HIDDEN__ void __init_tcb_stack_guard(bionic_tcb* tcb);
+__LIBC_HIDDEN__ void __init_tcb_dtv(bionic_tcb* tcb);
 __LIBC_HIDDEN__ void __init_bionic_tls_ptrs(bionic_tcb* tcb, bionic_tls* tls);
 __LIBC_HIDDEN__ bionic_tls* __allocate_temp_bionic_tls();
 __LIBC_HIDDEN__ void __free_temp_bionic_tls(bionic_tls* tls);
@@ -161,10 +163,11 @@
 __LIBC_HIDDEN__ int __init_thread(pthread_internal_t* thread);
 __LIBC_HIDDEN__ ThreadMapping __allocate_thread_mapping(size_t stack_size, size_t stack_guard_size);
 
-__LIBC_HIDDEN__ pthread_t           __pthread_internal_add(pthread_internal_t* thread);
-__LIBC_HIDDEN__ pthread_internal_t* __pthread_internal_find(pthread_t pthread_id);
-__LIBC_HIDDEN__ void                __pthread_internal_remove(pthread_internal_t* thread);
-__LIBC_HIDDEN__ void                __pthread_internal_remove_and_free(pthread_internal_t* thread);
+__LIBC_HIDDEN__ pthread_t __pthread_internal_add(pthread_internal_t* thread);
+__LIBC_HIDDEN__ pthread_internal_t* __pthread_internal_find(pthread_t pthread_id, const char* caller);
+__LIBC_HIDDEN__ pid_t __pthread_internal_gettid(pthread_t pthread_id, const char* caller);
+__LIBC_HIDDEN__ void __pthread_internal_remove(pthread_internal_t* thread);
+__LIBC_HIDDEN__ void __pthread_internal_remove_and_free(pthread_internal_t* thread);
 
 static inline __always_inline bionic_tcb* __get_bionic_tcb() {
   return reinterpret_cast<bionic_tcb*>(&__get_tls()[MIN_TLS_SLOT]);
@@ -179,6 +182,15 @@
   return *static_cast<bionic_tls*>(__get_tls()[TLS_SLOT_BIONIC_TLS]);
 }
 
+static inline __always_inline TlsDtv* __get_tcb_dtv(bionic_tcb* tcb) {
+  uintptr_t dtv_slot = reinterpret_cast<uintptr_t>(tcb->tls_slot(TLS_SLOT_DTV));
+  return reinterpret_cast<TlsDtv*>(dtv_slot - offsetof(TlsDtv, generation));
+}
+
+static inline void __set_tcb_dtv(bionic_tcb* tcb, TlsDtv* val) {
+  tcb->tls_slot(TLS_SLOT_DTV) = &val->generation;
+}
+
 extern "C" __LIBC_HIDDEN__ int __set_tls(void* ptr);
 
 __LIBC_HIDDEN__ void pthread_key_clean_all(void);
diff --git a/libc/bionic/pthread_join.cpp b/libc/bionic/pthread_join.cpp
index 9aad458..e230fab 100644
--- a/libc/bionic/pthread_join.cpp
+++ b/libc/bionic/pthread_join.cpp
@@ -30,15 +30,17 @@
 
 #include "private/bionic_defs.h"
 #include "private/bionic_futex.h"
+#include "private/bionic_systrace.h"
 #include "pthread_internal.h"
 
 __BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_join(pthread_t t, void** return_value) {
+  ScopedTrace trace("pthread_join");
   if (t == pthread_self()) {
     return EDEADLK;
   }
 
-  pthread_internal_t* thread = __pthread_internal_find(t);
+  pthread_internal_t* thread = __pthread_internal_find(t, "pthread_join");
   if (thread == nullptr) {
     return ESRCH;
   }
diff --git a/libc/bionic/pthread_kill.cpp b/libc/bionic/pthread_kill.cpp
index 1531574..8b38f4c 100644
--- a/libc/bionic/pthread_kill.cpp
+++ b/libc/bionic/pthread_kill.cpp
@@ -35,7 +35,7 @@
 int pthread_kill(pthread_t t, int sig) {
   ErrnoRestorer errno_restorer;
 
-  pid_t tid = pthread_gettid_np(t);
+  pid_t tid = __pthread_internal_gettid(t, "pthread_kill");
 
   // tid gets reset to 0 on thread exit by CLONE_CHILD_CLEARTID.
   if (tid == 0 || tid == -1) return ESRCH;
diff --git a/libc/bionic/pthread_setname_np.cpp b/libc/bionic/pthread_setname_np.cpp
index f582d53..f673983 100644
--- a/libc/bionic/pthread_setname_np.cpp
+++ b/libc/bionic/pthread_setname_np.cpp
@@ -42,9 +42,10 @@
 // This value is not exported by kernel headers.
 #define MAX_TASK_COMM_LEN 16
 
-static int __open_task_comm_fd(pthread_t t, int flags) {
+static int __open_task_comm_fd(pthread_t t, int flags, const char* caller) {
   char comm_name[64];
-  snprintf(comm_name, sizeof(comm_name), "/proc/self/task/%d/comm", pthread_gettid_np(t));
+  snprintf(comm_name, sizeof(comm_name), "/proc/self/task/%d/comm",
+           __pthread_internal_gettid(t, caller));
   return open(comm_name, O_CLOEXEC | flags);
 }
 
@@ -59,7 +60,7 @@
   }
 
   // We have to get another thread's name.
-  int fd = __open_task_comm_fd(t, O_RDONLY);
+  int fd = __open_task_comm_fd(t, O_RDONLY, "pthread_getname_np");
   if (fd == -1) return errno;
 
   ssize_t n = TEMP_FAILURE_RETRY(read(fd, buf, buf_size));
@@ -91,7 +92,7 @@
   }
 
   // We have to set another thread's name.
-  int fd = __open_task_comm_fd(t, O_WRONLY);
+  int fd = __open_task_comm_fd(t, O_WRONLY, "pthread_setname_np");
   if (fd == -1) return errno;
 
   ssize_t n = TEMP_FAILURE_RETRY(write(fd, thread_name, thread_name_len));
diff --git a/libc/bionic/pthread_setschedparam.cpp b/libc/bionic/pthread_setschedparam.cpp
index 10826d1..8a02728 100644
--- a/libc/bionic/pthread_setschedparam.cpp
+++ b/libc/bionic/pthread_setschedparam.cpp
@@ -31,11 +31,12 @@
 #include <sched.h>
 
 #include "private/ErrnoRestorer.h"
+#include "pthread_internal.h"
 
 int pthread_setschedparam(pthread_t t, int policy, const sched_param* param) {
   ErrnoRestorer errno_restorer;
 
-  pid_t tid = pthread_gettid_np(t);
+  pid_t tid = __pthread_internal_gettid(t, "pthread_setschedparam");
   if (tid == -1) return ESRCH;
 
   return (sched_setscheduler(tid, policy, param) == -1) ? errno : 0;
@@ -44,7 +45,7 @@
 int pthread_setschedprio(pthread_t t, int priority) {
   ErrnoRestorer errno_restorer;
 
-  pid_t tid = pthread_gettid_np(t);
+  pid_t tid = __pthread_internal_gettid(t, "pthread_setschedprio");
   if (tid == -1) return ESRCH;
 
   sched_param param = { .sched_priority = priority };
diff --git a/libc/bionic/pthread_sigqueue.cpp b/libc/bionic/pthread_sigqueue.cpp
index 34bda38..5d13ed5 100644
--- a/libc/bionic/pthread_sigqueue.cpp
+++ b/libc/bionic/pthread_sigqueue.cpp
@@ -38,7 +38,7 @@
 int pthread_sigqueue(pthread_t t, int sig, const union sigval value) {
   ErrnoRestorer errno_restorer;
 
-  pid_t tid = pthread_gettid_np(t);
+  pid_t tid = __pthread_internal_gettid(t, "pthread_sigqueue");
   if (tid == -1) return ESRCH;
 
   siginfo_t siginfo;
diff --git a/libc/bionic/sigaction.cpp b/libc/bionic/sigaction.cpp
index 42dcccd..96e6f3c 100644
--- a/libc/bionic/sigaction.cpp
+++ b/libc/bionic/sigaction.cpp
@@ -43,7 +43,8 @@
   if (bionic_new_action != nullptr) {
     kernel_new_action.sa_flags = bionic_new_action->sa_flags;
     kernel_new_action.sa_handler = bionic_new_action->sa_handler;
-    kernel_new_action.sa_mask = filter_reserved_signals(bionic_new_action->sa_mask, SIG_SETMASK);
+    // Don't filter signals here; if the caller asked for everything to be blocked, we should obey.
+    kernel_new_action.sa_mask = bionic_new_action->sa_mask;
 #if defined(SA_RESTORER)
     kernel_new_action.sa_restorer = bionic_new_action->sa_restorer;
 #if defined(__aarch64__)
@@ -95,6 +96,7 @@
 #if defined(SA_RESTORER)
     kernel_new.sa_restorer = bionic_new->sa_restorer;
 #endif
+    // Don't filter signals here; if the caller asked for everything to be blocked, we should obey.
     memcpy(&kernel_new.sa_mask, &bionic_new->sa_mask, sizeof(bionic_new->sa_mask));
   }
 
@@ -122,7 +124,8 @@
       kernel_new.sa_restorer = (kernel_new.sa_flags & SA_SIGINFO) ? &__restore_rt : &__restore;
     }
 #endif
-    kernel_new.sa_mask = filter_reserved_signals(kernel_new.sa_mask, SIG_SETMASK);
+    // Don't filter signals here; if the caller asked for everything to be blocked, we should obey.
+    kernel_new.sa_mask = kernel_new.sa_mask;
   }
 
   return __rt_sigaction(signal,
diff --git a/libc/bionic/system_property_set.cpp b/libc/bionic/system_property_set.cpp
index bc3ba76..c508db1 100644
--- a/libc/bionic/system_property_set.cpp
+++ b/libc/bionic/system_property_set.cpp
@@ -42,6 +42,7 @@
 #include <unistd.h>
 
 #include <async_safe/log.h>
+#include <async_safe/CHECK.h>
 
 #include "private/bionic_defs.h"
 #include "private/bionic_macros.h"
diff --git a/libc/include/sys/cdefs.h b/libc/include/sys/cdefs.h
index e9d0e4b..a919a79 100644
--- a/libc/include/sys/cdefs.h
+++ b/libc/include/sys/cdefs.h
@@ -280,7 +280,7 @@
  */
 #  define __BIONIC_FORTIFY_VARIADIC static __inline__
 /* Error functions don't have bodies, so they can just be static. */
-#  define __BIONIC_ERROR_FUNCTION_VISIBILITY static
+#  define __BIONIC_ERROR_FUNCTION_VISIBILITY static __attribute__((unused))
 #else
 /* Further increase sharing for some inline functions */
 #  define __pass_object_size_n(n)
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 9dfdbc0..62aea27 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1446,8 +1446,10 @@
 
 LIBC_Q { # introduced=Q
   global:
+    ___tls_get_addr; # x86
     __aeabi_read_tp; # arm
     __res_randomid;
+    __tls_get_addr; # arm x86_64
     android_fdsan_close_with_tag;
     android_fdsan_create_owner_tag;
     android_fdsan_exchange_owner_tag;
@@ -1480,15 +1482,8 @@
     android_getaddrinfofornet; # apex
 
     # Used by libandroid_runtime
+    android_mallopt; # apex
     gMallocLeakZygoteChild; # apex
-
-    # TODO(b/120266448) hide these symbols again
-    # Used by libndk_translation
-    __getdents64; # arm x86 mips apex
-    tkill; # arm x86 mips apex
-    # Used by PtsBionicDeviceTestCases
-    __bionic_brk; # arm x86 mips apex
-    __system_property_add; # apex
 } LIBC_P;
 
 LIBC_PRIVATE {
@@ -1565,7 +1560,8 @@
     __arm_fadvise64_64; # arm
     __ashldi3; # arm
     __ashrdi3; # arm
-    __bionic_libgcc_compat_symbols; # arm x86
+    __bionic_brk; # arm x86 mips
+    __bionic_libcrt_compat_symbols; # arm x86
     __cmpdf2; # arm
     __divdf3; # arm
     __divdi3; # arm x86 mips
@@ -1590,6 +1586,7 @@
     __gedf2; # arm
     __get_thread; # arm x86 mips
     __get_tls; # arm x86 mips
+    __getdents64; # arm x86 mips
     __gnu_ldivmod_helper; # arm
     __gnu_uldivmod_helper; # arm
     __gnu_Unwind_Backtrace; # arm
@@ -1697,6 +1694,7 @@
     strntoumax; # arm x86 mips
     strtotimeval; # arm x86 mips
     sysv_signal; # arm x86 mips
+    tkill; # arm x86 mips
     wait3; # arm x86 mips
     wcswcs; # arm x86 mips
 } LIBC_Q;
@@ -1708,6 +1706,7 @@
 
 LIBC_PLATFORM {
   global:
+    __system_property_add;
     __system_property_area__; # var
     __system_property_area_init;
     __system_property_set_filename;
diff --git a/libc/malloc_debug/Config.cpp b/libc/malloc_debug/Config.cpp
index 926b265..dd20b5c 100644
--- a/libc/malloc_debug/Config.cpp
+++ b/libc/malloc_debug/Config.cpp
@@ -132,6 +132,9 @@
     {
         "verify_pointers", {TRACK_ALLOCS, &Config::VerifyValueEmpty},
     },
+    {
+        "abort_on_error", {ABORT_ON_ERROR, &Config::VerifyValueEmpty},
+    },
 };
 
 bool Config::ParseValue(const std::string& option, const std::string& value, size_t min_value,
diff --git a/libc/malloc_debug/Config.h b/libc/malloc_debug/Config.h
index 86d1ee4..011dc77 100644
--- a/libc/malloc_debug/Config.h
+++ b/libc/malloc_debug/Config.h
@@ -44,6 +44,7 @@
 constexpr uint64_t LEAK_TRACK = 0x100;
 constexpr uint64_t RECORD_ALLOCS = 0x200;
 constexpr uint64_t BACKTRACE_FULL = 0x400;
+constexpr uint64_t ABORT_ON_ERROR = 0x800;
 
 // In order to guarantee posix compliance, set the minimum alignment
 // to 8 bytes for 32 bit systems and 16 bytes for 64 bit systems.
diff --git a/libc/malloc_debug/GuardData.cpp b/libc/malloc_debug/GuardData.cpp
index debc14e..c307dc9 100644
--- a/libc/malloc_debug/GuardData.cpp
+++ b/libc/malloc_debug/GuardData.cpp
@@ -64,6 +64,9 @@
   error_log("Backtrace at time of failure:");
   BacktraceAndLog();
   error_log(LOG_DIVIDER);
+  if (g_debug->config().options() & ABORT_ON_ERROR) {
+    abort();
+  }
 }
 
 FrontGuardData::FrontGuardData(DebugData* debug_data, const Config& config, size_t* offset)
diff --git a/libc/malloc_debug/PointerData.cpp b/libc/malloc_debug/PointerData.cpp
index b0e2fc8..6e9d24f 100644
--- a/libc/malloc_debug/PointerData.cpp
+++ b/libc/malloc_debug/PointerData.cpp
@@ -206,7 +206,7 @@
     std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
     auto entry = pointers_.find(pointer);
     if (entry == pointers_.end()) {
-      // Error.
+      // Attempt to remove unknown pointer.
       error_log("No tracked pointer found for 0x%" PRIxPTR, pointer);
       return;
     }
@@ -266,12 +266,12 @@
   error_log("  hash_index %zu does not have matching frame data.", hash_index);
 }
 
-void PointerData::LogFreeError(const FreePointerInfoType& info, size_t usable_size) {
+void PointerData::LogFreeError(const FreePointerInfoType& info, size_t max_cmp_bytes) {
   error_log(LOG_DIVIDER);
   uint8_t* memory = reinterpret_cast<uint8_t*>(info.pointer);
   error_log("+++ ALLOCATION %p USED AFTER FREE", memory);
   uint8_t fill_free_value = g_debug->config().fill_free_value();
-  for (size_t i = 0; i < usable_size; i++) {
+  for (size_t i = 0; i < max_cmp_bytes; i++) {
     if (memory[i] != fill_free_value) {
       error_log("  allocation[%zu] = 0x%02x (expected 0x%02x)", i, memory[i], fill_free_value);
     }
@@ -283,6 +283,9 @@
   }
 
   error_log(LOG_DIVIDER);
+  if (g_debug->config().options() & ABORT_ON_ERROR) {
+    abort();
+  }
 }
 
 void PointerData::VerifyFreedPointer(const FreePointerInfoType& info) {
@@ -295,6 +298,9 @@
       error_log("+++ ALLOCATION 0x%" PRIxPTR " HAS CORRUPTED HEADER TAG 0x%x AFTER FREE",
                 info.pointer, header->tag);
       error_log(LOG_DIVIDER);
+      if (g_debug->config().options() & ABORT_ON_ERROR) {
+        abort();
+      }
 
       // Stop processing here, it is impossible to tell how the header
       // may have been damaged.
@@ -308,11 +314,12 @@
   size_t bytes = (usable_size < g_debug->config().fill_on_free_bytes())
                      ? usable_size
                      : g_debug->config().fill_on_free_bytes();
+  size_t max_cmp_bytes = bytes;
   const uint8_t* memory = reinterpret_cast<const uint8_t*>(info.pointer);
   while (bytes > 0) {
     size_t bytes_to_cmp = (bytes < g_cmp_mem.size()) ? bytes : g_cmp_mem.size();
     if (memcmp(memory, g_cmp_mem.data(), bytes_to_cmp) != 0) {
-      LogFreeError(info, usable_size);
+      LogFreeError(info, max_cmp_bytes);
     }
     bytes -= bytes_to_cmp;
     memory = &memory[bytes_to_cmp];
diff --git a/libc/malloc_debug/README.md b/libc/malloc_debug/README.md
index a8289b3..93b9b1e 100644
--- a/libc/malloc_debug/README.md
+++ b/libc/malloc_debug/README.md
@@ -394,6 +394,13 @@
 
 **NOTE**: This option is not available until the P release of Android.
 
+### abort\_on\_error
+When malloc debug detects an error, abort after sending the error
+log message.
+
+**NOTE**: If leak\_track is enabled, no abort occurs if leaks have been
+detected when the process is exiting.
+
 Additional Errors
 -----------------
 There are a few other error messages that might appear in the log.
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index 9075a9c..2e6afff 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -154,6 +154,9 @@
   error_log("Backtrace at time of failure:");
   BacktraceAndLog();
   error_log(LOG_DIVIDER);
+  if (g_debug->config().options() & ABORT_ON_ERROR) {
+    abort();
+  }
 }
 
 static bool VerifyPointer(const void* pointer, const char* function_name) {
diff --git a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
index a083b4f..fb54ee5 100644
--- a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
@@ -725,3 +725,21 @@
       "value must be <= 50000000: 100000000\n");
   ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
 }
+
+TEST_F(MallocDebugConfigTest, abort_on_error) {
+  ASSERT_TRUE(InitConfig("abort_on_error")) << getFakeLogPrint();
+  ASSERT_EQ(ABORT_ON_ERROR, config->options());
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, trigger_abort_fail) {
+  ASSERT_FALSE(InitConfig("abort_on_error=200")) << getFakeLogPrint();
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string log_msg(
+      "6 malloc_debug malloc_testing: value set for option 'abort_on_error' "
+      "which does not take a value\n");
+  ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index 2d6346f..6da95ca 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -990,6 +990,35 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
+TEST_F(MallocDebugTest, free_track_pointer_modified_after_free) {
+  Init("free_track=4 fill_on_free=2 free_track_backtrace_num_frames=0");
+
+  void* pointers[5];
+  for (size_t i = 0; i < sizeof(pointers) / sizeof(void*); i++) {
+    pointers[i] = debug_malloc(100);
+    ASSERT_TRUE(pointers[i] != nullptr);
+    memset(pointers[i], 0, 100);
+  }
+
+  debug_free(pointers[0]);
+
+  // overwrite the whole pointer, only expect errors on the fill bytes we check.
+  memset(pointers[0], 0x20, 100);
+
+  for (size_t i = 1; i < sizeof(pointers) / sizeof(void*); i++) {
+    debug_free(pointers[i]);
+  }
+
+  std::string expected_log(DIVIDER);
+  expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n",
+                                              pointers[0]);
+  expected_log += "6 malloc_debug   allocation[0] = 0x20 (expected 0xef)\n";
+  expected_log += "6 malloc_debug   allocation[1] = 0x20 (expected 0xef)\n";
+  expected_log += DIVIDER;
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
 TEST_F(MallocDebugTest, get_malloc_leak_info_invalid) {
   Init("fill");
 
@@ -2380,3 +2409,59 @@
   expected_log += DIVIDER;
   ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
 }
+
+TEST_F(MallocDebugTest, abort_on_error_log_error) {
+  Init("abort_on_error verify_pointers");
+
+  void* pointer = debug_malloc(10);
+  memset(pointer, 0, 10);
+  debug_free(pointer);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+
+  EXPECT_DEATH(debug_free(pointer), "");
+}
+
+TEST_F(MallocDebugTest, abort_on_error_guard_corrupted) {
+  Init("abort_on_error front_guard=32");
+
+  uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+  ASSERT_TRUE(pointer != nullptr);
+  pointer[-16] = 0x00;
+  EXPECT_DEATH(debug_free(pointer), "");
+  pointer[-16] = 0xaa;
+  debug_free(pointer);
+}
+
+TEST_F(MallocDebugTest, abort_on_error_use_after_free) {
+  Init("abort_on_error free_track=100 free_track_backtrace_num_frames=0");
+
+  uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+  ASSERT_TRUE(pointer != nullptr);
+  memset(pointer, 0, 100);
+  debug_free(pointer);
+
+  pointer[56] = 0x91;
+
+  EXPECT_DEATH(debug_finalize(), "");
+
+  pointer[56] = 0xef;
+}
+
+TEST_F(MallocDebugTest, abort_on_error_header_tag_corrupted) {
+  Init("abort_on_error free_track=100 free_track_backtrace_num_frames=0 rear_guard");
+
+  uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+  ASSERT_TRUE(pointer != nullptr);
+  memset(pointer, 0, 100);
+  debug_free(pointer);
+
+  uint8_t tag_value = pointer[-get_tag_offset()];
+  pointer[-get_tag_offset()] = 0x00;
+
+  EXPECT_DEATH(debug_finalize(), "");
+
+  pointer[-get_tag_offset()] = tag_value;
+}
+
diff --git a/libc/arch-mips/bionic/libgcc_compat.c b/libc/private/ScopedRWLock.h
similarity index 69%
copy from libc/arch-mips/bionic/libgcc_compat.c
copy to libc/private/ScopedRWLock.h
index 1a0f566..f034505 100644
--- a/libc/arch-mips/bionic/libgcc_compat.c
+++ b/libc/private/ScopedRWLock.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2008 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,16 +26,26 @@
  * SUCH DAMAGE.
  */
 
-extern char __divdi3;
-extern char __moddi3;
-extern char __popcountsi2;
-extern char __udivdi3;
-extern char __umoddi3;
+#pragma once
 
-void* __bionic_libgcc_compat_symbols[] = {
-    &__divdi3,
-    &__moddi3,
-    &__popcountsi2,
-    &__udivdi3,
-    &__umoddi3,
+#include <pthread.h>
+
+#include "private/bionic_macros.h"
+
+template <bool write> class ScopedRWLock {
+ public:
+  explicit ScopedRWLock(pthread_rwlock_t* rwlock) : rwlock_(rwlock) {
+    (write ? pthread_rwlock_wrlock : pthread_rwlock_rdlock)(rwlock_);
+  }
+
+  ~ScopedRWLock() {
+    pthread_rwlock_unlock(rwlock_);
+  }
+
+ private:
+  pthread_rwlock_t* rwlock_;
+  BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedRWLock);
 };
+
+typedef ScopedRWLock<true> ScopedWriteLock;
+typedef ScopedRWLock<false> ScopedReadLock;
diff --git a/linker/linker_allocator.h b/libc/private/bionic_allocator.h
similarity index 80%
rename from linker/linker_allocator.h
rename to libc/private/bionic_allocator.h
index 44a8b0d..c705ce4 100644
--- a/linker/linker_allocator.h
+++ b/libc/private/bionic_allocator.h
@@ -36,13 +36,11 @@
 #include <stddef.h>
 #include <unistd.h>
 
-#include <async_safe/log.h>
-
 const uint32_t kSmallObjectMaxSizeLog2 = 10;
 const uint32_t kSmallObjectMinSizeLog2 = 4;
 const uint32_t kSmallObjectAllocatorsCount = kSmallObjectMaxSizeLog2 - kSmallObjectMinSizeLog2 + 1;
 
-class LinkerSmallObjectAllocator;
+class BionicSmallObjectAllocator;
 
 // This structure is placed at the beginning of each addressable page
 // and has all information we need to find the corresponding memory allocator.
@@ -53,7 +51,7 @@
     // we use allocated_size for large objects allocator
     size_t allocated_size;
     // and allocator_addr for small ones.
-    LinkerSmallObjectAllocator* allocator_addr;
+    BionicSmallObjectAllocator* allocator_addr;
   };
 };
 
@@ -63,14 +61,14 @@
 };
 
 // This structure is placed at the beginning of each page managed by
-// LinkerSmallObjectAllocator.  Note that a page_info struct is expected at the
+// BionicSmallObjectAllocator.  Note that a page_info struct is expected at the
 // beginning of each page as well, and therefore this structure contains a
 // page_info as its *first* field.
 struct small_object_page_info {
   page_info info;  // Must be the first field.
 
   // Doubly linked list for traversing all pages allocated by a
-  // LinkerSmallObjectAllocator.
+  // BionicSmallObjectAllocator.
   small_object_page_info* next_page;
   small_object_page_info* prev_page;
 
@@ -81,9 +79,9 @@
   size_t free_blocks_cnt;
 };
 
-class LinkerSmallObjectAllocator {
+class BionicSmallObjectAllocator {
  public:
-  LinkerSmallObjectAllocator(uint32_t type, size_t block_size);
+  BionicSmallObjectAllocator(uint32_t type, size_t block_size);
   void* alloc();
   void free(void* ptr);
 
@@ -103,20 +101,23 @@
   small_object_page_info* page_list_;
 };
 
-class LinkerMemoryAllocator {
+class BionicAllocator {
  public:
-  constexpr LinkerMemoryAllocator() : allocators_(nullptr), allocators_buf_() {}
+  constexpr BionicAllocator() : allocators_(nullptr), allocators_buf_() {}
   void* alloc(size_t size);
+  void* memalign(size_t align, size_t size);
 
   // Note that this implementation of realloc never shrinks allocation
   void* realloc(void* ptr, size_t size);
   void free(void* ptr);
  private:
-  void* alloc_mmap(size_t size);
-  page_info* get_page_info(void* ptr);
-  LinkerSmallObjectAllocator* get_small_object_allocator(uint32_t type);
+  void* alloc_mmap(size_t align, size_t size);
+  inline void* alloc_impl(size_t align, size_t size);
+  inline page_info* get_page_info_unchecked(void* ptr);
+  inline page_info* get_page_info(void* ptr);
+  BionicSmallObjectAllocator* get_small_object_allocator(uint32_t type);
   void initialize_allocators();
 
-  LinkerSmallObjectAllocator* allocators_;
-  uint8_t allocators_buf_[sizeof(LinkerSmallObjectAllocator)*kSmallObjectAllocatorsCount];
+  BionicSmallObjectAllocator* allocators_;
+  uint8_t allocators_buf_[sizeof(BionicSmallObjectAllocator)*kSmallObjectAllocatorsCount];
 };
diff --git a/libc/private/bionic_asm_tls.h b/libc/private/bionic_asm_tls.h
index 06e3dce..92f707a 100644
--- a/libc/private/bionic_asm_tls.h
+++ b/libc/private/bionic_asm_tls.h
@@ -65,6 +65,9 @@
 //
 //  - TLS_SLOT_BIONIC_TLS: Optimizes accesses to bionic_tls by one load versus
 //    finding it using __get_thread().
+//
+//  - TLS_SLOT_APP: Available for use by apps in Android Q and later. (This slot
+//    was used for errno in P and earlier.)
 
 #if defined(__arm__) || defined(__aarch64__)
 
@@ -81,17 +84,15 @@
 #define TLS_SLOT_BIONIC_TLS       -1
 #define TLS_SLOT_DTV              0
 #define TLS_SLOT_THREAD_ID        1
-// Slot 2 is free (was historically used for TLS_SLOT_ERRNO)
+#define TLS_SLOT_APP              2 // was historically used for errno
 #define TLS_SLOT_OPENGL           3
 #define TLS_SLOT_OPENGL_API       4
 #define TLS_SLOT_STACK_GUARD      5
 #define TLS_SLOT_SANITIZER        6 // was historically used for dlerror
 #define TLS_SLOT_ART_THREAD_SELF  7
-#define TLS_SLOT_TSAN             8 // should be replaced with TLS_SLOT_SANITIZER
 
 // The maximum slot is fixed by the minimum TLS alignment in Bionic executables.
-// It should be changed to 7 once TLS_SLOT_TSAN is removed.
-#define MAX_TLS_SLOT              8
+#define MAX_TLS_SLOT              7
 
 #elif defined(__i386__) || defined(__x86_64__)
 
@@ -103,16 +104,15 @@
 
 #define TLS_SLOT_SELF             0
 #define TLS_SLOT_THREAD_ID        1
-// Slot 2 is free (was historically used for TLS_SLOT_ERRNO)
+#define TLS_SLOT_APP              2 // was historically used for errno
 #define TLS_SLOT_OPENGL           3
 #define TLS_SLOT_OPENGL_API       4
 #define TLS_SLOT_STACK_GUARD      5
 #define TLS_SLOT_SANITIZER        6 // was historically used for dlerror
 #define TLS_SLOT_ART_THREAD_SELF  7
-#define TLS_SLOT_TSAN             8 // should be replaced with TLS_SLOT_SANITIZER
-#define TLS_SLOT_DTV              9
-#define TLS_SLOT_BIONIC_TLS       10
-#define MAX_TLS_SLOT              10 // update this value when reserving a slot
+#define TLS_SLOT_DTV              8
+#define TLS_SLOT_BIONIC_TLS       9
+#define MAX_TLS_SLOT              9 // update this value when reserving a slot
 
 #endif
 
diff --git a/libc/private/bionic_elf_tls.h b/libc/private/bionic_elf_tls.h
index e847669..fa1af76 100644
--- a/libc/private/bionic_elf_tls.h
+++ b/libc/private/bionic_elf_tls.h
@@ -28,7 +28,25 @@
 
 #pragma once
 
+#include <link.h>
+#include <pthread.h>
+#include <stdatomic.h>
 #include <stdint.h>
+#include <sys/cdefs.h>
+
+__LIBC_HIDDEN__ extern _Atomic(size_t) __libc_tls_generation_copy;
+
+struct TlsSegment {
+  size_t size = 0;
+  size_t alignment = 1;
+  const void* init_ptr = "";    // Field is non-null even when init_size is 0.
+  size_t init_size = 0;
+};
+
+__LIBC_HIDDEN__ bool __bionic_get_tls_segment(const ElfW(Phdr)* phdr_table, size_t phdr_count,
+                                              ElfW(Addr) load_bias, TlsSegment* out);
+
+__LIBC_HIDDEN__ bool __bionic_check_tls_alignment(size_t* alignment);
 
 struct StaticTlsLayout {
   constexpr StaticTlsLayout() {}
@@ -45,13 +63,17 @@
 public:
   size_t offset_bionic_tcb() const { return offset_bionic_tcb_; }
   size_t offset_bionic_tls() const { return offset_bionic_tls_; }
+  size_t offset_thread_pointer() const;
 
   size_t size() const { return offset_; }
   size_t alignment() const { return alignment_; }
   bool overflowed() const { return overflowed_; }
 
-  void reserve_tcb();
+  size_t reserve_exe_segment_and_tcb(const TlsSegment* exe_segment, const char* progname);
   void reserve_bionic_tls();
+  size_t reserve_solib_segment(const TlsSegment& segment) {
+    return reserve(segment.size, segment.alignment);
+  }
   void finish_layout();
 
 private:
@@ -63,3 +85,93 @@
 
   size_t round_up_with_overflow_check(size_t value, size_t alignment);
 };
+
+static constexpr size_t kTlsGenerationNone = 0;
+static constexpr size_t kTlsGenerationFirst = 1;
+
+// The first ELF TLS module has ID 1. Zero is reserved for the first word of
+// the DTV, a generation count. Unresolved weak symbols also use module ID 0.
+static constexpr size_t kTlsUninitializedModuleId = 0;
+
+static inline size_t __tls_module_id_to_idx(size_t id) { return id - 1; }
+static inline size_t __tls_module_idx_to_id(size_t idx) { return idx + 1; }
+
+// A descriptor for a single ELF TLS module.
+struct TlsModule {
+  TlsSegment segment;
+
+  // Offset into the static TLS block or SIZE_MAX for a dynamic module.
+  size_t static_offset = SIZE_MAX;
+
+  // The generation in which this module was loaded. Dynamic TLS lookups use
+  // this field to detect when a module has been unloaded.
+  size_t first_generation = kTlsGenerationNone;
+
+  // Used by the dynamic linker to track the associated soinfo* object.
+  void* soinfo_ptr = nullptr;
+};
+
+// Table of the ELF TLS modules. Either the dynamic linker or the static
+// initialization code prepares this table, and it's then used during thread
+// creation and for dynamic TLS lookups.
+struct TlsModules {
+  constexpr TlsModules() {}
+
+  // A pointer to the TLS generation counter in libc.so. The counter is
+  // incremented each time an solib is loaded or unloaded.
+  _Atomic(size_t) generation = kTlsGenerationFirst;
+  _Atomic(size_t) *generation_libc_so = nullptr;
+
+  // Access to the TlsModule[] table requires taking this lock.
+  pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
+
+  // Pointer to a block of TlsModule objects. The first module has ID 1 and
+  // is stored at index 0 in this table.
+  size_t module_count = 0;
+  TlsModule* module_table = nullptr;
+};
+
+void __init_static_tls(void* static_tls);
+
+// Dynamic Thread Vector. Each thread has a different DTV. For each module
+// (executable or solib), the DTV has a pointer to that module's TLS memory. The
+// DTV is initially empty and is allocated on-demand. It grows as more modules
+// are dlopen'ed. See https://www.akkadia.org/drepper/tls.pdf.
+//
+// The layout of the DTV is specified in various documents, but it is not part
+// of Bionic's public ABI. A compiler can't generate code to access it directly,
+// because it can't access libc's global generation counter.
+struct TlsDtv {
+  // Number of elements in this object's modules field.
+  size_t count;
+
+  // A pointer to an older TlsDtv object that should be freed when the thread
+  // exits. The objects aren't immediately freed because a DTV could be
+  // reallocated by a signal handler that interrupted __tls_get_addr's fast
+  // path.
+  TlsDtv* next;
+
+  // The DTV slot points at this field, which allows omitting an add instruction
+  // on the fast path for a TLS lookup. The arm64 tlsdesc_resolver.S depends on
+  // the layout of fields past this point.
+  size_t generation;
+  void* modules[];
+};
+
+struct TlsIndex {
+  size_t module_id;
+  size_t offset;
+};
+
+#if defined(__i386__)
+#define TLS_GET_ADDR_CCONV __attribute__((regparm(1)))
+#define TLS_GET_ADDR ___tls_get_addr
+#else
+#define TLS_GET_ADDR_CCONV
+#define TLS_GET_ADDR __tls_get_addr
+#endif
+
+extern "C" void* TLS_GET_ADDR(const TlsIndex* ti) TLS_GET_ADDR_CCONV;
+
+struct bionic_tcb;
+void __free_dynamic_tls(bionic_tcb* tcb);
diff --git a/libc/private/bionic_globals.h b/libc/private/bionic_globals.h
index b5e677e..447b3b9 100644
--- a/libc/private/bionic_globals.h
+++ b/libc/private/bionic_globals.h
@@ -29,10 +29,12 @@
 #ifndef _PRIVATE_BIONIC_GLOBALS_H
 #define _PRIVATE_BIONIC_GLOBALS_H
 
+#include <stdatomic.h>
 #include <sys/cdefs.h>
 #include <link.h>
 #include <pthread.h>
 
+#include "private/bionic_allocator.h"
 #include "private/bionic_elf_tls.h"
 #include "private/bionic_fdsan.h"
 #include "private/bionic_malloc_dispatch.h"
@@ -42,7 +44,18 @@
 struct libc_globals {
   vdso_entry vdso[VDSO_END];
   long setjmp_cookie;
-  MallocDispatch malloc_dispatch;
+
+  // In order to allow a complete switch between dispatch tables without
+  // the need for copying each function by function in the structure,
+  // use a single atomic pointer to switch.
+  // The current_dispatch_table pointer can only ever be set to a complete
+  // table. Any dispatch table that is pointed to by current_dispatch_table
+  // cannot be modified after that. If the pointer changes in the future,
+  // the old pointer must always stay valid.
+  // The malloc_dispatch_table is modified by malloc debug, malloc hooks,
+  // and heaprofd. Only one of these modes can be active at any given time.
+  _Atomic(const MallocDispatch*) current_dispatch_table;
+  MallocDispatch malloc_dispatch_table;
 };
 
 __LIBC_HIDDEN__ extern WriteProtected<libc_globals> __libc_globals;
@@ -69,6 +82,8 @@
   abort_msg_t* abort_msg = nullptr;
 
   StaticTlsLayout static_tls_layout;
+  TlsModules tls_modules;
+  BionicAllocator tls_allocator;
 
   // Values passed from the linker to libc.so.
   const char* init_progname = nullptr;
@@ -84,6 +99,7 @@
 
 #if defined(__i386__)
 __LIBC_HIDDEN__ extern void* __libc_sysinfo;
+__LIBC_HIDDEN__ void __libc_int0x80();
 __LIBC_HIDDEN__ void __libc_init_sysinfo();
 #endif
 
diff --git a/libc/arch-mips/bionic/libgcc_compat.c b/libc/private/bionic_malloc.h
similarity index 65%
copy from libc/arch-mips/bionic/libgcc_compat.c
copy to libc/private/bionic_malloc.h
index 1a0f566..5f4a75d 100644
--- a/libc/arch-mips/bionic/libgcc_compat.c
+++ b/libc/private/bionic_malloc.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,16 +26,23 @@
  * SUCH DAMAGE.
  */
 
-extern char __divdi3;
-extern char __moddi3;
-extern char __popcountsi2;
-extern char __udivdi3;
-extern char __umoddi3;
+#pragma once
 
-void* __bionic_libgcc_compat_symbols[] = {
-    &__divdi3,
-    &__moddi3,
-    &__popcountsi2,
-    &__udivdi3,
-    &__umoddi3,
+#include <stdbool.h>
+
+// Opcodes for android_mallopt.
+
+enum {
+  // Marks the calling process as a profileable zygote child, possibly
+  // initializing profiling infrastructure.
+  M_INIT_ZYGOTE_CHILD_PROFILING = 1,
+#define M_INIT_ZYGOTE_CHILD_PROFILING M_INIT_ZYGOTE_CHILD_PROFILING
+  M_RESET_HOOKS = 2,
+#define M_RESET_HOOKS M_RESET_HOOKS
 };
+
+// Manipulates bionic-specific handling of memory allocation APIs such as
+// malloc. Only for use by the Android platform itself.
+//
+// On success, returns true. On failure, returns false and sets errno.
+extern "C" bool android_mallopt(int opcode, void* arg, size_t arg_size);
diff --git a/libc/private/bionic_malloc_dispatch.h b/libc/private/bionic_malloc_dispatch.h
index f15b72c..0dce03d 100644
--- a/libc/private/bionic_malloc_dispatch.h
+++ b/libc/private/bionic_malloc_dispatch.h
@@ -31,7 +31,6 @@
 
 #include <stddef.h>
 #include <stdint.h>
-#include <stdatomic.h>
 #include <private/bionic_config.h>
 
 // Entry in malloc dispatch table.
@@ -55,25 +54,25 @@
 #endif
 
 struct MallocDispatch {
-  _Atomic MallocCalloc calloc;
-  _Atomic MallocFree free;
-  _Atomic MallocMallinfo mallinfo;
-  _Atomic MallocMalloc malloc;
-  _Atomic MallocMallocUsableSize malloc_usable_size;
-  _Atomic MallocMemalign memalign;
-  _Atomic MallocPosixMemalign posix_memalign;
+  MallocCalloc calloc;
+  MallocFree free;
+  MallocMallinfo mallinfo;
+  MallocMalloc malloc;
+  MallocMallocUsableSize malloc_usable_size;
+  MallocMemalign memalign;
+  MallocPosixMemalign posix_memalign;
 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-  _Atomic MallocPvalloc pvalloc;
+  MallocPvalloc pvalloc;
 #endif
-  _Atomic MallocRealloc realloc;
+  MallocRealloc realloc;
 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-  _Atomic MallocValloc valloc;
+  MallocValloc valloc;
 #endif
-  _Atomic MallocIterate iterate;
-  _Atomic MallocMallocDisable malloc_disable;
-  _Atomic MallocMallocEnable malloc_enable;
-  _Atomic MallocMallopt mallopt;
-  _Atomic MallocAlignedAlloc aligned_alloc;
+  MallocIterate iterate;
+  MallocMallocDisable malloc_disable;
+  MallocMallocEnable malloc_enable;
+  MallocMallopt mallopt;
+  MallocAlignedAlloc aligned_alloc;
 } __attribute__((aligned(32)));
 
 #endif
diff --git a/libc/seccomp/include/seccomp_policy.h b/libc/seccomp/include/seccomp_policy.h
index 49280f4..fd0fb60 100644
--- a/libc/seccomp/include/seccomp_policy.h
+++ b/libc/seccomp/include/seccomp_policy.h
@@ -17,8 +17,14 @@
 #pragma once
 
 #include <stddef.h>
+#include <stdint.h>
 #include <linux/filter.h>
 
 bool set_app_seccomp_filter();
+bool set_app_zygote_seccomp_filter();
 bool set_system_seccomp_filter();
 bool set_global_seccomp_filter();
+
+// Installs a filter that limits setresuid/setresgid to a range of
+// [uid_gid_min..uid_gid_max] (for the real-, effective- and super-ids).
+bool install_setuidgid_seccomp_filter(uint32_t uid_gid_min, uint32_t uid_gid_max);
diff --git a/libc/seccomp/seccomp_bpfs.h b/libc/seccomp/seccomp_bpfs.h
index 797dfc5..d9e8047 100644
--- a/libc/seccomp/seccomp_bpfs.h
+++ b/libc/seccomp/seccomp_bpfs.h
@@ -21,6 +21,8 @@
 
 extern const struct sock_filter arm_app_filter[];
 extern const size_t arm_app_filter_size;
+extern const struct sock_filter arm_app_zygote_filter[];
+extern const size_t arm_app_zygote_filter_size;
 extern const struct sock_filter arm_system_filter[];
 extern const size_t arm_system_filter_size;
 extern const struct sock_filter arm_global_filter[];
@@ -28,6 +30,8 @@
 
 extern const struct sock_filter arm64_app_filter[];
 extern const size_t arm64_app_filter_size;
+extern const struct sock_filter arm64_app_zygote_filter[];
+extern const size_t arm64_app_zygote_filter_size;
 extern const struct sock_filter arm64_system_filter[];
 extern const size_t arm64_system_filter_size;
 extern const struct sock_filter arm64_global_filter[];
@@ -35,6 +39,8 @@
 
 extern const struct sock_filter x86_app_filter[];
 extern const size_t x86_app_filter_size;
+extern const struct sock_filter x86_app_zygote_filter[];
+extern const size_t x86_app_zygote_filter_size;
 extern const struct sock_filter x86_system_filter[];
 extern const size_t x86_system_filter_size;
 extern const struct sock_filter x86_global_filter[];
@@ -42,6 +48,8 @@
 
 extern const struct sock_filter x86_64_app_filter[];
 extern const size_t x86_64_app_filter_size;
+extern const struct sock_filter x86_64_app_zygote_filter[];
+extern const size_t x86_64_app_zygote_filter_size;
 extern const struct sock_filter x86_64_system_filter[];
 extern const size_t x86_64_system_filter_size;
 extern const struct sock_filter x86_64_global_filter[];
@@ -49,6 +57,8 @@
 
 extern const struct sock_filter mips_app_filter[];
 extern const size_t mips_app_filter_size;
+extern const struct sock_filter mips_app_zygote_filter[];
+extern const size_t mips_app_zygote_filter_size;
 extern const struct sock_filter mips_system_filter[];
 extern const size_t mips_system_filter_size;
 extern const struct sock_filter mips_global_filter[];
@@ -56,6 +66,8 @@
 
 extern const struct sock_filter mips64_app_filter[];
 extern const size_t mips64_app_filter_size;
+extern const struct sock_filter mips64_app_zygote_filter[];
+extern const size_t mips64_app_zygote_filter_size;
 extern const struct sock_filter mips64_system_filter[];
 extern const size_t mips64_system_filter_size;
 extern const struct sock_filter mips64_global_filter[];
diff --git a/libc/seccomp/seccomp_policy.cpp b/libc/seccomp/seccomp_policy.cpp
index 3d617be..222a2c8 100644
--- a/libc/seccomp/seccomp_policy.cpp
+++ b/libc/seccomp/seccomp_policy.cpp
@@ -20,78 +20,111 @@
 #include <linux/audit.h>
 #include <linux/seccomp.h>
 #include <sys/prctl.h>
+#include <sys/syscall.h>
 
 #include <vector>
 
 #include <android-base/logging.h>
 
+#include "func_to_syscall_nrs.h"
 #include "seccomp_bpfs.h"
 
-
 #if defined __arm__ || defined __aarch64__
 
 #define DUAL_ARCH
 #define PRIMARY_ARCH AUDIT_ARCH_AARCH64
 static const struct sock_filter* primary_app_filter = arm64_app_filter;
 static const size_t primary_app_filter_size = arm64_app_filter_size;
+static const struct sock_filter* primary_app_zygote_filter = arm64_app_zygote_filter;
+static const size_t primary_app_zygote_filter_size = arm64_app_zygote_filter_size;
 static const struct sock_filter* primary_system_filter = arm64_system_filter;
 static const size_t primary_system_filter_size = arm64_system_filter_size;
 static const struct sock_filter* primary_global_filter = arm64_global_filter;
 static const size_t primary_global_filter_size = arm64_global_filter_size;
+
+static const long primary_setresgid = __arm64_setresgid;
+static const long primary_setresuid = __arm64_setresuid;
 #define SECONDARY_ARCH AUDIT_ARCH_ARM
 static const struct sock_filter* secondary_app_filter = arm_app_filter;
 static const size_t secondary_app_filter_size = arm_app_filter_size;
+static const struct sock_filter* secondary_app_zygote_filter = arm_app_zygote_filter;
+static const size_t secondary_app_zygote_filter_size = arm_app_zygote_filter_size;
 static const struct sock_filter* secondary_system_filter = arm_system_filter;
 static const size_t secondary_system_filter_size = arm_system_filter_size;
 static const struct sock_filter* secondary_global_filter = arm_global_filter;
 static const size_t secondary_global_filter_size = arm_global_filter_size;
 
+static const long secondary_setresgid = __arm_setresgid;
+static const long secondary_setresuid = __arm_setresuid;
 #elif defined __i386__ || defined __x86_64__
 
 #define DUAL_ARCH
 #define PRIMARY_ARCH AUDIT_ARCH_X86_64
 static const struct sock_filter* primary_app_filter = x86_64_app_filter;
 static const size_t primary_app_filter_size = x86_64_app_filter_size;
+static const struct sock_filter* primary_app_zygote_filter = x86_64_app_zygote_filter;
+static const size_t primary_app_zygote_filter_size = x86_64_app_zygote_filter_size;
 static const struct sock_filter* primary_system_filter = x86_64_system_filter;
 static const size_t primary_system_filter_size = x86_64_system_filter_size;
 static const struct sock_filter* primary_global_filter = x86_64_global_filter;
 static const size_t primary_global_filter_size = x86_64_global_filter_size;
+
+static const long primary_setresgid = __x86_64_setresgid;
+static const long primary_setresuid = __x86_64_setresuid;
 #define SECONDARY_ARCH AUDIT_ARCH_I386
 static const struct sock_filter* secondary_app_filter = x86_app_filter;
 static const size_t secondary_app_filter_size = x86_app_filter_size;
+static const struct sock_filter* secondary_app_zygote_filter = x86_app_zygote_filter;
+static const size_t secondary_app_zygote_filter_size = x86_app_zygote_filter_size;
 static const struct sock_filter* secondary_system_filter = x86_system_filter;
 static const size_t secondary_system_filter_size = x86_system_filter_size;
 static const struct sock_filter* secondary_global_filter = x86_global_filter;
 static const size_t secondary_global_filter_size = x86_global_filter_size;
 
+static const long secondary_setresgid = __x86_setresgid;
+static const long secondary_setresuid = __x86_setresuid;
 #elif defined __mips__ || defined __mips64__
 
 #define DUAL_ARCH
 #define PRIMARY_ARCH AUDIT_ARCH_MIPSEL64
 static const struct sock_filter* primary_app_filter = mips64_app_filter;
 static const size_t primary_app_filter_size = mips64_app_filter_size;
+static const struct sock_filter* primary_app_zygote_filter = mips64_app_zygote_filter;
+static const size_t primary_app_zygote_filter_size = mips64_app_zygote_filter_size;
 static const struct sock_filter* primary_system_filter = mips64_system_filter;
 static const size_t primary_system_filter_size = mips64_system_filter_size;
 static const struct sock_filter* primary_global_filter = mips64_global_filter;
 static const size_t primary_global_filter_size = mips64_global_filter_size;
+
+static const long primary_setresgid = __mips64_setresgid;
+static const long primary_setresuid = __mips64_setresuid;
 #define SECONDARY_ARCH AUDIT_ARCH_MIPSEL
 static const struct sock_filter* secondary_app_filter = mips_app_filter;
 static const size_t secondary_app_filter_size = mips_app_filter_size;
+static const struct sock_filter* secondary_app_zygote_filter = mips_app_zygote_filter;
+static const size_t secondary_app_zygote_filter_size = mips_app_zygote_filter_size;
 static const struct sock_filter* secondary_system_filter = mips_system_filter;
 static const size_t secondary_system_filter_size = mips_system_filter_size;
 static const struct sock_filter* secondary_global_filter = mips_global_filter;
 static const size_t secondary_global_filter_size = mips_global_filter_size;
 
+static const long secondary_setresgid = __mips_setresgid;
+static const long secondary_setresuid = __mips_setresuid;
 #else
 #error No architecture was defined!
 #endif
 
 
 #define syscall_nr (offsetof(struct seccomp_data, nr))
+#define syscall_arg(_n) (offsetof(struct seccomp_data, args[_n]))
 #define arch_nr (offsetof(struct seccomp_data, arch))
 
 typedef std::vector<sock_filter> filter;
 
+inline void Allow(filter& f) {
+    f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW));
+}
+
 inline void Disallow(filter& f) {
     f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRAP));
 }
@@ -128,6 +161,49 @@
 }
 #endif
 
+static void ValidateSyscallArgInRange(filter& f, __u32 arg_num, __u32 range_min, __u32 range_max) {
+    const __u32 syscall_arg = syscall_arg(arg_num);
+
+    if (range_max == UINT32_MAX) {
+        LOG(FATAL) << "range_max exceeds maximum argument range.";
+        return;
+    }
+
+    f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_arg));
+    f.push_back(BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, range_min, 0, 1));
+    f.push_back(BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, range_max + 1, 0, 1));
+    Disallow(f);
+}
+
+// This filter is meant to be installed in addition to a regular whitelist filter.
+// Therefore, it's default action has to be Allow, except when the evaluated
+// system call matches setresuid/setresgid and the arguments don't fall within the
+// passed in range.
+//
+// The regular whitelist only allows setresuid/setresgid for UID/GID changes, so
+// that's the only system call we need to check here. A CTS test ensures the other
+// calls will remain blocked.
+static void ValidateSetUidGid(filter& f, uint32_t uid_gid_min, uint32_t uid_gid_max, bool primary) {
+    // Check setresuid(ruid, euid, sguid) fall within range
+    ExamineSyscall(f);
+    __u32 setresuid_nr = primary ? primary_setresuid : secondary_setresuid;
+    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, setresuid_nr, 0, 12));
+    for (int arg = 0; arg < 3; arg++) {
+        ValidateSyscallArgInRange(f, arg, uid_gid_min, uid_gid_max);
+    }
+
+    // Check setresgid(rgid, egid, sgid) fall within range
+    ExamineSyscall(f);
+    __u32 setresgid_nr = primary ? primary_setresgid : secondary_setresgid;
+    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, setresgid_nr, 0, 12));
+    for (int arg = 0; arg < 3; arg++) {
+        ValidateSyscallArgInRange(f, arg, uid_gid_min, uid_gid_max);
+    }
+
+    // Default is to allow; other filters may still reject this call.
+    Allow(f);
+}
+
 static bool install_filter(filter const& f) {
     struct sock_fprog prog = {
         static_cast<unsigned short>(f.size()),
@@ -141,8 +217,33 @@
     return true;
 }
 
+bool _install_setuidgid_filter(uint32_t uid_gid_min, uint32_t uid_gid_max) {
+    filter f;
+#ifdef DUAL_ARCH
+    // Note that for mixed 64/32 bit architectures, ValidateArchitecture inserts a
+    // jump that must be changed to point to the start of the 32-bit policy
+    // 32 bit syscalls will not hit the policy between here and the call to SetJump
+    auto offset_to_secondary_filter = ValidateArchitectureAndJumpIfNeeded(f);
+#else
+    ValidateArchitecture(f);
+#endif
+
+    ValidateSetUidGid(f, uid_gid_min, uid_gid_max, true /* primary */);
+
+#ifdef DUAL_ARCH
+    if (!SetValidateArchitectureJumpTarget(offset_to_secondary_filter, f)) {
+        return false;
+    }
+
+    ValidateSetUidGid(f, uid_gid_min, uid_gid_max, false /* primary */);
+#endif
+
+    return install_filter(f);
+}
+
 enum FilterType {
   APP,
+  APP_ZYGOTE,
   SYSTEM,
   GLOBAL
 };
@@ -159,6 +260,12 @@
         s = secondary_app_filter;
         s_size = secondary_app_filter_size;
         break;
+      case APP_ZYGOTE:
+        p = primary_app_zygote_filter;
+        p_size = primary_app_zygote_filter_size;
+        s = secondary_app_zygote_filter;
+        s_size = secondary_app_zygote_filter_size;
+        break;
       case SYSTEM:
         p = primary_system_filter;
         p_size = primary_system_filter_size;
@@ -210,6 +317,10 @@
     return _set_seccomp_filter(FilterType::APP);
 }
 
+bool set_app_zygote_seccomp_filter() {
+    return _set_seccomp_filter(FilterType::APP_ZYGOTE);
+}
+
 bool set_system_seccomp_filter() {
     return _set_seccomp_filter(FilterType::SYSTEM);
 }
@@ -217,3 +328,7 @@
 bool set_global_seccomp_filter() {
     return _set_seccomp_filter(FilterType::GLOBAL);
 }
+
+bool install_setuidgid_seccomp_filter(uint32_t uid_gid_min, uint32_t uid_gid_max) {
+    return _install_setuidgid_filter(uid_gid_min, uid_gid_max);
+}
diff --git a/libc/stdio/fmemopen.cpp b/libc/stdio/fmemopen.cpp
index 9d8c41f..6e333ba 100644
--- a/libc/stdio/fmemopen.cpp
+++ b/libc/stdio/fmemopen.cpp
@@ -149,7 +149,9 @@
   } else if (mode[0] == 'w') {
     ck->size = 0;
     ck->offset = 0;
-    ck->buf[0] = '\0';
+    if (capacity > 0) {
+      ck->buf[0] = '\0';
+    }
   }
 
   return fp;
diff --git a/libc/symbol_ordering b/libc/symbol_ordering
new file mode 100644
index 0000000..6fcc09e
--- /dev/null
+++ b/libc/symbol_ordering
@@ -0,0 +1,202 @@
+# This file is generated by sorting symbols in the .bss section in libc.so by
+# their sizes and taking out symbols that are unique to a target.  By sorting
+# symbols by size, we usually have less dirty pages at runtime, because small
+# symbols are grouped together.
+
+_ZZ17__find_icu_symbolPKcE9found_icu
+_ZL24gHeapprofdInitInProgress
+_ZL27gHeapprofdInitHookInstalled
+je_opt_abort
+je_opt_abort_conf
+je_opt_junk_alloc
+je_opt_junk_free
+je_opt_utrace
+je_opt_xmalloc
+je_opt_zero
+malloc_disabled_tcache
+had_conf_error
+malloc_slow_flags
+je_opt_background_thread
+ctl_initialized
+je_log_init_done
+mmap_flags
+os_overcommits
+je_opt_stats_print
+je_tsd_booted
+global_hashtable_initialized
+gmtcheck.gmt_is_set
+restartloop
+_ZZ12bindresvportE4port
+ru_counter
+ru_a
+ru_x
+ru_b
+ru_seed
+ru_g
+ru_seed2
+ru_msb
+je_narenas_auto
+je_ncpus
+je_init_system_thp_mode
+je_nhbins
+je_tsd_tsd
+optreset
+_rs_forked
+daylight
+gMallocLeakZygoteChild
+_ZL18netdClientInitOnce
+je_opt_narenas
+narenas_total
+je_malloc_disable.once_control
+je_opt_metadata_thp
+je_opt_thp
+stack_nelms
+tcaches_past
+ncleanups
+error_message_count
+error_one_per_line
+_ZZ13error_at_lineE9last_line
+_ZL13g_locale_once
+_ZL30g_propservice_protocol_version
+_res_cache_once
+_res_key
+_rs_forkdetect._rs_pid
+ru_pid
+lcl_is_set
+__cxa_finalize.call_depth
+seed48.sseed
+ether_aton.addr
+je_background_thread_info
+je_malloc_message
+je_tcache_bin_info
+je_tcache_maxclass
+je_tcaches
+optarg
+suboptarg
+timezone
+_ZGVZ17__find_icu_symbolPKcE9found_icu
+_ZL17g_libicuuc_handle
+__malloc_hook
+__realloc_hook
+__free_hook
+__memalign_hook
+je_malloc_conf
+malloc_initializer
+a0
+je_opt_dirty_decay_ms
+je_opt_muzzy_decay_ms
+dirty_decay_ms_default.0
+muzzy_decay_ms_default.0
+b0
+ctl_arenas
+ctl_stats
+je_hooks_arena_new_hook
+os_page
+tcaches_avail
+_ZN9prop_area8pa_size_E
+_ZN9prop_area13pa_data_size_E
+_ZL6g_lock
+_ZL6g_tags
+_ZZ8c16rtombE15__private_state
+_ZZ8c32rtombE15__private_state
+environ
+error_print_progname
+_ZZ13error_at_lineE9last_file
+_ZZ14__icu_charTypejE10u_charType
+_ZGVZ14__icu_charTypejE10u_charType
+_ZZ25__icu_getIntPropertyValuej9UPropertyE21u_getIntPropertyValue
+_ZGVZ25__icu_getIntPropertyValuej9UPropertyE21u_getIntPropertyValue
+_ZZ23__icu_hasBinaryPropertyj9UPropertyPFiiEE19u_hasBinaryProperty
+_ZGVZ23__icu_hasBinaryPropertyj9UPropertyPFiiEE19u_hasBinaryProperty
+__progname
+_ZZ8mbrtoc16E15__private_state
+_ZZ8mbrtoc32E15__private_state
+_ZL14syslog_log_tag
+__system_property_area__
+_ZZ7mbrtowcE15__private_state
+_ZZ10mbsnrtowcsE15__private_state
+_ZZ7wcrtombE15__private_state
+_ZZ10wcsnrtombsE15__private_state
+_ZZ8iswcntrlE10u_charType
+_ZGVZ8iswcntrlE10u_charType
+_ZZ8iswdigitE9u_isdigit
+_ZGVZ8iswdigitE9u_isdigit
+_ZZ8iswpunctE9u_ispunct
+_ZGVZ8iswpunctE9u_ispunct
+_ZZ8towlowerE9u_tolower
+_ZGVZ8towlowerE9u_tolower
+_ZZ8towupperE9u_toupper
+_ZGVZ8towupperE9u_toupper
+global_hashtable
+handlers
+p5s
+ut
+rs
+rsx
+mbrlen.mbs
+mbtowc.mbs
+wctomb.mbs
+ru_reseed
+ru_prf
+tmpnam.tmpcount
+lastenv
+strtok.last
+__stack_chk_guard
+lclptr
+gmtptr
+_ZGVZ14tzset_unlockedE20persist_sys_timezone
+_ZL13g_thread_list
+__atexit
+je_opt_stats_print_opts
+nuls
+precsize_ntoa.retbuf
+__p_secstodate.output
+_ZL13g_atfork_list
+inet_ntoa.b
+ether_ntoa.buf
+__sym_ntos.unname
+__sym_ntop.unname
+__p_type.typebuf
+__p_class.classbuf
+malloc_disabled_lock
+_ZL11g_arc4_lock
+_res_cache_list_lock
+__p_option.nbuf
+__p_time.nbuf
+atexit_mutex
+random_mutex
+__res_randomid.__libc_mutex_random
+locallock
+g_atexit_lock
+_ZL10gFunctions
+_ZL13vendor_passwd
+_ZL12vendor_group
+tm
+_ZL18g_thread_list_lock
+buf_asctime
+__dtoa_locks
+freelist
+__loc_ntoa.tmpbuf
+_ZL8g_locale
+je_arenas_lock
+je_background_thread_lock
+init_lock
+ctl_mtx
+tcaches_mtx
+je_tsd_init_head
+_ZZ14tzset_unlockedE20persist_sys_timezone
+arena_binind_div_info
+__hexdig_D2A
+lcl_TZname
+utmp
+inet_nsap_ntoa_tmpbuf
+_ZL17system_properties
+_ZL7key_map
+private_mem
+__libc_globals
+tmpnam.buf
+_res_cache_list
+_nres
+je_extent_mutex_pool
+je_arenas
+je_extents_rtree
diff --git a/libc/system_properties/Android.bp b/libc/system_properties/Android.bp
index f94cda9..911afb1 100644
--- a/libc/system_properties/Android.bp
+++ b/libc/system_properties/Android.bp
@@ -12,8 +12,8 @@
     whole_static_libs: [
         "libpropertyinfoparser",
     ],
-    static_libs: [
-        "libasync_safe",
+    header_libs: [
+        "libasync_safe_headers",
     ],
 
     include_dirs: [
diff --git a/libc/tools/genfunctosyscallnrs.py b/libc/tools/genfunctosyscallnrs.py
new file mode 100755
index 0000000..6a456f2
--- /dev/null
+++ b/libc/tools/genfunctosyscallnrs.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+
+import argparse
+import collections
+import logging
+import os
+import re
+import subprocess
+import textwrap
+
+from gensyscalls import SysCallsTxtParser
+from genseccomp import parse_syscall_NRs
+
+def load_syscall_names_from_file(file_path, architecture):
+  parser = SysCallsTxtParser()
+  parser.parse_open_file(open(file_path))
+  arch_map = {}
+  for syscall in parser.syscalls:
+    if syscall.get(architecture):
+      arch_map[syscall["func"]] = syscall["name"];
+
+  return arch_map
+
+def gen_syscall_nrs(out_file, base_syscall_file, syscall_NRs):
+  for arch in ('arm', 'arm64', 'mips', 'mips64', 'x86', 'x86_64'):
+    base_names = load_syscall_names_from_file(base_syscall_file, arch)
+
+    for func,syscall in base_names.iteritems():
+      out_file.write("#define __" + arch + "_" + func + " " + str(syscall_NRs[arch][syscall]) + ";\n")
+
+def main():
+  parser = argparse.ArgumentParser(
+      description="Generates a mapping of bionic functions to system call numbers per architecture.")
+  parser.add_argument("--verbose", "-v", help="Enables verbose logging.")
+  parser.add_argument("--out-dir",
+                      help="The output directory for the output files")
+  parser.add_argument("base_file", metavar="base-file", type=str,
+                      help="The path of the base syscall list (SYSCALLS.TXT).")
+  parser.add_argument("files", metavar="FILE", type=str, nargs="+",
+                      help=("A syscall name-number mapping file for an architecture.\n"))
+  args = parser.parse_args()
+
+  if args.verbose:
+    logging.basicConfig(level=logging.DEBUG)
+  else:
+    logging.basicConfig(level=logging.INFO)
+
+  syscall_files = []
+  syscall_NRs = {}
+  for filename in args.files:
+    m = re.search(r"libseccomp_gen_syscall_nrs_([^/]+)", filename)
+    syscall_NRs[m.group(1)] = parse_syscall_NRs(filename)
+
+  output_path = os.path.join(args.out_dir, "func_to_syscall_nrs.h")
+  with open(output_path, "w") as output_file:
+    gen_syscall_nrs(out_file=output_file,
+             syscall_NRs=syscall_NRs, base_syscall_file=args.base_file)
+
+if __name__ == "__main__":
+  main()
diff --git a/libc/upstream-netbsd/common/lib/libc/stdlib/random.c b/libc/upstream-netbsd/common/lib/libc/stdlib/random.c
index e7503c7..0e6eac0 100644
--- a/libc/upstream-netbsd/common/lib/libc/stdlib/random.c
+++ b/libc/upstream-netbsd/common/lib/libc/stdlib/random.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: random.c,v 1.4 2014/06/12 20:59:46 christos Exp $	*/
+/*	$NetBSD: random.c,v 1.5 2016/02/08 05:27:24 dholland Exp $	*/
 
 /*
  * Copyright (c) 1983, 1993
@@ -35,7 +35,7 @@
 #if 0
 static char sccsid[] = "@(#)random.c	8.2 (Berkeley) 5/19/95";
 #else
-__RCSID("$NetBSD: random.c,v 1.4 2014/06/12 20:59:46 christos Exp $");
+__RCSID("$NetBSD: random.c,v 1.5 2016/02/08 05:27:24 dholland Exp $");
 #endif
 #endif /* LIBC_SCCS and not lint */
 
@@ -92,7 +92,7 @@
  * state information, which will allow a degree seven polynomial.  (Note:
  * the zeroeth word of state information also has some other information
  * stored in it -- see setstate() for details).
- * 
+ *
  * The random number generation technique is a linear feedback shift register
  * approach, employing trinomials (since there are fewer terms to sum up that
  * way).  In this approach, the least significant bit of all the numbers in
@@ -318,12 +318,12 @@
  * the break values for the different R.N.G.'s, we choose the best (largest)
  * one we can and set things up for it.  srandom() is then called to
  * initialize the state information.
- * 
+ *
  * Note that on return from srandom(), we set state[-1] to be the type
  * multiplexed with the current value of the rear pointer; this is so
  * successive calls to initstate() won't lose this information and will be
  * able to restart with setstate().
- * 
+ *
  * Note: the first thing we do is save the current state, if any, just like
  * setstate() so that it doesn't matter when initstate is called.
  *
@@ -511,7 +511,7 @@
 {
 	static u_long randseed = 1;
 	long x, hi, lo, t;
- 
+
 	/*
 	 * Compute x[n + 1] = (7^5 * x[n]) mod (2^31 - 1).
 	 * From "Random number generators: good ones are hard to find",
diff --git a/libc/upstream-netbsd/lib/libc/include/isc/dst.h b/libc/upstream-netbsd/lib/libc/include/isc/dst.h
index 5537e3d..a25cf01 100644
--- a/libc/upstream-netbsd/lib/libc/include/isc/dst.h
+++ b/libc/upstream-netbsd/lib/libc/include/isc/dst.h
@@ -1,4 +1,4 @@
-/*	$NetBSD: dst.h,v 1.1.1.4 2009/04/12 16:35:44 christos Exp $	*/
+/*	$NetBSD: dst.h,v 1.2 2014/08/03 19:14:24 wiz Exp $	*/
 
 #ifndef DST_H
 #define DST_H
@@ -55,7 +55,7 @@
 #define	dst_write_key		__dst_write_key
 
 /* 
- * DST Crypto API defintions 
+ * DST Crypto API definitions 
  */
 void     dst_init(void);
 int      dst_check_algorithm(const int);
diff --git a/libc/upstream-netbsd/lib/libc/regex/regcomp.c b/libc/upstream-netbsd/lib/libc/regex/regcomp.c
index 6af9734..4a0d99a 100644
--- a/libc/upstream-netbsd/lib/libc/regex/regcomp.c
+++ b/libc/upstream-netbsd/lib/libc/regex/regcomp.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: regcomp.c,v 1.36 2015/09/12 19:08:47 christos Exp $	*/
+/*	$NetBSD: regcomp.c,v 1.38 2019/02/07 22:22:31 christos Exp $	*/
 
 /*-
  * Copyright (c) 1992, 1993, 1994
@@ -76,7 +76,7 @@
 #if 0
 static char sccsid[] = "@(#)regcomp.c	8.5 (Berkeley) 3/20/94";
 #else
-__RCSID("$NetBSD: regcomp.c,v 1.36 2015/09/12 19:08:47 christos Exp $");
+__RCSID("$NetBSD: regcomp.c,v 1.38 2019/02/07 22:22:31 christos Exp $");
 #endif
 #endif /* LIBC_SCCS and not lint */
 
@@ -474,6 +474,8 @@
 		REQUIRE(!MORE() || !isdigit((unsigned char)PEEK()), REG_BADRPT);
 		/* FALLTHROUGH */
 	default:
+		if (p->error != 0)
+			return;
 		ordinary(p, c);
 		break;
 	}
@@ -692,6 +694,8 @@
 		REQUIRE(starordinary, REG_BADRPT);
 		/* FALLTHROUGH */
 	default:
+		if (p->error != 0)
+			return(0);
 		ordinary(p, c &~ BACKSL);
 		break;
 	}
@@ -1007,7 +1011,7 @@
 	}
 	len = p->next - sp;
 	for (cp = cnames; cp->name != NULL; cp++)
-		if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0')
+		if (strncmp(cp->name, sp, len) == 0 && strlen(cp->name) == len)
 			return(cp->code);	/* known name */
 	if (len == 1)
 		return(*sp);	/* single character */
diff --git a/libc/upstream-netbsd/lib/libc/stdlib/lldiv.c b/libc/upstream-netbsd/lib/libc/stdlib/lldiv.c
deleted file mode 100644
index 47104b3..0000000
--- a/libc/upstream-netbsd/lib/libc/stdlib/lldiv.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/*	$NetBSD: lldiv.c,v 1.4 2012/06/25 22:32:45 abs Exp $	*/
-
-/*
- * Copyright (c) 1990, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Chris Torek.
- *
- * 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 <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-#if 0
-static char sccsid[] = "from: @(#)ldiv.c	8.1 (Berkeley) 6/4/93";
-#else
-__RCSID("$NetBSD: lldiv.c,v 1.4 2012/06/25 22:32:45 abs Exp $");
-#endif
-#endif /* LIBC_SCCS and not lint */
-
-#include "namespace.h"
-#include <stdlib.h>		/* lldiv_t */
-
-#ifdef __weak_alias
-__weak_alias(lldiv, _lldiv)
-#endif
-
-/* LONGLONG */
-lldiv_t
-lldiv(long long int num, long long int denom)
-{
-	lldiv_t r;
-
-	/* see div.c for comments */
-
-	r.quot = num / denom;
-	r.rem = num % denom;
-	if (num >= 0 && r.rem < 0) {
-		r.quot++;
-		r.rem -= denom;
-	}
-	return (r);
-}
diff --git a/libc/upstream-netbsd/lib/libc/stdlib/div.c b/libc/upstream-openbsd/lib/libc/stdlib/div.c
similarity index 86%
rename from libc/upstream-netbsd/lib/libc/stdlib/div.c
rename to libc/upstream-openbsd/lib/libc/stdlib/div.c
index f3bd32f..beaa428 100644
--- a/libc/upstream-netbsd/lib/libc/stdlib/div.c
+++ b/libc/upstream-openbsd/lib/libc/stdlib/div.c
@@ -1,8 +1,7 @@
-/*	$NetBSD: div.c,v 1.8 2012/06/25 22:32:45 abs Exp $	*/
-
+/*	$OpenBSD: div.c,v 1.6 2015/09/13 08:31:47 guenther Exp $ */
 /*
- * Copyright (c) 1990, 1993
- *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1990 Regents of the University of California.
+ * All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Chris Torek.
@@ -32,15 +31,6 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-#if 0
-static char sccsid[] = "@(#)div.c	8.1 (Berkeley) 6/4/93";
-#else
-__RCSID("$NetBSD: div.c,v 1.8 2012/06/25 22:32:45 abs Exp $");
-#endif
-#endif /* LIBC_SCCS and not lint */
-
 #include <stdlib.h>		/* div_t */
 
 div_t
@@ -79,3 +69,4 @@
 	}
 	return (r);
 }
+DEF_STRONG(div);
diff --git a/libc/upstream-netbsd/lib/libc/stdlib/ldiv.c b/libc/upstream-openbsd/lib/libc/stdlib/ldiv.c
similarity index 81%
rename from libc/upstream-netbsd/lib/libc/stdlib/ldiv.c
rename to libc/upstream-openbsd/lib/libc/stdlib/ldiv.c
index 507c831..775065f 100644
--- a/libc/upstream-netbsd/lib/libc/stdlib/ldiv.c
+++ b/libc/upstream-openbsd/lib/libc/stdlib/ldiv.c
@@ -1,8 +1,7 @@
-/*	$NetBSD: ldiv.c,v 1.8 2012/06/25 22:32:45 abs Exp $	*/
-
+/*	$OpenBSD: ldiv.c,v 1.5 2005/08/08 08:05:36 espie Exp $ */
 /*
- * Copyright (c) 1990, 1993
- *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1990 Regents of the University of California.
+ * All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Chris Torek.
@@ -32,15 +31,6 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-#if 0
-static char sccsid[] = "@(#)ldiv.c	8.1 (Berkeley) 6/4/93";
-#else
-__RCSID("$NetBSD: ldiv.c,v 1.8 2012/06/25 22:32:45 abs Exp $");
-#endif
-#endif /* LIBC_SCCS and not lint */
-
 #include <stdlib.h>		/* ldiv_t */
 
 ldiv_t
diff --git a/libc/upstream-netbsd/lib/libc/stdlib/ldiv.c b/libc/upstream-openbsd/lib/libc/stdlib/lldiv.c
similarity index 78%
copy from libc/upstream-netbsd/lib/libc/stdlib/ldiv.c
copy to libc/upstream-openbsd/lib/libc/stdlib/lldiv.c
index 507c831..59c37b8 100644
--- a/libc/upstream-netbsd/lib/libc/stdlib/ldiv.c
+++ b/libc/upstream-openbsd/lib/libc/stdlib/lldiv.c
@@ -1,8 +1,7 @@
-/*	$NetBSD: ldiv.c,v 1.8 2012/06/25 22:32:45 abs Exp $	*/
-
+/*	$OpenBSD: lldiv.c,v 1.2 2016/08/14 23:18:03 guenther Exp $	*/
 /*
- * Copyright (c) 1990, 1993
- *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1990 Regents of the University of California.
+ * All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Chris Torek.
@@ -32,21 +31,12 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-#if 0
-static char sccsid[] = "@(#)ldiv.c	8.1 (Berkeley) 6/4/93";
-#else
-__RCSID("$NetBSD: ldiv.c,v 1.8 2012/06/25 22:32:45 abs Exp $");
-#endif
-#endif /* LIBC_SCCS and not lint */
+#include <stdlib.h>		/* lldiv_t */
 
-#include <stdlib.h>		/* ldiv_t */
-
-ldiv_t
-ldiv(long num, long denom)
+lldiv_t
+lldiv(long long num, long long denom)
 {
-	ldiv_t r;
+	lldiv_t r;
 
 	/* see div.c for comments */
 
@@ -58,3 +48,5 @@
 	}
 	return (r);
 }
+
+__weak_alias(qdiv, lldiv);
diff --git a/libc/upstream-netbsd/lib/libc/string/memccpy.c b/libc/upstream-openbsd/lib/libc/string/memccpy.c
similarity index 83%
rename from libc/upstream-netbsd/lib/libc/string/memccpy.c
rename to libc/upstream-openbsd/lib/libc/string/memccpy.c
index c086241..635061b 100644
--- a/libc/upstream-netbsd/lib/libc/string/memccpy.c
+++ b/libc/upstream-openbsd/lib/libc/string/memccpy.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: memccpy.c,v 1.13 2012/06/25 22:32:46 abs Exp $	*/
+/*	$OpenBSD: memccpy.c,v 1.7 2015/08/31 02:53:57 guenther Exp $	*/
 
 /*-
  * Copyright (c) 1990, 1993
@@ -29,25 +29,12 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-#if 0
-static char sccsid[] = "@(#)memccpy.c	8.1 (Berkeley) 6/4/93";
-#else
-__RCSID("$NetBSD: memccpy.c,v 1.13 2012/06/25 22:32:46 abs Exp $");
-#endif
-#endif /* LIBC_SCCS and not lint */
-
-#include <assert.h>
 #include <string.h>
 
 void *
 memccpy(void *t, const void *f, int c, size_t n)
 {
 
-	_DIAGASSERT(t != 0);
-	_DIAGASSERT(f != 0);
-
 	if (n) {
 		unsigned char *tp = t;
 		const unsigned char *fp = f;
@@ -59,3 +46,4 @@
 	}
 	return (0);
 }
+DEF_WEAK(memccpy);
diff --git a/libc/upstream-netbsd/lib/libc/string/strcasestr.c b/libc/upstream-openbsd/lib/libc/string/strcasestr.c
similarity index 83%
rename from libc/upstream-netbsd/lib/libc/string/strcasestr.c
rename to libc/upstream-openbsd/lib/libc/string/strcasestr.c
index f8a9444..abb3e15 100644
--- a/libc/upstream-netbsd/lib/libc/string/strcasestr.c
+++ b/libc/upstream-openbsd/lib/libc/string/strcasestr.c
@@ -1,4 +1,5 @@
-/*	$NetBSD: strcasestr.c,v 1.3 2005/11/29 03:12:00 christos Exp $	*/
+/*	$OpenBSD: strcasestr.c,v 1.4 2015/08/31 02:53:57 guenther Exp $	*/
+/*	$NetBSD: strcasestr.c,v 1.2 2005/02/09 21:35:47 kleink Exp $	*/
 
 /*-
  * Copyright (c) 1990, 1993
@@ -32,13 +33,6 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-__RCSID("$NetBSD: strcasestr.c,v 1.3 2005/11/29 03:12:00 christos Exp $");
-#endif /* LIBC_SCCS and not lint */
-
-#include "namespace.h"
-#include <assert.h>
 #include <ctype.h>
 #include <string.h>
 
@@ -51,11 +45,8 @@
 	char c, sc;
 	size_t len;
 
-	_DIAGASSERT(s != NULL);
-	_DIAGASSERT(find != NULL);
-
 	if ((c = *find++) != 0) {
-		c = tolower((unsigned char)c);
+		c = (char)tolower((unsigned char)c);
 		len = strlen(find);
 		do {
 			do {
@@ -65,5 +56,6 @@
 		} while (strncasecmp(s, find, len) != 0);
 		s--;
 	}
-	return __UNCONST(s);
+	return ((char *)s);
 }
+DEF_WEAK(strcasestr);
diff --git a/libc/upstream-netbsd/lib/libc/string/strcoll.c b/libc/upstream-openbsd/lib/libc/string/strcoll.c
similarity index 78%
rename from libc/upstream-netbsd/lib/libc/string/strcoll.c
rename to libc/upstream-openbsd/lib/libc/string/strcoll.c
index 77a0942..47a6ea4 100644
--- a/libc/upstream-netbsd/lib/libc/string/strcoll.c
+++ b/libc/upstream-openbsd/lib/libc/string/strcoll.c
@@ -1,8 +1,7 @@
-/*	$NetBSD: strcoll.c,v 1.10 2012/06/25 22:32:46 abs Exp $	*/
-
+/*	$OpenBSD: strcoll.c,v 1.6 2015/08/31 02:53:57 guenther Exp $ */
 /*-
- * Copyright (c) 1990, 1993
- *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Chris Torek.
@@ -32,16 +31,6 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-#if 0
-static char sccsid[] = "@(#)strcoll.c	8.1 (Berkeley) 6/4/93";
-#else
-__RCSID("$NetBSD: strcoll.c,v 1.10 2012/06/25 22:32:46 abs Exp $");
-#endif
-#endif /* LIBC_SCCS and not lint */
-
-#include <assert.h>
 #include <string.h>
 
 /*
@@ -50,10 +39,7 @@
 int
 strcoll(const char *s1, const char *s2)
 {
-
-	_DIAGASSERT(s1 != NULL);
-	_DIAGASSERT(s2 != NULL);
-
 	/* LC_COLLATE is unimplemented, hence always "C" */
 	return (strcmp(s1, s2));
 }
+DEF_STRONG(strcoll);
diff --git a/libc/upstream-netbsd/lib/libc/string/strxfrm.c b/libc/upstream-openbsd/lib/libc/string/strxfrm.c
similarity index 73%
rename from libc/upstream-netbsd/lib/libc/string/strxfrm.c
rename to libc/upstream-openbsd/lib/libc/string/strxfrm.c
index 42c2a24..97df097 100644
--- a/libc/upstream-netbsd/lib/libc/string/strxfrm.c
+++ b/libc/upstream-openbsd/lib/libc/string/strxfrm.c
@@ -1,8 +1,7 @@
-/*	$NetBSD: strxfrm.c,v 1.12 2012/06/25 22:32:46 abs Exp $	*/
-
+/*	$OpenBSD: strxfrm.c,v 1.7 2015/08/31 02:53:57 guenther Exp $ */
 /*-
- * Copyright (c) 1990, 1993
- *	The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Chris Torek.
@@ -32,16 +31,6 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-#if 0
-static char sccsid[] = "@(#)strxfrm.c	8.1 (Berkeley) 6/4/93";
-#else
-__RCSID("$NetBSD: strxfrm.c,v 1.12 2012/06/25 22:32:46 abs Exp $");
-#endif
-#endif /* LIBC_SCCS and not lint */
-
-#include <assert.h>
 #include <string.h>
 
 /*
@@ -52,19 +41,12 @@
 size_t
 strxfrm(char *dst, const char *src, size_t n)
 {
-	size_t srclen, copysize;
-
-	_DIAGASSERT(src != NULL);
 
 	/*
 	 * Since locales are unimplemented, this is just a copy.
 	 */
-	srclen = strlen(src);
-	if (n != 0) {
-		_DIAGASSERT(dst != NULL);
-		copysize = srclen < n ? srclen : n - 1;
-		(void)memcpy(dst, src, copysize);
-		dst[copysize] = 0;
-	}
-	return (srclen);
+	if (n == 0)
+		return (strlen(src));
+	return (strlcpy(dst, src, n));
 }
+DEF_STRONG(strxfrm);
diff --git a/libdl/Android.bp b/libdl/Android.bp
index 262da6c..642cc7a 100644
--- a/libdl/Android.bp
+++ b/libdl/Android.bp
@@ -93,6 +93,9 @@
     nocrt: true,
     system_shared_libs: [],
 
+    // Opt out of native_coverage when opting out of system_shared_libs
+    native_coverage: false,
+
     // This is placeholder library the actual implementation is (currently)
     // provided by the linker.
     shared_libs: ["ld-android"],
@@ -105,6 +108,7 @@
         symbol_file: "libdl.map.txt",
         versions: ["10000"],
     },
+    required: ["libdl.mountpoint"],
 }
 
 ndk_library {
diff --git a/libm/Android.bp b/libm/Android.bp
index 28cf1fd..ec319e2 100644
--- a/libm/Android.bp
+++ b/libm/Android.bp
@@ -509,12 +509,14 @@
     stl: "none",
 
     // TODO(ivanlozano): Remove after b/118321713
+    no_libcrt: true,
     xom: false,
 
     stubs: {
         symbol_file: "libm.map.txt",
         versions: ["10000"],
     },
+    required: ["libm.mountpoint"],
 }
 
 ndk_library {
diff --git a/libm/upstream-freebsd/lib/msun/ld128/e_powl.c b/libm/upstream-freebsd/lib/msun/ld128/e_powl.c
index 15a57dd..2f3ee55 100644
--- a/libm/upstream-freebsd/lib/msun/ld128/e_powl.c
+++ b/libm/upstream-freebsd/lib/msun/ld128/e_powl.c
@@ -32,7 +32,7 @@
  *	1. Compute and return log2(x) in two pieces:
  *		log2(x) = w1 + w2,
  *	   where w1 has 113-53 = 60 bit trailing zeros.
- *	2. Perform y*log2(x) = n+y' by simulating muti-precision
+ *	2. Perform y*log2(x) = n+y' by simulating multi-precision
  *	   arithmetic, where |y'|<=0.5.
  *	3. Return x**y = 2**n*exp(y'*log2)
  *
@@ -60,7 +60,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/lib/msun/ld128/e_powl.c 336362 2018-07-17 07:42:14Z bde $");
+__FBSDID("$FreeBSD: head/lib/msun/ld128/e_powl.c 342651 2018-12-31 15:43:06Z pfg $");
 
 #include <float.h>
 #include <math.h>
diff --git a/libm/upstream-freebsd/lib/msun/ld128/e_rem_pio2l.h b/libm/upstream-freebsd/lib/msun/ld128/e_rem_pio2l.h
index 5d78c4d..1ed79ae 100644
--- a/libm/upstream-freebsd/lib/msun/ld128/e_rem_pio2l.h
+++ b/libm/upstream-freebsd/lib/msun/ld128/e_rem_pio2l.h
@@ -14,7 +14,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/lib/msun/ld128/e_rem_pio2l.h 336545 2018-07-20 12:42:24Z bde $");
 
 /* ld128 version of __ieee754_rem_pio2l(x,y)
  * 
@@ -74,14 +74,9 @@
 	if (ex < BIAS + 45 || ex == BIAS + 45 &&
 	    u.bits.manh < 0x921fb54442d1LL) {
 	    /* |x| ~< 2^45*(pi/2), medium size */
-	    /* Use a specialized rint() to get fn.  Assume round-to-nearest. */
-	    fn = x*invpio2+0x1.8p112;
-	    fn = fn-0x1.8p112;
-#ifdef HAVE_EFFICIENT_I64RINT
+	    /* TODO: use only double precision for fn, as in expl(). */
+	    fn = rnintl(x * invpio2);
 	    n  = i64rint(fn);
-#else
-	    n  = fn;
-#endif
 	    r  = x-fn*pio2_1;
 	    w  = fn*pio2_1t;	/* 1st round good to 180 bit */
 	    {
diff --git a/libm/upstream-freebsd/lib/msun/ld128/k_expl.h b/libm/upstream-freebsd/lib/msun/ld128/k_expl.h
index 4c041e8..b80d00e 100644
--- a/libm/upstream-freebsd/lib/msun/ld128/k_expl.h
+++ b/libm/upstream-freebsd/lib/msun/ld128/k_expl.h
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/lib/msun/ld128/k_expl.h 326219 2017-11-26 02:00:33Z pfg $");
+__FBSDID("$FreeBSD: head/lib/msun/ld128/k_expl.h 336545 2018-07-20 12:42:24Z bde $");
 
 /*
  * ld128 version of k_expl.h.  See ../ld80/s_expl.c for most comments.
@@ -244,16 +244,8 @@
 	int n, n2;
 
 	/* Reduce x to (k*ln2 + endpoint[n2] + r1 + r2). */
-	/* Use a specialized rint() to get fn.  Assume round-to-nearest. */
-	/* XXX assume no extra precision for the additions, as for trig fns. */
-	/* XXX this set of comments is now quadruplicated. */
-	/* XXX but see ../src/e_exp.c for a fix using double_t. */
-	fn = (double)x * INV_L + 0x1.8p52 - 0x1.8p52;
-#if defined(HAVE_EFFICIENT_IRINT)
+	fn = rnint((double)x * INV_L);
 	n = irint(fn);
-#else
-	n = (int)fn;
-#endif
 	n2 = (unsigned)n % INTERVALS;
 	/* Depend on the sign bit being propagated: */
 	*kp = n >> LOG2_INTERVALS;
diff --git a/libm/upstream-freebsd/lib/msun/ld128/s_expl.c b/libm/upstream-freebsd/lib/msun/ld128/s_expl.c
index 53bc04a..f4c18be 100644
--- a/libm/upstream-freebsd/lib/msun/ld128/s_expl.c
+++ b/libm/upstream-freebsd/lib/msun/ld128/s_expl.c
@@ -29,7 +29,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/lib/msun/ld128/s_expl.c 326219 2017-11-26 02:00:33Z pfg $");
+__FBSDID("$FreeBSD: head/lib/msun/ld128/s_expl.c 336545 2018-07-20 12:42:24Z bde $");
 
 /*
  * ld128 version of s_expl.c.  See ../ld80/s_expl.c for most comments.
@@ -268,13 +268,8 @@
 	}
 
 	/* Reduce x to (k*ln2 + endpoint[n2] + r1 + r2). */
-	/* Use a specialized rint() to get fn.  Assume round-to-nearest. */
-	fn = (double)x * INV_L + 0x1.8p52 - 0x1.8p52;
-#if defined(HAVE_EFFICIENT_IRINT)
+	fn = rnint((double)x * INV_L);
 	n = irint(fn);
-#else
-	n = (int)fn;
-#endif
 	n2 = (unsigned)n % INTERVALS;
 	k = n >> LOG2_INTERVALS;
 	r1 = x - fn * L1;
diff --git a/libm/upstream-freebsd/lib/msun/src/e_j0.c b/libm/upstream-freebsd/lib/msun/src/e_j0.c
index cfa71c2..6bca542 100644
--- a/libm/upstream-freebsd/lib/msun/src/e_j0.c
+++ b/libm/upstream-freebsd/lib/msun/src/e_j0.c
@@ -11,7 +11,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/lib/msun/src/e_j0.c 336089 2018-07-08 16:26:13Z markj $");
+__FBSDID("$FreeBSD: head/lib/msun/src/e_j0.c 343023 2019-01-14 15:48:35Z pfg $");
 
 /* __ieee754_j0(x), __ieee754_y0(x)
  * Bessel function of the first and second kinds of order zero.
@@ -80,7 +80,7 @@
 S03  =  5.13546550207318111446e-07, /* 0x3EA13B54, 0xCE84D5A9 */
 S04  =  1.16614003333790000205e-09; /* 0x3E1408BC, 0xF4745D8F */
 
-static const double zero = 0.0;
+static const double zero = 0, qrtr = 0.25;
 
 double
 __ieee754_j0(double x)
@@ -97,7 +97,7 @@
 		c = cos(x);
 		ss = s-c;
 		cc = s+c;
-		if(ix<0x7fe00000) {  /* make sure x+x not overflow */
+		if(ix<0x7fe00000) {  /* Make sure x+x does not overflow. */
 		    z = -cos(x+x);
 		    if ((s*c)<zero) cc = z/ss;
 		    else 	    ss = z/cc;
@@ -123,9 +123,9 @@
 	r =  z*(R02+z*(R03+z*(R04+z*R05)));
 	s =  one+z*(S01+z*(S02+z*(S03+z*S04)));
 	if(ix < 0x3FF00000) {	/* |x| < 1.00 */
-	    return one + z*(-0.25+(r/s));
+	    return one + z*((r/s)-qrtr);
 	} else {
-	    u = 0.5*x;
+	    u = x/2;
 	    return((one+u)*(one-u)+z*(r/s));
 	}
 }
@@ -374,6 +374,7 @@
 static __inline double
 qzero(double x)
 {
+	static const double eighth = 0.125;
 	const double *p,*q;
 	double s,r,z;
 	int32_t ix;
@@ -386,5 +387,5 @@
 	z = one/(x*x);
 	r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))));
 	s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5])))));
-	return (-.125 + r/s)/x;
+	return (r/s-eighth)/x;
 }
diff --git a/libm/upstream-freebsd/lib/msun/src/e_j0f.c b/libm/upstream-freebsd/lib/msun/src/e_j0f.c
index e53b218..714caac 100644
--- a/libm/upstream-freebsd/lib/msun/src/e_j0f.c
+++ b/libm/upstream-freebsd/lib/msun/src/e_j0f.c
@@ -14,7 +14,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/lib/msun/src/e_j0f.c 283032 2015-05-17 16:27:06Z kargl $");
+__FBSDID("$FreeBSD: head/lib/msun/src/e_j0f.c 343023 2019-01-14 15:48:35Z pfg $");
 
 /*
  * See e_j0.c for complete comments.
@@ -42,7 +42,7 @@
 S03  =  5.1354652442e-07, /* 0x3509daa6 */
 S04  =  1.1661400734e-09; /* 0x30a045e8 */
 
-static const float zero = 0.0;
+static const float zero = 0, qrtr = 0.25;
 
 float
 __ieee754_j0f(float x)
@@ -59,7 +59,7 @@
 		c = cosf(x);
 		ss = s-c;
 		cc = s+c;
-		if(ix<0x7f000000) {  /* make sure x+x not overflow */
+		if(ix<0x7f000000) {  /* Make sure x+x does not overflow. */
 		    z = -cosf(x+x);
 		    if ((s*c)<zero) cc = z/ss;
 		    else 	    ss = z/cc;
@@ -85,9 +85,9 @@
 	r =  z*(R02+z*(R03+z*(R04+z*R05)));
 	s =  one+z*(S01+z*(S02+z*(S03+z*S04)));
 	if(ix < 0x3F800000) {	/* |x| < 1.00 */
-	    return one + z*((float)-0.25+(r/s));
+	    return one + z*((r/s)-qrtr);
 	} else {
-	    u = (float)0.5*x;
+	    u = x/2;
 	    return((one+u)*(one-u)+z*(r/s));
 	}
 }
@@ -328,6 +328,7 @@
 static __inline float
 qzerof(float x)
 {
+	static const float eighth = 0.125;
 	const float *p,*q;
 	float s,r,z;
 	int32_t ix;
@@ -340,5 +341,5 @@
 	z = one/(x*x);
 	r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))));
 	s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5])))));
-	return (-(float).125 + r/s)/x;
+	return (r/s-eighth)/x;
 }
diff --git a/libm/upstream-freebsd/lib/msun/src/e_pow.c b/libm/upstream-freebsd/lib/msun/src/e_pow.c
index 411dd52..b4f6a5a 100644
--- a/libm/upstream-freebsd/lib/msun/src/e_pow.c
+++ b/libm/upstream-freebsd/lib/msun/src/e_pow.c
@@ -10,7 +10,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/lib/msun/src/e_pow.c 336362 2018-07-17 07:42:14Z bde $");
+__FBSDID("$FreeBSD: head/lib/msun/src/e_pow.c 342851 2019-01-07 17:35:09Z pfg $");
 
 /* __ieee754_pow(x,y) return x**y
  *
@@ -133,7 +133,7 @@
 		k = (iy>>20)-0x3ff;	   /* exponent */
 		if(k>20) {
 		    j = ly>>(52-k);
-		    if((j<<(52-k))==ly) yisint = 2-(j&1);
+		    if(((u_int32_t)j<<(52-k))==ly) yisint = 2-(j&1);
 		} else if(ly==0) {
 		    j = iy>>(20-k);
 		    if((j<<(20-k))==iy) yisint = 2-(j&1);
diff --git a/libm/upstream-freebsd/lib/msun/src/e_rem_pio2.c b/libm/upstream-freebsd/lib/msun/src/e_rem_pio2.c
index be2630b..3e62c2b 100644
--- a/libm/upstream-freebsd/lib/msun/src/e_rem_pio2.c
+++ b/libm/upstream-freebsd/lib/msun/src/e_rem_pio2.c
@@ -14,7 +14,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/lib/msun/src/e_rem_pio2.c 336545 2018-07-20 12:42:24Z bde $");
 
 /* __ieee754_rem_pio2(x,y)
  * 
@@ -127,14 +127,8 @@
 	}
 	if(ix<0x413921fb) {	/* |x| ~< 2^20*(pi/2), medium size */
 medium:
-	    /* Use a specialized rint() to get fn.  Assume round-to-nearest. */
-	    STRICT_ASSIGN(double,fn,x*invpio2+0x1.8p52);
-	    fn = fn-0x1.8p52;
-#ifdef HAVE_EFFICIENT_IRINT
+	    fn = rnint((double_t)x*invpio2);
 	    n  = irint(fn);
-#else
-	    n  = (int32_t)fn;
-#endif
 	    r  = x-fn*pio2_1;
 	    w  = fn*pio2_1t;	/* 1st round good to 85 bit */
 	    {
diff --git a/libm/upstream-freebsd/lib/msun/src/e_rem_pio2f.c b/libm/upstream-freebsd/lib/msun/src/e_rem_pio2f.c
index f1ee7a0..2a89d27 100644
--- a/libm/upstream-freebsd/lib/msun/src/e_rem_pio2f.c
+++ b/libm/upstream-freebsd/lib/msun/src/e_rem_pio2f.c
@@ -15,7 +15,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/lib/msun/src/e_rem_pio2f.c 336545 2018-07-20 12:42:24Z bde $");
 
 /* __ieee754_rem_pio2f(x,y)
  *
@@ -55,14 +55,8 @@
 	ix = hx&0x7fffffff;
     /* 33+53 bit pi is good enough for medium size */
 	if(ix<0x4dc90fdb) {		/* |x| ~< 2^28*(pi/2), medium size */
-	    /* Use a specialized rint() to get fn.  Assume round-to-nearest. */
-	    STRICT_ASSIGN(double,fn,x*invpio2+0x1.8p52);
-	    fn = fn-0x1.8p52;
-#ifdef HAVE_EFFICIENT_IRINT
+	    fn = rnint((float_t)x*invpio2);
 	    n  = irint(fn);
-#else
-	    n  = (int32_t)fn;
-#endif
 	    r  = x-fn*pio2_1;
 	    w  = fn*pio2_1t;
 	    *y = r-w;
diff --git a/libm/upstream-freebsd/lib/msun/src/k_rem_pio2.c b/libm/upstream-freebsd/lib/msun/src/k_rem_pio2.c
index 81363d4..0869e03 100644
--- a/libm/upstream-freebsd/lib/msun/src/k_rem_pio2.c
+++ b/libm/upstream-freebsd/lib/msun/src/k_rem_pio2.c
@@ -12,7 +12,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/lib/msun/src/k_rem_pio2.c 298896 2016-05-01 19:37:33Z pfg $");
+__FBSDID("$FreeBSD: head/lib/msun/src/k_rem_pio2.c 342651 2018-12-31 15:43:06Z pfg $");
 
 /*
  * __kernel_rem_pio2(x,y,e0,nx,prec)
@@ -52,7 +52,7 @@
  *			64-bit  precision	2
  *			113-bit precision	3
  *		The actual value is the sum of them. Thus for 113-bit
- *		precison, one may have to do something like:
+ *		precision, one may have to do something like:
  *
  *		long double t,w,r_head, r_tail;
  *		t = (long double)y[2] + (long double)y[1];
diff --git a/libm/upstream-freebsd/lib/msun/src/s_cbrt.c b/libm/upstream-freebsd/lib/msun/src/s_cbrt.c
index d75ad0b..268425e 100644
--- a/libm/upstream-freebsd/lib/msun/src/s_cbrt.c
+++ b/libm/upstream-freebsd/lib/msun/src/s_cbrt.c
@@ -13,8 +13,9 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/lib/msun/src/s_cbrt.c 298896 2016-05-01 19:37:33Z pfg $");
+__FBSDID("$FreeBSD: head/lib/msun/src/s_cbrt.c 342563 2018-12-28 01:34:08Z jhibbits $");
 
+#include <float.h>
 #include "math.h"
 #include "math_private.h"
 
diff --git a/libm/upstream-freebsd/lib/msun/src/s_cpow.c b/libm/upstream-freebsd/lib/msun/src/s_cpow.c
index 9be5c51..1cb99aa 100644
--- a/libm/upstream-freebsd/lib/msun/src/s_cpow.c
+++ b/libm/upstream-freebsd/lib/msun/src/s_cpow.c
@@ -44,11 +44,12 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/lib/msun/src/s_cpow.c 336299 2018-07-15 00:23:10Z mmacy $");
+__FBSDID("$FreeBSD: head/lib/msun/src/s_cpow.c 336563 2018-07-20 18:27:30Z dim $");
 
 #include <complex.h>
 #include <float.h>
 #include <math.h>
+#include "math_private.h"
 
 double complex
 cpow(double complex a, double complex z)
@@ -60,7 +61,7 @@
 	y = cimag (z);
 	absa = cabs (a);
 	if (absa == 0.0) {
-		return (0.0 + 0.0 * I);
+		return (CMPLX(0.0, 0.0));
 	}
 	arga = carg (a);
 	r = pow (absa, x);
@@ -69,6 +70,6 @@
 		r = r * exp (-y * arga);
 		theta = theta + y * log (absa);
 	}
-	w = r * cos (theta) + (r * sin (theta)) * I;
+	w = CMPLX(r * cos (theta),  r * sin (theta));
 	return (w);
 }
diff --git a/libm/upstream-freebsd/lib/msun/src/s_cpowf.c b/libm/upstream-freebsd/lib/msun/src/s_cpowf.c
index 6e27f57..a2ef525 100644
--- a/libm/upstream-freebsd/lib/msun/src/s_cpowf.c
+++ b/libm/upstream-freebsd/lib/msun/src/s_cpowf.c
@@ -44,10 +44,11 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/lib/msun/src/s_cpowf.c 336299 2018-07-15 00:23:10Z mmacy $");
+__FBSDID("$FreeBSD: head/lib/msun/src/s_cpowf.c 336563 2018-07-20 18:27:30Z dim $");
 
 #include <complex.h>
 #include <math.h>
+#include "math_private.h"
 
 float complex
 cpowf(float complex a, float complex z)
@@ -59,7 +60,7 @@
 	y = cimagf(z);
 	absa = cabsf (a);
 	if (absa == 0.0f) {
-		return (0.0f + 0.0f * I);
+		return (CMPLXF(0.0f, 0.0f));
 	}
 	arga = cargf (a);
 	r = powf (absa, x);
@@ -68,6 +69,6 @@
 		r = r * expf (-y * arga);
 		theta = theta + y * logf (absa);
 	}
-	w = r * cosf (theta) + (r * sinf (theta)) * I;
+	w = CMPLXF(r * cosf (theta), r * sinf (theta));
 	return (w);
 }
diff --git a/libm/upstream-freebsd/lib/msun/src/s_cpowl.c b/libm/upstream-freebsd/lib/msun/src/s_cpowl.c
index fcb5c7f..f9c6373 100644
--- a/libm/upstream-freebsd/lib/msun/src/s_cpowl.c
+++ b/libm/upstream-freebsd/lib/msun/src/s_cpowl.c
@@ -44,10 +44,11 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/lib/msun/src/s_cpowl.c 336299 2018-07-15 00:23:10Z mmacy $");
+__FBSDID("$FreeBSD: head/lib/msun/src/s_cpowl.c 336563 2018-07-20 18:27:30Z dim $");
 
 #include <complex.h>
 #include <math.h>
+#include "math_private.h"
 
 long double complex
 cpowl(long double complex a, long double complex z)
@@ -59,7 +60,7 @@
 	y = cimagl(z);
 	absa = cabsl(a);
 	if (absa == 0.0L) {
-		return (0.0L + 0.0L * I);
+		return (CMPLXL(0.0L, 0.0L));
 	}
 	arga = cargl(a);
 	r = powl(absa, x);
@@ -68,6 +69,6 @@
 		r = r * expl(-y * arga);
 		theta = theta + y * logl(absa);
 	}
-	w = r * cosl(theta) + (r * sinl(theta)) * I;
+	w = CMPLXL(r * cosl(theta), r * sinl(theta));
 	return (w);
 }
diff --git a/libm/upstream-freebsd/lib/msun/src/s_cproj.c b/libm/upstream-freebsd/lib/msun/src/s_cproj.c
index 3083f2b..7677b5f 100644
--- a/libm/upstream-freebsd/lib/msun/src/s_cproj.c
+++ b/libm/upstream-freebsd/lib/msun/src/s_cproj.c
@@ -27,9 +27,10 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/lib/msun/src/s_cproj.c 326219 2017-11-26 02:00:33Z pfg $");
+__FBSDID("$FreeBSD: head/lib/msun/src/s_cproj.c 342563 2018-12-28 01:34:08Z jhibbits $");
 
 #include <complex.h>
+#include <float.h>
 #include <math.h>
 
 #include "math_private.h"
diff --git a/libm/upstream-freebsd/lib/msun/src/s_erf.c b/libm/upstream-freebsd/lib/msun/src/s_erf.c
index e1d63bc..3e39d80 100644
--- a/libm/upstream-freebsd/lib/msun/src/s_erf.c
+++ b/libm/upstream-freebsd/lib/msun/src/s_erf.c
@@ -11,7 +11,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/lib/msun/src/s_erf.c 342563 2018-12-28 01:34:08Z jhibbits $");
 
 /* double erf(double x)
  * double erfc(double x)
@@ -107,7 +107,7 @@
  *	   	erfc/erf(NaN) is NaN
  */
 
-
+#include <float.h>
 #include "math.h"
 #include "math_private.h"
 
diff --git a/linker/Android.bp b/linker/Android.bp
index 38a53f8..613be3d 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -4,7 +4,6 @@
     recovery_available: true,
 
     srcs: [
-        "linker_allocator.cpp",
         "linker_memory.cpp",
     ],
     cflags: [
@@ -104,6 +103,7 @@
     name: "linker_sources_arm64",
     srcs: [
         "arch/arm64/begin.S",
+        "arch/arm64/tlsdesc_resolver.S",
     ],
 }
 
@@ -176,6 +176,11 @@
         "-Wextra",
         "-Wunused",
         "-Werror",
+
+        // Define _USING_LIBCXX so <stdatomic.h> defers to the <atomic> header. When a Soong module
+        // uses the platform libc++, Soong automatically passes this macro, but the dynamic linker
+        // links against libc++ manually.
+        "-D_USING_LIBCXX",
     ],
 
     // TODO: split out the asflags.
@@ -278,13 +283,23 @@
 
     name: "linker",
     symlinks: ["linker_asan"],
+    // The linker in the system partition is now only for bootstrapping
+    relative_install_path: "bootstrap",
     recovery_available: true,
     multilib: {
+        lib32: {
+            cflags: ["-DLIB_PATH=\"lib\""],
+        },
         lib64: {
+            cflags: ["-DLIB_PATH=\"lib64\""],
             suffix: "64",
         },
     },
     system_shared_libs: [],
+
+    // Opt out of native_coverage when opting out of system_shared_libs
+    native_coverage: false,
+
     target: {
         android: {
             static_libs: ["libdebuggerd_handler_fallback"],
@@ -292,6 +307,7 @@
     },
     compile_multilib: "both",
     xom: false,
+    required: ["linker.mountpoint"],
 }
 
 cc_library {
@@ -352,7 +368,46 @@
     nocrt: true,
     system_shared_libs: [],
 
+    // Opt out of native_coverage when opting out of system_shared_libs
+    native_coverage: false,
+
     sanitize: {
         never: true,
     },
 }
+
+cc_test {
+    name: "linker-unit-tests",
+
+    cflags: [
+        "-g",
+        "-Wall",
+        "-Wextra",
+        "-Wunused",
+        "-Werror",
+    ],
+
+    // We need to access Bionic private headers in the linker.
+    include_dirs: ["bionic/libc"],
+
+    srcs: [
+        // Tests.
+        "linker_block_allocator_test.cpp",
+        "linker_config_test.cpp",
+        "linked_list_test.cpp",
+        "linker_sleb128_test.cpp",
+        "linker_utils_test.cpp",
+
+        // Parts of the linker that we're testing.
+        "linker_block_allocator.cpp",
+        "linker_config.cpp",
+        "linker_test_globals.cpp",
+        "linker_utils.cpp",
+    ],
+
+    static_libs: [
+        "libasync_safe",
+        "libbase",
+        "liblog",
+    ],
+}
diff --git a/linker/Android.mk b/linker/Android.mk
deleted file mode 100644
index ea7451c..0000000
--- a/linker/Android.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/linker/arch/arm64/tlsdesc_resolver.S b/linker/arch/arm64/tlsdesc_resolver.S
new file mode 100644
index 0000000..ef46839
--- /dev/null
+++ b/linker/arch/arm64/tlsdesc_resolver.S
@@ -0,0 +1,203 @@
+/*
+ * 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>
+#include <private/bionic_asm_tls.h>
+
+.globl __tls_get_addr
+
+// These resolver functions must preserve every register except x0. They set x0
+// to the offset of the TLS symbol relative to the thread pointer.
+
+ENTRY_PRIVATE(tlsdesc_resolver_static)
+  ldr x0, [x0, #8]
+  ret
+END(tlsdesc_resolver_static)
+
+ENTRY_PRIVATE(tlsdesc_resolver_dynamic)
+  stp x19, x20, [sp, #-32]!
+  .cfi_def_cfa_offset 32
+  .cfi_rel_offset x19, 0
+  .cfi_rel_offset x20, 8
+  stp x21, x22, [sp, #16]
+  .cfi_rel_offset x21, 16
+  .cfi_rel_offset x22, 24
+
+  mrs x19, tpidr_el0            // __get_tls()
+  ldr x20, [x19, #(TLS_SLOT_DTV * 8)]
+  ldr x21, [x20]                // TlsDtv::generation
+
+  ldr x0, [x0, #8]              // TlsDynamicResolverArg*
+  ldr x22, [x0]                 // TlsDynamicResolverArg::generation
+
+  cmp x21, x22
+  b.lo .fallback
+
+  ldr x21, [x0, #8]             // TlsIndex::module
+  ldr x22, [x0, #16]            // TlsIndex::offset
+  ldr x21, [x20, x21, lsl #3]   // TlsDtv::modules[module]
+  cbz x21, .fallback
+  add x0, x21, x22
+  sub x0, x0, x19
+
+  ldp x21, x22, [sp, #16]
+  .cfi_remember_state
+  .cfi_restore x21
+  .cfi_restore x22
+  ldp x19, x20, [sp], #32
+  .cfi_adjust_cfa_offset -32
+  .cfi_restore x19
+  .cfi_restore x20
+  ret
+
+.fallback:
+  .cfi_restore_state
+  ldp x21, x22, [sp, #16]
+  .cfi_restore x21
+  .cfi_restore x22
+  ldp x19, x20, [sp], #32
+  .cfi_adjust_cfa_offset -32
+  .cfi_restore x19
+  .cfi_restore x20
+  b tlsdesc_resolver_dynamic_slow_path
+END(tlsdesc_resolver_dynamic)
+
+#define SAVE_REG(x, slot)                 \
+    str x, [sp, #((slot) * 8)];           \
+    .cfi_rel_offset x, (slot) * 8;        \
+
+#define SAVE_GPR_PAIR(x, y, slot)         \
+    stp x, y, [sp, #((slot) * 8)];        \
+    .cfi_rel_offset x, (slot) * 8;        \
+    .cfi_rel_offset y, ((slot) + 1) * 8;  \
+
+#define SAVE_VEC_PAIR(x, y, slot)         \
+    stp x, y, [sp, #((slot) * 8)];        \
+    .cfi_rel_offset x, (slot) * 8;        \
+    .cfi_rel_offset y, ((slot) + 2) * 8;  \
+
+#define RESTORE_REG(x, slot)              \
+    ldr x, [sp, #((slot) * 8)];           \
+    .cfi_restore x;                       \
+
+#define RESTORE_REG_PAIR(x, y, slot)      \
+    ldp x, y, [sp, #((slot) * 8)];        \
+    .cfi_restore x;                       \
+    .cfi_restore y;                       \
+
+// On entry, x0 is the address of a TlsDynamicResolverArg object rather than
+// the TlsDescriptor address passed to the original resolver function.
+ENTRY_PRIVATE(tlsdesc_resolver_dynamic_slow_path)
+  sub sp, sp, #(8 * 84)
+  .cfi_def_cfa_offset (8 * 84)
+  SAVE_GPR_PAIR(x29, x30, 0)
+  mov x29, sp
+
+  // Avoid leaking the contents of the shadow call stack register (x18) into
+  // memory. x19 through x29 are callee-save registers, so we do not need to
+  // save them.
+  SAVE_GPR_PAIR(x1,  x2,  2)
+  SAVE_GPR_PAIR(x3,  x4,  4)
+  SAVE_GPR_PAIR(x5,  x6,  6)
+  SAVE_GPR_PAIR(x7,  x8,  8)
+  SAVE_GPR_PAIR(x9,  x10, 10)
+  SAVE_GPR_PAIR(x11, x12, 12)
+  SAVE_GPR_PAIR(x13, x14, 14)
+  SAVE_GPR_PAIR(x15, x16, 16)
+  SAVE_REG(x17, 18)
+
+  SAVE_VEC_PAIR(q0,  q1,  20)
+  SAVE_VEC_PAIR(q2,  q3,  24)
+  SAVE_VEC_PAIR(q4,  q5,  28)
+  SAVE_VEC_PAIR(q6,  q7,  32)
+  SAVE_VEC_PAIR(q8,  q9,  36)
+  SAVE_VEC_PAIR(q10, q11, 40)
+  SAVE_VEC_PAIR(q12, q13, 44)
+  SAVE_VEC_PAIR(q14, q15, 48)
+  SAVE_VEC_PAIR(q16, q17, 52)
+  SAVE_VEC_PAIR(q18, q19, 56)
+  SAVE_VEC_PAIR(q20, q21, 60)
+  SAVE_VEC_PAIR(q22, q23, 64)
+  SAVE_VEC_PAIR(q24, q25, 68)
+  SAVE_VEC_PAIR(q26, q27, 72)
+  SAVE_VEC_PAIR(q28, q29, 76)
+  SAVE_VEC_PAIR(q30, q31, 80)
+
+  add x0, x0, #8
+  bl __tls_get_addr
+  mrs x1, tpidr_el0 // __get_tls()
+  sub x0, x0, x1
+
+  RESTORE_REG_PAIR(q30, q31, 80)
+  RESTORE_REG_PAIR(q28, q29, 76)
+  RESTORE_REG_PAIR(q26, q27, 72)
+  RESTORE_REG_PAIR(q24, q25, 68)
+  RESTORE_REG_PAIR(q22, q23, 64)
+  RESTORE_REG_PAIR(q20, q21, 60)
+  RESTORE_REG_PAIR(q18, q19, 56)
+  RESTORE_REG_PAIR(q16, q17, 52)
+  RESTORE_REG_PAIR(q14, q15, 48)
+  RESTORE_REG_PAIR(q12, q13, 44)
+  RESTORE_REG_PAIR(q10, q11, 40)
+  RESTORE_REG_PAIR(q8,  q9,  36)
+  RESTORE_REG_PAIR(q6,  q7,  32)
+  RESTORE_REG_PAIR(q4,  q5,  28)
+  RESTORE_REG_PAIR(q2,  q3,  24)
+  RESTORE_REG_PAIR(q0,  q1,  20)
+
+  RESTORE_REG(x17, 18)
+  RESTORE_REG_PAIR(x15, x16, 16)
+  RESTORE_REG_PAIR(x13, x14, 14)
+  RESTORE_REG_PAIR(x11, x12, 12)
+  RESTORE_REG_PAIR(x9,  x10, 10)
+  RESTORE_REG_PAIR(x7,  x8,  8)
+  RESTORE_REG_PAIR(x5,  x6,  6)
+  RESTORE_REG_PAIR(x3,  x4,  4)
+  RESTORE_REG_PAIR(x1,  x2,  2)
+
+  RESTORE_REG_PAIR(x29, x30, 0)
+  add sp, sp, #(8 * 84)
+  .cfi_def_cfa_offset 0
+  ret
+END(tlsdesc_resolver_dynamic_slow_path)
+
+// The address of an unresolved weak TLS symbol evaluates to NULL with TLSDESC.
+// The value returned by this function is added to the thread pointer, so return
+// a negated thread pointer to cancel it out.
+ENTRY_PRIVATE(tlsdesc_resolver_unresolved_weak)
+  str x19, [sp, #-16]!
+  .cfi_def_cfa_offset 16
+  .cfi_rel_offset x19, 0
+  ldr x19, [x0, #8]
+  mrs x0, tpidr_el0             // __get_tls()
+  sub x0, x19, x0
+  ldr x19, [sp], #16
+  .cfi_def_cfa_offset 0
+  .cfi_restore x19
+  ret
+END(tlsdesc_resolver_unresolved_weak)
diff --git a/linker/tests/linked_list_test.cpp b/linker/linked_list_test.cpp
similarity index 99%
rename from linker/tests/linked_list_test.cpp
rename to linker/linked_list_test.cpp
index 2b88ed0..71a7d09 100644
--- a/linker/tests/linked_list_test.cpp
+++ b/linker/linked_list_test.cpp
@@ -32,7 +32,7 @@
 
 #include <gtest/gtest.h>
 
-#include "../linked_list.h"
+#include "linked_list.h"
 
 namespace {
 
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 1f259e1..8a70ca9 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -65,8 +65,10 @@
 #include "linker_phdr.h"
 #include "linker_relocs.h"
 #include "linker_reloc_iterators.h"
+#include "linker_tls.h"
 #include "linker_utils.h"
 
+#include "private/bionic_globals.h"
 #include "android-base/macros.h"
 #include "android-base/strings.h"
 #include "android-base/stringprintf.h"
@@ -89,19 +91,21 @@
 static const char* const kLdConfigVndkLiteFilePath = "/system/etc/ld.config.vndk_lite.txt";
 
 #if defined(__LP64__)
-static const char* const kSystemLibDir     = "/system/lib64";
-static const char* const kOdmLibDir        = "/odm/lib64";
-static const char* const kVendorLibDir     = "/vendor/lib64";
-static const char* const kAsanSystemLibDir = "/data/asan/system/lib64";
-static const char* const kAsanOdmLibDir    = "/data/asan/odm/lib64";
-static const char* const kAsanVendorLibDir = "/data/asan/vendor/lib64";
+static const char* const kSystemLibDir        = "/system/lib64";
+static const char* const kOdmLibDir           = "/odm/lib64";
+static const char* const kVendorLibDir        = "/vendor/lib64";
+static const char* const kAsanSystemLibDir    = "/data/asan/system/lib64";
+static const char* const kAsanOdmLibDir       = "/data/asan/odm/lib64";
+static const char* const kAsanVendorLibDir    = "/data/asan/vendor/lib64";
+static const char* const kRuntimeApexLibDir   = "/apex/com.android.runtime/lib64";
 #else
-static const char* const kSystemLibDir     = "/system/lib";
-static const char* const kOdmLibDir        = "/odm/lib";
-static const char* const kVendorLibDir     = "/vendor/lib";
-static const char* const kAsanSystemLibDir = "/data/asan/system/lib";
-static const char* const kAsanOdmLibDir    = "/data/asan/odm/lib";
-static const char* const kAsanVendorLibDir = "/data/asan/vendor/lib";
+static const char* const kSystemLibDir        = "/system/lib";
+static const char* const kOdmLibDir           = "/odm/lib";
+static const char* const kVendorLibDir        = "/vendor/lib";
+static const char* const kAsanSystemLibDir    = "/data/asan/system/lib";
+static const char* const kAsanOdmLibDir       = "/data/asan/odm/lib";
+static const char* const kAsanVendorLibDir    = "/data/asan/vendor/lib";
+static const char* const kRuntimeApexLibDir   = "/apex/com.android.runtime/lib";
 #endif
 
 static const char* const kAsanLibDirPrefix = "/data/asan";
@@ -226,6 +230,44 @@
 }
 // END OF WORKAROUND
 
+// Workaround for dlopen(/system/lib(64)/<soname>) when .so is in /apex. http://b/121248172
+/**
+ * Translate /system path to /apex path if needed
+ * The workaround should work only when targetSdkVersion < Q.
+ *
+ * param out_name_to_apex pointing to /apex path
+ * return true if translation is needed
+ */
+static bool translateSystemPathToApexPath(const char* name, std::string* out_name_to_apex) {
+  static const char* const kSystemToRuntimeApexLibs[] = {
+    "libicuuc.so",
+    "libicui18n.so",
+  };
+  // New mapping for new apex should be added below
+
+  // Nothing to do if target sdk version is Q or above
+  if (get_application_target_sdk_version() >= __ANDROID_API_Q__) {
+    return false;
+  }
+
+  // If the path isn't /system/lib, there's nothing to do.
+  if (name == nullptr || dirname(name) != kSystemLibDir) {
+    return false;
+  }
+
+  const char* base_name = basename(name);
+
+  for (const char* soname : kSystemToRuntimeApexLibs) {
+    if (strcmp(base_name, soname) == 0) {
+      *out_name_to_apex = std::string(kRuntimeApexLibDir) + "/" + base_name;
+      return true;
+    }
+  }
+
+  return false;
+}
+// End Workaround for dlopen(/system/lib/<soname>) when .so is in /apex.
+
 static std::vector<std::string> g_ld_preload_names;
 
 static bool g_anonymous_namespace_initialized;
@@ -534,6 +576,10 @@
     allocator_.free(ptr);
   }
 
+  static void purge() {
+    allocator_.purge();
+  }
+
  private:
   static LinkerBlockAllocator allocator_;
 };
@@ -551,6 +597,10 @@
   static void free(T* ptr) {
     SizeBasedAllocator<sizeof(T)>::free(ptr);
   }
+
+  static void purge() {
+    SizeBasedAllocator<sizeof(T)>::purge();
+  }
 };
 
 class LoadTask {
@@ -601,6 +651,9 @@
   }
 
   void set_fd(int fd, bool assume_ownership) {
+    if (fd_ != -1 && close_fd_) {
+      close(fd_);
+    }
     fd_ = fd;
     close_fd_ = assume_ownership;
   }
@@ -1105,14 +1158,6 @@
     fd = open_library_on_paths(zip_archive_cache, name, file_offset, ns->get_default_library_paths(), realpath);
   }
 
-  // TODO(dimitry): workaround for http://b/26394120 (the grey-list)
-  if (fd == -1 && ns->is_greylist_enabled() && is_greylisted(ns, name, needed_by)) {
-    // try searching for it on default_namespace default_library_path
-    fd = open_library_on_paths(zip_archive_cache, name, file_offset,
-                               g_default_namespace.get_default_library_paths(), realpath);
-  }
-  // END OF WORKAROUND
-
   return fd;
 }
 
@@ -1491,6 +1536,20 @@
     return true;
   }
 
+  // TODO(dimitry): workaround for http://b/26394120 (the grey-list)
+  if (ns->is_greylist_enabled() && is_greylisted(ns, task->get_name(), task->get_needed_by())) {
+    // For the libs in the greylist, switch to the default namespace and then
+    // try the load again from there. The library could be loaded from the
+    // default namespace or from another namespace (e.g. runtime) that is linked
+    // from the default namespace.
+    ns = &g_default_namespace;
+    if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags,
+                     search_linked_namespaces)) {
+      return true;
+    }
+  }
+  // END OF WORKAROUND
+
   if (search_linked_namespaces) {
     // if a library was not found - look into linked namespaces
     // preserve current dlerror in the case it fails.
@@ -1652,6 +1711,7 @@
     if (!si->is_linked() && !si->prelink_image()) {
       return false;
     }
+    register_soinfo_tls(si);
   }
 
   // Step 4: Construct the global group. Note: DF_1_GLOBAL bit of a library is
@@ -1887,6 +1947,7 @@
            si->get_realpath(),
            si);
     notify_gdb_of_unload(si);
+    unregister_soinfo_tls(si);
     get_cfi_shadow()->BeforeUnload(si);
     soinfo_free(si);
   }
@@ -2059,13 +2120,16 @@
   android_namespace_t* ns = get_caller_namespace(caller);
 
   LD_LOG(kLogDlopen,
-         "dlopen(name=\"%s\", flags=0x%x, extinfo=%s, caller=\"%s\", caller_ns=%s@%p) ...",
+         "dlopen(name=\"%s\", flags=0x%x, extinfo=%s, caller=\"%s\", caller_ns=%s@%p, targetSdkVersion=%i) ...",
          name,
          flags,
          android_dlextinfo_to_string(extinfo).c_str(),
          caller == nullptr ? "(null)" : caller->get_realpath(),
          ns == nullptr ? "(null)" : ns->get_name(),
-         ns);
+         ns,
+         get_application_target_sdk_version());
+
+  auto purge_guard = android::base::make_scope_guard([&]() { purge_unused_memory(); });
 
   auto failure_guard = android::base::make_scope_guard(
       [&]() { LD_LOG(kLogDlopen, "... dlopen failed: %s", linker_get_error_buffer()); });
@@ -2097,6 +2161,32 @@
     }
   }
 
+  // Workaround for dlopen(/system/lib/<soname>) when .so is in /apex. http://b/121248172
+  // The workaround works only when targetSdkVersion < Q.
+  std::string name_to_apex;
+  if (translateSystemPathToApexPath(name, &name_to_apex)) {
+    const char* new_name = name_to_apex.c_str();
+    LD_LOG(kLogDlopen, "dlopen considering translation from %s to APEX path %s",
+           name,
+           new_name);
+    // Some APEXs could be optionally disabled. Only translate the path
+    // when the old file is absent and the new file exists.
+    // TODO(b/124218500): Re-enable it once app compat issue is resolved
+    /*
+    if (file_exists(name)) {
+      LD_LOG(kLogDlopen, "dlopen %s exists, not translating", name);
+    } else
+    */
+    if (!file_exists(new_name)) {
+      LD_LOG(kLogDlopen, "dlopen %s does not exist, not translating",
+             new_name);
+    } else {
+      LD_LOG(kLogDlopen, "dlopen translation accepted: using %s", new_name);
+      name = new_name;
+    }
+  }
+  // End Workaround for dlopen(/system/lib/<soname>) when .so is in /apex.
+
   std::string asan_name_holder;
 
   const char* translated_name = name;
@@ -2666,16 +2756,33 @@
 #else
 static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) {
   if (ELFW(R_TYPE)(rel->r_info) == R_GENERIC_RELATIVE ||
-      ELFW(R_TYPE)(rel->r_info) == R_GENERIC_IRELATIVE) {
+      ELFW(R_TYPE)(rel->r_info) == R_GENERIC_IRELATIVE ||
+      ELFW(R_TYPE)(rel->r_info) == R_GENERIC_TLS_DTPREL ||
+      ELFW(R_TYPE)(rel->r_info) == R_GENERIC_TLS_TPREL) {
     return *reinterpret_cast<ElfW(Addr)*>(reloc_addr);
   }
   return 0;
 }
 #endif
 
+static bool is_tls_reloc(ElfW(Word) type) {
+  switch (type) {
+    case R_GENERIC_TLS_DTPMOD:
+    case R_GENERIC_TLS_DTPREL:
+    case R_GENERIC_TLS_TPREL:
+    case R_GENERIC_TLSDESC:
+      return true;
+    default:
+      return false;
+  }
+}
+
 template<typename ElfRelIteratorT>
 bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& rel_iterator,
                       const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
+  const size_t tls_tp_base = __libc_shared_globals()->static_tls_layout.offset_thread_pointer();
+  std::vector<std::pair<TlsDescriptor*, size_t>> deferred_tlsdesc_relocs;
+
   for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
     const auto rel = rel_iterator.next();
     if (rel == nullptr) {
@@ -2698,7 +2805,26 @@
     const ElfW(Sym)* s = nullptr;
     soinfo* lsi = nullptr;
 
-    if (sym != 0) {
+    if (sym == 0) {
+      // By convention in ld.bfd and lld, an omitted symbol on a TLS relocation
+      // is a reference to the current module.
+      if (is_tls_reloc(type)) {
+        lsi = this;
+      }
+    } else if (ELF_ST_BIND(symtab_[sym].st_info) == STB_LOCAL && is_tls_reloc(type)) {
+      // In certain situations, the Gold linker accesses a TLS symbol using a
+      // relocation to an STB_LOCAL symbol in .dynsym of either STT_SECTION or
+      // STT_TLS type. Bionic doesn't support these relocations, so issue an
+      // error. References:
+      //  - https://groups.google.com/d/topic/generic-abi/dJ4_Y78aQ2M/discussion
+      //  - https://sourceware.org/bugzilla/show_bug.cgi?id=17699
+      s = &symtab_[sym];
+      sym_name = get_string(s->st_name);
+      DL_ERR("unexpected TLS reference to local symbol \"%s\": "
+             "sym type %d, rel type %u (idx %zu of \"%s\")",
+             sym_name, ELF_ST_TYPE(s->st_info), type, idx, get_realpath());
+      return false;
+    } else {
       sym_name = get_string(symtab_[sym].st_name);
       const version_info* vi = nullptr;
 
@@ -2735,6 +2861,10 @@
           case R_GENERIC_GLOB_DAT:
           case R_GENERIC_RELATIVE:
           case R_GENERIC_IRELATIVE:
+          case R_GENERIC_TLS_DTPMOD:
+          case R_GENERIC_TLS_DTPREL:
+          case R_GENERIC_TLS_TPREL:
+          case R_GENERIC_TLSDESC:
 #if defined(__aarch64__)
           case R_AARCH64_ABS64:
           case R_AARCH64_ABS32:
@@ -2782,12 +2912,26 @@
           }
         }
 #endif
-        if (ELF_ST_TYPE(s->st_info) == STT_TLS) {
-          DL_ERR("unsupported ELF TLS symbol \"%s\" referenced by \"%s\"",
-                 sym_name, get_realpath());
-          return false;
+        if (is_tls_reloc(type)) {
+          if (ELF_ST_TYPE(s->st_info) != STT_TLS) {
+            DL_ERR("reference to non-TLS symbol \"%s\" from TLS relocation in \"%s\"",
+                   sym_name, get_realpath());
+            return false;
+          }
+          if (lsi->get_tls() == nullptr) {
+            DL_ERR("TLS relocation refers to symbol \"%s\" in solib \"%s\" with no TLS segment",
+                   sym_name, lsi->get_realpath());
+            return false;
+          }
+          sym_addr = s->st_value;
+        } else {
+          if (ELF_ST_TYPE(s->st_info) == STT_TLS) {
+            DL_ERR("reference to TLS symbol \"%s\" from non-TLS relocation in \"%s\"",
+                   sym_name, get_realpath());
+            return false;
+          }
+          sym_addr = lsi->resolve_symbol_address(s);
         }
-        sym_addr = lsi->resolve_symbol_address(s);
 #if !defined(__LP64__)
         if (protect_segments) {
           if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) {
@@ -2860,6 +3004,104 @@
           *reinterpret_cast<ElfW(Addr)*>(reloc) = ifunc_addr;
         }
         break;
+      case R_GENERIC_TLS_TPREL:
+        count_relocation(kRelocRelative);
+        MARK(rel->r_offset);
+        {
+          ElfW(Addr) tpoff = 0;
+          if (lsi == nullptr) {
+            // Unresolved weak relocation. Leave tpoff at 0 to resolve
+            // &weak_tls_symbol to __get_tls().
+          } else {
+            CHECK(lsi->get_tls() != nullptr); // We rejected a missing TLS segment above.
+            const TlsModule& mod = get_tls_module(lsi->get_tls()->module_id);
+            if (mod.static_offset != SIZE_MAX) {
+              tpoff += mod.static_offset - tls_tp_base;
+            } else {
+              DL_ERR("TLS symbol \"%s\" in dlopened \"%s\" referenced from \"%s\" using IE access model",
+                     sym_name, lsi->get_realpath(), get_realpath());
+              return false;
+            }
+          }
+          tpoff += sym_addr + addend;
+          TRACE_TYPE(RELO, "RELO TLS_TPREL %16p <- %16p %s\n",
+                     reinterpret_cast<void*>(reloc),
+                     reinterpret_cast<void*>(tpoff), sym_name);
+          *reinterpret_cast<ElfW(Addr)*>(reloc) = tpoff;
+        }
+        break;
+
+#if !defined(__aarch64__)
+      // Omit support for DTPMOD/DTPREL on arm64, at least until
+      // http://b/123385182 is fixed. arm64 uses TLSDESC instead.
+      case R_GENERIC_TLS_DTPMOD:
+        count_relocation(kRelocRelative);
+        MARK(rel->r_offset);
+        {
+          size_t module_id = 0;
+          if (lsi == nullptr) {
+            // Unresolved weak relocation. Evaluate the module ID to 0.
+          } else {
+            CHECK(lsi->get_tls() != nullptr); // We rejected a missing TLS segment above.
+            module_id = lsi->get_tls()->module_id;
+          }
+          TRACE_TYPE(RELO, "RELO TLS_DTPMOD %16p <- %zu %s\n",
+                     reinterpret_cast<void*>(reloc), module_id, sym_name);
+          *reinterpret_cast<ElfW(Addr)*>(reloc) = module_id;
+        }
+        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);
+        *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend;
+        break;
+#endif  // !defined(__aarch64__)
+
+#if defined(__aarch64__)
+      // Bionic currently only implements TLSDESC for arm64. This implementation should work with
+      // 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) {
+            // Unresolved weak relocation.
+            desc->func = tlsdesc_resolver_unresolved_weak;
+            desc->arg = addend;
+            TRACE_TYPE(RELO, "RELO TLSDESC %16p <- unresolved weak 0x%zx %s\n",
+                       reinterpret_cast<void*>(reloc), static_cast<size_t>(addend), sym_name);
+          } else {
+            CHECK(lsi->get_tls() != nullptr); // We rejected a missing TLS segment above.
+            size_t module_id = lsi->get_tls()->module_id;
+            const TlsModule& mod = get_tls_module(module_id);
+            if (mod.static_offset != SIZE_MAX) {
+              desc->func = tlsdesc_resolver_static;
+              desc->arg = mod.static_offset - tls_tp_base + sym_addr + addend;
+              TRACE_TYPE(RELO, "RELO TLSDESC %16p <- static (0x%zx - 0x%zx + 0x%zx + 0x%zx) %s\n",
+                         reinterpret_cast<void*>(reloc), mod.static_offset, tls_tp_base,
+                         static_cast<size_t>(sym_addr), static_cast<size_t>(addend), sym_name);
+            } else {
+              tlsdesc_args_.push_back({
+                .generation = mod.first_generation,
+                .index.module_id = module_id,
+                .index.offset = sym_addr + addend,
+              });
+              // Defer the TLSDESC relocation until the address of the TlsDynamicResolverArg object
+              // is finalized.
+              deferred_tlsdesc_relocs.push_back({ desc, tlsdesc_args_.size() - 1 });
+              const TlsDynamicResolverArg& desc_arg = tlsdesc_args_.back();
+              TRACE_TYPE(RELO, "RELO TLSDESC %16p <- dynamic (gen %zu, mod %zu, off %zu) %s",
+                         reinterpret_cast<void*>(reloc), desc_arg.generation,
+                         desc_arg.index.module_id, desc_arg.index.offset, sym_name);
+            }
+          }
+        }
+        break;
+#endif  // defined(R_GENERIC_TLSDESC)
 
 #if defined(__aarch64__)
       case R_AARCH64_ABS64:
@@ -2961,14 +3203,6 @@
          */
         DL_ERR("%s R_AARCH64_COPY relocations are not supported", get_realpath());
         return false;
-      case R_AARCH64_TLS_TPREL64:
-        TRACE_TYPE(RELO, "RELO TLS_TPREL64 *** %16llx <- %16llx - %16llx\n",
-                   reloc, (sym_addr + addend), rel->r_offset);
-        break;
-      case R_AARCH64_TLSDESC:
-        TRACE_TYPE(RELO, "RELO TLSDESC *** %16llx <- %16llx - %16llx\n",
-                   reloc, (sym_addr + addend), rel->r_offset);
-        break;
 #elif defined(__x86_64__)
       case R_X86_64_32:
         count_relocation(kRelocRelative);
@@ -3038,6 +3272,13 @@
         return false;
     }
   }
+
+  for (const std::pair<TlsDescriptor*, size_t>& pair : deferred_tlsdesc_relocs) {
+    TlsDescriptor* desc = pair.first;
+    desc->func = tlsdesc_resolver_dynamic;
+    desc->arg = reinterpret_cast<size_t>(&tlsdesc_args_[pair.second]);
+  }
+
   return true;
 }
 #endif  // !defined(__mips__)
@@ -3073,6 +3314,19 @@
                                   &ARM_exidx, &ARM_exidx_count);
 #endif
 
+  TlsSegment tls_segment;
+  if (__bionic_get_tls_segment(phdr, phnum, load_bias, &tls_segment)) {
+    if (!__bionic_check_tls_alignment(&tls_segment.alignment)) {
+      if (!relocating_linker) {
+        DL_ERR("TLS segment alignment in \"%s\" is not a power of 2: %zu",
+               get_realpath(), tls_segment.alignment);
+      }
+      return false;
+    }
+    tls_ = std::make_unique<soinfo_tls>();
+    tls_->segment = tls_segment;
+  }
+
   // Extract useful information from dynamic section.
   // Note that: "Except for the DT_NULL element at the end of the array,
   // and the relative order of DT_NEEDED elements, entries may appear in any order."
@@ -3438,13 +3692,14 @@
         // this is parsed after we have strtab initialized (see below).
         break;
 
+      case DT_TLSDESC_GOT:
+      case DT_TLSDESC_PLT:
+        // These DT entries are used for lazy TLSDESC relocations. Bionic
+        // resolves everything eagerly, so these can be ignored.
+        break;
+
       default:
         if (!relocating_linker) {
-          if (d->d_tag == DT_TLSDESC_GOT || d->d_tag == DT_TLSDESC_PLT) {
-            DL_ERR("unsupported ELF TLS DT entry in \"%s\"", get_realpath());
-            return false;
-          }
-
           const char* tag_name;
           if (d->d_tag == DT_RPATH) {
             tag_name = "DT_RPATH";
@@ -3897,3 +4152,17 @@
   }
   return it->second;
 }
+
+void purge_unused_memory() {
+  // For now, we only purge the memory used by LoadTask because we know those
+  // are temporary objects.
+  //
+  // Purging other LinkerBlockAllocator hardly yields much because they hold
+  // information about namespaces and opened libraries, which are not freed
+  // when the control leaves the linker.
+  //
+  // Purging BionicAllocator may give us a few dirty pages back, but those pages
+  // would be already zeroed out, so they compress easily in ZRAM.  Therefore,
+  // it is not worth munmap()'ing those pages.
+  TypeBasedAllocator<LoadTask>::purge();
+}
diff --git a/linker/linker.h b/linker/linker.h
index 91d3ddf..964c266 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -186,3 +186,5 @@
 
 void increment_dso_handle_reference_counter(void* dso_handle);
 void decrement_dso_handle_reference_counter(void* dso_handle);
+
+void purge_unused_memory();
diff --git a/linker/linker_block_allocator.cpp b/linker/linker_block_allocator.cpp
index dca944e..fdb4c85 100644
--- a/linker/linker_block_allocator.cpp
+++ b/linker/linker_block_allocator.cpp
@@ -33,6 +33,9 @@
 #include <sys/prctl.h>
 #include <unistd.h>
 
+static constexpr size_t kAllocateSize = PAGE_SIZE * 100;
+static_assert(kAllocateSize % PAGE_SIZE == 0, "Invalid kAllocateSize.");
+
 // the multiplier should be power of 2
 static constexpr size_t round_up(size_t size, size_t multiplier) {
   return (size + (multiplier - 1)) & ~(multiplier-1);
@@ -40,7 +43,7 @@
 
 struct LinkerBlockAllocatorPage {
   LinkerBlockAllocatorPage* next;
-  uint8_t bytes[PAGE_SIZE - 16] __attribute__((aligned(16)));
+  uint8_t bytes[kAllocateSize - 16] __attribute__((aligned(16)));
 };
 
 struct FreeBlockInfo {
@@ -52,7 +55,8 @@
   : block_size_(
       round_up(block_size < sizeof(FreeBlockInfo) ? sizeof(FreeBlockInfo) : block_size, 16)),
     page_list_(nullptr),
-    free_block_list_(nullptr)
+    free_block_list_(nullptr),
+    allocated_(0)
 {}
 
 void* LinkerBlockAllocator::alloc() {
@@ -73,6 +77,8 @@
 
   memset(block_info, 0, block_size_);
 
+  ++allocated_;
+
   return block_info;
 }
 
@@ -101,32 +107,34 @@
   block_info->num_free_blocks = 1;
 
   free_block_list_ = block_info;
+
+  --allocated_;
 }
 
 void LinkerBlockAllocator::protect_all(int prot) {
   for (LinkerBlockAllocatorPage* page = page_list_; page != nullptr; page = page->next) {
-    if (mprotect(page, PAGE_SIZE, prot) == -1) {
+    if (mprotect(page, kAllocateSize, prot) == -1) {
       abort();
     }
   }
 }
 
 void LinkerBlockAllocator::create_new_page() {
-  static_assert(sizeof(LinkerBlockAllocatorPage) == PAGE_SIZE,
+  static_assert(sizeof(LinkerBlockAllocatorPage) == kAllocateSize,
                 "Invalid sizeof(LinkerBlockAllocatorPage)");
 
   LinkerBlockAllocatorPage* page = reinterpret_cast<LinkerBlockAllocatorPage*>(
-      mmap(nullptr, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0));
+      mmap(nullptr, kAllocateSize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0));
 
   if (page == MAP_FAILED) {
     abort(); // oom
   }
 
-  prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, page, PAGE_SIZE, "linker_alloc");
+  prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, page, kAllocateSize, "linker_alloc");
 
   FreeBlockInfo* first_block = reinterpret_cast<FreeBlockInfo*>(page->bytes);
   first_block->next_block = free_block_list_;
-  first_block->num_free_blocks = (PAGE_SIZE - sizeof(LinkerBlockAllocatorPage*))/block_size_;
+  first_block->num_free_blocks = (kAllocateSize - sizeof(LinkerBlockAllocatorPage*))/block_size_;
 
   free_block_list_ = first_block;
 
@@ -142,7 +150,7 @@
   LinkerBlockAllocatorPage* page = page_list_;
   while (page != nullptr) {
     const uint8_t* page_ptr = reinterpret_cast<const uint8_t*>(page);
-    if (block >= (page_ptr + sizeof(page->next)) && block < (page_ptr + PAGE_SIZE)) {
+    if (block >= (page_ptr + sizeof(page->next)) && block < (page_ptr + kAllocateSize)) {
       return page;
     }
 
@@ -151,3 +159,18 @@
 
   abort();
 }
+
+void LinkerBlockAllocator::purge() {
+  if (allocated_) {
+    return;
+  }
+
+  LinkerBlockAllocatorPage* page = page_list_;
+  while (page) {
+    LinkerBlockAllocatorPage* next = page->next;
+    munmap(page, kAllocateSize);
+    page = next;
+  }
+  page_list_ = nullptr;
+  free_block_list_ = nullptr;
+}
diff --git a/linker/linker_block_allocator.h b/linker/linker_block_allocator.h
index bd44fc8..8ae4094 100644
--- a/linker/linker_block_allocator.h
+++ b/linker/linker_block_allocator.h
@@ -50,6 +50,9 @@
   void free(void* block);
   void protect_all(int prot);
 
+  // Purge all pages if all previously allocated blocks have been freed.
+  void purge();
+
  private:
   void create_new_page();
   LinkerBlockAllocatorPage* find_page(void* block);
@@ -57,6 +60,7 @@
   size_t block_size_;
   LinkerBlockAllocatorPage* page_list_;
   void* free_block_list_;
+  size_t allocated_;
 
   DISALLOW_COPY_AND_ASSIGN(LinkerBlockAllocator);
 };
@@ -66,17 +70,17 @@
  * of a single fixed-size type. Allocations are backed by page-sized private
  * anonymous mmaps.
  *
- * The differences between this allocator and LinkerMemoryAllocator are:
- * 1. This allocator manages space more efficiently. LinkerMemoryAllocator
- *    operates in power-of-two sized blocks up to 1k, when this implementation
- *    splits the page to aligned size of structure; For example for structures
- *    with size 513 this allocator will use 516 (520 for lp64) bytes of data
- *    where generalized implementation is going to use 1024 sized blocks.
+ * The differences between this allocator and BionicAllocator are:
+ * 1. This allocator manages space more efficiently. BionicAllocator operates in
+ *    power-of-two sized blocks up to 1k, when this implementation splits the
+ *    page to aligned size of structure; For example for structures with size
+ *    513 this allocator will use 516 (520 for lp64) bytes of data where
+ *    generalized implementation is going to use 1024 sized blocks.
  *
- * 2. This allocator does not munmap allocated memory, where LinkerMemoryAllocator does.
+ * 2. This allocator does not munmap allocated memory, where BionicAllocator does.
  *
- * 3. This allocator provides mprotect services to the user, where LinkerMemoryAllocator
- *    always treats it's memory as READ|WRITE.
+ * 3. This allocator provides mprotect services to the user, where BionicAllocator
+ *    always treats its memory as READ|WRITE.
  */
 template<typename T>
 class LinkerTypeAllocator {
diff --git a/linker/tests/linker_block_allocator_test.cpp b/linker/linker_block_allocator_test.cpp
similarity index 98%
rename from linker/tests/linker_block_allocator_test.cpp
rename to linker/linker_block_allocator_test.cpp
index d5eb97c..359eefb 100644
--- a/linker/tests/linker_block_allocator_test.cpp
+++ b/linker/linker_block_allocator_test.cpp
@@ -32,7 +32,7 @@
 
 #include <gtest/gtest.h>
 
-#include "../linker_block_allocator.h"
+#include "linker_block_allocator.h"
 
 #include <unistd.h>
 
@@ -145,4 +145,3 @@
   testing::FLAGS_gtest_death_test_style = "threadsafe";
   ASSERT_EXIT(protect_all(), testing::KilledBySignal(SIGSEGV), "trying to access protected page");
 }
-
diff --git a/linker/linker_config.cpp b/linker/linker_config.cpp
index 0e75c85..5a728d3 100644
--- a/linker/linker_config.cpp
+++ b/linker/linker_config.cpp
@@ -41,6 +41,7 @@
 
 #include <limits.h>
 #include <stdlib.h>
+#include <unistd.h>
 
 #include <string>
 #include <unordered_map>
@@ -238,9 +239,17 @@
       // If the path can be resolved, resolve it
       char buf[PATH_MAX];
       std::string resolved_path;
-      if (realpath(value.c_str(), buf)) {
+      if (access(value.c_str(), R_OK) != 0) {
+        if (errno == ENOENT) {
+          // no need to test for non-existing path. skip.
+          continue;
+        }
+        // If not accessible, don't call realpath as it will just cause
+        // SELinux denial spam. Use the path unresolved.
+        resolved_path = value;
+      } else if (realpath(value.c_str(), buf)) {
         resolved_path = buf;
-      } else if (errno != ENOENT)  {
+      } else {
         // realpath is expected to fail with EPERM in some situations, so log
         // the failure with INFO rather than DL_WARN. e.g. A binary in
         // /data/local/tmp may attempt to stat /postinstall. See
@@ -251,9 +260,6 @@
              value.c_str(),
              strerror(errno));
         resolved_path = value;
-      } else {
-        // ENOENT: no need to test if binary is under the path
-        continue;
       }
 
       if (file_is_under_dir(binary_realpath, resolved_path)) {
diff --git a/linker/tests/linker_config_test.cpp b/linker/linker_config_test.cpp
similarity index 99%
rename from linker/tests/linker_config_test.cpp
rename to linker/linker_config_test.cpp
index 14fd132..6a55bb2 100644
--- a/linker/tests/linker_config_test.cpp
+++ b/linker/linker_config_test.cpp
@@ -32,8 +32,8 @@
 
 #include <gtest/gtest.h>
 
-#include "../linker_config.h"
-#include "../linker_utils.h"
+#include "linker_config.h"
+#include "linker_utils.h"
 
 #include <unistd.h>
 
diff --git a/linker/linker_debug.h b/linker/linker_debug.h
index 862ea12..7a1cb3c 100644
--- a/linker/linker_debug.h
+++ b/linker/linker_debug.h
@@ -56,6 +56,7 @@
 #include <unistd.h>
 
 #include <async_safe/log.h>
+#include <async_safe/CHECK.h>
 
 __LIBC_HIDDEN__ extern int g_ld_debug_verbosity;
 
diff --git a/linker/linker_logger.cpp b/linker/linker_logger.cpp
index d0e5072..ec07a55 100644
--- a/linker/linker_logger.cpp
+++ b/linker/linker_logger.cpp
@@ -118,11 +118,7 @@
   flags_ |= ParseProperty(debug_ld_app);
 }
 
-void LinkerLogger::Log(uint32_t type, const char* format, ...) {
-  if ((flags_ & type) == 0) {
-    return;
-  }
-
+void LinkerLogger::Log(const char* format, ...) {
   va_list ap;
   va_start(ap, format);
   async_safe_format_log_va_list(ANDROID_LOG_DEBUG, "linker", format, ap);
diff --git a/linker/linker_logger.h b/linker/linker_logger.h
index 1828799..f9fc38e 100644
--- a/linker/linker_logger.h
+++ b/linker/linker_logger.h
@@ -35,10 +35,10 @@
 
 #include <android-base/macros.h>
 
-#define LD_LOG(type, x...) \
-  { \
-    g_linker_logger.Log(type, x); \
-  }
+#define LD_LOG(type, x...)                                       \
+  do {                                                           \
+    if (g_linker_logger.IsEnabled(type)) g_linker_logger.Log(x); \
+  } while (0)
 
 constexpr const uint32_t kLogErrors = 1 << 0;
 constexpr const uint32_t kLogDlopen = 1 << 1;
@@ -49,7 +49,12 @@
   LinkerLogger() : flags_(0) { }
 
   void ResetState();
-  void Log(uint32_t type, const char* format, ...);
+  void Log(const char* format, ...);
+
+  uint32_t IsEnabled(uint32_t type) {
+    return flags_ & type;
+  }
+
  private:
   uint32_t flags_;
 
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index 9b4ce47..9129a52 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -415,6 +415,8 @@
     }
   }
 
+  linker_setup_exe_static_tls(g_argv[0]);
+
   // Load ld_preloads and dependencies.
   std::vector<const char*> needed_library_name_list;
   size_t ld_preloads_count = 0;
@@ -452,8 +454,7 @@
     si->increment_ref_count();
   }
 
-  layout_linker_static_tls();
-
+  linker_finalize_static_tls();
   __libc_init_main_thread_final();
 
   if (!get_cfi_shadow()->InitialLinkDone(solist)) __linker_cannot_link(g_argv[0]);
@@ -502,6 +503,10 @@
   fflush(stdout);
 #endif
 
+  // We are about to hand control over to the executable loaded.  We don't want
+  // to leave dirty pages behind unnecessarily.
+  purge_unused_memory();
+
   ElfW(Addr) entry = exe_info.entry_point;
   TRACE("[ Ready to execute \"%s\" @ %p ]", si->get_realpath(), reinterpret_cast<void*>(entry));
   return entry;
@@ -548,6 +553,24 @@
   async_safe_fatal("Could not find a PHDR: broken executable?");
 }
 
+// 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.
+__attribute__((constructor(1))) static void detect_self_exec() {
+  // Normally, the linker initializes the auxv global before calling its
+  // constructors. If the linker loads itself, though, the first loader calls
+  // the second loader's constructors before calling __linker_init.
+  if (__libc_shared_globals()->auxv != nullptr) {
+    return;
+  }
+#if defined(__i386__)
+  // We don't have access to the auxv struct from here, so use the int 0x80
+  // fallback.
+  __libc_sysinfo = reinterpret_cast<void*>(__libc_int0x80);
+#endif
+  __linker_error("error: linker cannot load itself\n");
+}
+
 static ElfW(Addr) __attribute__((noinline))
 __linker_init_post_relocation(KernelArgumentBlock& args, soinfo& linker_so);
 
@@ -570,18 +593,8 @@
   // another program), AT_BASE is 0.
   ElfW(Addr) linker_addr = getauxval(AT_BASE);
   if (linker_addr == 0) {
-    // Detect an attempt to run the linker on itself (e.g.
-    // `linker64 /system/bin/linker64`). If the kernel loaded this instance of
-    // the linker, then AT_ENTRY will refer to &_start. If it doesn't, then
-    // something else must have loaded this instance of the linker. It's
-    // simpler if we only allow one copy of the linker to be loaded at a time.
-    if (getauxval(AT_ENTRY) != reinterpret_cast<uintptr_t>(&_start)) {
-      // The first linker already relocated this one and set up TLS, so we don't
-      // need further libc initialization.
-      __linker_error("error: linker cannot load itself\n");
-    }
-    // Otherwise, the AT_PHDR and AT_PHNUM aux values describe this linker
-    // instance, so use the phdr to find the linker's base address.
+    // The AT_PHDR and AT_PHNUM aux values describe this linker instance, so use
+    // the phdr to find the linker's base address.
     ElfW(Addr) load_bias;
     get_elf_base_from_phdr(
       reinterpret_cast<ElfW(Phdr)*>(getauxval(AT_PHDR)), getauxval(AT_PHNUM),
diff --git a/linker/linker_memory.cpp b/linker/linker_memory.cpp
index f2cce01..ce29997 100644
--- a/linker/linker_memory.cpp
+++ b/linker/linker_memory.cpp
@@ -26,7 +26,7 @@
  * SUCH DAMAGE.
  */
 
-#include "linker_allocator.h"
+#include "private/bionic_allocator.h"
 
 #include <stdlib.h>
 #include <sys/cdefs.h>
@@ -36,7 +36,7 @@
 
 #include <async_safe/log.h>
 
-static LinkerMemoryAllocator g_linker_allocator;
+static BionicAllocator g_bionic_allocator;
 static std::atomic<pid_t> fallback_tid(0);
 
 // Used by libdebuggerd_handler to switch allocators during a crash dump, in
@@ -56,16 +56,16 @@
   }
 }
 
-static LinkerMemoryAllocator& get_fallback_allocator() {
-  static LinkerMemoryAllocator fallback_allocator;
+static BionicAllocator& get_fallback_allocator() {
+  static BionicAllocator fallback_allocator;
   return fallback_allocator;
 }
 
-static LinkerMemoryAllocator& get_allocator() {
+static BionicAllocator& get_allocator() {
   if (__predict_false(fallback_tid) && __predict_false(gettid() == fallback_tid)) {
     return get_fallback_allocator();
   }
-  return g_linker_allocator;
+  return g_bionic_allocator;
 }
 
 void* malloc(size_t byte_count) {
diff --git a/linker/tests/linker_sleb128_test.cpp b/linker/linker_sleb128_test.cpp
similarity index 98%
rename from linker/tests/linker_sleb128_test.cpp
rename to linker/linker_sleb128_test.cpp
index 551faf2..9e819c6 100644
--- a/linker/tests/linker_sleb128_test.cpp
+++ b/linker/linker_sleb128_test.cpp
@@ -32,7 +32,7 @@
 
 #include <gtest/gtest.h>
 
-#include "../linker_sleb128.h"
+#include "linker_sleb128.h"
 
 TEST(linker_sleb128, smoke) {
   std::vector<uint8_t> encoding;
diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp
index 93079ca..89119aa 100644
--- a/linker/linker_soinfo.cpp
+++ b/linker/linker_soinfo.cpp
@@ -82,8 +82,15 @@
   split_path(path, ":", &runpaths);
 
   std::string origin = dirname(get_realpath());
-  // FIXME: add $LIB and $PLATFORM.
-  std::vector<std::pair<std::string, std::string>> params = {{"ORIGIN", origin}};
+  // FIXME: add $PLATFORM.
+  std::vector<std::pair<std::string, std::string>> params = {
+    {"ORIGIN", origin},
+#if defined(LIB_PATH)
+    {"LIB", LIB_PATH},
+#else
+#error "LIB_PATH not defined"
+#endif
+  };
   for (auto&& s : runpaths) {
     format_string(&s, params);
   }
@@ -628,6 +635,10 @@
   return secondary_namespaces_;
 }
 
+soinfo_tls* soinfo::get_tls() const {
+  return has_min_version(5) ? tls_.get() : nullptr;
+}
+
 ElfW(Addr) soinfo::resolve_symbol_address(const ElfW(Sym)* s) const {
   if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) {
     return call_ifunc_resolver(s->st_value + load_bias);
diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h
index 44bff28..dd9c6aa 100644
--- a/linker/linker_soinfo.h
+++ b/linker/linker_soinfo.h
@@ -30,9 +30,13 @@
 
 #include <link.h>
 
+#include <memory>
 #include <string>
+#include <vector>
 
+#include "private/bionic_elf_tls.h"
 #include "linker_namespaces.h"
+#include "linker_tls.h"
 
 #define FLAG_LINKED           0x00000001
 #define FLAG_EXE              0x00000004 // The main executable
@@ -61,7 +65,7 @@
                                          // unset.
 #define FLAG_NEW_SOINFO       0x40000000 // new soinfo format
 
-#define SOINFO_VERSION 4
+#define SOINFO_VERSION 5
 
 typedef void (*linker_dtor_function_t)();
 typedef void (*linker_ctor_function_t)(int, char**, char**);
@@ -100,6 +104,11 @@
 // TODO(dimitry): remove reference from soinfo member functions to this class.
 class VersionTracker;
 
+struct soinfo_tls {
+  TlsSegment segment;
+  size_t module_id = kTlsUninitializedModuleId;
+};
+
 #if defined(__work_around_b_24465209__)
 #define SOINFO_NAME_LEN 128
 #endif
@@ -284,6 +293,8 @@
   void add_secondary_namespace(android_namespace_t* secondary_ns);
   android_namespace_list_t& get_secondary_namespaces();
 
+  soinfo_tls* get_tls() const;
+
   void set_mapped_by_caller(bool reserved_map);
   bool is_mapped_by_caller() const;
 
@@ -366,6 +377,10 @@
   // version >= 4
   ElfW(Relr)* relr_;
   size_t relr_count_;
+
+  // version >= 5
+  std::unique_ptr<soinfo_tls> tls_;
+  std::vector<TlsDynamicResolverArg> tlsdesc_args_;
 };
 
 // This function is used by dlvsym() to calculate hash of sym_ver
diff --git a/linker/tests/linker_globals.cpp b/linker/linker_test_globals.cpp
similarity index 100%
rename from linker/tests/linker_globals.cpp
rename to linker/linker_test_globals.cpp
diff --git a/linker/linker_tls.cpp b/linker/linker_tls.cpp
index 3327453..d2edbb3 100644
--- a/linker/linker_tls.cpp
+++ b/linker/linker_tls.cpp
@@ -28,20 +28,92 @@
 
 #include "linker_tls.h"
 
+#include <vector>
+
+#include "async_safe/CHECK.h"
+#include "private/ScopedRWLock.h"
+#include "private/ScopedSignalBlocker.h"
 #include "private/bionic_defs.h"
 #include "private/bionic_elf_tls.h"
 #include "private/bionic_globals.h"
 #include "private/linker_native_bridge.h"
+#include "linker_main.h"
+#include "linker_soinfo.h"
+
+static bool g_static_tls_finished;
+static std::vector<TlsModule> g_tls_modules;
+
+static size_t get_unused_module_index() {
+  for (size_t i = 0; i < g_tls_modules.size(); ++i) {
+    if (g_tls_modules[i].soinfo_ptr == nullptr) {
+      return i;
+    }
+  }
+  g_tls_modules.push_back({});
+  __libc_shared_globals()->tls_modules.module_count = g_tls_modules.size();
+  __libc_shared_globals()->tls_modules.module_table = g_tls_modules.data();
+  return g_tls_modules.size() - 1;
+}
+
+static void register_tls_module(soinfo* si, size_t static_offset) {
+  TlsModules& libc_modules = __libc_shared_globals()->tls_modules;
+
+  // The global TLS module table points at the std::vector of modules declared
+  // in this file, so acquire a write lock before modifying the std::vector.
+  ScopedSignalBlocker ssb;
+  ScopedWriteLock locker(&libc_modules.rwlock);
+
+  size_t module_idx = get_unused_module_index();
+
+  soinfo_tls* si_tls = si->get_tls();
+  si_tls->module_id = __tls_module_idx_to_id(module_idx);
+
+  const size_t new_generation = ++libc_modules.generation;
+  __libc_tls_generation_copy = new_generation;
+  if (libc_modules.generation_libc_so != nullptr) {
+    *libc_modules.generation_libc_so = new_generation;
+  }
+
+  g_tls_modules[module_idx] = {
+    .segment = si_tls->segment,
+    .static_offset = static_offset,
+    .first_generation = new_generation,
+    .soinfo_ptr = si,
+  };
+}
+
+static void unregister_tls_module(soinfo* si) {
+  ScopedSignalBlocker ssb;
+  ScopedWriteLock locker(&__libc_shared_globals()->tls_modules.rwlock);
+
+  soinfo_tls* si_tls = si->get_tls();
+  TlsModule& mod = g_tls_modules[__tls_module_id_to_idx(si_tls->module_id)];
+  CHECK(mod.static_offset == SIZE_MAX);
+  CHECK(mod.soinfo_ptr == si);
+  mod = {};
+  si_tls->module_id = kTlsUninitializedModuleId;
+}
+
+// The reference is valid until a TLS module is registered or unregistered.
+const TlsModule& get_tls_module(size_t module_id) {
+  size_t module_idx = __tls_module_id_to_idx(module_id);
+  CHECK(module_idx < g_tls_modules.size());
+  return g_tls_modules[module_idx];
+}
 
 __BIONIC_WEAK_FOR_NATIVE_BRIDGE
 extern "C" void __linker_reserve_bionic_tls_in_static_tls() {
   __libc_shared_globals()->static_tls_layout.reserve_bionic_tls();
 }
 
-// Stub for linker static TLS layout.
-void layout_linker_static_tls() {
+void linker_setup_exe_static_tls(const char* progname) {
+  soinfo* somain = solist_get_somain();
   StaticTlsLayout& layout = __libc_shared_globals()->static_tls_layout;
-  layout.reserve_tcb();
+  if (somain->get_tls() == nullptr) {
+    layout.reserve_exe_segment_and_tcb(nullptr, progname);
+  } else {
+    register_tls_module(somain, layout.reserve_exe_segment_and_tcb(&somain->get_tls()->segment, progname));
+  }
 
   // The pthread key data is located at the very front of bionic_tls. As a
   // temporary workaround, allocate bionic_tls just after the thread pointer so
@@ -49,8 +121,32 @@
   // small enough. Specifically, Golang scans forward 384 words from the TP on
   // ARM.
   //  - http://b/118381796
-  //  - https://groups.google.com/d/msg/golang-dev/yVrkFnYrYPE/2G3aFzYqBgAJ
+  //  - https://github.com/golang/go/issues/29674
   __linker_reserve_bionic_tls_in_static_tls();
+}
 
-  layout.finish_layout();
+void linker_finalize_static_tls() {
+  g_static_tls_finished = true;
+  __libc_shared_globals()->static_tls_layout.finish_layout();
+}
+
+void register_soinfo_tls(soinfo* si) {
+  soinfo_tls* si_tls = si->get_tls();
+  if (si_tls == nullptr || si_tls->module_id != kTlsUninitializedModuleId) {
+    return;
+  }
+  size_t static_offset = SIZE_MAX;
+  if (!g_static_tls_finished) {
+    StaticTlsLayout& layout = __libc_shared_globals()->static_tls_layout;
+    static_offset = layout.reserve_solib_segment(si_tls->segment);
+  }
+  register_tls_module(si, static_offset);
+}
+
+void unregister_soinfo_tls(soinfo* si) {
+  soinfo_tls* si_tls = si->get_tls();
+  if (si_tls == nullptr || si_tls->module_id == kTlsUninitializedModuleId) {
+    return;
+  }
+  return unregister_tls_module(si);
 }
diff --git a/linker/linker_tls.h b/linker/linker_tls.h
index 2f0a57d..87e1f0d 100644
--- a/linker/linker_tls.h
+++ b/linker/linker_tls.h
@@ -28,4 +28,38 @@
 
 #pragma once
 
-void layout_linker_static_tls();
+#include <stdlib.h>
+
+#include "private/bionic_elf_tls.h"
+
+struct TlsModule;
+struct soinfo;
+
+void linker_setup_exe_static_tls(const char* progname);
+void linker_finalize_static_tls();
+
+void register_soinfo_tls(soinfo* si);
+void unregister_soinfo_tls(soinfo* si);
+
+const TlsModule& get_tls_module(size_t module_id);
+
+typedef size_t TlsDescResolverFunc(size_t);
+
+struct TlsDescriptor {
+#if defined(__arm__)
+  size_t arg;
+  TlsDescResolverFunc* func;
+#else
+  TlsDescResolverFunc* func;
+  size_t arg;
+#endif
+};
+
+struct TlsDynamicResolverArg {
+  size_t generation;
+  TlsIndex index;
+};
+
+__LIBC_HIDDEN__ extern "C" size_t tlsdesc_resolver_static(size_t);
+__LIBC_HIDDEN__ extern "C" size_t tlsdesc_resolver_dynamic(size_t);
+__LIBC_HIDDEN__ extern "C" size_t tlsdesc_resolver_unresolved_weak(size_t);
diff --git a/linker/tests/linker_utils_test.cpp b/linker/linker_utils_test.cpp
similarity index 99%
rename from linker/tests/linker_utils_test.cpp
rename to linker/linker_utils_test.cpp
index e406af5..44907da 100644
--- a/linker/tests/linker_utils_test.cpp
+++ b/linker/linker_utils_test.cpp
@@ -32,7 +32,7 @@
 
 #include <gtest/gtest.h>
 
-#include "../linker_utils.h"
+#include "linker_utils.h"
 
 TEST(linker_utils, format_string) {
   std::vector<std::pair<std::string, std::string>> params = {{ "LIB", "lib32"}, { "SDKVER", "42"}};
diff --git a/linker/tests/Android.mk b/linker/tests/Android.mk
deleted file mode 100644
index 9268e31..0000000
--- a/linker/tests/Android.mk
+++ /dev/null
@@ -1,56 +0,0 @@
-#
-# Copyright (C) 2012 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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := linker-unit-tests
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_CFLAGS += -g -Wall -Wextra -Wunused -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../libc/
-
-LOCAL_SRC_FILES := \
-  linker_block_allocator_test.cpp \
-  linker_config_test.cpp \
-  linker_globals.cpp \
-  linked_list_test.cpp \
-  linker_memory_allocator_test.cpp \
-  linker_sleb128_test.cpp \
-  linker_utils_test.cpp \
-  ../linker_allocator.cpp \
-  ../linker_block_allocator.cpp \
-  ../linker_config.cpp \
-  ../linker_utils.cpp \
-
-LOCAL_STATIC_LIBRARIES += libasync_safe libbase liblog
-
-include $(BUILD_NATIVE_TEST)
diff --git a/tests/Android.bp b/tests/Android.bp
index beed07a..b039f00 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -40,7 +40,7 @@
     ],
     stl: "libc++",
     sanitize: {
-        never: true,
+        address: false,
     },
     bootstrap: true,
 }
@@ -95,6 +95,7 @@
         "grp_pwd_file_test.cpp",
         "iconv_test.cpp",
         "ifaddrs_test.cpp",
+        "ifunc_test.cpp",
         "inttypes_test.cpp",
         "iso646_test.c",
         "langinfo_test.cpp",
@@ -218,6 +219,44 @@
     generated_headers: ["generated_android_ids"],
 }
 
+cc_test_library {
+    name: "libBionicElfTlsTests",
+    defaults: ["bionic_tests_defaults"],
+    srcs: [
+        "elftls_test.cpp",
+    ],
+    include_dirs: [
+        "bionic/libc",
+    ],
+    shared: {
+        enabled: false,
+    },
+    cflags: [
+        "-fno-emulated-tls",
+    ],
+}
+
+cc_test_library {
+    name: "libBionicElfTlsLoaderTests",
+    defaults: ["bionic_tests_defaults"],
+    srcs: [
+        "elftls_dl_test.cpp",
+    ],
+    include_dirs: [
+        "bionic/libc",
+    ],
+    static_libs: [
+        "liblog",
+        "libbase",
+    ],
+    shared: {
+        enabled: false,
+    },
+    cflags: [
+        "-fno-emulated-tls",
+    ],
+}
+
 // -----------------------------------------------------------------------------
 // Fortify tests.
 // -----------------------------------------------------------------------------
@@ -306,6 +345,7 @@
     defaults: ["bionic_tests_defaults"],
     whole_static_libs: [
         "libBionicStandardTests",
+        "libBionicElfTlsTests",
         "libfortify1-tests-clang",
         "libfortify2-tests-clang",
     ],
@@ -345,7 +385,7 @@
                 "libdl_test.cpp",
             ],
             static_libs: [
-                "libpagemap",
+                "libmeminfo",
                 "libziparchive",
                 "libLLVMObject",
                 "libLLVMBitReader",
@@ -389,6 +429,7 @@
     whole_static_libs: [
         "libBionicTests",
         "libBionicLoaderTests",
+        "libBionicElfTlsLoaderTests",
     ],
 
     static_libs: [
@@ -423,11 +464,13 @@
                 "libdl_preempt_test_1",
                 "libdl_preempt_test_2",
                 "libdl_test_df_1_global",
+                "libtest_elftls_shared_var",
+                "libtest_elftls_tprel",
             ],
             static_libs: [
                 // The order of these libraries matters, do not shuffle them.
                 "libbase",
-                "libpagemap",
+                "libmeminfo",
                 "libziparchive",
                 "libz",
                 "libutils",
@@ -471,12 +514,14 @@
     required: [
         "cfi_test_helper",
         "cfi_test_helper2",
+        "elftls_dlopen_ie_error_helper",
         "exec_linker_helper",
         "exec_linker_helper_lib",
         "libtest_dt_runpath_a",
         "libtest_dt_runpath_b",
         "libtest_dt_runpath_c",
         "libtest_dt_runpath_x",
+        "libtest_dt_runpath_y",
         "libatest_simple_zip",
         "libcfi-test",
         "libcfi-test-bad",
@@ -490,7 +535,6 @@
         "libdl_preempt_test_1",
         "libdl_preempt_test_2",
         "libdl_test_df_1_global",
-        "libelf-tls-library",
         "libgnu-hash-table-library",
         "libsysv-hash-table-library",
         "libtestshared",
@@ -528,6 +572,13 @@
         "libtest_dlsym_from_this",
         "libtest_dlsym_weak_func",
         "libtest_dt_runpath_d",
+        "libtest_elftls_dynamic",
+        "libtest_elftls_dynamic_filler_1",
+        "libtest_elftls_dynamic_filler_2",
+        "libtest_elftls_dynamic_filler_3",
+        "libtest_elftls_shared_var",
+        "libtest_elftls_shared_var_ie",
+        "libtest_elftls_tprel",
         "libtest_empty",
         "libtest_ifunc_variable_impl",
         "libtest_ifunc_variable",
@@ -615,6 +666,13 @@
         "gtest_preinit_debuggerd.cpp",
         "gtest_globals.cpp",
         "gtest_main.cpp",
+
+        // The Bionic allocator has its own C++ API. It isn't packaged into its
+        // own library, so it can only be tested when it's part of libc.a.
+        "bionic_allocator_test.cpp",
+    ],
+    include_dirs: [
+        "bionic/libc",
     ],
     whole_static_libs: [
         "libBionicTests",
@@ -629,6 +687,8 @@
         "libbase",
         "libdebuggerd_handler",
         "libgtest_isolated",
+        "libtest_elftls_shared_var",
+        "libtest_elftls_tprel",
     ],
 
     static_executable: true,
@@ -662,12 +722,15 @@
     shared_libs: [
         "libdl_preempt_test_1",
         "libdl_preempt_test_2",
-
         "libdl_test_df_1_global",
+        "libtest_elftls_shared_var",
+        "libtest_elftls_tprel",
     ],
 
     whole_static_libs: [
         "libBionicStandardTests",
+        "libBionicElfTlsTests",
+        "libBionicElfTlsLoaderTests",
         "libfortify1-tests-clang",
         "libfortify2-tests-clang",
     ],
diff --git a/tests/Android.build.mk b/tests/Android.build.mk
index 266c7d7..04fc92d 100644
--- a/tests/Android.build.mk
+++ b/tests/Android.build.mk
@@ -35,8 +35,20 @@
     native_tests_var := TARGET_OUT_DATA_NATIVE_TESTS
   endif
 
-  LOCAL_MODULE_PATH_32 := $($(TARGET_2ND_ARCH_VAR_PREFIX)$(native_tests_var))/$($(module)_install_to_native_tests_dir)
-  LOCAL_MODULE_PATH_64 := $($(native_tests_var))/$($(module)_install_to_native_tests_dir)
+  ifneq ($($(module)_install_to_native_tests_dir_32),)
+    tests_dir_32 := $($(module)_install_to_native_tests_dir_32)
+  else
+    tests_dir_32 := $($(module)_install_to_native_tests_dir)
+  endif
+
+  ifneq ($($(module)_install_to_native_tests_dir_64),)
+    tests_dir_64 := $($(module)_install_to_native_tests_dir_64)
+  else
+    tests_dir_64 := $($(module)_install_to_native_tests_dir)
+  endif
+
+  LOCAL_MODULE_PATH_32 := $($(TARGET_2ND_ARCH_VAR_PREFIX)$(native_tests_var))/$(tests_dir_32)
+  LOCAL_MODULE_PATH_64 := $($(native_tests_var))/$(tests_dir_64)
 endif
 endif
 
diff --git a/tests/__aeabi_read_tp_test.cpp b/tests/__aeabi_read_tp_test.cpp
index ab96af9..6974658 100644
--- a/tests/__aeabi_read_tp_test.cpp
+++ b/tests/__aeabi_read_tp_test.cpp
@@ -32,7 +32,12 @@
 
 #if defined(__arm__)
 extern "C" void* __aeabi_read_tp();
-TEST(aeabi, read_tp) {
-  ASSERT_EQ(__aeabi_read_tp(), static_cast<void*>(__get_tls()));
-}
 #endif
+
+TEST(aeabi, read_tp) {
+#if defined(__arm__)
+  ASSERT_EQ(__aeabi_read_tp(), static_cast<void*>(__get_tls()));
+#else
+  GTEST_LOG_(INFO) << "__aeabi_read_tp is only available on arm32.\n";
+#endif
+}
diff --git a/linker/tests/linker_memory_allocator_test.cpp b/tests/bionic_allocator_test.cpp
similarity index 75%
rename from linker/tests/linker_memory_allocator_test.cpp
rename to tests/bionic_allocator_test.cpp
index c284eaa..f710907 100644
--- a/linker/tests/linker_memory_allocator_test.cpp
+++ b/tests/bionic_allocator_test.cpp
@@ -32,7 +32,7 @@
 
 #include <gtest/gtest.h>
 
-#include "../linker_allocator.h"
+#include "private/bionic_allocator.h"
 
 #include <unistd.h>
 
@@ -61,20 +61,20 @@
 
 static size_t kPageSize = sysconf(_SC_PAGE_SIZE);
 
-TEST(linker_memory, test_alloc_0) {
-  LinkerMemoryAllocator allocator;
+TEST(bionic_allocator, test_alloc_0) {
+  BionicAllocator allocator;
   void* ptr = allocator.alloc(0);
   ASSERT_TRUE(ptr != nullptr);
   allocator.free(ptr);
 }
 
-TEST(linker_memory, test_free_nullptr) {
-  LinkerMemoryAllocator allocator;
+TEST(bionic_allocator, test_free_nullptr) {
+  BionicAllocator allocator;
   allocator.free(nullptr);
 }
 
-TEST(linker_memory, test_realloc) {
-  LinkerMemoryAllocator allocator;
+TEST(bionic_allocator, test_realloc) {
+  BionicAllocator allocator;
   uint32_t* array = reinterpret_cast<uint32_t*>(allocator.alloc(512));
   const size_t array_size = 512 / sizeof(uint32_t);
 
@@ -127,8 +127,8 @@
   ASSERT_EQ(nullptr, allocator.realloc(reallocated_ptr, 0));
 }
 
-TEST(linker_memory, test_small_smoke) {
-  LinkerMemoryAllocator allocator;
+TEST(bionic_allocator, test_small_smoke) {
+  BionicAllocator allocator;
 
   uint8_t zeros[16];
   memset(zeros, 0, sizeof(zeros));
@@ -150,8 +150,8 @@
   allocator.free(ptr2);
 }
 
-TEST(linker_memory, test_huge_smoke) {
-  LinkerMemoryAllocator allocator;
+TEST(bionic_allocator, test_huge_smoke) {
+  BionicAllocator allocator;
 
   // this should trigger proxy-to-mmap
   test_struct_huge* ptr1 =
@@ -170,8 +170,8 @@
   allocator.free(ptr1);
 }
 
-TEST(linker_memory, test_large) {
-  LinkerMemoryAllocator allocator;
+TEST(bionic_allocator, test_large) {
+  BionicAllocator allocator;
 
   test_struct_large* ptr1 =
       reinterpret_cast<test_struct_large*>(allocator.alloc(sizeof(test_struct_large)));
@@ -212,4 +212,49 @@
   allocator.free(ptr_to_free);
 }
 
+TEST(bionic_allocator, test_memalign_small) {
+  BionicAllocator allocator;
+  void* ptr;
 
+  // simple case
+  ptr = allocator.memalign(0x100, 0x100);
+  ASSERT_TRUE(ptr != nullptr);
+  ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr) % 0x100);
+  allocator.free(ptr);
+
+  // small objects are automatically aligned to their size.
+  ptr = allocator.alloc(0x200);
+  ASSERT_TRUE(ptr != nullptr);
+  ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr) % 0x200);
+  allocator.free(ptr);
+
+  // the size (0x10) is bumped up to the alignment (0x100)
+  ptr = allocator.memalign(0x100, 0x10);
+  ASSERT_TRUE(ptr != nullptr);
+  ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr) % 0x100);
+  allocator.free(ptr);
+}
+
+TEST(bionic_allocator, test_memalign_large) {
+  BionicAllocator allocator;
+  void* ptr;
+
+  // a large object with alignment < PAGE_SIZE
+  ptr = allocator.memalign(0x100, 0x2000);
+  ASSERT_TRUE(ptr != nullptr);
+  ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr) % 0x100);
+  allocator.free(ptr);
+
+  // a large object with alignment == PAGE_SIZE
+  ptr = allocator.memalign(0x1000, 0x2000);
+  ASSERT_TRUE(ptr != nullptr);
+  ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr) % 0x1000);
+  allocator.free(ptr);
+
+  // A large object with alignment > PAGE_SIZE is only guaranteed to have page
+  // alignment.
+  ptr = allocator.memalign(0x2000, 0x4000);
+  ASSERT_TRUE(ptr != nullptr);
+  ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr) % 0x1000);
+  allocator.free(ptr);
+}
diff --git a/tests/buffer_tests.cpp b/tests/buffer_tests.cpp
index 7563448..fb0b6d8 100644
--- a/tests/buffer_tests.cpp
+++ b/tests/buffer_tests.cpp
@@ -20,6 +20,7 @@
 
 #include <gtest/gtest.h>
 #include "buffer_tests.h"
+#include "utils.h"
 
 // For the comparison buffer tests, the maximum length to test for the
 // miscompare checks.
@@ -227,16 +228,11 @@
   }
 }
 
-// Malloc can return a tagged pointer, which is not accepted in mm system calls like mprotect.
-// Clear top 8 bits of the address on 64-bit platforms.
+// Malloc can return a tagged pointer, which is not accepted in mm system calls like mprotect
+// in the preliminary version of the syscall tagging support in the current Pixel 2 kernel.
+// Note: the final version of the kernel patchset may relax this requirement.
 static int MprotectHeap(void* addr, size_t len, int prot) {
-#if defined(__LP64__)
-  constexpr uintptr_t mask = (static_cast<uintptr_t>(1) << 56) - 1;
-  void* untagged_addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & mask);
-#else
-  void* untagged_addr = addr;
-#endif
-  return mprotect(untagged_addr, len, prot);
+  return mprotect(untag_address(addr), len, prot);
 }
 
 void RunSingleBufferAlignTest(
diff --git a/tests/dl_test.cpp b/tests/dl_test.cpp
index aea92b4..468ce3d 100644
--- a/tests/dl_test.cpp
+++ b/tests/dl_test.cpp
@@ -138,7 +138,7 @@
 
 TEST(dl, preinit_system_calls) {
 #if defined(__BIONIC__)
-  SKIP_WITH_HWASAN; // hwasan not initialized in preinit_array
+  SKIP_WITH_HWASAN; // hwasan not initialized in preinit_array, b/124007027
   std::string helper = GetTestlibRoot() +
       "/preinit_syscall_test_helper/preinit_syscall_test_helper";
   chmod(helper.c_str(), 0755); // TODO: "x" lost in CTS, b/34945607
@@ -150,6 +150,7 @@
 
 TEST(dl, preinit_getauxval) {
 #if defined(__BIONIC__)
+  SKIP_WITH_HWASAN; // hwasan not initialized in preinit_array, b/124007027
   std::string helper = GetTestlibRoot() +
       "/preinit_getauxval_test_helper/preinit_getauxval_test_helper";
   chmod(helper.c_str(), 0755); // TODO: "x" lost in CTS, b/34945607
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 34013a7..aff5e37 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -36,7 +36,7 @@
 #include <sys/vfs.h>
 #include <sys/wait.h>
 
-#include <pagemap/pagemap.h>
+#include <meminfo/procmeminfo.h>
 #include <ziparchive/zip_archive.h>
 
 #include "gtest_globals.h"
@@ -232,6 +232,12 @@
   dlclose(handle);
 }
 
+TEST(dlfcn, dlopen_from_nullptr_android_api_level) {
+  // Regression test for http://b/123972211. Testing dlopen(nullptr) when target sdk is P
+  android_set_application_target_sdk_version(__ANDROID_API_P__);
+  ASSERT_TRUE(dlopen(nullptr, RTLD_NOW) != nullptr);
+}
+
 TEST(dlfcn, dlopen_from_zip_absolute_path) {
   const std::string lib_zip_path = "/libdlext_test_zip/libdlext_test_zip_zipaligned.zip";
   const std::string lib_path = GetTestlibRoot() + lib_zip_path;
@@ -488,33 +494,23 @@
 
 void GetPss(bool shared_relro, const char* lib, const char* relro_file, pid_t pid,
             size_t* total_pss) {
-  pm_kernel_t* kernel;
-  ASSERT_EQ(0, pm_kernel_create(&kernel));
-
-  pm_process_t* process;
-  ASSERT_EQ(0, pm_process_create(kernel, pid, &process));
-
-  pm_map_t** maps;
-  size_t num_maps;
-  ASSERT_EQ(0, pm_process_maps(process, &maps, &num_maps));
+  android::meminfo::ProcMemInfo proc_mem(pid);
+  const std::vector<android::meminfo::Vma>& maps = proc_mem.Maps();
+  ASSERT_GT(maps.size(), 0UL);
 
   // Calculate total PSS of the library.
   *total_pss = 0;
   bool saw_relro_file = false;
-  for (size_t i = 0; i < num_maps; ++i) {
-    if (android::base::EndsWith(maps[i]->name, lib) || strcmp(maps[i]->name, relro_file) == 0) {
-      if (strcmp(maps[i]->name, relro_file) == 0) saw_relro_file = true;
+  for (auto& vma : maps) {
+    if (android::base::EndsWith(vma.name, lib) || (vma.name == relro_file)) {
+      if (vma.name == relro_file) {
+          saw_relro_file = true;
+      }
 
-      pm_memusage_t usage;
-      ASSERT_EQ(0, pm_map_usage(maps[i], &usage));
-      *total_pss += usage.pss;
+      *total_pss += vma.usage.pss;
     }
   }
 
-  free(maps);
-  pm_process_destroy(process);
-  pm_kernel_destroy(kernel);
-
   if (shared_relro) ASSERT_TRUE(saw_relro_file);
 }
 
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 5f48e67..8a3b6f3 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -1082,13 +1082,6 @@
   ASSERT_SUBSTR("libsysv-hash-table-library.so", dlinfo.dli_fname);
 }
 
-TEST(dlfcn, dlopen_library_with_ELF_TLS) {
-  dlerror(); // Clear any pending errors.
-  void* handle = dlopen("libelf-tls-library.so", RTLD_NOW);
-  ASSERT_TRUE(handle == nullptr);
-  ASSERT_SUBSTR("unsupported ELF TLS", dlerror());
-}
-
 TEST(dlfcn, dlopen_bad_flags) {
   dlerror(); // Clear any pending errors.
   void* handle;
diff --git a/tests/elftls_dl_test.cpp b/tests/elftls_dl_test.cpp
new file mode 100644
index 0000000..36bdc3b
--- /dev/null
+++ b/tests/elftls_dl_test.cpp
@@ -0,0 +1,271 @@
+/*
+ * 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 <dlfcn.h>
+#include <gtest/gtest.h>
+
+#include <thread>
+
+#include "gtest_globals.h"
+#include "private/__get_tls.h"
+#include "utils.h"
+
+#if defined(__BIONIC__)
+#include "bionic/pthread_internal.h"
+#endif
+
+// Access libtest_elftls_shared_var.so's TLS variable using an IE access.
+__attribute__((tls_model("initial-exec"))) extern "C" __thread int elftls_shared_var;
+
+TEST(elftls_dl, dlopen_shared_var_ie) {
+  // libtest_elftls_shared_var_ie.so can be dlopen'ed, even though it contains a
+  // TLS IE access, because its IE access references a TLS variable from
+  // libtest_elftls_shared_var.so, which is DT_NEEDED by the executable. This
+  // pattern appears in sanitizers, which use TLS IE instrumentation in shared
+  // objects to access special variables exported from the executable or from a
+  // preloaded solib.
+  void* lib = dlopen("libtest_elftls_shared_var_ie.so", RTLD_LOCAL | RTLD_NOW);
+  ASSERT_NE(nullptr, lib);
+
+  auto bump_shared_var = reinterpret_cast<int(*)()>(dlsym(lib, "bump_shared_var"));
+  ASSERT_NE(nullptr, bump_shared_var);
+
+  ASSERT_EQ(21, ++elftls_shared_var);
+  ASSERT_EQ(22, bump_shared_var());
+
+  std::thread([bump_shared_var] {
+    ASSERT_EQ(21, ++elftls_shared_var);
+    ASSERT_EQ(22, bump_shared_var());
+  }).join();
+}
+
+TEST(elftls_dl, dlopen_ie_error) {
+  std::string helper = GetTestlibRoot() +
+      "/elftls_dlopen_ie_error_helper/elftls_dlopen_ie_error_helper";
+  std::string src_path = GetTestlibRoot() + "/libtest_elftls_shared_var_ie.so";
+  std::string dst_path = GetTestlibRoot() + "/libtest_elftls_shared_var.so";
+#if defined(__BIONIC__)
+  std::string error =
+      "dlerror: dlopen failed: TLS symbol \"elftls_shared_var\" in dlopened \"" + dst_path + "\" " +
+      "referenced from \"" + src_path + "\" using IE access model\n";
+#else
+  // glibc will reserve some surplus static TLS memory, allowing this test to pass.
+  std::string error = "success\n";
+#endif
+
+  chmod(helper.c_str(), 0755); // TODO: "x" lost in CTS, b/34945607
+  ExecTestHelper eth;
+  eth.SetArgs({ helper.c_str(), nullptr });
+  eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, error.c_str());
+}
+
+// Use a GD access (__tls_get_addr or TLSDESC) to modify a variable in static
+// TLS memory.
+TEST(elftls_dl, access_static_tls) {
+  void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
+  ASSERT_NE(nullptr, lib);
+
+  auto bump_shared_var = reinterpret_cast<int(*)()>(dlsym(lib, "bump_shared_var"));
+  ASSERT_NE(nullptr, bump_shared_var);
+
+  ASSERT_EQ(21, ++elftls_shared_var);
+  ASSERT_EQ(22, bump_shared_var());
+
+  std::thread([bump_shared_var] {
+    ASSERT_EQ(21, ++elftls_shared_var);
+    ASSERT_EQ(22, bump_shared_var());
+  }).join();
+}
+
+TEST(elftls_dl, bump_local_vars) {
+  void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
+  ASSERT_NE(nullptr, lib);
+
+  auto bump_local_vars = reinterpret_cast<int(*)()>(dlsym(lib, "bump_local_vars"));
+  ASSERT_NE(nullptr, bump_local_vars);
+
+  ASSERT_EQ(42, bump_local_vars());
+  std::thread([bump_local_vars] {
+    ASSERT_EQ(42, bump_local_vars());
+  }).join();
+}
+
+extern "C" int* missing_weak_tls_addr();
+
+// The Bionic linker resolves a TPREL relocation to an unresolved weak TLS
+// symbol to 0, which is added to the thread pointer. N.B.: A TPREL relocation
+// in a static executable is resolved by the static linker instead, and static
+// linker behavior varies (especially with bfd and gold). See
+// https://bugs.llvm.org/show_bug.cgi?id=40570.
+TEST(elftls_dl, tprel_missing_weak) {
+  ASSERT_EQ(static_cast<void*>(__get_tls()), missing_weak_tls_addr());
+  std::thread([] {
+    ASSERT_EQ(static_cast<void*>(__get_tls()), missing_weak_tls_addr());
+  }).join();
+}
+
+// The behavior of accessing an unresolved weak TLS symbol using a dynamic TLS
+// relocation depends on which kind of implementation the target uses. With
+// TLSDESC, the result is NULL. With __tls_get_addr, the result is the
+// generation count (or maybe undefined behavior)? This test only tests TLSDESC.
+TEST(elftls_dl, tlsdesc_missing_weak) {
+#if defined(__aarch64__)
+  void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
+  ASSERT_NE(nullptr, lib);
+
+  auto missing_weak_dyn_tls_addr = reinterpret_cast<int*(*)()>(dlsym(lib, "missing_weak_dyn_tls_addr"));
+  ASSERT_NE(nullptr, missing_weak_dyn_tls_addr);
+
+  ASSERT_EQ(nullptr, missing_weak_dyn_tls_addr());
+  std::thread([missing_weak_dyn_tls_addr] {
+    ASSERT_EQ(nullptr, missing_weak_dyn_tls_addr());
+  }).join();
+#else
+  GTEST_LOG_(INFO) << "This test is only run on TLSDESC-based targets.\n";
+#endif
+}
+
+TEST(elftls_dl, dtv_resize) {
+#if defined(__BIONIC__)
+#define LOAD_LIB(soname) ({                           \
+    auto lib = dlopen(soname, RTLD_LOCAL | RTLD_NOW); \
+    ASSERT_NE(nullptr, lib);                          \
+    reinterpret_cast<int(*)()>(dlsym(lib, "bump"));   \
+  })
+
+  auto dtv = []() -> TlsDtv* { return __get_tcb_dtv(__get_bionic_tcb()); };
+
+  static_assert(sizeof(TlsDtv) == 3 * sizeof(void*),
+                "This test assumes that the Dtv has a 3-word header");
+
+  // Initially there are 3 modules:
+  //  - the main test executable
+  //  - libtest_elftls_shared_var
+  //  - libtest_elftls_tprel
+
+  // The initial DTV is an empty DTV with no generation and a size of 0.
+  TlsDtv* zero_dtv = dtv();
+  ASSERT_EQ(0u, zero_dtv->count);
+  ASSERT_EQ(nullptr, zero_dtv->next);
+  ASSERT_EQ(kTlsGenerationNone, zero_dtv->generation);
+
+  // Load the fourth module.
+  auto func1 = LOAD_LIB("libtest_elftls_dynamic_filler_1.so");
+  ASSERT_EQ(101, func1());
+
+  // After loading one module, the DTV should be initialized to the next
+  // power-of-2 size (including the header).
+  TlsDtv* initial_dtv = dtv();
+  ASSERT_EQ(5u, initial_dtv->count);
+  ASSERT_EQ(zero_dtv, initial_dtv->next);
+  ASSERT_LT(0u, initial_dtv->generation);
+
+  // Load module 5.
+  auto func2 = LOAD_LIB("libtest_elftls_dynamic_filler_2.so");
+  ASSERT_EQ(102, func1());
+  ASSERT_EQ(201, func2());
+  ASSERT_EQ(initial_dtv, dtv());
+  ASSERT_EQ(5u, initial_dtv->count);
+
+  // Load module 6.
+  auto func3 = LOAD_LIB("libtest_elftls_dynamic_filler_3.so");
+  ASSERT_EQ(103, func1());
+  ASSERT_EQ(202, func2());
+
+#if defined(__aarch64__)
+  // The arm64 TLSDESC resolver doesn't update the DTV if it is new enough for
+  // the given access.
+  ASSERT_EQ(5u, dtv()->count);
+#else
+  // __tls_get_addr updates the DTV anytime the generation counter changes.
+  ASSERT_EQ(13u, dtv()->count);
+#endif
+
+  ASSERT_EQ(301, func3());
+
+  TlsDtv* new_dtv = dtv();
+  ASSERT_EQ(13u, new_dtv->count);
+  ASSERT_NE(initial_dtv, new_dtv);
+  ASSERT_EQ(initial_dtv, new_dtv->next);
+
+#undef LOAD_LIB
+#else
+  GTEST_LOG_(INFO) << "This test is skipped for glibc because it tests Bionic internals.";
+#endif
+}
+
+// Verify that variables are reset to their initial values after the library
+// containing them is closed.
+TEST(elftls_dl, dlclose_resets_values) {
+  for (int round = 0; round < 2; ++round) {
+    void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
+    ASSERT_NE(nullptr, lib);
+
+    auto bump_local_vars = reinterpret_cast<int(*)()>(dlsym(lib, "bump_local_vars"));
+    ASSERT_NE(nullptr, bump_local_vars);
+
+    ASSERT_EQ(42, bump_local_vars());
+    ASSERT_EQ(44, bump_local_vars());
+
+    ASSERT_EQ(0, dlclose(lib));
+  }
+}
+
+// Calling dlclose should remove the entry for the solib from the global list of
+// ELF TLS modules. Test that repeatedly loading and unloading a library doesn't
+// increase the DTV size.
+TEST(elftls_dl, dlclose_removes_entry) {
+#if defined(__BIONIC__)
+  auto dtv = []() -> TlsDtv* { return __get_tcb_dtv(__get_bionic_tcb()); };
+
+  bool first = true;
+  size_t count = 0;
+
+  // Use a large number of rounds in case the DTV is initially larger than
+  // expected.
+  for (int round = 0; round < 32; ++round) {
+    void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
+    ASSERT_NE(nullptr, lib);
+
+    auto bump_local_vars = reinterpret_cast<int(*)()>(dlsym(lib, "bump_local_vars"));
+    ASSERT_NE(nullptr, bump_local_vars);
+
+    ASSERT_EQ(42, bump_local_vars());
+    if (first) {
+      first = false;
+      count = dtv()->count;
+    } else {
+      ASSERT_EQ(count, dtv()->count);
+    }
+
+    dlclose(lib);
+  }
+#else
+  GTEST_LOG_(INFO) << "This test is skipped for glibc because it tests Bionic internals.";
+#endif
+}
diff --git a/tests/elftls_test.cpp b/tests/elftls_test.cpp
new file mode 100644
index 0000000..7c072b6
--- /dev/null
+++ b/tests/elftls_test.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <thread>
+
+// Specify the LE access model explicitly. This file is compiled into the
+// bionic-unit-tests executable, but the compiler sees an -fpic object file
+// output into a static library, so it defaults to dynamic TLS accesses.
+
+// This variable will be zero-initialized (.tbss)
+__attribute__((tls_model("local-exec"))) static __thread int tlsvar_le_zero;
+
+// This variable will have an initializer (.tdata)
+__attribute__((tls_model("local-exec"))) static __thread int tlsvar_le_init = 10;
+
+// Access libtest_elftls_shared_var's TLS variable using an IE access.
+__attribute__((tls_model("initial-exec"))) extern "C" __thread int elftls_shared_var;
+
+TEST(elftls, basic_le) {
+  // Check the variables on the main thread.
+  ASSERT_EQ(11, ++tlsvar_le_init);
+  ASSERT_EQ(1, ++tlsvar_le_zero);
+
+  // Check variables on a new thread.
+  std::thread([] {
+    ASSERT_EQ(11, ++tlsvar_le_init);
+    ASSERT_EQ(1, ++tlsvar_le_zero);
+  }).join();
+}
+
+TEST(elftls, shared_ie) {
+  ASSERT_EQ(21, ++elftls_shared_var);
+  std::thread([] {
+    ASSERT_EQ(21, ++elftls_shared_var);
+  }).join();
+}
+
+extern "C" int bump_static_tls_var_1();
+extern "C" int bump_static_tls_var_2();
+
+TEST(elftls, tprel_addend) {
+  ASSERT_EQ(4, bump_static_tls_var_1());
+  ASSERT_EQ(8, bump_static_tls_var_2());
+  std::thread([] {
+    ASSERT_EQ(4, bump_static_tls_var_1());
+    ASSERT_EQ(8, bump_static_tls_var_2());
+  }).join();
+}
+
+// Because this C++ source file is built with -fpic, the compiler will access
+// this variable using a GD model. Typically, the static linker will relax the
+// GD to LE, but the arm32 linker doesn't do TLS relaxations, so we can test
+// calling __tls_get_addr in a static executable. The static linker knows that
+// the main executable's TlsIndex::module_id is 1 and writes that into the GOT.
+__thread int tlsvar_general = 30;
+
+TEST(elftls, general) {
+  ASSERT_EQ(31, ++tlsvar_general);
+  std::thread([] {
+    ASSERT_EQ(31, ++tlsvar_general);
+  }).join();
+}
diff --git a/tests/getauxval_test.cpp b/tests/getauxval_test.cpp
index 63bc963..aa21817 100644
--- a/tests/getauxval_test.cpp
+++ b/tests/getauxval_test.cpp
@@ -14,29 +14,14 @@
  * limitations under the License.
  */
 
+#include <sys/auxv.h>
+
 #include <errno.h>
 #include <sys/cdefs.h>
 #include <sys/utsname.h>
 #include <gtest/gtest.h>
 
-// getauxval() was only added as of glibc version 2.16.
-// See: http://lwn.net/Articles/519085/
-// Don't try to compile this code on older glibc versions.
-
-#if defined(__BIONIC__)
-  #define GETAUXVAL_CAN_COMPILE 1
-#elif defined(__GLIBC_PREREQ)
-  #if __GLIBC_PREREQ(2, 16)
-    #define GETAUXVAL_CAN_COMPILE 1
-  #endif
-#endif
-
-#if defined(GETAUXVAL_CAN_COMPILE)
-#include <sys/auxv.h>
-#endif
-
 TEST(getauxval, expected_values) {
-#if defined(GETAUXVAL_CAN_COMPILE)
   ASSERT_EQ(0UL, getauxval(AT_SECURE));
   ASSERT_EQ(getuid(), getauxval(AT_UID));
   ASSERT_EQ(geteuid(), getauxval(AT_EUID));
@@ -48,19 +33,12 @@
   ASSERT_NE(0UL, getauxval(AT_PHNUM));
   ASSERT_NE(0UL, getauxval(AT_ENTRY));
   ASSERT_NE(0UL, getauxval(AT_PAGESZ));
-#else
-  GTEST_LOG_(INFO) << "This test requires a C library with getauxval.\n";
-#endif
 }
 
 TEST(getauxval, unexpected_values) {
-#if defined(GETAUXVAL_CAN_COMPILE)
   errno = 0;
   ASSERT_EQ(0UL, getauxval(0xdeadbeef));
   ASSERT_EQ(ENOENT, errno);
-#else
-  GTEST_LOG_(INFO) << "This test requires a C library with getauxval.\n";
-#endif
 }
 
 TEST(getauxval, arm_has_AT_HWCAP2) {
diff --git a/tests/grp_pwd_test.cpp b/tests/grp_pwd_test.cpp
index ca34205..eb8fe2a 100644
--- a/tests/grp_pwd_test.cpp
+++ b/tests/grp_pwd_test.cpp
@@ -34,6 +34,7 @@
 #include <private/android_filesystem_config.h>
 
 #if defined(__BIONIC__)
+#include <android/api-level.h>
 #include <android-base/properties.h>
 #endif
 
@@ -198,7 +199,7 @@
 }
 
 TEST(pwd, getpwnam_app_id_u0_i1) {
-  check_get_passwd("u0_i1", 99001, TYPE_APP);
+  check_get_passwd("u0_i1", 90001, TYPE_APP);
 }
 
 TEST(pwd, getpwnam_app_id_u1_root) {
@@ -218,9 +219,8 @@
 }
 
 TEST(pwd, getpwnam_app_id_u1_i0) {
-  check_get_passwd("u1_i0", 199000, TYPE_APP);
+  check_get_passwd("u1_i0", 190000, TYPE_APP);
 }
-
 #if defined(__BIONIC__)
 template <typename T>
 static void expect_ids(const T& ids) {
@@ -248,11 +248,9 @@
   expect_range(AID_SHARED_GID_START, AID_SHARED_GID_END);
   expect_range(AID_ISOLATED_START, AID_ISOLATED_END);
 
-  // Upgrading devices launched before API level 28 may not comply with the below check.
-  // Due to the difficulty in changing uids after launch, it is waived for these devices.
-  // Also grant this check for device launched with 28(P) to give the vendor time to
-  // adopt the AID scheme.
-  if (android::base::GetIntProperty("ro.product.first_api_level", 0) <= 28) {
+  // TODO(73062966): We still don't have a good way to create vendor AIDs in the system or other
+  // non-vendor partitions, therefore we keep this check disabled.
+  if (android::base::GetIntProperty("ro.product.first_api_level", 0) <= __ANDROID_API_Q__) {
     return;
   }
 
@@ -464,7 +462,7 @@
 }
 
 TEST(grp, getgrnam_app_id_u0_i1) {
-  check_get_group("u0_i1", 99001);
+  check_get_group("u0_i1", 90001);
 }
 
 TEST(grp, getgrnam_app_id_u1_root) {
@@ -484,7 +482,7 @@
 }
 
 TEST(grp, getgrnam_app_id_u1_i0) {
-  check_get_group("u1_i0", 199000);
+  check_get_group("u1_i0", 190000);
 }
 
 TEST(grp, getgrnam_r_reentrancy) {
diff --git a/tests/headers/posix/signal_h.c b/tests/headers/posix/signal_h.c
index 661b55e..c2e544e 100644
--- a/tests/headers/posix/signal_h.c
+++ b/tests/headers/posix/signal_h.c
@@ -53,7 +53,7 @@
   STRUCT_MEMBER(struct sigevent, int, sigev_signo);
   STRUCT_MEMBER(struct sigevent, union sigval, sigev_value);
   STRUCT_MEMBER_FUNCTION_POINTER(struct sigevent, void (*f)(union sigval), sigev_notify_function);
-#if defined(__BIONIC__) || defined(__GLIBC__)
+#if defined(__BIONIC__)
   STRUCT_MEMBER(struct sigevent, void*, sigev_notify_attributes);
 #else
   STRUCT_MEMBER(struct sigevent, pthread_attr_t*, sigev_notify_attributes);
diff --git a/tests/libs/elf_tls_test_library.cpp b/tests/ifunc_test.cpp
similarity index 67%
rename from tests/libs/elf_tls_test_library.cpp
rename to tests/ifunc_test.cpp
index 56d0171..7ab5899 100644
--- a/tests/libs/elf_tls_test_library.cpp
+++ b/tests/ifunc_test.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -14,6 +14,18 @@
  * limitations under the License.
  */
 
-thread_local int elf_tls_variable;
+#include <gtest/gtest.h>
 
-extern "C" int* get() { return &elf_tls_variable; }
+int ret42() {
+  return 42;
+}
+
+extern "C" void* resolver() {
+  return (void*)ret42;
+}
+
+int ifunc() __attribute__((ifunc("resolver")));
+
+TEST(ifunc, function) {
+  ASSERT_EQ(42, ifunc());
+}
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index 79c9a06..d58b6b8 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -40,14 +40,64 @@
 }
 
 // -----------------------------------------------------------------------------
-// Library to test ELF TLS
+// Libraries and helper binaries for ELF TLS
 // -----------------------------------------------------------------------------
 cc_test_library {
-    name: "libelf-tls-library",
+    name: "libtest_elftls_shared_var",
     defaults: ["bionic_testlib_defaults"],
-    srcs: ["elf_tls_test_library.cpp"],
+    srcs: ["elftls_shared_var.cpp"],
     cflags: ["-fno-emulated-tls"],
-    allow_undefined_symbols: true, // __tls_get_addr is undefined.
+}
+
+cc_test_library {
+    name: "libtest_elftls_shared_var_ie",
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["elftls_shared_var_ie.cpp"],
+    cflags: ["-fno-emulated-tls"],
+    shared_libs: ["libtest_elftls_shared_var"],
+}
+
+cc_test_library {
+    name: "libtest_elftls_tprel",
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["elftls_tprel.cpp"],
+    cflags: ["-fno-emulated-tls"],
+}
+
+cc_test {
+    name: "elftls_dlopen_ie_error_helper",
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["elftls_dlopen_ie_error_helper.cpp"],
+    ldflags: ["-Wl,--rpath,${ORIGIN}/.."],
+}
+
+cc_test_library {
+    name: "libtest_elftls_dynamic",
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["elftls_dynamic.cpp"],
+    cflags: ["-fno-emulated-tls"],
+    shared_libs: ["libtest_elftls_shared_var"],
+}
+
+cc_test_library {
+    name: "libtest_elftls_dynamic_filler_1",
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["elftls_dynamic_filler.cpp"],
+    cflags: ["-fno-emulated-tls", "-DTLS_FILLER=100"],
+}
+
+cc_test_library {
+    name: "libtest_elftls_dynamic_filler_2",
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["elftls_dynamic_filler.cpp"],
+    cflags: ["-fno-emulated-tls", "-DTLS_FILLER=200"],
+}
+
+cc_test_library {
+    name: "libtest_elftls_dynamic_filler_3",
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["elftls_dynamic_filler.cpp"],
+    cflags: ["-fno-emulated-tls", "-DTLS_FILLER=300"],
 }
 
 // -----------------------------------------------------------------------------
@@ -237,6 +287,10 @@
         "libnstest_public",
         "libnstest_private",
     ],
+    // The dlext.ns_anonymous test copies the loaded segments of this shared
+    // object into a new mapping, so every segment must be readable. Turn off
+    // eXecute-Only-Memory. See http://b/123034666.
+    xom: false,
 }
 
 cc_test_library {
diff --git a/tests/libs/Android.build.dlext_testzip.mk b/tests/libs/Android.build.dlext_testzip.mk
index 19fd64b..8775c29 100644
--- a/tests/libs/Android.build.dlext_testzip.mk
+++ b/tests/libs/Android.build.dlext_testzip.mk
@@ -59,21 +59,38 @@
 lib_b := $(call intermediates-dir-for,SHARED_LIBRARIES,libtest_dt_runpath_b,,,$(bionic_2nd_arch_prefix))/libtest_dt_runpath_b.so
 lib_c := $(call intermediates-dir-for,SHARED_LIBRARIES,libtest_dt_runpath_c,,,$(bionic_2nd_arch_prefix))/libtest_dt_runpath_c.so
 lib_x := $(call intermediates-dir-for,SHARED_LIBRARIES,libtest_dt_runpath_x,,,$(bionic_2nd_arch_prefix))/libtest_dt_runpath_x.so
+lib_y := $(call intermediates-dir-for,SHARED_LIBRARIES,libtest_dt_runpath_y,,,$(bionic_2nd_arch_prefix))/libtest_dt_runpath_y.so
 
 $(LOCAL_BUILT_MODULE) : PRIVATE_LIB_D := $(lib_d)
 $(LOCAL_BUILT_MODULE) : PRIVATE_LIB_A := $(lib_a)
 $(LOCAL_BUILT_MODULE) : PRIVATE_LIB_B := $(lib_b)
 $(LOCAL_BUILT_MODULE) : PRIVATE_LIB_C := $(lib_c)
 $(LOCAL_BUILT_MODULE) : PRIVATE_LIB_X := $(lib_x)
-$(LOCAL_BUILT_MODULE) : $(lib_d) $(lib_a) $(lib_b) $(lib_c) $(lib_x) $(BIONIC_TESTS_ZIPALIGN)
+$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_Y := $(lib_y)
+ifeq ($(TARGET_IS_64_BIT),true)
+  ifeq ($(TARGET_TRANSLATE_2ND_ARCH),true)
+    $(LOCAL_BUILT_MODULE) : PRIVATE_LIB_OR_LIB64 := $(if $(LOCAL_2ND_ARCH_VAR_PREFIX),lib/$(TARGET_2ND_ARCH),lib64)
+  else
+    $(LOCAL_BUILT_MODULE) : PRIVATE_LIB_OR_LIB64 := $(if $(LOCAL_2ND_ARCH_VAR_PREFIX),lib,lib64)
+  endif
+else
+  ifeq ($(TARGET_TRANSLATE_2ND_ARCH),true)
+    $(LOCAL_BUILT_MODULE) : PRIVATE_LIB_OR_LIB64 := $(if $(LOCAL_2ND_ARCH_VAR_PREFIX),lib/$(TARGET_2ND_ARCH),lib)
+  else
+    $(LOCAL_BUILT_MODULE) : PRIVATE_LIB_OR_LIB64 := lib
+  endif
+endif
+$(LOCAL_BUILT_MODULE) : $(lib_d) $(lib_a) $(lib_b) $(lib_c) $(lib_x) $(lib_y) $(BIONIC_TESTS_ZIPALIGN)
 	@echo "Aligning zip: $@"
 	$(hide) rm -rf $@.unaligned $@ $(dir $@)/zipdir && mkdir -p $(dir $@)/zipdir/libdir && \
-    mkdir -p $(dir $@)/zipdir/libdir/dt_runpath_a && mkdir -p $(dir $@)/zipdir/libdir/dt_runpath_b_c_x
+    mkdir -p $(dir $@)/zipdir/libdir/dt_runpath_a && mkdir -p $(dir $@)/zipdir/libdir/dt_runpath_b_c_x && \
+    mkdir -p $(dir $@)/zipdir/libdir/dt_runpath_y/$(PRIVATE_LIB_OR_LIB64)
 	$(hide) cp $(PRIVATE_LIB_D) $(dir $@)/zipdir/libdir
 	$(hide) cp $(PRIVATE_LIB_A) $(dir $@)/zipdir/libdir/dt_runpath_a
 	$(hide) cp $(PRIVATE_LIB_B) $(dir $@)/zipdir/libdir/dt_runpath_b_c_x
 	$(hide) cp $(PRIVATE_LIB_C) $(dir $@)/zipdir/libdir/dt_runpath_b_c_x
 	$(hide) cp $(PRIVATE_LIB_X) $(dir $@)/zipdir/libdir/dt_runpath_b_c_x
+	$(hide) cp $(PRIVATE_LIB_Y) $(dir $@)/zipdir/libdir/dt_runpath_y/$(PRIVATE_LIB_OR_LIB64)
 	$(hide) touch $(dir $@)/zipdir/empty_file.txt
 	$(hide) (cd $(dir $@)/zipdir && zip -qrD0 ../$(notdir $@).unaligned .)
 	$(hide) $(BIONIC_TESTS_ZIPALIGN) 4096 $@.unaligned $@
diff --git a/tests/libs/Android.build.dt_runpath.mk b/tests/libs/Android.build.dt_runpath.mk
index a3fcac5..b0d8e4b 100644
--- a/tests/libs/Android.build.dt_runpath.mk
+++ b/tests/libs/Android.build.dt_runpath.mk
@@ -21,11 +21,12 @@
 #
 # Dependencies
 #
-# libtest_dt_runpath_d.so                       runpath: ${ORIGIN}/dt_runpath_b_c_x
+# libtest_dt_runpath_d.so                       runpath: ${ORIGIN}/dt_runpath_b_c_x, ${ORIGIN}/dt_runpath_y/${LIB}
 # |-> dt_runpath_b_c_x/libtest_dt_runpath_b.so  runpath: ${ORIGIN}/../dt_runpath_a
 # |   |-> dt_runpath_a/libtest_dt_runpath_a.so
 # |-> dt_runpath_b_c_x/libtest_dt_runpath_c.so  runpath: ${ORIGIN}/invalid_dt_runpath
 # |   |-> libtest_dt_runpath_a.so (soname)
+# |-> dt_runpath_y/lib[64]/libtest_dt_runpath_y.so
 #
 # This one is used to test dlopen
 # dt_runpath_b_c_x/libtest_dt_runpath_x.so
@@ -61,12 +62,18 @@
 module := libtest_dt_runpath_c
 include $(LOCAL_PATH)/Android.build.testlib.mk
 
-# D depends on B and C with DT_RUNPATH.
+# D depends on B, C, and Y with DT_RUNPATH.
 libtest_dt_runpath_d_src_files := \
     dlopen_b.cpp
 
-libtest_dt_runpath_d_shared_libraries := libtest_dt_runpath_b libtest_dt_runpath_c
-libtest_dt_runpath_d_ldflags := -Wl,--rpath,\$${ORIGIN}/dt_runpath_b_c_x -Wl,--enable-new-dtags
+libtest_dt_runpath_d_shared_libraries := \
+    libtest_dt_runpath_b \
+    libtest_dt_runpath_c \
+    libtest_dt_runpath_y
+libtest_dt_runpath_d_ldflags := \
+    -Wl,--rpath,\$${ORIGIN}/dt_runpath_b_c_x \
+    -Wl,--rpath,\$${ORIGIN}/dt_runpath_y/\$${LIB} \
+    -Wl,--enable-new-dtags
 libtest_dt_runpath_d_ldlibs := -ldl
 module := libtest_dt_runpath_d
 include $(LOCAL_PATH)/Android.build.testlib.mk
@@ -77,8 +84,14 @@
 libtest_dt_runpath_d_zip_src_files := \
     dlopen_b.cpp
 
-libtest_dt_runpath_d_zip_shared_libraries := libtest_dt_runpath_b libtest_dt_runpath_c
-libtest_dt_runpath_d_zip_ldflags := -Wl,--rpath,\$${ORIGIN}/dt_runpath_b_c_x -Wl,--enable-new-dtags
+libtest_dt_runpath_d_zip_shared_libraries := \
+    libtest_dt_runpath_b \
+    libtest_dt_runpath_c \
+    libtest_dt_runpath_y
+libtest_dt_runpath_d_zip_ldflags := \
+    -Wl,--rpath,\$${ORIGIN}/dt_runpath_b_c_x \
+    -Wl,--rpath,\$${ORIGIN}/dt_runpath_y/\$${LIB} \
+    -Wl,--enable-new-dtags
 libtest_dt_runpath_d_zip_ldlibs := -ldl
 libtest_dt_runpath_d_zip_install_to_native_tests_dir := $(module)
 
@@ -96,3 +109,20 @@
 module := libtest_dt_runpath_x
 include $(LOCAL_PATH)/Android.build.testlib.mk
 
+# A leaf library in lib or lib64 directory
+libtest_dt_runpath_y_src_files := \
+    empty.cpp
+
+ifeq ($(TARGET_TRANSLATE_2ND_ARCH),true)
+libtest_dt_runpath_y_install_to_native_tests_dir_32 := bionic-loader-test-libs/dt_runpath_y/lib/$(TARGET_2ND_ARCH)
+else
+libtest_dt_runpath_y_install_to_native_tests_dir_32 := bionic-loader-test-libs/dt_runpath_y/lib
+endif
+ifeq ($(TARGET_IS_64_BIT),true)
+libtest_dt_runpath_y_install_to_native_tests_dir_64 := bionic-loader-test-libs/dt_runpath_y/lib64
+else
+libtest_dt_runpath_y_install_to_native_tests_dir_64 := bionic-loader-test-libs/dt_runpath_y/lib
+endif
+
+module := libtest_dt_runpath_y
+include $(LOCAL_PATH)/Android.build.testlib.mk
diff --git a/libc/arch-mips/bionic/libgcc_compat.c b/tests/libs/elftls_dlopen_ie_error_helper.cpp
similarity index 68%
copy from libc/arch-mips/bionic/libgcc_compat.c
copy to tests/libs/elftls_dlopen_ie_error_helper.cpp
index 1a0f566..5902e07 100644
--- a/libc/arch-mips/bionic/libgcc_compat.c
+++ b/tests/libs/elftls_dlopen_ie_error_helper.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,16 +26,20 @@
  * SUCH DAMAGE.
  */
 
-extern char __divdi3;
-extern char __moddi3;
-extern char __popcountsi2;
-extern char __udivdi3;
-extern char __umoddi3;
+#include <dlfcn.h>
+#include <stdio.h>
 
-void* __bionic_libgcc_compat_symbols[] = {
-    &__divdi3,
-    &__moddi3,
-    &__popcountsi2,
-    &__udivdi3,
-    &__umoddi3,
-};
+// This helper executable attempts to load libtest_elftls_shared_var_ie.so,
+// then reports success or failure. With Bionic, it is expected to fail, because
+// libtest_elftls_shared_var_ie.so tries to access a dynamically-allocated TLS
+// variable using the IE access model intended for static TLS.
+
+int main() {
+  void* lib = dlopen("libtest_elftls_shared_var_ie.so", RTLD_LOCAL | RTLD_NOW);
+  if (lib) {
+    printf("success\n");
+  } else {
+    printf("dlerror: %s\n", dlerror());
+  }
+  return 0;
+}
diff --git a/tests/libs/elftls_dynamic.cpp b/tests/libs/elftls_dynamic.cpp
new file mode 100644
index 0000000..7fa239c
--- /dev/null
+++ b/tests/libs/elftls_dynamic.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+// This shared object test library is dlopen'ed by the main test executable.
+// This variable comes from libtest_elftls_shared_var.so, which is part of
+// static TLS. Verify that a GD-model access can access the variable.
+//
+// Accessing the static TLS variable from an solib prevents the static linker
+// from relaxing the GD access to IE and lets us test that __tls_get_addr and
+// the tlsdesc resolver handle a static TLS variable.
+extern "C" __thread int elftls_shared_var;
+
+extern "C" int bump_shared_var() {
+  return ++elftls_shared_var;
+}
+
+// The static linker denotes the current module by omitting the symbol from
+// the DTPMOD/TLSDESC relocations.
+static __thread int local_var_1 = 15;
+static __thread int local_var_2 = 25;
+
+extern "C" int bump_local_vars() {
+  return ++local_var_1 + ++local_var_2;
+}
+
+__attribute__((weak)) extern "C" __thread int missing_weak_dyn_tls;
+
+extern "C" int* missing_weak_dyn_tls_addr() {
+  return &missing_weak_dyn_tls;
+}
diff --git a/libc/arch-mips/bionic/libgcc_compat.c b/tests/libs/elftls_dynamic_filler.cpp
similarity index 81%
copy from libc/arch-mips/bionic/libgcc_compat.c
copy to tests/libs/elftls_dynamic_filler.cpp
index 1a0f566..9c00ab0 100644
--- a/libc/arch-mips/bionic/libgcc_compat.c
+++ b/tests/libs/elftls_dynamic_filler.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,16 +26,8 @@
  * SUCH DAMAGE.
  */
 
-extern char __divdi3;
-extern char __moddi3;
-extern char __popcountsi2;
-extern char __udivdi3;
-extern char __umoddi3;
+__thread int var = TLS_FILLER;
 
-void* __bionic_libgcc_compat_symbols[] = {
-    &__divdi3,
-    &__moddi3,
-    &__popcountsi2,
-    &__udivdi3,
-    &__umoddi3,
-};
+extern "C" int bump() {
+  return ++var;
+}
diff --git a/libc/arch-mips/bionic/libgcc_compat.c b/tests/libs/elftls_shared_var.cpp
similarity index 81%
copy from libc/arch-mips/bionic/libgcc_compat.c
copy to tests/libs/elftls_shared_var.cpp
index 1a0f566..27a15f0 100644
--- a/libc/arch-mips/bionic/libgcc_compat.c
+++ b/tests/libs/elftls_shared_var.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,16 +26,7 @@
  * SUCH DAMAGE.
  */
 
-extern char __divdi3;
-extern char __moddi3;
-extern char __popcountsi2;
-extern char __udivdi3;
-extern char __umoddi3;
+// This shared object merely declares a global TLS variable without accessing
+// it.
 
-void* __bionic_libgcc_compat_symbols[] = {
-    &__divdi3,
-    &__moddi3,
-    &__popcountsi2,
-    &__udivdi3,
-    &__umoddi3,
-};
+extern "C" __thread int elftls_shared_var = 20;
diff --git a/libc/arch-mips/bionic/libgcc_compat.c b/tests/libs/elftls_shared_var_ie.cpp
similarity index 79%
copy from libc/arch-mips/bionic/libgcc_compat.c
copy to tests/libs/elftls_shared_var_ie.cpp
index 1a0f566..14e2ab0 100644
--- a/libc/arch-mips/bionic/libgcc_compat.c
+++ b/tests/libs/elftls_shared_var_ie.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,16 +26,10 @@
  * SUCH DAMAGE.
  */
 
-extern char __divdi3;
-extern char __moddi3;
-extern char __popcountsi2;
-extern char __udivdi3;
-extern char __umoddi3;
+// Accessing a symbol in libtest_elftls_shared_var.so using an IE access should
+// work iff the solib is part of static TLS.
+__attribute__((tls_model("initial-exec"))) extern "C" __thread int elftls_shared_var;
 
-void* __bionic_libgcc_compat_symbols[] = {
-    &__divdi3,
-    &__moddi3,
-    &__popcountsi2,
-    &__udivdi3,
-    &__umoddi3,
-};
+extern "C" int bump_shared_var() {
+  return ++elftls_shared_var;
+}
diff --git a/libc/arch-mips/bionic/libgcc_compat.c b/tests/libs/elftls_tprel.cpp
similarity index 60%
copy from libc/arch-mips/bionic/libgcc_compat.c
copy to tests/libs/elftls_tprel.cpp
index 1a0f566..eb2fd93 100644
--- a/libc/arch-mips/bionic/libgcc_compat.c
+++ b/tests/libs/elftls_tprel.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,16 +26,26 @@
  * SUCH DAMAGE.
  */
 
-extern char __divdi3;
-extern char __moddi3;
-extern char __popcountsi2;
-extern char __udivdi3;
-extern char __umoddi3;
+// This shared object tests TPREL relocations in the dynamic linker. It's always
+// part of static TLS.
 
-void* __bionic_libgcc_compat_symbols[] = {
-    &__divdi3,
-    &__moddi3,
-    &__popcountsi2,
-    &__udivdi3,
-    &__umoddi3,
-};
+// For accesses to these variables, the bfd and lld linkers generate a TPREL
+// relocation with no symbol but a non-zero addend.
+__attribute__((tls_model("initial-exec"))) static __thread int tls_var_1 = 3;
+__attribute__((tls_model("initial-exec"))) static __thread int tls_var_2 = 7;
+
+extern "C" int bump_static_tls_var_1() {
+  return ++tls_var_1;
+}
+
+extern "C" int bump_static_tls_var_2() {
+  return ++tls_var_2;
+}
+
+__attribute__((tls_model("initial-exec"), weak)) extern "C" __thread int missing_weak_tls;
+
+extern "C" int* missing_weak_tls_addr() {
+  // The dynamic linker should resolve a TPREL relocation to this symbol to 0,
+  // which this function adds to the thread pointer.
+  return &missing_weak_tls;
+}
diff --git a/tests/malloc_iterate_test.cpp b/tests/malloc_iterate_test.cpp
index 5e60a6d..76583eb 100644
--- a/tests/malloc_iterate_test.cpp
+++ b/tests/malloc_iterate_test.cpp
@@ -92,14 +92,15 @@
   test_data->total_allocated_bytes = 0;
 
   // Find all of the maps that are [anon:libc_malloc].
-  ASSERT_TRUE(android::procinfo::ReadMapFile("/proc/self/maps",
-    [&](uint64_t start, uint64_t end, uint16_t, uint64_t, const char* name) {
-    if (std::string(name) == "[anon:libc_malloc]") {
-      malloc_disable();
-      malloc_iterate(start, end - start, SavePointers, test_data);
-      malloc_enable();
-    }
-  }));
+  ASSERT_TRUE(android::procinfo::ReadMapFile(
+      "/proc/self/maps",
+      [&](uint64_t start, uint64_t end, uint16_t, uint64_t, ino_t, const char* name) {
+        if (std::string(name) == "[anon:libc_malloc]") {
+          malloc_disable();
+          malloc_iterate(start, end - start, SavePointers, test_data);
+          malloc_enable();
+        }
+      }));
 
   for (size_t i = 0; i < test_data->allocs.size(); i++) {
     EXPECT_EQ(1UL, test_data->allocs[i].count) << "Failed on size " << test_data->allocs[i].size;
@@ -180,14 +181,15 @@
   TestDataType test_data = {};
 
   // Find all of the maps that are not [anon:libc_malloc].
-  ASSERT_TRUE(android::procinfo::ReadMapFile("/proc/self/maps",
-    [&](uint64_t start, uint64_t end, uint16_t, uint64_t, const char* name) {
-    if (std::string(name) != "[anon:libc_malloc]") {
-      malloc_disable();
-      malloc_iterate(start, end - start, SavePointers, &test_data);
-      malloc_enable();
-    }
-  }));
+  ASSERT_TRUE(android::procinfo::ReadMapFile(
+      "/proc/self/maps",
+      [&](uint64_t start, uint64_t end, uint16_t, uint64_t, ino_t, const char* name) {
+        if (std::string(name) != "[anon:libc_malloc]") {
+          malloc_disable();
+          malloc_iterate(start, end - start, SavePointers, &test_data);
+          malloc_enable();
+        }
+      }));
 
   ASSERT_EQ(0UL, test_data.total_allocated_bytes);
 #else
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index 4a01278..658f8bd 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -16,6 +16,7 @@
 
 #include <gtest/gtest.h>
 
+#include <elf.h>
 #include <limits.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -24,7 +25,10 @@
 
 #include <tinyxml2.h>
 
+#include <android-base/file.h>
+
 #include "private/bionic_config.h"
+#include "private/bionic_malloc.h"
 #include "utils.h"
 
 #if defined(__BIONIC__)
@@ -514,6 +518,7 @@
 
 TEST(malloc, mallopt_decay) {
 #if defined(__BIONIC__)
+  SKIP_WITH_HWASAN; // hwasan does not implement mallopt
   errno = 0;
   ASSERT_EQ(1, mallopt(M_DECAY_TIME, 1));
   ASSERT_EQ(1, mallopt(M_DECAY_TIME, 0));
@@ -526,6 +531,7 @@
 
 TEST(malloc, mallopt_purge) {
 #if defined(__BIONIC__)
+  SKIP_WITH_HWASAN; // hwasan does not implement mallopt
   errno = 0;
   ASSERT_EQ(1, mallopt(M_PURGE, 0));
 #else
@@ -563,6 +569,7 @@
 
 TEST(malloc, mallinfo) {
 #if defined(__BIONIC__)
+  SKIP_WITH_HWASAN; // hwasan does not implement mallinfo
   static size_t sizes[] = {
     8, 32, 128, 4096, 32768, 131072, 1024000, 10240000, 20480000, 300000000
   };
@@ -584,10 +591,13 @@
       size_t new_allocated = mallinfo().uordblks;
       if (allocated != new_allocated) {
         size_t usable_size = malloc_usable_size(ptrs[i]);
-        ASSERT_GE(new_allocated, allocated + usable_size)
-            << "Failed at size " << size << " usable size " << usable_size;
-        pass = true;
-        break;
+        // Only check if the total got bigger by at least allocation size.
+        // Sometimes the mallinfo numbers can go backwards due to compaction
+        // and/or freeing of cached data.
+        if (new_allocated >= allocated + usable_size) {
+          pass = true;
+          break;
+        }
       }
     }
     for (void* ptr : ptrs) {
@@ -601,3 +611,60 @@
   GTEST_LOG_(INFO) << "Host glibc does not pass this test, skipping.\n";
 #endif
 }
+
+TEST(android_mallopt, error_on_unexpected_option) {
+#if defined(__BIONIC__)
+  const int unrecognized_option = -1;
+  errno = 0;
+  EXPECT_EQ(false, android_mallopt(unrecognized_option, nullptr, 0));
+  EXPECT_EQ(ENOTSUP, errno);
+#else
+  GTEST_LOG_(INFO) << "This tests a bionic implementation detail.\n";
+#endif
+}
+
+bool IsDynamic() {
+#if defined(__LP64__)
+  Elf64_Ehdr ehdr;
+#else
+  Elf32_Ehdr ehdr;
+#endif
+  std::string path(android::base::GetExecutablePath());
+
+  int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+  if (fd == -1) {
+    // Assume dynamic on error.
+    return true;
+  }
+  bool read_completed = android::base::ReadFully(fd, &ehdr, sizeof(ehdr));
+  close(fd);
+  // Assume dynamic in error cases.
+  return !read_completed || ehdr.e_type == ET_DYN;
+}
+
+TEST(android_mallopt, init_zygote_child_profiling) {
+#if defined(__BIONIC__)
+  // Successful call.
+  errno = 0;
+  if (IsDynamic()) {
+    EXPECT_EQ(true, android_mallopt(M_INIT_ZYGOTE_CHILD_PROFILING, nullptr, 0));
+    EXPECT_EQ(0, errno);
+  } else {
+    // Not supported in static executables.
+    EXPECT_EQ(false, android_mallopt(M_INIT_ZYGOTE_CHILD_PROFILING, nullptr, 0));
+    EXPECT_EQ(ENOTSUP, errno);
+  }
+
+  // Unexpected arguments rejected.
+  errno = 0;
+  char unexpected = 0;
+  EXPECT_EQ(false, android_mallopt(M_INIT_ZYGOTE_CHILD_PROFILING, &unexpected, 1));
+  if (IsDynamic()) {
+    EXPECT_EQ(EINVAL, errno);
+  } else {
+    EXPECT_EQ(ENOTSUP, errno);
+  }
+#else
+  GTEST_LOG_(INFO) << "This tests a bionic implementation detail.\n";
+#endif
+}
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index 1c57264..973ca53 100644
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -25,6 +25,7 @@
 #include <stdio.h>
 #include <sys/mman.h>
 #include <sys/prctl.h>
+#include <sys/resource.h>
 #include <sys/syscall.h>
 #include <time.h>
 #include <unistd.h>
@@ -336,11 +337,14 @@
   static void* thread_fn(void* arg) {
     TestBug37410* data = reinterpret_cast<TestBug37410*>(arg);
 
+    // Unlocking data->mutex will cause the main thread to exit, invalidating *data. Save the handle.
+    pthread_t main_thread = data->main_thread;
+
     // Let the main thread know we're running.
     pthread_mutex_unlock(&data->mutex);
 
     // And wait for the main thread to exit.
-    pthread_join(data->main_thread, nullptr);
+    pthread_join(main_thread, nullptr);
 
     return nullptr;
   }
@@ -494,7 +498,8 @@
   pthread_t dead_thread;
   MakeDeadThread(dead_thread);
 
-  EXPECT_DEATH(pthread_setname_np(dead_thread, "short 3"), "invalid pthread_t");
+  EXPECT_DEATH(pthread_setname_np(dead_thread, "short 3"),
+               "invalid pthread_t (.*) passed to pthread_setname_np");
 }
 
 TEST_F(pthread_DeathTest, pthread_setname_np__null_thread) {
@@ -507,7 +512,8 @@
   MakeDeadThread(dead_thread);
 
   char name[64];
-  EXPECT_DEATH(pthread_getname_np(dead_thread, name, sizeof(name)), "invalid pthread_t");
+  EXPECT_DEATH(pthread_getname_np(dead_thread, name, sizeof(name)),
+               "invalid pthread_t (.*) passed to pthread_getname_np");
 }
 
 TEST_F(pthread_DeathTest, pthread_getname_np__null_thread) {
@@ -563,7 +569,8 @@
   pthread_t dead_thread;
   MakeDeadThread(dead_thread);
 
-  EXPECT_DEATH(pthread_detach(dead_thread), "invalid pthread_t");
+  EXPECT_DEATH(pthread_detach(dead_thread),
+               "invalid pthread_t (.*) passed to pthread_detach");
 }
 
 TEST_F(pthread_DeathTest, pthread_detach__null_thread) {
@@ -590,7 +597,8 @@
   MakeDeadThread(dead_thread);
 
   clockid_t c;
-  EXPECT_DEATH(pthread_getcpuclockid(dead_thread, &c), "invalid pthread_t");
+  EXPECT_DEATH(pthread_getcpuclockid(dead_thread, &c),
+               "invalid pthread_t (.*) passed to pthread_getcpuclockid");
 }
 
 TEST_F(pthread_DeathTest, pthread_getcpuclockid__null_thread) {
@@ -605,7 +613,8 @@
 
   int policy;
   sched_param param;
-  EXPECT_DEATH(pthread_getschedparam(dead_thread, &policy, &param), "invalid pthread_t");
+  EXPECT_DEATH(pthread_getschedparam(dead_thread, &policy, &param),
+               "invalid pthread_t (.*) passed to pthread_getschedparam");
 }
 
 TEST_F(pthread_DeathTest, pthread_getschedparam__null_thread) {
@@ -621,7 +630,8 @@
 
   int policy = 0;
   sched_param param;
-  EXPECT_DEATH(pthread_setschedparam(dead_thread, policy, &param), "invalid pthread_t");
+  EXPECT_DEATH(pthread_setschedparam(dead_thread, policy, &param),
+               "invalid pthread_t (.*) passed to pthread_setschedparam");
 }
 
 TEST_F(pthread_DeathTest, pthread_setschedparam__null_thread) {
@@ -635,7 +645,8 @@
   pthread_t dead_thread;
   MakeDeadThread(dead_thread);
 
-  EXPECT_DEATH(pthread_setschedprio(dead_thread, 123), "invalid pthread_t");
+  EXPECT_DEATH(pthread_setschedprio(dead_thread, 123),
+               "invalid pthread_t (.*) passed to pthread_setschedprio");
 }
 
 TEST_F(pthread_DeathTest, pthread_setschedprio__null_thread) {
@@ -647,7 +658,8 @@
   pthread_t dead_thread;
   MakeDeadThread(dead_thread);
 
-  EXPECT_DEATH(pthread_join(dead_thread, nullptr), "invalid pthread_t");
+  EXPECT_DEATH(pthread_join(dead_thread, nullptr),
+               "invalid pthread_t (.*) passed to pthread_join");
 }
 
 TEST_F(pthread_DeathTest, pthread_join__null_thread) {
@@ -659,7 +671,8 @@
   pthread_t dead_thread;
   MakeDeadThread(dead_thread);
 
-  EXPECT_DEATH(pthread_kill(dead_thread, 0), "invalid pthread_t");
+  EXPECT_DEATH(pthread_kill(dead_thread, 0),
+               "invalid pthread_t (.*) passed to pthread_kill");
 }
 
 TEST_F(pthread_DeathTest, pthread_kill__null_thread) {
@@ -1540,7 +1553,7 @@
   void* maps_stack_hi = nullptr;
   std::vector<map_record> maps;
   ASSERT_TRUE(Maps::parse_maps(&maps));
-  uintptr_t stack_address = reinterpret_cast<uintptr_t>(&maps_stack_hi);
+  uintptr_t stack_address = reinterpret_cast<uintptr_t>(untag_address(&maps_stack_hi));
   for (const auto& map : maps) {
     if (map.addr_start <= stack_address && map.addr_end > stack_address){
       maps_stack_hi = reinterpret_cast<void*>(map.addr_end);
@@ -1619,9 +1632,9 @@
 
   // Verify if the stack used by the signal handler is the alternate stack just registered.
   ASSERT_LE(getstack_signal_handler_arg.signal_stack_base, &attr);
-  ASSERT_LT(static_cast<void*>(&attr),
+  ASSERT_LT(static_cast<void*>(untag_address(&attr)),
             static_cast<char*>(getstack_signal_handler_arg.signal_stack_base) +
-            getstack_signal_handler_arg.signal_stack_size);
+                getstack_signal_handler_arg.signal_stack_size);
 
   // Verify if the main thread's stack got in the signal handler is correct.
   ASSERT_EQ(getstack_signal_handler_arg.main_stack_base, stack_base);
@@ -1680,7 +1693,7 @@
 
   // Test whether &local_variable is in [stack_base, stack_base + stack_size).
   ASSERT_LE(reinterpret_cast<char*>(stack_base), &local_variable);
-  ASSERT_LT(&local_variable, reinterpret_cast<char*>(stack_base) + stack_size);
+  ASSERT_LT(untag_address(&local_variable), reinterpret_cast<char*>(stack_base) + stack_size);
 }
 
 // Check whether something on stack is in the range of
diff --git a/tests/signal_test.cpp b/tests/signal_test.cpp
index dd27aef..77b004f 100644
--- a/tests/signal_test.cpp
+++ b/tests/signal_test.cpp
@@ -392,11 +392,19 @@
   static uint64_t sigset;
   struct sigaction sa = {};
   sa.sa_handler = [](int) { sigset = GetSignalMask(); };
+  sa.sa_flags = SA_ONSTACK | SA_NODEFER;
   sigfillset(&sa.sa_mask);
   sigaction(SIGUSR1, &sa, nullptr);
   raise(SIGUSR1);
-  ASSERT_NE(0ULL, sigset);
-  TestSignalMaskFiltered(sigset);
+
+  // On LP32, struct sigaction::sa_mask is only 32-bits wide.
+  unsigned long expected_sigset = ~0UL;
+
+  // SIGKILL and SIGSTOP are always blocked.
+  expected_sigset &= ~(1UL << (SIGKILL - 1));
+  expected_sigset &= ~(1UL << (SIGSTOP - 1));
+
+  ASSERT_EQ(static_cast<uint64_t>(expected_sigset), sigset);
 }
 
 TEST(signal, sigaction64_filter) {
@@ -404,11 +412,18 @@
   static uint64_t sigset;
   struct sigaction64 sa = {};
   sa.sa_handler = [](int) { sigset = GetSignalMask(); };
+  sa.sa_flags = SA_ONSTACK | SA_NODEFER;
   sigfillset64(&sa.sa_mask);
   sigaction64(SIGUSR1, &sa, nullptr);
   raise(SIGUSR1);
-  ASSERT_NE(0ULL, sigset);
-  TestSignalMaskFiltered(sigset);
+
+  uint64_t expected_sigset = ~0ULL;
+
+  // SIGKILL and SIGSTOP are always blocked.
+  expected_sigset &= ~(1ULL << (SIGKILL - 1));
+  expected_sigset &= ~(1ULL << (SIGSTOP - 1));
+
+  ASSERT_EQ(expected_sigset, sigset);
 }
 
 TEST(signal, sigprocmask_setmask_filter) {
diff --git a/tests/stack_protector_test.cpp b/tests/stack_protector_test.cpp
index 34e3c11..7a85cc3 100644
--- a/tests/stack_protector_test.cpp
+++ b/tests/stack_protector_test.cpp
@@ -104,6 +104,11 @@
 TEST_F(stack_protector_DeathTest, modify_stack_protector) {
   // In another file to prevent inlining, which removes stack protection.
   extern void modify_stack_protector_test();
+#if __has_feature(hwaddress_sanitizer)
+  ASSERT_EXIT(modify_stack_protector_test(),
+              testing::KilledBySignal(SIGABRT), "tag-mismatch");
+#else
   ASSERT_EXIT(modify_stack_protector_test(),
               testing::KilledBySignal(SIGABRT), "stack corruption detected");
+#endif
 }
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 479fd9d..ad6ed45 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -1820,6 +1820,14 @@
   ASSERT_EQ(0, fclose(fp));
 }
 
+TEST(STDIO_TEST, fmemopen_zero_length_buffer_overrun) {
+  char buf[2] = "x";
+  ASSERT_EQ('x', buf[0]);
+  FILE* fp = fmemopen(buf, 0, "w");
+  ASSERT_EQ('x', buf[0]);
+  ASSERT_EQ(0, fclose(fp));
+}
+
 TEST(STDIO_TEST, fmemopen_write_only_allocated) {
   // POSIX says fmemopen "may fail if the mode argument does not include a '+'".
   // BSD fails, glibc doesn't. We side with the more lenient.
diff --git a/tests/stdlib_test.cpp b/tests/stdlib_test.cpp
index a249b75..408a9c7 100644
--- a/tests/stdlib_test.cpp
+++ b/tests/stdlib_test.cpp
@@ -36,14 +36,6 @@
 #include "math_data_test.h"
 #include "utils.h"
 
-#if defined(__BIONIC__)
-  #define ALIGNED_ALLOC_AVAILABLE 1
-#elif defined(__GLIBC_PREREQ)
-  #if __GLIBC_PREREQ(2, 16)
-    #define ALIGNED_ALLOC_AVAILABLE 1
-  #endif
-#endif
-
 template <typename T = int (*)(char*)>
 class GenericTemporaryFile {
  public:
@@ -274,7 +266,6 @@
 
 TEST(stdlib, aligned_alloc_sweep) {
   SKIP_WITH_HWASAN;
-#if defined(ALIGNED_ALLOC_AVAILABLE)
   // Verify powers of 2 up to 2048 allocate, and verify that all other
   // alignment values between the powers of 2 fail.
   size_t last_align = 1;
@@ -292,31 +283,20 @@
     free(ptr);
     last_align = align;
   }
-#else
-  GTEST_LOG_(INFO) << "This test requires a C library that has aligned_alloc.\n";
-#endif
 }
 
 TEST(stdlib, aligned_alloc_overflow) {
   SKIP_WITH_HWASAN;
-#if defined(ALIGNED_ALLOC_AVAILABLE)
   ASSERT_TRUE(aligned_alloc(16, SIZE_MAX) == nullptr);
-#else
-  GTEST_LOG_(INFO) << "This test requires a C library that has aligned_alloc.\n";
-#endif
 }
 
 TEST(stdlib, aligned_alloc_size_not_multiple_of_alignment) {
   SKIP_WITH_HWASAN;
-#if defined(ALIGNED_ALLOC_AVAILABLE)
   for (size_t size = 1; size <= 2048; size++) {
     void* ptr = aligned_alloc(2048, size);
     ASSERT_TRUE(ptr != nullptr) << "Failed at size " << std::to_string(size);
     free(ptr);
   }
-#else
-  GTEST_LOG_(INFO) << "This test requires a C library that has aligned_alloc.\n";
-#endif
 }
 
 TEST(stdlib, realpath__NULL_filename) {
diff --git a/tests/string_test.cpp b/tests/string_test.cpp
index fd2a787..b27ca87 100644
--- a/tests/string_test.cpp
+++ b/tests/string_test.cpp
@@ -1554,3 +1554,40 @@
   ASSERT_EQ(haystack + 1, strstr(haystack, "i"));
   ASSERT_EQ(haystack + 4, strstr(haystack, "da"));
 }
+
+TEST(STRING_TEST, strcasestr_smoke) {
+  const char* haystack = "bIg dAdDy/gIaNt hAyStAcKs";
+  ASSERT_EQ(haystack, strcasestr(haystack, ""));
+  ASSERT_EQ(haystack + 0, strcasestr(haystack, "B"));
+  ASSERT_EQ(haystack + 1, strcasestr(haystack, "i"));
+  ASSERT_EQ(haystack + 4, strcasestr(haystack, "Da"));
+}
+
+TEST(STRING_TEST, strcoll_smoke) {
+  ASSERT_TRUE(strcoll("aab", "aac") < 0);
+  ASSERT_TRUE(strcoll("aab", "aab") == 0);
+  ASSERT_TRUE(strcoll("aac", "aab") > 0);
+}
+
+TEST(STRING_TEST, strxfrm_smoke) {
+  const char* src1 = "aab";
+  char dst1[16] = {};
+  ASSERT_GT(strxfrm(dst1, src1, sizeof(dst1)), 0U);
+  const char* src2 = "aac";
+  char dst2[16] = {};
+  ASSERT_GT(strxfrm(dst2, src2, sizeof(dst2)), 0U);
+  ASSERT_TRUE(strcmp(dst1, dst2) < 0);
+}
+
+TEST(STRING_TEST, memccpy_smoke) {
+  char dst[32];
+
+  memset(dst, 0, sizeof(dst));
+  char* p = static_cast<char*>(memccpy(dst, "hello world", ' ', 32));
+  ASSERT_STREQ("hello ", dst);
+  ASSERT_EQ(ptrdiff_t(6), p - dst);
+
+  memset(dst, 0, sizeof(dst));
+  ASSERT_EQ(nullptr, memccpy(dst, "hello world", ' ', 4));
+  ASSERT_STREQ("hell", dst);
+}
diff --git a/tests/sys_ptrace_test.cpp b/tests/sys_ptrace_test.cpp
index 83fd93b..04dcd4e 100644
--- a/tests/sys_ptrace_test.cpp
+++ b/tests/sys_ptrace_test.cpp
@@ -35,6 +35,8 @@
 #include <android-base/macros.h>
 #include <android-base/unique_fd.h>
 
+#include "utils.h"
+
 using namespace std::chrono_literals;
 
 using android::base::unique_fd;
@@ -193,7 +195,7 @@
     return;
   }
 
-  set_watchpoint(child, uintptr_t(&data) + offset, size);
+  set_watchpoint(child, uintptr_t(untag_address(&data)) + offset, size);
 
   ASSERT_EQ(0, ptrace(PTRACE_CONT, child, nullptr, nullptr)) << strerror(errno);
   ASSERT_EQ(child, TEMP_FAILURE_RETRY(waitpid(child, &status, __WALL))) << strerror(errno);
diff --git a/tests/sys_time_test.cpp b/tests/sys_time_test.cpp
index d033364..5dda7ab 100644
--- a/tests/sys_time_test.cpp
+++ b/tests/sys_time_test.cpp
@@ -147,7 +147,7 @@
     tv2.tv_usec += 1000000;
   }
 
-  // Should be less than (a very generous, to try to avoid flakiness) 5ms (5000us).
+  // To try to avoid flakiness we'll accept answers within 10,000us (0.01s).
   ASSERT_EQ(0, tv2.tv_sec);
-  ASSERT_LT(tv2.tv_usec, 5000);
+  ASSERT_LT(tv2.tv_usec, 10'000);
 }
diff --git a/tests/time_test.cpp b/tests/time_test.cpp
index 4ec5976..50830ee 100644
--- a/tests/time_test.cpp
+++ b/tests/time_test.cpp
@@ -607,9 +607,9 @@
     ts2.tv_nsec += NS_PER_S;
   }
 
-  // Should be less than (a very generous, to try to avoid flakiness) 1000000ns.
+  // To try to avoid flakiness we'll accept answers within 10,000,000ns (0.01s).
   ASSERT_EQ(0, ts2.tv_sec);
-  ASSERT_LT(ts2.tv_nsec, 1000000);
+  ASSERT_LT(ts2.tv_nsec, 10'000'000);
 }
 
 TEST(time, clock_gettime_CLOCK_REALTIME) {
diff --git a/tests/uchar_test.cpp b/tests/uchar_test.cpp
index 522d5ac..48c500d 100644
--- a/tests/uchar_test.cpp
+++ b/tests/uchar_test.cpp
@@ -15,12 +15,7 @@
  */
 
 
-#include <sys/cdefs.h>
-#if defined(__BIONIC__)
-#define HAVE_UCHAR 1
-#elif defined(__GLIBC__)
-#define HAVE_UCHAR __GLIBC_PREREQ(2, 16)
-#endif
+#include <uchar.h>
 
 #include <gtest/gtest.h>
 
@@ -29,21 +24,12 @@
 #include <locale.h>
 #include <stdint.h>
 
-#if HAVE_UCHAR
-#include <uchar.h>
-#endif
-
 TEST(uchar, sizeof_uchar_t) {
-#if HAVE_UCHAR
   EXPECT_EQ(2U, sizeof(char16_t));
   EXPECT_EQ(4U, sizeof(char32_t));
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
 TEST(uchar, start_state) {
-#if HAVE_UCHAR
   char out[MB_LEN_MAX];
   mbstate_t ps;
 
@@ -64,31 +50,19 @@
   EXPECT_EQ(static_cast<size_t>(-2), mbrtoc32(nullptr, "\xf0\xa4", 1, &ps));
   EXPECT_EQ(1U, c32rtomb(out, L'\0', &ps));
   EXPECT_TRUE(mbsinit(&ps));
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
 TEST(uchar, c16rtomb_null_out) {
-#if HAVE_UCHAR
   EXPECT_EQ(1U, c16rtomb(nullptr, L'\0', nullptr));
   EXPECT_EQ(1U, c16rtomb(nullptr, L'h', nullptr));
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
 TEST(uchar, c16rtomb_null_char) {
-#if HAVE_UCHAR
   char bytes[MB_LEN_MAX];
   EXPECT_EQ(1U, c16rtomb(bytes, L'\0', nullptr));
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
 TEST(uchar, c16rtomb) {
-#if HAVE_UCHAR
   char bytes[MB_LEN_MAX];
 
   memset(bytes, 0, sizeof(bytes));
@@ -113,13 +87,9 @@
   EXPECT_EQ('\xe2', bytes[0]);
   EXPECT_EQ('\x82', bytes[1]);
   EXPECT_EQ('\xac', bytes[2]);
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
 TEST(uchar, c16rtomb_surrogate) {
-#if HAVE_UCHAR
   char bytes[MB_LEN_MAX];
 
   memset(bytes, 0, sizeof(bytes));
@@ -129,13 +99,9 @@
   EXPECT_EQ('\x8a', bytes[1]);
   EXPECT_EQ('\xaf', bytes[2]);
   EXPECT_EQ('\x8d', bytes[3]);
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
 TEST(uchar, c16rtomb_invalid) {
-#if HAVE_UCHAR
   char bytes[MB_LEN_MAX];
 
   memset(bytes, 0, sizeof(bytes));
@@ -143,21 +109,13 @@
 
   EXPECT_EQ(0U, c16rtomb(bytes, 0xdbea, nullptr));
   EXPECT_EQ(static_cast<size_t>(-1), c16rtomb(bytes, 0xdbea, nullptr));
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
 TEST(uchar, mbrtoc16_null) {
-#if HAVE_UCHAR
   ASSERT_EQ(0U, mbrtoc16(nullptr, nullptr, 0, nullptr));
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
 TEST(uchar, mbrtoc16_zero_len) {
-#if HAVE_UCHAR
   char16_t out;
 
   out = L'x';
@@ -168,13 +126,9 @@
   ASSERT_EQ(0U, mbrtoc16(&out, "", 0, nullptr));
   ASSERT_EQ(1U, mbrtoc16(&out, "hello", 1, nullptr));
   ASSERT_EQ(L'h', out);
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
 TEST(uchar, mbrtoc16) {
-#if HAVE_UCHAR
   char16_t out;
 
   ASSERT_STREQ("C.UTF-8", setlocale(LC_CTYPE, "C.UTF-8"));
@@ -189,13 +143,9 @@
   // 3-byte UTF-8.
   ASSERT_EQ(3U, mbrtoc16(&out, "\xe2\x82\xac" "def", 6, nullptr));
   ASSERT_EQ(static_cast<char16_t>(0x20ac), out);
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
 TEST(uchar, mbrtoc16_surrogate) {
-#if HAVE_UCHAR
   char16_t out;
 
   ASSERT_EQ(static_cast<size_t>(-3),
@@ -203,32 +153,20 @@
   ASSERT_EQ(static_cast<char16_t>(0xdbea), out);
   ASSERT_EQ(4U, mbrtoc16(&out, "\xf4\x8a\xaf\x8d" "ef", 6, nullptr));
   ASSERT_EQ(static_cast<char16_t>(0xdfcd), out);
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
 TEST(uchar, mbrtoc16_reserved_range) {
-#if HAVE_UCHAR
   char16_t out;
   ASSERT_EQ(static_cast<size_t>(-1),
             mbrtoc16(&out, "\xf0\x80\xbf\xbf", 6, nullptr));
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
 TEST(uchar, mbrtoc16_beyond_range) {
-#if HAVE_UCHAR
   char16_t out;
   ASSERT_EQ(static_cast<size_t>(-1),
             mbrtoc16(&out, "\xf5\x80\x80\x80", 6, nullptr));
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
-#if HAVE_UCHAR
 void test_mbrtoc16_incomplete(mbstate_t* ps) {
   ASSERT_STREQ("C.UTF-8", setlocale(LC_CTYPE, "C.UTF-8"));
   uselocale(LC_GLOBAL_LOCALE);
@@ -259,22 +197,16 @@
   ASSERT_EQ(static_cast<size_t>(-1), mbrtoc16(&out, "\x20" "cdef", 5, ps));
   ASSERT_EQ(EILSEQ, errno);
 }
-#endif
 
 TEST(uchar, mbrtoc16_incomplete) {
-#if HAVE_UCHAR
   mbstate_t ps;
   memset(&ps, 0, sizeof(ps));
 
   test_mbrtoc16_incomplete(&ps);
   test_mbrtoc16_incomplete(nullptr);
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
 TEST(uchar, c32rtomb) {
-#if HAVE_UCHAR
   EXPECT_EQ(1U, c32rtomb(nullptr, L'\0', nullptr));
   EXPECT_EQ(1U, c32rtomb(nullptr, L'h', nullptr));
 
@@ -317,13 +249,9 @@
   // Invalid code point.
   EXPECT_EQ(static_cast<size_t>(-1), c32rtomb(bytes, 0xffffffff, nullptr));
   EXPECT_EQ(EILSEQ, errno);
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
 TEST(uchar, mbrtoc32_valid_non_characters) {
-#if HAVE_UCHAR
   ASSERT_STREQ("C.UTF-8", setlocale(LC_CTYPE, "C.UTF-8"));
   uselocale(LC_GLOBAL_LOCALE);
 
@@ -332,13 +260,9 @@
   ASSERT_EQ(0xfffeU, out[0]);
   ASSERT_EQ(3U, mbrtoc32(out, "\xef\xbf\xbf", 3, nullptr));
   ASSERT_EQ(0xffffU, out[0]);
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
 TEST(uchar, mbrtoc32_out_of_range) {
-#if HAVE_UCHAR
   ASSERT_STREQ("C.UTF-8", setlocale(LC_CTYPE, "C.UTF-8"));
   uselocale(LC_GLOBAL_LOCALE);
 
@@ -346,13 +270,9 @@
   errno = 0;
   ASSERT_EQ(static_cast<size_t>(-1), mbrtoc32(out, "\xf5\x80\x80\x80", 4, nullptr));
   ASSERT_EQ(EILSEQ, errno);
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
 TEST(uchar, mbrtoc32) {
-#if HAVE_UCHAR
   char32_t out[8];
 
   out[0] = L'x';
@@ -393,12 +313,8 @@
   // Illegal over-long sequence.
   ASSERT_EQ(static_cast<size_t>(-1), mbrtoc32(out, "\xf0\x82\x82\xac" "ef", 6, nullptr));
   ASSERT_EQ(EILSEQ, errno);
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
 
-#if HAVE_UCHAR
 void test_mbrtoc32_incomplete(mbstate_t* ps) {
   ASSERT_STREQ("C.UTF-8", setlocale(LC_CTYPE, "C.UTF-8"));
   uselocale(LC_GLOBAL_LOCALE);
@@ -427,16 +343,11 @@
   ASSERT_EQ(static_cast<size_t>(-1), mbrtoc32(&out, "\x20" "cdef", 5, ps));
   ASSERT_EQ(EILSEQ, errno);
 }
-#endif
 
 TEST(uchar, mbrtoc32_incomplete) {
-#if HAVE_UCHAR
   mbstate_t ps;
   memset(&ps, 0, sizeof(ps));
 
   test_mbrtoc32_incomplete(&ps);
   test_mbrtoc32_incomplete(nullptr);
-#else
-  GTEST_LOG_(INFO) << "uchar.h is unavailable.\n";
-#endif
 }
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index f4a7f1f..cb94e45 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -538,7 +538,7 @@
 
 static int CloneStartRoutine(int (*start_routine)(void*)) {
   void* child_stack[1024];
-  return clone(start_routine, &child_stack[1024], SIGCHLD, nullptr);
+  return clone(start_routine, untag_address(&child_stack[1024]), SIGCHLD, nullptr);
 }
 
 static int GetPidCachingCloneStartRoutine(void*) {
diff --git a/tests/utils.h b/tests/utils.h
index 5fc1d93..cc1aa8c 100644
--- a/tests/utils.h
+++ b/tests/utils.h
@@ -66,6 +66,16 @@
 
 #define SKIP_WITH_HWASAN if (running_with_hwasan()) { return; }
 
+static inline void* untag_address(void* addr) {
+#if defined(__LP64__)
+  if (running_with_hwasan()) {
+    constexpr uintptr_t mask = (static_cast<uintptr_t>(1) << 56) - 1;
+    addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & mask);
+  }
+#endif
+  return addr;
+}
+
 #if defined(__linux__)
 
 #include <sys/sysmacros.h>
diff --git a/tools/Android.mk b/tools/Android.mk
deleted file mode 100644
index 4dd66fe..0000000
--- a/tools/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(call all-subdir-makefiles)