load_elf.c revision 114937
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 * 2750477Speter * $FreeBSD: head/sys/boot/common/load_elf.c 114937 2003-05-12 05:48:09Z peter $ 2839830Speter */ 2939830Speter 3039830Speter#include <sys/param.h> 3139830Speter#include <sys/exec.h> 3240143Speter#include <sys/linker.h> 3359854Sbp#include <sys/module.h> 3439830Speter#include <string.h> 3539830Speter#include <machine/elf.h> 3639830Speter#include <stand.h> 3739830Speter#define FREEBSD_ELF 3839830Speter#include <link.h> 3939830Speter 4039830Speter#include "bootstrap.h" 4139830Speter 4259854Sbp#define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l) 4339830Speter 44114379Speter#if defined(__i386__) && __ELF_WORD_SIZE == 64 45114379Speter#undef ELF_TARG_CLASS 46114379Speter#undef ELF_TARG_MACH 47114379Speter#define ELF_TARG_CLASS ELFCLASS64 48114379Speter#define ELF_TARG_MACH EM_X86_64 49114379Speter#endif 5059854Sbp 5159854Sbptypedef struct elf_file { 5259854Sbp Elf_Phdr *ph; 5359854Sbp Elf_Ehdr *ehdr; 5459854Sbp Elf_Sym *symtab; 5593922Speter Elf_Hashelt *hashtab; 5693922Speter Elf_Hashelt nbuckets; 5793922Speter Elf_Hashelt nchains; 5893922Speter Elf_Hashelt *buckets; 5993922Speter Elf_Hashelt *chains; 60109616Sjake Elf_Rela *rela; 61109616Sjake size_t relasz; 6259854Sbp char *strtab; 6359854Sbp size_t strsz; 6459854Sbp int fd; 6559854Sbp caddr_t firstpage; 6664187Sjhb size_t firstlen; 6759854Sbp int kernel; 68114379Speter u_int64_t off; 6959854Sbp} *elf_file_t; 7059854Sbp 71114379Speterstatic int __elfN(loadimage)(struct preloaded_file *mp, elf_file_t ef, u_int64_t loadaddr); 72114379Speterstatic int __elfN(lookup_symbol)(struct preloaded_file *mp, elf_file_t ef, const char* name, Elf_Sym* sym); 73109616Sjake#ifdef __sparc__ 74114379Speterstatic void __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, 75109616Sjake void *p, void *val, size_t len); 76109616Sjake#endif 77114379Speterstatic int __elfN(parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef); 7864187Sjhbstatic char *fake_modname(const char *name); 7959854Sbp 80114379Speterconst char *__elfN(kerneltype) = "elf kernel"; 81114379Speterconst char *__elfN(moduletype) = "elf module"; 8239830Speter 8339830Speter/* 8439830Speter * Attempt to load the file (file) as an ELF module. It will be stored at 8539830Speter * (dest), and a pointer to a module structure describing the loaded object 8639830Speter * will be saved in (result). 8739830Speter */ 8839830Speterint 89114379Speter__elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result) 9039830Speter{ 9159854Sbp struct preloaded_file *fp, *kfp; 9259854Sbp struct elf_file ef; 9359854Sbp Elf_Ehdr *ehdr; 9459854Sbp int err; 9539830Speter u_int pad; 9664187Sjhb ssize_t bytes_read; 9739830Speter 9859854Sbp fp = NULL; 9959854Sbp bzero(&ef, sizeof(struct elf_file)); 10039830Speter 10139830Speter /* 10239830Speter * Open the image, read and validate the ELF header 10339830Speter */ 10439830Speter if (filename == NULL) /* can't handle nameless */ 10539830Speter return(EFTYPE); 10659854Sbp if ((ef.fd = open(filename, O_RDONLY)) == -1) 10739830Speter return(errno); 10859854Sbp ef.firstpage = malloc(PAGE_SIZE); 10959854Sbp if (ef.firstpage == NULL) { 11059854Sbp close(ef.fd); 11140465Speter return(ENOMEM); 11259854Sbp } 11364187Sjhb bytes_read = read(ef.fd, ef.firstpage, PAGE_SIZE); 11464187Sjhb ef.firstlen = (size_t)bytes_read; 11564187Sjhb if (bytes_read < 0 || ef.firstlen <= sizeof(Elf_Ehdr)) { 11639830Speter err = EFTYPE; /* could be EIO, but may be small file */ 11739830Speter goto oerr; 11839830Speter } 11959854Sbp ehdr = ef.ehdr = (Elf_Ehdr *)ef.firstpage; 12039830Speter 12139830Speter /* Is it ELF? */ 12240465Speter if (!IS_ELF(*ehdr)) { 12339830Speter err = EFTYPE; 12439830Speter goto oerr; 12539830Speter } 12640465Speter if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ 12740465Speter ehdr->e_ident[EI_DATA] != ELF_TARG_DATA || 12840465Speter ehdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ 12940465Speter ehdr->e_version != EV_CURRENT || 13040465Speter ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */ 13139830Speter err = EFTYPE; 13239830Speter goto oerr; 13339830Speter } 13439830Speter 13539830Speter 13639830Speter /* 13739830Speter * Check to see what sort of module we are. 13839830Speter */ 13959854Sbp kfp = file_findfile(NULL, NULL); 14040465Speter if (ehdr->e_type == ET_DYN) { 14139830Speter /* Looks like a kld module */ 14259854Sbp if (kfp == NULL) { 143114379Speter printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module before kernel\n"); 14439830Speter err = EPERM; 14539830Speter goto oerr; 14639830Speter } 147114379Speter if (strcmp(__elfN(kerneltype), kfp->f_type)) { 148114379Speter printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module with kernel type '%s'\n", kfp->f_type); 14939830Speter err = EPERM; 15039830Speter goto oerr; 15139830Speter } 15239830Speter /* Looks OK, got ahead */ 15359854Sbp ef.kernel = 0; 15439830Speter 15539830Speter /* Page-align the load address */ 15640143Speter pad = (u_int)dest & PAGE_MASK; 15739830Speter if (pad != 0) { 15839830Speter pad = PAGE_SIZE - pad; 15940143Speter dest += pad; 16039830Speter } 16140465Speter } else if (ehdr->e_type == ET_EXEC) { 16239830Speter /* Looks like a kernel */ 16359854Sbp if (kfp != NULL) { 164114379Speter printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: kernel already loaded\n"); 16539830Speter err = EPERM; 16639830Speter goto oerr; 16739830Speter } 16839830Speter /* 16939830Speter * Calculate destination address based on kernel entrypoint 17039830Speter */ 171114379Speter dest = ehdr->e_entry; 17239830Speter if (dest == 0) { 173114379Speter printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: not a kernel (maybe static binary?)\n"); 17439830Speter err = EPERM; 17539830Speter goto oerr; 17639830Speter } 17759854Sbp ef.kernel = 1; 17839830Speter 17939830Speter } else { 18039830Speter err = EFTYPE; 18139830Speter goto oerr; 18239830Speter } 18339830Speter 18439830Speter /* 18539830Speter * Ok, we think we should handle this. 18639830Speter */ 18759854Sbp fp = file_alloc(); 18859854Sbp if (fp == NULL) { 189114379Speter printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: cannot allocate module info\n"); 19040143Speter err = EPERM; 19140143Speter goto out; 19240143Speter } 19359854Sbp if (ef.kernel) 19440143Speter setenv("kernelname", filename, 1); 19583321Speter fp->f_name = strdup(filename); 196114379Speter fp->f_type = strdup(ef.kernel ? __elfN(kerneltype) : __elfN(moduletype)); 19739830Speter 19840291Speter#ifdef ELF_VERBOSE 19959854Sbp if (ef.kernel) 200114379Speter printf("%s entry at 0x%jx\n", filename, (uintmax_t)dest); 20140327Speter#else 20240327Speter printf("%s ", filename); 20340291Speter#endif 20439830Speter 205114379Speter fp->f_size = __elfN(loadimage)(fp, &ef, dest); 20659854Sbp if (fp->f_size == 0 || fp->f_addr == 0) 20739830Speter goto ioerr; 20839830Speter 20939830Speter /* save exec header as metadata */ 21059854Sbp file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof(*ehdr), ehdr); 21139830Speter 21239830Speter /* Load OK, return module pointer */ 21359854Sbp *result = (struct preloaded_file *)fp; 21439830Speter err = 0; 21539830Speter goto out; 21639830Speter 21739830Speter ioerr: 21839830Speter err = EIO; 21939830Speter oerr: 22059854Sbp file_discard(fp); 22139830Speter out: 22259854Sbp if (ef.firstpage) 22359854Sbp free(ef.firstpage); 22459854Sbp close(ef.fd); 22539830Speter return(err); 22639830Speter} 22739830Speter 22839830Speter/* 22939830Speter * With the file (fd) open on the image, and (ehdr) containing 23040143Speter * the Elf header, load the image at (off) 23139830Speter */ 23239830Speterstatic int 233114379Speter__elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, u_int64_t off) 23439830Speter{ 23564187Sjhb int i; 23664187Sjhb u_int j; 23759854Sbp Elf_Ehdr *ehdr; 23859854Sbp Elf_Phdr *phdr, *php; 23939887Speter Elf_Shdr *shdr; 24039830Speter int ret; 24139830Speter vm_offset_t firstaddr; 24239830Speter vm_offset_t lastaddr; 24339830Speter void *buf; 24439887Speter size_t resid, chunk; 24564187Sjhb ssize_t result; 24639887Speter vm_offset_t dest; 247114379Speter Elf_Addr ssym, esym; 24840143Speter Elf_Dyn *dp; 249114379Speter Elf_Addr adp; 25040143Speter int ndp; 25140254Speter int symstrindex; 25240254Speter int symtabindex; 253114379Speter Elf_Size size; 25464187Sjhb u_int fpcopy; 25539830Speter 25640143Speter dp = NULL; 25740143Speter shdr = NULL; 25839830Speter ret = 0; 25939830Speter firstaddr = lastaddr = 0; 26059854Sbp ehdr = ef->ehdr; 26159854Sbp if (ef->kernel) { 26239830Speter#ifdef __i386__ 263114379Speter#if __ELF_WORD_SIZE == 64 264114379Speter off = - (off & 0xffffffffff000000ull);/* x86_64 relocates after locore */ 265114379Speter#else 26644069Stegge off = - (off & 0xff000000u); /* i386 relocates after locore */ 267114379Speter#endif 26839830Speter#else 26939830Speter off = 0; /* alpha is direct mapped for kernels */ 27039830Speter#endif 27140143Speter } 27259854Sbp ef->off = off; 27339830Speter 27459854Sbp if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(*phdr)) > ef->firstlen) { 275114379Speter printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: program header not within first page\n"); 27640465Speter goto out; 27740465Speter } 27859854Sbp phdr = (Elf_Phdr *)(ef->firstpage + ehdr->e_phoff); 27940465Speter 28039830Speter for (i = 0; i < ehdr->e_phnum; i++) { 28139830Speter /* We want to load PT_LOAD segments only.. */ 28239830Speter if (phdr[i].p_type != PT_LOAD) 28339830Speter continue; 28439830Speter 28540254Speter#ifdef ELF_VERBOSE 28639887Speter printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx", 28739830Speter (long)phdr[i].p_filesz, (long)phdr[i].p_offset, 28839830Speter (long)(phdr[i].p_vaddr + off), 28939887Speter (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); 29040254Speter#else 29140291Speter if ((phdr[i].p_flags & PF_W) == 0) { 29240291Speter printf("text=0x%lx ", (long)phdr[i].p_filesz); 29340291Speter } else { 29440291Speter printf("data=0x%lx", (long)phdr[i].p_filesz); 29540291Speter if (phdr[i].p_filesz < phdr[i].p_memsz) 29640291Speter printf("+0x%lx", (long)(phdr[i].p_memsz -phdr[i].p_filesz)); 29740291Speter printf(" "); 29840291Speter } 29940254Speter#endif 30040465Speter fpcopy = 0; 30159854Sbp if (ef->firstlen > phdr[i].p_offset) { 30259854Sbp fpcopy = ef->firstlen - phdr[i].p_offset; 30359854Sbp archsw.arch_copyin(ef->firstpage + phdr[i].p_offset, 30440465Speter phdr[i].p_vaddr + off, fpcopy); 30539830Speter } 30640465Speter if (phdr[i].p_filesz > fpcopy) { 30764187Sjhb if (lseek(ef->fd, (off_t)(phdr[i].p_offset + fpcopy), 30864187Sjhb SEEK_SET) == -1) { 309114379Speter printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadexec: cannot seek\n"); 31040465Speter goto out; 31140465Speter } 31259854Sbp if (archsw.arch_readin(ef->fd, phdr[i].p_vaddr + off + fpcopy, 31364187Sjhb phdr[i].p_filesz - fpcopy) != (ssize_t)(phdr[i].p_filesz - fpcopy)) { 314114379Speter printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadexec: archsw.readin failed\n"); 31540465Speter goto out; 31640465Speter } 31739830Speter } 31839830Speter /* clear space from oversized segments; eg: bss */ 31939830Speter if (phdr[i].p_filesz < phdr[i].p_memsz) { 32040254Speter#ifdef ELF_VERBOSE 32139887Speter printf(" (bss: 0x%lx-0x%lx)", 32239830Speter (long)(phdr[i].p_vaddr + off + phdr[i].p_filesz), 32339830Speter (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); 32440254Speter#endif 32539830Speter 32639887Speter /* no archsw.arch_bzero */ 32739830Speter buf = malloc(PAGE_SIZE); 32859854Sbp if (buf == NULL) { 329114379Speter printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: malloc() failed\n"); 33059854Sbp goto out; 33159854Sbp } 33239830Speter bzero(buf, PAGE_SIZE); 33339830Speter resid = phdr[i].p_memsz - phdr[i].p_filesz; 33439830Speter dest = phdr[i].p_vaddr + off + phdr[i].p_filesz; 33539830Speter while (resid > 0) { 33639830Speter chunk = min(PAGE_SIZE, resid); 33739830Speter archsw.arch_copyin(buf, dest, chunk); 33839830Speter resid -= chunk; 33939830Speter dest += chunk; 34039830Speter } 34139830Speter free(buf); 34239830Speter } 34340254Speter#ifdef ELF_VERBOSE 34439887Speter printf("\n"); 34540254Speter#endif 34639830Speter 34740143Speter if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off)) 34839830Speter firstaddr = phdr[i].p_vaddr + off; 34940143Speter if (lastaddr == 0 || lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz)) 35039830Speter lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz; 35139830Speter } 35240254Speter lastaddr = roundup(lastaddr, sizeof(long)); 35339830Speter 35439887Speter /* 35539887Speter * Now grab the symbol tables. This isn't easy if we're reading a 35639887Speter * .gz file. I think the rule is going to have to be that you must 35739887Speter * strip a file to remove symbols before gzipping it so that we do not 35840254Speter * try to lseek() on it. 35939887Speter */ 36039887Speter chunk = ehdr->e_shnum * ehdr->e_shentsize; 36140291Speter if (chunk == 0 || ehdr->e_shoff == 0) 36240291Speter goto nosyms; 36339887Speter shdr = malloc(chunk); 36439887Speter if (shdr == NULL) 36539887Speter goto nosyms; 36664187Sjhb if (lseek(ef->fd, (off_t)ehdr->e_shoff, SEEK_SET) == -1) { 367114379Speter printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: cannot lseek() to section headers"); 36839887Speter goto nosyms; 36939887Speter } 37064187Sjhb result = read(ef->fd, shdr, chunk); 37164187Sjhb if (result < 0 || (size_t)result != chunk) { 372114379Speter printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: read section headers failed"); 37339887Speter goto nosyms; 37439887Speter } 37540254Speter symtabindex = -1; 37640254Speter symstrindex = -1; 37739887Speter for (i = 0; i < ehdr->e_shnum; i++) { 37840254Speter if (shdr[i].sh_type != SHT_SYMTAB) 37940143Speter continue; 38039887Speter for (j = 0; j < ehdr->e_phnum; j++) { 38139887Speter if (phdr[j].p_type != PT_LOAD) 38239887Speter continue; 38339887Speter if (shdr[i].sh_offset >= phdr[j].p_offset && 38439887Speter (shdr[i].sh_offset + shdr[i].sh_size <= 38539887Speter phdr[j].p_offset + phdr[j].p_filesz)) { 38639887Speter shdr[i].sh_offset = 0; 38739887Speter shdr[i].sh_size = 0; 38839887Speter break; 38939887Speter } 39039887Speter } 39139887Speter if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0) 39239887Speter continue; /* alread loaded in a PT_LOAD above */ 39340254Speter /* Save it for loading below */ 39440254Speter symtabindex = i; 39540254Speter symstrindex = shdr[i].sh_link; 39640254Speter } 39740254Speter if (symtabindex < 0 || symstrindex < 0) 39840254Speter goto nosyms; 39939887Speter 40040254Speter /* Ok, committed to a load. */ 40140254Speter#ifndef ELF_VERBOSE 40240291Speter printf("syms=["); 40340254Speter#endif 40440254Speter ssym = lastaddr; 40540254Speter for (i = symtabindex; i >= 0; i = symstrindex) { 40640254Speter#ifdef ELF_VERBOSE 40740254Speter char *secname; 40840254Speter 40940254Speter switch(shdr[i].sh_type) { 41040254Speter case SHT_SYMTAB: /* Symbol table */ 41140254Speter secname = "symtab"; 41240254Speter break; 41340254Speter case SHT_STRTAB: /* String table */ 41440254Speter secname = "strtab"; 41540254Speter break; 41640254Speter default: 41740254Speter secname = "WHOA!!"; 41840254Speter break; 41940254Speter } 42040254Speter#endif 42140254Speter 42240254Speter size = shdr[i].sh_size; 42340254Speter archsw.arch_copyin(&size, lastaddr, sizeof(size)); 424114379Speter lastaddr += sizeof(size); 42540254Speter 42640254Speter#ifdef ELF_VERBOSE 42742288Speter printf("\n%s: 0x%lx@0x%lx -> 0x%lx-0x%lx", secname, 42839887Speter shdr[i].sh_size, shdr[i].sh_offset, 42939887Speter lastaddr, lastaddr + shdr[i].sh_size); 43040254Speter#else 43140291Speter if (i == symstrindex) 43240291Speter printf("+"); 433114379Speter printf("0x%lx+0x%lx", (long)sizeof(size), (long)size); 43440254Speter#endif 43540254Speter 43664187Sjhb if (lseek(ef->fd, (off_t)shdr[i].sh_offset, SEEK_SET) == -1) { 437114379Speter printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not seek for symbols - skipped!"); 43840254Speter lastaddr = ssym; 43940254Speter ssym = 0; 44040254Speter goto nosyms; 44139887Speter } 44264187Sjhb result = archsw.arch_readin(ef->fd, lastaddr, shdr[i].sh_size); 44364187Sjhb if (result < 0 || (size_t)result != shdr[i].sh_size) { 444114379Speter printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not read symbols - skipped!"); 44540254Speter lastaddr = ssym; 44640254Speter ssym = 0; 44740254Speter goto nosyms; 44839887Speter } 44939887Speter /* Reset offsets relative to ssym */ 45039887Speter lastaddr += shdr[i].sh_size; 451114379Speter lastaddr = roundup(lastaddr, sizeof(size)); 45240254Speter if (i == symtabindex) 45340254Speter symtabindex = -1; 45440254Speter else if (i == symstrindex) 45540254Speter symstrindex = -1; 45639887Speter } 45739887Speter esym = lastaddr; 45840254Speter#ifndef ELF_VERBOSE 45942288Speter printf("]"); 46040254Speter#endif 46139887Speter 46259854Sbp file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym); 46359854Sbp file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym); 46439887Speter 46539887Speternosyms: 46642288Speter printf("\n"); 46739887Speter 46839830Speter ret = lastaddr - firstaddr; 46959854Sbp fp->f_addr = firstaddr; 47040143Speter 47159854Sbp php = NULL; 47240143Speter for (i = 0; i < ehdr->e_phnum; i++) { 47340143Speter if (phdr[i].p_type == PT_DYNAMIC) { 47459854Sbp php = phdr + i; 475114379Speter adp = php->p_vaddr; 476114379Speter file_addmetadata(fp, MODINFOMD_DYNAMIC, sizeof(adp), &adp); 47740143Speter break; 47840143Speter } 47940143Speter } 48040143Speter 48159854Sbp if (php == NULL) /* this is bad, we cannot get to symbols or _DYNAMIC */ 48240143Speter goto out; 48340143Speter 48459854Sbp ndp = php->p_filesz / sizeof(Elf_Dyn); 48559854Sbp if (ndp == 0) 48640143Speter goto out; 48759854Sbp dp = malloc(php->p_filesz); 48859854Sbp if (dp == NULL) 48959854Sbp goto out; 49059854Sbp archsw.arch_copyout(php->p_vaddr + off, dp, php->p_filesz); 49159854Sbp 49259854Sbp ef->strsz = 0; 49340143Speter for (i = 0; i < ndp; i++) { 49440143Speter if (dp[i].d_tag == NULL) 49540143Speter break; 49640143Speter switch (dp[i].d_tag) { 49759854Sbp case DT_HASH: 498114379Speter ef->hashtab = (Elf_Hashelt*)(uintptr_t)(dp[i].d_un.d_ptr + off); 49959854Sbp break; 50040143Speter case DT_STRTAB: 501114379Speter ef->strtab = (char *)(uintptr_t)(dp[i].d_un.d_ptr + off); 50240143Speter break; 50340143Speter case DT_STRSZ: 50459854Sbp ef->strsz = dp[i].d_un.d_val; 50540143Speter break; 50659854Sbp case DT_SYMTAB: 507114379Speter ef->symtab = (Elf_Sym*)(uintptr_t)(dp[i].d_un.d_ptr + off); 50859854Sbp break; 509109616Sjake case DT_RELA: 510114379Speter ef->rela = (Elf_Rela *)(uintptr_t)(dp[i].d_un.d_ptr + off); 511109616Sjake break; 512109616Sjake case DT_RELASZ: 513109616Sjake ef->relasz = dp[i].d_un.d_val; 514109616Sjake break; 51540143Speter default: 51640143Speter break; 51740143Speter } 51840143Speter } 51959854Sbp if (ef->hashtab == NULL || ef->symtab == NULL || 52059854Sbp ef->strtab == NULL || ef->strsz == 0) 52140143Speter goto out; 52259854Sbp COPYOUT(ef->hashtab, &ef->nbuckets, sizeof(ef->nbuckets)); 52359854Sbp COPYOUT(ef->hashtab + 1, &ef->nchains, sizeof(ef->nchains)); 52459854Sbp ef->buckets = ef->hashtab + 2; 52559854Sbp ef->chains = ef->buckets + ef->nbuckets; 526114379Speter if (__elfN(parse_modmetadata)(fp, ef) == 0) 52759854Sbp goto out; 52840143Speter 52959854Sbp if (ef->kernel) /* kernel must not depend on anything */ 53059854Sbp goto out; 53159854Sbp 53239830Speterout: 53340143Speter if (dp) 53440143Speter free(dp); 53540143Speter if (shdr) 53640143Speter free(shdr); 53739830Speter return ret; 53839830Speter} 53959854Sbp 54059854Sbpstatic char invalid_name[] = "bad"; 54183321Speter 54259854Sbpchar * 54378463Speterfake_modname(const char *name) 54478463Speter{ 54578696Sdwmalone const char *sp, *ep; 54678696Sdwmalone char *fp; 54764187Sjhb size_t len; 54859854Sbp 54959854Sbp sp = strrchr(name, '/'); 55059854Sbp if (sp) 55159854Sbp sp++; 55259854Sbp else 55359854Sbp sp = name; 55459854Sbp ep = strrchr(name, '.'); 55559854Sbp if (ep) { 55659854Sbp if (ep == name) { 55759854Sbp sp = invalid_name; 55859854Sbp ep = invalid_name + sizeof(invalid_name) - 1; 55959854Sbp } 56059854Sbp } else 56159854Sbp ep = name + strlen(name); 56259854Sbp len = ep - sp; 56378696Sdwmalone fp = malloc(len + 1); 56478696Sdwmalone if (fp == NULL) 56559854Sbp return NULL; 56678696Sdwmalone memcpy(fp, sp, len); 56778696Sdwmalone fp[len] = '\0'; 56878696Sdwmalone return fp; 56959854Sbp} 57059854Sbp 571114937Speter#if defined(__i386__) && __ELF_WORD_SIZE == 64 572114937Speterstruct mod_metadata64 { 573114937Speter int md_version; /* structure version MDTV_* */ 574114937Speter int md_type; /* type of entry MDT_* */ 575114937Speter u_int64_t md_data; /* specific data */ 576114937Speter u_int64_t md_cval; /* common string label */ 577114937Speter}; 578114937Speter#endif 579114937Speter 58059854Sbpint 581114379Speter__elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef) 58278463Speter{ 58359854Sbp struct mod_metadata md; 584114937Speter#if defined(__i386__) && __ELF_WORD_SIZE == 64 585114937Speter struct mod_metadata64 md64; 586114937Speter#endif 58783321Speter struct mod_depend *mdepend; 58883321Speter struct mod_version mver; 58959854Sbp Elf_Sym sym; 590114937Speter char *s; 59183321Speter int modcnt, minfolen; 592114937Speter Elf_Addr v, p, p_stop; 59359854Sbp 594114379Speter if (__elfN(lookup_symbol)(fp, ef, "__start_set_modmetadata_set", &sym) != 0) 59559854Sbp return ENOENT; 596114937Speter p = sym.st_value + ef->off; 597114379Speter if (__elfN(lookup_symbol)(fp, ef, "__stop_set_modmetadata_set", &sym) != 0) 59878465Speter return ENOENT; 599114937Speter p_stop = sym.st_value + ef->off; 60059854Sbp 60159854Sbp modcnt = 0; 60278465Speter while (p < p_stop) { 603109616Sjake COPYOUT(p, &v, sizeof(v)); 604109616Sjake#ifdef __sparc64__ 605114379Speter __elfN(reloc_ptr)(fp, ef, p, &v, sizeof(v)); 606109616Sjake#else 607109616Sjake v += ef->off; 608109616Sjake#endif 609114937Speter#if defined(__i386__) && __ELF_WORD_SIZE == 64 610114937Speter COPYOUT(v, &md64, sizeof(md64)); 611114937Speter md.md_version = md64.md_version; 612114937Speter md.md_type = md64.md_type; 613114937Speter md.md_cval = (const char *)(uintptr_t)(md64.md_cval + ef->off); 614114937Speter md.md_data = (void *)(uintptr_t)(md64.md_data + ef->off); 615114937Speter#else 616109616Sjake COPYOUT(v, &md, sizeof(md)); 617109616Sjake#ifdef __sparc64__ 618114379Speter __elfN(reloc_ptr)(fp, ef, v, &md, sizeof(md)); 619109616Sjake#else 620109616Sjake md.md_cval += ef->off; 621109616Sjake md.md_data += ef->off; 622109616Sjake#endif 623114937Speter#endif 624114937Speter p += sizeof(Elf_Addr); 62559854Sbp switch(md.md_type) { 62659854Sbp case MDT_DEPEND: 62759854Sbp if (ef->kernel) /* kernel must not depend on anything */ 62859854Sbp break; 629109616Sjake s = strdupout((vm_offset_t)md.md_cval); 63083321Speter minfolen = sizeof(*mdepend) + strlen(s) + 1; 63183321Speter mdepend = malloc(minfolen); 63283321Speter if (mdepend == NULL) 63383321Speter return ENOMEM; 634109616Sjake COPYOUT((vm_offset_t)md.md_data, mdepend, sizeof(*mdepend)); 63583321Speter strcpy((char*)(mdepend + 1), s); 63659854Sbp free(s); 63783321Speter file_addmetadata(fp, MODINFOMD_DEPLIST, minfolen, mdepend); 63883321Speter free(mdepend); 63959854Sbp break; 64059854Sbp case MDT_VERSION: 641109616Sjake s = strdupout((vm_offset_t)md.md_cval); 642109616Sjake COPYOUT((vm_offset_t)md.md_data, &mver, sizeof(mver)); 64383321Speter file_addmodule(fp, s, mver.mv_version, NULL); 64459854Sbp free(s); 64559854Sbp modcnt++; 64659854Sbp break; 64759854Sbp } 64859854Sbp } 64959854Sbp if (modcnt == 0) { 65059854Sbp s = fake_modname(fp->f_name); 65183321Speter file_addmodule(fp, s, 1, NULL); 65259854Sbp free(s); 65359854Sbp } 65459854Sbp return 0; 65559854Sbp} 65659854Sbp 65759854Sbpstatic unsigned long 65859854Sbpelf_hash(const char *name) 65959854Sbp{ 66059854Sbp const unsigned char *p = (const unsigned char *) name; 66159854Sbp unsigned long h = 0; 66259854Sbp unsigned long g; 66359854Sbp 66459854Sbp while (*p != '\0') { 66559854Sbp h = (h << 4) + *p++; 66659854Sbp if ((g = h & 0xf0000000) != 0) 66759854Sbp h ^= g >> 24; 66859854Sbp h &= ~g; 66959854Sbp } 67059854Sbp return h; 67159854Sbp} 67259854Sbp 673114379Speterstatic const char __elfN(bad_symtable)[] = "elf" __XSTRING(__ELF_WORD_SIZE) "_lookup_symbol: corrupt symbol table\n"; 67459854Sbpint 675114379Speter__elfN(lookup_symbol)(struct preloaded_file *fp, elf_file_t ef, const char* name, 67659854Sbp Elf_Sym *symp) 67759854Sbp{ 67894248Sjake Elf_Hashelt symnum; 67959854Sbp Elf_Sym sym; 68059854Sbp char *strp; 68159854Sbp unsigned long hash; 68259854Sbp 68359854Sbp hash = elf_hash(name); 68459854Sbp COPYOUT(&ef->buckets[hash % ef->nbuckets], &symnum, sizeof(symnum)); 68559854Sbp 68659854Sbp while (symnum != STN_UNDEF) { 68759854Sbp if (symnum >= ef->nchains) { 688114379Speter printf(__elfN(bad_symtable)); 68959854Sbp return ENOENT; 69059854Sbp } 69159854Sbp 69259854Sbp COPYOUT(ef->symtab + symnum, &sym, sizeof(sym)); 69359854Sbp if (sym.st_name == 0) { 694114379Speter printf(__elfN(bad_symtable)); 69559854Sbp return ENOENT; 69659854Sbp } 69759854Sbp 69859854Sbp strp = strdupout((vm_offset_t)(ef->strtab + sym.st_name)); 69959854Sbp if (strcmp(name, strp) == 0) { 70059854Sbp free(strp); 70159854Sbp if (sym.st_shndx != SHN_UNDEF || 70259854Sbp (sym.st_value != 0 && 70359854Sbp ELF_ST_TYPE(sym.st_info) == STT_FUNC)) { 70459854Sbp *symp = sym; 70559854Sbp return 0; 70659854Sbp } 70759854Sbp return ENOENT; 70859854Sbp } 70959854Sbp free(strp); 71059854Sbp COPYOUT(&ef->chains[symnum], &symnum, sizeof(symnum)); 71159854Sbp } 71259854Sbp return ENOENT; 71359854Sbp} 714109616Sjake 715109616Sjake#ifdef __sparc__ 716109616Sjake/* 717109616Sjake * Apply any intra-module relocations to the value. *p is the load address 718109616Sjake * of the value and val/len is the value to be modified. This does NOT modify 719109616Sjake * the image in-place, because this is done by kern_linker later on. 720109616Sjake */ 721109616Sjakestatic void 722114379Speter__elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, 723109616Sjake void *p, void *val, size_t len) 724109616Sjake{ 725109616Sjake Elf_Addr off = (Elf_Addr)p - ef->off, word; 726109616Sjake size_t n; 727109616Sjake Elf_Rela r; 728109616Sjake 729109616Sjake for (n = 0; n < ef->relasz / sizeof(r); n++) { 730109616Sjake COPYOUT(ef->rela + n, &r, sizeof(r)); 731109616Sjake 732109616Sjake if (r.r_offset >= off && r.r_offset < off + len && 733109616Sjake ELF_R_TYPE(r.r_info) == R_SPARC_RELATIVE) { 734109616Sjake word = ef->off + r.r_addend; 735109616Sjake bcopy(&word, (char *)val + (r.r_offset - off), 736109616Sjake sizeof(word)); 737109616Sjake } 738109616Sjake } 739109616Sjake} 740109616Sjake#endif 741