riscv: Enable multi-arg ifunc resolvers

RISC-V is apparently the first architecture to pass more than one
argument to ifunc resolvers. The helper macros in libc-symbols.h,
__ifunc_resolver(), __ifunc(), and __ifunc_hidden(), are incompatible
with this. These macros have an "arg" (non-final) parameter that
represents the parameter signature of the ifunc resolver. The result is
an inability to pass the required comma through in a single preprocessor
argument.

Rearrange the __ifunc_resolver() macro to be variadic, and pass the
types as those variable parameters. Move the guts of __ifunc() and
__ifunc_hidden() into new macros, __ifunc_args(), and
__ifunc_args_hidden(), that pass the variable arguments down through to
__ifunc_resolver(). Then redefine __ifunc() and __ifunc_hidden(), which
are used in a bunch of places, to simply shuffle the arguments down into
__ifunc_args[_hidden]. Finally, define a riscv-ifunc.h header, which
provides convenience macros to those looking to write ifunc selectors
that use both arguments.

Signed-off-by: Evan Green <evan@rivosinc.com>
Reviewed-by: Florian Weimer <fweimer@redhat.com>
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
This commit is contained in:
Evan Green 2024-02-27 14:56:41 -08:00 committed by Palmer Dabbelt
parent 78308ce77a
commit a29bb320a1
No known key found for this signature in database
GPG Key ID: 2E1319F35FBB1889
2 changed files with 44 additions and 11 deletions

View File

@ -667,9 +667,9 @@ for linking")
#endif #endif
/* Helper / base macros for indirect function symbols. */ /* Helper / base macros for indirect function symbols. */
#define __ifunc_resolver(type_name, name, expr, arg, init, classifier) \ #define __ifunc_resolver(type_name, name, expr, init, classifier, ...) \
classifier inhibit_stack_protector \ classifier inhibit_stack_protector \
__typeof (type_name) *name##_ifunc (arg) \ __typeof (type_name) *name##_ifunc (__VA_ARGS__) \
{ \ { \
init (); \ init (); \
__typeof (type_name) *res = expr; \ __typeof (type_name) *res = expr; \
@ -677,13 +677,13 @@ for linking")
} }
#ifdef HAVE_GCC_IFUNC #ifdef HAVE_GCC_IFUNC
# define __ifunc(type_name, name, expr, arg, init) \ # define __ifunc_args(type_name, name, expr, init, ...) \
extern __typeof (type_name) name __attribute__ \ extern __typeof (type_name) name __attribute__ \
((ifunc (#name "_ifunc"))); \ ((ifunc (#name "_ifunc"))); \
__ifunc_resolver (type_name, name, expr, arg, init, static) __ifunc_resolver (type_name, name, expr, init, static, __VA_ARGS__)
# define __ifunc_hidden(type_name, name, expr, arg, init) \ # define __ifunc_args_hidden(type_name, name, expr, init, ...) \
__ifunc (type_name, name, expr, arg, init) __ifunc_args (type_name, name, expr, init, __VA_ARGS__)
#else #else
/* Gcc does not support __attribute__ ((ifunc (...))). Use the old behaviour /* Gcc does not support __attribute__ ((ifunc (...))). Use the old behaviour
as fallback. But keep in mind that the debug information for the ifunc as fallback. But keep in mind that the debug information for the ifunc
@ -694,18 +694,24 @@ for linking")
different signatures. (Gcc support is disabled at least on a ppc64le different signatures. (Gcc support is disabled at least on a ppc64le
Ubuntu 14.04 system.) */ Ubuntu 14.04 system.) */
# define __ifunc(type_name, name, expr, arg, init) \ # define __ifunc_args(type_name, name, expr, init, ...) \
extern __typeof (type_name) name; \ extern __typeof (type_name) name; \
__typeof (type_name) *name##_ifunc (arg) __asm__ (#name); \ __typeof (type_name) *name##_ifunc (__VA_ARGS__) __asm__ (#name); \
__ifunc_resolver (type_name, name, expr, arg, init,) \ __ifunc_resolver (type_name, name, expr, init, , __VA_ARGS__) \
__asm__ (".type " #name ", %gnu_indirect_function"); __asm__ (".type " #name ", %gnu_indirect_function");
# define __ifunc_hidden(type_name, name, expr, arg, init) \ # define __ifunc_args_hidden(type_name, name, expr, init, ...) \
extern __typeof (type_name) __libc_##name; \ extern __typeof (type_name) __libc_##name; \
__ifunc (type_name, __libc_##name, expr, arg, init) \ __ifunc (type_name, __libc_##name, expr, __VA_ARGS__, init) \
strong_alias (__libc_##name, name); strong_alias (__libc_##name, name);
#endif /* !HAVE_GCC_IFUNC */ #endif /* !HAVE_GCC_IFUNC */
#define __ifunc(type_name, name, expr, arg, init) \
__ifunc_args (type_name, name, expr, init, arg)
#define __ifunc_hidden(type_name, name, expr, arg, init) \
__ifunc_args_hidden (type_name, name, expr, init, arg)
/* The following macros are used for indirect function symbols in libc.so. /* The following macros are used for indirect function symbols in libc.so.
First of all, you need to have the function prototyped somewhere, First of all, you need to have the function prototyped somewhere,
say in foo.h: say in foo.h:

View File

@ -0,0 +1,27 @@
/* Common definition for ifunc resolvers. Linux/RISC-V version.
This file is part of the GNU C Library.
Copyright (C) 2024 Free Software Foundation, Inc.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <sysdep.h>
#include <ifunc-init.h>
#include <sys/hwprobe.h>
#define INIT_ARCH()
#define riscv_libc_ifunc(name, expr) \
__ifunc_args (name, name, expr(hwcap, hwprobe), INIT_ARCH, \
uint64_t hwcap, __riscv_hwprobe_t hwprobe)