Merge "Adopt GNU calling convention for ifunc resolvers."
diff --git a/libc/Android.bp b/libc/Android.bp
index 8eefe10..45c4569 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1070,6 +1070,7 @@
         "bionic/atof.cpp",
         "bionic/bionic_allocator.cpp",
         "bionic/bionic_arc4random.cpp",
+        "bionic/bionic_call_ifunc_resolver.cpp",
         "bionic/bionic_futex.cpp",
         "bionic/bionic_netlink.cpp",
         "bionic/bionic_systrace.cpp",
diff --git a/libc/bionic/bionic_call_ifunc_resolver.cpp b/libc/bionic/bionic_call_ifunc_resolver.cpp
new file mode 100644
index 0000000..8522835
--- /dev/null
+++ b/libc/bionic/bionic_call_ifunc_resolver.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 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 "private/bionic_call_ifunc_resolver.h"
+#include <sys/auxv.h>
+#include <sys/ifunc.h>
+
+ElfW(Addr) __bionic_call_ifunc_resolver(ElfW(Addr) resolver_addr) {
+#if defined(__aarch64__)
+  typedef ElfW(Addr) (*ifunc_resolver_t)(uint64_t, __ifunc_arg_t*);
+  static __ifunc_arg_t arg = { sizeof(__ifunc_arg_t), getauxval(AT_HWCAP), getauxval(AT_HWCAP2) };
+  return reinterpret_cast<ifunc_resolver_t>(resolver_addr)(arg._hwcap | _IFUNC_ARG_HWCAP, &arg);
+#elif defined(__arm__)
+  typedef ElfW(Addr) (*ifunc_resolver_t)(unsigned long);
+  static unsigned long hwcap = getauxval(AT_HWCAP);
+  return reinterpret_cast<ifunc_resolver_t>(resolver_addr)(hwcap);
+#else
+  typedef ElfW(Addr) (*ifunc_resolver_t)(void);
+  return reinterpret_cast<ifunc_resolver_t>(resolver_addr)();
+#endif
+}
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index b4bddce..0b74023 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -39,6 +39,7 @@
 #include "libc_init_common.h"
 #include "pthread_internal.h"
 
+#include "private/bionic_call_ifunc_resolver.h"
 #include "private/bionic_elf_tls.h"
 #include "private/bionic_globals.h"
 #include "private/bionic_macros.h"
@@ -81,11 +82,10 @@
     return;
   }
 
-  typedef ElfW(Addr) (*ifunc_resolver_t)(void);
   for (ElfW(Rela) *r = __rela_iplt_start; r != __rela_iplt_end; ++r) {
     ElfW(Addr)* offset = reinterpret_cast<ElfW(Addr)*>(r->r_offset);
     ElfW(Addr) resolver = r->r_addend;
-    *offset = reinterpret_cast<ifunc_resolver_t>(resolver)();
+    *offset = __bionic_call_ifunc_resolver(resolver);
   }
 }
 #else
@@ -103,11 +103,10 @@
     return;
   }
 
-  typedef ElfW(Addr) (*ifunc_resolver_t)(void);
   for (ElfW(Rel) *r = __rel_iplt_start; r != __rel_iplt_end; ++r) {
     ElfW(Addr)* offset = reinterpret_cast<ElfW(Addr)*>(r->r_offset);
     ElfW(Addr) resolver = *offset;
-    *offset = reinterpret_cast<ifunc_resolver_t>(resolver)();
+    *offset = __bionic_call_ifunc_resolver(resolver);
   }
 }
 #endif
diff --git a/libc/include/sys/ifunc.h b/libc/include/sys/ifunc.h
new file mode 100644
index 0000000..7fbca4a
--- /dev/null
+++ b/libc/include/sys/ifunc.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 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 <sys/cdefs.h>
+
+/**
+ * @file sys/ifunc.h
+ * @brief Declarations used for ifunc resolvers. Currently only meaningful for arm64.
+ */
+
+__BEGIN_DECLS
+
+#if defined(__aarch64__)
+
+/**
+ * Provides information about hardware capabilities to ifunc resolvers.
+ *
+ * Starting with API level 30, ifunc resolvers on arm64 are passed two arguments. The first is a
+ * uint64_t whose value is equal to getauxval(AT_HWCAP) | _IFUNC_ARG_HWCAP. The second is a pointer
+ * to a data structure of this type. Prior to API level 30, no arguments are passed to ifunc
+ * resolvers. Code that wishes to be compatible with prior API levels should not accept any
+ * arguments in the resolver.
+ */
+typedef struct __ifunc_arg_t {
+  /** Set to sizeof(__ifunc_arg_t). */
+  unsigned long _size;
+
+  /** Set to getauxval(AT_HWCAP). */
+  unsigned long _hwcap;
+
+  /** Set to getauxval(AT_HWCAP2). */
+  unsigned long _hwcap2;
+} __ifunc_arg_t;
+
+/**
+ * If this bit is set in the first argument to an ifunc resolver, indicates that the second argument
+ * is a pointer to a data structure of type __ifunc_arg_t. This bit is always set on Android
+ * starting with API level 30.
+ */
+#define _IFUNC_ARG_HWCAP (1ULL << 62)
+
+#endif
+
+__END_DECLS
diff --git a/libc/private/bionic_call_ifunc_resolver.h b/libc/private/bionic_call_ifunc_resolver.h
new file mode 100644
index 0000000..e0ea35b
--- /dev/null
+++ b/libc/private/bionic_call_ifunc_resolver.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 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 <link.h>
+#include <sys/cdefs.h>
+
+__LIBC_HIDDEN__ ElfW(Addr) __bionic_call_ifunc_resolver(ElfW(Addr) resolver_addr);
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 3c30e73..f4fccac 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -68,6 +68,7 @@
 #include "linker_tls.h"
 #include "linker_utils.h"
 
+#include "private/bionic_call_ifunc_resolver.h"
 #include "private/bionic_globals.h"
 #include "android-base/macros.h"
 #include "android-base/strings.h"
@@ -2689,11 +2690,9 @@
 ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) {
   if (g_is_ldd) return 0;
 
-  typedef ElfW(Addr) (*ifunc_resolver_t)(void);
-  ifunc_resolver_t ifunc_resolver = reinterpret_cast<ifunc_resolver_t>(resolver_addr);
-  ElfW(Addr) ifunc_addr = ifunc_resolver();
+  ElfW(Addr) ifunc_addr = __bionic_call_ifunc_resolver(resolver_addr);
   TRACE_TYPE(RELO, "Called ifunc_resolver@%p. The result is %p",
-      ifunc_resolver, reinterpret_cast<void*>(ifunc_addr));
+      reinterpret_cast<void *>(resolver_addr), reinterpret_cast<void*>(ifunc_addr));
 
   return ifunc_addr;
 }
diff --git a/tests/ifunc_test.cpp b/tests/ifunc_test.cpp
index 7ab5899..e69271f 100644
--- a/tests/ifunc_test.cpp
+++ b/tests/ifunc_test.cpp
@@ -16,12 +16,19 @@
 
 #include <gtest/gtest.h>
 
+#include <sys/auxv.h>
+#if defined(__BIONIC__)
+#include <sys/ifunc.h>
+#endif
+
+typedef int (*fn_ptr_t)();
+
 int ret42() {
   return 42;
 }
 
-extern "C" void* resolver() {
-  return (void*)ret42;
+extern "C" fn_ptr_t resolver() {
+  return ret42;
 }
 
 int ifunc() __attribute__((ifunc("resolver")));
@@ -29,3 +36,51 @@
 TEST(ifunc, function) {
   ASSERT_EQ(42, ifunc());
 }
+
+#if defined(__BIONIC__)
+
+#if defined(__aarch64__)
+
+static uint64_t g_hwcap;
+static __ifunc_arg_t g_arg;
+
+extern "C" fn_ptr_t hwcap_resolver(uint64_t hwcap, __ifunc_arg_t* arg) {
+  g_hwcap = hwcap;
+  g_arg = *arg;
+  return ret42;
+}
+
+#elif defined(__arm__)
+
+static unsigned long g_hwcap;
+
+extern "C" fn_ptr_t hwcap_resolver(unsigned long hwcap) {
+  g_hwcap = hwcap;
+  return ret42;
+}
+
+#else
+
+extern "C" fn_ptr_t hwcap_resolver() {
+  return ret42;
+}
+
+#endif
+
+int hwcap() __attribute__((ifunc("hwcap_resolver")));
+
+TEST(ifunc, hwcap) {
+  ASSERT_EQ(42, hwcap());
+
+#if defined(__aarch64__)
+  EXPECT_EQ(getauxval(AT_HWCAP) | _IFUNC_ARG_HWCAP, g_hwcap);
+
+  EXPECT_EQ(sizeof(__ifunc_arg_t), g_arg._size);
+  EXPECT_EQ(getauxval(AT_HWCAP), g_arg._hwcap);
+  EXPECT_EQ(getauxval(AT_HWCAP2), g_arg._hwcap2);
+#elif defined(__arm__)
+  EXPECT_EQ(getauxval(AT_HWCAP), g_hwcap);
+#endif
+}
+
+#endif  // defined(__BIONIC__)