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