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