[U-Boot] [RFC] [PATCH V2] arm: arm926ejs: use ELF relocations

Albert Aribaud
2010-10-05T06:31:27Z

HISTORY:

V1 Initial patch
V2 Rebased on latest mainline master

This patch is *not* a submission for master!

It is a proof of concept of ELF relocations for ARM, hastily done
in a day's work time for people on the list to try and to comment.
All comments are welcome, as several suggestions have been made
today on the list that I did not have time to incorporate, such as
rewriting the elf table fixup code in C.

The basic idea of this patch is to replace the -fPIC compile-time
option with the -pie link-time option. This removes the GOT but adds
the .rel.dyn and .dynsym tables, which together allow fixing up code
more completely than with -fPIC and the GOT; for instance, all pointers
inside structures are fixed up with -pie, whereas they are not with GOT.

Note that references to linker-file-generated symbols were also made
relative to _start rather than absolute. This is not needed as such,
but it will be useful when optimizing the relocation tables. Actually
I should have separated this from the ELF relocation support per se.

The edminiv2.h config file is there for reference only; this is the
one I used for tests. Latest numbers are:

With GOT relocs:

text data bss dec hex filename .bin size
141376 4388 16640 162404 27a64 ./u-boot 145764

With ELF relocs:

text data bss dec hex filename .bin size
149677 3727 16636 170040 29838 ./u-boot 153408

The size difference is essentially due to .rel.dyn not being optimal.
As discussed, an added build step should allow reducing it by half and
making ELF sizes roughly similar to GOT ones.

Tests and comments not only welcome but also heartily called for.

Amicalement,
Albert.

---
arch/arm/config.mk | 3 +-
arch/arm/cpu/arm926ejs/start.S | 172 +++++++++++++++++++++----------------
arch/arm/cpu/arm926ejs/u-boot.lds | 9 ++
arch/arm/include/asm/u-boot-arm.h | 14 ++--
arch/arm/lib/board.c | 8 +-
include/configs/edminiv2.h | 7 ++
6 files changed, 126 insertions(+), 87 deletions(-)

diff --git a/arch/arm/config.mk b/arch/arm/config.mk
index 6923f6d..e9e02da 100644
--- a/arch/arm/config.mk
+++ b/arch/arm/config.mk
@@ -35,7 +35,7 @@ endif

ifndef CONFIG_SYS_ARM_WITHOUT_RELOC
# needed for relocation
-PLATFORM_RELFLAGS += -fPIC
+#PLATFORM_RELFLAGS += -fPIC
endif

ifdef CONFIG_SYS_ARM_WITHOUT_RELOC
@@ -72,3 +72,4 @@ PLATFORM_LIBS += $(OBJTREE)/arch/arm/lib/eabi_compat.o
endif
endif
LDSCRIPT := $(SRCTREE)/$(CPUDIR)/u-boot.lds
+PLATFORM_LDFLAGS += -pie
\ No newline at end of file
diff --git a/arch/arm/cpu/arm926ejs/start.S b/arch/arm/cpu/arm926ejs/start.S
index 16ee972..5a7ae7e 100644
--- a/arch/arm/cpu/arm926ejs/start.S
+++ b/arch/arm/cpu/arm926ejs/start.S
@@ -10,6 +10,7 @@
* Copyright (c) 2002 Gary Jennejohn <garyj@denx.de>
* Copyright (c) 2003 Richard Woodruff <r-woodruff2@ti.com>
* Copyright (c) 2003 Kshitij <kshitij@ti.com>
+ * Copyright (c) 2010 Albert Aribaud <albert.aribaud@free.fr>
*
* See file CREDITS for list of people who contributed to this
* project.
@@ -118,22 +119,19 @@ _fiq:
_TEXT_BASE:
.word TEXT_BASE

-#if defined(CONFIG_SYS_ARM_WITHOUT_RELOC)
-.globl _armboot_start
-_armboot_start:
- .word _start
-#endif
-
/*
* These are defined in the board-specific linker script.
+ * Subtracting _start from them lets the linker put their
+ * relative position in the executable instead of leaving
+ * them null.
*/
-.globl _bss_start
-_bss_start:
- .word __bss_start
+.globl _bss_start_ofs
+_bss_start_ofs:
+ .word __bss_start - _start

-.globl _bss_end
-_bss_end:
- .word _end
+.globl _bss_end_ofs
+_bss_end_ofs:
+ .word _end - _start

#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
@@ -153,29 +151,21 @@ FIQ_STACK_START:
IRQ_STACK_START_IN:
.word 0x0badc0de

-.globl _datarel_start
-_datarel_start:
- .word __datarel_start
-
-.globl _datarelrolocal_start
-_datarelrolocal_start:
- .word __datarelrolocal_start
-
-.globl _datarellocal_start
-_datarellocal_start:
- .word __datarellocal_start
+.globl _datarel_start_ofs
+_datarel_start_ofs:
+ .word __datarel_start - _start

-.globl _datarelro_start
-_datarelro_start:
- .word __datarelro_start
+.globl _datarelrolocal_start_ofs
+_datarelrolocal_start_ofs:
+ .word __datarelrolocal_start - _start

-.globl _got_start
-_got_start:
- .word __got_start
+.globl _datarellocal_start_ofs
+_datarellocal_start_ofs:
+ .word __datarellocal_start - _start

-.globl _got_end
-_got_end:
- .word __got_end
+.globl _datarelro_start_ofs
+_datarelro_start_ofs:
+ .word __datarelro_start - _start

/*
* the actual reset code
@@ -226,9 +216,8 @@ stack_setup:

adr r0, _start
ldr r2, _TEXT_BASE
- ldr r3, _bss_start
- sub r2, r3, r2 /* r2 <- size of armboot */
- add r2, r0, r2 /* r2 <- source end address */
+ ldr r3, _bss_start_ofs
+ add r2, r0, r3 /* r2 <- source end address */
cmp r0, r6
beq clear_bss

@@ -240,36 +229,54 @@ copy_loop:
ble copy_loop

#ifndef CONFIG_PRELOADER
- /* fix got entries */
- ldr r1, _TEXT_BASE /* Text base */
- mov r0, r7 /* reloc addr */
- ldr r2, _got_start /* addr in Flash */
- ldr r3, _got_end /* addr in Flash */
- sub r3, r3, r1
- add r3, r3, r0
- sub r2, r2, r1
- add r2, r2, r0
-
+ /*
+ * fix .rel.dyn relocations
+ */
+ ldr r0, _TEXT_BASE /* r0 <- Text base */
+ sub r9, r7, r0 /* r9 <- relocation offset */
+ ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */
+ add r10, r10, r0 /* r10 <- sym table in FLASH */
+ ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */
+ add r2, r2, r0 /* r2 <- rel dyn start in FLASH */
+ ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */
+ add r3, r3, r0 /* r3 <- rel dyn end in FLASH */
fixloop:
- ldr r4, [r2]
- sub r4, r4, r1
- add r4, r4, r0
- str r4, [r2]
- add r2, r2, #4
+ ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */
+ add r0, r9 /* r0 <- location to fix up in RAM */
+ ldr r1, [r2, #4]
+ and r8, r1, #0xff
+ cmp r8, #23 /* relative fixup? */
+ beq fixrel
+ cmp r8, #2 /* absolute fixup? */
+ beq fixabs
+ /* ignore unknown type of fixup */
+ b fixnext
+fixabs:
+ /* absolute fix: set location to (offset) symbol value */
+ mov r1, r1, LSR #4 /* r1 <- symbol index in .dynsym */
+ add r1, r10, r1 /* r1 <- address of symbol in table */
+ ldr r1, [r1, #4] /* r1 <- symbol value */
+ add r1, r9 /* r1 <- relocated sym addr */
+ b fixnext
+fixrel:
+ /* relative fix: increase location by offset */
+ ldr r1, [r0]
+ add r1, r1, r9
+fixnext:
+ str r1, [r0]
+ add r2, r2, #8 /* each rel.dyn entry is 8 bytes */
cmp r2, r3
- bne fixloop
+ ble fixloop
#endif
#endif /* #ifndef CONFIG_SKIP_RELOCATE_UBOOT */

clear_bss:
#ifndef CONFIG_PRELOADER
- ldr r0, _bss_start
- ldr r1, _bss_end
+ ldr r0, _bss_start_ofs
+ ldr r1, _bss_end_ofs
ldr r3, _TEXT_BASE /* Text base */
mov r4, r7 /* reloc addr */
- sub r0, r0, r3
add r0, r0, r4
- sub r1, r1, r3
add r1, r1, r4
mov r2, #0x00000000 /* clear */

@@ -287,24 +294,33 @@ clbss_l:str r2, [r0] /* clear loop... */
* initialization, now running from RAM.
*/
#ifdef CONFIG_NAND_SPL
- ldr pc, _nand_boot
-
-_nand_boot: .word nand_boot
+ ldr r0, _nand_boot_ofs
+ adr r1, _start
+ add pc, r0, r1
+_nand_boot_ofs
+ : .word nand_boot - _start
#else
- ldr r0, _TEXT_BASE
- ldr r2, _board_init_r
- sub r2, r2, r0
- add r2, r2, r7 /* position from board_init_r in RAM */
+ ldr r0, _board_init_r_ofs
+ adr r1, _start
+ add r0, r0, r1
+ add lr, r0, r9
/* setup parameters for board_init_r */
mov r0, r5 /* gd_t */
mov r1, r7 /* dest_addr */
/* jump to it ... */
- mov lr, r2
mov pc, lr

-_board_init_r: .word board_init_r
+_board_init_r_ofs:
+ .word board_init_r - _start
#endif

+_rel_dyn_start_ofs:
+ .word __rel_dyn_start - _start
+_rel_dyn_end_ofs:
+ .word __rel_dyn_end - _start
+_dynsym_start_ofs:
+ .word __dynsym_start - _start
+
#else /* #if !defined(CONFIG_SYS_ARM_WITHOUT_RELOC) */
/*
* the actual reset code
@@ -333,10 +349,8 @@ relocate: /* relocate U-Boot to RAM */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq stack_setup
- ldr r2, _armboot_start
- ldr r3, _bss_start
- sub r2, r3, r2 /* r2 <- size of armboot */
- add r2, r0, r2 /* r2 <- source end address */
+ ldr r3, _bss_start_ofs /* r3 <- _bss_start - _start */
+ add r2, r0, r3 /* r2 <- source end address */

copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
@@ -360,8 +374,11 @@ stack_setup:
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */

clear_bss:
- ldr r0, _bss_start /* find start of bss segment */
- ldr r1, _bss_end /* stop here */
+ adr r2, _start
+ ldr r0, _bss_start_ofs /* find start of bss segment */
+ add r0, r0, r2
+ ldr r1, _bss_end_ofs /* stop here */
+ add r1, r1, r2
mov r2, #0x00000000 /* clear */

#ifndef CONFIG_PRELOADER
@@ -374,13 +391,16 @@ clbss_l:str r2, [r0] /* clear loop... */
bl red_LED_on
#endif /* CONFIG_PRELOADER */

- ldr pc, _start_armboot
+ ldr r0, _start_armboot_ofs
+ adr r1, _start
+ add r0, r0, r1
+ ldr pc, r0

-_start_armboot:
+_start_armboot_ofs:
#ifdef CONFIG_NAND_SPL
- .word nand_boot
+ .word nand_boot - _start
#else
- .word start_armboot
+ .word start_armboot - _start
#endif /* CONFIG_NAND_SPL */
#endif /* #if !defined(CONFIG_SYS_ARM_WITHOUT_RELOC) */

@@ -469,7 +489,7 @@ cpu_init_crit:
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Save user registers (now in svc mode) r0-r12
#if defined(CONFIG_SYS_ARM_WITHOUT_RELOC)
- ldr r2, _armboot_start
+ adr r2, _start
sub r2, r2, #(CONFIG_STACKSIZE+CONFIG_SYS_MALLOC_LEN)
sub r2, r2, #(CONFIG_SYS_GBL_DATA_SIZE+8) @ set base 2 words into abort stack
#else
@@ -507,7 +527,7 @@ cpu_init_crit:

.macro get_bad_stack
#if defined(CONFIG_SYS_ARM_WITHOUT_RELOC)
- ldr r13, _armboot_start @ setup our mode stack
+ adr r13, _start @ setup our mode stack
sub r13, r13, #(CONFIG_STACKSIZE+CONFIG_SYS_MALLOC_LEN)
sub r13, r13, #(CONFIG_SYS_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack
#else
diff --git a/arch/arm/cpu/arm926ejs/u-boot.lds b/arch/arm/cpu/arm926ejs/u-boot.lds
index 02eb8ca..f07a54a 100644
--- a/arch/arm/cpu/arm926ejs/u-boot.lds
+++ b/arch/arm/cpu/arm926ejs/u-boot.lds
@@ -51,6 +51,14 @@ SECTIONS
*(.data.rel.ro)
}

+ . = ALIGN(4);
+ __rel_dyn_start = .;
+ .rel.dyn : { *(.rel.dyn) }
+ __rel_dyn_end = .;
+
+ __dynsym_start = .;
+ .dynsym : { *(.dynsym) }
+
__got_start = .;
. = ALIGN(4);
.got : { *(.got) }
@@ -65,4 +73,5 @@ SECTIONS
__bss_start = .;
.bss (NOLOAD) : { *(.bss) . = ALIGN(4); }
_end = .;
+
}
diff --git a/arch/arm/include/asm/u-boot-arm.h b/arch/arm/include/asm/u-boot-arm.h
index faf800a..4ac4f61 100644
--- a/arch/arm/include/asm/u-boot-arm.h
+++ b/arch/arm/include/asm/u-boot-arm.h
@@ -30,18 +30,18 @@
#define _U_BOOT_ARM_H_ 1

/* for the following variables, see start.S */
-extern ulong _bss_start; /* code + data end == BSS start */
-extern ulong _bss_end; /* BSS end */
+extern ulong _bss_start_ofs; /* BSS start relative to _start */
+extern ulong _bss_end_ofs; /* BSS end relative to _start */
extern ulong IRQ_STACK_START; /* top of IRQ stack */
extern ulong FIQ_STACK_START; /* top of FIQ stack */
#if defined(CONFIG_SYS_ARM_WITHOUT_RELOC)
-extern ulong _armboot_start; /* code start */
+extern ulong _armboot_start_ofs; /* code start */
#else
extern ulong _TEXT_BASE; /* code start */
-extern ulong _datarel_start;
-extern ulong _datarelrolocal_start;
-extern ulong _datarellocal_start;
-extern ulong _datarelro_start;
+extern ulong _datarel_start_ofs;
+extern ulong _datarelrolocal_start_ofs;
+extern ulong _datarellocal_start_ofs;
+extern ulong _datarelro_start_ofs;
extern ulong IRQ_STACK_START_IN; /* 8 bytes in IRQ stack */
#endif

diff --git a/arch/arm/lib/board.c b/arch/arm/lib/board.c
index 5f2dfd0..e411d93 100644
--- a/arch/arm/lib/board.c
+++ b/arch/arm/lib/board.c
@@ -147,7 +147,7 @@ static int display_banner (void)
#else
_armboot_start,
#endif
- _bss_start, _bss_end);
+ _bss_start_ofs+_TEXT_BASE, _bss_end_ofs+_TEXT_BASE);
#ifdef CONFIG_MODEM_SUPPORT
debug ("Modem Support enabled\n");
#endif
@@ -517,7 +517,7 @@ void board_init_f (ulong bootflag)

memset ((void*)gd, 0, sizeof (gd_t));

- gd->mon_len = _bss_end - _TEXT_BASE;
+ gd->mon_len = _bss_end_ofs;

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
@@ -679,6 +679,7 @@ static char *failed = "*** failed ***\n";
*
************************************************************************
*/
+
void board_init_r (gd_t *id, ulong dest_addr)
{
char *s;
@@ -702,7 +703,7 @@ void board_init_r (gd_t *id, ulong dest_addr)

gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */

- monitor_flash_len = _bss_start - _TEXT_BASE;
+ monitor_flash_len = _bss_start_ofs;
debug ("monitor flash len: %08lX\n", monitor_flash_len);
board_init(); /* Setup chipselects */

@@ -914,6 +915,7 @@ extern void davinci_eth_set_mac_addr (const u_int8_t *addr);

/* NOTREACHED - no way out of command loop except booting */
}
+
#endif /* defined(CONFIG_SYS_ARM_WITHOUT_RELOC) */

void hang (void)
diff --git a/include/configs/edminiv2.h b/include/configs/edminiv2.h
index ccfc660..8bcdfcc 100644
--- a/include/configs/edminiv2.h
+++ b/include/configs/edminiv2.h
@@ -223,4 +223,11 @@
#define CONFIG_SYS_RESET_ADDRESS 0xffff0000
#define CONFIG_SYS_MAXARGS 16

+/* additions for new relocation code, must be added to all boards */
+#define CONFIG_RELOC_FIXUP_WORKS
+#undef CONFIG_SYS_ARM_WITHOUT_RELOC
+#define CONFIG_SYS_SDRAM_BASE 0
+#define CONFIG_SYS_INIT_SP_ADDR \
+ (CONFIG_SYS_SDRAM_BASE + 0x1000 - CONFIG_SYS_GBL_DATA_SIZE)
+
#endif /* _CONFIG_EDMINIV2_H */
--
1.7.0.4