• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }