load_elf.c revision 60432
1/*-
2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * Copyright (c) 1998 Peter Wemm <peter@freebsd.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/sys/boot/common/load_elf.c 60432 2000-05-12 05:16:37Z bp $
28 */
29
30#include <sys/param.h>
31#include <sys/exec.h>
32#include <sys/reboot.h>
33#include <sys/linker.h>
34#include <sys/module.h>
35#include <string.h>
36#include <machine/bootinfo.h>
37#include <machine/elf.h>
38#include <stand.h>
39#define FREEBSD_ELF
40#include <link.h>
41
42#include "bootstrap.h"
43
44#define COPYOUT(s,d,l)	archsw.arch_copyout((vm_offset_t)(s), d, l)
45
46
47typedef struct elf_file {
48    Elf_Phdr 	*ph;
49    Elf_Ehdr	*ehdr;
50    Elf_Sym	*symtab;
51    Elf_Off	*hashtab;
52    Elf_Off	nbuckets;
53    Elf_Off	nchains;
54    Elf_Off*	buckets;
55    Elf_Off*	chains;
56    char	*strtab;
57    size_t	strsz;
58    int		fd;
59    caddr_t	firstpage;
60    int		firstlen;
61    int		kernel;
62    vm_offset_t	off;
63} *elf_file_t;
64
65static int elf_loadimage(struct preloaded_file *mp, elf_file_t ef, vm_offset_t loadaddr);
66static int elf_lookup_symbol(struct preloaded_file *mp, elf_file_t ef, const char* name,	Elf_Sym* sym);
67static int elf_parse_modmetadata(struct preloaded_file *mp, elf_file_t ef);
68
69char	*elf_kerneltype = "elf kernel";
70char	*elf_moduletype = "elf module";
71
72/*
73 * Attempt to load the file (file) as an ELF module.  It will be stored at
74 * (dest), and a pointer to a module structure describing the loaded object
75 * will be saved in (result).
76 */
77int
78elf_loadfile(char *filename, vm_offset_t dest, struct preloaded_file **result)
79{
80    struct preloaded_file	*fp, *kfp;
81    struct elf_file		ef;
82    Elf_Ehdr 			*ehdr;
83    int				err;
84    u_int			pad;
85    char			*s;
86
87    fp = NULL;
88    bzero(&ef, sizeof(struct elf_file));
89
90    /*
91     * Open the image, read and validate the ELF header
92     */
93    if (filename == NULL)	/* can't handle nameless */
94	return(EFTYPE);
95    if ((ef.fd = open(filename, O_RDONLY)) == -1)
96	return(errno);
97    ef.firstpage = malloc(PAGE_SIZE);
98    if (ef.firstpage == NULL) {
99	close(ef.fd);
100	return(ENOMEM);
101    }
102    ef.firstlen = read(ef.fd, ef.firstpage, PAGE_SIZE);
103    if (ef.firstlen <= sizeof(Elf_Ehdr)) {
104	err = EFTYPE;		/* could be EIO, but may be small file */
105	goto oerr;
106    }
107    ehdr = ef.ehdr = (Elf_Ehdr *)ef.firstpage;
108
109    /* Is it ELF? */
110    if (!IS_ELF(*ehdr)) {
111	err = EFTYPE;
112	goto oerr;
113    }
114    if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||	/* Layout ? */
115	ehdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
116	ehdr->e_ident[EI_VERSION] != EV_CURRENT ||	/* Version ? */
117	ehdr->e_version != EV_CURRENT ||
118	ehdr->e_machine != ELF_TARG_MACH) {		/* Machine ? */
119	err = EFTYPE;
120	goto oerr;
121    }
122
123
124    /*
125     * Check to see what sort of module we are.
126     */
127    kfp = file_findfile(NULL, NULL);
128    if (ehdr->e_type == ET_DYN) {
129	/* Looks like a kld module */
130	if (kfp == NULL) {
131	    printf("elf_loadfile: can't load module before kernel\n");
132	    err = EPERM;
133	    goto oerr;
134	}
135	if (strcmp(elf_kerneltype, kfp->f_type)) {
136	    printf("elf_loadfile: can't load module with kernel type '%s'\n", kfp->f_type);
137	    err = EPERM;
138	    goto oerr;
139	}
140	/* Looks OK, got ahead */
141	ef.kernel = 0;
142
143	/* Page-align the load address */
144	pad = (u_int)dest & PAGE_MASK;
145	if (pad != 0) {
146	    pad = PAGE_SIZE - pad;
147	    dest += pad;
148	}
149    } else if (ehdr->e_type == ET_EXEC) {
150	/* Looks like a kernel */
151	if (kfp != NULL) {
152	    printf("elf_loadfile: kernel already loaded\n");
153	    err = EPERM;
154	    goto oerr;
155	}
156	/*
157	 * Calculate destination address based on kernel entrypoint
158	 */
159	dest = (vm_offset_t) ehdr->e_entry;
160	if (dest == 0) {
161	    printf("elf_loadfile: not a kernel (maybe static binary?)\n");
162	    err = EPERM;
163	    goto oerr;
164	}
165	ef.kernel = 1;
166
167    } else {
168	err = EFTYPE;
169	goto oerr;
170    }
171
172    /*
173     * Ok, we think we should handle this.
174     */
175    fp = file_alloc();
176    if (fp == NULL) {
177	    printf("elf_loadfile: cannot allocate module info\n");
178	    err = EPERM;
179	    goto out;
180    }
181    if (ef.kernel)
182	setenv("kernelname", filename, 1);
183    s = strrchr(filename, '/');
184    if (s)
185	fp->f_name = strdup(s + 1);
186    else
187	fp->f_name = strdup(filename);
188    fp->f_type = strdup(ef.kernel ? elf_kerneltype : elf_moduletype);
189
190#ifdef ELF_VERBOSE
191    if (ef.kernel)
192	printf("%s entry at %p\n", filename, (void *) dest);
193#else
194    printf("%s ", filename);
195#endif
196
197    fp->f_size = elf_loadimage(fp, &ef, dest);
198    if (fp->f_size == 0 || fp->f_addr == 0)
199	goto ioerr;
200
201    /* save exec header as metadata */
202    file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof(*ehdr), ehdr);
203
204    /* Load OK, return module pointer */
205    *result = (struct preloaded_file *)fp;
206    err = 0;
207    goto out;
208
209 ioerr:
210    err = EIO;
211 oerr:
212    file_discard(fp);
213 out:
214    if (ef.firstpage)
215	free(ef.firstpage);
216    close(ef.fd);
217    return(err);
218}
219
220/*
221 * With the file (fd) open on the image, and (ehdr) containing
222 * the Elf header, load the image at (off)
223 */
224static int
225elf_loadimage(struct preloaded_file *fp, elf_file_t ef, vm_offset_t off)
226{
227    int 	i, j;
228    Elf_Ehdr	*ehdr;
229    Elf_Phdr	*phdr, *php;
230    Elf_Shdr	*shdr;
231    int		ret;
232    vm_offset_t firstaddr;
233    vm_offset_t lastaddr;
234    void	*buf;
235    size_t	resid, chunk;
236    vm_offset_t	dest;
237    vm_offset_t	ssym, esym;
238    Elf_Dyn	*dp;
239    int		ndp;
240    char	*s;
241    int		symstrindex;
242    int		symtabindex;
243    long	size;
244    int		fpcopy;
245
246    dp = NULL;
247    shdr = NULL;
248    ret = 0;
249    firstaddr = lastaddr = 0;
250    ehdr = ef->ehdr;
251    if (ef->kernel) {
252#ifdef __i386__
253	off = - (off & 0xff000000u);	/* i386 relocates after locore */
254#else
255	off = 0;		/* alpha is direct mapped for kernels */
256#endif
257    }
258    ef->off = off;
259
260    if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(*phdr)) > ef->firstlen) {
261	printf("elf_loadimage: program header not within first page\n");
262	goto out;
263    }
264    phdr = (Elf_Phdr *)(ef->firstpage + ehdr->e_phoff);
265
266    for (i = 0; i < ehdr->e_phnum; i++) {
267	/* We want to load PT_LOAD segments only.. */
268	if (phdr[i].p_type != PT_LOAD)
269	    continue;
270
271#ifdef ELF_VERBOSE
272	printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx",
273	    (long)phdr[i].p_filesz, (long)phdr[i].p_offset,
274	    (long)(phdr[i].p_vaddr + off),
275	    (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1));
276#else
277	if ((phdr[i].p_flags & PF_W) == 0) {
278	    printf("text=0x%lx ", (long)phdr[i].p_filesz);
279	} else {
280	    printf("data=0x%lx", (long)phdr[i].p_filesz);
281	    if (phdr[i].p_filesz < phdr[i].p_memsz)
282		printf("+0x%lx", (long)(phdr[i].p_memsz -phdr[i].p_filesz));
283	    printf(" ");
284	}
285#endif
286	fpcopy = 0;
287	if (ef->firstlen > phdr[i].p_offset) {
288	    fpcopy = ef->firstlen - phdr[i].p_offset;
289	    archsw.arch_copyin(ef->firstpage + phdr[i].p_offset,
290			       phdr[i].p_vaddr + off, fpcopy);
291	}
292	if (phdr[i].p_filesz > fpcopy) {
293	    if (lseek(ef->fd, phdr[i].p_offset + fpcopy, SEEK_SET) == -1) {
294		printf("\nelf_loadexec: cannot seek\n");
295		goto out;
296	    }
297	    if (archsw.arch_readin(ef->fd, phdr[i].p_vaddr + off + fpcopy,
298		phdr[i].p_filesz - fpcopy) != phdr[i].p_filesz - fpcopy) {
299		printf("\nelf_loadexec: archsw.readin failed\n");
300		goto out;
301	    }
302	}
303	/* clear space from oversized segments; eg: bss */
304	if (phdr[i].p_filesz < phdr[i].p_memsz) {
305#ifdef ELF_VERBOSE
306	    printf(" (bss: 0x%lx-0x%lx)",
307		(long)(phdr[i].p_vaddr + off + phdr[i].p_filesz),
308		(long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1));
309#endif
310
311	    /* no archsw.arch_bzero */
312	    buf = malloc(PAGE_SIZE);
313	    if (buf == NULL) {
314		printf("\nelf_loadimage: malloc() failed\n");
315		goto out;
316	    }
317	    bzero(buf, PAGE_SIZE);
318	    resid = phdr[i].p_memsz - phdr[i].p_filesz;
319	    dest = phdr[i].p_vaddr + off + phdr[i].p_filesz;
320	    while (resid > 0) {
321		chunk = min(PAGE_SIZE, resid);
322		archsw.arch_copyin(buf, dest, chunk);
323		resid -= chunk;
324		dest += chunk;
325	    }
326	    free(buf);
327	}
328#ifdef ELF_VERBOSE
329	printf("\n");
330#endif
331
332	if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off))
333	    firstaddr = phdr[i].p_vaddr + off;
334	if (lastaddr == 0 || lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz))
335	    lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz;
336    }
337    lastaddr = roundup(lastaddr, sizeof(long));
338
339    /*
340     * Now grab the symbol tables.  This isn't easy if we're reading a
341     * .gz file.  I think the rule is going to have to be that you must
342     * strip a file to remove symbols before gzipping it so that we do not
343     * try to lseek() on it.
344     */
345    chunk = ehdr->e_shnum * ehdr->e_shentsize;
346    if (chunk == 0 || ehdr->e_shoff == 0)
347	goto nosyms;
348    shdr = malloc(chunk);
349    if (shdr == NULL)
350	goto nosyms;
351    if (lseek(ef->fd, ehdr->e_shoff, SEEK_SET) == -1) {
352	printf("\nelf_loadimage: cannot lseek() to section headers");
353	goto nosyms;
354    }
355    if (read(ef->fd, shdr, chunk) != chunk) {
356	printf("\nelf_loadimage: read section headers failed");
357	goto nosyms;
358    }
359    symtabindex = -1;
360    symstrindex = -1;
361    for (i = 0; i < ehdr->e_shnum; i++) {
362	if (shdr[i].sh_type != SHT_SYMTAB)
363	    continue;
364	for (j = 0; j < ehdr->e_phnum; j++) {
365	    if (phdr[j].p_type != PT_LOAD)
366		continue;
367	    if (shdr[i].sh_offset >= phdr[j].p_offset &&
368		(shdr[i].sh_offset + shdr[i].sh_size <=
369		 phdr[j].p_offset + phdr[j].p_filesz)) {
370		shdr[i].sh_offset = 0;
371		shdr[i].sh_size = 0;
372		break;
373	    }
374	}
375	if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0)
376	    continue;		/* alread loaded in a PT_LOAD above */
377	/* Save it for loading below */
378	symtabindex = i;
379	symstrindex = shdr[i].sh_link;
380    }
381    if (symtabindex < 0 || symstrindex < 0)
382	goto nosyms;
383
384    /* Ok, committed to a load. */
385#ifndef ELF_VERBOSE
386    printf("syms=[");
387#endif
388    ssym = lastaddr;
389    for (i = symtabindex; i >= 0; i = symstrindex) {
390#ifdef ELF_VERBOSE
391	char	*secname;
392
393	switch(shdr[i].sh_type) {
394	    case SHT_SYMTAB:		/* Symbol table */
395		secname = "symtab";
396		break;
397	    case SHT_STRTAB:		/* String table */
398		secname = "strtab";
399		break;
400	    default:
401		secname = "WHOA!!";
402		break;
403	}
404#endif
405
406	size = shdr[i].sh_size;
407	archsw.arch_copyin(&size, lastaddr, sizeof(size));
408	lastaddr += sizeof(long);
409
410#ifdef ELF_VERBOSE
411	printf("\n%s: 0x%lx@0x%lx -> 0x%lx-0x%lx", secname,
412	    shdr[i].sh_size, shdr[i].sh_offset,
413	    lastaddr, lastaddr + shdr[i].sh_size);
414#else
415	if (i == symstrindex)
416	    printf("+");
417	printf("0x%lx+0x%lx", (long)sizeof(size), size);
418#endif
419
420	if (lseek(ef->fd, shdr[i].sh_offset, SEEK_SET) == -1) {
421	    printf("\nelf_loadimage: could not seek for symbols - skipped!");
422	    lastaddr = ssym;
423	    ssym = 0;
424	    goto nosyms;
425	}
426	if (archsw.arch_readin(ef->fd, lastaddr, shdr[i].sh_size) !=
427	    shdr[i].sh_size) {
428	    printf("\nelf_loadimage: could not read symbols - skipped!");
429	    lastaddr = ssym;
430	    ssym = 0;
431	    goto nosyms;
432	}
433	/* Reset offsets relative to ssym */
434	lastaddr += shdr[i].sh_size;
435	lastaddr = roundup(lastaddr, sizeof(long));
436	if (i == symtabindex)
437	    symtabindex = -1;
438	else if (i == symstrindex)
439	    symstrindex = -1;
440    }
441    esym = lastaddr;
442#ifndef ELF_VERBOSE
443    printf("]");
444#endif
445
446    file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym);
447    file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym);
448
449nosyms:
450    printf("\n");
451
452    ret = lastaddr - firstaddr;
453    fp->f_addr = firstaddr;
454
455    php = NULL;
456    for (i = 0; i < ehdr->e_phnum; i++) {
457	if (phdr[i].p_type == PT_DYNAMIC) {
458	    php = phdr + i;
459	    dp = (Elf_Dyn *)(php->p_vaddr);
460	    file_addmetadata(fp, MODINFOMD_DYNAMIC, sizeof(dp), &dp);
461	    dp = NULL;
462	    break;
463	}
464    }
465
466    if (php == NULL)	/* this is bad, we cannot get to symbols or _DYNAMIC */
467	goto out;
468
469    ndp = php->p_filesz / sizeof(Elf_Dyn);
470    if (ndp == 0)
471	goto out;
472    dp = malloc(php->p_filesz);
473    if (dp == NULL)
474	goto out;
475    archsw.arch_copyout(php->p_vaddr + off, dp, php->p_filesz);
476
477    ef->strsz = 0;
478    for (i = 0; i < ndp; i++) {
479	if (dp[i].d_tag == NULL)
480	    break;
481	switch (dp[i].d_tag) {
482	case DT_HASH:
483	    ef->hashtab = (Elf_Off*)(dp[i].d_un.d_ptr + off);
484	    break;
485	case DT_STRTAB:
486	    ef->strtab = (char *)(dp[i].d_un.d_ptr + off);
487	    break;
488	case DT_STRSZ:
489	    ef->strsz = dp[i].d_un.d_val;
490	    break;
491	case DT_SYMTAB:
492	    ef->symtab = (Elf_Sym*)(dp[i].d_un.d_ptr + off);
493	    break;
494	default:
495	    break;
496	}
497    }
498    if (ef->hashtab == NULL || ef->symtab == NULL ||
499	ef->strtab == NULL || ef->strsz == 0)
500	goto out;
501    COPYOUT(ef->hashtab, &ef->nbuckets, sizeof(ef->nbuckets));
502    COPYOUT(ef->hashtab + 1, &ef->nchains, sizeof(ef->nchains));
503    ef->buckets = ef->hashtab + 2;
504    ef->chains = ef->buckets + ef->nbuckets;
505    if (elf_parse_modmetadata(fp, ef) == 0)
506	goto out;
507
508    if (ef->kernel)			/* kernel must not depend on anything */
509	goto out;
510
511    for (i = 0; i < ndp; i++) {
512        if (dp[i].d_tag == NULL)
513	    break;
514	if (dp[i].d_tag != DT_NEEDED)
515	    continue;
516	j = dp[i].d_un.d_ptr;
517	if (j < 1 || j > ef->strsz - 2)
518	    continue;
519	s = strdupout((vm_offset_t)&ef->strtab[j]);
520	file_addmetadata(fp, MODINFOMD_DEPLIST, strlen(s) + 1, s);
521	free(s);
522    }
523
524out:
525    if (dp)
526	free(dp);
527    if (shdr)
528	free(shdr);
529    return ret;
530}
531
532static char invalid_name[] = "bad";
533char *
534fake_modname(char *name) {
535    char *sp, *ep;
536    int len;
537
538    sp = strrchr(name, '/');
539    if (sp)
540	sp++;
541    else
542	sp = name;
543    ep = strrchr(name, '.');
544    if (ep) {
545	    if (ep == name) {
546		sp = invalid_name;
547		ep = invalid_name + sizeof(invalid_name) - 1;
548	    }
549    } else
550	ep = name + strlen(name);
551    len = ep - sp;
552    ep = malloc(len + 1);
553    if (ep == NULL)
554	return NULL;
555    memcpy(ep, sp, len);
556    ep[len] = '\0';
557    return ep;
558}
559
560int
561elf_parse_modmetadata(struct preloaded_file *fp, elf_file_t ef) {
562    struct mod_metadata md;
563    Elf_Sym sym;
564    void **p, *v;
565    char *s;
566    long entries;
567    int modcnt;
568
569    if (elf_lookup_symbol(fp, ef, "modmetadata_set", &sym) != 0)
570	return ENOENT;
571    COPYOUT(sym.st_value + ef->off, &entries, sizeof(entries));
572
573    modcnt = 0;
574    p = (void*)(sym.st_value + ef->off + sizeof(entries));
575    while (entries--) {
576	COPYOUT(p++, &v, sizeof(v));
577	COPYOUT(v + ef->off, &md, sizeof(md));
578	switch(md.md_type) {
579	  case MDT_DEPEND:
580	    if (ef->kernel)		/* kernel must not depend on anything */
581	      break;
582	    s = strdupout((vm_offset_t)(md.md_cval + ef->off));
583	    file_addmetadata(fp, MODINFOMD_DEPLIST, strlen(s) + 1, s);
584	    free(s);
585	    break;
586	  case MDT_VERSION:
587	    s = strdupout((vm_offset_t)(md.md_cval + ef->off));
588	    file_addmodule(fp, s, NULL);
589	    printf("  module: %s\n", s);
590	    free(s);
591	    modcnt++;
592	    break;
593	}
594    }
595    if (modcnt == 0) {
596	s = fake_modname(fp->f_name);
597	file_addmodule(fp, s, NULL);
598	free(s);
599    }
600    return 0;
601}
602
603static unsigned long
604elf_hash(const char *name)
605{
606    const unsigned char *p = (const unsigned char *) name;
607    unsigned long h = 0;
608    unsigned long g;
609
610    while (*p != '\0') {
611	h = (h << 4) + *p++;
612	if ((g = h & 0xf0000000) != 0)
613	    h ^= g >> 24;
614	h &= ~g;
615    }
616    return h;
617}
618
619static const char elf_bad_symtable[] = "elf_lookup_symbol: corrupt symbol table\n";
620int
621elf_lookup_symbol(struct preloaded_file *fp, elf_file_t ef, const char* name,
622		  Elf_Sym *symp)
623{
624    unsigned long symnum;
625    Elf_Sym sym;
626    char *strp;
627    unsigned long hash;
628
629    hash = elf_hash(name);
630    COPYOUT(&ef->buckets[hash % ef->nbuckets], &symnum, sizeof(symnum));
631
632    while (symnum != STN_UNDEF) {
633	if (symnum >= ef->nchains) {
634	    printf(elf_bad_symtable);
635	    return ENOENT;
636	}
637
638	COPYOUT(ef->symtab + symnum, &sym, sizeof(sym));
639	if (sym.st_name == 0) {
640	    printf(elf_bad_symtable);
641	    return ENOENT;
642	}
643
644	strp = strdupout((vm_offset_t)(ef->strtab + sym.st_name));
645	if (strcmp(name, strp) == 0) {
646	    free(strp);
647	    if (sym.st_shndx != SHN_UNDEF ||
648		(sym.st_value != 0 &&
649		 ELF_ST_TYPE(sym.st_info) == STT_FUNC)) {
650		*symp = sym;
651		return 0;
652	    }
653	    return ENOENT;
654	}
655	free(strp);
656	COPYOUT(&ef->chains[symnum], &symnum, sizeof(symnum));
657    }
658    return ENOENT;
659}
660