Indirect functions (often called IFUNCs) are a GNU
extension to ELF that defers the selection of a function
implementation until load time. A symbol of type
STT_GNU_IFUNC does not designate the implementation of a
function. Instead, it designates a resolver function.
The GNU C Library invokes the resolver while processing relocations that
reference the symbol, and the address returned by the resolver becomes
the target of those references. The typical use of indirect functions
is to select the variant of a function best suited to the system the
program is running on, for example based on the available instruction
set extensions.
Indirect functions are usually defined using the ifunc function
attribute provided by GCC and compatible compilers:
static int
my_func_generic (int a)
{
/* ... */
}
static int
my_func_vectorized (int a)
{
/* ... */
}
/* The resolver returns the address of the selected
implementation. */
static typeof (my_func_generic) *
my_func_resolver (void)
{
if (vector_extension_available ())
return my_func_vectorized;
return my_func_generic;
}
int my_func (int a) __attribute__ ((ifunc ("my_func_resolver")));
The return value is the address of the selected implementation.
On many architectures, the resolver receives arguments that describe
the capabilities of the system, typically derived from the AT_HWCAP
value in the auxiliary vector (see Auxiliary Vector). These arguments
exist because of the restricted environment resolvers run in (see below):
they allow a resolver to select an implementation without having to obtain
the information through other means. Where such arguments are available,
resolvers should use them instead of querying the system through
function calls.
The architecture-specific conventions are:
#include <sys/ifunc.h> void *resolver (uint64_t hwcap, const __ifunc_arg_t *arg);
The first argument contains the AT_HWCAP value. If the
_IFUNC_ARG_HWCAP bit is set, the second argument is valid and
points to a __ifunc_arg_t structure that provides access to all
hardware capability values: the _size member contains the size of
the structure in bytes, and the _hwcap, _hwcap2,
_hwcap3, and _hwcap4 members contain the
AT_HWCAP, AT_HWCAP2, AT_HWCAP3, and
AT_HWCAP4 values, respectively. Resolvers must check
_size before accessing a member, because the GNU C Library may pass
a structure that ends before that member. The __ifunc_hwcap
inline function in sys/ifunc.h performs the required checks
when obtaining a hardware capability value by index.
The canonical documentation for this interface is the GNU C Library ifunc interface section of the ELF for the Arm 64-bit Architecture (AArch64) ABI, available at https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst.
void *resolver (unsigned long int hwcap);
The argument contains the AT_HWCAP value.
#include <sys/ifunc.h> void *resolver (const __ifunc_arg_t *arg);
The argument points to a __ifunc_arg_t structure whose
_size member contains the size of the structure in bytes and
whose _hwcap member contains the AT_HWCAP value. Resolvers
must check _size before accessing members added by later versions
of the structure.
void *resolver (unsigned long int hwcap);
The argument contains the AT_HWCAP value. The
AT_HWCAP2 value is not passed as an argument.
#include <sys/hwprobe.h>
void *resolver (uint64_t hwcap, __riscv_hwprobe_t hwprobe,
void *reserved);
The first argument contains the AT_HWCAP value. The second
argument is a pointer to the __riscv_hwprobe function
(see RISC-V-specific Facilities), passed so that the resolver can probe for extensions
without referencing a symbol in another object. Versions of
the GNU C Library before 2.40 pass a null pointer here, so resolvers must
check the argument before calling it; the __riscv_hwprobe_one
inline function in sys/hwprobe.h performs this check. The
third argument is reserved for future extension.
void *resolver (unsigned long int hwcap);
The argument contains the AT_HWCAP value.
void *resolver (int hwcap);
The argument contains the AT_HWCAP value.
void *resolver (void);
No arguments are passed: on x86 the CPUID instruction can be
executed directly from the resolver instead. Resolvers can use the
__cpuid and __cpuid_count macros from the GCC
cpuid.h header.
Alternatively, the CPU_FEATURE_PRESENT and
CPU_FEATURE_ACTIVE macros from sys/platform/x86.h
(see X86-specific Facilities) can be used; the data they consult is initialized by
the GNU C Library before any resolver runs.
Resolver functions run early, before the process or the newly loaded objects are fully initialized. The exact point at which a resolver runs depends on how the indirect function is referenced and how the program is linked:
main. Non-lazy references include data
relocations against the function address (for example, initialized
function pointer variables), and all references if lazy binding is
disabled (for example, with the -z now link-time option or
the LD_BIND_NOW environment variable).
dlopen, resolvers run during the
relocation of the newly loaded objects, before their ELF constructors
run.
main.
A resolver can be invoked more than once: in general, it runs once for every relocation that references the indirect function symbol, and distinct objects referencing the same symbol cause separate resolver invocations. Under lazy binding, resolver invocations can also happen concurrently on multiple threads. Resolvers must therefore be idempotent: they should return the same implementation on every invocation and avoid side effects.
Because resolvers run during relocation processing, only a restricted execution environment is available to them. The GNU C Library provides the guarantees listed below, each annotated with the version of the GNU C Library from which it applies. Versions before 2.44 provided none of them: resolvers could run before thread-local storage, the TCB, and the stack protector were initialized, and before the relocations of other objects had been processed. A resolver that must remain compatible with those versions should not rely on any of these guarantees, and should restrict itself to the architecture-provided resolver arguments and to data defined in the same translation unit.
glibc.cpu.hwcaps
(see Hardware Capability Tunables). (Since the GNU C Library 2.44.)
__thread, _Thread_local) defined by the same object
that defines the resolver, using any TLS access model. The initial
values of such variables are visible to the resolver, and stores
performed by the resolver persist after it returns. This applies to
objects loaded at process startup, to objects loaded with
dlopen, and to statically linked programs. (Since the GNU C Library
2.44.)
dlopen, newly loaded objects are relocated after their
dependencies. A resolver in a newly loaded object may reference data
and call functions defined by the object itself, by its dependencies
(direct and indirect), and by objects that were already loaded before
the dlopen call. (Since the GNU C Library 2.44.)
The GNU C Library itself defines indirect functions and the guarantees above apply to these internal resolvers as well.
Everything not listed above should be considered unsupported. In particular, the following restrictions apply:
malloc, dlopen,
dlsym, and creating threads.
__libc_single_threaded).
For recommendations on the use of indirect functions in hardened or security-sensitive applications, see Avoiding Unexpected Issues With Dynamic Linking.