load_elf.c revision 40327
139830Speter/*- 239830Speter * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 339830Speter * Copyright (c) 1998 Peter Wemm <peter@freebsd.org> 439830Speter * All rights reserved. 539830Speter * 639830Speter * Redistribution and use in source and binary forms, with or without 739830Speter * modification, are permitted provided that the following conditions 839830Speter * are met: 939830Speter * 1. Redistributions of source code must retain the above copyright 1039830Speter * notice, this list of conditions and the following disclaimer. 1139830Speter * 2. Redistributions in binary form must reproduce the above copyright 1239830Speter * notice, this list of conditions and the following disclaimer in the 1339830Speter * documentation and/or other materials provided with the distribution. 1439830Speter * 1539830Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1639830Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1739830Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1839830Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1939830Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2039830Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2139830Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2239830Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2339830Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2439830Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2539830Speter * SUCH DAMAGE. 2639830Speter * 2740327Speter * $Id: load_elf.c,v 1.5 1998/10/13 09:25:27 peter Exp $ 2839830Speter */ 2939830Speter 3039830Speter#include <sys/param.h> 3139830Speter#include <sys/exec.h> 3239830Speter#include <sys/reboot.h> 3340143Speter#include <sys/linker.h> 3439830Speter#include <string.h> 3539830Speter#include <machine/bootinfo.h> 3639830Speter#include <machine/elf.h> 3739830Speter#include <stand.h> 3839830Speter#define FREEBSD_ELF 3939830Speter#include <link.h> 4039830Speter 4139830Speter#include "bootstrap.h" 4239830Speter 4340143Speterstatic int elf_loadimage(struct loaded_module *mp, int fd, vm_offset_t loadaddr, Elf_Ehdr *ehdr, int kernel); 4439830Speter 4539830Speterchar *elf_kerneltype = "elf kernel"; 4639830Speterchar *elf_moduletype = "elf module"; 4739830Speter 4839830Speter/* 4939830Speter * Attempt to load the file (file) as an ELF module. It will be stored at 5039830Speter * (dest), and a pointer to a module structure describing the loaded object 5139830Speter * will be saved in (result). 5239830Speter */ 5339830Speterint 5439830Speterelf_loadmodule(char *filename, vm_offset_t dest, struct loaded_module **result) 5539830Speter{ 5639830Speter struct loaded_module *mp, *kmp; 5739830Speter Elf_Ehdr ehdr; 5839830Speter int fd; 5939830Speter int err, kernel; 6039830Speter u_int pad; 6140143Speter char *s; 6239830Speter 6339830Speter mp = NULL; 6439830Speter 6539830Speter /* 6639830Speter * Open the image, read and validate the ELF header 6739830Speter */ 6839830Speter if (filename == NULL) /* can't handle nameless */ 6939830Speter return(EFTYPE); 7039830Speter if ((fd = open(filename, O_RDONLY)) == -1) 7139830Speter return(errno); 7239830Speter if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) { 7339830Speter err = EFTYPE; /* could be EIO, but may be small file */ 7439830Speter goto oerr; 7539830Speter } 7639830Speter 7739830Speter /* Is it ELF? */ 7839830Speter if (!IS_ELF(ehdr)) { 7939830Speter err = EFTYPE; 8039830Speter goto oerr; 8139830Speter } 8239830Speter if (ehdr.e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ 8339830Speter ehdr.e_ident[EI_DATA] != ELF_TARG_DATA || 8439830Speter ehdr.e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ 8539830Speter ehdr.e_version != EV_CURRENT || 8639830Speter ehdr.e_machine != ELF_TARG_MACH) { /* Machine ? */ 8739830Speter err = EFTYPE; 8839830Speter goto oerr; 8939830Speter } 9039830Speter 9139830Speter 9239830Speter /* 9339830Speter * Check to see what sort of module we are. 9439830Speter */ 9539830Speter kmp = mod_findmodule(NULL, NULL); 9639830Speter if (ehdr.e_type == ET_DYN) { 9739830Speter /* Looks like a kld module */ 9839830Speter if (kmp == NULL) { 9939830Speter printf("elf_loadmodule: can't load module before kernel\n"); 10039830Speter err = EPERM; 10139830Speter goto oerr; 10239830Speter } 10339830Speter if (strcmp(elf_kerneltype, kmp->m_type)) { 10439830Speter printf("elf_loadmodule: can't load module with kernel type '%s'\n", kmp->m_type); 10539830Speter err = EPERM; 10639830Speter goto oerr; 10739830Speter } 10839830Speter /* Looks OK, got ahead */ 10939830Speter kernel = 0; 11039830Speter 11139830Speter /* Page-align the load address */ 11240143Speter pad = (u_int)dest & PAGE_MASK; 11339830Speter if (pad != 0) { 11439830Speter pad = PAGE_SIZE - pad; 11540143Speter dest += pad; 11639830Speter } 11739830Speter } else if (ehdr.e_type == ET_EXEC) { 11839830Speter /* Looks like a kernel */ 11939830Speter if (kmp != NULL) { 12039830Speter printf("elf_loadmodule: kernel already loaded\n"); 12139830Speter err = EPERM; 12239830Speter goto oerr; 12339830Speter } 12439830Speter /* 12539830Speter * Calculate destination address based on kernel entrypoint 12639830Speter */ 12739830Speter dest = (vm_offset_t) ehdr.e_entry; 12839830Speter if (dest == 0) { 12939830Speter printf("elf_loadmodule: not a kernel (maybe static binary?)\n"); 13039830Speter err = EPERM; 13139830Speter goto oerr; 13239830Speter } 13339830Speter kernel = 1; 13439830Speter 13539830Speter } else { 13639830Speter err = EFTYPE; 13739830Speter goto oerr; 13839830Speter } 13939830Speter 14039830Speter /* 14139830Speter * Ok, we think we should handle this. 14239830Speter */ 14339830Speter mp = mod_allocmodule(); 14440143Speter if (mp == NULL) { 14540143Speter printf("elf_loadmodule: cannot allocate module info\n"); 14640143Speter err = EPERM; 14740143Speter goto out; 14840143Speter } 14939830Speter if (kernel) 15040143Speter setenv("kernelname", filename, 1); 15140143Speter s = strrchr(filename, '/'); 15240143Speter if (s) 15340143Speter mp->m_name = strdup(s + 1); 15440143Speter else 15540143Speter mp->m_name = strdup(filename); 15639830Speter mp->m_type = strdup(kernel ? elf_kerneltype : elf_moduletype); 15739830Speter 15840291Speter#ifdef ELF_VERBOSE 15940254Speter if (kernel) 16040254Speter printf("%s entry at %p\n", filename, (void *) dest); 16140327Speter#else 16240327Speter printf("%s ", filename); 16340291Speter#endif 16439830Speter 16540143Speter mp->m_size = elf_loadimage(mp, fd, dest, &ehdr, kernel); 16640143Speter if (mp->m_size == 0 || mp->m_addr == 0) 16739830Speter goto ioerr; 16839830Speter 16939830Speter /* save exec header as metadata */ 17039830Speter mod_addmetadata(mp, MODINFOMD_ELFHDR, sizeof(ehdr), &ehdr); 17139830Speter 17239830Speter /* Load OK, return module pointer */ 17339830Speter *result = (struct loaded_module *)mp; 17439830Speter err = 0; 17539830Speter goto out; 17639830Speter 17739830Speter ioerr: 17839830Speter err = EIO; 17939830Speter oerr: 18039830Speter mod_discard(mp); 18139830Speter out: 18239830Speter close(fd); 18339830Speter return(err); 18439830Speter} 18539830Speter 18639830Speter/* 18739830Speter * With the file (fd) open on the image, and (ehdr) containing 18840143Speter * the Elf header, load the image at (off) 18939830Speter */ 19039830Speterstatic int 19140143Speterelf_loadimage(struct loaded_module *mp, int fd, vm_offset_t off, 19239887Speter Elf_Ehdr *ehdr, int kernel) 19339830Speter{ 19439887Speter int i, j; 19539830Speter Elf_Phdr *phdr; 19639887Speter Elf_Shdr *shdr; 19739830Speter int ret; 19839830Speter vm_offset_t firstaddr; 19939830Speter vm_offset_t lastaddr; 20039830Speter void *buf; 20139887Speter size_t resid, chunk; 20239887Speter vm_offset_t dest; 20339887Speter vm_offset_t ssym, esym; 20440143Speter Elf_Dyn *dp; 20540143Speter int ndp; 20640143Speter int deplen; 20740143Speter char *depdata; 20840143Speter char *s; 20940143Speter int len; 21040143Speter char *strtab; 21140143Speter size_t strsz; 21240254Speter int symstrindex; 21340254Speter int symtabindex; 21440254Speter long size; 21539830Speter 21640143Speter dp = NULL; 21740143Speter shdr = NULL; 21839830Speter ret = 0; 21939830Speter firstaddr = lastaddr = 0; 22040143Speter if (kernel) { 22139830Speter#ifdef __i386__ 22239830Speter off = 0x10000000; /* -0xf0000000 - i386 relocates after locore */ 22339830Speter#else 22439830Speter off = 0; /* alpha is direct mapped for kernels */ 22539830Speter#endif 22640143Speter } 22739830Speter 22839830Speter phdr = malloc(ehdr->e_phnum * sizeof(*phdr)); 22939830Speter if (phdr == NULL) 23039830Speter goto out; 23139830Speter 23239830Speter if (lseek(fd, ehdr->e_phoff, SEEK_SET) == -1) { 23339830Speter printf("elf_loadexec: lseek for phdr failed\n"); 23439830Speter goto out; 23539830Speter } 23639830Speter if (read(fd, phdr, ehdr->e_phnum * sizeof(*phdr)) != 23739830Speter ehdr->e_phnum * sizeof(*phdr)) { 23839830Speter printf("elf_loadexec: cannot read program header\n"); 23939830Speter goto out; 24039830Speter } 24139830Speter 24239830Speter for (i = 0; i < ehdr->e_phnum; i++) { 24339830Speter /* We want to load PT_LOAD segments only.. */ 24439830Speter if (phdr[i].p_type != PT_LOAD) 24539830Speter continue; 24639830Speter 24740254Speter#ifdef ELF_VERBOSE 24839887Speter printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx", 24939830Speter (long)phdr[i].p_filesz, (long)phdr[i].p_offset, 25039830Speter (long)(phdr[i].p_vaddr + off), 25139887Speter (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); 25240254Speter#else 25340291Speter if ((phdr[i].p_flags & PF_W) == 0) { 25440291Speter printf("text=0x%lx ", (long)phdr[i].p_filesz); 25540291Speter } else { 25640291Speter printf("data=0x%lx", (long)phdr[i].p_filesz); 25740291Speter if (phdr[i].p_filesz < phdr[i].p_memsz) 25840291Speter printf("+0x%lx", (long)(phdr[i].p_memsz -phdr[i].p_filesz)); 25940291Speter printf(" "); 26040291Speter } 26140254Speter#endif 26239830Speter 26339830Speter if (lseek(fd, phdr[i].p_offset, SEEK_SET) == -1) { 26439887Speter printf("\nelf_loadexec: cannot seek\n"); 26539830Speter goto out; 26639830Speter } 26739830Speter if (archsw.arch_readin(fd, phdr[i].p_vaddr + off, phdr[i].p_filesz) != 26839830Speter phdr[i].p_filesz) { 26939887Speter printf("\nelf_loadexec: archsw.readin failed\n"); 27039830Speter goto out; 27139830Speter } 27239830Speter /* clear space from oversized segments; eg: bss */ 27339830Speter if (phdr[i].p_filesz < phdr[i].p_memsz) { 27440254Speter#ifdef ELF_VERBOSE 27539887Speter printf(" (bss: 0x%lx-0x%lx)", 27639830Speter (long)(phdr[i].p_vaddr + off + phdr[i].p_filesz), 27739830Speter (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); 27840254Speter#endif 27939830Speter 28039887Speter /* no archsw.arch_bzero */ 28139830Speter buf = malloc(PAGE_SIZE); 28239830Speter bzero(buf, PAGE_SIZE); 28339830Speter resid = phdr[i].p_memsz - phdr[i].p_filesz; 28439830Speter dest = phdr[i].p_vaddr + off + phdr[i].p_filesz; 28539830Speter while (resid > 0) { 28639830Speter chunk = min(PAGE_SIZE, resid); 28739830Speter archsw.arch_copyin(buf, dest, chunk); 28839830Speter resid -= chunk; 28939830Speter dest += chunk; 29039830Speter } 29139830Speter free(buf); 29239830Speter } 29340254Speter#ifdef ELF_VERBOSE 29439887Speter printf("\n"); 29540254Speter#endif 29639830Speter 29740143Speter if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off)) 29839830Speter firstaddr = phdr[i].p_vaddr + off; 29940143Speter if (lastaddr == 0 || lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz)) 30039830Speter lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz; 30139830Speter } 30240254Speter lastaddr = roundup(lastaddr, sizeof(long)); 30339830Speter 30439887Speter /* 30539887Speter * Now grab the symbol tables. This isn't easy if we're reading a 30639887Speter * .gz file. I think the rule is going to have to be that you must 30739887Speter * strip a file to remove symbols before gzipping it so that we do not 30840254Speter * try to lseek() on it. 30939887Speter */ 31039887Speter chunk = ehdr->e_shnum * ehdr->e_shentsize; 31140291Speter if (chunk == 0 || ehdr->e_shoff == 0) 31240291Speter goto nosyms; 31339887Speter shdr = malloc(chunk); 31439887Speter if (shdr == NULL) 31539887Speter goto nosyms; 31639887Speter if (lseek(fd, ehdr->e_shoff, SEEK_SET) == -1) { 31740254Speter printf("\nelf_loadimage: cannot lseek() to section headers\n"); 31839887Speter goto nosyms; 31939887Speter } 32039887Speter if (read(fd, shdr, chunk) != chunk) { 32140254Speter printf("\nelf_loadimage: read section headers failed\n"); 32239887Speter goto nosyms; 32339887Speter } 32440254Speter symtabindex = -1; 32540254Speter symstrindex = -1; 32639887Speter for (i = 0; i < ehdr->e_shnum; i++) { 32740254Speter if (shdr[i].sh_type != SHT_SYMTAB) 32840143Speter continue; 32939887Speter for (j = 0; j < ehdr->e_phnum; j++) { 33039887Speter if (phdr[j].p_type != PT_LOAD) 33139887Speter continue; 33239887Speter if (shdr[i].sh_offset >= phdr[j].p_offset && 33339887Speter (shdr[i].sh_offset + shdr[i].sh_size <= 33439887Speter phdr[j].p_offset + phdr[j].p_filesz)) { 33539887Speter shdr[i].sh_offset = 0; 33639887Speter shdr[i].sh_size = 0; 33739887Speter break; 33839887Speter } 33939887Speter } 34039887Speter if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0) 34139887Speter continue; /* alread loaded in a PT_LOAD above */ 34240254Speter /* Save it for loading below */ 34340254Speter symtabindex = i; 34440254Speter symstrindex = shdr[i].sh_link; 34540254Speter } 34640254Speter if (symtabindex < 0 || symstrindex < 0) 34740254Speter goto nosyms; 34839887Speter 34940254Speter /* Ok, committed to a load. */ 35040254Speter#ifndef ELF_VERBOSE 35140291Speter printf("syms=["); 35240254Speter#endif 35340254Speter ssym = lastaddr; 35440254Speter for (i = symtabindex; i >= 0; i = symstrindex) { 35540254Speter#ifdef ELF_VERBOSE 35640254Speter char *secname; 35740254Speter 35840254Speter switch(shdr[i].sh_type) { 35940254Speter case SHT_SYMTAB: /* Symbol table */ 36040254Speter secname = "symtab"; 36140254Speter break; 36240254Speter case SHT_STRTAB: /* String table */ 36340254Speter secname = "strtab"; 36440254Speter break; 36540254Speter default: 36640254Speter secname = "WHOA!!"; 36740254Speter break; 36840254Speter } 36940254Speter#endif 37040254Speter 37140254Speter size = shdr[i].sh_size; 37240254Speter archsw.arch_copyin(&size, lastaddr, sizeof(size)); 37340254Speter lastaddr += sizeof(long); 37440254Speter 37540254Speter#ifdef ELF_VERBOSE 37639887Speter printf("%s: 0x%x@0x%x -> 0x%x-0x%x\n", secname, 37739887Speter shdr[i].sh_size, shdr[i].sh_offset, 37839887Speter lastaddr, lastaddr + shdr[i].sh_size); 37940254Speter#else 38040291Speter if (i == symstrindex) 38140291Speter printf("+"); 38240254Speter printf("0x%x+0x%lx", sizeof(size), size); 38340254Speter#endif 38440254Speter 38539887Speter if (lseek(fd, shdr[i].sh_offset, SEEK_SET) == -1) { 38639887Speter printf("\nelf_loadimage: could not seek for symbols - skipped!\n"); 38740254Speter lastaddr = ssym; 38840254Speter ssym = 0; 38940254Speter goto nosyms; 39039887Speter } 39139887Speter if (archsw.arch_readin(fd, lastaddr, shdr[i].sh_size) != 39239887Speter shdr[i].sh_size) { 39339887Speter printf("\nelf_loadimage: could not read symbols - skipped!\n"); 39440254Speter lastaddr = ssym; 39540254Speter ssym = 0; 39640254Speter goto nosyms; 39739887Speter } 39839887Speter /* Reset offsets relative to ssym */ 39939887Speter lastaddr += shdr[i].sh_size; 40039887Speter lastaddr = roundup(lastaddr, sizeof(long)); 40140254Speter if (i == symtabindex) 40240254Speter symtabindex = -1; 40340254Speter else if (i == symstrindex) 40440254Speter symstrindex = -1; 40539887Speter } 40639887Speter esym = lastaddr; 40740254Speter#ifndef ELF_VERBOSE 40840254Speter printf("]\n"); 40940254Speter#endif 41039887Speter 41140143Speter mod_addmetadata(mp, MODINFOMD_SSYM, sizeof(ssym), &ssym); 41240143Speter mod_addmetadata(mp, MODINFOMD_ESYM, sizeof(esym), &esym); 41339887Speter 41439887Speternosyms: 41539887Speter 41639830Speter ret = lastaddr - firstaddr; 41740143Speter mp->m_addr = firstaddr; 41840143Speter 41940143Speter for (i = 0; i < ehdr->e_phnum; i++) { 42040143Speter if (phdr[i].p_type == PT_DYNAMIC) { 42140143Speter dp = (Elf_Dyn *)(phdr[i].p_vaddr); 42240143Speter mod_addmetadata(mp, MODINFOMD_DYNAMIC, sizeof(dp), &dp); 42340143Speter dp = NULL; 42440143Speter break; 42540143Speter } 42640143Speter } 42740143Speter 42840143Speter if (kernel) /* kernel must not depend on anything */ 42940143Speter goto out; 43040143Speter 43140143Speter ndp = 0; 43240143Speter for (i = 0; i < ehdr->e_phnum; i++) { 43340143Speter if (phdr[i].p_type == PT_DYNAMIC) { 43440143Speter ndp = phdr[i].p_filesz / sizeof(Elf_Dyn); 43540143Speter dp = malloc(phdr[i].p_filesz); 43640143Speter archsw.arch_copyout(phdr[i].p_vaddr + off, dp, phdr[i].p_filesz); 43740143Speter } 43840143Speter } 43940143Speter if (dp == NULL || ndp == 0) 44040143Speter goto out; 44140143Speter strtab = NULL; 44240143Speter strsz = 0; 44340143Speter deplen = 0; 44440143Speter for (i = 0; i < ndp; i++) { 44540143Speter if (dp[i].d_tag == NULL) 44640143Speter break; 44740143Speter switch (dp[i].d_tag) { 44840143Speter case DT_STRTAB: 44940143Speter strtab = (char *)(dp[i].d_un.d_ptr + off); 45040143Speter break; 45140143Speter case DT_STRSZ: 45240143Speter strsz = dp[i].d_un.d_val; 45340143Speter break; 45440143Speter default: 45540143Speter break; 45640143Speter } 45740143Speter } 45840143Speter if (strtab == NULL || strsz == 0) 45940143Speter goto out; 46040143Speter 46140143Speter deplen = 0; 46240143Speter for (i = 0; i < ndp; i++) { 46340143Speter if (dp[i].d_tag == NULL) 46440143Speter break; 46540143Speter switch (dp[i].d_tag) { 46640143Speter case DT_NEEDED: /* count size for dependency list */ 46740143Speter j = dp[i].d_un.d_ptr; 46840143Speter if (j < 1 || j > (strsz - 2)) 46940143Speter continue; /* bad symbol name index */ 47040143Speter deplen += strlenout((vm_offset_t)&strtab[j]) + 1; 47140143Speter break; 47240143Speter default: 47340143Speter break; 47440143Speter } 47540143Speter } 47640143Speter 47740143Speter if (deplen > 0) { 47840143Speter depdata = malloc(deplen); 47940143Speter if (depdata == NULL) 48040143Speter goto out; 48140143Speter s = depdata; 48240143Speter for (i = 0; i < ndp; i++) { 48340143Speter if (dp[i].d_tag == NULL) 48440143Speter break; 48540143Speter switch (dp[i].d_tag) { 48640143Speter case DT_NEEDED: /* dependency list */ 48740143Speter j = dp[i].d_un.d_ptr; 48840143Speter len = strlenout((vm_offset_t)&strtab[j]) + 1; 48940143Speter archsw.arch_copyout((vm_offset_t)&strtab[j], s, len); 49040143Speter s += len; 49140143Speter break; 49240143Speter default: 49340143Speter break; 49440143Speter } 49540143Speter } 49640143Speter if ((s - depdata) > 0) 49740143Speter mod_addmetadata(mp, MODINFOMD_DEPLIST, s - depdata, depdata); 49840143Speter free(depdata); 49940143Speter } 50040143Speter 50139830Speterout: 50240143Speter if (dp) 50340143Speter free(dp); 50440143Speter if (shdr) 50540143Speter free(shdr); 50639830Speter if (phdr) 50739830Speter free(phdr); 50839830Speter return ret; 50939830Speter} 510