--- v8
+++ v5
@@ -1,203 +1,197 @@
-When apply_relocate_add is called, modules are already loaded at their
-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.
+Extend elf64_apply_relocate_add to support relative symbols. This is
+necessary because there is a difference between how the module loading
+mechanism and the kexec purgatory loading code use Elf64_Sym.st_value
+at relocation time: the former changes st_value to point to the absolute
+memory address before relocating the module, while the latter does that
+adjustment during relocation of the purgatory.
-This is not the case for kexec's purgatory, because it will only be
-copied to its final location right before being executed. Therefore,
-it needs to be relocated while it is still in a temporary buffer. In
-this case, Elf64_Shdr.sh_addr can't be used to access the sections'
-contents.
+Also, add a check_symbols argument so that the kexec code can be stricter
+about undefined symbols.
-This patch allows elf64_apply_relocate_add to be used when the ELF
-binary is not yet at its final location by adding an addr_base argument
-to specify the address at which the section will be loaded, and rela,
-loc_base and syms_base to point to the sections' contents.
+Finally, add relocation types used by the purgatory.
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
---
- arch/powerpc/include/asm/elf_util.h | 6 ++--
- arch/powerpc/kernel/elf_util_64.c | 63 +++++++++++++++++++++++++------------
- arch/powerpc/kernel/module_64.c | 17 ++++++++--
- 3 files changed, 61 insertions(+), 25 deletions(-)
+ arch/powerpc/include/asm/elf_util.h | 1 +
+ arch/powerpc/kernel/elf_util_64.c | 84 ++++++++++++++++++++++++++++++++++++-
+ arch/powerpc/kernel/module_64.c | 5 ++-
+ 3 files changed, 88 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/include/asm/elf_util.h b/arch/powerpc/include/asm/elf_util.h
-index 37372559fe62..a012ba03282d 100644
+index a012ba03282d..3405eeabe542 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);
+@@ -67,6 +67,7 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
+ const char *strtab, const Elf64_Rela *rela,
+ unsigned int num_rela, void *syms_base,
+ void *loc_base, Elf64_Addr addr_base,
++ bool relative_symbols, bool check_symbols,
+ 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
+index 8e5d400ac9f2..80f209a42abd 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.
+@@ -74,6 +74,8 @@ static void squash_toc_save_inst(const char *name, unsigned long addr) { }
+ * @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.
++ * @relative_symbols: Are the symbols' st_value members relative?
++ * @check_symbols: Fail if an unexpected symbol is found?
* @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)
+ *
+ * Applies RELA relocations to an ELF file already at its final location
+@@ -84,11 +86,13 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
+ const char *strtab, const Elf64_Rela *rela,
+ unsigned int num_rela, void *syms_base,
+ void *loc_base, Elf64_Addr addr_base,
++ bool relative_symbols, bool check_symbols,
+ 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 address;
++ unsigned long sec_base;
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,
+ const char *name;
+ Elf64_Sym *sym;
+@@ -121,8 +125,36 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
+ name, (unsigned long)sym->st_value,
(long)rela[i].r_addend);
++ if (check_symbols) {
++ /*
++ * TOC symbols appear as undefined but should be
++ * resolved as well, so allow them to be processed.
++ */
++ if (sym->st_shndx == SHN_UNDEF &&
++ strcmp(name, ".TOC.") != 0) {
++ pr_err("Undefined symbol: %s\n", name);
++ return -ENOEXEC;
++ } else if (sym->st_shndx == SHN_COMMON) {
++ pr_err("Symbol '%s' in common section.\n", name);
++ return -ENOEXEC;
++ }
++ }
++
++ if (relative_symbols && sym->st_shndx != SHN_ABS) {
++ if (sym->st_shndx >= elf_info->ehdr->e_shnum) {
++ pr_err("Invalid section %d for symbol %s\n",
++ sym->st_shndx, name);
++ return -ENOEXEC;
++ } else {
++ struct elf_shdr *sechdrs = elf_info->sechdrs;
++
++ sec_base = sechdrs[sym->st_shndx].sh_addr;
++ }
++ } else
++ sec_base = 0;
++
/* `Everything is relative'. */
-@@ -187,7 +210,7 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
- value += local_entry_offset(sym);
+- value = sym->st_value + rela[i].r_addend;
++ value = sym->st_value + sec_base + rela[i].r_addend;
- /* 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;
+ switch (ELF64_R_TYPE(rela[i].r_info)) {
+ case R_PPC64_ADDR32:
+@@ -135,6 +167,10 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
+ *(unsigned long *)location = value;
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_REL32:
++ *(uint32_t *)location = value - (uint32_t)(uint64_t)location;
++ break;
++
+ case R_PPC64_TOC:
+ *(unsigned long *)location = my_r2(elf_info);
+ break;
+@@ -186,6 +222,14 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
+ | (value & 0xfffc);
+ break;
++ case R_PPC64_TOC16_HI:
++ /* Subtract TOC pointer */
++ value -= my_r2(elf_info);
++ value = value >> 16;
++ *((uint16_t *) location)
++ = (*((uint16_t *) location) & ~0xffff)
++ | (value & 0xffff);
++
+ case R_PPC64_TOC16_HA:
+ /* Subtract TOC pointer */
+ value -= my_r2(elf_info);
+@@ -195,6 +239,21 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
+ | (value & 0xffff);
+ break;
+
++ case R_PPC64_REL14:
++ /* Convert value to relative */
++ value -= address;
++ if (value + 0x8000 > 0xffff || (value & 3) != 0) {
++ pr_err("%s: REL14 %li out of range!\n", obj_name,
++ (long int)value);
++ return -ENOEXEC;
++ }
++
++ /* Only replace bits 2 through 16 */
++ *(uint32_t *)location
++ = (*(uint32_t *)location & ~0xfffc)
++ | (value & 0xfffc);
++ break;
++
+ case R_PPC_REL24:
+ /* FIXME: Handle weak symbols here --RR */
+ if (sym->st_shndx == SHN_UNDEF) {
+@@ -263,6 +322,29 @@ int elf64_apply_relocate_add(const struct elf_info *elf_info,
+ ((uint32_t *)location)[1] = 0x38420000 + PPC_LO(value);
+ break;
+
++ case R_PPC64_ADDR16_LO:
++ *(uint16_t *)location = value & 0xffff;
++ break;
++
++ case R_PPC64_ADDR16_HI:
++ *(uint16_t *)location = (value >> 16) & 0xffff;
++ break;
++
++ case R_PPC64_ADDR16_HA:
++ *(uint16_t *)location = (((value + 0x8000) >> 16) &
++ 0xffff);
++ break;
++
++ case R_PPC64_ADDR16_HIGHER:
++ *(uint16_t *)location = (((uint64_t)value >> 32) &
++ 0xffff);
++ break;
++
++ case R_PPC64_ADDR16_HIGHEST:
++ *(uint16_t *)location = (((uint64_t)value >> 48) &
++ 0xffff);
++ break;
++
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);
+ value -= address;
diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
-index b929560ecd5b..590e2413974f 100644
+index 590e2413974f..10745ac6b004 100644
--- a/arch/powerpc/kernel/module_64.c
+++ b/arch/powerpc/kernel/module_64.c
-@@ -439,16 +439,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.
-+ */
+@@ -443,6 +443,9 @@ int restore_r2(u32 *instruction, const char *obj_name)
+ * 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.
++ *
++ * Also, simplify_symbols already changed all symbols' st_value members
++ * to absolute addresses.
+ */
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;
+@@ -472,7 +475,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
- 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) {
-@@ -460,8 +470,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);
+ return elf64_apply_relocate_add(&me->arch.elf_info, strtab, rela,
+ num_rela, syms_base, (void *) addr_base,
+- addr_base, me->name);
++ addr_base, false, false, me->name);
}
#ifdef CONFIG_DYNAMIC_FTRACE