==== MIPS IFUNC ABI specification ==== Introduction ---- This document describes the working of GNU indirect functions(IFUNC) for MIPS. Terminology ---- Shared file or shared object refers to any object file with e_type ET_DYN. Unless otherwise distinguished, this includes dynamic shared libraries (PIC) and position-independent executables (PIE). Executable file or executable refers to any object file with e_type ET_EXEC. Unless otherwise distinguished, this includes statically linked and dynamically linked executables. Relocations for IFUNC resolution ---- The relocation table may now contain a new relocation type generated by the static linker: R_MIPS_IRELATIVE(128) This relocation marks the location of an IFUNC indirection that needs to be resolved by the dynamic linker at load time. The relocation step calls the IFUNC resolver function given at the relocation offset possibly with additional processor-specific (hardware capability) arguments and writes the address returned by the resolver back to the relocation offset. For shared objects, the relocation step may add a base address to the relocation offset to calculate the run-time address of the resolver function. The operation can be described as: [B + O] <= ((ifunc_proto) B + O) () where O = relocation offset B = relative relocation bias; =0 for executable files ifunc_proto = void *(*)(void) IFUNCs in executable files ---- Code/data references to IFUNCs in executable files are resolved by means of indirection stubs and an indirection table. IGOT sections - the indirection table ---- Symbols defined in executable files have no GOT entries, so the indirection is acheived by creating an entry in the new IGOT section. Each entry in IGOT has a corresponding IRELATIVE relocation. This IGOT entry is initialized to point to the resolver function at link time and modified to point to the resolved function by the IRELATIVE fix-up. Executable files are not relocatable, so the relocation bias is always zero. IPLT section - indirection stubs ---- The IFUNC Procedure Linkage Table (IPLT) consists of a set of stubs generated by the static linker to stand in for IFUNC references in executable file that can not be resolved via a GOT entry. For executable files, both IFUNC calls and references are always routed through IPLT stubs. Every IFUNC definition must have a corresponding stub irrespective of its declared visibility. Call to an IFUNC is performed by an absolute JAL instruction to the IPLT stub. The stub redirects the call to the actual function by reading a pointer from the IGOT section. Data references use the absolute address of the IPLT stub in place of the IFUNC. IFUNCs in shared objects ---- In the case of shared objects, data references to IFUNCs use explicit dynamic relocations, where as calls or code-references are routed through the GOT which itself is relocated implicitly by the dynamic linker. Morever, global symbols can be pre-empted by the dynamic linker at load time. There are 4 scenarios to consider: Data relocations for local IFUNC symbols ---- The static linker replaces each R_MIPS_REL32 dynamic relocation for a local IFUNC reference with an IRELATIVE relocation. The IRELATIVE resolution step above includes an implicit relative relocation. Shared objects have a non-zero bias element provided by the dynamic linker. Code relocations for local IFUNC symbols ---- Non-preemptible IFUNC symbols have entries in the local GOT region. Call or reference in code is resolved by reading from the corresponding GOT entry. The local GOT can be considered as a series of implicit REL32 relocations. For IFUNC resolution, we must replace each implicit REL32 relocation with an explicit IRELATIVE relocation, just as we did above for explicit REL32 relocations in the case of data relocations. Thus, each IFUNC entry in the local IGOT gets a corresponding IRELATIVE relocation. To prevent these entries from being relocated twice (once implicitly as part of GOT relocation and again by the IRELATIVE relocation), a part of the GOT is marked as containing only explictly relocated entries. This part is skipped over during normal GOT relocation and only modified by explicit relocations. This part is marked off from the implicitly relocated local GOT region by the dynamic tag MIPS_GENERAL_GOTNO. Data relocations for global IFUNC symbols ---- For a global preemptible symbol, the dynamic linker must determine at load-time, whether the IFUNC is preempted by an external symbol. R_MIPS_REL32 relocations against such symbols cannot simply be replaced with IRELATIVE ones. The REL32 relocation step for such references must be modified to include an implicit IRELATIVE relocation, to be performed only if the resolved symbol is also an IFUNC. The relocations produced remain exactly the same in this case, but the relocation process employed by the dynamic linker changes to account for symbol preemption. Code relocations for global IFUNC symbols ---- Preemptible global IFUNC symbols have entries in the global GOT region. A call or reference in code is resolved by reading from the corresponding GOT entry. The global GOT can be considered as a series of implicit REL32 relocations. The order of symbols in the global GOT region is defined by the ABI as a one-to-one correspondence with entries in the dynamic symbol table. These entries cannot be repositioned or partitioned without significatly hampering the ABI. Moreover, the global GOT is relocated implicitly before the local GOT and all explicit relocations. If an IFUNC resolver function depends on any of the latter stages, it cannot be invoked safely as part of the implicitly global GOT relocation. The static linker generates an explicit R_MIPS_REL32 relocation against the GOT entry of each global IFUNC as a means of delaying the IFUNC resolution step. These R_MIPS_REL32 relocations for code behave similar to explicit data relocations described above, encapsulating both symbol preemption and an optional IRELATIVE relocation. The only additional precaution is to skip these GOT entries from the implicit GOT relocation step to prevent them from being relocated twice. This can be acheived trivially, without having to partition the global GOT with dynamic tags, because symbol information indication type as STT_GNU_IFUNC is readily available for such symbols from the dynamic symbol table. Dynamic Section ---- Dynamic section entries give information to the dynamic linker. The following new dynamic table entries are required for IFUNC resolution: MIPS_GENERAL_GOTNO(36) Number of explicitly relocated GOT entries. Alternatively, start of the normally relocated local GOT region. This entry is optional. If not present, its value is assumed by the linker to be 1(non-GNU) or 2(GNU). Revised GOT layout ---- GOT0: Reserved for dynamic linker GOT1: Reserved, GNU-specific, optional GOT2 ... MIPS_GENERAL_GOTNO - 1: Explicitly relocated GOT entries MIPS_GENERAL_GOTNO ... MIPS_LOCAL_GOTNO - 1: Implicitly relocated local GOT entries MIPS_LOCAL_GOTNO ... MIPS_SYMTABNO - 1: Global GOT entries, of which only those not corresponding to IFUNC symbols are implictly relocated.