DEFINE_IFUNC(9)

πŸ’– DEFINE_IFUNC πŸ’–

okay bestie, we're doing a kernel developer's manual page today and honestly? it's giving main character energy. this is DEFINE_IFUNC from section 9 of the manual, and it's all about defining a kernel function whose actual implementation gets picked at run-time. slay.


✨ NAME ✨

DEFINE_IFUNC β€” define a kernel function with an implementation selected at run-time πŸ¦‹


✨ SYNOPSIS ✨

#include <machine/ifunc.h>

DEFINE_IFUNC(qual, ret_type, name, args);

✨ DESCRIPTION ✨

so. ifuncs are a linker feature, right, and what they let you do is define functions where the actual implementation gets chosen either at boot-time or at module load-time. the DEFINE_IFUNC macro is literally how you define one of these. πŸŽ€

the choosing is done by a resolver function, and that resolver returns a pointer to whichever function got selected. and the timing? ifunc resolvers get invoked very early β€” like, during the machine-dependent initialization routine early, or at load time if we're talking about dynamically loaded modules. the resolution has to happen before the first call to an ifunc, no cap. and it happens after CPU features are enumerated and after the kernel's environment is initialized. periodt. πŸ’…

the typical use-case, iykyk, is a routine whose behavior depends on optional CPU features. for example β€” newer generations of a given CPU architecture might provide an instruction that optimizes some common operation. and to avoid the overhead of testing for that CPU feature every single time the operation runs, you can use an ifunc to provide two implementations: one that targets platforms with the extra instruction, and one for the older platforms. 🫢


the weird-looking part (it's fine, we promise)

because DEFINE_IFUNC is a macro that defines a dynamically typed function, its usage looks... somewhat unusual. πŸ™ˆ that's just how it is. here's the breakdown:

  • qual β€” a list of zero or more C function qualifiers to be applied to the ifunc. typically this is either empty or the static qualifier.
  • ret_type β€” the return type of the ifunc.
  • name β€” the name of the ifunc.
  • args β€” a parenthesized, comma-separated list of the parameter types of the function, exactly as they would appear in a C function declaration.

the resolver situation 🌸

the DEFINE_IFUNC usage must be followed by the resolver function body. the resolver must return a function with return type ret_type and parameter types args.

the resolver function is defined with the resolver gcc-style function attribute, and that's what causes the corresponding elf(5) function symbol to be of type STT_GNU_IFUNC instead of STT_FUNC. ✨

then the kernel linker invokes the resolver to process relocations targeting ifunc calls and PLT entries that reference such symbols. iconic behavior tbh. πŸ‘‘


✨ EXAMPLES ✨

⚠️ heads up bestie: ifunc resolvers are executed early during boot, before most kernel facilities are available. they're effectively limited to checking CPU feature flags and tunables. that's it, that's the whole toolbox. 😭
static size_t
fast_strlen(const char *s __unused)
{
    size_t len;

    /* Fast, but may not be correct in all cases. */
    __asm("movq $42,%0\n" : "=r" (len));
    return (len);
}

static size_t
slow_strlen(const char *s)
{
    const char *t;

    for (t = s; *t != '\0'; t++);
    return (t - s);
}

DEFINE_IFUNC(, size_t, strlen, (const char *))
{
    int enabled;

    enabled = 1;
    TUNABLE_INT_FETCH("debug.use_fast_strlen", &enabled);
    if (enabled && (cpu_features & CPUID_FAST_STRLEN) != 0)
        return (fast_strlen);
    else
        return (slow_strlen);
}

what this does is define a strlen() function with an optimized implementation for CPUs that advertise support. ate. πŸ’…


✨ SEE ALSO ✨


✨ NOTES ✨

🚨 real talk: ifuncs are not supported on all architectures. they require both toolchain support β€” to emit function symbols of type STT_GNU_IFUNC β€” and kernel linker support to invoke ifunc resolvers during boot or during module load. if you're missing either one, it's not happening. πŸ’€

and that's the whole page, bestie!! this one came from the Debian crew, dated May 18, 2019, and it's the section 9 manual page for DEFINE_IFUNC. go forth and resolve responsibly πŸ¦‹πŸ’–βœ¨

πŸ“¦ where this page comes from