Sorted out proc refcounting with ARCs
[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 #include <smp.h>
10
11 typedef struct
12 {
13         long entry;
14         long highest_addr;
15         long phdr;
16         int phnum;
17         int dynamic;
18         char interp[256];
19 } elf_info_t;
20
21 static int
22 load_one_elf(struct proc* p, struct file* f, int pgoffset, elf_info_t* ei)
23 {
24         int ret = -1;
25         ei->phdr = -1;
26         ei->dynamic = 0;
27         ei->highest_addr = 0;
28         off_t f_off = 0;
29         physaddr_t old_cr3 = rcr3();
30
31         /* Load the proc's address space, in case we need to directly write to its
32          * pages (like when we zero some of the BSS) */
33         lcr3(p->env_cr3);
34
35         // assume program headers fit in a page.
36         // if this isn't true, change the code below that maps in program headers
37         char* elf = (char*)kmalloc(PGSIZE,0);
38         
39         /* When reading on behalf of the kernel, we need to make sure no proc is
40          * "current".  This is a bit ghetto (TODO: KFOP) */
41         struct proc *cur_proc = current;
42         current = 0;
43         if(!elf || f->f_op->read(f, elf, PGSIZE, &f_off) == -1)
44                 goto fail;
45         current = cur_proc;
46
47         elf_t* elfhdr = (elf_t*)elf;
48         proghdr_t* proghdrs = (proghdr_t*)(elf+elfhdr->e_phoff);
49         if(elfhdr->e_phoff+elfhdr->e_phnum*sizeof(proghdr_t) > PGSIZE)
50                 goto fail;
51         if(elfhdr->e_phentsize != sizeof(proghdr_t))
52                 goto fail;
53
54         for(int i = 0; i < elfhdr->e_phnum; i++)
55         {
56                 proghdr_t* ph = proghdrs+i;
57                 if(ph->p_type == ELF_PROG_PHDR)
58                         ei->phdr = ph->p_va;
59                 if(ph->p_type == ELF_PROG_INTERP)
60                 {
61                         int maxlen = MIN(PGSIZE-ph->p_offset,sizeof(ei->interp));
62                         int len = strnlen(elf+ph->p_offset,maxlen);
63                         if(len < maxlen)
64                         {
65                                 memcpy(ei->interp,elf+ph->p_offset,maxlen+1);
66                                 ei->dynamic = 1;
67                         }
68                         else
69                                 goto fail;
70                 }
71
72                 if(ph->p_type == ELF_PROG_LOAD && ph->p_memsz)
73                 {
74                         if(ph->p_align % PGSIZE)
75                                 goto fail;
76                         if(ph->p_offset % PGSIZE != ph->p_va % PGSIZE)
77                                 goto fail;
78
79                         uintptr_t filestart = ROUNDDOWN(ph->p_offset,PGSIZE);
80                         uintptr_t fileend = ph->p_offset+ph->p_filesz;
81                         uintptr_t filesz = fileend-filestart;
82
83                         uintptr_t memstart = ROUNDDOWN(ph->p_va,PGSIZE);
84                         uintptr_t memend = ROUNDUP(ph->p_va + ph->p_memsz,PGSIZE);
85                         uintptr_t memsz = memend-memstart;
86                         if(memend > ei->highest_addr)
87                                 ei->highest_addr = memend;
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                 struct file *interp = do_file_open(ei.interp, 0, 0);
141                 if (!interp)
142                         return -1;
143                 /* careful, this could conflict with the mmap from the TLS up above */
144                 int error = load_one_elf(p, interp, 2, &interp_ei);
145                 kref_put(&interp->f_kref);
146                 if (error)
147                         return -1;
148         }
149
150         // fill in auxiliary info for dynamic linker/runtime
151         elf_aux_t auxp[] = {{ELF_AUX_PHDR,ei.phdr},
152                             {ELF_AUX_PHENT,sizeof(proghdr_t)},
153                             {ELF_AUX_PHNUM,ei.phnum},
154                             {ELF_AUX_ENTRY,ei.entry},
155                             #ifdef __sparc_v8__
156                             {ELF_AUX_HWCAP,ELF_HWCAP_SPARC_FLUSH},
157                             #endif
158                             {0,0}};
159
160         // put auxp after argv, envp in procinfo
161         int auxp_pos = -1;
162         for(int i = 0, zeros = 0; i < PROCINFO_MAX_ARGP; i++)
163                 if(p->procinfo->argp[i] == NULL)
164                         if(++zeros == 2)
165                                 auxp_pos = i+1;
166         if(auxp_pos == -1 ||
167            auxp_pos+sizeof(auxp)/sizeof(char*) >= PROCINFO_MAX_ARGP)
168                 return -1;
169         memcpy(p->procinfo->argp+auxp_pos,auxp,sizeof(auxp));
170
171         uintptr_t core0_entry = ei.dynamic ? interp_ei.entry : ei.entry;
172         proc_init_trapframe(&p->env_tf,0,core0_entry,USTACKTOP);
173         p->env_entry = ei.entry;
174
175         // map in stack using POPULATE (because SPARC requires it)
176         uintptr_t stacksz = USTACK_NUM_PAGES*PGSIZE;
177         if(do_mmap(p, USTACKTOP-stacksz, stacksz, PROT_READ | PROT_WRITE,
178                    MAP_FIXED | MAP_ANONYMOUS | MAP_POPULATE, NULL, 0) == MAP_FAILED)
179                 return -1;
180
181         // Set the heap bottom and top to just past where the text 
182         // region has been loaded
183         p->heap_top = (void*)ei.highest_addr;
184         p->procinfo->heap_bottom = p->heap_top;
185
186         return 0;
187 }
188