Processing ELF relocations – understanding the relocs, symbols, section data, and how they work together

I stumbled across this question and thought it deserved an answer.

Have elf.h handy. You can find it on the internet.

Each RELA section contains an array of Elf32_Rela entries as you know, but is also tied to a certain other section. r_offset is an offset into that other section (in this case – it works differently for shared libraries). You will find that section headers have a member called sh_info. This tells you which section that is. (It’s an index into the section header table as you would expect.)

The ‘symbol’, which you got from r_info is in fact an index into a symbol table residing in another section. Look for the member sh_link in your RELA section’s header.

The symbol table tells you the name of the symbol you are looking for, in the form of the st_name member of Elf32_Sym. st_name is an offset into a string section. Which section that is, you get from the sh_link member of your symbol table’s section header. Sorry if this gets confusing.

Elf32_Shdr *sh_table = elf_image + ((Elf32_Ehdr *)elf_image)->e_shoff;
Elf32_Rela *relocs = elf_image + sh_table[relocation_section_index]->sh_offset;

unsigned section_to_modify_index = sh_table[relocation_section_index].sh_info;
char *to_modify = elf_image + sh_table[section_to_modify_index].sh_offset;

unsigned symbol_table_index = sh_table[relocation_section_index].sh_link;
Elf32_Sym *symbol_table = elf_image + sh_table[symbol_table_index].sh_offset;

unsigned string_table_index = sh_table[symbol_table].sh_link;
char *string_table = elf_image + sh_table[string_table_index].sh_offset;

Let’s say we are working with relocation number i.

Elf32_Rela *rel = &relocs[i];
Elf32_Sym *sym = &symbol_table[ELF32_R_SYM(rel->r_info)];
char *symbol_name = string_table + sym->st_name;

Find the address of that symbol (let’s say that symbol_name == “printf”). The final value will go in (to_modify + rel->r_offset).

As for the table on pages 79-83 of the pdf you linked, it tells us what to put at that address, and how many bytes to write. Obviously the address we just got (of printf in this case) is part of most of them. It corresponds to the S in the expressions.

r_addend is just A. Sometimes the compiler needs to add a static constant to a reloc i guess.

B is the base address of the shared object, or 0 for executable programs because they are not moved.

So if ELF32_R_TYPE(rel->r_info) == R_PPC_ADDR32 we have S + A, and the word size is word32 so we would get:

*(uint32_t *)(to_modify + rel->r_offset) = address_of_printf + rel->r_addend;

…and we have successfully performed a relocation.

I can’t help you when it comes to the #lo, #hi, etc and the word sizes like low14. I know nothing about PPC but the linked pdf seems reasonable enough.

I also don’t know about the stub functions. You don’t normally need to know about those when linking (dynamically at least).

I’m not sure if I’ve answered all of your questions but you should be able to see what your example code does now at least.

Leave a Comment