1 /*	$OpenBSD: elf.c,v 1.7 2004/12/28 09:05:18 deraadt Exp $	*/
2 /*	$NetBSD: elf.c,v 1.8 2002/01/03 21:45:58 jdolecek Exp $	*/
3 
4 /*
5  * Copyright © 2013
6  *	Thorsten “mirabilos” Glaser <tg@mirbsd.org>
7  * Copyright (c) 1998 Johan Danielsson <joda@pdc.kth.se>
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include <sys/param.h>
35 
36 #if defined(__LP64__)
37 #define ELFSIZE 64
38 #else
39 #define ELFSIZE 32
40 #endif
41 #include <sys/exec_elf.h>
42 #ifndef ELF_HDR_SIZE
43 #define ELF_HDR_SIZE sizeof(Elf_Ehdr)
44 #endif
45 
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <sys/lkm.h>
49 
50 #include <err.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55 #include <fcntl.h>
56 
57 #include "modload.h"
58 
59 __RCSID("$MirOS: src/sbin/modload/elf.c,v 1.6 2013/10/31 20:06:43 tg Exp $");
60 
61 char *strtab;
62 
63 static void
read_section_header(int fd,Elf_Ehdr * ehdr,int num,Elf_Shdr * shdr)64 read_section_header(int fd, Elf_Ehdr *ehdr, int num, Elf_Shdr *shdr)
65 {
66 
67 	if (lseek(fd, ehdr->e_shoff + num * ehdr->e_shentsize, SEEK_SET) < 0)
68 		err(1, "lseek");
69 	if (read(fd, shdr, sizeof(*shdr)) != sizeof(*shdr))
70 		err(1, "read");
71 }
72 
73 struct elf_section {
74 	char *name;		/* name of section; points into string table */
75 	unsigned long type;	/* type of section */
76 	void *addr;		/* load address of section */
77 	off_t offset;		/* offset in file */
78 	size_t size;		/* size of section */
79 	size_t align;
80 	struct elf_section *next;
81 };
82 
83 /* adds the section 's' at the correct (sorted by address) place in
84    the list ponted to by head; *head may be NULL */
85 static void
add_section(struct elf_section ** head,struct elf_section * s)86 add_section(struct elf_section **head, struct elf_section *s)
87 {
88 	struct elf_section *p, **q;
89 	q = head;
90 	p = *head;
91 
92 	while (1) {
93 		if (p == NULL || p->addr > s->addr) {
94 			s->next = p;
95 			*q = s;
96 			return;
97 		}
98 		q = &p->next;
99 		p = p->next;
100 	}
101 }
102 
103 /* make a linked list of all sections containing ALLOCatable data */
104 static void
read_sections(int fd,Elf_Ehdr * ehdr,char * shstrtab,struct elf_section ** head)105 read_sections(int fd, Elf_Ehdr *ehdr, char *shstrtab, struct elf_section **head)
106 {
107 	int i;
108 	Elf_Shdr shdr;
109 
110 	*head = NULL;
111 	/* scan through section headers */
112 	for (i = 0; i < ehdr->e_shnum; i++) {
113 		struct elf_section *s;
114 		read_section_header(fd, ehdr, i, &shdr);
115 		if ((shdr.sh_flags & SHF_ALLOC) == 0 &&
116 		    shdr.sh_type != SHT_STRTAB &&
117 		    shdr.sh_type != SHT_SYMTAB &&
118 		    shdr.sh_type != SHT_DYNSYM) {
119 			/* skip non-ALLOC sections */
120 			continue;
121 		}
122 		s = malloc(sizeof(*s));
123 		if (s == NULL)
124 			errx(1, "failed to allocate %lu bytes",
125 			    (u_long)sizeof(*s));
126 		s->name = shstrtab + shdr.sh_name;
127 		s->type = shdr.sh_type;
128 		s->addr = (void *)shdr.sh_addr;
129 		s->offset = shdr.sh_offset;
130 		s->size = shdr.sh_size;
131 		s->align = shdr.sh_addralign;
132 		add_section(head, s);
133 	}
134 }
135 
136 /* get the symbol table sections and free the rest of them */
137 static void
get_symtab(struct elf_section ** stab)138 get_symtab(struct elf_section **stab)
139 {
140 	struct elf_section *head, *cur, *prev;
141 
142 	head = NULL;
143 	prev = NULL;
144 	cur = *stab;
145 	while (cur) {
146 		if ((cur->type == SHT_SYMTAB) || (cur->type == SHT_DYNSYM)) {
147 			if (head == NULL)
148 				head = cur;
149 			if (prev != NULL)
150 				prev->next = cur;
151 			prev = cur;
152 			cur = cur->next;
153 		} else {
154 			struct elf_section *p = cur;
155 			cur = cur->next;
156 			p->next = NULL;
157 			free(p);
158 		}
159 	}
160 
161 	if (prev)
162 		prev->next = NULL;
163 	*stab = head;
164 }
165 
166 /* free a list of section headers */
167 static void
free_sections(struct elf_section * head)168 free_sections(struct elf_section *head)
169 {
170 
171 	while (head) {
172 		struct elf_section *p = head;
173 		head = head->next;
174 		free(p);
175 	}
176 }
177 
178 /* read section header's string table */
179 static char *
read_shstring_table(int fd,Elf_Ehdr * ehdr)180 read_shstring_table(int fd, Elf_Ehdr *ehdr)
181 {
182 	Elf_Shdr shdr;
183 	char *shstrtab;
184 
185 	read_section_header(fd, ehdr, ehdr->e_shstrndx, &shdr);
186 
187 	shstrtab = malloc(shdr.sh_size);
188 	if (shstrtab == NULL)
189 		errx(1, "failed to allocate %lu bytes", (u_long)shdr.sh_size);
190 	if (lseek(fd, shdr.sh_offset, SEEK_SET) < 0)
191 		err(1, "lseek");
192 	if ((size_t)read(fd, shstrtab, shdr.sh_size) != shdr.sh_size)
193 		err(1, "read");
194 	return shstrtab;
195 }
196 
197 /* read string table */
198 static char *
read_string_table(int fd,struct elf_section * head,int * strtablen)199 read_string_table(int fd, struct elf_section *head, int *strtablen)
200 {
201 	char *string_table=NULL;
202 
203 	while (head) {
204 		if (strcmp(head->name, ".strtab") == 0 &&
205 		    head->type == SHT_STRTAB) {
206 			string_table = malloc(head->size);
207 			if (string_table == NULL)
208 				errx(1, "failed to allocate %lu bytes",
209 				    (u_long)head->size);
210 			if (lseek(fd, head->offset, SEEK_SET) < 0)
211 				err(1, "lseek");
212 			if ((size_t)read(fd, string_table, head->size)
213 			    != head->size)
214 				err(1, "read");
215 			*strtablen = head->size;
216 			break;
217 		} else
218 			head = head->next;
219 	}
220 	return string_table;
221 }
222 
223 static int
read_elf_header(int fd,Elf_Ehdr * ehdr)224 read_elf_header(int fd, Elf_Ehdr *ehdr)
225 {
226 	ssize_t n;
227 
228 	n = read(fd, ehdr, sizeof(*ehdr));
229 	if (n < 0)
230 		err(1, "failed reading %lu bytes", (u_long)sizeof(*ehdr));
231 	if (n != sizeof(*ehdr)) {
232 		if (debug)
233 			warnx("failed to read %lu bytes", (u_long)sizeof(*ehdr));
234 		return -1;
235 	}
236 	if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0 ||
237 	    ehdr->e_ident[EI_CLASS] != ELFCLASS)
238 		errx(4, "not in ELF%u format", ELFSIZE);
239 	if (ehdr->e_ehsize != ELF_HDR_SIZE)
240 		errx(4, "file has ELF%u identity, but wrong header size",
241 		    ELFSIZE);
242 	return 0;
243 }
244 
245 /* offset of data segment; this is horrible, but keeps the size of the
246    module to a minimum */
247 static ssize_t data_offset;
248 
249 /* return size needed by the module */
250 int
elf_mod_sizes(int fd,size_t * modsize,int * strtablen,struct lmc_resrv * resrvp,struct stat * sp)251 elf_mod_sizes(int fd, size_t *modsize, int *strtablen,
252     struct lmc_resrv *resrvp, struct stat *sp __attribute__((__unused__)))
253 {
254 	Elf_Ehdr ehdr;
255 	ssize_t off = 0;
256 	size_t data_hole = 0;
257 	char *shstrtab, *strtabl;
258 	struct elf_section *head, *s, *stab;
259 
260 	if (read_elf_header(fd, &ehdr) < 0)
261 		return -1;
262 	shstrtab = read_shstring_table(fd, &ehdr);
263 	read_sections(fd, &ehdr, shstrtab, &head);
264 
265 	for (s = head; s; s = s->next) {
266 		/* XXX impossible! */
267 		if (s->type == SHT_STRTAB && s->type == SHT_SYMTAB &&
268 		    s->type == SHT_DYNSYM)
269 			continue;
270 		if (debug)
271 			fprintf(stderr,
272 			    "%s: addr = %p size = %#lx align = %#lx\n",
273 			    s->name, s->addr, (u_long)s->size, (u_long)s->align);
274 		/*
275 		 * XXX try to get rid of the hole before the data
276 		 * section that GNU-ld likes to put there
277 		 */
278 		if (strcmp(s->name, ".rodata") == 0 && s->addr > (void *)off) {
279 			data_offset = roundup(off, s->align);
280 			if (debug)
281 				fprintf(stderr, ".rodata section forced to "
282 				    "offset %p (was %p)\n",
283 				    (void *)data_offset, s->addr);
284 			/* later remove size of compressed hole from off */
285 			data_hole = (ssize_t)s->addr - data_offset;
286 		}
287 		off = (ssize_t)s->addr + s->size;
288 	}
289 	off -= data_hole;
290 
291 	/* XXX round to pagesize? */
292 	*modsize = roundup(off, sysconf(_SC_PAGESIZE));
293 
294 	/* get string table length */
295 	strtabl = read_string_table(fd, head, strtablen);
296 	free(shstrtab);
297 	free(strtabl);
298 
299 	/* get symbol table sections */
300 	get_symtab(&head);
301 	stab = head;
302 	resrvp->sym_symsize = 0;
303 	while (stab) {
304 		resrvp->sym_symsize += stab->size;
305 		stab = stab->next;
306 	}
307 	resrvp->sym_size = resrvp->sym_symsize + *strtablen;
308 	free_sections(head);
309 
310 	return (0);
311 }
312 
313 /*
314  * Expected linker options:
315  *
316  * -R		executable to link against
317  * -e		entry point
318  * -o		output file
319  * -Ttext	address to link text segment to in hex (assumes it's
320  *		a page boundry)
321  * -Tdata	address to link data segment to in hex
322  * <target>	object file */
323 
324 #define	LINKCMD		"ld -Z -R %s -e %s -o %s -Ttext %p %s"
325 #define	LINKCMD2	"ld -Z -R %s -e %s -o %s -Ttext %p --section-start .rodata=%p %s"
326 
327 /* make a link command; XXX if data_offset above is non-zero, force
328    data address to be at start of text + offset */
329 void
elf_linkcmd(char * buf,size_t len,const char * kernel,const char * entry,const char * outfile,const void * address,const char * object)330 elf_linkcmd(char *buf, size_t len, const char *kernel,
331     const char *entry, const char *outfile, const void *address,
332     const char *object)
333 {
334 	ssize_t n;
335 
336 	if (!data_offset)
337 		n = snprintf(buf, len, LINKCMD, kernel, entry,
338 		    outfile, address, object);
339 	else
340 		n = snprintf(buf, len, LINKCMD2, kernel, entry,
341 		    outfile, address,
342 		    (const char*)address + data_offset, object);
343 	if (n < 0 || n >= (ssize_t)len)
344 		errx(1, "link command longer than %lu bytes", (u_long)len);
345 }
346 
347 /* load a prelinked module; returns entry point */
348 void *
elf_mod_load(int fd)349 elf_mod_load(int fd)
350 {
351 	Elf_Ehdr ehdr;
352 	size_t zero_size = 0;
353 	size_t b;
354 	ssize_t n;
355 	char *shstrtab;
356 	struct elf_section *head, *s;
357 	char buf[10 * BUFSIZ];
358 	void *addr = NULL;
359 
360 	if (read_elf_header(fd, &ehdr) < 0)
361 		return NULL;
362 
363 	shstrtab = read_shstring_table(fd, &ehdr);
364 	read_sections(fd, &ehdr, shstrtab, &head);
365 
366 	for (s = head; s; s = s->next) {
367 		if (s->type != SHT_STRTAB && s->type != SHT_SYMTAB &&
368 		    s->type != SHT_DYNSYM) {
369 			if (debug)
370 				fprintf(stderr, "loading '%s': addr = %p, "
371 				    "size = %#lx\n",
372 				    s->name, s->addr, (u_long)s->size);
373 			if (s->type == SHT_NOBITS) {
374 				/* skip some space */
375 				zero_size += s->size;
376 			} else {
377 				if (addr != NULL) {
378 					/*
379 					 * if there is a gap in the prelinked
380 					 * module, transfer some empty space.
381 					 */
382 					zero_size += (char*)s->addr -
383 					    (char*)addr;
384 				}
385 				if (zero_size) {
386 					loadspace(zero_size);
387 					zero_size = 0;
388 				}
389 				b = s->size;
390 				if (lseek(fd, s->offset, SEEK_SET) == -1)
391 					err(1, "lseek");
392 				while (b) {
393 					n = read(fd, buf, MIN(b, sizeof(buf)));
394 					if (n == 0)
395 						errx(1, "unexpected EOF");
396 					if (n < 0)
397 						err(1, "read");
398 					loadbuf(buf, n);
399 					b -= n;
400 				}
401 				addr = (char*)s->addr + s->size;
402 			}
403 		}
404 	}
405 	if (zero_size)
406 		loadspace(zero_size);
407 
408 	free_sections(head);
409 	free(shstrtab);
410 	return (void *)ehdr.e_entry;
411 }
412 
413 extern int devfd, modfd;
414 
415 void
elf_mod_symload(int strtablen)416 elf_mod_symload(int strtablen __attribute__((__unused__)))
417 {
418 	Elf_Ehdr ehdr;
419 	char *shstrtab;
420 	struct elf_section *head, *s;
421 	char *symbuf, *strbuf;
422 
423 	/*
424 	 * Seek to the text offset to start loading...
425 	 */
426 	if (lseek(modfd, 0, SEEK_SET) == -1)
427 		err(12, "lseek");
428 	if (read_elf_header(modfd, &ehdr) < 0)
429 		return;
430 
431 	shstrtab = read_shstring_table(modfd, &ehdr);
432 	read_sections(modfd, &ehdr, shstrtab, &head);
433 
434 	for (s = head; s; s = s->next) {
435 		struct elf_section *p = s;
436 
437 		if ((p->type == SHT_SYMTAB) || (p->type == SHT_DYNSYM)) {
438 			if (debug)
439 				fprintf(stderr, "loading '%s': addr = %p, "
440 				    "size = %#lx\n",
441 				    s->name, s->addr, (u_long)s->size);
442 			/*
443 			 * Seek to the file offset to start loading it...
444 			 */
445 			if (lseek(modfd, p->offset, SEEK_SET) == -1)
446 				err(12, "lseek");
447 			symbuf = malloc(p->size);
448 			if (symbuf == 0)
449 				err(13, "malloc");
450 			if ((size_t)read(modfd, symbuf, p->size) != p->size)
451 				err(14, "read");
452 
453 			loadsym(symbuf, p->size);
454 			free(symbuf);
455 		}
456 	}
457 
458 	for (s = head; s; s = s->next) {
459 		struct elf_section *p = s;
460 
461 		if ((p->type == SHT_STRTAB) &&
462 		    (strcmp(p->name, ".strtab") == 0 )) {
463 			if (debug)
464 				fprintf(stderr, "loading '%s': addr = %p, "
465 				    "size = %#lx\n",
466 				    s->name, s->addr, (u_long)s->size);
467 			/*
468 			 * Seek to the file offset to start loading it...
469 			 */
470 			if (lseek(modfd, p->offset, SEEK_SET) == -1)
471 				err(12, "lseek");
472 			strbuf = malloc(p->size);
473 			if (strbuf == 0)
474 				err(13, "malloc");
475 			if ((size_t)read(modfd, strbuf, p->size) != p->size)
476 				err(14, "read");
477 
478 			loadsym(strbuf, p->size);
479 			free(strbuf);
480 		}
481 	}
482 
483 	free(shstrtab);
484 	free_sections(head);
485 	return;
486 }
487