This documentation was written by Mark Johnston <markj@FreeBSD.org>
under sponsorship from the FreeBSD Foundation.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
$FreeBSD$
.Dd May 18, 2019 .Dt DEFINE_IFUNC 9 .Os .Sh NAME .Nm DEFINE_IFUNC .Nd define a kernel function with an implementation selected at run-time .Sh SYNOPSIS n machine/ifunc.h .Fn DEFINE_IFUNC qual ret_type name args .Sh DESCRIPTION ifuncs are a linker feature which allows the programmer to define functions whose implementation is selected at boot-time or module load-time. The .Nm 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.
p Because .Nm is a macro that defines a dynamically typed function, its usage looks somewhat unusual. The .Ar 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 .Dv static qualifier. .Ar ret_type is the return type of the ifunc. .Ar name is the name of the ifunc. .Ar args is a parenthesized, comma-separated list of the parameter types of the function, as they would appear in a C function declaration.
p The .Nm usage must be followed by the resolver function body. The resolver must return a function with return type .Ar ret_type and parameter types .Ar args . The resolver function is defined with the .Ql resolver gcc-style function attribute, causing the corresponding .Xr elf 5 function symbol to be of type .Dv STT_GNU_IFUNC instead of .Dv STT_FUNC . The kernel linker invokes the resolver to process relocations targeting ifunc calls and PLT entries referencing such symbols. .Sh EXAMPLES ifunc resolvers are executed early during boot, before most kernel facilities are available. They are effectively limited to checking CPU feature flags and tunables. d -literal 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); } .Ed
p This defines a .Fn strlen function with an optimized implementation for CPUs that advertise support. .Sh SEE ALSO .Xr elf 5 .Sh NOTES ifuncs are not supported on all architectures. They require both toolchain support, to emit function symbols of type .Dv STT_GNU_IFUNC , and kernel linker support to invoke ifunc resolvers during boot or during module load.