feat: shell and signal demo as loadable user executable
[lunaix-os.git] / lunaix-os / kernel / loader / elf.c
1 #include <lunaix/common.h>
2 #include <lunaix/elf.h>
3 #include <lunaix/fs.h>
4 #include <lunaix/ld.h>
5 #include <lunaix/mm/mmap.h>
6 #include <lunaix/mm/valloc.h>
7 #include <lunaix/mm/vmm.h>
8 #include <lunaix/spike.h>
9
10 int
11 elf_map_segment(struct ld_param* ldparam,
12                 struct v_file* elfile,
13                 struct elf32_phdr* phdr)
14 {
15     assert(PG_ALIGNED(phdr->p_offset));
16
17     int proct = 0;
18     if ((phdr->p_flags & PF_R)) {
19         proct |= PROT_READ;
20     }
21     if ((phdr->p_flags & PF_W)) {
22         proct |= PROT_WRITE;
23     }
24     if ((phdr->p_flags & PF_X)) {
25         proct |= PROT_EXEC;
26     }
27
28     struct mmap_param param = { .vms_mnt = ldparam->vms_mnt,
29                                 .pvms = &ldparam->proc->mm,
30                                 .proct = proct,
31                                 .offset = PG_ALIGN(phdr->p_offset),
32                                 .mlen = ROUNDUP(phdr->p_memsz, PG_SIZE),
33                                 .flen = phdr->p_filesz + PG_MOD(phdr->p_va),
34                                 .flags = MAP_FIXED | MAP_PRIVATE,
35                                 .type = REGION_TYPE_CODE };
36
37     struct mm_region* seg_reg;
38     int status = mem_map(NULL, &seg_reg, PG_ALIGN(phdr->p_va), elfile, &param);
39
40     if (!status) {
41         size_t next_addr = phdr->p_memsz + phdr->p_va;
42         ldparam->info.end = MAX(ldparam->info.end, ROUNDUP(next_addr, PG_SIZE));
43         ldparam->info.mem_sz += phdr->p_memsz;
44     }
45
46     return status;
47 }
48
49 int
50 elf_setup_mapping(struct ld_param* ldparam,
51                   struct v_file* elfile,
52                   struct elf32_ehdr* ehdr)
53 {
54     int status = 0;
55     size_t tbl_sz = ehdr->e_phnum * SIZE_PHDR;
56     struct elf32_phdr* phdrs = valloc(tbl_sz);
57
58     if (!phdrs) {
59         status = ENOMEM;
60         goto done;
61     }
62
63     tbl_sz = 1 << ILOG2(tbl_sz);
64     status = elfile->ops->read(elfile->inode, phdrs, tbl_sz, ehdr->e_phoff);
65
66     if (status < 0) {
67         goto done;
68     }
69
70     if (PG_ALIGN(phdrs[0].p_va) != USER_START) {
71         status = ENOEXEC;
72         goto done;
73     }
74
75     size_t entries = tbl_sz / SIZE_PHDR;
76     for (size_t i = 0; i < entries; i++) {
77         struct elf32_phdr* phdr = &phdrs[i];
78
79         if (phdr->p_type == PT_LOAD) {
80             if (phdr->p_align == PG_SIZE) {
81                 status = elf_map_segment(ldparam, elfile, phdr);
82             } else {
83                 // surprising alignment!
84                 status = ENOEXEC;
85             }
86         }
87         // TODO process other types of segments
88
89         if (status) {
90             // errno in the middle of mapping restructuring, it is impossible
91             // to recover!
92             ldparam->status |= LD_STAT_FKUP;
93             goto done;
94         }
95     }
96
97 done:
98     vfree(phdrs);
99     return status;
100 }
101
102 int
103 elf_load(struct ld_param* ldparam, struct v_file* elfile)
104 {
105     struct elf32_ehdr* ehdr = valloc(SIZE_EHDR);
106     int status = elfile->ops->read(elfile->inode, ehdr, SIZE_EHDR, 0);
107
108     if (status < 0) {
109         goto done;
110     }
111
112     if (!elf_check_exec(ehdr)) {
113         status = ENOEXEC;
114         goto done;
115     }
116
117     if ((status = elf_setup_mapping(ldparam, elfile, ehdr))) {
118         goto done;
119     }
120
121     ldparam->info.ehdr_out = *ehdr;
122
123 done:
124     vfree(ehdr);
125     return status;
126 }