[PATCH v3] aarch64: add support for hwcap3,4
enh
enh@google.com
Tue Apr 22 15:52:09 GMT 2025
On Tue, Apr 22, 2025 at 8:43 AM Yury Khrustalev <yury.khrustalev@arm.com> wrote:
>
> Add basic support for hwcap3 and hwcap4 in dynamic loader and
> ifunc resolvers.
>
> This patch tries to support both runtime an compile-time checks
> for compatibility.
>
> At runtime, resolver functions must check the value of the _size
> field to determine which hwcap fields are available.
>
> Existing resolver functions that rely on the second argument
> passed to the resolver function will use the _hwcap and _hwcap2
> fields. It is recommended in this patch that new resolvers use
> the _hwcap_array field instead when they are available at compile
> time.
>
> When compiling code for resolvers, old or new, the Glibc headers
> available at the compilation time maybe older or newer compared
> to the Glibc version that is available at runtime. To allow old
> resolvers to compile, we introduce union of a legacy struct with
> the _hwcap and _hwcap2 fields and an array _hwcap_array with the
> length of _IFUNC_HWCAP_MAX elements (currently 4). First 2 hwcap
> fields can therefore be accessed in two ways.
>
> Resolvers may use new macro defined in sys/ifunc.h:
>
> _IFUNC_ARG_SIZE
>
> for compile-time checks, for example:
>
> #ifndef _IFUNC_ARG_SIZE
> // use _hwcap and _hwcap2 fields
> #else
> // use _hwcap_array array field
> #endif
is this actually useful? this seems likely to encourage error-prone
code, and _IFUNC_HWCAP_MAX is already available if you want to know
what version your header is. (though tbh _that_ seems more
troublesome, because it encourages folks to forget that it's the
_runtime_ size that actually matters. for bionic i think i'll just
inline the 4 in the array declaration instead.)
so maybe just having the new _IFUNC_ARG_SIZE_VER0/_IFUNC_ARG_SIZE_VER1
-- which is also genuinely useful at run time, and makes it a bit
clearer that callers need to think about _both_ cases, not either/or
-- is what someone wanting to be "clever" at compile time should use?
> This patch also adds static asserts to ensure that when Glibc is
> compiled, legacy fields _hwcap{,2} are aligned precisely with the
> first two elements of _hwcap_array.
> ---
> Corresponding ABI spec update: https://github.com/ARM-software/abi-aa/pull/320
>
> Regression tested on AArch64 and no regressions have been found.
>
> OK for trunk?
>
> base commit: 39183b953c
>
> Changes in v3:
> - Instead of adding new fields to __ifunc_arg_t introduce a union
> to allow further seemless extension and at the same time letting
> existing code compile without change.
> Link to v2:
> https://inbox.sourceware.org/libc-alpha/20250404141511.1767584-1-yury.khrustalev@arm.com/
>
> Changes in v2:
> - Expanded comment in <sys/ifunc.h> to explain how to use the _size
> field to check the version of the contents of the __ifunc_arg_t
> struct.
> Link to v1:
> https://inbox.sourceware.org/libc-alpha/20250401141851.129625-1-yury.khrustalev@arm.com/
> ---
> sysdeps/aarch64/dl-irel.h | 32 ++++++++++++++++++++++----
> sysdeps/aarch64/sys/ifunc.h | 38 +++++++++++++++++++++++++++----
> sysdeps/aarch64/tst-ifunc-arg-1.c | 4 ++++
> sysdeps/aarch64/tst-ifunc-arg-2.c | 4 ++++
> 4 files changed, 69 insertions(+), 9 deletions(-)
>
> diff --git a/sysdeps/aarch64/dl-irel.h b/sysdeps/aarch64/dl-irel.h
> index ae402bc367..76719edae4 100644
> --- a/sysdeps/aarch64/dl-irel.h
> +++ b/sysdeps/aarch64/dl-irel.h
> @@ -21,11 +21,33 @@
> #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,
> + "sizeof (__ifunc_arg_t) != _IFUNC_ARG_SIZE");
> +
> +_Static_assert (offsetof (__ifunc_arg_t, _hwcap)
> + == offsetof (__ifunc_arg_t, _hwcap_array[0]),
> + "__ifunc_arg_t incorrect offset of _hwcap");
> +_Static_assert (offsetofend (__ifunc_arg_t, _hwcap)
> + == offsetofend (__ifunc_arg_t, _hwcap_array[0]),
> + "__ifunc_arg_t incorrect offset of _hwcap end");
> +
> +_Static_assert (offsetof (__ifunc_arg_t, _hwcap2)
> + == offsetof (__ifunc_arg_t, _hwcap_array[1]),
> + "__ifunc_arg_t incorrect offset of _hwcap2");
> +_Static_assert (offsetofend (__ifunc_arg_t, _hwcap2)
> + == offsetofend (__ifunc_arg_t, _hwcap_array[1]),
> + "__ifunc_arg_t incorrect offset of _hwcap2 end");
> +
> +#undef offsetofend
> +#undef sizeof_field
> +
> #define ELF_MACHINE_IRELA 1
>
> static inline ElfW(Addr)
> @@ -35,8 +57,10 @@ elf_ifunc_invoke (ElfW(Addr) addr)
> __ifunc_arg_t arg;
>
> arg._size = sizeof (arg);
> - arg._hwcap = GLRO(dl_hwcap);
> - arg._hwcap2 = GLRO(dl_hwcap2);
> + arg._hwcap_array[0] = GLRO(dl_hwcap);
> + arg._hwcap_array[1] = GLRO(dl_hwcap2);
> + arg._hwcap_array[2] = GLRO(dl_hwcap3);
> + arg._hwcap_array[3] = 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..88adc0ddb2 100644
> --- a/sysdeps/aarch64/sys/ifunc.h
> +++ b/sysdeps/aarch64/sys/ifunc.h
> @@ -21,22 +21,50 @@
>
> /* A second argument is passed to the ifunc resolver. */
> #define _IFUNC_ARG_HWCAP (1ULL << 62)
> +#define _IFUNC_HWCAP_MAX 4
>
> /* 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. */
> + 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, this second argument is a pointer to this struct and the
> + _size field is set to size of the struct and the remaining fields
> + are set to the respective values of AT_HWCAP{,2,3,4,...} settings.
> +
> + When a resolver function needs to access second argument, it must
> + check the value of the size field first to make sure it then uses
> + only available hwcap fields.
> +
> + Legacy resolver functions may access the _hwcap and _hwcap2 fields
> + however they are deprecated and all new implementations must use
> + elements of the _hwcap_array field when it is available.
> +
> + The following macros defined below can be used for compile-time
> + compatibility checks: _IFUNC_ARG_SIZE, _IFUNC_ARG_SIZE_VER0,
> + _IFUNC_ARG_SIZE_VER1. If _IFUNC_ARG_SIZE is not defined, then the
> + _hwcap_array field is not available yet. */
>
> -/* 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;
> + union {
> + struct {
> + unsigned long _hwcap;
> + unsigned long _hwcap2;
> + };
> + unsigned long _hwcap_array[_IFUNC_HWCAP_MAX];
> + };
> };
>
> typedef struct __ifunc_arg_t __ifunc_arg_t;
>
> +#define _IFUNC_ARG_SIZE_VER0 24 /* sizeof 1st published struct */
> +#define _IFUNC_ARG_SIZE_VER1 40 /* sizeof 2nd published struct */
> +#define _IFUNC_ARG_SIZE _IFUNC_ARG_SIZE_VER1
> +
> #endif
> diff --git a/sysdeps/aarch64/tst-ifunc-arg-1.c b/sysdeps/aarch64/tst-ifunc-arg-1.c
> index b90c836000..121320b3a2 100644
> --- a/sysdeps/aarch64/tst-ifunc-arg-1.c
> +++ b/sysdeps/aarch64/tst-ifunc-arg-1.c
> @@ -57,6 +57,10 @@ 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._hwcap_array[0], getauxval (AT_HWCAP));
> + TEST_COMPARE (saved_arg2._hwcap_array[1], getauxval (AT_HWCAP2));
> + TEST_COMPARE (saved_arg2._hwcap_array[2], getauxval (AT_HWCAP3));
> + TEST_COMPARE (saved_arg2._hwcap_array[3], 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..5028787b05 100644
> --- a/sysdeps/aarch64/tst-ifunc-arg-2.c
> +++ b/sysdeps/aarch64/tst-ifunc-arg-2.c
> @@ -60,6 +60,10 @@ 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._hwcap_array[0], getauxval (AT_HWCAP));
> + TEST_COMPARE (saved_arg2._hwcap_array[1], getauxval (AT_HWCAP2));
> + TEST_COMPARE (saved_arg2._hwcap_array[2], getauxval (AT_HWCAP3));
> + TEST_COMPARE (saved_arg2._hwcap_array[3], getauxval (AT_HWCAP4));
> return 0;
> }
>
> --
> 2.39.5
>
More information about the Libc-alpha
mailing list