This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
Re: [RFC PATCH] aarch64: new ifunc resolver ABI
- From: Szabolcs Nagy <Szabolcs dot Nagy at arm dot com>
- To: GNU C Library <libc-alpha at sourceware dot org>
- Cc: nd <nd at arm dot com>, Florian Weimer <fweimer at redhat dot com>
- Date: Wed, 12 Jun 2019 10:41:35 +0000
- Subject: Re: [RFC PATCH] aarch64: new ifunc resolver ABI
- References: <d242d10d-8f83-1913-44d3-4c9fc49e0477@arm.com>
On 12/06/2019 11:09, Szabolcs Nagy wrote:
> Passing a second argument to the ifunc resolver allows accessing
> AT_HWCAP2 values from the resolver. AArch64 will start using AT_HWCAP2
> on linux because for ilp32 to remain compatible with lp64 ABI no more
> than 32bit hwcap flags can be in AT_HWCAP which is already used up.
>
> Currently the relocation ordering logic does not guarantee that ifunc
> resolvers can call libc apis or access libc objects, so only the
> resolver arguments and runtime environment dependent instructions can
> be used to do the dispatch (this affects ifunc resolvers outside of
> the libc).
>
> Since ifunc resolver is target specific and only supposed to be
> called by the dynamic linker, the call ABI can be changed in a
> backward compatible way:
>
> Old call ABI passed hwcap as uint64_t, new abi sets the
> _IFUNC_ARG_HWCAP flag in the hwcap and passes a second argument
> that's a pointer to an extendible struct. A resolver has to check
> the _IFUNC_ARG_HWCAP flag before accessing the second argument.
>
> The new sys/ifunc.h installed header has the definitions for the
> new ABI, everything is in the implementation reserved namespace.
>
> An alternative approach is to try to support extern calls from ifunc
> resolvers such as getauxval, but that seems non-trivial
> https://sourceware.org/ml/libc-alpha/2017-01/msg00468.html
>
> 2019-06-12 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.
>
now with the correct patch attached.
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>