refactor: elf parsing utility and exec related
[lunaix-os.git] / lunaix-os / kernel / loader / elf.c
1 #include <klibc/string.h>
2 #include <lunaix/common.h>
3 #include <lunaix/elf.h>
4 #include <lunaix/exec.h>
5 #include <lunaix/fs.h>
6 #include <lunaix/mm/mmap.h>
7 #include <lunaix/mm/valloc.h>
8 #include <lunaix/mm/vmm.h>
9 #include <lunaix/spike.h>
10
11 static inline int
12 elf32_read(struct v_file* elf, void* data, size_t off, size_t len)
13 {
14     // it is wise to do cached read
15     return pcache_read(elf->inode, data, len, off);
16 }
17
18 int
19 elf32_map_segment(struct load_context* ldctx,
20                   const struct elf32* elf,
21                   struct elf32_phdr* phdre)
22 {
23     struct v_file* elfile = (struct v_file*)elf->elf_file;
24
25     assert(PG_ALIGNED(phdre->p_offset));
26
27     int proct = 0;
28     if ((phdre->p_flags & PF_R)) {
29         proct |= PROT_READ;
30     }
31     if ((phdre->p_flags & PF_W)) {
32         proct |= PROT_WRITE;
33     }
34     if ((phdre->p_flags & PF_X)) {
35         proct |= PROT_EXEC;
36     }
37
38     struct exec_container* container = ldctx->container;
39     struct mmap_param param = { .vms_mnt = container->vms_mnt,
40                                 .pvms = &container->proc->mm,
41                                 .proct = proct,
42                                 .offset = PG_ALIGN(phdre->p_offset),
43                                 .mlen = ROUNDUP(phdre->p_memsz, PG_SIZE),
44                                 .flen = phdre->p_filesz + PG_MOD(phdre->p_va),
45                                 .flags = MAP_FIXED | MAP_PRIVATE,
46                                 .type = REGION_TYPE_CODE };
47
48     struct mm_region* seg_reg;
49     int status = mem_map(NULL, &seg_reg, PG_ALIGN(phdre->p_va), elfile, &param);
50
51     if (!status) {
52         size_t next_addr = phdre->p_memsz + phdre->p_va;
53         ldctx->end = MAX(ldctx->end, ROUNDUP(next_addr, PG_SIZE));
54         ldctx->mem_sz += phdre->p_memsz;
55     } else {
56         // we probably fucked up our process
57         terminate_proc(-1);
58     }
59
60     return status;
61 }
62
63 int
64 elf32_open(struct elf32* elf, const char* path)
65 {
66     struct v_dnode* elfdn;
67     struct v_inode* elfin;
68     struct v_file* elffile;
69     int error = 0;
70
71     if ((error = vfs_walk_proc(path, &elfdn, NULL, 0))) {
72         return error;
73     }
74
75     if ((error = vfs_open(elfdn, &elffile))) {
76         return error;
77     }
78
79     return elf32_openat(elf, elffile);
80 }
81
82 int
83 elf32_openat(struct elf32* elf, void* elf_vfile)
84 {
85     int status = 0;
86     elf->pheaders = NULL;
87     elf->elf_file = elf_vfile;
88
89     if ((status = elf32_read_ehdr(elf)) < 0) {
90         elf32_close(elf);
91         return status;
92     }
93
94     if ((status = elf32_read_phdr(elf)) < 0) {
95         elf32_close(elf);
96         return status;
97     }
98
99     return status;
100 }
101
102 int
103 elf32_close(struct elf32* elf)
104 {
105     if (elf->pheaders) {
106         vfree(elf->pheaders);
107     }
108
109     if (elf->elf_file) {
110         vfs_close((struct v_file*)elf->elf_file);
111     }
112
113     memset(elf, 0, sizeof(*elf));
114 }
115
116 int
117 elf32_static_linked(const struct elf32* elf)
118 {
119     for (size_t i = 0; i < elf->eheader.e_phnum; i++) {
120         struct elf32_phdr* phdre = &elf->pheaders[i];
121         if (phdre->p_type == PT_INTERP) {
122             return 0;
123         }
124     }
125     return 1;
126 }
127
128 size_t
129 elf32_loadable_memsz(const struct elf32* elf)
130 {
131     // XXX: Hmmmm, I am not sure if we need this. This is designed to be handy
132     // if we decided to map the heap region before transfer to loader. As
133     // currently, we push *everything* to user-space loader, thus we modify the
134     // brk syscall to do the initial heap mapping.
135
136     size_t sz = 0;
137     for (size_t i = 0; i < elf->eheader.e_phnum; i++) {
138         struct elf32_phdr* phdre = &elf->pheaders[i];
139         if (phdre->p_type == PT_LOAD) {
140             sz += phdre->p_memsz;
141         }
142     }
143
144     return sz;
145 }
146
147 int
148 elf32_find_loader(const struct elf32* elf, char* path_out, size_t len)
149 {
150     int retval = NO_LOADER;
151
152     assert_msg(len >= sizeof(DEFAULT_LOADER), "path_out: too small");
153
154     struct v_file* elfile = (struct v_file*)elf->elf_file;
155
156     for (size_t i = 0; i < elf->eheader.e_phnum; i++) {
157         struct elf32_phdr* phdre = &elf->pheaders[i];
158         if (phdre->p_type == PT_INTERP) {
159             assert_msg(len >= phdre->p_filesz, "path_out: too small");
160             retval =
161               elf_read(elfile, path_out, phdre->p_offset, phdre->p_filesz);
162
163             if (retval < 0) {
164                 return retval;
165             }
166
167             break;
168         }
169     }
170
171     return retval;
172 }
173
174 int
175 elf32_read_ehdr(struct elf32* elf)
176 {
177     struct v_file* elfile = (struct v_file*)elf->elf_file;
178     int status = elf_read(elfile, (void*)&elf->eheader, 0, SIZE_EHDR);
179
180     if (status < 0) {
181         return status;
182     }
183 }
184
185 int
186 elf32_read_phdr(struct elf32* elf)
187 {
188     int status = 0;
189
190     struct v_file* elfile = (struct v_file*)elf->elf_file;
191
192     size_t entries = elf->eheader.e_phnum;
193     size_t tbl_sz = entries * SIZE_PHDR;
194
195     struct elf32_phdr* phdrs = valloc(tbl_sz);
196
197     if (!phdrs) {
198         return ENOMEM;
199     }
200
201     status = elf_read(elfile, phdrs, elf->eheader.e_phoff, tbl_sz);
202
203     if (status < 0) {
204         vfree(phdrs);
205         return status;
206     }
207
208     elf->pheaders = phdrs;
209     return entries;
210 }
211
212 int
213 elf32_check_exec(const struct elf32* elf)
214 {
215     struct elf32_ehdr* ehdr = elf->pheaders;
216
217     return *(u32_t*)(ehdr->e_ident) == ELFMAGIC &&
218            ehdr->e_ident[EI_CLASS] == ELFCLASS32 &&
219            ehdr->e_ident[EI_DATA] == ELFDATA2LSB && ehdr->e_type == ET_EXEC &&
220            ehdr->e_machine == EM_386;
221 }
222
223 int
224 elf32_load(struct load_context* ldctx, const struct elf32* elf)
225 {
226     int err = 0;
227
228     struct v_file* elfile = (struct v_file*)elf->elf_file;
229
230     for (size_t i = 0; i < elf->eheader.e_phnum && !err; i++) {
231         struct elf32_phdr* phdr = &elf->pheaders[i];
232
233         if (phdr->p_type == PT_LOAD) {
234             if (phdr->p_align != PG_SIZE) {
235                 // surprising alignment!
236                 err = ENOEXEC;
237                 continue;
238             }
239
240             err = elf_map_segment(ldctx, elf, phdr);
241         }
242         // TODO Handle relocation
243     }
244
245 done:
246     return err;
247 }