# GNU indirect function UNDER CONSTRUCTION. GNU indirect function (ifunc) is a mechanism making a direct function call resolve to an implementation picked by a resolver. It is mainly used in glibc but has adoption in FreeBSD. For some performance critical functions, e.g. memcpy/memset/strcpy, glibc provides multiple implementations optimized for different architecture levels. The application just uses `memcpy(...)` which compiles to call memcpy. The linker will create a PLT for `memcpy` and produce an associated special dynamic relocation referencing the resolver symbol/address. During relocation resolving at runtime, the return value of the resolver will be placed in the GOT entry and the PLT entry will load the address. ## Representation ifunc has a dedicated symbol type `STT_GNU_IFUNC` to mark it different from a regular function (`STT_FUNC`). The value 10 is in the OS-specific range (10~12). `readelf -s` tell you that the symbol is ifunc if OSABI is `ELFOSABI_GNU` or `ELFOSABI_FREEBSD`. On Linux, by default GNU as uses `ELFOSABI_NONE` (0). If ifunc is used, the OSABI will be changed to `ELFOSABI_GNU`. Similarly, GNU ld sets the OSABI to `ELFOSABI_GNU` if ifunc is used. gold does not do this [PR17735](https://sourceware.org/bugzilla/show_bug.cgi?id=17735). Things are loose in LLVM. The integrated assembler and LLD do not set `ELFOSABI_GNU`. Currently the only problem I know is the `readelf -s` display. Everything else works fine. ### Assembler behavior In assembly, you can assign the type `STT_GNU_IFUNC` to a symbol via `.type foo, @gnu_indirect_function`. An ifunc symbol is typically `STB_GLOBAL`. In the object file, `st_shndx` and `st_value` of an `STT_GNU_IFUNC` symbol indicate the resolver. After linking, if the symbol is still `STT_GNU_IFUNC`, its `st_value` field indicates the resolver address in the linked image. Assemblers usually convert relocations referencing a local symbol to reference the section symbol, but this behavior needs to be inhibited for `STT_GNU_IFUNC`. ### Example ``` cat > b.s < a.c < static void fff_impl() { printf("fff_impl()\n"); } static int z; void *fff_resolver() { return (char *)&fff_impl + z++; } __attribute__((ifunc("fff_resolver"))) void fff(); typedef void fptr(void); fptr *local_fptr = fff; extern fptr *global_fptr0, *global_fptr1; int main() { printf("local %p global0 %p global1 %p\n", local_fptr, global_fptr0, global_fptr1); return 0; } ``` ### Relocation resolving order `R_*_IRELATIVE` relocations are resolved eagerly. In glibc, there used to be a problem where ifunc resolvers ran before `GL(dl_hwcap)` and `GL(dl_hwcap2)` were set up https://sourceware.org/bugzilla/show_bug.cgi?id=27072. For the relocation resolver, the main executable needs to be processed the last to process `R_*_COPY`. Without ifunc, the resolving order of shared objects can be arbitrary. For ifunc, if the ifunc is defined in a processed module, it is fine. If the ifunc is defined in an unprocessed module, it may crash. For an ifunc defined in an executable, calling it from a shared object can be problematic because the executable's relocations haven't been resolved. The issue can be circumvented by converting the non-preemptible ifunc defined in the executable to `STT_FUNC`. GNU ld's x86 port made the change [PR23169](https://sourceware.org/bugzilla/show_bug.cgi?id=23169). ## `-z ifunc-noplt` Mark Johnston introduced `-z ifunc-noplt` for FreeBSD https://reviews.llvm.org/D61613. With this option, all relocations referencing `STT_GNU_IFUNC` will be emitted as dynamic relocations (if `.dynsym` is created). The canonical PLT entry will not be used. ## Miscellaneous GNU ld has implemented a diagnostic (["i686 ifunc and non-default symbol visibility"](https://sourceware.org/bugzilla/show_bug.cgi?id=20515)) to flag `R_386_PC32` referencing non-default visibility ifunc in `-pie` and `-shared` links. This diagnostic looks like the most prominent reason blocking my proposal to use `R_386_PLT32` for `call/jump foo`. See [Copy relocations, canonical PLT entries and protected visibility](maskray-5.md) for details. https://sourceware.org/glibc/wiki/GNU_IFUNC misses a lot of information. There are quite a few arch differences. I asked for clarification https://sourceware.org/pipermail/libc-alpha/2021-January/121752.html ### Dynamic loader In glibc, `_dl_runtime_resolver` needs to save and restore vector and floating point registers. ifunc resolvers add another reason that `_dl_runtime_resolver` cannot only use integer registers. (The other reasons are that ld.so has string function calls which may use vectors and external calls to libc.so.)