diff options
Diffstat (limited to 'common/elf.c')
-rw-r--r-- | common/elf.c | 104 |
1 files changed, 73 insertions, 31 deletions
diff --git a/common/elf.c b/common/elf.c index af22be37e6..62f793010f 100644 --- a/common/elf.c +++ b/common/elf.c @@ -10,6 +10,7 @@ #include <libfile.h> #include <memory.h> #include <unistd.h> +#include <zero_page.h> #include <linux/fs.h> #include <linux/list_sort.h> @@ -59,14 +60,13 @@ static int request_elf_segment(struct elf_image *elf, void *phdr) { void *dst = (void *) (phys_addr_t) elf_phdr_p_paddr(elf, phdr); int ret; - u64 p_filesz = elf_phdr_p_filesz(elf, phdr); u64 p_memsz = elf_phdr_p_memsz(elf, phdr); /* we care only about PT_LOAD segments */ if (elf_phdr_p_type(elf, phdr) != PT_LOAD) return 0; - if (!p_filesz) + if (!p_memsz) return 0; if (dst < elf->low_addr) @@ -74,9 +74,9 @@ static int request_elf_segment(struct elf_image *elf, void *phdr) if (dst + p_memsz > elf->high_addr) elf->high_addr = dst + p_memsz; - pr_debug("Requesting segment 0x%p (%llu bytes)\n", dst, p_filesz); + pr_debug("Requesting segment 0x%p (%llu bytes)\n", dst, p_memsz); - ret = elf_request_region(elf, (resource_size_t)dst, p_filesz, phdr); + ret = elf_request_region(elf, (resource_size_t)dst, p_memsz, phdr); if (ret) return ret; @@ -101,47 +101,57 @@ static int elf_section_cmp(void *priv, struct list_head *a, struct list_head *b) static int load_elf_to_memory(struct elf_image *elf) { void *dst; - int ret, fd; + int ret = 0, fd = -1; u64 p_filesz, p_memsz, p_offset; struct elf_section *r; struct list_head *list = &elf->list; - fd = open(elf->filename, O_RDONLY); - if (fd < 0) { - pr_err("could not open: %s\n", errno_str()); - return -errno; + if (elf->filename) { + fd = open(elf->filename, O_RDONLY); + if (fd < 0) { + pr_err("could not open: %m\n"); + return -errno; + } } + zero_page_access(); + list_for_each_entry(r, list, list) { p_offset = elf_phdr_p_offset(elf, r->phdr); p_filesz = elf_phdr_p_filesz(elf, r->phdr); p_memsz = elf_phdr_p_memsz(elf, r->phdr); dst = (void *) (phys_addr_t) elf_phdr_p_paddr(elf, r->phdr); - ret = lseek(fd, p_offset, SEEK_SET); - if (ret == -1) { - pr_err("lseek at offset 0x%llx failed\n", p_offset); - close(fd); - return ret; - } - pr_debug("Loading phdr offset 0x%llx to 0x%p (%llu bytes)\n", p_offset, dst, p_filesz); - if (read_full(fd, dst, p_filesz) < 0) { - pr_err("could not read elf segment: %s\n", - errno_str()); - close(fd); - return -errno; + if (fd >= 0) { + ret = lseek(fd, p_offset, SEEK_SET); + if (ret == -1) { + pr_err("lseek at offset 0x%llx failed\n", + p_offset); + goto out; + } + + if (read_full(fd, dst, p_filesz) < 0) { + pr_err("could not read elf segment: %m\n"); + ret = -errno; + goto out; + } + } else { + memcpy(dst, elf->hdr_buf + p_offset, p_filesz); } if (p_filesz < p_memsz) memset(dst + p_filesz, 0x00, p_memsz - p_filesz); } +out: + zero_page_faulting(); + close(fd); - return 0; + return ret >= 0 ? 0 : ret; } static int load_elf_image_segments(struct elf_image *elf) @@ -202,6 +212,37 @@ static int elf_check_image(struct elf_image *elf, void *buf) return 0; } +static void elf_init_struct(struct elf_image *elf) +{ + INIT_LIST_HEAD(&elf->list); + elf->low_addr = (void *) (unsigned long) -1; + elf->high_addr = 0; + elf->filename = NULL; +} + +struct elf_image *elf_open_binary(void *buf) +{ + int ret; + struct elf_image *elf; + + elf = calloc(1, sizeof(*elf)); + if (!elf) + return ERR_PTR(-ENOMEM); + + elf_init_struct(elf); + + elf->hdr_buf = buf; + ret = elf_check_image(elf, buf); + if (ret) { + free(elf); + return ERR_PTR(-EINVAL); + } + + elf->entry = elf_hdr_e_entry(elf, elf->hdr_buf); + + return elf; +} + static struct elf_image *elf_check_init(const char *filename) { int ret, fd; @@ -213,20 +254,18 @@ static struct elf_image *elf_check_init(const char *filename) if (!elf) return ERR_PTR(-ENOMEM); - INIT_LIST_HEAD(&elf->list); - elf->low_addr = (void *) (unsigned long) -1; - elf->high_addr = 0; + elf_init_struct(elf); /* First pass is to read elf header only */ fd = open(filename, O_RDONLY); if (fd < 0) { - pr_err("could not open: %s\n", errno_str()); + pr_err("could not open: %m\n"); ret = -errno; goto err_free_elf; } if (read_full(fd, &hdr, sizeof(hdr)) < 0) { - pr_err("could not read elf header: %s\n", errno_str()); + pr_err("could not read elf header: %m\n"); close(fd); ret = -errno; goto err_free_elf; @@ -254,13 +293,13 @@ static struct elf_image *elf_check_init(const char *filename) */ fd = open(filename, O_RDONLY); if (fd < 0) { - pr_err("could not open: %s\n", errno_str()); + pr_err("could not open: %m\n"); ret = -errno; goto err_free_hdr_buf; } if (read_full(fd, elf->hdr_buf, hdr_size) < 0) { - pr_err("could not read elf program headers: %s\n", errno_str()); + pr_err("could not read elf program headers: %m\n"); ret = -errno; close(fd); goto err_free_hdr_buf; @@ -299,7 +338,10 @@ void elf_close(struct elf_image *elf) { elf_release_regions(elf); - free(elf->hdr_buf); - free(elf->filename); + if (elf->filename) { + free(elf->hdr_buf); + free(elf->filename); + } + free(elf); } |