Merge "Fix the (unused) return type for readlinkat() in SYSCALLS.TXT." into main
diff --git a/docs/status.md b/docs/status.md
index 1838b85..fc68b80 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -62,6 +62,7 @@
     functions for avoiding $TZ if you need to use multiple timezones in
     multi-threaded C).
   * `mbsrtowcs_l` and `wcsrtombs_l` aliases for `mbsrtowcs` and `wcsrtombs`.
+  * New system call wrapper: `__riscv_flush_icache` (`<sys/cachectl.h>`).
 
 New libc functions in U (API level 34):
   * `close_range` and `copy_file_range` (Linux-specific GNU extensions).
diff --git a/libc/SECCOMP_ALLOWLIST_COMMON.TXT b/libc/SECCOMP_ALLOWLIST_COMMON.TXT
index efbf28b..4a8d501 100644
--- a/libc/SECCOMP_ALLOWLIST_COMMON.TXT
+++ b/libc/SECCOMP_ALLOWLIST_COMMON.TXT
@@ -53,6 +53,7 @@
 int 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
 # 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
@@ -73,5 +74,5 @@
 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
-# Since Linux 5.4, not in glibc. Probed for and conditionally used by ART.
-int userfaultfd(int) all
+# Since Linux 6.4, not in glibc (https://www.openwall.com/lists/libc-coord/2023/07/13/1).
+int riscv_hwprobe(riscv_hwprobe*, size_t, size_t, unsigned long*, unsigned) riscv64
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index 8266313..e8dde7c 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -356,7 +356,7 @@
 int     cacheflush:__ARM_NR_cacheflush(long start, long end, long flags)  arm
 
 # riscv64-specific
-int _flush_icache:riscv_flush_icache(void*, void*, unsigned long) riscv64
+int __riscv_flush_icache:riscv_flush_icache(void*, void*, unsigned long) riscv64
 
 # x86-specific
 int     __set_thread_area:set_thread_area(void*) x86
diff --git a/libc/bionic/__libc_init_main_thread.cpp b/libc/bionic/__libc_init_main_thread.cpp
index 95f46e9..1b539f2 100644
--- a/libc/bionic/__libc_init_main_thread.cpp
+++ b/libc/bionic/__libc_init_main_thread.cpp
@@ -150,7 +150,7 @@
   // stack.)
   ThreadMapping mapping = __allocate_thread_mapping(0, PTHREAD_GUARD_SIZE);
   if (mapping.mmap_base == nullptr) {
-    async_safe_fatal("failed to mmap main thread static TLS: %s", strerror(errno));
+    async_safe_fatal("failed to mmap main thread static TLS: %m");
   }
 
   const StaticTlsLayout& layout = __libc_shared_globals()->static_tls_layout;
diff --git a/libc/bionic/android_profiling_dynamic.cpp b/libc/bionic/android_profiling_dynamic.cpp
index 8c9127e..849d04b 100644
--- a/libc/bionic/android_profiling_dynamic.cpp
+++ b/libc/bionic/android_profiling_dynamic.cpp
@@ -126,15 +126,14 @@
 static void HandleTracedPerfSignal() {
   ScopedFd sock_fd{ socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0 /*protocol*/) };
   if (sock_fd.get() == -1) {
-    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create socket: %s", strerror(errno));
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create socket: %m");
     return;
   }
 
   sockaddr_un saddr{ AF_UNIX, "/dev/socket/traced_perf" };
   size_t addrlen = sizeof(sockaddr_un);
   if (connect(sock_fd.get(), reinterpret_cast<const struct sockaddr*>(&saddr), addrlen) == -1) {
-    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to connect to traced_perf socket: %s",
-                          strerror(errno));
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to connect to traced_perf socket: %m");
     return;
   }
 
@@ -153,13 +152,11 @@
   }
 
   if (maps_fd.get() == -1) {
-    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open /proc/self/maps: %s",
-                          strerror(errno));
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open /proc/self/maps: %m");
     return;
   }
   if (mem_fd.get() == -1) {
-    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open /proc/self/mem: %s",
-                          strerror(errno));
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open /proc/self/mem: %m");
     return;
   }
 
@@ -183,7 +180,7 @@
   memcpy(CMSG_DATA(cmsg), send_fds, num_fds * sizeof(int));
 
   if (sendmsg(sock_fd.get(), &msg_hdr, 0) == -1) {
-    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to sendmsg: %s", strerror(errno));
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to sendmsg: %m");
   }
 }
 
diff --git a/libc/bionic/atexit.cpp b/libc/bionic/atexit.cpp
index dae15e0..29f9c7b 100644
--- a/libc/bionic/atexit.cpp
+++ b/libc/bionic/atexit.cpp
@@ -158,7 +158,7 @@
 
   const int prot = PROT_READ | (writable ? PROT_WRITE : 0);
   if (mprotect(reinterpret_cast<char*>(array_) + start_byte, byte_len, prot) != 0) {
-    async_safe_fatal("mprotect failed on atexit array: %s", strerror(errno));
+    async_safe_fatal("mprotect failed on atexit array: %m");
   }
 }
 
@@ -198,8 +198,8 @@
   }
   if (new_pages == MAP_FAILED) {
     async_safe_format_log(ANDROID_LOG_WARN, "libc",
-                          "__cxa_atexit: mmap/mremap failed to allocate %zu bytes: %s",
-                          new_capacity_bytes, strerror(errno));
+                          "__cxa_atexit: mmap/mremap failed to allocate %zu bytes: %m",
+                          new_capacity_bytes);
   } else {
     result = true;
     prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, new_pages, new_capacity_bytes, "atexit handlers");
diff --git a/libc/bionic/bionic_allocator.cpp b/libc/bionic/bionic_allocator.cpp
index 53f944f..80e8b08 100644
--- a/libc/bionic/bionic_allocator.cpp
+++ b/libc/bionic/bionic_allocator.cpp
@@ -192,7 +192,7 @@
   void* const map_ptr =
       mmap(nullptr, page_size(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
   if (map_ptr == MAP_FAILED) {
-    async_safe_fatal("mmap failed: %s", strerror(errno));
+    async_safe_fatal("mmap failed: %m");
   }
 
   prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_ptr, page_size(), "bionic_alloc_small_objects");
@@ -273,7 +273,7 @@
                        -1, 0);
 
   if (map_ptr == MAP_FAILED) {
-    async_safe_fatal("mmap failed: %s", strerror(errno));
+    async_safe_fatal("mmap failed: %m");
   }
 
   prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_ptr, allocated_size, "bionic_alloc_lob");
diff --git a/libc/bionic/fdsan.cpp b/libc/bionic/fdsan.cpp
index 3eb8c19..84d2c94 100644
--- a/libc/bionic/fdsan.cpp
+++ b/libc/bionic/fdsan.cpp
@@ -87,7 +87,7 @@
     void* allocation =
         mmap(nullptr, aligned_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     if (allocation == MAP_FAILED) {
-      async_safe_fatal("fdsan: mmap failed: %s", strerror(errno));
+      async_safe_fatal("fdsan: mmap failed: %m");
     }
 
     FdTableOverflow* new_overflow = reinterpret_cast<FdTableOverflow*>(allocation);
diff --git a/libc/bionic/ifaddrs.cpp b/libc/bionic/ifaddrs.cpp
index 0c80f4e..cb36cc6 100644
--- a/libc/bionic/ifaddrs.cpp
+++ b/libc/bionic/ifaddrs.cpp
@@ -281,8 +281,7 @@
   ScopedFd s(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
   if (s.get() == -1) {
     async_safe_format_log(ANDROID_LOG_ERROR, "libc",
-                          "socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC) failed in ifaddrs: %s",
-                          strerror(errno));
+                          "socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC) failed in ifaddrs: %m");
     return;
   }
 
@@ -294,8 +293,8 @@
       addr->ifa.ifa_flags = ifr.ifr_flags;
     } else {
       async_safe_format_log(ANDROID_LOG_ERROR, "libc",
-                            "ioctl(SIOCGIFFLAGS) for \"%s\" failed in ifaddrs: %s",
-                            addr->ifa.ifa_name, strerror(errno));
+                            "ioctl(SIOCGIFFLAGS) for \"%s\" failed in ifaddrs: %m",
+                            addr->ifa.ifa_name);
     }
   }
 }
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index d922641..7ef79b6 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -340,11 +340,11 @@
 #if !defined(__LP64__)
   int old_value = personality(0xffffffff);
   if (old_value == -1) {
-    async_safe_fatal("error getting old personality value: %s", strerror(errno));
+    async_safe_fatal("error getting old personality value: %m");
   }
 
   if (personality((static_cast<unsigned int>(old_value) & ~PER_MASK) | PER_LINUX32) == -1) {
-    async_safe_fatal("error setting PER_LINUX32 personality: %s", strerror(errno));
+    async_safe_fatal("error setting PER_LINUX32 personality: %m");
   }
 #endif
 }
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index 3b0ab82..d7a2856 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -357,8 +357,7 @@
         void* pg_start =
             reinterpret_cast<void*>(page_start(reinterpret_cast<uintptr_t>(stack_top)));
         if (mprotect(pg_start, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_MTE | PROT_GROWSDOWN)) {
-          async_safe_fatal("error: failed to set PROT_MTE on main thread stack: %s\n",
-                           strerror(errno));
+          async_safe_fatal("error: failed to set PROT_MTE on main thread stack: %m");
         }
       }
 
diff --git a/libc/bionic/pthread_attr.cpp b/libc/bionic/pthread_attr.cpp
index cdae72c..de4cc9e 100644
--- a/libc/bionic/pthread_attr.cpp
+++ b/libc/bionic/pthread_attr.cpp
@@ -158,12 +158,12 @@
 static uintptr_t __get_main_stack_startstack() {
   FILE* fp = fopen("/proc/self/stat", "re");
   if (fp == nullptr) {
-    async_safe_fatal("couldn't open /proc/self/stat: %s", strerror(errno));
+    async_safe_fatal("couldn't open /proc/self/stat: %m");
   }
 
   char line[BUFSIZ];
   if (fgets(line, sizeof(line), fp) == nullptr) {
-    async_safe_fatal("couldn't read /proc/self/stat: %s", strerror(errno));
+    async_safe_fatal("couldn't read /proc/self/stat: %m");
   }
 
   fclose(fp);
@@ -205,7 +205,7 @@
   // Hunt for the region that contains that address.
   FILE* fp = fopen("/proc/self/maps", "re");
   if (fp == nullptr) {
-    async_safe_fatal("couldn't open /proc/self/maps: %s", strerror(errno));
+    async_safe_fatal("couldn't open /proc/self/maps: %m");
   }
   char line[BUFSIZ];
   while (fgets(line, sizeof(line), fp) != nullptr) {
@@ -219,7 +219,7 @@
       }
     }
   }
-  async_safe_fatal("Stack not found in /proc/self/maps");
+  async_safe_fatal("stack not found in /proc/self/maps");
 }
 
 __BIONIC_WEAK_FOR_NATIVE_BRIDGE
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index 75226d3..194db18 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -78,8 +78,7 @@
                           MAP_PRIVATE | MAP_ANONYMOUS,
                           -1, 0);
   if (allocation == MAP_FAILED) {
-    // Avoid strerror because it might need bionic_tls.
-    async_safe_fatal("failed to allocate bionic_tls: error %d", errno);
+    async_safe_fatal("failed to allocate bionic_tls: %m");
   }
   return static_cast<bionic_tls*>(allocation);
 }
@@ -138,8 +137,7 @@
   // stack pointer. This is deliberately the only place where the address is stored.
   char* scs = scs_aligned_guard_region + scs_offset;
   if (mprotect(scs, SCS_SIZE, PROT_READ | PROT_WRITE) == -1) {
-    async_safe_fatal("failed to mprotect() on shadow stack %p %d error %s", scs, SCS_SIZE,
-                     strerror(errno));
+    async_safe_fatal("shadow stack read-write mprotect(%p, %d) failed: %m", scs, SCS_SIZE);
   }
 #if defined(__aarch64__)
   __asm__ __volatile__("mov x18, %0" ::"r"(scs));
@@ -174,12 +172,11 @@
     if (need_set) {
       if (policy == -1) {
         async_safe_format_log(ANDROID_LOG_WARN, "libc",
-                              "pthread_create sched_getscheduler failed: %s", strerror(errno));
+                              "pthread_create sched_getscheduler failed: %m");
         return errno;
       }
       if (sched_getparam(0, &param) == -1) {
-        async_safe_format_log(ANDROID_LOG_WARN, "libc",
-                              "pthread_create sched_getparam failed: %s", strerror(errno));
+        async_safe_format_log(ANDROID_LOG_WARN, "libc", "pthread_create sched_getparam failed: %m");
         return errno;
       }
     }
@@ -195,8 +192,8 @@
   if (need_set) {
     if (sched_setscheduler(thread->tid, policy, &param) == -1) {
       async_safe_format_log(ANDROID_LOG_WARN, "libc",
-                            "pthread_create sched_setscheduler(%d, {%d}) call failed: %s", policy,
-                            param.sched_priority, strerror(errno));
+                            "pthread_create sched_setscheduler(%d, {%d}) call failed: %m", policy,
+                            param.sched_priority);
 #if defined(__LP64__)
       // For backwards compatibility reasons, we only report failures on 64-bit devices.
       return errno;
@@ -231,10 +228,9 @@
   const int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
   char* const space = static_cast<char*>(mmap(nullptr, mmap_size, PROT_NONE, flags, -1, 0));
   if (space == MAP_FAILED) {
-    async_safe_format_log(ANDROID_LOG_WARN,
-                          "libc",
-                          "pthread_create failed: couldn't allocate %zu-bytes mapped space: %s",
-                          mmap_size, strerror(errno));
+    async_safe_format_log(ANDROID_LOG_WARN, "libc",
+                          "pthread_create failed: couldn't allocate %zu-bytes mapped space: %m",
+                          mmap_size);
     return {};
   }
   const size_t writable_size = mmap_size - stack_guard_size - PTHREAD_GUARD_SIZE;
@@ -249,8 +245,8 @@
   if (mprotect(space + stack_guard_size, writable_size, prot) != 0) {
     async_safe_format_log(
         ANDROID_LOG_WARN, "libc",
-        "pthread_create failed: couldn't mprotect %s %zu-byte thread mapping region: %s", prot_str,
-        writable_size, strerror(errno));
+        "pthread_create failed: couldn't mprotect %s %zu-byte thread mapping region: %m", prot_str,
+        writable_size);
     munmap(space, mmap_size);
     return {};
   }
@@ -461,8 +457,7 @@
     if (thread->mmap_size != 0) {
       munmap(thread->mmap_base, thread->mmap_size);
     }
-    async_safe_format_log(ANDROID_LOG_WARN, "libc", "pthread_create failed: clone failed: %s",
-                          strerror(clone_errno));
+    async_safe_format_log(ANDROID_LOG_WARN, "libc", "pthread_create failed: clone failed: %m");
     return clone_errno;
   }
 
diff --git a/libc/bionic/system_property_set.cpp b/libc/bionic/system_property_set.cpp
index 212aafc..f7999db 100644
--- a/libc/bionic/system_property_set.cpp
+++ b/libc/bionic/system_property_set.cpp
@@ -272,10 +272,9 @@
     PropertyServiceConnection connection;
     if (!connection.IsValid()) {
       errno = connection.GetLastError();
-      async_safe_format_log(
-          ANDROID_LOG_WARN, "libc",
-          "Unable to set property \"%s\" to \"%s\": connection failed; errno=%d (%s)", key, value,
-          errno, strerror(errno));
+      async_safe_format_log(ANDROID_LOG_WARN, "libc",
+                            "Unable to set property \"%s\" to \"%s\": connection failed: %m", key,
+                            value);
       return -1;
     }
 
@@ -283,8 +282,8 @@
     if (!writer.WriteUint32(PROP_MSG_SETPROP2).WriteString(key).WriteString(value).Send()) {
       errno = connection.GetLastError();
       async_safe_format_log(ANDROID_LOG_WARN, "libc",
-                            "Unable to set property \"%s\" to \"%s\": write failed; errno=%d (%s)",
-                            key, value, errno, strerror(errno));
+                            "Unable to set property \"%s\" to \"%s\": write failed: %m", key,
+                            value);
       return -1;
     }
 
@@ -292,8 +291,7 @@
     if (!connection.RecvInt32(&result)) {
       errno = connection.GetLastError();
       async_safe_format_log(ANDROID_LOG_WARN, "libc",
-                            "Unable to set property \"%s\" to \"%s\": recv failed; errno=%d (%s)",
-                            key, value, errno, strerror(errno));
+                            "Unable to set property \"%s\" to \"%s\": recv failed: %m", key, value);
       return -1;
     }
 
diff --git a/libc/include/bits/getentropy.h b/libc/include/bits/getentropy.h
new file mode 100644
index 0000000..a5a14f7
--- /dev/null
+++ b/libc/include/bits/getentropy.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#pragma once
+
+/**
+ * @file bits/getentropy.h
+ * @brief The getentropy() function.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+/**
+ * [getentropy(3)](http://man7.org/linux/man-pages/man3/getentropy.3.html) fills the given buffer
+ * with random bytes.
+ *
+ * Returns 0 on success, and returns -1 and sets `errno` on failure.
+ *
+ * Available since API level 28.
+ *
+ * See also arc4random_buf() which is available in all API levels.
+ */
+int getentropy(void* _Nonnull __buffer, size_t __buffer_size) __wur __INTRODUCED_IN(28);
+
+__END_DECLS
diff --git a/libc/include/sys/cachectl.h b/libc/include/sys/cachectl.h
index 5ec295d..b5fabe3 100644
--- a/libc/include/sys/cachectl.h
+++ b/libc/include/sys/cachectl.h
@@ -28,6 +28,32 @@
 
 #pragma once
 
+/*
+ * @file sys/cachectl.h
+ * @brief Architecture-specific cache control.
+ */
+
 #include <sys/cdefs.h>
 
-/* This header file is obsolete. */
+__BEGIN_DECLS
+
+#if defined(__riscv)
+
+/**
+ * Flag for __riscv_flush_icache() to indicate that only the current
+ * thread's instruction cache needs to be flushed (rather than the
+ * default of all threads).
+ */
+#define SYS_RISCV_FLUSH_ICACHE_LOCAL 1UL
+
+/**
+ * __riscv_flush_icache(2) flushes the instruction cache for the given range of addresses.
+ * The address range is currently (Linux 6.4) ignored, so both pointers may be null.
+ *
+ * Returns 0 on success, and returns -1 and sets `errno` on failure.
+ */
+int __riscv_flush_icache(void* _Nullable __start, void* _Nullable __end, unsigned long __flags);
+
+#endif
+
+__END_DECLS
diff --git a/libc/include/sys/random.h b/libc/include/sys/random.h
index 0251176..2ff5349 100644
--- a/libc/include/sys/random.h
+++ b/libc/include/sys/random.h
@@ -38,19 +38,9 @@
 
 #include <linux/random.h>
 
-__BEGIN_DECLS
+#include <bits/getentropy.h>
 
-/**
- * [getentropy(3)](http://man7.org/linux/man-pages/man3/getentropy.3.html) fills the given buffer
- * with random bytes.
- *
- * Returns 0 on success, and returns -1 and sets `errno` on failure.
- *
- * Available since API level 28.
- *
- * See also arc4random_buf() which is available in all API levels.
- */
-int getentropy(void* _Nonnull __buffer, size_t __buffer_size) __wur __INTRODUCED_IN(28);
+__BEGIN_DECLS
 
 /**
  * [getrandom(2)](http://man7.org/linux/man-pages/man2/getrandom.2.html) fills the given buffer
diff --git a/libc/include/unistd.h b/libc/include/unistd.h
index 9385264..dcad630 100644
--- a/libc/include/unistd.h
+++ b/libc/include/unistd.h
@@ -34,6 +34,7 @@
 #include <sys/select.h>
 
 #include <bits/fcntl.h>
+#include <bits/getentropy.h>
 #include <bits/getopt.h>
 #include <bits/ioctl.h>
 #include <bits/lockf.h>
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index c69e609..41d18a6 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1589,6 +1589,7 @@
     localtime_rz;
     mbsrtowcs_l;
     mktime_z;
+    __riscv_flush_icache; # riscv64
     timespec_getres;
     tzalloc;
     tzfree;
diff --git a/libc/tools/gensyscalls.py b/libc/tools/gensyscalls.py
index 4f33777..8c457c8 100755
--- a/libc/tools/gensyscalls.py
+++ b/libc/tools/gensyscalls.py
@@ -227,11 +227,6 @@
     aliases = syscall["aliases"]
     for alias in aliases:
         stub += "\nALIAS_SYMBOL(%s, %s)\n" % (alias, syscall["func"])
-
-    # Use hidden visibility on LP64 for any functions beginning with underscores.
-    if pointer_length == 64 and syscall["func"].startswith("__"):
-        stub += '.hidden ' + syscall["func"] + '\n'
-
     return stub
 
 
diff --git a/tests/Android.bp b/tests/Android.bp
index 36a3a37..8dc8eaf 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -470,6 +470,7 @@
         "struct_layout_test.cpp",
         "sstream_test.cpp",
         "sys_auxv_test.cpp",
+        "sys_cachectl_test.cpp",
         "sys_epoll_test.cpp",
         "sys_mman_test.cpp",
         "sys_msg_test.cpp",
diff --git a/tests/sys_cachectl_test.cpp b/tests/sys_cachectl_test.cpp
new file mode 100644
index 0000000..d9ece4f
--- /dev/null
+++ b/tests/sys_cachectl_test.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <gtest/gtest.h>
+
+#if __has_include(<sys/cachectl.h>)
+#include <sys/cachectl.h>
+#endif
+
+TEST(sys_cachectl, __riscv_flush_icache) {
+#if defined(__riscv) && __has_include(<sys/cachectl.h>)
+  // As of Linux 6.4, the address range is ignored (so nullptr is fine),
+  // and the flags you actually want are 0 ("all threads"),
+  // but we test the unusual flag just to make sure it works.
+  ASSERT_EQ(0, __riscv_flush_icache(nullptr, nullptr, SYS_RISCV_FLUSH_ICACHE_LOCAL));
+#else
+  GTEST_SKIP() << "__riscv_flush_icache requires riscv64";
+#endif
+}
diff --git a/tests/uchar_test.cpp b/tests/uchar_test.cpp
index 4dc6314..1936a8d 100644
--- a/tests/uchar_test.cpp
+++ b/tests/uchar_test.cpp
@@ -24,6 +24,22 @@
 #include <locale.h>
 #include <stdint.h>
 
+// Modern versions of UTF-8 (https://datatracker.ietf.org/doc/html/rfc3629 and
+// newer) explicitly disallow code points beyond U+10FFFF, which exclude all 5-
+// and 6-byte sequences. Earlier versions of UTF-8 allowed the wider range:
+// https://datatracker.ietf.org/doc/html/rfc2279.
+//
+// Bionic's unicode implementation was written after the high values were
+// excluded, so it has never supported them. Other implementations (at least
+// as of glibc 2.36), do support those sequences.
+#if defined(__ANDROID__) || defined(ANDROID_HOST_MUSL)
+constexpr bool kLibcSupportsLongUtf8Sequences = 0;
+#elif defined(__GLIBC__)
+constexpr bool kLibcSupportsLongUtf8Sequences = 1;
+#else
+#error kLibcSupportsLongUtf8Sequences must be configured for this platform
+#endif
+
 TEST(uchar, sizeof_uchar_t) {
   EXPECT_EQ(2U, sizeof(char16_t));
   EXPECT_EQ(4U, sizeof(char32_t));
@@ -146,10 +162,24 @@
   ASSERT_EQ(static_cast<char16_t>(0xdbea), out);
   ASSERT_EQ(4U, mbrtoc16(&out, "\xf4\x8a\xaf\x8d" "ef", 6, nullptr));
   ASSERT_EQ(static_cast<char16_t>(0xdfcd), out);
-  // Illegal 5-byte UTF-8.
+}
+
+TEST(uchar, mbrtoc16_long_sequences) {
+  ASSERT_STREQ("C.UTF-8", setlocale(LC_CTYPE, "C.UTF-8"));
+  uselocale(LC_GLOBAL_LOCALE);
+
+  char16_t out = u'\0';
   errno = 0;
-  ASSERT_EQ(static_cast<size_t>(-1), mbrtoc16(&out, "\xf8\xa1\xa2\xa3\xa4", 5, nullptr));
-  ASSERT_EQ(EILSEQ, errno);
+  auto result = mbrtoc16(&out, "\xf8\xa1\xa2\xa3\xa4", 5, nullptr);
+  if (kLibcSupportsLongUtf8Sequences) {
+    EXPECT_EQ(5U, result);
+    EXPECT_EQ(0, errno);
+    EXPECT_EQ(u'\uf94a', out);
+  } else {
+    EXPECT_EQ(static_cast<size_t>(-1), result);
+    EXPECT_EQ(EILSEQ, errno);
+    EXPECT_EQ(u'\0', out);
+  }
 }
 
 TEST(uchar, mbrtoc16_reserved_range) {