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