Merge "Update linkerconfig configuration format"
diff --git a/libc/Android.bp b/libc/Android.bp
index a761e3b..06eb6dd 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -62,6 +62,7 @@
     cppflags: [],
     include_dirs: [
         "bionic/libc/async_safe/include",
+        "bionic/libc/platform",
         // For android_filesystem_config.h.
         "system/core/libcutils/include",
     ],
@@ -1077,6 +1078,7 @@
         "bionic/mblen.cpp",
         "bionic/mbrtoc16.cpp",
         "bionic/mbrtoc32.cpp",
+        "bionic/memory_mitigation_state.cpp",
         "bionic/mempcpy.cpp",
         "bionic/mkdir.cpp",
         "bionic/mkfifo.cpp",
@@ -1726,7 +1728,7 @@
         versions: [
             "29",
             "R",
-            "10000",
+            "current",
         ],
     },
 
@@ -1738,6 +1740,10 @@
     // Sorting bss symbols by size usually results in less dirty pages at run
     // time, because small symbols are grouped together.
     sort_bss_symbols_by_size: true,
+
+    lto: {
+        never: true,
+    },
 }
 
 genrule {
@@ -1785,8 +1791,9 @@
         "//external/perfetto:__subpackages__",
         "//external/scudo:__subpackages__",
         "//system/core/debuggerd:__subpackages__",
-        "//system/core/libunwindstack:__subpackages__",
+        "//system/core/libcutils:__subpackages__",
         "//system/memory/libmemunreachable:__subpackages__",
+        "//system/unwinding/libunwindstack:__subpackages__",
         "//tools/security/sanitizer-status:__subpackages__",
     ],
     vendor_available: true,
@@ -1800,11 +1807,10 @@
     stl: "none",
     sdk_version: "current",
 
+    min_sdk_version: "29",
     apex_available: [
         "//apex_available:platform",
-        "com.android.runtime",
-        "com.android.art.release", // from libdexfile_external
-        "com.android.art.debug", // from libdexfile_external
+        "//apex_available:anyapex",
     ],
 }
 
diff --git a/libc/arch-arm64/bionic/__bionic_clone.S b/libc/arch-arm64/bionic/__bionic_clone.S
index c3ff0e5..e9932ad 100644
--- a/libc/arch-arm64/bionic/__bionic_clone.S
+++ b/libc/arch-arm64/bionic/__bionic_clone.S
@@ -57,3 +57,5 @@
     ldp     x0, x1, [sp], #16
     b       __start_thread
 END(__bionic_clone)
+
+NOTE_GNU_PROPERTY()
diff --git a/libc/arch-arm64/bionic/_exit_with_stack_teardown.S b/libc/arch-arm64/bionic/_exit_with_stack_teardown.S
index 6a7b1e5..c53a1f4 100644
--- a/libc/arch-arm64/bionic/_exit_with_stack_teardown.S
+++ b/libc/arch-arm64/bionic/_exit_with_stack_teardown.S
@@ -39,3 +39,5 @@
   svc #0
   // The exit syscall does not return.
 END(_exit_with_stack_teardown)
+
+NOTE_GNU_PROPERTY()
diff --git a/libc/arch-arm64/bionic/setjmp.S b/libc/arch-arm64/bionic/setjmp.S
index a2b2370..07270c9 100644
--- a/libc/arch-arm64/bionic/setjmp.S
+++ b/libc/arch-arm64/bionic/setjmp.S
@@ -118,6 +118,8 @@
 // int sigsetjmp(sigjmp_buf env, int save_signal_mask);
 ENTRY(sigsetjmp)
 __BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(sigsetjmp)
+  hint #25 // paciasp
+  .cfi_negate_ra_state
   stp x0, x30, [sp, #-16]!
   .cfi_def_cfa_offset 16
   .cfi_rel_offset x0, 0
@@ -184,6 +186,8 @@
 #endif
 
   mov w0, #0
+  hint #29 // autiasp
+  .cfi_negate_ra_state
   ret
 END(sigsetjmp)
 
@@ -250,7 +254,9 @@
 1:
   // Restore core registers.
   bic x2, x2, #1
+  // x30 was saved with PAC to jmp_buf in sigsetjmp().
   ldp x30, x10, [x0, #(_JB_X30_SP  * 8)]
+  .cfi_negate_ra_state
   ldp x28, x29, [x0, #(_JB_X28_X29 * 8)]
   ldp x26, x27, [x0, #(_JB_X26_X27 * 8)]
   ldp x24, x25, [x0, #(_JB_X24_X25 * 8)]
@@ -290,6 +296,8 @@
   // Set return value.
   cmp w1, wzr
   csinc w0, w1, wzr, ne
+  hint #29 // autiasp
+  .cfi_negate_ra_state
   ret
 END(siglongjmp)
 
@@ -297,3 +305,5 @@
 __BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(longjmp)
 ALIAS_SYMBOL(_longjmp, siglongjmp)
 __BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(_longjmp)
+
+NOTE_GNU_PROPERTY()
diff --git a/libc/arch-arm64/bionic/syscall.S b/libc/arch-arm64/bionic/syscall.S
index 8389f98..9e6f68a 100644
--- a/libc/arch-arm64/bionic/syscall.S
+++ b/libc/arch-arm64/bionic/syscall.S
@@ -47,3 +47,5 @@
 
     ret
 END(syscall)
+
+NOTE_GNU_PROPERTY()
diff --git a/libc/arch-arm64/bionic/vfork.S b/libc/arch-arm64/bionic/vfork.S
index 5cfb8b0..81b84a3 100644
--- a/libc/arch-arm64/bionic/vfork.S
+++ b/libc/arch-arm64/bionic/vfork.S
@@ -67,6 +67,8 @@
 
     // Clean up stack shadow in the parent process.
     // https://github.com/google/sanitizers/issues/925
+    hint #25 // paciasp
+    .cfi_negate_ra_state
     stp x0, x30, [sp, #-16]!
     .cfi_adjust_cfa_offset 16
     .cfi_rel_offset x0, 0
@@ -79,9 +81,13 @@
     .cfi_adjust_cfa_offset -16
     .cfi_restore x0
     .cfi_restore x30
+    hint #29 // autiasp
+    .cfi_negate_ra_state
 
 #endif
 
 .L_exit:
     ret
 END(vfork)
+
+NOTE_GNU_PROPERTY()
diff --git a/libc/arch-arm64/generic/bionic/__memcpy_chk.S b/libc/arch-arm64/generic/bionic/__memcpy_chk.S
index a6eeca4..a8e9e83 100644
--- a/libc/arch-arm64/generic/bionic/__memcpy_chk.S
+++ b/libc/arch-arm64/generic/bionic/__memcpy_chk.S
@@ -43,3 +43,5 @@
 
   bl __memcpy_chk_fail
 END(__memcpy_chk)
+
+NOTE_GNU_PROPERTY()
diff --git a/libc/arch-arm64/generic/bionic/memcpy.S b/libc/arch-arm64/generic/bionic/memcpy.S
index baadb92..bc1945c 100644
--- a/libc/arch-arm64/generic/bionic/memcpy.S
+++ b/libc/arch-arm64/generic/bionic/memcpy.S
@@ -33,3 +33,5 @@
 ENTRY(__memcpy)
   #include "memcpy_base.S"
 END(__memcpy)
+
+NOTE_GNU_PROPERTY()
diff --git a/libc/arch-arm64/generic/bionic/memmove.S b/libc/arch-arm64/generic/bionic/memmove.S
index 335b7d6..0f752ea 100644
--- a/libc/arch-arm64/generic/bionic/memmove.S
+++ b/libc/arch-arm64/generic/bionic/memmove.S
@@ -153,3 +153,5 @@
 
 ALIAS_SYMBOL(memcpy, memmove)
 #endif
+
+NOTE_GNU_PROPERTY()
diff --git a/libc/arch-arm64/generic/bionic/memset.S b/libc/arch-arm64/generic/bionic/memset.S
index 12fc09d..19d3510 100644
--- a/libc/arch-arm64/generic/bionic/memset.S
+++ b/libc/arch-arm64/generic/bionic/memset.S
@@ -249,3 +249,5 @@
 	b	L(tail64)
 
 END(memset)
+
+NOTE_GNU_PROPERTY()
diff --git a/libc/arch-arm64/generic/bionic/wmemmove.S b/libc/arch-arm64/generic/bionic/wmemmove.S
index e4f67f7..b130530 100644
--- a/libc/arch-arm64/generic/bionic/wmemmove.S
+++ b/libc/arch-arm64/generic/bionic/wmemmove.S
@@ -28,3 +28,5 @@
 #define WMEMMOVE
 #include "memmove.S"
 #undef WMEMMOVE
+
+NOTE_GNU_PROPERTY()
diff --git a/libc/arch-arm64/static_function_dispatch.S b/libc/arch-arm64/static_function_dispatch.S
index 65a1492..161ece8 100644
--- a/libc/arch-arm64/static_function_dispatch.S
+++ b/libc/arch-arm64/static_function_dispatch.S
@@ -42,3 +42,5 @@
 FUNCTION_DELEGATE(strlen, __strlen_aarch64_mte)
 FUNCTION_DELEGATE(strrchr, __strrchr_aarch64_mte)
 FUNCTION_DELEGATE(strncmp, __strncmp_aarch64_mte)
+
+NOTE_GNU_PROPERTY()
diff --git a/libc/arch-common/bionic/crtbegin.c b/libc/arch-common/bionic/crtbegin.c
index b7043dc..1f8dfd2 100644
--- a/libc/arch-common/bionic/crtbegin.c
+++ b/libc/arch-common/bionic/crtbegin.c
@@ -49,7 +49,7 @@
 #define POST "; .size _start, .-_start"
 
 #if defined(__aarch64__)
-__asm__(PRE "mov x0,sp; b _start_main" POST);
+__asm__(PRE "/* BTI J */ hint #36; mov x0,sp; b _start_main" POST);
 #elif defined(__arm__)
 __asm__(PRE "mov r0,sp; b _start_main" POST);
 #elif defined(__i386__)
diff --git a/libc/arch-common/bionic/crtbrand.S b/libc/arch-common/bionic/crtbrand.S
index 34d6480..3d80d73 100644
--- a/libc/arch-common/bionic/crtbrand.S
+++ b/libc/arch-common/bionic/crtbrand.S
@@ -26,6 +26,12 @@
  * SUCH DAMAGE.
  */
 
+#if defined(__aarch64__)
+#include <private/bionic_asm_arm64.h>
+
+__bionic_asm_custom_note_gnu_section()
+#endif
+
   .section .note.android.ident,"a",%note
   .balign 4
   .type abitag, %object
diff --git a/libc/arch-common/bionic/crtend.S b/libc/arch-common/bionic/crtend.S
index 87d1120..9676db8 100644
--- a/libc/arch-common/bionic/crtend.S
+++ b/libc/arch-common/bionic/crtend.S
@@ -28,6 +28,12 @@
 
 #include "asm_multiarch.h"
 
+#if defined(__aarch64__)
+#include <private/bionic_asm_arm64.h>
+
+__bionic_asm_custom_note_gnu_section()
+#endif
+
 	.section .preinit_array, "aw"
 	ASM_ALIGN_TO_PTR_SIZE
 	ASM_PTR_SIZE(0)
@@ -43,11 +49,9 @@
 #if defined(__linux__) && defined(__ELF__)
 	.section .note.GNU-stack,"",%progbits
 #endif
-#if defined(__i386__) || defined(__x86_64__)
+#if !defined(__arm__)
 	.section	.eh_frame,"a",@progbits
-#if defined(__i386__)
 	.balign 4
-#endif
 	.type	__FRAME_END__, @object
 	.size	__FRAME_END__, 4
 __FRAME_END__:
diff --git a/libc/arch-common/bionic/crtend_so.S b/libc/arch-common/bionic/crtend_so.S
index e7b8cac..5875acb 100644
--- a/libc/arch-common/bionic/crtend_so.S
+++ b/libc/arch-common/bionic/crtend_so.S
@@ -26,14 +26,18 @@
  * SUCH DAMAGE.
  */
 
+#if defined(__aarch64__)
+#include <private/bionic_asm_arm64.h>
+
+__bionic_asm_custom_note_gnu_section()
+#endif
+
 #if defined(__linux__) && defined(__ELF__)
 	.section .note.GNU-stack,"",%progbits
 #endif
-#if defined(__i386__) || defined(__x86_64__)
+#if !defined(__arm__)
 	.section	.eh_frame,"a",@progbits
-#if defined(__i386__)
 	.balign 4
-#endif
 	.type	__FRAME_END__, @object
 	.size	__FRAME_END__, 4
 __FRAME_END__:
diff --git a/libc/arch-x86/bionic/__restore.S b/libc/arch-x86/bionic/__restore.S
index cb18fd0..5977eab 100644
--- a/libc/arch-x86/bionic/__restore.S
+++ b/libc/arch-x86/bionic/__restore.S
@@ -27,14 +27,7 @@
  */
 
 #include <private/bionic_asm.h>
-
-// DWARF constants.
-#define DW_CFA_def_cfa_expression 0x0f
-#define DW_CFA_expression 0x10
-#define DW_EH_PE_pcrel 0x10
-#define DW_EH_PE_sdata4 0x0b
-#define DW_OP_breg4 0x74
-#define DW_OP_deref 0x06
+#include <private/bionic_asm_dwarf_exprs.h>
 
 // Offsets into struct sigcontext.
 #define OFFSET_EDI 16
@@ -52,84 +45,47 @@
 #define DW_x86_REG_ECX 1
 #define DW_x86_REG_EDX 2
 #define DW_x86_REG_EBX 3
+#define DW_x86_REG_ESP 4
 #define DW_x86_REG_EBP 5
 #define DW_x86_REG_ESI 6
 #define DW_x86_REG_EDI 7
 #define DW_x86_REG_EIP 8
 
-#define cfi_signal_frame_start(f) \
-.section .eh_frame,"a",@progbits; \
-.L ## f ## _START_EH_FRAME: \
-  .long 2f - 1f; /* CIE length. */ \
-1:.long 0;       /* CIE ID. */ \
-  .byte 1;       /* Version. */ \
-  .string "zRS"; /* Augmentation string. */ \
-  .uleb128 1;    /* Code alignment factor. */ \
-  .sleb128 -4;   /* Data alignment factor. */ \
-  .uleb128 DW_x86_REG_EIP;    /* Return address register. */ \
-  .uleb128 1;    /* 1 byte of augmentation data. */ \
-  .byte (DW_EH_PE_pcrel|DW_EH_PE_sdata4); /* FDE encoding. */ \
-  .align 8; \
-2: \
-  .long .L ## f ## _END_FDE - .L ## f ## _START_FDE;   /* FDE length. */ \
-.L ## f ## _START_FDE: \
-  .long .L ## f ## _START_FDE - .L ## f ## _START_EH_FRAME; /* CIE location. */ \
-  .long (.L ## f ## _START - 1) - .;                   /* pcrel start address (see FDE encoding above). */ \
-  .long .L ## f ## _END - (.L ## f ## _START - 1);     /* Function this FDE applies to. */ \
-  .uleb128 0;                                          /* FDE augmentation length. */ \
+#define RESTORE_GPR(reg, extra_offset)                    \
+    m_cfi_breg_offset DW_x86_REG_ ## reg,                 \
+                      DW_x86_REG_ESP,                     \
+                      (OFFSET_ ## reg + (extra_offset));
 
-#define cfi_signal_frame_end(f) \
-.L ## f ## _END_FDE: \
+// Restoring ESP is unnecessary as the unwinder simply uses the CFA value.
+#define RESTORE_GPRS(extra_offset)                                      \
+    m_cfi_def_cfa_deref DW_x86_REG_ESP, (OFFSET_ESP + (extra_offset));  \
+    RESTORE_GPR(EDI, extra_offset)                                      \
+    RESTORE_GPR(ESI, extra_offset)                                      \
+    RESTORE_GPR(EBP, extra_offset)                                      \
+    RESTORE_GPR(EBX, extra_offset)                                      \
+    RESTORE_GPR(EDX, extra_offset)                                      \
+    RESTORE_GPR(ECX, extra_offset)                                      \
+    RESTORE_GPR(EAX, extra_offset)                                      \
+    RESTORE_GPR(EIP, extra_offset)                                      \
 
-#define cfi_def_cfa(offset) \
-  .byte DW_CFA_def_cfa_expression; \
-  .uleb128 2f-1f; \
-1:.byte DW_OP_breg4; \
-  .sleb128 offset; \
-  .byte DW_OP_deref; \
-2: \
+  .text
 
-#define cfi_offset(reg_number,offset) \
-  .byte DW_CFA_expression; \
-  .uleb128 reg_number; \
-  .uleb128 2f-1f; \
-1:.byte DW_OP_breg4; \
-  .sleb128 offset; \
-2: \
-
-ENTRY_PRIVATE(__restore)
-.L__restore_START:
+  .cfi_startproc
+  .cfi_signal_frame
+  RESTORE_GPRS(4)
+  nop   // See comment in libc/arch-x86_64/bionic/__restore_rt.S about this nop.
+ENTRY_PRIVATE_NO_DWARF(__restore)
   popl %eax
+  RESTORE_GPRS(0)
   movl $__NR_sigreturn, %eax
   int $0x80
-.L__restore_END:
 END(__restore)
-cfi_signal_frame_start(__restore)
-  cfi_def_cfa(OFFSET_ESP + 4)
-  cfi_offset(DW_x86_REG_EDI, OFFSET_EDI + 4)
-  cfi_offset(DW_x86_REG_ESI, OFFSET_ESI + 4)
-  cfi_offset(DW_x86_REG_EBP, OFFSET_EBP + 4)
-  cfi_offset(DW_x86_REG_EBX, OFFSET_EBX + 4)
-  cfi_offset(DW_x86_REG_EDX, OFFSET_EDX + 4)
-  cfi_offset(DW_x86_REG_ECX, OFFSET_ECX + 4)
-  cfi_offset(DW_x86_REG_EAX, OFFSET_EAX + 4)
-  cfi_offset(DW_x86_REG_EIP, OFFSET_EIP + 4)
-cfi_signal_frame_end(__restore)
 
-ENTRY_PRIVATE(__restore_rt)
-.L__restore_rt_START:
+  .cfi_startproc
+  .cfi_signal_frame
+  RESTORE_GPRS(160)
+  nop   // See comment in libc/arch-x86_64/bionic/__restore_rt.S about this nop.
+ENTRY_PRIVATE_NO_DWARF(__restore_rt)
   movl $__NR_rt_sigreturn, %eax
   int $0x80
-.L__restore_rt_END:
 END(__restore_rt)
-cfi_signal_frame_start(__restore_rt)
-  cfi_def_cfa(OFFSET_ESP + 160)
-  cfi_offset(DW_x86_REG_EDI, OFFSET_EDI + 160)
-  cfi_offset(DW_x86_REG_ESI, OFFSET_ESI + 160)
-  cfi_offset(DW_x86_REG_EBP, OFFSET_EBP + 160)
-  cfi_offset(DW_x86_REG_EBX, OFFSET_EBX + 160)
-  cfi_offset(DW_x86_REG_EDX, OFFSET_EDX + 160)
-  cfi_offset(DW_x86_REG_ECX, OFFSET_ECX + 160)
-  cfi_offset(DW_x86_REG_EAX, OFFSET_EAX + 160)
-  cfi_offset(DW_x86_REG_EIP, OFFSET_EIP + 160)
-cfi_signal_frame_end(__restore_rt)
diff --git a/libc/arch-x86_64/bionic/__restore_rt.S b/libc/arch-x86_64/bionic/__restore_rt.S
index 785b3b3..f3e4012 100644
--- a/libc/arch-x86_64/bionic/__restore_rt.S
+++ b/libc/arch-x86_64/bionic/__restore_rt.S
@@ -27,15 +27,9 @@
  */
 
 #include <private/bionic_asm.h>
+#include <private/bionic_asm_dwarf_exprs.h>
 
-// DWARF constants.
-#define DW_CFA_def_cfa_expression 0x0f
-#define DW_CFA_expression 0x10
-#define DW_EH_PE_pcrel 0x10
-#define DW_EH_PE_sdata4 0x0b
-#define DW_OP_breg4 0x74
-#define DW_OP_breg7 0x77
-#define DW_OP_deref 0x06
+// In the signal trampoline frame, rsp points to a ucontext_t struct.
 
 // Offsets into struct ucontext_t of uc_mcontext.gregs[x].
 #define OFFSET_R8 40
@@ -49,11 +43,11 @@
 #define OFFSET_RDI 104
 #define OFFSET_RSI 112
 #define OFFSET_RBP 120
-#define OFFSET_RSP 160
 #define OFFSET_RBX 128
 #define OFFSET_RDX 136
 #define OFFSET_RAX 144
 #define OFFSET_RCX 152
+#define OFFSET_RSP 160
 #define OFFSET_RIP 168
 
 // Non-standard DWARF constants for the x86-64 registers.
@@ -75,69 +69,47 @@
 #define DW_x86_64_R15 15
 #define DW_x86_64_RIP 16
 
-#define cfi_signal_frame_start(f) \
-.section .eh_frame,"a",@progbits; \
-.L ## f ## _START_EH_FRAME: \
-  .long 2f - 1f; /* CIE length. */ \
-1:.long 0;       /* CIE ID. */ \
-  .byte 1;       /* Version. */ \
-  .string "zRS"; /* Augmentation string. */ \
-  .uleb128 1;    /* Code alignment factor. */ \
-  .sleb128 -8;   /* Data alignment factor. */ \
-  .uleb128 DW_x86_64_RIP;   /* Return address register. */ \
-  .uleb128 1;    /* 1 byte of augmentation data. */ \
-  .byte (DW_EH_PE_pcrel | DW_EH_PE_sdata4); /* FDE encoding. */ \
-  .align 8; \
-2: \
-  .long .L ## f ## _END_FDE - .L ## f ## _START_FDE;   /* FDE length. */ \
-.L ## f ## _START_FDE: \
-  .long .L ## f ## _START_FDE - .L ## f ## _START_EH_FRAME; /* CIE location. */ \
-  .long (.L ## f ## _START - 1) - .;                   /* pcrel start address (see FDE encoding above). */ \
-  .long .L ## f ## _END - (.L ## f ## _START - 1);     /* Function this FDE applies to. */ \
-  .uleb128 0;                                          /* FDE augmentation length. */ \
+// Insert a nop between .cfi_startproc and the trampoline symbol so that unwinders can find the FDE.
+// A function's last instruction can be a call instruction (e.g. to __cxa_throw), in which case the
+// return address (e.g. from __cxa_throw to the caller) will be just after the function. This
+// address may also be the start of the next function, so to avoid ambiguity, unwinders assume that
+// a return address PC can refer to the address just after a function, but never to the start of a
+// function. (This is implemented by subtracting 1 from the return address PC before looking it up.)
+// This is fine for ordinary functions, but breaks on trampolines. Inserting a nop fixes it.
+//
+// N.B. Unwinders have two other strategies for recognizing the signal trampoline:
+//  - Read the instructions that the return address PC points at and look for a sigreturn syscall.
+//    (Hence, the instructions must not change at all.)
+//  - Do a symbol table lookup and check that against the PC (e.g. LLDB looks for
+//    __kernel_rt_sigreturn and __restore_rt.)
+// Either way, the nop is needed to avoid ambiguity if the function before the trampoline could end
+// with a call.
 
-#define cfi_signal_frame_end(f) \
-.L ## f ## _END_FDE: \
+#define RESTORE_GPR(reg) m_cfi_breg_offset DW_x86_64_ ## reg, DW_x86_64_RSP, OFFSET_ ## reg;
 
-#define cfi_def_cfa(offset) \
-  .byte DW_CFA_def_cfa_expression; \
-  .uleb128 2f-1f; \
-1:.byte DW_OP_breg7; \
-  .sleb128 offset; \
-  .byte DW_OP_deref; \
-2: \
-
-#define cfi_offset(reg_number,offset) \
-  .byte DW_CFA_expression; \
-  .uleb128 reg_number; \
-  .uleb128 2f-1f; \
-1:.byte DW_OP_breg7; \
-  .sleb128 offset; \
-2: \
-
-ENTRY_PRIVATE(__restore_rt)
-.L__restore_rt_START:
+  .text
+  .cfi_startproc
+  .cfi_signal_frame
+  m_cfi_def_cfa_deref DW_x86_64_RSP, OFFSET_RSP
+  RESTORE_GPR(R8)
+  RESTORE_GPR(R9)
+  RESTORE_GPR(R10)
+  RESTORE_GPR(R11)
+  RESTORE_GPR(R12)
+  RESTORE_GPR(R13)
+  RESTORE_GPR(R14)
+  RESTORE_GPR(R15)
+  RESTORE_GPR(RDI)
+  RESTORE_GPR(RSI)
+  RESTORE_GPR(RBP)
+  RESTORE_GPR(RBX)
+  RESTORE_GPR(RDX)
+  RESTORE_GPR(RAX)
+  RESTORE_GPR(RCX)
+  // Restoring RSP is unnecessary as the unwinder simply uses the CFA value.
+  RESTORE_GPR(RIP)
+  nop
+ENTRY_PRIVATE_NO_DWARF(__restore_rt)
   mov $__NR_rt_sigreturn, %rax
   syscall
-.L__restore_rt_END:
 END(__restore_rt)
-cfi_signal_frame_start(__restore_rt)
-  cfi_def_cfa(OFFSET_RSP)
-  cfi_offset(DW_x86_64_R8, OFFSET_R8)
-  cfi_offset(DW_x86_64_R9, OFFSET_R9)
-  cfi_offset(DW_x86_64_R10, OFFSET_R10)
-  cfi_offset(DW_x86_64_R11, OFFSET_R11)
-  cfi_offset(DW_x86_64_R12, OFFSET_R12)
-  cfi_offset(DW_x86_64_R13, OFFSET_R13)
-  cfi_offset(DW_x86_64_R14, OFFSET_R14)
-  cfi_offset(DW_x86_64_R15, OFFSET_R15)
-  cfi_offset(DW_x86_64_RDI, OFFSET_RDI)
-  cfi_offset(DW_x86_64_RSI, OFFSET_RSI)
-  cfi_offset(DW_x86_64_RBP, OFFSET_RBP)
-  cfi_offset(DW_x86_64_RSP, OFFSET_RSP)
-  cfi_offset(DW_x86_64_RBX, OFFSET_RBX)
-  cfi_offset(DW_x86_64_RDX, OFFSET_RDX)
-  cfi_offset(DW_x86_64_RAX, OFFSET_RAX)
-  cfi_offset(DW_x86_64_RCX, OFFSET_RCX)
-  cfi_offset(DW_x86_64_RIP, OFFSET_RIP)
-cfi_signal_frame_end(__restore_rt)
diff --git a/libc/bionic/fork.cpp b/libc/bionic/fork.cpp
index 5bc7d3f..8c5cf2b 100644
--- a/libc/bionic/fork.cpp
+++ b/libc/bionic/fork.cpp
@@ -33,8 +33,8 @@
 #include "private/bionic_defs.h"
 #include "pthread_internal.h"
 
-__BIONIC_WEAK_FOR_NATIVE_BRIDGE
-extern "C" __LIBC_HIDDEN__ int __clone_for_fork() {
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE_INLINE
+int __clone_for_fork() {
   pthread_internal_t* self = __get_thread();
 
   int result = clone(nullptr, nullptr, (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD),
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index 01b6a42..80adbbe 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -58,6 +58,7 @@
 __LIBC_HIDDEN__ WriteProtected<libc_globals> __libc_globals;
 
 // Not public, but well-known in the BSDs.
+__BIONIC_WEAK_VARIABLE_FOR_NATIVE_BRIDGE
 const char* __progname;
 
 void __libc_init_globals() {
diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp
index ed5537f..6b7006d 100644
--- a/libc/bionic/malloc_common.cpp
+++ b/libc/bionic/malloc_common.cpp
@@ -46,6 +46,7 @@
 #include "malloc_common.h"
 #include "malloc_limit.h"
 #include "malloc_tagged_pointers.h"
+#include "memory_mitigation_state.h"
 
 // =============================================================================
 // Global variables instantations.
@@ -326,6 +327,9 @@
       return MaybeInitGwpAsan(globals, *reinterpret_cast<bool*>(arg));
     });
   }
+  if (opcode == M_DISABLE_MEMORY_MITIGATIONS) {
+    return DisableMemoryMitigations(arg, arg_size);
+  }
   errno = ENOTSUP;
   return false;
 }
diff --git a/libc/bionic/malloc_common_dynamic.cpp b/libc/bionic/malloc_common_dynamic.cpp
index 6a82ae3..eeeaff9 100644
--- a/libc/bionic/malloc_common_dynamic.cpp
+++ b/libc/bionic/malloc_common_dynamic.cpp
@@ -70,6 +70,7 @@
 #include "malloc_common_dynamic.h"
 #include "malloc_heapprofd.h"
 #include "malloc_limit.h"
+#include "memory_mitigation_state.h"
 
 // =============================================================================
 // Global variables instantations.
@@ -533,6 +534,9 @@
       return MaybeInitGwpAsan(globals, *reinterpret_cast<bool*>(arg));
     });
   }
+  if (opcode == M_DISABLE_MEMORY_MITIGATIONS) {
+    return DisableMemoryMitigations(arg, arg_size);
+  }
   // Try heapprofd's mallopt, as it handles options not covered here.
   return HeapprofdMallopt(opcode, arg, arg_size);
 }
diff --git a/libc/bionic/memory_mitigation_state.cpp b/libc/bionic/memory_mitigation_state.cpp
new file mode 100644
index 0000000..82b0b7b
--- /dev/null
+++ b/libc/bionic/memory_mitigation_state.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "memory_mitigation_state.h"
+
+#include <dirent.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdatomic.h>
+#include <stdlib.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+
+#include <bionic/mte.h>
+#include <bionic/reserved_signals.h>
+
+#include "private/ScopedRWLock.h"
+#include "pthread_internal.h"
+
+extern "C" void scudo_malloc_set_zero_contents(int zero_contents);
+extern "C" void scudo_malloc_disable_memory_tagging();
+
+#ifdef ANDROID_EXPERIMENTAL_MTE
+static bool set_tcf_on_all_threads(int tcf) {
+  static int g_tcf;
+  g_tcf = tcf;
+
+  return android_run_on_all_threads(
+      [](void*) {
+        int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+        if (tagged_addr_ctrl < 0) {
+          return false;
+        }
+
+        tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | g_tcf;
+        if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) < 0) {
+          return false;
+        }
+        return true;
+      },
+      nullptr);
+}
+#endif
+
+bool DisableMemoryMitigations(void* arg, size_t arg_size) {
+  if (arg || arg_size) {
+    return false;
+  }
+
+#ifdef USE_SCUDO
+  scudo_malloc_set_zero_contents(0);
+
+#ifdef ANDROID_EXPERIMENTAL_MTE
+  if (mte_supported() && set_tcf_on_all_threads(PR_MTE_TCF_NONE)) {
+    scudo_malloc_disable_memory_tagging();
+  }
+#endif
+#endif
+
+  return true;
+}
diff --git a/libc/bionic/memory_mitigation_state.h b/libc/bionic/memory_mitigation_state.h
new file mode 100644
index 0000000..ffa1912
--- /dev/null
+++ b/libc/bionic/memory_mitigation_state.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <stddef.h>
+
+bool DisableMemoryMitigations(void* arg, size_t arg_size);
diff --git a/libc/bionic/pthread_cond.cpp b/libc/bionic/pthread_cond.cpp
index e857069..793dcd9 100644
--- a/libc/bionic/pthread_cond.cpp
+++ b/libc/bionic/pthread_cond.cpp
@@ -116,7 +116,8 @@
   }
 
 #if defined(__LP64__)
-  char __reserved[44];
+  atomic_uint waiters;
+  char __reserved[40];
 #endif
 };
 
@@ -141,6 +142,10 @@
   }
   atomic_init(&cond->state, init_state);
 
+#if defined(__LP64__)
+  atomic_init(&cond->waiters, 0);
+#endif
+
   return 0;
 }
 
@@ -163,6 +168,12 @@
   // not be called. That's why pthread_wait/signal pair can't be used as a method for memory
   // synchronization. And it doesn't help even if we use any fence here.
 
+#if defined(__LP64__)
+  if (atomic_load_explicit(&cond->waiters, memory_order_relaxed) == 0) {
+    return 0;
+  }
+#endif
+
   // The increase of value should leave flags alone, even if the value can overflows.
   atomic_fetch_add_explicit(&cond->state, COND_COUNTER_STEP, memory_order_relaxed);
 
@@ -178,9 +189,19 @@
   }
 
   unsigned int old_state = atomic_load_explicit(&cond->state, memory_order_relaxed);
+
+#if defined(__LP64__)
+  atomic_fetch_add_explicit(&cond->waiters, 1, memory_order_relaxed);
+#endif
+
   pthread_mutex_unlock(mutex);
   int status = __futex_wait_ex(&cond->state, cond->process_shared(), old_state,
                                use_realtime_clock, abs_timeout_or_null);
+
+#if defined(__LP64__)
+  atomic_fetch_sub_explicit(&cond->waiters, 1, memory_order_relaxed);
+#endif
+
   pthread_mutex_lock(mutex);
 
   if (status == -ETIMEDOUT) {
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index c528105..206d5fd 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -39,6 +39,7 @@
 
 #include <async_safe/log.h>
 
+#include "private/ScopedRWLock.h"
 #include "private/bionic_constants.h"
 #include "private/bionic_defs.h"
 #include "private/bionic_globals.h"
@@ -357,6 +358,7 @@
   return nullptr;
 }
 
+pthread_rwlock_t g_thread_creation_lock = PTHREAD_RWLOCK_INITIALIZER;
 
 __BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr,
@@ -406,6 +408,8 @@
   tls = &tls_descriptor;
 #endif
 
+  ScopedReadLock locker(&g_thread_creation_lock);
+
   sigset64_t block_all_mask;
   sigfillset64(&block_all_mask);
   __rt_sigprocmask(SIG_SETMASK, &block_all_mask, &thread->start_mask, sizeof(thread->start_mask));
diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp
index 81dab57..bde95ec 100644
--- a/libc/bionic/pthread_exit.cpp
+++ b/libc/bionic/pthread_exit.cpp
@@ -35,6 +35,7 @@
 
 #include "private/bionic_constants.h"
 #include "private/bionic_defs.h"
+#include "private/ScopedRWLock.h"
 #include "private/ScopedSignalBlocker.h"
 #include "pthread_internal.h"
 
@@ -103,9 +104,18 @@
          !atomic_compare_exchange_weak(&thread->join_state, &old_state, THREAD_EXITED_NOT_JOINED)) {
   }
 
-  // We don't want to take a signal after unmapping the stack, the shadow call
-  // stack, or dynamic TLS memory.
-  ScopedSignalBlocker ssb;
+  // android_run_on_all_threads() needs to see signals blocked atomically with setting the
+  // terminating flag, so take the creation lock while doing these operations.
+  {
+    ScopedReadLock locker(&g_thread_creation_lock);
+    atomic_store(&thread->terminating, true);
+
+    // We don't want to take a signal after unmapping the stack, the shadow call stack, or dynamic
+    // TLS memory.
+    sigset64_t set;
+    sigfillset64(&set);
+    __rt_sigprocmask(SIG_BLOCK, &set, nullptr, sizeof(sigset64_t));
+  }
 
 #ifdef __aarch64__
   // Free the shadow call stack and guard pages.
diff --git a/libc/bionic/pthread_internal.cpp b/libc/bionic/pthread_internal.cpp
index e091158..6a7ee2f 100644
--- a/libc/bionic/pthread_internal.cpp
+++ b/libc/bionic/pthread_internal.cpp
@@ -29,12 +29,15 @@
 #include "pthread_internal.h"
 
 #include <errno.h>
+#include <semaphore.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
 
 #include <async_safe/log.h>
+#include <bionic/reserved_signals.h>
 
+#include "private/ErrnoRestorer.h"
 #include "private/ScopedRWLock.h"
 #include "private/bionic_futex.h"
 #include "private/bionic_tls.h"
@@ -115,3 +118,76 @@
   }
   return nullptr;
 }
+
+bool android_run_on_all_threads(bool (*func)(void*), void* arg) {
+  // Take the locks in this order to avoid inversion (pthread_create ->
+  // __pthread_internal_add).
+  ScopedWriteLock creation_locker(&g_thread_creation_lock);
+  ScopedReadLock list_locker(&g_thread_list_lock);
+
+  // Call the function directly for the current thread so that we don't need to worry about
+  // the consequences of synchronizing with ourselves.
+  if (!func(arg)) {
+    return false;
+  }
+
+  static sem_t g_sem;
+  if (sem_init(&g_sem, 0, 0) != 0) {
+    return false;
+  }
+
+  static bool (*g_func)(void*);
+  static void *g_arg;
+  g_func = func;
+  g_arg = arg;
+
+  static _Atomic(bool) g_retval;
+  atomic_init(&g_retval, true);
+
+  auto handler = [](int, siginfo_t*, void*) {
+    ErrnoRestorer restorer;
+    if (!g_func(g_arg)) {
+      atomic_store(&g_retval, false);
+    }
+    sem_post(&g_sem);
+  };
+
+  struct sigaction act = {}, oldact;
+  act.sa_flags = SA_SIGINFO;
+  act.sa_sigaction = handler;
+  sigfillset(&act.sa_mask);
+  if (sigaction(BIONIC_SIGNAL_RUN_ON_ALL_THREADS, &act, &oldact) != 0) {
+    sem_destroy(&g_sem);
+    return false;
+  }
+
+  pid_t my_pid = getpid();
+  size_t num_tids = 0;
+  for (pthread_internal_t* t = g_thread_list; t != nullptr; t = t->next) {
+    // The function is called directly for the current thread above, so no need to send a signal to
+    // ourselves to call it here.
+    if (t == __get_thread()) continue;
+
+    // If a thread is terminating (has blocked signals) or has already terminated, our signal will
+    // never be received, so we need to check for that condition and skip the thread if it is the
+    // case.
+    if (atomic_load(&t->terminating)) continue;
+
+    if (tgkill(my_pid, t->tid, BIONIC_SIGNAL_RUN_ON_ALL_THREADS) == 0) {
+      ++num_tids;
+    } else {
+      atomic_store(&g_retval, false);
+    }
+  }
+
+  for (size_t i = 0; i != num_tids; ++i) {
+    if (TEMP_FAILURE_RETRY(sem_wait(&g_sem)) != 0) {
+      atomic_store(&g_retval, false);
+      break;
+    }
+  }
+
+  sigaction(BIONIC_SIGNAL_RUN_ON_ALL_THREADS, &oldact, 0);
+  sem_destroy(&g_sem);
+  return atomic_load(&g_retval);
+}
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index 1f055f5..071a5bc 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -132,6 +132,11 @@
   // top of the stack quickly, which would otherwise require special logic for the main thread.
   uintptr_t stack_top;
 
+  // Whether the thread is in the process of terminating (has blocked signals), or has already
+  // terminated. This is used by android_run_on_all_threads() to avoid sending a signal to a thread
+  // that will never receive it.
+  _Atomic(bool) terminating;
+
   Lock startup_handshake_lock;
 
   void* mmap_base;
@@ -242,3 +247,7 @@
 __LIBC_HIDDEN__ extern void __bionic_atfork_run_prepare();
 __LIBC_HIDDEN__ extern void __bionic_atfork_run_child();
 __LIBC_HIDDEN__ extern void __bionic_atfork_run_parent();
+
+extern "C" bool android_run_on_all_threads(bool (*func)(void*), void* arg);
+
+extern pthread_rwlock_t g_thread_creation_lock;
diff --git a/libc/bionic/sys_thread_properties.cpp b/libc/bionic/sys_thread_properties.cpp
index 24d7551..d1a73b7 100644
--- a/libc/bionic/sys_thread_properties.cpp
+++ b/libc/bionic/sys_thread_properties.cpp
@@ -39,6 +39,11 @@
 #include <sys/uio.h>
 #include <sys/user.h>
 
+#if defined(__i386__)
+#include <asm/ldt.h>
+#endif
+
+#include "private/ErrnoRestorer.h"
 #include "private/bionic_elf_tls.h"
 #include "private/bionic_globals.h"
 #include "private/bionic_tls.h"
@@ -72,21 +77,30 @@
 
   // Find the thread-pointer register for the given thread.
   void** tp_reg = nullptr;
-
-#if defined(__x86_64__) || defined(__i386__)
+#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) {
-#if defined(__x86_64__)
-    tp_reg = reinterpret_cast<void**>(regs.fs);
-#elif defined(__i386__)
-    tp_reg = reinterpret_cast<void**>(regs.xgs);
-#endif
+    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__) || defined(__arm__)
+#elif defined(__aarch64__)
   uint64_t reg;
   struct iovec pt_iov {
     .iov_base = &reg, .iov_len = sizeof(reg),
@@ -95,6 +109,11 @@
   if (ptrace(PTRACE_GETREGSET, tid, NT_ARM_TLS, &pt_iov) == 0) {
     tp_reg = reinterpret_cast<void**>(reg);
   }
+#elif defined(__arm__)
+  if (ptrace(PTRACE_GET_THREAD_AREA, tid, nullptr, &tp_reg) != 0) {
+    // Reset the tp_reg if ptrace was unsuccessful.
+    tp_reg = nullptr;
+  }
 #endif
 
   if (tp_reg == nullptr) {
diff --git a/libc/dns/net/gethnamaddr.c b/libc/dns/net/gethnamaddr.c
index 7e18840..84942f8 100644
--- a/libc/dns/net/gethnamaddr.c
+++ b/libc/dns/net/gethnamaddr.c
@@ -90,6 +90,8 @@
 
 #include "hostent.h"
 
+#include "private/bionic_defs.h"
+
 #define maybe_ok(res, nm, ok) (((res)->options & RES_NOCHECKNAME) != 0U || \
                                (ok)(nm) != 0)
 #define maybe_hnok(res, hn) maybe_ok((res), (hn), res_hnok)
@@ -1533,6 +1535,7 @@
  * Non-reentrant versions.
  */
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 struct hostent *
 gethostbyname(const char *name)
 {
@@ -1590,6 +1593,7 @@
 	return hp;
 }
 
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 struct hostent *
 gethostbyaddr(const void *addr, socklen_t len, int af)
 {
diff --git a/libc/dns/net/getnameinfo.c b/libc/dns/net/getnameinfo.c
index 31d07c5..413b37e 100644
--- a/libc/dns/net/getnameinfo.c
+++ b/libc/dns/net/getnameinfo.c
@@ -68,6 +68,8 @@
 #include <stddef.h>
 #include <string.h>
 
+#include "private/bionic_defs.h"
+
 /* This macro is modelled after the ones in <netinet/in6.h>. */
 /* RFC 6052, section 2.1 */
 #define IN6_IS_ADDR_WKP(a) \
@@ -110,6 +112,7 @@
  * Top-level getnameinfo() code.  Look at the address family, and pick an
  * appropriate function to call.
  */
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 int getnameinfo(const struct sockaddr* sa, socklen_t salen, char* host, size_t hostlen,
 		char* serv, size_t servlen, int flags)
 {
diff --git a/libc/include/bits/elf_arm64.h b/libc/include/bits/elf_arm64.h
index 6bb8384..9330d7b 100644
--- a/libc/include/bits/elf_arm64.h
+++ b/libc/include/bits/elf_arm64.h
@@ -89,4 +89,9 @@
 #define R_AARCH64_TLSDESC               1031    /* 16-byte descriptor: resolver func + arg. */
 #define R_AARCH64_IRELATIVE             1032
 
+/* Dynamic array tags */
+#define DT_AARCH64_BTI_PLT              0x70000001
+#define DT_AARCH64_PAC_PLT              0x70000003
+#define DT_AARCH64_VARIANT_PCS          0x70000005
+
 #endif /* _AARCH64_ELF_MACHDEP_H_ */
diff --git a/libc/include/malloc.h b/libc/include/malloc.h
index 833fa59..a237254 100644
--- a/libc/include/malloc.h
+++ b/libc/include/malloc.h
@@ -170,6 +170,16 @@
  * Available since API level 28.
  */
 #define M_PURGE (-101)
+/*
+ * mallopt() option for per-thread memory initialization tuning.
+ * The value argument should be one of:
+ * 1: Disable automatic heap initialization and, where possible, memory tagging,
+ *    on this thread.
+ * 0: Normal behavior.
+ *
+ * Available since API level 31.
+ */
+#define M_THREAD_DISABLE_MEM_INIT (-103)
 /**
  * mallopt() option to set the maximum number of items in the secondary
  * cache of the scudo allocator.
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index a224eab..a17a33f 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1476,8 +1476,8 @@
     # Used by libandroid_net
     android_getaddrinfofornet; # apex
 
-    # Used by libandroid_runtime, libmedia and libmediautils
-    android_mallopt; # apex
+    # Used by libandroid_runtime, libcutils, libmedia, and libmediautils
+    android_mallopt; # apex llndk
 } LIBC_P;
 
 LIBC_R { # introduced=R
@@ -1718,6 +1718,7 @@
     android_gethostbyaddrfornetcontext;
     android_gethostbynamefornet;
     android_gethostbynamefornetcontext;
+    android_run_on_all_threads;
     android_unsafe_frame_pointer_chase;
     arc4random_addrandom; # arm x86
     arc4random_stir; # arm x86
diff --git a/libc/malloc_debug/MapData.cpp b/libc/malloc_debug/MapData.cpp
index e8fbc54..ded81a2 100644
--- a/libc/malloc_debug/MapData.cpp
+++ b/libc/malloc_debug/MapData.cpp
@@ -116,14 +116,17 @@
     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;
     }
-    if (phdr.p_type == PT_LOAD && phdr.p_offset == entry->offset) {
+    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;
+      entry->load_bias = phdr.p_vaddr - phdr.p_offset;
       return;
     }
     addr += sizeof(phdr);
diff --git a/libc/malloc_hooks/Android.bp b/libc/malloc_hooks/Android.bp
index 77b523e..487f3fb 100644
--- a/libc/malloc_hooks/Android.bp
+++ b/libc/malloc_hooks/Android.bp
@@ -70,6 +70,7 @@
     cflags: [
         "-Wall",
         "-Werror",
+        "-O1",  // FIXME: http://b/169206016 - issues with aligned_alloc and -O2
     ],
     test_suites: ["general-tests"],
 }
diff --git a/libc/platform/bionic/malloc.h b/libc/platform/bionic/malloc.h
index f9eb03f..16ef3a0 100644
--- a/libc/platform/bionic/malloc.h
+++ b/libc/platform/bionic/malloc.h
@@ -105,6 +105,13 @@
   //   arg_size = sizeof(bool)
   M_INITIALIZE_GWP_ASAN = 10,
 #define M_INITIALIZE_GWP_ASAN M_INITIALIZE_GWP_ASAN
+  // Disable heap initialization across the whole process. If the hardware supports memory
+  // tagging, it also disables memory tagging. May be called at any time including
+  // when multiple threads are running. arg and arg_size are unused and must be set to 0.
+  // Note that the memory mitigations are only implemented in scudo and therefore this API call will
+  // have no effect when using another allocator.
+  M_DISABLE_MEMORY_MITIGATIONS = 11,
+#define M_DISABLE_MEMORY_MITIGATIONS M_DISABLE_MEMORY_MITIGATIONS
 };
 
 enum HeapTaggingLevel {
diff --git a/libc/platform/bionic/reserved_signals.h b/libc/platform/bionic/reserved_signals.h
index e8e517e..dab58af 100644
--- a/libc/platform/bionic/reserved_signals.h
+++ b/libc/platform/bionic/reserved_signals.h
@@ -43,9 +43,7 @@
 //   37 (__SIGRTMIN + 5)        coverage (libprofile-extras)
 //   38 (__SIGRTMIN + 6)        heapprofd ART managed heap dumps
 //   39 (__SIGRTMIN + 7)        fdtrack
-//
-// If you change this, also change __ndk_legacy___libc_current_sigrtmin
-// in <android/legacy_signal_inlines.h> to match.
+//   40 (__SIGRTMIN + 8)        android_run_on_all_threads (bionic/pthread_internal.cpp)
 
 #define BIONIC_SIGNAL_POSIX_TIMERS (__SIGRTMIN + 0)
 #define BIONIC_SIGNAL_BACKTRACE (__SIGRTMIN + 1)
@@ -53,8 +51,9 @@
 #define BIONIC_SIGNAL_PROFILER (__SIGRTMIN + 4)
 #define BIONIC_SIGNAL_ART_PROFILER (__SIGRTMIN + 6)
 #define BIONIC_SIGNAL_FDTRACK (__SIGRTMIN + 7)
+#define BIONIC_SIGNAL_RUN_ON_ALL_THREADS (__SIGRTMIN + 8)
 
-#define __SIGRT_RESERVED 8
+#define __SIGRT_RESERVED 9
 static inline __always_inline sigset64_t filter_reserved_signals(sigset64_t sigset, int how) {
   int (*block)(sigset64_t*, int);
   int (*unblock)(sigset64_t*, int);
@@ -83,5 +82,6 @@
   unblock(&sigset, __SIGRTMIN + 5);
   unblock(&sigset, __SIGRTMIN + 6);
   unblock(&sigset, __SIGRTMIN + 7);
+  unblock(&sigset, __SIGRTMIN + 8);
   return sigset;
 }
diff --git a/libc/private/bionic_asm.h b/libc/private/bionic_asm.h
index 6409563..6d4f7d5 100644
--- a/libc/private/bionic_asm.h
+++ b/libc/private/bionic_asm.h
@@ -35,6 +35,7 @@
 #define __bionic_asm_custom_entry(f)
 #define __bionic_asm_custom_end(f)
 #define __bionic_asm_function_type @function
+#define __bionic_asm_custom_note_gnu_section()
 
 #if defined(__aarch64__)
 #include <private/bionic_asm_arm64.h>
@@ -83,4 +84,7 @@
     .globl alias; \
     .equ alias, original
 
+#define NOTE_GNU_PROPERTY() \
+    __bionic_asm_custom_note_gnu_section()
+
 #endif
diff --git a/libc/private/bionic_asm_arm64.h b/libc/private/bionic_asm_arm64.h
index 463ca31..c11732a 100644
--- a/libc/private/bionic_asm_arm64.h
+++ b/libc/private/bionic_asm_arm64.h
@@ -41,3 +41,32 @@
 
 #undef __bionic_asm_function_type
 #define __bionic_asm_function_type %function
+
+#if defined(__ARM_FEATURE_BTI_DEFAULT)
+#define __bionic_asm_aarch64_feature_bti    (1 << 0)
+#undef __bionic_asm_custom_entry
+#define __bionic_asm_custom_entry(f)        hint #34  // BTI C
+#else
+#define __bionic_asm_aarch64_feature_bti    0
+#endif
+
+#if defined(__ARM_FEATURE_PAC_DEFAULT)
+#define __bionic_asm_aarch64_feature_pac    (1 << 1)
+#else
+#define __bionic_asm_aarch64_feature_pac    0
+#endif
+
+#undef __bionic_asm_custom_note_gnu_section
+#define __bionic_asm_custom_note_gnu_section() \
+    .pushsection .note.gnu.property, "a"; \
+    .balign 8; \
+    .long 4; \
+    .long 0x10; \
+    .long 0x5; /* NT_GNU_PROPERTY_TYPE_0 */ \
+    .asciz "GNU"; \
+    .long 0xc0000000; /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */ \
+    .long 4; \
+    .long (__bionic_asm_aarch64_feature_pac | \
+           __bionic_asm_aarch64_feature_bti); \
+    .long 0; \
+    .popsection; \
diff --git a/libc/private/bionic_asm_dwarf_exprs.h b/libc/private/bionic_asm_dwarf_exprs.h
new file mode 100644
index 0000000..f988c6e
--- /dev/null
+++ b/libc/private/bionic_asm_dwarf_exprs.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+// Define assembler macros for generating DWARF CFI instructions that use DWARF expressions.
+// Assemblers don't natively support DWARF expressions, so use the C preprocessor and assembler
+// macros to lower them to .cfi_escape directives.
+//
+// Signal trampolines need to use DWARF expressions to record the locations of saved registers,
+// because the offsets from the restored SP to the saved registers is variable. e.g. A signal frame
+// can have optional FP/SIMD extensions, and there may be extra padding if the interrupted SP wasn't
+// aligned.
+
+// DWARF constants.
+#define DW_CFA_def_cfa_expression 0x0f
+#define DW_CFA_expression 0x10
+#define DW_OP_breg0 0x70
+#define DW_OP_deref 0x06
+
+// Return the size of a small uleb128 value: either 1 or 2 bytes
+#define ULEB128_14BIT_SIZE(val) \
+  (1 + (((val) > 0x7f) & 1))
+
+// Return the size of a small sleb128 value: either 1 or 2 bytes
+#define SLEB128_14BIT_SIZE(val)       \
+  (1 + (((val) < -0x40) & 1) +        \
+       (((val) > 0x3f) & 1)     )
+
+// Output a 1 or 2-byte CFI uleb128 absolute value.
+.macro m_cfi_uleb128 val
+  .if (\val) < 0 || (\val) > 0x3fff
+    .error "m_cfi_uleb128 value is out of range (\val)"
+  .elseif (\val) > 0x7f
+    .cfi_escape ((\val) & 0x7f) | 0x80
+    .cfi_escape (\val) >> 7
+  .else
+    .cfi_escape (\val)
+  .endif
+.endm
+
+// Output a 1 or 2-byte CFI sleb128 absolute value.
+.macro m_cfi_sleb128 val
+  .if (\val) < -0x2000 || (\val) > 0x1fff
+    .error "m_cfi_sleb128 value is out of range (\val)"
+  .elseif (\val) < -0x40 || (\val) > 0x3f
+    .cfi_escape ((\val) & 0x7f) | 0x80
+    .cfi_escape ((\val) >> 7) & 0x7f
+  .else
+    .cfi_escape (\val) & 0x7f
+  .endif
+.endm
+
+.macro check_base_reg reg_no
+  .if (\reg_no) < 0 || (\reg_no) > 31
+    .error "base register is out of range for DW_OP_breg0..DW_OP_breg31 (\reg_no)"
+  .endif
+.endm
+
+// Set CFA to the expression, *(base_reg + offset)
+.macro m_cfi_def_cfa_deref base_reg, offset
+  check_base_reg (\base_reg)
+  .cfi_escape DW_CFA_def_cfa_expression
+  m_cfi_uleb128 (1 + SLEB128_14BIT_SIZE(\offset) + 1)   // size of DWARF expression in bytes
+  .cfi_escape DW_OP_breg0 + (\base_reg)                 // expr: 1 byte
+  m_cfi_sleb128 (\offset)                               // expr: 1 or 2 bytes
+  .cfi_escape DW_OP_deref                               // expr: 1 byte
+.endm
+
+// Set the address of the register's previous value to the expression, (base_reg + offset)
+.macro m_cfi_breg_offset dest_reg, base_reg, offset
+  check_base_reg (\base_reg)
+  .cfi_escape DW_CFA_expression
+  m_cfi_uleb128 (\dest_reg)
+  m_cfi_uleb128 (1 + SLEB128_14BIT_SIZE(\offset)) // size of DWARF expression in bytes
+  .cfi_escape DW_OP_breg0 + (\base_reg)           // expr: 1 byte
+  m_cfi_sleb128 (\offset)                         // expr: 1 or 2 bytes
+.endm
diff --git a/libc/private/bionic_defs.h b/libc/private/bionic_defs.h
index 1d4f86b..5a48f25 100644
--- a/libc/private/bionic_defs.h
+++ b/libc/private/bionic_defs.h
@@ -33,7 +33,15 @@
  * This label is used to mark libc/libdl symbols that may need to be replaced
  * by native bridge implementation.
  */
+#ifdef __ANDROID_NATIVE_BRIDGE__
 #define __BIONIC_WEAK_FOR_NATIVE_BRIDGE __attribute__((__weak__, __noinline__))
 #define __BIONIC_WEAK_VARIABLE_FOR_NATIVE_BRIDGE __attribute__((__weak__))
+#define __BIONIC_WEAK_FOR_NATIVE_BRIDGE_INLINE \
+  __BIONIC_WEAK_FOR_NATIVE_BRIDGE extern "C" __LIBC_HIDDEN__
+#else
+#define __BIONIC_WEAK_FOR_NATIVE_BRIDGE
+#define __BIONIC_WEAK_VARIABLE_FOR_NATIVE_BRIDGE
+#define __BIONIC_WEAK_FOR_NATIVE_BRIDGE_INLINE static inline
+#endif
 
 #endif /* __BIONIC_PRIVATE_BIONIC_DEFS_H_ */
diff --git a/libc/tools/gensyscalls.py b/libc/tools/gensyscalls.py
index 0271a04..0e0e25f 100755
--- a/libc/tools/gensyscalls.py
+++ b/libc/tools/gensyscalls.py
@@ -459,6 +459,8 @@
         if syscall.has_key("asm-%s" % arch):
             print(syscall["asm-%s" % arch])
 
+    if arch == 'arm64':
+        print('\nNOTE_GNU_PROPERTY()\n')
 
 if __name__ == "__main__":
     if len(sys.argv) < 2:
diff --git a/libdl/Android.bp b/libdl/Android.bp
index 6a3a82e..1a5439f 100644
--- a/libdl/Android.bp
+++ b/libdl/Android.bp
@@ -116,7 +116,7 @@
         symbol_file: "libdl.map.txt",
         versions: [
             "29",
-            "10000",
+            "current",
         ],
     },
 
@@ -124,6 +124,10 @@
         "//apex_available:platform",
         "com.android.runtime",
     ],
+
+    lto: {
+        never: true,
+    },
 }
 
 cc_library {
@@ -193,13 +197,17 @@
 
     stubs: {
         symbol_file: "libdl_android.map.txt",
-        versions: ["10000"],
+        versions: ["current"],
     },
 
     apex_available: [
         "//apex_available:platform",
         "com.android.runtime",
     ],
+
+    lto: {
+        never: true,
+    },
 }
 
 ndk_library {
diff --git a/libm/Android.bp b/libm/Android.bp
index 6a348e1..7f96975 100644
--- a/libm/Android.bp
+++ b/libm/Android.bp
@@ -498,7 +498,7 @@
         symbol_file: "libm.map.txt",
         versions: [
             "29",
-            "10000",
+            "current",
         ],
     },
 
@@ -506,6 +506,10 @@
         "//apex_available:platform",
         "com.android.runtime",
     ],
+
+    lto: {
+        never: true,
+    },
 }
 
 ndk_library {
diff --git a/libm/arm64/lrint.S b/libm/arm64/lrint.S
index 5f95ae8..e835d08 100644
--- a/libm/arm64/lrint.S
+++ b/libm/arm64/lrint.S
@@ -32,3 +32,5 @@
 ALIAS_SYMBOL(llrint, lrint);
 
 ALIAS_SYMBOL(llrintf, lrintf);
+
+NOTE_GNU_PROPERTY()
diff --git a/libm/arm64/sqrt.S b/libm/arm64/sqrt.S
index 3a58ef3..0659b13 100644
--- a/libm/arm64/sqrt.S
+++ b/libm/arm64/sqrt.S
@@ -25,3 +25,5 @@
   fsqrt s0, s0
   ret
 END(sqrtf)
+
+NOTE_GNU_PROPERTY()
diff --git a/linker/Android.bp b/linker/Android.bp
index 08b2c7b..15585a7 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -39,6 +39,9 @@
         "linker_wrapper.cpp",
     ],
     arch: {
+        arm64: {
+            srcs: ["arch/arm64/begin.S"],
+        },
         x86_64: {
             srcs: ["arch/x86_64/begin.S"],
         },
@@ -166,6 +169,7 @@
         "linker_namespaces.cpp",
         "linker_logger.cpp",
         "linker_mapped_file_fragment.cpp",
+        "linker_note_gnu_property.cpp",
         "linker_phdr.cpp",
         "linker_relocate.cpp",
         "linker_sdk_versions.cpp",
@@ -353,6 +357,10 @@
             ],
         }
     },
+
+    lto: {
+        never: true,
+    },
 }
 
 // ========================================================
@@ -423,6 +431,10 @@
         "//apex_available:platform",
         "com.android.runtime",
     ],
+
+    lto: {
+        never: true,
+    },
 }
 
 cc_test {
@@ -444,6 +456,7 @@
         "linker_block_allocator_test.cpp",
         "linker_config_test.cpp",
         "linked_list_test.cpp",
+        "linker_note_gnu_property_test.cpp",
         "linker_sleb128_test.cpp",
         "linker_utils_test.cpp",
         "linker_gnu_hash_test.cpp",
@@ -452,6 +465,7 @@
         "linker_block_allocator.cpp",
         "linker_config.cpp",
         "linker_debug.cpp",
+        "linker_note_gnu_property.cpp",
         "linker_test_globals.cpp",
         "linker_utils.cpp",
     ],
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 302e4b3..77f754c 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -3141,6 +3141,14 @@
         // resolves everything eagerly, so these can be ignored.
         break;
 
+#if defined(__aarch64__)
+      case DT_AARCH64_BTI_PLT:
+      case DT_AARCH64_PAC_PLT:
+      case DT_AARCH64_VARIANT_PCS:
+        // Ignored: AArch64 processor-specific dynamic array tags.
+        break;
+#endif
+
       default:
         if (!relocating_linker) {
           const char* tag_name;
diff --git a/linker/linker.h b/linker/linker.h
index 3e851da..e1775fb 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -181,3 +181,9 @@
 int get_application_target_sdk_version();
 ElfW(Versym) find_verdef_version_index(const soinfo* si, const version_info* vi);
 bool validate_verdef_section(const soinfo* si);
+
+struct platform_properties {
+#if defined(__aarch64__)
+  bool bti_supported = false;
+#endif
+};
diff --git a/linker/linker_globals.cpp b/linker/linker_globals.cpp
index 31da02c..4a17d09 100644
--- a/linker/linker_globals.cpp
+++ b/linker/linker_globals.cpp
@@ -40,6 +40,8 @@
 
 std::unordered_map<uintptr_t, soinfo*> g_soinfo_handles_map;
 
+platform_properties g_platform_properties;
+
 static char __linker_dl_err_buf[768];
 
 char* linker_get_error_buffer() {
diff --git a/linker/linker_globals.h b/linker/linker_globals.h
index 83cedca..0998629 100644
--- a/linker/linker_globals.h
+++ b/linker/linker_globals.h
@@ -79,11 +79,14 @@
 
 struct soinfo;
 struct android_namespace_t;
+struct platform_properties;
 
 extern android_namespace_t g_default_namespace;
 
 extern std::unordered_map<uintptr_t, soinfo*> g_soinfo_handles_map;
 
+extern platform_properties g_platform_properties;
+
 // Error buffer "variable"
 char* linker_get_error_buffer();
 size_t linker_get_error_buffer_size();
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index 41bb4ba..aad8f6f 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -297,6 +297,13 @@
   return result;
 }
 
+static void platform_properties_init() {
+#if defined(__aarch64__)
+  const unsigned long hwcap2 = getauxval(AT_HWCAP2);
+  g_platform_properties.bti_supported = (hwcap2 & HWCAP2_BTI) != 0;
+#endif
+}
+
 static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load) {
   ProtectedDataGuard guard;
 
@@ -311,6 +318,9 @@
   // Initialize system properties
   __system_properties_init(); // may use 'environ'
 
+  // Initialize platform properties.
+  platform_properties_init();
+
   // Register the debuggerd signal handler.
   linker_debuggerd_init();
 
@@ -381,6 +391,20 @@
   solinker->set_realpath(interp);
   init_link_map_head(*solinker);
 
+#if defined(__aarch64__)
+  if (exe_to_load == nullptr) {
+    // Kernel does not add PROT_BTI to executable pages of the loaded ELF.
+    // Apply appropriate protections here if it is needed.
+    auto note_gnu_property = GnuPropertySection(somain);
+    if (note_gnu_property.IsBTICompatible() &&
+        (phdr_table_protect_segments(somain->phdr, somain->phnum, somain->load_bias,
+                                     &note_gnu_property) < 0)) {
+      __linker_error("error: can't protect segments for \"%s\": %s", exe_info.path.c_str(),
+                     strerror(errno));
+    }
+  }
+#endif
+
   // Register the main executable and the linker upfront to have
   // gdb aware of them before loading the rest of the dependency
   // tree.
diff --git a/linker/linker_note_gnu_property.cpp b/linker/linker_note_gnu_property.cpp
new file mode 100644
index 0000000..be1aebc
--- /dev/null
+++ b/linker/linker_note_gnu_property.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "linker_note_gnu_property.h"
+
+#include <elf.h>
+#include <link.h>
+
+#include "linker.h"
+#include "linker_debug.h"
+#include "linker_globals.h"
+#include "linker_soinfo.h"
+
+GnuPropertySection::GnuPropertySection(const soinfo* si)
+    : GnuPropertySection(si->phdr, si->phnum, si->load_bias, si->get_realpath()) {}
+
+GnuPropertySection::GnuPropertySection(const ElfW(Phdr)* phdr, size_t phdr_count,
+                                       const ElfW(Addr) load_bias, const char* name) {
+  // Try to find PT_GNU_PROPERTY segment.
+  auto note_gnu_property = FindSegment(phdr, phdr_count, load_bias, name);
+  // Perform some validity checks.
+  if (note_gnu_property && SanityCheck(note_gnu_property, name)) {
+    // Parse section.
+    Parse(note_gnu_property, name);
+  }
+}
+
+const ElfW(NhdrGNUProperty)* GnuPropertySection::FindSegment(const ElfW(Phdr)* phdr,
+                                                             size_t phdr_count,
+                                                             const ElfW(Addr) load_bias,
+                                                             const char* name) const {
+  // According to Linux gABI extension this segment should contain
+  // .note.gnu.property section only.
+  if (phdr != nullptr) {
+    for (size_t i = 0; i < phdr_count; ++i) {
+      if (phdr[i].p_type != PT_GNU_PROPERTY) {
+        continue;
+      }
+
+      TRACE("\"%s\" PT_GNU_PROPERTY: found at segment index %zu", name, i);
+
+      // Check segment size.
+      if (phdr[i].p_memsz < sizeof(ElfW(NhdrGNUProperty))) {
+        DL_ERR_AND_LOG(
+            "\"%s\" PT_GNU_PROPERTY segment is too small. Segment "
+            "size is %zu, minimum is %zu.",
+            name, static_cast<size_t>(phdr[i].p_memsz), sizeof(ElfW(NhdrGNUProperty)));
+        return nullptr;
+      }
+
+      // PT_GNU_PROPERTY contains .note.gnu.property which has SHF_ALLOC
+      // attribute, therefore it is loaded.
+      auto note_nhdr = reinterpret_cast<ElfW(NhdrGNUProperty)*>(load_bias + phdr[i].p_vaddr);
+
+      // Check that the n_descsz <= p_memsz
+      if ((phdr[i].p_memsz - sizeof(ElfW(NhdrGNUProperty))) < note_nhdr->nhdr.n_descsz) {
+        DL_ERR_AND_LOG(
+            "\"%s\" PT_GNU_PROPERTY segment p_memsz (%zu) is too small for note n_descsz (%zu).",
+            name, static_cast<size_t>(phdr[i].p_memsz),
+            static_cast<size_t>(note_nhdr->nhdr.n_descsz));
+        return nullptr;
+      }
+
+      return note_nhdr;
+    }
+  }
+
+  TRACE("\"%s\" PT_GNU_PROPERTY: not found", name);
+  return nullptr;
+}
+
+bool GnuPropertySection::SanityCheck(const ElfW(NhdrGNUProperty)* note_nhdr,
+                                     const char* name) const {
+  // Check .note section type
+  if (note_nhdr->nhdr.n_type != NT_GNU_PROPERTY_TYPE_0) {
+    DL_ERR_AND_LOG("\"%s\" .note.gnu.property: unexpected note type. Expected %u, got %u.", name,
+                   NT_GNU_PROPERTY_TYPE_0, note_nhdr->nhdr.n_type);
+    return false;
+  }
+
+  if (note_nhdr->nhdr.n_namesz != 4) {
+    DL_ERR_AND_LOG("\"%s\" .note.gnu.property: unexpected name size. Expected 4, got %u.", name,
+                   note_nhdr->nhdr.n_namesz);
+    return false;
+  }
+
+  if (strncmp(note_nhdr->n_name, "GNU", 4) != 0) {
+    DL_ERR_AND_LOG("\"%s\" .note.gnu.property: unexpected name. Expected 'GNU', got '%s'.", name,
+                   note_nhdr->n_name);
+    return false;
+  }
+
+  return true;
+}
+
+bool GnuPropertySection::Parse(const ElfW(NhdrGNUProperty)* note_nhdr, const char* name) {
+  // The total length of the program property array is in _bytes_.
+  ElfW(Word) offset = 0;
+  while (offset < note_nhdr->nhdr.n_descsz) {
+    DEBUG("\"%s\" .note.gnu.property: processing at offset 0x%x", name, offset);
+
+    // At least the "header" part must fit.
+    // The ABI doesn't say that pr_datasz can't be 0.
+    if ((note_nhdr->nhdr.n_descsz - offset) < sizeof(ElfW(Prop))) {
+      DL_ERR_AND_LOG(
+          "\"%s\" .note.gnu.property: no more space left for a "
+          "Program Property Note header.",
+          name);
+      return false;
+    }
+
+    // Loop on program property array.
+    const ElfW(Prop)* property = reinterpret_cast<const ElfW(Prop)*>(&note_nhdr->n_desc[offset]);
+    const ElfW(Word) property_size =
+        align_up(sizeof(ElfW(Prop)) + property->pr_datasz, sizeof(ElfW(Addr)));
+    if ((note_nhdr->nhdr.n_descsz - offset) < property_size) {
+      DL_ERR_AND_LOG(
+          "\"%s\" .note.gnu.property: property descriptor size is "
+          "invalid. Expected at least %u bytes, got %u.",
+          name, property_size, note_nhdr->nhdr.n_descsz - offset);
+      return false;
+    }
+
+    // Cache found properties.
+    switch (property->pr_type) {
+#if defined(__aarch64__)
+      case GNU_PROPERTY_AARCH64_FEATURE_1_AND: {
+        if (property->pr_datasz != 4) {
+          DL_ERR_AND_LOG(
+              "\"%s\" .note.gnu.property: property descriptor size is "
+              "invalid. Expected %u bytes for GNU_PROPERTY_AARCH64_FEATURE_1_AND, got %u.",
+              name, 4, property->pr_datasz);
+          return false;
+        }
+
+        const ElfW(Word) flags = *reinterpret_cast<const ElfW(Word)*>(&property->pr_data[0]);
+        properties_.bti_compatible = (flags & GNU_PROPERTY_AARCH64_FEATURE_1_BTI) != 0;
+        if (properties_.bti_compatible) {
+          INFO("[ BTI compatible: \"%s\" ]", name);
+        }
+        break;
+      }
+#endif
+      default:
+        DEBUG("\"%s\" .note.gnu.property: found property pr_type %u pr_datasz 0x%x", name,
+              property->pr_type, property->pr_datasz);
+        break;
+    }
+
+    // Move offset, this should be safe to add because of previous checks.
+    offset += property_size;
+  }
+
+  return true;
+}
+
+#if defined(__aarch64__)
+bool GnuPropertySection::IsBTICompatible() const {
+  return (g_platform_properties.bti_supported && properties_.bti_compatible);
+}
+#endif
diff --git a/linker/linker_note_gnu_property.h b/linker/linker_note_gnu_property.h
new file mode 100644
index 0000000..b8b4ef7
--- /dev/null
+++ b/linker/linker_note_gnu_property.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <elf.h>
+#include <link.h>
+
+#include "linker_soinfo.h"
+
+// The Elf* structures below are derived from the document
+// Linux Extensions to gABI (https://github.com/hjl-tools/linux-abi/wiki).
+// Essentially, these types would be defined in <elf.h>, but this is not
+// the case at the moment.
+
+struct Elf32_Prop {
+  Elf32_Word pr_type;
+  Elf32_Word pr_datasz;
+  char pr_data[0];
+};
+
+// On 32-bit machines this should be 4-byte aligned.
+struct Elf32_NhdrGNUProperty {
+  Elf32_Nhdr nhdr;
+  char n_name[4];
+  char n_desc[0];
+};
+
+struct Elf64_Prop {
+  Elf64_Word pr_type;
+  Elf64_Word pr_datasz;
+  char pr_data[0];
+};
+
+// On 64-bit machines this should be 8-byte aligned.
+struct Elf64_NhdrGNUProperty {
+  Elf64_Nhdr nhdr;
+  char n_name[4];
+  char n_desc[0];
+};
+
+struct ElfProgramProperty {
+#if defined(__aarch64__)
+  bool bti_compatible = false;
+#endif
+};
+
+// Representation of the .note.gnu.property section found in the segment
+// with p_type = PT_GNU_PROPERTY.
+class GnuPropertySection {
+ public:
+  GnuPropertySection(){};
+  explicit GnuPropertySection(const soinfo* si);
+  GnuPropertySection(const ElfW(Phdr)* phdr, size_t phdr_count, const ElfW(Addr) load_bias,
+                     const char* name);
+
+#if defined(__aarch64__)
+  bool IsBTICompatible() const;
+#endif
+
+ private:
+  const ElfW(NhdrGNUProperty)* FindSegment(const ElfW(Phdr)* phdr, size_t phdr_count,
+                                           const ElfW(Addr) load_bias, const char* name) const;
+  bool SanityCheck(const ElfW(NhdrGNUProperty)* note_nhdr, const char* name) const;
+  bool Parse(const ElfW(NhdrGNUProperty)* note_nhdr, const char* name);
+
+  ElfProgramProperty properties_ __unused;
+};
diff --git a/linker/linker_note_gnu_property_test.cpp b/linker/linker_note_gnu_property_test.cpp
new file mode 100644
index 0000000..41fc47b
--- /dev/null
+++ b/linker/linker_note_gnu_property_test.cpp
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "linker.h"
+#include "linker_globals.h"
+#include "linker_note_gnu_property.h"
+#include "platform/bionic/macros.h"
+
+#define SONAME "test_so"
+
+static char error_buffer[1024];
+
+char* linker_get_error_buffer() {
+  return error_buffer;
+}
+
+size_t linker_get_error_buffer_size() {
+  return std::size(error_buffer);
+}
+
+static void reset_error_buffer() {
+  error_buffer[0] = '\0';
+}
+
+platform_properties g_platform_properties {
+#if defined(__aarch64__)
+  // Assume "hardware" supports Armv8.5-A BTI.
+  .bti_supported = true
+#endif
+};
+
+// Helper macro to make the test cleaner.
+#define PHDR_WITH_NOTE_GNU_PROPERTY(__prop)                                   \
+  reset_error_buffer();                                                       \
+  ElfW(Phdr) phdrs[] = {                                                      \
+      {.p_type = PT_LOAD},                                                    \
+      {                                                                       \
+          .p_type = PT_GNU_PROPERTY,                                          \
+          .p_vaddr = reinterpret_cast<ElfW(Addr)>(__prop),                    \
+          .p_memsz = sizeof(ElfW(NhdrGNUProperty)) + (__prop)->nhdr.n_descsz, \
+      },                                                                      \
+      {.p_type = PT_NULL},                                                    \
+  };                                                                          \
+  auto note = GnuPropertySection(&phdrs[0], std::size(phdrs), 0, SONAME)
+
+// Helper to check for no error message.
+#define ASSERT_NO_ERROR_MSG() ASSERT_STREQ(error_buffer, "")
+
+// Helper to check expected error message.
+#define ASSERT_ERROR_MSG_EQ(__expected) ASSERT_STREQ(error_buffer, "\"" SONAME "\" " __expected)
+
+static void test_bti_not_supported(GnuPropertySection& note __unused) {
+#if defined(__aarch64__)
+  ASSERT_FALSE(note.IsBTICompatible());
+#endif
+}
+
+#if defined(__aarch64__)
+static void test_bti_supported(GnuPropertySection& note __unused) {
+  ASSERT_TRUE(note.IsBTICompatible());
+}
+#endif
+
+// Helper class to build a well-formed .note.gnu.property section.
+class GnuPropertySectionBuilder {
+ public:
+  GnuPropertySectionBuilder() {
+    note = reinterpret_cast<ElfW(NhdrGNUProperty)*>(&section[0]);
+    note->nhdr.n_namesz = 4;
+    note->nhdr.n_descsz = 0;
+    note->nhdr.n_type = NT_GNU_PROPERTY_TYPE_0;
+    memcpy(note->n_name, "GNU", 4);
+  }
+
+  template <typename T>
+  bool push(ElfW(Word) pr_type, ElfW(Word) pr_datasz, const T* pr_data) {
+    // Must be aligned.
+    const uintptr_t addition = align_up(pr_datasz, sizeof(ElfW(Addr)));
+    if ((offset() + addition) > kMaxSectionSize) {
+      return false;
+    }
+    ++entries;
+    ElfW(Prop)* prop = reinterpret_cast<ElfW(Prop)*>(&section[offset()]);
+    // Header
+    prop->pr_type = pr_type;
+    prop->pr_datasz = pr_datasz;
+    step(2 * sizeof(ElfW(Word)));
+    // Data
+    memcpy(&section[offset()], reinterpret_cast<const void*>(pr_data), pr_datasz);
+    step(pr_datasz);
+    // Padding
+    memset(&section[offset()], 0xAA, addition - pr_datasz);
+    step(addition - pr_datasz);
+    return true;
+  }
+
+  ElfW(NhdrGNUProperty)* data() const { return note; }
+
+  void dump() const {
+    std::cout << ".note.gnu.property\n";
+    dump_member("n_namesz", note->nhdr.n_namesz);
+    dump_member("n_descsz", note->nhdr.n_descsz);
+    dump_member("n_type  ", note->nhdr.n_type);
+    dump_member("n_name  ", note->n_name);
+    dump_member("entries ", entries);
+    if (entries > 0) {
+      std::cout << "    raw data:";
+      const uintptr_t offset = note->nhdr.n_descsz + 16;
+      for (uintptr_t offs = 16; offs < offset; ++offs) {
+        std::cout << std::hex;
+        if ((offs % 8) == 0) {
+          std::cout << "\n   ";
+        }
+        auto value = static_cast<unsigned>(section[offs]);
+        std::cout << " ";
+        if (value < 0x10) {
+          std::cout << "0";
+        }
+        std::cout << static_cast<unsigned>(section[offs]);
+      }
+      std::cout << std::dec << "\n";
+    }
+  }
+
+  void corrupt_n_descsz(ElfW(Word) n_descsz) { note->nhdr.n_descsz = n_descsz; }
+
+ private:
+  template <typename T>
+  void dump_member(const char* name, T value) const {
+    std::cout << "  " << name << " " << value << "\n";
+  }
+
+  ElfW(Word) offset() const { return note->nhdr.n_descsz + 16; }
+
+  template <typename T>
+  void step(T value) {
+    note->nhdr.n_descsz += static_cast<ElfW(Word)>(value);
+  }
+
+  static const size_t kMaxSectionSize = 1024;
+
+  alignas(8) uint8_t section[kMaxSectionSize];
+  ElfW(NhdrGNUProperty)* note;
+  size_t entries = 0;
+};
+
+// Tests that the default constructed instance does not report support
+// for Armv8.5-A BTI.
+TEST(note_gnu_property, default) {
+  GnuPropertySection note;
+  test_bti_not_supported(note);
+  ASSERT_NO_ERROR_MSG();
+}
+
+// Tests that an instance without valid phdr pointer does not report
+// support for Armv8.5-A BTI.
+TEST(note_gnu_property, phdr_null) {
+  auto note = GnuPropertySection(nullptr, 0, 0, SONAME);
+  test_bti_not_supported(note);
+  ASSERT_NO_ERROR_MSG();
+}
+
+// Tests that an instance without finding PT_GNU_PROPERTY does not
+// report support for Armv8.5-A BTI.
+TEST(note_gnu_property, no_pt_gnu_property) {
+  ElfW(Phdr) phdrs[] = {
+      {.p_type = PT_LOAD},
+      {.p_type = PT_NULL},
+  };
+
+  reset_error_buffer();
+  auto note = GnuPropertySection(&phdrs[0], std::size(phdrs), 0, SONAME);
+  test_bti_not_supported(note);
+  ASSERT_NO_ERROR_MSG();
+}
+
+// Tests the validity check for invalid PT_GNU_PROPERTY size.
+TEST(note_gnu_property, pt_gnu_property_bad_size) {
+  ElfW(Phdr) phdrs[] = {
+      {.p_type = PT_LOAD},
+      {
+          .p_type = PT_GNU_PROPERTY,
+          .p_vaddr = 0,
+          .p_memsz = sizeof(ElfW(NhdrGNUProperty)) - 1,  // Invalid
+      },
+      {.p_type = PT_NULL},
+  };
+
+  reset_error_buffer();
+  auto note = GnuPropertySection(&phdrs[0], std::size(phdrs), 0, SONAME);
+  test_bti_not_supported(note);
+  ASSERT_ERROR_MSG_EQ("PT_GNU_PROPERTY segment is too small. Segment size is 15, minimum is 16.");
+}
+
+// Tests that advertised n_descsz should still fit into p_memsz.
+TEST(note_gnu_property, pt_gnu_property_too_small) {
+  ElfW(NhdrGNUProperty) prop = {
+      .nhdr = {.n_namesz = PT_GNU_PROPERTY, .n_descsz = 1, .n_type = NT_GNU_PROPERTY_TYPE_0},
+      .n_name = "GNU",
+  };
+  ElfW(Phdr) phdrs[] = {
+      {
+          .p_type = PT_GNU_PROPERTY,
+          .p_vaddr = reinterpret_cast<ElfW(Addr)>(&prop),
+          .p_memsz = sizeof(ElfW(NhdrGNUProperty)),  // Off by one
+      },
+  };
+
+  reset_error_buffer();
+  auto note = GnuPropertySection(&phdrs[0], std::size(phdrs), 0, SONAME);
+  test_bti_not_supported(note);
+  ASSERT_ERROR_MSG_EQ("PT_GNU_PROPERTY segment p_memsz (16) is too small for note n_descsz (1).");
+}
+
+// Tests the validity check for invalid .note.gnu.property type.
+TEST(note_gnu_property, pt_gnu_property_bad_type) {
+  ElfW(NhdrGNUProperty) prop = {
+      .nhdr =
+          {
+              .n_namesz = 4,
+              .n_descsz = 0,
+              .n_type = NT_GNU_PROPERTY_TYPE_0 - 1  // Invalid
+          },
+      .n_name = "GNU",
+  };
+  PHDR_WITH_NOTE_GNU_PROPERTY(&prop);
+  test_bti_not_supported(note);
+  ASSERT_ERROR_MSG_EQ(".note.gnu.property: unexpected note type. Expected 5, got 4.");
+}
+
+// Tests the validity check for invalid .note.gnu.property name size.
+TEST(note_gnu_property, pt_gnu_property_bad_namesz) {
+  ElfW(NhdrGNUProperty) prop = {
+      .nhdr = {.n_namesz = 3,  // Invalid
+               .n_descsz = 0,
+               .n_type = NT_GNU_PROPERTY_TYPE_0},
+      .n_name = "GNU",
+  };
+  PHDR_WITH_NOTE_GNU_PROPERTY(&prop);
+  test_bti_not_supported(note);
+  ASSERT_ERROR_MSG_EQ(".note.gnu.property: unexpected name size. Expected 4, got 3.");
+}
+
+// Tests the validity check for invalid .note.gnu.property name.
+TEST(note_gnu_property, pt_gnu_property_bad_name) {
+  ElfW(NhdrGNUProperty) prop = {
+      .nhdr = {.n_namesz = 4, .n_descsz = 0, .n_type = NT_GNU_PROPERTY_TYPE_0},
+      .n_name = "ABC",  // Invalid
+  };
+  PHDR_WITH_NOTE_GNU_PROPERTY(&prop);
+  test_bti_not_supported(note);
+  ASSERT_ERROR_MSG_EQ(".note.gnu.property: unexpected name. Expected 'GNU', got 'ABC'.");
+}
+
+// Tests the validity check for not enough space for a Program Property header.
+TEST(note_gnu_property, pt_gnu_property_pphdr_no_space) {
+  ElfW(NhdrGNUProperty) prop = {
+      .nhdr = {.n_namesz = 4,
+               .n_descsz = 7,  // Invalid
+               .n_type = NT_GNU_PROPERTY_TYPE_0},
+      .n_name = "GNU",
+  };
+  PHDR_WITH_NOTE_GNU_PROPERTY(&prop);
+  test_bti_not_supported(note);
+  ASSERT_ERROR_MSG_EQ(".note.gnu.property: no more space left for a Program Property Note header.");
+}
+
+// Tests an empty .note.gnu.property.
+TEST(note_gnu_property, pt_gnu_property_no_data) {
+  GnuPropertySectionBuilder prop;
+  PHDR_WITH_NOTE_GNU_PROPERTY(prop.data());
+  test_bti_not_supported(note);
+  ASSERT_NO_ERROR_MSG();
+}
+
+// Tests a .note.gnu.property section with elements with pr_datasz = 0.
+TEST(note_gnu_property, pt_gnu_property_no_prop) {
+  GnuPropertySectionBuilder prop;
+  ASSERT_TRUE(prop.push(1, 0, (void*)nullptr));
+  ASSERT_TRUE(prop.push(2, 0, (void*)nullptr));
+  ASSERT_TRUE(prop.push(3, 0, (void*)nullptr));
+  PHDR_WITH_NOTE_GNU_PROPERTY(prop.data());
+  test_bti_not_supported(note);
+  ASSERT_NO_ERROR_MSG();
+}
+
+// Tests that GNU_PROPERTY_AARCH64_FEATURE_1_AND must have pr_datasz = 4.
+TEST(note_gnu_property, pt_gnu_property_bad_pr_datasz) {
+#if defined(__aarch64__)
+  GnuPropertySectionBuilder prop;
+  ElfW(Word) pr_data[] = {GNU_PROPERTY_AARCH64_FEATURE_1_BTI, 0, 0};
+  ASSERT_TRUE(prop.push(GNU_PROPERTY_AARCH64_FEATURE_1_AND, 12, &pr_data));
+  PHDR_WITH_NOTE_GNU_PROPERTY(prop.data());
+  test_bti_not_supported(note);
+  ASSERT_ERROR_MSG_EQ(
+      ".note.gnu.property: property descriptor size is invalid. Expected 4 bytes for "
+      "GNU_PROPERTY_AARCH64_FEATURE_1_AND, got 12.");
+#else
+  GTEST_SKIP() << "BTI is not supported on this architecture.";
+#endif
+}
+
+// Tests a .note.gnu.property section with only GNU_PROPERTY_AARCH64_FEATURE_1_BTI property array.
+TEST(note_gnu_property, pt_gnu_property_ok_1) {
+#if defined(__aarch64__)
+  GnuPropertySectionBuilder prop;
+  ElfW(Word) pr_data[] = {GNU_PROPERTY_AARCH64_FEATURE_1_BTI};
+  ASSERT_TRUE(prop.push(GNU_PROPERTY_AARCH64_FEATURE_1_AND, sizeof(pr_data), &pr_data));
+  PHDR_WITH_NOTE_GNU_PROPERTY(prop.data());
+  ASSERT_NO_ERROR_MSG();
+  test_bti_supported(note);
+#else
+  GTEST_SKIP() << "BTI is not supported on this architecture.";
+#endif
+}
+
+// Tests a .note.gnu.property section with only GNU_PROPERTY_AARCH64_FEATURE_1_BTI property array.
+TEST(note_gnu_property, pt_gnu_property_ok_2) {
+#if defined(__aarch64__)
+  GnuPropertySectionBuilder prop;
+  ElfW(Word) pr_data[] = {static_cast<ElfW(Word)>(~GNU_PROPERTY_AARCH64_FEATURE_1_BTI)};
+  ASSERT_TRUE(prop.push(GNU_PROPERTY_AARCH64_FEATURE_1_AND, sizeof(pr_data), &pr_data));
+  PHDR_WITH_NOTE_GNU_PROPERTY(prop.data());
+  ASSERT_NO_ERROR_MSG();
+  test_bti_not_supported(note);
+#else
+  GTEST_SKIP() << "BTI is not supported on this architecture.";
+#endif
+}
+
+// Tests a .note.gnu.property section with more property arrays.
+TEST(note_gnu_property, pt_gnu_property_ok_3) {
+#if defined(__aarch64__)
+  GnuPropertySectionBuilder prop;
+
+  ElfW(Word) pr_data_0[8] = {0xCD};
+  ASSERT_TRUE(prop.push(1, 4, &pr_data_0));
+  ASSERT_TRUE(prop.push(2, 3, &pr_data_0));
+  ASSERT_TRUE(prop.push(3, 8, &pr_data_0));
+
+  ElfW(Word) pr_data[] = {GNU_PROPERTY_AARCH64_FEATURE_1_BTI};
+  ASSERT_TRUE(prop.push(GNU_PROPERTY_AARCH64_FEATURE_1_AND, sizeof(pr_data), &pr_data));
+
+  ASSERT_TRUE(prop.push(4, 1, &pr_data_0));
+
+  PHDR_WITH_NOTE_GNU_PROPERTY(prop.data());
+  ASSERT_NO_ERROR_MSG();
+  test_bti_supported(note);
+#else
+  GTEST_SKIP() << "BTI is not supported on this architecture.";
+#endif
+}
+
+// Tests a .note.gnu.property but with bad property descriptor size.
+TEST(note_gnu_property, pt_gnu_property_bad_n_descsz) {
+#if defined(__aarch64__)
+  GnuPropertySectionBuilder prop;
+  ElfW(Word) pr_data[] = {GNU_PROPERTY_AARCH64_FEATURE_1_BTI};
+  ASSERT_TRUE(prop.push(GNU_PROPERTY_AARCH64_FEATURE_1_AND, sizeof(pr_data), &pr_data));
+
+  ElfW(Word) n_descsz;
+  if (sizeof(ElfW(Addr)) == 4) {
+    n_descsz = 11;
+  } else {
+    n_descsz = 15;
+  }
+
+  prop.corrupt_n_descsz(n_descsz);
+
+  PHDR_WITH_NOTE_GNU_PROPERTY(prop.data());
+  if (sizeof(ElfW(Addr)) == 4) {
+    ASSERT_ERROR_MSG_EQ(
+        ".note.gnu.property: property descriptor size is invalid. Expected at least 12 bytes, got "
+        "11.");
+  } else {
+    ASSERT_ERROR_MSG_EQ(
+        ".note.gnu.property: property descriptor size is invalid. Expected at least 16 bytes, got "
+        "15.");
+  }
+  test_bti_not_supported(note);
+#else
+  GTEST_SKIP() << "BTI is not supported on this architecture.";
+#endif
+}
+
+// Tests if platform support is missing.
+TEST(note_gnu_property, no_platform_support) {
+#if defined(__aarch64__)
+  auto bti_supported_orig = g_platform_properties.bti_supported;
+  g_platform_properties.bti_supported = false;
+
+  GnuPropertySectionBuilder prop;
+  ElfW(Word) pr_data[] = {GNU_PROPERTY_AARCH64_FEATURE_1_BTI};
+  ASSERT_TRUE(prop.push(GNU_PROPERTY_AARCH64_FEATURE_1_AND, sizeof(pr_data), &pr_data));
+  PHDR_WITH_NOTE_GNU_PROPERTY(prop.data());
+  ASSERT_NO_ERROR_MSG();
+  test_bti_not_supported(note);
+
+  g_platform_properties.bti_supported = bti_supported_orig;
+#else
+  GTEST_SKIP() << "BTI is not supported on this architecture.";
+#endif
+}
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index 1e89094..9b1b99f 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -169,8 +169,16 @@
   if (did_load_) {
     return true;
   }
-  if (ReserveAddressSpace(address_space) && LoadSegments() && FindPhdr()) {
+  if (ReserveAddressSpace(address_space) && LoadSegments() && FindPhdr() &&
+      FindGnuPropertySection()) {
     did_load_ = true;
+#if defined(__aarch64__)
+    // For Armv8.5-A loaded executable segments may require PROT_BTI.
+    if (note_gnu_property_.IsBTICompatible()) {
+      did_load_ = (phdr_table_protect_segments(phdr_table_, phdr_num_, load_bias_,
+                                               &note_gnu_property_) == 0);
+    }
+#endif
   }
 
   return did_load_;
@@ -748,15 +756,21 @@
     ElfW(Addr) seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias;
     ElfW(Addr) seg_page_end   = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias;
 
-    int prot = PFLAGS_TO_PROT(phdr->p_flags);
-    if ((extra_prot_flags & PROT_WRITE) != 0) {
+    int prot = PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags;
+    if ((prot & PROT_WRITE) != 0) {
       // make sure we're never simultaneously writable / executable
       prot &= ~PROT_EXEC;
     }
+#if defined(__aarch64__)
+    if ((prot & PROT_EXEC) == 0) {
+      // Though it is not specified don't add PROT_BTI if segment is not
+      // executable.
+      prot &= ~PROT_BTI;
+    }
+#endif
 
-    int ret = mprotect(reinterpret_cast<void*>(seg_page_start),
-                       seg_page_end - seg_page_start,
-                       prot | extra_prot_flags);
+    int ret =
+        mprotect(reinterpret_cast<void*>(seg_page_start), seg_page_end - seg_page_start, prot);
     if (ret < 0) {
       return -1;
     }
@@ -768,16 +782,26 @@
  * You should only call this after phdr_table_unprotect_segments and
  * applying all relocations.
  *
+ * AArch64: also called from linker_main and ElfReader::Load to apply
+ *     PROT_BTI for loaded main so and other so-s.
+ *
  * Input:
  *   phdr_table  -> program header table
  *   phdr_count  -> number of entries in tables
  *   load_bias   -> load bias
+ *   prop        -> GnuPropertySection or nullptr
  * Return:
  *   0 on error, -1 on failure (error code in errno).
  */
-int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table,
-                                size_t phdr_count, ElfW(Addr) load_bias) {
-  return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, 0);
+int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
+                                ElfW(Addr) load_bias, const GnuPropertySection* prop __unused) {
+  int prot = 0;
+#if defined(__aarch64__)
+  if ((prop != nullptr) && prop->IsBTICompatible()) {
+    prot |= PROT_BTI;
+  }
+#endif
+  return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, prot);
 }
 
 /* Change the protection of all loaded segments in memory to writable.
@@ -1081,7 +1105,7 @@
  * Return:
  *   pointer to the program interpreter string.
  */
-const char* phdr_table_get_interpreter_name(const ElfW(Phdr) * phdr_table, size_t phdr_count,
+const char* phdr_table_get_interpreter_name(const ElfW(Phdr)* phdr_table, size_t phdr_count,
                                             ElfW(Addr) load_bias) {
   for (size_t i = 0; i<phdr_count; ++i) {
     const ElfW(Phdr)& phdr = phdr_table[i];
@@ -1124,6 +1148,15 @@
   return false;
 }
 
+// Tries to find .note.gnu.property section.
+// It is not considered an error if such section is missing.
+bool ElfReader::FindGnuPropertySection() {
+#if defined(__aarch64__)
+  note_gnu_property_ = GnuPropertySection(phdr_table_, phdr_num_, load_start(), name_.c_str());
+#endif
+  return true;
+}
+
 // Ensures that our program header is actually within a loadable
 // segment. This should help catch badly-formed ELF files that
 // would cause the linker to crash later when trying to access it.
diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h
index 4cb48f5..548dc51 100644
--- a/linker/linker_phdr.h
+++ b/linker/linker_phdr.h
@@ -37,6 +37,7 @@
 
 #include "linker.h"
 #include "linker_mapped_file_fragment.h"
+#include "linker_note_gnu_property.h"
 
 class ElfReader {
  public:
@@ -67,6 +68,7 @@
   bool ReserveAddressSpace(address_space_params* address_space);
   bool LoadSegments();
   bool FindPhdr();
+  bool FindGnuPropertySection();
   bool CheckPhdr(ElfW(Addr));
   bool CheckFileRange(ElfW(Addr) offset, size_t size, size_t alignment);
 
@@ -110,13 +112,16 @@
 
   // Is map owned by the caller
   bool mapped_by_caller_;
+
+  // Only used by AArch64 at the moment.
+  GnuPropertySection note_gnu_property_ __unused;
 };
 
 size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count,
                                 ElfW(Addr)* min_vaddr = nullptr, ElfW(Addr)* max_vaddr = nullptr);
 
-int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table,
-                                size_t phdr_count, ElfW(Addr) load_bias);
+int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
+                                ElfW(Addr) load_bias, const GnuPropertySection* prop = nullptr);
 
 int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
                                   ElfW(Addr) load_bias);
@@ -139,5 +144,5 @@
                                     ElfW(Addr) load_bias, ElfW(Dyn)** dynamic,
                                     ElfW(Word)* dynamic_flags);
 
-const char* phdr_table_get_interpreter_name(const ElfW(Phdr) * phdr_table, size_t phdr_count,
+const char* phdr_table_get_interpreter_name(const ElfW(Phdr)* phdr_table, size_t phdr_count,
                                             ElfW(Addr) load_bias);
diff --git a/tests/Android.bp b/tests/Android.bp
index 8760256..9b95c61 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -41,6 +41,9 @@
         // We want to test deprecated API too.
         "-Wno-deprecated-declarations",
 
+        // Needed to test pthread_internal_t layout.
+        "-Wno-invalid-offsetof",
+
         // For glibc.
         "-D__STDC_LIMIT_MACROS",
     ],
@@ -392,6 +395,7 @@
         "string_posix_strerror_r_test.cpp",
         "strings_nofortify_test.cpp",
         "strings_test.cpp",
+        "struct_layout_test.cpp",
         "sstream_test.cpp",
         "sys_auxv_test.cpp",
         "sys_epoll_test.cpp",
@@ -987,6 +991,8 @@
         "ld_config_test_helper_lib1",
         "ld_config_test_helper_lib2",
         "ld_config_test_helper_lib3",
+        "tls_properties_helper",
+        "thread_exit_cb_helper",
     ],
 }
 
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index ef4fddd..4b86faf 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -80,7 +80,7 @@
 }
 
 cc_test {
-   name: "thread_exit_cb_helper.cpp",
+   name: "thread_exit_cb_helper",
    defaults: ["bionic_testlib_defaults"],
    srcs: ["thread_exit_cb_helper.cpp"],
    cflags: ["-fno-emulated-tls"],
diff --git a/tests/libs/tls_properties_helper.cpp b/tests/libs/tls_properties_helper.cpp
index 3f8d118..5982de4 100644
--- a/tests/libs/tls_properties_helper.cpp
+++ b/tests/libs/tls_properties_helper.cpp
@@ -34,8 +34,19 @@
 
 #include <assert.h>
 #include <dlfcn.h>
+#include <elf.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sched.h>
 #include <stdio.h>
-#include <unistd.h>  // for gettid
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/uio.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+#include <unistd.h>
 
 // Helper binary to use TLS-related functions in thread_properties
 
@@ -59,20 +70,54 @@
 // Tests iterate_dynamic tls chunks.
 // Export a var from the shared so.
 __thread char large_tls_var[4 * 1024 * 1024];
+// found_count  has to be Global variable so that the non-capturing lambda
+// can access it.
+int found_count = 0;
 void test_iter_tls() {
   void* lib = dlopen("libtest_elftls_dynamic.so", RTLD_LOCAL | RTLD_NOW);
-
-  int i = 0;
-  auto cb = [&](void* dtls_begin, void* dtls_end, size_t dso_id, void* arg) {
-    printf("iterate_cb i = %d\n", i++);
+  large_tls_var[1025] = 'a';
+  auto cb = +[](void* dtls_begin, void* dtls_end, size_t dso_id, void* arg) {
+    if (&large_tls_var >= dtls_begin && &large_tls_var < dtls_end) ++found_count;
   };
   __libc_iterate_dynamic_tls(gettid(), cb, nullptr);
+
+  // It should be found exactly once.
+  assert(found_count == 1);
   printf("done_iterate_dynamic_tls\n");
 }
 
+void* parent_addr = nullptr;
+void test_iterate_another_thread_tls() {
+  large_tls_var[1025] = 'b';
+  parent_addr = &large_tls_var;
+  found_count = 0;
+
+  pid_t pid = fork();
+  assert(pid != -1);
+  int status;
+  if (pid) {
+    // Parent.
+    assert(pid == wait(&status));
+    assert(0 == status);
+  } else {
+    // Child.
+    pid_t parent_pid = getppid();
+    assert(0 == ptrace(PTRACE_ATTACH, parent_pid));
+    assert(parent_pid == waitpid(parent_pid, &status, 0));
+
+    auto cb = +[](void* dtls_begin, void* dtls_end, size_t dso_id, void* arg) {
+      if (parent_addr >= dtls_begin && parent_addr < dtls_end) ++found_count;
+    };
+    __libc_iterate_dynamic_tls(parent_pid, cb, nullptr);
+    // It should be found exactly once.
+    assert(found_count == 1);
+    printf("done_iterate_another_thread_tls\n");
+  }
+}
 int main() {
   test_static_tls_bounds();
   test_iter_tls();
+  test_iterate_another_thread_tls();
   return 0;
 }
 
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index 55bd149..4ea6d2b 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -20,6 +20,7 @@
 #include <limits.h>
 #include <malloc.h>
 #include <pthread.h>
+#include <semaphore.h>
 #include <signal.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -45,6 +46,7 @@
 #include "SignalUtils.h"
 
 #include "platform/bionic/malloc.h"
+#include "platform/bionic/mte.h"
 #include "platform/bionic/mte_kernel.h"
 #include "platform/bionic/reserved_signals.h"
 #include "private/bionic_config.h"
@@ -84,6 +86,24 @@
   free(ptr);
 }
 
+TEST(malloc, calloc_mem_init_disabled) {
+#if defined(__BIONIC__)
+  // calloc should still zero memory if mem-init is disabled.
+  // With jemalloc the mallopts will fail but that shouldn't affect the
+  // execution of the test.
+  mallopt(M_THREAD_DISABLE_MEM_INIT, 1);
+  size_t alloc_len = 100;
+  char *ptr = reinterpret_cast<char*>(calloc(1, alloc_len));
+  for (size_t i = 0; i < alloc_len; i++) {
+    ASSERT_EQ(0, ptr[i]);
+  }
+  free(ptr);
+  mallopt(M_THREAD_DISABLE_MEM_INIT, 0);
+#else
+  GTEST_SKIP() << "bionic-only test";
+#endif
+}
+
 TEST(malloc, calloc_illegal) {
   SKIP_WITH_HWASAN;
   errno = 0;
@@ -1241,3 +1261,39 @@
   GTEST_SKIP() << "bionic extension";
 #endif
 }
+
+TEST(android_mallopt, disable_memory_mitigations) {
+#if defined(__BIONIC__)
+  if (!mte_supported()) {
+    GTEST_SKIP() << "This function can only be tested with MTE";
+  }
+
+#ifdef ANDROID_EXPERIMENTAL_MTE
+  sem_t sem;
+  ASSERT_EQ(0, sem_init(&sem, 0, 0));
+
+  pthread_t thread;
+  ASSERT_EQ(0, pthread_create(
+                   &thread, nullptr,
+                   [](void* ptr) -> void* {
+                     auto* sem = reinterpret_cast<sem_t*>(ptr);
+                     sem_wait(sem);
+                     return reinterpret_cast<void*>(prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0));
+                   },
+                   &sem));
+
+  ASSERT_TRUE(android_mallopt(M_DISABLE_MEMORY_MITIGATIONS, nullptr, 0));
+  ASSERT_EQ(0, sem_post(&sem));
+
+  int my_tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+  ASSERT_EQ(PR_MTE_TCF_NONE, my_tagged_addr_ctrl & PR_MTE_TCF_MASK);
+
+  void* retval;
+  ASSERT_EQ(0, pthread_join(thread, &retval));
+  int thread_tagged_addr_ctrl = reinterpret_cast<uintptr_t>(retval);
+  ASSERT_EQ(my_tagged_addr_ctrl, thread_tagged_addr_ctrl);
+#endif
+#else
+  GTEST_SKIP() << "bionic extension";
+#endif
+}
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index 851b86f..d9ad3cc 100644
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -2975,3 +2975,48 @@
   spin_helper.UnSpin();
   ASSERT_EQ(0, pthread_join(t, nullptr));
 }
+
+extern "C" bool android_run_on_all_threads(bool (*func)(void*), void* arg);
+
+TEST(pthread, run_on_all_threads) {
+#if defined(__BIONIC__)
+  pthread_t t;
+  ASSERT_EQ(
+      0, pthread_create(
+             &t, nullptr,
+             [](void*) -> void* {
+               pthread_attr_t detached;
+               if (pthread_attr_init(&detached) != 0 ||
+                   pthread_attr_setdetachstate(&detached, PTHREAD_CREATE_DETACHED) != 0) {
+                 return reinterpret_cast<void*>(errno);
+               }
+
+               for (int i = 0; i != 1000; ++i) {
+                 pthread_t t1, t2;
+                 if (pthread_create(
+                         &t1, &detached, [](void*) -> void* { return nullptr; }, nullptr) != 0 ||
+                     pthread_create(
+                         &t2, nullptr, [](void*) -> void* { return nullptr; }, nullptr) != 0 ||
+                     pthread_join(t2, nullptr) != 0) {
+                   return reinterpret_cast<void*>(errno);
+                 }
+               }
+
+               if (pthread_attr_destroy(&detached) != 0) {
+                 return reinterpret_cast<void*>(errno);
+               }
+               return nullptr;
+             },
+             nullptr));
+
+  for (int i = 0; i != 1000; ++i) {
+    ASSERT_TRUE(android_run_on_all_threads([](void* arg) { return arg == nullptr; }, nullptr));
+  }
+
+  void *retval;
+  ASSERT_EQ(0, pthread_join(t, &retval));
+  ASSERT_EQ(nullptr, retval);
+#else
+  GTEST_SKIP() << "bionic-only test";
+#endif
+}
diff --git a/tests/struct_layout_test.cpp b/tests/struct_layout_test.cpp
new file mode 100644
index 0000000..00fd4d5
--- /dev/null
+++ b/tests/struct_layout_test.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <stdio.h>
+
+#if defined(__BIONIC__)
+#include "bionic/pthread_internal.h"
+
+// Ensure that the layout of these data structures is architecture independent and only depends on
+// the bitness of the architecture.
+template <typename CheckSize, typename CheckOffset>
+void tests(CheckSize check_size, CheckOffset check_offset) {
+#define CHECK_SIZE(name, size) \
+    check_size(#name, sizeof(name), size);
+#define CHECK_OFFSET(name, field, offset) \
+    check_offset(#name, #field, offsetof(name, field), offset);
+#ifdef __LP64__
+  CHECK_SIZE(pthread_internal_t, 776);
+  CHECK_OFFSET(pthread_internal_t, next, 0);
+  CHECK_OFFSET(pthread_internal_t, prev, 8);
+  CHECK_OFFSET(pthread_internal_t, tid, 16);
+  CHECK_OFFSET(pthread_internal_t, attr, 24);
+  CHECK_OFFSET(pthread_internal_t, join_state, 80);
+  CHECK_OFFSET(pthread_internal_t, cleanup_stack, 88);
+  CHECK_OFFSET(pthread_internal_t, start_routine, 96);
+  CHECK_OFFSET(pthread_internal_t, start_routine_arg, 104);
+  CHECK_OFFSET(pthread_internal_t, return_value, 112);
+  CHECK_OFFSET(pthread_internal_t, start_mask, 120);
+  CHECK_OFFSET(pthread_internal_t, alternate_signal_stack, 128);
+  CHECK_OFFSET(pthread_internal_t, shadow_call_stack_guard_region, 136);
+  CHECK_OFFSET(pthread_internal_t, stack_top, 144);
+  CHECK_OFFSET(pthread_internal_t, startup_handshake_lock, 156);
+  CHECK_OFFSET(pthread_internal_t, mmap_base, 168);
+  CHECK_OFFSET(pthread_internal_t, mmap_size, 176);
+  CHECK_OFFSET(pthread_internal_t, mmap_base_unguarded, 184);
+  CHECK_OFFSET(pthread_internal_t, mmap_size_unguarded, 192);
+  CHECK_OFFSET(pthread_internal_t, vma_name_buffer, 200);
+  CHECK_OFFSET(pthread_internal_t, thread_local_dtors, 232);
+  CHECK_OFFSET(pthread_internal_t, current_dlerror, 240);
+  CHECK_OFFSET(pthread_internal_t, dlerror_buffer, 248);
+  CHECK_OFFSET(pthread_internal_t, bionic_tls, 760);
+  CHECK_OFFSET(pthread_internal_t, errno_value, 768);
+  CHECK_SIZE(bionic_tls, 12200);
+  CHECK_OFFSET(bionic_tls, key_data, 0);
+  CHECK_OFFSET(bionic_tls, locale, 2080);
+  CHECK_OFFSET(bionic_tls, basename_buf, 2088);
+  CHECK_OFFSET(bionic_tls, dirname_buf, 6184);
+  CHECK_OFFSET(bionic_tls, mntent_buf, 10280);
+  CHECK_OFFSET(bionic_tls, mntent_strings, 10320);
+  CHECK_OFFSET(bionic_tls, ptsname_buf, 11344);
+  CHECK_OFFSET(bionic_tls, ttyname_buf, 11376);
+  CHECK_OFFSET(bionic_tls, strerror_buf, 11440);
+  CHECK_OFFSET(bionic_tls, strsignal_buf, 11695);
+  CHECK_OFFSET(bionic_tls, group, 11952);
+  CHECK_OFFSET(bionic_tls, passwd, 12040);
+  CHECK_OFFSET(bionic_tls, fdtrack_disabled, 12192);
+  CHECK_OFFSET(bionic_tls, padding, 12193);
+#else
+  CHECK_SIZE(pthread_internal_t, 668);
+  CHECK_OFFSET(pthread_internal_t, next, 0);
+  CHECK_OFFSET(pthread_internal_t, prev, 4);
+  CHECK_OFFSET(pthread_internal_t, tid, 8);
+  CHECK_OFFSET(pthread_internal_t, attr, 16);
+  CHECK_OFFSET(pthread_internal_t, join_state, 40);
+  CHECK_OFFSET(pthread_internal_t, cleanup_stack, 44);
+  CHECK_OFFSET(pthread_internal_t, start_routine, 48);
+  CHECK_OFFSET(pthread_internal_t, start_routine_arg, 52);
+  CHECK_OFFSET(pthread_internal_t, return_value, 56);
+  CHECK_OFFSET(pthread_internal_t, start_mask, 60);
+  CHECK_OFFSET(pthread_internal_t, alternate_signal_stack, 68);
+  CHECK_OFFSET(pthread_internal_t, shadow_call_stack_guard_region, 72);
+  CHECK_OFFSET(pthread_internal_t, stack_top, 76);
+  CHECK_OFFSET(pthread_internal_t, startup_handshake_lock, 84);
+  CHECK_OFFSET(pthread_internal_t, mmap_base, 92);
+  CHECK_OFFSET(pthread_internal_t, mmap_size, 96);
+  CHECK_OFFSET(pthread_internal_t, mmap_base_unguarded, 100);
+  CHECK_OFFSET(pthread_internal_t, mmap_size_unguarded, 104);
+  CHECK_OFFSET(pthread_internal_t, vma_name_buffer, 108);
+  CHECK_OFFSET(pthread_internal_t, thread_local_dtors, 140);
+  CHECK_OFFSET(pthread_internal_t, current_dlerror, 144);
+  CHECK_OFFSET(pthread_internal_t, dlerror_buffer, 148);
+  CHECK_OFFSET(pthread_internal_t, bionic_tls, 660);
+  CHECK_OFFSET(pthread_internal_t, errno_value, 664);
+  CHECK_SIZE(bionic_tls, 11080);
+  CHECK_OFFSET(bionic_tls, key_data, 0);
+  CHECK_OFFSET(bionic_tls, locale, 1040);
+  CHECK_OFFSET(bionic_tls, basename_buf, 1044);
+  CHECK_OFFSET(bionic_tls, dirname_buf, 5140);
+  CHECK_OFFSET(bionic_tls, mntent_buf, 9236);
+  CHECK_OFFSET(bionic_tls, mntent_strings, 9260);
+  CHECK_OFFSET(bionic_tls, ptsname_buf, 10284);
+  CHECK_OFFSET(bionic_tls, ttyname_buf, 10316);
+  CHECK_OFFSET(bionic_tls, strerror_buf, 10380);
+  CHECK_OFFSET(bionic_tls, strsignal_buf, 10635);
+  CHECK_OFFSET(bionic_tls, group, 10892);
+  CHECK_OFFSET(bionic_tls, passwd, 10952);
+  CHECK_OFFSET(bionic_tls, fdtrack_disabled, 11076);
+  CHECK_OFFSET(bionic_tls, padding, 11077);
+#endif  // __LP64__
+#undef CHECK_SIZE
+#undef CHECK_OFFSET
+}
+#endif  // defined(__BIONIC__)
+
+TEST(struct_layout, sizes_offsets) {
+#if defined(__BIONIC__)
+  bool failed = false;
+
+  auto check_size = [&](const char* name, size_t size, size_t expected_size) {
+    EXPECT_EQ(expected_size, size) << "sizeof(" << name << ")";
+    if (size != expected_size) {
+      failed = true;
+    }
+  };
+  auto check_offset = [&](const char* name, const char* field, size_t offset,
+                          size_t expected_offset) {
+    EXPECT_EQ(expected_offset, offset) << "offsetof(" << name << ", " << field << ")";
+    if (offset != expected_offset) {
+      failed = true;
+    }
+  };
+  tests(check_size, check_offset);
+
+  if (failed) {
+    printf(
+        "Please update the tests function in bionic/tests/struct_layout_test.cpp with the "
+        "following contents:\n");
+
+    auto print_size = [&](const char* name, size_t size, size_t expected_size) {
+      (void)expected_size;
+      printf("  CHECK_SIZE(%s, %zu);\n", name, size);
+    };
+    auto print_offset = [&](const char* name, const char* field, size_t offset,
+                            size_t expected_offset) {
+      (void)expected_offset;
+      printf("  CHECK_OFFSET(%s, %s, %zu);\n", name, field, offset);
+    };
+    tests(print_size, print_offset);
+  }
+#else
+  GTEST_SKIP() << "bionic-only test";
+#endif
+}
diff --git a/tools/versioner/src/Driver.cpp b/tools/versioner/src/Driver.cpp
index 184c3d4..adf93c3 100644
--- a/tools/versioner/src/Driver.cpp
+++ b/tools/versioner/src/Driver.cpp
@@ -42,6 +42,7 @@
 #include <llvm/ADT/SmallVector.h>
 #include <llvm/ADT/StringRef.h>
 #include <llvm/Option/Option.h>
+#include <llvm/Support/Host.h>
 #include <llvm/Support/VirtualFileSystem.h>
 
 #include "Arch.h"