Merge "fdsan docs: be more consistent in using API levels." into main
diff --git a/android-changes-for-ndk-developers.md b/android-changes-for-ndk-developers.md
index 8d507d1..e9cfbac 100644
--- a/android-changes-for-ndk-developers.md
+++ b/android-changes-for-ndk-developers.md
@@ -445,6 +445,18 @@
 | No `dlclose`      | Works                      | Works   | Works |
 
 
+## ELF TLS (Available for API level >= 29)
+
+Android supports [ELF TLS](docs/elf-tls.md) starting at API level 29. Since
+NDK r26, clang will automatically enable ELF TLS for `minSdkVersion 29` or
+higher. Otherwise, the existing emutls implementation (which uses
+`pthread_key_create()` behind the scenes) will continue to be used. This
+means that convenient C/C++ thread-local syntax is available at any API level;
+at worst it will perform similarly to "roll your own" thread locals using
+`pthread_key_create()` but at best you'll get the performance benefit of
+ELF TLS, and the NDK will take care of the details.
+
+
 ## Use of IFUNC in libc (True for all API levels on devices running Android 10)
 
 On devices running API level 29, libc uses
diff --git a/docs/elf-tls.md b/docs/elf-tls.md
index d408b3f..6b03841 100644
--- a/docs/elf-tls.md
+++ b/docs/elf-tls.md
@@ -1,9 +1,4 @@
-# Android ELF TLS (Draft)
-
-Internal links:
- * [go/android-elf-tls](http://go/android-elf-tls)
- * [One-pager](https://docs.google.com/document/d/1leyPTnwSs24P2LGiqnU6HetnN5YnDlZkihigi6qdf_M)
- * Tracking bugs: http://b/110100012, http://b/78026329
+# Android ELF TLS
 
 [TOC]
 
diff --git a/docs/status.md b/docs/status.md
index bc8ab6a..e0364a8 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -125,6 +125,7 @@
   * `getloadavg` (BSD/GNU extension in <stdlib.h>)
 
 New libc behavior in Q (API level 29):
+  * Support for [ELF TLS](elf-tls.md).
   * Whole printf family now supports the GNU `%m` extension, rather than a
     special-case hack in `syslog`.
   * `popen` now always uses `O_CLOEXEC`, not just with the `e` extension.
diff --git a/libc/Android.bp b/libc/Android.bp
index 943d41f..5063364 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1,5 +1,3 @@
-// Define the common source files for all the libc instances
-// =========================================================
 package {
     default_applicable_licenses: ["bionic_libc_license"],
 }
@@ -114,6 +112,26 @@
     tidy_disabled_srcs: ["upstream-*/**/*.c"],
 }
 
+// Workaround for b/24465209.
+// We're unlikely to be able to remove this before we just
+// remove ILP32 support completely.
+// Note that we also still have `pack_relocations: false`
+// for both libc and libm, even on LP64.
+// ========================================================
+cc_defaults {
+    name: "bug_24465209_workaround",
+    target: {
+        android_arm: {
+            pack_relocations: false,
+            ldflags: ["-Wl,--hash-style=both"],
+        },
+        android_x86: {
+            pack_relocations: false,
+            ldflags: ["-Wl,--hash-style=both"],
+        },
+    },
+}
+
 // Defaults for native allocator libs/includes to make it
 // easier to change.
 // ========================================================
@@ -1529,6 +1547,7 @@
     defaults: [
         "libc_defaults",
         "libc_native_allocator_defaults",
+        "bug_24465209_workaround",
     ],
     name: "libc_library_defaults",
     product_variables: {
@@ -1566,14 +1585,6 @@
     // Do not pack libc.so relocations; see http://b/20645321 for details.
     pack_relocations: false,
 
-    // WARNING: The only libraries libc.so should depend on are libdl.so and ld-android.so!
-    // If you add other libraries, make sure to add -Wl,--exclude-libs=libgcc.a to the
-    // LOCAL_LDFLAGS for those libraries.  This ensures that symbols that are pulled into
-    // those new libraries from libgcc.a are not declared external; if that were the case,
-    // then libc would not pull those symbols from libgcc.a as it should, instead relying
-    // on the external symbols from the dependent libraries.  That would create a "cloaked"
-    // dependency on libgcc.a in libc though the libraries, which is not what you wanted!
-
     shared_libs: [
         "ld-android",
         "libdl",
@@ -1586,10 +1597,7 @@
 
     arch: {
         arm: {
-            // TODO: This is to work around b/24465209. Remove after root cause is fixed.
-            pack_relocations: false,
             ldflags: [
-                "-Wl,--hash-style=both",
                 // Since we are preserving the debug_frame, do not compress
                 // in this case to make unwinds as fast as possible.
                 "-Wl,--compress-debug-sections=none",
@@ -1633,10 +1641,6 @@
             },
         },
         x86: {
-            // TODO: This is to work around b/24465209. Remove after root cause is fixed.
-            pack_relocations: false,
-            ldflags: ["-Wl,--hash-style=both"],
-
             version_script: ":libc.x86.map",
             no_libcrt: true,
 
@@ -1916,7 +1920,10 @@
 // ========================================================
 
 cc_library {
-    defaults: ["libc_defaults"],
+    defaults: [
+        "libc_defaults",
+        "bug_24465209_workaround",
+    ],
     include_dirs: ["bionic/libstdc++/include"],
     srcs: [
         "bionic/__cxa_guard.cpp",
@@ -1941,12 +1948,8 @@
         },
     },
 
-    //TODO (dimitry): This is to work around b/24465209. Remove after root cause is fixed
     arch: {
         arm: {
-            // TODO: This is to work around b/24465209. Remove after root cause is fixed.
-            pack_relocations: false,
-            ldflags: ["-Wl,--hash-style=both"],
             version_script: ":libstdc++.arm.map",
         },
         arm64: {
@@ -1956,8 +1959,6 @@
             version_script: ":libstdc++.riscv64.map",
         },
         x86: {
-            pack_relocations: false,
-            ldflags: ["-Wl,--hash-style=both"],
             version_script: ":libstdc++.x86.map",
         },
         x86_64: {
diff --git a/libc/bionic/heap_tagging.cpp b/libc/bionic/heap_tagging.cpp
index cadab3c..3d92404 100644
--- a/libc/bionic/heap_tagging.cpp
+++ b/libc/bionic/heap_tagging.cpp
@@ -184,6 +184,9 @@
 
 #ifdef __aarch64__
 static inline __attribute__((no_sanitize("memtag"))) void untag_memory(void* from, void* to) {
+  if (from == to) {
+    return;
+  }
   __asm__ __volatile__(
       ".arch_extension mte\n"
       "1:\n"
diff --git a/libc/bionic/libc_init_dynamic.cpp b/libc/bionic/libc_init_dynamic.cpp
index 2dde2f1..541e71c 100644
--- a/libc/bionic/libc_init_dynamic.cpp
+++ b/libc/bionic/libc_init_dynamic.cpp
@@ -61,8 +61,9 @@
 };
 
 void memtag_stack_dlopen_callback() {
-  async_safe_format_log(ANDROID_LOG_DEBUG, "libc", "remapping stacks as PROT_MTE");
-  __pthread_internal_remap_stack_with_mte();
+  if (__pthread_internal_remap_stack_with_mte()) {
+    async_safe_format_log(ANDROID_LOG_DEBUG, "libc", "remapped stacks as PROT_MTE");
+  }
 }
 
 // Use an initializer so __libc_sysinfo will have a fallback implementation
diff --git a/libc/bionic/locale.cpp b/libc/bionic/locale.cpp
index 2f4d206..a1d6909 100644
--- a/libc/bionic/locale.cpp
+++ b/libc/bionic/locale.cpp
@@ -35,17 +35,8 @@
 #include <time.h>
 #include <wchar.h>
 
-#include "platform/bionic/macros.h"
-
-#if defined(__BIONIC_BUILD_FOR_ANDROID_SUPPORT)
-#define USE_TLS_SLOT 0
-#else
-#define USE_TLS_SLOT 1
-#endif
-
-#if USE_TLS_SLOT
 #include "bionic/pthread_internal.h"
-#endif
+#include "platform/bionic/macros.h"
 
 // We only support two locales, the "C" locale (also known as "POSIX"),
 // and the "C.UTF-8" locale (also known as "en_US.UTF-8").
@@ -82,10 +73,6 @@
   return get_locale_mb_cur_max(uselocale(nullptr));
 }
 
-#if !USE_TLS_SLOT
-static thread_local locale_t g_current_locale;
-#endif
-
 static pthread_once_t g_locale_once = PTHREAD_ONCE_INIT;
 static lconv g_locale;
 
@@ -180,11 +167,7 @@
 }
 
 static locale_t* get_current_locale_ptr() {
-#if USE_TLS_SLOT
   return &__get_bionic_tls().locale;
-#else
-  return &g_current_locale;
-#endif
 }
 
 locale_t uselocale(locale_t new_locale) {
diff --git a/libc/bionic/pthread_internal.cpp b/libc/bionic/pthread_internal.cpp
index 2342aff..8b9573f 100644
--- a/libc/bionic/pthread_internal.cpp
+++ b/libc/bionic/pthread_internal.cpp
@@ -176,12 +176,12 @@
   async_safe_fatal("stack not found in /proc/self/maps");
 }
 
-void __pthread_internal_remap_stack_with_mte() {
+bool __pthread_internal_remap_stack_with_mte() {
 #if defined(__aarch64__)
   // If process doesn't have MTE enabled, we don't need to do anything.
-  if (!atomic_load(&__libc_globals->memtag)) return;
+  if (!atomic_load(&__libc_globals->memtag)) return false;
   bool prev = atomic_exchange(&__libc_memtag_stack, true);
-  if (prev) return;
+  if (prev) return false;
   uintptr_t lo, hi;
   __find_main_stack_limits(&lo, &hi);
 
@@ -198,7 +198,10 @@
       async_safe_fatal("error: failed to set PROT_MTE on thread: %d", t->tid);
     }
   }
-#endif
+  return true;
+#else
+  return false;
+#endif  // defined(__aarch64__)
 }
 
 bool android_run_on_all_threads(bool (*func)(void*), void* arg) {
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index c2abdea..b0e9461 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -268,8 +268,9 @@
 __LIBC_HIDDEN__ extern void __bionic_atfork_run_child();
 __LIBC_HIDDEN__ extern void __bionic_atfork_run_parent();
 
-// Re-map all threads and successively launched threads with PROT_MTE.
-__LIBC_HIDDEN__ void __pthread_internal_remap_stack_with_mte();
+// Re-map all threads and successively launched threads with PROT_MTE. Returns 'true' if remapping
+// took place, 'false' on error or if the stacks were already remapped in the past.
+__LIBC_HIDDEN__ bool __pthread_internal_remap_stack_with_mte();
 
 extern "C" bool android_run_on_all_threads(bool (*func)(void*), void* arg);
 
diff --git a/libc/include/math.h b/libc/include/math.h
index fc6c228..343ab98 100644
--- a/libc/include/math.h
+++ b/libc/include/math.h
@@ -68,10 +68,7 @@
 
 #define isnormal(x) __builtin_isnormal(x)
 
-#define signbit(x) \
-    ((sizeof(x) == sizeof(float)) ? __builtin_signbitf(x) \
-    : (sizeof(x) == sizeof(double)) ? __builtin_signbit(x) \
-    : __builtin_signbitl(x))
+#define signbit(x) __builtin_signbit(x)
 
 double acos(double __x);
 float acosf(float __x);
@@ -308,20 +305,6 @@
 #define islessgreater(x, y) __builtin_islessgreater((x), (y))
 #define isunordered(x, y) __builtin_isunordered((x), (y))
 
-/*
- * https://code.google.com/p/android/issues/detail?id=271629
- * To be fully compliant with C++, we need to not define these (C doesn't
- * specify them either). Exposing these means that isinf and isnan will have a
- * return type of int in C++ rather than bool like they're supposed to be.
- *
- * GNU libstdc++ 4.9 isn't able to handle a standard compliant C library. Its
- * <cmath> will `#undef isnan` from math.h and only adds the function overloads
- * to the std namespace, making it impossible to use both <cmath> (which gets
- * included by a lot of other standard headers) and ::isnan.
- */
-int (isinf)(double __x) __attribute_const__;
-int (isnan)(double __x) __attribute_const__;
-
 /* POSIX extensions. */
 
 extern int signgam;
@@ -362,6 +345,7 @@
 double scalb(double __x, double __exponent);
 double drem(double __x, double __y);
 int finite(double __x) __attribute_const__;
+int isinff(float __x) __attribute_const__;
 int isnanf(float __x) __attribute_const__;
 double gamma_r(double __x, int* _Nonnull __sign);
 double lgamma_r(double __x, int* _Nonnull __sign);
@@ -402,6 +386,8 @@
 #define M_2_SQRTPIl     1.128379167095512573896158903121545172L /* 2/sqrt(pi) */
 #define M_SQRT2l        1.414213562373095048801688724209698079L /* sqrt(2) */
 #define M_SQRT1_2l      0.707106781186547524400844362104849039L /* 1/sqrt(2) */
+int isinfl(long double __x) __attribute_const__;
+int isnanl(long double __x) __attribute_const__;
 #endif
 
 __END_DECLS
diff --git a/libc/malloc_debug/Config.cpp b/libc/malloc_debug/Config.cpp
index 89a7ce7..0d442b4 100644
--- a/libc/malloc_debug/Config.cpp
+++ b/libc/malloc_debug/Config.cpp
@@ -187,6 +187,10 @@
         "record_allocs_file",
         {0, &Config::SetRecordAllocsFile},
     },
+    {
+        "record_allocs_on_exit",
+        {0, &Config::SetRecordAllocsOnExit},
+    },
 
     {
         "verify_pointers",
@@ -401,6 +405,14 @@
   return true;
 }
 
+bool Config::SetRecordAllocsOnExit(const std::string& option, const std::string& value) {
+  if (Config::VerifyValueEmpty(option, value)) {
+    record_allocs_on_exit_ = true;
+    return true;
+  }
+  return false;
+}
+
 bool Config::VerifyValueEmpty(const std::string& option, const std::string& value) {
   if (!value.empty()) {
     // This is not valid.
diff --git a/libc/malloc_debug/Config.h b/libc/malloc_debug/Config.h
index 754970f..8551712 100644
--- a/libc/malloc_debug/Config.h
+++ b/libc/malloc_debug/Config.h
@@ -98,6 +98,7 @@
   int record_allocs_signal() const { return record_allocs_signal_; }
   size_t record_allocs_num_entries() const { return record_allocs_num_entries_; }
   const std::string& record_allocs_file() const { return record_allocs_file_; }
+  bool record_allocs_on_exit() const { return record_allocs_on_exit_; }
 
   int check_unreachable_signal() const { return check_unreachable_signal_; }
 
@@ -139,6 +140,7 @@
 
   bool SetRecordAllocs(const std::string& option, const std::string& value);
   bool SetRecordAllocsFile(const std::string& option, const std::string& value);
+  bool SetRecordAllocsOnExit(const std::string& option, const std::string& value);
 
   bool VerifyValueEmpty(const std::string& option, const std::string& value);
 
@@ -170,6 +172,7 @@
   int record_allocs_signal_ = 0;
   size_t record_allocs_num_entries_ = 0;
   std::string record_allocs_file_;
+  bool record_allocs_on_exit_ = false;
 
   uint64_t options_ = 0;
   uint8_t fill_alloc_value_;
diff --git a/libc/malloc_debug/README.md b/libc/malloc_debug/README.md
index 4e39bed..750a469 100644
--- a/libc/malloc_debug/README.md
+++ b/libc/malloc_debug/README.md
@@ -456,6 +456,19 @@
 
 **NOTE**: This option is not available until the O release of Android.
 
+### record\_allocs\_on\_exit
+This option only has meaning if record\_allocs is set. It indicates that
+when the process terminates, the record file should be created
+automatically.
+
+The only caveat to this option is that when the process terminates,
+the file that will contain the records will be the normal file name
+with **.PID** appended. Where PID is the pid of the process that has
+terminated. This is to avoid cases where a number of processes exit
+at the same time and attempt to write to the same file.
+
+**NOTE**: This option is not available until the V release of Android.
+
 ### verify\_pointers
 Track all live allocations to determine if a pointer is used that does not
 exist. This option is a lightweight way to verify that all
diff --git a/libc/malloc_debug/RecordData.cpp b/libc/malloc_debug/RecordData.cpp
index 8a77170..79e051b 100644
--- a/libc/malloc_debug/RecordData.cpp
+++ b/libc/malloc_debug/RecordData.cpp
@@ -131,17 +131,30 @@
   record_obj_->WriteEntries();
 }
 
+void RecordData::WriteEntriesOnExit() {
+  if (record_obj_ == nullptr) return;
+
+  // Append the current pid to the file name to avoid multiple processes
+  // writing to the same file.
+  std::string file(record_obj_->file());
+  file += "." + std::to_string(getpid());
+  record_obj_->WriteEntries(file);
+}
+
 void RecordData::WriteEntries() {
+  WriteEntries(file_);
+}
+
+void RecordData::WriteEntries(const std::string& file) {
   std::lock_guard<std::mutex> entries_lock(entries_lock_);
   if (cur_index_ == 0) {
     info_log("No alloc entries to write.");
     return;
   }
 
-  int dump_fd =
-      open(dump_file_.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, 0755);
+  int dump_fd = open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, 0755);
   if (dump_fd == -1) {
-    error_log("Cannot create record alloc file %s: %s", dump_file_.c_str(), strerror(errno));
+    error_log("Cannot create record alloc file %s: %s", file.c_str(), strerror(errno));
     return;
   }
 
@@ -179,7 +192,7 @@
 
   entries_.resize(config.record_allocs_num_entries());
   cur_index_ = 0U;
-  dump_file_ = config.record_allocs_file();
+  file_ = config.record_allocs_file();
 
   return true;
 }
diff --git a/libc/malloc_debug/RecordData.h b/libc/malloc_debug/RecordData.h
index a02c956..7efa1f7 100644
--- a/libc/malloc_debug/RecordData.h
+++ b/libc/malloc_debug/RecordData.h
@@ -162,19 +162,23 @@
   void AddEntry(const RecordEntry* entry);
   void AddEntryOnly(const RecordEntry* entry);
 
+  const std::string& file() { return file_; }
   pthread_key_t key() { return key_; }
 
+  static void WriteEntriesOnExit();
+
  private:
   static void WriteData(int, siginfo_t*, void*);
   static RecordData* record_obj_;
 
   void WriteEntries();
+  void WriteEntries(const std::string& file);
 
   std::mutex entries_lock_;
   pthread_key_t key_;
   std::vector<std::unique_ptr<const RecordEntry>> entries_;
   size_t cur_index_;
-  std::string dump_file_;
+  std::string file_;
 
   BIONIC_DISALLOW_COPY_AND_ASSIGN(RecordData);
 };
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index b66b8e2..6d88092 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -451,6 +451,10 @@
     PointerData::LogLeaks();
   }
 
+  if ((g_debug->config().options() & RECORD_ALLOCS) && g_debug->config().record_allocs_on_exit()) {
+    RecordData::WriteEntriesOnExit();
+  }
+
   if ((g_debug->config().options() & BACKTRACE) && g_debug->config().backtrace_dump_on_exit()) {
     debug_dump_heap(android::base::StringPrintf("%s.%d.exit.txt",
                                                 g_debug->config().backtrace_dump_prefix().c_str(),
diff --git a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
index 84c9145..c79d052 100644
--- a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
@@ -571,6 +571,25 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
+TEST_F(MallocDebugConfigTest, record_allocs_on_exit) {
+  ASSERT_TRUE(InitConfig("record_allocs_on_exit")) << getFakeLogPrint();
+  ASSERT_EQ(0U, config->options());
+  ASSERT_TRUE(config->record_allocs_on_exit());
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, record_allocs_on_exit_error) {
+  ASSERT_FALSE(InitConfig("record_allocs_on_exit=something")) << getFakeLogPrint();
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string log_msg(
+      "6 malloc_debug malloc_testing: value set for option 'record_allocs_on_exit' "
+      "which does not take a value\n");
+  ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
 TEST_F(MallocDebugConfigTest, guard_min_error) {
   ASSERT_FALSE(InitConfig("guard=0"));
 
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index 334dada..ef8d235 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -185,6 +185,7 @@
 }
 
 static void VerifyRecords(std::vector<std::string>& expected, std::string& actual) {
+  ASSERT_TRUE(expected.size() != 0);
   size_t offset = 0;
   for (std::string& str : expected) {
     ASSERT_STREQ(str.c_str(), actual.substr(offset, str.size()).c_str());
@@ -1512,7 +1513,7 @@
 
     // Call the exit function manually.
     debug_finalize();
-    exit(0);
+    _exit(0);
   }
   ASSERT_NE(-1, pid);
   ASSERT_EQ(pid, TEMP_FAILURE_RETRY(waitpid(pid, nullptr, 0)));
@@ -1561,7 +1562,7 @@
 
     // Call the exit function manually.
     debug_finalize();
-    exit(0);
+    _exit(0);
   }
   ASSERT_NE(-1, pid);
   ASSERT_EQ(pid, TEMP_FAILURE_RETRY(waitpid(pid, nullptr, 0)));
@@ -1619,7 +1620,7 @@
 
     // Call the exit function manually.
     debug_finalize();
-    exit(0);
+    _exit(0);
   }
   ASSERT_NE(-1, pid);
   ASSERT_EQ(pid, TEMP_FAILURE_RETRY(waitpid(pid, nullptr, 0)));
@@ -2429,6 +2430,33 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
+TEST_F(MallocDebugTest, record_allocs_on_exit) {
+  InitRecordAllocs("record_allocs record_allocs_on_exit");
+
+  // The filename created on exit always appends the pid.
+  // Modify the variable so the file is deleted at the end of the test.
+  record_filename += '.' + std::to_string(getpid());
+
+  std::vector<std::string> expected;
+  void* ptr = debug_malloc(100);
+  expected.push_back(android::base::StringPrintf("%d: malloc %p 100", getpid(), ptr));
+  ptr = debug_malloc(200);
+  expected.push_back(android::base::StringPrintf("%d: malloc %p 200", getpid(), ptr));
+  ptr = debug_malloc(400);
+  expected.push_back(android::base::StringPrintf("%d: malloc %p 400", getpid(), ptr));
+
+  // Call the exit function manually.
+  debug_finalize();
+
+  // Read all of the contents.
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFileToString(record_filename, &actual));
+  VerifyRecords(expected, actual);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
 TEST_F(MallocDebugTest, verify_pointers) {
   Init("verify_pointers");
 
diff --git a/libdl/Android.bp b/libdl/Android.bp
index 95b412b..1bbd902 100644
--- a/libdl/Android.bp
+++ b/libdl/Android.bp
@@ -60,33 +60,14 @@
     native_bridge_supported: true,
     static_ndk_lib: true,
 
-    defaults: ["linux_bionic_supported"],
-
-    // NOTE: --exclude-libs=libgcc.a makes sure that any symbols libdl.so pulls from
-    // libgcc.a are made static to libdl.so.  This in turn ensures that libraries that
-    // a) pull symbols from libgcc.a and b) depend on libdl.so will not rely on libdl.so
-    // to provide those symbols, but will instead pull them from libgcc.a.  Specifically,
-    // we use this property to make sure libc.so has its own copy of the code from
-    // libgcc.a it uses.
-    //
-    // DO NOT REMOVE --exclude-libs!
-
-    ldflags: [
-        "-Wl,--exclude-libs=libgcc.a",
-        "-Wl,--exclude-libs=libgcc_stripped.a",
-        "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a",
-        "-Wl,--exclude-libs=libclang_rt.builtins-aarch64-android.a",
-        "-Wl,--exclude-libs=libclang_rt.builtins-i686-android.a",
-        "-Wl,--exclude-libs=libclang_rt.builtins-riscv64-android.a",
-        "-Wl,--exclude-libs=libclang_rt.builtins-x86_64-android.a",
+    defaults: [
+        "linux_bionic_supported",
+        "bug_24465209_workaround",
     ],
 
-    // for x86, exclude libgcc_eh.a for the same reasons as above
     arch: {
         arm: {
             version_script: ":libdl.arm.map",
-            pack_relocations: false,
-            ldflags: ["-Wl,--hash-style=both"],
         },
         arm64: {
             version_script: ":libdl.arm64.map",
@@ -95,15 +76,9 @@
             version_script: ":libdl.riscv64.map",
         },
         x86: {
-            pack_relocations: false,
-            ldflags: [
-                "-Wl,--exclude-libs=libgcc_eh.a",
-                "-Wl,--hash-style=both",
-            ],
             version_script: ":libdl.x86.map",
         },
         x86_64: {
-            ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
             version_script: ":libdl.x86_64.map",
         },
     },
@@ -162,37 +137,6 @@
     recovery_available: true,
     native_bridge_supported: true,
 
-    // NOTE: --exclude-libs=libgcc.a makes sure that any symbols libdl.so pulls from
-    // libgcc.a are made static to libdl.so.  This in turn ensures that libraries that
-    // a) pull symbols from libgcc.a and b) depend on libdl.so will not rely on libdl.so
-    // to provide those symbols, but will instead pull them from libgcc.a.  Specifically,
-    // we use this property to make sure libc.so has its own copy of the code from
-    // libgcc.a it uses.
-    //
-    // DO NOT REMOVE --exclude-libs!
-
-    ldflags: [
-        "-Wl,--exclude-libs=libgcc.a",
-        "-Wl,--exclude-libs=libgcc_stripped.a",
-        "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a",
-        "-Wl,--exclude-libs=libclang_rt.builtins-aarch64-android.a",
-        "-Wl,--exclude-libs=libclang_rt.builtins-i686-android.a",
-        "-Wl,--exclude-libs=libclang_rt.builtins-riscv64-android.a",
-        "-Wl,--exclude-libs=libclang_rt.builtins-x86_64-android.a",
-    ],
-
-    // for x86, exclude libgcc_eh.a for the same reasons as above
-    arch: {
-        x86: {
-            ldflags: [
-                "-Wl,--exclude-libs=libgcc_eh.a",
-            ],
-        },
-        x86_64: {
-            ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
-        },
-    },
-
     srcs: ["libdl_android.cpp"],
     version_script: "libdl_android.map.txt",
 
diff --git a/libm/Android.bp b/libm/Android.bp
index cc37fb7..00d90a0 100644
--- a/libm/Android.bp
+++ b/libm/Android.bp
@@ -21,7 +21,10 @@
 
 cc_library {
     name: "libm",
-    defaults: ["linux_bionic_supported"],
+    defaults: [
+        "linux_bionic_supported",
+        "bug_24465209_workaround",
+    ],
     ramdisk_available: true,
     vendor_ramdisk_available: true,
     recovery_available: true,
@@ -196,11 +199,6 @@
         "upstream-netbsd/lib/libm/complex/ctanhl.c",
         "upstream-netbsd/lib/libm/complex/ctanl.c",
 
-        // TODO: this comes from from upstream's libc, not libm, but it's an
-        // implementation detail that should have hidden visibility, so it needs
-        // to be in whatever library the math code is in.
-        "digittoint.c",
-
         // Functionality not in the BSDs.
         "significandl.c",
         "fake_long_double.c",
@@ -282,8 +280,6 @@
                 "upstream-freebsd/lib/msun/src/s_ceilf.c",
             ],
             instruction_set: "arm",
-            pack_relocations: false,
-            ldflags: ["-Wl,--hash-style=both"],
             version_script: ":libm.arm.map",
             no_libcrt: true,
             shared: {
@@ -368,11 +364,9 @@
                 "upstream-freebsd/lib/msun/src/s_truncf.c",
             ],
             local_include_dirs: ["i387"],
-            pack_relocations: false,
             // The x86 ABI doesn't include this, which is needed for the
             // roundss/roundsd instructions that we've used since Android M.
             cflags: ["-msse4.1"],
-            ldflags: ["-Wl,--hash-style=both"],
             version_script: ":libm.x86.map",
         },
 
diff --git a/libm/digittoint.c b/libm/digittoint.c
deleted file mode 100644
index 1824788..0000000
--- a/libm/digittoint.c
+++ /dev/null
@@ -1,46 +0,0 @@
-/*-
- * Copyright (c) 2007 David Schultz
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
- *
- * $FreeBSD$
- */
-
-#include <sys/cdefs.h>
-
-/* digittoint is in the FreeBSD C library, but implemented in terms of locale stuff. */
-__LIBC_HIDDEN__ int digittoint(char ch) {
-  int d = ch - '0';
-  if ((unsigned) d < 10) {
-    return d;
-  }
-  d = ch - 'a';
-  if ((unsigned) d < 6) {
-    return d + 10;
-  }
-  d = ch - 'A';
-  if ((unsigned) d < 6) {
-    return d + 10;
-  }
-  return -1;
-}
diff --git a/libm/freebsd-compat.h b/libm/freebsd-compat.h
index 7accc55..a3c7cd4 100644
--- a/libm/freebsd-compat.h
+++ b/libm/freebsd-compat.h
@@ -27,18 +27,12 @@
 #define __strong_reference(sym,aliassym) \
     extern __typeof (sym) aliassym __attribute__ ((__alias__ (#sym)))
 
-#define __warn_references(sym,msg) /* ignored */
-
-// digittoint is in BSD's <ctype.h>, but not ours, so we have a secret
-// implementation in libm. We reuse parts of libm in the NDK's
-// libandroid_support, where it's a static library, so we want all our
-// "hidden" functions start with a double underscore --- being HIDDEN
-// in the ELF sense is not sufficient.
-#define digittoint __libm_digittoint
-int digittoint(char ch);
-
-// Similarly rename _scan_nan.
-#define _scan_nan __libm_scan_nan
+// digittoint is in BSD's <ctype.h> but not ours.
+#include <ctype.h>
+static inline int digittoint(char ch) {
+  if (!isxdigit(ch)) return -1;
+  return isdigit(ch) ? (ch - '0') : (_tolower(ch) - 'a');
+}
 
 // FreeBSD exports these in <math.h> but we don't.
 double cospi(double);
diff --git a/linker/Android.bp b/linker/Android.bp
index 78109e8..da57f7a 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -1,14 +1,3 @@
-// ========================================================
-// linker_wrapper - Linux Bionic (on the host)
-// ========================================================
-
-// This is used for bionic on (host) Linux to bootstrap our linker embedded into
-// a binary.
-//
-// Host bionic binaries do not have a PT_INTERP section, instead this gets
-// embedded as the entry point, and the linker is embedded as ELF sections in
-// each binary. There's a linker script that sets all of that up (generated by
-// extract_linker), and defines the extern symbols used in this file.
 package {
     default_team: "trendy_team_native_tools_libraries",
     default_applicable_licenses: ["bionic_linker_license"],
@@ -25,6 +14,17 @@
     ],
 }
 
+// ========================================================
+// linker_wrapper - Linux Bionic (on the host)
+// ========================================================
+
+// This is used for bionic on (host) Linux to bootstrap our linker embedded into
+// a binary.
+//
+// Host bionic binaries do not have a PT_INTERP section, instead this gets
+// embedded as the entry point, and the linker is embedded as ELF sections in
+// each binary. There's a linker script that sets all of that up (generated by
+// extract_linker), and defines the extern symbols used in this file.
 cc_object {
     name: "linker_wrapper",
     host_supported: true,
@@ -327,8 +327,10 @@
         },
     },
 
+    static_executable: true,
+
     // -shared is used to overwrite the -Bstatic and -static flags triggered by enabling
-    // static_executable. This dynamic linker is actually a shared object linked with static
+    // static_executable. The dynamic linker is actually a shared object linked with static
     // libraries.
     ldflags: [
         "-shared",
@@ -344,18 +346,14 @@
         "-Wl,--pack-dyn-relocs=relr",
     ],
 
-    // we are going to link libc++_static manually because
-    // when stl is not set to "none" build system adds libdl
-    // to the list of static libraries which needs to be
-    // avoided in the case of building loader.
+    // We link libc++_static manually because otherwise the build system will
+    // automatically add libdl to the list of static libraries.
     stl: "none",
 
-    // we don't want crtbegin.o (because we have begin.o), so unset it
-    // just for this module
+    // We don't want crtbegin.o (because we have our own arch/*/begin.o),
+    // so unset it just for this module.
     nocrt: true,
 
-    static_executable: true,
-
     // Insert an extra objcopy step to add prefix to symbols. This is needed to prevent gdb
     // looking up symbols in the linker by mistake.
     prefix_symbols: "__dl_",
@@ -368,26 +366,17 @@
         "liblinker_main",
         "liblinker_malloc",
 
-        // Use a version of libc++ built without exceptions, because accessing EH globals uses
-        // ELF TLS, which is not supported in the loader.
+        // We use a version of libc++ built without exceptions,
+        // because accessing EH globals uses ELF TLS,
+        // which is not supported in the loader.
         "libc++_static_noexcept",
+
         "libc_nomalloc",
         "libc_dynamic_dispatch",
         "libm",
         "libunwind",
     ],
 
-    // Ensure that if the linker needs __gnu_Unwind_Find_exidx, then the linker will have a
-    // definition of the symbol. The linker links against libgcc.a, whose arm32 unwinder has a weak
-    // reference to __gnu_Unwind_Find_exidx, which isn't sufficient to pull in the strong definition
-    // of __gnu_Unwind_Find_exidx from libc. An unresolved weak reference would create a
-    // non-relative dynamic relocation in the linker binary, which complicates linker startup.
-    //
-    // This line should be unnecessary because the linker's dependency on libunwind_llvm.a should
-    // override libgcc.a, but this line provides a simpler guarantee. It can be removed once the
-    // linker stops linking against libgcc.a's arm32 unwinder.
-    whole_static_libs: ["libc_unwind_static"],
-
     system_shared_libs: [],
 
     // Opt out of native_coverage when opting out of system_shared_libs
@@ -474,35 +463,6 @@
 }
 
 cc_library {
-    // NOTE: --exclude-libs=libgcc.a makes sure that any symbols ld-android.so pulls from
-    // libgcc.a are made static to ld-android.so.  This in turn ensures that libraries that
-    // a) pull symbols from libgcc.a and b) depend on ld-android.so will not rely on ld-android.so
-    // to provide those symbols, but will instead pull them from libgcc.a.  Specifically,
-    // we use this property to make sure libc.so has its own copy of the code from
-    // libgcc.a it uses.
-    //
-    // DO NOT REMOVE --exclude-libs!
-
-    ldflags: [
-        "-Wl,--exclude-libs=libgcc.a",
-        "-Wl,--exclude-libs=libgcc_stripped.a",
-        "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a",
-        "-Wl,--exclude-libs=libclang_rt.builtins-aarch64-android.a",
-        "-Wl,--exclude-libs=libclang_rt.builtins-riscv64-android.a",
-        "-Wl,--exclude-libs=libclang_rt.builtins-i686-android.a",
-        "-Wl,--exclude-libs=libclang_rt.builtins-x86_64-android.a",
-    ],
-
-    // for x86, exclude libgcc_eh.a for the same reasons as above
-    arch: {
-        x86: {
-            ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
-        },
-        x86_64: {
-            ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
-        },
-    },
-
     srcs: ["ld_android.cpp"],
     cflags: [
         "-Wall",
diff --git a/tests/math_test.cpp b/tests/math_test.cpp
index 493f3af..273ef97 100644
--- a/tests/math_test.cpp
+++ b/tests/math_test.cpp
@@ -17,36 +17,37 @@
 #define _GNU_SOURCE 1
 #include <math.h>
 
-// This include (and the associated definition of __test_capture_signbit)
-// must be placed before any files that include <cmath> (gtest.h in this case).
+// <math.h> is required to define type-generic macros: fpclassify, signbit,
+// isfinite, isinf, isnan, isnormal, isgreater, isgreaterequal, isless,
+// islessequal, islessgreater, and isunordered.
 //
-// <math.h> is required to define generic macros signbit, isfinite and
-// several other such functions.
+// <cmath> is required to #undef these macros and make equivalent sets of
+// _overloaded_ functions available in namespace std. So the isnan() macro,
+// for example, is replaced by std::isnan(float), std::isnan(double),
+// and std::isnan(long double).
 //
-// <cmath> is required to undef declarations of these macros in the global
-// namespace and make equivalent functions available in namespace std. Our
-// stlport implementation does this only for signbit, isfinite, isinf and
-// isnan.
-//
-// NOTE: We don't write our test using std::signbit because we want to be
-// sure that we're testing the bionic version of signbit. The C++ libraries
-// are free to reimplement signbit or delegate to compiler builtins if they
-// please.
+// We're trying to test the bionic macros rather than whatever libc++'s
+// implementation happens to be, so we #include <math.h> and "capture" the
+// macros in our own _template_ functions in the global namespace before
+// we #include any files that include <cmath>, such as <gtest.h>.
 
-namespace {
-template<typename T> inline int test_capture_signbit(const T in) {
-  return signbit(in);
-}
-template<typename T> inline int test_capture_isfinite(const T in) {
-  return isfinite(in);
-}
-template<typename T> inline int test_capture_isnan(const T in) {
-  return isnan(in);
-}
-template<typename T> inline int test_capture_isinf(const T in) {
-  return isinf(in);
-}
-}
+#define capture_generic_macro(capture_function_name, generic_macro_name) \
+  template <typename T> inline int capture_function_name(const T in) { \
+    return generic_macro_name(in); \
+  }
+
+capture_generic_macro(test_capture_fpclassify, fpclassify)
+capture_generic_macro(test_capture_signbit, signbit)
+capture_generic_macro(test_capture_isfinite, isfinite)
+capture_generic_macro(test_capture_isinf, isinf)
+capture_generic_macro(test_capture_isnan, isnan)
+capture_generic_macro(test_capture_isnormal, isnormal)
+capture_generic_macro(test_capture_isgreater, isgreater)
+capture_generic_macro(test_capture_isgreaterequal, isgreaterequal)
+capture_generic_macro(test_capture_isless, isless)
+capture_generic_macro(test_capture_islessequal, islessequal)
+capture_generic_macro(test_capture_islessgreater, islessgreater)
+capture_generic_macro(test_capture_isunordered, isunordered)
 
 #include "math_data_test.h"
 
@@ -60,6 +61,22 @@
 
 #include <android-base/scopeguard.h>
 
+// Now we've included all the headers we need, we can redefine the generic
+// function-like macros to point to the bionic <math.h> versions we captured
+// earlier.
+#define fpclassify test_capture_fpclassify
+#define signbit test_capture_signbit
+#define isfinite test_capture_isfinite
+#define isinf test_capture_isinf
+#define isnan test_capture_isnan
+#define isnormal test_capture_isnormal
+#define isgreater test_capture_isgreater
+#define isgreaterequal test_capture_isgreaterequal
+#define isless test_capture_isless
+#define islessequal test_capture_islessequal
+#define islessgreater test_capture_islessgreater
+#define isunordered test_capture_isunordered
+
 static float float_subnormal() {
   union {
     float f;
@@ -124,36 +141,36 @@
 }
 
 TEST(math_h, isfinite) {
-  ASSERT_TRUE(test_capture_isfinite(123.0f));
-  ASSERT_TRUE(test_capture_isfinite(123.0));
-  ASSERT_TRUE(test_capture_isfinite(123.0L));
-  ASSERT_FALSE(test_capture_isfinite(HUGE_VALF));
-  ASSERT_FALSE(test_capture_isfinite(-HUGE_VALF));
-  ASSERT_FALSE(test_capture_isfinite(HUGE_VAL));
-  ASSERT_FALSE(test_capture_isfinite(-HUGE_VAL));
-  ASSERT_FALSE(test_capture_isfinite(HUGE_VALL));
-  ASSERT_FALSE(test_capture_isfinite(-HUGE_VALL));
+  ASSERT_TRUE(isfinite(123.0f));
+  ASSERT_TRUE(isfinite(123.0));
+  ASSERT_TRUE(isfinite(123.0L));
+  ASSERT_FALSE(isfinite(HUGE_VALF));
+  ASSERT_FALSE(isfinite(-HUGE_VALF));
+  ASSERT_FALSE(isfinite(HUGE_VAL));
+  ASSERT_FALSE(isfinite(-HUGE_VAL));
+  ASSERT_FALSE(isfinite(HUGE_VALL));
+  ASSERT_FALSE(isfinite(-HUGE_VALL));
 }
 
 TEST(math_h, isinf) {
-  ASSERT_FALSE(test_capture_isinf(123.0f));
-  ASSERT_FALSE(test_capture_isinf(123.0));
-  ASSERT_FALSE(test_capture_isinf(123.0L));
-  ASSERT_TRUE(test_capture_isinf(HUGE_VALF));
-  ASSERT_TRUE(test_capture_isinf(-HUGE_VALF));
-  ASSERT_TRUE(test_capture_isinf(HUGE_VAL));
-  ASSERT_TRUE(test_capture_isinf(-HUGE_VAL));
-  ASSERT_TRUE(test_capture_isinf(HUGE_VALL));
-  ASSERT_TRUE(test_capture_isinf(-HUGE_VALL));
+  ASSERT_FALSE(isinf(123.0f));
+  ASSERT_FALSE(isinf(123.0));
+  ASSERT_FALSE(isinf(123.0L));
+  ASSERT_TRUE(isinf(HUGE_VALF));
+  ASSERT_TRUE(isinf(-HUGE_VALF));
+  ASSERT_TRUE(isinf(HUGE_VAL));
+  ASSERT_TRUE(isinf(-HUGE_VAL));
+  ASSERT_TRUE(isinf(HUGE_VALL));
+  ASSERT_TRUE(isinf(-HUGE_VALL));
 }
 
 TEST(math_h, isnan) {
-  ASSERT_FALSE(test_capture_isnan(123.0f));
-  ASSERT_FALSE(test_capture_isnan(123.0));
-  ASSERT_FALSE(test_capture_isnan(123.0L));
-  ASSERT_TRUE(test_capture_isnan(nanf("")));
-  ASSERT_TRUE(test_capture_isnan(nan("")));
-  ASSERT_TRUE(test_capture_isnan(nanl("")));
+  ASSERT_FALSE(isnan(123.0f));
+  ASSERT_FALSE(isnan(123.0));
+  ASSERT_FALSE(isnan(123.0L));
+  ASSERT_TRUE(isnan(nanf("")));
+  ASSERT_TRUE(isnan(nan("")));
+  ASSERT_TRUE(isnan(nanl("")));
 }
 
 TEST(math_h, isnormal) {
@@ -167,17 +184,17 @@
 
 // TODO: isgreater, isgreaterequals, isless, islessequal, islessgreater, isunordered
 TEST(math_h, signbit) {
-  ASSERT_EQ(0, test_capture_signbit(0.0f));
-  ASSERT_EQ(0, test_capture_signbit(0.0));
-  ASSERT_EQ(0, test_capture_signbit(0.0L));
+  ASSERT_EQ(0, signbit(0.0f));
+  ASSERT_EQ(0, signbit(0.0));
+  ASSERT_EQ(0, signbit(0.0L));
 
-  ASSERT_EQ(0, test_capture_signbit(1.0f));
-  ASSERT_EQ(0, test_capture_signbit(1.0));
-  ASSERT_EQ(0, test_capture_signbit(1.0L));
+  ASSERT_EQ(0, signbit(1.0f));
+  ASSERT_EQ(0, signbit(1.0));
+  ASSERT_EQ(0, signbit(1.0L));
 
-  ASSERT_NE(0, test_capture_signbit(-1.0f));
-  ASSERT_NE(0, test_capture_signbit(-1.0));
-  ASSERT_NE(0, test_capture_signbit(-1.0L));
+  ASSERT_NE(0, signbit(-1.0f));
+  ASSERT_NE(0, signbit(-1.0));
+  ASSERT_NE(0, signbit(-1.0L));
 }
 
 // Historical BSD cruft that isn't exposed in <math.h> any more.
@@ -309,9 +326,7 @@
 // Historical BSD cruft that isn't exposed in <math.h> any more.
 extern "C" int __isinf(double);
 extern "C" int __isinff(float);
-extern "C" int isinff(float);
 extern "C" int __isinfl(long double);
-extern "C" int isinfl(long double);
 
 TEST(math_h, __isinf) {
 #if defined(ANDROID_HOST_MUSL)
@@ -367,9 +382,7 @@
 // Historical BSD cruft that isn't exposed in <math.h> any more.
 extern "C" int __isnan(double);
 extern "C" int __isnanf(float);
-extern "C" int isnanf(float);
 extern "C" int __isnanl(long double);
-extern "C" int isnanl(long double);
 
 TEST(math_h, __isnan) {
 #if defined(ANDROID_HOST_MUSL)