Hi,
I'm working in the Linaro toolchain team on adding ARM support for GNU indirect functions (STT_GNU_IFUNCs). The indirect function feature requires a new relocation type, which is typically called R_FOO_IRELATIVE. I'd therefore like to propose a new R_ARM_IRELATIVE relocation type for the ARM EABI.
This relocation is only used in ET_EXEC and ET_DYN objects. If the object has a PT_DYNAMIC tag, then the relocation may only appear in the DT_REL(A) table; it cannot appear in the DT_JMPREL table. (Note that this is a deliberate divergence from the x86 and x86_64 behaviour, which does allow the IRELATIVE relocation to be used in DT_JMPREL table, but which requires it to be applied at load time, regardless of bind-now vs. lazy semantics. However, the proposed ARM behaviour matches that of other targets like PowerPC.)
Static ET_EXEC objects may have R_ARM_IRELATIVE relocations. In this case, the relocations are stored in a relocation table that contains no other type of relocation (not even R_ARM_NONE). The static linker defines two symbols:
__rel_iplt_start, which the linker points to the start of this table __rel_iplt_end, which the linker points to the last byte of this table plus one.
The two symbols are equal if the executable has no R_ARM_IRELATIVE relocations. It is the executable's responsibility to apply these relocations as appropriate. If the static linker emits a symbol table, then it is not defined whether the linker includes __rel_iplt_start and __rel_iplt_end in that symbol table.
The static linker may (or may not) define __rel_iplt_start and __rel_iplt_end in dynamic objects. However, if it does define them, the symbols must refer to part of the DT_REL(A) table, and it is still the dynamic linker's responsibility to apply the relocations.
An R_ARM_IRELATIVE relocation applies to all bits of a 4-byte field. There are no alignment restrictions on the field. The relocation value is:
call(B(S) + A)
where call(X) represents the value of r0 after performing an indirect branch-with-link-and-exchange (BLX) to address X.
The dynamic linker must have applied all earlier DT_REL(A) relocations before calling X. It is undefined whether later DT_REL(A) relocations have been applied or not, and X must not make any assumptions about the status of those relocations.
If there is an R_ARM_IRELATIVE relocation with symbol S and addend A, then the relocation value:
call(B(S) + A)
is considered to be a load-time constant. It is possible for an object to have more than one R_ARM_IRELATIVE relocation with the same value of B(S) + A, and in such a case, it is not defined whether the dynamic linker invokes the target function each time, or whether it caches the results of earlier calls.
I realise this isn't the cleanest extension in the world. As Alan Modra noted on the binutils list, the choice of __rel_iplt_start and __rel_iplt_end is particularly unfortunate, since the relocations are not specific to "PLTs". However, the GNU extension has been defined this way, so unfortunately there isn't much room for target-specific variation.
Thanks, Richard