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