summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVarad Gautam <vrd@amazon.de>2019-06-27 12:12:42 +0200
committerSimon Horman <horms@verge.net.au>2019-07-03 09:56:22 +0200
commitd6bc88c06962b74c7d0d6af8f2193d2483caae97 (patch)
treead5daec75e7f95e74f9e8c6c8f5a694d1b5dcaca
parent40805e393b2fc84551a6705e1890e995e3ee8320 (diff)
downloadkexec-tools-d6bc88c06962b74c7d0d6af8f2193d2483caae97.tar.gz
elf: Support ELF loading with relocation
Add a helper to allow loading an image within specified address range. This will be used to load multiboot2 images later. Signed-off-by: Varad Gautam <vrd@amazon.de> Signed-off-by: Simon Horman <horms@verge.net.au>
-rw-r--r--kexec/kexec-elf-exec.c199
-rw-r--r--kexec/kexec-elf.h7
2 files changed, 141 insertions, 65 deletions
diff --git a/kexec/kexec-elf-exec.c b/kexec/kexec-elf-exec.c
index a9329ac0..bea7b3e3 100644
--- a/kexec/kexec-elf-exec.c
+++ b/kexec/kexec-elf-exec.c
@@ -11,6 +11,84 @@
static const int probe_debug = 0;
+static void load_elf_segments(struct mem_ehdr *ehdr, struct kexec_info *info, unsigned long base)
+{
+ size_t i;
+
+ /* Read in the PT_LOAD segments */
+ for(i = 0; i < ehdr->e_phnum; i++) {
+ struct mem_phdr *phdr;
+ size_t size;
+ phdr = &ehdr->e_phdr[i];
+ if (phdr->p_type != PT_LOAD) {
+ continue;
+ }
+ size = phdr->p_filesz;
+ if (size > phdr->p_memsz) {
+ size = phdr->p_memsz;
+ }
+ add_segment(info, phdr->p_data, size,
+ phdr->p_paddr + base, phdr->p_memsz);
+ }
+}
+
+static int get_elf_exec_load_base(struct mem_ehdr *ehdr, struct kexec_info *info,
+ unsigned long min, unsigned long max,
+ unsigned long align, unsigned long *base)
+{
+ unsigned long first, last;
+ size_t i;
+
+ /* Note on arm64:
+ * arm64's vmlinux has virtual address in physical address
+ * field of PT_LOAD segments. So the following validity check
+ * and relocation makes no sense on arm64.
+ */
+ if (ehdr->e_machine == EM_AARCH64)
+ return 0;
+
+ first = ULONG_MAX;
+ last = 0;
+ for(i = 0; i < ehdr->e_phnum; i++) {
+ unsigned long start, stop;
+ struct mem_phdr *phdr;
+ phdr = &ehdr->e_phdr[i];
+ if ((phdr->p_type != PT_LOAD) ||
+ (phdr->p_memsz == 0))
+ {
+ continue;
+ }
+ start = phdr->p_paddr;
+ stop = start + phdr->p_memsz;
+ if (first > start) {
+ first = start;
+ }
+ if (last < stop) {
+ last = stop;
+ }
+ if (align < phdr->p_align) {
+ align = phdr->p_align;
+ }
+ }
+
+ if ((max - min) < (last - first))
+ return -1;
+
+ if (!valid_memory_range(info, min > first ? min : first, max < last ? max : last)) {
+ unsigned long hole;
+ hole = locate_hole(info, last - first + 1, align, min, max, 1);
+ if (hole == ULONG_MAX)
+ return -1;
+
+ /* Base is the value that when added
+ * to any virtual address in the file
+ * yields it's load virtual address.
+ */
+ *base = hole - first;
+ }
+ return 0;
+}
+
int build_elf_exec_info(const char *buf, off_t len, struct mem_ehdr *ehdr,
uint32_t flags)
{
@@ -53,7 +131,6 @@ int elf_exec_load(struct mem_ehdr *ehdr, struct kexec_info *info)
{
unsigned long base;
int result;
- size_t i;
if (!ehdr->e_phdr) {
fprintf(stderr, "No program header?\n");
@@ -63,75 +140,48 @@ int elf_exec_load(struct mem_ehdr *ehdr, struct kexec_info *info)
/* If I have a dynamic executable find it's size
* and then find a location for it in memory.
- * Note on arm64:
- * arm64's vmlinux has virtual address in physical address
- * field of PT_LOAD segments. So the following validity check
- * and relocation makes no sense on arm64.
*/
base = 0;
- if ((ehdr->e_machine != EM_AARCH64) && (ehdr->e_type == ET_DYN)) {
- unsigned long first, last, align;
- first = ULONG_MAX;
- last = 0;
- align = 0;
- for(i = 0; i < ehdr->e_phnum; i++) {
- unsigned long start, stop;
- struct mem_phdr *phdr;
- phdr = &ehdr->e_phdr[i];
- if ((phdr->p_type != PT_LOAD) ||
- (phdr->p_memsz == 0))
- {
- continue;
- }
- start = phdr->p_paddr;
- stop = start + phdr->p_memsz;
- if (first > start) {
- first = start;
- }
- if (last < stop) {
- last = stop;
- }
- if (align < phdr->p_align) {
- align = phdr->p_align;
- }
- }
- /* If I can't use the default paddr find a new
- * hole for the dynamic executable.
- */
- if (!valid_memory_range(info, first, last)) {
- unsigned long hole;
- hole = locate_hole(info,
- last - first + 1, align,
- 0, elf_max_addr(ehdr), 1);
- if (hole == ULONG_MAX) {
- result = -1;
- goto out;
- }
- /* Base is the value that when added
- * to any virtual address in the file
- * yields it's load virtual address.
- */
- base = hole - first;
- }
-
+ if (ehdr->e_type == ET_DYN) {
+ result = get_elf_exec_load_base(ehdr, info, 0, elf_max_addr(ehdr), 0 /* align */, &base);
+ if (result < 0)
+ goto out;
}
- /* Read in the PT_LOAD segments */
- for(i = 0; i < ehdr->e_phnum; i++) {
- struct mem_phdr *phdr;
- size_t size;
- phdr = &ehdr->e_phdr[i];
- if (phdr->p_type != PT_LOAD) {
- continue;
- }
- size = phdr->p_filesz;
- if (size > phdr->p_memsz) {
- size = phdr->p_memsz;
- }
- add_segment(info,
- phdr->p_data, size,
- phdr->p_paddr + base, phdr->p_memsz);
+ load_elf_segments(ehdr, info, base);
+
+ /* Update entry point to reflect new load address*/
+ ehdr->e_entry += base;
+
+ result = 0;
+ out:
+ return result;
+}
+
+int elf_exec_load_relocatable(struct mem_ehdr *ehdr, struct kexec_info *info,
+ unsigned long reloc_min, unsigned long reloc_max,
+ unsigned long align)
+{
+ unsigned long base;
+ int result;
+
+ if (reloc_min > reloc_max) {
+ fprintf(stderr, "Bad relocation range, start=%lux > end=%lux.\n", reloc_min, reloc_max);
+ result = -1;
+ goto out;
}
+ if (!ehdr->e_phdr) {
+ fprintf(stderr, "No program header?\n");
+ result = -1;
+ goto out;
+ }
+
+ base = 0;
+ result = get_elf_exec_load_base(ehdr, info, reloc_min, reloc_max, align, &base);
+ if (result < 0)
+ goto out;
+
+ load_elf_segments(ehdr, info, base);
/* Update entry point to reflect new load address*/
ehdr->e_entry += base;
@@ -157,3 +207,22 @@ void elf_exec_build_load(struct kexec_info *info, struct mem_ehdr *ehdr,
die("ELF exec load failed\n");
}
}
+
+void elf_exec_build_load_relocatable(struct kexec_info *info, struct mem_ehdr *ehdr,
+ const char *buf, off_t len, uint32_t flags,
+ unsigned long reloc_min, unsigned long reloc_max,
+ unsigned long align)
+{
+ int result;
+ /* Parse the Elf file */
+ result = build_elf_exec_info(buf, len, ehdr, flags);
+ if (result < 0) {
+ die("%s: ELF exec parse failed\n", __func__);
+ }
+
+ /* Load the Elf data */
+ result = elf_exec_load_relocatable(ehdr, info, reloc_min, reloc_max, align);
+ if (result < 0) {
+ die("%s: ELF exec load failed\n", __func__);
+ }
+} \ No newline at end of file
diff --git a/kexec/kexec-elf.h b/kexec/kexec-elf.h
index 1164db41..1e512c8e 100644
--- a/kexec/kexec-elf.h
+++ b/kexec/kexec-elf.h
@@ -100,11 +100,18 @@ extern int build_elf_rel_info(const char *buf, off_t len, struct mem_ehdr *ehdr,
extern int build_elf_core_info(const char *buf, off_t len,
struct mem_ehdr *ehdr, uint32_t flags);
extern int elf_exec_load(struct mem_ehdr *ehdr, struct kexec_info *info);
+extern int elf_exec_load_relocatable(struct mem_ehdr *ehdr, struct kexec_info *info,
+ unsigned long reloc_min, unsigned long reloc_max,
+ unsigned long align);
extern int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info,
unsigned long min, unsigned long max, int end);
extern void elf_exec_build_load(struct kexec_info *info, struct mem_ehdr *ehdr,
const char *buf, off_t len, uint32_t flags);
+extern void elf_exec_build_load_relocatable(struct kexec_info *info, struct mem_ehdr *ehdr,
+ const char *buf, off_t len, uint32_t flags,
+ unsigned long reloc_min, unsigned long reloc_max,
+ unsigned long align);
extern void elf_rel_build_load(struct kexec_info *info, struct mem_ehdr *ehdr,
const char *buf, off_t len, unsigned long min, unsigned long max,
int end, uint32_t flags);