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