link_elf.c revision 46694
138514Sdfr/*- 238514Sdfr * Copyright (c) 1998 Doug Rabson 338514Sdfr * All rights reserved. 438514Sdfr * 538514Sdfr * Redistribution and use in source and binary forms, with or without 638514Sdfr * modification, are permitted provided that the following conditions 738514Sdfr * are met: 838514Sdfr * 1. Redistributions of source code must retain the above copyright 938514Sdfr * notice, this list of conditions and the following disclaimer. 1038514Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1138514Sdfr * notice, this list of conditions and the following disclaimer in the 1238514Sdfr * documentation and/or other materials provided with the distribution. 1338514Sdfr * 1438514Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1538514Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1638514Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1738514Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1838514Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1938514Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2038514Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2138514Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2238514Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2338514Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2438514Sdfr * SUCH DAMAGE. 2538514Sdfr * 2646694Speter * $Id: link_elf.c,v 1.16 1999/01/29 09:04:27 bde Exp $ 2738514Sdfr */ 2838514Sdfr 2938514Sdfr#include <sys/param.h> 3038514Sdfr#include <sys/kernel.h> 3138514Sdfr#include <sys/systm.h> 3238514Sdfr#include <sys/malloc.h> 3338514Sdfr#include <sys/proc.h> 3438514Sdfr#include <sys/namei.h> 3538514Sdfr#include <sys/fcntl.h> 3638514Sdfr#include <sys/vnode.h> 3738514Sdfr#include <sys/linker.h> 3838514Sdfr#include <machine/elf.h> 3938514Sdfr 4039071Sdfr#include <vm/vm.h> 4139071Sdfr#include <vm/vm_prot.h> 4239071Sdfr#include <vm/vm_param.h> 4339071Sdfr#include <sys/lock.h> 4439071Sdfr#include <vm/vm_object.h> 4539071Sdfr#include <vm/vm_kern.h> 4639071Sdfr#include <vm/vm_extern.h> 4739071Sdfr#include <vm/pmap.h> 4839071Sdfr#include <vm/vm_map.h> 4939071Sdfr 5040156Speterstatic int link_elf_load_module(const char*, linker_file_t*); 5138514Sdfrstatic int link_elf_load_file(const char*, linker_file_t*); 5238514Sdfrstatic int link_elf_lookup_symbol(linker_file_t, const char*, 5343301Sdillon c_linker_sym_t*); 5443309Sdillonstatic int link_elf_symbol_values(linker_file_t, c_linker_sym_t, linker_symval_t*); 5538514Sdfrstatic int link_elf_search_symbol(linker_file_t, caddr_t value, 5643301Sdillon c_linker_sym_t* sym, long* diffp); 5738514Sdfr 5840156Speterstatic void link_elf_unload_file(linker_file_t); 5940156Speterstatic void link_elf_unload_module(linker_file_t); 6038514Sdfr 6138514Sdfrstatic struct linker_class_ops link_elf_class_ops = { 6240156Speter link_elf_load_module, 6338514Sdfr}; 6438514Sdfr 6538514Sdfrstatic struct linker_file_ops link_elf_file_ops = { 6638514Sdfr link_elf_lookup_symbol, 6738514Sdfr link_elf_symbol_values, 6838514Sdfr link_elf_search_symbol, 6940156Speter link_elf_unload_file, 7038514Sdfr}; 7138514Sdfr 7240156Speterstatic struct linker_file_ops link_elf_module_ops = { 7340156Speter link_elf_lookup_symbol, 7440156Speter link_elf_symbol_values, 7540156Speter link_elf_search_symbol, 7640156Speter link_elf_unload_module, 7740156Speter}; 7838514Sdfrtypedef struct elf_file { 7939071Sdfr caddr_t address; /* Relocation address */ 8039071Sdfr#ifdef SPARSE_MAPPING 8139071Sdfr vm_object_t object; /* VM object to hold file pages */ 8239071Sdfr#endif 8339071Sdfr const Elf_Dyn* dynamic; /* Symbol table etc. */ 8438514Sdfr Elf_Off nbuckets; /* DT_HASH info */ 8538514Sdfr Elf_Off nchains; 8638514Sdfr const Elf_Off* buckets; 8738514Sdfr const Elf_Off* chains; 8838514Sdfr caddr_t hash; 8938514Sdfr caddr_t strtab; /* DT_STRTAB */ 9040254Speter int strsz; /* DT_STRSZ */ 9139071Sdfr const Elf_Sym* symtab; /* DT_SYMTAB */ 9239071Sdfr Elf_Addr* got; /* DT_PLTGOT */ 9339071Sdfr const Elf_Rel* pltrel; /* DT_JMPREL */ 9439071Sdfr int pltrelsize; /* DT_PLTRELSZ */ 9539071Sdfr const Elf_Rela* pltrela; /* DT_JMPREL */ 9639071Sdfr int pltrelasize; /* DT_PLTRELSZ */ 9739071Sdfr const Elf_Rel* rel; /* DT_REL */ 9839071Sdfr int relsize; /* DT_RELSZ */ 9939071Sdfr const Elf_Rela* rela; /* DT_RELA */ 10039071Sdfr int relasize; /* DT_RELASZ */ 10140254Speter caddr_t modptr; 10240254Speter const Elf_Sym* ddbsymtab; /* The symbol table we are using */ 10340254Speter long ddbsymcnt; /* Number of symbols */ 10440254Speter caddr_t ddbstrtab; /* String table */ 10540254Speter long ddbstrcnt; /* number of bytes in string table */ 10640292Speter caddr_t symbase; /* malloc'ed symbold base */ 10740292Speter caddr_t strbase; /* malloc'ed string base */ 10838514Sdfr} *elf_file_t; 10938514Sdfr 11038514Sdfrstatic int parse_dynamic(linker_file_t lf); 11138514Sdfrstatic int load_dependancies(linker_file_t lf); 11238514Sdfrstatic int relocate_file(linker_file_t lf); 11340254Speterstatic int parse_module_symbols(linker_file_t lf); 11438514Sdfr 11538514Sdfr/* 11638514Sdfr * The kernel symbol table starts here. 11738514Sdfr */ 11838514Sdfrextern struct _dynamic _DYNAMIC; 11938514Sdfr 12038514Sdfrstatic void 12138514Sdfrlink_elf_init(void* arg) 12238514Sdfr{ 12340156Speter#ifdef __ELF__ 12440156Speter Elf_Dyn *dp; 12540156Speter caddr_t modptr, baseptr, sizeptr; 12640156Speter elf_file_t ef; 12740156Speter char *modname; 12840156Speter#endif 12938514Sdfr 13038514Sdfr#if ELF_TARG_CLASS == ELFCLASS32 13138514Sdfr linker_add_class("elf32", NULL, &link_elf_class_ops); 13238514Sdfr#else 13338514Sdfr linker_add_class("elf64", NULL, &link_elf_class_ops); 13438514Sdfr#endif 13538514Sdfr 13640156Speter#ifdef __ELF__ 13740156Speter dp = (Elf_Dyn*) &_DYNAMIC; 13838514Sdfr if (dp) { 13938514Sdfr ef = malloc(sizeof(struct elf_file), M_LINKER, M_NOWAIT); 14038514Sdfr if (ef == NULL) 14138514Sdfr panic("link_elf_init: Can't create linker structures for kernel"); 14240397Speter bzero(ef, sizeof(*ef)); 14338514Sdfr 14438514Sdfr ef->address = 0; 14539071Sdfr#ifdef SPARSE_MAPPING 14639071Sdfr ef->object = 0; 14739071Sdfr#endif 14838514Sdfr ef->dynamic = dp; 14940156Speter modname = NULL; 15040156Speter modptr = preload_search_by_type("elf kernel"); 15140156Speter if (modptr) 15240156Speter modname = (char *)preload_search_info(modptr, MODINFO_NAME); 15340156Speter if (modname == NULL) 15440156Speter modname = "kernel"; 15540156Speter linker_kernel_file = linker_make_file(modname, ef, &link_elf_file_ops); 15638514Sdfr if (linker_kernel_file == NULL) 15738514Sdfr panic("link_elf_init: Can't create linker structures for kernel"); 15838514Sdfr parse_dynamic(linker_kernel_file); 15946694Speter linker_kernel_file->address = (caddr_t) KERNBASE; 16038514Sdfr linker_kernel_file->size = -(long)linker_kernel_file->address; 16140156Speter 16240156Speter if (modptr) { 16340254Speter ef->modptr = modptr; 16440156Speter baseptr = preload_search_info(modptr, MODINFO_ADDR); 16540156Speter if (baseptr) 16640156Speter linker_kernel_file->address = *(caddr_t *)baseptr; 16740156Speter sizeptr = preload_search_info(modptr, MODINFO_SIZE); 16840156Speter if (sizeptr) 16940156Speter linker_kernel_file->size = *(size_t *)sizeptr; 17040156Speter } 17140254Speter (void)parse_module_symbols(linker_kernel_file); 17238514Sdfr linker_current_file = linker_kernel_file; 17343185Sdfr linker_kernel_file->flags |= LINKER_FILE_LINKED; 17438514Sdfr } 17540156Speter#endif 17638514Sdfr} 17738514Sdfr 17840156SpeterSYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0); 17938514Sdfr 18038514Sdfrstatic int 18140254Speterparse_module_symbols(linker_file_t lf) 18240254Speter{ 18340254Speter elf_file_t ef = lf->priv; 18440254Speter caddr_t pointer; 18540254Speter caddr_t ssym, esym, base; 18640254Speter caddr_t strtab; 18740254Speter int strcnt; 18840254Speter Elf_Sym* symtab; 18940254Speter int symcnt; 19040254Speter 19140292Speter if (ef->modptr == NULL) 19240292Speter return 0; 19340254Speter pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_SSYM); 19440254Speter if (pointer == NULL) 19540254Speter return 0; 19640254Speter ssym = *(caddr_t *)pointer; 19740254Speter pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_ESYM); 19840254Speter if (pointer == NULL) 19940254Speter return 0; 20040254Speter esym = *(caddr_t *)pointer; 20140254Speter 20240254Speter base = ssym; 20340254Speter 20440254Speter symcnt = *(long *)base; 20540254Speter base += sizeof(long); 20640254Speter symtab = (Elf_Sym *)base; 20740254Speter base += roundup(symcnt, sizeof(long)); 20840254Speter 20940254Speter if (base > esym || base < ssym) { 21040254Speter printf("Symbols are corrupt!\n"); 21140254Speter return EINVAL; 21240254Speter } 21340254Speter 21440254Speter strcnt = *(long *)base; 21540254Speter base += sizeof(long); 21640254Speter strtab = base; 21740254Speter base += roundup(strcnt, sizeof(long)); 21840254Speter 21940254Speter if (base > esym || base < ssym) { 22040254Speter printf("Symbols are corrupt!\n"); 22140254Speter return EINVAL; 22240254Speter } 22340254Speter 22440254Speter ef->ddbsymtab = symtab; 22540254Speter ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); 22640254Speter ef->ddbstrtab = strtab; 22740254Speter ef->ddbstrcnt = strcnt; 22840254Speter 22940254Speter return 0; 23040254Speter} 23140254Speter 23240254Speterstatic int 23338514Sdfrparse_dynamic(linker_file_t lf) 23438514Sdfr{ 23538514Sdfr elf_file_t ef = lf->priv; 23639071Sdfr const Elf_Dyn *dp; 23739071Sdfr int plttype = DT_REL; 23838514Sdfr 23938514Sdfr for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { 24038514Sdfr switch (dp->d_tag) { 24138514Sdfr case DT_HASH: 24238514Sdfr { 24338514Sdfr /* From src/libexec/rtld-elf/rtld.c */ 24438514Sdfr const Elf_Off *hashtab = (const Elf_Off *) 24538514Sdfr (ef->address + dp->d_un.d_ptr); 24638514Sdfr ef->nbuckets = hashtab[0]; 24738514Sdfr ef->nchains = hashtab[1]; 24838514Sdfr ef->buckets = hashtab + 2; 24938514Sdfr ef->chains = ef->buckets + ef->nbuckets; 25038514Sdfr break; 25138514Sdfr } 25238514Sdfr case DT_STRTAB: 25339071Sdfr ef->strtab = (caddr_t) (ef->address + dp->d_un.d_ptr); 25438514Sdfr break; 25540254Speter case DT_STRSZ: 25640254Speter ef->strsz = dp->d_un.d_val; 25740254Speter break; 25838514Sdfr case DT_SYMTAB: 25939071Sdfr ef->symtab = (Elf_Sym*) (ef->address + dp->d_un.d_ptr); 26038514Sdfr break; 26138514Sdfr case DT_SYMENT: 26238514Sdfr if (dp->d_un.d_val != sizeof(Elf_Sym)) 26338514Sdfr return ENOEXEC; 26439071Sdfr break; 26539071Sdfr case DT_PLTGOT: 26639071Sdfr ef->got = (Elf_Addr *) (ef->address + dp->d_un.d_ptr); 26739071Sdfr break; 26839071Sdfr case DT_REL: 26939071Sdfr ef->rel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); 27039071Sdfr break; 27139071Sdfr case DT_RELSZ: 27239071Sdfr ef->relsize = dp->d_un.d_val; 27339071Sdfr break; 27439071Sdfr case DT_RELENT: 27539071Sdfr if (dp->d_un.d_val != sizeof(Elf_Rel)) 27639071Sdfr return ENOEXEC; 27739071Sdfr break; 27839071Sdfr case DT_JMPREL: 27939071Sdfr ef->pltrel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); 28039071Sdfr break; 28139071Sdfr case DT_PLTRELSZ: 28239071Sdfr ef->pltrelsize = dp->d_un.d_val; 28339071Sdfr break; 28439071Sdfr case DT_RELA: 28539071Sdfr ef->rela = (const Elf_Rela *) (ef->address + dp->d_un.d_ptr); 28639071Sdfr break; 28739071Sdfr case DT_RELASZ: 28839071Sdfr ef->relasize = dp->d_un.d_val; 28939071Sdfr break; 29039071Sdfr case DT_RELAENT: 29139071Sdfr if (dp->d_un.d_val != sizeof(Elf_Rela)) 29239071Sdfr return ENOEXEC; 29339071Sdfr break; 29439071Sdfr case DT_PLTREL: 29539071Sdfr plttype = dp->d_un.d_val; 29639071Sdfr if (plttype != DT_REL && plttype != DT_RELA) 29739071Sdfr return ENOEXEC; 29839071Sdfr break; 29938514Sdfr } 30038514Sdfr } 30139071Sdfr 30239071Sdfr if (plttype == DT_RELA) { 30339071Sdfr ef->pltrela = (const Elf_Rela *) ef->pltrel; 30439071Sdfr ef->pltrel = NULL; 30539071Sdfr ef->pltrelasize = ef->pltrelsize; 30639071Sdfr ef->pltrelsize = 0; 30739071Sdfr } 30839071Sdfr 30940254Speter ef->ddbsymtab = ef->symtab; 31040254Speter ef->ddbsymcnt = ef->nchains; 31140254Speter ef->ddbstrtab = ef->strtab; 31240254Speter ef->ddbstrcnt = ef->strsz; 31340254Speter 31438514Sdfr return 0; 31538514Sdfr} 31638514Sdfr 31739071Sdfrstatic void 31839071Sdfrlink_elf_error(const char *s) 31939071Sdfr{ 32039071Sdfr printf("kldload: %s\n", s); 32139071Sdfr} 32239071Sdfr 32338514Sdfrstatic int 32440156Speterlink_elf_load_module(const char *filename, linker_file_t *result) 32540156Speter{ 32640156Speter caddr_t modptr, baseptr, sizeptr, dynptr; 32740156Speter char *type; 32840156Speter elf_file_t ef; 32940156Speter linker_file_t lf; 33040156Speter int error; 33140156Speter vm_offset_t dp; 33240156Speter 33340156Speter /* Look to see if we have the module preloaded */ 33440156Speter modptr = preload_search_by_name(filename); 33540156Speter if (modptr == NULL) 33640156Speter return (link_elf_load_file(filename, result)); 33740156Speter 33840156Speter /* It's preloaded, check we can handle it and collect information */ 33940156Speter type = (char *)preload_search_info(modptr, MODINFO_TYPE); 34040156Speter baseptr = preload_search_info(modptr, MODINFO_ADDR); 34140156Speter sizeptr = preload_search_info(modptr, MODINFO_SIZE); 34240156Speter dynptr = preload_search_info(modptr, MODINFO_METADATA|MODINFOMD_DYNAMIC); 34340156Speter if (type == NULL || strcmp(type, "elf module") != 0) 34440156Speter return (EFTYPE); 34540156Speter if (baseptr == NULL || sizeptr == NULL || dynptr == NULL) 34640156Speter return (EINVAL); 34740156Speter 34840156Speter ef = malloc(sizeof(struct elf_file), M_LINKER, M_WAITOK); 34940156Speter if (ef == NULL) 35040156Speter return (ENOMEM); 35140397Speter bzero(ef, sizeof(*ef)); 35240292Speter ef->modptr = modptr; 35340156Speter ef->address = *(caddr_t *)baseptr; 35440156Speter#ifdef SPARSE_MAPPING 35540156Speter ef->object = 0; 35640156Speter#endif 35740156Speter dp = (vm_offset_t)ef->address + *(vm_offset_t *)dynptr; 35840156Speter ef->dynamic = (Elf_Dyn *)dp; 35940156Speter lf = linker_make_file(filename, ef, &link_elf_module_ops); 36040156Speter if (lf == NULL) { 36140156Speter free(ef, M_LINKER); 36240156Speter return ENOMEM; 36340156Speter } 36440156Speter lf->address = ef->address; 36540156Speter lf->size = *(size_t *)sizeptr; 36640156Speter 36740156Speter error = parse_dynamic(lf); 36840156Speter if (error) { 36940156Speter linker_file_unload(lf); 37040156Speter return error; 37140156Speter } 37240156Speter error = load_dependancies(lf); 37340156Speter if (error) { 37440156Speter linker_file_unload(lf); 37540156Speter return error; 37640156Speter } 37740156Speter error = relocate_file(lf); 37840156Speter if (error) { 37940156Speter linker_file_unload(lf); 38040156Speter return error; 38140156Speter } 38240254Speter (void)parse_module_symbols(lf); 38343185Sdfr lf->flags |= LINKER_FILE_LINKED; 38440156Speter *result = lf; 38540156Speter return (0); 38640156Speter} 38740156Speter 38840156Speterstatic int 38938514Sdfrlink_elf_load_file(const char* filename, linker_file_t* result) 39038514Sdfr{ 39138514Sdfr struct nameidata nd; 39238514Sdfr struct proc* p = curproc; /* XXX */ 39340962Speter Elf_Ehdr *hdr; 39440962Speter caddr_t firstpage; 39539071Sdfr int nbytes, i; 39639071Sdfr Elf_Phdr *phdr; 39739071Sdfr Elf_Phdr *phlimit; 39839071Sdfr Elf_Phdr *segs[2]; 39939071Sdfr int nsegs; 40039071Sdfr Elf_Phdr *phdyn; 40139071Sdfr Elf_Phdr *phphdr; 40239071Sdfr caddr_t mapbase; 40339071Sdfr size_t mapsize; 40439071Sdfr Elf_Off base_offset; 40539071Sdfr Elf_Addr base_vaddr; 40639071Sdfr Elf_Addr base_vlimit; 40738514Sdfr int error = 0; 40838514Sdfr int resid; 40938514Sdfr elf_file_t ef; 41038514Sdfr linker_file_t lf; 41140156Speter char *pathname; 41240292Speter Elf_Shdr *shdr; 41340292Speter int symtabindex; 41440292Speter int symstrindex; 41540292Speter int symcnt; 41640292Speter int strcnt; 41738514Sdfr 41840292Speter shdr = NULL; 41940292Speter lf = NULL; 42040292Speter 42140156Speter pathname = linker_search_path(filename); 42240156Speter if (pathname == NULL) 42340156Speter return ENOENT; 42440962Speter 42540156Speter NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, pathname, p); 42638514Sdfr error = vn_open(&nd, FREAD, 0); 42740156Speter free(pathname, M_LINKER); 42838514Sdfr if (error) 42938514Sdfr return error; 43038514Sdfr 43138514Sdfr /* 43239071Sdfr * Read the elf header from the file. 43338514Sdfr */ 43440962Speter firstpage = malloc(PAGE_SIZE, M_LINKER, M_WAITOK); 43540962Speter if (firstpage == NULL) { 43640962Speter error = ENOMEM; 43740962Speter goto out; 43840962Speter } 43940962Speter hdr = (Elf_Ehdr *)firstpage; 44040962Speter error = vn_rdwr(UIO_READ, nd.ni_vp, firstpage, PAGE_SIZE, 0, 44138514Sdfr UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); 44240962Speter nbytes = PAGE_SIZE - resid; 44338514Sdfr if (error) 44438514Sdfr goto out; 44538514Sdfr 44640962Speter if (!IS_ELF(*hdr)) { 44739071Sdfr error = ENOEXEC; 44838514Sdfr goto out; 44939071Sdfr } 45038514Sdfr 45140962Speter if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS 45240962Speter || hdr->e_ident[EI_DATA] != ELF_TARG_DATA) { 45339071Sdfr link_elf_error("Unsupported file layout"); 45439071Sdfr error = ENOEXEC; 45539071Sdfr goto out; 45639071Sdfr } 45740962Speter if (hdr->e_ident[EI_VERSION] != EV_CURRENT 45840962Speter || hdr->e_version != EV_CURRENT) { 45939071Sdfr link_elf_error("Unsupported file version"); 46039071Sdfr error = ENOEXEC; 46139071Sdfr goto out; 46239071Sdfr } 46340962Speter if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) { 46439071Sdfr link_elf_error("Unsupported file type"); 46539071Sdfr error = ENOEXEC; 46639071Sdfr goto out; 46739071Sdfr } 46840962Speter if (hdr->e_machine != ELF_TARG_MACH) { 46939071Sdfr link_elf_error("Unsupported machine"); 47039071Sdfr error = ENOEXEC; 47139071Sdfr goto out; 47239071Sdfr } 47339071Sdfr 47438514Sdfr /* 47539071Sdfr * We rely on the program header being in the first page. This is 47639071Sdfr * not strictly required by the ABI specification, but it seems to 47739071Sdfr * always true in practice. And, it simplifies things considerably. 47838514Sdfr */ 47940962Speter if (!((hdr->e_phentsize == sizeof(Elf_Phdr)) && 48040962Speter (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= PAGE_SIZE) && 48140962Speter (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= nbytes))) 48239071Sdfr link_elf_error("Unreadable program headers"); 48339071Sdfr 48438514Sdfr /* 48539071Sdfr * Scan the program header entries, and save key information. 48639071Sdfr * 48739071Sdfr * We rely on there being exactly two load segments, text and data, 48839071Sdfr * in that order. 48938514Sdfr */ 49040962Speter phdr = (Elf_Phdr *) (firstpage + hdr->e_phoff); 49140962Speter phlimit = phdr + hdr->e_phnum; 49239071Sdfr nsegs = 0; 49339071Sdfr phdyn = NULL; 49439071Sdfr phphdr = NULL; 49539071Sdfr while (phdr < phlimit) { 49639071Sdfr switch (phdr->p_type) { 49739071Sdfr 49839071Sdfr case PT_LOAD: 49939071Sdfr if (nsegs == 2) { 50039071Sdfr link_elf_error("Too many sections"); 50139071Sdfr error = ENOEXEC; 50239071Sdfr goto out; 50339071Sdfr } 50439071Sdfr segs[nsegs] = phdr; 50539071Sdfr ++nsegs; 50639071Sdfr break; 50739071Sdfr 50839071Sdfr case PT_PHDR: 50939071Sdfr phphdr = phdr; 51039071Sdfr break; 51139071Sdfr 51239071Sdfr case PT_DYNAMIC: 51339071Sdfr phdyn = phdr; 51439071Sdfr break; 51539071Sdfr } 51639071Sdfr 51739071Sdfr ++phdr; 51839071Sdfr } 51939071Sdfr if (phdyn == NULL) { 52039071Sdfr link_elf_error("Object is not dynamically-linked"); 52139071Sdfr error = ENOEXEC; 52238514Sdfr goto out; 52339071Sdfr } 52438514Sdfr 52538514Sdfr /* 52639071Sdfr * Allocate the entire address space of the object, to stake out our 52739071Sdfr * contiguous region, and to establish the base address for relocation. 52838514Sdfr */ 52939071Sdfr base_offset = trunc_page(segs[0]->p_offset); 53039071Sdfr base_vaddr = trunc_page(segs[0]->p_vaddr); 53139071Sdfr base_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_memsz); 53239071Sdfr mapsize = base_vlimit - base_vaddr; 53339071Sdfr 53439071Sdfr ef = malloc(sizeof(struct elf_file), M_LINKER, M_WAITOK); 53540397Speter bzero(ef, sizeof(*ef)); 53639071Sdfr#ifdef SPARSE_MAPPING 53739071Sdfr ef->object = vm_object_allocate(OBJT_DEFAULT, mapsize >> PAGE_SHIFT); 53839071Sdfr if (ef->object == NULL) { 53938514Sdfr free(ef, M_LINKER); 54039071Sdfr error = ENOMEM; 54138514Sdfr goto out; 54238514Sdfr } 54339071Sdfr vm_object_reference(ef->object); 54439071Sdfr ef->address = (caddr_t) vm_map_min(kernel_map); 54539071Sdfr error = vm_map_find(kernel_map, ef->object, 0, 54639071Sdfr (vm_offset_t *) &ef->address, 54739071Sdfr mapsize, 1, 54839071Sdfr VM_PROT_ALL, VM_PROT_ALL, 0); 54939071Sdfr if (error) { 55039071Sdfr vm_object_deallocate(ef->object); 55139071Sdfr free(ef, M_LINKER); 55239071Sdfr goto out; 55339071Sdfr } 55439071Sdfr#else 55539071Sdfr ef->address = malloc(mapsize, M_LINKER, M_WAITOK); 55639071Sdfr#endif 55739071Sdfr mapbase = ef->address; 55838514Sdfr 55939071Sdfr /* 56039071Sdfr * Read the text and data sections and zero the bss. 56139071Sdfr */ 56239071Sdfr for (i = 0; i < 2; i++) { 56339071Sdfr caddr_t segbase = mapbase + segs[i]->p_vaddr - base_vaddr; 56439071Sdfr error = vn_rdwr(UIO_READ, nd.ni_vp, 56539071Sdfr segbase, segs[i]->p_filesz, segs[i]->p_offset, 56639071Sdfr UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); 56739071Sdfr if (error) { 56839071Sdfr#ifdef SPARSE_MAPPING 56939071Sdfr vm_map_remove(kernel_map, (vm_offset_t) ef->address, 57039071Sdfr (vm_offset_t) ef->address 57139071Sdfr + (ef->object->size << PAGE_SHIFT)); 57239071Sdfr vm_object_deallocate(ef->object); 57339071Sdfr#else 57439071Sdfr free(ef->address, M_LINKER); 57539071Sdfr#endif 57639071Sdfr free(ef, M_LINKER); 57739071Sdfr goto out; 57839071Sdfr } 57939071Sdfr bzero(segbase + segs[i]->p_filesz, 58039071Sdfr segs[i]->p_memsz - segs[i]->p_filesz); 58139071Sdfr 58239071Sdfr#ifdef SPARSE_MAPPING 58339071Sdfr /* 58439071Sdfr * Wire down the pages 58539071Sdfr */ 58639071Sdfr vm_map_pageable(kernel_map, 58739071Sdfr (vm_offset_t) segbase, 58839071Sdfr (vm_offset_t) segbase + segs[i]->p_memsz, 58939071Sdfr FALSE); 59039071Sdfr#endif 59139071Sdfr } 59239071Sdfr 59339071Sdfr ef->dynamic = (const Elf_Dyn *) (mapbase + phdyn->p_vaddr - base_vaddr); 59439071Sdfr 59538514Sdfr lf = linker_make_file(filename, ef, &link_elf_file_ops); 59638514Sdfr if (lf == NULL) { 59739071Sdfr#ifdef SPARSE_MAPPING 59839071Sdfr vm_map_remove(kernel_map, (vm_offset_t) ef->address, 59939071Sdfr (vm_offset_t) ef->address 60039071Sdfr + (ef->object->size << PAGE_SHIFT)); 60139071Sdfr vm_object_deallocate(ef->object); 60239071Sdfr#else 60338514Sdfr free(ef->address, M_LINKER); 60439071Sdfr#endif 60538514Sdfr free(ef, M_LINKER); 60638514Sdfr error = ENOMEM; 60738514Sdfr goto out; 60838514Sdfr } 60938514Sdfr lf->address = ef->address; 61039071Sdfr lf->size = mapsize; 61138514Sdfr 61240156Speter error = parse_dynamic(lf); 61340292Speter if (error) 61438514Sdfr goto out; 61540156Speter error = load_dependancies(lf); 61640292Speter if (error) 61740156Speter goto out; 61840156Speter error = relocate_file(lf); 61940292Speter if (error) 62040156Speter goto out; 62140292Speter 62240292Speter /* Try and load the symbol table if it's present. (you can strip it!) */ 62340962Speter nbytes = hdr->e_shnum * hdr->e_shentsize; 62440962Speter if (nbytes == 0 || hdr->e_shoff == 0) 62540292Speter goto nosyms; 62640292Speter shdr = malloc(nbytes, M_LINKER, M_WAITOK); 62740292Speter if (shdr == NULL) { 62840292Speter error = ENOMEM; 62940292Speter goto out; 63040156Speter } 63140397Speter bzero(shdr, nbytes); 63240292Speter error = vn_rdwr(UIO_READ, nd.ni_vp, 63340962Speter (caddr_t)shdr, nbytes, hdr->e_shoff, 63440292Speter UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); 63540292Speter if (error) 63640292Speter goto out; 63740292Speter symtabindex = -1; 63840292Speter symstrindex = -1; 63940962Speter for (i = 0; i < hdr->e_shnum; i++) { 64040292Speter if (shdr[i].sh_type == SHT_SYMTAB) { 64140292Speter symtabindex = i; 64240292Speter symstrindex = shdr[i].sh_link; 64340292Speter } 64440292Speter } 64540292Speter if (symtabindex < 0 || symstrindex < 0) 64640292Speter goto nosyms; 64740156Speter 64840292Speter symcnt = shdr[symtabindex].sh_size; 64940292Speter ef->symbase = malloc(symcnt, M_LINKER, M_WAITOK); 65040292Speter strcnt = shdr[symstrindex].sh_size; 65140292Speter ef->strbase = malloc(strcnt, M_LINKER, M_WAITOK); 65240292Speter 65340292Speter if (ef->symbase == NULL || ef->strbase == NULL) { 65440292Speter error = ENOMEM; 65540292Speter goto out; 65640292Speter } 65740292Speter error = vn_rdwr(UIO_READ, nd.ni_vp, 65840292Speter ef->symbase, symcnt, shdr[symtabindex].sh_offset, 65940292Speter UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); 66040292Speter if (error) 66140292Speter goto out; 66240292Speter error = vn_rdwr(UIO_READ, nd.ni_vp, 66340292Speter ef->strbase, strcnt, shdr[symstrindex].sh_offset, 66440292Speter UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); 66540292Speter if (error) 66640292Speter goto out; 66740292Speter 66840292Speter ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); 66940292Speter ef->ddbsymtab = (const Elf_Sym *)ef->symbase; 67040292Speter ef->ddbstrcnt = strcnt; 67140292Speter ef->ddbstrtab = ef->strbase; 67240292Speter 67343185Sdfr lf->flags |= LINKER_FILE_LINKED; 67443185Sdfr 67540292Speternosyms: 67640292Speter 67738514Sdfr *result = lf; 67838514Sdfr 67938514Sdfrout: 68040292Speter if (error && lf) 68140292Speter linker_file_unload(lf); 68240292Speter if (shdr) 68340292Speter free(shdr, M_LINKER); 68440962Speter if (firstpage) 68540962Speter free(firstpage, M_LINKER); 68638514Sdfr VOP_UNLOCK(nd.ni_vp, 0, p); 68738514Sdfr vn_close(nd.ni_vp, FREAD, p->p_ucred, p); 68838514Sdfr 68938514Sdfr return error; 69038514Sdfr} 69138514Sdfr 69238514Sdfrstatic void 69340156Speterlink_elf_unload_file(linker_file_t file) 69438514Sdfr{ 69538514Sdfr elf_file_t ef = file->priv; 69638514Sdfr 69738514Sdfr if (ef) { 69839071Sdfr#ifdef SPARSE_MAPPING 69939071Sdfr if (ef->object) { 70039071Sdfr vm_map_remove(kernel_map, (vm_offset_t) ef->address, 70139071Sdfr (vm_offset_t) ef->address 70239071Sdfr + (ef->object->size << PAGE_SHIFT)); 70339071Sdfr vm_object_deallocate(ef->object); 70439071Sdfr } 70539071Sdfr#else 70640292Speter if (ef->address) 70740292Speter free(ef->address, M_LINKER); 70839071Sdfr#endif 70940292Speter if (ef->symbase) 71040292Speter free(ef->symbase, M_LINKER); 71140292Speter if (ef->strbase) 71240292Speter free(ef->strbase, M_LINKER); 71338514Sdfr free(ef, M_LINKER); 71438514Sdfr } 71538514Sdfr} 71638514Sdfr 71740156Speterstatic void 71840156Speterlink_elf_unload_module(linker_file_t file) 71940156Speter{ 72040156Speter elf_file_t ef = file->priv; 72140156Speter 72240156Speter if (ef) 72340156Speter free(ef, M_LINKER); 72440156Speter if (file->filename) 72540156Speter preload_delete_name(file->filename); 72640156Speter} 72740156Speter 72838514Sdfrstatic int 72938514Sdfrload_dependancies(linker_file_t lf) 73038514Sdfr{ 73138514Sdfr elf_file_t ef = lf->priv; 73238514Sdfr linker_file_t lfdep; 73338514Sdfr char* name; 73439071Sdfr const Elf_Dyn *dp; 73538514Sdfr int error = 0; 73638514Sdfr 73738514Sdfr /* 73838514Sdfr * All files are dependant on /kernel. 73938514Sdfr */ 74040906Speter if (linker_kernel_file) { 74140906Speter linker_kernel_file->refs++; 74240906Speter linker_file_add_dependancy(lf, linker_kernel_file); 74340906Speter } 74438514Sdfr 74539071Sdfr for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { 74639071Sdfr if (dp->d_tag == DT_NEEDED) { 74739071Sdfr name = ef->strtab + dp->d_un.d_val; 74838514Sdfr 74939071Sdfr error = linker_load_file(name, &lfdep); 75039071Sdfr if (error) 75139071Sdfr goto out; 75239071Sdfr error = linker_file_add_dependancy(lf, lfdep); 75339071Sdfr if (error) 75439071Sdfr goto out; 75538514Sdfr } 75638514Sdfr } 75738514Sdfr 75838514Sdfrout: 75938514Sdfr return error; 76038514Sdfr} 76138514Sdfr 76239071Sdfrstatic const char * 76340435Spetersymbol_name(elf_file_t ef, Elf_Word r_info) 76438514Sdfr{ 76539071Sdfr const Elf_Sym *ref; 76638514Sdfr 76740435Speter if (ELF_R_SYM(r_info)) { 76840435Speter ref = ef->symtab + ELF_R_SYM(r_info); 76940397Speter return ef->strtab + ref->st_name; 77039071Sdfr } else 77139071Sdfr return NULL; 77238514Sdfr} 77338514Sdfr 77438514Sdfrstatic int 77538514Sdfrrelocate_file(linker_file_t lf) 77638514Sdfr{ 77738514Sdfr elf_file_t ef = lf->priv; 77839071Sdfr const Elf_Rel *rellim; 77939071Sdfr const Elf_Rel *rel; 78039071Sdfr const Elf_Rela *relalim; 78139071Sdfr const Elf_Rela *rela; 78240435Speter const char *symname; 78338514Sdfr 78439071Sdfr /* Perform relocations without addend if there are any: */ 78540435Speter rel = ef->rel; 78640435Speter if (rel) { 78743388Sbde rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize); 78840435Speter while (rel < rellim) { 78940435Speter symname = symbol_name(ef, rel->r_info); 79042200Speter if (elf_reloc(lf, rel, ELF_RELOC_REL, symname)) { 79142200Speter printf("link_elf: symbol %s undefined\n", symname); 79240435Speter return ENOENT; 79342200Speter } 79440435Speter rel++; 79540435Speter } 79639071Sdfr } 79738514Sdfr 79839071Sdfr /* Perform relocations with addend if there are any: */ 79940435Speter rela = ef->rela; 80040435Speter if (rela) { 80143388Sbde relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize); 80240435Speter while (rela < relalim) { 80340435Speter symname = symbol_name(ef, rela->r_info); 80442200Speter if (elf_reloc(lf, rela, ELF_RELOC_RELA, symname)) { 80542200Speter printf("link_elf: symbol %s undefined\n", symname); 80640435Speter return ENOENT; 80742200Speter } 80840435Speter rela++; 80940435Speter } 81039071Sdfr } 81138514Sdfr 81239071Sdfr /* Perform PLT relocations without addend if there are any: */ 81340435Speter rel = ef->pltrel; 81440435Speter if (rel) { 81543388Sbde rellim = (const Elf_Rel *)((const char *)ef->pltrel + ef->pltrelsize); 81640435Speter while (rel < rellim) { 81740435Speter symname = symbol_name(ef, rel->r_info); 81842200Speter if (elf_reloc(lf, rel, ELF_RELOC_REL, symname)) { 81942200Speter printf("link_elf: symbol %s undefined\n", symname); 82040435Speter return ENOENT; 82142200Speter } 82240435Speter rel++; 82340435Speter } 82439071Sdfr } 82538514Sdfr 82639071Sdfr /* Perform relocations with addend if there are any: */ 82740435Speter rela = ef->pltrela; 82840435Speter if (rela) { 82943388Sbde relalim = (const Elf_Rela *)((const char *)ef->pltrela + ef->pltrelasize); 83040435Speter while (rela < relalim) { 83140435Speter symname = symbol_name(ef, rela->r_info); 83242200Speter if (elf_reloc(lf, rela, ELF_RELOC_RELA, symname)) { 83342200Speter printf("link_elf: symbol %s undefined\n", symname); 83440435Speter return ENOENT; 83542200Speter } 83640435Speter rela++; 83740435Speter } 83838514Sdfr } 83938514Sdfr 84038514Sdfr return 0; 84138514Sdfr} 84238514Sdfr 84339071Sdfr/* 84439071Sdfr * Hash function for symbol table lookup. Don't even think about changing 84539071Sdfr * this. It is specified by the System V ABI. 84639071Sdfr */ 84739071Sdfrstatic unsigned long 84839071Sdfrelf_hash(const char *name) 84938514Sdfr{ 85039071Sdfr const unsigned char *p = (const unsigned char *) name; 85139071Sdfr unsigned long h = 0; 85239071Sdfr unsigned long g; 85338514Sdfr 85439071Sdfr while (*p != '\0') { 85539071Sdfr h = (h << 4) + *p++; 85639071Sdfr if ((g = h & 0xf0000000) != 0) 85739071Sdfr h ^= g >> 24; 85839071Sdfr h &= ~g; 85939071Sdfr } 86039071Sdfr return h; 86138514Sdfr} 86238514Sdfr 86338514Sdfrint 86443301Sdillonlink_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym) 86538514Sdfr{ 86639071Sdfr elf_file_t ef = lf->priv; 86739071Sdfr unsigned long symnum; 86840254Speter const Elf_Sym* symp; 86940254Speter const char *strp; 87039071Sdfr unsigned long hash; 87139071Sdfr int i; 87238514Sdfr 87340254Speter /* First, search hashed global symbols */ 87439071Sdfr hash = elf_hash(name); 87539071Sdfr symnum = ef->buckets[hash % ef->nbuckets]; 87639071Sdfr 87739071Sdfr while (symnum != STN_UNDEF) { 87839071Sdfr if (symnum >= ef->nchains) { 87939071Sdfr printf("link_elf_lookup_symbol: corrupt symbol table\n"); 88039071Sdfr return ENOENT; 88138514Sdfr } 88238514Sdfr 88339071Sdfr symp = ef->symtab + symnum; 88439071Sdfr if (symp->st_name == 0) { 88539071Sdfr printf("link_elf_lookup_symbol: corrupt symbol table\n"); 88639071Sdfr return ENOENT; 88739071Sdfr } 88839071Sdfr 88939071Sdfr strp = ef->strtab + symp->st_name; 89039071Sdfr 89139071Sdfr if (strcmp(name, strp) == 0) { 89239071Sdfr if (symp->st_shndx != SHN_UNDEF || 89339071Sdfr (symp->st_value != 0 && 89439071Sdfr ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { 89543301Sdillon *sym = (c_linker_sym_t) symp; 89639071Sdfr return 0; 89739071Sdfr } else 89839071Sdfr return ENOENT; 89939071Sdfr } 90039071Sdfr 90139071Sdfr symnum = ef->chains[symnum]; 90239071Sdfr } 90339071Sdfr 90440254Speter /* If we have not found it, look at the full table (if loaded) */ 90540254Speter if (ef->symtab == ef->ddbsymtab) 90640254Speter return ENOENT; 90740254Speter 90840254Speter /* Exhaustive search */ 90940254Speter for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { 91040254Speter strp = ef->ddbstrtab + symp->st_name; 91140254Speter if (strcmp(name, strp) == 0) { 91240254Speter if (symp->st_shndx != SHN_UNDEF || 91340254Speter (symp->st_value != 0 && 91440254Speter ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { 91543301Sdillon *sym = (c_linker_sym_t) symp; 91640254Speter return 0; 91740254Speter } else 91840254Speter return ENOENT; 91940254Speter } 92040254Speter } 92140254Speter 92239071Sdfr return ENOENT; 92338514Sdfr} 92438514Sdfr 92540156Speterstatic int 92643309Sdillonlink_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, linker_symval_t* symval) 92738514Sdfr{ 92838514Sdfr elf_file_t ef = lf->priv; 92943311Sdillon const Elf_Sym* es = (const Elf_Sym*) sym; 93038514Sdfr 93140254Speter if (es >= ef->symtab && ((es - ef->symtab) < ef->nchains)) { 93240254Speter symval->name = ef->strtab + es->st_name; 93340254Speter symval->value = (caddr_t) ef->address + es->st_value; 93440254Speter symval->size = es->st_size; 93540254Speter return 0; 93640254Speter } 93740254Speter if (ef->symtab == ef->ddbsymtab) 93840254Speter return ENOENT; 93940254Speter if (es >= ef->ddbsymtab && ((es - ef->ddbsymtab) < ef->ddbsymcnt)) { 94040254Speter symval->name = ef->ddbstrtab + es->st_name; 94140254Speter symval->value = (caddr_t) ef->address + es->st_value; 94240254Speter symval->size = es->st_size; 94340254Speter return 0; 94440254Speter } 94540254Speter return ENOENT; 94638514Sdfr} 94738514Sdfr 94838514Sdfrstatic int 94938514Sdfrlink_elf_search_symbol(linker_file_t lf, caddr_t value, 95043301Sdillon c_linker_sym_t* sym, long* diffp) 95138514Sdfr{ 95238514Sdfr elf_file_t ef = lf->priv; 95338514Sdfr u_long off = (u_long) value; 95438514Sdfr u_long diff = off; 95539071Sdfr const Elf_Sym* es; 95639071Sdfr const Elf_Sym* best = 0; 95738514Sdfr int i; 95838514Sdfr 95940254Speter for (i = 0, es = ef->ddbsymtab; i < ef->ddbsymcnt; i++, es++) { 96038514Sdfr if (es->st_name == 0) 96138514Sdfr continue; 96238514Sdfr if (off >= es->st_value) { 96338514Sdfr if (off - es->st_value < diff) { 96438514Sdfr diff = off - es->st_value; 96538514Sdfr best = es; 96638514Sdfr if (diff == 0) 96738514Sdfr break; 96838514Sdfr } else if (off - es->st_value == diff) { 96938514Sdfr best = es; 97038514Sdfr } 97138514Sdfr } 97238514Sdfr } 97338514Sdfr if (best == 0) 97438514Sdfr *diffp = off; 97538514Sdfr else 97638514Sdfr *diffp = diff; 97743301Sdillon *sym = (c_linker_sym_t) best; 97838514Sdfr 97938514Sdfr return 0; 98038514Sdfr} 981