Inter-revision diff: patch 6

Comparing v8 (message) to v5 (message)

--- 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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help