37.4 Indirect Functions

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")));

37.4.1 IFUNC Resolver Calling Conventions

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:

AArch64
#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.

Arm
void *resolver (unsigned long int hwcap);

The argument contains the AT_HWCAP value.

LoongArch
#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.

PowerPC (32-bit and 64-bit)
void *resolver (unsigned long int hwcap);

The argument contains the AT_HWCAP value. The AT_HWCAP2 value is not passed as an argument.

RISC-V
#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.

s390 (64-bit)
void *resolver (unsigned long int hwcap);

The argument contains the AT_HWCAP value.

SPARC (32-bit and 64-bit)
void *resolver (int hwcap);

The argument contains the AT_HWCAP value.

x86 (i386 and x86-64)
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.

37.4.2 When IFUNC Resolvers Run

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:

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.

37.4.3 Supported Functionality in IFUNC Resolvers

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.

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:

For recommendations on the use of indirect functions in hardened or security-sensitive applications, see Avoiding Unexpected Issues With Dynamic Linking.