On Mon, Jul 14, 2025 at 5:32 AM Yury Khrustalev <yury.khrusta...@arm.com> wrote:
>
> This optional header is used to bring in the definition of the
> struct __ifunc_arg_t type. Since it has been added to glibc only
> recently, the previous implementation had to check whether this
> header is present and, if not, it provide its own definition.
>
> This creates dead code because either one of these two parts would
> not be tested. The ABI specification for ifunc resolvers allows to
> create own ABI-compatible definition for this type, which is the
> right way of doing it.
>
> In addition to improving consistency, the new approach also helps
> with addition of new fields to struct __ifunc_arg_t type without
> the need to work-around situations when the definition imported
> from the header lacks these new fields.
>
> ABI allows to define as many hwcap fields in this struct as needed,
> provided that at runtime we only access the fields that are permitted
> by the _size value.
>
> gcc/
>         * config/aarch64/aarch64.cc (build_ifunc_arg_type):
>         Add new fields _hwcap3 and _hwcap4.
>
> libatomic/
>         * config/linux/aarch64/host-config.h (__ifunc_arg_t):
>         Remove sys/ifunc.h and add new fields _hwcap3 and _hwcap4.
>
> libgcc/
>         * config/aarch64/cpuinfo.c (__ifunc_arg_t): Likewise.
>         (__init_cpu_features): obtain and assign values for the
>         fields _hwcap3 and _hwcap4.
>         (__init_cpu_features_constructor): check _size in the
>         arg argument.
>
> ---
>
> Regression has been checked on AArch64 and no regression has been
> found. OK for trunk?

I think this is ok. I assume you tested it with both a newish and older glibc.

Thanks,
Andrew


>
> base commit: 3a1067c8b8c
>
> ---
>  gcc/config/aarch64/aarch64.cc                | 12 +++++
>  libatomic/config/linux/aarch64/host-config.h | 12 +++--
>  libgcc/config/aarch64/cpuinfo.c              | 46 ++++++++++++++++----
>  3 files changed, 57 insertions(+), 13 deletions(-)
>
> diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
> index 6e16763f957..26c0f70f952 100644
> --- a/gcc/config/aarch64/aarch64.cc
> +++ b/gcc/config/aarch64/aarch64.cc
> @@ -20466,6 +20466,8 @@ aarch64_compare_version_priority (tree decl1, tree 
> decl2)
>       unsigned long _size; // Size of the struct, so it can grow.
>       unsigned long _hwcap;
>       unsigned long _hwcap2;
> +     unsigned long _hwcap3;
> +     unsigned long _hwcap4;
>     }
>   */
>
> @@ -20482,14 +20484,24 @@ build_ifunc_arg_type ()
>    tree field3 = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
>                             get_identifier ("_hwcap2"),
>                             long_unsigned_type_node);
> +  tree field4 = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
> +                           get_identifier ("_hwcap3"),
> +                           long_unsigned_type_node);
> +  tree field5 = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
> +                           get_identifier ("_hwcap4"),
> +                           long_unsigned_type_node);
>
>    DECL_FIELD_CONTEXT (field1) = ifunc_arg_type;
>    DECL_FIELD_CONTEXT (field2) = ifunc_arg_type;
>    DECL_FIELD_CONTEXT (field3) = ifunc_arg_type;
> +  DECL_FIELD_CONTEXT (field4) = ifunc_arg_type;
> +  DECL_FIELD_CONTEXT (field5) = ifunc_arg_type;
>
>    TYPE_FIELDS (ifunc_arg_type) = field1;
>    DECL_CHAIN (field1) = field2;
>    DECL_CHAIN (field2) = field3;
> +  DECL_CHAIN (field3) = field4;
> +  DECL_CHAIN (field4) = field5;
>
>    layout_type (ifunc_arg_type);
>
> diff --git a/libatomic/config/linux/aarch64/host-config.h 
> b/libatomic/config/linux/aarch64/host-config.h
> index d0d44bf18ea..c6f8d693f2c 100644
> --- a/libatomic/config/linux/aarch64/host-config.h
> +++ b/libatomic/config/linux/aarch64/host-config.h
> @@ -40,16 +40,20 @@
>  # define HWCAP2_LSE128 (1UL << 47)
>  #endif
>
> -#if __has_include(<sys/ifunc.h>)
> -# include <sys/ifunc.h>
> -#else
> +/* The following struct is ABI-correct description of the 2nd argument for an
> +   ifunc resolver as per SYSVABI spec (see link below).  It is safe to extend
> +   it with new fields.  The ifunc resolver implementations must always check
> +   the runtime size of the buffer using the value in the _size field.
> +   https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst. 
>  */
>  typedef struct __ifunc_arg_t {
>    unsigned long _size;
>    unsigned long _hwcap;
>    unsigned long _hwcap2;
> +  unsigned long _hwcap3;
> +  unsigned long _hwcap4;
>  } __ifunc_arg_t;
> +
>  # define _IFUNC_ARG_HWCAP (1ULL << 62)
> -#endif
>
>  /* From the file which imported `host-config.h' we can ascertain which
>     architectural extension provides relevant atomic support.  From this,
> diff --git a/libgcc/config/aarch64/cpuinfo.c b/libgcc/config/aarch64/cpuinfo.c
> index dda9dc69689..50f54cac26b 100644
> --- a/libgcc/config/aarch64/cpuinfo.c
> +++ b/libgcc/config/aarch64/cpuinfo.c
> @@ -27,15 +27,18 @@
>  #if __has_include(<sys/auxv.h>)
>  #include <sys/auxv.h>
>
> -#if __has_include(<sys/ifunc.h>)
> -#include <sys/ifunc.h>
> -#else
> +/* The following struct is ABI-correct description of the 2nd argument for an
> +   ifunc resolver as per SYSVABI spec (see link below).  It is safe to extend
> +   it with new fields.  The ifunc resolver implementations must always check
> +   the runtime size of the buffer using the value in the _size field.
> +   https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst. 
>  */
>  typedef struct __ifunc_arg_t {
>    unsigned long _size;
>    unsigned long _hwcap;
>    unsigned long _hwcap2;
> +  unsigned long _hwcap3;
> +  unsigned long _hwcap4;
>  } __ifunc_arg_t;
> -#endif
>
>  #if __has_include(<asm/hwcap.h>)
>  #include <asm/hwcap.h>
> @@ -237,6 +240,18 @@ struct {
>  #define HWCAP2_LRCPC3  (1UL << 46)
>  #endif
>
> +#ifndef AT_HWCAP3
> +#define AT_HWCAP3 29
> +#endif
> +
> +#ifndef AT_HWCAP4
> +#define AT_HWCAP4 30
> +#endif
> +
> +#define __IFUNC_ARG_SIZE_HWCAP2 (sizeof (unsigned long) * 3)
> +#define __IFUNC_ARG_SIZE_HWCAP3 (sizeof (unsigned long) * 4)
> +#define __IFUNC_ARG_SIZE_HWCAP4 (sizeof (unsigned long) * 5)
> +
>  static void
>  __init_cpu_features_constructor (unsigned long hwcap,
>                                  const __ifunc_arg_t *arg)
> @@ -247,8 +262,14 @@ __init_cpu_features_constructor (unsigned long hwcap,
>  #define extractBits(val, start, number) \
>    (val & ((1UL << number) - 1UL) << start) >> start
>    unsigned long hwcap2 = 0;
> -  if (hwcap & _IFUNC_ARG_HWCAP)
> +  if ((hwcap & _IFUNC_ARG_HWCAP) && arg->_size >= __IFUNC_ARG_SIZE_HWCAP2)
>      hwcap2 = arg->_hwcap2;
> +  unsigned long hwcap3 __attribute__ ((unused)) = 0;
> +  if ((hwcap & _IFUNC_ARG_HWCAP) && arg->_size >= __IFUNC_ARG_SIZE_HWCAP3)
> +    hwcap3 = arg->_hwcap3;
> +  unsigned long hwcap4 __attribute__ ((unused)) = 0;
> +  if ((hwcap & _IFUNC_ARG_HWCAP) && arg->_size >= __IFUNC_ARG_SIZE_HWCAP4)
> +    hwcap4 = arg->_hwcap4;
>    if (hwcap & HWCAP_CRC32)
>      setCPUFeature(FEAT_CRC);
>    if (hwcap & HWCAP_PMULL)
> @@ -383,17 +404,24 @@ __init_cpu_features(void)
>  {
>    unsigned long hwcap;
>    unsigned long hwcap2;
> +  unsigned long hwcap3;
> +  unsigned long hwcap4;
>
>    /* CPU features already initialized.  */
>    if (__atomic_load_n (&__aarch64_cpu_features.features, __ATOMIC_RELAXED))
>      return;
> -  hwcap = getauxval(AT_HWCAP);
> -  hwcap2 = getauxval(AT_HWCAP2);
> +  hwcap = getauxval (AT_HWCAP);
> +  hwcap2 = getauxval (AT_HWCAP2);
> +  hwcap3 = getauxval (AT_HWCAP3);
> +  hwcap4 = getauxval (AT_HWCAP4);
> +
>    __ifunc_arg_t arg;
> -  arg._size = sizeof(__ifunc_arg_t);
> +  arg._size = sizeof (__ifunc_arg_t);
>    arg._hwcap = hwcap;
>    arg._hwcap2 = hwcap2;
> -  __init_cpu_features_constructor(hwcap | _IFUNC_ARG_HWCAP, &arg);
> +  arg._hwcap3 = hwcap3;
> +  arg._hwcap4 = hwcap4;
> +  __init_cpu_features_constructor (hwcap | _IFUNC_ARG_HWCAP, &arg);
>  #undef extractBits
>  #undef getCPUFeature
>  #undef setCPUFeature
> --
> 2.39.5
>

Reply via email to