summaryrefslogtreecommitdiffstats
path: root/kexec/kexec-elf-exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'kexec/kexec-elf-exec.c')
-rw-r--r--kexec/kexec-elf-exec.c228
1 files changed, 228 insertions, 0 deletions
diff --git a/kexec/kexec-elf-exec.c b/kexec/kexec-elf-exec.c
new file mode 100644
index 0000000..bea7b3e
--- /dev/null
+++ b/kexec/kexec-elf-exec.c
@@ -0,0 +1,228 @@
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "elf.h"
+#include <boot/elf_boot.h>
+#include "kexec.h"
+#include "kexec-elf.h"
+
+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)
+{
+ struct mem_phdr *phdr, *end_phdr;
+ int result;
+ result = build_elf_info(buf, len, ehdr, flags);
+ if (result < 0) {
+ return result;
+ }
+ if ((ehdr->e_type != ET_EXEC) && (ehdr->e_type != ET_DYN) &&
+ (ehdr->e_type != ET_CORE)) {
+ /* not an ELF executable */
+ if (probe_debug) {
+ fprintf(stderr, "Not ELF type ET_EXEC or ET_DYN\n");
+ }
+ return -1;
+ }
+ if (!ehdr->e_phdr) {
+ /* No program header */
+ fprintf(stderr, "No ELF program header\n");
+ return -1;
+ }
+ end_phdr = &ehdr->e_phdr[ehdr->e_phnum];
+ for(phdr = ehdr->e_phdr; phdr != end_phdr; phdr++) {
+ /* Kexec does not support loading interpreters.
+ * In addition this check keeps us from attempting
+ * to kexec ordinay executables.
+ */
+ if (phdr->p_type == PT_INTERP) {
+ fprintf(stderr, "Requires an ELF interpreter\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+int elf_exec_load(struct mem_ehdr *ehdr, struct kexec_info *info)
+{
+ unsigned long base;
+ int result;
+
+ if (!ehdr->e_phdr) {
+ fprintf(stderr, "No program header?\n");
+ result = -1;
+ goto out;
+ }
+
+ /* If I have a dynamic executable find it's size
+ * and then find a location for it in memory.
+ */
+ base = 0;
+ 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;
+ }
+
+ 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;
+
+ result = 0;
+ out:
+ return result;
+}
+
+void elf_exec_build_load(struct kexec_info *info, struct mem_ehdr *ehdr,
+ const char *buf, off_t len, uint32_t flags)
+{
+ int result;
+ /* Parse the Elf file */
+ result = build_elf_exec_info(buf, len, ehdr, flags);
+ if (result < 0) {
+ die("ELF exec parse failed\n");
+ }
+
+ /* Load the Elf data */
+ result = elf_exec_load(ehdr, info);
+ if (result < 0) {
+ 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