4f9601b95c5d2769e812f41dc287220640239c33
[akaros.git] / user / vmm / load_elf.c
1 /* Copyright (c) 2017 Google Inc.
2  * See LICENSE for details.
3  *
4  * ELF loading. */
5
6 #include <stdio.h>
7 #include <fcntl.h>
8 #include <unistd.h>
9 #include <libelf.h>
10
11 /* load_elf loads and ELF file. This is almost always a kernel.
12  * We assume that memory is set up correctly, and it will go hard
13  * with you if it is not. The reference parameter records the highest
14  * address we wrote. The initrd can go there.*/
15 uintptr_t
16 load_elf(char *filename, uint64_t offset, uint64_t *highest,
17          Elf64_Ehdr *ehdr_out)
18 {
19         Elf64_Ehdr *ehdr;
20         Elf *elf;
21         size_t phnum = 0;
22         Elf64_Phdr *hdrs;
23         int fd;
24         uintptr_t ret;
25         uintptr_t kern_end = 0;
26
27         elf_version(EV_CURRENT);
28         fd = open(filename, O_RDONLY);
29         if (fd < 0) {
30                 fprintf(stderr, "Can't open %s: %r\n", filename);
31                 return 0;
32         }
33
34         elf = elf_begin(fd, ELF_C_READ, NULL);
35         if (elf == NULL) {
36                 fprintf(stderr, "%s: cannot read %s ELF file.\n", __func__, filename);
37                 close(fd);
38                 return 0;
39         }
40
41         ehdr = elf64_getehdr(elf);
42         if (ehdr == NULL) {
43                 fprintf(stderr, "%s: cannot get exec header of %s.\n",
44                         __func__, filename);
45                 goto fail;
46         }
47         fprintf(stderr, "%s ELF entry point is %p\n", filename,
48                 (void *)ehdr->e_entry);
49
50         if (elf_getphdrnum(elf, &phnum) < 0) {
51                 fprintf(stderr, "%s: cannot get program header num of %s.\n",
52                         __func__, filename);
53                 goto fail;
54         }
55         fprintf(stderr, "%s has %p program headers\n", filename, phnum);
56
57         hdrs = elf64_getphdr(elf);
58         if (hdrs == NULL) {
59                 fprintf(stderr, "%s: cannot get program headers of %s.\n",
60                         __func__, filename);
61                 goto fail;
62         }
63
64         for (int i = 0; i < phnum; i++) {
65                 size_t tot;
66                 Elf64_Phdr *h = &hdrs[i];
67                 uintptr_t pa;
68
69                 fprintf(stderr,
70                         "%d: type 0x%lx flags 0x%lx  offset 0x%lx vaddr 0x%lx\npaddr 0x%lx size 0x%lx  memsz 0x%lx align 0x%lx\n",
71                         i,
72                         h->p_type,              /* Segment type */
73                         h->p_flags,             /* Segment flags */
74                         h->p_offset,            /* Segment file offset */
75                         h->p_vaddr,             /* Segment virtual address */
76                         h->p_paddr,             /* Segment physical address */
77                         h->p_filesz,            /* Segment size in file */
78                         h->p_memsz,             /* Segment size in memory */
79                         h->p_align              /* Segment alignment */);
80                 if (h->p_type != PT_LOAD)
81                         continue;
82                 if ((h->p_flags & (PF_R | PF_W | PF_X)) == 0)
83                         continue;
84
85                 pa = h->p_paddr;
86                 fprintf(stderr,
87                         "Read header %d @offset %p to %p (elf PA is %p) %d bytes:",
88                         i, h->p_offset, pa, h->p_paddr, h->p_filesz);
89                 tot = 0;
90                 while (tot < h->p_filesz) {
91                         int amt = pread(fd, (void *)(pa + tot + offset), h->p_filesz - tot,
92                                         h->p_offset + tot);
93
94                         if (amt < 1)
95                                 break;
96                         tot += amt;
97                 }
98                 fprintf(stderr, "read a total of %d bytes\n", tot);
99                 if (tot < h->p_filesz) {
100                         fprintf(stderr, "%s: got %d bytes, wanted %d bytes\n",
101                                 filename, tot, h->p_filesz);
102                         goto fail;
103                 }
104                 if ((h->p_paddr + h->p_memsz) > kern_end)
105                         kern_end = h->p_paddr + h->p_memsz;
106         }
107
108         close(fd);
109         ret = ehdr->e_entry + offset;
110
111         // Save the values in the header, if the caller wanted them
112         if (ehdr_out)
113                 *ehdr_out = *ehdr;
114
115         elf_end(elf);
116         if (highest)
117                 *highest = kern_end;
118         return ret;
119 fail:
120         close(fd);
121         elf_end(elf);
122         return 0;
123 }