[asan] Fallback to non-ifunc dynamic shadow on android<22.

Summary: Android < 22 does not support ifunc.

Reviewers: pcc

Subscribers: srhines, kubamracek, hiraditya, llvm-commits

Differential Revision: https://reviews.llvm.org/D40116

llvm-svn: 318369
diff --git a/compiler-rt/lib/asan/asan_linux.cc b/compiler-rt/lib/asan/asan_linux.cc
index 902c64c..8ce4b96 100644
--- a/compiler-rt/lib/asan/asan_linux.cc
+++ b/compiler-rt/lib/asan/asan_linux.cc
@@ -83,7 +83,7 @@
 }
 
 #if ASAN_PREMAP_SHADOW
-uptr FindDynamicShadowStart() {
+uptr FindPremappedShadowStart() {
   uptr granularity = GetMmapGranularity();
   uptr shadow_start = reinterpret_cast<uptr>(&__asan_shadow);
   uptr shadow_size = PremapShadowSize();
@@ -93,8 +93,14 @@
   IncreaseTotalMmap(shadow_size + granularity);
   return shadow_start;
 }
-#else
+#endif
+
 uptr FindDynamicShadowStart() {
+#if ASAN_PREMAP_SHADOW
+  if (!PremapShadowFailed())
+    return FindPremappedShadowStart();
+#endif
+
   uptr granularity = GetMmapGranularity();
   uptr alignment = granularity * 8;
   uptr left_padding = granularity;
@@ -112,7 +118,6 @@
 
   return shadow_start;
 }
-#endif
 
 void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
   UNIMPLEMENTED();
diff --git a/compiler-rt/lib/asan/asan_premap_shadow.cc b/compiler-rt/lib/asan/asan_premap_shadow.cc
index 0e66eee..2d20c3b 100644
--- a/compiler-rt/lib/asan/asan_premap_shadow.cc
+++ b/compiler-rt/lib/asan/asan_premap_shadow.cc
@@ -50,6 +50,15 @@
   return shadow_start;
 }
 
+bool PremapShadowFailed() {
+  uptr shadow = reinterpret_cast<uptr>(&__asan_shadow);
+  uptr resolver = reinterpret_cast<uptr>(&__asan_premap_shadow);
+  // shadow == resolver is how Android KitKat and older handles ifunc.
+  // shadow == 0 just in case.
+  if (shadow == 0 || shadow == resolver)
+    return true;
+  return false;
+}
 } // namespace __asan
 
 extern "C" {
diff --git a/compiler-rt/lib/asan/asan_premap_shadow.h b/compiler-rt/lib/asan/asan_premap_shadow.h
index 6349e04..41acbdb 100644
--- a/compiler-rt/lib/asan/asan_premap_shadow.h
+++ b/compiler-rt/lib/asan/asan_premap_shadow.h
@@ -20,9 +20,11 @@
 namespace __asan {
 // Conservative upper limit.
 uptr PremapShadowSize();
+bool PremapShadowFailed();
 }
 #endif
 
 extern "C" INTERFACE_ATTRIBUTE void __asan_shadow();
+extern "C" decltype(__asan_shadow)* __asan_premap_shadow();
 
 #endif // ASAN_PREMAP_SHADOW_H
diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
index 9681de4..967464f 100644
--- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
@@ -565,7 +565,9 @@
   Mapping.OrShadowOffset = !IsAArch64 && !IsPPC64 && !IsSystemZ && !IsPS4CPU &&
                            !(Mapping.Offset & (Mapping.Offset - 1)) &&
                            Mapping.Offset != kDynamicShadowSentinel;
-  Mapping.InGlobal = ClWithIfunc && IsAndroid && IsArmOrThumb;
+  bool IsAndroidWithIfuncSupport =
+      IsAndroid && !TargetTriple.isAndroidVersionLT(21);
+  Mapping.InGlobal = ClWithIfunc && IsAndroidWithIfuncSupport && IsArmOrThumb;
 
   return Mapping;
 }
diff --git a/llvm/test/Instrumentation/AddressSanitizer/with-ifunc.ll b/llvm/test/Instrumentation/AddressSanitizer/with-ifunc.ll
index aea4f7c..4771a96 100644
--- a/llvm/test/Instrumentation/AddressSanitizer/with-ifunc.ll
+++ b/llvm/test/Instrumentation/AddressSanitizer/with-ifunc.ll
@@ -7,8 +7,16 @@
 ; RUN: opt -asan -asan-module -S -asan-with-ifunc=1 -asan-with-ifunc-suppress-remat=1 < %s | \
 ; RUN:     FileCheck %s --check-prefixes=CHECK,CHECK-IFUNC-NOREMAT
 
+; Pre-Lollipop Android does not support ifunc.
+; RUN: opt -asan -asan-module -S -asan-with-ifunc=1 -asan-with-ifunc-suppress-remat=0 -mtriple=armv7-linux-android20 < %s | \
+; RUN:     FileCheck %s --check-prefixes=CHECK,CHECK-NOIFUNC
+; RUN: opt -asan -asan-module -S -asan-with-ifunc=1 -asan-with-ifunc-suppress-remat=0 -mtriple=armv7-linux-android < %s | \
+; RUN:     FileCheck %s --check-prefixes=CHECK,CHECK-NOIFUNC
+; RUN: opt -asan -asan-module -S -asan-with-ifunc=1 -asan-with-ifunc-suppress-remat=0 -mtriple=armv7-linux-android21 < %s | \
+; RUN:     FileCheck %s --check-prefixes=CHECK,CHECK-IFUNC
+
 target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
-target triple = "armv7--linux-android"
+target triple = "armv7--linux-android22"
 
 ; CHECK-IFUNC: @__asan_shadow = external global [0 x i8]
 ; CHECK-NOIFUNC: @__asan_shadow_memory_dynamic_address = external global i32