[PATCH 6/6] ARM: Add relocatable binary support

Sascha Hauer s.hauer at pengutronix.de
Mon Mar 4 15:03:09 EST 2013


For making the same binary executable on different SoCs which have
different DRAM addresses we have to be independent of the compile
time link address.

This patch adds relocatable binary support for the ARM architecture.
With this two new functions are available. relocate_to_current_adr
will fixup the binary to continue executing from the current position.
relocate_to_adr will copy the binary to a given address, fixup the
binary and continue executing from there.

For the PBL and the real image relocatable support can be enabled
independently. This is done to (hopefully) better cope with setups
where the PBL runs from SRAM or ROM and the real binary does not.

Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
 arch/arm/Kconfig                   |  9 +++++
 arch/arm/Makefile                  |  4 +++
 arch/arm/cpu/Makefile              |  3 ++
 arch/arm/cpu/common.c              | 68 ++++++++++++++++++++++++++++++++++++++
 arch/arm/cpu/setupc.S              | 59 +++++++++++++++++++++++++++++++++
 arch/arm/cpu/start-pbl.c           | 18 +++++++---
 arch/arm/cpu/start.c               |  5 +++
 arch/arm/include/asm/barebox-arm.h | 26 +++++++++++++++
 arch/arm/lib/barebox.lds.S         | 17 ++++++++++
 arch/arm/pbl/Makefile              |  3 ++
 arch/arm/pbl/zbarebox.lds.S        | 16 +++++++++
 common/Kconfig                     |  2 +-
 pbl/Kconfig                        | 10 ++++++
 13 files changed, 235 insertions(+), 5 deletions(-)
 create mode 100644 arch/arm/cpu/common.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 7ac134e..efe0773 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -12,6 +12,15 @@ config ARM_LINUX
 	default y
 	depends on CMD_BOOTZ || CMD_BOOTU || CMD_BOOTM
 
+config RELOCATABLE
+	prompt "relocatable binary"
+	bool
+	help
+	  Generate a binary which can relocate itself during startup to run
+	  on different addresses. This is useful if your memory layout is not
+	  known during compile time. Selecting this will result in a slightly
+	  bigger image.
+
 config HAVE_MACH_ARM_HEAD
 	bool
 
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index b98d6b8..5125b87 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -181,6 +181,10 @@ CPPFLAGS += -fdata-sections -ffunction-sections
 LDFLAGS_barebox += -static --gc-sections
 endif
 
+ifdef CONFIG_RELOCATABLE
+LDFLAGS_barebox += -pie
+endif
+
 ifdef CONFIG_IMAGE_COMPRESSION
 KBUILD_BINARY := arch/arm/pbl/zbarebox.bin
 KBUILD_TARGET := zbarebox.bin
diff --git a/arch/arm/cpu/Makefile b/arch/arm/cpu/Makefile
index 44410ee..5935e1c 100644
--- a/arch/arm/cpu/Makefile
+++ b/arch/arm/cpu/Makefile
@@ -21,3 +21,6 @@ pbl-$(CONFIG_CPU_32v7) += cache-armv7.o
 obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o
 
 pbl-y += start-pbl.o setupc.o
+
+obj-y += common.o
+pbl-y += common.o
diff --git a/arch/arm/cpu/common.c b/arch/arm/cpu/common.c
new file mode 100644
index 0000000..856ddf2
--- /dev/null
+++ b/arch/arm/cpu/common.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer at pengutronix.de>, Pengutronix
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program 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.
+ *
+ */
+
+#include <common.h>
+#include <init.h>
+#include <sizes.h>
+#include <asm/barebox-arm.h>
+#include <asm/barebox-arm-head.h>
+#include <asm-generic/memory_layout.h>
+#include <asm/sections.h>
+#include <asm/pgtable.h>
+#include <asm/cache.h>
+
+/*
+ * relocate binary to the currently running address
+ */
+void relocate_to_current_adr(void)
+{
+	uint32_t offset;
+	uint32_t *dstart, *dend, *dynsym;
+	uint32_t *dynend;
+
+	/* Get offset between linked address and runtime address */
+	offset = get_runtime_offset();
+
+	dstart = (void *)(ld_var(__rel_dyn_start) - offset);
+	dend = (void *)(ld_var(__rel_dyn_end) - offset);
+
+	dynsym = (void *)(ld_var(__dynsym_start) - offset);
+	dynend = (void *)(ld_var(__dynsym_end) - offset);
+
+	while (dstart < dend) {
+		uint32_t *fixup = (uint32_t *)(*dstart - offset);
+		uint32_t type = *(dstart + 1);
+
+		if ((type & 0xff) == 0x17) {
+			*fixup = *fixup - offset;
+		} else {
+			int index = type >> 8;
+			uint32_t r = dynsym[index * 4 + 1];
+
+			*fixup = *fixup + r - offset;
+		}
+
+		*dstart -= offset;
+		dstart += 2;
+	}
+
+	while (dynsym < dynend)
+		*dynsym++ = 0;
+
+	arm_early_mmu_cache_flush();
+	flush_icache();
+}
diff --git a/arch/arm/cpu/setupc.S b/arch/arm/cpu/setupc.S
index d0de87d..7fd5d01 100644
--- a/arch/arm/cpu/setupc.S
+++ b/arch/arm/cpu/setupc.S
@@ -1,4 +1,5 @@
 #include <linux/linkage.h>
+#include <asm/sections.h>
 
 .section .text.setupc
 
@@ -35,3 +36,61 @@ ENTRY(setup_c)
 	pop	{r4, r5}
 	mov	pc, lr
 ENDPROC(setup_c)
+
+#ifdef CONFIG_RELOCATABLE
+/*
+ * void relocate_to_adr(unsigned long targetadr)
+ *
+ * Copy binary to targetadr, relocate code, clear bss and continue
+ * executing at new address.
+ */
+.section .text.relocate_to_adr
+ENTRY(relocate_to_adr)
+					/* r0: target address */
+	push	{r3, r4, r5, r6, r7, r8}
+	mov	r7, lr
+
+	mov	r6, r0
+
+	bl	get_runtime_offset
+
+	mov	r5, r0
+
+	ld_var	_text, r0, r4
+	mov	r8, r0
+
+	sub	r1, r0, r5		/* r1: from address */
+
+	cmp	r1, r6			/* already at correct address? */
+	beq	1f			/* yes, skip copy to new address */
+
+	ld_var	__bss_start, r2, r4
+
+	sub	r2, r2, r0		/* r2: size */
+	mov	r0, r6			/* r0: target */
+
+	add	r7, r7, r0		/* adjust return address */
+	sub	r7, r7, r1		/* lr += offset */
+
+	bl	memcpy			/* copy binary */
+
+#ifdef CONFIG_MMU
+	bl	arm_early_mmu_cache_flush
+#endif
+	mov	r0,#0
+	mcr	p15, 0, r0, c7, c5, 0	/* flush icache */
+
+	ldr	r0,=1f
+	sub	r0, r0, r8
+	add	r0, r0, r6
+	mov	pc, r0			/* jump to relocated address */
+1:
+	bl	relocate_to_current_adr	/* relocate binary */
+
+	mov	lr, r7
+
+	pop	{r3, r4, r5, r6, r7, r8}
+	mov	pc, lr
+
+ENDPROC(relocate_to_adr)
+#endif
diff --git a/arch/arm/cpu/start-pbl.c b/arch/arm/cpu/start-pbl.c
index 91bc8fe..6f03c4a 100644
--- a/arch/arm/cpu/start-pbl.c
+++ b/arch/arm/cpu/start-pbl.c
@@ -55,9 +55,13 @@ static noinline __noreturn void __barebox_arm_entry(uint32_t membase,
 	uint32_t pg_start, pg_end, pg_len;
 	void __noreturn (*barebox)(uint32_t, uint32_t, uint32_t);
 	uint32_t endmem = membase + memsize;
+	unsigned long barebox_base;
 
 	endmem -= STACK_SIZE; /* stack */
 
+	if (IS_ENABLED(CONFIG_PBL_RELOCATABLE))
+		relocate_to_current_adr();
+
 	/* Get offset between linked address and runtime address */
 	offset = get_runtime_offset();
 
@@ -65,8 +69,13 @@ static noinline __noreturn void __barebox_arm_entry(uint32_t membase,
 	pg_end = (uint32_t)&input_data_end - offset;
 	pg_len = pg_end - pg_start;
 
+	if (IS_ENABLED(CONFIG_RELOCATABLE))
+		barebox_base = arm_barebox_image_place(membase + memsize);
+	else
+		barebox_base = TEXT_BASE;
+
 	if (offset && (IS_ENABLED(CONFIG_PBL_FORCE_PIGGYDATA_COPY) ||
-				region_overlap(pg_start, pg_len, TEXT_BASE, pg_len * 4))) {
+				region_overlap(pg_start, pg_len, barebox_base, pg_len * 4))) {
 		/*
 		 * copy piggydata binary to its link address
 		 */
@@ -86,14 +95,15 @@ static noinline __noreturn void __barebox_arm_entry(uint32_t membase,
 	free_mem_ptr = endmem;
 	free_mem_end_ptr = free_mem_ptr + SZ_128K;
 
-	pbl_barebox_uncompress((void*)TEXT_BASE, (void *)pg_start, pg_len);
+	pbl_barebox_uncompress((void*)barebox_base, (void *)pg_start, pg_len);
 
+	arm_early_mmu_cache_flush();
 	flush_icache();
 
 	if (IS_ENABLED(CONFIG_THUMB2_BAREBOX))
-		barebox = (void *)(TEXT_BASE + 1);
+		barebox = (void *)(barebox_base + 1);
 	else
-		barebox = (void *)TEXT_BASE;
+		barebox = (void *)barebox_base;
 
 	barebox(membase, memsize, boarddata);
 }
diff --git a/arch/arm/cpu/start.c b/arch/arm/cpu/start.c
index cd34d9c..7c2bcd0 100644
--- a/arch/arm/cpu/start.c
+++ b/arch/arm/cpu/start.c
@@ -37,6 +37,11 @@ static noinline __noreturn void __start(uint32_t membase, uint32_t memsize,
 	unsigned long endmem = membase + memsize;
 	unsigned long malloc_start, malloc_end;
 
+	if (IS_ENABLED(CONFIG_RELOCATABLE)) {
+		unsigned long barebox_base = arm_barebox_image_place(endmem);
+		relocate_to_adr(barebox_base);
+	}
+
 	setup_c();
 
 	arm_stack_top = endmem;
diff --git a/arch/arm/include/asm/barebox-arm.h b/arch/arm/include/asm/barebox-arm.h
index f051fe3..76e31a0 100644
--- a/arch/arm/include/asm/barebox-arm.h
+++ b/arch/arm/include/asm/barebox-arm.h
@@ -26,6 +26,7 @@
 #define _BAREBOX_ARM_H_
 
 #include <sizes.h>
+#include <asm-generic/memory_layout.h>
 
 /* cpu/.../cpu.c */
 int	cleanup_before_linux(void);
@@ -40,6 +41,8 @@ void board_init_lowlevel(void);
 uint32_t get_runtime_offset(void);
 
 void setup_c(void);
+void relocate_to_current_adr(void);
+void relocate_to_adr(unsigned long target);
 void __noreturn barebox_arm_entry(uint32_t membase, uint32_t memsize, uint32_t boarddata);
 
 #ifdef CONFIG_RELOCATABLE
@@ -50,4 +53,27 @@ static inline void arm_fixup_vectors(void)
 }
 #endif
 
+/*
+ * For relocatable binaries find a suitable start address for the
+ * relocated binary. Beginning at the memory end substract the reserved
+ * space and round down a bit at the end. This is used by the pbl to
+ * extract the image to a suitable place so that the uncompressed image
+ * does not have to copy itself to another place. Also it's used by
+ * the uncompressed image to relocate itself to the same place.
+ */
+static inline unsigned long arm_barebox_image_place(unsigned long endmem)
+{
+	endmem -= STACK_SIZE;
+	endmem -= SZ_32K; /* ttb */
+	endmem -= SZ_128K; /* early malloc */
+	endmem -= SZ_1M; /* place for barebox image */
+
+	/*
+	 * round down to make translating the objdump easier
+	 */
+	endmem &= ~(SZ_1M - 1);
+
+	return endmem;
+}
+
 #endif	/* _BAREBOX_ARM_H_ */
diff --git a/arch/arm/lib/barebox.lds.S b/arch/arm/lib/barebox.lds.S
index e5aee8c..abdd69e 100644
--- a/arch/arm/lib/barebox.lds.S
+++ b/arch/arm/lib/barebox.lds.S
@@ -25,7 +25,11 @@ OUTPUT_ARCH(arm)
 ENTRY(start)
 SECTIONS
 {
+#ifdef CONFIG_RELOCATABLE
+	. = 0x0;
+#else
 	. = TEXT_BASE;
+#endif
 
 #ifndef CONFIG_PBL_IMAGE
 	PRE_IMAGE
@@ -88,7 +92,20 @@ SECTIONS
 	__usymtab : { BAREBOX_SYMS }
 	__usymtab_end = .;
 
+	.rel.dyn : {
+		__rel_dyn_start = .;
+		*(.rel*)
+		__rel_dyn_end = .;
+	}
+
+	.dynsym : {
+		__dynsym_start = .;
+		*(.dynsym)
+		__dynsym_end = .;
+	}
+
 	_edata = .;
+
 	. = ALIGN(4);
 	__bss_start = .;
 	.bss : { *(.bss*) }
diff --git a/arch/arm/pbl/Makefile b/arch/arm/pbl/Makefile
index 6eeee73..3f50f77 100644
--- a/arch/arm/pbl/Makefile
+++ b/arch/arm/pbl/Makefile
@@ -23,6 +23,9 @@ $(obj)/zbarebox.S: $(obj)/zbarebox FORCE
 PBL_CPPFLAGS		+= -fdata-sections -ffunction-sections
 LDFLAGS_zbarebox	:= -Map $(obj)/zbarebox.map
 LDFLAGS_zbarebox	+= -static --gc-sections
+ifdef CONFIG_PBL_RELOCATABLE
+LDFLAGS_zbarebox += -pie
+endif
 zbarebox-common := $(barebox-pbl-common) $(obj)/$(piggy_o)
 zbarebox-lds := $(obj)/zbarebox.lds
 
diff --git a/arch/arm/pbl/zbarebox.lds.S b/arch/arm/pbl/zbarebox.lds.S
index 564b3c6..6b23bbe 100644
--- a/arch/arm/pbl/zbarebox.lds.S
+++ b/arch/arm/pbl/zbarebox.lds.S
@@ -29,7 +29,11 @@ OUTPUT_ARCH(arm)
 ENTRY(pbl_start)
 SECTIONS
 {
+#ifdef CONFIG_PBL_RELOCATABLE
+	. = 0x0;
+#else
 	. = TEXT_BASE - SZ_2M;
+#endif
 
 	PRE_IMAGE
 
@@ -58,6 +62,18 @@ SECTIONS
 	. = ALIGN(4);
 	.data : { *(.data*) }
 
+	.rel.dyn : {
+		__rel_dyn_start = .;
+		*(.rel*)
+		__rel_dyn_end = .;
+	}
+
+	.dynsym : {
+		__dynsym_start = .;
+		*(.dynsym)
+		__dynsym_end = .;
+	}
+
 	. = ALIGN(4);
 	__bss_start = .;
 	.bss : { *(.bss*) }
diff --git a/common/Kconfig b/common/Kconfig
index 3a55e01..92a6fdf 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -243,7 +243,7 @@ config KALLSYMS
 	  This is useful to print a nice backtrace when an exception occurs.
 
 config RELOCATABLE
-	depends on PPC
+	depends on PPC || ARM
 	bool "generate relocatable barebox binary"
 	help
 	  A non relocatable barebox binary will run at it's compiled in
diff --git a/pbl/Kconfig b/pbl/Kconfig
index 39752cc..5c7f62e 100644
--- a/pbl/Kconfig
+++ b/pbl/Kconfig
@@ -18,6 +18,16 @@ config PBL_FORCE_PIGGYDATA_COPY
 
 if PBL_IMAGE
 
+config PBL_RELOCATABLE
+	depends on ARM
+	bool "relocatable pbl image"
+	help
+	  Generate a pbl binary which can relocate itself during startup to run
+	  on different addresses. This is useful if your memory layout is not
+	  known during compile time.
+	  This option only inflluences the PBL image. See RELOCATABLE to also make
+	  the real image relocatable.
+
 config IMAGE_COMPRESSION
 	bool
 	depends on HAVE_IMAGE_COMPRESSION
-- 
1.8.2.rc2




More information about the barebox mailing list