[MTE] add mte_enabled function am: fcdbee3221

Original change: https://android-review.googlesource.com/c/platform/bionic/+/3531278

Change-Id: I89cbcdee75e3c4ccf89cf5be671a21f9d3f20f14
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/README.md b/README.md
index 953e983..4b7e42d 100644
--- a/README.md
+++ b/README.md
@@ -268,36 +268,14 @@
 https://android-review.googlesource.com/c/platform/bionic/+/2073827
 
 ### Debugging tips
-1. Key error for a new codename in libc/libc.map.txt
 
-e.g. what you add in libc/libc.map.txt is:
+If a test fails to build with an undefined symbol error,
+this is most likely the _host_ reference test against glibc,
+and you need to add an `#if defined(__GLIBC__)` to the test.
+(Search for existing examples to copy & paste,
+in particular to make sure you include the `GTEST_SKIP()`.)
 
-```
-LIBC_V { # introduced=Vanilla
-  global:
-    xxx; // the new system call you add
-} LIBC_U;
-```
-
-The error output is:
-
-```
-Traceback (most recent call last):
-  File "/path/tp/out/soong/.temp/Soong.python_qucjwd7g/symbolfile/__init__.py", line 171,
-  in decode_api_level_tag
-    decoded = str(decode_api_level(value, api_map))
-  File "/path/to/out/soong/.temp/Soong.python_qucjwd7g/symbolfile/__init__.py", line 157,
-  in decode_api_level
-    return api_map[api]
-KeyError: 'Vanilla'
-```
-
-Solution: Ask in the team and wait for the update.
-
-2. Use of undeclared identifier of the new system call in the test
-
-Possible Solution: Check everything ready in the files mentioned above first.
-Maybe glibc matters. Follow the example and try #if defined(__GLIBC__).
+When we switch to musl for the host libc, this should be less of a problem.
 
 ## Updating kernel header files
 
@@ -314,9 +292,12 @@
 
 ## Updating tzdata
 
-This is handled by the libcore team, because they own icu, and that needs to be
-updated in sync with bionic). See
-[system/timezone/README.android](https://android.googlesource.com/platform/system/timezone/+/main/README.android).
+Tzdata updates are now handled by the libcore team because it needs to be
+updated in sync with icu's copy of the data, and they own that.
+
+See
+[system/timezone/README.android](https://android.googlesource.com/platform/system/timezone/+/main/README.android)
+for more information.
 
 
 ## Verifying changes
@@ -336,14 +317,68 @@
 directory `benchmarks/` containing benchmarks, and that has its own
 documentation on [running the benchmarks](benchmarks/README.md).
 
+### Building
+
+We assume you've already checked out the Android source tree.
+
+To build, make sure you're in the right directory and you've set up your environment:
+
+    $ cd main  # Or whatever you called your Android source tree.
+    $ source build/envsetup.sh
+
+Then choose an appropriate "lunch". If you're not testing on a device,
+two choices are particularly useful.
+
+If you want to be able to run tests and benchmarks directly on your x86-64
+host machine, use:
+
+    $ lunch aosp_cf_x86_64_phone-trunk_staging-userdebug
+
+Alternatively, if you want to (say) check generated arm64 code without having
+a specific device in mind, use:
+
+    $ lunch aosp_cf_arm64_phone-trunk_staging-userdebug
+
+Note that in both cases,
+these targets will also build the corresponding 32-bit variant.
+See below for where the 64-bit and 32-bit files end up.
+
+See [Build Android](https://source.android.com/docs/setup/build/building)
+for more details.
+
 ### Device tests
 
-    $ mma # In $ANDROID_ROOT/bionic.
-    $ adb root && adb remount && adb sync
+Once you've completed that setup, you can build:
+
+    $ cd bionic
+    $ mm
+
+This will build everything: bionic, the benchmarks, and the tests
+(and all the dependencies).
+
+If you want to test on a device,
+the first time after flashing your device,
+you'll need to remount the filesystems to be writable:
+
+    $ adb root
+    $ adb remount
+    $ adb reboot
+    $ adb wait-for-device
+    $ adb root
+    $ adb remount
+
+Then you can sync your locally built files across:
+
+    $ adb sync
+
+And then you can run the 32-bit tests (dynamic or static):
+
     $ adb shell /data/nativetest/bionic-unit-tests/bionic-unit-tests
     $ adb shell \
         /data/nativetest/bionic-unit-tests-static/bionic-unit-tests-static
-    # Only for 64-bit targets
+
+Or the 64-bit tests (dynamic or static):
+
     $ adb shell /data/nativetest64/bionic-unit-tests/bionic-unit-tests
     $ adb shell \
         /data/nativetest64/bionic-unit-tests-static/bionic-unit-tests-static
@@ -380,15 +415,52 @@
 ### Host tests
 
 The host tests require that you have `lunch`ed either an x86 or x86_64 target.
+
+(Obviously, in theory you could build for arm64 and run on an arm64 host,
+but we currently only support x86-64 host builds.)
+
+For example:
+
+    $ lunch aosp_cf_x86_64_phone-trunk_staging-userdebug
+
+Then build as normal:
+
+    $ cd bionic
+    $ mm
+
 Note that due to ABI limitations (specifically, the size of pthread_mutex_t),
-32-bit bionic requires PIDs less than 65536. To enforce this, set /proc/sys/kernel/pid_max
-to 65536.
+32-bit bionic requires PIDs less than 65536.
+To enforce this, set /proc/sys/kernel/pid_max to 65536.
+(The tests will remind you if you forget.)
+
+The easiest way to run is to use our provided script.
+
+To run the 32-bit tests on the host:
 
     $ ./tests/run-on-host.sh 32
-    $ ./tests/run-on-host.sh 64   # For x86_64-bit *targets* only.
+
+To run the 64-bit tests on the host:
+
+    $ ./tests/run-on-host.sh 64
 
 You can supply gtest flags as extra arguments to this script.
 
+This script starts by running build/run-on-host.sh which -- despite the name --
+is actually a script to set up your host to look more like an Android device.
+In particular, it creates a /system directory with appropriate symlinks to your
+"out" directory.
+
+An alternative is to run the static binaries directly from your "out" directory.
+
+To run the static 32-bit tests:
+
+    $ ../out/target/product/vsoc_x86_64/data/nativetest/bionic-unit-tests-static/bionic-unit-tests-static
+
+To run the static 64-bit tests:
+
+    $ ../out/target/product/vsoc_x86_64/data/nativetest64/bionic-unit-tests-static/bionic-unit-tests-static
+
+
 ### Against glibc
 
 As a way to check that our tests do in fact test the correct behavior (and not
diff --git a/TEST_MAPPING b/TEST_MAPPING
index e98c2ff..f81d348 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -19,6 +19,9 @@
       "name": "CtsGwpAsanTestCases"
     },
     {
+      "name": "CtsSeccompHostTestCases"
+    },
+    {
       "name": "CtsTaggingHostTestCases"
     },
     {
diff --git a/docs/native_allocator.md b/docs/native_allocator.md
index a97e705..75a1a70 100644
--- a/docs/native_allocator.md
+++ b/docs/native_allocator.md
@@ -140,6 +140,20 @@
 been allocator bugs that cause memory failures when too much virtual
 address space is consumed. For 64 bit executables, this can be ignored.
 
+NOTE: The default native allocator operates differently in an application
+versus command-line tools running in the shell. In order to run the same
+as an application, follow these instructions:
+
+    > adb shell
+    # export MALLOC_USE_APP_DEFAULTS=1
+    # <Run command-line benchmarks>
+
+Running without setting this environment variable can result in different
+performance and even different RSS usage for the benchmarks mentioned below.
+The environment variable has only been available since API level 36.
+Applications using different native allocator defaults than command-line
+tools has been present since API level 26 (Android O).
+
 ### Bionic Benchmarks
 These are the microbenchmarks that are part of the bionic benchmarks suite of
 benchmarks. These benchmarks can be built using this command:
diff --git a/libc/Android.bp b/libc/Android.bp
index a5bc482..f5624ef 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -223,12 +223,6 @@
         "bionic/getauxval.cpp",
     ],
     arch: {
-        arm64: {
-            srcs: ["arch-arm64/bionic/__set_tls.c"],
-        },
-        riscv64: {
-            srcs: ["arch-riscv64/bionic/__set_tls.c"],
-        },
         x86: {
             srcs: [
                 "arch-x86/bionic/__libc_init_sysinfo.cpp",
@@ -236,9 +230,6 @@
                 "arch-x86/bionic/__set_tls.cpp",
             ],
         },
-        x86_64: {
-            srcs: ["arch-x86_64/bionic/__set_tls.c"],
-        },
     },
 
     defaults: ["libc_defaults"],
@@ -790,6 +781,53 @@
 }
 
 // ========================================================
+// icu4x_bionic.a - Thin Rust wrapper around ICU4X
+// ========================================================
+
+rust_ffi_static {
+    name: "libicu4x_bionic",
+    crate_name: "icu4x_bionic",
+    crate_root: "bionic/icu4x.rs",
+    edition: "2021",
+    features: [],
+    rustlibs: [
+        "//external/rust/android-crates-io/crates/icu_casemap:libicu_casemap",
+        "//external/rust/android-crates-io/crates/icu_collections:libicu_collections",
+        "//external/rust/android-crates-io/crates/icu_properties:libicu_properties",
+    ],
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    vendor_available: true,
+    product_available: true,
+    ramdisk_available: true,
+    vendor_ramdisk_available: true,
+    recovery_available: true,
+    native_bridge_supported: true,
+    sdk_version: "minimum",
+    defaults: ["linux_bionic_supported"],
+}
+
+// current rust implementation detail; will be removed as part of a larger cleanup later
+// go/android-mto-staticlibs-in-make
+cc_rustlibs_for_make {
+    name: "libstatic_rustlibs_for_make",
+    whole_static_libs: ["libicu4x_bionic"],
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    vendor_available: true,
+    product_available: true,
+    ramdisk_available: true,
+    vendor_ramdisk_available: true,
+    recovery_available: true,
+    native_bridge_supported: true,
+    defaults: ["linux_bionic_supported"],
+}
+
+// ========================================================
 // libc_bionic.a - home-grown C library code
 // ========================================================
 
@@ -870,7 +908,6 @@
         "bionic/grp_pwd_file.cpp",
         "bionic/heap_zero_init.cpp",
         "bionic/iconv.cpp",
-        "bionic/icu_wrappers.cpp",
         "bionic/ifaddrs.cpp",
         "bionic/inotify_init.cpp",
         "bionic/ioctl.cpp",
@@ -1182,6 +1219,7 @@
     whole_static_libs: [
         "//external/llvm-libc:llvmlibc",
         "libsystemproperties",
+        "libicu4x_bionic",
     ],
 
     cppflags: ["-Wold-style-cast"],
@@ -1433,7 +1471,6 @@
         "bionic/android_mallopt.cpp",
         "bionic/gwp_asan_wrappers.cpp",
         "bionic/heap_tagging.cpp",
-        "bionic/icu.cpp",
         "bionic/malloc_common.cpp",
         "bionic/malloc_common_dynamic.cpp",
         "bionic/android_profiling_dynamic.cpp",
@@ -1452,7 +1489,6 @@
         "bionic/android_mallopt.cpp",
         "bionic/gwp_asan_wrappers.cpp",
         "bionic/heap_tagging.cpp",
-        "bionic/icu_static.cpp",
         "bionic/malloc_common.cpp",
         "bionic/malloc_limit.cpp",
     ],
@@ -2395,7 +2431,7 @@
     name: "generate_app_zygote_blocklist",
     out: ["SECCOMP_BLOCKLIST_APP_ZYGOTE.TXT"],
     srcs: ["SECCOMP_BLOCKLIST_APP.TXT"],
-    cmd: "grep -v '^int[ \t]*setresgid' $(in) > $(out)",
+    cmd: "grep -v '^setresgid' $(in) > $(out)",
 }
 
 filegroup {
@@ -2703,6 +2739,7 @@
     },
 }
 
+// TODO: add this directly to musl like libexecinfo and libb64?
 cc_library_host_static {
     name: "libfts",
     srcs: [
@@ -2715,6 +2752,7 @@
         "upstream-openbsd/android/include",
     ],
     cflags: [
+        "-std=gnu99",
         "-include openbsd-compat.h",
         "-Wno-unused-parameter",
     ],
@@ -2754,6 +2792,7 @@
         "upstream-openbsd/android/include",
     ],
     cflags: [
+        "-std=gnu99",
         "-include openbsd-compat.h",
     ],
     enabled: false,
diff --git a/libc/SECCOMP_ALLOWLIST_APP.TXT b/libc/SECCOMP_ALLOWLIST_APP.TXT
index 80b15b2..a46a33c 100644
--- a/libc/SECCOMP_ALLOWLIST_APP.TXT
+++ b/libc/SECCOMP_ALLOWLIST_APP.TXT
@@ -4,59 +4,59 @@
 # This file is processed by a python script named genseccomp.py.
 
 # Needed for debugging 32-bit Chrome
-int	pipe(int pipefd[2])	lp32
+pipe(int pipefd[2])	lp32
 
 # b/34651972
-int	access(const char *pathname, int mode)	lp32
-int	stat64(const char*, struct stat64*)	lp32
+access(const char *pathname, int mode)	lp32
+stat64(const char*, struct stat64*)	lp32
 
 # b/34813887
-int	open(const char *path, int oflag, ... ) lp32,x86_64
-int	getdents(unsigned int fd, struct linux_dirent *dirp, unsigned int count) lp32,x86_64
+open(const char *path, int oflag, ... ) lp32,x86_64
+getdents(unsigned int fd, struct linux_dirent *dirp, unsigned int count) lp32,x86_64
 
 # b/34719286
-int	eventfd(unsigned int initval, int flags)	lp32
+eventfd(unsigned int initval, int flags)	lp32
 
 # b/34817266
-int	epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)	lp32
+epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)	lp32
 
 # b/34908783
-int	epoll_create(int size)	lp32
+epoll_create(int size)	lp32
 
 # b/34979910
-int	creat(const char *pathname, mode_t mode)	lp32
-int	unlink(const char *pathname)	lp32
+creat(const char *pathname, mode_t mode)	lp32
+unlink(const char *pathname)	lp32
 
 # b/35059702
-int	lstat64(const char*, struct stat64*)	lp32
+lstat64(const char*, struct stat64*)	lp32
 
 # b/35217603
-int	fcntl(int fd, int cmd, ... /* arg */ )	lp32
-pid_t	fork()	lp32
-int	poll(struct pollfd *fds, nfds_t nfds, int timeout)	lp32
+fcntl(int fd, int cmd, ... /* arg */ )	lp32
+fork()	lp32
+poll(struct pollfd *fds, nfds_t nfds, int timeout)	lp32
 
 # b/35906875
-int	inotify_init()	lp32
-uid_t	getuid()	lp32
+inotify_init()	lp32
+getuid()	lp32
 
 # b/36435222
-int	remap_file_pages(void *addr, size_t size, int prot, size_t pgoff, int flags)	lp32
+remap_file_pages(void *addr, size_t size, int prot, size_t pgoff, int flags)	lp32
 
 # b/36449658
-int	rename(const char *oldpath, const char *newpath)	lp32
+rename(const char *oldpath, const char *newpath)	lp32
 
 # b/36726183. Note arm does not support mmap
-void*	mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)	x86
+mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)	x86
 
 # b/37769298
-int dup2(int oldfd, int newfd)	lp32
+dup2(int oldfd, int newfd)	lp32
 
 # b/62779795
-int compat_select:_newselect(int n, unsigned long* inp, unsigned long* outp, unsigned long* exp, struct timeval* timeout) lp32
+compat_select:_newselect(int n, unsigned long* inp, unsigned long* outp, unsigned long* exp, struct timeval* timeout) lp32
 
 # b/62090571
-int mkdir(const char *pathname, mode_t mode)	lp32
+mkdir(const char *pathname, mode_t mode)	lp32
 
 # Not used by bionic in U because riscv64 doesn't have it, but still
 # used by legacy apps (http://b/254179267).
-int renameat(int, const char*, int, const char*)  arm,x86,arm64,x86_64
+renameat(int, const char*, int, const char*)  arm,x86,arm64,x86_64
diff --git a/libc/SECCOMP_ALLOWLIST_COMMON.TXT b/libc/SECCOMP_ALLOWLIST_COMMON.TXT
index 5594910..b921aae 100644
--- a/libc/SECCOMP_ALLOWLIST_COMMON.TXT
+++ b/libc/SECCOMP_ALLOWLIST_COMMON.TXT
@@ -4,78 +4,78 @@
 # This file is processed by a python script named genseccomp.py.
 
 # Syscalls needed to boot android
-int	pivot_root(const char*, const char*)	lp64
-int	ioprio_get(int, int)	lp64
-int	ioprio_set(int, int, int)	lp64
+pivot_root(const char*, const char*)	lp64
+ioprio_get(int, int)	lp64
+ioprio_set(int, int, int)	lp64
 
 # Syscalls used internally by bionic, but not exposed directly.
-pid_t	gettid()	all
-int	futex(int*, int, int, const timespec*, int*, int)	all
-pid_t	clone(int (*)(void*), void*, int, void*, ...) all
-int	sigreturn(unsigned long)	lp32
-int	rt_sigreturn(unsigned long)	all
-int	rt_tgsigqueueinfo(pid_t, pid_t, int, siginfo_t*)	all
-int	restart_syscall()	all
+gettid()	all
+futex(int*, int, int, const timespec*, int*, int)	all
+clone(int (*)(void*), void*, int, void*, ...) all
+sigreturn(unsigned long)	lp32
+rt_sigreturn(unsigned long)	all
+rt_tgsigqueueinfo(pid_t, pid_t, int, siginfo_t*)	all
+restart_syscall()	all
 
 # The public API doesn't set errno, so we call this via inline assembler.
-int riscv_hwprobe(riscv_hwprobe*, size_t, size_t, unsigned long*, unsigned) riscv64
+riscv_hwprobe(riscv_hwprobe*, size_t, size_t, unsigned long*, unsigned) riscv64
 
 # vfork is used by bionic (and java.lang.ProcessBuilder) on some
 # architectures. (The others use clone(2) directly instead.)
-pid_t	vfork()	arm,x86,x86_64
+vfork()	arm,x86,x86_64
 
 # Needed for performance tools.
-int	perf_event_open(perf_event_attr*, pid_t, int, int, unsigned long)	all
+perf_event_open(perf_event_attr*, pid_t, int, int, unsigned long)	all
 
 # Needed for strace.
-int	tkill(int, int)	all
+tkill(int, int)	all
 
 # Needed for a CTS test of seccomp (b/34763393).
-int	seccomp(unsigned, unsigned, void*)	all
+seccomp(unsigned, unsigned, void*)	all
 
 # TODO: remove these now we've updated the toolchain (http://b/229989971).
-int open(const char*, int, ...)  arm,x86,x86_64
-int stat64(const char*, stat64*)  arm,x86
-ssize_t readlink(const char*, char*, size_t)  arm,x86,x86_64
-int stat(const char*, stat*)  arm,x86,x86_64
+open(const char*, int, ...)  arm,x86,x86_64
+stat64(const char*, stat64*)  arm,x86
+readlink(const char*, char*, size_t)  arm,x86,x86_64
+stat(const char*, stat*)  arm,x86,x86_64
 
 #
 # (Potentially) useful new syscalls which we don't yet use in bionic.
 #
 
 # Since Linux 2.5, not in glibc.
-int io_setup(unsigned, aio_context_t*) all
-int io_destroy(aio_context_t) all
-int io_submit(aio_context_t, long,  iocb**) all
-int io_getevents(aio_context_t, long, long, io_event*, timespec*) all
-int io_cancel(aio_context_t, iocb*, io_event*) all
+io_setup(unsigned, aio_context_t*) all
+io_destroy(aio_context_t) all
+io_submit(aio_context_t, long,  iocb**) all
+io_getevents(aio_context_t, long, long, io_event*, timespec*) all
+io_cancel(aio_context_t, iocb*, io_event*) all
 # Since Linux 3.19, not in glibc (and not really needed to implement fexecve).
-int execveat(int, const char*, char* const*, char* const*, int)  all
+execveat(int, const char*, char* const*, char* const*, int)  all
 # Since Linux 4.3, not in glibc. Probed for and conditionally used by ART.
-int membarrier(int, int) all
-int userfaultfd(int) all
+membarrier(int, int) all
+userfaultfd(int) all
 # Since Linux 5.1, not in glibc. Not used by bionic, and not likely ever
 # to be (because the last thing anyone needs is a new 32-bit ABI in the
 # 2020s!) but http://b/138781460 showed cuttlefish needed at least the
 # clock_gettime64 syscall.
-int clock_gettime64(clockid_t, timespec64*) lp32
-int clock_settime64(clockid_t, const timespec64*) lp32
-int clock_adjtime64(clockid_t, timex64*) lp32
-int clock_getres_time64(clockid_t, timespec64*) lp32
-int clock_nanosleep_time64(clockid_t, int, const timespec64*, timespec*) lp32
-int timer_gettime64(__kernel_timer_t, itimerspec64*) lp32
-int timer_settime64(__kernel_timer_t, int, const itimerspec64*, itimerspec64*) lp32
-int timerfd_gettime64(int, itimerspec64*) lp32
-int timerfd_settime64(int, int, const itimerspec64*, itimerspec64*) lp32
-int utimensat_time64(int, const char*, const timespec64[2], int) lp32
-int pselect6_time64(int, fd_set*, fd_set*, timespec64*, void*) lp32
-int ppoll_time64(pollfd*, unsigned int, timespec64*, const sigset64_t*, size_t) lp32
-int recvmmsg_time64(int, mmsghdr*, unsigned int, int, const timespec64*) lp32
-int rt_sigtimedwait_time64(const sigset64_t*, siginfo_t*, const timespec64*, size_t) lp32
-int futex_time64(int*, int, int, const timespec64*, int*, int) lp32
-int sched_rr_get_interval_time64(pid_t, timespec64*) lp32
+clock_gettime64(clockid_t, timespec64*) lp32
+clock_settime64(clockid_t, const timespec64*) lp32
+clock_adjtime64(clockid_t, timex64*) lp32
+clock_getres_time64(clockid_t, timespec64*) lp32
+clock_nanosleep_time64(clockid_t, int, const timespec64*, timespec*) lp32
+timer_gettime64(__kernel_timer_t, itimerspec64*) lp32
+timer_settime64(__kernel_timer_t, int, const itimerspec64*, itimerspec64*) lp32
+timerfd_gettime64(int, itimerspec64*) lp32
+timerfd_settime64(int, int, const itimerspec64*, itimerspec64*) lp32
+utimensat_time64(int, const char*, const timespec64[2], int) lp32
+pselect6_time64(int, fd_set*, fd_set*, timespec64*, void*) lp32
+ppoll_time64(pollfd*, unsigned int, timespec64*, const sigset64_t*, size_t) lp32
+recvmmsg_time64(int, mmsghdr*, unsigned int, int, const timespec64*) lp32
+rt_sigtimedwait_time64(const sigset64_t*, siginfo_t*, const timespec64*, size_t) lp32
+futex_time64(int*, int, int, const timespec64*, int*, int) lp32
+sched_rr_get_interval_time64(pid_t, timespec64*) lp32
 # Since Linux 5.3, not in glibc. Not used by bionic, but increasingly
 # likely to be useful as new features are added. In particular, cgroups
 # support seems potentially useful for Android (though the struct that
 # changes size over time is obviously problematic).
-pid_t clone3(clone_args*, size_t) all
+clone3(clone_args*, size_t) all
diff --git a/libc/SECCOMP_ALLOWLIST_SYSTEM.TXT b/libc/SECCOMP_ALLOWLIST_SYSTEM.TXT
index 756affe..ac90aac 100644
--- a/libc/SECCOMP_ALLOWLIST_SYSTEM.TXT
+++ b/libc/SECCOMP_ALLOWLIST_SYSTEM.TXT
@@ -3,4 +3,4 @@
 #
 # This file is processed by a python script named genseccomp.py.
 
-int bpf(int cmd, union bpf_attr *attr, unsigned int size) all
+bpf(int cmd, union bpf_attr *attr, unsigned int size) all
diff --git a/libc/SECCOMP_BLOCKLIST_APP.TXT b/libc/SECCOMP_BLOCKLIST_APP.TXT
index b9ecc02..5c317cf 100644
--- a/libc/SECCOMP_BLOCKLIST_APP.TXT
+++ b/libc/SECCOMP_BLOCKLIST_APP.TXT
@@ -11,40 +11,40 @@
 # before uid change, 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.
-int     setgid32(gid_t)     lp32
-int     setgid(gid_t)       lp64
-int     setuid32(uid_t)    lp32
-int     setuid(uid_t)      lp64
-int     setregid32(gid_t, gid_t)  lp32
-int     setregid(gid_t, gid_t)    lp64
-int     setreuid32(uid_t, uid_t)   lp32
-int     setreuid(uid_t, uid_t)     lp64
-int     setresgid32(gid_t, gid_t, gid_t)   lp32
-int     setresgid(gid_t, gid_t, gid_t)     lp64
+setgid32(gid_t)     lp32
+setgid(gid_t)       lp64
+setuid32(uid_t)    lp32
+setuid(uid_t)      lp64
+setregid32(gid_t, gid_t)  lp32
+setregid(gid_t, gid_t)    lp64
+setreuid32(uid_t, uid_t)   lp32
+setreuid(uid_t, uid_t)     lp64
+setresgid32(gid_t, gid_t, gid_t)   lp32
+setresgid(gid_t, gid_t, gid_t)     lp64
 # setresuid is explicitly allowed, see above.
-int     setfsgid32(gid_t) lp32
-int     setfsgid(gid_t)   lp64
-int     setfsuid32(uid_t) lp32
-int     setfsuid(uid_t)   lp64
-int     setgroups32(int, const gid_t*)   lp32
-int     setgroups(int, const gid_t*)     lp64
+setfsgid32(gid_t) lp32
+setfsgid(gid_t)   lp64
+setfsuid32(uid_t) lp32
+setfsuid(uid_t)   lp64
+setgroups32(int, const gid_t*)   lp32
+setgroups(int, const gid_t*)     lp64
 
 # Syscalls to modify times.
-int     adjtimex(struct timex*)   all
-int     clock_adjtime(clockid_t, struct timex*)   all
-int     clock_settime(clockid_t, const struct timespec*)  all
-int     settimeofday(const struct timeval*, const struct timezone*)   all
+adjtimex(struct timex*)   all
+clock_adjtime(clockid_t, struct timex*)   all
+clock_settime(clockid_t, const struct timespec*)  all
+settimeofday(const struct timeval*, const struct timezone*)   all
 
-int     acct(const char*  filepath)  all
-int     syslog(int, char*, int)   all
-int     chroot(const char*)  all
+acct(const char*  filepath)  all
+syslog(int, char*, int)   all
+chroot(const char*)  all
 
-int     init_module(void*, unsigned long, const char*)  all
-int     delete_module(const char*, unsigned int)   all
-int     mount(const char*, const char*, const char*, unsigned long, const void*)  all
-int     umount2(const char*, int)  all
-int     swapon(const char*, int) all
-int     swapoff(const char*) all
-int     setdomainname(const char*, size_t)  all
-int     sethostname(const char*, size_t)  all
-int     reboot(int, int, int, void*)  all
+init_module(void*, unsigned long, const char*)  all
+delete_module(const char*, unsigned int)   all
+mount(const char*, const char*, const char*, unsigned long, const void*)  all
+umount2(const char*, int)  all
+swapon(const char*, int) all
+swapoff(const char*) all
+setdomainname(const char*, size_t)  all
+sethostname(const char*, size_t)  all
+reboot(int, int, int, void*)  all
diff --git a/libc/SECCOMP_BLOCKLIST_COMMON.TXT b/libc/SECCOMP_BLOCKLIST_COMMON.TXT
index 22c9844..0c6e1ae 100644
--- a/libc/SECCOMP_BLOCKLIST_COMMON.TXT
+++ b/libc/SECCOMP_BLOCKLIST_COMMON.TXT
@@ -6,5 +6,5 @@
 #
 # This file is processed by a python script named genseccomp.py.
 
-int     swapon(const char*, int) all
-int     swapoff(const char*) all
+swapon(const char*, int) all
+swapoff(const char*) all
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index 1506e13..31651cd 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -1,385 +1,392 @@
 # This file is used to automatically generate bionic's system call stubs.
 #
+# It is processed by a python script named gensyscalls.py,
+# normally run via the genrules in libc/Android.bp.
+#
 # Each non-blank, non-comment line has the following format:
 #
-# return_type func_name[|alias_list][:syscall_name[:socketcall_id]]([parameter_list]) arch_list
+#     func_name[|alias_list][:syscall_name[:socketcall_id]]([parameter_list]) arch_list
 #
 # where:
-#       arch_list ::= "all" | arches
-#       arches    ::= arch |  arch "," arches
-#       arch      ::= "arm" | "arm64" | "riscv64" | "x86" | "x86_64" | "lp32" | "lp64"
+#     arch_list ::= "all" | arches
+#     arches    ::= arch |  arch "," arches
+#     arch      ::= "arm" | "arm64" | "riscv64" | "x86" | "x86_64" | "lp32" | "lp64"
 #
-# Note:
-#      - syscall_name corresponds to the name of the syscall, which may differ from
-#        the exported function name (example: the exit syscall is implemented by the _exit()
-#        function, which is not the same as the standard C exit() function which calls it)
+# syscall_name corresponds to the name of the syscall, which may differ from
+# the exported function name func_name. For example: the exit_group syscall
+# is exported by libc as the _exit() function, not exit() (which does more
+# work before calling _exit()).
 #
-#      - alias_list is optional comma separated list of function aliases.
+# alias_list is optional comma-separated list of function aliases.
+# For example, the traditional _exit() function has a C99 alias _Exit().
 #
-#      - The call_id parameter, given that func_name and syscall_name have
-#        been provided, allows the user to specify dispatch style syscalls.
-#        For example, socket() syscall on i386 actually becomes:
-#          socketcall(__NR_socket, 1, *(rest of args on stack)).
+# No return type is specified, because it's not needed.
 #
-#      - Each parameter type is assumed to be stored in 32 bits.
+# The socketcall_id parameter supports x86's
+# https://man7.org/linux/man-pages/man2/socketcall.2.html
+# and can be ignored for all other syscalls and architectures.
 #
-# This file is processed by a python script named gensyscalls.py, run via
-# genrules in Android.bp.
+# The number of registers required for the arguments is computed by the script,
+# based on the parameter list given here. It handles the need for register
+# pairs for 64-bit arguments on ILP32, and also arm32's requirement for such
+# pairs to start on an even register. This means that it's important to get
+# these types right!
+#
 
 # Calls that have historical 16-bit variants camping on the best names (CONFIG_UID16).
-uid_t getuid:getuid32()   lp32
-uid_t getuid:getuid()     lp64
-gid_t getgid:getgid32()   lp32
-gid_t getgid:getgid()     lp64
-uid_t geteuid:geteuid32() lp32
-uid_t geteuid:geteuid()   lp64
-gid_t getegid:getegid32() lp32
-gid_t getegid:getegid()   lp64
-uid_t getresuid:getresuid32(uid_t* ruid, uid_t* euid, uid_t* suid) lp32
-uid_t getresuid:getresuid(uid_t* ruid, uid_t* euid, uid_t* suid)   lp64
-gid_t getresgid:getresgid32(gid_t* rgid, gid_t* egid, gid_t* sgid) lp32
-gid_t getresgid:getresgid(gid_t* rgid, gid_t* egid, gid_t* sgid)   lp64
-int getgroups:getgroups32(int, gid_t*) lp32
-int getgroups:getgroups(int, gid_t*)   lp64
-int setgid:setgid32(gid_t) lp32
-int setgid:setgid(gid_t)   lp64
-int setuid:setuid32(uid_t) lp32
-int setuid:setuid(uid_t)   lp64
-int setreuid:setreuid32(uid_t, uid_t) lp32
-int setreuid:setreuid(uid_t, uid_t)   lp64
-int setresuid:setresuid32(uid_t, uid_t, uid_t) lp32
-int setresuid:setresuid(uid_t, uid_t, uid_t)   lp64
-int setresgid:setresgid32(gid_t, gid_t, gid_t) lp32
-int setresgid:setresgid(gid_t, gid_t, gid_t)   lp64
-int setfsgid:setfsgid32(gid_t) lp32
-int setfsgid:setfsgid(gid_t)   lp64
-int setfsuid:setfsuid32(uid_t) lp32
-int setfsuid:setfsuid(uid_t)   lp64
+getuid:getuid32()   lp32
+getuid:getuid()     lp64
+getgid:getgid32()   lp32
+getgid:getgid()     lp64
+geteuid:geteuid32() lp32
+geteuid:geteuid()   lp64
+getegid:getegid32() lp32
+getegid:getegid()   lp64
+getresuid:getresuid32(uid_t* ruid, uid_t* euid, uid_t* suid) lp32
+getresuid:getresuid(uid_t* ruid, uid_t* euid, uid_t* suid)   lp64
+getresgid:getresgid32(gid_t* rgid, gid_t* egid, gid_t* sgid) lp32
+getresgid:getresgid(gid_t* rgid, gid_t* egid, gid_t* sgid)   lp64
+getgroups:getgroups32(int, gid_t*) lp32
+getgroups:getgroups(int, gid_t*)   lp64
+setgid:setgid32(gid_t) lp32
+setgid:setgid(gid_t)   lp64
+setuid:setuid32(uid_t) lp32
+setuid:setuid(uid_t)   lp64
+setreuid:setreuid32(uid_t, uid_t) lp32
+setreuid:setreuid(uid_t, uid_t)   lp64
+setresuid:setresuid32(uid_t, uid_t, uid_t) lp32
+setresuid:setresuid(uid_t, uid_t, uid_t)   lp64
+setresgid:setresgid32(gid_t, gid_t, gid_t) lp32
+setresgid:setresgid(gid_t, gid_t, gid_t)   lp64
+setfsgid:setfsgid32(gid_t) lp32
+setfsgid:setfsgid(gid_t)   lp64
+setfsuid:setfsuid32(uid_t) lp32
+setfsuid:setfsuid(uid_t)   lp64
 
-ssize_t readahead(int, off64_t, size_t) all
-pid_t getpgid(pid_t) all
-pid_t getppid() all
-pid_t getsid(pid_t) all
-pid_t setsid() all
-int kill(pid_t, int) all
-int tgkill(pid_t tgid, pid_t tid, int sig) all
+readahead(int, off64_t, size_t) all
+getpgid(pid_t) all
+getppid() all
+getsid(pid_t) all
+setsid() all
+kill(pid_t, int) all
+tgkill(pid_t tgid, pid_t tid, int sig) all
 
-void* __brk:brk(void*) all
-int execve(const char*, char* const*, char* const*)  all
-int __ptrace:ptrace(int request, int pid, void* addr, void* data) all
+__brk:brk(void*) all
+execve(const char*, char* const*, char* const*)  all
+__ptrace:ptrace(int request, int pid, void* addr, void* data) all
 
 # <sys/resource.h>
-int getrusage(int, struct rusage*)  all
-int __getpriority:getpriority(int, id_t)  all
-int setpriority(int, id_t, int)   all
+getrusage(int, struct rusage*)  all
+__getpriority:getpriority(int, id_t)  all
+setpriority(int, id_t, int)   all
 # On LP64, rlimit and rlimit64 are the same.
 # On 32-bit systems we use prlimit64 to implement the rlimit64 functions.
-int getrlimit:ugetrlimit(int, struct rlimit*)  lp32
-int getrlimit|getrlimit64(int, struct rlimit*)  lp64
-int setrlimit(int, const struct rlimit*)  lp32
-int setrlimit|setrlimit64(int, const struct rlimit*)  lp64
-int prlimit64|prlimit(pid_t, int, struct rlimit64*, const struct rlimit64*)  lp64
-int prlimit64(pid_t, int, struct rlimit64*, const struct rlimit64*)  lp32
+getrlimit:ugetrlimit(int, struct rlimit*)  lp32
+getrlimit|getrlimit64(int, struct rlimit*)  lp64
+setrlimit(int, const struct rlimit*)  lp32
+setrlimit|setrlimit64(int, const struct rlimit*)  lp64
+prlimit64|prlimit(pid_t, int, struct rlimit64*, const struct rlimit64*)  lp64
+prlimit64(pid_t, int, struct rlimit64*, const struct rlimit64*)  lp32
 
-int     setgroups:setgroups32(int, const gid_t*)   lp32
-int     setgroups:setgroups(int, const gid_t*)     lp64
-int     setpgid(pid_t, pid_t)  all
-int     setregid:setregid32(gid_t, gid_t)  lp32
-int     setregid:setregid(gid_t, gid_t)    lp64
-int     chroot(const char*)  all
-int     prctl(int, unsigned long, unsigned long, unsigned long, unsigned long) all
-int     capget(cap_user_header_t header, cap_user_data_t data) all
-int     capset(cap_user_header_t header, const cap_user_data_t data) all
-int     sigaltstack(const stack_t*, stack_t*) all
-int     acct(const char*  filepath)  all
+setgroups:setgroups32(int, const gid_t*)   lp32
+setgroups:setgroups(int, const gid_t*)     lp64
+setpgid(pid_t, pid_t)  all
+setregid:setregid32(gid_t, gid_t)  lp32
+setregid:setregid(gid_t, gid_t)    lp64
+chroot(const char*)  all
+prctl(int, unsigned long, unsigned long, unsigned long, unsigned long) all
+capget(cap_user_header_t header, cap_user_data_t data) all
+capset(cap_user_header_t header, const cap_user_data_t data) all
+sigaltstack(const stack_t*, stack_t*) all
+acct(const char*  filepath)  all
 
 # file descriptors
-ssize_t     read(int, void*, size_t)        all
-ssize_t     write(int, const void*, size_t)       all
-ssize_t     pread64(int, void*, size_t, off64_t) lp32
-ssize_t     pread64|pread(int, void*, size_t, off_t) lp64
-ssize_t     pwrite64(int, void*, size_t, off64_t) lp32
-ssize_t     pwrite64|pwrite(int, void*, size_t, off_t) lp64
+read(int, void*, size_t)        all
+write(int, const void*, size_t)       all
+pread64(int, void*, size_t, off64_t) lp32
+pread64|pread(int, void*, size_t, off_t) lp64
+pwrite64(int, void*, size_t, off64_t) lp32
+pwrite64|pwrite(int, void*, size_t, off_t) lp64
 
 # On LP32, preadv/pwritev don't use off64_t --- they use pairs of 32-bit
 # arguments to avoid problems on architectures like arm32 where 64-bit arguments
 # must be in a register pair starting with an even-numbered register.
 # See linux/fs/read_write.c and https://lwn.net/Articles/311630/.
 # Note that there's an unused always-0 second long even on LP64!
-ssize_t     __preadv64:preadv(int, const struct iovec*, int, long, long) all
-ssize_t     __pwritev64:pwritev(int, const struct iovec*, int, long, long) all
-ssize_t     __preadv64v2:preadv2(int, const struct iovec*, int, long, long, int) all
-ssize_t     __pwritev64v2:pwritev2(int, const struct iovec*, int, long, long, int) all
+__preadv64:preadv(int, const struct iovec*, int, long, long) all
+__pwritev64:pwritev(int, const struct iovec*, int, long, long) all
+__preadv64v2:preadv2(int, const struct iovec*, int, long, long, int) all
+__pwritev64v2:pwritev2(int, const struct iovec*, int, long, long, int) all
 
-int         __close:close(int)  all
-int         close_range(unsigned int, unsigned int, int) all
-ssize_t     copy_file_range(int, off64_t*, int, off64_t*, size_t, unsigned int) all
-pid_t       __getpid:getpid()  all
-int memfd_create(const char*, unsigned) all
-int         munmap(void*, size_t)  all
-int         msync(const void*, size_t, int)    all
-int         mprotect(const void*, size_t, int)  all
-int         madvise(void*, size_t, int)  all
-ssize_t     process_madvise(int, const struct iovec*, size_t, int, unsigned int)     all
-int mlock(const void* addr, size_t len)    all
-int mlock2(const void* addr, size_t len, int flags)    all
-int         munlock(const void* addr, size_t len)   all
-int         mlockall(int flags)   all
-int mseal(void*, size_t, unsigned long) lp64
-int         munlockall()   all
-int         mincore(void*  start, size_t  length, unsigned char*  vec)   all
-int         __ioctl:ioctl(int, int, void*)  all
-ssize_t     readv(int, const struct iovec*, int)   all
-ssize_t     writev(int, const struct iovec*, int)  all
-int         __fcntl64:fcntl64(int, int, void*)  lp32
-int         __fcntl:fcntl(int, int, void*)  lp64
-int         flock(int, int)   all
-int         __fchmod:fchmod(int, mode_t)  all
-int         __pipe2:pipe2(int*, int) all
-int         __dup:dup(int)  all
-int         __dup3:dup3(int, int, int)   all
-int         fsync(int)  all
-int         fdatasync(int) all
-int         fchown:fchown32(int, uid_t, gid_t)  lp32
-int         fchown:fchown(int, uid_t, gid_t)    lp64
-void        sync(void)  all
-int         syncfs(int)  all
-int         __fsetxattr:fsetxattr(int, const char*, const void*, size_t, int) all
-ssize_t     __fgetxattr:fgetxattr(int, const char*, void*, size_t) all
-ssize_t     __flistxattr:flistxattr(int, char*, size_t) all
-int         fremovexattr(int, const char*) all
+__close:close(int)  all
+close_range(unsigned int, unsigned int, int) all
+copy_file_range(int, off64_t*, int, off64_t*, size_t, unsigned int) all
+__getpid:getpid()  all
+memfd_create(const char*, unsigned) all
+munmap(void*, size_t)  all
+msync(const void*, size_t, int)    all
+mprotect(const void*, size_t, int)  all
+madvise(void*, size_t, int)  all
+process_madvise(int, const struct iovec*, size_t, int, unsigned int)     all
+mlock(const void* addr, size_t len)    all
+mlock2(const void* addr, size_t len, int flags)    all
+munlock(const void* addr, size_t len)   all
+mlockall(int flags)   all
+mseal(void*, size_t, unsigned long) lp64
+munlockall()   all
+mincore(void*  start, size_t  length, unsigned char*  vec)   all
+__ioctl:ioctl(int, int, void*)  all
+readv(int, const struct iovec*, int)   all
+writev(int, const struct iovec*, int)  all
+__fcntl64:fcntl64(int, int, void*)  lp32
+__fcntl:fcntl(int, int, void*)  lp64
+flock(int, int)   all
+__fchmod:fchmod(int, mode_t)  all
+__pipe2:pipe2(int*, int) all
+__dup:dup(int)  all
+__dup3:dup3(int, int, int)   all
+fsync(int)  all
+fdatasync(int) all
+fchown:fchown32(int, uid_t, gid_t)  lp32
+fchown:fchown(int, uid_t, gid_t)    lp64
+sync(void)  all
+syncfs(int)  all
+__fsetxattr:fsetxattr(int, const char*, const void*, size_t, int) all
+__fgetxattr:fgetxattr(int, const char*, void*, size_t) all
+__flistxattr:flistxattr(int, char*, size_t) all
+fremovexattr(int, const char*) all
 
-int __getdents64:getdents64(unsigned int, struct dirent*, unsigned int)   all
+__getdents64:getdents64(unsigned int, struct dirent*, unsigned int)   all
 
-int __openat:openat(int, const char*, int, mode_t) all
-int __faccessat:faccessat(int, const char*, int)  all
-int __fchmodat:fchmodat(int, const char*, mode_t)  all
-int fchownat(int, const char*, uid_t, gid_t, int)  all
-int fstatat64|fstatat:fstatat64(int, const char*, struct stat*, int)   lp32
-int fstatat64|fstatat:newfstatat(int, const char*, struct stat*, int)  lp64
-int linkat(int, const char*, int, const char*, int)  all
-int mkdirat(int, const char*, mode_t)  all
-int mknodat(int, const char*, mode_t, dev_t)  all
-ssize_t readlinkat(int, const char*, char*, size_t)  all
-int renameat2(int, const char*, int, const char*, unsigned)  all
-int symlinkat(const char*, int, const char*)  all
-int unlinkat(int, const char*, int)   all
-int utimensat(int, const char*, const struct timespec times[2], int)  all
+__openat:openat(int, const char*, int, mode_t) all
+__faccessat:faccessat(int, const char*, int)  all
+__fchmodat:fchmodat(int, const char*, mode_t)  all
+fchownat(int, const char*, uid_t, gid_t, int)  all
+fstatat64|fstatat:fstatat64(int, const char*, struct stat*, int)   lp32
+fstatat64|fstatat:newfstatat(int, const char*, struct stat*, int)  lp64
+linkat(int, const char*, int, const char*, int)  all
+mkdirat(int, const char*, mode_t)  all
+mknodat(int, const char*, mode_t, dev_t)  all
+readlinkat(int, const char*, char*, size_t)  all
+renameat2(int, const char*, int, const char*, unsigned)  all
+symlinkat(const char*, int, const char*)  all
+unlinkat(int, const char*, int)   all
+utimensat(int, const char*, const struct timespec times[2], int)  all
 
 # Paired off_t/off64_t system calls. On 64-bit systems,
 # sizeof(off_t) == sizeof(off64_t), so there we emit two symbols that are
 # aliases. On 32-bit systems, we have two different system calls.
 # That means that every system call in this section should take three lines.
-off_t lseek(int, off_t, int) lp32
-int __llseek:_llseek(int, unsigned long, unsigned long, off64_t*, int) lp32
-off_t lseek|lseek64(int, off_t, int) lp64
-ssize_t sendfile(int out_fd, int in_fd, off_t* offset, size_t count) lp32
-ssize_t sendfile64(int out_fd, int in_fd, off64_t* offset, size_t count) lp32
-ssize_t sendfile|sendfile64(int out_fd, int in_fd, off_t* offset, size_t count) lp64
-int truncate(const char*, off_t) lp32
-int truncate64(const char*, off64_t) lp32
-int truncate|truncate64(const char*, off_t) lp64
+lseek(int, off_t, int) lp32
+__llseek:_llseek(int, unsigned long, unsigned long, off64_t*, int) lp32
+lseek|lseek64(int, off_t, int) lp64
+sendfile(int out_fd, int in_fd, off_t* offset, size_t count) lp32
+sendfile64(int out_fd, int in_fd, off64_t* offset, size_t count) lp32
+sendfile|sendfile64(int out_fd, int in_fd, off_t* offset, size_t count) lp64
+truncate(const char*, off_t) lp32
+truncate64(const char*, off64_t) lp32
+truncate|truncate64(const char*, off_t) lp64
 # (fallocate only gets two lines because there is no 32-bit variant.)
-int fallocate64:fallocate(int, int, off64_t, off64_t) lp32
-int fallocate|fallocate64(int, int, off_t, off_t) lp64
+fallocate64:fallocate(int, int, off64_t, off64_t) lp32
+fallocate|fallocate64(int, int, off_t, off_t) lp64
 # (ftruncate only gets two lines because 32-bit bionic only uses the 64-bit call.)
-int ftruncate64(int, off64_t) lp32
-int ftruncate|ftruncate64(int, off_t) lp64
+ftruncate64(int, off64_t) lp32
+ftruncate|ftruncate64(int, off_t) lp64
 # (mmap only gets two lines because 32-bit bionic only uses the 64-bit call.)
-void* __mmap2:mmap2(void*, size_t, int, int, int, long) lp32
-void* mmap|mmap64(void*, size_t, int, int, int, off_t) lp64
+__mmap2:mmap2(void*, size_t, int, int, int, long) lp32
+mmap|mmap64(void*, size_t, int, int, int, off_t) lp64
 
 # mremap is in C++ for 32-bit so we can add the PTRDIFF_MAX check.
-void* __mremap:mremap(void*, size_t, size_t, int, void*) lp32
-void* mremap(void*, size_t, size_t, int, void*) lp64
+__mremap:mremap(void*, size_t, size_t, int, void*) lp32
+mremap(void*, size_t, size_t, int, void*) lp64
 
 # posix_fadvise64 is awkward: arm has shuffled arguments,
 # the POSIX functions don't set errno, and no architecture has posix_fadvise.
-int __arm_fadvise64_64:arm_fadvise64_64(int, int, off64_t, off64_t) arm
-int __fadvise64:fadvise64_64(int, off64_t, off64_t, int) x86
-int __fadvise64:fadvise64(int, off64_t, off64_t, int) lp64
+__arm_fadvise64_64:arm_fadvise64_64(int, int, off64_t, off64_t) arm
+__fadvise64:fadvise64_64(int, off64_t, off64_t, int) x86
+__fadvise64:fadvise64(int, off64_t, off64_t, int) lp64
 
-int __fstatfs64:fstatfs64(int, size_t, struct statfs*)  lp32
-int __fstatfs:fstatfs(int, struct statfs*)  lp64
-int __statfs64:statfs64(const char*, size_t, struct statfs*)  lp32
-int __statfs:statfs(const char*, struct statfs*)  lp64
+__fstatfs64:fstatfs64(int, size_t, struct statfs*)  lp32
+__fstatfs:fstatfs(int, struct statfs*)  lp64
+__statfs64:statfs64(const char*, size_t, struct statfs*)  lp32
+__statfs:statfs(const char*, struct statfs*)  lp64
 
-int fstat64|fstat:fstat64(int, struct stat*) lp32
-int fstat64|fstat:fstat(int, struct stat*) lp64
+fstat64|fstat:fstat64(int, struct stat*) lp32
+fstat64|fstat:fstat(int, struct stat*) lp64
 
 # file system
-int     chdir(const char*)              all
-int     mount(const char*, const char*, const char*, unsigned long, const void*)  all
-int     umount2(const char*, int)  all
-int     __getcwd:getcwd(char* buf, size_t size)  all
-int     fchdir(int)    all
-int     setxattr(const char*, const char*, const void*, size_t, int) all
-int     lsetxattr(const char*, const char*, const void*, size_t, int) all
-ssize_t getxattr(const char*, const char*, void*, size_t) all
-ssize_t lgetxattr(const char*, const char*, void*, size_t) all
-ssize_t listxattr(const char*, char*, size_t) all
-ssize_t llistxattr(const char*, char*, size_t) all
-int     removexattr(const char*, const char*) all
-int     lremovexattr(const char*, const char*) all
-int statx(int, const char*, int, unsigned, struct statx*) all
-int     swapon(const char*, int) all
-int     swapoff(const char*) all
+chdir(const char*)              all
+mount(const char*, const char*, const char*, unsigned long, const void*)  all
+umount2(const char*, int)  all
+__getcwd:getcwd(char* buf, size_t size)  all
+fchdir(int)    all
+setxattr(const char*, const char*, const void*, size_t, int) all
+lsetxattr(const char*, const char*, const void*, size_t, int) all
+getxattr(const char*, const char*, void*, size_t) all
+lgetxattr(const char*, const char*, void*, size_t) all
+listxattr(const char*, char*, size_t) all
+llistxattr(const char*, char*, size_t) all
+removexattr(const char*, const char*) all
+lremovexattr(const char*, const char*) all
+statx(int, const char*, int, unsigned, struct statx*) all
+swapon(const char*, int) all
+swapoff(const char*) all
 
 # time
-int           settimeofday(const struct timeval*, const struct timezone*)   all
-clock_t       times(struct tms*)       all
-int           nanosleep(const struct timespec*, struct timespec*)   all
-int           clock_settime(clockid_t, const struct timespec*)  all
-int           __clock_nanosleep:clock_nanosleep(clockid_t, int, const struct timespec*, struct timespec*)  all
-int           getitimer(int, struct itimerval*)   all
-int           setitimer(int, const struct itimerval*, struct itimerval*)  all
-int           __timer_create:timer_create(clockid_t clockid, struct sigevent* evp, __kernel_timer_t* timerid)    all
-int           __timer_settime:timer_settime(__kernel_timer_t, int, const struct itimerspec*, struct itimerspec*) all
-int           __timer_gettime:timer_gettime(__kernel_timer_t, struct itimerspec*)                                all
-int           __timer_getoverrun:timer_getoverrun(__kernel_timer_t)                                              all
-int           __timer_delete:timer_delete(__kernel_timer_t)                                                      all
-int           timerfd_create(clockid_t, int)   all
-int           timerfd_settime(int, int, const struct itimerspec*, struct itimerspec*)   all
-int           timerfd_gettime(int, struct itimerspec*)   all
-int           adjtimex(struct timex*)   all
-int           clock_adjtime(clockid_t, struct timex*)   all
+settimeofday(const struct timeval*, const struct timezone*)   all
+times(struct tms*)       all
+nanosleep(const struct timespec*, struct timespec*)   all
+clock_settime(clockid_t, const struct timespec*)  all
+__clock_nanosleep:clock_nanosleep(clockid_t, int, const struct timespec*, struct timespec*)  all
+getitimer(int, struct itimerval*)   all
+setitimer(int, const struct itimerval*, struct itimerval*)  all
+__timer_create:timer_create(clockid_t clockid, struct sigevent* evp, __kernel_timer_t* timerid)    all
+__timer_settime:timer_settime(__kernel_timer_t, int, const struct itimerspec*, struct itimerspec*) all
+__timer_gettime:timer_gettime(__kernel_timer_t, struct itimerspec*)                                all
+__timer_getoverrun:timer_getoverrun(__kernel_timer_t)                                              all
+__timer_delete:timer_delete(__kernel_timer_t)                                                      all
+timerfd_create(clockid_t, int)   all
+timerfd_settime(int, int, const struct itimerspec*, struct itimerspec*)   all
+timerfd_gettime(int, struct itimerspec*)   all
+adjtimex(struct timex*)   all
+clock_adjtime(clockid_t, struct timex*)   all
 
 # signals
-int     __sigaction:sigaction(int, const struct sigaction*, struct sigaction*)  lp32
-int     __rt_sigaction:rt_sigaction(int, const struct sigaction*, struct sigaction*, size_t)  all
-int     __rt_sigpending:rt_sigpending(sigset64_t*, size_t)  all
-int     __rt_sigprocmask:rt_sigprocmask(int, const sigset64_t*, sigset64_t*, size_t)  all
-int     __rt_sigsuspend:rt_sigsuspend(const sigset64_t*, size_t)  all
-int     __rt_sigtimedwait:rt_sigtimedwait(const sigset64_t*, siginfo_t*, const timespec*, size_t)  all
-int     __rt_sigqueueinfo:rt_sigqueueinfo(pid_t, int, siginfo_t*)  all
-int     __signalfd4:signalfd4(int, const sigset64_t*, size_t, int)  all
+__sigaction:sigaction(int, const struct sigaction*, struct sigaction*)  lp32
+__rt_sigaction:rt_sigaction(int, const struct sigaction*, struct sigaction*, size_t)  all
+__rt_sigpending:rt_sigpending(sigset64_t*, size_t)  all
+__rt_sigprocmask:rt_sigprocmask(int, const sigset64_t*, sigset64_t*, size_t)  all
+__rt_sigsuspend:rt_sigsuspend(const sigset64_t*, size_t)  all
+__rt_sigtimedwait:rt_sigtimedwait(const sigset64_t*, siginfo_t*, const timespec*, size_t)  all
+__rt_sigqueueinfo:rt_sigqueueinfo(pid_t, int, siginfo_t*)  all
+__signalfd4:signalfd4(int, const sigset64_t*, size_t, int)  all
 
 # sockets
-int           __socket:socket(int, int, int)              arm,lp64
-int           __socketpair:socketpair(int, int, int, int*)    arm,lp64
-int           bind(int, struct sockaddr*, socklen_t)  arm,lp64
-int           __connect:connect(int, struct sockaddr*, socklen_t)   arm,lp64
-int           listen(int, int)                   arm,lp64
-int           __accept4:accept4(int, struct sockaddr*, socklen_t*, int)  arm,lp64
-int           getsockname(int, struct sockaddr*, socklen_t*)  arm,lp64
-int           getpeername(int, struct sockaddr*, socklen_t*)  arm,lp64
-ssize_t       __sendto:sendto(int, const void*, size_t, int, const struct sockaddr*, socklen_t)  arm,lp64
-ssize_t       recvfrom(int, void*, size_t, unsigned int, struct sockaddr*, socklen_t*)  arm,lp64
-int           shutdown(int, int)  arm,lp64
-int           setsockopt(int, int, int, const void*, socklen_t)  arm,lp64
-int           getsockopt(int, int, int, void*, socklen_t*)    arm,lp64
-ssize_t       __recvmsg:recvmsg(int, struct msghdr*, unsigned int)   arm,lp64
-ssize_t       __sendmsg:sendmsg(int, const struct msghdr*, unsigned int)  arm,lp64
-int           __recvmmsg:recvmmsg(int, struct mmsghdr*, unsigned int, int, const struct timespec*)   arm,lp64
-int           __sendmmsg:sendmmsg(int, struct mmsghdr*, unsigned int, int)   arm,lp64
+__socket:socket(int, int, int)              arm,lp64
+__socketpair:socketpair(int, int, int, int*)    arm,lp64
+bind(int, struct sockaddr*, socklen_t)  arm,lp64
+__connect:connect(int, struct sockaddr*, socklen_t)   arm,lp64
+listen(int, int)                   arm,lp64
+__accept4:accept4(int, struct sockaddr*, socklen_t*, int)  arm,lp64
+getsockname(int, struct sockaddr*, socklen_t*)  arm,lp64
+getpeername(int, struct sockaddr*, socklen_t*)  arm,lp64
+__sendto:sendto(int, const void*, size_t, int, const struct sockaddr*, socklen_t)  arm,lp64
+recvfrom(int, void*, size_t, unsigned int, struct sockaddr*, socklen_t*)  arm,lp64
+shutdown(int, int)  arm,lp64
+setsockopt(int, int, int, const void*, socklen_t)  arm,lp64
+getsockopt(int, int, int, void*, socklen_t*)    arm,lp64
+__recvmsg:recvmsg(int, struct msghdr*, unsigned int)   arm,lp64
+__sendmsg:sendmsg(int, const struct msghdr*, unsigned int)  arm,lp64
+__recvmmsg:recvmmsg(int, struct mmsghdr*, unsigned int, int, const struct timespec*)   arm,lp64
+__sendmmsg:sendmmsg(int, struct mmsghdr*, unsigned int, int)   arm,lp64
 
 # sockets for x86. These are done as an "indexed" call to socketcall syscall.
-int           __socket:socketcall:1(int, int, int) x86
-int           bind:socketcall:2(int, struct sockaddr*, int)  x86
-int           __connect:socketcall:3(int, struct sockaddr*, socklen_t)   x86
-int           listen:socketcall:4(int, int)                   x86
-int           getsockname:socketcall:6(int, struct sockaddr*, socklen_t*)  x86
-int           getpeername:socketcall:7(int, struct sockaddr*, socklen_t*)  x86
-int           __socketpair:socketcall:8(int, int, int, int*)    x86
-ssize_t       __sendto:socketcall:11(int, const void*, size_t, int, const struct sockaddr*, socklen_t)  x86
-ssize_t       recvfrom:socketcall:12(int, void*, size_t, unsigned int, struct sockaddr*, socklen_t*)  x86
-int           shutdown:socketcall:13(int, int)  x86
-int           setsockopt:socketcall:14(int, int, int, const void*, socklen_t)  x86
-int           getsockopt:socketcall:15(int, int, int, void*, socklen_t*)    x86
-int           __sendmsg:socketcall:16(int, const struct msghdr*, unsigned int)  x86
-int           __recvmsg:socketcall:17(int, struct msghdr*, unsigned int)   x86
-int           __accept4:socketcall:18(int, struct sockaddr*, socklen_t*, int)  x86
-int           __recvmmsg:socketcall:19(int, struct mmsghdr*, unsigned int, int, const struct timespec*)   x86
-int           __sendmmsg:socketcall:20(int, struct mmsghdr*, unsigned int, int)   x86
+__socket:socketcall:1(int, int, int) x86
+bind:socketcall:2(int, struct sockaddr*, int)  x86
+__connect:socketcall:3(int, struct sockaddr*, socklen_t)   x86
+listen:socketcall:4(int, int)                   x86
+getsockname:socketcall:6(int, struct sockaddr*, socklen_t*)  x86
+getpeername:socketcall:7(int, struct sockaddr*, socklen_t*)  x86
+__socketpair:socketcall:8(int, int, int, int*)    x86
+__sendto:socketcall:11(int, const void*, size_t, int, const struct sockaddr*, socklen_t)  x86
+recvfrom:socketcall:12(int, void*, size_t, unsigned int, struct sockaddr*, socklen_t*)  x86
+shutdown:socketcall:13(int, int)  x86
+setsockopt:socketcall:14(int, int, int, const void*, socklen_t)  x86
+getsockopt:socketcall:15(int, int, int, void*, socklen_t*)    x86
+__sendmsg:socketcall:16(int, const struct msghdr*, unsigned int)  x86
+__recvmsg:socketcall:17(int, struct msghdr*, unsigned int)   x86
+__accept4:socketcall:18(int, struct sockaddr*, socklen_t*, int)  x86
+__recvmmsg:socketcall:19(int, struct mmsghdr*, unsigned int, int, const struct timespec*)   x86
+__sendmmsg:socketcall:20(int, struct mmsghdr*, unsigned int, int)   x86
 
 # scheduler & real-time
-int sched_get_priority_max(int policy) all
-int sched_get_priority_min(int policy) all
-int __sched_getaffinity:sched_getaffinity(pid_t, size_t, cpu_set_t*) all
-int sched_getattr(pid_t, sched_attr*, unsigned, unsigned) all
-int sched_getparam(pid_t, sched_param*) all
-int sched_getscheduler(pid_t) all
-int sched_rr_get_interval(pid_t, timespec*) all
-int sched_setaffinity(pid_t, size_t, const cpu_set_t*) all
-int sched_setattr(pid_t, sched_attr*, unsigned) all
-int sched_setparam(pid_t, const sched_param*) all
-int sched_setscheduler(pid_t, int, const sched_param*)  all
-int sched_yield(void) all
+sched_get_priority_max(int policy) all
+sched_get_priority_min(int policy) all
+__sched_getaffinity:sched_getaffinity(pid_t, size_t, cpu_set_t*) all
+sched_getattr(pid_t, sched_attr*, unsigned, unsigned) all
+sched_getparam(pid_t, sched_param*) all
+sched_getscheduler(pid_t) all
+sched_rr_get_interval(pid_t, timespec*) all
+sched_setaffinity(pid_t, size_t, const cpu_set_t*) all
+sched_setattr(pid_t, sched_attr*, unsigned) all
+sched_setparam(pid_t, const sched_param*) all
+sched_setscheduler(pid_t, int, const sched_param*)  all
+sched_yield(void) all
 
 # other
-int     uname(struct utsname*)  all
-mode_t  umask(mode_t)  all
-int     __reboot:reboot(int, int, int, void*)  all
-int     init_module(void*, unsigned long, const char*)  all
-int     delete_module(const char*, unsigned int)   all
-int     klogctl:syslog(int, char*, int)   all
-int     sysinfo(struct sysinfo*)  all
-int     personality(unsigned long)  all
+uname(struct utsname*)  all
+umask(mode_t)  all
+__reboot:reboot(int, int, int, void*)  all
+init_module(void*, unsigned long, const char*)  all
+delete_module(const char*, unsigned int)   all
+klogctl:syslog(int, char*, int)   all
+sysinfo(struct sysinfo*)  all
+personality(unsigned long)  all
 
-int setns(int, int) all
-int unshare(int) all
+setns(int, int) all
+unshare(int) all
 
-int __getcpu:getcpu(unsigned*, unsigned*, void*) all
+__getcpu:getcpu(unsigned*, unsigned*, void*) all
 
-int     bpf(int, union bpf_attr *, unsigned int) all
+bpf(int, union bpf_attr *, unsigned int) all
 
-ssize_t tee(int, int, size_t, unsigned int)  all
-ssize_t splice(int, off64_t*, int, off64_t*, size_t, unsigned int)  all
-ssize_t vmsplice(int, const struct iovec*, size_t, unsigned int)  all
+tee(int, int, size_t, unsigned int)  all
+splice(int, off64_t*, int, off64_t*, size_t, unsigned int)  all
+vmsplice(int, const struct iovec*, size_t, unsigned int)  all
 
-int __epoll_create1:epoll_create1(int)  all
-int epoll_ctl(int, int op, int, struct epoll_event*)  all
-int __epoll_pwait:epoll_pwait(int, struct epoll_event*, int, int, const sigset64_t*, size_t)  all
-int __epoll_pwait2:epoll_pwait2(int, struct epoll_event*, int, const timespec64*, const sigset64_t*, size_t)  all
+__epoll_create1:epoll_create1(int)  all
+epoll_ctl(int, int op, int, struct epoll_event*)  all
+__epoll_pwait:epoll_pwait(int, struct epoll_event*, int, int, const sigset64_t*, size_t)  all
+__epoll_pwait2:epoll_pwait2(int, struct epoll_event*, int, const timespec64*, const sigset64_t*, size_t)  all
 
-int __eventfd:eventfd2(unsigned int, int)  all
+__eventfd:eventfd2(unsigned int, int)  all
 
-void _exit|_Exit:exit_group(int)  all
-void __exit:exit(int)  all
+_exit|_Exit:exit_group(int)  all
+__exit:exit(int)  all
 
-int inotify_init1(int)  all
-int inotify_add_watch(int, const char*, unsigned int)  all
-int inotify_rm_watch(int, unsigned int)  all
+inotify_init1(int)  all
+inotify_add_watch(int, const char*, unsigned int)  all
+inotify_rm_watch(int, unsigned int)  all
 
-int __pselect6:pselect6(int, fd_set*, fd_set*, fd_set*, timespec*, void*)  all
-int __ppoll:ppoll(pollfd*, unsigned int, timespec*, const sigset64_t*, size_t)  all
+__pselect6:pselect6(int, fd_set*, fd_set*, fd_set*, timespec*, void*)  all
+__ppoll:ppoll(pollfd*, unsigned int, timespec*, const sigset64_t*, size_t)  all
 
-ssize_t process_vm_readv(pid_t, const struct iovec*, unsigned long, const struct iovec*, unsigned long, unsigned long)  all
-ssize_t process_vm_writev(pid_t, const struct iovec*, unsigned long, const struct iovec*, unsigned long, unsigned long)  all
+process_vm_readv(pid_t, const struct iovec*, unsigned long, const struct iovec*, unsigned long, unsigned long)  all
+process_vm_writev(pid_t, const struct iovec*, unsigned long, const struct iovec*, unsigned long, unsigned long)  all
 
-int quotactl(int, const char*, int, char*)  all
+quotactl(int, const char*, int, char*)  all
 
-int __set_tid_address:set_tid_address(int*)  all
+__set_tid_address:set_tid_address(int*)  all
 
-int setdomainname(const char*, size_t)  all
-int sethostname(const char*, size_t)  all
+setdomainname(const char*, size_t)  all
+sethostname(const char*, size_t)  all
 
-int sync_file_range(int, off64_t, off64_t, unsigned int) x86,lp64
-int __sync_file_range2:sync_file_range2(int, unsigned int, off64_t, off64_t) arm
+sync_file_range(int, off64_t, off64_t, unsigned int) x86,lp64
+__sync_file_range2:sync_file_range2(int, unsigned int, off64_t, off64_t) arm
 
-pid_t wait4(pid_t, int*, int, struct rusage*)  all
-int __waitid:waitid(int, pid_t, siginfo_t*, int, void*)  all
+wait4(pid_t, int*, int, struct rusage*)  all
+__waitid:waitid(int, pid_t, siginfo_t*, int, void*)  all
 
 # ARM-specific
-int     __set_tls:__ARM_NR_set_tls(void*)                                 arm
-int     cacheflush:__ARM_NR_cacheflush(long start, long end, long flags)  arm
+__set_tls:__ARM_NR_set_tls(void*)                                 arm
+cacheflush:__ARM_NR_cacheflush(long start, long end, long flags)  arm
 
 # riscv64-specific
-int __riscv_flush_icache:riscv_flush_icache(void*, void*, unsigned long) riscv64
+__riscv_flush_icache:riscv_flush_icache(void*, void*, unsigned long) riscv64
 
 # x86-specific
-int     __set_thread_area:set_thread_area(void*) x86
-long arch_prctl(int, unsigned long) x86_64
+__set_thread_area:set_thread_area(void*) x86
+arch_prctl(int, unsigned long) x86_64
 
 # vdso stuff.
-int __clock_getres:clock_getres(clockid_t, struct timespec*) all
-int __clock_gettime:clock_gettime(clockid_t, struct timespec*) all
-int __gettimeofday:gettimeofday(struct timeval*, struct timezone*) all
+__clock_getres:clock_getres(clockid_t, struct timespec*) all
+__clock_gettime:clock_gettime(clockid_t, struct timespec*) all
+__gettimeofday:gettimeofday(struct timeval*, struct timezone*) all
 
 # <sys/random.h>
-ssize_t getrandom(void*, size_t, unsigned) all
+getrandom(void*, size_t, unsigned) all
 
 # <sys/pidfd.h>
-int __pidfd_open:pidfd_open(pid_t, unsigned int) all
-int __pidfd_getfd:pidfd_getfd(int, int, unsigned int) all
-int pidfd_send_signal(int, int, siginfo_t*, unsigned int) all
+__pidfd_open:pidfd_open(pid_t, unsigned int) all
+__pidfd_getfd:pidfd_getfd(int, int, unsigned int) all
+pidfd_send_signal(int, int, siginfo_t*, unsigned int) all
diff --git a/libc/arch-arm64/bionic/__set_tls.c b/libc/arch-arm64/bionic/__set_tls.c
deleted file mode 100644
index 0d88d11..0000000
--- a/libc/arch-arm64/bionic/__set_tls.c
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2013 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 <sys/cdefs.h>
-
-__LIBC_HIDDEN__ void __set_tls(void* tls) {
-  asm("msr tpidr_el0, %0" : : "r" (tls));
-}
diff --git a/libc/arch-riscv64/bionic/__set_tls.c b/libc/arch-riscv64/bionic/__set_tls.c
deleted file mode 100644
index 57383ab..0000000
--- a/libc/arch-riscv64/bionic/__set_tls.c
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2022 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 <sys/cdefs.h>
-
-__LIBC_HIDDEN__ void __set_tls(void* tls) {
-  asm("mv tp, %0" : : "r"(tls));
-}
diff --git a/libc/arch-x86_64/bionic/__set_tls.c b/libc/arch-x86_64/bionic/__set_tls.c
deleted file mode 100644
index 9460a03..0000000
--- a/libc/arch-x86_64/bionic/__set_tls.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2013 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 <sys/cdefs.h>
-
-// ARCH_SET_FS is not exposed via <sys/prctl.h> or <linux/prctl.h>.
-#include <asm/prctl.h>
-
-extern int arch_prctl(int, unsigned long);
-
-__LIBC_HIDDEN__ int __set_tls(void* ptr) {
-  return arch_prctl(ARCH_SET_FS, (unsigned long) ptr);
-}
diff --git a/libc/bionic/clock_getcpuclockid.cpp b/libc/bionic/clock_getcpuclockid.cpp
index 9ff1845..65ba2c7 100644
--- a/libc/bionic/clock_getcpuclockid.cpp
+++ b/libc/bionic/clock_getcpuclockid.cpp
@@ -34,11 +34,12 @@
 int clock_getcpuclockid(pid_t pid, clockid_t* clockid) {
   ErrnoRestorer errno_restorer;
 
-  // The tid is stored in the top bits, but negated.
+  // The pid is stored in the top bits, but negated.
   clockid_t result = ~static_cast<clockid_t>(pid) << 3;
   // Bits 0 and 1: clock type (0 = CPUCLOCK_PROF, 1 = CPUCLOCK_VIRT, 2 = CPUCLOCK_SCHED).
-  result |= 2;
-  // Bit 2: thread (set) or process (clear). Bit 2 already 0.
+  result |= 2 /* CPUCLOCK_SCHED */;
+  // Bit 2: thread (set) or process (clear).
+  result &= ~4 /* CPUCLOCK_PERTHREAD_MASK */;
 
   if (clock_getres(result, nullptr) == -1) {
     return ESRCH;
diff --git a/libc/bionic/icu.cpp b/libc/bionic/icu.cpp
deleted file mode 100644
index c11b9d6..0000000
--- a/libc/bionic/icu.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2016 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/icu.h"
-
-#include <dirent.h>
-#include <dlfcn.h>
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <async_safe/log.h>
-
-static void* g_libicu_handle = nullptr;
-
-static bool __find_icu() {
-  g_libicu_handle = dlopen("libicu.so", RTLD_LOCAL);
-  if (g_libicu_handle == nullptr) {
-    async_safe_format_log(ANDROID_LOG_ERROR, "bionic-icu", "couldn't open libicu.so: %s",
-                          dlerror());
-    return false;
-  }
-
-  return true;
-}
-
-void* __find_icu_symbol(const char* symbol_name) {
-  static bool found_icu = __find_icu();
-  if (!found_icu) return nullptr;
-
-  void* symbol = dlsym(g_libicu_handle, symbol_name);
-  if (symbol == nullptr) {
-    async_safe_format_log(ANDROID_LOG_ERROR, "bionic-icu", "couldn't find %s", symbol_name);
-  }
-  return symbol;
-}
diff --git a/libc/bionic/icu4x.rs b/libc/bionic/icu4x.rs
new file mode 100644
index 0000000..939ba2f
--- /dev/null
+++ b/libc/bionic/icu4x.rs
@@ -0,0 +1,98 @@
+// Copyright (C) 2025 The Android Open Source Project
+// SPDX-License-Identifier: Apache-2.0
+
+#![allow(missing_docs)] // Not particularly useful to document these thin wrappers
+
+//! This is a thin wrapper around ICU4X for use in Bionic
+
+use icu_casemap::CaseMapper;
+use icu_collections::codepointtrie::TrieValue;
+use icu_properties::props::*;
+use icu_properties::{CodePointMapData, CodePointSetData};
+
+#[no_mangle]
+pub extern "C" fn __icu4x_bionic_general_category(ch: u32) -> u8 {
+    CodePointMapData::<GeneralCategory>::new().get32(ch) as u8
+}
+
+#[no_mangle]
+pub extern "C" fn __icu4x_bionic_east_asian_width(ch: u32) -> u8 {
+    CodePointMapData::<EastAsianWidth>::new().get32(ch).to_u32() as u8
+}
+
+#[no_mangle]
+pub extern "C" fn __icu4x_bionic_hangul_syllable_type(ch: u32) -> u8 {
+    CodePointMapData::<HangulSyllableType>::new().get32(ch).to_u32() as u8
+}
+
+#[no_mangle]
+pub extern "C" fn __icu4x_bionic_is_alphabetic(ch: u32) -> bool {
+    CodePointSetData::new::<Alphabetic>().contains32(ch)
+}
+
+#[no_mangle]
+pub extern "C" fn __icu4x_bionic_is_default_ignorable_code_point(ch: u32) -> bool {
+    CodePointSetData::new::<DefaultIgnorableCodePoint>().contains32(ch)
+}
+
+#[no_mangle]
+pub extern "C" fn __icu4x_bionic_is_lowercase(ch: u32) -> bool {
+    CodePointSetData::new::<Lowercase>().contains32(ch)
+}
+
+#[no_mangle]
+pub extern "C" fn __icu4x_bionic_is_alnum(ch: u32) -> bool {
+    CodePointSetData::new::<Alnum>().contains32(ch)
+}
+
+#[no_mangle]
+pub extern "C" fn __icu4x_bionic_is_blank(ch: u32) -> bool {
+    CodePointSetData::new::<Blank>().contains32(ch)
+}
+
+#[no_mangle]
+pub extern "C" fn __icu4x_bionic_is_graph(ch: u32) -> bool {
+    CodePointSetData::new::<Graph>().contains32(ch)
+}
+
+#[no_mangle]
+pub extern "C" fn __icu4x_bionic_is_print(ch: u32) -> bool {
+    CodePointSetData::new::<Print>().contains32(ch)
+}
+
+#[no_mangle]
+pub extern "C" fn __icu4x_bionic_is_xdigit(ch: u32) -> bool {
+    CodePointSetData::new::<Xdigit>().contains32(ch)
+}
+
+#[no_mangle]
+pub extern "C" fn __icu4x_bionic_is_white_space(ch: u32) -> bool {
+    CodePointSetData::new::<WhiteSpace>().contains32(ch)
+}
+
+#[no_mangle]
+pub extern "C" fn __icu4x_bionic_is_uppercase(ch: u32) -> bool {
+    CodePointSetData::new::<Uppercase>().contains32(ch)
+}
+
+/// Convert a code point to uppercase
+#[no_mangle]
+pub extern "C" fn __icu4x_bionic_to_upper(ch: u32) -> u32 {
+    let Ok(ch) = char::try_from(ch) else {
+        return ch;
+    };
+    let cm = CaseMapper::new();
+
+    cm.simple_uppercase(ch) as u32
+}
+
+/// Convert a code point to lowercase
+#[no_mangle]
+pub extern "C" fn __icu4x_bionic_to_lower(ch: u32) -> u32 {
+    let Ok(ch) = char::try_from(ch) else {
+        return ch;
+    };
+    let cm = CaseMapper::new();
+
+    cm.simple_lowercase(ch) as u32
+}
diff --git a/libc/bionic/icu_static.cpp b/libc/bionic/icu_static.cpp
deleted file mode 100644
index cf24a38..0000000
--- a/libc/bionic/icu_static.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 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/icu.h"
-
-// We don't have dlopen/dlsym for static binaries yet.
-void* __find_icu_symbol(const char*) {
-  return nullptr;
-}
diff --git a/libc/bionic/icu_wrappers.cpp b/libc/bionic/icu_wrappers.cpp
deleted file mode 100644
index 523f5a6..0000000
--- a/libc/bionic/icu_wrappers.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2017 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/icu.h"
-
-int8_t __icu_charType(wint_t wc) {
-  typedef int8_t (*u_charType_t)(UChar32);
-  static auto u_charType = reinterpret_cast<u_charType_t>(__find_icu_symbol("u_charType"));
-  return u_charType ? u_charType(wc) : -1;
-}
-
-int32_t __icu_getIntPropertyValue(wint_t wc, UProperty property) {
-  typedef int32_t (*u_getIntPropertyValue_t)(UChar32, UProperty);
-  static auto u_getIntPropertyValue =
-      reinterpret_cast<u_getIntPropertyValue_t>(__find_icu_symbol("u_getIntPropertyValue"));
-  return u_getIntPropertyValue ? u_getIntPropertyValue(wc, property) : 0;
-}
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index 85e742c..ef9f896 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -28,34 +28,25 @@
 
 #include <android/api-level.h>
 #include <elf.h>
-#include <errno.h>
 #include <malloc.h>
 #include <signal.h>
 #include <stddef.h>
-#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/auxv.h>
 #include <sys/mman.h>
 
 #include "async_safe/log.h"
-#include "heap_tagging.h"
 #include "libc_init_common.h"
 #include "platform/bionic/macros.h"
-#include "platform/bionic/mte.h"
 #include "platform/bionic/page.h"
 #include "platform/bionic/reserved_signals.h"
 #include "private/KernelArgumentBlock.h"
-#include "private/bionic_asm.h"
-#include "private/bionic_asm_note.h"
 #include "private/bionic_call_ifunc_resolver.h"
 #include "private/bionic_elf_tls.h"
 #include "private/bionic_globals.h"
 #include "private/bionic_tls.h"
-#include "private/elf_note.h"
 #include "pthread_internal.h"
-#include "sys/system_properties.h"
-#include "sysprop_helpers.h"
 
 #if __has_feature(hwaddress_sanitizer)
 #include <sanitizer/hwasan_interface.h>
@@ -69,7 +60,6 @@
 #endif
 
 extern "C" int __cxa_atexit(void (*)(void *), void *, void *);
-extern "C" const char* __gnu_basename(const char* path);
 
 static void call_array(init_func_t** list, size_t count, int argc, char* argv[], char* envp[]) {
   while (count-- > 0) {
@@ -157,6 +147,7 @@
 
   layout.finish_layout();
 }
+
 void __libc_init_profiling_handlers() {
   // The dynamic variant of this function is more interesting, but this
   // at least ensures that static binaries aren't killed by the kernel's
diff --git a/libc/bionic/ndk_cruft.cpp b/libc/bionic/ndk_cruft.cpp
index b15a317..a69b77f 100644
--- a/libc/bionic/ndk_cruft.cpp
+++ b/libc/bionic/ndk_cruft.cpp
@@ -28,6 +28,9 @@
 
 // This file perpetuates the mistakes of the past.
 
+// LP64 doesn't need to support any legacy cruft.
+#if !defined(__LP64__)
+
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
@@ -47,10 +50,19 @@
 
 #include "platform/bionic/macros.h"
 
-extern "C" {
+#define __futex_wake __real_futex_wake
+#define __futex_wait __real_futex_wait
+#include "private/bionic_futex.h"
+#undef __futex_wake
+#undef __futex_wait
 
-// LP64 doesn't need to support any legacy cruft.
-#if !defined(__LP64__)
+#define __get_thread __real_get_thread
+#define __get_tls __real_get_tls
+#include "pthread_internal.h"
+#undef __get_thread
+#undef __get_tls
+
+extern "C" {
 
 // By the time any NDK-built code is running, there are plenty of threads.
 int __isthreaded = 1;
@@ -73,8 +85,7 @@
 
 // TODO: does anything still need this?
 void** __get_tls() {
-#include "platform/bionic/tls.h"
-  return __get_tls();
+  return __real_get_tls();
 }
 
 // This non-standard function was in our <string.h> for some reason.
@@ -213,12 +224,6 @@
   return vdprintf(fd, fmt, ap);
 }
 
-#define __futex_wake __real_futex_wake
-#define __futex_wait __real_futex_wait
-#include "private/bionic_futex.h"
-#undef __futex_wake
-#undef __futex_wait
-
 // This used to be in <sys/atomics.h>.
 int __futex_wake(volatile void* ftx, int count) {
   return __real_futex_wake(ftx, count);
@@ -356,14 +361,6 @@
   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();
@@ -388,6 +385,6 @@
     return fwrite(&value, sizeof(value), 1, fp) == 1 ? 0 : EOF;
 }
 
-#endif // !defined (__LP64__)
-
 } // extern "C"
+
+#endif // !defined (__LP64__)
diff --git a/libc/bionic/pthread_getcpuclockid.cpp b/libc/bionic/pthread_getcpuclockid.cpp
index 6d1884e..ccadc98 100644
--- a/libc/bionic/pthread_getcpuclockid.cpp
+++ b/libc/bionic/pthread_getcpuclockid.cpp
@@ -39,9 +39,9 @@
   // The tid is stored in the top bits, but negated.
   clockid_t result = ~static_cast<clockid_t>(tid) << 3;
   // Bits 0 and 1: clock type (0 = CPUCLOCK_PROF, 1 = CPUCLOCK_VIRT, 2 = CPUCLOCK_SCHED).
-  result |= 2;
+  result |= 2 /* CPUCLOCK_SCHED */;
   // Bit 2: thread (set) or process (clear)?
-  result |= (1 << 2);
+  result |= 4 /* CPUCLOCK_PERTHREAD_MASK */;
 
   *clockid = result;
   return 0;
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index cbaa9a6..ae9a791 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -240,8 +240,6 @@
   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);
 
 // Address space is precious on LP32, so use the minimum unit: one page.
diff --git a/libc/bionic/pthread_key.cpp b/libc/bionic/pthread_key.cpp
index 53f0f11..f8c765d 100644
--- a/libc/bionic/pthread_key.cpp
+++ b/libc/bionic/pthread_key.cpp
@@ -83,11 +83,18 @@
     size_t called_destructor_count = 0;
     for (size_t i = 0; i < BIONIC_PTHREAD_KEY_COUNT; ++i) {
       uintptr_t seq = atomic_load_explicit(&key_map[i].seq, memory_order_relaxed);
-      if (SeqOfKeyInUse(seq) && seq == key_data[i].seq && key_data[i].data != nullptr) {
-        // Other threads may be calling pthread_key_delete/pthread_key_create while current thread
-        // is exiting. So we need to ensure we read the right key_destructor.
+      if (SeqOfKeyInUse(seq) && seq == key_data[i].seq) {
+        // POSIX explicitly says that the destructor is only called if the
+        // thread has a non-null value for the key.
+        if (key_data[i].data == nullptr) {
+          continue;
+        }
+
+        // Other threads can call pthread_key_delete()/pthread_key_create()
+        // while this thread is exiting, so we need to ensure we read the right
+        // key_destructor.
         // We can rely on a user-established happens-before relationship between the creation and
-        // use of pthread key to ensure that we're not getting an earlier key_destructor.
+        // use of a pthread key to ensure that we're not getting an earlier key_destructor.
         // To avoid using the key_destructor of the newly created key in the same slot, we need to
         // recheck the sequence number after reading key_destructor. As a result, we either see the
         // right key_destructor, or the sequence number must have changed when we reread it below.
@@ -107,7 +114,6 @@
         // function is responsible for manually releasing the corresponding data.
         void* data = key_data[i].data;
         key_data[i].data = nullptr;
-
         (*key_destructor)(data);
         ++called_destructor_count;
       }
@@ -163,13 +169,13 @@
   key &= ~KEY_VALID_FLAG;
   uintptr_t seq = atomic_load_explicit(&key_map[key].seq, memory_order_relaxed);
   pthread_key_data_t* data = &get_thread_key_data()[key];
-  // It is user's responsibility to synchornize between the creation and use of pthread keys,
+  // It is the user's responsibility to synchronize between the creation and use of pthread keys,
   // so we use memory_order_relaxed when checking the sequence number.
   if (__predict_true(SeqOfKeyInUse(seq) && data->seq == seq)) {
     return data->data;
   }
-  // We arrive here when current thread holds the seq of an deleted pthread key. So the
-  // data is for the deleted pthread key, and should be cleared.
+  // We arrive here when the current thread holds the seq of a deleted pthread key.
+  // The data is for the deleted pthread key, and should be cleared.
   data->data = nullptr;
   return nullptr;
 }
diff --git a/libc/bionic/wctype.cpp b/libc/bionic/wctype.cpp
index 94597d9..8d0733d 100644
--- a/libc/bionic/wctype.cpp
+++ b/libc/bionic/wctype.cpp
@@ -35,7 +35,7 @@
 #include <wchar.h>
 
 #include "bionic/macros.h"
-#include "private/icu.h"
+#include "private/icu4x.h"
 
 enum {
   WC_TYPE_INVALID = 0,
@@ -54,60 +54,65 @@
   WC_TYPE_MAX
 };
 
-static u_hasBinaryProperty_t __find_u_hasBinaryProperty() {
-  static auto u_hasBinaryProperty =
-      reinterpret_cast<u_hasBinaryProperty_t>(__find_icu_symbol("u_hasBinaryProperty"));
-  return u_hasBinaryProperty;
+#define DO_ISW(prop_name, narrow_fn) \
+  if (__predict_true(wc < 0x80)) {   \
+    return narrow_fn(wc);            \
+  }                                  \
+  return __icu4x_bionic_is_##prop_name(wc);
+
+int iswalnum(wint_t wc) {
+  DO_ISW(alnum, isalnum);
 }
-
-#define DO_ISW(icu_constant, narrow_fn) \
-  u_hasBinaryProperty_t u_hasBinaryProperty; \
-  if (__predict_true(wc < 0x80) || \
-      !(u_hasBinaryProperty = __find_u_hasBinaryProperty())) { \
-    return narrow_fn(wc); \
-  } \
-  return u_hasBinaryProperty(wc, icu_constant); \
-
-int iswalnum(wint_t wc) { DO_ISW(UCHAR_POSIX_ALNUM, isalnum); }
 __strong_alias(iswalnum_l, iswalnum);
-int iswalpha(wint_t wc) { DO_ISW(UCHAR_ALPHABETIC, isalpha); }
+int iswalpha(wint_t wc) {
+  DO_ISW(alphabetic, isalpha);
+}
 __strong_alias(iswalpha_l, iswalpha);
-int iswblank(wint_t wc) { DO_ISW(UCHAR_POSIX_BLANK, isblank); }
+int iswblank(wint_t wc) {
+  DO_ISW(blank, isblank);
+}
 __strong_alias(iswblank_l, iswblank);
-int iswgraph(wint_t wc) { DO_ISW(UCHAR_POSIX_GRAPH, isgraph); }
+int iswgraph(wint_t wc) {
+  DO_ISW(graph, isgraph);
+}
 __strong_alias(iswgraph_l, iswgraph);
-int iswlower(wint_t wc) { DO_ISW(UCHAR_LOWERCASE, islower); }
+int iswlower(wint_t wc) {
+  DO_ISW(lowercase, islower);
+}
 __strong_alias(iswlower_l, iswlower);
-int iswprint(wint_t wc) { DO_ISW(UCHAR_POSIX_PRINT, isprint); }
+int iswprint(wint_t wc) {
+  DO_ISW(print, isprint);
+}
 __strong_alias(iswprint_l, iswprint);
-int iswspace(wint_t wc) { DO_ISW(UCHAR_WHITE_SPACE, isspace); }
+int iswspace(wint_t wc) {
+  DO_ISW(white_space, isspace);
+}
 __strong_alias(iswspace_l, iswspace);
-int iswupper(wint_t wc) { DO_ISW(UCHAR_UPPERCASE, isupper); }
+int iswupper(wint_t wc) {
+  DO_ISW(uppercase, isupper);
+}
 __strong_alias(iswupper_l, iswupper);
-int iswxdigit(wint_t wc) { DO_ISW(UCHAR_POSIX_XDIGIT, isxdigit); }
+int iswxdigit(wint_t wc) {
+  DO_ISW(xdigit, isxdigit);
+}
 __strong_alias(iswxdigit_l, iswxdigit);
 
 int iswcntrl(wint_t wc) {
   if (wc < 0x80) return iscntrl(wc);
-  typedef int8_t (*FnT)(UChar32);
-  static auto u_charType = reinterpret_cast<FnT>(__find_icu_symbol("u_charType"));
-  return u_charType ? (u_charType(wc) == U_CONTROL_CHAR) : iscntrl(wc);
+  return __icu4x_bionic_general_category(wc) == U_CONTROL_CHAR;
 }
 __strong_alias(iswcntrl_l, iswcntrl);
 
 int iswdigit(wint_t wc) {
   if (wc < 0x80) return isdigit(wc);
-  typedef UBool (*FnT)(UChar32);
-  static auto u_isdigit = reinterpret_cast<FnT>(__find_icu_symbol("u_isdigit"));
-  return u_isdigit ? u_isdigit(wc) : isdigit(wc);
+  return __icu4x_bionic_general_category(wc) == U_DECIMAL_NUMBER;
 }
 __strong_alias(iswdigit_l, iswdigit);
 
 int iswpunct(wint_t wc) {
   if (wc < 0x80) return ispunct(wc);
-  typedef UBool (*FnT)(UChar32);
-  static auto u_ispunct = reinterpret_cast<FnT>(__find_icu_symbol("u_ispunct"));
-  return u_ispunct ? u_ispunct(wc) : ispunct(wc);
+  int8_t chartype = __icu4x_bionic_general_category(wc);
+  return chartype >= U_DASH_PUNCTUATION && chartype <= U_OTHER_PUNCTUATION;
 }
 __strong_alias(iswpunct_l, iswpunct);
 
@@ -124,18 +129,14 @@
 wint_t towlower(wint_t wc) {
   if (wc < 0x80) return tolower(wc);
 
-  typedef UChar32 (*FnT)(UChar32);
-  static auto u_tolower = reinterpret_cast<FnT>(__find_icu_symbol("u_tolower"));
-  return u_tolower ? u_tolower(wc) : tolower(wc);
+  return __icu4x_bionic_to_lower(wc);
 }
 __strong_alias(towlower_l, towlower);
 
 wint_t towupper(wint_t wc) {
   if (wc < 0x80) return toupper(wc);
 
-  typedef UChar32 (*FnT)(UChar32);
-  static auto u_toupper = reinterpret_cast<FnT>(__find_icu_symbol("u_toupper"));
-  return u_toupper ? u_toupper(wc) : toupper(wc);
+  return __icu4x_bionic_to_upper(wc);
 }
 __strong_alias(towupper_l, towupper);
 
diff --git a/libc/bionic/wcwidth.cpp b/libc/bionic/wcwidth.cpp
index 776321f..633d83e 100644
--- a/libc/bionic/wcwidth.cpp
+++ b/libc/bionic/wcwidth.cpp
@@ -28,7 +28,7 @@
 
 #include <wchar.h>
 
-#include "private/icu.h"
+#include "private/icu4x.h"
 
 int wcwidth(wchar_t wc) {
   // Fast-path ASCII.
@@ -44,38 +44,33 @@
   // pretty arbitrary. See https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c for more details.
 
   // Fancy unicode control characters?
-  switch (__icu_charType(wc)) {
-   case -1:
-    // No icu4c available; give up.
-    return -1;
-   case U_CONTROL_CHAR:
-    return -1;
-   case U_NON_SPACING_MARK:
-   case U_ENCLOSING_MARK:
-    return 0;
-   case U_FORMAT_CHAR:
-    // A special case for soft hyphen (U+00AD) to match historical practice.
-    // See the tests for more commentary.
-    return (wc == 0x00ad) ? 1 : 0;
+  switch (__icu4x_bionic_general_category(wc)) {
+    case U_CONTROL_CHAR:
+      return -1;
+    case U_NON_SPACING_MARK:
+    case U_ENCLOSING_MARK:
+      return 0;
+    case U_FORMAT_CHAR:
+      // A special case for soft hyphen (U+00AD) to match historical practice.
+      // See the tests for more commentary.
+      return (wc == 0x00ad) ? 1 : 0;
   }
 
   // Medial and final jamo render as zero width when used correctly,
   // so we handle them specially rather than relying on East Asian Width.
-  switch (__icu_getIntPropertyValue(wc, UCHAR_HANGUL_SYLLABLE_TYPE)) {
-   case U_HST_VOWEL_JAMO:
-   case U_HST_TRAILING_JAMO:
-    return 0;
-   case U_HST_LEADING_JAMO:
-   case U_HST_LV_SYLLABLE:
-   case U_HST_LVT_SYLLABLE:
-    return 2;
+  switch (__icu4x_bionic_hangul_syllable_type(wc)) {
+    case U_HST_VOWEL_JAMO:
+    case U_HST_TRAILING_JAMO:
+      return 0;
+    case U_HST_LEADING_JAMO:
+    case U_HST_LV_SYLLABLE:
+    case U_HST_LVT_SYLLABLE:
+      return 2;
   }
 
   // Hangeul choseong filler U+115F is default ignorable, so we check default
   // ignorability only after we've already handled Hangeul jamo above.
-  static auto u_hasBinaryProperty =
-      reinterpret_cast<u_hasBinaryProperty_t>(__find_icu_symbol("u_hasBinaryProperty"));
-  if (u_hasBinaryProperty && u_hasBinaryProperty(wc, UCHAR_DEFAULT_IGNORABLE_CODE_POINT)) return 0;
+  if (__icu4x_bionic_is_default_ignorable_code_point(wc)) return 0;
 
   // A few weird special cases where EastAsianWidth is not helpful for us.
   if (wc >= 0x3248 && wc <= 0x4dff) {
@@ -88,15 +83,15 @@
 
   // The EastAsianWidth property is at least defined by the Unicode standard!
   // https://www.unicode.org/reports/tr11/
-  switch (__icu_getIntPropertyValue(wc, UCHAR_EAST_ASIAN_WIDTH)) {
-   case U_EA_AMBIGUOUS:
-   case U_EA_HALFWIDTH:
-   case U_EA_NARROW:
-   case U_EA_NEUTRAL:
-    return 1;
-   case U_EA_FULLWIDTH:
-   case U_EA_WIDE:
-    return 2;
+  switch (__icu4x_bionic_east_asian_width(wc)) {
+    case U_EA_AMBIGUOUS:
+    case U_EA_HALFWIDTH:
+    case U_EA_NARROW:
+    case U_EA_NEUTRAL:
+      return 1;
+    case U_EA_FULLWIDTH:
+    case U_EA_WIDE:
+      return 2;
   }
 
   return 0;
diff --git a/libc/include/android/api-level.h b/libc/include/android/api-level.h
index c9536c1..2a4f7df 100644
--- a/libc/include/android/api-level.h
+++ b/libc/include/android/api-level.h
@@ -31,16 +31,21 @@
 /**
  * @defgroup apilevels API Levels
  *
- * Defines functions and constants for working with Android API levels.
+ * Defines functions for working with Android API levels.
  * @{
  */
 
 /**
  * @file android/api-level.h
- * @brief Functions and constants for dealing with multiple API levels.
+ * @brief Functions for dealing with multiple API levels.
  *
- * See
- * https://android.googlesource.com/platform/bionic/+/main/docs/defines.md.
+ * See also
+ * https://developer.android.com/ndk/guides/using-newer-apis
+ * for more tutorial information on dealing with multiple API levels.
+ *
+ * See also
+ * https://android.googlesource.com/platform/bionic/+/main/docs/defines.md
+ * for when to use which `#define` when writing portable code.
  */
 
 #include <sys/cdefs.h>
@@ -96,82 +101,64 @@
 #define __ANDROID_API__ __ANDROID_API_FUTURE__
 #endif
 
-/** Names the Gingerbread API level (9), for comparison against `__ANDROID_API__`. */
+/** Deprecated name for API level 9. Prefer numeric API levels in new code. */
 #define __ANDROID_API_G__ 9
 
-/** Names the Ice-Cream Sandwich API level (14), for comparison against `__ANDROID_API__`. */
+/** Deprecated name for API level 14. Prefer numeric API levels in new code. */
 #define __ANDROID_API_I__ 14
 
-/** Names the Jellybean API level (16), for comparison against `__ANDROID_API__`. */
+/** Deprecated name for API level 16. Prefer numeric API levels in new code. */
 #define __ANDROID_API_J__ 16
 
-/** Names the Jellybean MR1 API level (17), for comparison against `__ANDROID_API__`. */
+/** Deprecated name for API level 17. Prefer numeric API levels in new code. */
 #define __ANDROID_API_J_MR1__ 17
 
-/** Names the Jellybean MR2 API level (18), for comparison against `__ANDROID_API__`. */
+/** Deprecated name for API level 18. Prefer numeric API levels in new code. */
 #define __ANDROID_API_J_MR2__ 18
 
-/** Names the KitKat API level (19), for comparison against `__ANDROID_API__`. */
+/** Deprecated name for API level 19. Prefer numeric API levels in new code. */
 #define __ANDROID_API_K__ 19
 
-/** Names the Lollipop API level (21), for comparison against `__ANDROID_API__`. */
+/** Deprecated name for API level 21. Prefer numeric API levels in new code. */
 #define __ANDROID_API_L__ 21
 
-/** Names the Lollipop MR1 API level (22), for comparison against `__ANDROID_API__`. */
+/** Deprecated name for API level 22. Prefer numeric API levels in new code. */
 #define __ANDROID_API_L_MR1__ 22
 
-/** Names the Marshmallow API level (23), for comparison against `__ANDROID_API__`. */
+/** Deprecated name for API level 23. Prefer numeric API levels in new code. */
 #define __ANDROID_API_M__ 23
 
-/** Names the Nougat API level (24), for comparison against `__ANDROID_API__`. */
+/** Deprecated name for API level 24. Prefer numeric API levels in new code. */
 #define __ANDROID_API_N__ 24
 
-/** Names the Nougat MR1 API level (25), for comparison against `__ANDROID_API__`. */
+/** Deprecated name for API level 25. Prefer numeric API levels in new code. */
 #define __ANDROID_API_N_MR1__ 25
 
-/** Names the Oreo API level (26), for comparison against `__ANDROID_API__`. */
+/** Deprecated name for API level 26. Prefer numeric API levels in new code. */
 #define __ANDROID_API_O__ 26
 
-/** Names the Oreo MR1 API level (27), for comparison against `__ANDROID_API__`. */
+/** Deprecated name for API level 27. Prefer numeric API levels in new code. */
 #define __ANDROID_API_O_MR1__ 27
 
-/** Names the Pie API level (28), for comparison against `__ANDROID_API__`. */
+/** Deprecated name for API level 28. Prefer numeric API levels in new code. */
 #define __ANDROID_API_P__ 28
 
-/**
- * Names the Android 10 (aka "Q" or "Quince Tart") API level (29), for
- * comparison against `__ANDROID_API__`.
- */
+/** Deprecated name for API level 29. Prefer numeric API levels in new code. */
 #define __ANDROID_API_Q__ 29
 
-/**
- * Names the Android 11 (aka "R" or "Red Velvet Cake") API level (30), for
- * comparison against `__ANDROID_API__`.
- */
+/** Deprecated name for API level 30. Prefer numeric API levels in new code. */
 #define __ANDROID_API_R__ 30
 
-/**
- * Names the Android 12 (aka "S" or "Snowcone") API level (31), for
- * comparison against `__ANDROID_API__`.
- */
+/** Deprecated name for API level 31. Prefer numeric API levels in new code. */
 #define __ANDROID_API_S__ 31
 
-/**
- * Names the Android 13 (aka "T" or "Tiramisu") API level (33), for
- * comparison against `__ANDROID_API__`.
- */
+/** Deprecated name for API level 33. Prefer numeric API levels in new code. */
 #define __ANDROID_API_T__ 33
 
-/**
- * Names the Android 14 (aka "U" or "UpsideDownCake") API level (34),
- * for comparison against `__ANDROID_API__`.
- */
+/** Deprecated name for API level 34. Prefer numeric API levels in new code. */
 #define __ANDROID_API_U__ 34
 
-/**
- * Names the Android 15 (aka "V" or "VanillaIceCream") API level (35),
- * for comparison against `__ANDROID_API__`.
- */
+/** Deprecated name for API level 35. Prefer numeric API levels in new code. */
 #define __ANDROID_API_V__ 35
 
 /* This file is included in <features.h>, and might be used from .S files. */
@@ -189,7 +176,6 @@
  *
  * Available since API level 24.
  */
-
 #if __BIONIC_AVAILABILITY_GUARD(24)
 int android_get_application_target_sdk_version() __INTRODUCED_IN(24);
 #endif /* __BIONIC_AVAILABILITY_GUARD(24) */
@@ -210,6 +196,8 @@
  * and is equivalent to the Java `Build.VERSION.SDK_INT` API.
  *
  * See also android_get_application_target_sdk_version().
+ *
+ * Available since API level 29.
  */
 int android_get_device_api_level() __INTRODUCED_IN(29);
 
diff --git a/libc/include/bits/fortify/string.h b/libc/include/bits/fortify/string.h
index 6f0ee4a..4ec356f 100644
--- a/libc/include/bits/fortify/string.h
+++ b/libc/include/bits/fortify/string.h
@@ -42,7 +42,6 @@
 size_t __strlcat_chk(char* _Nonnull, const char* _Nonnull, size_t, size_t);
 
 #if defined(__BIONIC_FORTIFY)
-void* _Nullable __memrchr_real(const void* _Nonnull, int, size_t) __RENAME(memrchr);
 
 #if __BIONIC_FORTIFY_RUNTIME_CHECKS_ENABLED
 /* No diag -- clang diagnoses misuses of this on its own.  */
@@ -62,6 +61,20 @@
 }
 #endif
 
+/* TODO: remove __clang_warning_if when https://issuetracker.google.com/400937647 is fixed. */
+__BIONIC_FORTIFY_INLINE
+void* _Nonnull memset(void* _Nonnull const s __pass_object_size0, int c, size_t n)
+        __diagnose_as_builtin(__builtin_memset, 1, 2, 3)
+        __overloadable
+        /* If you're a user who wants this warning to go away: use `(&memset)(foo, bar, baz)`. */
+        __clang_warning_if(c && !n, "'memset' will set 0 bytes; maybe the arguments got flipped?") {
+#if __BIONIC_FORTIFY_RUNTIME_CHECKS_ENABLED
+    return __builtin___memset_chk(s, c, n, __bos0(s));
+#else
+    return __builtin_memset(s, c, n);
+#endif
+}
+
 #if defined(__USE_GNU)
 #if __ANDROID_API__ >= 30
 __BIONIC_FORTIFY_INLINE
@@ -128,19 +141,6 @@
 }
 #endif
 
-/* No diag -- clang diagnoses misuses of this on its own.  */
-__BIONIC_FORTIFY_INLINE
-void* _Nonnull memset(void* _Nonnull const s __pass_object_size0, int c, size_t n) __overloadable
-        __diagnose_as_builtin(__builtin_memset, 1, 2, 3)
-        /* If you're a user who wants this warning to go away: use `(&memset)(foo, bar, baz)`. */
-        __clang_warning_if(c && !n, "'memset' will set 0 bytes; maybe the arguments got flipped?") {
-#if __BIONIC_FORTIFY_RUNTIME_CHECKS_ENABLED
-    return __builtin___memset_chk(s, c, n, __bos0(s));
-#else
-    return __builtin_memset(s, c, n);
-#endif
-}
-
 #if __ANDROID_API__ >= 23 && __BIONIC_FORTIFY_RUNTIME_CHECKS_ENABLED
 __BIONIC_FORTIFY_INLINE
 void* _Nullable memchr(const void* _Nonnull const s __pass_object_size, int c, size_t n) __overloadable {
@@ -153,6 +153,8 @@
     return __memchr_chk(s, c, n, bos);
 }
 
+void* _Nullable __memrchr_real(const void* _Nonnull, int, size_t) __RENAME(memrchr);
+
 __BIONIC_FORTIFY_INLINE
 void* _Nullable __memrchr_fortify(const void* _Nonnull const __pass_object_size s, int c, size_t n) __overloadable {
     size_t bos = __bos(s);
diff --git a/libc/include/bits/stdatomic.h b/libc/include/bits/stdatomic.h
index ebdc9e5..0d39a61 100644
--- a/libc/include/bits/stdatomic.h
+++ b/libc/include/bits/stdatomic.h
@@ -53,36 +53,16 @@
  * 7.17.1 Atomic lock-free macros.
  */
 
-#ifdef __GCC_ATOMIC_BOOL_LOCK_FREE
-#define	ATOMIC_BOOL_LOCK_FREE		__GCC_ATOMIC_BOOL_LOCK_FREE
-#endif
-#ifdef __GCC_ATOMIC_CHAR_LOCK_FREE
-#define	ATOMIC_CHAR_LOCK_FREE		__GCC_ATOMIC_CHAR_LOCK_FREE
-#endif
-#ifdef __GCC_ATOMIC_CHAR16_T_LOCK_FREE
-#define	ATOMIC_CHAR16_T_LOCK_FREE	__GCC_ATOMIC_CHAR16_T_LOCK_FREE
-#endif
-#ifdef __GCC_ATOMIC_CHAR32_T_LOCK_FREE
-#define	ATOMIC_CHAR32_T_LOCK_FREE	__GCC_ATOMIC_CHAR32_T_LOCK_FREE
-#endif
-#ifdef __GCC_ATOMIC_WCHAR_T_LOCK_FREE
-#define	ATOMIC_WCHAR_T_LOCK_FREE	__GCC_ATOMIC_WCHAR_T_LOCK_FREE
-#endif
-#ifdef __GCC_ATOMIC_SHORT_LOCK_FREE
-#define	ATOMIC_SHORT_LOCK_FREE		__GCC_ATOMIC_SHORT_LOCK_FREE
-#endif
-#ifdef __GCC_ATOMIC_INT_LOCK_FREE
-#define	ATOMIC_INT_LOCK_FREE		__GCC_ATOMIC_INT_LOCK_FREE
-#endif
-#ifdef __GCC_ATOMIC_LONG_LOCK_FREE
-#define	ATOMIC_LONG_LOCK_FREE		__GCC_ATOMIC_LONG_LOCK_FREE
-#endif
-#ifdef __GCC_ATOMIC_LLONG_LOCK_FREE
-#define	ATOMIC_LLONG_LOCK_FREE		__GCC_ATOMIC_LLONG_LOCK_FREE
-#endif
-#ifdef __GCC_ATOMIC_POINTER_LOCK_FREE
-#define	ATOMIC_POINTER_LOCK_FREE	__GCC_ATOMIC_POINTER_LOCK_FREE
-#endif
+#define	ATOMIC_BOOL_LOCK_FREE			__CLANG_ATOMIC_BOOL_LOCK_FREE
+#define	ATOMIC_CHAR_LOCK_FREE			__CLANG_ATOMIC_CHAR_LOCK_FREE
+#define	ATOMIC_CHAR16_T_LOCK_FREE	__CLANG_ATOMIC_CHAR16_T_LOCK_FREE
+#define	ATOMIC_CHAR32_T_LOCK_FREE	__CLANG_ATOMIC_CHAR32_T_LOCK_FREE
+#define	ATOMIC_WCHAR_T_LOCK_FREE	__CLANG_ATOMIC_WCHAR_T_LOCK_FREE
+#define	ATOMIC_SHORT_LOCK_FREE		__CLANG_ATOMIC_SHORT_LOCK_FREE
+#define	ATOMIC_INT_LOCK_FREE			__CLANG_ATOMIC_INT_LOCK_FREE
+#define	ATOMIC_LONG_LOCK_FREE			__CLANG_ATOMIC_LONG_LOCK_FREE
+#define	ATOMIC_LLONG_LOCK_FREE		__CLANG_ATOMIC_LLONG_LOCK_FREE
+#define	ATOMIC_POINTER_LOCK_FREE	__CLANG_ATOMIC_POINTER_LOCK_FREE
 
 /*
  * 7.17.2 Initialization.
@@ -92,31 +72,6 @@
 #define	atomic_init(obj, value)		__c11_atomic_init(obj, value)
 
 /*
- * Clang and recent GCC both provide predefined macros for the memory
- * orderings.  If we are using a compiler that doesn't define them, use the
- * clang values - these will be ignored in the fallback path.
- */
-
-#ifndef __ATOMIC_RELAXED
-#define __ATOMIC_RELAXED		0
-#endif
-#ifndef __ATOMIC_CONSUME
-#define __ATOMIC_CONSUME		1
-#endif
-#ifndef __ATOMIC_ACQUIRE
-#define __ATOMIC_ACQUIRE		2
-#endif
-#ifndef __ATOMIC_RELEASE
-#define __ATOMIC_RELEASE		3
-#endif
-#ifndef __ATOMIC_ACQ_REL
-#define __ATOMIC_ACQ_REL		4
-#endif
-#ifndef __ATOMIC_SEQ_CST
-#define __ATOMIC_SEQ_CST		5
-#endif
-
-/*
  * 7.17.3 Order and consistency.
  *
  * The memory_order_* constants that denote the barrier behaviour of the
@@ -140,11 +95,11 @@
  * 7.17.4 Fences.
  */
 
-static __inline void atomic_thread_fence(memory_order __order __attribute__((__unused__))) {
+static __inline void atomic_thread_fence(memory_order __order) {
 	__c11_atomic_thread_fence(__order);
 }
 
-static __inline void atomic_signal_fence(memory_order __order __attribute__((__unused__))) {
+static __inline void atomic_signal_fence(memory_order __order) {
 	__c11_atomic_signal_fence(__order);
 }
 
@@ -261,15 +216,17 @@
 /*
  * 7.17.8 Atomic flag type and operations.
  *
- * XXX: Assume atomic_bool can be used as an atomic_flag. Is there some
- * kind of compiler built-in type we could use?
+ * atomic_bool can be used to provide a lock-free atomic flag type on every
+ * Android architecture, so this shouldn't be needed in new Android code,
+ * but is in ISO C, and available for portability to PA-RISC and
+ * microcontrollers.
  */
 
 typedef struct {
 	atomic_bool	__flag;
 } atomic_flag;
 
-#define	ATOMIC_FLAG_INIT		{ ATOMIC_VAR_INIT(false) }
+#define	ATOMIC_FLAG_INIT {false}
 
 static __inline bool atomic_flag_test_and_set_explicit(volatile atomic_flag * _Nonnull __object, memory_order __order) {
 	return (atomic_exchange_explicit(&__object->__flag, 1, __order));
diff --git a/libc/include/fts.h b/libc/include/fts.h
index ac3ecc8..69e17df 100644
--- a/libc/include/fts.h
+++ b/libc/include/fts.h
@@ -98,7 +98,7 @@
 	int fts_rfd;			/* fd for root */
 	size_t fts_pathlen;		/* sizeof(path) */
 	int fts_nitems;			/* elements in the sort array */
-	int (* _Nullable fts_compar)(const FTSENT **, const FTSENT **);		/* compare function */
+	int (* _Nullable fts_compar)(const FTSENT* _Nonnull * _Nonnull, const FTSENT* _Nonnull * _Nonnull);		/* compare function */
 
 #define	FTS_COMFOLLOW	0x0001		/* follow command line symlinks */
 #define	FTS_LOGICAL	0x0002		/* logical walk */
diff --git a/libc/include/pthread.h b/libc/include/pthread.h
index cdf1b8c..5a3376a 100644
--- a/libc/include/pthread.h
+++ b/libc/include/pthread.h
@@ -170,8 +170,6 @@
 
 int pthread_getcpuclockid(pthread_t __pthread, clockid_t* _Nonnull __clock);
 
-void* _Nullable pthread_getspecific(pthread_key_t __key);
-
 pid_t pthread_gettid_np(pthread_t __pthread);
 
 int pthread_join(pthread_t __pthread, void* _Nullable * _Nullable __return_value_ptr);
@@ -189,6 +187,8 @@
  * different language, you should consider similar implementation choices and
  * avoid a direct one-to-one mapping from thread locals to pthread keys.
  *
+ * The destructor function is only called for non-null values.
+ *
  * Returns 0 on success and returns an error number on failure.
  */
 int pthread_key_create(pthread_key_t* _Nonnull __key_ptr, void (* _Nullable __key_destructor)(void* _Nullable));
@@ -197,10 +197,28 @@
  * [pthread_key_delete(3)](https://man7.org/linux/man-pages/man3/pthread_key_delete.3p.html)
  * deletes a key for thread-specific data.
  *
+ * Note that pthread_key_delete() does _not_ run destructor functions:
+ * the caller must take care of any necessary cleanup of thread-specific data themselves.
+ * This function only deletes the key itself.
+ *
  * Returns 0 on success and returns an error number on failure.
  */
 int pthread_key_delete(pthread_key_t __key);
 
+/**
+ * [pthread_getspecific(3)](https://man7.org/linux/man-pages/man3/pthread_getspecific.3p.html)
+ * returns the calling thread's thread-specific value for the given key.
+ */
+void* _Nullable pthread_getspecific(pthread_key_t __key);
+
+/**
+ * [pthread_setspecific(3)](https://man7.org/linux/man-pages/man3/pthread_setspecific.3p.html)
+ * sets the calling thread's thread-specific value for the given key.
+ *
+ * Returns 0 on success and returns an error number on failure.
+ */
+int pthread_setspecific(pthread_key_t __key, const void* _Nullable __value);
+
 int pthread_mutexattr_destroy(pthread_mutexattr_t* _Nonnull __attr);
 int pthread_mutexattr_getpshared(const pthread_mutexattr_t* _Nonnull __attr, int* _Nonnull __shared);
 int pthread_mutexattr_gettype(const pthread_mutexattr_t* _Nonnull __attr, int* _Nonnull __type);
@@ -406,9 +424,6 @@
 int pthread_setschedprio(pthread_t __pthread, int __priority) __INTRODUCED_IN(28);
 #endif /* __BIONIC_AVAILABILITY_GUARD(28) */
 
-
-int pthread_setspecific(pthread_key_t __key, const void* _Nullable __value);
-
 typedef void (* _Nullable __pthread_cleanup_func_t)(void* _Nullable);
 
 typedef struct __pthread_cleanup_t {
diff --git a/libc/platform/bionic/tls.h b/libc/platform/bionic/tls.h
index e01eccd..e77e91f 100644
--- a/libc/platform/bionic/tls.h
+++ b/libc/platform/bionic/tls.h
@@ -28,16 +28,78 @@
 
 #pragma once
 
+#include <sys/cdefs.h>
+
 #if defined(__aarch64__)
-# define __get_tls() ({ void** __val; __asm__("mrs %0, tpidr_el0" : "=r"(__val)); __val; })
+
+static inline void** __get_tls() {
+  void** result;
+  __asm__("mrs %0, tpidr_el0" : "=r"(result));
+  return result;
+}
+
+static inline void __set_tls(void* tls) {
+  __asm__("msr tpidr_el0, %0" : : "r" (tls));
+}
+
 #elif defined(__arm__)
-# define __get_tls() ({ void** __val; __asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(__val)); __val; })
+
+static inline void** __get_tls() {
+  void** result;
+  __asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(result));
+  return result;
+}
+
+// arm32 requires a syscall to set the thread pointer.
+// By historical accident it's public API, but not in any header except this one.
+__BEGIN_DECLS
+int __set_tls(void* tls);
+__END_DECLS
+
 #elif defined(__i386__)
-# define __get_tls() ({ void** __val; __asm__("movl %%gs:0, %0" : "=r"(__val)); __val; })
+
+static inline void** __get_tls() {
+  void** result;
+  __asm__("movl %%gs:0, %0" : "=r"(result));
+  return result;
+}
+
+// x86 is really hairy, so we keep that out of line.
+__BEGIN_DECLS
+int __set_tls(void* tls);
+__END_DECLS
+
 #elif defined(__riscv)
-# define __get_tls() ({ void** __val; __asm__("mv %0, tp" : "=r"(__val)); __val; })
+
+static inline void** __get_tls() {
+  void** result;
+  __asm__("mv %0, tp" : "=r"(result));
+  return result;
+}
+
+static inline void __set_tls(void* tls) {
+  __asm__("mv tp, %0" : : "r"(tls));
+}
+
 #elif defined(__x86_64__)
-# define __get_tls() ({ void** __val; __asm__("mov %%fs:0, %0" : "=r"(__val)); __val; })
+
+static inline void** __get_tls() {
+  void** result;
+  __asm__("mov %%fs:0, %0" : "=r"(result));
+  return result;
+}
+
+// ARCH_SET_FS is not exposed via <sys/prctl.h> or <linux/prctl.h>.
+#include <asm/prctl.h>
+// This syscall stub is generated but it's not declared in any header.
+__BEGIN_DECLS
+int arch_prctl(int, unsigned long);
+__END_DECLS
+
+static inline int __set_tls(void* tls) {
+  return arch_prctl(ARCH_SET_FS, reinterpret_cast<unsigned long>(tls));
+}
+
 #else
 #error unsupported architecture
 #endif
diff --git a/libc/private/icu.h b/libc/private/icu4x.h
similarity index 68%
rename from libc/private/icu.h
rename to libc/private/icu4x.h
index 8e4aa80..8b7e1d0 100644
--- a/libc/private/icu.h
+++ b/libc/private/icu4x.h
@@ -26,38 +26,20 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _PRIVATE_ICU_H
-#define _PRIVATE_ICU_H
+#pragma once
 
+#include <ctype.h>
 #include <stdint.h>
 #include <wchar.h>
 
-typedef int8_t UBool;
-#define FALSE 0
-#define TRUE 1
-
-typedef int32_t UChar32;
-
-enum UProperty {
-  UCHAR_ALPHABETIC = 0,
-  UCHAR_DEFAULT_IGNORABLE_CODE_POINT = 5,
-  UCHAR_LOWERCASE = 22,
-  UCHAR_POSIX_ALNUM = 44,
-  UCHAR_POSIX_BLANK = 45,
-  UCHAR_POSIX_GRAPH = 46,
-  UCHAR_POSIX_PRINT = 47,
-  UCHAR_POSIX_XDIGIT = 48,
-  UCHAR_UPPERCASE = 30,
-  UCHAR_WHITE_SPACE = 31,
-  UCHAR_EAST_ASIAN_WIDTH = 0x1004,
-  UCHAR_HANGUL_SYLLABLE_TYPE = 0x100b,
-};
-
 enum UCharCategory {
   U_NON_SPACING_MARK = 6,
   U_ENCLOSING_MARK = 7,
+  U_DECIMAL_NUMBER = 9,
   U_CONTROL_CHAR = 15,
   U_FORMAT_CHAR = 16,
+  U_DASH_PUNCTUATION = 19,
+  U_OTHER_PUNCTUATION = 23,
 };
 
 enum UEastAsianWidth {
@@ -78,11 +60,24 @@
   U_HST_LVT_SYLLABLE,
 };
 
-int8_t __icu_charType(wint_t wc);
-int32_t __icu_getIntPropertyValue(wint_t wc, UProperty property);
+__BEGIN_DECLS
 
-typedef UBool (*u_hasBinaryProperty_t)(UChar32, UProperty);
+uint8_t __icu4x_bionic_general_category(uint32_t cp);
+uint8_t __icu4x_bionic_east_asian_width(uint32_t cp);
+uint8_t __icu4x_bionic_hangul_syllable_type(uint32_t cp);
 
-void* __find_icu_symbol(const char* symbol_name);
+bool __icu4x_bionic_is_alphabetic(uint32_t cp);
+bool __icu4x_bionic_is_default_ignorable_code_point(uint32_t cp);
+bool __icu4x_bionic_is_lowercase(uint32_t cp);
+bool __icu4x_bionic_is_alnum(uint32_t cp);
+bool __icu4x_bionic_is_blank(uint32_t cp);
+bool __icu4x_bionic_is_graph(uint32_t cp);
+bool __icu4x_bionic_is_print(uint32_t cp);
+bool __icu4x_bionic_is_xdigit(uint32_t cp);
+bool __icu4x_bionic_is_white_space(uint32_t cp);
+bool __icu4x_bionic_is_uppercase(uint32_t cp);
 
-#endif  // _PRIVATE_ICU_H
+uint32_t __icu4x_bionic_to_upper(uint32_t ch);
+uint32_t __icu4x_bionic_to_lower(uint32_t ch);
+
+__END_DECLS
diff --git a/libc/system_properties/prop_area.cpp b/libc/system_properties/prop_area.cpp
index 9b153ca..faa3edf 100644
--- a/libc/system_properties/prop_area.cpp
+++ b/libc/system_properties/prop_area.cpp
@@ -72,7 +72,7 @@
   if (context) {
     if (fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0) != 0) {
       async_safe_format_log(ANDROID_LOG_ERROR, "libc",
-                            "fsetxattr failed to set context (%s) for \"%s\"", context, filename);
+                            "fsetxattr failed to set context (%s) for \"%s\": %m", context, filename);
       /*
        * fsetxattr() will fail during system properties tests due to selinux policy.
        * We do not want to create a custom policy for the tester, so we will continue in
diff --git a/libc/tools/gensyscalls.py b/libc/tools/gensyscalls.py
index 6eb0d5e..d371b72 100755
--- a/libc/tools/gensyscalls.py
+++ b/libc/tools/gensyscalls.py
@@ -27,6 +27,7 @@
 # ARM assembler templates for each syscall stub
 #
 
+# ARM assembler template for a syscall stub needing 4 or fewer registers
 arm_call_default = syscall_stub_header + """\
     mov     ip, r7
     .cfi_register r7, ip
@@ -41,6 +42,7 @@
 END(%(func)s)
 """
 
+# ARM assembler template for a syscall stub needing more than 4 registers
 arm_call_long = syscall_stub_header + """\
     mov     ip, sp
     stmfd   sp!, {r4, r5, r6, r7}
@@ -165,13 +167,13 @@
 
     # Second, check that there is no pointer type here
     if param.find("*") >= 0:
-            return False
+        return False
 
     # Ok
     return True
 
 
-def count_arm_param_registers(params):
+def count_param_registers_arm32(params):
     """This function is used to count the number of register used
        to pass parameters when invoking an ARM system call.
        This is because the ARM EABI mandates that 64-bit quantities
@@ -196,7 +198,7 @@
     return count
 
 
-def count_generic_param_registers(params):
+def count_param_registers_x86(params):
     count = 0
     for param in params:
         if param_uses_64bits(param):
@@ -206,13 +208,6 @@
     return count
 
 
-def count_generic_param_registers64(params):
-    count = 0
-    for param in params:
-        count += 1
-    return count
-
-
 # This lets us support regular system calls like __NR_write and also weird
 # ones like __ARM_NR_cacheflush, where the NR doesn't come at the start.
 def make__NR_name(name):
@@ -231,7 +226,7 @@
 
 
 def arm_genstub(syscall):
-    num_regs = count_arm_param_registers(syscall["params"])
+    num_regs = count_param_registers_arm32(syscall["params"])
     if num_regs > 4:
         return arm_call_long % syscall
     return arm_call_default % syscall
@@ -248,7 +243,7 @@
 def x86_genstub(syscall):
     result     = syscall_stub_header % syscall
 
-    numparams = count_generic_param_registers(syscall["params"])
+    numparams = count_param_registers_x86(syscall["params"])
     stack_bias = numparams*4 + 8
     offset = 0
     mov_result = ""
@@ -316,7 +311,7 @@
 
 def x86_64_genstub(syscall):
     result = syscall_stub_header % syscall
-    num_regs = count_generic_param_registers64(syscall["params"])
+    num_regs = len(syscall["params"])
     if (num_regs > 3):
         # rcx is used as 4th argument. Kernel wants it at r10.
         result += "    movq    %rcx, %r10\n"
@@ -338,8 +333,11 @@
     def parse_line(self, line):
         """ parse a syscall spec line.
 
-        line processing, format is
-           return type    func_name[|alias_list][:syscall_name[:socketcall_id]] ( [paramlist] ) architecture_list
+        format is one syscall per line:
+
+           func_name[|alias_list][:syscall_name[:socketcall_id]] ( [paramlist] ) architecture_list
+
+        with no line breaking/continuation allowed.
         """
         pos_lparen = line.find('(')
         E          = self.E
@@ -352,12 +350,7 @@
             E("missing or misplaced right parenthesis in '%s'" % line)
             return
 
-        return_type = line[:pos_lparen].strip().split()
-        if len(return_type) < 2:
-            E("missing return type in '%s'" % line)
-            return
-
-        syscall_func = return_type[-1]
+        syscall_func = line[:pos_lparen]
         socketcall_id = -1
 
         pos_colon = syscall_func.find(':')
diff --git a/linker/Android.bp b/linker/Android.bp
index 8300f01..ea4e699 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -76,14 +76,6 @@
 // Configuration for the linker binary and any of its static libraries.
 cc_defaults {
     name: "linker_defaults",
-    arch: {
-        arm: {
-            cflags: ["-D__work_around_b_24465209__"],
-        },
-        x86: {
-            cflags: ["-D__work_around_b_24465209__"],
-        },
-    },
 
     cflags: linker_common_flags,
     asflags: linker_common_flags,
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index f811d6d..fc95903 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -345,7 +345,7 @@
     __libdl_info->soname_ = linker_si.soname_;
     __libdl_info->target_sdk_version_ = __ANDROID_API__;
     __libdl_info->generate_handle();
-#if defined(__work_around_b_24465209__)
+#if !defined(__LP64__)
     strlcpy(__libdl_info->old_name_, __libdl_info->soname_.c_str(),
             sizeof(__libdl_info->old_name_));
 #endif
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 2393f13..2db6e81 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -346,7 +346,7 @@
     }
   }
 
-  if (si->has_min_version(6) && si->get_gap_size()) {
+  if (si->is_lp64_or_has_min_version(6) && si->get_gap_size()) {
     munmap(reinterpret_cast<void*>(si->get_gap_start()), si->get_gap_size());
   }
 
@@ -1907,7 +1907,7 @@
 
     local_unload_list.push_back(si);
 
-    if (si->has_min_version(0)) {
+    if (si->is_lp64_or_has_min_version(0)) {
       soinfo* child = nullptr;
       while ((child = si->get_children().pop_front()) != nullptr) {
         LD_DEBUG(any, "%s@%p needs to unload %s@%p", si->get_realpath(), si,
@@ -2671,7 +2671,7 @@
 
 template <typename F>
 static bool for_each_verdef(const soinfo* si, F functor) {
-  if (!si->has_min_version(2)) {
+  if (!si->is_lp64_or_has_min_version(2)) {
     return true;
   }
 
@@ -2762,7 +2762,7 @@
 }
 
 bool VersionTracker::init(const soinfo* si_from) {
-  if (!si_from->has_min_version(2)) {
+  if (!si_from->is_lp64_or_has_min_version(2)) {
     return true;
   }
 
diff --git a/linker/linker_namespaces.cpp b/linker/linker_namespaces.cpp
index eb9dae9..55168fd 100644
--- a/linker/linker_namespaces.cpp
+++ b/linker/linker_namespaces.cpp
@@ -73,7 +73,7 @@
   auto is_accessible_ftor = [this] (soinfo* si, bool allow_secondary) {
     // This is workaround for apps hacking into soinfo list.
     // and inserting their own entries into it. (http://b/37191433)
-    if (!si->has_min_version(3)) {
+    if (!si->is_lp64_or_has_min_version(3)) {
       DL_WARN("Warning: invalid soinfo version for \"%s\" (assuming inaccessible)",
               si->get_soname());
       return false;
diff --git a/linker/linker_relocate.cpp b/linker/linker_relocate.cpp
index bbf8359..94281cb 100644
--- a/linker/linker_relocate.cpp
+++ b/linker/linker_relocate.cpp
@@ -605,7 +605,7 @@
   Relocator relocator(version_tracker, lookup_list);
   relocator.si = this;
   relocator.si_strtab = strtab_;
-  relocator.si_strtab_size = has_min_version(1) ? strtab_size_ : SIZE_MAX;
+  relocator.si_strtab_size = is_lp64_or_has_min_version(1) ? strtab_size_ : SIZE_MAX;
   relocator.si_symtab = symtab_;
   relocator.tlsdesc_args = &tlsdesc_args_;
   relocator.tls_tp_base = __libc_shared_globals()->static_tls_layout.offset_thread_pointer();
diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp
index b626313..b3b9da3 100644
--- a/linker/linker_soinfo.cpp
+++ b/linker/linker_soinfo.cpp
@@ -217,7 +217,7 @@
 }
 
 void soinfo::set_dt_runpath(const char* path) {
-  if (!has_min_version(3)) {
+  if (!is_lp64_or_has_min_version(3)) {
     return;
   }
 
@@ -244,35 +244,19 @@
 }
 
 ElfW(Addr) soinfo::get_verneed_ptr() const {
-  if (has_min_version(2)) {
-    return verneed_ptr_;
-  }
-
-  return 0;
+  return is_lp64_or_has_min_version(2)? verneed_ptr_ : 0;
 }
 
 size_t soinfo::get_verneed_cnt() const {
-  if (has_min_version(2)) {
-    return verneed_cnt_;
-  }
-
-  return 0;
+  return is_lp64_or_has_min_version(2) ? verneed_cnt_ : 0;
 }
 
 ElfW(Addr) soinfo::get_verdef_ptr() const {
-  if (has_min_version(2)) {
-    return verdef_ptr_;
-  }
-
-  return 0;
+  return is_lp64_or_has_min_version(2) ? verdef_ptr_ : 0;
 }
 
 size_t soinfo::get_verdef_cnt() const {
-  if (has_min_version(2)) {
-    return verdef_cnt_;
-  }
-
-  return 0;
+  return is_lp64_or_has_min_version(2) ? verdef_cnt_ : 0;
 }
 
 SymbolLookupLib soinfo::get_lookup_lib() {
@@ -530,14 +514,14 @@
 }
 
 void soinfo::add_child(soinfo* child) {
-  if (has_min_version(0)) {
+  if (is_lp64_or_has_min_version(0)) {
     child->parents_.push_back(this);
     this->children_.push_back(child);
   }
 }
 
 void soinfo::remove_all_links() {
-  if (!has_min_version(0)) {
+  if (!is_lp64_or_has_min_version(0)) {
     return;
   }
 
@@ -571,47 +555,27 @@
 }
 
 dev_t soinfo::get_st_dev() const {
-  if (has_min_version(0)) {
-    return st_dev_;
-  }
-
-  return 0;
+  return is_lp64_or_has_min_version(0) ? st_dev_ : 0;
 };
 
 ino_t soinfo::get_st_ino() const {
-  if (has_min_version(0)) {
-    return st_ino_;
-  }
-
-  return 0;
+  return is_lp64_or_has_min_version(0) ? st_ino_ : 0;
 }
 
 off64_t soinfo::get_file_offset() const {
-  if (has_min_version(1)) {
-    return file_offset_;
-  }
-
-  return 0;
+  return is_lp64_or_has_min_version(1) ? file_offset_ : 0;
 }
 
 uint32_t soinfo::get_rtld_flags() const {
-  if (has_min_version(1)) {
-    return rtld_flags_;
-  }
-
-  return 0;
+  return is_lp64_or_has_min_version(1) ? rtld_flags_ : 0;
 }
 
 uint32_t soinfo::get_dt_flags_1() const {
-  if (has_min_version(1)) {
-    return dt_flags_1_;
-  }
-
-  return 0;
+  return is_lp64_or_has_min_version(1) ? dt_flags_1_ : 0;
 }
 
 void soinfo::set_dt_flags_1(uint32_t dt_flags_1) {
-  if (has_min_version(1)) {
+  if (is_lp64_or_has_min_version(1)) {
     if ((dt_flags_1 & DF_1_GLOBAL) != 0) {
       rtld_flags_ |= RTLD_GLOBAL;
     }
@@ -629,47 +593,33 @@
 }
 
 void soinfo::set_realpath(const char* path) {
-#if defined(__work_around_b_24465209__)
-  if (has_min_version(2)) {
+  if (is_lp64_or_has_min_version(2)) {
     realpath_ = path;
   }
-#else
-  realpath_ = path;
-#endif
 }
 
 const char* soinfo::get_realpath() const {
-#if defined(__work_around_b_24465209__)
-  if (has_min_version(2)) {
-    return realpath_.c_str();
-  } else {
-    return old_name_;
-  }
-#else
+#if defined(__LP64__)
   return realpath_.c_str();
+#else
+  return is_lp64_or_has_min_version(2) ? realpath_.c_str() : old_name_;
 #endif
 }
 
 void soinfo::set_soname(const char* soname) {
-#if defined(__work_around_b_24465209__)
-  if (has_min_version(2)) {
+  if (is_lp64_or_has_min_version(2)) {
     soname_ = soname;
   }
+#if !defined(__LP64__)
   strlcpy(old_name_, soname_.c_str(), sizeof(old_name_));
-#else
-  soname_ = soname;
 #endif
 }
 
 const char* soinfo::get_soname() const {
-#if defined(__work_around_b_24465209__)
-  if (has_min_version(2)) {
-    return soname_.c_str();
-  } else {
-    return old_name_;
-  }
-#else
+#if defined(__LP64__)
   return soname_.c_str();
+#else
+  return is_lp64_or_has_min_version(2) ? soname_.c_str() : old_name_;
 #endif
 }
 
@@ -678,59 +628,39 @@
 static soinfo_list_t g_empty_list;
 
 soinfo_list_t& soinfo::get_children() {
-  if (has_min_version(0)) {
-    return children_;
-  }
-
-  return g_empty_list;
+  return is_lp64_or_has_min_version(0) ? children_ : g_empty_list;
 }
 
 const soinfo_list_t& soinfo::get_children() const {
-  if (has_min_version(0)) {
-    return children_;
-  }
-
-  return g_empty_list;
+  return is_lp64_or_has_min_version(0) ? children_ : g_empty_list;
 }
 
 soinfo_list_t& soinfo::get_parents() {
-  if (has_min_version(0)) {
-    return parents_;
-  }
-
-  return g_empty_list;
+  return is_lp64_or_has_min_version(0) ? parents_ : g_empty_list;
 }
 
 static std::vector<std::string> g_empty_runpath;
 
 const std::vector<std::string>& soinfo::get_dt_runpath() const {
-  if (has_min_version(3)) {
-    return dt_runpath_;
-  }
-
-  return g_empty_runpath;
+  return is_lp64_or_has_min_version(3) ? dt_runpath_ : g_empty_runpath;
 }
 
 android_namespace_t* soinfo::get_primary_namespace() {
-  if (has_min_version(3)) {
-    return primary_namespace_;
-  }
-
-  return &g_default_namespace;
+  return is_lp64_or_has_min_version(3) ? primary_namespace_ : &g_default_namespace;
 }
 
 void soinfo::add_secondary_namespace(android_namespace_t* secondary_ns) {
-  CHECK(has_min_version(3));
+  CHECK(is_lp64_or_has_min_version(3));
   secondary_namespaces_.push_back(secondary_ns);
 }
 
 android_namespace_list_t& soinfo::get_secondary_namespaces() {
-  CHECK(has_min_version(3));
+  CHECK(is_lp64_or_has_min_version(3));
   return secondary_namespaces_;
 }
 
 const char* soinfo::get_string(ElfW(Word) index) const {
-  if (has_min_version(1) && (index >= strtab_size_)) {
+  if (is_lp64_or_has_min_version(1) && (index >= strtab_size_)) {
     async_safe_fatal("%s: strtab out of bounds error; STRSZ=%zd, name=%d",
         get_realpath(), strtab_size_, index);
   }
@@ -813,7 +743,7 @@
 // dlopen/load. Note that libraries opened by system
 // will always have 'current' target sdk version.
 int soinfo::get_target_sdk_version() const {
-  if (!has_min_version(2)) {
+  if (!is_lp64_or_has_min_version(2)) {
     return __ANDROID_API__;
   }
 
@@ -821,13 +751,13 @@
 }
 
 uintptr_t soinfo::get_handle() const {
-  CHECK(has_min_version(3));
+  CHECK(is_lp64_or_has_min_version(3));
   CHECK(handle_ != 0);
   return handle_;
 }
 
 void* soinfo::to_handle() {
-  if (get_application_target_sdk_version() < 24 || !has_min_version(3)) {
+  if (get_application_target_sdk_version() < 24 || !is_lp64_or_has_min_version(3)) {
     return this;
   }
 
@@ -835,7 +765,7 @@
 }
 
 void soinfo::generate_handle() {
-  CHECK(has_min_version(3));
+  CHECK(is_lp64_or_has_min_version(3));
   CHECK(handle_ == 0); // Make sure this is the first call
 
   // Make sure the handle is unique and does not collide
@@ -861,20 +791,20 @@
 }
 
 void soinfo::set_gap_start(ElfW(Addr) gap_start) {
-  CHECK(has_min_version(6));
+  CHECK(is_lp64_or_has_min_version(6));
   gap_start_ = gap_start;
 }
 ElfW(Addr) soinfo::get_gap_start() const {
-  CHECK(has_min_version(6));
+  CHECK(is_lp64_or_has_min_version(6));
   return gap_start_;
 }
 
 void soinfo::set_gap_size(size_t gap_size) {
-  CHECK(has_min_version(6));
+  CHECK(is_lp64_or_has_min_version(6));
   gap_size_ = gap_size;
 }
 size_t soinfo::get_gap_size() const {
-  CHECK(has_min_version(6));
+  CHECK(is_lp64_or_has_min_version(6));
   return gap_size_;
 }
 
diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h
index 37cee50..8f56c9f 100644
--- a/linker/linker_soinfo.h
+++ b/linker/linker_soinfo.h
@@ -154,31 +154,27 @@
   size_t module_id = kTlsUninitializedModuleId;
 };
 
-#if defined(__work_around_b_24465209__)
-#define SOINFO_NAME_LEN 128
-#endif
-
 struct soinfo {
-#if defined(__work_around_b_24465209__)
+#if !defined(__LP64__)
  private:
-  char old_name_[SOINFO_NAME_LEN];
+  char old_name_[128];
 #endif
  public:
   const ElfW(Phdr)* phdr;
   size_t phnum;
-#if defined(__work_around_b_24465209__)
+#if !defined(__LP64__)
   ElfW(Addr) unused0; // DO NOT USE, maintained for compatibility.
 #endif
   ElfW(Addr) base;
   size_t size;
 
-#if defined(__work_around_b_24465209__)
+#if !defined(__LP64__)
   uint32_t unused1;  // DO NOT USE, maintained for compatibility.
 #endif
 
   ElfW(Dyn)* dynamic;
 
-#if defined(__work_around_b_24465209__)
+#if !defined(__LP64__)
   uint32_t unused2; // DO NOT USE, maintained for compatibility
   uint32_t unused3; // DO NOT USE, maintained for compatibility
 #endif
@@ -294,19 +290,16 @@
   bool can_unload() const;
   bool is_gnu_hash() const;
 
-  bool inline has_min_version(uint32_t min_version __unused) const {
-#if defined(__work_around_b_24465209__)
-    return (flags_ & FLAG_NEW_SOINFO) != 0 && version_ >= min_version;
-#else
-    // If you make this return non-true in the case where
-    // __work_around_b_24465209__ is not defined, you will have to change
-    // memtag_dynamic_entries() and vma_names().
+  inline bool is_lp64_or_has_min_version(uint32_t min_version __unused) const {
+#if defined(__LP64__)
     return true;
+#else
+    return (flags_ & FLAG_NEW_SOINFO) != 0 && version_ >= min_version;
 #endif
   }
 
   const ElfW(Versym)* get_versym_table() const {
-    return has_min_version(2) ? versym_ : nullptr;
+    return is_lp64_or_has_min_version(2) ? versym_ : nullptr;
   }
 
   bool is_linked() const;
@@ -343,7 +336,7 @@
   android_namespace_list_t& get_secondary_namespaces();
 
   soinfo_tls* get_tls() const {
-    return has_min_version(5) ? tls_.get() : nullptr;
+    return is_lp64_or_has_min_version(5) ? tls_.get() : nullptr;
   }
 
   void set_mapped_by_caller(bool reserved_map);
@@ -362,13 +355,11 @@
   size_t get_gap_size() const;
 
   const memtag_dynamic_entries_t* memtag_dynamic_entries() const {
-#ifdef __aarch64__
-#ifdef __work_around_b_24465209__
-#error "Assuming aarch64 does not use versioned soinfo."
-#endif
+#if defined(__aarch64__)
     return &memtag_dynamic_entries_;
-#endif
+#else
     return nullptr;
+#endif
   }
   void* memtag_globals() const {
     const memtag_dynamic_entries_t* entries = memtag_dynamic_entries();
@@ -403,13 +394,11 @@
     return !is_linker() && memtag_globals() && memtag_globalssz() > 0 && __libc_mte_enabled();
   }
   std::list<std::string>* vma_names() {
-#ifdef __aarch64__
-#ifdef __work_around_b_24465209__
-#error "Assuming aarch64 does not use versioned soinfo."
-#endif
+#if defined(__aarch64__)
     return &vma_names_;
-#endif
+#else
     return nullptr;
+#endif
 };
 
   void set_should_use_16kib_app_compat(bool should_use_16kib_app_compat) {
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index b8826c1..b5bf753 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -968,7 +968,6 @@
 
   // create memfd
   int memfd = memfd_create("foobar", MFD_CLOEXEC);
-  if (memfd == -1 && errno == ENOSYS) GTEST_SKIP() << "no memfd_create() in this kernel";
   ASSERT_TRUE(memfd != -1) << strerror(errno);
 
   // Check st.f_type is TMPFS_MAGIC for memfd
diff --git a/tests/headers/posix/stdatomic_h.c b/tests/headers/posix/stdatomic_h.c
index 9f46e6a..270faae 100644
--- a/tests/headers/posix/stdatomic_h.c
+++ b/tests/headers/posix/stdatomic_h.c
@@ -66,6 +66,9 @@
   MACRO(ATOMIC_POINTER_LOCK_FREE);
 
   atomic_flag f = ATOMIC_FLAG_INIT;
+
+  // ATOMIC_VAR_INIT() has been removed from C23,
+  // but not from POSIX 2024.
   atomic_int i = ATOMIC_VAR_INIT(123);
 
   i = kill_dependency(i);
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index 5ce7d4d..680ef6e 100644
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -59,6 +59,39 @@
   ASSERT_EQ(EINVAL, pthread_key_delete(key));
 }
 
+static std::vector<void*> example_key_destructor_data;
+static pthread_key_t example_key;
+static void example_key_destructor(void *data) {
+  // By the time the destructor function is running,
+  // this thread's value for the key should have been zeroed.
+  ASSERT_EQ(NULL, pthread_getspecific(example_key));
+
+  // Store the value so we can check we got the expected result.
+  example_key_destructor_data.push_back(data);
+}
+
+TEST(pthread, pthread_key_destructors) {
+  ASSERT_EQ(0, pthread_key_create(&example_key, example_key_destructor));
+
+  // Check that the destructor isn't called for a default null value.
+  std::thread([]() {}).join();
+  ASSERT_TRUE(example_key_destructor_data.empty());
+
+  // Check that the destructor isn't called for an explicit null value.
+  std::thread([]() {
+    ASSERT_EQ(0, pthread_setspecific(example_key, (void*) 1234));
+    ASSERT_EQ(0, pthread_setspecific(example_key, nullptr));
+  }).join();
+  ASSERT_TRUE(example_key_destructor_data.empty());
+
+  // Check that the destructor is called for a non-null value.
+  std::thread([]() { ASSERT_EQ(0, pthread_setspecific(example_key, (void*) 1234)); }).join();
+  ASSERT_EQ(1u, example_key_destructor_data.size());
+  ASSERT_EQ((void*) 1234, example_key_destructor_data[0]);
+
+  ASSERT_EQ(0, pthread_key_delete(example_key));
+}
+
 TEST(pthread, pthread_keys_max) {
   // POSIX says PTHREAD_KEYS_MAX should be at least _POSIX_THREAD_KEYS_MAX.
   ASSERT_GE(PTHREAD_KEYS_MAX, _POSIX_THREAD_KEYS_MAX);
diff --git a/tests/pty_test.cpp b/tests/pty_test.cpp
index d5d8994..a5e5a57 100644
--- a/tests/pty_test.cpp
+++ b/tests/pty_test.cpp
@@ -139,9 +139,10 @@
   arg.fd = tty;
   arg.data_count = TEST_DATA_COUNT;
   arg.matched = true;
-  ASSERT_EQ(0, pthread_create(&thread, nullptr,
-                              reinterpret_cast<void*(*)(void*)>(PtyReader_28979140),
-                              &arg));
+  ASSERT_EQ(0, pthread_create(&thread, nullptr, [](void* arg)->void* {
+    PtyReader_28979140(static_cast<PtyReader_28979140_Arg*>(arg));
+    return nullptr;
+  }, &arg));
 
   CPU_ZERO(&cpus);
   CPU_SET(arg.main_cpu_id, &cpus);
diff --git a/tests/stdatomic_test.cpp b/tests/stdatomic_test.cpp
index 8a54080..23e9b3e 100644
--- a/tests/stdatomic_test.cpp
+++ b/tests/stdatomic_test.cpp
@@ -16,8 +16,13 @@
 
 #include <gtest/gtest.h>
 
-// The real <stdatomic.h> checks for the availability of C++'s atomics and uses them if present. Since
-// we want to test the libc versions, we instead include <bits/stdatomic.h> where they're actually defined.
+// The real <stdatomic.h> checks for the availability of C++'s <atomic> and
+// uses that instead if present.
+// We want to test the C interfaces, so we instead include
+// <bits/stdatomic.h> directly.
+// This doesn't entirely work because gtest also (transitively) pulls in <atomic>.
+// It's not clear there's a good fix for this,
+// other than switching to a non-C++ unit test framework for bionic.
 #include <bits/stdatomic.h>
 
 #include <pthread.h>
@@ -37,8 +42,18 @@
 }
 
 TEST(stdatomic, init) {
-  atomic_int v = 123;
+  // ATOMIC_VAR_INIT has been removed from C23,
+  // but is still in POSIX 2024.
+  // Even if it is removed from there,
+  // we should probably keep it indefinitely for source compatibility.
+  // libc++'s <atomic> (which we can't entirely avoid: see above)
+  // marks the macro deprecated,
+  // so we need to silence that.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-pragma"
+  atomic_int v = ATOMIC_VAR_INIT(123);
   ASSERT_EQ(123, atomic_load(&v));
+#pragma clang diagnostic pop
 
   atomic_store_explicit(&v, 456, memory_order_relaxed);
   ASSERT_EQ(456, atomic_load(&v));
diff --git a/tests/stdio_ext_test.cpp b/tests/stdio_ext_test.cpp
index dce1a66..dc0e9ef 100644
--- a/tests/stdio_ext_test.cpp
+++ b/tests/stdio_ext_test.cpp
@@ -29,6 +29,8 @@
 #include <wchar.h>
 #include <locale.h>
 
+#include <thread>
+
 #include <android-base/file.h>
 
 #include "utils.h"
@@ -235,23 +237,20 @@
   fclose(fp);
 }
 
-static void LockingByCallerHelper(std::atomic<pid_t>* pid) {
-  *pid = gettid();
-  flockfile(stdout);
-  funlockfile(stdout);
-}
-
 TEST(stdio_ext, __fsetlocking_BYCALLER) {
   // Check if users can use flockfile/funlockfile to protect stdio operations.
   int old_state = __fsetlocking(stdout, FSETLOCKING_BYCALLER);
   flockfile(stdout);
-  pthread_t thread;
+
   std::atomic<pid_t> pid(0);
-  ASSERT_EQ(0, pthread_create(&thread, nullptr,
-                              reinterpret_cast<void* (*)(void*)>(LockingByCallerHelper), &pid));
+  std::thread thread([&]() {
+    pid = gettid();
+    flockfile(stdout);
+    funlockfile(stdout);
+  });
   WaitUntilThreadSleep(pid);
   funlockfile(stdout);
 
-  ASSERT_EQ(0, pthread_join(thread, nullptr));
+  thread.join();
   __fsetlocking(stdout, old_state);
 }
diff --git a/tests/sys_mman_test.cpp b/tests/sys_mman_test.cpp
index 54a0b64..85096bb 100644
--- a/tests/sys_mman_test.cpp
+++ b/tests/sys_mman_test.cpp
@@ -295,7 +295,6 @@
   // Is the MFD_CLOEXEC flag obeyed?
   errno = 0;
   int fd = memfd_create("doesn't matter", 0);
-  if (fd == -1 && errno == ENOSYS) GTEST_SKIP() << "no memfd_create() in this kernel";
   ASSERT_NE(-1, fd) << strerror(errno);
 
   int f = fcntl(fd, F_GETFD);
diff --git a/tests/utils.h b/tests/utils.h
index 4740e59..3b4f2a9 100644
--- a/tests/utils.h
+++ b/tests/utils.h
@@ -65,12 +65,6 @@
 #define KNOWN_FAILURE_ON_BIONIC(x) x
 #endif
 
-// bionic's dlsym doesn't work in static binaries, so we can't access icu,
-// so any unicode test case will fail.
-static inline bool have_dl() {
-  return (dlopen("libc.so", 0) != nullptr);
-}
-
 static inline bool running_with_native_bridge() {
 #if defined(__BIONIC__)
   static const prop_info* pi = __system_property_find("ro.dalvik.vm.isa." ABI_STRING);
diff --git a/tests/wchar_test.cpp b/tests/wchar_test.cpp
index ba2a4d8..c76f800 100644
--- a/tests/wchar_test.cpp
+++ b/tests/wchar_test.cpp
@@ -1062,16 +1062,12 @@
 }
 
 TEST(wchar, wcwidth_non_spacing_and_enclosing_marks_and_format) {
-  if (!have_dl()) return;
-
   EXPECT_EQ(0, wcwidth(0x0300)); // Combining grave.
   EXPECT_EQ(0, wcwidth(0x20dd)); // Combining enclosing circle.
   EXPECT_EQ(0, wcwidth(0x200b)); // Zero width space.
 }
 
 TEST(wchar, wcwidth_non_spacing_special_cases) {
-  if (!have_dl()) return;
-
   // U+00AD is a soft hyphen, which normally shouldn't be rendered at all.
   // I think the assumption here is that you elide the soft hyphen character
   // completely in that case, and never call wcwidth() if you don't want to
@@ -1100,8 +1096,6 @@
 }
 
 TEST(wchar, wcwidth_cjk) {
-  if (!have_dl()) return;
-
   EXPECT_EQ(2, wcwidth(0x4e00)); // Start of CJK unified block.
   EXPECT_EQ(2, wcwidth(0x9fff)); // End of CJK unified block.
   EXPECT_EQ(2, wcwidth(0x3400)); // Start of CJK extension A block.
@@ -1111,16 +1105,12 @@
 }
 
 TEST(wchar, wcwidth_korean_combining_jamo) {
-  if (!have_dl()) return;
-
   AssertWcwidthRange(0x1160, 0x1200, 0); // Original range.
   EXPECT_EQ(0, wcwidth(0xd7b0)); // Newer.
   EXPECT_EQ(0, wcwidth(0xd7cb));
 }
 
 TEST(wchar, wcwidth_korean_jeongeul_syllables) {
-  if (!have_dl()) return;
-
   EXPECT_EQ(2, wcwidth(0xac00)); // Start of block.
   EXPECT_EQ(2, wcwidth(0xd7a3)); // End of defined code points as of Unicode 15.
 
@@ -1129,8 +1119,6 @@
 }
 
 TEST(wchar, wcwidth_kana) {
-  if (!have_dl()) return;
-
   // Hiragana (most, not undefined).
   AssertWcwidthRange(0x3041, 0x3097, 2);
   // Katakana.
@@ -1138,30 +1126,22 @@
 }
 
 TEST(wchar, wcwidth_circled_two_digit_cjk) {
-  if (!have_dl()) return;
-
   // Circled two-digit CJK "speed sign" numbers are wide,
   // though EastAsianWidth is ambiguous.
   AssertWcwidthRange(0x3248, 0x3250, 2);
 }
 
 TEST(wchar, wcwidth_hexagrams) {
-  if (!have_dl()) return;
-
   // Hexagrams are wide, though EastAsianWidth is neutral.
   AssertWcwidthRange(0x4dc0, 0x4e00, 2);
 }
 
 TEST(wchar, wcwidth_default_ignorables) {
-  if (!have_dl()) return;
-
   AssertWcwidthRange(0xfff0, 0xfff8, 0); // Unassigned by default ignorable.
   EXPECT_EQ(0, wcwidth(0xe0000)); // ...through 0xe0fff.
 }
 
 TEST(wchar, wcwidth_hangeul_compatibility_jamo) {
-  if (!have_dl()) return;
-
   // These are actually the *compatibility* jamo code points, *not* the regular
   // jamo code points (U+1100-U+11FF) using a jungseong filler. If you use the
   // Android IME to type any of these, you get these code points.
diff --git a/tests/wctype_test.cpp b/tests/wctype_test.cpp
index f4b7a8f..1a2bbc1 100644
--- a/tests/wctype_test.cpp
+++ b/tests/wctype_test.cpp
@@ -37,20 +37,14 @@
   for (const wchar_t* p = trues; *p; ++p) {
     const wchar_t val_ch = *p;
     const int val_int = static_cast<int>(val_ch);
-    if (!have_dl() && val_ch > 0x7f) {
-      GTEST_LOG_(INFO) << "skipping unicode test " << val_int;
-      continue;
-    }
+
     EXPECT_TRUE(fn(val_ch)) << val_int;
     EXPECT_TRUE(fn_l(val_ch, l.l)) << val_int;
   }
   for (const wchar_t* p = falses; *p; ++p) {
     const wchar_t val_ch = *p;
     const int val_int = static_cast<int>(val_ch);
-    if (!have_dl() && val_ch > 0x7f) {
-      GTEST_LOG_(INFO) << "skipping unicode test " << val_int;
-      continue;
-    }
+
     EXPECT_FALSE(fn(val_ch)) << val_int;
     EXPECT_FALSE(fn_l(val_ch, l.l)) << val_int;
   }
@@ -111,14 +105,10 @@
   EXPECT_EQ(wint_t('a'), towlower(L'A'));
   EXPECT_EQ(wint_t('z'), towlower(L'z'));
   EXPECT_EQ(wint_t('z'), towlower(L'Z'));
-  if (have_dl()) {
-    EXPECT_EQ(wint_t(L'ç'), towlower(L'ç'));
-    EXPECT_EQ(wint_t(L'ç'), towlower(L'Ç'));
-    EXPECT_EQ(wint_t(L'δ'), towlower(L'δ'));
-    EXPECT_EQ(wint_t(L'δ'), towlower(L'Δ'));
-  } else {
-    GTEST_SKIP() << "icu not available";
-  }
+  EXPECT_EQ(wint_t(L'ç'), towlower(L'ç'));
+  EXPECT_EQ(wint_t(L'ç'), towlower(L'Ç'));
+  EXPECT_EQ(wint_t(L'δ'), towlower(L'δ'));
+  EXPECT_EQ(wint_t(L'δ'), towlower(L'Δ'));
 }
 
 TEST(wctype, towlower_l) {
@@ -129,14 +119,10 @@
   EXPECT_EQ(wint_t('a'), towlower_l(L'A', l.l));
   EXPECT_EQ(wint_t('z'), towlower_l(L'z', l.l));
   EXPECT_EQ(wint_t('z'), towlower_l(L'Z', l.l));
-  if (have_dl()) {
-    EXPECT_EQ(wint_t(L'ç'), towlower_l(L'ç', l.l));
-    EXPECT_EQ(wint_t(L'ç'), towlower_l(L'Ç', l.l));
-    EXPECT_EQ(wint_t(L'δ'), towlower_l(L'δ', l.l));
-    EXPECT_EQ(wint_t(L'δ'), towlower_l(L'Δ', l.l));
-  } else {
-    GTEST_SKIP() << "icu not available";
-  }
+  EXPECT_EQ(wint_t(L'ç'), towlower_l(L'ç', l.l));
+  EXPECT_EQ(wint_t(L'ç'), towlower_l(L'Ç', l.l));
+  EXPECT_EQ(wint_t(L'δ'), towlower_l(L'δ', l.l));
+  EXPECT_EQ(wint_t(L'δ'), towlower_l(L'Δ', l.l));
 }
 
 TEST(wctype, towupper) {
@@ -146,14 +132,10 @@
   EXPECT_EQ(wint_t('A'), towupper(L'A'));
   EXPECT_EQ(wint_t('Z'), towupper(L'z'));
   EXPECT_EQ(wint_t('Z'), towupper(L'Z'));
-  if (have_dl()) {
-    EXPECT_EQ(wint_t(L'Ç'), towupper(L'ç'));
-    EXPECT_EQ(wint_t(L'Ç'), towupper(L'Ç'));
-    EXPECT_EQ(wint_t(L'Δ'), towupper(L'δ'));
-    EXPECT_EQ(wint_t(L'Δ'), towupper(L'Δ'));
-  } else {
-    GTEST_SKIP() << "icu not available";
-  }
+  EXPECT_EQ(wint_t(L'Ç'), towupper(L'ç'));
+  EXPECT_EQ(wint_t(L'Ç'), towupper(L'Ç'));
+  EXPECT_EQ(wint_t(L'Δ'), towupper(L'δ'));
+  EXPECT_EQ(wint_t(L'Δ'), towupper(L'Δ'));
 }
 
 TEST(wctype, towupper_l) {
@@ -164,14 +146,10 @@
   EXPECT_EQ(wint_t('A'), towupper_l(L'A', l.l));
   EXPECT_EQ(wint_t('Z'), towupper_l(L'z', l.l));
   EXPECT_EQ(wint_t('Z'), towupper_l(L'Z', l.l));
-  if (have_dl()) {
-    EXPECT_EQ(wint_t(L'Ç'), towupper_l(L'ç', l.l));
-    EXPECT_EQ(wint_t(L'Ç'), towupper_l(L'Ç', l.l));
-    EXPECT_EQ(wint_t(L'Δ'), towupper_l(L'δ', l.l));
-    EXPECT_EQ(wint_t(L'Δ'), towupper_l(L'Δ', l.l));
-  } else {
-    GTEST_SKIP() << "icu not available";
-  }
+  EXPECT_EQ(wint_t(L'Ç'), towupper_l(L'ç', l.l));
+  EXPECT_EQ(wint_t(L'Ç'), towupper_l(L'Ç', l.l));
+  EXPECT_EQ(wint_t(L'Δ'), towupper_l(L'δ', l.l));
+  EXPECT_EQ(wint_t(L'Δ'), towupper_l(L'Δ', l.l));
 }
 
 TEST(wctype, wctype) {