d899f9a767d3a7014e6e23b6b0d14953ad9bf1f0
[akaros.git] / kern / src / elf.c
1 #include <mm.h>
2 #include <frontend.h>
3 #include <string.h>
4 #include <kmalloc.h>
5 #include <syscall.h>
6 #include <elf.h>
7 #include <pmap.h>
8 #include <smp.h>
9 #include <arch/arch.h>
10
11 #ifdef CONFIG_64BIT
12 # define elf_field(obj, field) (elf64 ? (obj##64)->field : (obj##32)->field)
13 #else
14 # define elf_field(obj, field) ((obj##32)->field)
15 #endif
16
17 /* Check if the file is valid elf file (i.e. by checking for ELF_MAGIC in the
18  * header) */
19 bool is_valid_elf(struct file *f)
20 {
21         elf64_t h;
22         off64_t o = 0;
23         struct proc *c = switch_to(0);
24
25         if (f->f_op->read(f, (char*)&h, sizeof(elf64_t), &o) != sizeof(elf64_t)) {
26                 goto fail;
27         }
28         if (h.e_magic != ELF_MAGIC) {
29                 goto fail;
30         }
31 success:
32         switch_back(0, c);
33         return TRUE;
34 fail:
35         switch_back(0, c);
36         return FALSE;
37 }
38
39 /* We need the writable flag for ld.  Even though the elf header says it wants
40  * RX (and not W) for its main program header, it will page fault (eip 56f0,
41  * 46f0 after being relocated to 0x1000, va 0x20f4). */
42 static int load_one_elf(struct proc *p, struct file *f, uintptr_t pgoffset,
43                         elf_info_t *ei, bool writable)
44 {
45         int ret = -1;
46         ei->phdr = -1;
47         ei->dynamic = 0;
48         ei->highest_addr = 0;
49         off64_t f_off = 0;
50         void* phdrs = 0;
51         int mm_perms, mm_flags = MAP_FIXED;
52         
53         /* When reading on behalf of the kernel, we need to make sure no proc is
54          * "current".  This is a bit ghetto (TODO: KFOP) */
55         struct proc *old_proc = switch_to(0);
56
57         /* Read in ELF header. */
58         elf64_t elfhdr_storage;
59         elf32_t* elfhdr32 = (elf32_t*)&elfhdr_storage;
60         elf64_t* elfhdr64 = &elfhdr_storage;
61         if (f->f_op->read(f, (char*)elfhdr64, sizeof(elf64_t), &f_off)
62                 != sizeof(elf64_t)) {
63                 /* if you ever debug this, be sure to 0 out elfhrd_storage in advance */
64                 printk("[kernel] load_one_elf: failed to read file\n");
65                 goto fail;
66         }
67         if (elfhdr64->e_magic != ELF_MAGIC) {
68                 printk("[kernel] load_one_elf: file is not an elf!\n");
69                 goto fail;
70         }
71         bool elf32 = elfhdr32->e_ident[ELF_IDENT_CLASS] == ELFCLASS32;
72         bool elf64 = elfhdr64->e_ident[ELF_IDENT_CLASS] == ELFCLASS64;
73         if (elf64 == elf32) {
74                 printk("[kernel] load_one_elf: ID as both 32 and 64 bit\n");
75                 goto fail;
76         }
77         #ifndef CONFIG_64BIT
78         if (elf64) {
79                 printk("[kernel] load_one_elf: 64 bit elf on 32 bit kernel\n");
80                 goto fail;
81         }
82         #endif
83         /* Not sure what RISCV's 64 bit kernel can do here, so this check is x86
84          * only */
85         #ifdef CONFIG_X86
86         if (elf32) {
87                 printk("[kernel] load_one_elf: 32 bit elf on 64 bit kernel\n");
88                 goto fail;
89         }
90         #endif
91
92         size_t phsz = elf64 ? sizeof(proghdr64_t) : sizeof(proghdr32_t);
93         uint16_t e_phnum = elf_field(elfhdr, e_phnum);
94         uint16_t e_phoff = elf_field(elfhdr, e_phoff);
95
96         /* Read in program headers. */
97         if (e_phnum > 10000 || e_phoff % (elf32 ? 4 : 8) != 0) {
98                 printk("[kernel] load_one_elf: Bad program headers\n");
99                 goto fail;
100         }
101         phdrs = kmalloc(e_phnum * phsz, 0);
102         f_off = e_phoff;
103         if (!phdrs || f->f_op->read(f, phdrs, e_phnum * phsz, &f_off) !=
104                       e_phnum * phsz) {
105                 printk("[kernel] load_one_elf: could not get program headers\n");
106                 goto fail;
107         }
108         for (int i = 0; i < e_phnum; i++) {
109                 proghdr32_t* ph32 = (proghdr32_t*)phdrs + i;
110                 proghdr64_t* ph64 = (proghdr64_t*)phdrs + i;
111                 uint16_t p_type = elf_field(ph, p_type);
112                 uintptr_t p_va = elf_field(ph, p_va);
113                 uintptr_t p_offset = elf_field(ph, p_offset);
114                 uintptr_t p_align = elf_field(ph, p_align);
115                 uintptr_t p_memsz = elf_field(ph, p_memsz);
116                 uintptr_t p_filesz = elf_field(ph, p_filesz);
117                 uintptr_t p_flags = elf_field(ph, p_flags);
118
119                 /* Here's the ld hack, mentioned above */
120                 p_flags |= (writable ? ELF_PROT_WRITE : 0);
121                 /* All mmaps need to be fixed to their VAs.  If the program wants it to
122                  * be a writable region, we also need the region to be private. */
123                 mm_flags = MAP_FIXED | (p_flags & ELF_PROT_WRITE ? MAP_PRIVATE : 0);
124
125                 if (p_type == ELF_PROG_PHDR)
126                         ei->phdr = p_va;
127                 else if (p_type == ELF_PROG_INTERP) {
128                         f_off = p_offset;
129                         ssize_t maxlen = sizeof(ei->interp);
130                         ssize_t bytes = f->f_op->read(f, ei->interp, maxlen, &f_off);
131                         /* trying to catch errors.  don't know how big it could be, but it
132                          * should be at least 0. */
133                         if (bytes <= 0) {
134                                 printk("[kernel] load_one_elf: could not read ei->interp\n");
135                                 goto fail;
136                         }
137
138                         maxlen = MIN(maxlen, bytes);
139                         if (strnlen(ei->interp, maxlen) == maxlen) {
140                                 printk("[kernel] load_one_elf: interpreter name too long\n");
141                                 goto fail;
142                         }
143
144                         ei->dynamic = 1;
145                 }
146                 else if (p_type == ELF_PROG_LOAD && p_memsz) {
147                         if (p_align % PGSIZE) {
148                                 printk("[kernel] load_one_elf: not page aligned\n");
149                                 goto fail;
150                         }
151                         if (p_offset % PGSIZE != p_va % PGSIZE) {
152                                 printk("[kernel] load_one_elf: offset difference \n");
153                                 goto fail;
154                         }
155
156                         uintptr_t filestart = ROUNDDOWN(p_offset, PGSIZE);
157                         uintptr_t filesz = p_offset + p_filesz - filestart;
158
159                         uintptr_t memstart = ROUNDDOWN(p_va, PGSIZE);
160                         uintptr_t memsz = ROUNDUP(p_va + p_memsz, PGSIZE) - memstart;
161                         memstart += pgoffset * PGSIZE;
162
163                         if (memstart + memsz > ei->highest_addr)
164                                 ei->highest_addr = memstart + memsz;
165
166                         mm_perms = 0;
167                         mm_perms |= (p_flags & ELF_PROT_READ  ? PROT_READ : 0);
168                         mm_perms |= (p_flags & ELF_PROT_WRITE ? PROT_WRITE : 0);
169                         mm_perms |= (p_flags & ELF_PROT_EXEC  ? PROT_EXEC : 0);
170
171                         if (filesz) {
172                                 /* Due to elf-ghetto-ness, we need to zero the first part of
173                                  * the BSS from the last page of the data segment.  If we end
174                                  * on a partial page, we map it in separately with
175                                  * MAP_POPULATE so that we can zero the rest of it now. We
176                                  * translate to the KVA so we don't need to worry about using
177                                  * the proc's mapping */
178                                 uintptr_t partial = PGOFF(filesz);
179
180                                 if (filesz - partial) {
181                                         /* Map the complete pages. */
182                                         if (do_mmap(p, memstart, filesz - partial, mm_perms,
183                                                     mm_flags, f, filestart) == MAP_FAILED) {
184                                                 printk("[kernel] load_one_elf: complete mmap failed\n");
185                                                 goto fail;
186                                         }
187                                 }
188                                 /* Note that we (probably) only need to do this zeroing the end
189                                  * of a partial file page when we are dealing with
190                                  * ELF_PROT_WRITE-able PHs, and not for all cases.  */
191                                 if (partial) {
192                                         /* Need our own populated, private copy of the page so that
193                                          * we can zero the remainder - and not zero chunks of the
194                                          * real file in the page cache. */
195                                         mm_flags |= MAP_PRIVATE | MAP_POPULATE;
196
197                                         /* Map the final partial page. */
198                                         uintptr_t last_page = memstart + filesz - partial;
199                                         if (do_mmap(p, last_page, PGSIZE, mm_perms, mm_flags,
200                                                     f, filestart + filesz - partial) == MAP_FAILED) {
201                                                 printk("[kernel] load_one_elf: partial mmap failed\n");
202                                                 goto fail;
203                                         }
204
205                                         /* Zero the end of it.  This is a huge pain in the ass.  The
206                                          * filesystems should zero out the last bits of a page if
207                                          * the file doesn't fill the last page.  But we're dealing
208                                          * with windows into otherwise complete files. */
209                                         pte_t pte = pgdir_walk(p->env_pgdir, (void*)last_page, 0);
210                                         /* if we were able to get a PTE, then there is a real page
211                                          * backing the VMR, and we need to zero the excess.  if
212                                          * there isn't, then the page fault code should handle it.
213                                          * since we set populate above, we should have a PTE, except
214                                          * in cases where the offset + len window exceeded the file
215                                          * size.  in this case, we let them mmap it, but didn't
216                                          * populate it.  there will be a PF right away if someone
217                                          * tries to use this.  check out do_mmap for more info. */
218                                         if (pte_walk_okay(pte)) {
219                                                 void* last_page_kva = KADDR(pte_get_paddr(pte));
220                                                 memset(last_page_kva + partial, 0, PGSIZE - partial);
221                                         }
222
223                                         filesz = ROUNDUP(filesz, PGSIZE);
224                                 }
225                         }
226                         /* Any extra pages are mapped anonymously... (a bit weird) */
227                         if (filesz < memsz)
228                                 if (do_mmap(p, memstart + filesz, memsz-filesz,
229                                             PROT_READ | PROT_WRITE, MAP_PRIVATE,
230                                                 NULL, 0) == MAP_FAILED) {
231                                         printk("[kernel] load_one_elf: anon mmap failed\n");
232                                         goto fail;
233                                 }
234                 }
235         }
236         /* map in program headers anyway if not present in binary.
237          * useful for TLS in static programs. */
238         if (ei->phdr == -1) {
239                 uintptr_t filestart = ROUNDDOWN(e_phoff, PGSIZE);
240                 uintptr_t filesz = e_phoff + (e_phnum * phsz) - filestart;
241                 void *phdr_addr = do_mmap(p, 0, filesz, PROT_READ | PROT_WRITE,
242                                           MAP_PRIVATE, f, filestart);
243                 if (phdr_addr == MAP_FAILED) {
244                         printk("[kernel] load_one_elf: prog header mmap failed\n");
245                         goto fail;
246                 }
247                 ei->phdr = (long)phdr_addr + e_phoff;
248         }
249         ei->entry = elf_field(elfhdr, e_entry) + pgoffset*PGSIZE;
250         ei->phnum = e_phnum;
251         ei->elf64 = elf64;
252         ret = 0;
253         /* Fall-through */
254 fail:
255         if (phdrs)
256                 kfree(phdrs);
257         switch_back(0, old_proc);
258         return ret;
259 }
260
261 int load_elf(struct proc* p, struct file* f,
262              int argc, char *argv[], int envc, char *envp[])
263 {
264         elf_info_t ei, interp_ei;
265         if (load_one_elf(p, f, 0, &ei, FALSE))
266                 return -1;
267
268         if (ei.dynamic) {
269                 struct file *interp = do_file_open(ei.interp, 0, 0);
270                 if (!interp)
271                         return -1;
272                 /* Load dynamic linker at 1M. Obvious MIB joke avoided.
273                  * It used to be loaded at page 1, but the existence of valid addresses
274                  * that low masked bad derefs through NULL pointer structs. This in turn
275                  * helped us waste a full day debugging a bug in the Go runtime. True!
276                  * Note that MMAP_LOWEST_VA also has this value but we want to make this
277                  * explicit. */
278                 int error = load_one_elf(p, interp, MiB>>12, &interp_ei, TRUE);
279                 kref_put(&interp->f_kref);
280                 if (error)
281                         return -1;
282         }
283
284         /* Copy the contents of the argenv array into procinfo. This is only
285          * temporary so that we can verify everything works with the new structure
286          * up to this point.  Soon we will map this stuff on the stack properly, as
287          * per the SYSV ABI. */
288         if (argc + 1 + envc + 1 > PROCINFO_MAX_ARGP)
289                 return -1;
290         int pos = 0;
291         for(int i = 0; i < argc; i++) {
292                 int len = strlen(argv[i]) + 1;
293                 if(pos + len > PROCINFO_ARGBUF_SIZE)
294                         return -1;
295                 p->procinfo->argp[i] = ((procinfo_t*)UINFO)->argbuf + pos;
296                 memcpy(p->procinfo->argbuf + pos, argv[i], len);
297                 pos += len;
298         }
299         p->procinfo->argp[argc] = NULL;
300         for(int i = 0; i < envc; i++) {
301                 int len = strlen(envp[i]) + 1;
302                 if(pos + len > PROCINFO_ARGBUF_SIZE)
303                         return -1;
304                 p->procinfo->argp[argc + 1 + i] = ((procinfo_t*)UINFO)->argbuf + pos;
305                 memcpy(p->procinfo->argbuf + pos, envp[i], len);
306                 pos += len;
307         }
308         p->procinfo->argp[argc + 1 + envc] = NULL;
309
310         // fill in auxiliary info for dynamic linker/runtime
311         elf_aux_t auxp[] = {{ELF_AUX_PHDR, ei.phdr},
312                             {ELF_AUX_PHENT, sizeof(proghdr32_t)},
313                             {ELF_AUX_PHNUM, ei.phnum},
314                             {ELF_AUX_ENTRY, ei.entry},
315                             {0, 0}};
316
317         // put auxp after argv, envp in procinfo
318         int auxp_pos = -1;
319         for (int i = 0, zeros = 0; i < PROCINFO_MAX_ARGP; i++)
320                 if (p->procinfo->argp[i] == NULL)
321                         if (++zeros == 2)
322                                 auxp_pos = i + 1;
323         if (auxp_pos == -1 ||
324             auxp_pos + sizeof(auxp) / sizeof(char*) >= PROCINFO_MAX_ARGP)
325                 return -1;
326         memcpy(p->procinfo->argp+auxp_pos,auxp,sizeof(auxp));
327
328         uintptr_t core0_entry = ei.dynamic ? interp_ei.entry : ei.entry;
329         proc_init_ctx(&p->scp_ctx, 0, core0_entry, USTACKTOP, 0);
330         p->env_entry = ei.entry;
331
332         int flags = MAP_FIXED | MAP_ANONYMOUS;
333         uintptr_t stacksz = USTACK_NUM_PAGES*PGSIZE;
334         if (do_mmap(p, USTACKTOP-stacksz, stacksz, PROT_READ | PROT_WRITE,
335                     flags, NULL, 0) == MAP_FAILED)
336                 return -1;
337
338         // Set the heap bottom and top to just past where the text 
339         // region has been loaded
340         p->heap_top = (void*)ei.highest_addr;
341         p->procinfo->heap_bottom = p->heap_top;
342
343         return 0;
344 }
345