Merge "tests: make fortify tests go on more than x86" into main
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/libc/Android.bp b/libc/Android.bp
index 18f1ad7..f5624ef 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -781,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
 // ========================================================
 
@@ -861,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",
@@ -1173,6 +1219,7 @@
     whole_static_libs: [
         "//external/llvm-libc:llvmlibc",
         "libsystemproperties",
+        "libicu4x_bionic",
     ],
 
     cppflags: ["-Wold-style-cast"],
@@ -1424,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",
@@ -1443,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",
     ],
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_mte.cpp b/libc/bionic/libc_init_mte.cpp
index eda2eec..d23b056 100644
--- a/libc/bionic/libc_init_mte.cpp
+++ b/libc/bionic/libc_init_mte.cpp
@@ -246,6 +246,7 @@
 __attribute__((no_sanitize("hwaddress", "memtag"))) void __libc_init_mte(
     const memtag_dynamic_entries_t* memtag_dynamic_entries, const void* phdr_start, size_t phdr_ct,
     uintptr_t load_bias) {
+  if (__libc_shared_globals()->is_hwasan) return;
   bool memtag_stack = false;
   HeapTaggingLevel level =
       __get_tagging_level(memtag_dynamic_entries, phdr_start, phdr_ct, load_bias, &memtag_stack);
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index ef9f896..9cc3060 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -158,7 +158,7 @@
 }
 
 __attribute__((no_sanitize("memtag"))) __noreturn static void __real_libc_init(
-    KernelArgumentBlock& args, void* raw_args, void (*onexit)(void) __unused,
+    KernelArgumentBlock& args, void* raw_args __unused, void (*onexit)(void) __unused,
     int (*slingshot)(int, char**, char**), structors_array_t const* const structors,
     bionic_tcb* temp_tcb) {
   BIONIC_STOP_UNWIND;
@@ -171,10 +171,12 @@
   layout_static_tls(args);
   __libc_init_main_thread_final();
   __libc_init_common();
+#if !__has_feature(hwaddress_sanitizer)
   __libc_init_mte(/*memtag_dynamic_entries=*/nullptr,
                   reinterpret_cast<ElfW(Phdr)*>(getauxval(AT_PHDR)), getauxval(AT_PHNUM),
                   /*load_bias = */ 0);
   __libc_init_mte_stack(/*stack_top = */ raw_args);
+#endif
   __libc_init_scudo();
   __libc_globals.mutate(__libc_init_malloc);
   __libc_init_profiling_handlers();
@@ -196,8 +198,9 @@
   if (structors->fini_array_count > 0) {
     __cxa_atexit(call_fini_array, const_cast<structors_array_t*>(structors), nullptr);
   }
-
+#if !__has_feature(hwaddress_sanitizer)
   __libc_init_mte_late();
+#endif
 
   exit(slingshot(args.argc, args.argv, args.envp));
 }
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/signal.cpp b/libc/bionic/signal.cpp
index 5979ed7..77e6acf 100644
--- a/libc/bionic/signal.cpp
+++ b/libc/bionic/signal.cpp
@@ -288,16 +288,14 @@
 }
 
 int sigwait64(const sigset64_t* set, int* sig) {
-  while (true) {
-    // __rt_sigtimedwait can return EAGAIN or EINTR, we need to loop
-    // around them since sigwait is only allowed to return EINVAL.
-    int result = sigtimedwait64(set, nullptr, nullptr);
-    if (result >= 0) {
-      *sig = result;
-      return 0;
-    }
-    if (errno != EAGAIN && errno != EINTR) return errno;
-  }
+  // sigtimedwait64() doesn't fail with EINVAL on Linux,
+  // and EAGAIN can only happen with a timeout,
+  // so the error reporting here is effectively dead code.
+  ErrnoRestorer errno_restorer;
+  int result = TEMP_FAILURE_RETRY(sigtimedwait64(set, nullptr, nullptr));
+  if (result == -1) return errno;
+  *sig = result;
+  return 0;
 }
 
 int sigwaitinfo(const sigset_t* set, siginfo_t* info) {
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 9550b91..2a4f7df 100644
--- a/libc/include/android/api-level.h
+++ b/libc/include/android/api-level.h
@@ -39,8 +39,13 @@
  * @file android/api-level.h
  * @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>
@@ -171,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) */
@@ -192,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 0b4b70b..4ec356f 100644
--- a/libc/include/bits/fortify/string.h
+++ b/libc/include/bits/fortify/string.h
@@ -61,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
@@ -127,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 {
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/malloc.h b/libc/include/malloc.h
index ba68401..bb4916a 100644
--- a/libc/include/malloc.h
+++ b/libc/include/malloc.h
@@ -122,7 +122,11 @@
  * [malloc_usable_size(3)](https://man7.org/linux/man-pages/man3/malloc_usable_size.3.html)
  * returns the actual size of the given heap block.
  */
-__nodiscard size_t malloc_usable_size(const void* _Nullable __ptr);
+__nodiscard size_t malloc_usable_size(const void* _Nullable __ptr)
+#if defined(_FORTIFY_SOURCE)
+    __clang_error_if(_FORTIFY_SOURCE == 3, "malloc_usable_size() and _FORTIFY_SOURCE=3 are incompatible")
+#endif
+;
 
 #define __MALLINFO_BODY \
   /** Total number of non-mmapped bytes currently allocated from OS. */ \
diff --git a/libc/platform/bionic/mte.h b/libc/platform/bionic/mte.h
index 610cb45..27cbae1 100644
--- a/libc/platform/bionic/mte.h
+++ b/libc/platform/bionic/mte.h
@@ -50,6 +50,16 @@
   return supported;
 }
 
+static inline bool mte_enabled() {
+#ifdef __aarch64__
+  int level = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+  return level >= 0 && (level & PR_TAGGED_ADDR_ENABLE) &&
+         (level & PR_MTE_TCF_MASK) != PR_MTE_TCF_NONE;
+#else
+  return false;
+#endif
+}
+
 inline void* get_tagged_address(const void* ptr) {
 #if defined(__aarch64__)
   if (mte_supported()) {
diff --git a/libc/platform/bionic/tls.h b/libc/platform/bionic/tls.h
index e77e91f..3e0ef53 100644
--- a/libc/platform/bionic/tls.h
+++ b/libc/platform/bionic/tls.h
@@ -32,7 +32,7 @@
 
 #if defined(__aarch64__)
 
-static inline void** __get_tls() {
+static inline void** __get_tls(void) {
   void** result;
   __asm__("mrs %0, tpidr_el0" : "=r"(result));
   return result;
@@ -44,7 +44,7 @@
 
 #elif defined(__arm__)
 
-static inline void** __get_tls() {
+static inline void** __get_tls(void) {
   void** result;
   __asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(result));
   return result;
@@ -58,7 +58,7 @@
 
 #elif defined(__i386__)
 
-static inline void** __get_tls() {
+static inline void** __get_tls(void) {
   void** result;
   __asm__("movl %%gs:0, %0" : "=r"(result));
   return result;
@@ -71,7 +71,7 @@
 
 #elif defined(__riscv)
 
-static inline void** __get_tls() {
+static inline void** __get_tls(void) {
   void** result;
   __asm__("mv %0, tp" : "=r"(result));
   return result;
@@ -83,7 +83,7 @@
 
 #elif defined(__x86_64__)
 
-static inline void** __get_tls() {
+static inline void** __get_tls(void) {
   void** result;
   __asm__("mov %%fs:0, %0" : "=r"(result));
   return result;
diff --git a/libc/private/bionic_globals.h b/libc/private/bionic_globals.h
index 77ac21f..2346a4d 100644
--- a/libc/private/bionic_globals.h
+++ b/libc/private/bionic_globals.h
@@ -152,6 +152,8 @@
   bool initial_memtag_stack_abi = false;
   int64_t heap_tagging_upgrade_timer_sec = 0;
 
+  bool is_hwasan = false;
+
   void (*memtag_stack_dlopen_callback)() = nullptr;
   pthread_mutex_t crash_detail_page_lock = PTHREAD_MUTEX_INITIALIZER;
   crash_detail_page_t* crash_detail_page = nullptr;
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/linker/linker.cpp b/linker/linker.cpp
index 2db6e81..4cf93b9 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -3614,14 +3614,7 @@
   return kLdConfigFilePath;
 }
 
-
-std::vector<android_namespace_t*> init_default_namespaces(const char* executable_path) {
-  g_default_namespace.set_name("(default)");
-
-  soinfo* somain = solist_get_somain();
-
-  const char *interp = phdr_table_get_interpreter_name(somain->phdr, somain->phnum,
-                                                       somain->load_bias);
+void init_sanitizer_mode(const char *interp ) {
   const char* bname = (interp != nullptr) ? basename(interp) : nullptr;
 
   g_is_asan = bname != nullptr &&
@@ -3637,7 +3630,13 @@
   g_is_hwasan = (bname != nullptr &&
               strcmp(bname, "linker_hwasan64") == 0) ||
               (hwasan_env != nullptr && !getauxval(AT_SECURE) && strcmp(hwasan_env, "1") == 0);
+  __libc_shared_globals()->is_hwasan = g_is_hwasan;
 #endif
+}
+
+std::vector<android_namespace_t*> init_default_namespaces(const char* executable_path) {
+  g_default_namespace.set_name("(default)");
+
   const Config* config = nullptr;
 
   {
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index 40f6b9a..425bcda 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -366,6 +366,7 @@
   }
   solinker->set_realpath(interp);
   init_link_map_head(*solinker);
+  init_sanitizer_mode(interp);
 
 #if defined(__aarch64__)
   __libc_init_mte(somain->memtag_dynamic_entries(), somain->phdr, somain->phnum, somain->load_bias);
diff --git a/linker/linker_main.h b/linker/linker_main.h
index ffbcf0f..bec9d35 100644
--- a/linker/linker_main.h
+++ b/linker/linker_main.h
@@ -48,6 +48,7 @@
 
 class ElfReader;
 
+void init_sanitizer_mode(const char *interp);
 std::vector<android_namespace_t*> init_default_namespaces(const char* executable_path);
 soinfo* soinfo_alloc(android_namespace_t* ns, const char* name,
                      const struct stat* file_stat, off64_t file_offset,
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/heap_tagging_level_test.cpp b/tests/heap_tagging_level_test.cpp
index c0aa176..4defc5d 100644
--- a/tests/heap_tagging_level_test.cpp
+++ b/tests/heap_tagging_level_test.cpp
@@ -105,7 +105,7 @@
 
 TEST(heap_tagging_level, sync_async_bad_accesses_die) {
 #if defined(__BIONIC__) && defined(__aarch64__)
-  if (!mte_supported() || !running_with_mte()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "requires MTE to be enabled";
   }
 
@@ -177,7 +177,7 @@
     EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
     EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
     EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
-  } else if (mte_supported() && running_with_mte()) {
+  } else if (mte_supported() && mte_enabled()) {
     // ASYNC -> ...
     EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
     EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
@@ -211,7 +211,7 @@
 #if defined(__BIONIC__) && defined(__aarch64__)
   // We can't test SYNC -> NONE in tagging_level_transitions because we can only make one transition
   // to NONE (which we use to test ASYNC -> NONE), so we test it here separately.
-  if (!mte_supported() || !running_with_mte()) {
+  if (!mte_supported() || !mte_enabled()) {
     GTEST_SKIP() << "requires MTE to be enabled";
   }
 
diff --git a/tests/memtag_stack_dlopen_test.cpp b/tests/memtag_stack_dlopen_test.cpp
index 68ddb81..eb69a62 100644
--- a/tests/memtag_stack_dlopen_test.cpp
+++ b/tests/memtag_stack_dlopen_test.cpp
@@ -38,9 +38,13 @@
 #include "mte_utils.h"
 #include "utils.h"
 
+#if defined(__BIONIC__)
+#include <bionic/mte.h>
+#endif
+
 TEST(MemtagStackDlopenTest, DependentBinaryGetsMemtagStack) {
 #if defined(__BIONIC__) && defined(__aarch64__)
-  if (!running_with_mte()) GTEST_SKIP() << "Test requires MTE.";
+  if (!mte_enabled()) GTEST_SKIP() << "Test requires MTE.";
   if (is_stack_mte_on())
     GTEST_SKIP() << "Stack MTE needs to be off for this test. Are you running fullmte?";
 
@@ -58,7 +62,7 @@
 
 TEST(MemtagStackDlopenTest, DependentBinaryGetsMemtagStack2) {
 #if defined(__BIONIC__) && defined(__aarch64__)
-  if (!running_with_mte()) GTEST_SKIP() << "Test requires MTE.";
+  if (!mte_enabled()) GTEST_SKIP() << "Test requires MTE.";
   if (is_stack_mte_on())
     GTEST_SKIP() << "Stack MTE needs to be off for this test. Are you running fullmte?";
 
@@ -77,7 +81,7 @@
 TEST(MemtagStackDlopenTest, DlopenRemapsStack) {
 #if defined(__BIONIC__) && defined(__aarch64__)
   // If this test is failing, look at crash logcat for why the test binary died.
-  if (!running_with_mte()) GTEST_SKIP() << "Test requires MTE.";
+  if (!mte_enabled()) GTEST_SKIP() << "Test requires MTE.";
   if (is_stack_mte_on())
     GTEST_SKIP() << "Stack MTE needs to be off for this test. Are you running fullmte?";
 
@@ -98,7 +102,7 @@
 TEST(MemtagStackDlopenTest, DlopenRemapsStack2) {
 #if defined(__BIONIC__) && defined(__aarch64__)
   // If this test is failing, look at crash logcat for why the test binary died.
-  if (!running_with_mte()) GTEST_SKIP() << "Test requires MTE.";
+  if (!mte_enabled()) GTEST_SKIP() << "Test requires MTE.";
   if (is_stack_mte_on())
     GTEST_SKIP() << "Stack MTE needs to be off for this test. Are you running fullmte?";
 
diff --git a/tests/memtag_stack_test.cpp b/tests/memtag_stack_test.cpp
index 9d02830..98e340a 100644
--- a/tests/memtag_stack_test.cpp
+++ b/tests/memtag_stack_test.cpp
@@ -19,6 +19,7 @@
 #include <gtest/gtest.h>
 
 #if defined(__BIONIC__)
+#include <android-base/test_utils.h>
 #include "gtest_globals.h"
 #include "platform/bionic/mte.h"
 #include "utils.h"
@@ -32,6 +33,9 @@
     GTEST_SKIP() << "MTE unsupported";
   }
   bool is_static = std::get<1>(GetParam());
+  if (running_with_hwasan() && !is_static) {
+    GTEST_SKIP() << "Can't run with HWASanified libc.so";
+  }
   std::string helper =
       GetTestLibRoot() + (is_static ? "/stack_tagging_static_helper" : "/stack_tagging_helper");
   const char* arg = std::get<0>(GetParam());
diff --git a/tests/mte_test.cpp b/tests/mte_test.cpp
index 5eb804f..67db820 100644
--- a/tests/mte_test.cpp
+++ b/tests/mte_test.cpp
@@ -39,7 +39,7 @@
 #endif
   }
 #if defined(__aarch64__)
-  if (mte_supported() && running_with_mte()) {
+  if (mte_supported() && mte_enabled()) {
     EXPECT_DEATH(
         {
           volatile int load ATTRIBUTE_UNUSED = *mistagged_p;
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/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) {