1 /*
2 * Copyright (c) 2023 Institute of Parallel And Distributed Systems (IPADS), Shanghai Jiao Tong University (SJTU)
3 * Licensed under the Mulan PSL v2.
4 * You can use this software according to the terms and conditions of the Mulan PSL v2.
5 * You may obtain a copy of Mulan PSL v2 at:
6 * http://license.coscl.org.cn/MulanPSL2
7 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
8 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
9 * PURPOSE.
10 * See the Mulan PSL v2 for more details.
11 */
12 /**
13 * @file libchcoreelf.c
14 *
15 * @brief ChCore-specialized ELF loading library. See core function
16 * load_elf_from_range to understand the implementation of this library.
17 */
18 #include "libchcoreelf.h"
19 #include <endian.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <malloc.h>
25 #include <chcore/syscall.h>
26 #include <chcore/bug.h>
27
28 #define ELF_HEADER_SIZE (sizeof(struct elf_header))
29
30 /**
31 * @brief Defining a function which can be used to randomly access an
32 * abstract "range". See load_elf_from_range() for more details.
33 *
34 * A call to this function should load all bytes from offset to offset+len
35 * into the buffer buf. Otherwise this function should fail.
36 *
37 * @param param [In] custom parameter, this parameter can be used to
38 * indicate the start of a range, see file_range_loader and memory_range_loader
39 * for examples.
40 * @param offset [In] offset in the range
41 * @param len [In] length of the data to be loaded
42 * @param buf [Out] buffer to store the loaded data. Caller should guarantee
43 * that the buffer is large enough to hold the data.
44 *
45 * @return 0 on success, negative error code on failure.
46 */
47 typedef int (*range_loader_t)(void *param, off_t offset, size_t len, char *buf);
48
is_elf_magic(struct elf_indent * indent)49 static bool is_elf_magic(struct elf_indent *indent)
50 {
51 return (indent->ei_magic[0] == 0x7F && indent->ei_magic[1] == 'E'
52 && indent->ei_magic[2] == 'L' && indent->ei_magic[3] == 'F');
53 }
54
55 #define ELF_ENDIAN_LE(indent) ((indent).ei_data == 1)
56 #define ELF_ENDIAN_BE(indent) ((indent).ei_data == 2)
57
58 #define ELF_BITS_32(indent) ((indent).ei_class == 1)
59 #define ELF_BITS_64(indent) ((indent).ei_class == 2)
60
61 /**
62 * Parse an ELF file header. We use the 64-bit structure `struct elf_header` as
63 * the output structure.
64 *
65 * To support both 32-bit and 64-bit ELF files, we first try to parse the small
66 * leading part elf_ident of elf_header which is 32/64-bit independent. Then we
67 * parse 32/64-bit dependent part of elf_header.
68 *
69 * On error, the negative error code is returned.
70 * On success, 0 is returned, and the header is written in the given parameter.
71 */
parse_elf_header(const char * code,struct elf_header * header)72 int parse_elf_header(const char *code, struct elf_header *header)
73 {
74 struct elf_header *header_64 = (struct elf_header *)code;
75 struct elf_header_32 *header_32 = (struct elf_header_32 *)code;
76
77 if (!is_elf_magic(&header_64->e_indent)) {
78 return -EINVAL;
79 }
80
81 header->e_indent = *(struct elf_indent *)code;
82
83 if (ELF_ENDIAN_LE(header->e_indent)) {
84 /*
85 * For the first few bytes, both 32-bit and 64-bit ELF headers
86 * have the same field width. So, we simply use header_64 at
87 * first.
88 */
89 header->e_type = le16toh(header_64->e_type);
90 header->e_machine = le16toh(header_64->e_machine);
91 header->e_version = le32toh(header_32->e_version);
92 if (ELF_BITS_32(header->e_indent)) {
93 header->e_entry = le32toh(header_32->e_entry);
94 header->e_phoff = le32toh(header_32->e_phoff);
95 header->e_shoff = le32toh(header_32->e_shoff);
96 header->e_flags = le32toh(header_32->e_flags);
97 header->e_ehsize = le16toh(header_32->e_ehsize);
98 header->e_phentsize = le16toh(header_32->e_phentsize);
99 header->e_phnum = le16toh(header_32->e_phnum);
100 header->e_shentsize = le16toh(header_32->e_shentsize);
101 header->e_shnum = le16toh(header_32->e_shnum);
102 header->e_shstrndx = le16toh(header_32->e_shstrndx);
103 } else if (ELF_BITS_64(header->e_indent)) {
104 header->e_entry = le64toh(header_64->e_entry);
105 header->e_phoff = le64toh(header_64->e_phoff);
106 header->e_shoff = le64toh(header_64->e_shoff);
107 header->e_flags = le32toh(header_64->e_flags);
108 header->e_ehsize = le16toh(header_64->e_ehsize);
109 header->e_phentsize = le16toh(header_64->e_phentsize);
110 header->e_phnum = le16toh(header_64->e_phnum);
111 header->e_shentsize = le16toh(header_64->e_shentsize);
112 header->e_shnum = le16toh(header_64->e_shnum);
113 header->e_shstrndx = le16toh(header_64->e_shstrndx);
114 } else {
115 return -EINVAL;
116 }
117 } else if (ELF_ENDIAN_BE(header->e_indent)) {
118 /*
119 * We use header_64 for the same reason as above.
120 */
121 header->e_type = be16toh(header_64->e_type);
122 header->e_machine = be16toh(header_64->e_machine);
123 header->e_version = be32toh(header_32->e_version);
124 if (ELF_BITS_32(header->e_indent)) {
125 header->e_entry = be32toh(header_32->e_entry);
126 header->e_phoff = be32toh(header_32->e_phoff);
127 header->e_shoff = be32toh(header_32->e_shoff);
128 header->e_flags = be32toh(header_32->e_flags);
129 header->e_ehsize = be16toh(header_32->e_ehsize);
130 header->e_phentsize = be16toh(header_32->e_phentsize);
131 header->e_phnum = be16toh(header_32->e_phnum);
132 header->e_shentsize = be16toh(header_32->e_shentsize);
133 header->e_shnum = be16toh(header_32->e_shnum);
134 header->e_shstrndx = be16toh(header_32->e_shstrndx);
135 } else if (ELF_BITS_64(header->e_indent)) {
136 header->e_entry = be64toh(header_64->e_entry);
137 header->e_phoff = be64toh(header_64->e_phoff);
138 header->e_shoff = be64toh(header_64->e_shoff);
139 header->e_flags = be32toh(header_64->e_flags);
140 header->e_ehsize = be16toh(header_64->e_ehsize);
141 header->e_phentsize = be16toh(header_64->e_phentsize);
142 header->e_phnum = be16toh(header_64->e_phnum);
143 header->e_shentsize = be16toh(header_64->e_shentsize);
144 header->e_shnum = be16toh(header_64->e_shnum);
145 header->e_shstrndx = be16toh(header_64->e_shstrndx);
146 } else {
147 return -EINVAL;
148 }
149 } else {
150 return -EINVAL;
151 }
152 return 0;
153 }
154
155 /**
156 * Parse an ELF program header. We use the 64-bit structure
157 * `struct elf_program_header` as the output structure.
158 *
159 * On error, the negative error code is returned.
160 * On success, 0 is returned, and the header is written in the given parameter.
161 */
parse_elf_program_header(const char * code,const struct elf_header * elf_header,struct elf_program_header * header)162 static int parse_elf_program_header(const char *code,
163 const struct elf_header *elf_header,
164 struct elf_program_header *header)
165 {
166 struct elf_program_header *header_64;
167 struct elf_program_header_32 *header_32;
168
169 if (ELF_ENDIAN_LE(elf_header->e_indent)) {
170 if (ELF_BITS_32(elf_header->e_indent)) {
171 header_32 = (struct elf_program_header_32 *)code;
172 header->p_type = le32toh(header_32->p_type);
173 header->p_flags = le32toh(header_32->p_flags);
174 header->p_offset = le32toh(header_32->p_offset);
175 header->p_vaddr = le32toh(header_32->p_vaddr);
176 header->p_paddr = le32toh(header_32->p_paddr);
177 header->p_filesz = le32toh(header_32->p_filesz);
178 header->p_memsz = le32toh(header_32->p_memsz);
179 header->p_align = le32toh(header_32->p_align);
180 } else if (ELF_BITS_64(elf_header->e_indent)) {
181 header_64 = (struct elf_program_header *)code;
182 header->p_type = le32toh(header_64->p_type);
183 header->p_flags = le32toh(header_64->p_flags);
184 header->p_offset = le64toh(header_64->p_offset);
185 header->p_vaddr = le64toh(header_64->p_vaddr);
186 header->p_paddr = le64toh(header_64->p_paddr);
187 header->p_filesz = le64toh(header_64->p_filesz);
188 header->p_memsz = le64toh(header_64->p_memsz);
189 header->p_align = le64toh(header_64->p_align);
190 } else {
191 return -EINVAL;
192 }
193 } else if (ELF_ENDIAN_BE(elf_header->e_indent)) {
194 if (ELF_BITS_32(elf_header->e_indent)) {
195 header_32 = (struct elf_program_header_32 *)code;
196 header->p_type = be32toh(header_32->p_type);
197 header->p_flags = be32toh(header_32->p_flags);
198 header->p_offset = be32toh(header_32->p_offset);
199 header->p_vaddr = be32toh(header_32->p_vaddr);
200 header->p_paddr = be32toh(header_32->p_paddr);
201 header->p_filesz = be32toh(header_32->p_filesz);
202 header->p_memsz = be32toh(header_32->p_memsz);
203 header->p_align = be32toh(header_32->p_align);
204 } else if (ELF_BITS_64(elf_header->e_indent)) {
205 header_64 = (struct elf_program_header *)code;
206 header->p_type = be32toh(header_64->p_type);
207 header->p_flags = be32toh(header_64->p_flags);
208 header->p_offset = be64toh(header_64->p_offset);
209 header->p_vaddr = be64toh(header_64->p_vaddr);
210 header->p_paddr = be64toh(header_64->p_paddr);
211 header->p_filesz = be64toh(header_64->p_filesz);
212 header->p_memsz = be64toh(header_64->p_memsz);
213 header->p_align = be64toh(header_64->p_align);
214 } else {
215 return -EINVAL;
216 }
217 } else {
218 return -EINVAL;
219 }
220 return 0;
221 }
222
223 /**
224 * Parse an ELF section header. We use the 64-bit structure
225 * `struct elf_section_header` as the output structure.
226 *
227 * On error, the negative error code is returned.
228 * On success, 0 is returned, and the header is written in the given parameter.
229 */
parse_elf_section_header(const char * code,const struct elf_header * elf_header,struct elf_section_header * header)230 static int parse_elf_section_header(const char *code,
231 const struct elf_header *elf_header,
232 struct elf_section_header *header)
233 {
234 struct elf_section_header *header_64;
235 struct elf_section_header_32 *header_32;
236
237 if (ELF_ENDIAN_LE(elf_header->e_indent)) {
238 if (ELF_BITS_32(elf_header->e_indent)) {
239 header_32 = (struct elf_section_header_32 *)code;
240 header->sh_name = le32toh(header_32->sh_name);
241 header->sh_type = le32toh(header_32->sh_type);
242 header->sh_flags = le32toh(header_32->sh_flags);
243 header->sh_addr = le32toh(header_32->sh_addr);
244 header->sh_offset = le32toh(header_32->sh_offset);
245 header->sh_size = le32toh(header_32->sh_size);
246 header->sh_link = le32toh(header_32->sh_link);
247 header->sh_info = le32toh(header_32->sh_info);
248 header->sh_addralign = le32toh(header_32->sh_addralign);
249 header->sh_entsize = le32toh(header_32->sh_entsize);
250 } else if (ELF_BITS_64(elf_header->e_indent)) {
251 header_64 = (struct elf_section_header *)code;
252 header->sh_name = le32toh(header_64->sh_name);
253 header->sh_type = le32toh(header_64->sh_type);
254 header->sh_flags = le64toh(header_64->sh_flags);
255 header->sh_addr = le64toh(header_64->sh_addr);
256 header->sh_offset = le64toh(header_64->sh_offset);
257 header->sh_size = le64toh(header_64->sh_size);
258 header->sh_link = le32toh(header_64->sh_link);
259 header->sh_info = le32toh(header_64->sh_info);
260 header->sh_addralign = le64toh(header_64->sh_addralign);
261 header->sh_entsize = le64toh(header_64->sh_entsize);
262 } else {
263 return -EINVAL;
264 }
265 } else if (ELF_ENDIAN_BE(elf_header->e_indent)) {
266 if (ELF_BITS_32(elf_header->e_indent)) {
267 header_32 = (struct elf_section_header_32 *)code;
268 header->sh_name = be32toh(header_32->sh_name);
269 header->sh_type = be32toh(header_32->sh_type);
270 header->sh_flags = be32toh(header_32->sh_flags);
271 header->sh_addr = be32toh(header_32->sh_addr);
272 header->sh_offset = be32toh(header_32->sh_offset);
273 header->sh_size = be32toh(header_32->sh_size);
274 header->sh_link = be32toh(header_32->sh_link);
275 header->sh_info = be32toh(header_32->sh_info);
276 header->sh_addralign = be32toh(header_32->sh_addralign);
277 header->sh_entsize = be32toh(header_32->sh_entsize);
278 } else if (ELF_BITS_64(elf_header->e_indent)) {
279 header_64 = (struct elf_section_header *)code;
280 header->sh_name = be32toh(header_64->sh_name);
281 header->sh_type = be32toh(header_64->sh_type);
282 header->sh_flags = be64toh(header_64->sh_flags);
283 header->sh_addr = be64toh(header_64->sh_addr);
284 header->sh_offset = be64toh(header_64->sh_offset);
285 header->sh_size = be64toh(header_64->sh_size);
286 header->sh_link = be32toh(header_64->sh_link);
287 header->sh_info = be32toh(header_64->sh_info);
288 header->sh_addralign = be64toh(header_64->sh_addralign);
289 header->sh_entsize = be64toh(header_64->sh_entsize);
290 } else {
291 return -EINVAL;
292 }
293 } else {
294 return -EINVAL;
295 }
296 return 0;
297 }
298
elf_free(struct elf_file * elf)299 static void elf_free(struct elf_file *elf)
300 {
301 if (elf->s_headers)
302 free(elf->s_headers);
303 if (elf->p_headers)
304 free(elf->p_headers);
305 free(elf);
306 }
307
new_user_elf(void)308 static struct user_elf *new_user_elf(void)
309 {
310 struct user_elf *elf;
311
312 elf = malloc(sizeof(*elf));
313 if (!elf)
314 return NULL;
315 memset(elf, 0, sizeof(*elf));
316 return elf;
317 }
318
free_user_elf(struct user_elf * user_elf)319 void free_user_elf(struct user_elf *user_elf)
320 {
321 int segs = user_elf->segs_nr;
322 if (segs > 0) {
323 for (int i = 0; i < segs; i++) {
324 if (user_elf->user_elf_segs[i].elf_pmo > 0) {
325 usys_revoke_cap(user_elf->user_elf_segs[i].elf_pmo, false);
326 }
327 }
328 free(user_elf->user_elf_segs);
329 }
330 free(user_elf);
331 }
332
333 /**
334 * @brief Treat a file as a randomly accessiable sequence of bytes, i.e. a
335 * "range", by combining usage of read() and lseek()
336 *
337 * @param param [In] fd of the file to be read
338 */
file_range_loader(void * param,off_t offset,size_t len,char * buf)339 static int file_range_loader(void *param, off_t offset, size_t len, char *buf)
340 {
341 int fd = (int)(long)param;
342 ssize_t ret;
343 if (lseek(fd, offset, SEEK_SET) < 0) {
344 return -errno;
345 }
346
347 if ((ret = read(fd, buf, len)) < 0) {
348 return -errno;
349 }
350
351 /**
352 * We are accessing a file, so the read() call is expected to read all
353 * required data. If it doesn't, it's an error.
354 */
355 if (ret != len) {
356 return -EINVAL;
357 }
358
359 return 0;
360 }
361
memory_range_loader(void * param,off_t offset,size_t len,char * buf)362 static int memory_range_loader(void *param, off_t offset, size_t len, char *buf)
363 {
364 memcpy(buf, (char *)param + offset, len);
365 return 0;
366 }
367
368 #define MALLOC_OR_FAIL(ptr, size) \
369 do { \
370 (ptr) = malloc(size); \
371 if (!(ptr)) { \
372 ret = -ENOMEM; \
373 goto out_nomem; \
374 } \
375 } while (0)
376
377 /**
378 * @brief Load a small heading part of the range, and try to parse it into
379 * an elf_header struct.
380 *
381 * @param loader [In] abstract range loader function
382 * @param param [In] custom parameter for @loader
383 * @param elf_header [Out] returning pointer to newly loaded header if
384 * successfully load and parse its content. The ownership of this pointer is
385 * transfered to the caller, so the caller should free it after use.
386 * @return 0 if success, otherwise -errno is returned. All memory resources
387 * consumed by this function are guaranteed to be freed if not success.
388 */
__load_elf_header(range_loader_t loader,void * param,struct elf_header ** elf_header)389 static int __load_elf_header(range_loader_t loader, void *param,
390 struct elf_header **elf_header)
391 {
392 int ret;
393 char buf[ELF_HEADER_SIZE];
394 struct elf_header *header;
395
396 MALLOC_OR_FAIL(header, sizeof(struct elf_header));
397
398 ret = loader(param, 0, sizeof(struct elf_header), buf);
399 if (ret < 0) {
400 goto out_fail;
401 }
402
403 ret = parse_elf_header(buf, header);
404 if (ret < 0) {
405 goto out_fail;
406 }
407
408 *elf_header = header;
409 return 0;
410 out_fail:
411 free(header);
412 out_nomem:
413 return ret;
414 }
415
416 /**
417 * @brief Load program headers and section headers from the range,
418 * and try to parse it into an elf_file struct.
419 *
420 * @param elf_header [In] this function only borrow the pointer, and will not
421 * free it. It's the caller's responsibility to control the life cycle of this
422 * pointer.
423 * @param loader [In] abstract range loader function
424 * @param param [In] custom parameter for @loader
425 * @param elf_header [Out] returning pointer to newly loaded header if
426 * successfully load and parse its content. The ownership of this pointer is
427 * transfered to the caller, so the caller should free it after use.
428 * @return 0 if success, otherwise -errno is returned. All memory resources
429 * consumed by this function are guaranteed to be freed if not success.
430 */
__load_elf_ph_sh(struct elf_header * header,range_loader_t loader,void * param,struct elf_file ** elf_file)431 static int __load_elf_ph_sh(struct elf_header *header, range_loader_t loader,
432 void *param, struct elf_file **elf_file)
433 {
434 int ret = 0;
435 struct elf_program_header *ph_buf = NULL;
436 struct elf_section_header *sh_buf = NULL;
437 struct elf_file *elf = NULL;
438 char *ph_file_buf = NULL;
439 char *sh_file_buf = NULL;
440
441 MALLOC_OR_FAIL(ph_buf, header->e_phnum * sizeof(struct elf_program_header));
442 MALLOC_OR_FAIL(sh_buf, header->e_shnum * sizeof(struct elf_section_header));
443 MALLOC_OR_FAIL(elf, sizeof(struct elf_file));
444 MALLOC_OR_FAIL(ph_file_buf, header->e_phnum * (size_t)header->e_phentsize);
445 MALLOC_OR_FAIL(sh_file_buf, header->e_shnum * (size_t)header->e_shentsize);
446
447 /**
448 * ph_file_buf would be an array of e_phnum elements, size of each
449 * element is e_phentsize if success. Each element is a program header
450 * in the ELF file.
451 */
452 ret = loader(param,
453 header->e_phoff,
454 header->e_phnum * (size_t)header->e_phentsize,
455 ph_file_buf);
456 if (ret < 0) {
457 goto out_fail;
458 }
459
460 for (int i = 0; i < header->e_phnum; i++) {
461 ret = parse_elf_program_header(ph_file_buf
462 + (ptrdiff_t)header->e_phentsize * i,
463 header,
464 &ph_buf[i]);
465 if (ret < 0) {
466 goto out_fail;
467 }
468 }
469
470 /**
471 * sh_file_buf would be an array of e_shnum elements, size of each
472 * element is e_shentsize if success. Each element is a section header
473 * in the ELF file.
474 */
475 ret = loader(param,
476 header->e_shoff,
477 header->e_shnum * (size_t)header->e_shentsize,
478 sh_file_buf);
479 if (ret < 0) {
480 goto out_fail;
481 }
482
483 for (int i = 0; i < header->e_shnum; i++) {
484 ret = parse_elf_section_header(sh_file_buf
485 + (ptrdiff_t)header->e_shentsize * i,
486 header,
487 &sh_buf[i]);
488 if (ret < 0) {
489 goto out_fail;
490 }
491 }
492
493 elf->p_headers = ph_buf;
494 elf->s_headers = sh_buf;
495 elf->header = *header;
496
497 free(ph_file_buf);
498 free(sh_file_buf);
499
500 *elf_file = elf;
501 return 0;
502 out_fail:
503 out_nomem:
504 if (ph_buf) {
505 free(ph_buf);
506 }
507 if (sh_buf) {
508 free(sh_buf);
509 }
510 if (elf) {
511 elf_free(elf);
512 }
513 if (ph_file_buf) {
514 free(ph_file_buf);
515 }
516 if (sh_file_buf) {
517 free(sh_file_buf);
518 }
519 return ret;
520 }
521
522 /**
523 * @brief Translate the permission bits in p_flags into ChCore VMR
524 * permissions.
525 */
526 #define PFLAGS2VMRFLAGS(PF) \
527 (((PF)&PF_X ? VM_EXEC : 0) | ((PF)&PF_W ? VM_WRITE : 0) \
528 | ((PF)&PF_R ? VM_READ : 0))
529
530 #define OFFSET_MASK 0xfff
531
532 /**
533 * @brief Load each loadable (PT_LOAD) segment in elf_file into a ChCore
534 * pmo from the range. Content of each segment is stored in memory backed
535 * by its corresponding pmo. Those pmos, stored in user_elf->user_elf_segs,
536 * can be mapped into the address space of a process later, but this library
537 * is only used for loading ELF file content, not constructing ELF memory
538 * image in the caller's address space, so we don't do that here.
539 *
540 * @param elf_file [In] this function only borrow the pointer, and will not
541 * free it. It's the caller's responsibility to control the life cycle of this
542 * pointer.
543 * @param loader [In] abstract range loader function
544 * @param param [In] custom parameter for @loader
545 * @param user_elf [Out] returning pointer to newly loaded header if
546 * successfully load and parse its content. The ownership of this pointer is
547 * transfered to the caller, so the caller should free it after use.
548 * @return 0 if success, otherwise -errno is returned. All memory resources
549 * consumed by this function are guaranteed to be freed if not success.
550 */
__load_elf_into_pmos(struct elf_file * elf_file,range_loader_t loader,void * param,struct user_elf ** user_elf)551 static int __load_elf_into_pmos(struct elf_file *elf_file,
552 range_loader_t loader, void *param,
553 struct user_elf **user_elf)
554 {
555 int ret = 0;
556 int loadable_segs = 0;
557 struct user_elf *elf = NULL;
558 struct elf_program_header *cur_ph;
559 struct user_elf_seg *cur_user_elf_seg;
560 size_t seg_sz, seg_map_sz, seg_file_sz;
561 u64 p_vaddr;
562 unsigned long phdr_addr = 0;
563 cap_t seg_pmo;
564 char *seg_buf, *seg_load_start;
565
566 /**
567 * Calculate the number of loadable segments. We only need to
568 * load those segments.
569 */
570 for (int i = 0; i < elf_file->header.e_phnum; i++) {
571 if (elf_file->p_headers[i].p_type == PT_LOAD) {
572 loadable_segs++;
573 }
574 }
575
576 elf = new_user_elf();
577 if (!elf) {
578 ret = -ENOMEM;
579 goto out_nomem;
580 }
581
582 elf->segs_nr = loadable_segs;
583 MALLOC_OR_FAIL(elf->user_elf_segs,
584 loadable_segs * sizeof(struct user_elf_seg));
585 memset(elf->user_elf_segs, 0, loadable_segs * sizeof(struct user_elf_seg));
586
587 /**
588 * Loop over all segments of elf_file(controlled by i), find and load
589 * each loadable segment(controlled by j), and fill its user_elf_seg structure.
590 */
591 for (int i = 0, j = 0; i < elf_file->header.e_phnum; i++) {
592 cur_ph = &elf_file->p_headers[i];
593 cur_user_elf_seg = &elf->user_elf_segs[j];
594 if (cur_ph->p_type != PT_LOAD && cur_ph->p_type != PT_PHDR) {
595 continue;
596 }
597
598 if (cur_ph->p_type == PT_PHDR) {
599 phdr_addr = cur_ph->p_vaddr;
600 continue;
601 }
602
603 seg_sz = cur_ph->p_memsz;
604 p_vaddr = cur_ph->p_vaddr;
605 seg_file_sz = cur_ph->p_filesz;
606
607 /** seg_sz and p_vaddr may not page aligned */
608 seg_map_sz = ROUND_UP(seg_sz + p_vaddr, PAGE_SIZE)
609 - ROUND_DOWN(p_vaddr, PAGE_SIZE);
610
611 ret = usys_create_pmo(seg_map_sz, PMO_DATA);
612 if (ret < 0) {
613 goto out_fail;
614 }
615
616 seg_pmo = ret;
617 seg_buf =
618 chcore_auto_map_pmo(seg_pmo, seg_map_sz, VMR_READ | VMR_WRITE);
619 if (!seg_buf) {
620 ret = -errno;
621 usys_revoke_cap(seg_pmo, false);
622 goto out_fail;
623 }
624
625 memset(seg_buf, 0, seg_map_sz);
626
627 /*
628 * OFFSET_MASK is for calculating the final offset for loading
629 * different segments from ELF.
630 * ELF segment can specify not aligned address.
631 */
632 seg_load_start = seg_buf + (p_vaddr & OFFSET_MASK);
633
634 /**
635 * With former chcore_auto_map_pmo, we directly load content of the
636 * range into the pmo, without an intermediate buffer.
637 */
638 ret = loader(param, cur_ph->p_offset, seg_file_sz, seg_load_start);
639 if (ret < 0) {
640 chcore_auto_unmap_pmo(seg_pmo, (vaddr_t)seg_buf, seg_map_sz);
641 usys_revoke_cap(seg_pmo, false);
642 goto out_fail;
643 }
644
645 cur_user_elf_seg->elf_pmo = seg_pmo;
646 cur_user_elf_seg->seg_sz = seg_sz;
647 cur_user_elf_seg->p_vaddr = p_vaddr;
648 cur_user_elf_seg->perm = PFLAGS2VMRFLAGS(cur_ph->p_flags);
649
650 if (cur_user_elf_seg->perm & VMR_EXEC) {
651 usys_cache_flush(
652 (unsigned long)seg_load_start, seg_file_sz, SYNC_IDCACHE);
653 }
654
655 chcore_auto_unmap_pmo(seg_pmo, (vaddr_t)seg_buf, seg_map_sz);
656
657 j++;
658 }
659
660 /**
661 * Calculation of AT_PHDR address is a little unintuitive. It seems that
662 * we have to use a separate pmo to store and map the program header table.
663 * But actually, the PT_LOAD segments have already stored all data required
664 * in runtime of the ELF file. Especially, the first PT_LOAD segment would
665 * contains the ELF header and program headers of the ELF file, i.e., the
666 * p_offset of this segment would be 0. So the virtual address of this
667 * segment is the position of the start of the ELF file in the memory, and
668 * the address of program header table can be calculated by adding this
669 * virtual address with phoff from the ELF header.
670 */
671 elf->elf_meta.phdr_addr =
672 phdr_addr ? phdr_addr :
673 elf_file->p_headers[0].p_vaddr + elf_file->header.e_phoff;
674 elf->elf_meta.phentsize = elf_file->header.e_phentsize;
675 elf->elf_meta.phnum = elf_file->header.e_phnum;
676 elf->elf_meta.flags = elf_file->header.e_flags;
677 elf->elf_meta.entry = elf_file->header.e_entry;
678 elf->elf_meta.type = elf_file->header.e_type;
679
680 *user_elf = elf;
681
682 return 0;
683 out_fail:
684 out_nomem:
685 if (elf) {
686 free_user_elf(elf);
687 }
688 return ret;
689 }
690
load_elf_header_from_fs(const char * path,struct elf_header ** elf_header)691 int load_elf_header_from_fs(const char *path, struct elf_header **elf_header)
692 {
693 int ret = 0, fd;
694 ret = open(path, O_RDONLY);
695
696 if (ret < 0) {
697 return -errno;
698 }
699
700 fd = ret;
701 ret = __load_elf_header(file_range_loader, (void *)(long)fd, elf_header);
702
703 close(fd);
704
705 return ret;
706 }
707
load_elf_by_header_from_fs(const char * path,struct elf_header * elf_header,struct user_elf ** elf)708 int load_elf_by_header_from_fs(const char *path, struct elf_header *elf_header,
709 struct user_elf **elf)
710 {
711 int ret = 0, fd;
712 struct elf_file *elf_file;
713 ret = open(path, O_RDONLY);
714
715 if (ret < 0) {
716 ret = -errno;
717 goto out;
718 }
719
720 fd = ret;
721 ret = __load_elf_ph_sh(
722 elf_header, file_range_loader, (void *)(long)fd, &elf_file);
723 if (ret < 0) {
724 goto out_ph_sh_failed;
725 }
726
727 ret = __load_elf_into_pmos(
728 elf_file, file_range_loader, (void *)(long)fd, elf);
729
730 if (ret == 0) {
731 strncpy((*elf)->path, path, ELF_PATH_LEN);
732 }
733
734 elf_free(elf_file);
735 out_ph_sh_failed:
736 close(fd);
737 out:
738 return ret;
739 }
740
741 /**
742 * @brief This function illustrates the steps required to load an ELF file
743 * from a large, abstract "range". A range is a sequence of bytes, and can
744 * be accessed randomly. For example, a memory buffer can be regarded as a
745 * "range", and a file, combining read() and lseek(), is also a "range" of
746 * course.
747 *
748 * Random access to the range is abstracted through @loader parameter. All
749 * internal operations also use it to perform abstracted random access. So
750 * the caller just need to implement a specific loader to load ELF file
751 * content from a new source.
752 *
753 * To load an ELF file, we have to perform 3 steps.
754 * Step-1: Load header of the ELF file. From the header, we can determine
755 * the ELF is 32-bit or 64-bit, the offset of program headers and section
756 * headers.
757 * Step-2: Load program headers and section headers. Program headers indicates
758 * the memory image of the ELF file. We should use those headers to load
759 * remaining ELF content into memory.
760 * Step-3: Load every loadable(PT_LOAD) segment into memory. A PT_LOAD segment
761 * indicaties a range in the ELF file which should be loaded into a specific
762 * virtual address range. However, this function do not map each segment into
763 * virtual address range they specified. See comments of __load_elf_into_pmos
764 * for more details.
765 *
766 * @param loader [In] abstracted range random access function
767 * @param param [In] custom parameter which would be passed to @loader. It can
768 * be used to store information about the start of the range.
769 * @param elf [Out] returning pointer to newly loaded user_elf struct if
770 * success. The ownership of this pointer is transfered to the caller, so the
771 * caller should free it after use.
772 * @return 0 if success, otherwise -errno is returned.
773 */
load_elf_from_range(range_loader_t loader,void * param,struct user_elf ** elf)774 static int load_elf_from_range(range_loader_t loader, void *param,
775 struct user_elf **elf)
776 {
777 int ret = 0;
778 struct elf_header *elf_header;
779 struct elf_file *elf_file;
780
781 ret = __load_elf_header(loader, param, &elf_header);
782 if (ret < 0) {
783 goto out;
784 }
785
786 ret = __load_elf_ph_sh(elf_header, loader, param, &elf_file);
787 if (ret < 0) {
788 goto out_ph_sh_failed;
789 }
790
791 ret = __load_elf_into_pmos(elf_file, loader, param, elf);
792
793 elf_free(elf_file);
794 out_ph_sh_failed:
795 free(elf_header);
796 out:
797 return ret;
798 }
799
load_elf_from_fs(const char * path,struct user_elf ** elf)800 int load_elf_from_fs(const char *path, struct user_elf **elf)
801 {
802 int ret = 0, fd;
803 ret = open(path, O_RDONLY);
804 if (ret < 0) {
805 return -errno;
806 }
807
808 fd = ret;
809
810 ret = load_elf_from_range(file_range_loader, (void *)(long)fd, elf);
811
812 if (ret == 0) {
813 strncpy((*elf)->path, path, ELF_PATH_LEN);
814 }
815
816 close(fd);
817
818 return ret;
819 }
820
load_elf_from_mem(const char * code,struct user_elf ** elf)821 int load_elf_from_mem(const char *code, struct user_elf **elf)
822 {
823 return load_elf_from_range(memory_range_loader, (void *)code, elf);
824 }