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@xxxxxxxxx> --- kexec/kexec-elf-exec.c | 199 +++++++++++++++++++++++++++++++++---------------- kexec/kexec-elf.h | 7 ++ 2 files changed, 141 insertions(+), 65 deletions(-) diff --git a/kexec/kexec-elf-exec.c b/kexec/kexec-elf-exec.c index a9329ac..bea7b3e 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 1164db4..1e512c8 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); -- 2.7.4 Amazon Development Center Germany GmbH Krausenstr. 38 10117 Berlin Geschaeftsfuehrer: Christian Schlaeger, Ralf Herbrich Ust-ID: DE 289 237 879 Eingetragen am Amtsgericht Charlottenburg HRB 149173 B _______________________________________________ kexec mailing list kexec@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/kexec