rel v rela relocations

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.c

ianw@baci:~/tmp/fptest$ gcc -nostdlib -shared -fpic -o addendtest.c ./

ianw@baci:~/tmp/fptest$ readelf -r ./

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).