few fixes/additions:
[newos.git] / kernel / elf.c
blob1e414ddae0e19d857015bae2d745db342e6ac22d
1 /*
2 ** Copyright 2001, Travis Geiselbrecht. All rights reserved.
3 ** Distributed under the terms of the NewOS License.
4 */
5 #include <kernel/kernel.h>
6 #include <sys/errors.h>
7 #include <kernel/elf.h>
8 #include <kernel/vfs.h>
9 #include <kernel/vm.h>
10 #include <kernel/thread.h>
11 #include <kernel/debug.h>
12 #include <kernel/heap.h>
13 #include <kernel/arch/cpu.h>
15 #include <sys/elf32.h>
17 #include <libc/string.h>
18 #include <libc/printf.h>
20 struct elf_region {
21 region_id id;
22 addr start;
23 addr size;
24 long delta;
27 struct elf_image_info {
28 struct elf_image_info *next;
29 image_id id;
30 void *vnode;
31 struct elf_region regions[2]; // describes the text and data regions
32 addr dynamic_ptr; // pointer to the dynamic section
34 // pointer to symbol participation data structures
35 char *needed;
36 unsigned int *symhash;
37 struct Elf32_Sym *syms;
38 char *strtab;
39 struct Elf32_Rel *rel;
40 int rel_len;
41 struct Elf32_Rela *rela;
42 int rela_len;
45 static struct elf_image_info *kernel_images = NULL;
46 static struct elf_image_info *kernel_image = NULL;
47 static mutex image_lock;
48 static mutex image_load_lock;
49 static image_id next_image_id = 0;
51 #define STRING(image, offset) ((char *)(&(image)->strtab[(offset)]))
52 #define SYMNAME(image, sym) STRING(image, (sym)->st_name)
53 #define SYMBOL(image, num) ((struct Elf32_Sym *)&(image)->syms[num])
54 #define HASHTABSIZE(image) ((image)->symhash[0])
55 #define HASHBUCKETS(image) ((unsigned int *)&(image)->symhash[2])
56 #define HASHCHAINS(image) ((unsigned int *)&(image)->symhash[2+HASHTABSIZE(image)])
58 static void insert_image_in_list(struct elf_image_info *image)
60 mutex_lock(&image_lock);
62 image->next = kernel_images;
63 kernel_images = image;
65 mutex_unlock(&image_lock);
68 static struct elf_image_info *find_image(image_id id)
70 struct elf_image_info *image;
72 mutex_lock(&image_lock);
74 for(image = kernel_images; image; image = image->next) {
75 if(image->id == id)
76 break;
78 mutex_unlock(&image_lock);
80 return image;
83 static struct elf_image_info *find_image_by_vnode(void *vnode)
85 struct elf_image_info *image;
87 mutex_lock(&image_lock);
89 for(image = kernel_images; image; image = image->next) {
90 if(image->vnode == vnode)
91 break;
93 mutex_unlock(&image_lock);
95 return image;
98 static struct elf_image_info *create_image_struct()
100 struct elf_image_info *image;
102 image = kmalloc(sizeof(struct elf_image_info));
103 if(!image)
104 return NULL;
105 memset(image, 0, sizeof(struct elf_image_info));
106 image->regions[0].id = -1;
107 image->regions[1].id = -1;
108 image->id = atomic_add(&next_image_id, 1);
109 return image;
112 static unsigned long elf_hash(const unsigned char *name)
114 unsigned long hash = 0;
115 unsigned long temp;
117 while(*name) {
118 hash = (hash << 4) + *name++;
119 if((temp = hash & 0xf0000000))
120 hash ^= temp >> 24;
121 hash &= ~temp;
123 return hash;
126 static void dump_image_info(struct elf_image_info *image)
128 int i;
130 dprintf("elf_image_info at 0x%x:\n", image);
131 dprintf(" next 0x%x\n", image->next);
132 dprintf(" id 0x%x\n", image->id);
133 for(i=0; i<2; i++) {
134 dprintf(" regions[%d].id 0x%x\n", i, image->regions[i].id);
135 dprintf(" regions[%d].start 0x%x\n", i, image->regions[i].start);
136 dprintf(" regions[%d].size 0x%x\n", i, image->regions[i].size);
137 dprintf(" regions[%d].delta %d\n", i, image->regions[i].delta);
139 dprintf(" dynamic_ptr 0x%x\n", image->dynamic_ptr);
140 dprintf(" needed 0x%x\n", image->needed);
141 dprintf(" symhash 0x%x\n", image->symhash);
142 dprintf(" syms 0x%x\n", image->syms);
143 dprintf(" strtab 0x%x\n", image->strtab);
144 dprintf(" rel 0x%x\n", image->rel);
145 dprintf(" rel_len 0x%x\n", image->rel_len);
146 dprintf(" rela 0x%x\n", image->rela);
147 dprintf(" rela_len 0x%x\n", image->rela_len);
150 static void dump_symbol(struct elf_image_info *image, struct Elf32_Sym *sym)
153 dprintf("symbol at 0x%x, in image 0x%x\n", sym, image);
155 dprintf(" name index %d, '%s'\n", sym->st_name, SYMNAME(image, sym));
156 dprintf(" st_value 0x%x\n", sym->st_value);
157 dprintf(" st_size %d\n", sym->st_size);
158 dprintf(" st_info 0x%x\n", sym->st_info);
159 dprintf(" st_other 0x%x\n", sym->st_other);
160 dprintf(" st_shndx %d\n", sym->st_shndx);
163 static struct Elf32_Sym *elf_find_symbol(struct elf_image_info *image, const char *name)
165 unsigned int hash;
166 unsigned int i;
168 hash = elf_hash(name) % HASHTABSIZE(image);
169 for(i = HASHBUCKETS(image)[hash]; i != STN_UNDEF; i = HASHCHAINS(image)[i]) {
170 if(!strcmp(SYMNAME(image, &image->syms[i]), name)) {
171 return &image->syms[i];
175 return NULL;
178 addr elf_lookup_symbol(image_id id, const char *symbol)
180 struct elf_image_info *image;
181 struct Elf32_Sym *sym;
183 image = find_image(id);
184 if(!image)
185 return 0;
187 sym = elf_find_symbol(image, symbol);
188 if(!sym)
189 return 0;
191 if(sym->st_shndx == SHN_UNDEF) {
192 return 0;
194 return sym->st_value + image->regions[0].delta;
197 static int elf_parse_dynamic_section(struct elf_image_info *image)
199 struct Elf32_Dyn *d;
200 int i;
201 int needed_offset = -1;
203 // dprintf("top of elf_parse_dynamic_section\n");
205 image->symhash = 0;
206 image->syms = 0;
207 image->strtab = 0;
209 d = (struct Elf32_Dyn *)image->dynamic_ptr;
210 if(!d)
211 return ERR_GENERAL;
213 for(i=0; d[i].d_tag != DT_NULL; i++) {
214 switch(d[i].d_tag) {
215 case DT_NEEDED:
216 needed_offset = d[i].d_un.d_ptr + image->regions[0].delta;
217 break;
218 case DT_HASH:
219 image->symhash = (unsigned int *)(d[i].d_un.d_ptr + image->regions[0].delta);
220 break;
221 case DT_STRTAB:
222 image->strtab = (char *)(d[i].d_un.d_ptr + image->regions[0].delta);
223 break;
224 case DT_SYMTAB:
225 image->syms = (struct Elf32_Sym *)(d[i].d_un.d_ptr + image->regions[0].delta);
226 break;
227 case DT_REL:
228 image->rel = (struct Elf32_Rel *)(d[i].d_un.d_ptr + image->regions[0].delta);
229 break;
230 case DT_RELSZ:
231 image->rel_len = d[i].d_un.d_val;
232 break;
233 case DT_RELA:
234 image->rela = (struct Elf32_Rela *)(d[i].d_un.d_ptr + image->regions[0].delta);
235 break;
236 case DT_RELASZ:
237 image->rela_len = d[i].d_un.d_val;
238 break;
239 default:
240 continue;
244 // lets make sure we found all the required sections
245 if(!image->symhash || !image->syms || !image->strtab)
246 return ERR_GENERAL;
248 // dprintf("needed_offset = %d\n", needed_offset);
250 if(needed_offset >= 0)
251 image->needed = STRING(image, needed_offset);
253 return NO_ERROR;
256 // this function first tries to see if the first image and it's already resolved symbol is okay, otherwise
257 // it tries to link against the shared_image
258 // XXX gross hack and needs to be done better
259 static addr elf_resolve_symbol(struct elf_image_info *image, struct Elf32_Sym *sym, struct elf_image_info *shared_image)
261 struct Elf32_Sym *sym2;
263 switch(sym->st_shndx) {
264 case SHN_UNDEF:
265 // it's undefined, must be outside this image, try the other image
266 sym2 = elf_find_symbol(shared_image, SYMNAME(image, sym));
267 if(!sym2) {
268 dprintf("elf_resolve_symbol: could not resolve symbol '%s'\n", SYMNAME(image, sym));
269 return 0;
272 // make sure they're the same type
273 if(ELF32_ST_TYPE(sym->st_info) != ELF32_ST_TYPE(sym2->st_info)) {
274 dprintf("elf_resolve_symbol: found symbol '%s' in shared image but wrong type\n", SYMNAME(image, sym));
275 return 0;
278 if(ELF32_ST_BIND(sym2->st_info) != STB_GLOBAL && ELF32_ST_BIND(sym2->st_info) != STB_WEAK) {
279 dprintf("elf_resolve_symbol: found symbol '%s' but not exported\n", SYMNAME(image, sym));
280 return 0;
283 return sym2->st_value + shared_image->regions[0].delta;
284 case SHN_ABS:
285 return sym->st_value;
286 case SHN_COMMON:
287 // XXX finish this
288 dprintf("elf_resolve_symbol: COMMON symbol, finish me!\n");
289 return 0;
290 default:
291 // standard symbol
292 return sym->st_value + image->regions[0].delta;
296 // XXX for now just link against the kernel
297 static int elf_relocate(struct elf_image_info *image)
299 int i;
300 struct Elf32_Sym *sym;
301 addr S;
302 addr A;
303 addr P;
304 addr final_val;
306 S = A = P = 0;
308 // dprintf("top of elf_relocate\n");
310 // deal with the rels first
311 if(image->rel) {
312 for(i = 0; i * (int)sizeof(struct Elf32_Rel) < image->rel_len; i++) {
313 // dprintf("looking at rel type %d, offset 0x%x\n", ELF32_R_TYPE(image->rel[i].r_info), image->rel[i].r_offset);
315 // calc S
316 switch(ELF32_R_TYPE(image->rel[i].r_info)) {
317 case R_386_32:
318 case R_386_PC32:
319 case R_386_GLOB_DAT:
320 case R_386_JMP_SLOT:
321 case R_386_GOTOFF:
322 sym = SYMBOL(image, ELF32_R_SYM(image->rel[i].r_info));
324 S = elf_resolve_symbol(image, sym, kernel_image);
325 // dprintf("S 0x%x\n", S);
327 // calc A
328 switch(ELF32_R_TYPE(image->rel[i].r_info)) {
329 case R_386_32:
330 case R_386_PC32:
331 case R_386_GOT32:
332 case R_386_PLT32:
333 case R_386_RELATIVE:
334 case R_386_GOTOFF:
335 case R_386_GOTPC:
336 A = *(addr *)(image->regions[0].delta + image->rel[i].r_offset);
337 // dprintf("A 0x%x\n", A);
338 break;
340 // calc P
341 switch(ELF32_R_TYPE(image->rel[i].r_info)) {
342 case R_386_PC32:
343 case R_386_GOT32:
344 case R_386_PLT32:
345 case R_386_GOTPC:
346 P = image->regions[0].delta + image->rel[i].r_offset;
347 // dprintf("P 0x%x\n", P);
348 break;
351 switch(ELF32_R_TYPE(image->rel[i].r_info)) {
352 case R_386_NONE:
353 continue;
354 case R_386_32:
355 final_val = S + A;
356 break;
357 case R_386_PC32:
358 final_val = S + A - P;
359 break;
360 case R_386_RELATIVE:
361 // B + A;
362 final_val = image->regions[0].delta + A;
363 break;
364 default:
365 dprintf("unhandled relocation type %d\n", ELF32_R_TYPE(image->rel[i].r_info));
366 return ERR_NOT_ALLOWED;
368 *(addr *)(image->regions[0].delta + image->rel[i].r_offset) = final_val;
372 if(image->rela) {
373 dprintf("RELA relocations not supported\n");
374 return ERR_NOT_ALLOWED;
375 for(i = 1; i * (int)sizeof(struct Elf32_Rela) < image->rela_len; i++) {
376 dprintf("rela: type %d\n", ELF32_R_TYPE(image->rela[i].r_info));
379 return 0;
382 static int verify_eheader(struct Elf32_Ehdr *eheader)
384 if(memcmp(eheader->e_ident, ELF_MAGIC, 4) != 0)
385 return ERR_INVALID_BINARY;
387 if(eheader->e_ident[4] != ELFCLASS32)
388 return ERR_INVALID_BINARY;
390 if(eheader->e_phoff == 0)
391 return ERR_INVALID_BINARY;
393 if(eheader->e_phentsize < sizeof(struct Elf32_Phdr))
394 return ERR_INVALID_BINARY;
396 return 0;
399 int elf_load_uspace(const char *path, struct proc *p, int flags, addr *entry)
401 struct Elf32_Ehdr eheader;
402 struct Elf32_Phdr *pheaders = NULL;
403 int fd;
404 int err;
405 int i;
406 ssize_t len;
408 dprintf("elf_load: entry path '%s', proc 0x%x\n", path, p);
410 fd = sys_open(path, STREAM_TYPE_FILE, 0);
411 if(fd < 0)
412 return fd;
414 len = sys_read(fd, &eheader, 0, sizeof(eheader));
415 if(len < 0) {
416 err = len;
417 goto error;
419 if(len != sizeof(eheader)) {
420 // short read
421 err = ERR_INVALID_BINARY;
422 goto error;
424 err = verify_eheader(&eheader);
425 if(err < 0)
426 goto error;
428 pheaders = kmalloc(eheader.e_phnum * eheader.e_phentsize);
429 if(pheaders == NULL) {
430 dprintf("error allocating space for program headers\n");
431 err = ERR_NO_MEMORY;
432 goto error;
435 dprintf("reading in program headers at 0x%x, len 0x%x\n", eheader.e_phoff, eheader.e_phnum * eheader.e_phentsize);
436 len = sys_read(fd, pheaders, eheader.e_phoff, eheader.e_phnum * eheader.e_phentsize);
437 if(len < 0) {
438 err = len;
439 dprintf("error reading in program headers\n");
440 goto error;
442 if(len != eheader.e_phnum * eheader.e_phentsize) {
443 dprintf("short read while reading in program headers\n");
444 err = -1;
445 goto error;
448 for(i=0; i < eheader.e_phnum; i++) {
449 char region_name[64];
450 region_id id;
451 char *region_addr;
453 sprintf(region_name, "%s_seg%d", path, i);
455 region_addr = (char *)ROUNDOWN(pheaders[i].p_vaddr, PAGE_SIZE);
456 id = vm_create_anonymous_region(p->aspace_id, region_name, (void **)&region_addr, REGION_ADDR_EXACT_ADDRESS,
457 ROUNDUP(pheaders[i].p_memsz + (pheaders[i].p_vaddr % PAGE_SIZE), PAGE_SIZE), REGION_WIRING_LAZY, LOCK_RW);
458 if(id < 0) {
459 dprintf("error allocating region!\n");
460 err = ERR_INVALID_BINARY;
461 goto error;
464 len = sys_read(fd, region_addr + (pheaders[i].p_vaddr % PAGE_SIZE), pheaders[i].p_offset, pheaders[i].p_filesz);
465 if(len < 0) {
466 err = len;
467 dprintf("error reading in seg %d\n", i);
468 goto error;
471 if(i == 0)
472 *entry = pheaders[i].p_vaddr;
475 dprintf("elf_load: done!\n");
477 err = 0;
479 error:
480 if(pheaders)
481 kfree(pheaders);
482 sys_close(fd);
484 return err;
487 image_id elf_load_kspace(const char *path)
489 struct Elf32_Ehdr eheader;
490 struct Elf32_Phdr *pheaders;
491 struct elf_image_info *image;
492 void *vnode = NULL;
493 int fd;
494 int err;
495 int i;
496 ssize_t len;
498 dprintf("elf_load_kspace: entry path '%s'\n", path);
500 fd = sys_open(path, STREAM_TYPE_FILE, 0);
501 if(fd < 0)
502 return fd;
504 err = vfs_get_vnode_from_fd(fd, true, &vnode);
505 if(err < 0)
506 goto error0;
508 // XXX awful hack to keep someone else from trying to load this image
509 // probably not a bad thing, shouldn't be too many races
510 mutex_lock(&image_load_lock);
512 // make sure it's not loaded already. Search by vnode
513 if(find_image_by_vnode(vnode)) {
514 err = ERR_NOT_ALLOWED;
515 goto error;
518 len = sys_read(fd, &eheader, 0, sizeof(eheader));
519 if(len < 0) {
520 err = len;
521 goto error;
523 if(len != sizeof(eheader)) {
524 // short read
525 err = ERR_INVALID_BINARY;
526 goto error;
528 err = verify_eheader(&eheader);
529 if(err < 0)
530 goto error;
532 image = create_image_struct();
533 if(!image) {
534 err = ERR_NO_MEMORY;
535 goto error;
537 image->vnode = vnode;
539 pheaders = kmalloc(eheader.e_phnum * eheader.e_phentsize);
540 if(pheaders == NULL) {
541 dprintf("error allocating space for program headers\n");
542 err = ERR_NO_MEMORY;
543 goto error1;
546 // dprintf("reading in program headers at 0x%x, len 0x%x\n", eheader.e_phoff, eheader.e_phnum * eheader.e_phentsize);
547 len = sys_read(fd, pheaders, eheader.e_phoff, eheader.e_phnum * eheader.e_phentsize);
548 if(len < 0) {
549 err = len;
550 dprintf("error reading in program headers\n");
551 goto error2;
553 if(len != eheader.e_phnum * eheader.e_phentsize) {
554 dprintf("short read while reading in program headers\n");
555 err = -1;
556 goto error2;
559 for(i=0; i < eheader.e_phnum; i++) {
560 char region_name[64];
561 bool ro_segment_handled = false;
562 bool rw_segment_handled = false;
563 int image_region;
564 int lock;
566 // dprintf("looking at program header %d\n", i);
568 switch(pheaders[i].p_type) {
569 case PT_LOAD:
570 break;
571 case PT_DYNAMIC:
572 image->dynamic_ptr = pheaders[i].p_vaddr;
573 continue;
574 default:
575 dprintf("unhandled pheader type 0x%x\n", pheaders[i].p_type);
576 continue;
579 // we're here, so it must be a PT_LOAD segment
580 if((pheaders[i].p_flags & (PF_R | PF_W | PF_X)) == (PF_R | PF_W)) {
581 // this is the writable segment
582 if(rw_segment_handled) {
583 // we've already created this segment
584 continue;
586 rw_segment_handled = true;
587 image_region = 1;
588 lock = LOCK_RW|LOCK_KERNEL;
589 sprintf(region_name, "%s_rw", path);
590 } else if((pheaders[i].p_flags & (PF_R | PF_W | PF_X)) == (PF_R | PF_X)) {
591 // this is the non-writable segment
592 if(ro_segment_handled) {
593 // we've already created this segment
594 continue;
596 ro_segment_handled = true;
597 image_region = 0;
598 // lock = LOCK_RO|LOCK_KERNEL;
599 lock = LOCK_RW|LOCK_KERNEL;
600 sprintf(region_name, "%s_ro", path);
601 } else {
602 dprintf("weird program header flags 0x%x\n", pheaders[i].p_flags);
603 continue;
605 image->regions[image_region].size = ROUNDUP(pheaders[i].p_memsz + (pheaders[i].p_vaddr % PAGE_SIZE), PAGE_SIZE);
606 image->regions[image_region].id = vm_create_anonymous_region(vm_get_kernel_aspace_id(), region_name,
607 (void **)&image->regions[image_region].start, REGION_ADDR_ANY_ADDRESS,
608 image->regions[image_region].size, REGION_WIRING_WIRED, lock);
609 if(image->regions[image_region].id < 0) {
610 dprintf("error allocating region!\n");
611 err = ERR_INVALID_BINARY;
612 goto error2;
614 image->regions[image_region].delta = image->regions[image_region].start - pheaders[i].p_vaddr;
616 // dprintf("elf_load_kspace: created a region at 0x%x\n", image->regions[image_region].start);
618 len = sys_read(fd, (void *)(image->regions[image_region].start + (pheaders[i].p_vaddr % PAGE_SIZE)),
619 pheaders[i].p_offset, pheaders[i].p_filesz);
620 if(len < 0) {
621 err = len;
622 dprintf("error reading in seg %d\n", i);
623 goto error3;
627 if(image->regions[1].start != 0) {
628 if(image->regions[0].delta != image->regions[1].delta) {
629 dprintf("could not load binary, fix the region problem!\n");
630 err = ERR_NO_MEMORY;
631 goto error3;
635 // modify the dynamic ptr by the delta of the regions
636 image->dynamic_ptr += image->regions[0].delta;
638 err = elf_parse_dynamic_section(image);
639 if(err < 0)
640 goto error3;
642 err = elf_relocate(image);
643 if(err < 0)
644 goto error3;
646 // dprintf("elf_load_kspace: done!\n");
648 err = 0;
650 kfree(pheaders);
651 sys_close(fd);
653 insert_image_in_list(image);
655 mutex_unlock(&image_load_lock);
657 return image->id;
659 error3:
660 if(image->regions[1].id >= 0)
661 vm_delete_region(vm_get_kernel_aspace_id(), image->regions[1].id);
662 if(image->regions[0].id >= 0)
663 vm_delete_region(vm_get_kernel_aspace_id(), image->regions[0].id);
664 error2:
665 kfree(image);
666 error1:
667 kfree(pheaders);
668 error:
669 mutex_unlock(&image_load_lock);
670 error0:
671 if(vnode)
672 vfs_put_vnode_ptr(vnode);
673 sys_close(fd);
675 return err;
678 int elf_init(kernel_args *ka)
680 vm_region_info rinfo;
681 int err;
683 // build a image structure for the kernel, which has already been loaded
684 kernel_image = create_image_struct();
686 // text segment
687 kernel_image->regions[0].id = vm_find_region_by_name(vm_get_kernel_aspace_id(), "kernel_ro");
688 if(kernel_image->regions[0].id < 0)
689 panic("elf_init: could not look up kernel text segment region\n");
690 vm_get_region_info(kernel_image->regions[0].id, &rinfo);
691 kernel_image->regions[0].start = rinfo.base;
692 kernel_image->regions[0].size = rinfo.size;
694 // data segment
695 kernel_image->regions[1].id = vm_find_region_by_name(vm_get_kernel_aspace_id(), "kernel_rw");
696 if(kernel_image->regions[1].id < 0)
697 panic("elf_init: could not look up kernel data segment region\n");
698 vm_get_region_info(kernel_image->regions[1].id, &rinfo);
699 kernel_image->regions[1].start = rinfo.base;
700 kernel_image->regions[1].size = rinfo.size;
702 // we know where the dynamic section is
703 kernel_image->dynamic_ptr = (addr)ka->kernel_dynamic_section_addr.start;
705 // parse the dynamic section
706 if(elf_parse_dynamic_section(kernel_image) < 0)
707 panic("elf_init: elf_parse_dynamic_section doesn't like the kernel image\n");
709 // insert it first in the list of kernel images loaded
710 kernel_images = NULL;
711 insert_image_in_list(kernel_image);
713 mutex_init(&image_lock, "kimages_lock");
714 mutex_init(&image_load_lock, "kimages_load_lock");
716 return 0;