load_elf.c revision 39887
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 *	$Id: load_elf.c,v 1.1 1998/09/30 19:38:26 peter Exp $
28 */
29
30#include <sys/param.h>
31#include <sys/exec.h>
32#include <sys/reboot.h>
33#include <string.h>
34#include <machine/bootinfo.h>
35#include <machine/elf.h>
36#include <stand.h>
37#define FREEBSD_ELF
38#include <link.h>
39
40#include "bootstrap.h"
41
42static int		elf_loadimage(struct loaded_module *mp, int fd, vm_offset_t *loadaddr, Elf_Ehdr *ehdr, int kernel);
43#if 0
44static vm_offset_t	elf_findkldident(struct loaded_module *mp, Elf_Ehdr *ehdr);
45static int		elf_fixupkldmod(struct loaded_module *mp, Elf_Ehdr *ehdr);
46#endif
47
48char	*elf_kerneltype = "elf kernel";
49char	*elf_moduletype = "elf module";
50
51/*
52 * Attempt to load the file (file) as an ELF module.  It will be stored at
53 * (dest), and a pointer to a module structure describing the loaded object
54 * will be saved in (result).
55 */
56int
57elf_loadmodule(char *filename, vm_offset_t dest, struct loaded_module **result)
58{
59    struct loaded_module	*mp, *kmp;
60    Elf_Ehdr			ehdr;
61    int				fd;
62    vm_offset_t			addr;
63    int				err, kernel;
64    u_int			pad;
65
66    mp = NULL;
67
68    /*
69     * Open the image, read and validate the ELF header
70     */
71    if (filename == NULL)	/* can't handle nameless */
72	return(EFTYPE);
73    if ((fd = open(filename, O_RDONLY)) == -1)
74	return(errno);
75    if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) {
76	err = EFTYPE;		/* could be EIO, but may be small file */
77	goto oerr;
78    }
79
80    /* Is it ELF? */
81    if (!IS_ELF(ehdr)) {
82	err = EFTYPE;
83	goto oerr;
84    }
85    if (ehdr.e_ident[EI_CLASS] != ELF_TARG_CLASS ||	/* Layout ? */
86	ehdr.e_ident[EI_DATA] != ELF_TARG_DATA ||
87	ehdr.e_ident[EI_VERSION] != EV_CURRENT ||	/* Version ? */
88	ehdr.e_version != EV_CURRENT ||
89	ehdr.e_machine != ELF_TARG_MACH) {		/* Machine ? */
90	err = EFTYPE;
91	goto oerr;
92    }
93
94
95    /*
96     * Check to see what sort of module we are.
97     */
98    kmp = mod_findmodule(NULL, NULL);
99    if (ehdr.e_type == ET_DYN) {
100	/* Looks like a kld module */
101	if (kmp == NULL) {
102	    printf("elf_loadmodule: can't load module before kernel\n");
103	    err = EPERM;
104	    goto oerr;
105	}
106	if (strcmp(elf_kerneltype, kmp->m_type)) {
107	    printf("elf_loadmodule: can't load module with kernel type '%s'\n", kmp->m_type);
108	    err = EPERM;
109	    goto oerr;
110	}
111	/* Looks OK, got ahead */
112	kernel = 0;
113
114	/* Page-align the load address */
115	addr = dest;
116	pad = (u_int)addr & PAGE_MASK;
117	if (pad != 0) {
118	    pad = PAGE_SIZE - pad;
119	    addr += pad;
120	}
121    } else if (ehdr.e_type == ET_EXEC) {
122	/* Looks like a kernel */
123	if (kmp != NULL) {
124	    printf("elf_loadmodule: kernel already loaded\n");
125	    err = EPERM;
126	    goto oerr;
127	}
128	/*
129	 * Calculate destination address based on kernel entrypoint
130	 */
131	dest = (vm_offset_t) ehdr.e_entry;
132	if (dest == 0) {
133	    printf("elf_loadmodule: not a kernel (maybe static binary?)\n");
134	    err = EPERM;
135	    goto oerr;
136	}
137	kernel = 1;
138
139	addr = dest;
140    } else {
141	err = EFTYPE;
142	goto oerr;
143    }
144
145    /*
146     * Ok, we think we should handle this.
147     */
148    mp = mod_allocmodule();
149    if (kernel)
150	mp->m_name = strdup(filename);		/* XXX should we prune the name? */
151    mp->m_type = strdup(kernel ? elf_kerneltype : elf_moduletype);
152
153    printf("%s at %p\n", filename, (void *) addr);
154
155    mp->m_size = elf_loadimage(mp, fd, &addr, &ehdr, kernel);
156    if (mp->m_size == 0)
157	goto ioerr;
158    mp->m_addr = addr;			/* save the aligned load address */
159
160#if 0
161    /* Handle KLD module data */
162    if (!kernel && ((err = elf_fixupkldmod(mp, &ehdr)) != 0))
163	goto oerr;
164#endif
165
166    /* save exec header as metadata */
167    mod_addmetadata(mp, MODINFOMD_ELFHDR, sizeof(ehdr), &ehdr);
168
169    /* Load OK, return module pointer */
170    *result = (struct loaded_module *)mp;
171    err = 0;
172    goto out;
173
174 ioerr:
175    err = EIO;
176 oerr:
177    mod_discard(mp);
178 out:
179    close(fd);
180    return(err);
181}
182
183/*
184 * With the file (fd) open on the image, and (ehdr) containing
185 * the Elf header, load the image at (addr)
186 */
187static int
188elf_loadimage(struct loaded_module *mp, int fd, vm_offset_t *addr,
189	      Elf_Ehdr *ehdr, int kernel)
190{
191    int 	i, j;
192    Elf_Phdr	*phdr;
193    Elf_Shdr	*shdr;
194    Elf_Ehdr	local_ehdr;
195    int		ret;
196    vm_offset_t firstaddr;
197    vm_offset_t lastaddr;
198    vm_offset_t	off;
199    void	*buf;
200    size_t	resid, chunk;
201    vm_offset_t	dest;
202    char	*secname;
203    vm_offset_t	shdrpos;
204    vm_offset_t	ssym, esym;
205
206    ret = 0;
207    firstaddr = lastaddr = 0;
208    if (kernel)
209#ifdef __i386__
210	off = 0x10000000;	/* -0xf0000000  - i386 relocates after locore */
211#else
212	off = 0;		/* alpha is direct mapped for kernels */
213#endif
214    else
215	off = *addr;		/* load relative to passed address */
216
217    phdr = malloc(ehdr->e_phnum * sizeof(*phdr));
218    if (phdr == NULL)
219	goto out;
220
221    if (lseek(fd, ehdr->e_phoff, SEEK_SET) == -1) {
222	printf("elf_loadexec: lseek for phdr failed\n");
223	goto out;
224    }
225    if (read(fd, phdr, ehdr->e_phnum * sizeof(*phdr)) !=
226	ehdr->e_phnum * sizeof(*phdr)) {
227	printf("elf_loadexec: cannot read program header\n");
228	goto out;
229    }
230
231    for (i = 0; i < ehdr->e_phnum; i++) {
232	/* We want to load PT_LOAD segments only.. */
233	if (phdr[i].p_type != PT_LOAD)
234	    continue;
235
236	printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx",
237	    (long)phdr[i].p_filesz, (long)phdr[i].p_offset,
238	    (long)(phdr[i].p_vaddr + off),
239	    (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1));
240
241	if (lseek(fd, phdr[i].p_offset, SEEK_SET) == -1) {
242	    printf("\nelf_loadexec: cannot seek\n");
243	    goto out;
244	}
245	if (archsw.arch_readin(fd, phdr[i].p_vaddr + off, phdr[i].p_filesz) !=
246	    phdr[i].p_filesz) {
247	    printf("\nelf_loadexec: archsw.readin failed\n");
248	    goto out;
249	}
250	/* clear space from oversized segments; eg: bss */
251	if (phdr[i].p_filesz < phdr[i].p_memsz) {
252	    printf(" (bss: 0x%lx-0x%lx)",
253		(long)(phdr[i].p_vaddr + off + phdr[i].p_filesz),
254		(long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1));
255
256	    /* no archsw.arch_bzero */
257	    buf = malloc(PAGE_SIZE);
258	    bzero(buf, PAGE_SIZE);
259	    resid = phdr[i].p_memsz - phdr[i].p_filesz;
260	    dest = phdr[i].p_vaddr + off + phdr[i].p_filesz;
261	    while (resid > 0) {
262		chunk = min(PAGE_SIZE, resid);
263		archsw.arch_copyin(buf, dest, chunk);
264		resid -= chunk;
265		dest += chunk;
266	    }
267	    free(buf);
268	}
269	printf("\n");
270
271	if (firstaddr < (phdr[i].p_vaddr + off))
272	    firstaddr = phdr[i].p_vaddr + off;
273	if (lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz))
274	    lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz;
275    }
276
277    /*
278     * Now grab the symbol tables.  This isn't easy if we're reading a
279     * .gz file.  I think the rule is going to have to be that you must
280     * strip a file to remove symbols before gzipping it so that we do not
281     * try to lseek() on it.  The layout is a bit wierd, but it's what
282     * the NetBSD-derived ddb/db_elf.c wants.
283     */
284    lastaddr = roundup(lastaddr, sizeof(long));
285    chunk = ehdr->e_shnum * ehdr->e_shentsize;
286    shdr = malloc(chunk);
287    if (shdr == NULL)
288	goto nosyms;
289    ssym = lastaddr;
290    printf("Symbols: ELF Ehdr @ 0x%x; ", lastaddr);
291    lastaddr += sizeof(*ehdr);
292    lastaddr = roundup(lastaddr, sizeof(long));
293    /* Copy out executable header modified for base offsets */
294    local_ehdr = *ehdr;
295    local_ehdr.e_phoff = 0;
296    local_ehdr.e_phentsize = 0;
297    local_ehdr.e_phnum = 0;
298    local_ehdr.e_shoff = lastaddr - ssym;
299    archsw.arch_copyin(&local_ehdr, ssym, sizeof(*ehdr));
300    if (lseek(fd, ehdr->e_shoff, SEEK_SET) == -1) {
301	printf("elf_loadimage: cannot lseek() to section headers\n");
302	lastaddr = ssym;	/* wind back */
303	ssym = 0;
304	goto nosyms;
305    }
306    if (read(fd, shdr, chunk) != chunk) {
307	printf("elf_loadimage: read section headers failed\n");
308	lastaddr = ssym;	/* wind back */
309	ssym = 0;
310	goto nosyms;
311    }
312    shdrpos = lastaddr;
313    printf("Section table: 0x%x@0x%x\n", chunk, shdrpos);
314    lastaddr += chunk;
315    lastaddr = roundup(lastaddr, sizeof(long));
316    for (i = 0; i < ehdr->e_shnum; i++) {
317	switch(shdr[i].sh_type) {
318	    /*
319	     * These are the symbol tables.  Their names are relative to
320	     * an arbitary string table.
321	     */
322	    case SHT_SYMTAB:		/* Symbol table */
323		secname = "symtab";
324		break;
325	    case SHT_DYNSYM:		/* Dynamic linking symbol table */
326		secname = "dynsym";
327		break;
328	    /*
329	     * And here are the string tables.  These can be referred to from
330	     * a number of sources, including the dynsym, the section table
331	     * names itself, etc.
332	     */
333	    case SHT_STRTAB:		/* String table */
334		secname = "strtab";
335		break;
336	    default:			/* Skip it */
337		continue;
338	}
339	for (j = 0; j < ehdr->e_phnum; j++) {
340	    if (phdr[j].p_type != PT_LOAD)
341		continue;
342	    if (shdr[i].sh_offset >= phdr[j].p_offset &&
343		(shdr[i].sh_offset + shdr[i].sh_size <=
344		 phdr[j].p_offset + phdr[j].p_filesz)) {
345		shdr[i].sh_offset = 0;
346		shdr[i].sh_size = 0;
347		break;
348	    }
349	}
350	if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0)
351	    continue;		/* alread loaded in a PT_LOAD above */
352
353	printf("%s: 0x%x@0x%x -> 0x%x-0x%x\n", secname,
354	    shdr[i].sh_size, shdr[i].sh_offset,
355	    lastaddr, lastaddr + shdr[i].sh_size);
356
357	if (lseek(fd, shdr[i].sh_offset, SEEK_SET) == -1) {
358	    printf("\nelf_loadimage: could not seek for symbols - skipped!\n");
359	    shdr[i].sh_offset = 0;
360	    shdr[i].sh_size = 0;
361	    continue;
362	}
363	if (archsw.arch_readin(fd, lastaddr, shdr[i].sh_size) !=
364	    shdr[i].sh_size) {
365	    printf("\nelf_loadimage: could not read symbols - skipped!\n");
366	    shdr[i].sh_offset = 0;
367	    shdr[i].sh_size = 0;
368	    continue;
369	}
370	/* Reset offsets relative to ssym */
371	shdr[i].sh_offset = lastaddr - ssym;
372	lastaddr += shdr[i].sh_size;
373	lastaddr = roundup(lastaddr, sizeof(long));
374    }
375    archsw.arch_copyin(shdr, lastaddr, sizeof(*ehdr));
376    esym = lastaddr;
377
378    mod_addmetadata(mp, MODINFOMD_ELFSSYM, sizeof(ssym), &ssym);
379    mod_addmetadata(mp, MODINFOMD_ELFESYM, sizeof(esym), &esym);
380
381nosyms:
382
383    ret = lastaddr - firstaddr;
384    *addr = firstaddr;
385out:
386    if (phdr)
387	free(phdr);
388    return ret;
389}
390