diff mbox

[U-Boot,1/4] arm64: Add tool to statically apply RELA relocations

Message ID 1380840511-7414-2-git-send-email-scottwood@freescale.com
State Superseded
Delegated to: Albert ARIBAUD
Headers show

Commit Message

Scott Wood Oct. 3, 2013, 10:48 p.m. UTC
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>
---
 Makefile              |  12 ++++
 tools/Makefile        |   6 ++
 tools/relocate-rela.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 203 insertions(+)
 create mode 100644 tools/relocate-rela.c

Comments

fenghua@phytium.com.cn Oct. 4, 2013, 4:10 p.m. UTC | #1
> 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

How it is beneficial than rel format?
Why aarch64-gcc use rela format only instead of supporting two format?
these confuse me a few months.

David,
Scott Wood Oct. 4, 2013, 4:57 p.m. UTC | #2
On Sat, 2013-10-05 at 00:10 +0800, FengHua wrote:
> > 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
> 
> How it is beneficial than rel format?

It avoids the need to read anything other than the rela descriptor, and
thus makes relocation faster.

> Why aarch64-gcc use rela format only instead of supporting two format?
> these confuse me a few months.

It's probably specified by the ABI which type to use, and it's not worth
adding toolchain support for something different just for weird cases
like this.

-Scott
Albert ARIBAUD Oct. 5, 2013, 7:52 a.m. UTC | #3
Hi Scott,

On Thu, 3 Oct 2013 17:48:28 -0500, Scott Wood <scottwood@freescale.com>
wrote:

> 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.

I love it when support for a feature which offers more capabilities is
replaced with support for one which offers less. What's the point of a
relocation system that produces a binary which will *never* work if
not relocated first? What's the point of zeroing position-dependent
locations instead of putting some useful value in it, like the value
they would have if no relocation occurred? :/

I really don't understand why REL-style relocation is impossible. Is it
an EABI specification? A toolchain limitation? A toolchain design
decision (i.e., a limitation that will not be lifted)?

OTOH, I don't have an EABI doc for arm64. Could someone just
copy-paster its URL to me? Thanks in advance.

> 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.

Can you clarify what makes this tool beneficial as opposed to e.g.
doing an objcopy from .elf to binary? After all, if we're going to
relocate at build time from address A to B, why not directly build
for address B, objcopy the resulting ELF and be done with it?

Amicalement,
Scott Wood Oct. 8, 2013, 12:55 a.m. UTC | #4
On Sat, 2013-10-05 at 09:52 +0200, Albert ARIBAUD wrote:
> Hi Scott,
> 
> On Thu, 3 Oct 2013 17:48:28 -0500, Scott Wood <scottwood@freescale.com>
> wrote:
> 
> > 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.
> 
> I love it when support for a feature which offers more capabilities is
> replaced with support for one which offers less. What's the point of a
> relocation system that produces a binary which will *never* work if
> not relocated first? What's the point of zeroing position-dependent
> locations instead of putting some useful value in it, like the value
> they would have if no relocation occurred? :/

Yeah, it's annoying.  It also seems to affect gdb printing global
variables before a program has started.

> I really don't understand why REL-style relocation is impossible. Is it
> an EABI specification? A toolchain limitation? A toolchain design
> decision (i.e., a limitation that will not be lifted)?

It looks like one of the latter two.  I don't know which.  I tried
looking at the linker code to see if there was an option to switch, and
had difficulty following it.  If someone else wants to engage with the
binutils people and get a REL option added, that'd be great, but I don't
have the bandwidth right now, and in any case it would be a while before
we could rely on such a solution.

> OTOH, I don't have an EABI doc for arm64. Could someone just
> copy-paster its URL to me? Thanks in advance.

I don't know of an "E"ABI for arm64, but googling "aarch64 abi" turns up
an ELF ABI document as the first result.  I tried to copy and paste the
URL but it's google-encoded crap, and it's PDF so I can't copy it from
the browser window that opens.

That document says that both REL and RELA are acceptable.

> > 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.
> 
> Can you clarify what makes this tool beneficial as opposed to e.g.
> doing an objcopy from .elf to binary? After all, if we're going to
> relocate at build time from address A to B, why not directly build
> for address B, objcopy the resulting ELF and be done with it?

We do use objcopy, but it doesn't apply the relocations.  It doesn't
matter what address we build for; if the relocations aren't applied, all
to-be-relocated pointers will be zero.

-Scott
Albert ARIBAUD Oct. 8, 2013, 8:10 a.m. UTC | #5
Hi Scott,

On Mon, 7 Oct 2013 19:55:46 -0500, Scott Wood <scottwood@freescale.com>
wrote:

> On Sat, 2013-10-05 at 09:52 +0200, Albert ARIBAUD wrote:
> > Hi Scott,
> > 
> > On Thu, 3 Oct 2013 17:48:28 -0500, Scott Wood <scottwood@freescale.com>
> > wrote:
> > 
> > > 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.
> > 
> > I love it when support for a feature which offers more capabilities is
> > replaced with support for one which offers less. What's the point of a
> > relocation system that produces a binary which will *never* work if
> > not relocated first? What's the point of zeroing position-dependent
> > locations instead of putting some useful value in it, like the value
> > they would have if no relocation occurred? :/
> 
> Yeah, it's annoying.  It also seems to affect gdb printing global
> variables before a program has started.
> 
> > I really don't understand why REL-style relocation is impossible. Is it
> > an EABI specification? A toolchain limitation? A toolchain design
> > decision (i.e., a limitation that will not be lifted)?
> 
> It looks like one of the latter two.  I don't know which.  I tried
> looking at the linker code to see if there was an option to switch, and
> had difficulty following it.  If someone else wants to engage with the
> binutils people and get a REL option added, that'd be great, but I don't
> have the bandwidth right now, and in any case it would be a while before
> we could rely on such a solution.
> 
> > OTOH, I don't have an EABI doc for arm64. Could someone just
> > copy-paster its URL to me? Thanks in advance.
> 
> I don't know of an "E"ABI for arm64, but googling "aarch64 abi" turns up
> an ELF ABI document as the first result.  I tried to copy and paste the
> URL but it's google-encoded crap, and it's PDF so I can't copy it from
> the browser window that opens.
> 
> That document says that both REL and RELA are acceptable.
> 
> > > 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.
> > 
> > Can you clarify what makes this tool beneficial as opposed to e.g.
> > doing an objcopy from .elf to binary? After all, if we're going to
> > relocate at build time from address A to B, why not directly build
> > for address B, objcopy the resulting ELF and be done with it?
> 
> We do use objcopy, but it doesn't apply the relocations.  It doesn't
> matter what address we build for; if the relocations aren't applied, all
> to-be-relocated pointers will be zero.

Thanks Scott fot the heads-up. I have found the arm64 ABI and traced it
back to this URL:

<http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ihi0056b/index.html>

(Somehow the document can be read in Firefox but evince chokes on it)

I see that some relocation types are deemed mandatory (4.6.1, page 13)
and the way I read it, R_AARCH64_RELATIVE should be one of them...

I'll have a stab at investigating both binutil and gcc.

Meanwhile, even if we end up being able to use R_AARCH64_RELATIVE, part
of the patch series will remain useful, notably the filtering in the
makefile. I would just like a note added somewhere stating that this is
hopefully an interim solution until R_AARCH64_RELATIVE is available.
 
> -Scott

Amicalement,
fenghua@phytium.com.cn Oct. 8, 2013, 2:22 p.m. UTC | #6
> diff --git a/tools/relocate-rela.c b/tools/relocate-rela.c
> new file mode 100644
> index 0000000..47afe0b
> --- /dev/null
> +++ b/tools/relocate-rela.c
> @@ -0,0 +1,185 @@
> +/*
> + * 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>
> +
> +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

hi Scott,
     the R_AARCH64_RELATIVE is not deinfed in my system. Whether we should define it at somewhere?

David
Scott Wood Oct. 8, 2013, 3:06 p.m. UTC | #7
On Tue, 2013-10-08 at 22:22 +0800, FengHua wrote:
> > diff --git a/tools/relocate-rela.c b/tools/relocate-rela.c
> > new file mode 100644
> > index 0000000..47afe0b
> > --- /dev/null
> > +++ b/tools/relocate-rela.c
> > @@ -0,0 +1,185 @@
> > +/*
> > + * 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>
> > +
> > +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
> 
> hi Scott,
>      the R_AARCH64_RELATIVE is not deinfed in my system. Whether we should define it at somewhere?

A newer host elf.h should fix this, but if it's going to be a problem
maybe we should just define it ourselves (value is 1027).

-Scott
Scott Wood Oct. 8, 2013, 4:22 p.m. UTC | #8
On Tue, 2013-10-08 at 10:10 +0200, Albert ARIBAUD wrote:
> Thanks Scott fot the heads-up. I have found the arm64 ABI and traced it
> back to this URL:
> 
> <http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ihi0056b/index.html>
> 
> (Somehow the document can be read in Firefox but evince chokes on it)

It works for me in evince 3.6.1.

-Scott
Albert ARIBAUD Oct. 9, 2013, 9:04 a.m. UTC | #9
Hi Scott,

On Tue, 8 Oct 2013 11:22:15 -0500, Scott Wood <scottwood@freescale.com>
wrote:

> On Tue, 2013-10-08 at 10:10 +0200, Albert ARIBAUD wrote:
> > Thanks Scott fot the heads-up. I have found the arm64 ABI and traced it
> > back to this URL:
> > 
> > <http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ihi0056b/index.html>
> > 
> > (Somehow the document can be read in Firefox but evince chokes on it)
> 
> It works for me in evince 3.6.1.

Thanks. Mine is 3.10.0. Could be a regression.

> -Scott

Amicalement,
diff mbox

Patch

diff --git a/Makefile b/Makefile
index 07abef4..5024dd8 100644
--- a/Makefile
+++ b/Makefile
@@ -392,6 +392,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
 
@@ -431,6 +442,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 c36cde2..a5eb85e 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..47afe0b
--- /dev/null
+++ b/tools/relocate-rela.c
@@ -0,0 +1,185 @@ 
+/*
+ * 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>
+
+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;
+}