diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2006-07-27 02:36:23 -0600 |
---|---|---|
committer | Eric W. Biederman <ebiederm@xmission.com> | 2006-07-27 02:36:23 -0600 |
commit | 283261998a9846019d898bc454b363e4aaf3d181 (patch) | |
tree | a4af6da4c5a2c6f7669d918c1f07dc68d6aa0ab2 /kexec/kexec-elf-exec.c |
kexec-tools-1.101
- Initial import into git
- initial nbi image formage support
- ppc32 initial register setting fixes.
- gzipped multiboot file support
Diffstat (limited to 'kexec/kexec-elf-exec.c')
-rw-r--r-- | kexec/kexec-elf-exec.c | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/kexec/kexec-elf-exec.c b/kexec/kexec-elf-exec.c new file mode 100644 index 0000000..324516c --- /dev/null +++ b/kexec/kexec-elf-exec.c @@ -0,0 +1,149 @@ +#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; + +int build_elf_exec_info(const char *buf, off_t len, struct mem_ehdr *ehdr) +{ + struct mem_phdr *phdr, *end_phdr; + int result; + result = build_elf_info(buf, len, ehdr); + if (result < 0) { + return result; + } + if ((ehdr->e_type != ET_EXEC) && (ehdr->e_type != ET_DYN)) { + /* 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(const struct mem_ehdr *ehdr, struct kexec_info *info) +{ + unsigned long base; + int result; + int i; + + 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) { + 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 (start > first) { + start = first; + } + 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(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; + } + + } + + /* 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); + } + result = 0; + out: + return result; +} + +void elf_exec_build_load(struct kexec_info *info, struct mem_ehdr *ehdr, + const char *buf, off_t len) +{ + int result; + /* Parse the Elf file */ + result = build_elf_exec_info(buf, len, ehdr); + 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"); + } +} |