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