[CRIU] [PATCH 13/16] ppc64: pie -- Add ppc64le relocation's processing

Cyrill Gorcunov gorcunov at openvz.org
Thu Jun 4 14:04:14 PDT 2015


From: Laurent Dufour <ldufour at linux.vnet.ibm.com>

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 <ldufour at linux.vnet.ibm.com>
Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
 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 61d9b6a5c0ef..357129213fef 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 31cef5d222d8..c4e70b554c81 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 a66046422118..325ff96e1018 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 000000000000..4ee188d554d3
--- /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 e7163f0a5081..a1c189fe94ea 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 4c870b907599..000000000000
--- 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 8219e4af1be3..a77acf1efd26 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 add7a0011c82..2ad4bcfca827 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 0b64c7b7ccc3..f1e36b6ff691 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 dd54c3f0b546..530af8bf8d9c 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 000000000000..472725f9fe7c
--- /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 8d36cbf64845..33ca3e12eb54 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 64e82497469d..406a0a89718c 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 79a97e5b2f53..5b0bc7917f07 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
-- 
2.4.2



More information about the CRIU mailing list