c5e13227d0b5fdd6d69d3a109df1ceacd07461a9
[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 KERN64
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 /* We need the writable flag for ld.  Even though the elf header says it wants
18  * RX (and not W) for its main program header, it will page fault (eip 56f0,
19  * 46f0 after being relocated to 0x1000, va 0x20f4). */
20 static int load_one_elf(struct proc *p, struct file *f, uintptr_t pgoffset,
21                         elf_info_t *ei, bool writable)
22 {
23         int ret = -1;
24         ei->phdr = -1;
25         ei->dynamic = 0;
26         ei->highest_addr = 0;
27         off64_t f_off = 0;
28         void* phdrs = 0;
29         int mm_perms, mm_flags = MAP_FIXED;
30         
31         /* When reading on behalf of the kernel, we need to make sure no proc is
32          * "current".  This is a bit ghetto (TODO: KFOP) */
33         struct proc *cur_proc = current;
34         current = 0;
35
36         /* Read in ELF header. */
37         elf64_t elfhdr_storage;
38         elf32_t* elfhdr32 = (elf32_t*)&elfhdr_storage;
39         elf64_t* elfhdr64 = &elfhdr_storage;
40         if (f->f_op->read(f, (char*)elfhdr64, sizeof(elf64_t), &f_off)
41                 != sizeof(elf64_t)) {
42                 /* if you ever debug this, be sure to 0 out elfhrd_storage in advance */
43                 printk("[kernel] load_one_elf: failed to read file\n");
44                 goto fail;
45         }
46         if (elfhdr64->e_magic != ELF_MAGIC) {
47                 printk("[kernel] load_one_elf: file is not an elf!\n");
48                 goto fail;
49         }
50         bool elf32 = elfhdr32->e_ident[ELF_IDENT_CLASS] == ELFCLASS32;
51         bool elf64 = elfhdr64->e_ident[ELF_IDENT_CLASS] == ELFCLASS64;
52         if (elf64 == elf32) {
53                 printk("[kernel] load_one_elf: ID as both 32 and 64 bit\n");
54                 goto fail;
55         }
56         #ifndef KERN64
57         if (elf64) {
58                 printk("[kernel] load_one_elf: 64 bit elf on 32 bit kernel\n");
59                 goto fail;
60         }
61         #endif
62
63         size_t phsz = elf64 ? sizeof(proghdr64_t) : sizeof(proghdr32_t);
64         uint16_t e_phnum = elf_field(elfhdr, e_phnum);
65         uint16_t e_phoff = elf_field(elfhdr, e_phoff);
66
67         /* Read in program headers. */
68         if (e_phnum > 10000 || e_phoff % (elf32 ? 4 : 8) != 0) {
69                 printk("[kernel] load_one_elf: Bad program headers\n");
70                 goto fail;
71         }
72         phdrs = kmalloc(e_phnum * phsz, 0);
73         f_off = e_phoff;
74         if (!phdrs || f->f_op->read(f, phdrs, e_phnum * phsz, &f_off) !=
75                       e_phnum * phsz) {
76                 printk("[kernel] load_one_elf: could not get program headers\n");
77                 goto fail;
78         }
79         for (int i = 0; i < e_phnum; i++) {
80                 proghdr32_t* ph32 = (proghdr32_t*)phdrs + i;
81                 proghdr64_t* ph64 = (proghdr64_t*)phdrs + i;
82                 uint16_t p_type = elf_field(ph, p_type);
83                 uintptr_t p_va = elf_field(ph, p_va);
84                 uintptr_t p_offset = elf_field(ph, p_offset);
85                 uintptr_t p_align = elf_field(ph, p_align);
86                 uintptr_t p_memsz = elf_field(ph, p_memsz);
87                 uintptr_t p_filesz = elf_field(ph, p_filesz);
88                 uintptr_t p_flags = elf_field(ph, p_flags);
89
90                 /* Here's the ld hack, mentioned above */
91                 p_flags |= (writable ? ELF_PROT_WRITE : 0);
92                 /* All mmaps need to be fixed to their VAs.  If the program wants it to
93                  * be a writable region, we also need the region to be private. */
94                 mm_flags = MAP_FIXED | (p_flags & ELF_PROT_WRITE ? MAP_PRIVATE : 0);
95
96                 if (p_type == ELF_PROG_PHDR)
97                         ei->phdr = p_va;
98                 else if (p_type == ELF_PROG_INTERP) {
99                         f_off = p_offset;
100                         ssize_t maxlen = sizeof(ei->interp);
101                         ssize_t bytes = f->f_op->read(f, ei->interp, maxlen, &f_off);
102                         /* trying to catch errors.  don't know how big it could be, but it
103                          * should be at least 0. */
104                         if (bytes <= 0) {
105                                 printk("[kernel] load_one_elf: could not read ei->interp\n");
106                                 goto fail;
107                         }
108
109                         maxlen = MIN(maxlen, bytes);
110                         if (strnlen(ei->interp, maxlen) == maxlen) {
111                                 printk("[kernel] load_one_elf: interpreter name too long\n");
112                                 goto fail;
113                         }
114
115                         ei->dynamic = 1;
116                 }
117                 else if (p_type == ELF_PROG_LOAD && p_memsz) {
118                         if (p_align % PGSIZE) {
119                                 printk("[kernel] load_one_elf: not page aligned\n");
120                                 goto fail;
121                         }
122                         if (p_offset % PGSIZE != p_va % PGSIZE) {
123                                 printk("[kernel] load_one_elf: offset difference \n");
124                                 goto fail;
125                         }
126
127                         uintptr_t filestart = ROUNDDOWN(p_offset, PGSIZE);
128                         uintptr_t filesz = p_offset + p_filesz - filestart;
129
130                         uintptr_t memstart = ROUNDDOWN(p_va, PGSIZE);
131                         uintptr_t memsz = ROUNDUP(p_va + p_memsz, PGSIZE) - memstart;
132                         memstart += pgoffset * PGSIZE;
133
134                         if (memstart + memsz > ei->highest_addr)
135                                 ei->highest_addr = memstart + memsz;
136
137                         mm_perms = 0;
138                         mm_perms |= (p_flags & ELF_PROT_READ  ? PROT_READ : 0);
139                         mm_perms |= (p_flags & ELF_PROT_WRITE ? PROT_WRITE : 0);
140                         mm_perms |= (p_flags & ELF_PROT_EXEC  ? PROT_EXEC : 0);
141
142                         if (filesz) {
143                                 /* Due to elf-ghetto-ness, we need to zero the first part of
144                                  * the BSS from the last page of the data segment.  If we end
145                                  * on a partial page, we map it in separately with
146                                  * MAP_POPULATE so that we can zero the rest of it now. We
147                                  * translate to the KVA so we don't need to worry about using
148                                  * the proc's mapping */
149                                 uintptr_t partial = PGOFF(filesz);
150
151                                 if (filesz - partial) {
152                                         /* Map the complete pages. */
153                                         if (do_mmap(p, memstart, filesz - partial, mm_perms,
154                                                     mm_flags, f, filestart) == MAP_FAILED) {
155                                                 printk("[kernel] load_one_elf: complete mmap failed\n");
156                                                 goto fail;
157                                         }
158                                 }
159                                 /* Note that we (probably) only need to do this zeroing the end
160                                  * of a partial file page when we are dealing with
161                                  * ELF_PROT_WRITE-able PHs, and not for all cases.  */
162                                 if (partial) {
163                                         /* Need our own populated, private copy of the page so that
164                                          * we can zero the remainder - and not zero chunks of the
165                                          * real file in the page cache. */
166                                         mm_flags |= MAP_PRIVATE | MAP_POPULATE;
167
168                                         /* Map the final partial page. */
169                                         uintptr_t last_page = memstart + filesz - partial;
170                                         if (do_mmap(p, last_page, PGSIZE, mm_perms, mm_flags,
171                                                     f, filestart + filesz - partial) == MAP_FAILED) {
172                                                 printk("[kernel] load_one_elf: partial mmap failed\n");
173                                                 goto fail;
174                                         }
175
176                                         /* Zero the end of it. */
177                                         pte_t *pte = pgdir_walk(p->env_pgdir, (void*)last_page, 0);
178                                         assert(pte);
179                                         void* last_page_kva = ppn2kva(PTE2PPN(*pte));
180                                         memset(last_page_kva + partial, 0, PGSIZE - partial);
181
182                                         filesz = ROUNDUP(filesz, PGSIZE);
183                                 }
184                         }
185                         /* Any extra pages are mapped anonymously... (a bit weird) */
186                         if (filesz < memsz)
187                                 if (do_mmap(p, memstart + filesz, memsz-filesz,
188                                             PROT_READ | PROT_WRITE, MAP_PRIVATE,
189                                                 NULL, 0) == MAP_FAILED) {
190                                         printk("[kernel] load_one_elf: anon mmap failed\n");
191                                         goto fail;
192                                 }
193                 }
194         }
195         /* map in program headers anyway if not present in binary.
196          * useful for TLS in static programs. */
197         if (ei->phdr == -1) {
198                 uintptr_t filestart = ROUNDDOWN(e_phoff, PGSIZE);
199                 uintptr_t filesz = e_phoff + (e_phnum * phsz) - filestart;
200                 void *phdr_addr = do_mmap(p, 0, filesz, PROT_READ | PROT_WRITE,
201                                           MAP_PRIVATE, f, filestart);
202                 if (phdr_addr == MAP_FAILED) {
203                         printk("[kernel] load_one_elf: prog header mmap failed\n");
204                         goto fail;
205                 }
206                 ei->phdr = (long)phdr_addr + e_phoff;
207         }
208         ei->entry = elf_field(elfhdr, e_entry) + pgoffset*PGSIZE;
209         ei->phnum = e_phnum;
210         ei->elf64 = elf64;
211         ret = 0;
212         /* Fall-through */
213 fail:
214         if (phdrs)
215                 kfree(phdrs);
216         current = cur_proc;
217         return ret;
218 }
219
220 int load_elf(struct proc* p, struct file* f)
221 {
222         elf_info_t ei, interp_ei;
223         if (load_one_elf(p, f, 0, &ei, FALSE))
224                 return -1;
225
226         if (ei.dynamic) {
227                 struct file *interp = do_file_open(ei.interp, 0, 0);
228                 if (!interp)
229                         return -1;
230                 /* Load dynamic linker one page into the address space */
231                 int error = load_one_elf(p, interp, 1, &interp_ei, TRUE);
232                 kref_put(&interp->f_kref);
233                 if (error)
234                         return -1;
235         }
236
237         // fill in auxiliary info for dynamic linker/runtime
238         elf_aux_t auxp[] = {{ELF_AUX_PHDR, ei.phdr},
239                             {ELF_AUX_PHENT, sizeof(proghdr32_t)},
240                             {ELF_AUX_PHNUM, ei.phnum},
241                             {ELF_AUX_ENTRY, ei.entry},
242                             #ifdef __sparc_v8__
243                             {ELF_AUX_HWCAP, ELF_HWCAP_SPARC_FLUSH},
244                             #endif
245                             {0, 0}};
246
247         // put auxp after argv, envp in procinfo
248         int auxp_pos = -1;
249         for (int i = 0, zeros = 0; i < PROCINFO_MAX_ARGP; i++)
250                 if (p->procinfo->argp[i] == NULL)
251                         if (++zeros == 2)
252                                 auxp_pos = i + 1;
253         if (auxp_pos == -1 ||
254             auxp_pos + sizeof(auxp) / sizeof(char*) >= PROCINFO_MAX_ARGP)
255                 return -1;
256         memcpy(p->procinfo->argp+auxp_pos,auxp,sizeof(auxp));
257
258         uintptr_t core0_entry = ei.dynamic ? interp_ei.entry : ei.entry;
259         proc_init_trapframe(&p->env_tf,0,core0_entry,USTACKTOP);
260         p->env_entry = ei.entry;
261
262         int flags = MAP_FIXED | MAP_ANONYMOUS;
263         #ifdef __sparc_v8__
264         flags |= MAP_POPULATE; // SPARC stacks must be mapped in
265         #endif
266         uintptr_t stacksz = USTACK_NUM_PAGES*PGSIZE;
267         if (do_mmap(p, USTACKTOP-stacksz, stacksz, PROT_READ | PROT_WRITE,
268                     flags, NULL, 0) == MAP_FAILED)
269                 return -1;
270
271         // Set the heap bottom and top to just past where the text 
272         // region has been loaded
273         p->heap_top = (void*)ei.highest_addr;
274         p->procinfo->heap_bottom = p->heap_top;
275
276         return 0;
277 }
278