Patchwork [RFC] aarch64: new ifunc resolver ABI

login
register
mail settings
Submitter Szabolcs Nagy
Date June 12, 2019, 10:41 a.m.
Message ID <b689d4e3-6d0d-8282-28b8-8831a4581a8d@arm.com>
Download mbox | patch
Permalink /patch/33091/
State New
Headers show

Comments

Szabolcs Nagy - June 12, 2019, 10:41 a.m.
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.

Patch

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>