Merge branch 'interrupt-rework' into prog-loader
[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_populate_mapped(struct mm_region* region, void* pg, off_t segfoff)
12 {
13     size_t segsz = region->flen;
14     size_t segmoff = segfoff - region->foff;
15
16     if (segmoff >= segsz) {
17         return 0;
18     }
19
20     struct v_file* file = region->mfile;
21     size_t rdlen = MIN(segsz - segmoff, PG_SIZE);
22
23     if (rdlen == PG_SIZE) {
24         // This is because we want to exploit any optimization on read_page
25         return file->ops->read_page(file->inode, pg, PG_SIZE, segfoff);
26     } else {
27         // we don't want to over-read the segment!
28         return file->ops->read(file->inode, pg, rdlen, segfoff);
29     }
30 }
31
32 int
33 elf_map_segment(struct ld_param* ldparam,
34                 struct v_file* elfile,
35                 struct elf32_phdr* phdr)
36 {
37     int proct = 0;
38     if ((phdr->p_flags & PF_R)) {
39         proct |= PROT_READ;
40     }
41     if ((phdr->p_flags & PF_W)) {
42         proct |= PROT_WRITE;
43     }
44     if ((phdr->p_flags & PF_X)) {
45         proct |= PROT_EXEC;
46     }
47
48     struct mm_region* seg_reg;
49     struct mmap_param param = { .vms_mnt = ldparam->vms_mnt,
50                                 .pvms = &ldparam->proc->mm,
51                                 .proct = proct,
52                                 .offset = phdr->p_offset,
53                                 .mlen = ROUNDUP(phdr->p_memsz, PG_SIZE),
54                                 .flen = phdr->p_filesz,
55                                 .flags = MAP_FIXED | MAP_PRIVATE,
56                                 .type = REGION_TYPE_CODE };
57
58     int status = mem_map(NULL, &seg_reg, PG_ALIGN(phdr->p_va), elfile, &param);
59
60     if (!status) {
61         seg_reg->init_page = __elf_populate_mapped;
62
63         size_t next_addr = phdr->p_memsz + phdr->p_va;
64         ldparam->info.end = MAX(ldparam->info.end, ROUNDUP(next_addr, PG_SIZE));
65         ldparam->info.mem_sz += phdr->p_memsz;
66     }
67
68     return status;
69 }
70
71 int
72 elf_setup_mapping(struct ld_param* ldparam,
73                   struct v_file* elfile,
74                   struct elf32_ehdr* ehdr)
75 {
76     int status = 0;
77     size_t tbl_sz = ehdr->e_phnum * SIZE_PHDR;
78     struct elf32_phdr* phdrs = valloc(tbl_sz);
79
80     if (!phdrs) {
81         status = ENOMEM;
82         goto done;
83     }
84
85     tbl_sz = 1 << ILOG2(tbl_sz);
86     status = elfile->ops->read(elfile->inode, phdrs, tbl_sz, ehdr->e_phoff);
87
88     if (status < 0) {
89         goto done;
90     }
91
92     if (PG_ALIGN(phdrs[0].p_va) != USER_START) {
93         status = ENOEXEC;
94         goto done;
95     }
96
97     size_t entries = tbl_sz / SIZE_PHDR;
98     for (size_t i = 0; i < entries; i++) {
99         struct elf32_phdr* phdr = &phdrs[i];
100
101         if (phdr->p_type == PT_LOAD) {
102             if (phdr->p_align == PG_SIZE) {
103                 status = elf_map_segment(ldparam, elfile, phdr);
104             } else {
105                 // surprising alignment!
106                 status = ENOEXEC;
107             }
108         }
109         // TODO process other types of segments
110
111         if (status) {
112             // errno in the middle of mapping restructuring, it is impossible
113             // to recover!
114             ldparam->status |= LD_STAT_FKUP;
115             goto done;
116         }
117     }
118
119 done:
120     vfree(phdrs);
121     return status;
122 }
123
124 int
125 elf_load(struct ld_param* ldparam, struct v_file* elfile)
126 {
127     struct elf32_ehdr* ehdr = valloc(SIZE_EHDR);
128     int status = elfile->ops->read(elfile->inode, ehdr, SIZE_EHDR, 0);
129
130     if (status < 0) {
131         goto done;
132     }
133
134     if (!elf_check_exec(ehdr)) {
135         status = ENOEXEC;
136         goto done;
137     }
138
139     if ((status = elf_setup_mapping(ldparam, elfile, ehdr))) {
140         goto done;
141     }
142
143     ldparam->info.ehdr_out = *ehdr;
144
145 done:
146     vfree(ehdr);
147     return status;
148 }