[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