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