Added arch framework for handling page faults
[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
10 struct elf_info
11 {
12         long entry;
13         long highest_addr;
14         long phdr;
15         int phnum;
16         int dynamic;
17         char interp[256];
18 };
19
20 int load_one_elf(struct proc* p, int fd, int pgoffset, struct elf_info* ei)
21 {
22         int ret = -1;
23         ei->phdr = -1;
24         ei->dynamic = 0;
25         ei->highest_addr = 0;
26
27         char* elf = (char*)kmalloc(PGSIZE,0);
28         if(!elf || read_page(p,fd,PADDR(elf),0) == -1)
29                 goto fail;
30
31         elf_t* elfhdr = (elf_t*)elf;
32         proghdr_t* proghdrs = (proghdr_t*)(elf+elfhdr->e_phoff);
33         if(elfhdr->e_phoff+elfhdr->e_phnum*sizeof(proghdr_t) > PGSIZE)
34                 goto fail;
35         if(elfhdr->e_phentsize != sizeof(proghdr_t))
36                 goto fail;
37
38         for(int i = 0; i < elfhdr->e_phnum; i++)
39         {
40                 proghdr_t* ph = proghdrs+i;
41                 if(ph->p_type == ELF_PROG_PHDR)
42                         ei->phdr = ph->p_va;
43                 if(ph->p_type == ELF_PROG_INTERP)
44                 {
45                         int maxlen = MIN(PGSIZE-ph->p_offset,sizeof(ei->interp));
46                         int len = strnlen(elf+ph->p_offset,maxlen);
47                         if(len < maxlen)
48                         {
49                                 memcpy(ei->interp,elf+ph->p_offset,maxlen+1);
50                                 ei->dynamic = 1;
51                         }
52                         else
53                                 goto fail;
54                 }
55
56                 if(ph->p_type == ELF_PROG_LOAD && ph->p_memsz)
57                 {
58                         if(ph->p_align % PGSIZE)
59                                 goto fail;
60                         if(ph->p_offset % PGSIZE != ph->p_va % PGSIZE)
61                                 goto fail;
62
63                         uintptr_t filestart = ROUNDDOWN(ph->p_offset,PGSIZE);
64                         uintptr_t fileend = ph->p_offset+ph->p_filesz;
65                         uintptr_t filesz = fileend-filestart;
66
67                         uintptr_t memstart = ROUNDDOWN(ph->p_va,PGSIZE);
68                         uintptr_t memend = ROUNDUP(ph->p_va + ph->p_memsz,PGSIZE);
69                         uintptr_t memsz = memend-memstart;
70                         if(memend > ei->highest_addr)
71                                 ei->highest_addr = memend;
72
73                         // mmap will zero the rest of the page if filesz % PGSIZE != 0
74                         if(filesz)
75                                 // TODO: waterman, figure out proper permissions
76                                 if(mmap(p, memstart+pgoffset*PGSIZE, filesz,
77                                         PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED,
78                                         fd, filestart/PGSIZE) == MAP_FAILED)
79                                         goto fail;
80
81                         filesz = ROUNDUP(filesz,PGSIZE);
82                         if(filesz < memsz)
83                                 if(mmap(p, memstart+filesz+pgoffset*PGSIZE, memsz-filesz,
84                                         PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_ANON,
85                                         -1, 0) == MAP_FAILED)
86                                         goto fail;
87                 }
88         }
89
90         ei->entry = elfhdr->e_entry + pgoffset*PGSIZE;
91         ei->phnum = elfhdr->e_phnum;
92
93         ret = 0;
94 fail:
95         kfree(elf);
96         return ret;
97 }
98
99 int load_elf(struct proc* p, const char* fn)
100 {
101         struct elf_info ei,interp_ei;
102         int fd = open_file(p,fn,0,0);
103         if(fd == -1 || load_one_elf(p,fd,0,&ei))
104                 return -1;
105         close_file(p,fd);
106
107         if(ei.dynamic)
108         {
109                 // plzplzplz let us use the stack and PADDR()
110                 char* str = kmalloc(sizeof(ei.interp),0);
111                 if(!str)
112                         return -1;
113                 memcpy(str,ei.interp,sizeof(ei.interp));
114                 int fd2 = open_file(p,str,0,0);
115                 kfree(str);
116
117                 if(fd2 == -1 || load_one_elf(p,fd2,1,&interp_ei))
118                         return -1;
119                 close_file(p,fd2);
120
121                 // fill in info for dynamic linker
122                 elf_aux_t auxp[] = {{ELF_AUX_PHDR,ei.phdr},
123                                     {ELF_AUX_PHENT,sizeof(proghdr_t)},
124                                     {ELF_AUX_PHNUM,ei.phnum},
125                                     {ELF_AUX_ENTRY,ei.entry},
126                                     {0,0}};
127
128                 // put auxp after argv, envp in procinfo
129                 int auxp_pos = -1;
130                 for(int i = 0, zeros = 0; i < PROCINFO_MAX_ARGP; i++)
131                         if(p->env_procinfo->argp[i] == NULL)
132                                 if(++zeros == 2)
133                                         auxp_pos = i+1;
134                 if(auxp_pos == -1 ||
135                    auxp_pos+sizeof(auxp)/sizeof(char*) >= PROCINFO_MAX_ARGP)
136                         return -1;
137                 memcpy(p->env_procinfo->argp+auxp_pos,auxp,sizeof(auxp));
138         }
139
140         uintptr_t core0_entry = ei.dynamic ? interp_ei.entry : ei.entry;
141         proc_init_trapframe(&p->env_tf,0,core0_entry,USTACKTOP);
142         p->env_entry = ei.entry;
143
144         uintptr_t stacksz = USTACK_NUM_PAGES*PGSIZE;
145         if(mmap(p,USTACKTOP-stacksz,stacksz,PROT_READ|PROT_WRITE,
146                 MAP_FIXED|MAP_ANON,-1,0) == MAP_FAILED)
147                 return -1;
148
149         // Set the heap bottom and top to just past where the text 
150         // region has been loaded
151         p->heap_bottom = (void*)ei.highest_addr;
152         p->heap_top = p->heap_bottom;
153
154         return 0;
155 }
156