xref: /haiku/src/system/boot/loader/elf.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
1 /*
2  * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "elf.h"
8 
9 #include <boot/arch.h>
10 #include <boot/platform.h>
11 #include <boot/stage2.h>
12 #include <elf32.h>
13 #include <kernel.h>
14 
15 #include <unistd.h>
16 #include <string.h>
17 #include <stdlib.h>
18 
19 //#define TRACE_ELF
20 #ifdef TRACE_ELF
21 #	define TRACE(x) dprintf x
22 #else
23 #	define TRACE(x) ;
24 #endif
25 
26 
27 static status_t
28 verify_elf_header(struct Elf32_Ehdr &header)
29 {
30 	if (memcmp(header.e_ident, ELF_MAGIC, 4) != 0
31 		|| header.e_ident[4] != ELFCLASS32
32 		|| header.e_phoff == 0
33 		|| !header.IsHostEndian()
34 		|| header.e_phentsize != sizeof(struct Elf32_Phdr))
35 		return B_BAD_TYPE;
36 
37 	return B_OK;
38 }
39 
40 
41 static status_t
42 elf_parse_dynamic_section(struct preloaded_image *image)
43 {
44 	image->syms = 0;
45 	image->rel = 0;
46 	image->rel_len = 0;
47 	image->rela = 0;
48 	image->rela_len = 0;
49 	image->pltrel = 0;
50 	image->pltrel_len = 0;
51 	image->pltrel_type = 0;
52 
53 	struct Elf32_Dyn *d = (struct Elf32_Dyn *)image->dynamic_section.start;
54 	if (!d)
55 		return B_ERROR;
56 
57 	for (int i = 0; d[i].d_tag != DT_NULL; i++) {
58 		switch (d[i].d_tag) {
59 			case DT_HASH:
60 			case DT_STRTAB:
61 				break;
62 			case DT_SYMTAB:
63 				image->syms = (struct Elf32_Sym *)(d[i].d_un.d_ptr
64 					+ image->text_region.delta);
65 				break;
66 			case DT_REL:
67 				image->rel = (struct Elf32_Rel *)(d[i].d_un.d_ptr
68 					+ image->text_region.delta);
69 				break;
70 			case DT_RELSZ:
71 				image->rel_len = d[i].d_un.d_val;
72 				break;
73 			case DT_RELA:
74 				image->rela = (struct Elf32_Rela *)(d[i].d_un.d_ptr
75 					+ image->text_region.delta);
76 				break;
77 			case DT_RELASZ:
78 				image->rela_len = d[i].d_un.d_val;
79 				break;
80 			case DT_JMPREL:
81 				image->pltrel = (struct Elf32_Rel *)(d[i].d_un.d_ptr
82 					+ image->text_region.delta);
83 				break;
84 			case DT_PLTRELSZ:
85 				image->pltrel_len = d[i].d_un.d_val;
86 				break;
87 			case DT_PLTREL:
88 				image->pltrel_type = d[i].d_un.d_val;
89 				break;
90 
91 			default:
92 				continue;
93 		}
94 	}
95 
96 	// lets make sure we found all the required sections
97 	if (image->syms == NULL)
98 		return B_ERROR;
99 
100 	return B_OK;
101 }
102 
103 
104 static status_t
105 load_elf_symbol_table(int fd, preloaded_image *image)
106 {
107 	struct Elf32_Ehdr &elfHeader = image->elf_header;
108 	Elf32_Sym *symbolTable = NULL;
109 	Elf32_Shdr *stringHeader = NULL;
110 	uint32 numSymbols = 0;
111 	char *stringTable;
112 	status_t status;
113 
114 	// get section headers
115 
116 	ssize_t size = elfHeader.e_shnum * elfHeader.e_shentsize;
117 	Elf32_Shdr *sectionHeaders = (struct Elf32_Shdr *)malloc(size);
118 	if (sectionHeaders == NULL) {
119 		dprintf("error allocating space for section headers\n");
120 		return B_NO_MEMORY;
121 	}
122 
123 	ssize_t length = read_pos(fd, elfHeader.e_shoff, sectionHeaders, size);
124 	if (length < size) {
125 		TRACE(("error reading in program headers\n"));
126 		status = B_ERROR;
127 		goto error1;
128 	}
129 
130 	// find symbol table in section headers
131 
132 	for (int32 i = 0; i < elfHeader.e_shnum; i++) {
133 		if (sectionHeaders[i].sh_type == SHT_SYMTAB) {
134 			stringHeader = &sectionHeaders[sectionHeaders[i].sh_link];
135 
136 			if (stringHeader->sh_type != SHT_STRTAB) {
137 				TRACE(("doesn't link to string table\n"));
138 				status = B_BAD_DATA;
139 				goto error1;
140 			}
141 
142 			// read in symbol table
143 			symbolTable = (Elf32_Sym *)kernel_args_malloc(
144 				size = sectionHeaders[i].sh_size);
145 			if (symbolTable == NULL) {
146 				status = B_NO_MEMORY;
147 				goto error1;
148 			}
149 
150 			length = read_pos(fd, sectionHeaders[i].sh_offset, symbolTable,
151 				size);
152 			if (length < size) {
153 				TRACE(("error reading in symbol table\n"));
154 				status = B_ERROR;
155 				goto error1;
156 			}
157 
158 			numSymbols = size / sizeof(Elf32_Sym);
159 			break;
160 		}
161 	}
162 
163 	if (symbolTable == NULL) {
164 		TRACE(("no symbol table\n"));
165 		status = B_BAD_VALUE;
166 		goto error1;
167 	}
168 
169 	// read in string table
170 
171 	stringTable = (char *)kernel_args_malloc(size = stringHeader->sh_size);
172 	if (stringTable == NULL) {
173 		status = B_NO_MEMORY;
174 		goto error2;
175 	}
176 
177 	length = read_pos(fd, stringHeader->sh_offset, stringTable, size);
178 	if (length < size) {
179 		TRACE(("error reading in string table\n"));
180 		status = B_ERROR;
181 		goto error3;
182 	}
183 
184 	TRACE(("loaded %ld debug symbols\n", numSymbols));
185 
186 	// insert tables into image
187 	image->debug_symbols = symbolTable;
188 	image->num_debug_symbols = numSymbols;
189 	image->debug_string_table = stringTable;
190 	image->debug_string_table_size = size;
191 
192 	free(sectionHeaders);
193 	return B_OK;
194 
195 error3:
196 	kernel_args_free(stringTable);
197 error2:
198 	kernel_args_free(symbolTable);
199 error1:
200 	free(sectionHeaders);
201 
202 	return status;
203 }
204 
205 
206 status_t
207 elf_load_image(int fd, preloaded_image *image)
208 {
209 	size_t totalSize;
210 	status_t status;
211 
212 	TRACE(("elf_load_image(fd = %d, image = %p)\n", fd, image));
213 
214 	struct Elf32_Ehdr &elfHeader = image->elf_header;
215 
216 	ssize_t length = read_pos(fd, 0, &elfHeader, sizeof(Elf32_Ehdr));
217 	if (length < (ssize_t)sizeof(Elf32_Ehdr))
218 		return B_BAD_TYPE;
219 
220 	status = verify_elf_header(elfHeader);
221 	if (status < B_OK)
222 		return status;
223 
224 	ssize_t size = elfHeader.e_phnum * elfHeader.e_phentsize;
225 	Elf32_Phdr *programHeaders = (struct Elf32_Phdr *)malloc(size);
226 	if (programHeaders == NULL) {
227 		dprintf("error allocating space for program headers\n");
228 		return B_NO_MEMORY;
229 	}
230 
231 	length = read_pos(fd, elfHeader.e_phoff, programHeaders, size);
232 	if (length < size) {
233 		TRACE(("error reading in program headers\n"));
234 		status = B_ERROR;
235 		goto error1;
236 	}
237 
238 	// create an area large enough to hold the image
239 
240 	image->data_region.size = 0;
241 	image->text_region.size = 0;
242 
243 	for (int32 i = 0; i < elfHeader.e_phnum; i++) {
244 		Elf32_Phdr &header = programHeaders[i];
245 
246 		switch (header.p_type) {
247 			case PT_LOAD:
248 				break;
249 			case PT_DYNAMIC:
250 				image->dynamic_section.start = header.p_vaddr;
251 				image->dynamic_section.size = header.p_memsz;
252 				continue;
253 			case PT_INTERP:
254 			case PT_PHDR:
255 				// known but unused type
256 				continue;
257 			default:
258 				dprintf("unhandled pheader type 0x%lx\n", header.p_type);
259 				continue;
260 		}
261 
262 		elf_region *region;
263 		if (header.IsReadWrite()) {
264 			if (image->data_region.size != 0) {
265 				dprintf("elf: rw already handled!\n");
266 				continue;
267 			}
268 			region = &image->data_region;
269 		} else if (header.IsExecutable()) {
270 			if (image->text_region.size != 0) {
271 				dprintf("elf: ro already handled!\n");
272 				continue;
273 			}
274 			region = &image->text_region;
275 		} else
276 			continue;
277 
278 		region->start = ROUNDDOWN(header.p_vaddr, B_PAGE_SIZE);
279 		region->size = ROUNDUP(header.p_memsz + (header.p_vaddr % B_PAGE_SIZE),
280 			B_PAGE_SIZE);
281 		region->delta = -region->start;
282 
283 		TRACE(("segment %d: start = %p, size = %lu, delta = %lx\n", i,
284 			region->start, region->size, region->delta));
285 	}
286 
287 	// found both, text and data?
288 	if (image->data_region.size == 0 || image->text_region.size == 0) {
289 		dprintf("Couldn't find both text and data segment!\n");
290 		status = B_BAD_DATA;
291 		goto error1;
292 	}
293 
294 	// get the segment order
295 	elf_region *firstRegion;
296 	elf_region *secondRegion;
297 	if (image->text_region.start < image->data_region.start) {
298 		firstRegion = &image->text_region;
299 		secondRegion = &image->data_region;
300 	} else {
301 		firstRegion = &image->data_region;
302 		secondRegion = &image->text_region;
303 	}
304 
305 	// Check whether the segments have an unreasonable amount of unused space
306 	// inbetween.
307 	totalSize = secondRegion->start + secondRegion->size - firstRegion->start;
308 	if (totalSize > image->text_region.size + image->data_region.size
309 		+ 8 * 1024) {
310 		status = B_BAD_DATA;
311 		goto error1;
312 	}
313 
314 	// The kernel and the modules are relocatable, thus
315 	// platform_allocate_region() can automatically allocate an address,
316 	// but shall prefer the specified base address.
317 	if (platform_allocate_region((void **)&firstRegion->start, totalSize,
318 			B_READ_AREA | B_WRITE_AREA, false) < B_OK) {
319 		status = B_NO_MEMORY;
320 		goto error1;
321 	}
322 
323 	// initialize the region pointers to the allocated region
324 	secondRegion->start += firstRegion->start + firstRegion->delta;
325 
326 	image->data_region.delta += image->data_region.start;
327 	image->text_region.delta += image->text_region.start;
328 
329 	// load program data
330 
331 	for (int i = 0; i < elfHeader.e_phnum; i++) {
332 		Elf32_Phdr &header = programHeaders[i];
333 
334 		if (header.p_type != PT_LOAD)
335 			continue;
336 
337 		elf_region *region;
338 		if (header.IsReadWrite())
339 			region = &image->data_region;
340 		else if (header.IsExecutable())
341 			region = &image->text_region;
342 		else
343 			continue;
344 
345 		TRACE(("load segment %d (%ld bytes)...\n", i, header.p_filesz));
346 
347 		length = read_pos(fd, header.p_offset,
348 			(void *)(region->start + (header.p_vaddr % B_PAGE_SIZE)),
349 			header.p_filesz);
350 		if (length < (ssize_t)header.p_filesz) {
351 			status = B_BAD_DATA;
352 			dprintf("error reading in seg %d\n", i);
353 			goto error2;
354 		}
355 
356 		// Clear anything above the file size (that may also contain the BSS
357 		// area)
358 
359 		uint32 offset = (header.p_vaddr % B_PAGE_SIZE) + header.p_filesz;
360 		if (offset < region->size)
361 			memset((void *)(region->start + offset), 0, region->size - offset);
362 	}
363 
364 	// offset dynamic section, and program entry addresses by the delta of the
365 	// regions
366 	image->dynamic_section.start += image->text_region.delta;
367 	image->elf_header.e_entry += image->text_region.delta;
368 
369 	image->num_debug_symbols = 0;
370 	image->debug_symbols = NULL;
371 	image->debug_string_table = NULL;
372 
373 	// ToDo: this should be enabled by kernel settings!
374 	if (1)
375 		load_elf_symbol_table(fd, image);
376 
377 	free(programHeaders);
378 
379 	return B_OK;
380 
381 error2:
382 	if (image->text_region.start != 0)
383 		platform_free_region((void *)image->text_region.start, totalSize);
384 error1:
385 	free(programHeaders);
386 
387 	return status;
388 }
389 
390 
391 status_t
392 elf_load_image(Directory *directory, const char *path)
393 {
394 	preloaded_image *image;
395 
396 	TRACE(("elf_load_image(directory = %p, \"%s\")\n", directory, path));
397 
398 	int fd = open_from(directory, path, O_RDONLY);
399 	if (fd < 0)
400 		return fd;
401 
402 	// check if this file has already been loaded
403 
404 	struct stat stat;
405 	fstat(fd, &stat);
406 
407 	image = gKernelArgs.preloaded_images;
408 	for (; image != NULL; image = image->next) {
409 		if (image->inode == stat.st_ino) {
410 			// file has already been loaded, no need to load it twice!
411 			close(fd);
412 			return B_OK;
413 		}
414 	}
415 
416 	// we still need to load it, so do it
417 
418 	image = (preloaded_image *)kernel_args_malloc(sizeof(preloaded_image));
419 	if (image == NULL) {
420 		close(fd);
421 		return B_NO_MEMORY;
422 	}
423 
424 	status_t status = elf_load_image(fd, image);
425 	if (status == B_OK) {
426 		image->name = kernel_args_strdup(path);
427 		image->inode = stat.st_ino;
428 
429 		// insert to kernel args
430 		image->next = gKernelArgs.preloaded_images;
431 		gKernelArgs.preloaded_images = image;
432 	} else
433 		kernel_args_free(image);
434 
435 	close(fd);
436 	return status;
437 }
438 
439 
440 status_t
441 elf_relocate_image(struct preloaded_image *image)
442 {
443 	status_t status = elf_parse_dynamic_section(image);
444 	if (status != B_OK)
445 		return status;
446 
447 	// deal with the rels first
448 	if (image->rel) {
449 		TRACE(("total %i relocs\n",
450 			image->rel_len / (int)sizeof(struct Elf32_Rel)));
451 
452 		status = boot_arch_elf_relocate_rel(image, image->rel, image->rel_len);
453 		if (status < B_OK)
454 			return status;
455 	}
456 
457 	if (image->pltrel) {
458 		TRACE(("total %i plt-relocs\n",
459 			image->pltrel_len / (int)sizeof(struct Elf32_Rel)));
460 
461 		if (image->pltrel_type == DT_REL) {
462 			status = boot_arch_elf_relocate_rel(image, image->pltrel,
463 				image->pltrel_len);
464 		} else {
465 			status = boot_arch_elf_relocate_rela(image,
466 				(struct Elf32_Rela *)image->pltrel, image->pltrel_len);
467 		}
468 		if (status < B_OK)
469 			return status;
470 	}
471 
472 	if (image->rela) {
473 		TRACE(("total %i rela relocs\n",
474 			image->rela_len / (int)sizeof(struct Elf32_Rela)));
475 		status = boot_arch_elf_relocate_rela(image, image->rela,
476 			image->rela_len);
477 		if (status < B_OK)
478 			return status;
479 	}
480 
481 	return B_OK;
482 }
483 
484 
485 status_t
486 boot_elf_resolve_symbol(struct preloaded_image *image,
487 	struct Elf32_Sym *symbol, addr_t *symbolAddress)
488 {
489 	switch (symbol->st_shndx) {
490 		case SHN_UNDEF:
491 			// Since we do that only for the kernel, there shouldn't be
492 			// undefined symbols.
493 			return B_MISSING_SYMBOL;
494 		case SHN_ABS:
495 			*symbolAddress = symbol->st_value;
496 			return B_NO_ERROR;
497 		case SHN_COMMON:
498 			// ToDo: finish this
499 			TRACE(("elf_resolve_symbol: COMMON symbol, finish me!\n"));
500 			return B_ERROR;
501 		default:
502 			// standard symbol
503 			*symbolAddress = symbol->st_value + image->text_region.delta;
504 			return B_NO_ERROR;
505 	}
506 }
507