| #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__); |
| } |
| } |