[PATCH] powerpc: build-time fixup alternate feature relative addresses

Christophe Leroy christophe.leroy at csgroup.eu
Fri Jan 26 06:02:09 AEDT 2024


Hi Nic,

Le 21/05/2017 à 03:01, Nicholas Piggin a écrit :
> Implement build-time fixup of alternate feature relative addresses for
> the out-of-line ("else") patch code. This is done post-link with a new
> powerpc build tool that parses relocations and fixup structures, and
> adjusts branch instructions.
> 
> This gives us the ability to link patch code anywhere in the kernel,
> without branches to targets outside the patch code having to be
> reached directly (without a linker stub). This allows patch code to be
> moved out from the head section, and avoids build failures with
> unresolvable branche.

Is it worth keeping this hanging in patchwork ? It seems outdated and 
doesn't apply. Could this me done with objtool instead ?

Christophe

> 
> The downside is increased complexity of the build steps.
> 
> Currently:
> 
> - The patch code must be linked with branch targets reached directly.
>    This is done by omitting the "x" attribute from the patch code's
>    section, which suppresses branch stub creation. Instead, a link error
>    is raised if a branch cannot reach its target directly.
> 
> - The runtime patcher adjusts external branch targets to compensate
>    for the distance moved (from link to runtime location).
> 
> After this change:
> 
> - The patch code is linked with branch stubs allowed, by using the "x"
>    section attribute. This allows flexibility in patch code placement.
> 
> - The final link is made with --emit-relocs, which emits relocations
>    for these branches.
> 
> - After link, a tool fixes patch code external branches to be be a
>    direct branch to target, relative to their runtime location. Any
>    branch stubs in the patch code section are ignored.
> 
> - After feature fixup, newer toolchains can strip off the additional
>    relocations.
> 
> - The runtime patcher can move the patch code into its runtime
>    location with no branch adjustment.
> 
> - After patching, original patch code can be discarded from runtime
>    image.
> 
> Signed-off-by: Nicholas Piggin <npiggin at gmail.com>
> ---
> 
> I'm reposting this with some fixes required to work with current
> kernels (relative exception tables). I think it's probably the right
> way to go in the long term, it gives a lot of control over instruction
> patching to have a build-time pass. It is more complexity though, so
> I don't know whether it would be better to wait until we hit more
> problems with the existing system first.
> 
> 
>   arch/powerpc/Makefile                      |   8 +-
>   arch/powerpc/Makefile.postlink             |  24 +-
>   arch/powerpc/include/asm/feature-fixups.h  |   5 +-
>   arch/powerpc/kernel/vmlinux.lds.S          |   8 +-
>   arch/powerpc/lib/feature-fixups.c          |  19 +-
>   arch/powerpc/tools/Makefile                |   3 +
>   arch/powerpc/tools/relocs/.gitignore       |   1 +
>   arch/powerpc/tools/relocs/Makefile         |  12 +
>   arch/powerpc/tools/relocs/code-patching.c  |  82 ++++
>   arch/powerpc/tools/relocs/code-patching.h  |   7 +
>   arch/powerpc/tools/relocs/elf_sections.c   | 337 ++++++++++++++
>   arch/powerpc/tools/relocs/elf_sections.h   |  50 ++
>   arch/powerpc/tools/relocs/process_relocs.c | 718 +++++++++++++++++++++++++++++
>   13 files changed, 1253 insertions(+), 21 deletions(-)
>   create mode 100644 arch/powerpc/tools/Makefile
>   create mode 100644 arch/powerpc/tools/relocs/.gitignore
>   create mode 100644 arch/powerpc/tools/relocs/Makefile
>   create mode 100644 arch/powerpc/tools/relocs/code-patching.c
>   create mode 100644 arch/powerpc/tools/relocs/code-patching.h
>   create mode 100644 arch/powerpc/tools/relocs/elf_sections.c
>   create mode 100644 arch/powerpc/tools/relocs/elf_sections.h
>   create mode 100644 arch/powerpc/tools/relocs/process_relocs.c
> 
> diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
> index edc682f7b462..bc4791aecd03 100644
> --- a/arch/powerpc/Makefile
> +++ b/arch/powerpc/Makefile
> @@ -104,6 +104,11 @@ LDFLAGS_vmlinux-$(CONFIG_RELOCATABLE) := -pie
>   LDFLAGS_vmlinux	:= $(LDFLAGS_vmlinux-y)
>   LDFLAGS_vmlinux += $(call ld-option,--orphan-handling=warn)
>   
> +# --emit-relocs required for post-link fixup of alternate feature
> +# text section relocations.
> +LDFLAGS_vmlinux	+= --emit-relocs
> +KBUILD_LDFLAGS_MODULE += --emit-relocs
> +
>   ifeq ($(CONFIG_PPC64),y)
>   ifeq ($(call cc-option-yn,-mcmodel=medium),y)
>   	# -mcmodel=medium breaks modules because it uses 32bit offsets from
> @@ -429,6 +434,7 @@ checkbin:
>   		false ; \
>   	fi
>   
> +archscripts: scripts_basic
> +	$(Q)$(MAKE) $(build)=arch/powerpc/tools
>   
>   CLEAN_FILES += $(TOUT)
> -
> diff --git a/arch/powerpc/Makefile.postlink b/arch/powerpc/Makefile.postlink
> index 5db43ebbe2df..bf9b180e3530 100644
> --- a/arch/powerpc/Makefile.postlink
> +++ b/arch/powerpc/Makefile.postlink
> @@ -2,7 +2,9 @@
>   # Post-link powerpc pass
>   # ===========================================================================
>   #
> -# 1. Check that vmlinux relocations look sane
> +# 1. Invoke process_relocs to fix feature fixup alternate section branches.
> +# 2. Check that vmlinux relocations look sane
> +# 3. Strip static relocations (created by --emit-relocs) if binutils >= 2.27
>   
>   PHONY := __archpost
>   __archpost:
> @@ -23,19 +25,35 @@ else
>   	$(CONFIG_SHELL) $(srctree)/arch/powerpc/tools/relocs_check.sh "$(OBJDUMP)" "$@"
>   endif
>   
> +quiet_cmd_process_alt_ftr_relocs = FTR_ALT $@
> +      cmd_process_alt_ftr_relocs = 					\
> +	arch/powerpc/tools/relocs/process_relocs "$@"
> +
> +ifeq ($(call ld-ifversion, -ge, 227000000, y),y)
> +quiet_cmd_strip_relocs = STRIP   $@
> +      cmd_strip_relocs =						\
> +	$(OBJCOPY) $(shell for sec in					\
> +		$$(readelf -St $@ | grep -B1 RELA | grep "\.rela" |	\
> +			cut -d"]" -f2 | sed "s/[[:space:]]//" |		\
> +			grep -v "^\.rela\.dyn$$" | sed "s/\.rela//") ;	\
> +			do echo -n "--remove-relocation=$$sec " ; done) $@
> +endif
> +
>   # `@true` prevents complaint when there is nothing to be done
>   
>   vmlinux: FORCE
> -	@true
> +	$(call if_changed,process_alt_ftr_relocs)
>   ifdef CONFIG_PPC64
>   	$(call cmd,head_check)
>   endif
>   ifdef CONFIG_RELOCATABLE
>   	$(call if_changed,relocs_check)
>   endif
> +	$(call if_changed,strip_relocs)
>   
>   %.ko: FORCE
> -	@true
> +	$(call if_changed,process_alt_ftr_relocs)
> +	$(call if_changed,strip_relocs)
>   
>   clean:
>   	rm -f .tmp_symbols.txt
> diff --git a/arch/powerpc/include/asm/feature-fixups.h b/arch/powerpc/include/asm/feature-fixups.h
> index 2de2319b99e2..9f78f2f62886 100644
> --- a/arch/powerpc/include/asm/feature-fixups.h
> +++ b/arch/powerpc/include/asm/feature-fixups.h
> @@ -16,6 +16,9 @@
>    * useable with the vdso shared library. There is also an assumption
>    * that values will be negative, that is, the fixup table has to be
>    * located after the code it fixes up.
> + *
> + * Please ensure that new section names, modifications to FTR_ENTRY
> + * encoding, etc., is handled by arch/powerpc/tools/relocs/ code.
>    */
>   #if defined(CONFIG_PPC64) && !defined(__powerpc64__)
>   /* 64 bits kernel, 32 bits code (ie. vdso32) */
> @@ -33,7 +36,7 @@
>   
>   #define FTR_SECTION_ELSE_NESTED(label)			\
>   label##2:						\
> -	.pushsection __ftr_alt_##label,"a";		\
> +	.pushsection __ftr_alt_##label,"ax";		\
>   	.align 2;					\
>   label##3:
>   
> diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S
> index 0e78129e033a..d3a4ad960176 100644
> --- a/arch/powerpc/kernel/vmlinux.lds.S
> +++ b/arch/powerpc/kernel/vmlinux.lds.S
> @@ -87,8 +87,7 @@ SECTIONS
>   	.text : AT(ADDR(.text) - LOAD_OFFSET) {
>   		ALIGN_FUNCTION();
>   #endif
> -		/* careful! __ftr_alt_* sections need to be close to .text */
> -		*(.text.hot .text .text.fixup .text.unlikely .fixup __ftr_alt_* .ref.text);
> +		*(.text.hot .text .text.fixup .text.unlikely .fixup .ref.text);
>   		SCHED_TEXT
>   		CPUIDLE_TEXT
>   		LOCK_TEXT
> @@ -112,7 +111,6 @@ SECTIONS
>   		*(.got2)
>   		__got2_end = .;
>   #endif /* CONFIG_PPC32 */
> -
>   	} :kernel
>   
>   	. = ALIGN(PAGE_SIZE);
> @@ -141,6 +139,10 @@ SECTIONS
>   	__init_begin = .;
>   	INIT_TEXT_SECTION(PAGE_SIZE) :kernel
>   
> +	.__ftr_alternates.text : AT(ADDR(.__ftr_alternates.text) - LOAD_OFFSET) {
> +		*(__ftr_alt*);
> +	}
> +
>   	/* .exit.text is discarded at runtime, not link time,
>   	 * to deal with references from __bug_table
>   	 */
> diff --git a/arch/powerpc/lib/feature-fixups.c b/arch/powerpc/lib/feature-fixups.c
> index f3917705c686..26cfb3919589 100644
> --- a/arch/powerpc/lib/feature-fixups.c
> +++ b/arch/powerpc/lib/feature-fixups.c
> @@ -47,20 +47,13 @@ static unsigned int *calc_addr(struct fixup_entry *fcur, long offset)
>   static int patch_alt_instruction(unsigned int *src, unsigned int *dest,
>   				 unsigned int *alt_start, unsigned int *alt_end)
>   {
> -	unsigned int instr;
> +	unsigned int instr = *src;
>   
> -	instr = *src;
> -
> -	if (instr_is_relative_branch(*src)) {
> -		unsigned int *target = (unsigned int *)branch_target(src);
> -
> -		/* Branch within the section doesn't need translating */
> -		if (target < alt_start || target >= alt_end) {
> -			instr = translate_branch(dest, src);
> -			if (!instr)
> -				return 1;
> -		}
> -	}
> +	/*
> +	 * We used to translate relative branches here, however we now
> +	 * do that by fixing up relocations after link with process_relocs
> +	 * tool in arch/powerpc/tools/relocs/
> +	 */
>   
>   	patch_instruction(dest, instr);
>   
> diff --git a/arch/powerpc/tools/Makefile b/arch/powerpc/tools/Makefile
> new file mode 100644
> index 000000000000..38dbf0427080
> --- /dev/null
> +++ b/arch/powerpc/tools/Makefile
> @@ -0,0 +1,3 @@
> +always		:= $(hostprogs-y) $(hostprogs-m)
> +
> +subdir-y	+= relocs
> diff --git a/arch/powerpc/tools/relocs/.gitignore b/arch/powerpc/tools/relocs/.gitignore
> new file mode 100644
> index 000000000000..5cf4382690d2
> --- /dev/null
> +++ b/arch/powerpc/tools/relocs/.gitignore
> @@ -0,0 +1 @@
> +process_relocs
> diff --git a/arch/powerpc/tools/relocs/Makefile b/arch/powerpc/tools/relocs/Makefile
> new file mode 100644
> index 000000000000..bea662d7c1ba
> --- /dev/null
> +++ b/arch/powerpc/tools/relocs/Makefile
> @@ -0,0 +1,12 @@
> +HOST_EXTRACFLAGS		+= -Wno-unused-function
> +ifdef CONFIG_DEBUG_BUGVERBOSE
> +HOST_EXTRACFLAGS		+= -DCONFIG_DEBUG_BUGVERBOSE
> +endif
> +
> +hostprogs-y			:= process_relocs
> +
> +process_relocs-objs		:= process_relocs.o elf_sections.o code-patching.o
> +
> +always				:= $(hostprogs-y)
> +
> +HOSTLOADLIBES_process_relocs	+= -lelf
> diff --git a/arch/powerpc/tools/relocs/code-patching.c b/arch/powerpc/tools/relocs/code-patching.c
> new file mode 100644
> index 000000000000..db564a03b51c
> --- /dev/null
> +++ b/arch/powerpc/tools/relocs/code-patching.c
> @@ -0,0 +1,82 @@
> +/*
> + *  Copyright 2008 Michael Ellerman, 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 of the License, or (at your option) any later version.
> + */
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <inttypes.h>
> +#include <errno.h>
> +#include "code-patching.h"
> +
> +#define BRANCH_SET_LINK 0x1
> +#define BRANCH_ABSOLUTE 0x2
> +
> +static int set_uncond_branch_target(uint32_t *insn,
> +		const uint64_t addr, uint64_t target)
> +{
> +	uint32_t i = *insn;
> +	int64_t offset;
> +
> +	offset = target;
> +	if (!(i & BRANCH_ABSOLUTE))
> +		offset = offset - addr;
> +
> +	/* Check we can represent the target in the instruction format */
> +	if (offset < -0x2000000 || offset > 0x1fffffc || offset & 0x3)
> +		return -EOVERFLOW;
> +
> +	/* Mask out the flags and target, so they don't step on each other. */
> +	*insn = 0x48000000 | (i & 0x3) | (offset & 0x03FFFFFC);
> +
> +	return 0;
> +}
> +
> +static int set_cond_branch_target(uint32_t *insn,
> +		const uint64_t addr, uint64_t target)
> +{
> +	uint32_t i = *insn;
> +	int64_t offset;
> +
> +	offset = target;
> +	if (!(i & BRANCH_ABSOLUTE))
> +		offset = offset - addr;
> +
> +	/* Check we can represent the target in the instruction format */
> +	if (offset < -0x8000 || offset > 0x7FFF || offset & 0x3)
> +		return -EOVERFLOW;
> +
> +	/* Mask out the flags and target, so they don't step on each other. */
> +	*insn = 0x40000000 | (i & 0x3FF0003) | (offset & 0xFFFC);
> +
> +	return 0;
> +}
> +
> +static uint32_t branch_opcode(uint32_t instr)
> +{
> +	return (instr >> 26) & 0x3F;
> +}
> +
> +static int instr_is_branch_iform(uint32_t instr)
> +{
> +	return branch_opcode(instr) == 18;
> +}
> +
> +static int instr_is_branch_bform(uint32_t instr)
> +{
> +	return branch_opcode(instr) == 16;
> +}
> +
> +int set_branch_target(uint32_t *insn,
> +		const uint64_t addr, uint64_t target)
> +{
> +	if (instr_is_branch_iform(*insn))
> +		return set_uncond_branch_target(insn, addr, target);
> +	else if (instr_is_branch_bform(*insn))
> +		return set_cond_branch_target(insn, addr, target);
> +
> +	return -EINVAL;
> +}
> diff --git a/arch/powerpc/tools/relocs/code-patching.h b/arch/powerpc/tools/relocs/code-patching.h
> new file mode 100644
> index 000000000000..1d3cbbe99102
> --- /dev/null
> +++ b/arch/powerpc/tools/relocs/code-patching.h
> @@ -0,0 +1,7 @@
> +#ifndef __CODE_PATCHING_H__
> +#define __CODE_PATCHING_H__
> +
> +int set_branch_target(uint32_t *insn,
> +		const uint64_t addr, uint64_t target);
> +
> +#endif
> diff --git a/arch/powerpc/tools/relocs/elf_sections.c b/arch/powerpc/tools/relocs/elf_sections.c
> new file mode 100644
> index 000000000000..718020d93394
> --- /dev/null
> +++ b/arch/powerpc/tools/relocs/elf_sections.c
> @@ -0,0 +1,337 @@
> +#define _GNU_SOURCE
> +#include <assert.h>
> +#include <errno.h>
> +#include <string.h>
> +#include <fcntl.h>
> +#include <gelf.h>
> +#include <elf.h>
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <sys/mman.h>
> +
> +#include "elf_sections.h"
> +
> +#define dbg_printf(...)
> +
> +static const char *rel_type_name(unsigned int type)
> +{
> +	static const char *const type_name[] = {
> +#define REL_TYPE(X)[X] = #X
> +		REL_TYPE(R_PPC64_NONE),
> +		REL_TYPE(R_PPC64_ADDR32),
> +		REL_TYPE(R_PPC64_ADDR24),
> +		REL_TYPE(R_PPC64_ADDR16),
> +		REL_TYPE(R_PPC64_ADDR16_LO),
> +		REL_TYPE(R_PPC64_ADDR16_HI),
> +		REL_TYPE(R_PPC64_ADDR16_HA),
> +		REL_TYPE(R_PPC64_ADDR14),
> +		REL_TYPE(R_PPC64_ADDR14_BRTAKEN),
> +		REL_TYPE(R_PPC64_ADDR14_BRNTAKEN),
> +		REL_TYPE(R_PPC64_REL24),
> +		REL_TYPE(R_PPC64_REL14),
> +		REL_TYPE(R_PPC64_REL14_BRTAKEN),
> +		REL_TYPE(R_PPC64_REL14_BRNTAKEN),
> +		REL_TYPE(R_PPC64_GOT16),
> +		REL_TYPE(R_PPC64_GOT16_LO),
> +		REL_TYPE(R_PPC64_GOT16_HI),
> +		REL_TYPE(R_PPC64_GOT16_HA),
> +		REL_TYPE(R_PPC64_COPY),
> +		REL_TYPE(R_PPC64_GLOB_DAT),
> +		REL_TYPE(R_PPC64_JMP_SLOT),
> +		REL_TYPE(R_PPC64_RELATIVE),
> +		REL_TYPE(R_PPC64_UADDR32),
> +		REL_TYPE(R_PPC64_UADDR16),
> +		REL_TYPE(R_PPC64_REL32),
> +		REL_TYPE(R_PPC64_PLT32),
> +		REL_TYPE(R_PPC64_PLTREL32),
> +		REL_TYPE(R_PPC64_PLT16_LO),
> +		REL_TYPE(R_PPC64_PLT16_HI),
> +		REL_TYPE(R_PPC64_PLT16_HA),
> +		REL_TYPE(R_PPC64_SECTOFF),
> +		REL_TYPE(R_PPC64_SECTOFF_LO),
> +		REL_TYPE(R_PPC64_SECTOFF_HI),
> +		REL_TYPE(R_PPC64_SECTOFF_HA),
> +		REL_TYPE(R_PPC64_ADDR30),
> +		REL_TYPE(R_PPC64_ADDR64),
> +		REL_TYPE(R_PPC64_ADDR16_HIGHER),
> +		REL_TYPE(R_PPC64_ADDR16_HIGHERA),
> +		REL_TYPE(R_PPC64_ADDR16_HIGHEST),
> +		REL_TYPE(R_PPC64_ADDR16_HIGHESTA),
> +		REL_TYPE(R_PPC64_UADDR64),
> +		REL_TYPE(R_PPC64_REL64),
> +		REL_TYPE(R_PPC64_PLT64),
> +		REL_TYPE(R_PPC64_PLTREL64),
> +		REL_TYPE(R_PPC64_TOC16),
> +		REL_TYPE(R_PPC64_TOC16_LO),
> +		REL_TYPE(R_PPC64_TOC16_HI),
> +		REL_TYPE(R_PPC64_TOC16_HA),
> +		REL_TYPE(R_PPC64_TOC),
> +		REL_TYPE(R_PPC64_PLTGOT16),
> +		REL_TYPE(R_PPC64_PLTGOT16_LO),
> +		REL_TYPE(R_PPC64_PLTGOT16_HI),
> +		REL_TYPE(R_PPC64_PLTGOT16_HA),
> +		REL_TYPE(R_PPC64_ADDR16_DS),
> +		REL_TYPE(R_PPC64_ADDR16_LO_DS),
> +		REL_TYPE(R_PPC64_GOT16_DS),
> +		REL_TYPE(R_PPC64_GOT16_LO_DS),
> +		REL_TYPE(R_PPC64_PLT16_LO_DS),
> +		REL_TYPE(R_PPC64_SECTOFF_DS),
> +		REL_TYPE(R_PPC64_SECTOFF_LO_DS),
> +		REL_TYPE(R_PPC64_TOC16_DS),
> +		REL_TYPE(R_PPC64_TOC16_LO_DS),
> +		REL_TYPE(R_PPC64_PLTGOT16_DS),
> +		REL_TYPE(R_PPC64_PLTGOT16_LO_DS),
> +		REL_TYPE(R_PPC64_TLS),
> +		REL_TYPE(R_PPC64_DTPMOD64),
> +		REL_TYPE(R_PPC64_TPREL16),
> +		REL_TYPE(R_PPC64_TPREL16_LO),
> +		REL_TYPE(R_PPC64_TPREL16_HI),
> +		REL_TYPE(R_PPC64_TPREL16_HA),
> +		REL_TYPE(R_PPC64_TPREL64),
> +		REL_TYPE(R_PPC64_DTPREL16),
> +		REL_TYPE(R_PPC64_DTPREL16_LO),
> +		REL_TYPE(R_PPC64_DTPREL16_HI),
> +		REL_TYPE(R_PPC64_DTPREL16_HA),
> +		REL_TYPE(R_PPC64_DTPREL64),
> +		REL_TYPE(R_PPC64_GOT_TLSGD16),
> +		REL_TYPE(R_PPC64_GOT_TLSGD16_LO),
> +		REL_TYPE(R_PPC64_GOT_TLSGD16_HI),
> +		REL_TYPE(R_PPC64_GOT_TLSGD16_HA),
> +		REL_TYPE(R_PPC64_GOT_TLSLD16),
> +		REL_TYPE(R_PPC64_GOT_TLSLD16_LO),
> +		REL_TYPE(R_PPC64_GOT_TLSLD16_HI),
> +		REL_TYPE(R_PPC64_GOT_TLSLD16_HA),
> +		REL_TYPE(R_PPC64_GOT_TPREL16_DS),
> +		REL_TYPE(R_PPC64_GOT_TPREL16_LO_DS),
> +		REL_TYPE(R_PPC64_GOT_TPREL16_HI),
> +		REL_TYPE(R_PPC64_GOT_TPREL16_HA),
> +		REL_TYPE(R_PPC64_GOT_DTPREL16_DS),
> +		REL_TYPE(R_PPC64_GOT_DTPREL16_LO_DS),
> +		REL_TYPE(R_PPC64_GOT_DTPREL16_HI),
> +		REL_TYPE(R_PPC64_GOT_DTPREL16_HA),
> +		REL_TYPE(R_PPC64_TPREL16_DS),
> +		REL_TYPE(R_PPC64_TPREL16_LO_DS),
> +		REL_TYPE(R_PPC64_TPREL16_HIGHER),
> +		REL_TYPE(R_PPC64_TPREL16_HIGHERA),
> +		REL_TYPE(R_PPC64_TPREL16_HIGHEST),
> +		REL_TYPE(R_PPC64_TPREL16_HIGHESTA),
> +		REL_TYPE(R_PPC64_DTPREL16_DS),
> +		REL_TYPE(R_PPC64_DTPREL16_LO_DS),
> +		REL_TYPE(R_PPC64_DTPREL16_HIGHER),
> +		REL_TYPE(R_PPC64_DTPREL16_HIGHERA),
> +		REL_TYPE(R_PPC64_DTPREL16_HIGHEST),
> +		REL_TYPE(R_PPC64_DTPREL16_HIGHESTA),
> +		REL_TYPE(R_PPC64_TLSGD),
> +		REL_TYPE(R_PPC64_TLSLD),
> +		REL_TYPE(R_PPC64_TOCSAVE),
> +/*		REL_TYPE(R_PPC64_ENTRY), */
> +		REL_TYPE(R_PPC64_REL16),
> +		REL_TYPE(R_PPC64_REL16_LO),
> +		REL_TYPE(R_PPC64_REL16_HI),
> +		REL_TYPE(R_PPC64_REL16_HA),
> +#undef REL_TYPE
> +	};
> +	const char *name = "UNKNOWN";
> +
> +	if (type < sizeof(type_name) / sizeof(typeof(type_name[0])) && type_name[type])
> +		name = type_name[type];
> +	return name;
> +}
> +
> +static struct section *get_section(struct elf *elf, Elf_Scn *scn)
> +{
> +	struct section *section;
> +
> +	section = malloc(sizeof(struct section));
> +
> +	section->scn = scn;
> +
> +	if (gelf_getshdr(scn, &section->shdr) == NULL) {
> +		fprintf(stderr, "gelf_getshdr failed: %s\n", elf_errmsg(-1));
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	section->name = elf_strptr(elf->elf, elf->shstrndx, section->shdr.sh_name);
> +	if (section->name == NULL) {
> +		fprintf(stderr, "gelf_strptr failed: %s\n", elf_errmsg(-1));
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	section->data = elf_getdata(scn, NULL);
> +	if (section->data) {
> +		assert(elf_getdata(scn, section->data) == NULL);
> +	}
> +
> +	section->symtab = NULL;
> +	if (section->shdr.sh_type == SHT_SYMTAB)
> +		goto no_symtab;
> +	if (section->shdr.sh_type == SHT_DYNSYM)
> +		goto no_symtab;
> +	if (section->shdr.sh_type == SHT_DYNAMIC)
> +		goto no_symtab;
> +
> +	/* printf("symtab index:%d\n", elf_scnshndx(scn)); ??? */
> +	if (section->shdr.sh_link) {
> +		Elf_Scn *link_scn;
> +
> +		link_scn = elf_getscn(elf->elf, section->shdr.sh_link);
> +		section->symtab = get_section(elf, link_scn);
> +
> +		assert(section->symtab->shdr.sh_type == SHT_SYMTAB ||
> +			section->symtab->shdr.sh_type == SHT_DYNSYM);
> +	}
> +
> +no_symtab:
> +	section->strtab = NULL;
> +	if (section->symtab == NULL) {
> +		if (section->shdr.sh_link) {
> +			Elf_Scn *link_scn;
> +
> +			link_scn = elf_getscn(elf->elf, section->shdr.sh_link);
> +			section->strtab = get_section(elf, link_scn);
> +
> +			assert(section->strtab->shdr.sh_type == SHT_STRTAB);
> +		}
> +	}
> +
> +	return section;
> +}
> +
> +struct symbol *elf_sections_get_symbol(struct elf *elf, struct section *section, unsigned long nr)
> +{
> +	struct symbol *symbol;
> +	Elf_Scn *scn;
> +
> +	symbol = malloc(sizeof(struct symbol));
> +
> +	if (gelf_getsym(section->symtab->data, nr, &symbol->sym) == NULL) {
> +		fprintf(stderr, "gelf_getsym failed: %s\n", elf_errmsg(-1));
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	scn = elf_getscn(elf->elf, symbol->sym.st_shndx);
> +	symbol->section = get_section(elf, scn);
> +	symbol->_name = symbol->section->name;
> +	if (symbol->sym.st_name) {
> +		symbol->name = elf_strptr(elf->elf, elf_ndxscn(section->symtab->strtab->scn), symbol->sym.st_name);
> +		symbol->_name = symbol->name;
> +	} else {
> +		symbol->name = NULL;
> +	}
> +
> +	return symbol;
> +}
> +
> +struct relocation *elf_sections_get_reloc(struct elf *elf, struct section *section, size_t n)
> +{
> +	struct relocation *relocation;
> +
> +	relocation = malloc(sizeof(struct relocation));
> +
> +	if (section->shdr.sh_type == SHT_REL) {
> +		if (gelf_getrel(section->data, n, &relocation->rel) != &relocation->rel) {
> +			return NULL;
> +		}
> +
> +		relocation->type_name = rel_type_name(GELF_R_TYPE(relocation->rel.r_info));
> +		relocation->symbol = elf_sections_get_symbol(elf, section, GELF_R_SYM(relocation->rel.r_info));
> +		relocation->offset = relocation->rel.r_offset;
> +		relocation->target = relocation->symbol->sym.st_value;
> +
> +	} else if (section->shdr.sh_type == SHT_RELA) {
> +		if (gelf_getrela(section->data, n, &relocation->rela) != &relocation->rela) {
> +			return NULL;
> +		}
> +
> +		relocation->type_name = rel_type_name(GELF_R_TYPE(relocation->rela.r_info));
> +		relocation->symbol = elf_sections_get_symbol(elf, section, GELF_R_SYM(relocation->rela.r_info));
> +		relocation->offset = relocation->rela.r_offset;
> +		relocation->target = relocation->symbol->sym.st_value;
> +		relocation->target += relocation->rela.r_addend;
> +
> +	}  else {
> +		assert(0);
> +	}
> +
> +	return relocation;
> +}
> +
> +struct elf *elf_sections_init(int fd)
> +{
> +	struct elf *elf;
> +	Elf_Scn *scn;
> +
> +	elf = malloc(sizeof(struct elf));
> +	assert(elf);
> +
> +	if (elf_version(EV_CURRENT) == EV_NONE) {
> +		fprintf(stderr, "libelf not initialized: %s\n", elf_errmsg(-1));
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	if ((elf->elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
> +		fprintf(stderr, "elf_begin failed: %s\n", elf_errmsg(-1));
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	if (elf_kind(elf->elf) != ELF_K_ELF) {
> +		fprintf(stderr, "Not an ELF object.\n");
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	if (gelf_getehdr(elf->elf, &elf->ehdr) == NULL) {
> +		fprintf(stderr, "gelf_getehdr failed: %s\n", elf_errmsg(-1));
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	if (elf->ehdr.e_version != EV_CURRENT) {
> +		fprintf(stderr, "Unknown ELF version\n");
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	if (elf->ehdr.e_machine != EM_PPC && elf->ehdr.e_machine != EM_PPC64) {
> +		fprintf(stderr, "Not a PPC/PPC64 machine\n");
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	if (elf_getshdrstrndx(elf->elf, &elf->shstrndx) != 0) {
> +		fprintf(stderr, "elf_getshdrstrndx failed: %s\n", elf_errmsg(-1));
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	scn = elf_getscn(elf->elf, elf->shstrndx);
> +	elf->strtab = get_section(elf, scn);
> +	assert(elf->strtab->shdr.sh_type == SHT_STRTAB);
> +
> +	return elf;
> +}
> +
> +void elf_sections_exit(struct elf *elf)
> +{
> +	elf_end(elf->elf);
> +}
> +
> +int elf_sections_processor(struct elf *elf,
> +				int (*fn)(struct section *section, void *arg),
> +				void *arg)
> +{
> +	Elf_Scn *scn;
> +	int err;
> +
> +	scn = NULL ;
> +	while ((scn = elf_nextscn(elf->elf, scn)) != NULL) {
> +		struct section *section;
> +
> +		section = get_section(elf, scn);
> +
> +		err = fn(section, arg);
> +		if (err)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> diff --git a/arch/powerpc/tools/relocs/elf_sections.h b/arch/powerpc/tools/relocs/elf_sections.h
> new file mode 100644
> index 000000000000..c3bc744efca8
> --- /dev/null
> +++ b/arch/powerpc/tools/relocs/elf_sections.h
> @@ -0,0 +1,50 @@
> +#ifndef __ELF_SECTIONS_H__
> +#define __ELF_SECTIONS_H__
> +
> +#include <gelf.h>
> +#include <elf.h>
> +
> +struct section {
> +	Elf_Scn *scn;
> +	GElf_Shdr shdr;
> +	const char *name;
> +	Elf_Data *data;
> +
> +	struct section *symtab;
> +	struct section *strtab;
> +};
> +
> +struct symbol {
> +	GElf_Sym sym;
> +	struct section *section;
> +	const char *name;
> +	const char *_name;
> +};
> +
> +struct relocation {
> +	GElf_Rel rel;
> +	GElf_Rela rela;
> +
> +	const char *type_name;
> +	struct symbol *symbol;
> +
> +	uint64_t offset;
> +	uint64_t target;
> +};
> +
> +struct elf {
> +	Elf *elf;
> +	GElf_Ehdr ehdr;
> +	size_t shstrndx;
> +	struct section *strtab;
> +};
> +
> +struct symbol *elf_sections_get_symbol(struct elf *elf, struct section *section, unsigned long nr);
> +struct relocation *elf_sections_get_reloc(struct elf *elf, struct section *section, size_t n);
> +struct elf *elf_sections_init(int fd);
> +void elf_sections_exit(struct elf *elf);
> +int elf_sections_processor(struct elf *elf,
> +				int (*fn)(struct section *section, void *arg),
> +				void *arg);
> +
> +#endif
> diff --git a/arch/powerpc/tools/relocs/process_relocs.c b/arch/powerpc/tools/relocs/process_relocs.c
> new file mode 100644
> index 000000000000..a678179cfd0b
> --- /dev/null
> +++ b/arch/powerpc/tools/relocs/process_relocs.c
> @@ -0,0 +1,718 @@
> +#define _GNU_SOURCE
> +#include <assert.h>
> +#include <errno.h>
> +#include <string.h>
> +#include <fcntl.h>
> +#include <gelf.h>
> +#include <elf.h>
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <getopt.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <sys/mman.h>
> +#include <asm/byteorder.h>
> +#include "elf_sections.h"
> +#include "code-patching.h"
> +
> +/*
> + * This program runs through relocation data in PPC/PPC64 vmlinux ELF
> + * image generated with --emit-relocs, and performs some processing and
> + * checks.
> + *
> + * Presently, it has the following functions:
> + * 1. Fix relocations for branches inside alternate feature sections
> + *    (the "else" patches), so that they are correct for their destination
> + *    address. They never get executed at their linked location.
> + *
> + *    This is done by parsing all fixup_entry structures in the _ftr_fixup
> + *    sections, and keeping those with non-zero alternate patch. Then all
> + *    relocations in the .__ftr_alternates.text section are parsed, and those
> + *    matching addresses in our fixup_entry alternates patches get
> + *    struct insn_patch created for them. Finally, all struct insn_patch'es
> + *    are iterated and written to the image in-place. Care needs to be taken
> + *    with nested fixups, see check_and_flatten_fixup_entries().
> + *
> + * 2. Check that no __ex_table or __bug_table entries point to alternate
> + *    sections. We don't support that at present.
> + */
> +
> +#define dbg_printf(...)
> +
> +static int is_kernel = 0;
> +
> +struct fixup_entry_64 {
> +	uint64_t mask;
> +	uint64_t value;
> +	uint64_t start_off;
> +	uint64_t end_off;
> +	uint64_t alt_start_off;
> +	uint64_t alt_end_off;
> +} __attribute__((packed));
> +
> +#define fixup_entry fixup_entry_64
> +
> +struct fixup_entry_32 {
> +	uint32_t mask;
> +	uint32_t value;
> +	uint32_t start_off;
> +	uint32_t end_off;
> +	uint32_t alt_start_off;
> +	uint32_t alt_end_off;
> +} __attribute__((packed));
> +
> +struct exception_entry_64 {
> +	int32_t insn;
> +	int32_t fixup;
> +};
> +
> +struct exception_entry_32 {
> +	uint32_t insn;
> +	uint32_t fixup;
> +};
> +
> +struct bug_entry_64 {
> +	uint64_t bug_addr;
> +#ifdef CONFIG_DEBUG_BUGVERBOSE
> +	uint64_t file;
> +	uint16_t line;
> +#endif
> +	uint16_t flags;
> +};
> +
> +struct bug_entry_32 {
> +	uint32_t bug_addr;
> +#ifdef CONFIG_DEBUG_BUGVERBOSE
> +	uint32_t file;
> +	uint16_t line;
> +#endif
> +	uint16_t flags;
> +};
> +
> +struct insn_patch {
> +	uint32_t	insn;		/* New instruction */
> +	off_t		offset;		/* Image location to patch */
> +};
> +
> +static int is_64bit(struct elf *elf)
> +{
> +	return elf->ehdr.e_ident[EI_CLASS] == ELFCLASS64;
> +}
> +
> +static int is_32bit(struct elf *elf)
> +{
> +	return elf->ehdr.e_ident[EI_CLASS] == ELFCLASS32;
> +}
> +
> +static int is_le(struct elf *elf)
> +{
> +	return elf->ehdr.e_ident[EI_DATA] == ELFDATA2LSB;
> +}
> +
> +static int is_be(struct elf *elf)
> +{
> +	return elf->ehdr.e_ident[EI_DATA] == ELFDATA2MSB;
> +}
> +
> +
> +static struct elf *elf;
> +
> +static uint16_t f16_to_cpu(uint16_t val)
> +{
> +	if (is_le(elf))
> +		return __le16_to_cpu(val);
> +	else
> +		return __be16_to_cpu(val);
> +}
> +
> +static uint32_t f32_to_cpu(uint32_t val)
> +{
> +	if (is_le(elf))
> +		return __le32_to_cpu(val);
> +	else
> +		return __be32_to_cpu(val);
> +}
> +
> +static uint64_t f64_to_cpu(uint64_t val)
> +{
> +	if (is_le(elf))
> +		return __le64_to_cpu(val);
> +	else
> +		return __be64_to_cpu(val);
> +}
> +
> +static uint16_t cpu_to_f16(uint16_t val)
> +{
> +	if (is_le(elf))
> +		return __cpu_to_le16(val);
> +	else
> +		return __cpu_to_be16(val);
> +}
> +
> +static uint32_t cpu_to_f32(uint32_t val)
> +{
> +	if (is_le(elf))
> +		return __cpu_to_le32(val);
> +	else
> +		return __cpu_to_be32(val);
> +}
> +
> +static uint64_t cpu_to_f64(uint64_t val)
> +{
> +	if (is_le(elf))
> +		return __cpu_to_le64(val);
> +	else
> +		return __cpu_to_be64(val);
> +}
> +
> +static struct section *ftr_alt;
> +
> +static unsigned int nr_fes = 0;
> +static struct fixup_entry *fes = NULL;
> +
> +/* Could grab the start/end of .__ftr_alternates.text directly */
> +static uint64_t fe_alt_start = -1;
> +static uint64_t fe_alt_end = 0;
> +
> +static struct fixup_entry *find_fe_altaddr(uint64_t addr)
> +{
> +	unsigned int i;
> +
> +	if (addr < fe_alt_start)
> +		return NULL;
> +	if (addr >= fe_alt_end)
> +		return NULL;
> +
> +	for (i = 0; i < nr_fes; i++) {
> +		if (addr >= fes[i].alt_start_off && addr < fes[i].alt_end_off)
> +			return &fes[i];
> +	}
> +	return NULL;
> +}
> +
> +/*
> + * Fixup entries can be nested, so we need to find the final address. The
> + * "if" side never needs to be fixed -- if it's nested inside an "else" part,
> + * it will get fixed as part of the fixup for that else part. "else" nested
> + * in "else" needs to be fixed -- possibly multiple nestings.
> + *
> + * The inner-most nestings always come first, so we traverse the array
> + * backward, fixing up destination of nested parts according to their
> + * parent, which takes care of multiple nestings without further work.
> + *
> + * We can also do some sanity checks here.
> + */
> +static void check_and_flatten_fixup_entries(void)
> +{
> +	static struct fixup_entry *fe;
> +	unsigned int i;
> +
> +	i = nr_fes;
> +	while (i) {
> +		static struct fixup_entry *parent;
> +		uint64_t nested_off; /* offset from start of parent */
> +		uint64_t size;
> +
> +		i--;
> +		fe = &fes[i];
> +
> +		parent = find_fe_altaddr(fe->start_off);
> +		if (!parent) {
> +			parent = find_fe_altaddr(fe->end_off);
> +			assert(!parent); /* Should be entirely contained */
> +			continue;
> +		}
> +
> +		size = fe->end_off - fe->start_off;
> +		nested_off = fe->start_off - parent->alt_start_off;
> +
> +		dbg_printf("flattening child fixup entry [%lx-%lx]->[%lx-%lx] to parent [%lx-%lx]->[%lx-%lx] new child [%lx-%lx]->[%lx-%lx]\n", fe->alt_start_off, fe->alt_end_off, fe->start_off, fe->end_off, parent->alt_start_off, parent->alt_end_off, parent->start_off, parent->end_off, fe->alt_start_off, fe->alt_end_off, parent->start_off + nested_off, parent->start_off + nested_off + size);
> +
> +		fe->start_off = parent->start_off + nested_off;
> +		fe->end_off = fe->start_off + size;
> +	}
> +
> +}
> +
> +static unsigned int nr_ips = 0;
> +static struct insn_patch *ips = NULL;
> +
> +static void create_branch_patch(struct relocation *relocation, struct fixup_entry *fe)
> +{
> +	struct insn_patch *ip;
> +	uint64_t addr = relocation->offset;
> +	uint64_t dst_addr;
> +	uint64_t scn_delta;
> +	uint64_t offset;
> +	uint32_t insn;
> +	uint32_t *i;
> +
> +	assert(addr >= ftr_alt->shdr.sh_addr &&
> +		addr < ftr_alt->shdr.sh_addr + ftr_alt->shdr.sh_size);
> +
> +	scn_delta = addr - ftr_alt->shdr.sh_addr;
> +
> +	assert(scn_delta < ftr_alt->data->d_size);
> +
> +	i = ftr_alt->data->d_buf + scn_delta;
> +
> +	insn = f32_to_cpu(*i);
> +
> +	offset = ftr_alt->shdr.sh_offset + scn_delta;
> +	dst_addr = addr - fe->alt_start_off + fe->start_off;
> +
> +	if (set_branch_target(&insn, dst_addr, relocation->target)) {
> +		fprintf(stderr, "ftr_alt branch target out of range or not a branch. address=%llx\n", (unsigned long long)addr);
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	if (insn == *i) /* Nothing to do */
> +		return;
> +
> +	ips = realloc(ips, (nr_ips + 1) * sizeof(struct insn_patch));
> +	ip = &ips[nr_ips];
> +	nr_ips++;
> +
> +	ip->insn = insn;
> +	ip->offset = offset;
> +
> +	dbg_printf("update branch insn (%x->%x)\n", *i, ip->insn);
> +}
> +
> +static int process_alt_data(struct section *section, void *arg)
> +{
> +	if (strcmp(section->name, ".__ftr_alternates.text") != 0)
> +		return 0;
> +
> +	dbg_printf("section %-4.4jd %s\n", (uintmax_t)elf_ndxscn(section->scn), section->name);
> +	assert(section->shdr.sh_type == SHT_PROGBITS);
> +
> +	ftr_alt = section;
> +
> +	return 0;
> +}
> +
> +static int process_fixup_entries(struct section *section, void *arg)
> +{
> +	Elf_Data *data;
> +	unsigned int nr, i;
> +
> +	if (strstr(section->name, "_ftr_fixup") == 0)
> +		return 0;
> +
> +	if (section->shdr.sh_type != SHT_PROGBITS)
> +		return 0;
> +
> +	dbg_printf("section %-4.4jd %s\n", (uintmax_t)elf_ndxscn(section->scn), section->name);
> +
> +	data = section->data;
> +	assert(data);
> +	assert(data->d_size > 0);
> +
> +	if (is_64bit(elf)) {
> +		assert(data->d_size % sizeof(struct fixup_entry_64) == 0);
> +		nr = data->d_size / sizeof(struct fixup_entry_64);
> +	} else {
> +		assert(data->d_size % sizeof(struct fixup_entry_32) == 0);
> +		nr = data->d_size / sizeof(struct fixup_entry_32);
> +	}
> +
> +	for (i = 0; i < nr; i++) {
> +		struct fixup_entry *dst;
> +		unsigned long idx;
> +		unsigned long long off;
> +
> +		if (is_64bit(elf)) {
> +			struct fixup_entry_64 *src;
> +
> +			idx = i * sizeof(struct fixup_entry_64);
> +
> +			off = section->shdr.sh_addr + data->d_off + idx;
> +			src = data->d_buf + idx;
> +
> +			if (src->alt_start_off == src->alt_end_off)
> +				continue;
> +
> +			fes = realloc(fes, (nr_fes + 1) * sizeof(struct fixup_entry));
> +			dst = &fes[nr_fes];
> +			nr_fes++;
> +
> +			dst->mask = f64_to_cpu(src->mask);
> +			dst->value = f64_to_cpu(src->value);
> +			dst->start_off = f64_to_cpu(src->start_off) + off;
> +			dst->end_off = f64_to_cpu(src->end_off) + off;
> +			dst->alt_start_off = f64_to_cpu(src->alt_start_off) + off;
> +			dst->alt_end_off = f64_to_cpu(src->alt_end_off) + off;
> +
> +		} else {
> +			struct fixup_entry_32 *src;
> +
> +			idx = i * sizeof(struct fixup_entry_32);
> +
> +			off = section->shdr.sh_addr + data->d_off + idx;
> +			src = data->d_buf + idx;
> +
> +			if (src->alt_start_off == src->alt_end_off)
> +				continue;
> +
> +			fes = realloc(fes, (nr_fes + 1) * sizeof(struct fixup_entry));
> +			dst = &fes[nr_fes];
> +			nr_fes++;
> +
> +			dst->mask = f32_to_cpu(src->mask);
> +			dst->value = f32_to_cpu(src->value);
> +			dst->start_off = f32_to_cpu(src->start_off) + off;
> +			dst->end_off = f32_to_cpu(src->end_off) + off;
> +			dst->alt_start_off = f32_to_cpu(src->alt_start_off) + off;
> +			dst->alt_end_off = f32_to_cpu(src->alt_end_off) + off;
> +
> +		}
> +		if (dst->alt_start_off < fe_alt_start)
> +			fe_alt_start = dst->alt_start_off;
> +		if (dst->alt_end_off > fe_alt_end)
> +			fe_alt_end = dst->alt_end_off;
> +
> +		dbg_printf("%llx fixup entry %llx:%llx (%llx-%llx) <- (%llx-%llx)\n", off,
> +			(unsigned long long)dst->mask, (unsigned long long)dst->value,
> +			(unsigned long long)dst->start_off, (unsigned long long)dst->end_off,
> +			(unsigned long long)dst->alt_start_off, (unsigned long long)dst->alt_end_off);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Check no exceptions are in "alt" sections. We don't relocate them as
> + * yet.
> + */
> +static int process_exception_entries(struct section *section, void *arg)
> +{
> +	Elf_Data *data;
> +	unsigned int nr, i;
> +
> +	if (strcmp(section->name, "__ex_table") != 0)
> +		return 0;
> +
> +	if (section->shdr.sh_type != SHT_PROGBITS)
> +		return 0;
> +
> +	dbg_printf("section %-4.4jd %s\n", (uintmax_t)elf_ndxscn(section->scn), section->name);
> +
> +	data = section->data;
> +	assert(data);
> +	assert(data->d_size > 0);
> +
> +	if (is_64bit(elf)) {
> +		assert(data->d_size % sizeof(struct exception_entry_64) == 0);
> +		nr = data->d_size / sizeof(struct exception_entry_64);
> +	} else {
> +		assert(data->d_size % sizeof(struct exception_entry_32) == 0);
> +		nr = data->d_size / sizeof(struct exception_entry_32);
> +	}
> +
> +	for (i = 0; i < nr; i++) {
> +		unsigned long idx;
> +		uint64_t exaddr;
> +		unsigned long long off;
> +
> +		if (is_64bit(elf)) {
> +			struct exception_entry_64 *ex;
> +
> +			idx = i * sizeof(struct exception_entry_64);
> +
> +			off = section->shdr.sh_addr + data->d_off + idx;
> +
> +			ex = data->d_buf + idx;
> +
> +			exaddr = f32_to_cpu(ex->insn) + off;
> +
> +		} else {
> +			struct exception_entry_32 *ex;
> +
> +			idx = i * sizeof(struct exception_entry_32);
> +
> +			ex = data->d_buf + idx;
> +
> +			exaddr = f32_to_cpu(ex->insn);
> +		}
> +
> +		dbg_printf("exception addr:%llx\n", exaddr);
> +
> +		if (exaddr < fe_alt_start)
> +			continue;
> +		if (exaddr >= fe_alt_end)
> +			continue;
> +
> +		/*
> +		 * This would be the place to create exception address patches
> +		 * if we want to support that feature
> +		 */
> +		fprintf(stderr, "ftr_alt code contains an exception entry, which is not allowed. address=%llx\n", (unsigned long long)exaddr);
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Check no exceptions are in "alt" sections. We don't relocate them as
> + * yet.
> + */
> +static int process_bug_entries(struct section *section, void *arg)
> +{
> +	Elf_Data *data;
> +	unsigned int nr, i;
> +
> +	if (strcmp(section->name, "__bug_table") != 0)
> +		return 0;
> +
> +	if (section->shdr.sh_type != SHT_PROGBITS)
> +		return 0;
> +
> +	dbg_printf("section %-4.4jd %s\n", (uintmax_t)elf_ndxscn(section->scn), section->name);
> +
> +	data = section->data;
> +	assert(data);
> +	assert(data->d_size > 0);
> +
> +	if (is_64bit(elf)) {
> +		assert(data->d_size % sizeof(struct bug_entry_64) == 0);
> +		nr = data->d_size / sizeof(struct bug_entry_64);
> +	} else {
> +		assert(data->d_size % sizeof(struct bug_entry_32) == 0);
> +		nr = data->d_size / sizeof(struct bug_entry_32);
> +	}
> +
> +	for (i = 0; i < nr; i++) {
> +		unsigned long idx;
> +		uint64_t bugaddr;
> +
> +		if (is_64bit(elf)) {
> +			struct bug_entry_64 *bug;
> +
> +			idx = i * sizeof(struct bug_entry_64);
> +
> +			bug = data->d_buf + idx;
> +
> +			bugaddr = f64_to_cpu(bug->bug_addr);
> +
> +		} else {
> +			struct bug_entry_32 *bug;
> +
> +			idx = i * sizeof(struct bug_entry_32);
> +
> +			bug = data->d_buf + idx;
> +
> +			bugaddr = f32_to_cpu(bug->bug_addr);
> +		}
> +
> +		dbg_printf("bug addr:%llx\n", bugaddr);
> +
> +		if (bugaddr < fe_alt_start)
> +			continue;
> +		if (bugaddr >= fe_alt_end)
> +			continue;
> +
> +		/*
> +		 * This would be the place to create bug address patches if
> +		 * we want to support that feature
> +		 */
> +		fprintf(stderr, "ftr_alt code contains a BUG entry, which is not allowed. address=%llx\n", (unsigned long long)bugaddr);
> +		/* Could print file, line here for verbose case */
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	return 0;
> +}
> +
> +
> +static int process_alt_relocations(struct section *section, void *arg)
> +{
> +	struct relocation *relocation;
> +	size_t n;
> +
> +	if (strcmp(section->name, ".rela.__ftr_alternates.text") != 0)
> +		return 0;
> +
> +	assert(section->shdr.sh_type == SHT_RELA);
> +
> +	dbg_printf("section %-4.4jd %s\n", (uintmax_t)elf_ndxscn(section->scn), section->name);
> +
> +	n = 0;
> +	while ((relocation = elf_sections_get_reloc(elf, section, n)) != NULL) {
> +		struct fixup_entry *fe;
> +
> +		n++;
> +
> +		dbg_printf("%llx %s %s %llx + %llx\n",
> +			(unsigned long long)relocation->offset,
> +			relocation->type_name,
> +			relocation->symbol->_name,
> +			(unsigned long long)relocation->symbol->sym.st_value,
> +			(unsigned long long)relocation->rela.r_addend);
> +
> +		fe = find_fe_altaddr(relocation->offset);
> +		if (fe) {
> +			dbg_printf("reloc has fe %llx:%llx (%llx-%llx) <- (%llx-%llx)\n",
> +				(unsigned long long)fe->mask,
> +				(unsigned long long)fe->value,
> +				(unsigned long long)fe->start_off,
> +				(unsigned long long)fe->end_off,
> +				(unsigned long long)fe->alt_start_off,
> +				(unsigned long long)fe->alt_end_off);
> +
> +			if (relocation->target >= fe->alt_start_off &&
> +				relocation->target < fe->alt_end_off) {
> +				dbg_printf("  reloc within patch code\n");
> +				continue;
> +			}
> +
> +			/*
> +			 * We really should check for all branches either side
> +			 * of fixup_entry from outside (including within
> +			 * different fixup code). It's almost guaranteed to go
> +			 * badly. Not just relocations, but branches too,
> +			 * because nearby branches might get resolved without
> +			 * a relocation.
> +			 */
> +			if (relocation->target >= ftr_alt->shdr.sh_addr &&
> +				relocation->target < ftr_alt->shdr.sh_addr +
> +						ftr_alt->shdr.sh_size) {
> +				fprintf(stderr, "ftr_alt branch target is another ftr_alt region, which is not allowed. address=%llx\n", (unsigned long long)relocation->offset);
> +				exit(EXIT_FAILURE);
> +			}
> +
> +			/*
> +			 * Resolved module symbols should work. Unresolved
> +			 * ones would need their relocations fixed in the
> +			 * same manner as instructions are fixed.
> +			 */
> +			if (!is_kernel) {
> +				fprintf(stderr, "module code with alt feature relocations is currently not supported\n");
> +				exit(EXIT_FAILURE);
> +			}
> +
> +			create_branch_patch(relocation, fe);
> +		} else {
> +			dbg_printf("  reloc has no fe\n");
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +
> +int main(int argc, char *argv[])
> +{
> +	int fd;
> +	int err;
> +	unsigned int i;
> +	struct stat stat;
> +	void *mem;
> +	int opt;
> +
> +	if (argc != 2)
> +
> +	while ((opt = getopt(argc, argv, "k")) != -1) {
> +		switch (opt) {
> +		case 'k':
> +			is_kernel = 1;
> +			break;
> +		default:
> +			exit(EXIT_FAILURE);
> +		}
> +	}
> +
> +	if (!strcmp(argv[optind], "vmlinux"))
> +		is_kernel = 1;
> +
> +	if (optind >= argc || optind + 1 < argc)
> +		exit(EXIT_FAILURE);
> +
> +	fd = open(argv[optind], O_RDONLY, 0);
> +	if (fd == -1) {
> +		fprintf(stderr, "open %s failed: %s\n", argv[1], strerror(errno));
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	elf = elf_sections_init(fd);
> +
> +	err = elf_sections_processor(elf, process_alt_data, NULL);
> +	assert(!err);
> +
> +	/* Gather and massage the fixup entries */
> +	err = elf_sections_processor(elf, process_fixup_entries, NULL);
> +	assert(!err);
> +
> +	check_and_flatten_fixup_entries();
> +
> +	/* We don't handle module relocations for these symbols as yet */
> +	if (is_kernel) {
> +		/* Sanity checking */
> +		err = elf_sections_processor(elf, process_exception_entries, NULL);
> +		assert(!err);
> +
> +		err = elf_sections_processor(elf, process_bug_entries, NULL);
> +		assert(!err);
> +	}
> +
> +	/* Check the relocations and create necessary instruction patches */
> +	err = elf_sections_processor(elf, process_alt_relocations, NULL);
> +	assert(!err);
> +
> +	/* Done with analysis phase */
> +
> +	elf_sections_exit(elf);
> +
> +	if (close(fd) == -1) {
> +		fprintf(stderr, "close %s failed: %s\n", argv[1], strerror(errno));
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	if (!nr_ips) {
> +		dbg_printf("Nothing to do.\n");
> +		exit(EXIT_SUCCESS);
> +	}
> +
> +	/* Now apply the instruction patches by writing to the file */
> +
> +	dbg_printf("%u instructions to patch.\n", nr_ips);
> +
> +	fd = open(argv[1], O_RDWR, 0);
> +	if (fd == -1) {
> +		fprintf(stderr, "open %s failed: %s\n", argv[1], strerror(errno));
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	if (fstat(fd, &stat) == -1) {
> +		perror("stat");
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	mem = mmap(0, stat.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
> +	if (mem == MAP_FAILED) {
> +		perror("mmap");
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	for (i = 0; i < nr_ips; i++) {
> +		struct insn_patch *ip = &ips[i];
> +
> +		assert(ip->offset < stat.st_size);
> +		*(uint32_t *)(mem + ip->offset) = ip->insn;
> +	}
> +
> +	if (munmap(mem, stat.st_size) == -1) {
> +		perror("mmap");
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	if (close(fd) == -1) {
> +		fprintf(stderr, "close %s failed: %s\n", argv[1], strerror(errno));
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	exit(EXIT_SUCCESS);
> +}




More information about the Linuxppc-dev mailing list