@@ -64,7 +64,9 @@ static inline unsigned long my_r2(const struct elf_info *elf_info)
}
int elf64_apply_relocate_add(const struct elf_info *elf_info,
- const char *strtab, unsigned int symindex,
- unsigned int relsec, const char *obj_name);
+ const char *strtab, const Elf64_Rela *rela,
+ unsigned int num_rela, void *syms_base,
+ void *loc_base, Elf64_Addr addr_base,
+ const char *obj_name);
#endif /* _ASM_POWERPC_ELF_UTIL_H */
@@ -69,33 +69,56 @@ static void squash_toc_save_inst(const char *name, unsigned long addr) { }
* elf64_apply_relocate_add - apply 64 bit RELA relocations
* @elf_info: Support information for the ELF binary being relocated.
* @strtab: String table for the associated symbol table.
- * @symindex: Section header index for the associated symbol table.
- * @relsec: Section header index for the relocations to apply.
+ * @rela: Contents of the section with the relocations to apply.
+ * @num_rela: Number of relocation entries in the section.
+ * @syms_base: Contents of the associated symbol table.
+ * @loc_base: Contents of the section to which relocations apply.
+ * @addr_base: The address where the section will be loaded in memory.
* @obj_name: The name of the ELF binary, for information messages.
+ *
+ * Applies RELA relocations to an ELF file already at its final location
+ * in memory (in which case loc_base == addr_base), or still in a temporary
+ * buffer.
*/
int elf64_apply_relocate_add(const struct elf_info *elf_info,
- const char *strtab, unsigned int symindex,
- unsigned int relsec, const char *obj_name)
+ const char *strtab, const Elf64_Rela *rela,
+ unsigned int num_rela, void *syms_base,
+ void *loc_base, Elf64_Addr addr_base,
+ const char *obj_name)
{
unsigned int i;
- Elf64_Shdr *sechdrs = elf_info->sechdrs;
- Elf64_Rela *rela = (void *)sechdrs[relsec].sh_addr;
- Elf64_Sym *sym;
unsigned long *location;
+ unsigned long address;
unsigned long value;
+ const char *name;
+ Elf64_Sym *sym;
+
+ for (i = 0; i < num_rela; i++) {
+ /*
+ * rels[i].r_offset contains the byte offset from the beginning
+ * of section to the storage unit affected.
+ *
+ * This is the location to update in the temporary buffer where
+ * the section is currently loaded. The section will finally
+ * be loaded to a different address later, pointed to by
+ * addr_base.
+ */
+ location = loc_base + rela[i].r_offset;
+
+ /* Final address of the location. */
+ address = addr_base + rela[i].r_offset;
+ /* This is the symbol the relocation is referring to. */
+ sym = (Elf64_Sym *) syms_base + ELF64_R_SYM(rela[i].r_info);
- for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
- /* This is where to make the change */
- location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
- + rela[i].r_offset;
- /* This is the symbol it is referring to */
- sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
- + ELF64_R_SYM(rela[i].r_info);
+ if (sym->st_name)
+ name = strtab + sym->st_name;
+ else
+ name = "<unnamed symbol>";
pr_debug("RELOC at %p: %li-type as %s (0x%lx) + %li\n",
location, (long)ELF64_R_TYPE(rela[i].r_info),
- strtab + sym->st_name, (unsigned long)sym->st_value,
+ name, (unsigned long)sym->st_value,
(long)rela[i].r_addend);
/* `Everything is relative'. */
@@ -187,7 +210,7 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
value += local_entry_offset(sym);
/* Convert value to relative */
- value -= (unsigned long)location;
+ value -= address;
if (value + 0x2000000 > 0x3ffffff || (value & 3) != 0){
pr_err("%s: REL24 %li out of range!\n",
obj_name, (long int)value);
@@ -202,7 +225,7 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
case R_PPC64_REL64:
/* 64 bits relative (used by features fixups) */
- *location = value - (unsigned long)location;
+ *location = value - address;
break;
case R_PPC64_TOCSAVE:
@@ -218,7 +241,7 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
* Optimize ELFv2 large code model entry point if
* the TOC is within 2GB range of current location.
*/
- value = my_r2(elf_info) - (unsigned long)location;
+ value = my_r2(elf_info) - address;
if (value + 0x80008000 > 0xffffffff)
break;
/*
@@ -242,7 +265,7 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
case R_PPC64_REL16_HA:
/* Subtract location pointer */
- value -= (unsigned long)location;
+ value -= address;
value = ((value + 0x8000) >> 16);
*((uint16_t *) location)
= (*((uint16_t *) location) & ~0xffff)
@@ -251,7 +274,7 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
case R_PPC64_REL16_LO:
/* Subtract location pointer */
- value -= (unsigned long)location;
+ value -= address;
*((uint16_t *) location)
= (*((uint16_t *) location) & ~0xffff)
| (value & 0xffff);
@@ -438,16 +438,26 @@ int restore_r2(u32 *instruction, const char *obj_name)
return 1;
}
+/*
+ * When this function is called, the module is already at its final location in
+ * memory, so Elf64_Shdr.sh_addr can be used for accessing the section
+ * contents as well as the base address for relocations.
+ */
int apply_relocate_add(Elf64_Shdr *sechdrs,
const char *strtab,
unsigned int symindex,
unsigned int relsec,
struct module *me)
{
+ Elf64_Shdr *rel_section = &sechdrs[relsec];
+ void *syms_base = (void *) sechdrs[symindex].sh_addr;
+ Elf64_Addr addr_base = sechdrs[rel_section->sh_info].sh_addr;
+ const Elf64_Rela *rela = (const Elf64_Rela *) rel_section->sh_addr;
+ unsigned int num_rela = rel_section->sh_size / sizeof(Elf64_Rela);
Elf64_Sym *sym;
pr_debug("Applying ADD relocate section %u to %u\n", relsec,
- sechdrs[relsec].sh_info);
+ rel_section->sh_info);
/* First time we're called, we can fix up .TOC. */
if (!me->arch.toc_fixed) {
@@ -459,8 +469,9 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
me->arch.toc_fixed = true;
}
- return elf64_apply_relocate_add(&me->arch.elf_info, strtab, symindex,
- relsec, me->name);
+ return elf64_apply_relocate_add(&me->arch.elf_info, strtab, rela,
+ num_rela, syms_base, (void *) addr_base,
+ addr_base, me->name);
}
#ifdef CONFIG_DYNAMIC_FTRACE