diff --git a/arch/powerpc/include/asm/elf_util.h b/arch/powerpc/include/asm/elf_util.h
index 37372559fe62..a012ba03282d 100644
--- a/arch/powerpc/include/asm/elf_util.h
+++ b/arch/powerpc/include/asm/elf_util.h
@@ -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 */
diff --git a/arch/powerpc/kernel/elf_util_64.c b/arch/powerpc/kernel/elf_util_64.c
index decad2c34f38..8e5d400ac9f2 100644
--- a/arch/powerpc/kernel/elf_util_64.c
+++ b/arch/powerpc/kernel/elf_util_64.c
@@ -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);
diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
index f2b25b4f9066..7f3b17bcac05 100644
--- a/arch/powerpc/kernel/module_64.c
+++ b/arch/powerpc/kernel/module_64.c
@@ -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
