about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--sysdeps/aarch64/Makefile6
-rw-r--r--sysdeps/aarch64/dl-irel.h9
-rw-r--r--sysdeps/aarch64/sys/ifunc.h42
-rw-r--r--sysdeps/aarch64/tst-ifunc-arg-1.c63
-rw-r--r--sysdeps/aarch64/tst-ifunc-arg-2.c66
6 files changed, 193 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index 21e6374ab6..fe5fd79eac 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2019-07-04  Szabolcs Nagy  <szabolcs.nagy@arm.com>
+
+	* sysdeps/aarch64/Makefile: Install sys/ifunc.h and add tests.
+	* sysdeps/aarch64/dl-irel.h (elf_ifunc_invoke): Update to new ABI.
+	* sysdeps/aarch64/sys/ifunc.h: New file.
+	* sysdeps/aarch64/tst-ifunc-arg-1.c: New file.
+	* sysdeps/aarch64/tst-ifunc-arg-2.c: New file.
+
 2019-07-01  Florian Weimer  <fweimer@redhat.com>
 
 	[BZ #20188]
diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile
index 94baaf52dd..9cb141004d 100644
--- a/sysdeps/aarch64/Makefile
+++ b/sysdeps/aarch64/Makefile
@@ -3,6 +3,8 @@ long-double-fcts = yes
 ifeq ($(subdir),elf)
 sysdep-dl-routines += tlsdesc dl-tlsdesc
 gen-as-const-headers += dl-link.sym
+
+tests-internal += tst-ifunc-arg-1 tst-ifunc-arg-2
 endif
 
 ifeq ($(subdir),csu)
@@ -16,3 +18,7 @@ endif
 ifeq ($(subdir),math)
 CPPFLAGS += -I../soft-fp
 endif
+
+ifeq ($(subdir),misc)
+sysdep_headers += sys/ifunc.h
+endif
diff --git a/sysdeps/aarch64/dl-irel.h b/sysdeps/aarch64/dl-irel.h
index 4f669e70d7..8db6ef57dd 100644
--- a/sysdeps/aarch64/dl-irel.h
+++ b/sysdeps/aarch64/dl-irel.h
@@ -24,6 +24,7 @@
 #include <unistd.h>
 #include <ldsodefs.h>
 #include <sysdep.h>
+#include <sys/ifunc.h>
 
 #define ELF_MACHINE_IRELA	1
 
@@ -31,7 +32,13 @@ static inline ElfW(Addr)
 __attribute ((always_inline))
 elf_ifunc_invoke (ElfW(Addr) addr)
 {
-  return ((ElfW(Addr) (*) (uint64_t)) (addr)) (GLRO(dl_hwcap));
+  __ifunc_arg_t arg;
+
+  arg._size = sizeof (arg);
+  arg._hwcap = GLRO(dl_hwcap);
+  arg._hwcap2 = GLRO(dl_hwcap2);
+  return ((ElfW(Addr) (*) (uint64_t, const __ifunc_arg_t *)) (addr))
+	 (GLRO(dl_hwcap) | _IFUNC_ARG_HWCAP, &arg);
 }
 
 static inline void
diff --git a/sysdeps/aarch64/sys/ifunc.h b/sysdeps/aarch64/sys/ifunc.h
new file mode 100644
index 0000000000..ef200e9f26
--- /dev/null
+++ b/sysdeps/aarch64/sys/ifunc.h
@@ -0,0 +1,42 @@
+/* Definitions used by AArch64 indirect function resolvers.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _SYS_IFUNC_H
+#define _SYS_IFUNC_H
+
+/* A second argument is passed to the ifunc resolver.  */
+#define _IFUNC_ARG_HWCAP	(1ULL << 62)
+
+/* The prototype of a gnu indirect function resolver on AArch64 is
+
+     ElfW(Addr) ifunc_resolver (uint64_t, const __ifunc_arg_t *);
+
+   the first argument should have the _IFUNC_ARG_HWCAP bit set and
+   the remaining bits should match the AT_HWCAP settings.  */
+
+/* Second argument to an ifunc resolver.  */
+struct __ifunc_arg_t
+{
+  unsigned long _size; /* Size of the struct, so it can grow.  */
+  unsigned long _hwcap;
+  unsigned long _hwcap2;
+};
+
+typedef struct __ifunc_arg_t __ifunc_arg_t;
+
+#endif
diff --git a/sysdeps/aarch64/tst-ifunc-arg-1.c b/sysdeps/aarch64/tst-ifunc-arg-1.c
new file mode 100644
index 0000000000..cedd987030
--- /dev/null
+++ b/sysdeps/aarch64/tst-ifunc-arg-1.c
@@ -0,0 +1,63 @@
+/* Test STT_GNU_IFUNC resolver with second argument.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stdint.h>
+#include <sys/auxv.h>
+#include <sys/ifunc.h>
+#include <support/check.h>
+
+static int
+one (void)
+{
+  return 1;
+}
+
+static uint64_t saved_arg1;
+static __ifunc_arg_t saved_arg2;
+
+/* extern visible ifunc symbol.  */
+int
+foo (void);
+
+void *
+foo_ifunc (uint64_t, const __ifunc_arg_t *) __asm__ ("foo");
+__asm__(".type foo, %gnu_indirect_function");
+
+void *
+inhibit_stack_protector
+foo_ifunc (uint64_t arg1, const __ifunc_arg_t *arg2)
+{
+  saved_arg1 = arg1;
+  if (arg1 & _IFUNC_ARG_HWCAP)
+      saved_arg2 = *arg2;
+  return (void *) one;
+}
+
+static int
+do_test (void)
+{
+  TEST_VERIFY (foo () == 1);
+  TEST_VERIFY (saved_arg1 & _IFUNC_ARG_HWCAP);
+  TEST_COMPARE ((uint32_t)saved_arg1, (uint32_t)getauxval (AT_HWCAP));
+  TEST_COMPARE (saved_arg2._size, sizeof (__ifunc_arg_t));
+  TEST_COMPARE (saved_arg2._hwcap, getauxval (AT_HWCAP));
+  TEST_COMPARE (saved_arg2._hwcap2, getauxval (AT_HWCAP2));
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/aarch64/tst-ifunc-arg-2.c b/sysdeps/aarch64/tst-ifunc-arg-2.c
new file mode 100644
index 0000000000..9564818126
--- /dev/null
+++ b/sysdeps/aarch64/tst-ifunc-arg-2.c
@@ -0,0 +1,66 @@
+/* Test R_*_IRELATIVE resolver with second argument.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stdint.h>
+#include <sys/auxv.h>
+#include <sys/ifunc.h>
+#include <support/check.h>
+
+static int
+one (void)
+{
+  return 1;
+}
+
+static uint64_t saved_arg1;
+static __ifunc_arg_t saved_arg2;
+
+/* local ifunc symbol.  */
+int
+__attribute__ ((visibility ("hidden")))
+foo (void);
+
+static void *
+__attribute__ ((used))
+foo_ifunc (uint64_t, const __ifunc_arg_t *) __asm__ ("foo");
+__asm__(".type foo, %gnu_indirect_function");
+
+static void *
+__attribute__ ((used))
+inhibit_stack_protector
+foo_ifunc (uint64_t arg1, const __ifunc_arg_t *arg2)
+{
+  saved_arg1 = arg1;
+  if (arg1 & _IFUNC_ARG_HWCAP)
+      saved_arg2 = *arg2;
+  return (void *) one;
+}
+
+static int
+do_test (void)
+{
+  TEST_VERIFY (foo () == 1);
+  TEST_VERIFY (saved_arg1 & _IFUNC_ARG_HWCAP);
+  TEST_COMPARE ((uint32_t)saved_arg1, (uint32_t)getauxval (AT_HWCAP));
+  TEST_COMPARE (saved_arg2._size, sizeof (__ifunc_arg_t));
+  TEST_COMPARE (saved_arg2._hwcap, getauxval (AT_HWCAP));
+  TEST_COMPARE (saved_arg2._hwcap2, getauxval (AT_HWCAP2));
+  return 0;
+}
+
+#include <support/test-driver.c>