]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
aarch64: add support for hwcap3,4
authorYury Khrustalev <yury.khrustalev@arm.com>
Wed, 12 Mar 2025 15:28:24 +0000 (15:28 +0000)
committerYury Khrustalev <yury.khrustalev@arm.com>
Thu, 5 Jun 2025 13:38:03 +0000 (14:38 +0100)
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.

Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
sysdeps/aarch64/dl-irel.h
sysdeps/aarch64/sys/ifunc.h
sysdeps/aarch64/tst-ifunc-arg-1.c
sysdeps/aarch64/tst-ifunc-arg-2.c

index ae402bc367bcf947cb4f8a8362d4471676338df1..7bae3c3c4908e87025c891f87552f72a0bbd750b 100644 (file)
 #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);
 }
index 7781b37a29b6c4b58e9fc896b0a3e63518d2a612..42d173195fd116882c197718ecae1837e2d8e786 100644 (file)
 /* 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;
index b90c836000816f12314ca77264726410b298b076..9cc3d40c48084439d459f5beeb6394ed5e51c4c6 100644 (file)
@@ -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;
 }
 
index dac144d9373f164c460c227a9c16cf0508b0c770..d9950f0e37a42666d261362ed426b33555d776f2 100644 (file)
@@ -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;
 }