summaryrefslogtreecommitdiffstats
path: root/common/elf.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/elf.c')
-rw-r--r--common/elf.c104
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);
}