Checkin of Andrews stuff needed to compile glibc
[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                                 if(mmap(p, memstart+pgoffset*PGSIZE, filesz,
70                                         PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED,
71                                         fd, filestart/PGSIZE) == MAP_FAILED)
72                                         goto fail;
73
74                         filesz = ROUNDUP(filesz,PGSIZE);
75                         if(filesz < memsz)
76                                 if(mmap(p, memstart+filesz+pgoffset*PGSIZE, memsz-filesz,
77                                         PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_ANON,
78                                         -1, 0) == MAP_FAILED)
79                                         goto fail;
80                 }
81         }
82
83         ei->entry = elfhdr->e_entry + pgoffset*PGSIZE;
84         ei->phnum = elfhdr->e_phnum;
85
86         ret = 0;
87 fail:
88         kfree(elf);
89         return ret;
90 }
91
92 int load_elf(struct proc* p, const char* fn)
93 {
94         struct elf_info ei,interp_ei;
95         int fd = open_file(p,fn,0,0);
96         if(fd == -1 || load_one_elf(p,fd,0,&ei))
97                 return -1;
98         close_file(p,fd);
99
100         if(ei.dynamic)
101         {
102                 int fd2 = open_file(p,ei.interp,0,0);
103                 if(fd2 == -1 || load_one_elf(p,fd2,1,&interp_ei))
104                         return -1;
105                 close_file(p,fd2);
106
107                 // fill in info for dynamic linker
108                 elf_aux_t auxp[] = {{ELF_AUX_PHDR,ei.phdr},
109                                     {ELF_AUX_PHENT,sizeof(proghdr_t)},
110                                     {ELF_AUX_PHNUM,ei.phnum},
111                                     {ELF_AUX_ENTRY,ei.entry},
112                                     {0,0}};
113
114                 // put auxp after argv, envp in procinfo
115                 int auxp_pos = -1;
116                 for(int i = 0, zeros = 0; i < PROCINFO_MAX_ARGP; i++)
117                         if(p->env_procinfo->argp[i] == NULL)
118                                 if(++zeros == 2)
119                                         auxp_pos = i+1;
120                 if(auxp_pos == -1 ||
121                    auxp_pos+sizeof(auxp)/sizeof(char*) >= PROCINFO_MAX_ARGP)
122                         return -1;
123                 memcpy(p->env_procinfo->argp+auxp_pos,auxp,sizeof(auxp));
124         }
125
126         intptr_t core0_entry = ei.dynamic ? interp_ei.entry : ei.entry;
127         proc_set_program_counter(&p->env_tf,core0_entry);
128         p->env_entry = ei.entry;
129
130         uintptr_t stacksz = USTACK_NUM_PAGES*PGSIZE;
131         if(mmap(p,USTACKTOP-stacksz,stacksz,PROT_READ|PROT_WRITE,
132                 MAP_FIXED|MAP_ANON,-1,0) == MAP_FAILED)
133                 return -1;
134
135         return 0;
136 }
137
138 intreg_t sys_exec(struct proc* p, const char fn[MAX_PATH_LEN], procinfo_t* pi)
139 {
140         if(p->state != PROC_RUNNING_S)
141                 return -1;
142
143         char kfn[PGSIZE];
144         if(memcpy_from_user(p,kfn,fn,PGSIZE))
145                 return -1;
146
147         if(memcpy_from_user(p,p->env_procinfo,pi,sizeof(procinfo_t)))
148         {
149                 proc_destroy(p);
150                 return -1;
151         }
152         proc_init_procinfo(p);
153
154         env_segment_free(p,0,USTACKTOP);
155         proc_init_trapframe(current_tf,0);
156
157         if(load_elf(p,kfn))
158         {
159                 proc_destroy(p);
160                 return -1;
161         }
162
163         return 0;
164 }
165