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