Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / boot / common / load_elf.c
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: src/sys/boot/common/load_elf.c,v 1.13.2.1 2000/12/28 13:12:35 ps Exp $
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
43 static int      elf_loadimage(struct loaded_module *mp, int fd, vm_offset_t loadaddr, Elf_Ehdr *ehdr, int kernel, caddr_t firstpage, int firstlen);
44
45 char    *elf_kerneltype = "elf kernel";
46 char    *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  */
53 int
54 elf_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  */
199 static int
200 elf_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     ssize_t     result;
212     vm_offset_t dest;
213     vm_offset_t ssym, esym;
214     Elf_Dyn     *dp;
215     int         ndp;
216     char        *s;
217     char        *strtab;
218     size_t      strsz;
219     int         symstrindex;
220     int         symtabindex;
221     long        size;
222     u_int       fpcopy;
223
224     dp = NULL;
225     shdr = NULL;
226     ret = 0;
227     firstaddr = lastaddr = 0;
228     if (kernel) {
229 #ifdef __i386__
230         off = - (off & 0xff000000u);    /* i386 relocates after locore */
231 #else
232         off = 0;                /* alpha is direct mapped for kernels */
233 #endif
234     }
235
236     if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(*phdr)) > firstlen) {
237         printf("elf_loadimage: program header not within first page\n");
238         goto out;
239     }
240     phdr = (Elf_Phdr *)(firstpage + ehdr->e_phoff);
241
242     for (i = 0; i < ehdr->e_phnum; i++) {
243         /* We want to load PT_LOAD segments only.. */
244         if (phdr[i].p_type != PT_LOAD)
245             continue;
246
247 #ifdef ELF_VERBOSE
248         printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx",
249             (long)phdr[i].p_filesz, (long)phdr[i].p_offset,
250             (long)(phdr[i].p_vaddr + off),
251             (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1));
252 #else
253         if ((phdr[i].p_flags & PF_W) == 0) {
254             printf("text=0x%lx ", (long)phdr[i].p_filesz);
255         } else {
256             printf("data=0x%lx", (long)phdr[i].p_filesz);
257             if (phdr[i].p_filesz < phdr[i].p_memsz)
258                 printf("+0x%lx", (long)(phdr[i].p_memsz -phdr[i].p_filesz));
259             printf(" ");
260         }
261 #endif
262         fpcopy = 0;
263         if (firstlen > phdr[i].p_offset) {
264             fpcopy = firstlen - phdr[i].p_offset;
265             archsw.arch_copyin(firstpage + phdr[i].p_offset,
266                                phdr[i].p_vaddr + off, fpcopy);
267         }
268         if (phdr[i].p_filesz > fpcopy) {
269             if (lseek(fd, phdr[i].p_offset + fpcopy, SEEK_SET) == -1) {
270                 printf("\nelf_loadexec: cannot seek\n");
271                 goto out;
272             }
273             if (archsw.arch_readin(fd, phdr[i].p_vaddr + off + fpcopy,
274                 phdr[i].p_filesz - fpcopy) != phdr[i].p_filesz - fpcopy) {
275                 printf("\nelf_loadexec: archsw.readin failed\n");
276                 goto out;
277             }
278         }
279         /* clear space from oversized segments; eg: bss */
280         if (phdr[i].p_filesz < phdr[i].p_memsz) {
281 #ifdef ELF_VERBOSE
282             printf(" (bss: 0x%lx-0x%lx)",
283                 (long)(phdr[i].p_vaddr + off + phdr[i].p_filesz),
284                 (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1));
285 #endif
286
287             /* no archsw.arch_bzero */
288             buf = malloc(PAGE_SIZE);
289             bzero(buf, PAGE_SIZE);
290             resid = phdr[i].p_memsz - phdr[i].p_filesz;
291             dest = phdr[i].p_vaddr + off + phdr[i].p_filesz;
292             while (resid > 0) {
293                 chunk = min(PAGE_SIZE, resid);
294                 archsw.arch_copyin(buf, dest, chunk);
295                 resid -= chunk;
296                 dest += chunk;
297             }
298             free(buf);
299         }
300 #ifdef ELF_VERBOSE
301         printf("\n");
302 #endif
303
304         if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off))
305             firstaddr = phdr[i].p_vaddr + off;
306         if (lastaddr == 0 || lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz))
307             lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz;
308     }
309     lastaddr = roundup(lastaddr, sizeof(long));
310
311     /*
312      * Now grab the symbol tables.  This isn't easy if we're reading a
313      * .gz file.  I think the rule is going to have to be that you must
314      * strip a file to remove symbols before gzipping it so that we do not
315      * try to lseek() on it.
316      */
317     chunk = ehdr->e_shnum * ehdr->e_shentsize;
318     if (chunk == 0 || ehdr->e_shoff == 0)
319         goto nosyms;
320     shdr = malloc(chunk);
321     if (shdr == NULL)
322         goto nosyms;
323     if (lseek(fd, ehdr->e_shoff, SEEK_SET) == -1) {
324         printf("\nelf_loadimage: cannot lseek() to section headers");
325         goto nosyms;
326     }
327     if (read(fd, shdr, chunk) != chunk) {
328         printf("\nelf_loadimage: read section headers failed");
329         goto nosyms;
330     }
331     symtabindex = -1;
332     symstrindex = -1;
333     for (i = 0; i < ehdr->e_shnum; i++) {
334         if (shdr[i].sh_type != SHT_SYMTAB)
335             continue;
336         for (j = 0; j < ehdr->e_phnum; j++) {
337             if (phdr[j].p_type != PT_LOAD)
338                 continue;
339             if (shdr[i].sh_offset >= phdr[j].p_offset &&
340                 (shdr[i].sh_offset + shdr[i].sh_size <=
341                  phdr[j].p_offset + phdr[j].p_filesz)) {
342                 shdr[i].sh_offset = 0;
343                 shdr[i].sh_size = 0;
344                 break;
345             }
346         }
347         if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0)
348             continue;           /* alread loaded in a PT_LOAD above */
349         /* Save it for loading below */
350         symtabindex = i;
351         symstrindex = shdr[i].sh_link;
352     }
353     if (symtabindex < 0 || symstrindex < 0)
354         goto nosyms;
355
356     /* Ok, committed to a load. */
357 #ifndef ELF_VERBOSE
358     printf("syms=[");
359 #endif
360     ssym = lastaddr;
361     for (i = symtabindex; i >= 0; i = symstrindex) {
362 #ifdef ELF_VERBOSE
363         char    *secname;
364
365         switch(shdr[i].sh_type) {
366             case SHT_SYMTAB:            /* Symbol table */
367                 secname = "symtab";
368                 break;
369             case SHT_STRTAB:            /* String table */
370                 secname = "strtab";
371                 break;
372             default:
373                 secname = "WHOA!!";
374                 break;
375         }
376 #endif
377
378         size = shdr[i].sh_size;
379         archsw.arch_copyin(&size, lastaddr, sizeof(size));
380         lastaddr += sizeof(long);
381
382 #ifdef ELF_VERBOSE
383         printf("\n%s: 0x%lx@0x%lx -> 0x%lx-0x%lx", secname,
384             shdr[i].sh_size, shdr[i].sh_offset,
385             lastaddr, lastaddr + shdr[i].sh_size);
386 #else
387         if (i == symstrindex)
388             printf("+");
389         printf("0x%lx+0x%lx", (long)sizeof(size), size);
390 #endif
391
392         if (lseek(fd, shdr[i].sh_offset, SEEK_SET) == -1) {
393             printf("\nelf_loadimage: could not seek for symbols - skipped!");
394             lastaddr = ssym;
395             ssym = 0;
396             goto nosyms;
397         }
398         if (archsw.arch_readin(fd, lastaddr, shdr[i].sh_size) !=
399             shdr[i].sh_size) {
400             printf("\nelf_loadimage: could not read symbols - skipped!");
401             lastaddr = ssym;
402             ssym = 0;
403             goto nosyms;
404         }
405         /* Reset offsets relative to ssym */
406         lastaddr += shdr[i].sh_size;
407         lastaddr = roundup(lastaddr, sizeof(long));
408         if (i == symtabindex)
409             symtabindex = -1;
410         else if (i == symstrindex)
411             symstrindex = -1;
412     }
413     esym = lastaddr;
414 #ifndef ELF_VERBOSE
415     printf("]");
416 #endif
417
418     mod_addmetadata(mp, MODINFOMD_SSYM, sizeof(ssym), &ssym);
419     mod_addmetadata(mp, MODINFOMD_ESYM, sizeof(esym), &esym);
420
421 nosyms:
422     printf("\n");
423
424     ret = lastaddr - firstaddr;
425     mp->m_addr = firstaddr;
426
427     for (i = 0; i < ehdr->e_phnum; i++) {
428         if (phdr[i].p_type == PT_DYNAMIC) {
429             dp = (Elf_Dyn *)(phdr[i].p_vaddr);
430             mod_addmetadata(mp, MODINFOMD_DYNAMIC, sizeof(dp), &dp);
431             dp = NULL;
432             break;
433         }
434     }
435
436     if (kernel)         /* kernel must not depend on anything */
437         goto out;
438
439     ndp = 0;
440     for (i = 0; i < ehdr->e_phnum; i++) {
441         if (phdr[i].p_type == PT_DYNAMIC) {
442             ndp = phdr[i].p_filesz / sizeof(Elf_Dyn);
443             dp = malloc(phdr[i].p_filesz);
444             archsw.arch_copyout(phdr[i].p_vaddr + off, dp, phdr[i].p_filesz);
445         }
446     }
447     if (dp == NULL || ndp == 0)
448         goto out;
449     strtab = NULL;
450     strsz = 0;
451     for (i = 0; i < ndp; i++) {
452         if (dp[i].d_tag == NULL)
453             break;
454         switch (dp[i].d_tag) {
455         case DT_STRTAB:
456             strtab = (char *)(dp[i].d_un.d_ptr + off);
457             break;
458         case DT_STRSZ:
459             strsz = dp[i].d_un.d_val;
460             break;
461         default:
462             break;
463         }
464     }
465     if (strtab == NULL || strsz == 0)
466         goto out;
467
468     for (i = 0; i < ndp; i++) {
469         if (dp[i].d_tag == NULL)
470             break;
471         if (dp[i].d_tag != DT_NEEDED)
472             continue;
473         j = dp[i].d_un.d_ptr;
474         if (j < 1 || j > (strsz - 2))
475             continue;   /* bad symbol name index */
476         s = strdupout((vm_offset_t)&strtab[j]);
477         mod_addmetadata(mp, MODINFOMD_DEPLIST, strlen(s) + 1, s);
478         free(s);
479     }
480
481 out:
482     if (dp)
483         free(dp);
484     if (shdr)
485         free(shdr);
486     return ret;
487 }