Working on a kernel elf loader.
[newos.git] / kernel / elf.c
blobd5c096a431da5d2b1d2bb0d0f54ecbd232c61523
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;
26 struct elf_image_info {
27 struct elf_image_info *next;
28 struct elf_region regions[2]; // describes the text and data regions
29 addr dynamic_ptr; // pointer to the dynamic section
31 // pointer to symbol participation data structures
32 unsigned int *symhash;
33 struct Elf32_Sym *syms;
34 char *strtab;
37 static struct elf_image_info *kernel_images = NULL;
38 static struct elf_image_info *kernel_image = NULL;
40 #define SYMNAME(image, sym) ((char *)(&(image)->strtab[(sym)->st_name]))
41 #define HASHTABSIZE(image) ((image)->symhash[0])
42 #define HASHBUCKETS(image) ((unsigned int *)&(image)->symhash[2])
43 #define HASHCHAINS(image) ((unsigned int *)&(image)->symhash[2+HASHTABSIZE(image)])
45 static unsigned long elf_hash(const unsigned char *name)
47 unsigned long hash = 0;
48 unsigned long temp;
50 while(*name) {
51 hash = (hash << 4) + *name++;
52 if((temp = hash & 0xf0000000))
53 hash ^= temp >> 24;
54 hash &= ~temp;
56 return hash;
59 static int verify_eheader(struct Elf32_Ehdr *eheader)
61 if(memcmp(eheader->e_ident, ELF_MAGIC, 4) != 0)
62 return ERR_INVALID_BINARY;
64 if(eheader->e_ident[4] != ELFCLASS32)
65 return ERR_INVALID_BINARY;
67 if(eheader->e_phoff == 0)
68 return ERR_INVALID_BINARY;
70 if(eheader->e_phentsize < sizeof(struct Elf32_Phdr))
71 return ERR_INVALID_BINARY;
73 return 0;
76 int elf_load_uspace(const char *path, struct proc *p, int flags, addr *entry)
78 struct Elf32_Ehdr eheader;
79 struct Elf32_Phdr *pheaders;
80 int fd;
81 int err;
82 int i;
83 ssize_t len;
85 dprintf("elf_load: entry path '%s', proc 0x%x\n", path, p);
87 fd = sys_open(path, STREAM_TYPE_FILE, 0);
88 if(fd < 0)
89 return fd;
91 len = sys_read(fd, &eheader, 0, sizeof(eheader));
92 if(len < 0) {
93 err = len;
94 goto error;
96 if(len != sizeof(eheader)) {
97 // short read
98 err = ERR_INVALID_BINARY;
99 goto error;
101 err = verify_eheader(&eheader);
102 if(err < 0)
103 goto error;
105 pheaders = kmalloc(eheader.e_phnum * eheader.e_phentsize);
106 if(pheaders == NULL) {
107 dprintf("error allocating space for program headers\n");
108 err = ERR_NO_MEMORY;
109 goto error;
112 dprintf("reading in program headers at 0x%x, len 0x%x\n", eheader.e_phoff, eheader.e_phnum * eheader.e_phentsize);
113 len = sys_read(fd, pheaders, eheader.e_phoff, eheader.e_phnum * eheader.e_phentsize);
114 if(len < 0) {
115 err = len;
116 dprintf("error reading in program headers\n");
117 goto error;
119 if(len != eheader.e_phnum * eheader.e_phentsize) {
120 dprintf("short read while reading in program headers\n");
121 err = -1;
122 goto error;
125 for(i=0; i < eheader.e_phnum; i++) {
126 char region_name[64];
127 region_id id;
128 char *region_addr;
130 sprintf(region_name, "%s_seg%d", path, i);
132 region_addr = (char *)ROUNDOWN(pheaders[i].p_vaddr, PAGE_SIZE);
133 id = vm_create_anonymous_region(p->aspace_id, region_name, (void **)&region_addr, REGION_ADDR_EXACT_ADDRESS,
134 ROUNDUP(pheaders[i].p_memsz + (pheaders[i].p_vaddr % PAGE_SIZE), PAGE_SIZE), REGION_WIRING_LAZY, LOCK_RW);
135 if(id < 0) {
136 dprintf("error allocating region!\n");
137 err = ERR_INVALID_BINARY;
138 goto error;
141 len = sys_read(fd, region_addr + (pheaders[i].p_vaddr % PAGE_SIZE), pheaders[i].p_offset, pheaders[i].p_filesz);
142 if(len < 0) {
143 err = len;
144 dprintf("error reading in seg %d\n", i);
145 goto error;
148 if(i == 0)
149 *entry = pheaders[i].p_vaddr;
152 dprintf("elf_load: done!\n");
154 err = 0;
156 error:
157 sys_close(fd);
159 return err;
162 static int elf_parse_dynamic_section(struct elf_image_info *image)
164 struct Elf32_Dyn *d;
165 int i;
167 image->symhash = 0;
168 image->syms = 0;
169 image->strtab = 0;
171 d = (struct Elf32_Dyn *)image->dynamic_ptr;
172 if(!d)
173 return ERR_GENERAL;
175 for(i=0; d[i].d_tag != DT_NULL; i++) {
176 switch(d[i].d_tag) {
177 case DT_HASH:
178 image->symhash = (unsigned int *)d[i].d_un.d_ptr;
179 break;
180 case DT_STRTAB:
181 image->strtab = (char *)d[i].d_un.d_ptr;
182 break;
183 case DT_SYMTAB:
184 image->syms = (struct Elf32_Sym *)d[i].d_un.d_ptr;
185 break;
186 default:
187 continue;
191 // lets make sure we found all the required sections
192 if(!image->symhash || !image->syms || !image->strtab)
193 return ERR_GENERAL;
195 return NO_ERROR;
198 static struct Elf32_Sym *elf_find_symbol(struct elf_image_info *image, const char *name)
200 unsigned int hash;
201 unsigned int i;
203 hash = elf_hash(name) % HASHTABSIZE(image);
204 for(i = HASHBUCKETS(image)[hash]; i != STN_UNDEF; i = HASHCHAINS(image)[i]) {
205 if(!strcmp(SYMNAME(image, &image->syms[i]), name)) {
206 return &image->syms[i];
210 return NULL;
213 int elf_init(kernel_args *ka)
215 vm_region_info rinfo;
216 int err;
218 // build a image structure for the kernel, which has already been loaded
219 kernel_image = kmalloc(sizeof(struct elf_image_info));
220 memset(kernel_image, 0, sizeof(struct elf_image_info));
222 // text segment
223 kernel_image->regions[0].id = vm_find_region_by_name(vm_get_kernel_aspace_id(), "kernel_ro");
224 if(kernel_image->regions[0].id < 0)
225 panic("elf_init: could not look up kernel text segment region\n");
226 vm_get_region_info(kernel_image->regions[0].id, &rinfo);
227 kernel_image->regions[0].start = rinfo.base;
228 kernel_image->regions[0].size = rinfo.size;
230 // data segment
231 kernel_image->regions[1].id = vm_find_region_by_name(vm_get_kernel_aspace_id(), "kernel_rw");
232 if(kernel_image->regions[1].id < 0)
233 panic("elf_init: could not look up kernel data segment region\n");
234 vm_get_region_info(kernel_image->regions[1].id, &rinfo);
235 kernel_image->regions[1].start = rinfo.base;
236 kernel_image->regions[1].size = rinfo.size;
238 // we know where the dynamic section is
239 kernel_image->dynamic_ptr = (addr)ka->kernel_dynamic_section_addr.start;
241 // parse the dynamic section
242 if(elf_parse_dynamic_section(kernel_image) < 0)
243 panic("elf_init: elf_parse_dynamic_section doesn't like the kernel image\n");
245 // insert it first in the list of kernel images loaded
246 kernel_image->next = NULL;
247 kernel_images = kernel_image;
249 return 0;