elf loader now mostly supports 64b elfs
[akaros.git] / kern / src / elf.c
1 #include <mm.h>
2 #include <frontend.h>
3 #include <string.h>
4 #include <ros/mman.h>
5 #include <kmalloc.h>
6 #include <syscall.h>
7 #include <elf.h>
8 #include <pmap.h>
9 #include <smp.h>
10 #include <arch/arch.h>
11
12 #ifdef KERN64
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 static int load_one_elf(struct proc *p, struct file *f, int pgoffset,
19                         elf_info_t *ei)
20 {
21         int ret = -1;
22         ei->phdr = -1;
23         ei->dynamic = 0;
24         ei->highest_addr = 0;
25         off_t f_off = 0;
26
27         /* assume program headers fit in a page.  if this isn't true, change the
28          * code below that maps in program headers */
29         char* elf = (char*)kmalloc(PGSIZE, 0);
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         if (!elf || f->f_op->read(f, elf, PGSIZE, &f_off) == -1)
36                 goto fail;
37         current = cur_proc;
38
39         elf32_t* elfhdr32 = (elf32_t*)elf;
40         elf64_t* elfhdr64 = (elf64_t*)elf;
41         bool elf32 = elfhdr32->e_ident[ELF_IDENT_CLASS] == ELFCLASS32;
42         bool elf64 = elfhdr32->e_ident[ELF_IDENT_CLASS] == ELFCLASS64;
43         if (!elf64 && !elf32)
44                 goto fail;
45         #ifndef KERN64
46         if(elf64)
47                 goto fail;
48         #endif
49         
50         proghdr32_t* proghdrs32 = (proghdr32_t*)(elf + elfhdr32->e_phoff);
51         proghdr64_t* proghdrs64 = (proghdr64_t*)(elf + elfhdr64->e_phoff);
52         uintptr_t e_phoff = elf_field(elfhdr, e_phoff);
53         size_t phsz = elf64 ? sizeof(proghdr64_t) : sizeof(proghdr32_t);
54         uint16_t e_phnum = elf_field(elfhdr, e_phnum);
55         // we don't support prog hdrs extending past the first elf page
56         if (e_phoff + e_phnum * phsz > PGSIZE)
57                 goto fail;
58
59         for (int i = 0; i < e_phnum; i++) {
60                 proghdr32_t* ph32 = proghdrs32+i;
61                 proghdr64_t* ph64 = proghdrs64+i;
62                 uint16_t p_type = elf_field(ph, p_type);
63                 uintptr_t p_va = elf_field(ph, p_va);
64                 uintptr_t p_offset = elf_field(ph, p_offset);
65                 uintptr_t p_align = elf_field(ph, p_align);
66                 uintptr_t p_memsz = elf_field(ph, p_memsz);
67                 uintptr_t p_filesz = elf_field(ph, p_filesz);
68
69                 if (p_type == ELF_PROG_PHDR)
70                         ei->phdr = elf_field(ph, p_va);
71                 if (p_type == ELF_PROG_INTERP) {
72                         int maxlen = MIN(PGSIZE - p_offset, sizeof(ei->interp));
73                         int len = strnlen(elf + p_offset, maxlen);
74                         if (len < maxlen) {
75                                 memcpy(ei->interp, elf + p_offset, maxlen + 1);
76                                 ei->dynamic = 1;
77                         }
78                         else
79                                 goto fail;
80                 }
81
82                 if (p_type == ELF_PROG_LOAD && p_memsz) {
83                         if (p_align % PGSIZE)
84                                 goto fail;
85                         if (p_offset % PGSIZE != p_va % PGSIZE)
86                                 goto fail;
87
88                         uintptr_t filestart = ROUNDDOWN(p_offset, PGSIZE);
89                         uintptr_t fileend = p_offset + p_filesz;
90                         uintptr_t filesz = fileend - filestart;
91
92                         uintptr_t memstart = ROUNDDOWN(p_va, PGSIZE);
93                         uintptr_t memend = ROUNDUP(p_va + p_memsz, PGSIZE);
94                         uintptr_t memsz = memend - memstart;
95                         if (memend > ei->highest_addr)
96                                 ei->highest_addr = memend;
97                         /* This needs to be a PRIVATE mapping, and the stuff after the file
98                          * needs to be zeroed. */
99                         if (filesz) {
100                                 /* TODO: figure out proper permissions from the elf */
101                                 if (do_mmap(p, memstart + pgoffset * PGSIZE, filesz,
102                                            PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE,
103                                            f, filestart) == MAP_FAILED)
104                                         goto fail;
105                                 /* Due to elf-ghetto-ness, we need to zero the first part of the
106                                  * BSS from the last page of the data segment.  We translate to
107                                  * the KVA so we don't need to worry about using the proc's
108                                  * mapping */
109                                 uintptr_t z_s = memstart + pgoffset * PGSIZE + filesz;
110                                 pte_t *pte = pgdir_walk(p->env_pgdir, (void*)z_s, 0);
111                                 assert(pte);
112                                 uintptr_t kva_z_s = (uintptr_t)ppn2kva(PTE2PPN(*pte)) + PGOFF(z_s);
113                                 uintptr_t kva_z_e = ROUNDUP(kva_z_s, PGSIZE);
114                                 memset((void*)kva_z_s, 0, kva_z_e - kva_z_s);
115                                 filesz = ROUNDUP(filesz, PGSIZE);
116                         }
117                         /* Any extra pages are mapped anonymously... (a bit weird) */
118                         if (filesz < memsz)
119                                 if (do_mmap(p, memstart + filesz + pgoffset*PGSIZE, memsz-filesz,
120                                            PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_ANON,
121                                            NULL, 0) == MAP_FAILED)
122                                         goto fail;
123                 }
124         }
125         /* map in program headers anyway if not present in binary.
126          * useful for TLS in static programs. */
127         if (ei->phdr == -1) {
128                 void *phdr_addr = do_mmap(p, MMAP_LOWEST_VA, PGSIZE, PROT_READ, 0, f,
129                                           0);
130                 if (phdr_addr == MAP_FAILED)
131                         goto fail;
132                 ei->phdr = (long)phdr_addr + e_phoff;
133         }
134         ei->entry = elf_field(elfhdr, e_entry) + pgoffset*PGSIZE;
135         ei->phnum = e_phnum;
136         ei->elf64 = elf64;
137         ret = 0;
138 fail:
139         kfree(elf);
140         return ret;
141 }
142
143 int load_elf(struct proc* p, struct file* f)
144 {
145         elf_info_t ei, interp_ei;
146         if (load_one_elf(p, f, 0,& ei))
147                 return -1;
148
149         if (ei.dynamic) {
150                 struct file *interp = do_file_open(ei.interp, 0, 0);
151                 if (!interp)
152                         return -1;
153                 /* careful, this could conflict with the mmap from the TLS up above */
154                 int error = load_one_elf(p, interp, 2, &interp_ei);
155                 kref_put(&interp->f_kref);
156                 if (error)
157                         return -1;
158         }
159
160         // fill in auxiliary info for dynamic linker/runtime
161         elf_aux_t auxp[] = {{ELF_AUX_PHDR, ei.phdr},
162                             {ELF_AUX_PHENT, sizeof(proghdr32_t)},
163                             {ELF_AUX_PHNUM, ei.phnum},
164                             {ELF_AUX_ENTRY, ei.entry},
165                             #ifdef __sparc_v8__
166                             {ELF_AUX_HWCAP, ELF_HWCAP_SPARC_FLUSH},
167                             #endif
168                             {0, 0}};
169
170         // put auxp after argv, envp in procinfo
171         int auxp_pos = -1;
172         for (int i = 0, zeros = 0; i < PROCINFO_MAX_ARGP; i++)
173                 if (p->procinfo->argp[i] == NULL)
174                         if (++zeros == 2)
175                                 auxp_pos = i + 1;
176         if (auxp_pos == -1 ||
177             auxp_pos + sizeof(auxp) / sizeof(char*) >= PROCINFO_MAX_ARGP)
178                 return -1;
179         memcpy(p->procinfo->argp+auxp_pos,auxp,sizeof(auxp));
180
181         uintptr_t core0_entry = ei.dynamic ? interp_ei.entry : ei.entry;
182         proc_init_trapframe(&p->env_tf,0,core0_entry,USTACKTOP);
183         p->env_entry = ei.entry;
184
185         // map in stack using POPULATE (because SPARC requires it)
186         uintptr_t stacksz = USTACK_NUM_PAGES*PGSIZE;
187         if (do_mmap(p, USTACKTOP-stacksz, stacksz, PROT_READ | PROT_WRITE,
188                     MAP_FIXED | MAP_ANONYMOUS | MAP_POPULATE, NULL, 0) == MAP_FAILED)
189                 return -1;
190
191         // Set the heap bottom and top to just past where the text 
192         // region has been loaded
193         p->heap_top = (void*)ei.highest_addr;
194         p->procinfo->heap_bottom = p->heap_top;
195
196         return 0;
197 }
198