link_elf.c revision 105468
138514Sdfr/*- 259603Sdfr * Copyright (c) 1998-2000 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 * 2650477Speter * $FreeBSD: head/sys/kern/link_elf.c 105468 2002-10-19 18:59:33Z marcel $ 2738514Sdfr */ 2838514Sdfr 2959603Sdfr#include "opt_ddb.h" 3059603Sdfr 3138514Sdfr#include <sys/param.h> 3276166Smarkm#include <sys/systm.h> 3338514Sdfr#include <sys/kernel.h> 3476166Smarkm#include <sys/lock.h> 3538514Sdfr#include <sys/malloc.h> 3677642Sdd#include <sys/mutex.h> 3738514Sdfr#include <sys/proc.h> 3838514Sdfr#include <sys/namei.h> 3938514Sdfr#include <sys/fcntl.h> 4038514Sdfr#include <sys/vnode.h> 4138514Sdfr#include <sys/linker.h> 4276166Smarkm 4338514Sdfr#include <machine/elf.h> 4485735Sgreen#ifdef GPROF 4585735Sgreen#include <machine/profile.h> 4685735Sgreen#endif 4738514Sdfr 4839071Sdfr#include <vm/vm.h> 4939071Sdfr#include <vm/vm_param.h> 5052128Speter#ifdef SPARSE_MAPPING 5139071Sdfr#include <vm/vm_object.h> 5239071Sdfr#include <vm/vm_kern.h> 5339071Sdfr#include <vm/vm_extern.h> 5452128Speter#endif 5539071Sdfr#include <vm/pmap.h> 5639071Sdfr#include <vm/vm_map.h> 5776166Smarkm 58102288Speter#include <sys/link_elf.h> 5939071Sdfr 6059603Sdfr#include "linker_if.h" 6138514Sdfr 6238514Sdfrtypedef struct elf_file { 6359603Sdfr struct linker_file lf; /* Common fields */ 6459603Sdfr int preloaded; /* Was file pre-loaded */ 6539071Sdfr caddr_t address; /* Relocation address */ 6639071Sdfr#ifdef SPARSE_MAPPING 6739071Sdfr vm_object_t object; /* VM object to hold file pages */ 6839071Sdfr#endif 6959603Sdfr Elf_Dyn* dynamic; /* Symbol table etc. */ 7080700Sjake Elf_Hashelt nbuckets; /* DT_HASH info */ 7180700Sjake Elf_Hashelt nchains; 7280700Sjake const Elf_Hashelt* buckets; 7380700Sjake const Elf_Hashelt* chains; 7438514Sdfr caddr_t hash; 7538514Sdfr caddr_t strtab; /* DT_STRTAB */ 7640254Speter int strsz; /* DT_STRSZ */ 7739071Sdfr const Elf_Sym* symtab; /* DT_SYMTAB */ 7839071Sdfr Elf_Addr* got; /* DT_PLTGOT */ 7939071Sdfr const Elf_Rel* pltrel; /* DT_JMPREL */ 8039071Sdfr int pltrelsize; /* DT_PLTRELSZ */ 8139071Sdfr const Elf_Rela* pltrela; /* DT_JMPREL */ 8239071Sdfr int pltrelasize; /* DT_PLTRELSZ */ 8339071Sdfr const Elf_Rel* rel; /* DT_REL */ 8439071Sdfr int relsize; /* DT_RELSZ */ 8539071Sdfr const Elf_Rela* rela; /* DT_RELA */ 8639071Sdfr int relasize; /* DT_RELASZ */ 8740254Speter caddr_t modptr; 8840254Speter const Elf_Sym* ddbsymtab; /* The symbol table we are using */ 8940254Speter long ddbsymcnt; /* Number of symbols */ 9040254Speter caddr_t ddbstrtab; /* String table */ 9140254Speter long ddbstrcnt; /* number of bytes in string table */ 9240292Speter caddr_t symbase; /* malloc'ed symbold base */ 9340292Speter caddr_t strbase; /* malloc'ed string base */ 9459603Sdfr#ifdef DDB 9559603Sdfr struct link_map gdb; /* hooks for gdb */ 9659603Sdfr#endif 9738514Sdfr} *elf_file_t; 9838514Sdfr 99105468Smarcelstatic int link_elf_link_common_finish(linker_file_t); 10059751Speterstatic int link_elf_link_preload(linker_class_t cls, 10159751Speter const char*, linker_file_t*); 10259751Speterstatic int link_elf_link_preload_finish(linker_file_t); 10359751Speterstatic int link_elf_load_file(linker_class_t, const char*, linker_file_t*); 10459603Sdfrstatic int link_elf_lookup_symbol(linker_file_t, const char*, 10559603Sdfr c_linker_sym_t*); 10659603Sdfrstatic int link_elf_symbol_values(linker_file_t, c_linker_sym_t, linker_symval_t*); 10759603Sdfrstatic int link_elf_search_symbol(linker_file_t, caddr_t value, 10859603Sdfr c_linker_sym_t* sym, long* diffp); 10938514Sdfr 11059603Sdfrstatic void link_elf_unload_file(linker_file_t); 11159751Speterstatic void link_elf_unload_preload(linker_file_t); 11278161Speterstatic int link_elf_lookup_set(linker_file_t, const char *, 11378161Speter void ***, void ***, int *); 11485736Sgreenstatic int link_elf_each_function_name(linker_file_t, 11585736Sgreen int (*)(const char *, void *), 11685736Sgreen void *); 11759603Sdfr 11859603Sdfrstatic kobj_method_t link_elf_methods[] = { 11959603Sdfr KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol), 12059603Sdfr KOBJMETHOD(linker_symbol_values, link_elf_symbol_values), 12159603Sdfr KOBJMETHOD(linker_search_symbol, link_elf_search_symbol), 12259603Sdfr KOBJMETHOD(linker_unload, link_elf_unload_file), 12359751Speter KOBJMETHOD(linker_load_file, link_elf_load_file), 12459751Speter KOBJMETHOD(linker_link_preload, link_elf_link_preload), 12559751Speter KOBJMETHOD(linker_link_preload_finish, link_elf_link_preload_finish), 12678161Speter KOBJMETHOD(linker_lookup_set, link_elf_lookup_set), 12785736Sgreen KOBJMETHOD(linker_each_function_name, link_elf_each_function_name), 12859603Sdfr { 0, 0 } 12959603Sdfr}; 13059603Sdfr 13159603Sdfrstatic struct linker_class link_elf_class = { 13259603Sdfr#if ELF_TARG_CLASS == ELFCLASS32 13359603Sdfr "elf32", 13459603Sdfr#else 13559603Sdfr "elf64", 13659603Sdfr#endif 13759603Sdfr link_elf_methods, sizeof(struct elf_file) 13859603Sdfr}; 13959603Sdfr 14059603Sdfrstatic int parse_dynamic(elf_file_t ef); 14159603Sdfrstatic int relocate_file(elf_file_t ef); 14259751Speterstatic int link_elf_preload_parse_symbols(elf_file_t ef); 14359603Sdfr 14459603Sdfr#ifdef DDB 14566719Sjhbstatic void r_debug_state(struct r_debug *dummy_one, 14666719Sjhb struct link_map *dummy_two); 14759603Sdfr 14838514Sdfr/* 14959603Sdfr * A list of loaded modules for GDB to use for loading symbols. 15059603Sdfr */ 15159603Sdfrstruct r_debug r_debug; 15259603Sdfr 15366719Sjhb#define GDB_STATE(s) r_debug.r_state = s; r_debug_state(NULL, NULL); 15459603Sdfr 15559603Sdfr/* 15659603Sdfr * Function for the debugger to set a breakpoint on to gain control. 15759603Sdfr */ 158104094Sphkstatic void 15966719Sjhbr_debug_state(struct r_debug *dummy_one __unused, 16066719Sjhb struct link_map *dummy_two __unused) 16159603Sdfr{ 16259603Sdfr} 16359603Sdfr 164105467Smarcelstatic void 165105467Smarcellink_elf_add_gdb(struct link_map *l) 166105467Smarcel{ 167105467Smarcel struct link_map *prev; 16859603Sdfr 169105468Smarcel l->l_next = NULL; 170105467Smarcel 171105468Smarcel if (r_debug.r_map == NULL) { 172105468Smarcel /* Add first. */ 173105468Smarcel l->l_prev = NULL; 174105468Smarcel r_debug.r_map = l; 175105468Smarcel } else { 176105468Smarcel /* Append to list. */ 177105468Smarcel for (prev = r_debug.r_map; prev->l_next != NULL; prev = prev->l_next) 178105468Smarcel ; 179105468Smarcel l->l_prev = prev; 180105468Smarcel prev->l_next = l; 181105468Smarcel } 182105467Smarcel} 183105467Smarcel 184105467Smarcelstatic void 185105467Smarcellink_elf_delete_gdb(struct link_map *l) 186105467Smarcel{ 187105467Smarcel if (l->l_prev == NULL) { 188105468Smarcel /* Remove first. */ 189105467Smarcel if ((r_debug.r_map = l->l_next) != NULL) 190105467Smarcel l->l_next->l_prev = NULL; 191105468Smarcel } else { 192105468Smarcel /* Remove any but first. */ 193105468Smarcel if ((l->l_prev->l_next = l->l_next) != NULL) 194105468Smarcel l->l_next->l_prev = l->l_prev; 195105467Smarcel } 196105467Smarcel} 197105467Smarcel#endif /* DDB */ 198105467Smarcel 19995228Smarcel#ifdef __ia64__ 20095228SmarcelElf_Addr link_elf_get_gp(linker_file_t); 20195228Smarcel#endif 20295228Smarcel 20359603Sdfr/* 20438514Sdfr * The kernel symbol table starts here. 20538514Sdfr */ 20638514Sdfrextern struct _dynamic _DYNAMIC; 20738514Sdfr 20838514Sdfrstatic void 209105467Smarcellink_elf_error(const char *s) 210105467Smarcel{ 211105467Smarcel printf("kldload: %s\n", s); 212105467Smarcel} 213105467Smarcel 214105468Smarcel/* 215105468Smarcel * Actions performed after linking/loading both the preloaded kernel and any 216105468Smarcel * modules; whether preloaded or dynamicly loaded. 217105468Smarcel */ 218105468Smarcelstatic int 219105468Smarcellink_elf_link_common_finish(linker_file_t lf) 220105468Smarcel{ 221105468Smarcel#ifdef DDB 222105468Smarcel elf_file_t ef = (elf_file_t)lf; 223105468Smarcel char *newfilename; 224105468Smarcel#endif 225105468Smarcel 226105468Smarcel#ifdef DDB 227105468Smarcel GDB_STATE(RT_ADD); 228105468Smarcel ef->gdb.l_addr = lf->address; 229105468Smarcel newfilename = malloc(strlen(lf->filename) + 1, M_LINKER, M_WAITOK); 230105468Smarcel strcpy(newfilename, lf->filename); 231105468Smarcel ef->gdb.l_name = newfilename; 232105468Smarcel ef->gdb.l_ld = ef->dynamic; 233105468Smarcel link_elf_add_gdb(&ef->gdb); 234105468Smarcel GDB_STATE(RT_CONSISTENT); 235105468Smarcel#endif 236105468Smarcel 237105468Smarcel return (0); 238105468Smarcel} 239105468Smarcel 240105467Smarcelstatic void 24138514Sdfrlink_elf_init(void* arg) 24238514Sdfr{ 24340156Speter Elf_Dyn *dp; 24440156Speter caddr_t modptr, baseptr, sizeptr; 24540156Speter elf_file_t ef; 24640156Speter char *modname; 24738514Sdfr 24859603Sdfr linker_add_class(&link_elf_class); 24938514Sdfr 25040156Speter dp = (Elf_Dyn*) &_DYNAMIC; 25182848Speter modname = NULL; 25282848Speter modptr = preload_search_by_type("elf kernel"); 25382848Speter if (modptr) 25482848Speter modname = (char *)preload_search_info(modptr, MODINFO_NAME); 25582848Speter if (modname == NULL) 25682848Speter modname = "kernel"; 25782848Speter linker_kernel_file = linker_make_file(modname, &link_elf_class); 25882848Speter if (linker_kernel_file == NULL) 25982848Speter panic("link_elf_init: Can't create linker structures for kernel"); 260105468Smarcel 26182848Speter ef = (elf_file_t) linker_kernel_file; 26282848Speter ef->preloaded = 1; 26382848Speter ef->address = 0; 26459603Sdfr#ifdef SPARSE_MAPPING 26582848Speter ef->object = 0; 26659603Sdfr#endif 26782848Speter ef->dynamic = dp; 26859603Sdfr 26982848Speter if (dp) 27082848Speter parse_dynamic(ef); 27182848Speter linker_kernel_file->address = (caddr_t) KERNBASE; 27282848Speter linker_kernel_file->size = -(intptr_t)linker_kernel_file->address; 27340156Speter 27482848Speter if (modptr) { 27582848Speter ef->modptr = modptr; 27682848Speter baseptr = preload_search_info(modptr, MODINFO_ADDR); 27782848Speter if (baseptr) 27882848Speter linker_kernel_file->address = *(caddr_t *)baseptr; 27982848Speter sizeptr = preload_search_info(modptr, MODINFO_SIZE); 28082848Speter if (sizeptr) 28182848Speter linker_kernel_file->size = *(size_t *)sizeptr; 28282848Speter } 28382848Speter (void)link_elf_preload_parse_symbols(ef); 28459603Sdfr 28559603Sdfr#ifdef DDB 286105468Smarcel r_debug.r_map = NULL; 28782848Speter r_debug.r_brk = r_debug_state; 28882848Speter r_debug.r_state = RT_CONSISTENT; 289105468Smarcel#endif 29059603Sdfr 291105468Smarcel (void)link_elf_link_common_finish(linker_kernel_file); 29238514Sdfr} 29338514Sdfr 29440156SpeterSYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0); 29538514Sdfr 29638514Sdfrstatic int 29759751Speterlink_elf_preload_parse_symbols(elf_file_t ef) 29840254Speter{ 29940254Speter caddr_t pointer; 30040254Speter caddr_t ssym, esym, base; 30140254Speter caddr_t strtab; 30240254Speter int strcnt; 30340254Speter Elf_Sym* symtab; 30440254Speter int symcnt; 30540254Speter 30640292Speter if (ef->modptr == NULL) 30740292Speter return 0; 30840254Speter pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_SSYM); 30940254Speter if (pointer == NULL) 31040254Speter return 0; 31140254Speter ssym = *(caddr_t *)pointer; 31240254Speter pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_ESYM); 31340254Speter if (pointer == NULL) 31440254Speter return 0; 31540254Speter esym = *(caddr_t *)pointer; 31640254Speter 31740254Speter base = ssym; 31840254Speter 31940254Speter symcnt = *(long *)base; 32040254Speter base += sizeof(long); 32140254Speter symtab = (Elf_Sym *)base; 32240254Speter base += roundup(symcnt, sizeof(long)); 32340254Speter 32440254Speter if (base > esym || base < ssym) { 32540254Speter printf("Symbols are corrupt!\n"); 32640254Speter return EINVAL; 32740254Speter } 32840254Speter 32940254Speter strcnt = *(long *)base; 33040254Speter base += sizeof(long); 33140254Speter strtab = base; 33240254Speter base += roundup(strcnt, sizeof(long)); 33340254Speter 33440254Speter if (base > esym || base < ssym) { 33540254Speter printf("Symbols are corrupt!\n"); 33640254Speter return EINVAL; 33740254Speter } 33840254Speter 33940254Speter ef->ddbsymtab = symtab; 34040254Speter ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); 34140254Speter ef->ddbstrtab = strtab; 34240254Speter ef->ddbstrcnt = strcnt; 34340254Speter 34440254Speter return 0; 34540254Speter} 34640254Speter 34740254Speterstatic int 34859603Sdfrparse_dynamic(elf_file_t ef) 34938514Sdfr{ 35059603Sdfr Elf_Dyn *dp; 35139071Sdfr int plttype = DT_REL; 35238514Sdfr 35338514Sdfr for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { 35438514Sdfr switch (dp->d_tag) { 35538514Sdfr case DT_HASH: 35638514Sdfr { 35738514Sdfr /* From src/libexec/rtld-elf/rtld.c */ 35880700Sjake const Elf_Hashelt *hashtab = (const Elf_Hashelt *) 35938514Sdfr (ef->address + dp->d_un.d_ptr); 36038514Sdfr ef->nbuckets = hashtab[0]; 36138514Sdfr ef->nchains = hashtab[1]; 36238514Sdfr ef->buckets = hashtab + 2; 36338514Sdfr ef->chains = ef->buckets + ef->nbuckets; 36438514Sdfr break; 36538514Sdfr } 36638514Sdfr case DT_STRTAB: 36739071Sdfr ef->strtab = (caddr_t) (ef->address + dp->d_un.d_ptr); 36838514Sdfr break; 36940254Speter case DT_STRSZ: 37040254Speter ef->strsz = dp->d_un.d_val; 37140254Speter break; 37238514Sdfr case DT_SYMTAB: 37339071Sdfr ef->symtab = (Elf_Sym*) (ef->address + dp->d_un.d_ptr); 37438514Sdfr break; 37538514Sdfr case DT_SYMENT: 37638514Sdfr if (dp->d_un.d_val != sizeof(Elf_Sym)) 37738514Sdfr return ENOEXEC; 37839071Sdfr break; 37939071Sdfr case DT_PLTGOT: 38039071Sdfr ef->got = (Elf_Addr *) (ef->address + dp->d_un.d_ptr); 38139071Sdfr break; 38239071Sdfr case DT_REL: 38339071Sdfr ef->rel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); 38439071Sdfr break; 38539071Sdfr case DT_RELSZ: 38639071Sdfr ef->relsize = dp->d_un.d_val; 38739071Sdfr break; 38839071Sdfr case DT_RELENT: 38939071Sdfr if (dp->d_un.d_val != sizeof(Elf_Rel)) 39039071Sdfr return ENOEXEC; 39139071Sdfr break; 39239071Sdfr case DT_JMPREL: 39339071Sdfr ef->pltrel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); 39439071Sdfr break; 39539071Sdfr case DT_PLTRELSZ: 39639071Sdfr ef->pltrelsize = dp->d_un.d_val; 39739071Sdfr break; 39839071Sdfr case DT_RELA: 39939071Sdfr ef->rela = (const Elf_Rela *) (ef->address + dp->d_un.d_ptr); 40039071Sdfr break; 40139071Sdfr case DT_RELASZ: 40239071Sdfr ef->relasize = dp->d_un.d_val; 40339071Sdfr break; 40439071Sdfr case DT_RELAENT: 40539071Sdfr if (dp->d_un.d_val != sizeof(Elf_Rela)) 40639071Sdfr return ENOEXEC; 40739071Sdfr break; 40839071Sdfr case DT_PLTREL: 40939071Sdfr plttype = dp->d_un.d_val; 41039071Sdfr if (plttype != DT_REL && plttype != DT_RELA) 41139071Sdfr return ENOEXEC; 41239071Sdfr break; 41359603Sdfr#ifdef DDB 41459603Sdfr case DT_DEBUG: 41559603Sdfr dp->d_un.d_ptr = (Elf_Addr) &r_debug; 41659603Sdfr break; 41759603Sdfr#endif 41838514Sdfr } 41938514Sdfr } 42039071Sdfr 42139071Sdfr if (plttype == DT_RELA) { 42239071Sdfr ef->pltrela = (const Elf_Rela *) ef->pltrel; 42339071Sdfr ef->pltrel = NULL; 42439071Sdfr ef->pltrelasize = ef->pltrelsize; 42539071Sdfr ef->pltrelsize = 0; 42639071Sdfr } 42739071Sdfr 42840254Speter ef->ddbsymtab = ef->symtab; 42940254Speter ef->ddbsymcnt = ef->nchains; 43040254Speter ef->ddbstrtab = ef->strtab; 43140254Speter ef->ddbstrcnt = ef->strsz; 43240254Speter 43338514Sdfr return 0; 43438514Sdfr} 43538514Sdfr 43638514Sdfrstatic int 43759751Speterlink_elf_link_preload(linker_class_t cls, 43859751Speter const char* filename, linker_file_t *result) 43940156Speter{ 44040156Speter caddr_t modptr, baseptr, sizeptr, dynptr; 44140156Speter char *type; 44240156Speter elf_file_t ef; 44340156Speter linker_file_t lf; 44440156Speter int error; 44540156Speter vm_offset_t dp; 44640156Speter 44759751Speter /* Look to see if we have the file preloaded */ 44840156Speter modptr = preload_search_by_name(filename); 44940156Speter if (modptr == NULL) 45059751Speter return ENOENT; 45140156Speter 45240156Speter type = (char *)preload_search_info(modptr, MODINFO_TYPE); 45340156Speter baseptr = preload_search_info(modptr, MODINFO_ADDR); 45440156Speter sizeptr = preload_search_info(modptr, MODINFO_SIZE); 45540156Speter dynptr = preload_search_info(modptr, MODINFO_METADATA|MODINFOMD_DYNAMIC); 45640156Speter if (type == NULL || strcmp(type, "elf module") != 0) 45740156Speter return (EFTYPE); 45840156Speter if (baseptr == NULL || sizeptr == NULL || dynptr == NULL) 45940156Speter return (EINVAL); 46040156Speter 46159603Sdfr lf = linker_make_file(filename, &link_elf_class); 46259603Sdfr if (lf == NULL) { 46359603Sdfr return ENOMEM; 46459603Sdfr } 46559603Sdfr 46659603Sdfr ef = (elf_file_t) lf; 46759603Sdfr ef->preloaded = 1; 46840292Speter ef->modptr = modptr; 46940156Speter ef->address = *(caddr_t *)baseptr; 47040156Speter#ifdef SPARSE_MAPPING 47140156Speter ef->object = 0; 47240156Speter#endif 47340156Speter dp = (vm_offset_t)ef->address + *(vm_offset_t *)dynptr; 47440156Speter ef->dynamic = (Elf_Dyn *)dp; 47540156Speter lf->address = ef->address; 47640156Speter lf->size = *(size_t *)sizeptr; 47740156Speter 47859603Sdfr error = parse_dynamic(ef); 47940156Speter if (error) { 48040156Speter linker_file_unload(lf); 48140156Speter return error; 48240156Speter } 48359751Speter *result = lf; 48459751Speter return (0); 48559751Speter} 48659751Speter 48759751Speterstatic int 48859751Speterlink_elf_link_preload_finish(linker_file_t lf) 48959751Speter{ 49059751Speter elf_file_t ef; 49159751Speter int error; 49259751Speter 49359751Speter ef = (elf_file_t) lf; 49459751Speter#if 0 /* this will be more trouble than it's worth for now */ 49559751Speter for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { 49659751Speter if (dp->d_tag != DT_NEEDED) 49759751Speter continue; 49859751Speter modname = ef->strtab + dp->d_un.d_val; 49959751Speter error = linker_load_module(modname, lf); 50059751Speter if (error) 50159751Speter goto out; 50240156Speter } 50359751Speter#endif 50459603Sdfr error = relocate_file(ef); 50559751Speter if (error) 50640156Speter return error; 50759751Speter (void)link_elf_preload_parse_symbols(ef); 50859603Sdfr 509105468Smarcel return (link_elf_link_common_finish(lf)); 51040156Speter} 51140156Speter 51240156Speterstatic int 513105468Smarcellink_elf_load_file(linker_class_t cls, const char* filename, 514105468Smarcel linker_file_t* result) 51538514Sdfr{ 51638514Sdfr struct nameidata nd; 51783366Sjulian struct thread* td = curthread; /* XXX */ 51840962Speter Elf_Ehdr *hdr; 51940962Speter caddr_t firstpage; 52039071Sdfr int nbytes, i; 52139071Sdfr Elf_Phdr *phdr; 52239071Sdfr Elf_Phdr *phlimit; 52339071Sdfr Elf_Phdr *segs[2]; 52439071Sdfr int nsegs; 52539071Sdfr Elf_Phdr *phdyn; 52639071Sdfr Elf_Phdr *phphdr; 52739071Sdfr caddr_t mapbase; 52839071Sdfr size_t mapsize; 52939071Sdfr Elf_Off base_offset; 53039071Sdfr Elf_Addr base_vaddr; 53139071Sdfr Elf_Addr base_vlimit; 53238514Sdfr int error = 0; 53362550Smckusick int resid, flags; 53438514Sdfr elf_file_t ef; 53538514Sdfr linker_file_t lf; 53640292Speter Elf_Shdr *shdr; 53740292Speter int symtabindex; 53840292Speter int symstrindex; 53940292Speter int symcnt; 54040292Speter int strcnt; 54138514Sdfr 54279224Sdillon GIANT_REQUIRED; 54379224Sdillon 54440292Speter shdr = NULL; 54540292Speter lf = NULL; 54640292Speter 54783366Sjulian NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, td); 54862550Smckusick flags = FREAD; 54962550Smckusick error = vn_open(&nd, &flags, 0); 55038514Sdfr if (error) 55138514Sdfr return error; 55254655Seivind NDFREE(&nd, NDF_ONLY_PNBUF); 55338514Sdfr 55438514Sdfr /* 55539071Sdfr * Read the elf header from the file. 55638514Sdfr */ 55740962Speter firstpage = malloc(PAGE_SIZE, M_LINKER, M_WAITOK); 55840962Speter if (firstpage == NULL) { 55940962Speter error = ENOMEM; 56040962Speter goto out; 56140962Speter } 56240962Speter hdr = (Elf_Ehdr *)firstpage; 56340962Speter error = vn_rdwr(UIO_READ, nd.ni_vp, firstpage, PAGE_SIZE, 0, 564101941Srwatson UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, 565101941Srwatson &resid, td); 56640962Speter nbytes = PAGE_SIZE - resid; 56738514Sdfr if (error) 56838514Sdfr goto out; 56938514Sdfr 57040962Speter if (!IS_ELF(*hdr)) { 57139071Sdfr error = ENOEXEC; 57238514Sdfr goto out; 57339071Sdfr } 57438514Sdfr 57540962Speter if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS 57640962Speter || hdr->e_ident[EI_DATA] != ELF_TARG_DATA) { 57739071Sdfr link_elf_error("Unsupported file layout"); 57839071Sdfr error = ENOEXEC; 57939071Sdfr goto out; 58039071Sdfr } 58140962Speter if (hdr->e_ident[EI_VERSION] != EV_CURRENT 58240962Speter || hdr->e_version != EV_CURRENT) { 58339071Sdfr link_elf_error("Unsupported file version"); 58439071Sdfr error = ENOEXEC; 58539071Sdfr goto out; 58639071Sdfr } 58740962Speter if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) { 58839071Sdfr link_elf_error("Unsupported file type"); 58939071Sdfr error = ENOEXEC; 59039071Sdfr goto out; 59139071Sdfr } 59240962Speter if (hdr->e_machine != ELF_TARG_MACH) { 59339071Sdfr link_elf_error("Unsupported machine"); 59439071Sdfr error = ENOEXEC; 59539071Sdfr goto out; 59639071Sdfr } 59739071Sdfr 59838514Sdfr /* 59939071Sdfr * We rely on the program header being in the first page. This is 60039071Sdfr * not strictly required by the ABI specification, but it seems to 60139071Sdfr * always true in practice. And, it simplifies things considerably. 60238514Sdfr */ 60340962Speter if (!((hdr->e_phentsize == sizeof(Elf_Phdr)) && 60440962Speter (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= PAGE_SIZE) && 60540962Speter (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= nbytes))) 60639071Sdfr link_elf_error("Unreadable program headers"); 60739071Sdfr 60838514Sdfr /* 60939071Sdfr * Scan the program header entries, and save key information. 61039071Sdfr * 61139071Sdfr * We rely on there being exactly two load segments, text and data, 61239071Sdfr * in that order. 61338514Sdfr */ 61440962Speter phdr = (Elf_Phdr *) (firstpage + hdr->e_phoff); 61540962Speter phlimit = phdr + hdr->e_phnum; 61639071Sdfr nsegs = 0; 61739071Sdfr phdyn = NULL; 61839071Sdfr phphdr = NULL; 61939071Sdfr while (phdr < phlimit) { 62039071Sdfr switch (phdr->p_type) { 62139071Sdfr 62239071Sdfr case PT_LOAD: 62339071Sdfr if (nsegs == 2) { 62439071Sdfr link_elf_error("Too many sections"); 62539071Sdfr error = ENOEXEC; 62639071Sdfr goto out; 62739071Sdfr } 62839071Sdfr segs[nsegs] = phdr; 62939071Sdfr ++nsegs; 63039071Sdfr break; 63139071Sdfr 63239071Sdfr case PT_PHDR: 63339071Sdfr phphdr = phdr; 63439071Sdfr break; 63539071Sdfr 63639071Sdfr case PT_DYNAMIC: 63739071Sdfr phdyn = phdr; 63839071Sdfr break; 63965503Sbp 64065503Sbp case PT_INTERP: 64165503Sbp link_elf_error("Unsupported file type"); 64265503Sbp error = ENOEXEC; 64365503Sbp goto out; 64439071Sdfr } 64539071Sdfr 64639071Sdfr ++phdr; 64739071Sdfr } 64839071Sdfr if (phdyn == NULL) { 64939071Sdfr link_elf_error("Object is not dynamically-linked"); 65039071Sdfr error = ENOEXEC; 65138514Sdfr goto out; 65239071Sdfr } 65338514Sdfr 65438514Sdfr /* 65539071Sdfr * Allocate the entire address space of the object, to stake out our 65639071Sdfr * contiguous region, and to establish the base address for relocation. 65738514Sdfr */ 65839071Sdfr base_offset = trunc_page(segs[0]->p_offset); 65939071Sdfr base_vaddr = trunc_page(segs[0]->p_vaddr); 66039071Sdfr base_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_memsz); 66139071Sdfr mapsize = base_vlimit - base_vaddr; 66239071Sdfr 66359603Sdfr lf = linker_make_file(filename, &link_elf_class); 66459603Sdfr if (!lf) { 66559603Sdfr error = ENOMEM; 66659603Sdfr goto out; 66759603Sdfr } 66859603Sdfr 66959603Sdfr ef = (elf_file_t) lf; 67039071Sdfr#ifdef SPARSE_MAPPING 67139071Sdfr ef->object = vm_object_allocate(OBJT_DEFAULT, mapsize >> PAGE_SHIFT); 67239071Sdfr if (ef->object == NULL) { 67339071Sdfr error = ENOMEM; 67438514Sdfr goto out; 67538514Sdfr } 67639071Sdfr vm_object_reference(ef->object); 67739071Sdfr ef->address = (caddr_t) vm_map_min(kernel_map); 67839071Sdfr error = vm_map_find(kernel_map, ef->object, 0, 67939071Sdfr (vm_offset_t *) &ef->address, 68039071Sdfr mapsize, 1, 68139071Sdfr VM_PROT_ALL, VM_PROT_ALL, 0); 68239071Sdfr if (error) { 68339071Sdfr vm_object_deallocate(ef->object); 68459603Sdfr ef->object = 0; 68539071Sdfr goto out; 68639071Sdfr } 68739071Sdfr#else 68839071Sdfr ef->address = malloc(mapsize, M_LINKER, M_WAITOK); 68959603Sdfr if (!ef->address) { 69059603Sdfr error = ENOMEM; 69159603Sdfr goto out; 69259603Sdfr } 69339071Sdfr#endif 69439071Sdfr mapbase = ef->address; 69538514Sdfr 69639071Sdfr /* 69739071Sdfr * Read the text and data sections and zero the bss. 69839071Sdfr */ 69939071Sdfr for (i = 0; i < 2; i++) { 70039071Sdfr caddr_t segbase = mapbase + segs[i]->p_vaddr - base_vaddr; 70139071Sdfr error = vn_rdwr(UIO_READ, nd.ni_vp, 70239071Sdfr segbase, segs[i]->p_filesz, segs[i]->p_offset, 703101941Srwatson UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, 704101941Srwatson &resid, td); 70539071Sdfr if (error) { 70639071Sdfr goto out; 70739071Sdfr } 70839071Sdfr bzero(segbase + segs[i]->p_filesz, 70939071Sdfr segs[i]->p_memsz - segs[i]->p_filesz); 71039071Sdfr 71139071Sdfr#ifdef SPARSE_MAPPING 71239071Sdfr /* 71339071Sdfr * Wire down the pages 71439071Sdfr */ 715102547Sjake vm_map_wire(kernel_map, 716102547Sjake (vm_offset_t) segbase, 717102547Sjake (vm_offset_t) segbase + segs[i]->p_memsz, 718102547Sjake FALSE); 71939071Sdfr#endif 72039071Sdfr } 72139071Sdfr 72285734Sgreen#ifdef GPROF 72385734Sgreen /* Update profiling information with the new text segment. */ 72485734Sgreen kmupetext((uintfptr_t)(mapbase + segs[0]->p_vaddr - base_vaddr + 72585734Sgreen segs[0]->p_memsz)); 72685734Sgreen#endif 72785734Sgreen 72859603Sdfr ef->dynamic = (Elf_Dyn *) (mapbase + phdyn->p_vaddr - base_vaddr); 72939071Sdfr 73038514Sdfr lf->address = ef->address; 73139071Sdfr lf->size = mapsize; 73238514Sdfr 73359603Sdfr error = parse_dynamic(ef); 73440292Speter if (error) 73538514Sdfr goto out; 73686469Siedowse error = linker_load_dependencies(lf); 73740292Speter if (error) 73840156Speter goto out; 73959751Speter#if 0 /* this will be more trouble than it's worth for now */ 74059751Speter for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { 74159751Speter if (dp->d_tag != DT_NEEDED) 74259751Speter continue; 74359751Speter modname = ef->strtab + dp->d_un.d_val; 74459751Speter error = linker_load_module(modname, lf); 74559751Speter if (error) 74659751Speter goto out; 74759751Speter } 74859751Speter#endif 74959603Sdfr error = relocate_file(ef); 75040292Speter if (error) 75140156Speter goto out; 75240292Speter 75340292Speter /* Try and load the symbol table if it's present. (you can strip it!) */ 75440962Speter nbytes = hdr->e_shnum * hdr->e_shentsize; 75540962Speter if (nbytes == 0 || hdr->e_shoff == 0) 75640292Speter goto nosyms; 75769781Sdwmalone shdr = malloc(nbytes, M_LINKER, M_WAITOK | M_ZERO); 75840292Speter if (shdr == NULL) { 75940292Speter error = ENOMEM; 76040292Speter goto out; 76140156Speter } 76240292Speter error = vn_rdwr(UIO_READ, nd.ni_vp, 76340962Speter (caddr_t)shdr, nbytes, hdr->e_shoff, 764101941Srwatson UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, 765101941Srwatson &resid, td); 76640292Speter if (error) 76740292Speter goto out; 76840292Speter symtabindex = -1; 76940292Speter symstrindex = -1; 77040962Speter for (i = 0; i < hdr->e_shnum; i++) { 77140292Speter if (shdr[i].sh_type == SHT_SYMTAB) { 77240292Speter symtabindex = i; 77340292Speter symstrindex = shdr[i].sh_link; 77440292Speter } 77540292Speter } 77640292Speter if (symtabindex < 0 || symstrindex < 0) 77740292Speter goto nosyms; 77840156Speter 77940292Speter symcnt = shdr[symtabindex].sh_size; 78040292Speter ef->symbase = malloc(symcnt, M_LINKER, M_WAITOK); 78140292Speter strcnt = shdr[symstrindex].sh_size; 78240292Speter ef->strbase = malloc(strcnt, M_LINKER, M_WAITOK); 78340292Speter 78440292Speter if (ef->symbase == NULL || ef->strbase == NULL) { 78540292Speter error = ENOMEM; 78640292Speter goto out; 78740292Speter } 78840292Speter error = vn_rdwr(UIO_READ, nd.ni_vp, 78940292Speter ef->symbase, symcnt, shdr[symtabindex].sh_offset, 790101941Srwatson UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, 791101941Srwatson &resid, td); 79240292Speter if (error) 79340292Speter goto out; 79440292Speter error = vn_rdwr(UIO_READ, nd.ni_vp, 79540292Speter ef->strbase, strcnt, shdr[symstrindex].sh_offset, 796101941Srwatson UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, 797101941Srwatson &resid, td); 79840292Speter if (error) 79940292Speter goto out; 80040292Speter 80140292Speter ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); 80240292Speter ef->ddbsymtab = (const Elf_Sym *)ef->symbase; 80340292Speter ef->ddbstrcnt = strcnt; 80440292Speter ef->ddbstrtab = ef->strbase; 80540292Speter 806105468Smarcel error = link_elf_link_common_finish(lf); 807105468Smarcel if (error) 808105468Smarcel goto out; 80959603Sdfr 81040292Speternosyms: 81140292Speter 81238514Sdfr *result = lf; 81338514Sdfr 81438514Sdfrout: 81540292Speter if (error && lf) 81640292Speter linker_file_unload(lf); 81740292Speter if (shdr) 81840292Speter free(shdr, M_LINKER); 81940962Speter if (firstpage) 82040962Speter free(firstpage, M_LINKER); 82183366Sjulian VOP_UNLOCK(nd.ni_vp, 0, td); 82291406Sjhb vn_close(nd.ni_vp, FREAD, td->td_ucred, td); 82338514Sdfr 82438514Sdfr return error; 82538514Sdfr} 82638514Sdfr 82738514Sdfrstatic void 82840156Speterlink_elf_unload_file(linker_file_t file) 82938514Sdfr{ 83059603Sdfr elf_file_t ef = (elf_file_t) file; 83138514Sdfr 83259603Sdfr#ifdef DDB 83359603Sdfr if (ef->gdb.l_ld) { 83459603Sdfr GDB_STATE(RT_DELETE); 83583282Speter free((void *)(uintptr_t)ef->gdb.l_name, M_LINKER); 83659603Sdfr link_elf_delete_gdb(&ef->gdb); 83759603Sdfr GDB_STATE(RT_CONSISTENT); 83859603Sdfr } 83959603Sdfr#endif 84059603Sdfr 84159603Sdfr if (ef->preloaded) { 84259751Speter link_elf_unload_preload(file); 84359603Sdfr return; 84459603Sdfr } 845105468Smarcel 84639071Sdfr#ifdef SPARSE_MAPPING 84759603Sdfr if (ef->object) { 84859603Sdfr vm_map_remove(kernel_map, (vm_offset_t) ef->address, 84959603Sdfr (vm_offset_t) ef->address 85059603Sdfr + (ef->object->size << PAGE_SHIFT)); 85159603Sdfr vm_object_deallocate(ef->object); 85259603Sdfr } 85339071Sdfr#else 85459603Sdfr if (ef->address) 85559603Sdfr free(ef->address, M_LINKER); 85639071Sdfr#endif 85759603Sdfr if (ef->symbase) 85859603Sdfr free(ef->symbase, M_LINKER); 85959603Sdfr if (ef->strbase) 86059603Sdfr free(ef->strbase, M_LINKER); 86138514Sdfr} 86238514Sdfr 86340156Speterstatic void 86459751Speterlink_elf_unload_preload(linker_file_t file) 86540156Speter{ 86640156Speter if (file->filename) 86740156Speter preload_delete_name(file->filename); 86840156Speter} 86940156Speter 87039071Sdfrstatic const char * 87140435Spetersymbol_name(elf_file_t ef, Elf_Word r_info) 87238514Sdfr{ 87339071Sdfr const Elf_Sym *ref; 87438514Sdfr 87540435Speter if (ELF_R_SYM(r_info)) { 87640435Speter ref = ef->symtab + ELF_R_SYM(r_info); 87740397Speter return ef->strtab + ref->st_name; 87839071Sdfr } else 87939071Sdfr return NULL; 88038514Sdfr} 88138514Sdfr 88238514Sdfrstatic int 88359603Sdfrrelocate_file(elf_file_t ef) 88438514Sdfr{ 88539071Sdfr const Elf_Rel *rellim; 88639071Sdfr const Elf_Rel *rel; 88739071Sdfr const Elf_Rela *relalim; 88839071Sdfr const Elf_Rela *rela; 88940435Speter const char *symname; 89038514Sdfr 89139071Sdfr /* Perform relocations without addend if there are any: */ 89240435Speter rel = ef->rel; 89340435Speter if (rel) { 89443388Sbde rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize); 89540435Speter while (rel < rellim) { 89695410Smarcel if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL)) { 89795410Smarcel symname = symbol_name(ef, rel->r_info); 89859744Speter printf("link_elf: symbol %s undefined\n", symname); 89940435Speter return ENOENT; 90042200Speter } 90140435Speter rel++; 90240435Speter } 90339071Sdfr } 90438514Sdfr 90539071Sdfr /* Perform relocations with addend if there are any: */ 90640435Speter rela = ef->rela; 90740435Speter if (rela) { 90843388Sbde relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize); 90940435Speter while (rela < relalim) { 91095410Smarcel if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA)) { 91195410Smarcel symname = symbol_name(ef, rela->r_info); 91259744Speter printf("link_elf: symbol %s undefined\n", symname); 91340435Speter return ENOENT; 91442200Speter } 91540435Speter rela++; 91640435Speter } 91739071Sdfr } 91838514Sdfr 91939071Sdfr /* Perform PLT relocations without addend if there are any: */ 92040435Speter rel = ef->pltrel; 92140435Speter if (rel) { 92243388Sbde rellim = (const Elf_Rel *)((const char *)ef->pltrel + ef->pltrelsize); 92340435Speter while (rel < rellim) { 92495410Smarcel if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL)) { 92595410Smarcel symname = symbol_name(ef, rel->r_info); 92659744Speter printf("link_elf: symbol %s undefined\n", symname); 92740435Speter return ENOENT; 92842200Speter } 92940435Speter rel++; 93040435Speter } 93139071Sdfr } 93238514Sdfr 93339071Sdfr /* Perform relocations with addend if there are any: */ 93440435Speter rela = ef->pltrela; 93540435Speter if (rela) { 93643388Sbde relalim = (const Elf_Rela *)((const char *)ef->pltrela + ef->pltrelasize); 93740435Speter while (rela < relalim) { 93895410Smarcel if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA)) { 93995410Smarcel symname = symbol_name(ef, rela->r_info); 94059744Speter printf("link_elf: symbol %s undefined\n", symname); 94140435Speter return ENOENT; 94242200Speter } 94340435Speter rela++; 94440435Speter } 94538514Sdfr } 94638514Sdfr 94738514Sdfr return 0; 94838514Sdfr} 94938514Sdfr 95039071Sdfr/* 95139071Sdfr * Hash function for symbol table lookup. Don't even think about changing 95239071Sdfr * this. It is specified by the System V ABI. 95339071Sdfr */ 95439071Sdfrstatic unsigned long 95539071Sdfrelf_hash(const char *name) 95638514Sdfr{ 95739071Sdfr const unsigned char *p = (const unsigned char *) name; 95839071Sdfr unsigned long h = 0; 95939071Sdfr unsigned long g; 96038514Sdfr 96139071Sdfr while (*p != '\0') { 96239071Sdfr h = (h << 4) + *p++; 96339071Sdfr if ((g = h & 0xf0000000) != 0) 96439071Sdfr h ^= g >> 24; 96539071Sdfr h &= ~g; 96639071Sdfr } 96739071Sdfr return h; 96838514Sdfr} 96938514Sdfr 970104094Sphkstatic int 97143301Sdillonlink_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym) 97238514Sdfr{ 97359603Sdfr elf_file_t ef = (elf_file_t) lf; 97439071Sdfr unsigned long symnum; 97540254Speter const Elf_Sym* symp; 97640254Speter const char *strp; 97739071Sdfr unsigned long hash; 97839071Sdfr int i; 97938514Sdfr 98040254Speter /* First, search hashed global symbols */ 98139071Sdfr hash = elf_hash(name); 98239071Sdfr symnum = ef->buckets[hash % ef->nbuckets]; 98339071Sdfr 98439071Sdfr while (symnum != STN_UNDEF) { 98539071Sdfr if (symnum >= ef->nchains) { 98639071Sdfr printf("link_elf_lookup_symbol: corrupt symbol table\n"); 98739071Sdfr return ENOENT; 98838514Sdfr } 98938514Sdfr 99039071Sdfr symp = ef->symtab + symnum; 99139071Sdfr if (symp->st_name == 0) { 99239071Sdfr printf("link_elf_lookup_symbol: corrupt symbol table\n"); 99339071Sdfr return ENOENT; 99439071Sdfr } 99539071Sdfr 99639071Sdfr strp = ef->strtab + symp->st_name; 99739071Sdfr 99839071Sdfr if (strcmp(name, strp) == 0) { 99939071Sdfr if (symp->st_shndx != SHN_UNDEF || 100039071Sdfr (symp->st_value != 0 && 100139071Sdfr ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { 100243301Sdillon *sym = (c_linker_sym_t) symp; 100339071Sdfr return 0; 100439071Sdfr } else 100539071Sdfr return ENOENT; 100639071Sdfr } 100739071Sdfr 100839071Sdfr symnum = ef->chains[symnum]; 100939071Sdfr } 101039071Sdfr 101140254Speter /* If we have not found it, look at the full table (if loaded) */ 101240254Speter if (ef->symtab == ef->ddbsymtab) 101340254Speter return ENOENT; 101440254Speter 101540254Speter /* Exhaustive search */ 101640254Speter for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { 101740254Speter strp = ef->ddbstrtab + symp->st_name; 101840254Speter if (strcmp(name, strp) == 0) { 101940254Speter if (symp->st_shndx != SHN_UNDEF || 102040254Speter (symp->st_value != 0 && 102140254Speter ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { 102243301Sdillon *sym = (c_linker_sym_t) symp; 102340254Speter return 0; 102440254Speter } else 102540254Speter return ENOENT; 102640254Speter } 102740254Speter } 102840254Speter 102939071Sdfr return ENOENT; 103038514Sdfr} 103138514Sdfr 103240156Speterstatic int 103343309Sdillonlink_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, linker_symval_t* symval) 103438514Sdfr{ 103559603Sdfr elf_file_t ef = (elf_file_t) lf; 103643311Sdillon const Elf_Sym* es = (const Elf_Sym*) sym; 103738514Sdfr 1038102348Smarcel if (es >= ef->symtab && es < (ef->symtab + ef->nchains)) { 103940254Speter symval->name = ef->strtab + es->st_name; 104040254Speter symval->value = (caddr_t) ef->address + es->st_value; 104140254Speter symval->size = es->st_size; 104240254Speter return 0; 104340254Speter } 104440254Speter if (ef->symtab == ef->ddbsymtab) 104540254Speter return ENOENT; 1046102348Smarcel if (es >= ef->ddbsymtab && es < (ef->ddbsymtab + ef->ddbsymcnt)) { 104740254Speter symval->name = ef->ddbstrtab + es->st_name; 104840254Speter symval->value = (caddr_t) ef->address + es->st_value; 104940254Speter symval->size = es->st_size; 105040254Speter return 0; 105140254Speter } 105240254Speter return ENOENT; 105338514Sdfr} 105438514Sdfr 105538514Sdfrstatic int 105638514Sdfrlink_elf_search_symbol(linker_file_t lf, caddr_t value, 105743301Sdillon c_linker_sym_t* sym, long* diffp) 105838514Sdfr{ 105959603Sdfr elf_file_t ef = (elf_file_t) lf; 106055090Sbde u_long off = (uintptr_t) (void *) value; 106138514Sdfr u_long diff = off; 106255090Sbde u_long st_value; 106339071Sdfr const Elf_Sym* es; 106439071Sdfr const Elf_Sym* best = 0; 106538514Sdfr int i; 106638514Sdfr 106740254Speter for (i = 0, es = ef->ddbsymtab; i < ef->ddbsymcnt; i++, es++) { 106838514Sdfr if (es->st_name == 0) 106938514Sdfr continue; 107055090Sbde st_value = es->st_value + (uintptr_t) (void *) ef->address; 107153820Speter if (off >= st_value) { 107253820Speter if (off - st_value < diff) { 107353820Speter diff = off - st_value; 107438514Sdfr best = es; 107538514Sdfr if (diff == 0) 107638514Sdfr break; 107753820Speter } else if (off - st_value == diff) { 107838514Sdfr best = es; 107938514Sdfr } 108038514Sdfr } 108138514Sdfr } 108238514Sdfr if (best == 0) 108338514Sdfr *diffp = off; 108438514Sdfr else 108538514Sdfr *diffp = diff; 108643301Sdillon *sym = (c_linker_sym_t) best; 108738514Sdfr 108838514Sdfr return 0; 108938514Sdfr} 109078161Speter 109178161Speter/* 109278161Speter * Look up a linker set on an ELF system. 109378161Speter */ 109478161Speterstatic int 109578161Speterlink_elf_lookup_set(linker_file_t lf, const char *name, 109678161Speter void ***startp, void ***stopp, int *countp) 109778161Speter{ 109878161Speter c_linker_sym_t sym; 109978161Speter linker_symval_t symval; 110078161Speter char *setsym; 110178161Speter void **start, **stop; 110278161Speter int len, error = 0, count; 110378161Speter 110478161Speter len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */ 110578161Speter setsym = malloc(len, M_LINKER, M_WAITOK); 110678161Speter if (setsym == NULL) 110778161Speter return ENOMEM; 110878161Speter 110978161Speter /* get address of first entry */ 111078161Speter snprintf(setsym, len, "%s%s", "__start_set_", name); 111178161Speter error = link_elf_lookup_symbol(lf, setsym, &sym); 111278161Speter if (error) 111378161Speter goto out; 111478161Speter link_elf_symbol_values(lf, sym, &symval); 111578161Speter if (symval.value == 0) { 111678161Speter error = ESRCH; 111778161Speter goto out; 111878161Speter } 111978161Speter start = (void **)symval.value; 112078161Speter 112178161Speter /* get address of last entry */ 112278161Speter snprintf(setsym, len, "%s%s", "__stop_set_", name); 112378161Speter error = link_elf_lookup_symbol(lf, setsym, &sym); 112478161Speter if (error) 112578161Speter goto out; 112678161Speter link_elf_symbol_values(lf, sym, &symval); 112778161Speter if (symval.value == 0) { 112878161Speter error = ESRCH; 112978161Speter goto out; 113078161Speter } 113178161Speter stop = (void **)symval.value; 113278161Speter 113378161Speter /* and the number of entries */ 113478161Speter count = stop - start; 113578161Speter 113678161Speter /* and copy out */ 113778161Speter if (startp) 113878161Speter *startp = start; 113978161Speter if (stopp) 114078161Speter *stopp = stop; 114178161Speter if (countp) 114278161Speter *countp = count; 114378161Speter 114478161Speterout: 114578161Speter free(setsym, M_LINKER); 114678161Speter return error; 114778161Speter} 114885736Sgreen 114985736Sgreenstatic int 115085736Sgreenlink_elf_each_function_name(linker_file_t file, 115185736Sgreen int (*callback)(const char *, void *), void *opaque) { 115285736Sgreen elf_file_t ef = (elf_file_t)file; 115385736Sgreen const Elf_Sym* symp; 115485736Sgreen int i, error; 115585736Sgreen 115685736Sgreen /* Exhaustive search */ 115785736Sgreen for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { 115885736Sgreen if (symp->st_value != 0 && 115985736Sgreen ELF_ST_TYPE(symp->st_info) == STT_FUNC) { 116085736Sgreen error = callback(ef->ddbstrtab + symp->st_name, opaque); 116185736Sgreen if (error) 116285736Sgreen return (error); 116385736Sgreen } 116485736Sgreen } 116585736Sgreen return (0); 116685736Sgreen} 116795228Smarcel 116895228Smarcel#ifdef __ia64__ 116995228Smarcel/* 117095228Smarcel * Each KLD has its own GP. The GP value for each load module is given by 117195228Smarcel * DT_PLTGOT on ia64. We need GP to construct function descriptors, but 117295228Smarcel * don't have direct access to the ELF file structure. The link_elf_get_gp() 117395228Smarcel * function returns the GP given a pointer to a generic linker file struct. 117495228Smarcel */ 117595228SmarcelElf_Addr 117695228Smarcellink_elf_get_gp(linker_file_t lf) 117795228Smarcel{ 117895228Smarcel elf_file_t ef = (elf_file_t)lf; 117995228Smarcel return (Elf_Addr)ef->got; 118095228Smarcel} 118195228Smarcel#endif 118295410Smarcel 1183104072Sjakeconst Elf_Sym * 1184104072Sjakeelf_get_sym(linker_file_t lf, Elf_Word symidx) 1185104072Sjake{ 1186104072Sjake elf_file_t ef = (elf_file_t)lf; 1187104072Sjake 1188104072Sjake if (symidx >= ef->nchains) 1189104072Sjake return (NULL); 1190104072Sjake return (ef->symtab + symidx); 1191104072Sjake} 1192104072Sjake 1193105147Smarcelconst char * 1194105147Smarcelelf_get_symname(linker_file_t lf, Elf_Word symidx) 1195105147Smarcel{ 1196105147Smarcel elf_file_t ef = (elf_file_t)lf; 1197105147Smarcel const Elf_Sym *sym; 1198105147Smarcel 1199105147Smarcel if (symidx >= ef->nchains) 1200105147Smarcel return (NULL); 1201105147Smarcel sym = ef->symtab + symidx; 1202105147Smarcel return (ef->strtab + sym->st_name); 1203105147Smarcel} 1204105147Smarcel 120595410Smarcel/* 120695410Smarcel * Symbol lookup function that can be used when the symbol index is known (ie 120795410Smarcel * in relocations). It uses the symbol index instead of doing a fully fledged 120895410Smarcel * hash table based lookup when such is valid. For example for local symbols. 120995410Smarcel * This is not only more efficient, it's also more correct. It's not always 121095410Smarcel * the case that the symbol can be found through the hash table. 121195410Smarcel */ 121295410SmarcelElf_Addr 121395410Smarcelelf_lookup(linker_file_t lf, Elf_Word symidx, int deps) 121495410Smarcel{ 121595410Smarcel elf_file_t ef = (elf_file_t)lf; 121695410Smarcel const Elf_Sym *sym; 121795410Smarcel const char *symbol; 121895410Smarcel 121995410Smarcel /* Don't even try to lookup the symbol if the index is bogus. */ 122095410Smarcel if (symidx >= ef->nchains) 122195410Smarcel return (0); 122295410Smarcel 122395410Smarcel sym = ef->symtab + symidx; 122495410Smarcel 122595410Smarcel /* 122695410Smarcel * Don't do a full lookup when the symbol is local. It may even 122795410Smarcel * fail because it may not be found through the hash table. 122895410Smarcel */ 122995410Smarcel if (ELF_ST_BIND(sym->st_info) == STB_LOCAL) { 123095410Smarcel /* Force lookup failure when we have an insanity. */ 123195410Smarcel if (sym->st_shndx == SHN_UNDEF || sym->st_value == 0) 123295410Smarcel return (0); 123395410Smarcel return ((Elf_Addr)ef->address + sym->st_value); 123495410Smarcel } 123595410Smarcel 123695410Smarcel /* 123795410Smarcel * XXX we can avoid doing a hash table based lookup for global 123895410Smarcel * symbols as well. This however is not always valid, so we'll 123995410Smarcel * just do it the hard way for now. Performance tweaks can 124095410Smarcel * always be added. 124195410Smarcel */ 124295410Smarcel 124395410Smarcel symbol = ef->strtab + sym->st_name; 124495410Smarcel 124595410Smarcel /* Force a lookup failure if the symbol name is bogus. */ 124695410Smarcel if (*symbol == 0) 124795410Smarcel return (0); 124895410Smarcel 124995410Smarcel return ((Elf_Addr)linker_file_lookup_symbol(lf, symbol, deps)); 125095410Smarcel} 1251