Indirect Functions (The GNU C Library)

### 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:

- In dynamically linked programs, resolvers for non-lazy references run during relocation processing at program startup, before ELF constructors and before `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).
- With lazy binding, the resolver for a function call through the PLT may not run until the function is called for the first time. Programs should not depend on whether a resolver runs at load time or at the time of the first call.
- For objects loaded with `dlopen`, resolvers run during the relocation of the newly loaded objects, before their ELF constructors run.
- In statically linked programs (including static PIE), resolvers are invoked by the startup code in the GNU C Library, before ELF constructors and before `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.

#### 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 thread control block (TCB) of the initial thread is set up before any resolver runs. This applies to dynamically linked programs and to statically linked programs (including static PIE). Consequently, resolvers may be compiled with stack protector instrumentation (`-fstack-protector` and its variants): the stack canary is initialized before the first resolver runs. (Since the GNU C Library 2.44.)
- Tunables (see Tunables) and the architecture-specific CPU feature detection used by the GNU C Library are processed before any resolver runs. A resolver that selects an implementation based on the GNU C Library’s CPU feature data therefore observes values that already reflect any tunable that masks hardware capabilities, such as `glibc.cpu.hwcaps` (see Hardware Capability Tunables). (Since the GNU C Library 2.44.)
- A resolver may read from and write to thread-local variables (`__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.)
- At program startup, the regular (non-IFUNC) relocations of all initially loaded objects are processed before any resolver runs. A resolver may therefore reference data and call functions defined in other initially loaded objects. (Since the GNU C Library 2.44.)
- During `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:

- ELF constructors of the objects being loaded have not run when their resolvers are invoked. Resolvers must not depend on any initialization performed by constructors, such as the construction of C++ global objects, even in other objects whose relocations have already been processed.
- Accessing thread-local variables defined by other objects is not supported.
- Functions that depend on a fully initialized the GNU C Library, or that may call back into the dynamic linker, are not supported in resolvers. This includes (but is not limited to) `malloc`, `dlopen`, `dlsym`, and creating threads.
- Because a resolved address is bound permanently, resolvers must not base their decision on state that can change during the lifetime of the process (see Detecting Single-Threaded Execution, for an example involving `__libc_single_threaded`).

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