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