load_elf.c revision 153504
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 */ 2739830Speter 28119483Sobrien#include <sys/cdefs.h> 29119483Sobrien__FBSDID("$FreeBSD: head/sys/boot/common/load_elf.c 153504 2005-12-18 04:52:37Z marcel $"); 30119483Sobrien 3139830Speter#include <sys/param.h> 3239830Speter#include <sys/exec.h> 3340143Speter#include <sys/linker.h> 3459854Sbp#include <sys/module.h> 3539830Speter#include <string.h> 3639830Speter#include <machine/elf.h> 3739830Speter#include <stand.h> 3839830Speter#define FREEBSD_ELF 3939830Speter#include <link.h> 4039830Speter 4139830Speter#include "bootstrap.h" 4239830Speter 4359854Sbp#define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l) 4439830Speter 45114379Speter#if defined(__i386__) && __ELF_WORD_SIZE == 64 46114379Speter#undef ELF_TARG_CLASS 47114379Speter#undef ELF_TARG_MACH 48114379Speter#define ELF_TARG_CLASS ELFCLASS64 49114379Speter#define ELF_TARG_MACH EM_X86_64 50114379Speter#endif 5159854Sbp 5259854Sbptypedef struct elf_file { 5359854Sbp Elf_Phdr *ph; 5459854Sbp Elf_Ehdr *ehdr; 5559854Sbp Elf_Sym *symtab; 5693922Speter Elf_Hashelt *hashtab; 5793922Speter Elf_Hashelt nbuckets; 5893922Speter Elf_Hashelt nchains; 5993922Speter Elf_Hashelt *buckets; 6093922Speter Elf_Hashelt *chains; 61134458Siedowse Elf_Rel *rel; 62134458Siedowse size_t relsz; 63109616Sjake Elf_Rela *rela; 64109616Sjake size_t relasz; 6559854Sbp char *strtab; 6659854Sbp size_t strsz; 6759854Sbp int fd; 6859854Sbp caddr_t firstpage; 6964187Sjhb size_t firstlen; 7059854Sbp int kernel; 71114379Speter u_int64_t off; 7259854Sbp} *elf_file_t; 7359854Sbp 74114379Speterstatic int __elfN(loadimage)(struct preloaded_file *mp, elf_file_t ef, u_int64_t loadaddr); 75114379Speterstatic int __elfN(lookup_symbol)(struct preloaded_file *mp, elf_file_t ef, const char* name, Elf_Sym* sym); 76134458Siedowsestatic int __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, 77134458Siedowse Elf_Addr p, void *val, size_t len); 78114379Speterstatic int __elfN(parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef); 79134458Siedowsestatic symaddr_fn __elfN(symaddr); 8064187Sjhbstatic char *fake_modname(const char *name); 8159854Sbp 82114379Speterconst char *__elfN(kerneltype) = "elf kernel"; 83114379Speterconst char *__elfN(moduletype) = "elf module"; 8439830Speter 8539830Speter/* 8639830Speter * Attempt to load the file (file) as an ELF module. It will be stored at 8739830Speter * (dest), and a pointer to a module structure describing the loaded object 8839830Speter * will be saved in (result). 8939830Speter */ 9039830Speterint 91114379Speter__elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result) 9239830Speter{ 9359854Sbp struct preloaded_file *fp, *kfp; 9459854Sbp struct elf_file ef; 9559854Sbp Elf_Ehdr *ehdr; 9659854Sbp int err; 9739830Speter u_int pad; 9864187Sjhb ssize_t bytes_read; 9939830Speter 10059854Sbp fp = NULL; 10159854Sbp bzero(&ef, sizeof(struct elf_file)); 10239830Speter 10339830Speter /* 10439830Speter * Open the image, read and validate the ELF header 10539830Speter */ 10639830Speter if (filename == NULL) /* can't handle nameless */ 10739830Speter return(EFTYPE); 10859854Sbp if ((ef.fd = open(filename, O_RDONLY)) == -1) 10939830Speter return(errno); 11059854Sbp ef.firstpage = malloc(PAGE_SIZE); 11159854Sbp if (ef.firstpage == NULL) { 11259854Sbp close(ef.fd); 11340465Speter return(ENOMEM); 11459854Sbp } 11564187Sjhb bytes_read = read(ef.fd, ef.firstpage, PAGE_SIZE); 11664187Sjhb ef.firstlen = (size_t)bytes_read; 11764187Sjhb if (bytes_read < 0 || ef.firstlen <= sizeof(Elf_Ehdr)) { 11839830Speter err = EFTYPE; /* could be EIO, but may be small file */ 11939830Speter goto oerr; 12039830Speter } 12159854Sbp ehdr = ef.ehdr = (Elf_Ehdr *)ef.firstpage; 12239830Speter 12339830Speter /* Is it ELF? */ 12440465Speter if (!IS_ELF(*ehdr)) { 12539830Speter err = EFTYPE; 12639830Speter goto oerr; 12739830Speter } 12840465Speter if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ 12940465Speter ehdr->e_ident[EI_DATA] != ELF_TARG_DATA || 13040465Speter ehdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ 13140465Speter ehdr->e_version != EV_CURRENT || 13240465Speter ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */ 13339830Speter err = EFTYPE; 13439830Speter goto oerr; 13539830Speter } 13639830Speter 13739830Speter 13839830Speter /* 13939830Speter * Check to see what sort of module we are. 14039830Speter */ 14159854Sbp kfp = file_findfile(NULL, NULL); 14240465Speter if (ehdr->e_type == ET_DYN) { 14339830Speter /* Looks like a kld module */ 14459854Sbp if (kfp == NULL) { 145114379Speter printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module before kernel\n"); 14639830Speter err = EPERM; 14739830Speter goto oerr; 14839830Speter } 149114379Speter if (strcmp(__elfN(kerneltype), kfp->f_type)) { 150114379Speter printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module with kernel type '%s'\n", kfp->f_type); 15139830Speter err = EPERM; 15239830Speter goto oerr; 15339830Speter } 15439830Speter /* Looks OK, got ahead */ 15559854Sbp ef.kernel = 0; 15639830Speter 15739830Speter /* Page-align the load address */ 15840143Speter pad = (u_int)dest & PAGE_MASK; 15939830Speter if (pad != 0) { 16039830Speter pad = PAGE_SIZE - pad; 16140143Speter dest += pad; 16239830Speter } 16340465Speter } else if (ehdr->e_type == ET_EXEC) { 16439830Speter /* Looks like a kernel */ 16559854Sbp if (kfp != NULL) { 166114379Speter printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: kernel already loaded\n"); 16739830Speter err = EPERM; 16839830Speter goto oerr; 16939830Speter } 17039830Speter /* 17139830Speter * Calculate destination address based on kernel entrypoint 17239830Speter */ 173114379Speter dest = ehdr->e_entry; 17439830Speter if (dest == 0) { 175114379Speter printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: not a kernel (maybe static binary?)\n"); 17639830Speter err = EPERM; 17739830Speter goto oerr; 17839830Speter } 17959854Sbp ef.kernel = 1; 18039830Speter 18139830Speter } else { 18239830Speter err = EFTYPE; 18339830Speter goto oerr; 18439830Speter } 18539830Speter 18639830Speter /* 18739830Speter * Ok, we think we should handle this. 18839830Speter */ 18959854Sbp fp = file_alloc(); 19059854Sbp if (fp == NULL) { 191114379Speter printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: cannot allocate module info\n"); 19240143Speter err = EPERM; 19340143Speter goto out; 19440143Speter } 19559854Sbp if (ef.kernel) 19640143Speter setenv("kernelname", filename, 1); 19783321Speter fp->f_name = strdup(filename); 198114379Speter fp->f_type = strdup(ef.kernel ? __elfN(kerneltype) : __elfN(moduletype)); 19939830Speter 20040291Speter#ifdef ELF_VERBOSE 20159854Sbp if (ef.kernel) 202114379Speter printf("%s entry at 0x%jx\n", filename, (uintmax_t)dest); 20340327Speter#else 20440327Speter printf("%s ", filename); 20540291Speter#endif 20639830Speter 207114379Speter fp->f_size = __elfN(loadimage)(fp, &ef, dest); 20859854Sbp if (fp->f_size == 0 || fp->f_addr == 0) 20939830Speter goto ioerr; 21039830Speter 21139830Speter /* save exec header as metadata */ 21259854Sbp file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof(*ehdr), ehdr); 21339830Speter 21439830Speter /* Load OK, return module pointer */ 21559854Sbp *result = (struct preloaded_file *)fp; 21639830Speter err = 0; 21739830Speter goto out; 21839830Speter 21939830Speter ioerr: 22039830Speter err = EIO; 22139830Speter oerr: 22259854Sbp file_discard(fp); 22339830Speter out: 22459854Sbp if (ef.firstpage) 22559854Sbp free(ef.firstpage); 22659854Sbp close(ef.fd); 22739830Speter return(err); 22839830Speter} 22939830Speter 23039830Speter/* 23139830Speter * With the file (fd) open on the image, and (ehdr) containing 23240143Speter * the Elf header, load the image at (off) 23339830Speter */ 23439830Speterstatic int 235114379Speter__elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, u_int64_t off) 23639830Speter{ 23764187Sjhb int i; 23864187Sjhb u_int j; 23959854Sbp Elf_Ehdr *ehdr; 24059854Sbp Elf_Phdr *phdr, *php; 24139887Speter Elf_Shdr *shdr; 24239830Speter int ret; 24339830Speter vm_offset_t firstaddr; 24439830Speter vm_offset_t lastaddr; 245134441Siedowse size_t chunk; 24664187Sjhb ssize_t result; 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) { 307134441Siedowse if (kern_pread(ef->fd, phdr[i].p_vaddr + off + fpcopy, 308134441Siedowse phdr[i].p_filesz - fpcopy, phdr[i].p_offset + fpcopy) != 0) { 309134441Siedowse printf("\nelf" __XSTRING(__ELF_WORD_SIZE) 310134441Siedowse "_loadimage: read failed\n"); 31140465Speter goto out; 31240465Speter } 31339830Speter } 31439830Speter /* clear space from oversized segments; eg: bss */ 31539830Speter if (phdr[i].p_filesz < phdr[i].p_memsz) { 31640254Speter#ifdef ELF_VERBOSE 31739887Speter printf(" (bss: 0x%lx-0x%lx)", 31839830Speter (long)(phdr[i].p_vaddr + off + phdr[i].p_filesz), 31939830Speter (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); 32040254Speter#endif 32139830Speter 322134441Siedowse kern_bzero(phdr[i].p_vaddr + off + phdr[i].p_filesz, 323134441Siedowse phdr[i].p_memsz - phdr[i].p_filesz); 32439830Speter } 32540254Speter#ifdef ELF_VERBOSE 32639887Speter printf("\n"); 32740254Speter#endif 32839830Speter 32940143Speter if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off)) 33039830Speter firstaddr = phdr[i].p_vaddr + off; 33140143Speter if (lastaddr == 0 || lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz)) 33239830Speter lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz; 33339830Speter } 33440254Speter lastaddr = roundup(lastaddr, sizeof(long)); 33539830Speter 33639887Speter /* 33739887Speter * Now grab the symbol tables. This isn't easy if we're reading a 33839887Speter * .gz file. I think the rule is going to have to be that you must 33939887Speter * strip a file to remove symbols before gzipping it so that we do not 34040254Speter * try to lseek() on it. 34139887Speter */ 34239887Speter chunk = ehdr->e_shnum * ehdr->e_shentsize; 34340291Speter if (chunk == 0 || ehdr->e_shoff == 0) 34440291Speter goto nosyms; 345134441Siedowse shdr = alloc_pread(ef->fd, ehdr->e_shoff, chunk); 346134441Siedowse if (shdr == NULL) { 347134441Siedowse printf("\nelf" __XSTRING(__ELF_WORD_SIZE) 348134441Siedowse "_loadimage: failed to read section headers"); 34939887Speter goto nosyms; 35039887Speter } 35140254Speter symtabindex = -1; 35240254Speter symstrindex = -1; 35339887Speter for (i = 0; i < ehdr->e_shnum; i++) { 35440254Speter if (shdr[i].sh_type != SHT_SYMTAB) 35540143Speter continue; 35639887Speter for (j = 0; j < ehdr->e_phnum; j++) { 35739887Speter if (phdr[j].p_type != PT_LOAD) 35839887Speter continue; 35939887Speter if (shdr[i].sh_offset >= phdr[j].p_offset && 36039887Speter (shdr[i].sh_offset + shdr[i].sh_size <= 36139887Speter phdr[j].p_offset + phdr[j].p_filesz)) { 36239887Speter shdr[i].sh_offset = 0; 36339887Speter shdr[i].sh_size = 0; 36439887Speter break; 36539887Speter } 36639887Speter } 36739887Speter if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0) 36839887Speter continue; /* alread loaded in a PT_LOAD above */ 36940254Speter /* Save it for loading below */ 37040254Speter symtabindex = i; 37140254Speter symstrindex = shdr[i].sh_link; 37240254Speter } 37340254Speter if (symtabindex < 0 || symstrindex < 0) 37440254Speter goto nosyms; 37539887Speter 37640254Speter /* Ok, committed to a load. */ 37740254Speter#ifndef ELF_VERBOSE 37840291Speter printf("syms=["); 37940254Speter#endif 38040254Speter ssym = lastaddr; 38140254Speter for (i = symtabindex; i >= 0; i = symstrindex) { 38240254Speter#ifdef ELF_VERBOSE 38340254Speter char *secname; 38440254Speter 38540254Speter switch(shdr[i].sh_type) { 38640254Speter case SHT_SYMTAB: /* Symbol table */ 38740254Speter secname = "symtab"; 38840254Speter break; 38940254Speter case SHT_STRTAB: /* String table */ 39040254Speter secname = "strtab"; 39140254Speter break; 39240254Speter default: 39340254Speter secname = "WHOA!!"; 39440254Speter break; 39540254Speter } 39640254Speter#endif 39740254Speter 39840254Speter size = shdr[i].sh_size; 39940254Speter archsw.arch_copyin(&size, lastaddr, sizeof(size)); 400114379Speter lastaddr += sizeof(size); 40140254Speter 40240254Speter#ifdef ELF_VERBOSE 40342288Speter printf("\n%s: 0x%lx@0x%lx -> 0x%lx-0x%lx", secname, 40439887Speter shdr[i].sh_size, shdr[i].sh_offset, 40539887Speter lastaddr, lastaddr + shdr[i].sh_size); 40640254Speter#else 40740291Speter if (i == symstrindex) 40840291Speter printf("+"); 409114379Speter printf("0x%lx+0x%lx", (long)sizeof(size), (long)size); 41040254Speter#endif 41140254Speter 41264187Sjhb if (lseek(ef->fd, (off_t)shdr[i].sh_offset, SEEK_SET) == -1) { 413114379Speter printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not seek for symbols - skipped!"); 41440254Speter lastaddr = ssym; 41540254Speter ssym = 0; 41640254Speter goto nosyms; 41739887Speter } 41864187Sjhb result = archsw.arch_readin(ef->fd, lastaddr, shdr[i].sh_size); 41964187Sjhb if (result < 0 || (size_t)result != shdr[i].sh_size) { 420114379Speter printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not read symbols - skipped!"); 42140254Speter lastaddr = ssym; 42240254Speter ssym = 0; 42340254Speter goto nosyms; 42439887Speter } 42539887Speter /* Reset offsets relative to ssym */ 42639887Speter lastaddr += shdr[i].sh_size; 427114379Speter lastaddr = roundup(lastaddr, sizeof(size)); 42840254Speter if (i == symtabindex) 42940254Speter symtabindex = -1; 43040254Speter else if (i == symstrindex) 43140254Speter symstrindex = -1; 43239887Speter } 43339887Speter esym = lastaddr; 43440254Speter#ifndef ELF_VERBOSE 43542288Speter printf("]"); 43640254Speter#endif 43739887Speter 43859854Sbp file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym); 43959854Sbp file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym); 44039887Speter 44139887Speternosyms: 44242288Speter printf("\n"); 44339887Speter 44439830Speter ret = lastaddr - firstaddr; 44559854Sbp fp->f_addr = firstaddr; 44640143Speter 44759854Sbp php = NULL; 44840143Speter for (i = 0; i < ehdr->e_phnum; i++) { 44940143Speter if (phdr[i].p_type == PT_DYNAMIC) { 45059854Sbp php = phdr + i; 451114379Speter adp = php->p_vaddr; 452114379Speter file_addmetadata(fp, MODINFOMD_DYNAMIC, sizeof(adp), &adp); 45340143Speter break; 45440143Speter } 45540143Speter } 45640143Speter 45759854Sbp if (php == NULL) /* this is bad, we cannot get to symbols or _DYNAMIC */ 45840143Speter goto out; 45940143Speter 46059854Sbp ndp = php->p_filesz / sizeof(Elf_Dyn); 46159854Sbp if (ndp == 0) 46240143Speter goto out; 46359854Sbp dp = malloc(php->p_filesz); 46459854Sbp if (dp == NULL) 46559854Sbp goto out; 46659854Sbp archsw.arch_copyout(php->p_vaddr + off, dp, php->p_filesz); 46759854Sbp 46859854Sbp ef->strsz = 0; 46940143Speter for (i = 0; i < ndp; i++) { 470126837Sbde if (dp[i].d_tag == 0) 47140143Speter break; 47240143Speter switch (dp[i].d_tag) { 47359854Sbp case DT_HASH: 474114379Speter ef->hashtab = (Elf_Hashelt*)(uintptr_t)(dp[i].d_un.d_ptr + off); 47559854Sbp break; 47640143Speter case DT_STRTAB: 477114379Speter ef->strtab = (char *)(uintptr_t)(dp[i].d_un.d_ptr + off); 47840143Speter break; 47940143Speter case DT_STRSZ: 48059854Sbp ef->strsz = dp[i].d_un.d_val; 48140143Speter break; 48259854Sbp case DT_SYMTAB: 483114379Speter ef->symtab = (Elf_Sym*)(uintptr_t)(dp[i].d_un.d_ptr + off); 48459854Sbp break; 485134458Siedowse case DT_REL: 486134458Siedowse ef->rel = (Elf_Rel *)(uintptr_t)(dp[i].d_un.d_ptr + off); 487134458Siedowse break; 488134458Siedowse case DT_RELSZ: 489134458Siedowse ef->relsz = dp[i].d_un.d_val; 490134458Siedowse break; 491109616Sjake case DT_RELA: 492114379Speter ef->rela = (Elf_Rela *)(uintptr_t)(dp[i].d_un.d_ptr + off); 493109616Sjake break; 494109616Sjake case DT_RELASZ: 495109616Sjake ef->relasz = dp[i].d_un.d_val; 496109616Sjake break; 49740143Speter default: 49840143Speter break; 49940143Speter } 50040143Speter } 50159854Sbp if (ef->hashtab == NULL || ef->symtab == NULL || 50259854Sbp ef->strtab == NULL || ef->strsz == 0) 50340143Speter goto out; 50459854Sbp COPYOUT(ef->hashtab, &ef->nbuckets, sizeof(ef->nbuckets)); 50559854Sbp COPYOUT(ef->hashtab + 1, &ef->nchains, sizeof(ef->nchains)); 50659854Sbp ef->buckets = ef->hashtab + 2; 50759854Sbp ef->chains = ef->buckets + ef->nbuckets; 508114379Speter if (__elfN(parse_modmetadata)(fp, ef) == 0) 50959854Sbp goto out; 51040143Speter 51159854Sbp if (ef->kernel) /* kernel must not depend on anything */ 51259854Sbp goto out; 51359854Sbp 51439830Speterout: 51540143Speter if (dp) 51640143Speter free(dp); 51740143Speter if (shdr) 51840143Speter free(shdr); 51939830Speter return ret; 52039830Speter} 52159854Sbp 52259854Sbpstatic char invalid_name[] = "bad"; 52383321Speter 52459854Sbpchar * 52578463Speterfake_modname(const char *name) 52678463Speter{ 52778696Sdwmalone const char *sp, *ep; 52878696Sdwmalone char *fp; 52964187Sjhb size_t len; 53059854Sbp 53159854Sbp sp = strrchr(name, '/'); 53259854Sbp if (sp) 53359854Sbp sp++; 53459854Sbp else 53559854Sbp sp = name; 53659854Sbp ep = strrchr(name, '.'); 53759854Sbp if (ep) { 53859854Sbp if (ep == name) { 53959854Sbp sp = invalid_name; 54059854Sbp ep = invalid_name + sizeof(invalid_name) - 1; 54159854Sbp } 54259854Sbp } else 54359854Sbp ep = name + strlen(name); 54459854Sbp len = ep - sp; 54578696Sdwmalone fp = malloc(len + 1); 54678696Sdwmalone if (fp == NULL) 54759854Sbp return NULL; 54878696Sdwmalone memcpy(fp, sp, len); 54978696Sdwmalone fp[len] = '\0'; 55078696Sdwmalone return fp; 55159854Sbp} 55259854Sbp 553114937Speter#if defined(__i386__) && __ELF_WORD_SIZE == 64 554114937Speterstruct mod_metadata64 { 555114937Speter int md_version; /* structure version MDTV_* */ 556114937Speter int md_type; /* type of entry MDT_* */ 557114937Speter u_int64_t md_data; /* specific data */ 558114937Speter u_int64_t md_cval; /* common string label */ 559114937Speter}; 560114937Speter#endif 561114937Speter 56259854Sbpint 563114379Speter__elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef) 56478463Speter{ 56559854Sbp struct mod_metadata md; 566114937Speter#if defined(__i386__) && __ELF_WORD_SIZE == 64 567114937Speter struct mod_metadata64 md64; 568114937Speter#endif 56983321Speter struct mod_depend *mdepend; 57083321Speter struct mod_version mver; 57159854Sbp Elf_Sym sym; 572114937Speter char *s; 573134458Siedowse int error, modcnt, minfolen; 574114937Speter Elf_Addr v, p, p_stop; 57559854Sbp 576114379Speter if (__elfN(lookup_symbol)(fp, ef, "__start_set_modmetadata_set", &sym) != 0) 57759854Sbp return ENOENT; 578114937Speter p = sym.st_value + ef->off; 579114379Speter if (__elfN(lookup_symbol)(fp, ef, "__stop_set_modmetadata_set", &sym) != 0) 58078465Speter return ENOENT; 581114937Speter p_stop = sym.st_value + ef->off; 58259854Sbp 58359854Sbp modcnt = 0; 58478465Speter while (p < p_stop) { 585109616Sjake COPYOUT(p, &v, sizeof(v)); 586134458Siedowse error = __elfN(reloc_ptr)(fp, ef, p, &v, sizeof(v)); 587134458Siedowse if (error == EOPNOTSUPP) 588134458Siedowse v += ef->off; 589134458Siedowse else if (error != 0) 590134458Siedowse return (error); 591114937Speter#if defined(__i386__) && __ELF_WORD_SIZE == 64 592114937Speter COPYOUT(v, &md64, sizeof(md64)); 593134458Siedowse error = __elfN(reloc_ptr)(fp, ef, v, &md64, sizeof(md64)); 594134458Siedowse if (error == EOPNOTSUPP) { 595134458Siedowse md64.md_cval += ef->off; 596134458Siedowse md64.md_data += ef->off; 597134458Siedowse } else if (error != 0) 598134458Siedowse return (error); 599114937Speter md.md_version = md64.md_version; 600114937Speter md.md_type = md64.md_type; 601134458Siedowse md.md_cval = (const char *)(uintptr_t)md64.md_cval; 602134458Siedowse md.md_data = (void *)(uintptr_t)md64.md_data; 603114937Speter#else 604109616Sjake COPYOUT(v, &md, sizeof(md)); 605134458Siedowse error = __elfN(reloc_ptr)(fp, ef, v, &md, sizeof(md)); 606134458Siedowse if (error == EOPNOTSUPP) { 607134458Siedowse md.md_cval += ef->off; 608134458Siedowse md.md_data += ef->off; 609134458Siedowse } else if (error != 0) 610134458Siedowse return (error); 611109616Sjake#endif 612114937Speter p += sizeof(Elf_Addr); 61359854Sbp switch(md.md_type) { 61459854Sbp case MDT_DEPEND: 61559854Sbp if (ef->kernel) /* kernel must not depend on anything */ 61659854Sbp break; 617109616Sjake s = strdupout((vm_offset_t)md.md_cval); 61883321Speter minfolen = sizeof(*mdepend) + strlen(s) + 1; 61983321Speter mdepend = malloc(minfolen); 62083321Speter if (mdepend == NULL) 62183321Speter return ENOMEM; 622109616Sjake COPYOUT((vm_offset_t)md.md_data, mdepend, sizeof(*mdepend)); 62383321Speter strcpy((char*)(mdepend + 1), s); 62459854Sbp free(s); 62583321Speter file_addmetadata(fp, MODINFOMD_DEPLIST, minfolen, mdepend); 62683321Speter free(mdepend); 62759854Sbp break; 62859854Sbp case MDT_VERSION: 629109616Sjake s = strdupout((vm_offset_t)md.md_cval); 630109616Sjake COPYOUT((vm_offset_t)md.md_data, &mver, sizeof(mver)); 63183321Speter file_addmodule(fp, s, mver.mv_version, NULL); 63259854Sbp free(s); 63359854Sbp modcnt++; 63459854Sbp break; 63559854Sbp } 63659854Sbp } 63759854Sbp if (modcnt == 0) { 63859854Sbp s = fake_modname(fp->f_name); 63983321Speter file_addmodule(fp, s, 1, NULL); 64059854Sbp free(s); 64159854Sbp } 64259854Sbp return 0; 64359854Sbp} 64459854Sbp 64559854Sbpstatic unsigned long 64659854Sbpelf_hash(const char *name) 64759854Sbp{ 64859854Sbp const unsigned char *p = (const unsigned char *) name; 64959854Sbp unsigned long h = 0; 65059854Sbp unsigned long g; 65159854Sbp 65259854Sbp while (*p != '\0') { 65359854Sbp h = (h << 4) + *p++; 65459854Sbp if ((g = h & 0xf0000000) != 0) 65559854Sbp h ^= g >> 24; 65659854Sbp h &= ~g; 65759854Sbp } 65859854Sbp return h; 65959854Sbp} 66059854Sbp 661114379Speterstatic const char __elfN(bad_symtable)[] = "elf" __XSTRING(__ELF_WORD_SIZE) "_lookup_symbol: corrupt symbol table\n"; 66259854Sbpint 663114379Speter__elfN(lookup_symbol)(struct preloaded_file *fp, elf_file_t ef, const char* name, 66459854Sbp Elf_Sym *symp) 66559854Sbp{ 66694248Sjake Elf_Hashelt symnum; 66759854Sbp Elf_Sym sym; 66859854Sbp char *strp; 66959854Sbp unsigned long hash; 67059854Sbp 67159854Sbp hash = elf_hash(name); 67259854Sbp COPYOUT(&ef->buckets[hash % ef->nbuckets], &symnum, sizeof(symnum)); 67359854Sbp 67459854Sbp while (symnum != STN_UNDEF) { 67559854Sbp if (symnum >= ef->nchains) { 676114379Speter printf(__elfN(bad_symtable)); 67759854Sbp return ENOENT; 67859854Sbp } 67959854Sbp 68059854Sbp COPYOUT(ef->symtab + symnum, &sym, sizeof(sym)); 68159854Sbp if (sym.st_name == 0) { 682114379Speter printf(__elfN(bad_symtable)); 68359854Sbp return ENOENT; 68459854Sbp } 68559854Sbp 68659854Sbp strp = strdupout((vm_offset_t)(ef->strtab + sym.st_name)); 68759854Sbp if (strcmp(name, strp) == 0) { 68859854Sbp free(strp); 68959854Sbp if (sym.st_shndx != SHN_UNDEF || 69059854Sbp (sym.st_value != 0 && 69159854Sbp ELF_ST_TYPE(sym.st_info) == STT_FUNC)) { 69259854Sbp *symp = sym; 69359854Sbp return 0; 69459854Sbp } 69559854Sbp return ENOENT; 69659854Sbp } 69759854Sbp free(strp); 69859854Sbp COPYOUT(&ef->chains[symnum], &symnum, sizeof(symnum)); 69959854Sbp } 70059854Sbp return ENOENT; 70159854Sbp} 702109616Sjake 703109616Sjake/* 704134458Siedowse * Apply any intra-module relocations to the value. p is the load address 705109616Sjake * of the value and val/len is the value to be modified. This does NOT modify 706109616Sjake * the image in-place, because this is done by kern_linker later on. 707134458Siedowse * 708134458Siedowse * Returns EOPNOTSUPP if no relocation method is supplied. 709109616Sjake */ 710134458Siedowsestatic int 711114379Speter__elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, 712134458Siedowse Elf_Addr p, void *val, size_t len) 713109616Sjake{ 714109616Sjake size_t n; 715134458Siedowse Elf_Rela a; 716134458Siedowse Elf_Rel r; 717134458Siedowse int error; 718109616Sjake 719134458Siedowse /* 720134458Siedowse * The kernel is already relocated, but we still want to apply 721134458Siedowse * offset adjustments. 722134458Siedowse */ 723134458Siedowse if (ef->kernel) 724134458Siedowse return (EOPNOTSUPP); 725109616Sjake 726134458Siedowse for (n = 0; n < ef->relsz / sizeof(r); n++) { 727134458Siedowse COPYOUT(ef->rel + n, &r, sizeof(r)); 728134458Siedowse 729134458Siedowse error = __elfN(reloc)(ef, __elfN(symaddr), &r, ELF_RELOC_REL, 730134458Siedowse ef->off, p, val, len); 731134458Siedowse if (error != 0) 732134458Siedowse return (error); 733109616Sjake } 734134458Siedowse for (n = 0; n < ef->relasz / sizeof(a); n++) { 735134458Siedowse COPYOUT(ef->rela + n, &a, sizeof(a)); 736134458Siedowse 737134458Siedowse error = __elfN(reloc)(ef, __elfN(symaddr), &a, ELF_RELOC_RELA, 738134458Siedowse ef->off, p, val, len); 739134458Siedowse if (error != 0) 740134458Siedowse return (error); 741134458Siedowse } 742134458Siedowse 743134458Siedowse return (0); 744109616Sjake} 745134458Siedowse 746134458Siedowsestatic Elf_Addr 747153504Smarcel__elfN(symaddr)(struct elf_file *ef, Elf_Size symidx) 748134458Siedowse{ 749134458Siedowse 750134458Siedowse /* Symbol lookup by index not required here. */ 751134458Siedowse return (0); 752134458Siedowse} 753