Skip to contentSkip to conversation

powerpc: build-time fixup alternate feature relative addresses

RFC PATCH New Nicholas Piggin 1 participant 1 message Dec 13, 2016 PowerPC

lore
Share & tools
lore

Nicholas Piggin

author reviewer
#0 lore raw

RFC PATCH 1/1 powerpc: build-time fixup alternate feature relative addresses

Hi,

I'd like to revisit this patch to do the fixup of relative addresses for "else" part of alternate instruction patching at compile-time rather than runtime. Without this I pretty quickly run into "ftr_alt relocation truncated to fit" errors when trying to use feature patching for anything non-trivial.

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.

The "else" part of the feature patching system currently requires the linker generate correct relative addresses for the linked location. The the kernel instruction patching code copies instructions delta bytes to runtime location, and applies negative delta to relative branches as it does.

This is nice and simple, however the requirement to generate valid relative addresses at the linked location is constraining:

- Sometimes it can't be done. Particularly in exception handlers that have fixed locations, and when both if and else parts of the fixup have relative references to the same code. This has caused headaches in the exception code, where things have to be juggled around until they work.

- It requires patch code to be placed near its destination location, despite never being executed in-place. This compounds real estate shortage in exception code, and puts this unused code near to (likely) performance critical code, which is not ideal.

- Alternate patch code can't be discarded after alt fixups are completed. Not a significant problem in practice, but it's tidy to have it, and the feature patching stuff is likely to see increased usage in future.

With this change, the relative branch adjustment is done at build, so patch code branches are correct for their destination location. Kernel runtime patching just moves the instructions.

__ftr_alt sections get moved out of the main text section and into an init region, which stops them cluttering the runtime text and allows them to be discarded at boot time.

Relative branches in the alt sections still need the linker to be happy otherwise it will fail the link. So the alt sections are made allocatable and executable, so branch stub trampolines for these branches can be added for them. The relocation and patching system simply ignores these branch stubs.

Static relocations have to be included in the vmlinux file after linking, so --emit-relocs is added to the build. Newer versoins of binutils can strip these off after they are used too. With older binutils, the resulting vmlinux becomes significantly larger due to the relocation data.

---
 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 | 715 +++++++++++++++++++++++++++++
 13 files changed, 1250 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
arch/powerpc/Makefile +7 -1
index 6a9f319f8ad0..b944b2112d45 100644
@@ -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
@@ -417,6 +422,7 @@ checkbin:
 		false ; \
 	fi
 
+archscripts: scripts_basic
+	$(Q)$(MAKE) $(build)=arch/powerpc/tools
 
 CLEAN_FILES += $(TOUT)
-
arch/powerpc/Makefile.postlink +21 -3
index 4c1eafd0d52d..361484c3bb57 100644
@@ -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
arch/powerpc/include/asm/feature-fixups.h +4 -1
index ddf54f5bbdd1..52bbd0eeaf3c 100644
@@ -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:
 
arch/powerpc/kernel/vmlinux.lds.S +5 -3
index 6bd42d39e737..55a54b0c792a 100644
@@ -85,8 +85,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
@@ -110,7 +109,6 @@ SECTIONS
 		*(.got2)
 		__got2_end = .;
 #endif /* CONFIG_PPC32 */
-
 	} :kernel
 
 	. = ALIGN(PAGE_SIZE);
@@ -139,6 +137,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
 	 */
arch/powerpc/lib/feature-fixups.c +6 -13
index 043415f0bdb1..53531c425c0a 100644
@@ -46,20 +46,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);
 
arch/powerpc/tools/Makefile new file +3 -0
new file mode 100644
index 000000000000..38dbf0427080
@@ -0,0 +1,3 @@
+always		:= $(hostprogs-y) $(hostprogs-m)
+
+subdir-y	+= relocs
arch/powerpc/tools/relocs/.gitignore new file +1 -0
new file mode 100644
index 000000000000..5cf4382690d2
@@ -0,0 +1 @@
+process_relocs
arch/powerpc/tools/relocs/Makefile new file +12 -0
new file mode 100644
index 000000000000..bea662d7c1ba
@@ -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
arch/powerpc/tools/relocs/code-patching.c new file +82 -0

82 lines not on this page · raw message

arch/powerpc/tools/relocs/code-patching.h new file +7 -0
new file mode 100644
index 000000000000..1d3cbbe99102
@@ -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
arch/powerpc/tools/relocs/elf_sections.c new file +337 -0

337 lines not on this page · raw message

arch/powerpc/tools/relocs/elf_sections.h new file +50 -0

50 lines not on this page · raw message

arch/powerpc/tools/relocs/process_relocs.c new file +715 -0

715 lines not on this page · raw message

Signature · 1 line
2.11.0