Implement serial logging. Finish modularizing power management.
[monolithium.git] / kernel / src / exec / elf.c
1 /*
2  * elf.c
3  *
4  * Copyright (C) 2016 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <exec/elf.h>
21 #include <process.h>
22 #include <module.h>
23 #include <log.h>
24
25 extern void *get_kernel_symbol(const char *name);
26
27 static dword_t elf_hash(const byte_t *name)
28 {
29     dword_t hash = 0;
30
31     while (*name)
32     {
33         hash = (hash << 4) + *name++;
34         hash = (hash ^ ((hash >> 24) & 0xF0)) & ~(hash & 0xF0000000);
35     }
36
37     return hash;
38 }
39
40 dword_t load_elf(handle_t file, thread_state_t *initial_state)
41 {
42     elf_ident_t ident;
43     char filename[MAX_PATH];
44     qword_t filesize;
45     size_t bytes_read;
46
47     sysret_t ret = syscall(SYSCALL_QUERY_FILE, file, FILE_INFO_NAME, filename, sizeof(filename));
48     if (ret != ERR_SUCCESS) return ret;
49
50     ret = syscall(SYSCALL_QUERY_FILE, file, FILE_INFO_SIZE, &filesize, sizeof(filesize));
51     if (ret != ERR_SUCCESS) return ret;
52
53     ret = syscall(SYSCALL_READ_FILE, file, &ident, 0ULL, (size_t)sizeof(ident), &bytes_read);
54     if (ret != ERR_SUCCESS) return ret;
55
56     if (ident.magic != ELF_MAGIC) return ERR_INVALID;
57     if (ident.class != ELF_CLASS_32BIT) return ERR_INVALID;
58
59     elf32_header_t header;
60     ret = syscall(SYSCALL_READ_FILE, file, &header, 16ULL, (size_t)sizeof(header), &bytes_read);
61     if (ret != ERR_SUCCESS) return ret;
62
63     handle_t section;
64     ret = syscall(SYSCALL_CREATE_MEMORY_SECTION, filename, file, (size_t)filesize, MEMORY_SECTION_WRITABLE, &section);
65     if (ret != ERR_SUCCESS) return ret;
66
67     dword_t i;
68     elf32_segment_t *segments = __builtin_alloca(header.segment_count * sizeof(elf32_segment_t));
69
70     ret = syscall(SYSCALL_READ_FILE,
71                   file,
72                   segments,
73                   (qword_t)header.segment_offset,
74                   (size_t)(header.segment_count * sizeof(elf32_segment_t)),
75                   &bytes_read);
76     if (ret != ERR_SUCCESS) return ret;
77
78     for (i = 0; i < header.segment_count; i++)
79     {
80         if (segments[i].type ==  ELF_SEGMENT_NULL)
81         {
82             continue;
83         }
84         else if (ELF_SEGMENT_LOAD)
85         {
86             dword_t flags = MEMORY_BLOCK_USERMODE | MEMORY_BLOCK_EVICTABLE;
87             if (segments[i].flags & ELF_SEGMENT_READ) flags |= MEMORY_BLOCK_ACCESSIBLE;
88             if (segments[i].flags & ELF_SEGMENT_WRITE) flags |= MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_WRITABLE;
89             if (segments[i].flags & ELF_SEGMENT_EXEC) flags |= MEMORY_BLOCK_EXECUTABLE;
90
91             qword_t offset = (qword_t)segments[i].offset - PAGE_OFFSET(segments[i].virtual_address);
92
93             ret = syscall(SYSCALL_MAP_MEMORY_SECTION,
94                           INVALID_HANDLE,
95                           section,
96                           &segments[i].virtual_address,
97                           offset,
98                           PAGE_ALIGN_UP(segments[i].mem_size),
99                           flags);
100             if (ret != ERR_SUCCESS) break;
101         }
102         else
103         {
104             ret = ERR_INVALID;
105             break;
106         }
107     }
108
109     initial_state->regs.eip = header.entry;
110
111     void *stack_address = NULL;
112     ret = syscall(SYSCALL_ALLOC_MEMORY,
113                   INVALID_HANDLE,
114                   &stack_address,
115                   0x200000,
116                   MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_WRITABLE | MEMORY_BLOCK_USERMODE | MEMORY_BLOCK_EVICTABLE);
117     if (ret != ERR_SUCCESS) return ret;
118
119     initial_state->regs.esp = (uintptr_t)stack_address + 0x200000;
120     syscall(SYSCALL_CLOSE_OBJECT, section);
121     return ret;
122 }
123
124 dword_t load_module_elf(void *address, module_t *module)
125 {
126     dword_t ret = ERR_SUCCESS;
127     elf_ident_t *ident = (elf_ident_t*)address;
128     if (ident->magic != ELF_MAGIC) return ERR_INVALID;
129     if (ident->class != ELF_CLASS_32BIT) return ERR_INVALID;
130
131     elf32_header_t *header = (elf32_header_t*)((uintptr_t)address + 16);
132     elf32_segment_t *segments = (elf32_segment_t*)((uintptr_t)address + header->segment_offset);
133     uintptr_t lowest_address = (uintptr_t)-1;
134     uintptr_t highest_address = 0;
135     size_t total_size = 0;
136     const dword_t *hash = NULL;
137     const elf32_symbol_t *symtab = NULL;
138     const char *strtab = NULL;
139     const elf32_rel_t *rel = NULL;
140     size_t rel_size = 0, rel_entsize = 0;
141     const elf32_rela_t *rela = NULL;
142     size_t rela_size = 0, rela_entsize = 0;
143     const void *plt_rel = NULL;
144     dword_t plt_rel_type = 0;
145     size_t plt_rel_size = 0;
146
147     inline void *lookup_symbol(const char *name)
148     {
149         const dword_t *bucket = &hash[2];
150         const dword_t *chain = &hash[2 + hash[0]];
151         dword_t index = bucket[elf_hash((const byte_t*)name) % hash[0]];
152
153         while (index != ELF_SYMBOL_UNDEF)
154         {
155             if (strcmp(name, &strtab[symtab[index].st_name]) == 0)
156             {
157                 return (void*)((uintptr_t)module->base_address - lowest_address + symtab[index].st_value);
158             }
159
160             index = chain[index];
161         }
162
163         return NULL;
164     }
165
166     inline void relocate_with_addend(const elf32_rela_t *rela)
167     {
168         void *location = (void*)(module->base_address - lowest_address + rela->r_offset);
169         dword_t index = ELF_R_SYM(rela->r_info);
170         uintptr_t symbol = 0;
171
172         if (index != ELF_SYMBOL_UNDEF)
173         {
174             symbol = symtab[index].st_value
175                 ? (uintptr_t)module->base_address - lowest_address + symtab[index].st_value
176                 : (uintptr_t)get_kernel_symbol(&strtab[symtab[index].st_name]);
177         }
178
179         switch (ELF_R_TYPE(rela->r_info))
180         {
181         case ELF_R_386_32: *(dword_t*)location += symbol + rela->r_addend; break;
182         case ELF_R_386_PC32: *(dword_t*)location += symbol + rela->r_addend - (uintptr_t)location; break;
183
184         case ELF_R_386_GOT32:
185             log_write(LOG_WARNING, "missing support: GOT32\n");
186             /* *(dword_t*)location = G + rela->r_addend */;
187             break;
188
189         case ELF_R_386_PLT32:
190             log_write(LOG_WARNING, "missing support: PLT32\n");
191             /* *(dword_t*)location = L + rela->r_addend - (uintptr_t)location; */ break;
192
193         case ELF_R_386_RELATIVE: *(dword_t*)location += (uintptr_t)module->base_address + rela->r_addend; break;
194
195         case ELF_R_386_GOTOFF:
196             log_write(LOG_WARNING, "missing support: GOTOFF\n");
197             /* *(dword_t*)location = symbol + rela->r_addend - GOT; */ break;
198
199         case ELF_R_386_GOTPC:
200             log_write(LOG_WARNING, "missing support: GOTPC\n");
201             /* *(dword_t*)location = GOT + rela->r_addend - (uintptr_t)location; */
202             break;
203
204         case ELF_R_386_32PLT:
205             log_write(LOG_WARNING, "missing support: 32PLT\n");
206             /* *(dword_t*)location = L + rela->r_addend */;
207             break;
208
209         case ELF_R_386_16: *(word_t*)location += symbol + rela->r_addend; break;
210         case ELF_R_386_PC16: *(word_t*)location += symbol + rela->r_addend - (uintptr_t)location; break;
211         case ELF_R_386_8: *(byte_t*)location += symbol + rela->r_addend; break;
212         case ELF_R_386_PC8: *(byte_t*)location += symbol + rela->r_addend - (uintptr_t)location; break;
213
214         case ELF_R_386_GLOB_DAT:
215         case ELF_R_386_JMP_SLOT:
216             *(dword_t*)location = symbol; break;
217         }
218     }
219
220     inline void relocate(const elf32_rel_t *rel)
221     {
222         elf32_rela_t rela;
223         rela.r_offset = rel->r_offset;
224         rela.r_info = rel->r_info;
225         rela.r_addend = 0;
226         relocate_with_addend(&rela);
227     }
228
229     int i;
230     for (i = 0; i < header->segment_count; i++)
231     {
232         if (segments[i].type == ELF_SEGMENT_LOAD)
233         {
234             uintptr_t segment_highest = PAGE_ALIGN_UP(segments[i].virtual_address + segments[i].mem_size);
235             if (segments[i].virtual_address < lowest_address) lowest_address = segments[i].virtual_address;
236             if (segment_highest > highest_address) highest_address = segment_highest;
237         }
238     }
239
240     if (highest_address <= lowest_address) return ERR_INVALID;
241     total_size = highest_address - lowest_address;
242
243     while (TRUE)
244     {
245         /* Temporary HACK until memory region reservation is implemented */
246         module->base_address = alloc_pool(NULL, PAGE_ALIGN_UP(total_size), 0);
247         if (ret != ERR_SUCCESS) return ret;
248
249         bool_t success = TRUE;
250         free_pool(module->base_address);
251
252         for (i = 0; i < header->segment_count; i++) if (segments[i].type == ELF_SEGMENT_LOAD)
253         {
254             dword_t flags = 0;
255             if (segments[i].flags & ELF_SEGMENT_READ) flags |= MEMORY_BLOCK_ACCESSIBLE;
256             if (segments[i].flags & ELF_SEGMENT_WRITE) flags |= MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_WRITABLE;
257             if (segments[i].flags & ELF_SEGMENT_EXEC) flags |= MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_EXECUTABLE;
258
259             module_segment_t *mod_seg = (module_segment_t*)malloc(sizeof(module_segment_t));
260             mod_seg->address = (void*)((uintptr_t)module->base_address + segments[i].virtual_address - lowest_address);
261             mod_seg->size = segments[i].mem_size;
262
263             if (!alloc_pool(mod_seg->address, mod_seg->size, flags | MEMORY_BLOCK_WRITABLE))
264             {
265                 success = FALSE;
266                 free(mod_seg);
267                 break;
268             }
269
270             memcpy(mod_seg->address, (void*)((uintptr_t)address + segments[i].offset), segments[i].file_size);
271             list_append(&module->segments, &mod_seg->link);
272         }
273
274         if (!success)
275         {
276             module->base_address = NULL;
277
278             while (module->segments.next != &module->segments)
279             {
280                 module_segment_t *mod_seg = CONTAINER_OF(module->segments.next, module_segment_t, link);
281                 free_pool(mod_seg->address);
282                 list_remove(&mod_seg->link);
283                 free(mod_seg);
284             }
285         }
286         else
287         {
288             break;
289         }
290     }
291
292     elf32_dynamic_t *dynamic;
293     for (i = 0; i < header->segment_count; i++) if (segments[i].type == ELF_SEGMENT_DYNAMIC)
294     {
295         for (dynamic = (elf32_dynamic_t*)((uintptr_t)address + segments[i].offset);
296              dynamic->d_tag != ELF_DYNAMIC_TAG_NULL;
297              dynamic++)
298         {
299             switch (dynamic->d_tag)
300             {
301             case ELF_DYNAMIC_TAG_HASH:
302                 hash = (const dword_t*)((uintptr_t)module->base_address - lowest_address + dynamic->d_ptr);
303                 break;
304             case ELF_DYNAMIC_TAG_SYMTAB:
305                 symtab = (const elf32_symbol_t*)((uintptr_t)module->base_address - lowest_address + dynamic->d_ptr);
306                 break;
307             case ELF_DYNAMIC_TAG_STRTAB:
308                 strtab = (const char*)((uintptr_t)module->base_address - lowest_address + dynamic->d_ptr);
309                 break;
310             case ELF_DYNAMIC_TAG_REL:
311                 rel = (const elf32_rel_t*)((uintptr_t)module->base_address - lowest_address + dynamic->d_ptr);
312                 break;
313             case ELF_DYNAMIC_TAG_RELSZ:
314                 rel_size = dynamic->d_val;
315                 break;
316             case ELF_DYNAMIC_TAG_RELENT:
317                 rel_entsize = dynamic->d_val;
318                 break;
319             case ELF_DYNAMIC_TAG_RELA:
320                 rela = (const elf32_rela_t*)((uintptr_t)module->base_address - lowest_address + dynamic->d_ptr);
321                 break;
322             case ELF_DYNAMIC_TAG_RELASZ:
323                 rela_size = dynamic->d_val;
324                 break;
325             case ELF_DYNAMIC_TAG_RELAENT:
326                 rela_entsize = dynamic->d_val;
327                 break;
328             case ELF_DYNAMIC_TAG_JMPREL:
329                 plt_rel = (const void*)((uintptr_t)module->base_address - lowest_address + dynamic->d_ptr);
330                 break;
331             case ELF_DYNAMIC_TAG_PLTRELSZ:
332                 plt_rel_size = dynamic->d_val;
333                 break;
334             case ELF_DYNAMIC_TAG_PLTREL:
335                 plt_rel_type = dynamic->d_val;
336                 break;
337             }
338         }
339     }
340
341     if (!hash || !symtab || !strtab)
342     {
343         while (module->segments.next != &module->segments)
344         {
345             module_segment_t *mod_seg = CONTAINER_OF(module->segments.next, module_segment_t, link);
346             free_pool(mod_seg->address);
347             list_remove(&mod_seg->link);
348             free(mod_seg);
349         }
350
351         return ERR_INVALID;
352     }
353
354     if (rel && rel_entsize == sizeof(elf32_rel_t))
355     {
356         for (i = 0; i < rel_size / rel_entsize; i++) relocate(&rel[i]);
357     }
358
359     if (rela && rela_entsize == sizeof(elf32_rela_t))
360     {
361         for (i = 0; i < rela_size / rela_entsize; i++) relocate_with_addend(&rela[i + 1]);
362     }
363
364     if (plt_rel)
365     {
366         if (plt_rel_type == ELF_DYNAMIC_TAG_REL)
367         {
368             for (i = 0; i < plt_rel_size / sizeof(elf32_rel_t); i++) relocate(&((elf32_rel_t*)plt_rel)[i]);
369         }
370         else if (plt_rel_type == ELF_DYNAMIC_TAG_RELA)
371         {
372             for (i = 0; i < plt_rel_size / sizeof(elf32_rela_t); i++) relocate_with_addend(&((elf32_rela_t*)plt_rel)[i]);
373         }
374     }
375
376     if (ret == ERR_SUCCESS)
377     {
378         const char *driver_name = lookup_symbol("driver_name");
379         if (driver_name != NULL) module->name = strdup(driver_name);
380         module->load_proc = lookup_symbol("driver_load");
381         module->unload_proc = lookup_symbol("driver_unload");
382     }
383
384     return ret;
385 }