Commit 77e69ee7 authored by Nicholas Piggin's avatar Nicholas Piggin Committed by Michael Ellerman

powerpc/64: modules support building with PCREL addresing

Build modules using PCREL addressing when CONFIG_PPC_KERNEL_PCREL=y.

- The module loader must handle several new relocation types:

  * R_PPC64_REL24_NOTOC is a function call handled like R_PPC_REL24, but
    does not restore r2 upon return. The external function call stub is
    changed to use pcrel addressing to load the function pointer rather
    than based on the module TOC.

  * R_PPC64_GOT_PCREL34 is a reference to external data. A GOT table
    must be built by hand, because the linker adds this during the final
    link (which is not done for kernel modules). The GOT table is built
    similarly to the way the external function call stub table is. This
    section is called .mygot because .got has a special meaning for the
    linker and can become upset.

  * R_PPC64_PCREL34 is used for local data addressing, but there is a
    special case where the percpu section is moved at load-time to the
    percpu area which is out of range of this relocation. This requires
    the PCREL34 relocations are converted to use GOT_PCREL34 addressing.
Signed-off-by: default avatarNicholas Piggin <npiggin@gmail.com>
[mpe: Some coding style & formatting fixups]
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/20230408021752.862660-7-npiggin@gmail.com
parent 7e3a68be
...@@ -107,9 +107,7 @@ LDFLAGS_vmlinux-$(CONFIG_RELOCATABLE) += -z notext ...@@ -107,9 +107,7 @@ LDFLAGS_vmlinux-$(CONFIG_RELOCATABLE) += -z notext
LDFLAGS_vmlinux := $(LDFLAGS_vmlinux-y) LDFLAGS_vmlinux := $(LDFLAGS_vmlinux-y)
ifdef CONFIG_PPC64 ifdef CONFIG_PPC64
ifdef CONFIG_PPC_KERNEL_PCREL ifndef CONFIG_PPC_KERNEL_PCREL
KBUILD_CFLAGS_MODULE += $(call cc-option,-mno-pcrel)
endif
ifeq ($(call cc-option-yn,-mcmodel=medium),y) ifeq ($(call cc-option-yn,-mcmodel=medium),y)
# -mcmodel=medium breaks modules because it uses 32bit offsets from # -mcmodel=medium breaks modules because it uses 32bit offsets from
# the TOC pointer to create pointers where possible. Pointers into the # the TOC pointer to create pointers where possible. Pointers into the
...@@ -124,6 +122,7 @@ else ...@@ -124,6 +122,7 @@ else
export NO_MINIMAL_TOC := -mno-minimal-toc export NO_MINIMAL_TOC := -mno-minimal-toc
endif endif
endif endif
endif
CFLAGS-$(CONFIG_PPC64) := $(call cc-option,-mtraceback=no) CFLAGS-$(CONFIG_PPC64) := $(call cc-option,-mtraceback=no)
ifdef CONFIG_PPC64_ELF_ABI_V2 ifdef CONFIG_PPC64_ELF_ABI_V2
......
...@@ -27,8 +27,13 @@ struct ppc_plt_entry { ...@@ -27,8 +27,13 @@ struct ppc_plt_entry {
struct mod_arch_specific { struct mod_arch_specific {
#ifdef __powerpc64__ #ifdef __powerpc64__
unsigned int stubs_section; /* Index of stubs section in module */ unsigned int stubs_section; /* Index of stubs section in module */
#ifdef CONFIG_PPC_KERNEL_PCREL
unsigned int got_section; /* What section is the GOT? */
unsigned int pcpu_section; /* .data..percpu section */
#else
unsigned int toc_section; /* What section is the TOC? */ unsigned int toc_section; /* What section is the TOC? */
bool toc_fixed; /* Have we fixed up .TOC.? */ bool toc_fixed; /* Have we fixed up .TOC.? */
#endif
/* For module function descriptor dereference */ /* For module function descriptor dereference */
unsigned long start_opd; unsigned long start_opd;
...@@ -52,12 +57,15 @@ struct mod_arch_specific { ...@@ -52,12 +57,15 @@ struct mod_arch_specific {
/* /*
* Select ELF headers. * Select ELF headers.
* Make empty section for module_frob_arch_sections to expand. * Make empty sections for module_frob_arch_sections to expand.
*/ */
#ifdef __powerpc64__ #ifdef __powerpc64__
# ifdef MODULE # ifdef MODULE
asm(".section .stubs,\"ax\",@nobits; .align 3; .previous"); asm(".section .stubs,\"ax\",@nobits; .align 3; .previous");
# ifdef CONFIG_PPC_KERNEL_PCREL
asm(".section .mygot,\"a\",@nobits; .align 3; .previous");
# endif
# endif # endif
#else #else
# ifdef MODULE # ifdef MODULE
......
...@@ -183,7 +183,7 @@ ...@@ -183,7 +183,7 @@
/* /*
* Used to name C functions called from asm * Used to name C functions called from asm
*/ */
#if defined(CONFIG_PPC_KERNEL_PCREL) && !defined(MODULE) #ifdef CONFIG_PPC_KERNEL_PCREL
#define CFUNC(name) name@notoc #define CFUNC(name) name@notoc
#else #else
#define CFUNC(name) name #define CFUNC(name) name
...@@ -216,7 +216,7 @@ ...@@ -216,7 +216,7 @@
.globl name; \ .globl name; \
name: name:
#if defined(CONFIG_PPC_KERNEL_PCREL) && !defined(MODULE) #ifdef CONFIG_PPC_KERNEL_PCREL
#define _GLOBAL_TOC _GLOBAL #define _GLOBAL_TOC _GLOBAL
#else #else
#define _GLOBAL_TOC(name) \ #define _GLOBAL_TOC(name) \
...@@ -379,7 +379,7 @@ GLUE(.,name): ...@@ -379,7 +379,7 @@ GLUE(.,name):
ori reg, reg, (expr)@l; \ ori reg, reg, (expr)@l; \
rldimi reg, tmp, 32, 0 rldimi reg, tmp, 32, 0
#if defined(CONFIG_PPC_KERNEL_PCREL) && !defined(MODULE) #ifdef CONFIG_PPC_KERNEL_PCREL
#define LOAD_REG_ADDR(reg,name) \ #define LOAD_REG_ADDR(reg,name) \
pla reg,name@pcrel pla reg,name@pcrel
......
...@@ -279,8 +279,12 @@ typedef elf_fpreg_t elf_vsrreghalf_t32[ELF_NVSRHALFREG]; ...@@ -279,8 +279,12 @@ typedef elf_fpreg_t elf_vsrreghalf_t32[ELF_NVSRHALFREG];
#define R_PPC64_TLSLD 108 #define R_PPC64_TLSLD 108
#define R_PPC64_TOCSAVE 109 #define R_PPC64_TOCSAVE 109
#define R_PPC64_REL24_NOTOC 116
#define R_PPC64_ENTRY 118 #define R_PPC64_ENTRY 118
#define R_PPC64_PCREL34 132
#define R_PPC64_GOT_PCREL34 133
#define R_PPC64_REL16 249 #define R_PPC64_REL16 249
#define R_PPC64_REL16_LO 250 #define R_PPC64_REL16_LO 250
#define R_PPC64_REL16_HI 251 #define R_PPC64_REL16_HI 251
......
...@@ -114,20 +114,32 @@ struct ppc64_stub_entry { ...@@ -114,20 +114,32 @@ struct ppc64_stub_entry {
func_desc_t funcdata; func_desc_t funcdata;
} __aligned(8); } __aligned(8);
struct ppc64_got_entry {
u64 addr;
};
/* /*
* PPC64 uses 24 bit jumps, but we need to jump into other modules or * PPC64 uses 24 bit jumps, but we need to jump into other modules or
* the kernel which may be further. So we jump to a stub. * the kernel which may be further. So we jump to a stub.
* *
* For ELFv1 we need to use this to set up the new r2 value (aka TOC * Target address and TOC are loaded from function descriptor in the
* pointer). For ELFv2 it's the callee's responsibility to set up the * ppc64_stub_entry.
* new r2, but for both we need to save the old r2. *
* r12 is used to generate the target address, which is required for the
* ELFv2 global entry point calling convention.
* *
* We could simply patch the new r2 value and function pointer into * TOC handling:
* the stub, but it's significantly shorter to put these values at the * - PCREL does not have a TOC.
* end of the stub code, and patch the stub address (32-bits relative * - ELFv2 non-PCREL just has to save r2, the callee is responsible for
* to the TOC ptr, r2) into the stub. * setting its own TOC pointer at the global entry address.
* - ELFv1 must load the new TOC pointer from the function descriptor.
*/ */
static u32 ppc64_stub_insns[] = { static u32 ppc64_stub_insns[] = {
#ifdef CONFIG_PPC_KERNEL_PCREL
/* pld r12,addr */
PPC_PREFIX_8LS | __PPC_PRFX_R(1),
PPC_INST_PLD | ___PPC_RT(_R12),
#else
PPC_RAW_ADDIS(_R11, _R2, 0), PPC_RAW_ADDIS(_R11, _R2, 0),
PPC_RAW_ADDI(_R11, _R11, 0), PPC_RAW_ADDI(_R11, _R11, 0),
/* Save current r2 value in magic place on the stack. */ /* Save current r2 value in magic place on the stack. */
...@@ -136,14 +148,18 @@ static u32 ppc64_stub_insns[] = { ...@@ -136,14 +148,18 @@ static u32 ppc64_stub_insns[] = {
#ifdef CONFIG_PPC64_ELF_ABI_V1 #ifdef CONFIG_PPC64_ELF_ABI_V1
/* Set up new r2 from function descriptor */ /* Set up new r2 from function descriptor */
PPC_RAW_LD(_R2, _R11, 40), PPC_RAW_LD(_R2, _R11, 40),
#endif
#endif #endif
PPC_RAW_MTCTR(_R12), PPC_RAW_MTCTR(_R12),
PPC_RAW_BCTR(), PPC_RAW_BCTR(),
}; };
/* Count how many different 24-bit relocations (different symbol, /*
different addend) */ * Count how many different r_type relocations (different symbol,
static unsigned int count_relocs(const Elf64_Rela *rela, unsigned int num) * different addend).
*/
static unsigned int count_relocs(const Elf64_Rela *rela, unsigned int num,
unsigned long r_type)
{ {
unsigned int i, r_info, r_addend, _count_relocs; unsigned int i, r_info, r_addend, _count_relocs;
...@@ -152,8 +168,8 @@ static unsigned int count_relocs(const Elf64_Rela *rela, unsigned int num) ...@@ -152,8 +168,8 @@ static unsigned int count_relocs(const Elf64_Rela *rela, unsigned int num)
r_info = 0; r_info = 0;
r_addend = 0; r_addend = 0;
for (i = 0; i < num; i++) for (i = 0; i < num; i++)
/* Only count 24-bit relocs, others don't need stubs */ /* Only count r_type relocs, others don't need stubs */
if (ELF64_R_TYPE(rela[i].r_info) == R_PPC_REL24 && if (ELF64_R_TYPE(rela[i].r_info) == r_type &&
(r_info != ELF64_R_SYM(rela[i].r_info) || (r_info != ELF64_R_SYM(rela[i].r_info) ||
r_addend != rela[i].r_addend)) { r_addend != rela[i].r_addend)) {
_count_relocs++; _count_relocs++;
...@@ -214,7 +230,14 @@ static unsigned long get_stubs_size(const Elf64_Ehdr *hdr, ...@@ -214,7 +230,14 @@ static unsigned long get_stubs_size(const Elf64_Ehdr *hdr,
relocs += count_relocs((void *)sechdrs[i].sh_addr, relocs += count_relocs((void *)sechdrs[i].sh_addr,
sechdrs[i].sh_size sechdrs[i].sh_size
/ sizeof(Elf64_Rela)); / sizeof(Elf64_Rela),
R_PPC_REL24);
#ifdef CONFIG_PPC_KERNEL_PCREL
relocs += count_relocs((void *)sechdrs[i].sh_addr,
sechdrs[i].sh_size
/ sizeof(Elf64_Rela),
R_PPC64_REL24_NOTOC);
#endif
} }
} }
...@@ -231,6 +254,95 @@ static unsigned long get_stubs_size(const Elf64_Ehdr *hdr, ...@@ -231,6 +254,95 @@ static unsigned long get_stubs_size(const Elf64_Ehdr *hdr,
return relocs * sizeof(struct ppc64_stub_entry); return relocs * sizeof(struct ppc64_stub_entry);
} }
#ifdef CONFIG_PPC_KERNEL_PCREL
static int count_pcpu_relocs(const Elf64_Shdr *sechdrs,
const Elf64_Rela *rela, unsigned int num,
unsigned int symindex, unsigned int pcpu)
{
unsigned int i, r_info, r_addend, _count_relocs;
_count_relocs = 0;
r_info = 0;
r_addend = 0;
for (i = 0; i < num; i++) {
Elf64_Sym *sym;
/* 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_shndx == pcpu &&
(r_info != ELF64_R_SYM(rela[i].r_info) ||
r_addend != rela[i].r_addend)) {
_count_relocs++;
r_info = ELF64_R_SYM(rela[i].r_info);
r_addend = rela[i].r_addend;
}
}
return _count_relocs;
}
/* Get size of potential GOT required. */
static unsigned long get_got_size(const Elf64_Ehdr *hdr,
const Elf64_Shdr *sechdrs,
struct module *me)
{
/* One extra reloc so it's always 0-addr terminated */
unsigned long relocs = 1;
unsigned int i, symindex = 0;
for (i = 1; i < hdr->e_shnum; i++) {
if (sechdrs[i].sh_type == SHT_SYMTAB) {
symindex = i;
break;
}
}
WARN_ON_ONCE(!symindex);
/* Every relocated section... */
for (i = 1; i < hdr->e_shnum; i++) {
if (sechdrs[i].sh_type == SHT_RELA) {
pr_debug("Found relocations in section %u\n", i);
pr_debug("Ptr: %p. Number: %llu\n", (void *)sechdrs[i].sh_addr,
sechdrs[i].sh_size / sizeof(Elf64_Rela));
/*
* Sort the relocation information based on a symbol and
* addend key. This is a stable O(n*log n) complexity
* algorithm but it will reduce the complexity of
* count_relocs() to linear complexity O(n)
*/
sort((void *)sechdrs[i].sh_addr,
sechdrs[i].sh_size / sizeof(Elf64_Rela),
sizeof(Elf64_Rela), relacmp, NULL);
relocs += count_relocs((void *)sechdrs[i].sh_addr,
sechdrs[i].sh_size
/ sizeof(Elf64_Rela),
R_PPC64_GOT_PCREL34);
/*
* Percpu data access typically gets linked with
* REL34 relocations, but the percpu section gets
* moved at load time and requires that to be
* converted to GOT linkage.
*/
if (IS_ENABLED(CONFIG_SMP) && symindex)
relocs += count_pcpu_relocs(sechdrs,
(void *)sechdrs[i].sh_addr,
sechdrs[i].sh_size
/ sizeof(Elf64_Rela),
symindex, me->arch.pcpu_section);
}
}
pr_debug("Looks like a total of %lu GOT entries, max\n", relocs);
return relocs * sizeof(struct ppc64_got_entry);
}
#else /* CONFIG_PPC_KERNEL_PCREL */
/* Still needed for ELFv2, for .TOC. */ /* Still needed for ELFv2, for .TOC. */
static void dedotify_versions(struct modversion_info *vers, static void dedotify_versions(struct modversion_info *vers,
unsigned long size) unsigned long size)
...@@ -280,6 +392,7 @@ static Elf64_Sym *find_dot_toc(Elf64_Shdr *sechdrs, ...@@ -280,6 +392,7 @@ static Elf64_Sym *find_dot_toc(Elf64_Shdr *sechdrs,
} }
return NULL; return NULL;
} }
#endif /* CONFIG_PPC_KERNEL_PCREL */
bool module_init_section(const char *name) bool module_init_section(const char *name)
{ {
...@@ -298,6 +411,15 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr, ...@@ -298,6 +411,15 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr,
for (i = 1; i < hdr->e_shnum; i++) { for (i = 1; i < hdr->e_shnum; i++) {
if (strcmp(secstrings + sechdrs[i].sh_name, ".stubs") == 0) if (strcmp(secstrings + sechdrs[i].sh_name, ".stubs") == 0)
me->arch.stubs_section = i; me->arch.stubs_section = i;
#ifdef CONFIG_PPC_KERNEL_PCREL
else if (strcmp(secstrings + sechdrs[i].sh_name, ".data..percpu") == 0)
me->arch.pcpu_section = i;
else if (strcmp(secstrings + sechdrs[i].sh_name, ".mygot") == 0) {
me->arch.got_section = i;
if (sechdrs[i].sh_addralign < 8)
sechdrs[i].sh_addralign = 8;
}
#else
else if (strcmp(secstrings + sechdrs[i].sh_name, ".toc") == 0) { else if (strcmp(secstrings + sechdrs[i].sh_name, ".toc") == 0) {
me->arch.toc_section = i; me->arch.toc_section = i;
if (sechdrs[i].sh_addralign < 8) if (sechdrs[i].sh_addralign < 8)
...@@ -312,6 +434,7 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr, ...@@ -312,6 +434,7 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr,
sechdrs[i].sh_size / sizeof(Elf64_Sym), sechdrs[i].sh_size / sizeof(Elf64_Sym),
(void *)hdr (void *)hdr
+ sechdrs[sechdrs[i].sh_link].sh_offset); + sechdrs[sechdrs[i].sh_link].sh_offset);
#endif
} }
if (!me->arch.stubs_section) { if (!me->arch.stubs_section) {
...@@ -319,15 +442,26 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr, ...@@ -319,15 +442,26 @@ int module_frob_arch_sections(Elf64_Ehdr *hdr,
return -ENOEXEC; return -ENOEXEC;
} }
#ifdef CONFIG_PPC_KERNEL_PCREL
if (!me->arch.got_section) {
pr_err("%s: doesn't contain .mygot.\n", me->name);
return -ENOEXEC;
}
/* Override the got size */
sechdrs[me->arch.got_section].sh_size = get_got_size(hdr, sechdrs, me);
#else
/* If we don't have a .toc, just use .stubs. We need to set r2 /* If we don't have a .toc, just use .stubs. We need to set r2
to some reasonable value in case the module calls out to to some reasonable value in case the module calls out to
other functions via a stub, or if a function pointer escapes other functions via a stub, or if a function pointer escapes
the module by some means. */ the module by some means. */
if (!me->arch.toc_section) if (!me->arch.toc_section)
me->arch.toc_section = me->arch.stubs_section; me->arch.toc_section = me->arch.stubs_section;
#endif
/* Override the stubs size */ /* Override the stubs size */
sechdrs[me->arch.stubs_section].sh_size = get_stubs_size(hdr, sechdrs); sechdrs[me->arch.stubs_section].sh_size = get_stubs_size(hdr, sechdrs);
return 0; return 0;
} }
...@@ -445,7 +579,11 @@ static bool is_mprofile_ftrace_call(const char *name) ...@@ -445,7 +579,11 @@ static bool is_mprofile_ftrace_call(const char *name)
*/ */
static inline unsigned long my_r2(const Elf64_Shdr *sechdrs, struct module *me) static inline unsigned long my_r2(const Elf64_Shdr *sechdrs, struct module *me)
{ {
#ifndef CONFIG_PPC_KERNEL_PCREL
return (sechdrs[me->arch.toc_section].sh_addr & ~0xfful) + 0x8000; return (sechdrs[me->arch.toc_section].sh_addr & ~0xfful) + 0x8000;
#else
return -1;
#endif
} }
/* Patch stub to reference function and correct r2 value. */ /* Patch stub to reference function and correct r2 value. */
...@@ -462,12 +600,36 @@ static inline int create_stub(const Elf64_Shdr *sechdrs, ...@@ -462,12 +600,36 @@ static inline int create_stub(const Elf64_Shdr *sechdrs,
if (is_mprofile_ftrace_call(name)) if (is_mprofile_ftrace_call(name))
return create_ftrace_stub(entry, addr, me); return create_ftrace_stub(entry, addr, me);
if ((unsigned long)entry->jump % 8 != 0) {
pr_err("%s: Address of stub entry is not 8-byte aligned\n", me->name);
return 0;
}
BUILD_BUG_ON(sizeof(ppc64_stub_insns) > sizeof(entry->jump));
for (i = 0; i < ARRAY_SIZE(ppc64_stub_insns); i++) { for (i = 0; i < ARRAY_SIZE(ppc64_stub_insns); i++) {
if (patch_instruction(&entry->jump[i], if (patch_instruction(&entry->jump[i],
ppc_inst(ppc64_stub_insns[i]))) ppc_inst(ppc64_stub_insns[i])))
return 0; return 0;
} }
if (IS_ENABLED(CONFIG_PPC_KERNEL_PCREL)) {
/* Stub uses address relative to itself! */
reladdr = 0 + offsetof(struct ppc64_stub_entry, funcdata);
BUILD_BUG_ON(reladdr != 32);
if (reladdr > 0x1FFFFFFFFL || reladdr < -0x200000000L) {
pr_err("%s: Address of %p out of range of 34-bit relative address.\n",
me->name, (void *)reladdr);
return 0;
}
pr_debug("Stub %p get data from reladdr %li\n", entry, reladdr);
/* May not even need this if we're relative to 0 */
if (patch_instruction(&entry->jump[0],
ppc_inst_prefix(entry->jump[0] | IMM_H18(reladdr),
entry->jump[1] | IMM_L(reladdr))))
return 0;
} else {
/* Stub uses address relative to r2. */ /* Stub uses address relative to r2. */
reladdr = (unsigned long)entry - my_r2(sechdrs, me); reladdr = (unsigned long)entry - my_r2(sechdrs, me);
if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) { if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) {
...@@ -484,6 +646,7 @@ static inline int create_stub(const Elf64_Shdr *sechdrs, ...@@ -484,6 +646,7 @@ static inline int create_stub(const Elf64_Shdr *sechdrs,
if (patch_instruction(&entry->jump[1], if (patch_instruction(&entry->jump[1],
ppc_inst(entry->jump[1] | PPC_LO(reladdr)))) ppc_inst(entry->jump[1] | PPC_LO(reladdr))))
return 0; return 0;
}
// func_desc_t is 8 bytes if ABIv2, else 16 bytes // func_desc_t is 8 bytes if ABIv2, else 16 bytes
desc = func_desc(addr); desc = func_desc(addr);
...@@ -527,6 +690,37 @@ static unsigned long stub_for_addr(const Elf64_Shdr *sechdrs, ...@@ -527,6 +690,37 @@ static unsigned long stub_for_addr(const Elf64_Shdr *sechdrs,
return (unsigned long)&stubs[i]; return (unsigned long)&stubs[i];
} }
#ifdef CONFIG_PPC_KERNEL_PCREL
/* Create GOT to load the location described in this ptr */
static unsigned long got_for_addr(const Elf64_Shdr *sechdrs,
unsigned long addr,
struct module *me,
const char *name)
{
struct ppc64_got_entry *got;
unsigned int i, num_got;
if (!IS_ENABLED(CONFIG_PPC_KERNEL_PCREL))
return addr;
num_got = sechdrs[me->arch.got_section].sh_size / sizeof(*got);
/* Find this stub, or if that fails, the next avail. entry */
got = (void *)sechdrs[me->arch.got_section].sh_addr;
for (i = 0; got[i].addr; i++) {
if (WARN_ON(i >= num_got))
return 0;
if (got[i].addr == addr)
return (unsigned long)&got[i];
}
got[i].addr = addr;
return (unsigned long)&got[i];
}
#endif
/* We expect a noop next: if it is, replace it with instruction to /* We expect a noop next: if it is, replace it with instruction to
restore r2. */ restore r2. */
static int restore_r2(const char *name, u32 *instruction, struct module *me) static int restore_r2(const char *name, u32 *instruction, struct module *me)
...@@ -534,6 +728,9 @@ static int restore_r2(const char *name, u32 *instruction, struct module *me) ...@@ -534,6 +728,9 @@ static int restore_r2(const char *name, u32 *instruction, struct module *me)
u32 *prev_insn = instruction - 1; u32 *prev_insn = instruction - 1;
u32 insn_val = *instruction; u32 insn_val = *instruction;
if (IS_ENABLED(CONFIG_PPC_KERNEL_PCREL))
return 0;
if (is_mprofile_ftrace_call(name)) if (is_mprofile_ftrace_call(name))
return 0; return 0;
...@@ -579,6 +776,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, ...@@ -579,6 +776,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
pr_debug("Applying ADD relocate section %u to %u\n", relsec, pr_debug("Applying ADD relocate section %u to %u\n", relsec,
sechdrs[relsec].sh_info); sechdrs[relsec].sh_info);
#ifndef CONFIG_PPC_KERNEL_PCREL
/* First time we're called, we can fix up .TOC. */ /* First time we're called, we can fix up .TOC. */
if (!me->arch.toc_fixed) { if (!me->arch.toc_fixed) {
sym = find_dot_toc(sechdrs, strtab, symindex); sym = find_dot_toc(sechdrs, strtab, symindex);
...@@ -588,7 +786,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, ...@@ -588,7 +786,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
sym->st_value = my_r2(sechdrs, me); sym->st_value = my_r2(sechdrs, me);
me->arch.toc_fixed = true; me->arch.toc_fixed = true;
} }
#endif
for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) { for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
/* This is where to make the change */ /* This is where to make the change */
location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
...@@ -616,6 +814,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, ...@@ -616,6 +814,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
*(unsigned long *)location = value; *(unsigned long *)location = value;
break; break;
#ifndef CONFIG_PPC_KERNEL_PCREL
case R_PPC64_TOC: case R_PPC64_TOC:
*(unsigned long *)location = my_r2(sechdrs, me); *(unsigned long *)location = my_r2(sechdrs, me);
break; break;
...@@ -675,8 +874,13 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, ...@@ -675,8 +874,13 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
= (*((uint16_t *) location) & ~0xffff) = (*((uint16_t *) location) & ~0xffff)
| (value & 0xffff); | (value & 0xffff);
break; break;
#endif
case R_PPC_REL24: case R_PPC_REL24:
#ifdef CONFIG_PPC_KERNEL_PCREL
/* PCREL still generates REL24 for mcount */
case R_PPC64_REL24_NOTOC:
#endif
/* FIXME: Handle weak symbols here --RR */ /* FIXME: Handle weak symbols here --RR */
if (sym->st_shndx == SHN_UNDEF || if (sym->st_shndx == SHN_UNDEF ||
sym->st_shndx == SHN_LIVEPATCH) { sym->st_shndx == SHN_LIVEPATCH) {
...@@ -724,6 +928,47 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, ...@@ -724,6 +928,47 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
*(u32 *)location = value; *(u32 *)location = value;
break; break;
#ifdef CONFIG_PPC_KERNEL_PCREL
case R_PPC64_PCREL34: {
unsigned long absvalue = value;
/* Convert value to relative */
value -= (unsigned long)location;
if (value + 0x200000000 > 0x3ffffffff) {
if (sym->st_shndx != me->arch.pcpu_section) {
pr_err("%s: REL34 %li out of range!\n",
me->name, (long)value);
return -ENOEXEC;
}
/*
* per-cpu section is special cased because
* it is moved during loading, so has to be
* converted to use GOT.
*/
value = got_for_addr(sechdrs, absvalue, me,
strtab + sym->st_name);
if (!value)
return -ENOENT;
value -= (unsigned long)location;
/* Turn pla into pld */
if (patch_instruction((u32 *)location,
ppc_inst_prefix((*(u32 *)location & ~0x02000000),
(*((u32 *)location + 1) & ~0xf8000000) | 0xe4000000)))
return -EFAULT;
}
if (patch_instruction((u32 *)location,
ppc_inst_prefix((*(u32 *)location & ~0x3ffff) | IMM_H18(value),
(*((u32 *)location + 1) & ~0xffff) | IMM_L(value))))
return -EFAULT;
break;
}
#else
case R_PPC64_TOCSAVE: case R_PPC64_TOCSAVE:
/* /*
* Marker reloc indicates we don't have to save r2. * Marker reloc indicates we don't have to save r2.
...@@ -731,8 +976,12 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, ...@@ -731,8 +976,12 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
* it. * it.
*/ */
break; break;
#endif
case R_PPC64_ENTRY: case R_PPC64_ENTRY:
if (IS_ENABLED(CONFIG_PPC_KERNEL_PCREL))
break;
/* /*
* Optimize ELFv2 large code model entry point if * Optimize ELFv2 large code model entry point if
* the TOC is within 2GB range of current location. * the TOC is within 2GB range of current location.
...@@ -775,6 +1024,20 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, ...@@ -775,6 +1024,20 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
| (value & 0xffff); | (value & 0xffff);
break; break;
#ifdef CONFIG_PPC_KERNEL_PCREL
case R_PPC64_GOT_PCREL34:
value = got_for_addr(sechdrs, value, me,
strtab + sym->st_name);
if (!value)
return -ENOENT;
value -= (unsigned long)location;
((uint32_t *)location)[0] = (((uint32_t *)location)[0] & ~0x3ffff) |
((value >> 16) & 0x3ffff);
((uint32_t *)location)[1] = (((uint32_t *)location)[1] & ~0xffff) |
(value & 0xffff);
break;
#endif
default: default:
pr_err("%s: Unknown ADD relocation: %lu\n", pr_err("%s: Unknown ADD relocation: %lu\n",
me->name, me->name,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment