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