Add IFUNC support for arm64 and IRELATIVE reloc

 There are number of changes in the way IFUNC related relocations are done:
 1. IRELATIVE relocations are now supported for x86/x86_64 and arm64.
 2. IFUNC relocations are now relying on static linker to generate
    them in correct order - this removes necessety of additional
    relocation pass for ifuncs.
 3. Related to 2: rela?.dyn relocations are preformed before .plt ones.
 4. Ifunc are resolved on symbol lookup this approach allowed to avoid
    mprotect(PROT_WRITE) call on r-x program segments.

Bug: 17399706
Bug: 17177284
Change-Id: I414dd3e82bd47cc03442c5dfc7c279949aec51ed
diff --git a/tests/Android.build.mk b/tests/Android.build.mk
index d4b0396..d54c851 100644
--- a/tests/Android.build.mk
+++ b/tests/Android.build.mk
@@ -37,6 +37,10 @@
 
 LOCAL_FORCE_STATIC_EXECUTABLE := $($(module)_force_static_executable)
 
+ifneq ($($(module)_multilib),)
+    LOCAL_MULTILIB := $($(module)_multilib)
+endif
+
 LOCAL_CFLAGS := \
     $(common_cflags) \
     $($(module)_cflags) \
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 3568f8f..4ba19c1 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -89,8 +89,8 @@
   ASSERT_EQ(0, dlclose(handle2));
 }
 
-// ifuncs are only supported on intel for now
-#if defined(__i386__) || defined(__x86_64__)
+// ifuncs are only supported on intel and arm64 for now
+#if defined (__aarch64__) || defined(__i386__) || defined(__x86_64__)
 TEST(dlfcn, ifunc) {
   typedef const char* (*fn_ptr)();
 
@@ -124,9 +124,13 @@
   typedef const char* (*fn_ptr)();
 
   void* handle = dlopen("libtest_ifunc.so", RTLD_NOW);
-  ASSERT_TRUE(handle != NULL) << dlerror();
-  fn_ptr is_ctor_called =  reinterpret_cast<fn_ptr>(dlsym(handle, "is_ctor_called"));
-  ASSERT_TRUE(is_ctor_called != NULL) << dlerror();
+  ASSERT_TRUE(handle != nullptr) << dlerror();
+  fn_ptr is_ctor_called =  reinterpret_cast<fn_ptr>(dlsym(handle, "is_ctor_called_irelative"));
+  ASSERT_TRUE(is_ctor_called != nullptr) << dlerror();
+  ASSERT_STREQ("false", is_ctor_called());
+
+  is_ctor_called =  reinterpret_cast<fn_ptr>(dlsym(handle, "is_ctor_called_jump_slot"));
+  ASSERT_TRUE(is_ctor_called != nullptr) << dlerror();
   ASSERT_STREQ("true", is_ctor_called());
   dlclose(handle);
 }
diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk
index be6565b..a675a4f 100644
--- a/tests/libs/Android.mk
+++ b/tests/libs/Android.mk
@@ -295,7 +295,7 @@
 # -----------------------------------------------------------------------------
 # Library used by ifunc tests
 # -----------------------------------------------------------------------------
-ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),x86 x86_64))
+ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),arm64 x86 x86_64))
     libtest_ifunc_src_files := \
         dlopen_testlib_ifunc.c
 
@@ -303,6 +303,14 @@
     module := libtest_ifunc
     build_type := target
     build_target := SHARED_LIBRARY
+
+    ifeq ($(TARGET_ARCH),arm64)
+      libtest_ifunc_multilib := 64
+      # TODO: This is a workaround - remove it once gcc
+      # removes its Android ifunc checks
+      libtest_ifunc_cflags := -mglibc
+    endif
+
     include $(TEST_PATH)/Android.build.mk
 endif
 
diff --git a/tests/libs/dlopen_testlib_ifunc.c b/tests/libs/dlopen_testlib_ifunc.c
index 4874841..b68a3dd 100644
--- a/tests/libs/dlopen_testlib_ifunc.c
+++ b/tests/libs/dlopen_testlib_ifunc.c
@@ -23,8 +23,17 @@
   g_flag = 1;
 }
 
+static const char* is_ctor_called() __attribute__ ((ifunc("is_ctor_called_ifun")));
+
 const char* foo() __attribute__ ((ifunc ("foo_ifunc")));
-const char* is_ctor_called() __attribute__ ((ifunc("is_ctor_called_ifun")));
+
+// Static linker creates GLOBAL/IFUNC symbol and JUMP_SLOT relocation type for plt segment
+const char* is_ctor_called_jump_slot() __attribute__ ((ifunc("is_ctor_called_ifun")));
+
+const char* is_ctor_called_irelative() {
+  // Call internal ifunc-resolved function with IRELATIVE reloc
+  return is_ctor_called();
+}
 
 const char* return_true() {
   return "true";