diff options
-rw-r--r-- | ChangeLog | 8 | ||||
-rw-r--r-- | sysdeps/aarch64/Makefile | 6 | ||||
-rw-r--r-- | sysdeps/aarch64/dl-irel.h | 9 | ||||
-rw-r--r-- | sysdeps/aarch64/sys/ifunc.h | 42 | ||||
-rw-r--r-- | sysdeps/aarch64/tst-ifunc-arg-1.c | 63 | ||||
-rw-r--r-- | sysdeps/aarch64/tst-ifunc-arg-2.c | 66 |
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> |