xref: /haiku/src/system/boot/loader/elf.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
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/platform.h>
10 #include <boot/stage2.h>
11 #include <elf32.h>
12 #include <kernel.h>
13 
14 #include <unistd.h>
15 #include <string.h>
16 #include <stdlib.h>
17 
18 //#define TRACE_ELF
19 #ifdef TRACE_ELF
20 #	define TRACE(x) dprintf x
21 #else
22 #	define TRACE(x) ;
23 #endif
24 
25 
26 static status_t
27 verify_elf_header(struct Elf32_Ehdr &header)
28 {
29 	if (memcmp(header.e_ident, ELF_MAGIC, 4) != 0
30 		|| header.e_ident[4] != ELFCLASS32
31 		|| header.e_phoff == 0
32 		|| !header.IsHostEndian()
33 		|| header.e_phentsize != sizeof(struct Elf32_Phdr))
34 		return B_BAD_TYPE;
35 
36 	return B_OK;
37 }
38 
39 
40 static status_t
41 load_elf_symbol_table(int fd, preloaded_image *image)
42 {
43 	struct Elf32_Ehdr &elfHeader = image->elf_header;
44 	Elf32_Sym *symbolTable = NULL;
45 	Elf32_Shdr *stringHeader = NULL;
46 	uint32 numSymbols = 0;
47 	char *stringTable;
48 	status_t status;
49 
50 	// get section headers
51 
52 	ssize_t size = elfHeader.e_shnum * elfHeader.e_shentsize;
53 	Elf32_Shdr *sectionHeaders = (struct Elf32_Shdr *)malloc(size);
54 	if (sectionHeaders == NULL) {
55 		dprintf("error allocating space for section headers\n");
56 		return B_NO_MEMORY;
57 	}
58 
59 	ssize_t length = read_pos(fd, elfHeader.e_shoff, sectionHeaders, size);
60 	if (length < size) {
61 		TRACE(("error reading in program headers\n"));
62 		status = B_ERROR;
63 		goto error1;
64 	}
65 
66 	// find symbol table in section headers
67 
68 	for (int32 i = 0; i < elfHeader.e_shnum; i++) {
69 		if (sectionHeaders[i].sh_type == SHT_SYMTAB) {
70 			stringHeader = &sectionHeaders[sectionHeaders[i].sh_link];
71 
72 			if (stringHeader->sh_type != SHT_STRTAB) {
73 				TRACE(("doesn't link to string table\n"));
74 				status = B_BAD_DATA;
75 				goto error1;
76 			}
77 
78 			// read in symbol table
79 			symbolTable = (Elf32_Sym *)kernel_args_malloc(size = sectionHeaders[i].sh_size);
80 			if (symbolTable == NULL) {
81 				status = B_NO_MEMORY;
82 				goto error1;
83 			}
84 
85 			length = read_pos(fd, sectionHeaders[i].sh_offset, symbolTable, size);
86 			if (length < size) {
87 				TRACE(("error reading in symbol table\n"));
88 				status = B_ERROR;
89 				goto error1;
90 			}
91 
92 			numSymbols = size / sizeof(Elf32_Sym);
93 			break;
94 		}
95 	}
96 
97 	if (symbolTable == NULL) {
98 		TRACE(("no symbol table\n"));
99 		status = B_BAD_VALUE;
100 		goto error1;
101 	}
102 
103 	// read in string table
104 
105 	stringTable = (char *)kernel_args_malloc(size = stringHeader->sh_size);
106 	if (stringTable == NULL) {
107 		status = B_NO_MEMORY;
108 		goto error2;
109 	}
110 
111 	length = read_pos(fd, stringHeader->sh_offset, stringTable, size);
112 	if (length < size) {
113 		TRACE(("error reading in string table\n"));
114 		status = B_ERROR;
115 		goto error3;
116 	}
117 
118 	TRACE(("loaded debug %ld symbols\n", numSymbols));
119 
120 	// insert tables into image
121 	image->debug_symbols = symbolTable;
122 	image->num_debug_symbols = numSymbols;
123 	image->debug_string_table = stringTable;
124 	image->debug_string_table_size = size;
125 
126 	free(sectionHeaders);
127 	return B_OK;
128 
129 error3:
130 	kernel_args_free(stringTable);
131 error2:
132 	kernel_args_free(symbolTable);
133 error1:
134 	free(sectionHeaders);
135 
136 	return status;
137 }
138 
139 
140 status_t
141 elf_load_image(int fd, preloaded_image *image)
142 {
143 	size_t totalSize;
144 	status_t status;
145 
146 	TRACE(("elf_load_image(fd = %d, image = %p)\n", fd, image));
147 
148 	struct Elf32_Ehdr &elfHeader = image->elf_header;
149 
150 	ssize_t length = read_pos(fd, 0, &elfHeader, sizeof(Elf32_Ehdr));
151 	if (length < (ssize_t)sizeof(Elf32_Ehdr))
152 		return B_BAD_TYPE;
153 
154 	status = verify_elf_header(elfHeader);
155 	if (status < B_OK)
156 		return status;
157 
158 	ssize_t size = elfHeader.e_phnum * elfHeader.e_phentsize;
159 	Elf32_Phdr *programHeaders = (struct Elf32_Phdr *)malloc(size);
160 	if (programHeaders == NULL) {
161 		dprintf("error allocating space for program headers\n");
162 		return B_NO_MEMORY;
163 	}
164 
165 	length = read_pos(fd, elfHeader.e_phoff, programHeaders, size);
166 	if (length < size) {
167 		TRACE(("error reading in program headers\n"));
168 		status = B_ERROR;
169 		goto error1;
170 	}
171 
172 	// create an area large enough to hold the image
173 
174 	image->data_region.size = 0;
175 	image->text_region.size = 0;
176 
177 	for (int32 i = 0; i < elfHeader.e_phnum; i++) {
178 		Elf32_Phdr &header = programHeaders[i];
179 
180 		switch (header.p_type) {
181 			case PT_LOAD:
182 				break;
183 			case PT_DYNAMIC:
184 				image->dynamic_section.start = header.p_vaddr;
185 				image->dynamic_section.size = header.p_memsz;
186 				continue;
187 			default:
188 				dprintf("unhandled pheader type 0x%lx\n", header.p_type);
189 				continue;
190 		}
191 
192 		elf_region *region;
193 		if (header.IsReadWrite()) {
194 			if (image->data_region.size != 0) {
195 				dprintf("elf: rw already handled!\n");
196 				continue;
197 			}
198 			region = &image->data_region;
199 		} else if (header.IsExecutable()) {
200 			if (image->text_region.size != 0) {
201 				dprintf("elf: ro already handled!\n");
202 				continue;
203 			}
204 			region = &image->text_region;
205 		} else
206 			continue;
207 
208 		region->start = ROUNDOWN(header.p_vaddr, B_PAGE_SIZE);
209 		region->size = ROUNDUP(header.p_memsz + (header.p_vaddr % B_PAGE_SIZE), B_PAGE_SIZE);
210 		region->delta = -region->start;
211 
212 		TRACE(("segment %d: start = %p, size = %lu, delta = %lx\n", i,
213 			region->start, region->size, region->delta));
214 	}
215 
216 	// get the segment order
217 	elf_region *firstRegion;
218 	elf_region *secondRegion;
219 	if (image->text_region.start < image->data_region.start) {
220 		firstRegion = &image->text_region;
221 		secondRegion = &image->data_region;
222 	} else {
223 		firstRegion = &image->data_region;
224 		secondRegion = &image->text_region;
225 	}
226 
227 	// Check whether the segments have an unreasonable amount of unused space
228 	// inbetween.
229 	totalSize = secondRegion->start + secondRegion->size - firstRegion->start;
230 	if (totalSize > image->text_region.size + image->data_region.size
231 		+ 8 * 1024) {
232 		status = B_BAD_DATA;
233 		goto error1;
234 	}
235 
236 	// if image->text_region.start == NULL (image is relocatable),
237 	// platform_allocate_region() automatically allocates an address
238 	if (platform_allocate_region((void **)&firstRegion->start, totalSize,
239 			B_READ_AREA | B_WRITE_AREA) < B_OK) {
240 		status = B_NO_MEMORY;
241 		goto error1;
242 	}
243 
244 	// initialize the region pointers to the allocated region
245 	secondRegion->start += firstRegion->start + firstRegion->delta;
246 
247 	image->data_region.delta += image->data_region.start;
248 	image->text_region.delta += image->text_region.start;
249 
250 	// load program data
251 
252 	for (int i = 0; i < elfHeader.e_phnum; i++) {
253 		Elf32_Phdr &header = programHeaders[i];
254 
255 		if (header.p_type != PT_LOAD)
256 			continue;
257 
258 		elf_region *region;
259 		if (header.IsReadWrite())
260 			region = &image->data_region;
261 		else if (header.IsExecutable())
262 			region = &image->text_region;
263 		else
264 			continue;
265 
266 		TRACE(("load segment %d (%ld bytes)...\n", i, header.p_filesz));
267 
268 		length = read_pos(fd, header.p_offset,
269 			(void *)(region->start + (header.p_vaddr % B_PAGE_SIZE)), header.p_filesz);
270 		if (length < (ssize_t)header.p_filesz) {
271 			status = B_BAD_DATA;
272 			dprintf("error reading in seg %d\n", i);
273 			goto error2;
274 		}
275 
276 		// clear anything above the file size (that may also contain the BSS area)
277 
278 		uint32 offset = (header.p_vaddr % B_PAGE_SIZE) + header.p_filesz;
279 		if (offset < region->size)
280 			memset((void *)(region->start + offset), 0, region->size - offset);
281 	}
282 
283 	// modify the dynamic section by the delta of the regions
284 	image->dynamic_section.start += image->text_region.delta;
285 
286 	image->num_debug_symbols = 0;
287 	image->debug_symbols = NULL;
288 	image->debug_string_table = NULL;
289 
290 	// ToDo: this should be enabled by kernel settings!
291 	if (1)
292 		load_elf_symbol_table(fd, image);
293 
294 	free(programHeaders);
295 
296 	return B_OK;
297 
298 error2:
299 	if (image->text_region.start != NULL)
300 		platform_free_region((void *)image->text_region.start, totalSize);
301 error1:
302 	free(programHeaders);
303 
304 	return status;
305 }
306 
307 
308 status_t
309 elf_load_image(Directory *directory, const char *path)
310 {
311 	preloaded_image *image;
312 
313 	TRACE(("elf_load_image(directory = %p, \"%s\")\n", directory, path));
314 
315 	int fd = open_from(directory, path, O_RDONLY);
316 	if (fd < 0)
317 		return fd;
318 
319 	// check if this file has already been loaded
320 
321 	struct stat stat;
322 	fstat(fd, &stat);
323 
324 	image = gKernelArgs.preloaded_images;
325 	for (; image != NULL; image = image->next) {
326 		if (image->inode == stat.st_ino) {
327 			// file has already been loaded, no need to load it twice!
328 			close(fd);
329 			return B_OK;
330 		}
331 	}
332 
333 	// we still need to load it, so do it
334 
335 	image = (preloaded_image *)kernel_args_malloc(sizeof(preloaded_image));
336 	if (image == NULL) {
337 		close(fd);
338 		return B_NO_MEMORY;
339 	}
340 
341 	status_t status = elf_load_image(fd, image);
342 	if (status == B_OK) {
343 		image->name = kernel_args_strdup(path);
344 		image->inode = stat.st_ino;
345 
346 		// insert to kernel args
347 		image->next = gKernelArgs.preloaded_images;
348 		gKernelArgs.preloaded_images = image;
349 	} else
350 		kernel_args_free(image);
351 
352 	close(fd);
353 	return status;
354 }
355 
356