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

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Mon Jun 2 18:58:08 GMT 2025



On 13/05/25 11:13, Yury Khrustalev wrote:
> Add basic support for hwcap3 and hwcap4 in dynamic loader and
> ifunc resolvers.
> 
> Describe new backward-compatible prototype for GNU indirect
> function resolvers that uses a pointer to uint64_t instead of
> a pointer to the __ifunc_arg_t struct.
> 
> This patch also defines macros
> 
>  - _IFUNC_HWCAP_MAX
>  - _IFUNC_ARG_SIZE_VER0
>  - _IFUNC_ARG_SIZE_VER1
> 
> To help with versioning of the __ifunc_arg_t struct.
> ---
>  sysdeps/aarch64/dl-irel.h         | 18 +++++++++++++--
>  sysdeps/aarch64/sys/ifunc.h       | 37 ++++++++++++++++++++++++++-----
>  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..9abf7a60a1 100644
> --- a/sysdeps/aarch64/dl-irel.h
> +++ b/sysdeps/aarch64/dl-irel.h
> @@ -21,11 +21,23 @@
>  #define _DL_IREL_H
>  
>  #include <stdio.h>
> -#include <unistd.h>
>  #include <ldsodefs.h>
> -#include <sysdep.h>
>  #include <sys/ifunc.h>
>  
> +#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 +49,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..fd4d49f00b 100644
> --- a/sysdeps/aarch64/sys/ifunc.h
> +++ b/sysdeps/aarch64/sys/ifunc.h
> @@ -22,21 +22,46 @@
>  /* 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.  */
>  };
>  
> +#define _IFUNC_ARG_SIZE_VER0 24 /* sizeof 1st published struct */
> +#define _IFUNC_ARG_SIZE_VER1 40 /* sizeof 2nd published struct */

I agree with Ellott that these are not really required and might cause
more confusion than being helpful.

The rest look ok to me.

> +
>  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
> 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;
>  }
>  



More information about the Libc-alpha mailing list