link_elf.c revision 86469
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 86469 2001-11-16 21:08:40Z iedowse $ 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 5873016Speter#ifdef __AOUT__ 5973016Speter#include <nlist.h> 6073016Speter#endif 6159603Sdfr#include <link.h> 6239071Sdfr 6359603Sdfr#include "linker_if.h" 6438514Sdfr 6538514Sdfrtypedef struct elf_file { 6659603Sdfr struct linker_file lf; /* Common fields */ 6759603Sdfr int preloaded; /* Was file pre-loaded */ 6839071Sdfr caddr_t address; /* Relocation address */ 6939071Sdfr#ifdef SPARSE_MAPPING 7039071Sdfr vm_object_t object; /* VM object to hold file pages */ 7139071Sdfr#endif 7259603Sdfr Elf_Dyn* dynamic; /* Symbol table etc. */ 7380700Sjake Elf_Hashelt nbuckets; /* DT_HASH info */ 7480700Sjake Elf_Hashelt nchains; 7580700Sjake const Elf_Hashelt* buckets; 7680700Sjake const Elf_Hashelt* chains; 7738514Sdfr caddr_t hash; 7838514Sdfr caddr_t strtab; /* DT_STRTAB */ 7940254Speter int strsz; /* DT_STRSZ */ 8039071Sdfr const Elf_Sym* symtab; /* DT_SYMTAB */ 8139071Sdfr Elf_Addr* got; /* DT_PLTGOT */ 8239071Sdfr const Elf_Rel* pltrel; /* DT_JMPREL */ 8339071Sdfr int pltrelsize; /* DT_PLTRELSZ */ 8439071Sdfr const Elf_Rela* pltrela; /* DT_JMPREL */ 8539071Sdfr int pltrelasize; /* DT_PLTRELSZ */ 8639071Sdfr const Elf_Rel* rel; /* DT_REL */ 8739071Sdfr int relsize; /* DT_RELSZ */ 8839071Sdfr const Elf_Rela* rela; /* DT_RELA */ 8939071Sdfr int relasize; /* DT_RELASZ */ 9040254Speter caddr_t modptr; 9140254Speter const Elf_Sym* ddbsymtab; /* The symbol table we are using */ 9240254Speter long ddbsymcnt; /* Number of symbols */ 9340254Speter caddr_t ddbstrtab; /* String table */ 9440254Speter long ddbstrcnt; /* number of bytes in string table */ 9540292Speter caddr_t symbase; /* malloc'ed symbold base */ 9640292Speter caddr_t strbase; /* malloc'ed string base */ 9759603Sdfr#ifdef DDB 9859603Sdfr struct link_map gdb; /* hooks for gdb */ 9959603Sdfr#endif 10038514Sdfr} *elf_file_t; 10138514Sdfr 10259751Speterstatic int link_elf_link_preload(linker_class_t cls, 10359751Speter const char*, linker_file_t*); 10459751Speterstatic int link_elf_link_preload_finish(linker_file_t); 10559751Speterstatic int link_elf_load_file(linker_class_t, const char*, linker_file_t*); 10659603Sdfrstatic int link_elf_lookup_symbol(linker_file_t, const char*, 10759603Sdfr c_linker_sym_t*); 10859603Sdfrstatic int link_elf_symbol_values(linker_file_t, c_linker_sym_t, linker_symval_t*); 10959603Sdfrstatic int link_elf_search_symbol(linker_file_t, caddr_t value, 11059603Sdfr c_linker_sym_t* sym, long* diffp); 11138514Sdfr 11259603Sdfrstatic void link_elf_unload_file(linker_file_t); 11359751Speterstatic void link_elf_unload_preload(linker_file_t); 11478161Speterstatic int link_elf_lookup_set(linker_file_t, const char *, 11578161Speter void ***, void ***, int *); 11685736Sgreenstatic int link_elf_each_function_name(linker_file_t, 11785736Sgreen int (*)(const char *, void *), 11885736Sgreen void *); 11959603Sdfr 12059603Sdfrstatic kobj_method_t link_elf_methods[] = { 12159603Sdfr KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol), 12259603Sdfr KOBJMETHOD(linker_symbol_values, link_elf_symbol_values), 12359603Sdfr KOBJMETHOD(linker_search_symbol, link_elf_search_symbol), 12459603Sdfr KOBJMETHOD(linker_unload, link_elf_unload_file), 12559751Speter KOBJMETHOD(linker_load_file, link_elf_load_file), 12659751Speter KOBJMETHOD(linker_link_preload, link_elf_link_preload), 12759751Speter KOBJMETHOD(linker_link_preload_finish, link_elf_link_preload_finish), 12878161Speter KOBJMETHOD(linker_lookup_set, link_elf_lookup_set), 12985736Sgreen KOBJMETHOD(linker_each_function_name, link_elf_each_function_name), 13059603Sdfr { 0, 0 } 13159603Sdfr}; 13259603Sdfr 13359603Sdfrstatic struct linker_class link_elf_class = { 13459603Sdfr#if ELF_TARG_CLASS == ELFCLASS32 13559603Sdfr "elf32", 13659603Sdfr#else 13759603Sdfr "elf64", 13859603Sdfr#endif 13959603Sdfr link_elf_methods, sizeof(struct elf_file) 14059603Sdfr}; 14159603Sdfr 14259603Sdfrstatic int parse_dynamic(elf_file_t ef); 14359603Sdfrstatic int relocate_file(elf_file_t ef); 14459751Speterstatic int link_elf_preload_parse_symbols(elf_file_t ef); 14559603Sdfr 14659603Sdfr#ifdef DDB 14766719Sjhbstatic void r_debug_state(struct r_debug *dummy_one, 14866719Sjhb struct link_map *dummy_two); 14959603Sdfr 15038514Sdfr/* 15159603Sdfr * A list of loaded modules for GDB to use for loading symbols. 15259603Sdfr */ 15359603Sdfrstruct r_debug r_debug; 15459603Sdfr 15566719Sjhb#define GDB_STATE(s) r_debug.r_state = s; r_debug_state(NULL, NULL); 15659603Sdfr 15759603Sdfr/* 15859603Sdfr * Function for the debugger to set a breakpoint on to gain control. 15959603Sdfr */ 16059603Sdfrvoid 16166719Sjhbr_debug_state(struct r_debug *dummy_one __unused, 16266719Sjhb struct link_map *dummy_two __unused) 16359603Sdfr{ 16459603Sdfr} 16559603Sdfr 16659603Sdfr#endif 16759603Sdfr 16859603Sdfr/* 16938514Sdfr * The kernel symbol table starts here. 17038514Sdfr */ 17138514Sdfrextern struct _dynamic _DYNAMIC; 17238514Sdfr 17338514Sdfrstatic void 17438514Sdfrlink_elf_init(void* arg) 17538514Sdfr{ 17640156Speter#ifdef __ELF__ 17740156Speter Elf_Dyn *dp; 17840156Speter caddr_t modptr, baseptr, sizeptr; 17940156Speter elf_file_t ef; 18040156Speter char *modname; 18181500Swpaul#ifdef DDB 18281500Swpaul char *newfilename; 18340156Speter#endif 18481500Swpaul#endif 18538514Sdfr 18659603Sdfr linker_add_class(&link_elf_class); 18738514Sdfr 18840156Speter#ifdef __ELF__ 18940156Speter dp = (Elf_Dyn*) &_DYNAMIC; 19082848Speter modname = NULL; 19182848Speter modptr = preload_search_by_type("elf kernel"); 19282848Speter if (modptr) 19382848Speter modname = (char *)preload_search_info(modptr, MODINFO_NAME); 19482848Speter if (modname == NULL) 19582848Speter modname = "kernel"; 19682848Speter linker_kernel_file = linker_make_file(modname, &link_elf_class); 19782848Speter if (linker_kernel_file == NULL) 19882848Speter panic("link_elf_init: Can't create linker structures for kernel"); 19982848Speter 20082848Speter ef = (elf_file_t) linker_kernel_file; 20182848Speter ef->preloaded = 1; 20282848Speter ef->address = 0; 20359603Sdfr#ifdef SPARSE_MAPPING 20482848Speter ef->object = 0; 20559603Sdfr#endif 20682848Speter ef->dynamic = dp; 20759603Sdfr 20882848Speter if (dp) 20982848Speter parse_dynamic(ef); 21082848Speter linker_kernel_file->address = (caddr_t) KERNBASE; 21182848Speter linker_kernel_file->size = -(intptr_t)linker_kernel_file->address; 21240156Speter 21382848Speter if (modptr) { 21482848Speter ef->modptr = modptr; 21582848Speter baseptr = preload_search_info(modptr, MODINFO_ADDR); 21682848Speter if (baseptr) 21782848Speter linker_kernel_file->address = *(caddr_t *)baseptr; 21882848Speter sizeptr = preload_search_info(modptr, MODINFO_SIZE); 21982848Speter if (sizeptr) 22082848Speter linker_kernel_file->size = *(size_t *)sizeptr; 22182848Speter } 22282848Speter (void)link_elf_preload_parse_symbols(ef); 22359603Sdfr 22459603Sdfr#ifdef DDB 22582848Speter ef->gdb.l_addr = linker_kernel_file->address; 22682848Speter newfilename = malloc(strlen(modname) + 1, M_LINKER, M_WAITOK); 22782848Speter strcpy(newfilename, modname); 22882848Speter ef->gdb.l_name = newfilename; 22982848Speter ef->gdb.l_ld = dp; 23082848Speter ef->gdb.l_prev = 0; 23182848Speter ef->gdb.l_next = 0; 23259603Sdfr 23382848Speter r_debug.r_map = &ef->gdb; 23482848Speter r_debug.r_brk = r_debug_state; 23582848Speter r_debug.r_state = RT_CONSISTENT; 23659603Sdfr 23782848Speter r_debug_state(NULL, NULL); /* say hello to gdb! */ 23859603Sdfr#endif 23959603Sdfr 24040156Speter#endif 24138514Sdfr} 24238514Sdfr 24340156SpeterSYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0); 24438514Sdfr 24538514Sdfrstatic int 24659751Speterlink_elf_preload_parse_symbols(elf_file_t ef) 24740254Speter{ 24840254Speter caddr_t pointer; 24940254Speter caddr_t ssym, esym, base; 25040254Speter caddr_t strtab; 25140254Speter int strcnt; 25240254Speter Elf_Sym* symtab; 25340254Speter int symcnt; 25440254Speter 25540292Speter if (ef->modptr == NULL) 25640292Speter return 0; 25740254Speter pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_SSYM); 25840254Speter if (pointer == NULL) 25940254Speter return 0; 26040254Speter ssym = *(caddr_t *)pointer; 26140254Speter pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_ESYM); 26240254Speter if (pointer == NULL) 26340254Speter return 0; 26440254Speter esym = *(caddr_t *)pointer; 26540254Speter 26640254Speter base = ssym; 26740254Speter 26840254Speter symcnt = *(long *)base; 26940254Speter base += sizeof(long); 27040254Speter symtab = (Elf_Sym *)base; 27140254Speter base += roundup(symcnt, sizeof(long)); 27240254Speter 27340254Speter if (base > esym || base < ssym) { 27440254Speter printf("Symbols are corrupt!\n"); 27540254Speter return EINVAL; 27640254Speter } 27740254Speter 27840254Speter strcnt = *(long *)base; 27940254Speter base += sizeof(long); 28040254Speter strtab = base; 28140254Speter base += roundup(strcnt, sizeof(long)); 28240254Speter 28340254Speter if (base > esym || base < ssym) { 28440254Speter printf("Symbols are corrupt!\n"); 28540254Speter return EINVAL; 28640254Speter } 28740254Speter 28840254Speter ef->ddbsymtab = symtab; 28940254Speter ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); 29040254Speter ef->ddbstrtab = strtab; 29140254Speter ef->ddbstrcnt = strcnt; 29240254Speter 29340254Speter return 0; 29440254Speter} 29540254Speter 29640254Speterstatic int 29759603Sdfrparse_dynamic(elf_file_t ef) 29838514Sdfr{ 29959603Sdfr Elf_Dyn *dp; 30039071Sdfr int plttype = DT_REL; 30138514Sdfr 30238514Sdfr for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { 30338514Sdfr switch (dp->d_tag) { 30438514Sdfr case DT_HASH: 30538514Sdfr { 30638514Sdfr /* From src/libexec/rtld-elf/rtld.c */ 30780700Sjake const Elf_Hashelt *hashtab = (const Elf_Hashelt *) 30838514Sdfr (ef->address + dp->d_un.d_ptr); 30938514Sdfr ef->nbuckets = hashtab[0]; 31038514Sdfr ef->nchains = hashtab[1]; 31138514Sdfr ef->buckets = hashtab + 2; 31238514Sdfr ef->chains = ef->buckets + ef->nbuckets; 31338514Sdfr break; 31438514Sdfr } 31538514Sdfr case DT_STRTAB: 31639071Sdfr ef->strtab = (caddr_t) (ef->address + dp->d_un.d_ptr); 31738514Sdfr break; 31840254Speter case DT_STRSZ: 31940254Speter ef->strsz = dp->d_un.d_val; 32040254Speter break; 32138514Sdfr case DT_SYMTAB: 32239071Sdfr ef->symtab = (Elf_Sym*) (ef->address + dp->d_un.d_ptr); 32338514Sdfr break; 32438514Sdfr case DT_SYMENT: 32538514Sdfr if (dp->d_un.d_val != sizeof(Elf_Sym)) 32638514Sdfr return ENOEXEC; 32739071Sdfr break; 32839071Sdfr case DT_PLTGOT: 32939071Sdfr ef->got = (Elf_Addr *) (ef->address + dp->d_un.d_ptr); 33039071Sdfr break; 33139071Sdfr case DT_REL: 33239071Sdfr ef->rel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); 33339071Sdfr break; 33439071Sdfr case DT_RELSZ: 33539071Sdfr ef->relsize = dp->d_un.d_val; 33639071Sdfr break; 33739071Sdfr case DT_RELENT: 33839071Sdfr if (dp->d_un.d_val != sizeof(Elf_Rel)) 33939071Sdfr return ENOEXEC; 34039071Sdfr break; 34139071Sdfr case DT_JMPREL: 34239071Sdfr ef->pltrel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); 34339071Sdfr break; 34439071Sdfr case DT_PLTRELSZ: 34539071Sdfr ef->pltrelsize = dp->d_un.d_val; 34639071Sdfr break; 34739071Sdfr case DT_RELA: 34839071Sdfr ef->rela = (const Elf_Rela *) (ef->address + dp->d_un.d_ptr); 34939071Sdfr break; 35039071Sdfr case DT_RELASZ: 35139071Sdfr ef->relasize = dp->d_un.d_val; 35239071Sdfr break; 35339071Sdfr case DT_RELAENT: 35439071Sdfr if (dp->d_un.d_val != sizeof(Elf_Rela)) 35539071Sdfr return ENOEXEC; 35639071Sdfr break; 35739071Sdfr case DT_PLTREL: 35839071Sdfr plttype = dp->d_un.d_val; 35939071Sdfr if (plttype != DT_REL && plttype != DT_RELA) 36039071Sdfr return ENOEXEC; 36139071Sdfr break; 36259603Sdfr#ifdef DDB 36359603Sdfr case DT_DEBUG: 36459603Sdfr dp->d_un.d_ptr = (Elf_Addr) &r_debug; 36559603Sdfr break; 36659603Sdfr#endif 36738514Sdfr } 36838514Sdfr } 36939071Sdfr 37039071Sdfr if (plttype == DT_RELA) { 37139071Sdfr ef->pltrela = (const Elf_Rela *) ef->pltrel; 37239071Sdfr ef->pltrel = NULL; 37339071Sdfr ef->pltrelasize = ef->pltrelsize; 37439071Sdfr ef->pltrelsize = 0; 37539071Sdfr } 37639071Sdfr 37740254Speter ef->ddbsymtab = ef->symtab; 37840254Speter ef->ddbsymcnt = ef->nchains; 37940254Speter ef->ddbstrtab = ef->strtab; 38040254Speter ef->ddbstrcnt = ef->strsz; 38140254Speter 38238514Sdfr return 0; 38338514Sdfr} 38438514Sdfr 38539071Sdfrstatic void 38639071Sdfrlink_elf_error(const char *s) 38739071Sdfr{ 38839071Sdfr printf("kldload: %s\n", s); 38939071Sdfr} 39039071Sdfr 39159603Sdfr#ifdef DDB 39259603Sdfr 39359603Sdfrstatic void 39459603Sdfrlink_elf_add_gdb(struct link_map *l) 39559603Sdfr{ 39659603Sdfr struct link_map *prev; 39759603Sdfr 39859603Sdfr /* 39959603Sdfr * Scan to the end of the list. 40059603Sdfr */ 40159603Sdfr for (prev = r_debug.r_map; prev->l_next != NULL; prev = prev->l_next) 40259603Sdfr ; 40359603Sdfr 40459603Sdfr /* Link in the new entry. */ 40559603Sdfr l->l_prev = prev; 40659603Sdfr l->l_next = prev->l_next; 40759603Sdfr prev->l_next = l; 40859603Sdfr} 40959603Sdfr 41059603Sdfrstatic void 41159603Sdfrlink_elf_delete_gdb(struct link_map *l) 41259603Sdfr{ 41359603Sdfr if (l->l_prev == NULL) { 41459603Sdfr if ((r_debug.r_map = l->l_next) != NULL) 41559603Sdfr l->l_next->l_prev = NULL; 41659603Sdfr return; 41759603Sdfr } 41859603Sdfr 41959603Sdfr if ((l->l_prev->l_next = l->l_next) != NULL) 42059603Sdfr l->l_next->l_prev = l->l_prev; 42159603Sdfr} 42259603Sdfr 42359603Sdfr#endif /* DDB */ 42459603Sdfr 42538514Sdfrstatic int 42659751Speterlink_elf_link_preload(linker_class_t cls, 42759751Speter const char* filename, linker_file_t *result) 42840156Speter{ 42940156Speter caddr_t modptr, baseptr, sizeptr, dynptr; 43040156Speter char *type; 43140156Speter elf_file_t ef; 43240156Speter linker_file_t lf; 43340156Speter int error; 43440156Speter vm_offset_t dp; 43540156Speter 43659751Speter /* Look to see if we have the file preloaded */ 43740156Speter modptr = preload_search_by_name(filename); 43840156Speter if (modptr == NULL) 43959751Speter return ENOENT; 44040156Speter 44140156Speter type = (char *)preload_search_info(modptr, MODINFO_TYPE); 44240156Speter baseptr = preload_search_info(modptr, MODINFO_ADDR); 44340156Speter sizeptr = preload_search_info(modptr, MODINFO_SIZE); 44440156Speter dynptr = preload_search_info(modptr, MODINFO_METADATA|MODINFOMD_DYNAMIC); 44540156Speter if (type == NULL || strcmp(type, "elf module") != 0) 44640156Speter return (EFTYPE); 44740156Speter if (baseptr == NULL || sizeptr == NULL || dynptr == NULL) 44840156Speter return (EINVAL); 44940156Speter 45059603Sdfr lf = linker_make_file(filename, &link_elf_class); 45159603Sdfr if (lf == NULL) { 45259603Sdfr return ENOMEM; 45359603Sdfr } 45459603Sdfr 45559603Sdfr ef = (elf_file_t) lf; 45659603Sdfr ef->preloaded = 1; 45740292Speter ef->modptr = modptr; 45840156Speter ef->address = *(caddr_t *)baseptr; 45940156Speter#ifdef SPARSE_MAPPING 46040156Speter ef->object = 0; 46140156Speter#endif 46240156Speter dp = (vm_offset_t)ef->address + *(vm_offset_t *)dynptr; 46340156Speter ef->dynamic = (Elf_Dyn *)dp; 46440156Speter lf->address = ef->address; 46540156Speter lf->size = *(size_t *)sizeptr; 46640156Speter 46759603Sdfr error = parse_dynamic(ef); 46840156Speter if (error) { 46940156Speter linker_file_unload(lf); 47040156Speter return error; 47140156Speter } 47259751Speter *result = lf; 47359751Speter return (0); 47459751Speter} 47559751Speter 47659751Speterstatic int 47759751Speterlink_elf_link_preload_finish(linker_file_t lf) 47859751Speter{ 47959751Speter elf_file_t ef; 48059751Speter int error; 48181500Swpaul#ifdef DDB 48281500Swpaul char *newfilename; 48381500Swpaul#endif 48459751Speter 48559751Speter ef = (elf_file_t) lf; 48659751Speter#if 0 /* this will be more trouble than it's worth for now */ 48759751Speter for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { 48859751Speter if (dp->d_tag != DT_NEEDED) 48959751Speter continue; 49059751Speter modname = ef->strtab + dp->d_un.d_val; 49159751Speter error = linker_load_module(modname, lf); 49259751Speter if (error) 49359751Speter goto out; 49440156Speter } 49559751Speter#endif 49659603Sdfr error = relocate_file(ef); 49759751Speter if (error) 49840156Speter return error; 49959751Speter (void)link_elf_preload_parse_symbols(ef); 50059603Sdfr 50159603Sdfr#ifdef DDB 50259603Sdfr GDB_STATE(RT_ADD); 50359603Sdfr ef->gdb.l_addr = lf->address; 50481500Swpaul newfilename = malloc(strlen(lf->filename) + 1, M_LINKER, M_WAITOK); 50581500Swpaul strcpy(newfilename, lf->filename); 50681500Swpaul ef->gdb.l_name = newfilename; 50759603Sdfr ef->gdb.l_ld = ef->dynamic; 50859603Sdfr link_elf_add_gdb(&ef->gdb); 50959603Sdfr GDB_STATE(RT_CONSISTENT); 51059603Sdfr#endif 51159603Sdfr 51240156Speter return (0); 51340156Speter} 51440156Speter 51540156Speterstatic int 51659751Speterlink_elf_load_file(linker_class_t cls, const char* filename, linker_file_t* result) 51738514Sdfr{ 51838514Sdfr struct nameidata nd; 51983366Sjulian struct thread* td = curthread; /* XXX */ 52083366Sjulian struct proc* p = td->td_proc; /* XXX */ 52140962Speter Elf_Ehdr *hdr; 52240962Speter caddr_t firstpage; 52339071Sdfr int nbytes, i; 52439071Sdfr Elf_Phdr *phdr; 52539071Sdfr Elf_Phdr *phlimit; 52639071Sdfr Elf_Phdr *segs[2]; 52739071Sdfr int nsegs; 52839071Sdfr Elf_Phdr *phdyn; 52939071Sdfr Elf_Phdr *phphdr; 53039071Sdfr caddr_t mapbase; 53139071Sdfr size_t mapsize; 53239071Sdfr Elf_Off base_offset; 53339071Sdfr Elf_Addr base_vaddr; 53439071Sdfr Elf_Addr base_vlimit; 53538514Sdfr int error = 0; 53662550Smckusick int resid, flags; 53738514Sdfr elf_file_t ef; 53838514Sdfr linker_file_t lf; 53940292Speter Elf_Shdr *shdr; 54040292Speter int symtabindex; 54140292Speter int symstrindex; 54240292Speter int symcnt; 54340292Speter int strcnt; 54481500Swpaul#ifdef DDB 54581201Sgreen char *newfilename; 54681500Swpaul#endif 54738514Sdfr 54879224Sdillon GIANT_REQUIRED; 54979224Sdillon 55040292Speter shdr = NULL; 55140292Speter lf = NULL; 55240292Speter 55383366Sjulian NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, td); 55462550Smckusick flags = FREAD; 55562550Smckusick error = vn_open(&nd, &flags, 0); 55638514Sdfr if (error) 55738514Sdfr return error; 55854655Seivind NDFREE(&nd, NDF_ONLY_PNBUF); 55938514Sdfr 56038514Sdfr /* 56139071Sdfr * Read the elf header from the file. 56238514Sdfr */ 56340962Speter firstpage = malloc(PAGE_SIZE, M_LINKER, M_WAITOK); 56440962Speter if (firstpage == NULL) { 56540962Speter error = ENOMEM; 56640962Speter goto out; 56740962Speter } 56840962Speter hdr = (Elf_Ehdr *)firstpage; 56940962Speter error = vn_rdwr(UIO_READ, nd.ni_vp, firstpage, PAGE_SIZE, 0, 57083366Sjulian UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, td); 57140962Speter nbytes = PAGE_SIZE - resid; 57238514Sdfr if (error) 57338514Sdfr goto out; 57438514Sdfr 57540962Speter if (!IS_ELF(*hdr)) { 57639071Sdfr error = ENOEXEC; 57738514Sdfr goto out; 57839071Sdfr } 57938514Sdfr 58040962Speter if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS 58140962Speter || hdr->e_ident[EI_DATA] != ELF_TARG_DATA) { 58239071Sdfr link_elf_error("Unsupported file layout"); 58339071Sdfr error = ENOEXEC; 58439071Sdfr goto out; 58539071Sdfr } 58640962Speter if (hdr->e_ident[EI_VERSION] != EV_CURRENT 58740962Speter || hdr->e_version != EV_CURRENT) { 58839071Sdfr link_elf_error("Unsupported file version"); 58939071Sdfr error = ENOEXEC; 59039071Sdfr goto out; 59139071Sdfr } 59240962Speter if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) { 59339071Sdfr link_elf_error("Unsupported file type"); 59439071Sdfr error = ENOEXEC; 59539071Sdfr goto out; 59639071Sdfr } 59740962Speter if (hdr->e_machine != ELF_TARG_MACH) { 59839071Sdfr link_elf_error("Unsupported machine"); 59939071Sdfr error = ENOEXEC; 60039071Sdfr goto out; 60139071Sdfr } 60239071Sdfr 60338514Sdfr /* 60439071Sdfr * We rely on the program header being in the first page. This is 60539071Sdfr * not strictly required by the ABI specification, but it seems to 60639071Sdfr * always true in practice. And, it simplifies things considerably. 60738514Sdfr */ 60840962Speter if (!((hdr->e_phentsize == sizeof(Elf_Phdr)) && 60940962Speter (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= PAGE_SIZE) && 61040962Speter (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= nbytes))) 61139071Sdfr link_elf_error("Unreadable program headers"); 61239071Sdfr 61338514Sdfr /* 61439071Sdfr * Scan the program header entries, and save key information. 61539071Sdfr * 61639071Sdfr * We rely on there being exactly two load segments, text and data, 61739071Sdfr * in that order. 61838514Sdfr */ 61940962Speter phdr = (Elf_Phdr *) (firstpage + hdr->e_phoff); 62040962Speter phlimit = phdr + hdr->e_phnum; 62139071Sdfr nsegs = 0; 62239071Sdfr phdyn = NULL; 62339071Sdfr phphdr = NULL; 62439071Sdfr while (phdr < phlimit) { 62539071Sdfr switch (phdr->p_type) { 62639071Sdfr 62739071Sdfr case PT_LOAD: 62839071Sdfr if (nsegs == 2) { 62939071Sdfr link_elf_error("Too many sections"); 63039071Sdfr error = ENOEXEC; 63139071Sdfr goto out; 63239071Sdfr } 63339071Sdfr segs[nsegs] = phdr; 63439071Sdfr ++nsegs; 63539071Sdfr break; 63639071Sdfr 63739071Sdfr case PT_PHDR: 63839071Sdfr phphdr = phdr; 63939071Sdfr break; 64039071Sdfr 64139071Sdfr case PT_DYNAMIC: 64239071Sdfr phdyn = phdr; 64339071Sdfr break; 64465503Sbp 64565503Sbp case PT_INTERP: 64665503Sbp link_elf_error("Unsupported file type"); 64765503Sbp error = ENOEXEC; 64865503Sbp goto out; 64939071Sdfr } 65039071Sdfr 65139071Sdfr ++phdr; 65239071Sdfr } 65339071Sdfr if (phdyn == NULL) { 65439071Sdfr link_elf_error("Object is not dynamically-linked"); 65539071Sdfr error = ENOEXEC; 65638514Sdfr goto out; 65739071Sdfr } 65838514Sdfr 65938514Sdfr /* 66039071Sdfr * Allocate the entire address space of the object, to stake out our 66139071Sdfr * contiguous region, and to establish the base address for relocation. 66238514Sdfr */ 66339071Sdfr base_offset = trunc_page(segs[0]->p_offset); 66439071Sdfr base_vaddr = trunc_page(segs[0]->p_vaddr); 66539071Sdfr base_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_memsz); 66639071Sdfr mapsize = base_vlimit - base_vaddr; 66739071Sdfr 66859603Sdfr lf = linker_make_file(filename, &link_elf_class); 66959603Sdfr if (!lf) { 67059603Sdfr error = ENOMEM; 67159603Sdfr goto out; 67259603Sdfr } 67359603Sdfr 67459603Sdfr ef = (elf_file_t) lf; 67539071Sdfr#ifdef SPARSE_MAPPING 67639071Sdfr ef->object = vm_object_allocate(OBJT_DEFAULT, mapsize >> PAGE_SHIFT); 67739071Sdfr if (ef->object == NULL) { 67838514Sdfr free(ef, M_LINKER); 67939071Sdfr error = ENOMEM; 68038514Sdfr goto out; 68138514Sdfr } 68239071Sdfr vm_object_reference(ef->object); 68339071Sdfr ef->address = (caddr_t) vm_map_min(kernel_map); 68439071Sdfr error = vm_map_find(kernel_map, ef->object, 0, 68539071Sdfr (vm_offset_t *) &ef->address, 68639071Sdfr mapsize, 1, 68739071Sdfr VM_PROT_ALL, VM_PROT_ALL, 0); 68839071Sdfr if (error) { 68939071Sdfr vm_object_deallocate(ef->object); 69059603Sdfr ef->object = 0; 69139071Sdfr goto out; 69239071Sdfr } 69339071Sdfr#else 69439071Sdfr ef->address = malloc(mapsize, M_LINKER, M_WAITOK); 69559603Sdfr if (!ef->address) { 69659603Sdfr error = ENOMEM; 69759603Sdfr goto out; 69859603Sdfr } 69939071Sdfr#endif 70039071Sdfr mapbase = ef->address; 70138514Sdfr 70239071Sdfr /* 70339071Sdfr * Read the text and data sections and zero the bss. 70439071Sdfr */ 70539071Sdfr for (i = 0; i < 2; i++) { 70639071Sdfr caddr_t segbase = mapbase + segs[i]->p_vaddr - base_vaddr; 70739071Sdfr error = vn_rdwr(UIO_READ, nd.ni_vp, 70839071Sdfr segbase, segs[i]->p_filesz, segs[i]->p_offset, 70983366Sjulian UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, td); 71039071Sdfr if (error) { 71139071Sdfr goto out; 71239071Sdfr } 71339071Sdfr bzero(segbase + segs[i]->p_filesz, 71439071Sdfr segs[i]->p_memsz - segs[i]->p_filesz); 71539071Sdfr 71639071Sdfr#ifdef SPARSE_MAPPING 71739071Sdfr /* 71839071Sdfr * Wire down the pages 71939071Sdfr */ 72039071Sdfr vm_map_pageable(kernel_map, 72139071Sdfr (vm_offset_t) segbase, 72239071Sdfr (vm_offset_t) segbase + segs[i]->p_memsz, 72339071Sdfr FALSE); 72439071Sdfr#endif 72539071Sdfr } 72639071Sdfr 72785734Sgreen#ifdef GPROF 72885734Sgreen /* Update profiling information with the new text segment. */ 72985734Sgreen kmupetext((uintfptr_t)(mapbase + segs[0]->p_vaddr - base_vaddr + 73085734Sgreen segs[0]->p_memsz)); 73185734Sgreen#endif 73285734Sgreen 73359603Sdfr ef->dynamic = (Elf_Dyn *) (mapbase + phdyn->p_vaddr - base_vaddr); 73439071Sdfr 73538514Sdfr lf->address = ef->address; 73639071Sdfr lf->size = mapsize; 73738514Sdfr 73859603Sdfr error = parse_dynamic(ef); 73940292Speter if (error) 74038514Sdfr goto out; 74186469Siedowse error = linker_load_dependencies(lf); 74240292Speter if (error) 74340156Speter goto out; 74459751Speter#if 0 /* this will be more trouble than it's worth for now */ 74559751Speter for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { 74659751Speter if (dp->d_tag != DT_NEEDED) 74759751Speter continue; 74859751Speter modname = ef->strtab + dp->d_un.d_val; 74959751Speter error = linker_load_module(modname, lf); 75059751Speter if (error) 75159751Speter goto out; 75259751Speter } 75359751Speter#endif 75459603Sdfr error = relocate_file(ef); 75540292Speter if (error) 75640156Speter goto out; 75740292Speter 75840292Speter /* Try and load the symbol table if it's present. (you can strip it!) */ 75940962Speter nbytes = hdr->e_shnum * hdr->e_shentsize; 76040962Speter if (nbytes == 0 || hdr->e_shoff == 0) 76140292Speter goto nosyms; 76269781Sdwmalone shdr = malloc(nbytes, M_LINKER, M_WAITOK | M_ZERO); 76340292Speter if (shdr == NULL) { 76440292Speter error = ENOMEM; 76540292Speter goto out; 76640156Speter } 76740292Speter error = vn_rdwr(UIO_READ, nd.ni_vp, 76840962Speter (caddr_t)shdr, nbytes, hdr->e_shoff, 76983366Sjulian UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, td); 77040292Speter if (error) 77140292Speter goto out; 77240292Speter symtabindex = -1; 77340292Speter symstrindex = -1; 77440962Speter for (i = 0; i < hdr->e_shnum; i++) { 77540292Speter if (shdr[i].sh_type == SHT_SYMTAB) { 77640292Speter symtabindex = i; 77740292Speter symstrindex = shdr[i].sh_link; 77840292Speter } 77940292Speter } 78040292Speter if (symtabindex < 0 || symstrindex < 0) 78140292Speter goto nosyms; 78240156Speter 78340292Speter symcnt = shdr[symtabindex].sh_size; 78440292Speter ef->symbase = malloc(symcnt, M_LINKER, M_WAITOK); 78540292Speter strcnt = shdr[symstrindex].sh_size; 78640292Speter ef->strbase = malloc(strcnt, M_LINKER, M_WAITOK); 78740292Speter 78840292Speter if (ef->symbase == NULL || ef->strbase == NULL) { 78940292Speter error = ENOMEM; 79040292Speter goto out; 79140292Speter } 79240292Speter error = vn_rdwr(UIO_READ, nd.ni_vp, 79340292Speter ef->symbase, symcnt, shdr[symtabindex].sh_offset, 79483366Sjulian UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, td); 79540292Speter if (error) 79640292Speter goto out; 79740292Speter error = vn_rdwr(UIO_READ, nd.ni_vp, 79840292Speter ef->strbase, strcnt, shdr[symstrindex].sh_offset, 79983366Sjulian UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, td); 80040292Speter if (error) 80140292Speter goto out; 80240292Speter 80340292Speter ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); 80440292Speter ef->ddbsymtab = (const Elf_Sym *)ef->symbase; 80540292Speter ef->ddbstrcnt = strcnt; 80640292Speter ef->ddbstrtab = ef->strbase; 80740292Speter 80859603Sdfr#ifdef DDB 80959603Sdfr GDB_STATE(RT_ADD); 81059603Sdfr ef->gdb.l_addr = lf->address; 81181201Sgreen newfilename = malloc(strlen(filename) + 1, M_LINKER, M_WAITOK); 81281201Sgreen strcpy(newfilename, filename); 81381201Sgreen ef->gdb.l_name = (const char *)newfilename; 81459603Sdfr ef->gdb.l_ld = ef->dynamic; 81559603Sdfr link_elf_add_gdb(&ef->gdb); 81659603Sdfr GDB_STATE(RT_CONSISTENT); 81759603Sdfr#endif 81859603Sdfr 81940292Speternosyms: 82040292Speter 82138514Sdfr *result = lf; 82238514Sdfr 82338514Sdfrout: 82440292Speter if (error && lf) 82540292Speter linker_file_unload(lf); 82640292Speter if (shdr) 82740292Speter free(shdr, M_LINKER); 82840962Speter if (firstpage) 82940962Speter free(firstpage, M_LINKER); 83083366Sjulian VOP_UNLOCK(nd.ni_vp, 0, td); 83183366Sjulian vn_close(nd.ni_vp, FREAD, p->p_ucred, td); 83238514Sdfr 83338514Sdfr return error; 83438514Sdfr} 83538514Sdfr 83638514Sdfrstatic void 83740156Speterlink_elf_unload_file(linker_file_t file) 83838514Sdfr{ 83959603Sdfr elf_file_t ef = (elf_file_t) file; 84038514Sdfr 84159603Sdfr#ifdef DDB 84259603Sdfr if (ef->gdb.l_ld) { 84359603Sdfr GDB_STATE(RT_DELETE); 84483282Speter free((void *)(uintptr_t)ef->gdb.l_name, M_LINKER); 84559603Sdfr link_elf_delete_gdb(&ef->gdb); 84659603Sdfr GDB_STATE(RT_CONSISTENT); 84759603Sdfr } 84859603Sdfr#endif 84959603Sdfr 85059603Sdfr if (ef->preloaded) { 85159751Speter link_elf_unload_preload(file); 85259603Sdfr return; 85359603Sdfr } 85439071Sdfr#ifdef SPARSE_MAPPING 85559603Sdfr if (ef->object) { 85659603Sdfr vm_map_remove(kernel_map, (vm_offset_t) ef->address, 85759603Sdfr (vm_offset_t) ef->address 85859603Sdfr + (ef->object->size << PAGE_SHIFT)); 85959603Sdfr vm_object_deallocate(ef->object); 86059603Sdfr } 86139071Sdfr#else 86259603Sdfr if (ef->address) 86359603Sdfr free(ef->address, M_LINKER); 86439071Sdfr#endif 86559603Sdfr if (ef->symbase) 86659603Sdfr free(ef->symbase, M_LINKER); 86759603Sdfr if (ef->strbase) 86859603Sdfr free(ef->strbase, M_LINKER); 86938514Sdfr} 87038514Sdfr 87140156Speterstatic void 87259751Speterlink_elf_unload_preload(linker_file_t file) 87340156Speter{ 87440156Speter if (file->filename) 87540156Speter preload_delete_name(file->filename); 87640156Speter} 87740156Speter 87839071Sdfrstatic const char * 87940435Spetersymbol_name(elf_file_t ef, Elf_Word r_info) 88038514Sdfr{ 88139071Sdfr const Elf_Sym *ref; 88238514Sdfr 88340435Speter if (ELF_R_SYM(r_info)) { 88440435Speter ref = ef->symtab + ELF_R_SYM(r_info); 88540397Speter return ef->strtab + ref->st_name; 88639071Sdfr } else 88739071Sdfr return NULL; 88838514Sdfr} 88938514Sdfr 89038514Sdfrstatic int 89159603Sdfrrelocate_file(elf_file_t ef) 89238514Sdfr{ 89339071Sdfr const Elf_Rel *rellim; 89439071Sdfr const Elf_Rel *rel; 89539071Sdfr const Elf_Rela *relalim; 89639071Sdfr const Elf_Rela *rela; 89740435Speter const char *symname; 89838514Sdfr 89939071Sdfr /* Perform relocations without addend if there are any: */ 90040435Speter rel = ef->rel; 90140435Speter if (rel) { 90243388Sbde rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize); 90340435Speter while (rel < rellim) { 90440435Speter symname = symbol_name(ef, rel->r_info); 90559603Sdfr if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL, symname)) { 90659744Speter printf("link_elf: symbol %s undefined\n", symname); 90740435Speter return ENOENT; 90842200Speter } 90940435Speter rel++; 91040435Speter } 91139071Sdfr } 91238514Sdfr 91339071Sdfr /* Perform relocations with addend if there are any: */ 91440435Speter rela = ef->rela; 91540435Speter if (rela) { 91643388Sbde relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize); 91740435Speter while (rela < relalim) { 91840435Speter symname = symbol_name(ef, rela->r_info); 91959603Sdfr if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA, symname)) { 92059744Speter printf("link_elf: symbol %s undefined\n", symname); 92140435Speter return ENOENT; 92242200Speter } 92340435Speter rela++; 92440435Speter } 92539071Sdfr } 92638514Sdfr 92739071Sdfr /* Perform PLT relocations without addend if there are any: */ 92840435Speter rel = ef->pltrel; 92940435Speter if (rel) { 93043388Sbde rellim = (const Elf_Rel *)((const char *)ef->pltrel + ef->pltrelsize); 93140435Speter while (rel < rellim) { 93240435Speter symname = symbol_name(ef, rel->r_info); 93359603Sdfr if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL, symname)) { 93459744Speter printf("link_elf: symbol %s undefined\n", symname); 93540435Speter return ENOENT; 93642200Speter } 93740435Speter rel++; 93840435Speter } 93939071Sdfr } 94038514Sdfr 94139071Sdfr /* Perform relocations with addend if there are any: */ 94240435Speter rela = ef->pltrela; 94340435Speter if (rela) { 94443388Sbde relalim = (const Elf_Rela *)((const char *)ef->pltrela + ef->pltrelasize); 94540435Speter while (rela < relalim) { 94640435Speter symname = symbol_name(ef, rela->r_info); 94759603Sdfr if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA, symname)) { 94859744Speter printf("link_elf: symbol %s undefined\n", symname); 94940435Speter return ENOENT; 95042200Speter } 95140435Speter rela++; 95240435Speter } 95338514Sdfr } 95438514Sdfr 95538514Sdfr return 0; 95638514Sdfr} 95738514Sdfr 95839071Sdfr/* 95939071Sdfr * Hash function for symbol table lookup. Don't even think about changing 96039071Sdfr * this. It is specified by the System V ABI. 96139071Sdfr */ 96239071Sdfrstatic unsigned long 96339071Sdfrelf_hash(const char *name) 96438514Sdfr{ 96539071Sdfr const unsigned char *p = (const unsigned char *) name; 96639071Sdfr unsigned long h = 0; 96739071Sdfr unsigned long g; 96838514Sdfr 96939071Sdfr while (*p != '\0') { 97039071Sdfr h = (h << 4) + *p++; 97139071Sdfr if ((g = h & 0xf0000000) != 0) 97239071Sdfr h ^= g >> 24; 97339071Sdfr h &= ~g; 97439071Sdfr } 97539071Sdfr return h; 97638514Sdfr} 97738514Sdfr 97838514Sdfrint 97943301Sdillonlink_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym) 98038514Sdfr{ 98159603Sdfr elf_file_t ef = (elf_file_t) lf; 98239071Sdfr unsigned long symnum; 98340254Speter const Elf_Sym* symp; 98440254Speter const char *strp; 98539071Sdfr unsigned long hash; 98639071Sdfr int i; 98738514Sdfr 98840254Speter /* First, search hashed global symbols */ 98939071Sdfr hash = elf_hash(name); 99039071Sdfr symnum = ef->buckets[hash % ef->nbuckets]; 99139071Sdfr 99239071Sdfr while (symnum != STN_UNDEF) { 99339071Sdfr if (symnum >= ef->nchains) { 99439071Sdfr printf("link_elf_lookup_symbol: corrupt symbol table\n"); 99539071Sdfr return ENOENT; 99638514Sdfr } 99738514Sdfr 99839071Sdfr symp = ef->symtab + symnum; 99939071Sdfr if (symp->st_name == 0) { 100039071Sdfr printf("link_elf_lookup_symbol: corrupt symbol table\n"); 100139071Sdfr return ENOENT; 100239071Sdfr } 100339071Sdfr 100439071Sdfr strp = ef->strtab + symp->st_name; 100539071Sdfr 100639071Sdfr if (strcmp(name, strp) == 0) { 100739071Sdfr if (symp->st_shndx != SHN_UNDEF || 100839071Sdfr (symp->st_value != 0 && 100939071Sdfr ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { 101043301Sdillon *sym = (c_linker_sym_t) symp; 101139071Sdfr return 0; 101239071Sdfr } else 101339071Sdfr return ENOENT; 101439071Sdfr } 101539071Sdfr 101639071Sdfr symnum = ef->chains[symnum]; 101739071Sdfr } 101839071Sdfr 101940254Speter /* If we have not found it, look at the full table (if loaded) */ 102040254Speter if (ef->symtab == ef->ddbsymtab) 102140254Speter return ENOENT; 102240254Speter 102340254Speter /* Exhaustive search */ 102440254Speter for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { 102540254Speter strp = ef->ddbstrtab + symp->st_name; 102640254Speter if (strcmp(name, strp) == 0) { 102740254Speter if (symp->st_shndx != SHN_UNDEF || 102840254Speter (symp->st_value != 0 && 102940254Speter ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { 103043301Sdillon *sym = (c_linker_sym_t) symp; 103140254Speter return 0; 103240254Speter } else 103340254Speter return ENOENT; 103440254Speter } 103540254Speter } 103640254Speter 103739071Sdfr return ENOENT; 103838514Sdfr} 103938514Sdfr 104040156Speterstatic int 104143309Sdillonlink_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, linker_symval_t* symval) 104238514Sdfr{ 104359603Sdfr elf_file_t ef = (elf_file_t) lf; 104443311Sdillon const Elf_Sym* es = (const Elf_Sym*) sym; 104538514Sdfr 104640254Speter if (es >= ef->symtab && ((es - ef->symtab) < ef->nchains)) { 104740254Speter symval->name = ef->strtab + es->st_name; 104840254Speter symval->value = (caddr_t) ef->address + es->st_value; 104940254Speter symval->size = es->st_size; 105040254Speter return 0; 105140254Speter } 105240254Speter if (ef->symtab == ef->ddbsymtab) 105340254Speter return ENOENT; 105440254Speter if (es >= ef->ddbsymtab && ((es - ef->ddbsymtab) < ef->ddbsymcnt)) { 105540254Speter symval->name = ef->ddbstrtab + es->st_name; 105640254Speter symval->value = (caddr_t) ef->address + es->st_value; 105740254Speter symval->size = es->st_size; 105840254Speter return 0; 105940254Speter } 106040254Speter return ENOENT; 106138514Sdfr} 106238514Sdfr 106338514Sdfrstatic int 106438514Sdfrlink_elf_search_symbol(linker_file_t lf, caddr_t value, 106543301Sdillon c_linker_sym_t* sym, long* diffp) 106638514Sdfr{ 106759603Sdfr elf_file_t ef = (elf_file_t) lf; 106855090Sbde u_long off = (uintptr_t) (void *) value; 106938514Sdfr u_long diff = off; 107055090Sbde u_long st_value; 107139071Sdfr const Elf_Sym* es; 107239071Sdfr const Elf_Sym* best = 0; 107338514Sdfr int i; 107438514Sdfr 107540254Speter for (i = 0, es = ef->ddbsymtab; i < ef->ddbsymcnt; i++, es++) { 107638514Sdfr if (es->st_name == 0) 107738514Sdfr continue; 107855090Sbde st_value = es->st_value + (uintptr_t) (void *) ef->address; 107953820Speter if (off >= st_value) { 108053820Speter if (off - st_value < diff) { 108153820Speter diff = off - st_value; 108238514Sdfr best = es; 108338514Sdfr if (diff == 0) 108438514Sdfr break; 108553820Speter } else if (off - st_value == diff) { 108638514Sdfr best = es; 108738514Sdfr } 108838514Sdfr } 108938514Sdfr } 109038514Sdfr if (best == 0) 109138514Sdfr *diffp = off; 109238514Sdfr else 109338514Sdfr *diffp = diff; 109443301Sdillon *sym = (c_linker_sym_t) best; 109538514Sdfr 109638514Sdfr return 0; 109738514Sdfr} 109878161Speter 109978161Speter/* 110078161Speter * Look up a linker set on an ELF system. 110178161Speter */ 110278161Speterstatic int 110378161Speterlink_elf_lookup_set(linker_file_t lf, const char *name, 110478161Speter void ***startp, void ***stopp, int *countp) 110578161Speter{ 110678161Speter c_linker_sym_t sym; 110778161Speter linker_symval_t symval; 110878161Speter char *setsym; 110978161Speter void **start, **stop; 111078161Speter int len, error = 0, count; 111178161Speter 111278161Speter len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */ 111378161Speter setsym = malloc(len, M_LINKER, M_WAITOK); 111478161Speter if (setsym == NULL) 111578161Speter return ENOMEM; 111678161Speter 111778161Speter /* get address of first entry */ 111878161Speter snprintf(setsym, len, "%s%s", "__start_set_", name); 111978161Speter error = link_elf_lookup_symbol(lf, setsym, &sym); 112078161Speter if (error) 112178161Speter goto out; 112278161Speter link_elf_symbol_values(lf, sym, &symval); 112378161Speter if (symval.value == 0) { 112478161Speter error = ESRCH; 112578161Speter goto out; 112678161Speter } 112778161Speter start = (void **)symval.value; 112878161Speter 112978161Speter /* get address of last entry */ 113078161Speter snprintf(setsym, len, "%s%s", "__stop_set_", name); 113178161Speter error = link_elf_lookup_symbol(lf, setsym, &sym); 113278161Speter if (error) 113378161Speter goto out; 113478161Speter link_elf_symbol_values(lf, sym, &symval); 113578161Speter if (symval.value == 0) { 113678161Speter error = ESRCH; 113778161Speter goto out; 113878161Speter } 113978161Speter stop = (void **)symval.value; 114078161Speter 114178161Speter /* and the number of entries */ 114278161Speter count = stop - start; 114378161Speter 114478161Speter /* and copy out */ 114578161Speter if (startp) 114678161Speter *startp = start; 114778161Speter if (stopp) 114878161Speter *stopp = stop; 114978161Speter if (countp) 115078161Speter *countp = count; 115178161Speter 115278161Speterout: 115378161Speter free(setsym, M_LINKER); 115478161Speter return error; 115578161Speter} 115685736Sgreen 115785736Sgreenstatic int 115885736Sgreenlink_elf_each_function_name(linker_file_t file, 115985736Sgreen int (*callback)(const char *, void *), void *opaque) { 116085736Sgreen elf_file_t ef = (elf_file_t)file; 116185736Sgreen const Elf_Sym* symp; 116285736Sgreen int i, error; 116385736Sgreen 116485736Sgreen /* Exhaustive search */ 116585736Sgreen for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { 116685736Sgreen if (symp->st_value != 0 && 116785736Sgreen ELF_ST_TYPE(symp->st_info) == STT_FUNC) { 116885736Sgreen error = callback(ef->ddbstrtab + symp->st_name, opaque); 116985736Sgreen if (error) 117085736Sgreen return (error); 117185736Sgreen } 117285736Sgreen } 117385736Sgreen return (0); 117485736Sgreen} 1175