[PATCH v7 1/2] aarch64: add support for hwcap3,4

Yury Khrustalev yury.khrustalev@arm.com
Thu Jun 5 12:20:29 GMT 2025


Add basic support for hwcap3 and hwcap4 in dynamic loader and
ifunc resolvers.

Describe new backward-compatible prototype for GNU indirect
function resolvers that use a pointer to uint64_t array in
stead of a pointer to the __ifunc_arg_t struct.

This patch also adds macro _IFUNC_HWCAP_MAX to specify current
number of hwcap elements.
---
 sysdeps/aarch64/dl-irel.h         | 21 +++++++++++++++++--
 sysdeps/aarch64/sys/ifunc.h       | 34 +++++++++++++++++++++++++------
 sysdeps/aarch64/tst-ifunc-arg-1.c |  3 +++
 sysdeps/aarch64/tst-ifunc-arg-2.c |  3 +++
 4 files changed, 53 insertions(+), 8 deletions(-)

diff --git a/sysdeps/aarch64/dl-irel.h b/sysdeps/aarch64/dl-irel.h
index ae402bc367..7bae3c3c49 100644
--- a/sysdeps/aarch64/dl-irel.h
+++ b/sysdeps/aarch64/dl-irel.h
@@ -21,11 +21,26 @@
 #define _DL_IREL_H
 
 #include <stdio.h>
-#include <unistd.h>
 #include <ldsodefs.h>
-#include <sysdep.h>
 #include <sys/ifunc.h>
 
+#define _IFUNC_ARG_SIZE_VER0 24 /* sizeof 1st published __ifunc_arg_t */
+#define _IFUNC_ARG_SIZE_VER1 40 /* sizeof 2nd published __ifunc_arg_t */
+
+#define sizeof_field(TYPE, MEMBER) sizeof ((((TYPE *)0)->MEMBER))
+#define offsetofend(TYPE, MEMBER) \
+  (offsetof (TYPE, MEMBER) + sizeof_field (TYPE, MEMBER))
+
+_Static_assert (sizeof (__ifunc_arg_t) == _IFUNC_ARG_SIZE_VER1,
+  "sizeof (__ifunc_arg_t) != _IFUNC_ARG_SIZE_VER1");
+
+_Static_assert (_IFUNC_ARG_SIZE_VER1
+  == (_IFUNC_HWCAP_MAX + 1) * sizeof (unsigned long),
+  "_IFUNC_ARG_SIZE_VER1 and _IFUNC_HWCAP_MAX mismatch");
+
+#undef offsetofend
+#undef sizeof_field
+
 #define ELF_MACHINE_IRELA	1
 
 static inline ElfW(Addr)
@@ -37,6 +52,8 @@ elf_ifunc_invoke (ElfW(Addr) addr)
   arg._size = sizeof (arg);
   arg._hwcap = GLRO(dl_hwcap);
   arg._hwcap2 = GLRO(dl_hwcap2);
+  arg._hwcap3 = GLRO(dl_hwcap3);
+  arg._hwcap4 = GLRO(dl_hwcap4);
   return ((ElfW(Addr) (*) (uint64_t, const __ifunc_arg_t *)) (addr))
 	 (GLRO(dl_hwcap) | _IFUNC_ARG_HWCAP, &arg);
 }
diff --git a/sysdeps/aarch64/sys/ifunc.h b/sysdeps/aarch64/sys/ifunc.h
index 7781b37a29..42d173195f 100644
--- a/sysdeps/aarch64/sys/ifunc.h
+++ b/sysdeps/aarch64/sys/ifunc.h
@@ -22,19 +22,41 @@
 /* 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
+/* Maximum number of HWCAP elements that are currently supported.  */
+#define _IFUNC_HWCAP_MAX	4
+
+/* The prototype of a GNU indirect function resolver on AArch64 is
+
+     ElfW(Addr) ifunc_resolver (uint64_t, const uint64_t *);
+
+   The following prototype is also compatible:
 
      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.  */
+   The first argument might have the _IFUNC_ARG_HWCAP bit set and
+   the remaining bits should match the AT_HWCAP settings.
+
+   If the _IFUNC_ARG_HWCAP bit is set in the first argument, then
+   the second argument is passed to the resolver function.  In
+   this case, the second argument is a const pointer to a buffer
+   that allows to access all available HWCAP elements.
+
+   This buffer has its size in bytes at offset 0.  The HWCAP elements
+   are available at offsets 8, 16, 24, 32... respectively for AT_HWCAP,
+   AT_HWCAP2, AT_HWCAP3, AT_HWCAP4...  (these offsets are multiples of
+   sizeof (unsigned long)).
+
+   Indirect function resolvers must check availability of HWCAP
+   elements at runtime before accessing them using the size of the
+   buffer.  */
 
-/* Second argument to an ifunc resolver.  */
 struct __ifunc_arg_t
 {
-  unsigned long _size; /* Size of the struct, so it can grow.  */
+  unsigned long _size;    /* Size of the struct, so it can grow.  */
   unsigned long _hwcap;
-  unsigned long _hwcap2;
+  unsigned long _hwcap2;  /* End of 1st published struct.  */
+  unsigned long _hwcap3;
+  unsigned long _hwcap4;  /* End of 2nd published struct.  */
 };
 
 typedef struct __ifunc_arg_t __ifunc_arg_t;
diff --git a/sysdeps/aarch64/tst-ifunc-arg-1.c b/sysdeps/aarch64/tst-ifunc-arg-1.c
index b90c836000..9cc3d40c48 100644
--- a/sysdeps/aarch64/tst-ifunc-arg-1.c
+++ b/sysdeps/aarch64/tst-ifunc-arg-1.c
@@ -57,6 +57,9 @@ do_test (void)
   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));
+  TEST_COMPARE (saved_arg2._hwcap3, getauxval (AT_HWCAP3));
+  TEST_COMPARE (saved_arg2._hwcap4, getauxval (AT_HWCAP4));
+
   return 0;
 }
 
diff --git a/sysdeps/aarch64/tst-ifunc-arg-2.c b/sysdeps/aarch64/tst-ifunc-arg-2.c
index dac144d937..d9950f0e37 100644
--- a/sysdeps/aarch64/tst-ifunc-arg-2.c
+++ b/sysdeps/aarch64/tst-ifunc-arg-2.c
@@ -60,6 +60,9 @@ do_test (void)
   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));
+  TEST_COMPARE (saved_arg2._hwcap3, getauxval (AT_HWCAP3));
+  TEST_COMPARE (saved_arg2._hwcap4, getauxval (AT_HWCAP4));
+
   return 0;
 }
 
-- 
2.39.5



More information about the Libc-alpha mailing list