aarch64: add support for hwcap3,4

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>
This commit is contained in:
Yury Khrustalev 2025-03-12 15:28:24 +00:00
parent 25f1d94576
commit ea14d04e9a
4 changed files with 53 additions and 8 deletions

View File

@ -21,11 +21,26 @@
#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);
}

View File

@ -22,19 +22,41 @@
/* 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 _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;

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

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