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