From 89d6b39cfe36da040d3f47b3f1601c05d6368e3f Mon Sep 17 00:00:00 2001 From: Laurent Dufour Date: Fri, 5 Jun 2015 00:04:14 +0300 Subject: [PATCH] ppc64: pie -- Add ppc64le relocation's processing This cleans the assembly code, removing no more needed trick with the register 2 (TOC pointer). As a consequence, the __export_restore_task_trampoline() and __export_unmap_trampoline() are no more needed. Thus, the changes introduced by the commit de9df91002a3 ("Per architecture restorer trampolines") in cr-restore.c are no more used but are not impacting runtime code anyway. Signed-off-by: Laurent Dufour Signed-off-by: Cyrill Gorcunov Signed-off-by: Pavel Emelyanov --- Makefile | 4 +- arch/ppc64/crtools.c | 5 + arch/ppc64/include/asm/restore.h | 13 +- arch/ppc64/misc.S | 197 +++++++++++++++++++++++++++++++ arch/ppc64/parasite-head.S | 26 ++-- arch/ppc64/restorer-trampoline.S | 33 ------ arch/ppc64/vdso-pie.c | 37 ------ pie/Makefile | 15 ++- pie/pie-relocs.h | 2 +- pie/piegen/Makefile | 3 + pie/piegen/elf-ppc64.c | 16 +++ pie/piegen/elf.c | 172 ++++++++++++++++++++++++++- pie/piegen/main.c | 15 +++ pie/piegen/piegen.h | 4 + 14 files changed, 442 insertions(+), 100 deletions(-) create mode 100644 arch/ppc64/misc.S delete mode 100644 arch/ppc64/restorer-trampoline.S create mode 100644 pie/piegen/elf-ppc64.c diff --git a/Makefile b/Makefile index b7adb3dc4..ea6a7c6c3 100644 --- a/Makefile +++ b/Makefile @@ -152,7 +152,7 @@ ARCH-LIB := $(ARCH_DIR)/crtools.built-in.o CRIU-SO := libcriu CRIU-LIB := lib/$(CRIU-SO).so CRIU-INC := lib/criu.h include/criu-plugin.h include/criu-log.h protobuf/rpc.proto -ifneq ($(filter i386 ia32 x86_64, $(ARCH)),) +ifneq ($(filter i386 ia32 x86_64 ppc64le, $(ARCH)),) PIEGEN := pie/piegen/piegen endif @@ -197,7 +197,7 @@ $(ARCH_DIR)/%:: protobuf config $(ARCH_DIR): protobuf config $(Q) $(MAKE) $(build)=$(ARCH_DIR) all -ifneq ($(filter i386 ia32 x86_64, $(ARCH)),) +ifneq ($(filter i386 ia32 x86_64 ppc64le, $(ARCH)),) pie/piegen/%: config $(Q) $(MAKE) $(build)=pie/piegen $@ pie/piegen: config diff --git a/arch/ppc64/crtools.c b/arch/ppc64/crtools.c index 31cef5d22..c4e70b554 100644 --- a/arch/ppc64/crtools.c +++ b/arch/ppc64/crtools.c @@ -39,6 +39,11 @@ static inline void __check_code_syscall(void) void parasite_setup_regs(unsigned long new_ip, void *stack, user_regs_struct_t *regs) { + /* + * OpenPOWER ABI requires that r12 is set to the calling function addressi + * to compute the TOC pointer. + */ + regs->gpr[12] = new_ip; regs->nip = new_ip; if (stack) regs->gpr[1] = (unsigned long) stack; diff --git a/arch/ppc64/include/asm/restore.h b/arch/ppc64/include/asm/restore.h index a66046422..325ff96e1 100644 --- a/arch/ppc64/include/asm/restore.h +++ b/arch/ppc64/include/asm/restore.h @@ -13,24 +13,19 @@ task_args) \ asm volatile( \ "mr 1,%0 \n" \ - "mr 3,%1 \n" \ - "mtctr 3 \n" \ + "mr 12,%1 \n" \ + "mtctr 12 \n" \ "mr 3,%2 \n" \ - "mr 2,%3 \n" \ "bctr \n" \ : \ : "r"(new_sp), \ "r"((unsigned long)restore_task_exec_start), \ - "r"(task_args), \ - "r"((unsigned long)task_args->bootstrap_start + 0x8000) \ - : "sp", "1", "2", "3", "memory") + "r"(task_args) \ + : "sp", "1", "2", "3", "12", "memory") /* There is nothing to do since TLS is accessed through r13 */ #define core_get_tls(pcore, ptls) int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core); -#define arch_export_restore_task __export_restore_task_trampoline -#define arch_export_unmap __export_unmap_trampoline - #endif /* __CR_ASM_RESTORE_H__ */ diff --git a/arch/ppc64/misc.S b/arch/ppc64/misc.S new file mode 100644 index 000000000..4ee188d55 --- /dev/null +++ b/arch/ppc64/misc.S @@ -0,0 +1,197 @@ +/* + * This is from linux/arch/powerpc/lib/crtsavres.S: + * + * Special support for eabi and SVR4 + * + * Copyright (C) 1995, 1996, 1998, 2000, 2001 Free Software Foundation, Inc. + * Copyright 2008 Freescale Semiconductor, Inc. + * Written By Michael Meissner + * + * Based on gcc/config/rs6000/crtsavres.asm from gcc + * 64 bit additions from reading the PPC elf64abi document. + * + * This file 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. + * + * In addition to the permissions in the GNU General Public License, the + * Free Software Foundation gives you unlimited permission to link the + * compiled version of this file with other programs, and to distribute + * those programs without any restriction coming from the use of this + * file. (The General Public License restrictions do apply in other + * respects; for example, they cover modification of the file, and + * distribution when not linked into another program.) + * + * This file 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * As a special exception, if you link this library with files + * compiled with GCC to produce an executable, this does not cause + * the resulting executable to be covered by the GNU General Public License. + * This exception does not however invalidate any other reasons why + * the executable file might be covered by the GNU General Public License. + */ + +#define r0 0 +#define r1 1 +#define r2 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 + + .text + +.globl _savegpr0_14 +_savegpr0_14: + std r14,-144(r1) +.globl _savegpr0_15 +_savegpr0_15: + std r15,-136(r1) +.globl _savegpr0_16 +_savegpr0_16: + std r16,-128(r1) +.globl _savegpr0_17 +_savegpr0_17: + std r17,-120(r1) +.globl _savegpr0_18 +_savegpr0_18: + std r18,-112(r1) +.globl _savegpr0_19 +_savegpr0_19: + std r19,-104(r1) +.globl _savegpr0_20 +_savegpr0_20: + std r20,-96(r1) +.globl _savegpr0_21 +_savegpr0_21: + std r21,-88(r1) +.globl _savegpr0_22 +_savegpr0_22: + std r22,-80(r1) +.globl _savegpr0_23 +_savegpr0_23: + std r23,-72(r1) +.globl _savegpr0_24 +_savegpr0_24: + std r24,-64(r1) +.globl _savegpr0_25 +_savegpr0_25: + std r25,-56(r1) +.globl _savegpr0_26 +_savegpr0_26: + std r26,-48(r1) +.globl _savegpr0_27 +_savegpr0_27: + std r27,-40(r1) +.globl _savegpr0_28 +_savegpr0_28: + std r28,-32(r1) +.globl _savegpr0_29 +_savegpr0_29: + std r29,-24(r1) +.globl _savegpr0_30 +_savegpr0_30: + std r30,-16(r1) +.globl _savegpr0_31 +_savegpr0_31: + std r31,-8(r1) + std r0,16(r1) + blr + +.globl _restgpr0_14 +_restgpr0_14: + ld r14,-144(r1) +.globl _restgpr0_15 +_restgpr0_15: + ld r15,-136(r1) +.globl _restgpr0_16 +_restgpr0_16: + ld r16,-128(r1) +.globl _restgpr0_17 +_restgpr0_17: + ld r17,-120(r1) +.globl _restgpr0_18 +_restgpr0_18: + ld r18,-112(r1) +.globl _restgpr0_19 +_restgpr0_19: + ld r19,-104(r1) +.globl _restgpr0_20 +_restgpr0_20: + ld r20,-96(r1) +.globl _restgpr0_21 +_restgpr0_21: + ld r21,-88(r1) +.globl _restgpr0_22 +_restgpr0_22: + ld r22,-80(r1) +.globl _restgpr0_23 +_restgpr0_23: + ld r23,-72(r1) +.globl _restgpr0_24 +_restgpr0_24: + ld r24,-64(r1) +.globl _restgpr0_25 +_restgpr0_25: + ld r25,-56(r1) +.globl _restgpr0_26 +_restgpr0_26: + ld r26,-48(r1) +.globl _restgpr0_27 +_restgpr0_27: + ld r27,-40(r1) +.globl _restgpr0_28 +_restgpr0_28: + ld r28,-32(r1) +.globl _restgpr0_29 +_restgpr0_29: + ld r0,16(r1) + ld r29,-24(r1) + mtlr r0 + ld r30,-16(r1) + ld r31,-8(r1) + blr + +.globl _restgpr0_30 +_restgpr0_30: + ld r30,-16(r1) +.globl _restgpr0_31 +_restgpr0_31: + ld r0,16(r1) + ld r31,-8(r1) + mtlr r0 + blr diff --git a/arch/ppc64/parasite-head.S b/arch/ppc64/parasite-head.S index e7163f0a5..a1c189fe9 100644 --- a/arch/ppc64/parasite-head.S +++ b/arch/ppc64/parasite-head.S @@ -9,7 +9,6 @@ ENTRY(__export_parasite_head_start) // int __used parasite_service(unsigned int cmd, void *args) // cmd = r3 = *__export_parasite_cmd (u32 ?) // args = r4 = @parasite_args_ptr + @pc - bl 0f 0: mflr r2 @@ -21,24 +20,27 @@ ENTRY(__export_parasite_head_start) lwz r3,0(r3) LOAD_REG_ADDR(r4,parasite_args_ptr) - lwz r4,0(r4) - add r4,r4,r2 // Fix up ptr + ld r4,0(r4) - // Set the TOC pointer - LOAD_REG_ADDR(r5,parasite_toc_ptr) - ld r5,0(r5) - add r2,r2,r5 // Fix up ptr + LOAD_REG_ADDR(r12,parasite_service_ptr) + ld r12,0(r12) + mtctr r12 - bl parasite_service + bctrl // call parasite_service twi 31,0,0 // Should generate SIGTRAP parasite_args_ptr: - .long __export_parasite_args - (0b - __export_parasite_head_start) + .quad __export_parasite_args + +parasite_service_ptr: + // We want to run the function prototype to set r2. + // Since the relocation will prefer the local entry + // point, we force it to the global one which is 2 + // instructions above the local one. + // FIXME: There should be a way to specify the global entry here. + .quad parasite_service - 8 __export_parasite_cmd: .long 0 -parasite_toc_ptr: - .long .TOC. - (0b - __export_parasite_head_start) - END(__export_parasite_head_start) diff --git a/arch/ppc64/restorer-trampoline.S b/arch/ppc64/restorer-trampoline.S deleted file mode 100644 index 4c870b907..000000000 --- a/arch/ppc64/restorer-trampoline.S +++ /dev/null @@ -1,33 +0,0 @@ -#include "asm/linkage.h" -#include "parasite.h" - - .section .head.text - .align 8 - - // Called through parasite_unmap - // This trampoline is there to restore r2 before jumping back to the - // C code. -#define LOAD_REG_ADDR(reg, name) \ - addis reg,r7,(name - 0b)@ha; \ - addi reg,r7,(name - 0b)@l; - -ENTRY(__export_unmap_trampoline) - bl 0f -0: mflr r7 - LOAD_REG_ADDR(r8,restorer_r2) - ld r2,0(r8) - b __export_unmap - //END(__export_restore_unmap_trampoline) - - // Called from JUMP_TO_RESTORER_BLOB, ctr contains the address where - // to jump to, and r3 etc contains the parameter. - // Assuming up to 4 parameters here since we are using r7 and r8. -ENTRY(__export_restore_task_trampoline) - bl 0f -0: mflr r7 - LOAD_REG_ADDR(r8,restorer_r2) - std r2,0(r8) - b __export_restore_task - -restorer_r2: - .long 0 diff --git a/arch/ppc64/vdso-pie.c b/arch/ppc64/vdso-pie.c index 8219e4af1..a77acf1ef 100644 --- a/arch/ppc64/vdso-pie.c +++ b/arch/ppc64/vdso-pie.c @@ -192,42 +192,6 @@ static unsigned long elf_hash(const unsigned char *name) return h; } -/* - * TODO : - * PIE linking doesn't work for this kind of definition. - * When build for the parasite code, the pointers to the string are - * computed from the start of the object but the generated code is - * assuming that the pointers are fixed by the loader. - * - * In addition, GCC create a call to C library memcpy when the table is - * containing more than 9 items. Since the parasite code is not linked - * with the C library an undefined symbol error is raised at build time. - * By initialising the table at run time, we are working around this - * issue. - */ -#ifdef __pie__ -static const char *VDSO_SYMBOL(int i) -{ - static char *vdso_symbols[VDSO_SYMBOL_MAX]; - static int init_done = 0; - -#define SET_VDSO_SYM(s) vdso_symbols[VDSO_SYMBOL_##s] = VDSO_SYMBOL_##s##_NAME - if (!init_done) { - SET_VDSO_SYM(CLOCK_GETRES); - SET_VDSO_SYM(CLOCK_GETTIME); - SET_VDSO_SYM(GET_SYSCALL_MAP); - SET_VDSO_SYM(GET_TBFREQ); - SET_VDSO_SYM(GETCPU); - SET_VDSO_SYM(GETTIMEOFDAY); - SET_VDSO_SYM(SIGTRAMP_RT64); - SET_VDSO_SYM(SYNC_DICACHE); - SET_VDSO_SYM(SYNC_DICACHE_P5); - SET_VDSO_SYM(TIME); - init_done = 1; - } - return vdso_symbols[i]; -} -#else #define SET_VDSO_SYM(s) [VDSO_SYMBOL_##s] = VDSO_SYMBOL_##s##_NAME const char *vdso_symbols[VDSO_SYMBOL_MAX] = { SET_VDSO_SYM(CLOCK_GETRES), @@ -242,7 +206,6 @@ const char *vdso_symbols[VDSO_SYMBOL_MAX] = { SET_VDSO_SYM(TIME) }; #define VDSO_SYMBOL(i) vdso_symbols[i] -#endif int vdso_fill_symtable(char *mem, size_t size, struct vdso_symtable *t) { diff --git a/pie/Makefile b/pie/Makefile index add7a0011..2ad4bcfca 100644 --- a/pie/Makefile +++ b/pie/Makefile @@ -14,6 +14,7 @@ ifeq ($(SRCARCH), ppc64) asm-e += $(ARCH_DIR)/vdso-trampoline.o asm-e += $(ARCH_DIR)/memcpy_power7.o asm-e += $(ARCH_DIR)/memcmp_64.o +asm-e += $(ARCH_DIR)/misc.o endif endif @@ -23,9 +24,6 @@ parasite-libs-e += $(SYSCALL-LIB) restorer-obj-y += restorer.o restorer-obj-e += $(ARCH_DIR)/restorer.o -ifeq ($(SRCARCH), ppc64) -restorer-asm-e += $(ARCH_DIR)/restorer-trampoline.o -endif restorer-libs-e += $(SYSCALL-LIB) # @@ -54,23 +52,30 @@ PIELDS := pie.lds.S .SECONDARY: -ifneq ($(filter i386 ia32 x86_64, $(ARCH)),) +ifneq ($(filter i386 ia32 x86_64 ppc64le, $(ARCH)),) ldflags-y += -r target-name = $(patsubst pie/%-blob.h,%,$(1)) +ifeq ($(SRCARCH),ppc64) +$(obj)/$(PIELDS): $(obj)/pie-reloc.lds.S.in + $(E) " GEN " $@ + $(Q) echo "OUTPUT_ARCH($(LDARCH))" > $(obj)/$(PIELDS) + $(Q) cat $< >> $(obj)/$(PIELDS) +else ifeq ($(ARCH),x86_64) $(obj)/$(PIELDS): $(obj)/pie-reloc.lds.S.in $(E) " GEN " $@ $(Q) echo "OUTPUT_ARCH(i386:x86-64)" > $(obj)/$(PIELDS) $(Q) echo "TARGET(elf64-x86-64)" >> $(obj)/$(PIELDS) $(Q) cat $< >> $(obj)/$(PIELDS) -else +else # i386 ia32 $(obj)/$(PIELDS): $(obj)/pie-reloc.lds.S.in $(E) " GEN " $@ $(Q) echo "OUTPUT_ARCH(i386)" > $(obj)/$(PIELDS) $(Q) echo "TARGET(elf32-i386)" >> $(obj)/$(PIELDS) $(Q) cat $< >> $(obj)/$(PIELDS) endif +endif $(obj)/%.built-in.bin.o: $(obj)/%.built-in.o $(obj)/$(PIELDS) $(E) " GEN " $@ diff --git a/pie/pie-relocs.h b/pie/pie-relocs.h index 0b64c7b7c..f1e36b6ff 100644 --- a/pie/pie-relocs.h +++ b/pie/pie-relocs.h @@ -6,7 +6,7 @@ #include "compiler.h" #include "config.h" -#if defined(CONFIG_X86_64) || defined(CONFIG_X86_32) +#if defined(CONFIG_X86_64) || defined(CONFIG_X86_32) || defined(CONFIG_PPC64) extern __maybe_unused void elf_relocs_apply(void *mem, void *vbase, size_t size, elf_reloc_t *elf_relocs, size_t nr_relocs); #else static always_inline void elf_relocs_apply(void *mem, void *vbase, size_t size, elf_reloc_t *elf_relocs, size_t nr_relocs) { } diff --git a/pie/piegen/Makefile b/pie/piegen/Makefile index dd54c3f0b..530af8bf8 100644 --- a/pie/piegen/Makefile +++ b/pie/piegen/Makefile @@ -5,6 +5,9 @@ ifneq ($(filter i386 ia32 x86_64, $(ARCH)),) obj-y += elf-x86-32.o obj-y += elf-x86-64.o endif +ifeq ($(SRCARCH),ppc64) +obj-y += elf-ppc64.o +endif cleanup-y += $(obj)/piegen cleanup-y += $(obj)/*.o diff --git a/pie/piegen/elf-ppc64.c b/pie/piegen/elf-ppc64.c new file mode 100644 index 000000000..472725f9f --- /dev/null +++ b/pie/piegen/elf-ppc64.c @@ -0,0 +1,16 @@ +#define ELF_PPC64 +#define handle_elf handle_elf_ppc64 + +#define Ehdr_t Elf64_Ehdr +#define Shdr_t Elf64_Shdr +#define Sym_t Elf64_Sym +#define Rel_t Elf64_Rel +#define Rela_t Elf64_Rela + +#define ELF_ST_TYPE ELF64_ST_TYPE +#define ELF_ST_BIND ELF64_ST_BIND + +#define ELF_R_SYM ELF64_R_SYM +#define ELF_R_TYPE ELF64_R_TYPE + +#include "elf.c" diff --git a/pie/piegen/elf.c b/pie/piegen/elf.c index 8d36cbf64..33ca3e12e 100644 --- a/pie/piegen/elf.c +++ b/pie/piegen/elf.c @@ -43,6 +43,25 @@ static bool test_pointer(const void *ptr, const void *start, const size_t size, } \ } while (0) +#ifdef ELF_PPC64 +static int do_relative_toc(long value, uint16_t *location, + unsigned long mask, int complain_signed) +{ + if (complain_signed && (value + 0x8000 > 0xffff)) { + pr_err("TOC16 relocation overflows (%ld)\n", value); + return -1; + } + + if ((~mask & 0xffff) & value) { + pr_err("bad TOC16 relocation (%ld) (0x%lx)\n", value, (~mask & 0xffff) & value); + return -1; + } + + *location = (*location & ~mask) | (value & mask); + return 0; +} +#endif + int handle_elf(const piegen_opt_t *opts, void *mem, size_t size) { const char *symstrings = NULL; @@ -56,6 +75,9 @@ int handle_elf(const piegen_opt_t *opts, void *mem, size_t size) const char *secstrings; size_t i, k, nr_gotpcrel = 0; +#ifdef ELF_PPC64 + s64 toc_offset = 0; +#endif pr_debug("Header\n------------\n"); pr_debug("\ttype 0x%x machine 0x%x version 0x%x\n", @@ -99,6 +121,13 @@ int handle_elf(const piegen_opt_t *opts, void *mem, size_t size) (unsigned)sh->sh_type, &secstrings[sh->sh_name]); sec_hdrs[i] = sh; + +#ifdef ELF_PPC64 + if (!strcmp(&secstrings[sh->sh_name], ".toc")) { + toc_offset = sh->sh_addr + 0x8000; + pr_debug("\t\tTOC offset 0x%lx\n", toc_offset); + } +#endif } if (!symtab_hdr) { @@ -141,6 +170,16 @@ int handle_elf(const piegen_opt_t *opts, void *mem, size_t size) pr_debug("\ttype 0x%-2x bind 0x%-2x shndx 0x%-4x value 0x%-2lx name %s\n", (unsigned)ELF_ST_TYPE(sym->st_info), (unsigned)ELF_ST_BIND(sym->st_info), (unsigned)sym->st_shndx, (unsigned long)sym->st_value, name); +#ifdef ELF_PPC64 + if (!sym->st_value && !strncmp(name, ".TOC.", 6)) { + if (!toc_offset) { + pr_err("No TOC pointer\n"); + goto err; + } + sym->st_value = toc_offset; + continue; + } +#endif if (strncmp(name, "__export", 8)) continue; if (sym->st_shndx && sym->st_shndx < hdr->e_shnum) { @@ -207,8 +246,22 @@ int handle_elf(const piegen_opt_t *opts, void *mem, size_t size) (unsigned long)ELF_R_TYPE(r->rel.r_info), (unsigned long)sh_src->sh_offset); - if (sym->st_shndx == SHN_UNDEF) + if (sym->st_shndx == SHN_UNDEF) { +#ifdef ELF_PPC64 + /* On PowerPC, TOC symbols appear to be + * undefined but should be processed as well. + * Their type is STT_NOTYPE, so report any + * other one. + */ + if (ELF32_ST_TYPE(sym->st_info) != STT_NOTYPE + || strncmp(name, ".TOC.", 6)) { + pr_err("Unexpected undefined symbol:%s\n", name); + goto err; + } +#else continue; +#endif + } ptr_func_exit((mem + sh_rel->sh_offset + r->rel.r_offset)); if (sh->sh_type == SHT_REL) { @@ -227,7 +280,124 @@ int handle_elf(const piegen_opt_t *opts, void *mem, size_t size) value32 = (s32)sh_src->sh_offset + (s32)sym->st_value; value64 = (s64)sh_src->sh_offset + (s64)sym->st_value; +#ifdef ELF_PPC64 +/* Snippet from the OpenPOWER ABI for Linux Supplement: + * The OpenPOWER ABI uses the three most-significant bits in the symbol + * st_other field specifies the number of instructions between a function's + * global entry point and local entry point. The global entry point is used + * when it is necessary to set up the TOC pointer (r2) for the function. The + * local entry point is used when r2 is known to already be valid for the + * function. A value of zero in these bits asserts that the function does + * not use r2. + * The st_other values have the following meanings: + * 0 and 1, the local and global entry points are the same. + * 2, the local entry point is at 1 instruction past the global entry point. + * 3, the local entry point is at 2 instructions past the global entry point. + * 4, the local entry point is at 4 instructions past the global entry point. + * 5, the local entry point is at 8 instructions past the global entry point. + * 6, the local entry point is at 16 instructions past the global entry point. + * 7, reserved. + * + * Here we are only handle the case '3' which is the most commonly seen. + */ +#define LOCAL_OFFSET(s) ((s->st_other >> 5) & 0x7) + if (LOCAL_OFFSET(sym)) { + if (LOCAL_OFFSET(sym) != 3) { + pr_err("Unexpected local offset value %d\n", + LOCAL_OFFSET(sym)); + goto err; + } + pr_debug("\t\t\tUsing local offset\n"); + value64 += 8; + value32 += 8; + } +#endif + switch (ELF_R_TYPE(r->rel.r_info)) { +#ifdef ELF_PPC64 + case R_PPC64_REL24: + /* Update PC relative offset, linker has not done this yet */ + pr_debug("\t\t\tR_PPC64_REL24 at 0x%-4lx val 0x%lx\n", + place, value64); + /* Convert value to relative */ + value64 -= place; + if (value64 + 0x2000000 > 0x3ffffff || (value64 & 3) != 0) { + pr_err("REL24 %li out of range!\n", (long int)value64); + goto err; + } + /* Only replace bits 2 through 26 */ + *(uint32_t *)where = (*(uint32_t *)where & ~0x03fffffc) | + (value64 & 0x03fffffc); + break; + + case R_PPC64_ADDR32: + pr_debug("\t\t\tR_PPC64_ADDR32 at 0x%-4lx val 0x%x\n", + place, (unsigned int)(value32 + addend32)); + pr_out(" { .offset = 0x%-8x, .type = PIEGEN_TYPE_INT, " + " .addend = %-8d, .value = 0x%-16x, " + "}, /* R_PPC64_ADDR32 */\n", + (unsigned int) place, addend32, value32); + break; + + case R_PPC64_ADDR64: + case R_PPC64_REL64: + pr_debug("\t\t\tR_PPC64_ADDR64 at 0x%-4lx val 0x%lx\n", + place, value64 + addend64); + pr_out("\t{ .offset = 0x%-8x, .type = PIEGEN_TYPE_LONG," + " .addend = %-8ld, .value = 0x%-16lx, " + "}, /* R_PPC64_ADDR64 */\n", + (unsigned int) place, (long)addend64, (long)value64); + break; + + case R_PPC64_TOC16_HA: + pr_debug("\t\t\tR_PPC64_TOC16_HA at 0x%-4lx val 0x%lx\n", + place, value64 + addend64 - toc_offset + 0x8000); + if (do_relative_toc((value64 + addend64 - toc_offset + 0x8000) >> 16, + where, 0xffff, 1)) + goto err; + break; + + case R_PPC64_TOC16_LO: + pr_debug("\t\t\tR_PPC64_TOC16_LO at 0x%-4lx val 0x%lx\n", + place, value64 + addend64 - toc_offset); + if (do_relative_toc(value64 + addend64 - toc_offset, + where, 0xffff, 1)) + goto err; + break; + + case R_PPC64_TOC16_LO_DS: + pr_debug("\t\t\tR_PPC64_TOC16_LO_DS at 0x%-4lx val 0x%lx\n", + place, value64 + addend64 - toc_offset); + if (do_relative_toc(value64 + addend64 - toc_offset, + where, 0xfffc, 0)) + goto err; + break; + + case R_PPC64_REL16_HA: + value64 += addend64 - place; + pr_debug("\t\t\tR_PPC64_REL16_HA at 0x%-4lx val 0x%lx\n", + place, value64); + /* check that we are dealing with the addis 2,12 instruction */ + if (((*(uint32_t*)where) & 0xffff0000) != 0x3c4c0000) { + pr_err("Unexpected instruction for R_PPC64_REL16_HA\n"); + goto err; + } + *(uint16_t *)where = ((value64 + 0x8000) >> 16) & 0xffff; + break; + + case R_PPC64_REL16_LO: + value64 += addend64 - place; + pr_debug("\t\t\tR_PPC64_REL16_LO at 0x%-4lx val 0x%lx\n", + place, value64); + /* check that we are dealing with the addi 2,2 instruction */ + if (((*(uint32_t*)where) & 0xffff0000) != 0x38420000) { + pr_err("Unexpected instruction for R_PPC64_REL16_LO"); + goto err; + } + *(uint16_t *)where = value64 & 0xffff; + break; + +#endif /* ELF_PPC64 */ #ifdef ELF_X86_64 case R_X86_64_32: /* Symbol + Addend (4 bytes) */ diff --git a/pie/piegen/main.c b/pie/piegen/main.c index 64e824974..406a0a897 100644 --- a/pie/piegen/main.c +++ b/pie/piegen/main.c @@ -44,6 +44,21 @@ static int handle_elf(const piegen_opt_t *opts, void *mem, size_t size) return handle_elf_x86_64(opts, mem, size); #endif +#if defined(CONFIG_PPC64) + const unsigned char elf_ident[EI_NIDENT] = { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +#else + 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x02, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +#endif + }; + + if (memcmp(mem, elf_ident, sizeof(elf_ident)) == 0) + return handle_elf_ppc64(opts, mem, size); +#endif /* CONFIG_PPC64 */ + pr_err("Unsupported Elf format detected\n"); return -1; } diff --git a/pie/piegen/piegen.h b/pie/piegen/piegen.h index 79a97e5b2..5b0bc7917 100644 --- a/pie/piegen/piegen.h +++ b/pie/piegen/piegen.h @@ -19,6 +19,10 @@ extern int handle_elf_x86_32(const piegen_opt_t *opts, void *mem, size_t size); extern int handle_elf_x86_64(const piegen_opt_t *opts, void *mem, size_t size); #endif +#if defined(CONFIG_PPC64) +extern int handle_elf_ppc64(const piegen_opt_t *opts, void *mem, size_t size); +#endif + #define pr_out(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__) #if 0