A relocation is simply a record that stores
an address that needs to be resolved
information on how to resolve it; i.e.
- a symbol name (actually a pointer to the symbol table, which then gives you the actual symbol)
- the type of relocation; i.e. what to do. (this is defined by the ABI)
ELF defines two types of relocations
typedef struct { Elf32_Addr r_offset; <--- address to fix Elf32_Word r_info; <--- symbol table pointer and relocation type } Elf32_Rel typedef struct { Elf32_Addr r_offset; Elf32_Word r_info; Elf32_Sword r_addend; } Elf32_Rela
Thus RELA relocations have an extra field, the addend.
ianw@baci:~/tmp/fptest$ cat addendtest.c extern int i[4]; int *j = i + 2; ianw@baci:~/tmp/fptest$ cat addendtest2.c int i[4]; ianw@baci:~/tmp/fptest$ gcc -nostdlib -shared -fpic -s -o addendtest2.so addendtest2.c ianw@baci:~/tmp/fptest$ gcc -nostdlib -shared -fpic -o addendtest.so addendtest.c ./addendtest2.so ianw@baci:~/tmp/fptest$ readelf -r ./addendtest.so Relocation section '.rela.dyn' at offset 0x3b8 contains 1 entries: Offset Info Type Sym. Value Sym. Name + Addend 0000000104f8 000f00000027 R_IA64_DIR64LSB 0000000000000000 i + 8
So, sizeof(int) == 4 and j points two ints into i (i.e. i + 8). R_IA64_DIR64LSB is just defined as SYMBOL + ADDEND which means that to fixup this relocation, we need to write to Offset the value of i (which we find and resolve) and add 8 to it.
If you try this on a 386, you will not have the explicit addend as it will use a REL relocation. You will actually have to read the memory at Offset to find the addend (it will be 8).
Having to read the memory before you do the relocation has all sorts of inefficiencies, and the only positive is that it saves space (because with RELA you have that extra field and "blank" spot waiting to be filled in the binary; with REL you keep that addend in the "blank spot"). Most modern architectures have dispensed with REL relocations all together (IA64, PPC64, AMD64).