Merge "Fix setfsgid()/setfsuid() for LP32."
diff --git a/libc/Android.bp b/libc/Android.bp
index fb4825d..67da126 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1444,6 +1444,7 @@
 
     whole_static_libs: [
         "gwp_asan",
+        "gwp_asan_crash_handler",
         "libarm-optimized-routines-string",
         "libasync_safe",
         "libc_bionic_ndk",
@@ -1686,6 +1687,7 @@
         cflags: ["-DLIBC_STATIC"],
         whole_static_libs: [
             "gwp_asan",
+            "gwp_asan_crash_handler",
             "libc_init_static",
             "libc_common_static",
             "libc_unwind_static",
@@ -1695,6 +1697,7 @@
         srcs: [ ":libc_sources_shared" ],
         whole_static_libs: [
             "gwp_asan",
+            "gwp_asan_crash_handler",
             "libc_init_dynamic",
             "libc_common_shared",
             "libunwind-exported",
diff --git a/libc/bionic/gwp_asan_wrappers.cpp b/libc/bionic/gwp_asan_wrappers.cpp
index fc59c88..251633d 100644
--- a/libc/bionic/gwp_asan_wrappers.cpp
+++ b/libc/bionic/gwp_asan_wrappers.cpp
@@ -36,6 +36,7 @@
 #include <string.h>
 #include <sys/types.h>
 
+#include "gwp_asan/crash_handler.h"
 #include "gwp_asan/guarded_pool_allocator.h"
 #include "gwp_asan/options.h"
 #include "gwp_asan_wrappers.h"
@@ -189,6 +190,7 @@
 }
 
 bool GwpAsanInitialized = false;
+bool GwpAsanRecoverable = false;
 
 // The probability (1 / SampleRate) that an allocation gets chosen to be put
 // into the special GWP-ASan pool.
@@ -222,8 +224,32 @@
 static const char* kMaxAllocsTargetedSyspropPrefix = "libc.debug.gwp_asan.max_allocs.";
 static const char* kMaxAllocsEnvVar = "GWP_ASAN_MAX_ALLOCS";
 
+static const char* kRecoverableSystemSysprop = "libc.debug.gwp_asan.recoverable.system_default";
+static const char* kRecoverableAppSysprop = "libc.debug.gwp_asan.recoverable.app_default";
+static const char* kRecoverableTargetedSyspropPrefix = "libc.debug.gwp_asan.recoverable.";
+static const char* kRecoverableEnvVar = "GWP_ASAN_RECOVERABLE";
+
 static const char kPersistPrefix[] = "persist.";
 
+bool NeedsGwpAsanRecovery(void* fault_ptr) {
+  fault_ptr = untag_address(fault_ptr);
+  return GwpAsanInitialized && GwpAsanRecoverable &&
+         __gwp_asan_error_is_mine(GuardedAlloc.getAllocatorState(),
+                                  reinterpret_cast<uintptr_t>(fault_ptr));
+}
+
+void GwpAsanPreCrashHandler(void* fault_ptr) {
+  fault_ptr = untag_address(fault_ptr);
+  if (!NeedsGwpAsanRecovery(fault_ptr)) return;
+  GuardedAlloc.preCrashReport(fault_ptr);
+}
+
+void GwpAsanPostCrashHandler(void* fault_ptr) {
+  fault_ptr = untag_address(fault_ptr);
+  if (!NeedsGwpAsanRecovery(fault_ptr)) return;
+  GuardedAlloc.postCrashReportRecoverableOnly(fault_ptr);
+}
+
 void SetDefaultGwpAsanOptions(Options* options, unsigned* process_sample_rate,
                               const android_mallopt_gwp_asan_options_t& mallopt_options) {
   options->Enabled = true;
@@ -236,14 +262,17 @@
   *process_sample_rate = 1;
   if (mallopt_options.desire == Action::TURN_ON_WITH_SAMPLING) {
     *process_sample_rate = kDefaultProcessSampling;
+  } else if (mallopt_options.desire == Action::TURN_ON_FOR_APP_SAMPLED_NON_CRASHING) {
+    *process_sample_rate = kDefaultProcessSampling;
+    options->Recoverable = true;
+    GwpAsanRecoverable = true;
   }
 }
 
-bool GetGwpAsanOption(unsigned long long* result,
-                      const android_mallopt_gwp_asan_options_t& mallopt_options,
-                      const char* system_sysprop, const char* app_sysprop,
-                      const char* targeted_sysprop_prefix, const char* env_var,
-                      const char* descriptive_name) {
+bool GetGwpAsanOptionImpl(char* value_out,
+                          const android_mallopt_gwp_asan_options_t& mallopt_options,
+                          const char* system_sysprop, const char* app_sysprop,
+                          const char* targeted_sysprop_prefix, const char* env_var) {
   const char* basename = "";
   if (mallopt_options.program_name) basename = __gnu_basename(mallopt_options.program_name);
 
@@ -278,17 +307,25 @@
     sysprop_names[3] = persist_default_sysprop;
   }
 
-  char settings_buf[PROP_VALUE_MAX];
-  if (!get_config_from_env_or_sysprops(env_var, sysprop_names, arraysize(sysprop_names),
-                                       settings_buf, PROP_VALUE_MAX)) {
+  return get_config_from_env_or_sysprops(env_var, sysprop_names, arraysize(sysprop_names),
+                                         value_out, PROP_VALUE_MAX);
+}
+
+bool GetGwpAsanIntegerOption(unsigned long long* result,
+                             const android_mallopt_gwp_asan_options_t& mallopt_options,
+                             const char* system_sysprop, const char* app_sysprop,
+                             const char* targeted_sysprop_prefix, const char* env_var,
+                             const char* descriptive_name) {
+  char buffer[PROP_VALUE_MAX];
+  if (!GetGwpAsanOptionImpl(buffer, mallopt_options, system_sysprop, app_sysprop,
+                            targeted_sysprop_prefix, env_var)) {
     return false;
   }
-
   char* end;
-  unsigned long long value = strtoull(settings_buf, &end, 10);
+  unsigned long long value = strtoull(buffer, &end, 10);
   if (value == ULLONG_MAX || *end != '\0') {
     warning_log("Invalid GWP-ASan %s: \"%s\". Using default value instead.", descriptive_name,
-                settings_buf);
+                buffer);
     return false;
   }
 
@@ -296,6 +333,33 @@
   return true;
 }
 
+bool GetGwpAsanBoolOption(bool* result, const android_mallopt_gwp_asan_options_t& mallopt_options,
+                          const char* system_sysprop, const char* app_sysprop,
+                          const char* targeted_sysprop_prefix, const char* env_var,
+                          const char* descriptive_name) {
+  char buffer[PROP_VALUE_MAX] = {};
+  if (!GetGwpAsanOptionImpl(buffer, mallopt_options, system_sysprop, app_sysprop,
+                            targeted_sysprop_prefix, env_var)) {
+    return false;
+  }
+
+  if (strncasecmp(buffer, "1", PROP_VALUE_MAX) == 0 ||
+      strncasecmp(buffer, "true", PROP_VALUE_MAX) == 0) {
+    *result = true;
+    return true;
+  } else if (strncasecmp(buffer, "0", PROP_VALUE_MAX) == 0 ||
+             strncasecmp(buffer, "false", PROP_VALUE_MAX) == 0) {
+    *result = false;
+    return true;
+  }
+
+  warning_log(
+      "Invalid GWP-ASan %s: \"%s\". Using default value \"%s\" instead. Valid values are \"true\", "
+      "\"1\", \"false\", or \"0\".",
+      descriptive_name, buffer, *result ? "true" : "false");
+  return false;
+}
+
 // Initialize the GWP-ASan options structure in *options, taking into account whether someone has
 // asked for specific GWP-ASan settings. The order of priority is:
 //  1. Environment variables.
@@ -310,22 +374,23 @@
   bool had_overrides = false;
 
   unsigned long long buf;
-  if (GetGwpAsanOption(&buf, mallopt_options, kSampleRateSystemSysprop, kSampleRateAppSysprop,
-                       kSampleRateTargetedSyspropPrefix, kSampleRateEnvVar, "sample rate")) {
+  if (GetGwpAsanIntegerOption(&buf, mallopt_options, kSampleRateSystemSysprop,
+                              kSampleRateAppSysprop, kSampleRateTargetedSyspropPrefix,
+                              kSampleRateEnvVar, "sample rate")) {
     options->SampleRate = buf;
     had_overrides = true;
   }
 
-  if (GetGwpAsanOption(&buf, mallopt_options, kProcessSamplingSystemSysprop,
-                       kProcessSamplingAppSysprop, kProcessSamplingTargetedSyspropPrefix,
-                       kProcessSamplingEnvVar, "process sampling rate")) {
+  if (GetGwpAsanIntegerOption(&buf, mallopt_options, kProcessSamplingSystemSysprop,
+                              kProcessSamplingAppSysprop, kProcessSamplingTargetedSyspropPrefix,
+                              kProcessSamplingEnvVar, "process sampling rate")) {
     *process_sample_rate = buf;
     had_overrides = true;
   }
 
-  if (GetGwpAsanOption(&buf, mallopt_options, kMaxAllocsSystemSysprop, kMaxAllocsAppSysprop,
-                       kMaxAllocsTargetedSyspropPrefix, kMaxAllocsEnvVar,
-                       "maximum simultaneous allocations")) {
+  if (GetGwpAsanIntegerOption(&buf, mallopt_options, kMaxAllocsSystemSysprop, kMaxAllocsAppSysprop,
+                              kMaxAllocsTargetedSyspropPrefix, kMaxAllocsEnvVar,
+                              "maximum simultaneous allocations")) {
     options->MaxSimultaneousAllocations = buf;
     had_overrides = true;
   } else if (had_overrides) {
@@ -337,6 +402,16 @@
     options->MaxSimultaneousAllocations =
         /* default */ kDefaultMaxAllocs / frequency_multiplier;
   }
+
+  bool recoverable = false;
+  if (GetGwpAsanBoolOption(&recoverable, mallopt_options, kRecoverableSystemSysprop,
+                           kRecoverableAppSysprop, kRecoverableTargetedSyspropPrefix,
+                           kRecoverableEnvVar, "recoverable")) {
+    options->Recoverable = recoverable;
+    GwpAsanRecoverable = recoverable;
+    had_overrides = true;
+  }
+
   return had_overrides;
 }
 
@@ -396,6 +471,9 @@
 
   __libc_shared_globals()->gwp_asan_state = GuardedAlloc.getAllocatorState();
   __libc_shared_globals()->gwp_asan_metadata = GuardedAlloc.getMetadataRegion();
+  __libc_shared_globals()->debuggerd_needs_gwp_asan_recovery = NeedsGwpAsanRecovery;
+  __libc_shared_globals()->debuggerd_gwp_asan_pre_crash_report = GwpAsanPreCrashHandler;
+  __libc_shared_globals()->debuggerd_gwp_asan_post_crash_report = GwpAsanPostCrashHandler;
 
   return true;
 }
diff --git a/libc/include/sys/quota.h b/libc/include/sys/quota.h
index f8faee7..79c653d 100644
--- a/libc/include/sys/quota.h
+++ b/libc/include/sys/quota.h
@@ -51,6 +51,6 @@
  *
  * Available since API level 26.
  */
-int quotactl(int __cmd, const char* __special, int __id, char* __addr) __INTRODUCED_IN(26);
+int quotactl(int __cmd, const char* _Nullable __special, int __id, char* __BIONIC_COMPLICATED_NULLNESS __addr) __INTRODUCED_IN(26);
 
 __END_DECLS
diff --git a/libc/include/sys/sysinfo.h b/libc/include/sys/sysinfo.h
index 4ecf986..cae5c49 100644
--- a/libc/include/sys/sysinfo.h
+++ b/libc/include/sys/sysinfo.h
@@ -43,7 +43,7 @@
  *
  * Returns 0 on success, and returns -1 and sets `errno` on failure.
  */
-int sysinfo(struct sysinfo* __info);
+int sysinfo(struct sysinfo* _Nonnull __info);
 
 /**
  * [get_nprocs_conf(3)](http://man7.org/linux/man-pages/man3/get_nprocs_conf.3.html) returns
diff --git a/libc/platform/bionic/malloc.h b/libc/platform/bionic/malloc.h
index ecc8743..3c290fc 100644
--- a/libc/platform/bionic/malloc.h
+++ b/libc/platform/bionic/malloc.h
@@ -121,15 +121,28 @@
   // apply to system apps. They use the "libc.debug.gwp_asan.*.system_default"
   // sysprops.
   enum Action {
-    // The app has opted-in to GWP-ASan, and should always have it enabled. This
-    // should only be used by apps.
+    // Enable GWP-ASan. This is used by apps that have `gwpAsanMode=always` in
+    // the manifest.
     TURN_ON_FOR_APP,
-    // System processes apps have GWP-ASan enabled by default, but use the
-    // process sampling method.
+    // Enable GWP-ASan, but only a small percentage of the time. This is used by
+    // system processes and system apps, and we use a lottery to determine which
+    // processes have GWP-ASan enabled. This allows us to mitigate system-wide
+    // memory overhead concerns, as each GWP-ASan enabled process uses ~70KiB of
+    // extra memory.
     TURN_ON_WITH_SAMPLING,
-    // Non-system apps don't have GWP-ASan by default.
+    // Don't enable GWP-ASan, unless overwritten by a system property or
+    // environment variable. This is used by apps that have `gwpAsanMode=never`
+    // in the manifest. Prior to Android 14, this also was used by non-system
+    // apps that didn't specify a `gwpAsanMode` in their manifest.
     DONT_TURN_ON_UNLESS_OVERRIDDEN,
-    // Note: GWP-ASan cannot be disabled once it's been enabled.
+    // Enable GWP-ASan, but only a small percentage of the time, and enable it
+    // in the non-crashing ("recoverable") mode. In Android 14, this is used by
+    // apps that don't specify `gwpAsanMode` (or use `gwpAsanMode=default`) in
+    // their manifest. GWP-ASan will detect heap memory safety bugs in this
+    // mode, and bug reports will be created by debuggerd, however the process
+    // will recover and continue to function as if the memory safety bug wasn't
+    // detected.
+    TURN_ON_FOR_APP_SAMPLED_NON_CRASHING,
   };
 
   Action desire = DONT_TURN_ON_UNLESS_OVERRIDDEN;
diff --git a/libc/private/bionic_globals.h b/libc/private/bionic_globals.h
index 520e50f..c375cc4 100644
--- a/libc/private/bionic_globals.h
+++ b/libc/private/bionic_globals.h
@@ -107,6 +107,9 @@
 
   const gwp_asan::AllocatorState* gwp_asan_state = nullptr;
   const gwp_asan::AllocationMetadata* gwp_asan_metadata = nullptr;
+  bool (*debuggerd_needs_gwp_asan_recovery)(void* fault_addr) = nullptr;
+  void (*debuggerd_gwp_asan_pre_crash_report)(void* fault_addr) = nullptr;
+  void (*debuggerd_gwp_asan_post_crash_report)(void* fault_addr) = nullptr;
 
   const char* scudo_stack_depot = nullptr;
   const char* scudo_region_info = nullptr;
diff --git a/libdl/libdl.cpp b/libdl/libdl.cpp
index a56a5ab..20f08d9 100644
--- a/libdl/libdl.cpp
+++ b/libdl/libdl.cpp
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
+#include <android/dlext.h>
 #include <dlfcn.h>
 #include <link.h>
+#include <signal.h>
 #include <stdlib.h>
-#include <android/dlext.h>
 
 // These functions are exported by the loader
 // TODO(dimitry): replace these with reference to libc.so
@@ -72,6 +73,9 @@
 __attribute__((__weak__, visibility("default")))
 int __loader_android_get_application_target_sdk_version();
 
+__attribute__((__weak__, visibility("default"))) bool __loader_android_handle_signal(
+    int signal_number, siginfo_t* info, void* context);
+
 // Proxy calls to bionic loader
 __attribute__((__weak__))
 void android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) {
@@ -138,4 +142,14 @@
   return __loader_android_get_application_target_sdk_version();
 }
 
+// Returns true if this function handled the signal, false if the caller should handle the signal
+// itself. This function returns true if the sigchain handler should immediately return, which
+// happens when the signal came from GWP-ASan, and we've dumped a debuggerd report and patched up
+// the GWP-ASan allocator to recover from the fault, and regular execution of the program can
+// continue.
+__attribute__((__weak__)) bool android_handle_signal(int signal_number, siginfo_t* info,
+                                                     void* context) {
+  return __loader_android_handle_signal(signal_number, info, context);
+}
+
 } // extern "C"
diff --git a/libdl/libdl.map.txt b/libdl/libdl.map.txt
index 473bdf2..043ec53 100644
--- a/libdl/libdl.map.txt
+++ b/libdl/libdl.map.txt
@@ -45,4 +45,5 @@
   global:
     android_get_LD_LIBRARY_PATH;
     __cfi_init;
+    android_handle_signal;
 } LIBC_OMR1;
diff --git a/libm/Android.bp b/libm/Android.bp
index b6c7b6a..f06bd4f 100644
--- a/libm/Android.bp
+++ b/libm/Android.bp
@@ -285,11 +285,10 @@
         arm: {
             srcs: [
                 "arm/fenv.c",
-                "arm/floor.S",
-                "arm/sqrt.S",
             ],
             exclude_srcs: [
                 "upstream-freebsd/lib/msun/src/s_floor.c",
+                "upstream-freebsd/lib/msun/src/s_floorf.c",
             ],
             instruction_set: "arm",
             pack_relocations: false,
diff --git a/libm/NOTICE b/libm/NOTICE
index bce49ad..61dd125 100644
--- a/libm/NOTICE
+++ b/libm/NOTICE
@@ -1148,37 +1148,6 @@
 
 -------------------------------------------------------------------
 
-Copyright (c) 2013-2014, NVIDIA Corporation.  All rights reserved.
-Johnny Qiu <joqiu@nvidia.com>
-Shu Zhang <chazhang@nvidia.com>
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-    * Redistributions of source code must retain the above copyright
-      notice, this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above
-      copyright notice, this list of conditions and the following
-      disclaimer in the documentation and/or other materials provided
-      with the distribution.
-    * Neither the name of The Linux Foundation nor the names of its
-      contributors may be used to endorse or promote products derived
-      from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
-WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
-ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
--------------------------------------------------------------------
-
 Copyright (c) 2014, Intel Corporation
 All rights reserved.
 
diff --git a/libm/arm/floor.S b/libm/arm/floor.S
deleted file mode 100644
index 3af8f76..0000000
--- a/libm/arm/floor.S
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (c) 2013-2014, NVIDIA Corporation.  All rights reserved.
- * Johnny Qiu <joqiu@nvidia.com>
- * Shu Zhang <chazhang@nvidia.com>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- *       copyright notice, this list of conditions and the following
- *       disclaimer in the documentation and/or other materials provided
- *       with the distribution.
- *     * Neither the name of The Linux Foundation nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
- * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <float.h>
-#include <private/bionic_asm.h>
-
-ENTRY(floor)    /* x in r0, r1 */
-
-        and             r3, r1, #0x80000000     /* sign(x) */
-        bic             r1, r1, #0x80000000     /* x = abs(x) */
-
-        /* extract exp of x */
-        lsr             r2, r1, #20
-        sub             r2, r2, #0x3fc
-        subs            r2, r2, #0x3            /* r2 <- exp */
-
-        /* |x| < 1.0? */
-        blt             .Lx_lt_one
-
-        /* x < 0? */
-        cmp             r3, #0
-        bne             .Lclr_frac_neg
-
-        /* |x| <= 2^20? */
-        cmp             r2, #20
-        ble             .Lclr_frac_r1
-
-        /* |x| < 2^52? */
-        cmp             r2, #52
-        blt             .Lclr_frac_r0
-
-        /* return x */
-        bx              lr
-
-.Lclr_frac_r1:
-        rsb             r2, r2, #20
-        lsr             r1, r1, r2
-        lsl             r1, r1, r2
-        mov             r0, #0
-        bx              lr
-
-.Lclr_frac_r0:
-        rsb             r2, r2, #52
-        lsr             r0, r0, r2
-        lsl             r0, r0, r2
-        bx              lr
-
-.Lclr_frac_neg:
-        /* |x| <= 2^20? */
-        cmp             r2, #20
-        ble             .Lclr_frac_r1_neg
-
-        /* |x| < 2^52? */
-        cmp             r2, #52
-        blt             .Lclr_frac_r0_neg
-
-        /* return x */
-        orr             r1, r1, #0x80000000
-        bx              lr
-
-.Lclr_frac_r1_neg:
-        rsb             r2, r2, #20
-        mov             r3, #1
-        lsl             r3, r3, r2
-        sub             r3, r3, #1
-        and             r3, r1, r3
-        orr             r3, r3, r0
-        lsr             r1, r1, r2
-        lsl             r1, r1, r2
-        mov             r0, #0
-        b               .Lreturn_x_neg
-
-.Lclr_frac_r0_neg:
-        rsb             r2, r2, #52
-        mov             r3, #1
-        lsl             r3, r3, r2
-        sub             r3, r3, #1
-        and             r3, r0, r3
-        lsr             r0, r0, r2
-        lsl             r0, r0, r2
-        b               .Lreturn_x_neg
-
-.Lx_lt_one:
-        /* x == +-0? */
-        cmp             r0, #0
-        cmpeq           r1, #0
-        orreq           r1, r1, r3
-        bxeq            lr
-
-        /* (x > 0) ? 0 : -1 */
-        mov             r1, #0x00100000
-        mov             r0, #0
-        cmp             r3, #0
-        movne           r1, #0xc0000000
-        sub             r1, r1, #0x00100000
-        bx              lr
-
-.Lreturn_x_neg:
-        cmp             r3, #0
-        orr             r1, r1, #0x80000000
-        bxeq            lr
-
-        vmov            d16, r0, r1
-        vmov.f64        d18, #1.0
-        vsub.f64        d16, d16, d18
-        vmov            r0, r1, d16
-        bx              lr
-
-END(floor)
-
-ALIAS_SYMBOL(floorl, floor);
diff --git a/libm/arm/sqrt.S b/libm/arm/sqrt.S
deleted file mode 100644
index f2981f4..0000000
--- a/libm/arm/sqrt.S
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2013-2014, NVIDIA Corporation.  All rights reserved.
- * Johnny Qiu <joqiu@nvidia.com>
- * Shu Zhang <chazhang@nvidia.com>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- *       copyright notice, this list of conditions and the following
- *       disclaimer in the documentation and/or other materials provided
- *       with the distribution.
- *     * Neither the name of The Linux Foundation nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
- * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <float.h>
-#include <private/bionic_asm.h>
-
-ENTRY(sqrt)
-    vmov.f64    d0, r0, r1
-    vsqrt.f64   d0, d0
-    vmov.f64    r0, r1, d0
-    bx          lr
-END(sqrt)
-
-ENTRY(sqrtf)
-    vmov.f32    s0, r0
-    vsqrt.f32   s0, s0
-    vmov.f32    r0, s0
-    bx          lr
-END(sqrtf)
-
-ALIAS_SYMBOL(sqrtl, sqrt);
diff --git a/libm/builtins.cpp b/libm/builtins.cpp
index 256436e..99758ff 100644
--- a/libm/builtins.cpp
+++ b/libm/builtins.cpp
@@ -32,9 +32,12 @@
 float copysignf(float x, float y) { return __builtin_copysignf(x, y); }
 #endif
 
-#if defined(__aarch64__)
+#if defined(__arm__) || defined(__aarch64__)
 float floorf(float x) { return __builtin_floorf(x); }
 double floor(double x) { return __builtin_floor(x); }
+#if defined(__ILP32__)
+__weak_reference(floor, floorl);
+#endif
 #endif
 
 #if defined(__aarch64__) || defined(__riscv)
@@ -66,9 +69,12 @@
 double round(double x) { return __builtin_round(x); }
 #endif
 
-#if defined(__aarch64__) || defined(__riscv)
+#if defined(__arm__) || defined(__aarch64__) || defined(__riscv)
 float sqrtf(float x) { return __builtin_sqrtf(x); }
 double sqrt(double x) { return __builtin_sqrt(x); }
+#if defined(__ILP32__)
+__weak_reference(sqrt, sqrtl);
+#endif
 #endif
 
 #if defined(__aarch64__)
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index af05027..a3f5246 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -28,8 +28,9 @@
 
 #include "linker.h"
 #include "linker_cfi.h"
-#include "linker_globals.h"
+#include "linker_debuggerd.h"
 #include "linker_dlwarning.h"
+#include "linker_globals.h"
 
 #include <link.h>
 #include <pthread.h>
@@ -92,6 +93,8 @@
 #if defined(__arm__)
 _Unwind_Ptr __loader_dl_unwind_find_exidx(_Unwind_Ptr pc, int* pcount) __LINKER_PUBLIC__;
 #endif
+bool __loader_android_handle_signal(int signal_number, siginfo_t* info,
+                                    void* context) __LINKER_PUBLIC__;
 }
 
 static pthread_mutex_t g_dl_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
@@ -302,6 +305,10 @@
   return __libc_shared_globals();
 }
 
+bool __loader_android_handle_signal(int signal_number, siginfo_t* info, void* context) {
+  return debuggerd_handle_signal(signal_number, info, context);
+}
+
 static uint8_t __libdl_info_buf[sizeof(soinfo)] __attribute__((aligned(8)));
 static soinfo* __libdl_info = nullptr;
 
diff --git a/linker/ld_android.cpp b/linker/ld_android.cpp
index 0239c30..1c03106 100644
--- a/linker/ld_android.cpp
+++ b/linker/ld_android.cpp
@@ -44,6 +44,7 @@
 __strong_alias(__loader_android_set_application_target_sdk_version, __internal_linker_error);
 __strong_alias(__loader_android_update_LD_LIBRARY_PATH, __internal_linker_error);
 __strong_alias(__loader_cfi_fail, __internal_linker_error);
+__strong_alias(__loader_android_handle_signal, __internal_linker_error);
 __strong_alias(__loader_dl_iterate_phdr, __internal_linker_error);
 __strong_alias(__loader_dladdr, __internal_linker_error);
 __strong_alias(__loader_dlclose, __internal_linker_error);
diff --git a/linker/linker.arm.map b/linker/linker.arm.map
index be438ca..b805cd6 100644
--- a/linker/linker.arm.map
+++ b/linker/linker.arm.map
@@ -24,6 +24,7 @@
     __loader_remove_thread_local_dtor;
     __loader_shared_globals;
     rtld_db_dlactivity;
+    __loader_android_handle_signal;
   local:
     *;
 };
diff --git a/linker/linker.generic.map b/linker/linker.generic.map
index f3c01c0..4d7f236 100644
--- a/linker/linker.generic.map
+++ b/linker/linker.generic.map
@@ -23,6 +23,7 @@
     __loader_remove_thread_local_dtor;
     __loader_shared_globals;
     rtld_db_dlactivity;
+    __loader_android_handle_signal;
   local:
     *;
 };
diff --git a/linker/linker_debuggerd.h b/linker/linker_debuggerd.h
index d701879..95f99e7 100644
--- a/linker/linker_debuggerd.h
+++ b/linker/linker_debuggerd.h
@@ -28,4 +28,7 @@
 
 #pragma once
 
+#include <signal.h>
+
 void linker_debuggerd_init();
+extern "C" bool debuggerd_handle_signal(int signal_number, siginfo_t* info, void* context);
diff --git a/linker/linker_debuggerd_android.cpp b/linker/linker_debuggerd_android.cpp
index 3d64628..ab6fc30 100644
--- a/linker/linker_debuggerd_android.cpp
+++ b/linker/linker_debuggerd_android.cpp
@@ -46,6 +46,17 @@
       .scudo_ring_buffer_size = __libc_shared_globals()->scudo_ring_buffer_size,
   };
 }
+
+static gwp_asan_callbacks_t get_gwp_asan_callbacks() {
+  return {
+      .debuggerd_needs_gwp_asan_recovery =
+          __libc_shared_globals()->debuggerd_needs_gwp_asan_recovery,
+      .debuggerd_gwp_asan_pre_crash_report =
+          __libc_shared_globals()->debuggerd_gwp_asan_pre_crash_report,
+      .debuggerd_gwp_asan_post_crash_report =
+          __libc_shared_globals()->debuggerd_gwp_asan_post_crash_report,
+  };
+}
 #endif
 
 void linker_debuggerd_init() {
@@ -53,9 +64,10 @@
   // so don't pass in any process info from the bootstrap linker.
   debuggerd_callbacks_t callbacks = {
 #if defined(__ANDROID_APEX__)
-      .get_process_info = get_process_info,
+    .get_process_info = get_process_info,
+    .get_gwp_asan_callbacks = get_gwp_asan_callbacks,
 #endif
-      .post_dump = notify_gdb_of_libraries,
+    .post_dump = notify_gdb_of_libraries,
   };
   debuggerd_init(&callbacks);
 }
diff --git a/linker/linker_debuggerd_stub.cpp b/linker/linker_debuggerd_stub.cpp
index 631e6e4..c671dd9 100644
--- a/linker/linker_debuggerd_stub.cpp
+++ b/linker/linker_debuggerd_stub.cpp
@@ -28,5 +28,11 @@
 
 #include "linker_debuggerd.h"
 
+#include <signal.h>
+
 void linker_debuggerd_init() {
 }
+extern "C" bool debuggerd_handle_signal(int /* signal_number */, siginfo_t* /* info */,
+                                        void* /* context */) {
+  return false;
+}