Message ID | 1386992858-44800-5-git-send-email-fenghua@phytium.com.cn |
---|---|
State | Accepted |
Delegated to: | Albert ARIBAUD |
Headers | show |
On Sat, Dec 14, 2013 at 9:17 AM, <fenghua@phytium.com.cn> wrote: > From: Scott Wood <scottwood@freescale.com> > > ARM64 uses the newer RELA-style relocations rather than the older REL. > RELA relocations have an addend in the relocation struct, rather than > expecting the loader to read a value from the location to be updated. > > While this is beneficial for ordinary program loading, it's problematic > for U-Boot because the location to be updated starts out with zero, > rather than a pre-relocation value. Since we need to be able to run C > code before relocation, we need a tool to apply the relocations at > build time. > > In theory this tool is applicable to other newer architectures (mainly > 64-bit), but currently the only relocations it supports are for arm64, > and it assumes a 64-bit little-endian target. If the latter limitation > is ever to be changed, we'll need a way to tell the tool what format > the image is in. Eventually this may be replaced by a tool that uses > libelf or similar and operates directly on the ELF file. I've written > some code for such an approach but libelf does not make it easy to poke > addresses by memory address (rather than by section), and I was > hesitant to write code to manually parse the program headers and do the > update outside of libelf (or to iterate over sections) -- especially > since it wouldn't get test coverage on things like binaries with > multiple PT_LOAD segments. This should be good enough for now to let > the manual relocation stuff be removed from the arm64 patches. > > Signed-off-by: Scott Wood <scottwood@freescale.com> > Signed-off-by: David Feng <fenghua@phytium.com.cn> > --- > Makefile | 12 ++++ > tools/Makefile | 6 ++ > tools/relocate-rela.c | 189 +++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 207 insertions(+) > create mode 100644 tools/relocate-rela.c > > diff --git a/Makefile b/Makefile > index 7310c4e..4a8e8c5 100644 > --- a/Makefile > +++ b/Makefile > @@ -334,6 +334,17 @@ else > BOARD_SIZE_CHECK = > endif > > +# Statically apply RELA-style relocations (currently arm64 only) > +ifneq ($(CONFIG_STATIC_RELA),) > +# $(1) is u-boot ELF, $(2) is u-boot bin, $(3) is text base > +DO_STATIC_RELA = \ > + start=$$($(NM) $(1) | grep __rel_dyn_start | cut -f 1 -d ' '); \ > + end=$$($(NM) $(1) | grep __rel_dyn_end | cut -f 1 -d ' '); \ > + $(obj)tools/relocate-rela $(2) $(3) $$start $$end > +else > +DO_STATIC_RELA = > +endif > + > # Always append ALL so that arch config.mk's can add custom ones > ALL-y += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map > > @@ -376,6 +387,7 @@ $(obj)u-boot.srec: $(obj)u-boot > > $(obj)u-boot.bin: $(obj)u-boot > $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@ > + $(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE)) > $(BOARD_SIZE_CHECK) > > $(obj)u-boot.ldr: $(obj)u-boot > diff --git a/tools/Makefile b/tools/Makefile > index 14d94e3..6d40398 100644 > --- a/tools/Makefile > +++ b/tools/Makefile > @@ -59,6 +59,7 @@ BIN_FILES-$(CONFIG_NETCONSOLE) += ncb$(SFX) > BIN_FILES-$(CONFIG_SHA1_CHECK_UB_IMG) += ubsha1$(SFX) > BIN_FILES-$(CONFIG_KIRKWOOD) += kwboot$(SFX) > BIN_FILES-y += proftool(SFX) > +BIN_FILES-$(CONFIG_STATIC_RELA) += relocate-rela$(SFX) > > # Source files which exist outside the tools directory > EXT_OBJ_FILES-$(CONFIG_BUILD_ENVCRC) += common/env_embedded.o > @@ -84,6 +85,7 @@ NOPED_OBJ_FILES-y += os_support.o > NOPED_OBJ_FILES-y += pblimage.o > NOPED_OBJ_FILES-y += proftool.o > NOPED_OBJ_FILES-y += ublimage.o > +NOPED_OBJ_FILES-y += relocate-rela.o > OBJ_FILES-$(CONFIG_BUILD_ENVCRC) += envcrc.o > OBJ_FILES-$(CONFIG_CMD_LOADS) += img2srec.o > OBJ_FILES-$(CONFIG_CMD_NET) += gen_eth_addr.o > @@ -250,6 +252,10 @@ $(obj)kwboot$(SFX): $(obj)kwboot.o > $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ > $(HOSTSTRIP) $@ > > +$(obj)relocate-rela$(SFX): $(obj)relocate-rela.o > + $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ > + $(HOSTSTRIP) $@ > + > # Some of the tool objects need to be accessed from outside the tools directory > $(obj)%.o: $(SRCTREE)/common/%.c > $(HOSTCC) -g $(HOSTCFLAGS_NOPED) -c -o $@ $< > diff --git a/tools/relocate-rela.c b/tools/relocate-rela.c > new file mode 100644 > index 0000000..93b4c39 > --- /dev/null > +++ b/tools/relocate-rela.c > @@ -0,0 +1,189 @@ > +/* > + * Copyright 2013 Freescale Semiconductor, Inc. > + * > + * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause > + * > + * 64-bit and little-endian target only until we need to support a different > + * arch that needs this. > + */ > + > +#include <elf.h> > +#include <errno.h> > +#include <inttypes.h> > +#include <stdarg.h> > +#include <stdbool.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > + > +#ifndef R_AARCH64_RELATIVE > +#define R_AARCH64_RELATIVE 1027 > +#endif > + > +static const bool debug_en; > + > +static void debug(const char *fmt, ...) > +{ > + va_list args; > + > + va_start(args, fmt); > + if (debug_en) > + vprintf(fmt, args); > +} > + > +static bool supported_rela(Elf64_Rela *rela) > +{ > + uint64_t mask = 0xffffffffULL; /* would be different on 32-bit */ > + uint32_t type = rela->r_info & mask; > + > + switch (type) { > +#ifdef R_AARCH64_RELATIVE > + case R_AARCH64_RELATIVE: > + return true; > +#endif > + default: > + fprintf(stderr, "warning: unsupported relocation type %" > + PRIu32 " at %" PRIx64 "\n", > + type, rela->r_offset); > + > + return false; > + } > +} > + > +static inline uint64_t swap64(uint64_t val) > +{ > + return ((val >> 56) & 0x00000000000000ffULL) | > + ((val >> 40) & 0x000000000000ff00ULL) | > + ((val >> 24) & 0x0000000000ff0000ULL) | > + ((val >> 8) & 0x00000000ff000000ULL) | > + ((val << 8) & 0x000000ff00000000ULL) | > + ((val << 24) & 0x0000ff0000000000ULL) | > + ((val << 40) & 0x00ff000000000000ULL) | > + ((val << 56) & 0xff00000000000000ULL); > +} > + > +#if __BYTE_ORDER == __LITTLE_ENDIAN > +static inline uint64_t be64(uint64_t val) > +{ > + return swap64(val); > +} > + > +static inline uint64_t le64(uint64_t val) > +{ > + return val; > +} > +#else > +static inline uint64_t le64(uint64_t val) > +{ > + return swap64(val); > +} > + > +static inline uint64_t be64(uint64_t val) > +{ > + return val; > +} > +#endif > + > +static bool read_num(const char *str, uint64_t *num) > +{ > + char *endptr; > + *num = strtoull(str, &endptr, 16); > + return str[0] && !endptr[0]; > +} > + > +int main(int argc, char **argv) > +{ > + FILE *f; > + int i, num; > + uint64_t rela_start, rela_end, text_base; > + > + if (argc != 5) { > + fprintf(stderr, "Statically apply ELF rela relocations\n"); > + fprintf(stderr, "Usage: %s <bin file> <text base> " \ > + "<rela start> <rela end>\n", argv[0]); Try to avoid unnecessary line continuations at fprintf
On Sat, 2013-12-14 at 10:39 +0530, Jagan Teki wrote: > On Sat, Dec 14, 2013 at 9:17 AM, <fenghua@phytium.com.cn> wrote: > > From: Scott Wood <scottwood@freescale.com> > > [snip] > > + if (argc != 5) { > > + fprintf(stderr, "Statically apply ELF rela relocations\n"); > > + fprintf(stderr, "Usage: %s <bin file> <text base> " \ > > + "<rela start> <rela end>\n", argv[0]); > > Try to avoid unnecessary line continuations at fprintf FWIW, it wasn't like that it in my original patch. I'm guessing he changed it due to checkpatch output; checkpatch recognizes printf() but not fprintf(). It should probably be changed to recognize *printf(), just as it recognizes *printk(). -Scott
diff --git a/Makefile b/Makefile index 7310c4e..4a8e8c5 100644 --- a/Makefile +++ b/Makefile @@ -334,6 +334,17 @@ else BOARD_SIZE_CHECK = endif +# Statically apply RELA-style relocations (currently arm64 only) +ifneq ($(CONFIG_STATIC_RELA),) +# $(1) is u-boot ELF, $(2) is u-boot bin, $(3) is text base +DO_STATIC_RELA = \ + start=$$($(NM) $(1) | grep __rel_dyn_start | cut -f 1 -d ' '); \ + end=$$($(NM) $(1) | grep __rel_dyn_end | cut -f 1 -d ' '); \ + $(obj)tools/relocate-rela $(2) $(3) $$start $$end +else +DO_STATIC_RELA = +endif + # Always append ALL so that arch config.mk's can add custom ones ALL-y += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map @@ -376,6 +387,7 @@ $(obj)u-boot.srec: $(obj)u-boot $(obj)u-boot.bin: $(obj)u-boot $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@ + $(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE)) $(BOARD_SIZE_CHECK) $(obj)u-boot.ldr: $(obj)u-boot diff --git a/tools/Makefile b/tools/Makefile index 14d94e3..6d40398 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -59,6 +59,7 @@ BIN_FILES-$(CONFIG_NETCONSOLE) += ncb$(SFX) BIN_FILES-$(CONFIG_SHA1_CHECK_UB_IMG) += ubsha1$(SFX) BIN_FILES-$(CONFIG_KIRKWOOD) += kwboot$(SFX) BIN_FILES-y += proftool(SFX) +BIN_FILES-$(CONFIG_STATIC_RELA) += relocate-rela$(SFX) # Source files which exist outside the tools directory EXT_OBJ_FILES-$(CONFIG_BUILD_ENVCRC) += common/env_embedded.o @@ -84,6 +85,7 @@ NOPED_OBJ_FILES-y += os_support.o NOPED_OBJ_FILES-y += pblimage.o NOPED_OBJ_FILES-y += proftool.o NOPED_OBJ_FILES-y += ublimage.o +NOPED_OBJ_FILES-y += relocate-rela.o OBJ_FILES-$(CONFIG_BUILD_ENVCRC) += envcrc.o OBJ_FILES-$(CONFIG_CMD_LOADS) += img2srec.o OBJ_FILES-$(CONFIG_CMD_NET) += gen_eth_addr.o @@ -250,6 +252,10 @@ $(obj)kwboot$(SFX): $(obj)kwboot.o $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ $(HOSTSTRIP) $@ +$(obj)relocate-rela$(SFX): $(obj)relocate-rela.o + $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ + $(HOSTSTRIP) $@ + # Some of the tool objects need to be accessed from outside the tools directory $(obj)%.o: $(SRCTREE)/common/%.c $(HOSTCC) -g $(HOSTCFLAGS_NOPED) -c -o $@ $< diff --git a/tools/relocate-rela.c b/tools/relocate-rela.c new file mode 100644 index 0000000..93b4c39 --- /dev/null +++ b/tools/relocate-rela.c @@ -0,0 +1,189 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause + * + * 64-bit and little-endian target only until we need to support a different + * arch that needs this. + */ + +#include <elf.h> +#include <errno.h> +#include <inttypes.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef R_AARCH64_RELATIVE +#define R_AARCH64_RELATIVE 1027 +#endif + +static const bool debug_en; + +static void debug(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + if (debug_en) + vprintf(fmt, args); +} + +static bool supported_rela(Elf64_Rela *rela) +{ + uint64_t mask = 0xffffffffULL; /* would be different on 32-bit */ + uint32_t type = rela->r_info & mask; + + switch (type) { +#ifdef R_AARCH64_RELATIVE + case R_AARCH64_RELATIVE: + return true; +#endif + default: + fprintf(stderr, "warning: unsupported relocation type %" + PRIu32 " at %" PRIx64 "\n", + type, rela->r_offset); + + return false; + } +} + +static inline uint64_t swap64(uint64_t val) +{ + return ((val >> 56) & 0x00000000000000ffULL) | + ((val >> 40) & 0x000000000000ff00ULL) | + ((val >> 24) & 0x0000000000ff0000ULL) | + ((val >> 8) & 0x00000000ff000000ULL) | + ((val << 8) & 0x000000ff00000000ULL) | + ((val << 24) & 0x0000ff0000000000ULL) | + ((val << 40) & 0x00ff000000000000ULL) | + ((val << 56) & 0xff00000000000000ULL); +} + +#if __BYTE_ORDER == __LITTLE_ENDIAN +static inline uint64_t be64(uint64_t val) +{ + return swap64(val); +} + +static inline uint64_t le64(uint64_t val) +{ + return val; +} +#else +static inline uint64_t le64(uint64_t val) +{ + return swap64(val); +} + +static inline uint64_t be64(uint64_t val) +{ + return val; +} +#endif + +static bool read_num(const char *str, uint64_t *num) +{ + char *endptr; + *num = strtoull(str, &endptr, 16); + return str[0] && !endptr[0]; +} + +int main(int argc, char **argv) +{ + FILE *f; + int i, num; + uint64_t rela_start, rela_end, text_base; + + if (argc != 5) { + fprintf(stderr, "Statically apply ELF rela relocations\n"); + fprintf(stderr, "Usage: %s <bin file> <text base> " \ + "<rela start> <rela end>\n", argv[0]); + fprintf(stderr, "All numbers in hex.\n"); + return 1; + } + + f = fopen(argv[1], "r+b"); + if (!f) { + fprintf(stderr, "%s: Cannot open %s: %s\n", + argv[0], argv[1], strerror(errno)); + return 2; + } + + if (!read_num(argv[2], &text_base) || + !read_num(argv[3], &rela_start) || + !read_num(argv[4], &rela_end)) { + fprintf(stderr, "%s: bad number\n", argv[0]); + return 3; + } + + if (rela_start > rela_end || rela_start < text_base || + (rela_end - rela_start) % 24) { + fprintf(stderr, "%s: bad rela bounds\n", argv[0]); + return 3; + } + + rela_start -= text_base; + rela_end -= text_base; + + num = (rela_end - rela_start) / sizeof(Elf64_Rela); + + for (i = 0; i < num; i++) { + Elf64_Rela rela, swrela; + uint64_t pos = rela_start + sizeof(Elf64_Rela) * i; + uint64_t addr; + + if (fseek(f, pos, SEEK_SET) < 0) { + fprintf(stderr, "%s: %s: seek to %" PRIx64 + " failed: %s\n", + argv[0], argv[1], pos, strerror(errno)); + } + + if (fread(&rela, sizeof(rela), 1, f) != 1) { + fprintf(stderr, "%s: %s: read rela failed at %" + PRIx64 "\n", + argv[0], argv[1], pos); + return 4; + } + + swrela.r_offset = le64(rela.r_offset); + swrela.r_info = le64(rela.r_info); + swrela.r_addend = le64(rela.r_addend); + + if (!supported_rela(&swrela)) + continue; + + debug("Rela %" PRIx64 " %" PRIu64 " %" PRIx64 "\n", + swrela.r_offset, swrela.r_info, swrela.r_addend); + + if (swrela.r_offset < text_base) { + fprintf(stderr, "%s: %s: bad rela at %" PRIx64 "\n", + argv[0], argv[1], pos); + return 4; + } + + addr = swrela.r_offset - text_base; + + if (fseek(f, addr, SEEK_SET) < 0) { + fprintf(stderr, "%s: %s: seek to %" + PRIx64 " failed: %s\n", + argv[0], argv[1], addr, strerror(errno)); + } + + if (fwrite(&rela.r_addend, sizeof(rela.r_addend), 1, f) != 1) { + fprintf(stderr, "%s: %s: write failed at %" PRIx64 "\n", + argv[0], argv[1], addr); + return 4; + } + } + + if (fclose(f) < 0) { + fprintf(stderr, "%s: %s: close failed: %s\n", + argv[0], argv[1], strerror(errno)); + return 4; + } + + return 0; +}