link_elf.c revision 151430
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 */ 2638514Sdfr 27116182Sobrien#include <sys/cdefs.h> 28116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/link_elf.c 151430 2005-10-17 23:21:55Z peter $"); 29116182Sobrien 30131928Smarcel#include "opt_gdb.h" 31107089Srwatson#include "opt_mac.h" 3259603Sdfr 3338514Sdfr#include <sys/param.h> 3476166Smarkm#include <sys/systm.h> 35129443Sbde#ifdef GPROF 36129443Sbde#include <sys/gmon.h> 37129443Sbde#endif 3838514Sdfr#include <sys/kernel.h> 3976166Smarkm#include <sys/lock.h> 40107089Srwatson#include <sys/mac.h> 4138514Sdfr#include <sys/malloc.h> 4277642Sdd#include <sys/mutex.h> 4338514Sdfr#include <sys/proc.h> 4438514Sdfr#include <sys/namei.h> 4538514Sdfr#include <sys/fcntl.h> 4638514Sdfr#include <sys/vnode.h> 4738514Sdfr#include <sys/linker.h> 4876166Smarkm 4938514Sdfr#include <machine/elf.h> 5038514Sdfr 5139071Sdfr#include <vm/vm.h> 5239071Sdfr#include <vm/vm_param.h> 5352128Speter#ifdef SPARSE_MAPPING 5439071Sdfr#include <vm/vm_object.h> 5539071Sdfr#include <vm/vm_kern.h> 5639071Sdfr#include <vm/vm_extern.h> 5752128Speter#endif 5839071Sdfr#include <vm/pmap.h> 5939071Sdfr#include <vm/vm_map.h> 6076166Smarkm 61102288Speter#include <sys/link_elf.h> 6239071Sdfr 6359603Sdfr#include "linker_if.h" 6438514Sdfr 65151430Speter#define MAXSEGS 4 66151430Speter 6738514Sdfrtypedef struct elf_file { 6859603Sdfr struct linker_file lf; /* Common fields */ 6959603Sdfr int preloaded; /* Was file pre-loaded */ 7039071Sdfr caddr_t address; /* Relocation address */ 7139071Sdfr#ifdef SPARSE_MAPPING 7239071Sdfr vm_object_t object; /* VM object to hold file pages */ 7339071Sdfr#endif 7459603Sdfr Elf_Dyn* dynamic; /* Symbol table etc. */ 7580700Sjake Elf_Hashelt nbuckets; /* DT_HASH info */ 7680700Sjake Elf_Hashelt nchains; 7780700Sjake const Elf_Hashelt* buckets; 7880700Sjake const Elf_Hashelt* chains; 7938514Sdfr caddr_t hash; 8038514Sdfr caddr_t strtab; /* DT_STRTAB */ 8140254Speter int strsz; /* DT_STRSZ */ 8239071Sdfr const Elf_Sym* symtab; /* DT_SYMTAB */ 8339071Sdfr Elf_Addr* got; /* DT_PLTGOT */ 8439071Sdfr const Elf_Rel* pltrel; /* DT_JMPREL */ 8539071Sdfr int pltrelsize; /* DT_PLTRELSZ */ 8639071Sdfr const Elf_Rela* pltrela; /* DT_JMPREL */ 8739071Sdfr int pltrelasize; /* DT_PLTRELSZ */ 8839071Sdfr const Elf_Rel* rel; /* DT_REL */ 8939071Sdfr int relsize; /* DT_RELSZ */ 9039071Sdfr const Elf_Rela* rela; /* DT_RELA */ 9139071Sdfr int relasize; /* DT_RELASZ */ 9240254Speter caddr_t modptr; 9340254Speter const Elf_Sym* ddbsymtab; /* The symbol table we are using */ 9440254Speter long ddbsymcnt; /* Number of symbols */ 9540254Speter caddr_t ddbstrtab; /* String table */ 9640254Speter long ddbstrcnt; /* number of bytes in string table */ 9740292Speter caddr_t symbase; /* malloc'ed symbold base */ 9840292Speter caddr_t strbase; /* malloc'ed string base */ 99131928Smarcel#ifdef GDB 10059603Sdfr struct link_map gdb; /* hooks for gdb */ 10159603Sdfr#endif 10238514Sdfr} *elf_file_t; 10338514Sdfr 104105468Smarcelstatic int link_elf_link_common_finish(linker_file_t); 10559751Speterstatic int link_elf_link_preload(linker_class_t cls, 10659751Speter const char*, linker_file_t*); 10759751Speterstatic int link_elf_link_preload_finish(linker_file_t); 10859751Speterstatic int link_elf_load_file(linker_class_t, const char*, linker_file_t*); 10959603Sdfrstatic int link_elf_lookup_symbol(linker_file_t, const char*, 11059603Sdfr c_linker_sym_t*); 11159603Sdfrstatic int link_elf_symbol_values(linker_file_t, c_linker_sym_t, linker_symval_t*); 11259603Sdfrstatic int link_elf_search_symbol(linker_file_t, caddr_t value, 11359603Sdfr c_linker_sym_t* sym, long* diffp); 11438514Sdfr 11559603Sdfrstatic void link_elf_unload_file(linker_file_t); 11659751Speterstatic void link_elf_unload_preload(linker_file_t); 11778161Speterstatic int link_elf_lookup_set(linker_file_t, const char *, 11878161Speter void ***, void ***, int *); 11985736Sgreenstatic int link_elf_each_function_name(linker_file_t, 12085736Sgreen int (*)(const char *, void *), 12185736Sgreen void *); 122109605Sjakestatic void link_elf_reloc_local(linker_file_t); 123129282Speterstatic Elf_Addr elf_lookup(linker_file_t lf, Elf_Word symidx, int deps); 12459603Sdfr 12559603Sdfrstatic kobj_method_t link_elf_methods[] = { 12659603Sdfr KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol), 12759603Sdfr KOBJMETHOD(linker_symbol_values, link_elf_symbol_values), 12859603Sdfr KOBJMETHOD(linker_search_symbol, link_elf_search_symbol), 12959603Sdfr KOBJMETHOD(linker_unload, link_elf_unload_file), 13059751Speter KOBJMETHOD(linker_load_file, link_elf_load_file), 13159751Speter KOBJMETHOD(linker_link_preload, link_elf_link_preload), 13259751Speter KOBJMETHOD(linker_link_preload_finish, link_elf_link_preload_finish), 13378161Speter KOBJMETHOD(linker_lookup_set, link_elf_lookup_set), 13485736Sgreen KOBJMETHOD(linker_each_function_name, link_elf_each_function_name), 13559603Sdfr { 0, 0 } 13659603Sdfr}; 13759603Sdfr 13859603Sdfrstatic struct linker_class link_elf_class = { 13959603Sdfr#if ELF_TARG_CLASS == ELFCLASS32 14059603Sdfr "elf32", 14159603Sdfr#else 14259603Sdfr "elf64", 14359603Sdfr#endif 14459603Sdfr link_elf_methods, sizeof(struct elf_file) 14559603Sdfr}; 14659603Sdfr 14759603Sdfrstatic int parse_dynamic(elf_file_t ef); 14859603Sdfrstatic int relocate_file(elf_file_t ef); 14959751Speterstatic int link_elf_preload_parse_symbols(elf_file_t ef); 15059603Sdfr 151131928Smarcel#ifdef GDB 15266719Sjhbstatic void r_debug_state(struct r_debug *dummy_one, 15366719Sjhb struct link_map *dummy_two); 15459603Sdfr 15538514Sdfr/* 15659603Sdfr * A list of loaded modules for GDB to use for loading symbols. 15759603Sdfr */ 15859603Sdfrstruct r_debug r_debug; 15959603Sdfr 16066719Sjhb#define GDB_STATE(s) r_debug.r_state = s; r_debug_state(NULL, NULL); 16159603Sdfr 16259603Sdfr/* 16359603Sdfr * Function for the debugger to set a breakpoint on to gain control. 16459603Sdfr */ 165104094Sphkstatic void 16666719Sjhbr_debug_state(struct r_debug *dummy_one __unused, 16766719Sjhb struct link_map *dummy_two __unused) 16859603Sdfr{ 16959603Sdfr} 17059603Sdfr 171105467Smarcelstatic void 172105467Smarcellink_elf_add_gdb(struct link_map *l) 173105467Smarcel{ 174105467Smarcel struct link_map *prev; 17559603Sdfr 176105468Smarcel l->l_next = NULL; 177105467Smarcel 178105468Smarcel if (r_debug.r_map == NULL) { 179105468Smarcel /* Add first. */ 180105468Smarcel l->l_prev = NULL; 181105468Smarcel r_debug.r_map = l; 182105468Smarcel } else { 183105468Smarcel /* Append to list. */ 184105468Smarcel for (prev = r_debug.r_map; prev->l_next != NULL; prev = prev->l_next) 185105468Smarcel ; 186105468Smarcel l->l_prev = prev; 187105468Smarcel prev->l_next = l; 188105468Smarcel } 189105467Smarcel} 190105467Smarcel 191105467Smarcelstatic void 192105467Smarcellink_elf_delete_gdb(struct link_map *l) 193105467Smarcel{ 194105467Smarcel if (l->l_prev == NULL) { 195105468Smarcel /* Remove first. */ 196105467Smarcel if ((r_debug.r_map = l->l_next) != NULL) 197105467Smarcel l->l_next->l_prev = NULL; 198105468Smarcel } else { 199105468Smarcel /* Remove any but first. */ 200105468Smarcel if ((l->l_prev->l_next = l->l_next) != NULL) 201105468Smarcel l->l_next->l_prev = l->l_prev; 202105467Smarcel } 203105467Smarcel} 204131928Smarcel#endif /* GDB */ 205105467Smarcel 20695228Smarcel#ifdef __ia64__ 20795228SmarcelElf_Addr link_elf_get_gp(linker_file_t); 20895228Smarcel#endif 20995228Smarcel 21059603Sdfr/* 21138514Sdfr * The kernel symbol table starts here. 21238514Sdfr */ 21338514Sdfrextern struct _dynamic _DYNAMIC; 21438514Sdfr 21538514Sdfrstatic void 216105467Smarcellink_elf_error(const char *s) 217105467Smarcel{ 218105467Smarcel printf("kldload: %s\n", s); 219105467Smarcel} 220105467Smarcel 221105468Smarcel/* 222105468Smarcel * Actions performed after linking/loading both the preloaded kernel and any 223105468Smarcel * modules; whether preloaded or dynamicly loaded. 224105468Smarcel */ 225105468Smarcelstatic int 226105468Smarcellink_elf_link_common_finish(linker_file_t lf) 227105468Smarcel{ 228131928Smarcel#ifdef GDB 229105468Smarcel elf_file_t ef = (elf_file_t)lf; 230105468Smarcel char *newfilename; 231105468Smarcel#endif 232105469Smarcel int error; 233105468Smarcel 234105469Smarcel /* Notify MD code that a module is being loaded. */ 235105469Smarcel error = elf_cpu_load_file(lf); 236105469Smarcel if (error) 237105469Smarcel return (error); 238105469Smarcel 239131928Smarcel#ifdef GDB 240105468Smarcel GDB_STATE(RT_ADD); 241105468Smarcel ef->gdb.l_addr = lf->address; 242111119Simp newfilename = malloc(strlen(lf->filename) + 1, M_LINKER, M_WAITOK); 243105468Smarcel strcpy(newfilename, lf->filename); 244105468Smarcel ef->gdb.l_name = newfilename; 245105468Smarcel ef->gdb.l_ld = ef->dynamic; 246105468Smarcel link_elf_add_gdb(&ef->gdb); 247105468Smarcel GDB_STATE(RT_CONSISTENT); 248105468Smarcel#endif 249105468Smarcel 250105468Smarcel return (0); 251105468Smarcel} 252105468Smarcel 253105467Smarcelstatic void 25438514Sdfrlink_elf_init(void* arg) 25538514Sdfr{ 25640156Speter Elf_Dyn *dp; 25740156Speter caddr_t modptr, baseptr, sizeptr; 25840156Speter elf_file_t ef; 25940156Speter char *modname; 26038514Sdfr 26159603Sdfr linker_add_class(&link_elf_class); 26238514Sdfr 26340156Speter dp = (Elf_Dyn*) &_DYNAMIC; 26482848Speter modname = NULL; 265113158Speter modptr = preload_search_by_type("elf" __XSTRING(__ELF_WORD_SIZE) " kernel"); 266113158Speter if (modptr == NULL) 267113158Speter modptr = preload_search_by_type("elf kernel"); 26882848Speter if (modptr) 26982848Speter modname = (char *)preload_search_info(modptr, MODINFO_NAME); 27082848Speter if (modname == NULL) 27182848Speter modname = "kernel"; 27282848Speter linker_kernel_file = linker_make_file(modname, &link_elf_class); 27382848Speter if (linker_kernel_file == NULL) 27482848Speter panic("link_elf_init: Can't create linker structures for kernel"); 275105468Smarcel 27682848Speter ef = (elf_file_t) linker_kernel_file; 27782848Speter ef->preloaded = 1; 27882848Speter ef->address = 0; 27959603Sdfr#ifdef SPARSE_MAPPING 28082848Speter ef->object = 0; 28159603Sdfr#endif 28282848Speter ef->dynamic = dp; 28359603Sdfr 28482848Speter if (dp) 28582848Speter parse_dynamic(ef); 28682848Speter linker_kernel_file->address = (caddr_t) KERNBASE; 28782848Speter linker_kernel_file->size = -(intptr_t)linker_kernel_file->address; 28840156Speter 28982848Speter if (modptr) { 29082848Speter ef->modptr = modptr; 29182848Speter baseptr = preload_search_info(modptr, MODINFO_ADDR); 29282848Speter if (baseptr) 29382848Speter linker_kernel_file->address = *(caddr_t *)baseptr; 29482848Speter sizeptr = preload_search_info(modptr, MODINFO_SIZE); 29582848Speter if (sizeptr) 29682848Speter linker_kernel_file->size = *(size_t *)sizeptr; 29782848Speter } 29882848Speter (void)link_elf_preload_parse_symbols(ef); 29959603Sdfr 300131928Smarcel#ifdef GDB 301105468Smarcel r_debug.r_map = NULL; 30282848Speter r_debug.r_brk = r_debug_state; 30382848Speter r_debug.r_state = RT_CONSISTENT; 304105468Smarcel#endif 30559603Sdfr 306105468Smarcel (void)link_elf_link_common_finish(linker_kernel_file); 30738514Sdfr} 30838514Sdfr 30940156SpeterSYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0); 31038514Sdfr 31138514Sdfrstatic int 31259751Speterlink_elf_preload_parse_symbols(elf_file_t ef) 31340254Speter{ 31440254Speter caddr_t pointer; 31540254Speter caddr_t ssym, esym, base; 31640254Speter caddr_t strtab; 31740254Speter int strcnt; 31840254Speter Elf_Sym* symtab; 31940254Speter int symcnt; 32040254Speter 32140292Speter if (ef->modptr == NULL) 32240292Speter return 0; 32340254Speter pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_SSYM); 32440254Speter if (pointer == NULL) 32540254Speter return 0; 32640254Speter ssym = *(caddr_t *)pointer; 32740254Speter pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_ESYM); 32840254Speter if (pointer == NULL) 32940254Speter return 0; 33040254Speter esym = *(caddr_t *)pointer; 33140254Speter 33240254Speter base = ssym; 33340254Speter 33440254Speter symcnt = *(long *)base; 33540254Speter base += sizeof(long); 33640254Speter symtab = (Elf_Sym *)base; 33740254Speter base += roundup(symcnt, sizeof(long)); 33840254Speter 33940254Speter if (base > esym || base < ssym) { 34040254Speter printf("Symbols are corrupt!\n"); 34140254Speter return EINVAL; 34240254Speter } 34340254Speter 34440254Speter strcnt = *(long *)base; 34540254Speter base += sizeof(long); 34640254Speter strtab = base; 34740254Speter base += roundup(strcnt, sizeof(long)); 34840254Speter 34940254Speter if (base > esym || base < ssym) { 35040254Speter printf("Symbols are corrupt!\n"); 35140254Speter return EINVAL; 35240254Speter } 35340254Speter 35440254Speter ef->ddbsymtab = symtab; 35540254Speter ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); 35640254Speter ef->ddbstrtab = strtab; 35740254Speter ef->ddbstrcnt = strcnt; 35840254Speter 35940254Speter return 0; 36040254Speter} 36140254Speter 36240254Speterstatic int 36359603Sdfrparse_dynamic(elf_file_t ef) 36438514Sdfr{ 36559603Sdfr Elf_Dyn *dp; 36639071Sdfr int plttype = DT_REL; 36738514Sdfr 36838514Sdfr for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { 36938514Sdfr switch (dp->d_tag) { 37038514Sdfr case DT_HASH: 37138514Sdfr { 37238514Sdfr /* From src/libexec/rtld-elf/rtld.c */ 37380700Sjake const Elf_Hashelt *hashtab = (const Elf_Hashelt *) 37438514Sdfr (ef->address + dp->d_un.d_ptr); 37538514Sdfr ef->nbuckets = hashtab[0]; 37638514Sdfr ef->nchains = hashtab[1]; 37738514Sdfr ef->buckets = hashtab + 2; 37838514Sdfr ef->chains = ef->buckets + ef->nbuckets; 37938514Sdfr break; 38038514Sdfr } 38138514Sdfr case DT_STRTAB: 38239071Sdfr ef->strtab = (caddr_t) (ef->address + dp->d_un.d_ptr); 38338514Sdfr break; 38440254Speter case DT_STRSZ: 38540254Speter ef->strsz = dp->d_un.d_val; 38640254Speter break; 38738514Sdfr case DT_SYMTAB: 38839071Sdfr ef->symtab = (Elf_Sym*) (ef->address + dp->d_un.d_ptr); 38938514Sdfr break; 39038514Sdfr case DT_SYMENT: 39138514Sdfr if (dp->d_un.d_val != sizeof(Elf_Sym)) 39238514Sdfr return ENOEXEC; 39339071Sdfr break; 39439071Sdfr case DT_PLTGOT: 39539071Sdfr ef->got = (Elf_Addr *) (ef->address + dp->d_un.d_ptr); 39639071Sdfr break; 39739071Sdfr case DT_REL: 39839071Sdfr ef->rel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); 39939071Sdfr break; 40039071Sdfr case DT_RELSZ: 40139071Sdfr ef->relsize = dp->d_un.d_val; 40239071Sdfr break; 40339071Sdfr case DT_RELENT: 40439071Sdfr if (dp->d_un.d_val != sizeof(Elf_Rel)) 40539071Sdfr return ENOEXEC; 40639071Sdfr break; 40739071Sdfr case DT_JMPREL: 40839071Sdfr ef->pltrel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); 40939071Sdfr break; 41039071Sdfr case DT_PLTRELSZ: 41139071Sdfr ef->pltrelsize = dp->d_un.d_val; 41239071Sdfr break; 41339071Sdfr case DT_RELA: 41439071Sdfr ef->rela = (const Elf_Rela *) (ef->address + dp->d_un.d_ptr); 41539071Sdfr break; 41639071Sdfr case DT_RELASZ: 41739071Sdfr ef->relasize = dp->d_un.d_val; 41839071Sdfr break; 41939071Sdfr case DT_RELAENT: 42039071Sdfr if (dp->d_un.d_val != sizeof(Elf_Rela)) 42139071Sdfr return ENOEXEC; 42239071Sdfr break; 42339071Sdfr case DT_PLTREL: 42439071Sdfr plttype = dp->d_un.d_val; 42539071Sdfr if (plttype != DT_REL && plttype != DT_RELA) 42639071Sdfr return ENOEXEC; 42739071Sdfr break; 428131928Smarcel#ifdef GDB 42959603Sdfr case DT_DEBUG: 43059603Sdfr dp->d_un.d_ptr = (Elf_Addr) &r_debug; 43159603Sdfr break; 43259603Sdfr#endif 43338514Sdfr } 43438514Sdfr } 43539071Sdfr 43639071Sdfr if (plttype == DT_RELA) { 43739071Sdfr ef->pltrela = (const Elf_Rela *) ef->pltrel; 43839071Sdfr ef->pltrel = NULL; 43939071Sdfr ef->pltrelasize = ef->pltrelsize; 44039071Sdfr ef->pltrelsize = 0; 44139071Sdfr } 44239071Sdfr 44340254Speter ef->ddbsymtab = ef->symtab; 44440254Speter ef->ddbsymcnt = ef->nchains; 44540254Speter ef->ddbstrtab = ef->strtab; 44640254Speter ef->ddbstrcnt = ef->strsz; 44740254Speter 44838514Sdfr return 0; 44938514Sdfr} 45038514Sdfr 45138514Sdfrstatic int 45259751Speterlink_elf_link_preload(linker_class_t cls, 45359751Speter const char* filename, linker_file_t *result) 45440156Speter{ 45540156Speter caddr_t modptr, baseptr, sizeptr, dynptr; 45640156Speter char *type; 45740156Speter elf_file_t ef; 45840156Speter linker_file_t lf; 45940156Speter int error; 46040156Speter vm_offset_t dp; 46140156Speter 46259751Speter /* Look to see if we have the file preloaded */ 46340156Speter modptr = preload_search_by_name(filename); 46440156Speter if (modptr == NULL) 46559751Speter return ENOENT; 46640156Speter 46740156Speter type = (char *)preload_search_info(modptr, MODINFO_TYPE); 46840156Speter baseptr = preload_search_info(modptr, MODINFO_ADDR); 46940156Speter sizeptr = preload_search_info(modptr, MODINFO_SIZE); 47040156Speter dynptr = preload_search_info(modptr, MODINFO_METADATA|MODINFOMD_DYNAMIC); 471113158Speter if (type == NULL || 472113158Speter (strcmp(type, "elf" __XSTRING(__ELF_WORD_SIZE) " module") != 0 && 473113158Speter strcmp(type, "elf module") != 0)) 47440156Speter return (EFTYPE); 47540156Speter if (baseptr == NULL || sizeptr == NULL || dynptr == NULL) 47640156Speter return (EINVAL); 47740156Speter 47859603Sdfr lf = linker_make_file(filename, &link_elf_class); 47959603Sdfr if (lf == NULL) { 48059603Sdfr return ENOMEM; 48159603Sdfr } 48259603Sdfr 48359603Sdfr ef = (elf_file_t) lf; 48459603Sdfr ef->preloaded = 1; 48540292Speter ef->modptr = modptr; 48640156Speter ef->address = *(caddr_t *)baseptr; 48740156Speter#ifdef SPARSE_MAPPING 48840156Speter ef->object = 0; 48940156Speter#endif 49040156Speter dp = (vm_offset_t)ef->address + *(vm_offset_t *)dynptr; 49140156Speter ef->dynamic = (Elf_Dyn *)dp; 49240156Speter lf->address = ef->address; 49340156Speter lf->size = *(size_t *)sizeptr; 49440156Speter 49559603Sdfr error = parse_dynamic(ef); 49640156Speter if (error) { 497132117Sphk linker_file_unload(lf, LINKER_UNLOAD_FORCE); 49840156Speter return error; 49940156Speter } 500109605Sjake link_elf_reloc_local(lf); 50159751Speter *result = lf; 50259751Speter return (0); 50359751Speter} 50459751Speter 50559751Speterstatic int 50659751Speterlink_elf_link_preload_finish(linker_file_t lf) 50759751Speter{ 50859751Speter elf_file_t ef; 50959751Speter int error; 51059751Speter 51159751Speter ef = (elf_file_t) lf; 51259751Speter#if 0 /* this will be more trouble than it's worth for now */ 51359751Speter for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { 51459751Speter if (dp->d_tag != DT_NEEDED) 51559751Speter continue; 51659751Speter modname = ef->strtab + dp->d_un.d_val; 51759751Speter error = linker_load_module(modname, lf); 51859751Speter if (error) 51959751Speter goto out; 52040156Speter } 52159751Speter#endif 52259603Sdfr error = relocate_file(ef); 52359751Speter if (error) 52440156Speter return error; 52559751Speter (void)link_elf_preload_parse_symbols(ef); 52659603Sdfr 527105468Smarcel return (link_elf_link_common_finish(lf)); 52840156Speter} 52940156Speter 53040156Speterstatic int 531105468Smarcellink_elf_load_file(linker_class_t cls, const char* filename, 532105468Smarcel linker_file_t* result) 53338514Sdfr{ 53438514Sdfr struct nameidata nd; 53583366Sjulian struct thread* td = curthread; /* XXX */ 53640962Speter Elf_Ehdr *hdr; 53740962Speter caddr_t firstpage; 53839071Sdfr int nbytes, i; 53939071Sdfr Elf_Phdr *phdr; 54039071Sdfr Elf_Phdr *phlimit; 541151430Speter Elf_Phdr *segs[MAXSEGS]; 54239071Sdfr int nsegs; 54339071Sdfr Elf_Phdr *phdyn; 54439071Sdfr Elf_Phdr *phphdr; 54539071Sdfr caddr_t mapbase; 54639071Sdfr size_t mapsize; 54739071Sdfr Elf_Off base_offset; 54839071Sdfr Elf_Addr base_vaddr; 54939071Sdfr Elf_Addr base_vlimit; 55038514Sdfr int error = 0; 55162550Smckusick int resid, flags; 55238514Sdfr elf_file_t ef; 55338514Sdfr linker_file_t lf; 55440292Speter Elf_Shdr *shdr; 55540292Speter int symtabindex; 55640292Speter int symstrindex; 55740292Speter int symcnt; 55840292Speter int strcnt; 55938514Sdfr 56079224Sdillon GIANT_REQUIRED; 56179224Sdillon 56240292Speter shdr = NULL; 56340292Speter lf = NULL; 56440292Speter 56583366Sjulian NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, td); 56662550Smckusick flags = FREAD; 567118094Sphk error = vn_open(&nd, &flags, 0, -1); 56838514Sdfr if (error) 56938514Sdfr return error; 57054655Seivind NDFREE(&nd, NDF_ONLY_PNBUF); 571107089Srwatson#ifdef MAC 572107089Srwatson error = mac_check_kld_load(curthread->td_ucred, nd.ni_vp); 573107089Srwatson if (error) { 574107089Srwatson firstpage = NULL; 575107089Srwatson goto out; 576107089Srwatson } 577107089Srwatson#endif 57838514Sdfr 57938514Sdfr /* 58039071Sdfr * Read the elf header from the file. 58138514Sdfr */ 582111119Simp firstpage = malloc(PAGE_SIZE, M_LINKER, M_WAITOK); 58340962Speter if (firstpage == NULL) { 58440962Speter error = ENOMEM; 58540962Speter goto out; 58640962Speter } 58740962Speter hdr = (Elf_Ehdr *)firstpage; 58840962Speter error = vn_rdwr(UIO_READ, nd.ni_vp, firstpage, PAGE_SIZE, 0, 589101941Srwatson UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, 590101941Srwatson &resid, td); 59140962Speter nbytes = PAGE_SIZE - resid; 59238514Sdfr if (error) 59338514Sdfr goto out; 59438514Sdfr 59540962Speter if (!IS_ELF(*hdr)) { 59639071Sdfr error = ENOEXEC; 59738514Sdfr goto out; 59839071Sdfr } 59938514Sdfr 60040962Speter if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS 60140962Speter || hdr->e_ident[EI_DATA] != ELF_TARG_DATA) { 60239071Sdfr link_elf_error("Unsupported file layout"); 60339071Sdfr error = ENOEXEC; 60439071Sdfr goto out; 60539071Sdfr } 60640962Speter if (hdr->e_ident[EI_VERSION] != EV_CURRENT 60740962Speter || hdr->e_version != EV_CURRENT) { 60839071Sdfr link_elf_error("Unsupported file version"); 60939071Sdfr error = ENOEXEC; 61039071Sdfr goto out; 61139071Sdfr } 61240962Speter if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) { 61339071Sdfr link_elf_error("Unsupported file type"); 61439071Sdfr error = ENOEXEC; 61539071Sdfr goto out; 61639071Sdfr } 61740962Speter if (hdr->e_machine != ELF_TARG_MACH) { 61839071Sdfr link_elf_error("Unsupported machine"); 61939071Sdfr error = ENOEXEC; 62039071Sdfr goto out; 62139071Sdfr } 62239071Sdfr 62338514Sdfr /* 62439071Sdfr * We rely on the program header being in the first page. This is 62539071Sdfr * not strictly required by the ABI specification, but it seems to 62639071Sdfr * always true in practice. And, it simplifies things considerably. 62738514Sdfr */ 62840962Speter if (!((hdr->e_phentsize == sizeof(Elf_Phdr)) && 62940962Speter (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= PAGE_SIZE) && 63040962Speter (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= nbytes))) 63139071Sdfr link_elf_error("Unreadable program headers"); 63239071Sdfr 63338514Sdfr /* 63439071Sdfr * Scan the program header entries, and save key information. 63539071Sdfr * 63639071Sdfr * We rely on there being exactly two load segments, text and data, 63739071Sdfr * in that order. 63838514Sdfr */ 63940962Speter phdr = (Elf_Phdr *) (firstpage + hdr->e_phoff); 64040962Speter phlimit = phdr + hdr->e_phnum; 64139071Sdfr nsegs = 0; 64239071Sdfr phdyn = NULL; 64339071Sdfr phphdr = NULL; 64439071Sdfr while (phdr < phlimit) { 64539071Sdfr switch (phdr->p_type) { 64639071Sdfr 64739071Sdfr case PT_LOAD: 648151430Speter if (nsegs == MAXSEGS) { 64939071Sdfr link_elf_error("Too many sections"); 65039071Sdfr error = ENOEXEC; 65139071Sdfr goto out; 65239071Sdfr } 653114946Sphk /* 654114946Sphk * XXX: We just trust they come in right order ?? 655114946Sphk */ 65639071Sdfr segs[nsegs] = phdr; 65739071Sdfr ++nsegs; 65839071Sdfr break; 65939071Sdfr 66039071Sdfr case PT_PHDR: 66139071Sdfr phphdr = phdr; 66239071Sdfr break; 66339071Sdfr 66439071Sdfr case PT_DYNAMIC: 66539071Sdfr phdyn = phdr; 66639071Sdfr break; 66765503Sbp 66865503Sbp case PT_INTERP: 66965503Sbp link_elf_error("Unsupported file type"); 67065503Sbp error = ENOEXEC; 67165503Sbp goto out; 67239071Sdfr } 67339071Sdfr 67439071Sdfr ++phdr; 67539071Sdfr } 67639071Sdfr if (phdyn == NULL) { 67739071Sdfr link_elf_error("Object is not dynamically-linked"); 67839071Sdfr error = ENOEXEC; 67938514Sdfr goto out; 68039071Sdfr } 681151430Speter if (nsegs == 0) { 682151430Speter link_elf_error("No sections"); 683114946Sphk error = ENOEXEC; 684114946Sphk goto out; 685114946Sphk } 68638514Sdfr 68738514Sdfr /* 68839071Sdfr * Allocate the entire address space of the object, to stake out our 68939071Sdfr * contiguous region, and to establish the base address for relocation. 69038514Sdfr */ 69139071Sdfr base_offset = trunc_page(segs[0]->p_offset); 69239071Sdfr base_vaddr = trunc_page(segs[0]->p_vaddr); 693151430Speter base_vlimit = round_page(segs[nsegs - 1]->p_vaddr + 694151430Speter segs[nsegs - 1]->p_memsz); 69539071Sdfr mapsize = base_vlimit - base_vaddr; 69639071Sdfr 69759603Sdfr lf = linker_make_file(filename, &link_elf_class); 69859603Sdfr if (!lf) { 69959603Sdfr error = ENOMEM; 70059603Sdfr goto out; 70159603Sdfr } 70259603Sdfr 70359603Sdfr ef = (elf_file_t) lf; 70439071Sdfr#ifdef SPARSE_MAPPING 70539071Sdfr ef->object = vm_object_allocate(OBJT_DEFAULT, mapsize >> PAGE_SHIFT); 70639071Sdfr if (ef->object == NULL) { 70739071Sdfr error = ENOMEM; 70838514Sdfr goto out; 70938514Sdfr } 71039071Sdfr ef->address = (caddr_t) vm_map_min(kernel_map); 71139071Sdfr error = vm_map_find(kernel_map, ef->object, 0, 71239071Sdfr (vm_offset_t *) &ef->address, 71339071Sdfr mapsize, 1, 71439071Sdfr VM_PROT_ALL, VM_PROT_ALL, 0); 71539071Sdfr if (error) { 71639071Sdfr vm_object_deallocate(ef->object); 71759603Sdfr ef->object = 0; 71839071Sdfr goto out; 71939071Sdfr } 72039071Sdfr#else 721111119Simp ef->address = malloc(mapsize, M_LINKER, M_WAITOK); 72259603Sdfr if (!ef->address) { 72359603Sdfr error = ENOMEM; 72459603Sdfr goto out; 72559603Sdfr } 72639071Sdfr#endif 72739071Sdfr mapbase = ef->address; 72838514Sdfr 72939071Sdfr /* 73039071Sdfr * Read the text and data sections and zero the bss. 73139071Sdfr */ 732151430Speter for (i = 0; i < nsegs; i++) { 73339071Sdfr caddr_t segbase = mapbase + segs[i]->p_vaddr - base_vaddr; 73439071Sdfr error = vn_rdwr(UIO_READ, nd.ni_vp, 73539071Sdfr segbase, segs[i]->p_filesz, segs[i]->p_offset, 736101941Srwatson UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, 737101941Srwatson &resid, td); 73839071Sdfr if (error) { 73939071Sdfr goto out; 74039071Sdfr } 74139071Sdfr bzero(segbase + segs[i]->p_filesz, 74239071Sdfr segs[i]->p_memsz - segs[i]->p_filesz); 743149539Salc 744149539Salc#ifdef SPARSE_MAPPING 745149539Salc /* 746149539Salc * Wire down the pages 747149539Salc */ 748149544Salc error = vm_map_wire(kernel_map, 749149539Salc (vm_offset_t) segbase, 750149539Salc (vm_offset_t) segbase + segs[i]->p_memsz, 751149539Salc VM_MAP_WIRE_SYSTEM|VM_MAP_WIRE_NOHOLES); 752149544Salc if (error != KERN_SUCCESS) { 753149544Salc error = ENOMEM; 754149544Salc goto out; 755149544Salc } 756149539Salc#endif 75739071Sdfr } 75839071Sdfr 75985734Sgreen#ifdef GPROF 76085734Sgreen /* Update profiling information with the new text segment. */ 76185734Sgreen kmupetext((uintfptr_t)(mapbase + segs[0]->p_vaddr - base_vaddr + 76285734Sgreen segs[0]->p_memsz)); 76385734Sgreen#endif 76485734Sgreen 76559603Sdfr ef->dynamic = (Elf_Dyn *) (mapbase + phdyn->p_vaddr - base_vaddr); 76639071Sdfr 76738514Sdfr lf->address = ef->address; 76839071Sdfr lf->size = mapsize; 76938514Sdfr 77059603Sdfr error = parse_dynamic(ef); 77140292Speter if (error) 77238514Sdfr goto out; 773109605Sjake link_elf_reloc_local(lf); 774109605Sjake 77586469Siedowse error = linker_load_dependencies(lf); 77640292Speter if (error) 77740156Speter goto out; 77859751Speter#if 0 /* this will be more trouble than it's worth for now */ 77959751Speter for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { 78059751Speter if (dp->d_tag != DT_NEEDED) 78159751Speter continue; 78259751Speter modname = ef->strtab + dp->d_un.d_val; 78359751Speter error = linker_load_module(modname, lf); 78459751Speter if (error) 78559751Speter goto out; 78659751Speter } 78759751Speter#endif 78859603Sdfr error = relocate_file(ef); 78940292Speter if (error) 79040156Speter goto out; 79140292Speter 79240292Speter /* Try and load the symbol table if it's present. (you can strip it!) */ 79340962Speter nbytes = hdr->e_shnum * hdr->e_shentsize; 79440962Speter if (nbytes == 0 || hdr->e_shoff == 0) 79540292Speter goto nosyms; 796111119Simp shdr = malloc(nbytes, M_LINKER, M_WAITOK | M_ZERO); 79740292Speter if (shdr == NULL) { 79840292Speter error = ENOMEM; 79940292Speter goto out; 80040156Speter } 80140292Speter error = vn_rdwr(UIO_READ, nd.ni_vp, 80240962Speter (caddr_t)shdr, nbytes, hdr->e_shoff, 803101941Srwatson UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, 804101941Srwatson &resid, td); 80540292Speter if (error) 80640292Speter goto out; 80740292Speter symtabindex = -1; 80840292Speter symstrindex = -1; 80940962Speter for (i = 0; i < hdr->e_shnum; i++) { 81040292Speter if (shdr[i].sh_type == SHT_SYMTAB) { 81140292Speter symtabindex = i; 81240292Speter symstrindex = shdr[i].sh_link; 81340292Speter } 81440292Speter } 81540292Speter if (symtabindex < 0 || symstrindex < 0) 81640292Speter goto nosyms; 81740156Speter 81840292Speter symcnt = shdr[symtabindex].sh_size; 819111119Simp ef->symbase = malloc(symcnt, M_LINKER, M_WAITOK); 82040292Speter strcnt = shdr[symstrindex].sh_size; 821111119Simp ef->strbase = malloc(strcnt, M_LINKER, M_WAITOK); 82240292Speter 82340292Speter if (ef->symbase == NULL || ef->strbase == NULL) { 82440292Speter error = ENOMEM; 82540292Speter goto out; 82640292Speter } 82740292Speter error = vn_rdwr(UIO_READ, nd.ni_vp, 82840292Speter ef->symbase, symcnt, shdr[symtabindex].sh_offset, 829101941Srwatson UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, 830101941Srwatson &resid, td); 83140292Speter if (error) 83240292Speter goto out; 83340292Speter error = vn_rdwr(UIO_READ, nd.ni_vp, 83440292Speter ef->strbase, strcnt, shdr[symstrindex].sh_offset, 835101941Srwatson UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, 836101941Srwatson &resid, td); 83740292Speter if (error) 83840292Speter goto out; 83940292Speter 84040292Speter ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); 84140292Speter ef->ddbsymtab = (const Elf_Sym *)ef->symbase; 84240292Speter ef->ddbstrcnt = strcnt; 84340292Speter ef->ddbstrtab = ef->strbase; 84440292Speter 845105468Smarcel error = link_elf_link_common_finish(lf); 846105468Smarcel if (error) 847105468Smarcel goto out; 84859603Sdfr 84940292Speternosyms: 85040292Speter 85138514Sdfr *result = lf; 85238514Sdfr 85338514Sdfrout: 85440292Speter if (error && lf) 855132117Sphk linker_file_unload(lf, LINKER_UNLOAD_FORCE); 85640292Speter if (shdr) 85740292Speter free(shdr, M_LINKER); 85840962Speter if (firstpage) 85940962Speter free(firstpage, M_LINKER); 86083366Sjulian VOP_UNLOCK(nd.ni_vp, 0, td); 86191406Sjhb vn_close(nd.ni_vp, FREAD, td->td_ucred, td); 86238514Sdfr 86338514Sdfr return error; 86438514Sdfr} 86538514Sdfr 86638514Sdfrstatic void 86740156Speterlink_elf_unload_file(linker_file_t file) 86838514Sdfr{ 86959603Sdfr elf_file_t ef = (elf_file_t) file; 87038514Sdfr 871131928Smarcel#ifdef GDB 87259603Sdfr if (ef->gdb.l_ld) { 87359603Sdfr GDB_STATE(RT_DELETE); 87483282Speter free((void *)(uintptr_t)ef->gdb.l_name, M_LINKER); 87559603Sdfr link_elf_delete_gdb(&ef->gdb); 87659603Sdfr GDB_STATE(RT_CONSISTENT); 87759603Sdfr } 87859603Sdfr#endif 87959603Sdfr 880105469Smarcel /* Notify MD code that a module is being unloaded. */ 881105469Smarcel elf_cpu_unload_file(file); 882105469Smarcel 88359603Sdfr if (ef->preloaded) { 88459751Speter link_elf_unload_preload(file); 88559603Sdfr return; 88659603Sdfr } 887105468Smarcel 88839071Sdfr#ifdef SPARSE_MAPPING 88959603Sdfr if (ef->object) { 89059603Sdfr vm_map_remove(kernel_map, (vm_offset_t) ef->address, 89159603Sdfr (vm_offset_t) ef->address 89259603Sdfr + (ef->object->size << PAGE_SHIFT)); 89359603Sdfr } 89439071Sdfr#else 89559603Sdfr if (ef->address) 89659603Sdfr free(ef->address, M_LINKER); 89739071Sdfr#endif 89859603Sdfr if (ef->symbase) 89959603Sdfr free(ef->symbase, M_LINKER); 90059603Sdfr if (ef->strbase) 90159603Sdfr free(ef->strbase, M_LINKER); 90238514Sdfr} 90338514Sdfr 90440156Speterstatic void 90559751Speterlink_elf_unload_preload(linker_file_t file) 90640156Speter{ 90740156Speter if (file->filename) 90840156Speter preload_delete_name(file->filename); 90940156Speter} 91040156Speter 91139071Sdfrstatic const char * 91240435Spetersymbol_name(elf_file_t ef, Elf_Word r_info) 91338514Sdfr{ 91439071Sdfr const Elf_Sym *ref; 91538514Sdfr 91640435Speter if (ELF_R_SYM(r_info)) { 91740435Speter ref = ef->symtab + ELF_R_SYM(r_info); 91840397Speter return ef->strtab + ref->st_name; 91939071Sdfr } else 92039071Sdfr return NULL; 92138514Sdfr} 92238514Sdfr 92338514Sdfrstatic int 92459603Sdfrrelocate_file(elf_file_t ef) 92538514Sdfr{ 92639071Sdfr const Elf_Rel *rellim; 92739071Sdfr const Elf_Rel *rel; 92839071Sdfr const Elf_Rela *relalim; 92939071Sdfr const Elf_Rela *rela; 93040435Speter const char *symname; 93138514Sdfr 93239071Sdfr /* Perform relocations without addend if there are any: */ 93340435Speter rel = ef->rel; 93440435Speter if (rel) { 93543388Sbde rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize); 93640435Speter while (rel < rellim) { 937129282Speter if (elf_reloc(&ef->lf, (Elf_Addr)ef->address, rel, ELF_RELOC_REL, 938129282Speter elf_lookup)) { 93995410Smarcel symname = symbol_name(ef, rel->r_info); 94059744Speter printf("link_elf: symbol %s undefined\n", symname); 94140435Speter return ENOENT; 94242200Speter } 94340435Speter rel++; 94440435Speter } 94539071Sdfr } 94638514Sdfr 94739071Sdfr /* Perform relocations with addend if there are any: */ 94840435Speter rela = ef->rela; 94940435Speter if (rela) { 95043388Sbde relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize); 95140435Speter while (rela < relalim) { 952129282Speter if (elf_reloc(&ef->lf, (Elf_Addr)ef->address, rela, ELF_RELOC_RELA, 953129282Speter elf_lookup)) { 95495410Smarcel symname = symbol_name(ef, rela->r_info); 95559744Speter printf("link_elf: symbol %s undefined\n", symname); 95640435Speter return ENOENT; 95742200Speter } 95840435Speter rela++; 95940435Speter } 96039071Sdfr } 96138514Sdfr 96239071Sdfr /* Perform PLT relocations without addend if there are any: */ 96340435Speter rel = ef->pltrel; 96440435Speter if (rel) { 96543388Sbde rellim = (const Elf_Rel *)((const char *)ef->pltrel + ef->pltrelsize); 96640435Speter while (rel < rellim) { 967129282Speter if (elf_reloc(&ef->lf, (Elf_Addr)ef->address, rel, ELF_RELOC_REL, 968129282Speter elf_lookup)) { 96995410Smarcel symname = symbol_name(ef, rel->r_info); 97059744Speter printf("link_elf: symbol %s undefined\n", symname); 97140435Speter return ENOENT; 97242200Speter } 97340435Speter rel++; 97440435Speter } 97539071Sdfr } 97638514Sdfr 97739071Sdfr /* Perform relocations with addend if there are any: */ 97840435Speter rela = ef->pltrela; 97940435Speter if (rela) { 98043388Sbde relalim = (const Elf_Rela *)((const char *)ef->pltrela + ef->pltrelasize); 98140435Speter while (rela < relalim) { 982129282Speter if (elf_reloc(&ef->lf, (Elf_Addr)ef->address, rela, ELF_RELOC_RELA, 983129282Speter elf_lookup)) { 98495410Smarcel symname = symbol_name(ef, rela->r_info); 98559744Speter printf("link_elf: symbol %s undefined\n", symname); 98640435Speter return ENOENT; 98742200Speter } 98840435Speter rela++; 98940435Speter } 99038514Sdfr } 99138514Sdfr 99238514Sdfr return 0; 99338514Sdfr} 99438514Sdfr 99539071Sdfr/* 99639071Sdfr * Hash function for symbol table lookup. Don't even think about changing 99739071Sdfr * this. It is specified by the System V ABI. 99839071Sdfr */ 99939071Sdfrstatic unsigned long 100039071Sdfrelf_hash(const char *name) 100138514Sdfr{ 100239071Sdfr const unsigned char *p = (const unsigned char *) name; 100339071Sdfr unsigned long h = 0; 100439071Sdfr unsigned long g; 100538514Sdfr 100639071Sdfr while (*p != '\0') { 100739071Sdfr h = (h << 4) + *p++; 100839071Sdfr if ((g = h & 0xf0000000) != 0) 100939071Sdfr h ^= g >> 24; 101039071Sdfr h &= ~g; 101139071Sdfr } 101239071Sdfr return h; 101338514Sdfr} 101438514Sdfr 1015104094Sphkstatic int 101643301Sdillonlink_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym) 101738514Sdfr{ 101859603Sdfr elf_file_t ef = (elf_file_t) lf; 101939071Sdfr unsigned long symnum; 102040254Speter const Elf_Sym* symp; 102140254Speter const char *strp; 102239071Sdfr unsigned long hash; 102339071Sdfr int i; 102438514Sdfr 102540254Speter /* First, search hashed global symbols */ 102639071Sdfr hash = elf_hash(name); 102739071Sdfr symnum = ef->buckets[hash % ef->nbuckets]; 102839071Sdfr 102939071Sdfr while (symnum != STN_UNDEF) { 103039071Sdfr if (symnum >= ef->nchains) { 103139071Sdfr printf("link_elf_lookup_symbol: corrupt symbol table\n"); 103239071Sdfr return ENOENT; 103338514Sdfr } 103438514Sdfr 103539071Sdfr symp = ef->symtab + symnum; 103639071Sdfr if (symp->st_name == 0) { 103739071Sdfr printf("link_elf_lookup_symbol: corrupt symbol table\n"); 103839071Sdfr return ENOENT; 103939071Sdfr } 104039071Sdfr 104139071Sdfr strp = ef->strtab + symp->st_name; 104239071Sdfr 104339071Sdfr if (strcmp(name, strp) == 0) { 104439071Sdfr if (symp->st_shndx != SHN_UNDEF || 104539071Sdfr (symp->st_value != 0 && 104639071Sdfr ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { 104743301Sdillon *sym = (c_linker_sym_t) symp; 104839071Sdfr return 0; 104939071Sdfr } else 105039071Sdfr return ENOENT; 105139071Sdfr } 105239071Sdfr 105339071Sdfr symnum = ef->chains[symnum]; 105439071Sdfr } 105539071Sdfr 105640254Speter /* If we have not found it, look at the full table (if loaded) */ 105740254Speter if (ef->symtab == ef->ddbsymtab) 105840254Speter return ENOENT; 105940254Speter 106040254Speter /* Exhaustive search */ 106140254Speter for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { 106240254Speter strp = ef->ddbstrtab + symp->st_name; 106340254Speter if (strcmp(name, strp) == 0) { 106440254Speter if (symp->st_shndx != SHN_UNDEF || 106540254Speter (symp->st_value != 0 && 106640254Speter ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { 106743301Sdillon *sym = (c_linker_sym_t) symp; 106840254Speter return 0; 106940254Speter } else 107040254Speter return ENOENT; 107140254Speter } 107240254Speter } 107340254Speter 107439071Sdfr return ENOENT; 107538514Sdfr} 107638514Sdfr 107740156Speterstatic int 107843309Sdillonlink_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, linker_symval_t* symval) 107938514Sdfr{ 108059603Sdfr elf_file_t ef = (elf_file_t) lf; 108143311Sdillon const Elf_Sym* es = (const Elf_Sym*) sym; 108238514Sdfr 1083102348Smarcel if (es >= ef->symtab && es < (ef->symtab + ef->nchains)) { 108440254Speter symval->name = ef->strtab + es->st_name; 108540254Speter symval->value = (caddr_t) ef->address + es->st_value; 108640254Speter symval->size = es->st_size; 108740254Speter return 0; 108840254Speter } 108940254Speter if (ef->symtab == ef->ddbsymtab) 109040254Speter return ENOENT; 1091102348Smarcel if (es >= ef->ddbsymtab && es < (ef->ddbsymtab + ef->ddbsymcnt)) { 109240254Speter symval->name = ef->ddbstrtab + es->st_name; 109340254Speter symval->value = (caddr_t) ef->address + es->st_value; 109440254Speter symval->size = es->st_size; 109540254Speter return 0; 109640254Speter } 109740254Speter return ENOENT; 109838514Sdfr} 109938514Sdfr 110038514Sdfrstatic int 110138514Sdfrlink_elf_search_symbol(linker_file_t lf, caddr_t value, 110243301Sdillon c_linker_sym_t* sym, long* diffp) 110338514Sdfr{ 110459603Sdfr elf_file_t ef = (elf_file_t) lf; 110555090Sbde u_long off = (uintptr_t) (void *) value; 110638514Sdfr u_long diff = off; 110755090Sbde u_long st_value; 110839071Sdfr const Elf_Sym* es; 110939071Sdfr const Elf_Sym* best = 0; 111038514Sdfr int i; 111138514Sdfr 111240254Speter for (i = 0, es = ef->ddbsymtab; i < ef->ddbsymcnt; i++, es++) { 111338514Sdfr if (es->st_name == 0) 111438514Sdfr continue; 111555090Sbde st_value = es->st_value + (uintptr_t) (void *) ef->address; 111653820Speter if (off >= st_value) { 111753820Speter if (off - st_value < diff) { 111853820Speter diff = off - st_value; 111938514Sdfr best = es; 112038514Sdfr if (diff == 0) 112138514Sdfr break; 112253820Speter } else if (off - st_value == diff) { 112338514Sdfr best = es; 112438514Sdfr } 112538514Sdfr } 112638514Sdfr } 112738514Sdfr if (best == 0) 112838514Sdfr *diffp = off; 112938514Sdfr else 113038514Sdfr *diffp = diff; 113143301Sdillon *sym = (c_linker_sym_t) best; 113238514Sdfr 113338514Sdfr return 0; 113438514Sdfr} 113578161Speter 113678161Speter/* 113778161Speter * Look up a linker set on an ELF system. 113878161Speter */ 113978161Speterstatic int 114078161Speterlink_elf_lookup_set(linker_file_t lf, const char *name, 114178161Speter void ***startp, void ***stopp, int *countp) 114278161Speter{ 114378161Speter c_linker_sym_t sym; 114478161Speter linker_symval_t symval; 114578161Speter char *setsym; 114678161Speter void **start, **stop; 114778161Speter int len, error = 0, count; 114878161Speter 114978161Speter len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */ 1150111119Simp setsym = malloc(len, M_LINKER, M_WAITOK); 115178161Speter if (setsym == NULL) 115278161Speter return ENOMEM; 115378161Speter 115478161Speter /* get address of first entry */ 115578161Speter snprintf(setsym, len, "%s%s", "__start_set_", name); 115678161Speter error = link_elf_lookup_symbol(lf, setsym, &sym); 115778161Speter if (error) 115878161Speter goto out; 115978161Speter link_elf_symbol_values(lf, sym, &symval); 116078161Speter if (symval.value == 0) { 116178161Speter error = ESRCH; 116278161Speter goto out; 116378161Speter } 116478161Speter start = (void **)symval.value; 116578161Speter 116678161Speter /* get address of last entry */ 116778161Speter snprintf(setsym, len, "%s%s", "__stop_set_", name); 116878161Speter error = link_elf_lookup_symbol(lf, setsym, &sym); 116978161Speter if (error) 117078161Speter goto out; 117178161Speter link_elf_symbol_values(lf, sym, &symval); 117278161Speter if (symval.value == 0) { 117378161Speter error = ESRCH; 117478161Speter goto out; 117578161Speter } 117678161Speter stop = (void **)symval.value; 117778161Speter 117878161Speter /* and the number of entries */ 117978161Speter count = stop - start; 118078161Speter 118178161Speter /* and copy out */ 118278161Speter if (startp) 118378161Speter *startp = start; 118478161Speter if (stopp) 118578161Speter *stopp = stop; 118678161Speter if (countp) 118778161Speter *countp = count; 118878161Speter 118978161Speterout: 119078161Speter free(setsym, M_LINKER); 119178161Speter return error; 119278161Speter} 119385736Sgreen 119485736Sgreenstatic int 119585736Sgreenlink_elf_each_function_name(linker_file_t file, 119685736Sgreen int (*callback)(const char *, void *), void *opaque) { 119785736Sgreen elf_file_t ef = (elf_file_t)file; 119885736Sgreen const Elf_Sym* symp; 119985736Sgreen int i, error; 120085736Sgreen 120185736Sgreen /* Exhaustive search */ 120285736Sgreen for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { 120385736Sgreen if (symp->st_value != 0 && 120485736Sgreen ELF_ST_TYPE(symp->st_info) == STT_FUNC) { 120585736Sgreen error = callback(ef->ddbstrtab + symp->st_name, opaque); 120685736Sgreen if (error) 120785736Sgreen return (error); 120885736Sgreen } 120985736Sgreen } 121085736Sgreen return (0); 121185736Sgreen} 121295228Smarcel 121395228Smarcel#ifdef __ia64__ 121495228Smarcel/* 121595228Smarcel * Each KLD has its own GP. The GP value for each load module is given by 121695228Smarcel * DT_PLTGOT on ia64. We need GP to construct function descriptors, but 121795228Smarcel * don't have direct access to the ELF file structure. The link_elf_get_gp() 121895228Smarcel * function returns the GP given a pointer to a generic linker file struct. 121995228Smarcel */ 122095228SmarcelElf_Addr 122195228Smarcellink_elf_get_gp(linker_file_t lf) 122295228Smarcel{ 122395228Smarcel elf_file_t ef = (elf_file_t)lf; 122495228Smarcel return (Elf_Addr)ef->got; 122595228Smarcel} 122695228Smarcel#endif 122795410Smarcel 1228104072Sjakeconst Elf_Sym * 1229104072Sjakeelf_get_sym(linker_file_t lf, Elf_Word symidx) 1230104072Sjake{ 1231104072Sjake elf_file_t ef = (elf_file_t)lf; 1232104072Sjake 1233104072Sjake if (symidx >= ef->nchains) 1234104072Sjake return (NULL); 1235104072Sjake return (ef->symtab + symidx); 1236104072Sjake} 1237104072Sjake 1238105147Smarcelconst char * 1239105147Smarcelelf_get_symname(linker_file_t lf, Elf_Word symidx) 1240105147Smarcel{ 1241105147Smarcel elf_file_t ef = (elf_file_t)lf; 1242105147Smarcel const Elf_Sym *sym; 1243105147Smarcel 1244105147Smarcel if (symidx >= ef->nchains) 1245105147Smarcel return (NULL); 1246105147Smarcel sym = ef->symtab + symidx; 1247105147Smarcel return (ef->strtab + sym->st_name); 1248105147Smarcel} 1249105147Smarcel 125095410Smarcel/* 125195410Smarcel * Symbol lookup function that can be used when the symbol index is known (ie 125295410Smarcel * in relocations). It uses the symbol index instead of doing a fully fledged 125395410Smarcel * hash table based lookup when such is valid. For example for local symbols. 125495410Smarcel * This is not only more efficient, it's also more correct. It's not always 125595410Smarcel * the case that the symbol can be found through the hash table. 125695410Smarcel */ 1257129282Speterstatic Elf_Addr 125895410Smarcelelf_lookup(linker_file_t lf, Elf_Word symidx, int deps) 125995410Smarcel{ 126095410Smarcel elf_file_t ef = (elf_file_t)lf; 126195410Smarcel const Elf_Sym *sym; 126295410Smarcel const char *symbol; 126395410Smarcel 126495410Smarcel /* Don't even try to lookup the symbol if the index is bogus. */ 126595410Smarcel if (symidx >= ef->nchains) 126695410Smarcel return (0); 126795410Smarcel 126895410Smarcel sym = ef->symtab + symidx; 126995410Smarcel 127095410Smarcel /* 127195410Smarcel * Don't do a full lookup when the symbol is local. It may even 127295410Smarcel * fail because it may not be found through the hash table. 127395410Smarcel */ 127495410Smarcel if (ELF_ST_BIND(sym->st_info) == STB_LOCAL) { 127595410Smarcel /* Force lookup failure when we have an insanity. */ 127695410Smarcel if (sym->st_shndx == SHN_UNDEF || sym->st_value == 0) 127795410Smarcel return (0); 127895410Smarcel return ((Elf_Addr)ef->address + sym->st_value); 127995410Smarcel } 128095410Smarcel 128195410Smarcel /* 128295410Smarcel * XXX we can avoid doing a hash table based lookup for global 128395410Smarcel * symbols as well. This however is not always valid, so we'll 128495410Smarcel * just do it the hard way for now. Performance tweaks can 128595410Smarcel * always be added. 128695410Smarcel */ 128795410Smarcel 128895410Smarcel symbol = ef->strtab + sym->st_name; 128995410Smarcel 129095410Smarcel /* Force a lookup failure if the symbol name is bogus. */ 129195410Smarcel if (*symbol == 0) 129295410Smarcel return (0); 129395410Smarcel 129495410Smarcel return ((Elf_Addr)linker_file_lookup_symbol(lf, symbol, deps)); 129595410Smarcel} 1296109605Sjake 1297109605Sjakestatic void 1298109605Sjakelink_elf_reloc_local(linker_file_t lf) 1299109605Sjake{ 1300109605Sjake const Elf_Rel *rellim; 1301109605Sjake const Elf_Rel *rel; 1302109605Sjake const Elf_Rela *relalim; 1303109605Sjake const Elf_Rela *rela; 1304109605Sjake elf_file_t ef = (elf_file_t)lf; 1305109605Sjake 1306109605Sjake /* Perform relocations without addend if there are any: */ 1307109605Sjake if ((rel = ef->rel) != NULL) { 1308109605Sjake rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize); 1309109605Sjake while (rel < rellim) { 1310129282Speter elf_reloc_local(lf, (Elf_Addr)ef->address, rel, ELF_RELOC_REL, 1311129282Speter elf_lookup); 1312109605Sjake rel++; 1313109605Sjake } 1314109605Sjake } 1315109605Sjake 1316109605Sjake /* Perform relocations with addend if there are any: */ 1317109605Sjake if ((rela = ef->rela) != NULL) { 1318109605Sjake relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize); 1319109605Sjake while (rela < relalim) { 1320129282Speter elf_reloc_local(lf, (Elf_Addr)ef->address, rela, ELF_RELOC_RELA, 1321129282Speter elf_lookup); 1322109605Sjake rela++; 1323109605Sjake } 1324109605Sjake } 1325109605Sjake} 1326