On Thu, Aug 11, 2016 at 08:08:09PM -0300, Thiago Jung Bauermann wrote: > The kexec_file_load system call needs to relocate the purgatory, so > factor out the module relocation code so that it can be shared. > > This patch's purpose is to move the ELF relocation logic from > apply_relocate_add to elf_util_64.c with as few changes as > possible. The following changes were needed: > > To avoid having module-specific code in a general purpose utility > function, struct elf_info was created to contain the information > needed for ELF binaries manipulation. > > my_r2, stub_for_addr and create_stub were changed to use it instead of > having to receive a struct module, since they are called from > elf64_apply_relocate_add. > > local_entry_offset and squash_toc_save_inst were only used by > apply_rellocate_add, so they were moved to elf_util_64.c as well. > > Signed-off-by: Thiago Jung Bauermann <bauerman at linux.vnet.ibm.com> > --- > arch/powerpc/include/asm/elf_util.h | 70 ++++++++ > arch/powerpc/include/asm/module.h | 14 +- > arch/powerpc/kernel/Makefile | 4 + > arch/powerpc/kernel/elf_util_64.c | 269 +++++++++++++++++++++++++++++++ > arch/powerpc/kernel/module_64.c | 312 ++++-------------------------------- > 5 files changed, 386 insertions(+), 283 deletions(-) > > diff --git a/arch/powerpc/include/asm/elf_util.h b/arch/powerpc/include/asm/elf_util.h > new file mode 100644 > index 000000000000..37372559fe62 > --- /dev/null > +++ b/arch/powerpc/include/asm/elf_util.h > @@ -0,0 +1,70 @@ > +/* > + * Utility functions to work with ELF files. > + * > + * Copyright (C) 2016, IBM Corporation > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2, or (at your option) > + * any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +#ifndef _ASM_POWERPC_ELF_UTIL_H > +#define _ASM_POWERPC_ELF_UTIL_H > + > +#include <linux/elf.h> > + > +struct elf_info { > + struct elf_shdr *sechdrs; > + > + /* Index of stubs section. */ > + unsigned int stubs_section; > + /* Index of TOC section. */ > + unsigned int toc_section; > +}; > + > +#ifdef __powerpc64__ > +#ifdef PPC64_ELF_ABI_v2 > + > +/* An address is simply the address of the function. */ > +typedef unsigned long func_desc_t; > +#else > + > +/* An address is address of the OPD entry, which contains address of fn. */ > +typedef struct ppc64_opd_entry func_desc_t; > +#endif /* PPC64_ELF_ABI_v2 */ > + > +/* Like PPC32, we need little trampolines to do > 24-bit jumps (into > + the kernel itself). But on PPC64, these need to be used for every > + jump, actually, to reset r2 (TOC+0x8000). */ > +struct ppc64_stub_entry > +{ > + /* 28 byte jump instruction sequence (7 instructions). We only > + * need 6 instructions on ABIv2 but we always allocate 7 so > + * so we don't have to modify the trampoline load instruction. */ > + u32 jump[7]; > + /* Used by ftrace to identify stubs */ > + u32 magic; > + /* Data for the above code */ > + func_desc_t funcdata; > +}; > +#endif > + > +/* r2 is the TOC pointer: it actually points 0x8000 into the TOC (this > + gives the value maximum span in an instruction which uses a signed > + offset) */ > +static inline unsigned long my_r2(const struct elf_info *elf_info) > +{ > + return elf_info->sechdrs[elf_info->toc_section].sh_addr + 0x8000; > +} > + > +int elf64_apply_relocate_add(const struct elf_info *elf_info, > + const char *strtab, unsigned int symindex, > + unsigned int relsec, const char *obj_name); > + > +#endif /* _ASM_POWERPC_ELF_UTIL_H */ > diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h > index cd4ffd86765f..f2073115d518 100644 > --- a/arch/powerpc/include/asm/module.h > +++ b/arch/powerpc/include/asm/module.h > @@ -12,7 +12,14 @@ > #include <linux/list.h> > #include <asm/bug.h> > #include <asm-generic/module.h> > +#include <asm/elf_util.h> > > +/* Both low and high 16 bits are added as SIGNED additions, so if low > + 16 bits has high bit set, high 16 bits must be adjusted. These > + macros do that (stolen from binutils). */ > +#define PPC_LO(v) ((v) & 0xffff) > +#define PPC_HI(v) (((v) >> 16) & 0xffff) > +#define PPC_HA(v) PPC_HI ((v) + 0x8000) > > #ifndef __powerpc64__ > /* > @@ -33,8 +40,7 @@ struct ppc_plt_entry { > > struct mod_arch_specific { > #ifdef __powerpc64__ > - unsigned int stubs_section; /* Index of stubs section in module */ > - unsigned int toc_section; /* What section is the TOC? */ > + struct elf_info elf_info; > bool toc_fixed; /* Have we fixed up .TOC.? */ > #ifdef CONFIG_DYNAMIC_FTRACE > unsigned long toc; > @@ -90,6 +96,10 @@ static inline int module_finalize_ftrace(struct module *mod, const Elf_Shdr *sec > } > #endif > > +unsigned long stub_for_addr(const struct elf_info *elf_info, unsigned long addr, > + const char *obj_name); > +int restore_r2(u32 *instruction, const char *obj_name); > + > struct exception_table_entry; > void sort_ex_table(struct exception_table_entry *start, > struct exception_table_entry *finish); > diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile > index b2027a5cf508..e38aace0a6e7 100644 > --- a/arch/powerpc/kernel/Makefile > +++ b/arch/powerpc/kernel/Makefile > @@ -123,6 +123,10 @@ ifneq ($(CONFIG_PPC_INDIRECT_PIO),y) > obj-y += iomap.o > endif > > +ifeq ($(CONFIG_MODULES)$(CONFIG_WORD_SIZE),y64) > +obj-y += elf_util_64.o > +endif > + > obj64-$(CONFIG_PPC_TRANSACTIONAL_MEM) += tm.o > > obj-$(CONFIG_PPC64) += $(obj64-y) > diff --git a/arch/powerpc/kernel/elf_util_64.c b/arch/powerpc/kernel/elf_util_64.c > new file mode 100644 > index 000000000000..decad2c34f38 > --- /dev/null > +++ b/arch/powerpc/kernel/elf_util_64.c > @@ -0,0 +1,269 @@ > +/* > + * Utility functions to work with ELF files. > + * > + * Copyright (C) 2016, IBM Corporation > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2, or (at your option) > + * any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +#include <asm/ppc-opcode.h> > +#include <asm/elf_util.h> > + > +/* > + * We just need to use the functions defined in <asm/module.h>, so just declare > + * struct module here and avoid having to import <linux/module.h>. > + */ > +struct module; > +#include <asm/module.h> > + > +#ifdef PPC64_ELF_ABI_v2 > +/* PowerPC64 specific values for the Elf64_Sym st_other field. */ > +#define STO_PPC64_LOCAL_BIT 5 > +#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT) > +#define PPC64_LOCAL_ENTRY_OFFSET(other) \ > + (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2) > + > +static unsigned int local_entry_offset(const Elf64_Sym *sym) > +{ > + /* sym->st_other indicates offset to local entry point > + * (otherwise it will assume r12 is the address of the start > + * of function and try to derive r2 from it). */ > + return PPC64_LOCAL_ENTRY_OFFSET(sym->st_other); > +} > +#else > +static unsigned int local_entry_offset(const Elf64_Sym *sym) > +{ > + return 0; > +} > +#endif > + > +#ifdef CC_USING_MPROFILE_KERNEL > +/* > + * In case of _mcount calls, do not save the current callee's TOC (in r2) into > + * the original caller's stack frame. If we did we would clobber the saved TOC > + * value of the original caller. > + */ > +static void squash_toc_save_inst(const char *name, unsigned long addr) > +{ > + struct ppc64_stub_entry *stub = (struct ppc64_stub_entry *)addr; > + > + /* Only for calls to _mcount */ > + if (strcmp("_mcount", name) != 0) > + return; > + > + stub->jump[2] = PPC_INST_NOP; > +} > +#else > +static void squash_toc_save_inst(const char *name, unsigned long addr) { } > +#endif > + > +/** > + * 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. > + * @obj_name: The name of the ELF binary, for information messages. > + */ > +int elf64_apply_relocate_add(const struct elf_info *elf_info, > + const char *strtab, unsigned int symindex, > + unsigned int relsec, 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 value; > + For the relocatable kernel we expect only R_PPC64_RELATIVE R_PPC64_NONE R_PPC64_ADDR64 In the future we can use this to check/assert the usage of this for the core kernel (vmlinux) when loaded. Did we check elf64_apply_relocate_add with zImage and vmlinux? Balbir Singh