Fixed bug in reading elf headers
[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         off_t f_off = 0;
28         physaddr_t old_cr3 = rcr3();
29
30         /* Load the proc's address space, in case we need to directly write to its
31          * pages (like when we zero some of the BSS) */
32         lcr3(p->env_cr3);
33
34         // assume program headers fit in a page.
35         // if this isn't true, change the code below that maps in program headers
36         char* elf = (char*)kmalloc(PGSIZE,0);
37         
38         /* When reading on behalf of the kernel, we need to make sure no proc is
39          * "current".  This is a bit ghetto (TODO: KFOP) */
40         struct proc *cur_proc = current;
41         current = 0;
42         if(!elf || f->f_op->read(f, elf, PGSIZE, &f_off) == -1)
43                 goto fail;
44         current = cur_proc;
45
46         elf_t* elfhdr = (elf_t*)elf;
47         proghdr_t* proghdrs = (proghdr_t*)(elf+elfhdr->e_phoff);
48         if(elfhdr->e_phoff+elfhdr->e_phnum*sizeof(proghdr_t) > PGSIZE)
49                 goto fail;
50         if(elfhdr->e_phentsize != sizeof(proghdr_t))
51                 goto fail;
52
53         for(int i = 0; i < elfhdr->e_phnum; i++)
54         {
55                 proghdr_t* ph = proghdrs+i;
56                 if(ph->p_type == ELF_PROG_PHDR)
57                         ei->phdr = ph->p_va;
58                 if(ph->p_type == ELF_PROG_INTERP)
59                 {
60                         int maxlen = MIN(PGSIZE-ph->p_offset,sizeof(ei->interp));
61                         int len = strnlen(elf+ph->p_offset,maxlen);
62                         if(len < maxlen)
63                         {
64                                 memcpy(ei->interp,elf+ph->p_offset,maxlen+1);
65                                 ei->dynamic = 1;
66                         }
67                         else
68                                 goto fail;
69                 }
70
71                 if(ph->p_type == ELF_PROG_LOAD && ph->p_memsz)
72                 {
73                         if(ph->p_align % PGSIZE)
74                                 goto fail;
75                         if(ph->p_offset % PGSIZE != ph->p_va % PGSIZE)
76                                 goto fail;
77
78                         uintptr_t filestart = ROUNDDOWN(ph->p_offset,PGSIZE);
79                         uintptr_t fileend = ph->p_offset+ph->p_filesz;
80                         uintptr_t filesz = fileend-filestart;
81
82                         uintptr_t memstart = ROUNDDOWN(ph->p_va,PGSIZE);
83                         uintptr_t memend = ROUNDUP(ph->p_va + ph->p_memsz,PGSIZE);
84                         uintptr_t memsz = memend-memstart;
85                         if(memend > ei->highest_addr)
86                                 ei->highest_addr = memend;
87
88                         /* This needs to be a PRIVATE mapping, and the stuff after the file
89                          * needs to be zeroed. */
90                         if (filesz) {
91                                 /* TODO: figure out proper permissions from the elf */
92                                 if (do_mmap(p, memstart + pgoffset * PGSIZE, filesz,
93                                            PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE,
94                                            f, filestart) == MAP_FAILED)
95                                         goto fail;
96                                 /* Due to elf-ghetto-ness, we need to zero the first part of the
97                                  * BSS from the last page of the data segment */
98                                 uintptr_t z_s = memstart + pgoffset * PGSIZE + filesz;
99                                 uintptr_t z_e = ROUNDUP(z_s, PGSIZE);
100                                 memset((void*)z_s, 0, z_e - z_s);
101                                 filesz = ROUNDUP(filesz, PGSIZE);
102                         }
103                         /* Any extra pages are mapped anonymously... (a bit weird) */
104                         if (filesz < memsz)
105                                 if (do_mmap(p, memstart + filesz + pgoffset*PGSIZE, memsz-filesz,
106                                            PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_ANON,
107                                            NULL, 0) == MAP_FAILED)
108                                         goto fail;
109                 }
110         }
111
112         // map in program headers anyway if not present in binary.
113         // useful for TLS in static programs.
114         if(ei->phdr == -1)
115         {
116                 void *phdr_addr = do_mmap(p, MMAP_LOWEST_VA, PGSIZE, PROT_READ, 0, f,
117                                           0);
118                 if(phdr_addr == MAP_FAILED)
119                         goto fail;
120                 ei->phdr = (long)phdr_addr + elfhdr->e_phoff;
121         }
122
123         ei->entry = elfhdr->e_entry + pgoffset*PGSIZE;
124         ei->phnum = elfhdr->e_phnum;
125
126         ret = 0;
127 fail:
128         kfree(elf);
129         lcr3(old_cr3);
130         return ret;
131 }
132
133 int load_elf(struct proc* p, struct file* f)
134 {
135         elf_info_t ei,interp_ei;
136         if(load_one_elf(p,f,0,&ei))
137                 return -1;
138
139         if(ei.dynamic)
140         {
141                 /* this won't work til we convert some more syscalls to use the FS */
142                 struct file *interp = path_to_file(ei.interp);
143                 /* this will probably conflict with the mmap from the TLS up above */
144                 if (interp == NULL || load_one_elf(p, interp, 2, &interp_ei))
145                         return -1;
146                 atomic_dec(&interp->f_refcnt);  /* TODO: REF */
147         }
148
149         // fill in auxiliary info for dynamic linker/runtime
150         elf_aux_t auxp[] = {{ELF_AUX_PHDR,ei.phdr},
151                             {ELF_AUX_PHENT,sizeof(proghdr_t)},
152                             {ELF_AUX_PHNUM,ei.phnum},
153                             {ELF_AUX_ENTRY,ei.entry},
154                             #ifdef __sparc_v8__
155                             {ELF_AUX_HWCAP,ELF_HWCAP_SPARC_FLUSH},
156                             #endif
157                             {0,0}};
158
159         // put auxp after argv, envp in procinfo
160         int auxp_pos = -1;
161         for(int i = 0, zeros = 0; i < PROCINFO_MAX_ARGP; i++)
162                 if(p->procinfo->argp[i] == NULL)
163                         if(++zeros == 2)
164                                 auxp_pos = i+1;
165         if(auxp_pos == -1 ||
166            auxp_pos+sizeof(auxp)/sizeof(char*) >= PROCINFO_MAX_ARGP)
167                 return -1;
168         memcpy(p->procinfo->argp+auxp_pos,auxp,sizeof(auxp));
169
170         uintptr_t core0_entry = ei.dynamic ? interp_ei.entry : ei.entry;
171         proc_init_trapframe(&p->env_tf,0,core0_entry,USTACKTOP);
172         p->env_entry = ei.entry;
173
174         // map in stack using POPULATE (because SPARC requires it)
175         uintptr_t stacksz = USTACK_NUM_PAGES*PGSIZE;
176         if(do_mmap(p, USTACKTOP-stacksz, stacksz, PROT_READ | PROT_WRITE,
177                    MAP_FIXED | MAP_ANONYMOUS | MAP_POPULATE, NULL, 0) == MAP_FAILED)
178                 return -1;
179
180         // Set the heap bottom and top to just past where the text 
181         // region has been loaded
182         p->heap_top = (void*)ei.highest_addr;
183         p->procinfo->heap_bottom = p->heap_top;
184
185         return 0;
186 }
187