Merge "bionic: max_android_page_size to 16384" into main
diff --git a/benchmarks/linker_relocation/include/linker_reloc_bench_asm.h b/benchmarks/linker_relocation/include/linker_reloc_bench_asm.h
index 885e47f..ea63e36 100644
--- a/benchmarks/linker_relocation/include/linker_reloc_bench_asm.h
+++ b/benchmarks/linker_relocation/include/linker_reloc_bench_asm.h
@@ -44,9 +44,7 @@
 
 #elif defined(__riscv)
 
-// No `lga` in clang unless https://reviews.llvm.org/D107278 lands.
-// `la` is equivalent when using PIC (which we do) though.
-#define GOT_RELOC(sym) la a0, sym
+#define GOT_RELOC(sym) lga a0, sym
 #define CALL(sym) call sym@plt
 #define DATA_WORD(val) .quad val
 #define MAIN .globl main; main: li a0, 0; ret
diff --git a/benchmarks/util.h b/benchmarks/util.h
index 99eed5f..347dc35 100644
--- a/benchmarks/util.h
+++ b/benchmarks/util.h
@@ -71,7 +71,7 @@
 
 bool LockToCPU(int cpu_to_lock);
 
-static __inline __attribute__ ((__always_inline__)) void MakeAllocationResident(
+static inline __attribute__((__always_inline__)) void MakeAllocationResident(
     void* ptr, size_t nbytes, int pagesize) {
   uint8_t* data = reinterpret_cast<uint8_t*>(ptr);
   for (size_t i = 0; i < nbytes; i += pagesize) {
diff --git a/libc/bionic/execinfo.cpp b/libc/bionic/execinfo.cpp
index d129f7c..e53a037 100644
--- a/libc/bionic/execinfo.cpp
+++ b/libc/bionic/execinfo.cpp
@@ -73,6 +73,11 @@
 #elif defined(__aarch64__)
     // All instructions are 4 bytes long, skip back one instruction.
     ip -= 4;
+#elif defined(__riscv)
+    // C instructions are the shortest at 2 bytes long. (Unlike thumb, it's
+    // non-trivial to recognize C instructions when going backwards in the
+    // instruction stream.)
+    ip -= 2;
 #elif defined(__i386__) || defined(__x86_64__)
     // It's difficult to decode exactly where the previous instruction is,
     // so subtract 1 to estimate where the instruction lives.
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index d86df30..3da0a92 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -297,6 +297,30 @@
   return level;
 }
 
+static int64_t __get_memtag_upgrade_secs() {
+  char* env = getenv("BIONIC_MEMTAG_UPGRADE_SECS");
+  if (!env) return 0;
+  int64_t timed_upgrade = 0;
+  static const char kAppProcessName[] = "app_process64";
+  const char* progname = __libc_shared_globals()->init_progname;
+  progname = progname ? __gnu_basename(progname) : nullptr;
+  // disable timed upgrade for zygote, as the thread spawned will violate the requirement
+  // that it be single-threaded.
+  if (!progname || strncmp(progname, kAppProcessName, sizeof(kAppProcessName)) != 0) {
+    char* endptr;
+    timed_upgrade = strtoll(env, &endptr, 10);
+    if (*endptr != '\0' || timed_upgrade < 0) {
+      async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                            "Invalid value for BIONIC_MEMTAG_UPGRADE_SECS: %s", env);
+      timed_upgrade = 0;
+    }
+  }
+  // Make sure that this does not get passed to potential processes inheriting
+  // this environment.
+  unsetenv("BIONIC_MEMTAG_UPGRADE_SECS");
+  return timed_upgrade;
+}
+
 // Figure out the desired memory tagging mode (sync/async, heap/globals/stack) for this executable.
 // This function is called from the linker before the main executable is relocated.
 __attribute__((no_sanitize("hwaddress", "memtag"))) void __libc_init_mte(
@@ -313,31 +337,7 @@
     }
     memtag_stack = true;
   }
-  char* env = getenv("BIONIC_MEMTAG_UPGRADE_SECS");
-  static const char kAppProcessName[] = "app_process64";
-  const char* progname = __libc_shared_globals()->init_progname;
-  progname = progname ? __gnu_basename(progname) : nullptr;
-  if (progname &&
-      strncmp(progname, kAppProcessName, sizeof(kAppProcessName)) == 0) {
-    // disable timed upgrade for zygote, as the thread spawned will violate the requirement
-    // that it be single-threaded.
-    env = nullptr;
-  }
-  int64_t timed_upgrade = 0;
-  if (env) {
-    char* endptr;
-    timed_upgrade = strtoll(env, &endptr, 10);
-    if (*endptr != '\0' || timed_upgrade < 0) {
-      async_safe_format_log(ANDROID_LOG_ERROR, "libc",
-                            "Invalid value for BIONIC_MEMTAG_UPGRADE_SECS: %s",
-                            env);
-      timed_upgrade = 0;
-    }
-    // Make sure that this does not get passed to potential processes inheriting
-    // this environment.
-    unsetenv("BIONIC_MEMTAG_UPGRADE_SECS");
-  }
-  if (timed_upgrade) {
+  if (int64_t timed_upgrade = __get_memtag_upgrade_secs()) {
     if (level == M_HEAP_TAGGING_LEVEL_ASYNC) {
       async_safe_format_log(ANDROID_LOG_INFO, "libc",
                             "Attempting timed MTE upgrade from async to sync.");
diff --git a/libc/bionic/posix_timers.cpp b/libc/bionic/posix_timers.cpp
index f522516..ccbbfcf 100644
--- a/libc/bionic/posix_timers.cpp
+++ b/libc/bionic/posix_timers.cpp
@@ -34,6 +34,8 @@
 #include <string.h>
 #include <time.h>
 
+#include "private/bionic_lock.h"
+
 // System calls.
 extern "C" int __rt_sigprocmask(int, const sigset64_t*, sigset64_t*, size_t);
 extern "C" int __rt_sigtimedwait(const sigset64_t*, siginfo_t*, const timespec*, size_t);
@@ -60,6 +62,7 @@
   int sigev_notify;
 
   // The fields below are only needed for a SIGEV_THREAD timer.
+  Lock startup_handshake_lock;
   pthread_t callback_thread;
   void (*callback)(sigval_t);
   sigval_t callback_argument;
@@ -73,6 +76,18 @@
 static void* __timer_thread_start(void* arg) {
   PosixTimer* timer = reinterpret_cast<PosixTimer*>(arg);
 
+  // Check that our parent managed to create the kernel timer and bail if not...
+  timer->startup_handshake_lock.lock();
+  if (timer->kernel_timer_id == -1) {
+    free(timer);
+    return nullptr;
+  }
+
+  // Give ourselves a specific meaningful name now we have a kernel timer.
+  char name[16]; // 16 is the kernel-imposed limit.
+  snprintf(name, sizeof(name), "POSIX timer %d", to_kernel_timer_id(timer));
+  pthread_setname_np(timer->callback_thread, name);
+
   sigset64_t sigset = {};
   sigaddset64(&sigset, TIMER_SIGNAL);
 
@@ -109,6 +124,7 @@
     return -1;
   }
 
+  timer->kernel_timer_id = -1;
   timer->sigev_notify = (evp == nullptr) ? SIGEV_SIGNAL : evp->sigev_notify;
 
   // If not a SIGEV_THREAD timer, the kernel can handle it without our help.
@@ -149,6 +165,10 @@
   sigaddset64(&sigset, TIMER_SIGNAL);
   sigset64_t old_sigset;
 
+  // Prevent the child thread from running until the timer has been created.
+  timer->startup_handshake_lock.init(false);
+  timer->startup_handshake_lock.lock();
+
   // Use __rt_sigprocmask instead of sigprocmask64 to avoid filtering out TIMER_SIGNAL.
   __rt_sigprocmask(SIG_BLOCK, &sigset, &old_sigset, sizeof(sigset));
 
@@ -162,21 +182,21 @@
     return -1;
   }
 
+  // Try to create the kernel timer.
   sigevent se = *evp;
   se.sigev_signo = TIMER_SIGNAL;
   se.sigev_notify = SIGEV_THREAD_ID;
   se.sigev_notify_thread_id = pthread_gettid_np(timer->callback_thread);
-  if (__timer_create(clock_id, &se, &timer->kernel_timer_id) == -1) {
-    __timer_thread_stop(timer);
+  rc = __timer_create(clock_id, &se, &timer->kernel_timer_id);
+
+  // Let the child run (whether we created the kernel timer or not).
+  timer->startup_handshake_lock.unlock();
+  // If __timer_create(2) failed, the child will kill itself and free the
+  // timer struct, so we just need to exit.
+  if (rc == -1) {
     return -1;
   }
 
-  // Give the thread a specific meaningful name.
-  // It can't do this itself because the kernel timer isn't created until after it's running.
-  char name[16]; // 16 is the kernel-imposed limit.
-  snprintf(name, sizeof(name), "POSIX timer %d", to_kernel_timer_id(timer));
-  pthread_setname_np(timer->callback_thread, name);
-
   *timer_id = timer;
   return 0;
 }
diff --git a/libc/bionic/sys_thread_properties.cpp b/libc/bionic/sys_thread_properties.cpp
index d1a73b7..d7188f5 100644
--- a/libc/bionic/sys_thread_properties.cpp
+++ b/libc/bionic/sys_thread_properties.cpp
@@ -77,35 +77,9 @@
 
   // Find the thread-pointer register for the given thread.
   void** tp_reg = nullptr;
-#if defined(__x86_64__)
-  {
-    ErrnoRestorer errno_restorer;
-    errno = 0;
-    uintptr_t fs_base = ptrace(PTRACE_PEEKUSER, tid, offsetof(user_regs_struct, fs_base), nullptr);
-    if (errno == 0) {
-      tp_reg = reinterpret_cast<void**>(fs_base);
-    }
-  }
-#elif defined(__i386__)
-  struct user_regs_struct regs;
-  struct iovec pt_iov = {
-      .iov_base = &regs,
-      .iov_len = sizeof(regs),
-  };
-
-  if (ptrace(PTRACE_GETREGSET, tid, NT_PRSTATUS, &pt_iov) == 0) {
-    struct user_desc u_info;
-    u_info.entry_number = regs.xgs >> 3;
-    if (ptrace(PTRACE_GET_THREAD_AREA, tid, u_info.entry_number, &u_info) == 0) {
-      tp_reg = reinterpret_cast<void**>(u_info.base_addr);
-    }
-  }
-#elif defined(__aarch64__)
+#if defined(__aarch64__)
   uint64_t reg;
-  struct iovec pt_iov {
-    .iov_base = &reg, .iov_len = sizeof(reg),
-  };
-
+  struct iovec pt_iov { .iov_base = &reg, .iov_len = sizeof(reg) };
   if (ptrace(PTRACE_GETREGSET, tid, NT_ARM_TLS, &pt_iov) == 0) {
     tp_reg = reinterpret_cast<void**>(reg);
   }
@@ -114,6 +88,31 @@
     // Reset the tp_reg if ptrace was unsuccessful.
     tp_reg = nullptr;
   }
+#elif defined(__i386__)
+  struct user_regs_struct regs;
+  struct iovec pt_iov = { .iov_base = &regs, .iov_len = sizeof(regs) };
+  if (ptrace(PTRACE_GETREGSET, tid, NT_PRSTATUS, &pt_iov) == 0) {
+    struct user_desc u_info;
+    u_info.entry_number = regs.xgs >> 3;
+    if (ptrace(PTRACE_GET_THREAD_AREA, tid, u_info.entry_number, &u_info) == 0) {
+      tp_reg = reinterpret_cast<void**>(u_info.base_addr);
+    }
+  }
+#elif defined(__riscv)
+  struct user_regs_struct regs;
+  struct iovec pt_iov = { .iov_base = &regs, .iov_len = sizeof(regs) };
+  if (ptrace(PTRACE_GETREGSET, tid, NT_PRSTATUS, &pt_iov) == 0) {
+    tp_reg = reinterpret_cast<void**>(regs.tp);
+  }
+#elif defined(__x86_64__)
+  {
+    ErrnoRestorer errno_restorer;
+    errno = 0;
+    uintptr_t fs_base = ptrace(PTRACE_PEEKUSER, tid, offsetof(user_regs_struct, fs_base), nullptr);
+    if (errno == 0) {
+      tp_reg = reinterpret_cast<void**>(fs_base);
+    }
+  }
 #endif
 
   if (tp_reg == nullptr) {
diff --git a/libc/dns/resolv/res_cache.c b/libc/dns/resolv/res_cache.c
index d6416e5..38de84b 100644
--- a/libc/dns/resolv/res_cache.c
+++ b/libc/dns/resolv/res_cache.c
@@ -1166,23 +1166,19 @@
     }
 }
 
-static inline void
-entry_mru_remove( Entry*  e )
-{
-    e->mru_prev->mru_next = e->mru_next;
-    e->mru_next->mru_prev = e->mru_prev;
+static __inline__ void entry_mru_remove(Entry* e) {
+  e->mru_prev->mru_next = e->mru_next;
+  e->mru_next->mru_prev = e->mru_prev;
 }
 
-static inline void
-entry_mru_add( Entry*  e, Entry*  list )
-{
-    Entry*  first = list->mru_next;
+static __inline__ void entry_mru_add(Entry* e, Entry* list) {
+  Entry* first = list->mru_next;
 
-    e->mru_next = first;
-    e->mru_prev = list;
+  e->mru_next = first;
+  e->mru_prev = list;
 
-    list->mru_next  = e;
-    first->mru_prev = e;
+  list->mru_next = e;
+  first->mru_prev = e;
 }
 
 /* compute the hash of a given entry, this is a hash of most
diff --git a/libc/include/android/api-level.h b/libc/include/android/api-level.h
index 113897c..1bde3a5 100644
--- a/libc/include/android/api-level.h
+++ b/libc/include/android/api-level.h
@@ -168,7 +168,10 @@
  */
 #define __ANDROID_API_U__ 34
 
-/** Names the "V" API level (35), for comparison against `__ANDROID_API__`. */
+/**
+ * Names the Android 15 (aka "V" or "VanillaIceCream") API level (35),
+ * for comparison against `__ANDROID_API__`.
+ */
 #define __ANDROID_API_V__ 35
 
 /* This file is included in <features.h>, and might be used from .S files. */
@@ -191,7 +194,7 @@
 #if __ANDROID_API__ < 29
 
 /* android_get_device_api_level is a static inline before API level 29. */
-#define __BIONIC_GET_DEVICE_API_LEVEL_INLINE static inline
+#define __BIONIC_GET_DEVICE_API_LEVEL_INLINE static __inline
 #include <bits/get_device_api_level_inlines.h>
 #undef __BIONIC_GET_DEVICE_API_LEVEL_INLINE
 
diff --git a/libc/include/android/dlext.h b/libc/include/android/dlext.h
index a5061c7..b42e5b2 100644
--- a/libc/include/android/dlext.h
+++ b/libc/include/android/dlext.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef __ANDROID_DLEXT_H__
-#define __ANDROID_DLEXT_H__
+#pragma once
 
 #include <stdbool.h>
 #include <stddef.h>
@@ -101,7 +100,7 @@
   ANDROID_DLEXT_FORCE_LOAD = 0x40,
 
   // Historically we had two other options for ART.
-  // They were last available in Android P.
+  // They were last available in API level 28.
   // Reuse these bits last!
   // ANDROID_DLEXT_FORCE_FIXED_VADDR = 0x80
   // ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS = 0x100
@@ -115,7 +114,7 @@
   ANDROID_DLEXT_USE_NAMESPACE = 0x200,
 
   /**
-   * Instructs dlopen to apply `ANDROID_DLEXT_RESERVED_ADDRESS`,
+   * Instructs dlopen() to apply `ANDROID_DLEXT_RESERVED_ADDRESS`,
    * `ANDROID_DLEXT_RESERVED_ADDRESS_HINT`, `ANDROID_DLEXT_WRITE_RELRO` and
    * `ANDROID_DLEXT_USE_RELRO` to any libraries loaded as dependencies of the
    * main library as well.
@@ -151,7 +150,7 @@
 
 struct android_namespace_t;
 
-/** Used to pass Android-specific arguments to `android_dlopen_ext`. */
+/** Used to pass Android-specific arguments to android_dlopen_ext(). */
 typedef struct {
   /** A bitmask of `ANDROID_DLEXT_` enum values. */
   uint64_t flags;
@@ -183,5 +182,3 @@
 __END_DECLS
 
 /** @} */
-
-#endif
diff --git a/libc/include/android/legacy_stdlib_inlines.h b/libc/include/android/legacy_stdlib_inlines.h
index 0ca1022..f0985fe 100644
--- a/libc/include/android/legacy_stdlib_inlines.h
+++ b/libc/include/android/legacy_stdlib_inlines.h
@@ -38,15 +38,15 @@
 
 __BEGIN_DECLS
 
-static inline double strtod_l(const char* _Nonnull __s, char* _Nullable * _Nullable __end_ptr, locale_t _Nonnull __l) {
+static __inline double strtod_l(const char* _Nonnull __s, char* _Nullable * _Nullable __end_ptr, locale_t _Nonnull __l) {
   return strtod(__s, __end_ptr);
 }
 
-static inline float strtof_l(const char* _Nonnull __s, char* _Nullable * _Nullable __end_ptr, locale_t _Nonnull __l) {
+static __inline float strtof_l(const char* _Nonnull __s, char* _Nullable * _Nullable __end_ptr, locale_t _Nonnull __l) {
   return strtof(__s, __end_ptr);
 }
 
-static inline long strtol_l(const char* _Nonnull __s, char* _Nullable * _Nullable __end_ptr, int __base, locale_t _Nonnull __l) {
+static __inline long strtol_l(const char* _Nonnull __s, char* _Nullable * _Nullable __end_ptr, int __base, locale_t _Nonnull __l) {
   return strtol(__s, __end_ptr, __base);
 }
 
diff --git a/libc/include/android/legacy_termios_inlines.h b/libc/include/android/legacy_termios_inlines.h
index e557525..a816b40 100644
--- a/libc/include/android/legacy_termios_inlines.h
+++ b/libc/include/android/legacy_termios_inlines.h
@@ -39,14 +39,14 @@
 #include <sys/ioctl.h>
 #include <sys/types.h>
 
-#define __BIONIC_TERMIOS_INLINE static inline
+#define __BIONIC_TERMIOS_INLINE static __inline
 #include <bits/termios_inlines.h>
 
 #endif
 
 #if __ANDROID_API__ < 35
 
-#define __BIONIC_TERMIOS_WINSIZE_INLINE static inline
+#define __BIONIC_TERMIOS_WINSIZE_INLINE static __inline
 #include <bits/termios_winsize_inlines.h>
 
 #endif
diff --git a/libc/include/android/legacy_threads_inlines.h b/libc/include/android/legacy_threads_inlines.h
index 06e7438..c614cd0 100644
--- a/libc/include/android/legacy_threads_inlines.h
+++ b/libc/include/android/legacy_threads_inlines.h
@@ -32,7 +32,7 @@
 
 #if __ANDROID_API__ < 30
 
-#define __BIONIC_THREADS_INLINE static inline
+#define __BIONIC_THREADS_INLINE static __inline
 #include <bits/threads_inlines.h>
 
 #endif
diff --git a/libc/include/android/legacy_unistd_inlines.h b/libc/include/android/legacy_unistd_inlines.h
index ac9f3b3..4a5206b 100644
--- a/libc/include/android/legacy_unistd_inlines.h
+++ b/libc/include/android/legacy_unistd_inlines.h
@@ -32,7 +32,7 @@
 
 #if __ANDROID_API__ < 28
 
-#define __BIONIC_SWAB_INLINE static inline
+#define __BIONIC_SWAB_INLINE static __inline
 #include <bits/swab.h>
 
 #endif
diff --git a/libc/include/bits/stdatomic.h b/libc/include/bits/stdatomic.h
index 8df86e2..c74eafd 100644
--- a/libc/include/bits/stdatomic.h
+++ b/libc/include/bits/stdatomic.h
@@ -138,11 +138,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 __attribute__((__unused__))) {
 	__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 __attribute__((__unused__))) {
 	__c11_atomic_signal_fence(__order);
 }
 
@@ -269,18 +269,18 @@
 
 #define	ATOMIC_FLAG_INIT		{ ATOMIC_VAR_INIT(false) }
 
-static inline bool atomic_flag_test_and_set_explicit(volatile atomic_flag * _Nonnull __object, memory_order __order) {
+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));
 }
 
-static inline void atomic_flag_clear_explicit(volatile atomic_flag * _Nonnull __object, memory_order __order) {
+static __inline void atomic_flag_clear_explicit(volatile atomic_flag * _Nonnull __object, memory_order __order) {
 	atomic_store_explicit(&__object->__flag, 0, __order);
 }
 
-static inline bool atomic_flag_test_and_set(volatile atomic_flag * _Nonnull __object) {
+static __inline bool atomic_flag_test_and_set(volatile atomic_flag * _Nonnull __object) {
 	return (atomic_flag_test_and_set_explicit(__object, memory_order_seq_cst));
 }
 
-static inline void atomic_flag_clear(volatile atomic_flag * _Nonnull __object) {
+static __inline void atomic_flag_clear(volatile atomic_flag * _Nonnull __object) {
 	atomic_flag_clear_explicit(__object, memory_order_seq_cst);
 }
diff --git a/libc/include/bits/swab.h b/libc/include/bits/swab.h
index ebb7c74..9591c2e 100644
--- a/libc/include/bits/swab.h
+++ b/libc/include/bits/swab.h
@@ -33,7 +33,7 @@
 #include <sys/types.h>
 
 #if !defined(__BIONIC_SWAB_INLINE)
-#define __BIONIC_SWAB_INLINE static inline
+#define __BIONIC_SWAB_INLINE static __inline
 #endif
 
 __BEGIN_DECLS
diff --git a/libc/include/bits/termios_inlines.h b/libc/include/bits/termios_inlines.h
index 702f433..a884b59 100644
--- a/libc/include/bits/termios_inlines.h
+++ b/libc/include/bits/termios_inlines.h
@@ -37,7 +37,7 @@
 #include <linux/termios.h>
 
 #if !defined(__BIONIC_TERMIOS_INLINE)
-#define __BIONIC_TERMIOS_INLINE static inline
+#define __BIONIC_TERMIOS_INLINE static __inline
 #endif
 
 __BEGIN_DECLS
@@ -45,7 +45,7 @@
 // Supporting separate input and output speeds would require an ABI
 // change for `struct termios`.
 
-static inline speed_t cfgetspeed(const struct termios* _Nonnull s) {
+static __inline speed_t cfgetspeed(const struct termios* _Nonnull s) {
   return __BIONIC_CAST(static_cast, speed_t, s->c_cflag & CBAUD);
 }
 
diff --git a/libc/include/bits/termios_winsize_inlines.h b/libc/include/bits/termios_winsize_inlines.h
index 0d188e7..ae246e4 100644
--- a/libc/include/bits/termios_winsize_inlines.h
+++ b/libc/include/bits/termios_winsize_inlines.h
@@ -36,7 +36,7 @@
 #include <linux/termios.h>
 
 #if !defined(__BIONIC_TERMIOS_WINSIZE_INLINE)
-#define __BIONIC_TERMIOS_WINSIZE_INLINE static inline
+#define __BIONIC_TERMIOS_WINSIZE_INLINE static __inline
 #endif
 
 __BEGIN_DECLS
diff --git a/libc/include/bits/threads_inlines.h b/libc/include/bits/threads_inlines.h
index 074e1ca..459866e 100644
--- a/libc/include/bits/threads_inlines.h
+++ b/libc/include/bits/threads_inlines.h
@@ -38,7 +38,7 @@
 
 __BEGIN_DECLS
 
-static inline int __bionic_thrd_error(int __pthread_code) {
+static __inline int __bionic_thrd_error(int __pthread_code) {
   switch (__pthread_code) {
     case 0: return 0;
     case ENOMEM: return thrd_nomem;
@@ -124,7 +124,7 @@
 };
 #pragma clang diagnostic pop
 
-static inline void* _Nonnull __bionic_thrd_trampoline(void* _Nonnull __arg) {
+static __inline void* _Nonnull __bionic_thrd_trampoline(void* _Nonnull __arg) {
   struct __bionic_thrd_data __data =
       *__BIONIC_CAST(static_cast, struct __bionic_thrd_data*, __arg);
   free(__arg);
diff --git a/libc/include/ctype.h b/libc/include/ctype.h
index 5cad412..c15ee56 100644
--- a/libc/include/ctype.h
+++ b/libc/include/ctype.h
@@ -42,7 +42,7 @@
  * also provide actual symbols for any caller that needs them.
  */
 #if !defined(__BIONIC_CTYPE_INLINE)
-#define __BIONIC_CTYPE_INLINE static inline
+#define __BIONIC_CTYPE_INLINE static __inline
 #endif
 
 /** Internal implementation detail. Do not use. */
diff --git a/libc/include/link.h b/libc/include/link.h
index 33fea49..ee1fc42 100644
--- a/libc/include/link.h
+++ b/libc/include/link.h
@@ -25,8 +25,13 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#ifndef _LINK_H_
-#define _LINK_H_
+
+#pragma once
+
+/**
+ * @file link.h
+ * @brief Extra dynamic linker functionality (see also <dlfcn.h>).
+ */
 
 #include <stdint.h>
 #include <sys/cdefs.h>
@@ -37,32 +42,80 @@
 __BEGIN_DECLS
 
 #if defined(__LP64__)
+/** Convenience macro to get the appropriate 32-bit or 64-bit <elf.h> type for the caller's bitness. */
 #define ElfW(type) Elf64_ ## type
 #else
+/** Convenience macro to get the appropriate 32-bit or 64-bit <elf.h> type for the caller's bitness. */
 #define ElfW(type) Elf32_ ## type
 #endif
 
+/**
+ * Information passed by dl_iterate_phdr() to the callback.
+ */
 struct dl_phdr_info {
+  /** The address of the shared object. */
   ElfW(Addr) dlpi_addr;
+  /** The name of the shared object. */
   const char* _Nullable dlpi_name;
+  /** Pointer to the shared object's program headers. */
   const ElfW(Phdr)* _Nullable dlpi_phdr;
+  /** Number of program headers pointed to by `dlpi_phdr`. */
   ElfW(Half) dlpi_phnum;
 
-  // These fields were added in Android R.
+  /**
+   * The total number of library load events at the time dl_iterate_phdr() was
+   * called.
+   *
+   * This field is only available since API level 30; you can use the size
+   * passed to the callback to determine whether you have the full struct,
+   * or just the fields up to and including `dlpi_phnum`.
+   */
   unsigned long long dlpi_adds;
+  /**
+   * The total number of library unload events at the time dl_iterate_phdr() was
+   * called.
+   *
+   * This field is only available since API level 30; you can use the size
+   * passed to the callback to determine whether you have the full struct,
+   * or just the fields up to and including `dlpi_phnum`.
+   */
   unsigned long long dlpi_subs;
+  /**
+   * The module ID for TLS relocations in this shared object.
+   *
+   * This field is only available since API level 30; you can use the size
+   * passed to the callback to determine whether you have the full struct,
+   * or just the fields up to and including `dlpi_phnum`.
+   */
   size_t dlpi_tls_modid;
+  /**
+   * The caller's TLS data for this shared object.
+   *
+   * This field is only available since API level 30; you can use the size
+   * passed to the callback to determine whether you have the full struct,
+   * or just the fields up to and including `dlpi_phnum`.
+   */
   void* _Nullable dlpi_tls_data;
 };
 
-int dl_iterate_phdr(int (* _Nonnull __callback)(struct dl_phdr_info* _Nonnull, size_t, void* _Nullable), void* _Nullable __data);
+/**
+ * [dl_iterate_phdr(3)](http://man7.org/linux/man-pages/man3/dl_iterate_phdr.3.html)
+ * calls the given callback once for every loaded shared object. The size
+ * argument to the callback lets you determine whether you have a smaller
+ * `dl_phdr_info` from before API level 30, or the newer full one.
+ * The data argument to the callback is whatever you pass as the data argument
+ * to dl_iterate_phdr().
+ *
+ * Returns the value returned by the final call to the callback.
+ */
+int dl_iterate_phdr(int (* _Nonnull __callback)(struct dl_phdr_info* _Nonnull __info, size_t __size, void* _Nullable __data), void* _Nullable __data);
 
 #ifdef __arm__
 typedef uintptr_t _Unwind_Ptr;
 _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr, int* _Nonnull);
 #endif
 
-/* Used by the dynamic linker to communicate with the debugger. */
+/** Used by the dynamic linker to communicate with the debugger. */
 struct link_map {
   ElfW(Addr) l_addr;
   char* _Nullable l_name;
@@ -71,7 +124,7 @@
   struct link_map* _Nullable l_prev;
 };
 
-/* Used by the dynamic linker to communicate with the debugger. */
+/** Used by the dynamic linker to communicate with the debugger. */
 struct r_debug {
   int32_t r_version;
   struct link_map* _Nullable r_map;
@@ -85,5 +138,3 @@
 };
 
 __END_DECLS
-
-#endif
diff --git a/libc/include/strings.h b/libc/include/strings.h
index 6ec3bdf..4b8cc08 100644
--- a/libc/include/strings.h
+++ b/libc/include/strings.h
@@ -50,7 +50,7 @@
 #include <bits/strcasecmp.h>
 
 #if !defined(__BIONIC_STRINGS_INLINE)
-#define __BIONIC_STRINGS_INLINE static inline
+#define __BIONIC_STRINGS_INLINE static __inline
 #endif
 
 #undef ffs
@@ -61,13 +61,13 @@
 
 /** Deprecated. Use memmove() instead. */
 #define bcopy(b1, b2, len) __bionic_bcopy((b1), (b2), (len))
-static inline __always_inline void __bionic_bcopy(const void* _Nonnull b1, void* _Nonnull b2, size_t len) {
+static __inline __always_inline void __bionic_bcopy(const void* _Nonnull b1, void* _Nonnull b2, size_t len) {
   __builtin_memmove(b2, b1, len);
 }
 
 /** Deprecated. Use memset() instead. */
 #define bzero(b, len) __bionic_bzero((b), (len))
-static inline __always_inline void __bionic_bzero(void* _Nonnull b, size_t len) {
+static __inline __always_inline void __bionic_bzero(void* _Nonnull b, size_t len) {
   __builtin_memset(b, 0, len);
 }
 
diff --git a/libc/include/sys/cdefs.h b/libc/include/sys/cdefs.h
index 3218d15..5d1718e 100644
--- a/libc/include/sys/cdefs.h
+++ b/libc/include/sys/cdefs.h
@@ -87,9 +87,12 @@
 #define	__STRING(x)	#x
 #define	___STRING(x)	__STRING(x)
 
-#if defined(__cplusplus)
-#define	__inline	inline		/* convert to C++ keyword */
-#endif /* !__cplusplus */
+// C++ has `inline` as a keyword, as does C99, but ANSI C (aka C89 aka C90)
+// does not. Everything accepts the `__inline__` extension though. We could
+// just use that directly in our own code, but there's historical precedent
+// for `__inline` meaning it's still used in upstream BSD code (and potentially
+// downstream in vendor or app code).
+#define	__inline __inline__
 
 #define __always_inline __attribute__((__always_inline__))
 #define __attribute_const__ __attribute__((__const__))
@@ -260,7 +263,7 @@
  * them available externally. FORTIFY'ed functions try to be as close to possible as 'invisible';
  * having stack protectors detracts from that (b/182948263).
  */
-#  define __BIONIC_FORTIFY_INLINE static inline __attribute__((__no_stack_protector__)) \
+#  define __BIONIC_FORTIFY_INLINE static __inline __attribute__((__no_stack_protector__)) \
       __always_inline __VERSIONER_FORTIFY_INLINE
 /*
  * We should use __BIONIC_FORTIFY_VARIADIC instead of __BIONIC_FORTIFY_INLINE
@@ -268,9 +271,9 @@
  * The __always_inline attribute is useless, misleading, and could trigger
  * clang compiler bug to incorrectly inline variadic functions.
  */
-#  define __BIONIC_FORTIFY_VARIADIC static inline
+#  define __BIONIC_FORTIFY_VARIADIC static __inline
 /* Error functions don't have bodies, so they can just be static. */
-#  define __BIONIC_ERROR_FUNCTION_VISIBILITY static __attribute__((__unused__))
+#  define __BIONIC_ERROR_FUNCTION_VISIBILITY static __unused
 #else
 /* Further increase sharing for some inline functions */
 #  define __pass_object_size_n(n)
diff --git a/libc/include/sys/system_properties.h b/libc/include/sys/system_properties.h
index dc869da..ae94db5 100644
--- a/libc/include/sys/system_properties.h
+++ b/libc/include/sys/system_properties.h
@@ -26,8 +26,12 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _INCLUDE_SYS_SYSTEM_PROPERTIES_H
-#define _INCLUDE_SYS_SYSTEM_PROPERTIES_H
+#pragma once
+
+/**
+ * @file system_properties.h
+ * @brief System properties.
+ */
 
 #include <sys/cdefs.h>
 #include <stdbool.h>
@@ -36,39 +40,53 @@
 
 __BEGIN_DECLS
 
+/** An opaque structure representing a system property. */
 typedef struct prop_info prop_info;
 
+/**
+ * The limit on the length of a property value.
+ * (See PROP_NAME_MAX for property names.)
+ */
 #define PROP_VALUE_MAX  92
 
-/*
+/**
  * Sets system property `name` to `value`, creating the system property if it doesn't already exist.
+ *
+ * Returns 0 on success, or -1 on failure.
  */
 int __system_property_set(const char* _Nonnull __name, const char* _Nonnull __value);
 
-/*
+/**
  * Returns a `prop_info` corresponding system property `name`, or nullptr if it doesn't exist.
- * Use __system_property_read_callback to query the current value.
+ * Use __system_property_read_callback() to query the current value.
  *
- * Property lookup is expensive, so it can be useful to cache the result of this function.
+ * Property lookup is expensive, so it can be useful to cache the result of this
+ * function rather than using __system_property_get().
  */
 const prop_info* _Nullable __system_property_find(const char* _Nonnull __name);
 
-/*
- * Calls `callback` with a consistent trio of name, value, and serial number for property `pi`.
+/**
+ * Calls `callback` with a consistent trio of name, value, and serial number
+ * for property `pi`.
+ *
+ * Available since API level 26.
  */
 void __system_property_read_callback(const prop_info* _Nonnull __pi,
     void (* _Nonnull __callback)(void* _Nullable __cookie, const char* _Nonnull __name, const char* _Nonnull __value, uint32_t __serial),
     void* _Nullable __cookie) __INTRODUCED_IN(26);
 
-/*
+/**
  * Passes a `prop_info` for each system property to the provided
- * callback.  Use __system_property_read_callback() to read the value.
+ * callback. Use __system_property_read_callback() to read the value of
+ * any of the properties.
  *
  * This method is for inspecting and debugging the property system, and not generally useful.
+ *
+ * Returns 0 on success, or -1 on failure.
  */
 int __system_property_foreach(void (* _Nonnull __callback)(const prop_info* _Nonnull __pi, void* _Nullable __cookie), void* _Nullable __cookie);
 
-/*
+/**
  * Waits for the specific system property identified by `pi` to be updated
  * past `old_serial`. Waits no longer than `relative_timeout`, or forever
  * if `relative_timeout` is null.
@@ -79,20 +97,24 @@
  *
  * Returns true and updates `*new_serial_ptr` on success, or false if the call
  * timed out.
+ *
+ * Available since API level 26.
  */
 struct timespec;
 bool __system_property_wait(const prop_info* _Nullable __pi, uint32_t __old_serial, uint32_t* _Nonnull __new_serial_ptr, const struct timespec* _Nullable __relative_timeout)
     __INTRODUCED_IN(26);
 
-/* Deprecated. In Android O and above, there's no limit on property name length. */
+/**
+ * Deprecated: there's no limit on the length of a property name since
+ * API level 26, though the limit on property values (PROP_VALUE_MAX) remains.
+ */
 #define PROP_NAME_MAX   32
-/* Deprecated. Use __system_property_read_callback instead. */
+
+/** Deprecated. Use __system_property_read_callback() instead. */
 int __system_property_read(const prop_info* _Nonnull __pi, char* _Nullable __name, char* _Nonnull __value);
-/* Deprecated. Use __system_property_read_callback instead. */
+/** Deprecated. Use __system_property_read_callback() instead. */
 int __system_property_get(const char* _Nonnull __name, char* _Nonnull __value);
-/* Deprecated. Use __system_property_foreach instead. */
+/** Deprecated. Use __system_property_foreach() instead. */
 const prop_info* _Nullable __system_property_find_nth(unsigned __n);
 
 __END_DECLS
-
-#endif
diff --git a/libc/kernel/tools/cpp.py b/libc/kernel/tools/cpp.py
index 0fd6e46..08b786a 100755
--- a/libc/kernel/tools/cpp.py
+++ b/libc/kernel/tools/cpp.py
@@ -2345,11 +2345,11 @@
 
     def test_function_keep_attribute_structs(self):
         text = """\
-static inline struct some_struct1 * function(struct some_struct2 * e) {
+static __inline__ struct some_struct1 * function(struct some_struct2 * e) {
 }
 """
         expected = """\
-static inline struct some_struct1 * function(struct some_struct2 * e) {
+static __inline__ struct some_struct1 * function(struct some_struct2 * e) {
 }
 """
         self.assertEqual(self.parse(text, set(["function"])), expected)
diff --git a/libc/malloc_debug/MapData.cpp b/libc/malloc_debug/MapData.cpp
index b22c109..c58882a 100644
--- a/libc/malloc_debug/MapData.cpp
+++ b/libc/malloc_debug/MapData.cpp
@@ -34,6 +34,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
+#include <sys/uio.h>
+#include <unistd.h>
 
 #include <vector>
 
@@ -69,148 +71,132 @@
 
   MapEntry* entry = new MapEntry(start, end, offset, name, name_len, flags);
   if (!(flags & PROT_READ)) {
-    // Any unreadable map will just get a zero load bias.
-    entry->load_bias = 0;
-    entry->init = true;
-    entry->valid = false;
+    // This will make sure that an unreadable map will prevent attempts to read
+    // elf data from the map.
+    entry->SetInvalid();
   }
   return entry;
 }
 
-template <typename T>
-static inline bool get_val(MapEntry* entry, uintptr_t addr, T* store) {
-  if (!(entry->flags & PROT_READ) || addr < entry->start || addr + sizeof(T) > entry->end) {
-    return false;
+void MapEntry::Init() {
+  if (init_) {
+    return;
   }
-  // Make sure the address is aligned properly.
-  if (addr & (sizeof(T) - 1)) {
-    return false;
-  }
-  *store = *reinterpret_cast<T*>(addr);
-  return true;
-}
+  init_ = true;
 
-static bool valid_elf(MapEntry* entry) {
-  uintptr_t addr = entry->start;
-  uintptr_t end;
-  if (__builtin_add_overflow(addr, SELFMAG, &end) || end >= entry->end) {
-    return false;
+  uintptr_t end_addr;
+  if (__builtin_add_overflow(start_, SELFMAG, &end_addr) || end_addr >= end_) {
+    return;
   }
 
-  return memcmp(reinterpret_cast<void*>(addr), ELFMAG, SELFMAG) == 0;
-}
-
-static void read_loadbias(MapEntry* entry) {
-  entry->load_bias = 0;
-  uintptr_t addr = entry->start;
   ElfW(Ehdr) ehdr;
-  if (!get_val<ElfW(Half)>(entry, addr + offsetof(ElfW(Ehdr), e_phnum), &ehdr.e_phnum)) {
-    return;
+  struct iovec src_io = {.iov_base = reinterpret_cast<void*>(start_), .iov_len = SELFMAG};
+  struct iovec dst_io = {.iov_base = ehdr.e_ident, .iov_len = SELFMAG};
+  ssize_t rc = process_vm_readv(getpid(), &dst_io, 1, &src_io, 1, 0);
+  valid_ = rc == SELFMAG && IS_ELF(ehdr);
+}
+
+uintptr_t MapEntry::GetLoadBias() {
+  if (!valid_) {
+    return 0;
   }
-  if (!get_val<ElfW(Off)>(entry, addr + offsetof(ElfW(Ehdr), e_phoff), &ehdr.e_phoff)) {
-    return;
+
+  if (load_bias_read_) {
+    return load_bias_;
   }
-  addr += ehdr.e_phoff;
+
+  load_bias_read_ = true;
+
+  ElfW(Ehdr) ehdr;
+  struct iovec src_io = {.iov_base = reinterpret_cast<void*>(start_), .iov_len = sizeof(ehdr)};
+  struct iovec dst_io = {.iov_base = &ehdr, .iov_len = sizeof(ehdr)};
+  ssize_t rc = process_vm_readv(getpid(), &dst_io, 1, &src_io, 1, 0);
+  if (rc != sizeof(ehdr)) {
+    return 0;
+  }
+
+  uintptr_t addr = start_ + ehdr.e_phoff;
   for (size_t i = 0; i < ehdr.e_phnum; i++) {
     ElfW(Phdr) phdr;
-    if (!get_val<ElfW(Word)>(entry, addr + offsetof(ElfW(Phdr), p_type), &phdr.p_type)) {
-      return;
-    }
-    if (!get_val<ElfW(Word)>(entry, addr + offsetof(ElfW(Phdr), p_flags), &phdr.p_flags)) {
-      return;
-    }
-    if (!get_val<ElfW(Off)>(entry, addr + offsetof(ElfW(Phdr), p_offset), &phdr.p_offset)) {
-      return;
+
+    src_io.iov_base = reinterpret_cast<void*>(addr);
+    src_io.iov_len = sizeof(phdr);
+    dst_io.iov_base = &phdr;
+    dst_io.iov_len = sizeof(phdr);
+    rc = process_vm_readv(getpid(), &dst_io, 1, &src_io, 1, 0);
+    if (rc != sizeof(phdr)) {
+      return 0;
     }
     if ((phdr.p_type == PT_LOAD) && (phdr.p_flags & PF_X) ) {
-      if (!get_val<ElfW(Addr)>(entry, addr + offsetof(ElfW(Phdr), p_vaddr), &phdr.p_vaddr)) {
-        return;
-      }
-      entry->load_bias = phdr.p_vaddr - phdr.p_offset;
-      return;
+      load_bias_ = phdr.p_vaddr - phdr.p_offset;
+      return load_bias_;
     }
     addr += sizeof(phdr);
   }
+  return 0;
 }
 
-static void inline init(MapEntry* entry) {
-  if (entry->init) {
-    return;
-  }
-  entry->init = true;
-  if (valid_elf(entry)) {
-    entry->valid = true;
-    read_loadbias(entry);
-  }
-}
-
-bool MapData::ReadMaps() {
+void MapData::ReadMaps() {
+  std::lock_guard<std::mutex> lock(m_);
   FILE* fp = fopen("/proc/self/maps", "re");
   if (fp == nullptr) {
-    return false;
+    return;
   }
 
+  ClearEntries();
+
   std::vector<char> buffer(1024);
   while (fgets(buffer.data(), buffer.size(), fp) != nullptr) {
     MapEntry* entry = parse_line(buffer.data());
     if (entry == nullptr) {
-      fclose(fp);
-      return false;
+      break;
     }
-
-    auto it = entries_.find(entry);
-    if (it == entries_.end()) {
-      entries_.insert(entry);
-    } else {
-      delete entry;
-    }
+    entries_.insert(entry);
   }
   fclose(fp);
-  return true;
 }
 
-MapData::~MapData() {
+void MapData::ClearEntries() {
   for (auto* entry : entries_) {
     delete entry;
   }
   entries_.clear();
 }
 
+MapData::~MapData() {
+  ClearEntries();
+}
+
 // Find the containing map info for the PC.
 const MapEntry* MapData::find(uintptr_t pc, uintptr_t* rel_pc) {
   MapEntry pc_entry(pc);
 
   std::lock_guard<std::mutex> lock(m_);
-
   auto it = entries_.find(&pc_entry);
   if (it == entries_.end()) {
-    ReadMaps();
-  }
-  it = entries_.find(&pc_entry);
-  if (it == entries_.end()) {
     return nullptr;
   }
 
   MapEntry* entry = *it;
-  init(entry);
+  entry->Init();
 
   if (rel_pc != nullptr) {
     // Need to check to see if this is a read-execute map and the read-only
     // map is the previous one.
-    if (!entry->valid && it != entries_.begin()) {
+    if (!entry->valid() && it != entries_.begin()) {
       MapEntry* prev_entry = *--it;
-      if (prev_entry->flags == PROT_READ && prev_entry->offset < entry->offset &&
-          prev_entry->name == entry->name) {
-        init(prev_entry);
+      if (prev_entry->flags() == PROT_READ && prev_entry->offset() < entry->offset() &&
+          prev_entry->name() == entry->name()) {
+        prev_entry->Init();
 
-        if (prev_entry->valid) {
-          entry->elf_start_offset = prev_entry->offset;
-          *rel_pc = pc - entry->start + entry->offset + prev_entry->load_bias;
+        if (prev_entry->valid()) {
+          entry->set_elf_start_offset(prev_entry->offset());
+          *rel_pc = pc - entry->start() + entry->offset() + prev_entry->GetLoadBias();
           return entry;
         }
       }
     }
-    *rel_pc = pc - entry->start + entry->offset + entry->load_bias;
+    *rel_pc = pc - entry->start() + entry->offset() + entry->GetLoadBias();
   }
   return entry;
 }
diff --git a/libc/malloc_debug/MapData.h b/libc/malloc_debug/MapData.h
index f2b3c1c..13bf9cb 100644
--- a/libc/malloc_debug/MapData.h
+++ b/libc/malloc_debug/MapData.h
@@ -36,26 +36,50 @@
 
 #include <platform/bionic/macros.h>
 
-struct MapEntry {
-  MapEntry(uintptr_t start, uintptr_t end, uintptr_t offset, const char* name, size_t name_len, int flags)
-      : start(start), end(end), offset(offset), name(name, name_len), flags(flags) {}
+class MapEntry {
+ public:
+  MapEntry() = default;
+  MapEntry(uintptr_t start, uintptr_t end, uintptr_t offset, const char* name, size_t name_len,
+           int flags)
+      : start_(start), end_(end), offset_(offset), name_(name, name_len), flags_(flags) {}
 
-  explicit MapEntry(uintptr_t pc) : start(pc), end(pc) {}
+  explicit MapEntry(uintptr_t pc) : start_(pc), end_(pc) {}
 
-  uintptr_t start;
-  uintptr_t end;
-  uintptr_t offset;
-  uintptr_t load_bias;
-  uintptr_t elf_start_offset = 0;
-  std::string name;
-  int flags;
-  bool init = false;
-  bool valid = false;
+  void Init();
+
+  uintptr_t GetLoadBias();
+
+  void SetInvalid() {
+    valid_ = false;
+    init_ = true;
+    load_bias_read_ = true;
+  }
+
+  bool valid() { return valid_; }
+  uintptr_t start() const { return start_; }
+  uintptr_t end() const { return end_; }
+  uintptr_t offset() const { return offset_; }
+  uintptr_t elf_start_offset() const { return elf_start_offset_; }
+  void set_elf_start_offset(uintptr_t elf_start_offset) { elf_start_offset_ = elf_start_offset; }
+  const std::string& name() const { return name_; }
+  int flags() const { return flags_; }
+
+ private:
+  uintptr_t start_;
+  uintptr_t end_;
+  uintptr_t offset_;
+  uintptr_t load_bias_ = 0;
+  uintptr_t elf_start_offset_ = 0;
+  std::string name_;
+  int flags_;
+  bool init_ = false;
+  bool valid_ = false;
+  bool load_bias_read_ = false;
 };
 
 // Ordering comparator that returns equivalence for overlapping entries
 struct compare_entries {
-  bool operator()(const MapEntry* a, const MapEntry* b) const { return a->end <= b->start; }
+  bool operator()(const MapEntry* a, const MapEntry* b) const { return a->end() <= b->start(); }
 };
 
 class MapData {
@@ -65,11 +89,15 @@
 
   const MapEntry* find(uintptr_t pc, uintptr_t* rel_pc = nullptr);
 
- private:
-  bool ReadMaps();
+  size_t NumMaps() { return entries_.size(); }
 
+  void ReadMaps();
+
+ private:
   std::mutex m_;
   std::set<MapEntry*, compare_entries> entries_;
 
+  void ClearEntries();
+
   BIONIC_DISALLOW_COPY_AND_ASSIGN(MapData);
 };
diff --git a/libc/malloc_debug/backtrace.cpp b/libc/malloc_debug/backtrace.cpp
index ecb3a80..6a32fca 100644
--- a/libc/malloc_debug/backtrace.cpp
+++ b/libc/malloc_debug/backtrace.cpp
@@ -50,7 +50,7 @@
 typedef struct _Unwind_Context __unwind_context;
 
 static MapData g_map_data;
-static const MapEntry* g_current_code_map = nullptr;
+static MapEntry g_current_code_map;
 
 static _Unwind_Reason_Code find_current_map(__unwind_context* context, void*) {
   uintptr_t ip = _Unwind_GetIP(context);
@@ -58,11 +58,15 @@
   if (ip == 0) {
     return _URC_END_OF_STACK;
   }
-  g_current_code_map = g_map_data.find(ip);
+  auto map = g_map_data.find(ip);
+  if (map != nullptr) {
+    g_current_code_map = *map;
+  }
   return _URC_END_OF_STACK;
 }
 
 void backtrace_startup() {
+  g_map_data.ReadMaps();
   _Unwind_Backtrace(find_current_map, nullptr);
 }
 
@@ -98,7 +102,8 @@
   }
 
   // Do not record the frames that fall in our own shared library.
-  if (g_current_code_map && (ip >= g_current_code_map->start) && ip < g_current_code_map->end) {
+  if (g_current_code_map.start() != 0 && (ip >= g_current_code_map.start()) &&
+      ip < g_current_code_map.end()) {
     return _URC_NO_REASON;
   }
 
@@ -113,6 +118,10 @@
 }
 
 std::string backtrace_string(const uintptr_t* frames, size_t frame_count) {
+  if (g_map_data.NumMaps() == 0) {
+    g_map_data.ReadMaps();
+  }
+
   std::string str;
 
   for (size_t frame_num = 0; frame_num < frame_count; frame_num++) {
@@ -130,14 +139,15 @@
     uintptr_t rel_pc = offset;
     const MapEntry* entry = g_map_data.find(frames[frame_num], &rel_pc);
 
-    const char* soname = (entry != nullptr) ? entry->name.c_str() : info.dli_fname;
+    const char* soname = (entry != nullptr) ? entry->name().c_str() : info.dli_fname;
     if (soname == nullptr) {
       soname = "<unknown>";
     }
 
     char offset_buf[128];
-    if (entry != nullptr && entry->elf_start_offset != 0) {
-      snprintf(offset_buf, sizeof(offset_buf), " (offset 0x%" PRIxPTR ")", entry->elf_start_offset);
+    if (entry != nullptr && entry->elf_start_offset() != 0) {
+      snprintf(offset_buf, sizeof(offset_buf), " (offset 0x%" PRIxPTR ")",
+               entry->elf_start_offset());
     } else {
       offset_buf[0] = '\0';
     }
@@ -167,5 +177,6 @@
 }
 
 void backtrace_log(const uintptr_t* frames, size_t frame_count) {
+  g_map_data.ReadMaps();
   error_log_string(backtrace_string(frames, frame_count).c_str());
 }
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index 089eceb..e27fd91 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -422,20 +422,11 @@
 
   ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(si->base);
 
-  // We haven't supported non-PIE since Lollipop for security reasons.
+  // For security reasons we dropped non-PIE support in API level 21,
+  // and the NDK no longer supports earlier API levels.
   if (elf_hdr->e_type != ET_DYN) {
-    // We don't use async_safe_fatal here because we don't want a tombstone:
-    // even after several years we still find ourselves on app compatibility
-    // investigations because some app's trying to launch an executable that
-    // hasn't worked in at least three years, and we've "helpfully" dropped a
-    // tombstone for them. The tombstone never provided any detail relevant to
-    // fixing the problem anyway, and the utility of drawing extra attention
-    // to the problem is non-existent at this late date.
-    async_safe_format_fd(STDERR_FILENO,
-                         "\"%s\": error: Android 5.0 and later only support "
-                         "position-independent executables (-fPIE).\n",
-                         g_argv[0]);
-    _exit(EXIT_FAILURE);
+    __linker_error("error: %s: Android only supports position-independent "
+                   "executables (-fPIE)\n", exe_info.path.c_str());
   }
 
   // Use LD_LIBRARY_PATH and LD_PRELOAD (but only if we aren't setuid/setgid).
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index fa712a1..b9229ca 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -298,7 +298,6 @@
   }
 
   if (header_.e_shentsize != sizeof(ElfW(Shdr))) {
-    // Fail if app is targeting Android O or above
     if (get_application_target_sdk_version() >= 26) {
       DL_ERR_AND_LOG("\"%s\" has unsupported e_shentsize: 0x%x (expected 0x%zx)",
                      name_.c_str(), header_.e_shentsize, sizeof(ElfW(Shdr)));
@@ -312,12 +311,10 @@
   }
 
   if (header_.e_shstrndx == 0) {
-    // Fail if app is targeting Android O or above
     if (get_application_target_sdk_version() >= 26) {
       DL_ERR_AND_LOG("\"%s\" has invalid e_shstrndx", name_.c_str());
       return false;
     }
-
     DL_WARN_documented_change(26,
                               "invalid-elf-header_section-headers-enforced-for-api-level-26",
                               "\"%s\" has invalid e_shstrndx", name_.c_str());
diff --git a/tests/setjmp_test.cpp b/tests/setjmp_test.cpp
index 6ae8bfd..0de0a01 100644
--- a/tests/setjmp_test.cpp
+++ b/tests/setjmp_test.cpp
@@ -174,31 +174,23 @@
   }
 }
 
-#if defined(__aarch64__)
+#if defined(__arm__)
+#define SET_FREG(n, v) asm volatile("vmov.f64 d"#n ", #"#v : : : "d"#n)
+#define GET_FREG(n) ({ double _r; asm volatile("fcpyd %P0, d"#n : "=w"(_r) : :); _r;})
+#define CLEAR_FREG(n) asm volatile("vmov.i64 d"#n ", #0x0" : : : "d"#n)
+#elif defined(__aarch64__)
 #define SET_FREG(n, v) asm volatile("fmov d"#n ", "#v : : : "d"#n)
+#define GET_FREG(n) ({ double _r; asm volatile("fmov %0, d"#n : "=r"(_r) : :); _r; })
 #define CLEAR_FREG(n) asm volatile("fmov d"#n ", xzr" : : : "d"#n)
+#endif
+
+#if defined(__arm__) || defined(__aarch64__)
 #define SET_FREGS \
   SET_FREG(8, 8.0); SET_FREG(9, 9.0); SET_FREG(10, 10.0); SET_FREG(11, 11.0); \
   SET_FREG(12, 12.0); SET_FREG(13, 13.0); SET_FREG(14, 14.0); SET_FREG(15, 15.0);
 #define CLEAR_FREGS \
   CLEAR_FREG(8); CLEAR_FREG(9); CLEAR_FREG(10); CLEAR_FREG(11); \
   CLEAR_FREG(12); CLEAR_FREG(13); CLEAR_FREG(14); CLEAR_FREG(15);
-#define GET_FREG(n) ({ double _r; asm volatile("fmov %0, d"#n : "=r"(_r) : :); _r; })
-#define CHECK_FREGS \
-    EXPECT_EQ(8.0, GET_FREG(8)); EXPECT_EQ(9.0, GET_FREG(9)); \
-    EXPECT_EQ(10.0, GET_FREG(10)); EXPECT_EQ(11.0, GET_FREG(11)); \
-    EXPECT_EQ(12.0, GET_FREG(12)); EXPECT_EQ(13.0, GET_FREG(13)); \
-    EXPECT_EQ(14.0, GET_FREG(14)); EXPECT_EQ(15.0, GET_FREG(15));
-#elif defined(__arm__)
-#define SET_FREG(n, v) \
-  ({ const double _v{v}; asm volatile("fcpyd d"#n ", %P0" : : "w"(_v) : "d"#n); })
-#define SET_FREGS \
-  SET_FREG(8, 8); SET_FREG(9, 9); SET_FREG(10, 10); SET_FREG(11, 11); \
-  SET_FREG(12, 12); SET_FREG(13, 13); SET_FREG(14, 14); SET_FREG(15, 15);
-#define CLEAR_FREGS \
-  SET_FREG(8, 0); SET_FREG(9, 0); SET_FREG(10, 0); SET_FREG(11, 0); \
-  SET_FREG(12, 0); SET_FREG(13, 0); SET_FREG(14, 0); SET_FREG(15, 0);
-#define GET_FREG(n) ({ double _r; asm volatile("fcpyd %P0, d"#n : "=w"(_r) : :); _r;})
 #define CHECK_FREGS \
     EXPECT_EQ(8.0, GET_FREG(8)); EXPECT_EQ(9.0, GET_FREG(9)); \
     EXPECT_EQ(10.0, GET_FREG(10)); EXPECT_EQ(11.0, GET_FREG(11)); \
diff --git a/tests/stack_protector_test.cpp b/tests/stack_protector_test.cpp
index c4be78c..aea791c 100644
--- a/tests/stack_protector_test.cpp
+++ b/tests/stack_protector_test.cpp
@@ -136,7 +136,7 @@
   if (stack_mte_enabled()) {
     GTEST_SKIP() << "Stack MTE is enabled, stack protector is not available";
   } else if (hwasan_enabled()) {
-    ASSERT_EXIT(modify_stack_protector_test(), testing::KilledBySignal(SIGABRT), "tag-mismatch");
+    GTEST_SKIP() << "HWASan is enabled, stack protector is not testable";
   } else {
     ASSERT_EXIT(modify_stack_protector_test(), testing::KilledBySignal(SIGABRT),
                 "stack corruption detected");
diff --git a/tests/time_test.cpp b/tests/time_test.cpp
index ca8e260..baafbf6 100644
--- a/tests/time_test.cpp
+++ b/tests/time_test.cpp
@@ -31,6 +31,8 @@
 #include <thread>
 
 #include "SignalUtils.h"
+#include "android-base/file.h"
+#include "android-base/strings.h"
 #include "utils.h"
 
 using namespace std::chrono_literals;
@@ -797,21 +799,41 @@
   ASSERT_EQ(1, timer_create_NULL_signal_handler_invocation_count);
 }
 
-TEST(time, timer_create_EINVAL) {
-  clockid_t invalid_clock = 16;
+static int GetThreadCount() {
+  std::string status;
+  if (android::base::ReadFileToString("/proc/self/status", &status)) {
+    for (const auto& line : android::base::Split(status, "\n")) {
+      int thread_count;
+      if (sscanf(line.c_str(), "Threads: %d", &thread_count) == 1) {
+        return thread_count;
+      }
+    }
+  }
+  return -1;
+}
 
-  // A SIGEV_SIGNAL timer is easy; the kernel does all that.
+TEST(time, timer_create_EINVAL) {
+  const clockid_t kInvalidClock = 16;
+
+  // A SIGEV_SIGNAL timer failure is easy; that's the kernel's problem.
   timer_t timer_id;
-  ASSERT_EQ(-1, timer_create(invalid_clock, nullptr, &timer_id));
+  ASSERT_EQ(-1, timer_create(kInvalidClock, nullptr, &timer_id));
   ASSERT_ERRNO(EINVAL);
 
-  // A SIGEV_THREAD timer is more interesting because we have stuff to clean up.
-  sigevent se;
-  memset(&se, 0, sizeof(se));
+  // A SIGEV_THREAD timer failure is more interesting because we have a thread
+  // to clean up (https://issuetracker.google.com/340125671).
+  sigevent se = {};
   se.sigev_notify = SIGEV_THREAD;
   se.sigev_notify_function = NoOpNotifyFunction;
-  ASSERT_EQ(-1, timer_create(invalid_clock, &se, &timer_id));
+  ASSERT_EQ(-1, timer_create(kInvalidClock, &se, &timer_id));
   ASSERT_ERRNO(EINVAL);
+
+  // timer_create() doesn't guarantee that the thread will be dead _before_
+  // it returns because that would require extra synchronization that's
+  // unnecessary in the normal (successful) case. A timeout here means we
+  // leaked a thread.
+  while (GetThreadCount() > 1) {
+  }
 }
 
 TEST(time, timer_create_multiple) {
diff --git a/tests/utils.h b/tests/utils.h
index dcb08f5..3c83b73 100644
--- a/tests/utils.h
+++ b/tests/utils.h
@@ -38,6 +38,7 @@
 #endif
 
 #include <atomic>
+#include <iomanip>
 #include <string>
 #include <regex>
 
@@ -253,7 +254,7 @@
     AssertChildExited(pid, expected_exit_status, &error_msg);
     if (expected_output_regex != nullptr) {
       if (!std::regex_search(output_, std::regex(expected_output_regex))) {
-        FAIL() << "regex " << expected_output_regex << " didn't match " << output_;
+        FAIL() << "regex " << std::quoted(expected_output_regex) << " didn't match " << std::quoted(output_);
       }
     }
   }