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