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