DEFINE_IFUNC(9) | Kernel Developer's Manual | DEFINE_IFUNC(9) |
DEFINE_IFUNC
—
define a kernel function with an implementation selected at
run-time
#include
<machine/ifunc.h>
DEFINE_IFUNC
(qual,
ret_type,
name,
args);
ifuncs are a linker feature which allows the programmer to define
functions whose implementation is selected at boot-time or module load-time.
The DEFINE_IFUNC
macro can be used to define an
ifunc. The selection is performed by a resolver function, which returns a
pointer to the selected function. ifunc resolvers are invoked very early
during the machine-dependent initialization routine, or at load time for
dynamically loaded modules. Resolution must occur before the first call to
an ifunc. ifunc resolution is performed after CPU features are enumerated
and after the kernel's environment is initialized. The typical use-case for
an ifunc is a routine whose behavior depends on optional CPU features. For
example, newer generations of a given CPU architecture may provide an
instruction to optimize a common operation. To avoid the overhead of testing
for the CPU feature each time the operation is performed, an ifunc can be
used to provide two implementations for the operation: one targeting
platforms with the extra instruction, and one for older platforms.
Because DEFINE_IFUNC
is a macro that
defines a dynamically typed function, its usage looks somewhat unusual. The
qual parameter is a list of zero or more C function
qualifiers to be applied to the ifunc. This parameter is typically empty or
the static
qualifier. ret_type
is the return type of the ifunc. name is the name of
the ifunc. args is a parenthesized, comma-separated
list of the parameter types of the function, as they would appear in a C
function declaration.
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, causing the corresponding elf(5) function
symbol to be of type STT_GNU_IFUNC
instead of
STT_FUNC
. The kernel linker invokes the resolver to
process relocations targeting ifunc calls and PLT entries referencing such
symbols.
ifunc resolvers are executed early during boot, before most kernel facilities are available. They are effectively limited to checking CPU feature flags and tunables.
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); }
This defines a strlen
() function with an
optimized implementation for CPUs that advertise support.
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.
May 18, 2019 | Debian |