load_elf.c revision 57468
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 57468 2000-02-25 05:10:44Z 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 <string.h>
35#include <machine/bootinfo.h>
36#include <machine/elf.h>
37#include <stand.h>
38#define FREEBSD_ELF
39#include <link.h>
40
41#include "bootstrap.h"
42
43static int	elf_loadimage(struct loaded_module *mp, int fd, vm_offset_t loadaddr, Elf_Ehdr *ehdr, int kernel, caddr_t firstpage, int firstlen);
44
45char	*elf_kerneltype = "elf kernel";
46char	*elf_moduletype = "elf module";
47
48/*
49 * Attempt to load the file (file) as an ELF module.  It will be stored at
50 * (dest), and a pointer to a module structure describing the loaded object
51 * will be saved in (result).
52 */
53int
54elf_loadmodule(char *filename, vm_offset_t dest, struct loaded_module **result)
55{
56    struct loaded_module	*mp, *kmp;
57    Elf_Ehdr			*ehdr;
58    int				fd;
59    int				err, kernel;
60    u_int			pad;
61    char			*s;
62    caddr_t			firstpage;
63    int				firstlen;
64
65    mp = NULL;
66
67    /*
68     * Open the image, read and validate the ELF header
69     */
70    if (filename == NULL)	/* can't handle nameless */
71	return(EFTYPE);
72    if ((fd = open(filename, O_RDONLY)) == -1)
73	return(errno);
74    firstpage = malloc(PAGE_SIZE);
75    if (firstpage == NULL)
76	return(ENOMEM);
77    firstlen = read(fd, firstpage, PAGE_SIZE);
78    if (firstlen <= sizeof(ehdr)) {
79	err = EFTYPE;		/* could be EIO, but may be small file */
80	goto oerr;
81    }
82    ehdr = (Elf_Ehdr *)firstpage;
83
84    /* Is it ELF? */
85    if (!IS_ELF(*ehdr)) {
86	err = EFTYPE;
87	goto oerr;
88    }
89    if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||	/* Layout ? */
90	ehdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
91	ehdr->e_ident[EI_VERSION] != EV_CURRENT ||	/* Version ? */
92	ehdr->e_version != EV_CURRENT ||
93	ehdr->e_machine != ELF_TARG_MACH) {		/* Machine ? */
94	err = EFTYPE;
95	goto oerr;
96    }
97
98
99    /*
100     * Check to see what sort of module we are.
101     */
102    kmp = mod_findmodule(NULL, NULL);
103    if (ehdr->e_type == ET_DYN) {
104	/* Looks like a kld module */
105	if (kmp == NULL) {
106	    printf("elf_loadmodule: can't load module before kernel\n");
107	    err = EPERM;
108	    goto oerr;
109	}
110	if (strcmp(elf_kerneltype, kmp->m_type)) {
111	    printf("elf_loadmodule: can't load module with kernel type '%s'\n", kmp->m_type);
112	    err = EPERM;
113	    goto oerr;
114	}
115	/* Looks OK, got ahead */
116	kernel = 0;
117
118	/* Page-align the load address */
119	pad = (u_int)dest & PAGE_MASK;
120	if (pad != 0) {
121	    pad = PAGE_SIZE - pad;
122	    dest += pad;
123	}
124    } else if (ehdr->e_type == ET_EXEC) {
125	/* Looks like a kernel */
126	if (kmp != NULL) {
127	    printf("elf_loadmodule: kernel already loaded\n");
128	    err = EPERM;
129	    goto oerr;
130	}
131	/*
132	 * Calculate destination address based on kernel entrypoint
133	 */
134	dest = (vm_offset_t) ehdr->e_entry;
135	if (dest == 0) {
136	    printf("elf_loadmodule: not a kernel (maybe static binary?)\n");
137	    err = EPERM;
138	    goto oerr;
139	}
140	kernel = 1;
141
142    } else {
143	err = EFTYPE;
144	goto oerr;
145    }
146
147    /*
148     * Ok, we think we should handle this.
149     */
150    mp = mod_allocmodule();
151    if (mp == NULL) {
152	    printf("elf_loadmodule: cannot allocate module info\n");
153	    err = EPERM;
154	    goto out;
155    }
156    if (kernel)
157	setenv("kernelname", filename, 1);
158    s = strrchr(filename, '/');
159    if (s)
160	mp->m_name = strdup(s + 1);
161    else
162	mp->m_name = strdup(filename);
163    mp->m_type = strdup(kernel ? elf_kerneltype : elf_moduletype);
164
165#ifdef ELF_VERBOSE
166    if (kernel)
167	printf("%s entry at %p\n", filename, (void *) dest);
168#else
169    printf("%s ", filename);
170#endif
171
172    mp->m_size = elf_loadimage(mp, fd, dest, ehdr, kernel, firstpage, firstlen);
173    if (mp->m_size == 0 || mp->m_addr == 0)
174	goto ioerr;
175
176    /* save exec header as metadata */
177    mod_addmetadata(mp, MODINFOMD_ELFHDR, sizeof(*ehdr), ehdr);
178
179    /* Load OK, return module pointer */
180    *result = (struct loaded_module *)mp;
181    err = 0;
182    goto out;
183
184 ioerr:
185    err = EIO;
186 oerr:
187    mod_discard(mp);
188 out:
189    if (firstpage)
190	free(firstpage);
191    close(fd);
192    return(err);
193}
194
195/*
196 * With the file (fd) open on the image, and (ehdr) containing
197 * the Elf header, load the image at (off)
198 */
199static int
200elf_loadimage(struct loaded_module *mp, int fd, vm_offset_t off,
201	      Elf_Ehdr *ehdr, int kernel, caddr_t firstpage, int firstlen)
202{
203    int 	i, j;
204    Elf_Phdr	*phdr;
205    Elf_Shdr	*shdr;
206    int		ret;
207    vm_offset_t firstaddr;
208    vm_offset_t lastaddr;
209    void	*buf;
210    size_t	resid, chunk;
211    vm_offset_t	dest;
212    vm_offset_t	ssym, esym;
213    Elf_Dyn	*dp;
214    int		ndp;
215    char	*s;
216    char	*strtab;
217    size_t	strsz;
218    int		symstrindex;
219    int		symtabindex;
220    long	size;
221    int		fpcopy;
222
223    dp = NULL;
224    shdr = NULL;
225    ret = 0;
226    firstaddr = lastaddr = 0;
227    if (kernel) {
228#ifdef __i386__
229	off = - (off & 0xff000000u);	/* i386 relocates after locore */
230#else
231	off = 0;		/* alpha is direct mapped for kernels */
232#endif
233    }
234
235    if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(*phdr)) > firstlen) {
236	printf("elf_loadimage: program header not within first page\n");
237	goto out;
238    }
239    phdr = (Elf_Phdr *)(firstpage + ehdr->e_phoff);
240
241    for (i = 0; i < ehdr->e_phnum; i++) {
242	/* We want to load PT_LOAD segments only.. */
243	if (phdr[i].p_type != PT_LOAD)
244	    continue;
245
246#ifdef ELF_VERBOSE
247	printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx",
248	    (long)phdr[i].p_filesz, (long)phdr[i].p_offset,
249	    (long)(phdr[i].p_vaddr + off),
250	    (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1));
251#else
252	if ((phdr[i].p_flags & PF_W) == 0) {
253	    printf("text=0x%lx ", (long)phdr[i].p_filesz);
254	} else {
255	    printf("data=0x%lx", (long)phdr[i].p_filesz);
256	    if (phdr[i].p_filesz < phdr[i].p_memsz)
257		printf("+0x%lx", (long)(phdr[i].p_memsz -phdr[i].p_filesz));
258	    printf(" ");
259	}
260#endif
261	fpcopy = 0;
262	if (firstlen > phdr[i].p_offset) {
263	    fpcopy = firstlen - phdr[i].p_offset;
264	    archsw.arch_copyin(firstpage + phdr[i].p_offset,
265			       phdr[i].p_vaddr + off, fpcopy);
266	}
267	if (phdr[i].p_filesz > fpcopy) {
268	    if (lseek(fd, phdr[i].p_offset + fpcopy, SEEK_SET) == -1) {
269		printf("\nelf_loadexec: cannot seek\n");
270		goto out;
271	    }
272	    if (archsw.arch_readin(fd, phdr[i].p_vaddr + off + fpcopy,
273		phdr[i].p_filesz - fpcopy) != phdr[i].p_filesz - fpcopy) {
274		printf("\nelf_loadexec: archsw.readin failed\n");
275		goto out;
276	    }
277	}
278	/* clear space from oversized segments; eg: bss */
279	if (phdr[i].p_filesz < phdr[i].p_memsz) {
280#ifdef ELF_VERBOSE
281	    printf(" (bss: 0x%lx-0x%lx)",
282		(long)(phdr[i].p_vaddr + off + phdr[i].p_filesz),
283		(long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1));
284#endif
285
286	    /* no archsw.arch_bzero */
287	    buf = malloc(PAGE_SIZE);
288	    bzero(buf, PAGE_SIZE);
289	    resid = phdr[i].p_memsz - phdr[i].p_filesz;
290	    dest = phdr[i].p_vaddr + off + phdr[i].p_filesz;
291	    while (resid > 0) {
292		chunk = min(PAGE_SIZE, resid);
293		archsw.arch_copyin(buf, dest, chunk);
294		resid -= chunk;
295		dest += chunk;
296	    }
297	    free(buf);
298	}
299#ifdef ELF_VERBOSE
300	printf("\n");
301#endif
302
303	if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off))
304	    firstaddr = phdr[i].p_vaddr + off;
305	if (lastaddr == 0 || lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz))
306	    lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz;
307    }
308    lastaddr = roundup(lastaddr, sizeof(long));
309
310    /*
311     * Now grab the symbol tables.  This isn't easy if we're reading a
312     * .gz file.  I think the rule is going to have to be that you must
313     * strip a file to remove symbols before gzipping it so that we do not
314     * try to lseek() on it.
315     */
316    chunk = ehdr->e_shnum * ehdr->e_shentsize;
317    if (chunk == 0 || ehdr->e_shoff == 0)
318	goto nosyms;
319    shdr = malloc(chunk);
320    if (shdr == NULL)
321	goto nosyms;
322    if (lseek(fd, ehdr->e_shoff, SEEK_SET) == -1) {
323	printf("\nelf_loadimage: cannot lseek() to section headers");
324	goto nosyms;
325    }
326    if (read(fd, shdr, chunk) != chunk) {
327	printf("\nelf_loadimage: read section headers failed");
328	goto nosyms;
329    }
330    symtabindex = -1;
331    symstrindex = -1;
332    for (i = 0; i < ehdr->e_shnum; i++) {
333	if (shdr[i].sh_type != SHT_SYMTAB)
334	    continue;
335	for (j = 0; j < ehdr->e_phnum; j++) {
336	    if (phdr[j].p_type != PT_LOAD)
337		continue;
338	    if (shdr[i].sh_offset >= phdr[j].p_offset &&
339		(shdr[i].sh_offset + shdr[i].sh_size <=
340		 phdr[j].p_offset + phdr[j].p_filesz)) {
341		shdr[i].sh_offset = 0;
342		shdr[i].sh_size = 0;
343		break;
344	    }
345	}
346	if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0)
347	    continue;		/* alread loaded in a PT_LOAD above */
348	/* Save it for loading below */
349	symtabindex = i;
350	symstrindex = shdr[i].sh_link;
351    }
352    if (symtabindex < 0 || symstrindex < 0)
353	goto nosyms;
354
355    /* Ok, committed to a load. */
356#ifndef ELF_VERBOSE
357    printf("syms=[");
358#endif
359    ssym = lastaddr;
360    for (i = symtabindex; i >= 0; i = symstrindex) {
361#ifdef ELF_VERBOSE
362	char	*secname;
363
364	switch(shdr[i].sh_type) {
365	    case SHT_SYMTAB:		/* Symbol table */
366		secname = "symtab";
367		break;
368	    case SHT_STRTAB:		/* String table */
369		secname = "strtab";
370		break;
371	    default:
372		secname = "WHOA!!";
373		break;
374	}
375#endif
376
377	size = shdr[i].sh_size;
378	archsw.arch_copyin(&size, lastaddr, sizeof(size));
379	lastaddr += sizeof(long);
380
381#ifdef ELF_VERBOSE
382	printf("\n%s: 0x%lx@0x%lx -> 0x%lx-0x%lx", secname,
383	    shdr[i].sh_size, shdr[i].sh_offset,
384	    lastaddr, lastaddr + shdr[i].sh_size);
385#else
386	if (i == symstrindex)
387	    printf("+");
388	printf("0x%lx+0x%lx", (long)sizeof(size), size);
389#endif
390
391	if (lseek(fd, shdr[i].sh_offset, SEEK_SET) == -1) {
392	    printf("\nelf_loadimage: could not seek for symbols - skipped!");
393	    lastaddr = ssym;
394	    ssym = 0;
395	    goto nosyms;
396	}
397	if (archsw.arch_readin(fd, lastaddr, shdr[i].sh_size) !=
398	    shdr[i].sh_size) {
399	    printf("\nelf_loadimage: could not read symbols - skipped!");
400	    lastaddr = ssym;
401	    ssym = 0;
402	    goto nosyms;
403	}
404	/* Reset offsets relative to ssym */
405	lastaddr += shdr[i].sh_size;
406	lastaddr = roundup(lastaddr, sizeof(long));
407	if (i == symtabindex)
408	    symtabindex = -1;
409	else if (i == symstrindex)
410	    symstrindex = -1;
411    }
412    esym = lastaddr;
413#ifndef ELF_VERBOSE
414    printf("]");
415#endif
416
417    mod_addmetadata(mp, MODINFOMD_SSYM, sizeof(ssym), &ssym);
418    mod_addmetadata(mp, MODINFOMD_ESYM, sizeof(esym), &esym);
419
420nosyms:
421    printf("\n");
422
423    ret = lastaddr - firstaddr;
424    mp->m_addr = firstaddr;
425
426    for (i = 0; i < ehdr->e_phnum; i++) {
427	if (phdr[i].p_type == PT_DYNAMIC) {
428	    dp = (Elf_Dyn *)(phdr[i].p_vaddr);
429	    mod_addmetadata(mp, MODINFOMD_DYNAMIC, sizeof(dp), &dp);
430	    dp = NULL;
431	    break;
432	}
433    }
434
435    if (kernel)		/* kernel must not depend on anything */
436	goto out;
437
438    ndp = 0;
439    for (i = 0; i < ehdr->e_phnum; i++) {
440	if (phdr[i].p_type == PT_DYNAMIC) {
441	    ndp = phdr[i].p_filesz / sizeof(Elf_Dyn);
442	    dp = malloc(phdr[i].p_filesz);
443	    archsw.arch_copyout(phdr[i].p_vaddr + off, dp, phdr[i].p_filesz);
444	}
445    }
446    if (dp == NULL || ndp == 0)
447	goto out;
448    strtab = NULL;
449    strsz = 0;
450    for (i = 0; i < ndp; i++) {
451	if (dp[i].d_tag == NULL)
452	    break;
453	switch (dp[i].d_tag) {
454	case DT_STRTAB:
455	    strtab = (char *)(dp[i].d_un.d_ptr + off);
456	    break;
457	case DT_STRSZ:
458	    strsz = dp[i].d_un.d_val;
459	    break;
460	default:
461	    break;
462	}
463    }
464    if (strtab == NULL || strsz == 0)
465	goto out;
466
467    for (i = 0; i < ndp; i++) {
468	if (dp[i].d_tag == NULL)
469	    break;
470	if (dp[i].d_tag != DT_NEEDED)
471	    continue;
472	j = dp[i].d_un.d_ptr;
473	if (j < 1 || j > (strsz - 2))
474	    continue;	/* bad symbol name index */
475	s = strdupout((vm_offset_t)&strtab[j]);
476	mod_addmetadata(mp, MODINFOMD_DEPLIST, strlen(s) + 1, s);
477	free(s);
478    }
479
480out:
481    if (dp)
482	free(dp);
483    if (shdr)
484	free(shdr);
485    return ret;
486}
487