Mips GNU IFUNC ABI I will use the terms STT_GNU_IFUNC and IFUNC interchangeably below. *************************************************************** Taken from: http://gcc.gnu.org/onlinedocs/gcc-4.8.0/gcc/Function-Attributes.html *************************************************************** ifunc ("resolver") The ifunc attribute is used to mark a function as an indirect function using the STT_GNU_IFUNC symbol type extension to the ELF standard. This allows the resolution of the symbol value to be determined dynamically at load time, and an optimized version of the routine can be selected for the particular processor or other system characteristics determined then. To use this attribute, first define the implementation functions available, and a resolver function that returns a pointer to the selected implementation function. The implementation functions' declarations must match the API of the function being implemented, the resolver's declaration is be a function returning pointer to void function returning void: void *my_memcpy (void *dst, const void *src, size_t len) { ... } static void (*resolve_memcpy (void)) (void) { return my_memcpy; // we'll just always select this routine } The exported header file declaring the function the user calls would contain: extern void *memcpy (void *, const void *, size_t); allowing the user to call this as a regular function, unaware of the implementation. Finally, the indirect function needs to be defined in the same translation unit as the resolver function: void *memcpy (void *, const void *, size_t) __attribute__ ((ifunc ("resolve_memcpy"))); Indirect functions cannot be weak, and require a recent binutils (at least version 2.20.1), and GNU C library (at least version 2.11.1). *************************************************************** *************************************************************** int func1 (void) __attribute__ ((ifunc ("func1_ifunc"))); static int f1_a(void){return 1;} static int f1_b(void){return 2;} static int f1_c(void){return 3;} void * func1_ifunc (void) { volatile int result; struct utsname buf; if ((uname(&buf))) result = 0x30; else result = 0x3; if (result & 0xf0) return f1_a; else if (result & 0x0f) return f1_b; else return f1_c; } *************************************************************** *************************************************************** A new symbol for the ifunc routine is created that associates it to the real ifunc1_func() function. .size func1_ifunc, .-func1_ifunc .globl func1 .type func1, @gnu_indirect_function func1 = func1_ifunc *************************************************************** *************************************************************** A new symbol func1 is created with the only difference from func1_ifunc being the section type STT_GNU_IFUNC. Section .symtab [No] Value Size Type Bind Other Shndx Name =============================================================== [22] 0xd8 0xbc FUNC GLOBAL DEFAULT (1) .text (42) func1_ifunc [25] 0xd8 0xbc IFUNC GLOBAL DEFAULT (1) .text (69) func1 *************************************************************** *************************************************************** There are 3 general linker output variations that are addressed here: 1) Static/Non-shared executables that do not use an independent dynamic linker at startup time. Instead there is an embedded startup routine to resolve IFUNC symbols or the R_MIPS_RELATIVE relocation created for them. 2) Executables that use shared objects and thus the dynamic linker. 3) Shared objects *** *1* *** Static executable links will for each IFUNC symbol produce the following: *) Generate an iplt stub which is text and will be part of the .iplt section. Currently the stub is the following instruction sequence: lui $15, %hi(.got.iplt entry) l[wd] $25, %lo(.got.iplt entry)($15) jr $25 nop The section attributes for .iplt are: sh_name: (.iplt) sh_type: SHT_PROGBITS sh_info: 0 sh_flags: SHF_ALLOC|SHF_EXECINSTR sh_addalign: 4 sh_entsize: 0 This stub will take the function address in the .igot.plt section for this IFUNC symbol and jump to it. *) Generate igot table (.igot.plt) This is an array of IFUNC addresses that point to the resolver function for this IFUNC. If the target ABI is 32 bit the entries will be 4 bytes each and if the ABI is 64 bit the entries will be 8 bytes each. This of course will preclude any linking of a combination of n32 and n64, but that will have more problems than ifunc. The section attributes for .iplt are: sh_name: (.igot.plt) sh_type: SHT_PROGBITS sh_info: 0 sh_flags: SHF_ALLOC|SHF_WRITE * sh_addalign: 4/8 sh_entsize: 4/8 (* Note, in spite of the name this is NOT a GP relative section) *) Generate R_MIPS_IRELATIVE relocations for the igot table. Produce a relocation of type R_MIPS_IRELATIVE (128) pointing at the .igot.plt table entry for this IFUNC. This goes into a .rel.dyn relocation section sh_name: (.rel.dyn) sh_type: SHT_REL sh_info: 0 sh_flags: SHF_ALLOC sh_addalign: 4/8 sh_entsize: 8/24 *) Generate the 2 symbols __rel_iplt_start and __rel_iplt_end which point at the beginning and end of the relocations for the .igot.iplt section. That is, first entry and last entry. Not last entry+1. Type: STT_NOTYPE Binding: STB_LOCAL Other: STO_DEFAULT Size: 0 Value: Address of begin or end of IFUNC relocations At startup, the embedded dynamic linker will find the beginning and end range of R_MIPS_IRELATIVE relocations through the special symbols __rel_iplt_start and __rel_iplt_end. The relocation entry contains the address of an associated entry in the .igot.plt table, which initially contains the address of the associated resolver function. The resolver is called and return value of the resolver replaces the .igot.plt table entry. Every call to the ifunc routine goes through the iplt at runtime. *** *2* *** Dynamically linked executables with ifunc definitions are for the most part the same as static executables, with the following exceptions: *) No __rel_iplt_start/end symbols. The dynamic linker handles dynamic relocations. *) Generate STT_GNU_IFUNC symbols in the .dynsym section with the value being the iplt entry for this IFUNC and transform them into STT_FUNC symbols. Any references to this IFUNC have to go through the stub. The dynamic linker (ld.so) will be doing the fixup at start- up. *** *3* *** Links producing dynamic shared objects do not produce iplt stubs, a .igot.plt table, or R_MIPS_IRELATIVE relocations. IFUNC resolution occurs during the processing of global GOT entries. For .dynsym entries of type IFUNC, the load offset for the resolver function stored in the GOT entry. The resolver function is called and the address returned by the resolver function replaces resolver offset value in the GOT entry. *************************************************************** *************************************************************** Entries in the GOT within the DT_MIPS_LOCAL_GOTNO should have their own entries and not share a common local GOT page entry. For global entries in the GOT that start at DT_MIPS_GOTSYM, the entries in the dynamic symbol table are indexed along with the entries in the GOT table. If the symbol is of type STT_GNU_IFUNC, the address of the resolver routine is computed by adding the run-time load address of the shared object and the offset for the resolver function, which stored in the GOT entry. The resolver is then called and the resultant address replaces the GOT entry. IFUNC entries cannot be quickstarted/prelinked since the runtime value is not known until load time. Any dynamic relocation resolution using R_MIPS_IRELATIVE will take the address pointed to by the relocation and assume it is the address of the resolver, run the resolver and replace the resolver address with the value returned by the resolver.