aboutsummaryrefslogtreecommitdiff
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>