mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Sascha Hauer <s.hauer@pengutronix.de>
To: BAREBOX <barebox@lists.infradead.org>
Cc: "Claude Sonnet 4.5" <noreply@anthropic.com>,
	Ahmad Fatoum <a.fatoum@pengutronix.de>
Subject: [PATCH v3 08/23] elf: implement elf_load_inplace()
Date: Thu, 08 Jan 2026 16:50:05 +0100	[thread overview]
Message-ID: <20260108-pbl-load-elf-v3-8-e28c931fc179@pengutronix.de> (raw)
In-Reply-To: <20260108-pbl-load-elf-v3-0-e28c931fc179@pengutronix.de>

Implement elf_load_inplace() to apply dynamic relocations to an ELF binary
that is already loaded in memory. Unlike elf_load(), this function does not
allocate memory or copy segments - it only modifies the existing image in
place.

This is useful for self-relocating loaders or when the ELF has been loaded
by external means (e.g., firmware or another bootloader).

For ET_DYN (position-independent) binaries, the relocation offset is
calculated relative to the first executable PT_LOAD segment (.text section),
taking into account the difference between the segment's virtual address
and its file offset.

The entry point is also adjusted to point to the relocated image.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Acked-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/elf.c  | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/elf.h |   8 ++++
 2 files changed, 138 insertions(+)

diff --git a/common/elf.c b/common/elf.c
index 67bb931576896ffd4fab15fd02893cc797dbd871..cba11640e52204add6cf601335b1904bd9188be0 100644
--- a/common/elf.c
+++ b/common/elf.c
@@ -634,3 +634,133 @@ void elf_close(struct elf_image *elf)
 
 	free(elf);
 }
+
+/**
+ * elf_load_inplace() - Apply dynamic relocations to an ELF binary in place
+ * @elf: ELF image previously opened with elf_open_binary()
+ *
+ * This function applies dynamic relocations to an ELF binary that is already
+ * loaded at its target address in memory. Unlike elf_load(), this does not
+ * allocate memory or copy segments - it only modifies the existing image.
+ *
+ * This is useful for self-relocating loaders or when the ELF has been loaded
+ * by external means (e.g., loaded by firmware or another bootloader).
+ *
+ * The ELF image must have been previously opened with elf_open_binary().
+ *
+ * For ET_DYN (position-independent) binaries, the relocation offset is
+ * calculated relative to the first executable PT_LOAD segment (.text section).
+ *
+ * For ET_EXEC binaries, no relocation is applied as they are expected to
+ * be at their link-time addresses.
+ *
+ * Returns: 0 on success, negative error code on failure
+ */
+int elf_load_inplace(struct elf_image *elf)
+{
+	const void *dyn_seg;
+	void *buf, *phdr;
+	void *elf_buf;
+	int i, ret;
+
+	buf = elf->hdr_buf;
+	elf_buf = elf->hdr_buf;
+
+	/*
+	 * First pass: Clear BSS segments (p_memsz > p_filesz).
+	 * This must be done before relocations as uninitialized data
+	 * must be zeroed per C standard.
+	 */
+	phdr = buf + elf_hdr_e_phoff(elf, buf);
+	for (i = 0; i < elf_hdr_e_phnum(elf, buf); i++) {
+		if (elf_phdr_p_type(elf, phdr) == PT_LOAD) {
+			u64 p_offset = elf_phdr_p_offset(elf, phdr);
+			u64 p_filesz = elf_phdr_p_filesz(elf, phdr);
+			u64 p_memsz = elf_phdr_p_memsz(elf, phdr);
+
+			/* Clear BSS (uninitialized data) */
+			if (p_filesz < p_memsz) {
+				void *bss_start = elf_buf + p_offset + p_filesz;
+				size_t bss_size = p_memsz - p_filesz;
+				memset(bss_start, 0x00, bss_size);
+			}
+		}
+		phdr += elf_size_of_phdr(elf);
+	}
+
+	/*
+	 * Calculate relocation offset for the in-place binary.
+	 * For ET_DYN, we need to find the first PT_LOAD segment
+	 * and use it as the relocation base.
+	 */
+	if (elf->type == ET_DYN) {
+		u64 text_vaddr = 0;
+		u64 text_offset = 0;
+		bool found_text = false;
+
+		/* Find first PT_LOAD segment */
+		phdr = buf + elf_hdr_e_phoff(elf, buf);
+		for (i = 0; i < elf_hdr_e_phnum(elf, buf); i++) {
+			if (elf_phdr_p_type(elf, phdr) == PT_LOAD) {
+				text_vaddr = elf_phdr_p_vaddr(elf, phdr);
+				text_offset = elf_phdr_p_offset(elf, phdr);
+				found_text = true;
+				break;
+			}
+			phdr += elf_size_of_phdr(elf);
+		}
+
+		if (!found_text) {
+			pr_err("No PT_LOAD segment found\n");
+			ret = -EINVAL;
+			goto out;
+		}
+
+		/*
+		 * Calculate relocation offset relative to .text section:
+		 * - .text is at file offset text_offset, so in memory at: elf_buf + text_offset
+		 * - .text has virtual address text_vaddr
+		 * - reloc_offset = (actual .text address) - (virtual .text address)
+		 */
+		elf->reloc_offset = ((unsigned long)elf_buf + text_offset) - text_vaddr;
+
+		pr_debug("In-place ELF relocation: text_vaddr=0x%llx, text_offset=0x%llx, "
+			 "load_addr=%p, offset=0x%08lx\n",
+			 text_vaddr, text_offset, elf_buf, elf->reloc_offset);
+
+		/* Adjust entry point to point to relocated image */
+		elf->entry += elf->reloc_offset;
+	} else {
+		/*
+		 * ET_EXEC binaries are at their link-time addresses,
+		 * no relocation needed
+		 */
+		elf->reloc_offset = 0;
+	}
+
+	/* Find PT_DYNAMIC segment */
+	dyn_seg = elf_find_dynamic_segment(elf);
+	if (!dyn_seg) {
+		/*
+		 * No PT_DYNAMIC segment found.
+		 * This is fine for statically-linked binaries or
+		 * binaries without relocations.
+		 */
+		pr_debug("No PT_DYNAMIC segment found\n");
+		ret = 0;
+		goto out;
+	}
+
+	/* Apply architecture-specific relocations */
+	ret = elf_apply_relocations(elf, dyn_seg);
+	if (ret) {
+		pr_err("In-place relocation failed: %d\n", ret);
+		goto out;
+	}
+
+	pr_debug("In-place ELF relocation completed successfully\n");
+	return 0;
+
+out:
+	return ret;
+}
diff --git a/include/elf.h b/include/elf.h
index c0b318f19fcf8adf8c3b83961456023307abf113..236e38fb29887315e01a165795e1bb861f738054 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -422,6 +422,14 @@ int elf_load(struct elf_image *elf);
  */
 void elf_set_load_address(struct elf_image *elf, void *addr);
 
+/*
+ * Apply dynamic relocations to an ELF binary already loaded in memory.
+ * This modifies the ELF image in place without allocating new memory.
+ * Useful for self-relocating loaders or externally loaded binaries.
+ * The elf parameter must have been previously opened with elf_open_binary().
+ */
+int elf_load_inplace(struct elf_image *elf);
+
 /*
  * Architecture-specific relocation handler.
  * Returns 0 on success, -ENOSYS if architecture doesn't support relocations,

-- 
2.47.3




  parent reply	other threads:[~2026-01-08 15:51 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-08 15:49 [PATCH v3 00/23] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
2026-01-08 15:49 ` [PATCH v3 01/23] Makefile.compiler: add objcopy-option Sascha Hauer
2026-01-08 16:25   ` Ahmad Fatoum
2026-01-08 15:49 ` [PATCH v3 02/23] elf: only accept images matching the native ELF_CLASS Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 03/23] elf: build for PBL as well Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 04/23] elf: add dynamic relocation support Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 05/23] ARM: implement elf_apply_relocations() for ELF " Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 06/23] riscv: define generic relocate_image Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 07/23] riscv: implement elf_apply_relocations() for ELF relocation support Sascha Hauer
2026-01-08 15:50 ` Sascha Hauer [this message]
2026-01-08 15:50 ` [PATCH v3 09/23] elf: create elf_open_binary_into() Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 10/23] Makefile: add vmbarebox build target Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 11/23] PBL: allow to link ELF image into PBL Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 12/23] mmu: add MAP_CACHED_RO mapping type Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 13/23] mmu: introduce pbl_remap_range() Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 14/23] ARM: drop arm_fixup_vectors() Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 15/23] ARM: linker script: create separate PT_LOAD segments for text, rodata, and data Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 16/23] ARM: link ELF image into PBL Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 17/23] ARM: PBL: setup MMU with proper permissions from ELF segments Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 18/23] riscv: linker script: create separate PT_LOAD segments for text, rodata, and data Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 19/23] riscv: link ELF image into PBL Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 20/23] riscv: Allwinner D1: Drop M-Mode Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 21/23] riscv: add ELF segment-based memory protection with MMU Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 22/23] ARM: cleanup barebox proper entry Sascha Hauer
2026-01-08 15:50 ` [PATCH v3 23/23] riscv: " Sascha Hauer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260108-pbl-load-elf-v3-8-e28c931fc179@pengutronix.de \
    --to=s.hauer@pengutronix.de \
    --cc=a.fatoum@pengutronix.de \
    --cc=barebox@lists.infradead.org \
    --cc=noreply@anthropic.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox