link_elf.c revision 116182
119370Spst/*- 2130809Smarcel * Copyright (c) 1998-2000 Doug Rabson 3130809Smarcel * All rights reserved. 4130809Smarcel * 5130809Smarcel * Redistribution and use in source and binary forms, with or without 619370Spst * modification, are permitted provided that the following conditions 719370Spst * are met: 899002Sobrien * 1. Redistributions of source code must retain the above copyright 919370Spst * notice, this list of conditions and the following disclaimer. 1099002Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1199002Sobrien * notice, this list of conditions and the following disclaimer in the 1299002Sobrien * documentation and/or other materials provided with the distribution. 1399002Sobrien * 1419370Spst * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1599002Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1699002Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1799002Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1899002Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1919370Spst * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2099002Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2199002Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2299002Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2399002Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2419370Spst * SUCH DAMAGE. 2519370Spst */ 2619370Spst 2719370Spst#include <sys/cdefs.h> 28130809Smarcel__FBSDID("$FreeBSD: head/sys/kern/link_elf.c 116182 2003-06-11 00:56:59Z obrien $"); 29130809Smarcel 30130809Smarcel#include "opt_ddb.h" 31130809Smarcel#include "opt_mac.h" 32130809Smarcel 3319370Spst#include <sys/param.h> 3419370Spst#include <sys/systm.h> 3519370Spst#include <sys/kernel.h> 3619370Spst#include <sys/lock.h> 3719370Spst#include <sys/mac.h> 38130809Smarcel#include <sys/malloc.h> 39130809Smarcel#include <sys/mutex.h> 4019370Spst#include <sys/proc.h> 4119370Spst#include <sys/namei.h> 4219370Spst#include <sys/fcntl.h> 4319370Spst#include <sys/vnode.h> 4419370Spst#include <sys/linker.h> 4519370Spst 4619370Spst#include <machine/elf.h> 4719370Spst#ifdef GPROF 4819370Spst#include <machine/profile.h> 4919370Spst#endif 5019370Spst 5119370Spst#include <vm/vm.h> 5219370Spst#include <vm/vm_param.h> 5346289Sdfr#ifdef SPARSE_MAPPING 5499002Sobrien#include <vm/vm_object.h> 5599002Sobrien#include <vm/vm_kern.h> 5619370Spst#include <vm/vm_extern.h> 5799002Sobrien#endif 5899002Sobrien#include <vm/pmap.h> 5999002Sobrien#include <vm/vm_map.h> 6099002Sobrien 6199002Sobrien#include <sys/link_elf.h> 6299002Sobrien 6399002Sobrien#include "linker_if.h" 6499002Sobrien 6599002Sobrientypedef struct elf_file { 6619370Spst struct linker_file lf; /* Common fields */ 6799002Sobrien int preloaded; /* Was file pre-loaded */ 6899002Sobrien caddr_t address; /* Relocation address */ 6999002Sobrien#ifdef SPARSE_MAPPING 7099002Sobrien vm_object_t object; /* VM object to hold file pages */ 7199002Sobrien#endif 7299002Sobrien Elf_Dyn* dynamic; /* Symbol table etc. */ 7346289Sdfr Elf_Hashelt nbuckets; /* DT_HASH info */ 7419370Spst Elf_Hashelt nchains; 7519370Spst const Elf_Hashelt* buckets; 7619370Spst const Elf_Hashelt* chains; 7799002Sobrien caddr_t hash; 7899002Sobrien caddr_t strtab; /* DT_STRTAB */ 7999002Sobrien int strsz; /* DT_STRSZ */ 8099002Sobrien const Elf_Sym* symtab; /* DT_SYMTAB */ 8119370Spst Elf_Addr* got; /* DT_PLTGOT */ 8299002Sobrien const Elf_Rel* pltrel; /* DT_JMPREL */ 8399002Sobrien int pltrelsize; /* DT_PLTRELSZ */ 8499002Sobrien const Elf_Rela* pltrela; /* DT_JMPREL */ 8519370Spst int pltrelasize; /* DT_PLTRELSZ */ 8699002Sobrien const Elf_Rel* rel; /* DT_REL */ 8799002Sobrien int relsize; /* DT_RELSZ */ 8899002Sobrien const Elf_Rela* rela; /* DT_RELA */ 8919370Spst int relasize; /* DT_RELASZ */ 9099002Sobrien caddr_t modptr; 9199002Sobrien const Elf_Sym* ddbsymtab; /* The symbol table we are using */ 9299002Sobrien long ddbsymcnt; /* Number of symbols */ 9319370Spst caddr_t ddbstrtab; /* String table */ 9499002Sobrien long ddbstrcnt; /* number of bytes in string table */ 9599002Sobrien caddr_t symbase; /* malloc'ed symbold base */ 9699002Sobrien caddr_t strbase; /* malloc'ed string base */ 9746289Sdfr#ifdef DDB 9899002Sobrien struct link_map gdb; /* hooks for gdb */ 9946289Sdfr#endif 10099002Sobrien} *elf_file_t; 10199002Sobrien 10246289Sdfrstatic int link_elf_link_common_finish(linker_file_t); 10399002Sobrienstatic int link_elf_link_preload(linker_class_t cls, 10446289Sdfr const char*, linker_file_t*); 10599002Sobrienstatic int link_elf_link_preload_finish(linker_file_t); 10699002Sobrienstatic int link_elf_load_file(linker_class_t, const char*, linker_file_t*); 10719370Spststatic int link_elf_lookup_symbol(linker_file_t, const char*, 10899002Sobrien c_linker_sym_t*); 10919370Spststatic int link_elf_symbol_values(linker_file_t, c_linker_sym_t, linker_symval_t*); 11099002Sobrienstatic int link_elf_search_symbol(linker_file_t, caddr_t value, 11199002Sobrien c_linker_sym_t* sym, long* diffp); 11299002Sobrien 11319370Spststatic void link_elf_unload_file(linker_file_t); 11499002Sobrienstatic void link_elf_unload_preload(linker_file_t); 11599002Sobrienstatic int link_elf_lookup_set(linker_file_t, const char *, 11619370Spst void ***, void ***, int *); 11799002Sobrienstatic int link_elf_each_function_name(linker_file_t, 11899002Sobrien int (*)(const char *, void *), 11999002Sobrien void *); 12099002Sobrienstatic void link_elf_reloc_local(linker_file_t); 12119370Spst 122130809Smarcelstatic kobj_method_t link_elf_methods[] = { 123130809Smarcel KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol), 12499002Sobrien KOBJMETHOD(linker_symbol_values, link_elf_symbol_values), 12599002Sobrien KOBJMETHOD(linker_search_symbol, link_elf_search_symbol), 12699002Sobrien KOBJMETHOD(linker_unload, link_elf_unload_file), 12799002Sobrien KOBJMETHOD(linker_load_file, link_elf_load_file), 12899002Sobrien KOBJMETHOD(linker_link_preload, link_elf_link_preload), 12999002Sobrien KOBJMETHOD(linker_link_preload_finish, link_elf_link_preload_finish), 13099002Sobrien KOBJMETHOD(linker_lookup_set, link_elf_lookup_set), 13199002Sobrien KOBJMETHOD(linker_each_function_name, link_elf_each_function_name), 13299002Sobrien { 0, 0 } 13319370Spst}; 13499002Sobrien 13599002Sobrienstatic struct linker_class link_elf_class = { 13699002Sobrien#if ELF_TARG_CLASS == ELFCLASS32 13719370Spst "elf32", 13899002Sobrien#else 13999002Sobrien "elf64", 14099002Sobrien#endif 14199002Sobrien link_elf_methods, sizeof(struct elf_file) 14299002Sobrien}; 14399002Sobrien 14499002Sobrienstatic int parse_dynamic(elf_file_t ef); 14599002Sobrienstatic int relocate_file(elf_file_t ef); 14699002Sobrienstatic int link_elf_preload_parse_symbols(elf_file_t ef); 14799002Sobrien 14899002Sobrien#ifdef DDB 14919370Spststatic void r_debug_state(struct r_debug *dummy_one, 15099002Sobrien struct link_map *dummy_two); 15199002Sobrien 15299002Sobrien/* 15399002Sobrien * A list of loaded modules for GDB to use for loading symbols. 15499002Sobrien */ 15599002Sobrienstruct r_debug r_debug; 15699002Sobrien 15799002Sobrien#define GDB_STATE(s) r_debug.r_state = s; r_debug_state(NULL, NULL); 158130809Smarcel 15999002Sobrien/* 16099002Sobrien * Function for the debugger to set a breakpoint on to gain control. 16199002Sobrien */ 16299002Sobrienstatic void 16399002Sobrienr_debug_state(struct r_debug *dummy_one __unused, 16499002Sobrien struct link_map *dummy_two __unused) 16599002Sobrien{ 16699002Sobrien} 16799002Sobrien 16899002Sobrienstatic void 16999002Sobrienlink_elf_add_gdb(struct link_map *l) 17099002Sobrien{ 17119370Spst struct link_map *prev; 17219370Spst 17399002Sobrien l->l_next = NULL; 17419370Spst 17519370Spst if (r_debug.r_map == NULL) { 17699002Sobrien /* Add first. */ 17719370Spst l->l_prev = NULL; 17819370Spst r_debug.r_map = l; 17999002Sobrien } else { 18099002Sobrien /* Append to list. */ 181130809Smarcel for (prev = r_debug.r_map; prev->l_next != NULL; prev = prev->l_next) 182130809Smarcel ; 183130809Smarcel l->l_prev = prev; 184130809Smarcel prev->l_next = l; 18546289Sdfr } 186130809Smarcel} 187130809Smarcel 188130809Smarcelstatic void 189130809Smarcellink_elf_delete_gdb(struct link_map *l) 190130809Smarcel{ 191130809Smarcel if (l->l_prev == NULL) { 192130809Smarcel /* Remove first. */ 193130809Smarcel if ((r_debug.r_map = l->l_next) != NULL) 194130809Smarcel l->l_next->l_prev = NULL; 195130809Smarcel } else { 196130809Smarcel /* Remove any but first. */ 197130809Smarcel if ((l->l_prev->l_next = l->l_next) != NULL) 198130809Smarcel l->l_next->l_prev = l->l_prev; 199130809Smarcel } 200130809Smarcel} 201130809Smarcel#endif /* DDB */ 202130809Smarcel 203130809Smarcel#ifdef __ia64__ 204130809SmarcelElf_Addr link_elf_get_gp(linker_file_t); 205130809Smarcel#endif 206130809Smarcel 207130809Smarcel/* 208130809Smarcel * The kernel symbol table starts here. 209130809Smarcel */ 210130809Smarcelextern struct _dynamic _DYNAMIC; 211130809Smarcel 212130809Smarcelstatic void 213130809Smarcellink_elf_error(const char *s) 214130809Smarcel{ 215130809Smarcel printf("kldload: %s\n", s); 216130809Smarcel} 217130809Smarcel 218130809Smarcel/* 219130809Smarcel * Actions performed after linking/loading both the preloaded kernel and any 220130809Smarcel * modules; whether preloaded or dynamicly loaded. 221130809Smarcel */ 222130809Smarcelstatic int 223130809Smarcellink_elf_link_common_finish(linker_file_t lf) 224130809Smarcel{ 225130809Smarcel#ifdef DDB 226130809Smarcel elf_file_t ef = (elf_file_t)lf; 227130809Smarcel char *newfilename; 228130809Smarcel#endif 229130809Smarcel int error; 230130809Smarcel 231130809Smarcel /* Notify MD code that a module is being loaded. */ 232131086Smarcel error = elf_cpu_load_file(lf); 233131086Smarcel if (error) 234131086Smarcel return (error); 235130809Smarcel 236130809Smarcel#ifdef DDB 237130809Smarcel GDB_STATE(RT_ADD); 238130809Smarcel ef->gdb.l_addr = lf->address; 239130809Smarcel newfilename = malloc(strlen(lf->filename) + 1, M_LINKER, M_WAITOK); 240130809Smarcel strcpy(newfilename, lf->filename); 241130809Smarcel ef->gdb.l_name = newfilename; 242130809Smarcel ef->gdb.l_ld = ef->dynamic; 243130809Smarcel link_elf_add_gdb(&ef->gdb); 244130809Smarcel GDB_STATE(RT_CONSISTENT); 245130809Smarcel#endif 246130809Smarcel 247130809Smarcel return (0); 248130809Smarcel} 249130809Smarcel 250130809Smarcelstatic void 251130809Smarcellink_elf_init(void* arg) 252130809Smarcel{ 253130809Smarcel Elf_Dyn *dp; 254130809Smarcel caddr_t modptr, baseptr, sizeptr; 255130809Smarcel elf_file_t ef; 256130809Smarcel char *modname; 257130809Smarcel 258130809Smarcel linker_add_class(&link_elf_class); 259130809Smarcel 260130809Smarcel dp = (Elf_Dyn*) &_DYNAMIC; 261130809Smarcel modname = NULL; 262130809Smarcel modptr = preload_search_by_type("elf" __XSTRING(__ELF_WORD_SIZE) " kernel"); 263130809Smarcel if (modptr == NULL) 264130809Smarcel modptr = preload_search_by_type("elf kernel"); 265130809Smarcel if (modptr) 266130809Smarcel modname = (char *)preload_search_info(modptr, MODINFO_NAME); 267130809Smarcel if (modname == NULL) 268130809Smarcel modname = "kernel"; 269130809Smarcel linker_kernel_file = linker_make_file(modname, &link_elf_class); 270130809Smarcel if (linker_kernel_file == NULL) 271130809Smarcel panic("link_elf_init: Can't create linker structures for kernel"); 272130809Smarcel 27319370Spst ef = (elf_file_t) linker_kernel_file; 27419370Spst ef->preloaded = 1; 27519370Spst ef->address = 0; 27699002Sobrien#ifdef SPARSE_MAPPING 27719370Spst ef->object = 0; 27819370Spst#endif 27999002Sobrien ef->dynamic = dp; 28019370Spst 28199002Sobrien if (dp) 28299002Sobrien parse_dynamic(ef); 28319370Spst linker_kernel_file->address = (caddr_t) KERNBASE; 28499002Sobrien linker_kernel_file->size = -(intptr_t)linker_kernel_file->address; 285130809Smarcel 28699002Sobrien if (modptr) { 28799002Sobrien ef->modptr = modptr; 28899002Sobrien baseptr = preload_search_info(modptr, MODINFO_ADDR); 28919370Spst if (baseptr) 29099002Sobrien linker_kernel_file->address = *(caddr_t *)baseptr; 291130809Smarcel sizeptr = preload_search_info(modptr, MODINFO_SIZE); 292130809Smarcel if (sizeptr) 293130809Smarcel linker_kernel_file->size = *(size_t *)sizeptr; 294130809Smarcel } 295130809Smarcel (void)link_elf_preload_parse_symbols(ef); 296130809Smarcel 29799002Sobrien#ifdef DDB 298130809Smarcel r_debug.r_map = NULL; 299130809Smarcel r_debug.r_brk = r_debug_state; 300130809Smarcel r_debug.r_state = RT_CONSISTENT; 301130809Smarcel#endif 30299002Sobrien 30399002Sobrien (void)link_elf_link_common_finish(linker_kernel_file); 30499002Sobrien} 30599002Sobrien 306130809SmarcelSYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0); 30799002Sobrien 30899002Sobrienstatic int 30999002Sobrienlink_elf_preload_parse_symbols(elf_file_t ef) 31099002Sobrien{ 31199002Sobrien caddr_t pointer; 31299002Sobrien caddr_t ssym, esym, base; 31319370Spst caddr_t strtab; 31499002Sobrien int strcnt; 31599002Sobrien Elf_Sym* symtab; 31699002Sobrien int symcnt; 31799002Sobrien 31819370Spst if (ef->modptr == NULL) 31999002Sobrien return 0; 32019370Spst pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_SSYM); 32199002Sobrien if (pointer == NULL) 32299002Sobrien return 0; 32319370Spst ssym = *(caddr_t *)pointer; 32499002Sobrien pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_ESYM); 32599002Sobrien if (pointer == NULL) 32699002Sobrien return 0; 32719370Spst esym = *(caddr_t *)pointer; 32899002Sobrien 32999002Sobrien base = ssym; 33099002Sobrien 33119370Spst symcnt = *(long *)base; 33299002Sobrien base += sizeof(long); 333130809Smarcel symtab = (Elf_Sym *)base; 33499002Sobrien base += roundup(symcnt, sizeof(long)); 33599002Sobrien 33619370Spst if (base > esym || base < ssym) { 33799002Sobrien printf("Symbols are corrupt!\n"); 33899002Sobrien return EINVAL; 33999002Sobrien } 340130809Smarcel 341130809Smarcel strcnt = *(long *)base; 342130809Smarcel base += sizeof(long); 343130809Smarcel strtab = base; 344130809Smarcel base += roundup(strcnt, sizeof(long)); 345130809Smarcel 346130809Smarcel if (base > esym || base < ssym) { 347130809Smarcel printf("Symbols are corrupt!\n"); 348130809Smarcel return EINVAL; 34999002Sobrien } 35099002Sobrien 35199002Sobrien ef->ddbsymtab = symtab; 35299002Sobrien ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); 353130809Smarcel ef->ddbstrtab = strtab; 35499002Sobrien ef->ddbstrcnt = strcnt; 35599002Sobrien 35699002Sobrien return 0; 35799002Sobrien} 35899002Sobrien 35999002Sobrienstatic int 36099002Sobrienparse_dynamic(elf_file_t ef) 36199002Sobrien{ 36299002Sobrien Elf_Dyn *dp; 36399002Sobrien int plttype = DT_REL; 36499002Sobrien 365130809Smarcel for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { 36699002Sobrien switch (dp->d_tag) { 36799002Sobrien case DT_HASH: 36899002Sobrien { 36999002Sobrien /* From src/libexec/rtld-elf/rtld.c */ 37099002Sobrien const Elf_Hashelt *hashtab = (const Elf_Hashelt *) 37199002Sobrien (ef->address + dp->d_un.d_ptr); 37299002Sobrien ef->nbuckets = hashtab[0]; 37399002Sobrien ef->nchains = hashtab[1]; 37499002Sobrien ef->buckets = hashtab + 2; 37599002Sobrien ef->chains = ef->buckets + ef->nbuckets; 37699002Sobrien break; 37799002Sobrien } 37899002Sobrien case DT_STRTAB: 37999002Sobrien ef->strtab = (caddr_t) (ef->address + dp->d_un.d_ptr); 38099002Sobrien break; 38199002Sobrien case DT_STRSZ: 38299002Sobrien ef->strsz = dp->d_un.d_val; 38399002Sobrien break; 38499002Sobrien case DT_SYMTAB: 38599002Sobrien ef->symtab = (Elf_Sym*) (ef->address + dp->d_un.d_ptr); 38699002Sobrien break; 38799002Sobrien case DT_SYMENT: 38899002Sobrien if (dp->d_un.d_val != sizeof(Elf_Sym)) 38999002Sobrien return ENOEXEC; 39099002Sobrien break; 39199002Sobrien case DT_PLTGOT: 39299002Sobrien ef->got = (Elf_Addr *) (ef->address + dp->d_un.d_ptr); 39399002Sobrien break; 39499002Sobrien case DT_REL: 39599002Sobrien ef->rel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); 39699002Sobrien break; 39799002Sobrien case DT_RELSZ: 39899002Sobrien ef->relsize = dp->d_un.d_val; 39999002Sobrien break; 40099002Sobrien case DT_RELENT: 401130809Smarcel if (dp->d_un.d_val != sizeof(Elf_Rel)) 402130809Smarcel return ENOEXEC; 403130809Smarcel break; 404130809Smarcel case DT_JMPREL: 40599002Sobrien ef->pltrel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); 40699002Sobrien break; 407130809Smarcel case DT_PLTRELSZ: 408130809Smarcel ef->pltrelsize = dp->d_un.d_val; 409130809Smarcel break; 410130809Smarcel case DT_RELA: 411130809Smarcel ef->rela = (const Elf_Rela *) (ef->address + dp->d_un.d_ptr); 412130809Smarcel break; 413130809Smarcel case DT_RELASZ: 414130809Smarcel ef->relasize = dp->d_un.d_val; 415130809Smarcel break; 416130809Smarcel case DT_RELAENT: 417130809Smarcel if (dp->d_un.d_val != sizeof(Elf_Rela)) 418130809Smarcel return ENOEXEC; 419130809Smarcel break; 420130809Smarcel case DT_PLTREL: 421130809Smarcel plttype = dp->d_un.d_val; 422130809Smarcel if (plttype != DT_REL && plttype != DT_RELA) 423130809Smarcel return ENOEXEC; 424130809Smarcel break; 42599002Sobrien#ifdef DDB 42699002Sobrien case DT_DEBUG: 42799002Sobrien dp->d_un.d_ptr = (Elf_Addr) &r_debug; 42899002Sobrien break; 42919370Spst#endif 43019370Spst } 43119370Spst } 43219370Spst 43319370Spst if (plttype == DT_RELA) { 43419370Spst ef->pltrela = (const Elf_Rela *) ef->pltrel; 43519370Spst ef->pltrel = NULL; 43619370Spst ef->pltrelasize = ef->pltrelsize; 43719370Spst ef->pltrelsize = 0; 43819370Spst } 43999002Sobrien 44019370Spst ef->ddbsymtab = ef->symtab; 44119370Spst ef->ddbsymcnt = ef->nchains; 44219370Spst ef->ddbstrtab = ef->strtab; 44319370Spst ef->ddbstrcnt = ef->strsz; 44419370Spst 44519370Spst return 0; 446130809Smarcel} 447130809Smarcel 448130809Smarcelstatic int 449130809Smarcellink_elf_link_preload(linker_class_t cls, 450130809Smarcel const char* filename, linker_file_t *result) 451130809Smarcel{ 452130809Smarcel caddr_t modptr, baseptr, sizeptr, dynptr; 453130809Smarcel char *type; 45419370Spst elf_file_t ef; 455130809Smarcel linker_file_t lf; 45699002Sobrien int error; 45719370Spst vm_offset_t dp; 45819370Spst 45919370Spst /* Look to see if we have the file preloaded */ 460130809Smarcel modptr = preload_search_by_name(filename); 46119370Spst if (modptr == NULL) 462130809Smarcel return ENOENT; 46319370Spst 46419370Spst type = (char *)preload_search_info(modptr, MODINFO_TYPE); 46519370Spst baseptr = preload_search_info(modptr, MODINFO_ADDR); 46699002Sobrien sizeptr = preload_search_info(modptr, MODINFO_SIZE); 46719370Spst dynptr = preload_search_info(modptr, MODINFO_METADATA|MODINFOMD_DYNAMIC); 46846289Sdfr if (type == NULL || 46946289Sdfr (strcmp(type, "elf" __XSTRING(__ELF_WORD_SIZE) " module") != 0 && 47046289Sdfr strcmp(type, "elf module") != 0)) 47146289Sdfr return (EFTYPE); 47299002Sobrien if (baseptr == NULL || sizeptr == NULL || dynptr == NULL) 47346289Sdfr return (EINVAL); 47499002Sobrien 47546289Sdfr lf = linker_make_file(filename, &link_elf_class); 47619370Spst if (lf == NULL) { 47719370Spst return ENOMEM; 47819370Spst } 47919370Spst 48019370Spst ef = (elf_file_t) lf; 48119370Spst ef->preloaded = 1; 48219370Spst ef->modptr = modptr; 48399002Sobrien ef->address = *(caddr_t *)baseptr; 48419370Spst#ifdef SPARSE_MAPPING 485130809Smarcel ef->object = 0; 486130809Smarcel#endif 48746289Sdfr dp = (vm_offset_t)ef->address + *(vm_offset_t *)dynptr; 488130809Smarcel ef->dynamic = (Elf_Dyn *)dp; 48946289Sdfr lf->address = ef->address; 49099002Sobrien lf->size = *(size_t *)sizeptr; 49119370Spst 49219370Spst error = parse_dynamic(ef); 49319370Spst if (error) { 49419370Spst linker_file_unload(lf); 49599002Sobrien return error; 49699002Sobrien } 49799002Sobrien link_elf_reloc_local(lf); 49899002Sobrien *result = lf; 49999002Sobrien return (0); 50019370Spst} 50199002Sobrien 50299002Sobrienstatic int 50319370Spstlink_elf_link_preload_finish(linker_file_t lf) 50499002Sobrien{ 50519370Spst elf_file_t ef; 50619370Spst int error; 50719370Spst 50819370Spst ef = (elf_file_t) lf; 50999002Sobrien#if 0 /* this will be more trouble than it's worth for now */ 51099002Sobrien for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { 51119370Spst if (dp->d_tag != DT_NEEDED) 51246289Sdfr continue; 51346289Sdfr modname = ef->strtab + dp->d_un.d_val; 51446289Sdfr error = linker_load_module(modname, lf); 51546289Sdfr if (error) 51646289Sdfr goto out; 51746289Sdfr } 51846289Sdfr#endif 51946289Sdfr error = relocate_file(ef); 52099002Sobrien if (error) 52146289Sdfr return error; 52299002Sobrien (void)link_elf_preload_parse_symbols(ef); 52399002Sobrien 52446289Sdfr return (link_elf_link_common_finish(lf)); 52599002Sobrien} 52619370Spst 52719370Spststatic int 52899002Sobrienlink_elf_load_file(linker_class_t cls, const char* filename, 52919370Spst linker_file_t* result) 53019370Spst{ 53119370Spst struct nameidata nd; 53219370Spst struct thread* td = curthread; /* XXX */ 53319370Spst Elf_Ehdr *hdr; 53419370Spst caddr_t firstpage; 53599002Sobrien int nbytes, i; 53619370Spst Elf_Phdr *phdr; 53719370Spst Elf_Phdr *phlimit; 53819370Spst Elf_Phdr *segs[2]; 53919370Spst int nsegs; 54019370Spst Elf_Phdr *phdyn; 54119370Spst Elf_Phdr *phphdr; 54219370Spst caddr_t mapbase; 54319370Spst size_t mapsize; 54499002Sobrien Elf_Off base_offset; 54519370Spst Elf_Addr base_vaddr; 54699002Sobrien Elf_Addr base_vlimit; 54719370Spst int error = 0; 54899002Sobrien int resid, flags; 54999002Sobrien elf_file_t ef; 55019370Spst linker_file_t lf; 55199002Sobrien Elf_Shdr *shdr; 55246289Sdfr int symtabindex; 55399002Sobrien int symstrindex; 55419370Spst int symcnt; 55599002Sobrien int strcnt; 55619370Spst 557130809Smarcel GIANT_REQUIRED; 55899002Sobrien 55919370Spst shdr = NULL; 560130809Smarcel lf = NULL; 56199002Sobrien 56219370Spst NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, td); 56399002Sobrien flags = FREAD; 56499002Sobrien error = vn_open(&nd, &flags, 0); 56599002Sobrien if (error) 56699002Sobrien return error; 56799002Sobrien NDFREE(&nd, NDF_ONLY_PNBUF); 56819370Spst#ifdef MAC 569130809Smarcel error = mac_check_kld_load(curthread->td_ucred, nd.ni_vp); 570130809Smarcel if (error) { 57119370Spst firstpage = NULL; 572130809Smarcel goto out; 573130809Smarcel } 57499002Sobrien#endif 57599002Sobrien 57699002Sobrien /* 57799002Sobrien * Read the elf header from the file. 57899002Sobrien */ 57946289Sdfr firstpage = malloc(PAGE_SIZE, M_LINKER, M_WAITOK); 58099002Sobrien if (firstpage == NULL) { 58146289Sdfr error = ENOMEM; 58246289Sdfr goto out; 58399002Sobrien } 58446289Sdfr hdr = (Elf_Ehdr *)firstpage; 58599002Sobrien error = vn_rdwr(UIO_READ, nd.ni_vp, firstpage, PAGE_SIZE, 0, 58646289Sdfr UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, 58799002Sobrien &resid, td); 58846289Sdfr nbytes = PAGE_SIZE - resid; 58999002Sobrien if (error) 59046289Sdfr goto out; 59199002Sobrien 59246289Sdfr if (!IS_ELF(*hdr)) { 59399002Sobrien error = ENOEXEC; 59446289Sdfr goto out; 59599002Sobrien } 59646289Sdfr 59799002Sobrien if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS 59846289Sdfr || hdr->e_ident[EI_DATA] != ELF_TARG_DATA) { 599130809Smarcel link_elf_error("Unsupported file layout"); 60046289Sdfr error = ENOEXEC; 60199002Sobrien goto out; 60246289Sdfr } 60399002Sobrien if (hdr->e_ident[EI_VERSION] != EV_CURRENT 60446289Sdfr || hdr->e_version != EV_CURRENT) { 60599002Sobrien link_elf_error("Unsupported file version"); 60646289Sdfr error = ENOEXEC; 60799002Sobrien goto out; 60846289Sdfr } 60999002Sobrien if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) { 61046289Sdfr link_elf_error("Unsupported file type"); 611130809Smarcel error = ENOEXEC; 612130809Smarcel goto out; 613130809Smarcel } 614130809Smarcel if (hdr->e_machine != ELF_TARG_MACH) { 615130809Smarcel link_elf_error("Unsupported machine"); 616130809Smarcel error = ENOEXEC; 617130809Smarcel goto out; 618130809Smarcel } 61919370Spst 62019370Spst /* 62199002Sobrien * We rely on the program header being in the first page. This is 62219370Spst * not strictly required by the ABI specification, but it seems to 62319370Spst * always true in practice. And, it simplifies things considerably. 62419370Spst */ 62519370Spst if (!((hdr->e_phentsize == sizeof(Elf_Phdr)) && 62699002Sobrien (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= PAGE_SIZE) && 62719370Spst (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= nbytes))) 628130809Smarcel link_elf_error("Unreadable program headers"); 629130809Smarcel 630130809Smarcel /* 631130809Smarcel * Scan the program header entries, and save key information. 632130809Smarcel * 63319370Spst * We rely on there being exactly two load segments, text and data, 63419370Spst * in that order. 63599002Sobrien */ 63619370Spst phdr = (Elf_Phdr *) (firstpage + hdr->e_phoff); 63719370Spst phlimit = phdr + hdr->e_phnum; 638130809Smarcel nsegs = 0; 639130809Smarcel phdyn = NULL; 64019370Spst phphdr = NULL; 64119370Spst while (phdr < phlimit) { 64219370Spst switch (phdr->p_type) { 64399002Sobrien 64419370Spst case PT_LOAD: 64519370Spst if (nsegs == 2) { 64619370Spst link_elf_error("Too many sections"); 64719370Spst error = ENOEXEC; 64819370Spst goto out; 64999002Sobrien } 65019370Spst /* 65119370Spst * XXX: We just trust they come in right order ?? 65219370Spst */ 65319370Spst segs[nsegs] = phdr; 65419370Spst ++nsegs; 65599002Sobrien break; 65619370Spst 65719370Spst case PT_PHDR: 65819370Spst phphdr = phdr; 65919370Spst break; 66019370Spst 66119370Spst case PT_DYNAMIC: 66219370Spst phdyn = phdr; 66319370Spst break; 66419370Spst 66519370Spst case PT_INTERP: 66699002Sobrien link_elf_error("Unsupported file type"); 66719370Spst error = ENOEXEC; 66819370Spst goto out; 66919370Spst } 67019370Spst 67119370Spst ++phdr; 67219370Spst } 67399002Sobrien if (phdyn == NULL) { 67419370Spst link_elf_error("Object is not dynamically-linked"); 675130809Smarcel error = ENOEXEC; 676130809Smarcel goto out; 677130809Smarcel } 678130809Smarcel if (nsegs != 2) { 679130809Smarcel link_elf_error("Too few sections"); 680130809Smarcel error = ENOEXEC; 681130809Smarcel goto out; 682130809Smarcel } 68319370Spst 68419370Spst /* 68519370Spst * Allocate the entire address space of the object, to stake out our 68619370Spst * contiguous region, and to establish the base address for relocation. 68799002Sobrien */ 68819370Spst base_offset = trunc_page(segs[0]->p_offset); 68919370Spst base_vaddr = trunc_page(segs[0]->p_vaddr); 69019370Spst base_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_memsz); 69119370Spst mapsize = base_vlimit - base_vaddr; 69299002Sobrien 69319370Spst lf = linker_make_file(filename, &link_elf_class); 69499002Sobrien if (!lf) { 69599002Sobrien error = ENOMEM; 69699002Sobrien goto out; 69719370Spst } 69899002Sobrien 69919370Spst ef = (elf_file_t) lf; 70019370Spst#ifdef SPARSE_MAPPING 70199002Sobrien ef->object = vm_object_allocate(OBJT_DEFAULT, mapsize >> PAGE_SHIFT); 70299002Sobrien if (ef->object == NULL) { 70399002Sobrien error = ENOMEM; 70499002Sobrien goto out; 70599002Sobrien } 70699002Sobrien vm_object_reference(ef->object); 70719370Spst ef->address = (caddr_t) vm_map_min(kernel_map); 70899002Sobrien error = vm_map_find(kernel_map, ef->object, 0, 70999002Sobrien (vm_offset_t *) &ef->address, 71019370Spst mapsize, 1, 71199002Sobrien VM_PROT_ALL, VM_PROT_ALL, 0); 71219370Spst if (error) { 71319370Spst vm_object_deallocate(ef->object); 71419370Spst ef->object = 0; 71519370Spst goto out; 71699002Sobrien } 71719370Spst#else 71899002Sobrien ef->address = malloc(mapsize, M_LINKER, M_WAITOK); 71919370Spst if (!ef->address) { 72046289Sdfr error = ENOMEM; 72146289Sdfr goto out; 72246289Sdfr } 72346289Sdfr#endif 72446289Sdfr mapbase = ef->address; 72546289Sdfr 72646289Sdfr /* 72746289Sdfr * Read the text and data sections and zero the bss. 72846289Sdfr */ 72999002Sobrien for (i = 0; i < 2; i++) { 73099002Sobrien caddr_t segbase = mapbase + segs[i]->p_vaddr - base_vaddr; 73199002Sobrien error = vn_rdwr(UIO_READ, nd.ni_vp, 73299002Sobrien segbase, segs[i]->p_filesz, segs[i]->p_offset, 73399002Sobrien UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, 73446289Sdfr &resid, td); 73599002Sobrien if (error) { 73699002Sobrien goto out; 73746289Sdfr } 73899002Sobrien bzero(segbase + segs[i]->p_filesz, 73946289Sdfr segs[i]->p_memsz - segs[i]->p_filesz); 74099002Sobrien 74199002Sobrien#ifdef SPARSE_MAPPING 74299002Sobrien /* 74399002Sobrien * Wire down the pages 74446289Sdfr */ 74599002Sobrien vm_map_wire(kernel_map, 74646289Sdfr (vm_offset_t) segbase, 74746289Sdfr (vm_offset_t) segbase + segs[i]->p_memsz, 74899002Sobrien FALSE); 74946289Sdfr#endif 75046289Sdfr } 75199002Sobrien 75246289Sdfr#ifdef GPROF 75346289Sdfr /* Update profiling information with the new text segment. */ 75499002Sobrien kmupetext((uintfptr_t)(mapbase + segs[0]->p_vaddr - base_vaddr + 75546289Sdfr segs[0]->p_memsz)); 756130809Smarcel#endif 757130809Smarcel 758130809Smarcel ef->dynamic = (Elf_Dyn *) (mapbase + phdyn->p_vaddr - base_vaddr); 759130809Smarcel 760130809Smarcel lf->address = ef->address; 761130809Smarcel lf->size = mapsize; 762130809Smarcel 76399002Sobrien error = parse_dynamic(ef); 764130809Smarcel if (error) 765130809Smarcel goto out; 76646289Sdfr link_elf_reloc_local(lf); 76746289Sdfr 76899002Sobrien error = linker_load_dependencies(lf); 76999002Sobrien if (error) 77099002Sobrien goto out; 77146289Sdfr#if 0 /* this will be more trouble than it's worth for now */ 77299002Sobrien for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { 77399002Sobrien if (dp->d_tag != DT_NEEDED) 77446289Sdfr continue; 77599002Sobrien modname = ef->strtab + dp->d_un.d_val; 77646289Sdfr error = linker_load_module(modname, lf); 77746289Sdfr if (error) 77846289Sdfr goto out; 77999002Sobrien } 78099002Sobrien#endif 78146289Sdfr error = relocate_file(ef); 78299002Sobrien if (error) 78346289Sdfr goto out; 78446289Sdfr 78599002Sobrien /* Try and load the symbol table if it's present. (you can strip it!) */ 78699002Sobrien nbytes = hdr->e_shnum * hdr->e_shentsize; 78746289Sdfr if (nbytes == 0 || hdr->e_shoff == 0) 78899002Sobrien goto nosyms; 78946289Sdfr shdr = malloc(nbytes, M_LINKER, M_WAITOK | M_ZERO); 79046289Sdfr if (shdr == NULL) { 791130809Smarcel error = ENOMEM; 79246289Sdfr goto out; 79399002Sobrien } 79446289Sdfr error = vn_rdwr(UIO_READ, nd.ni_vp, 79519370Spst (caddr_t)shdr, nbytes, hdr->e_shoff, 79619370Spst UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, 79719370Spst &resid, td); 79899002Sobrien if (error) 79919370Spst goto out; 80019370Spst symtabindex = -1; 80119370Spst symstrindex = -1; 80219370Spst for (i = 0; i < hdr->e_shnum; i++) { 80399002Sobrien if (shdr[i].sh_type == SHT_SYMTAB) { 80419370Spst symtabindex = i; 80519370Spst symstrindex = shdr[i].sh_link; 80619370Spst } 80799002Sobrien } 80899002Sobrien if (symtabindex < 0 || symstrindex < 0) 80919370Spst goto nosyms; 81019370Spst 81119370Spst symcnt = shdr[symtabindex].sh_size; 81299002Sobrien ef->symbase = malloc(symcnt, M_LINKER, M_WAITOK); 81399002Sobrien strcnt = shdr[symstrindex].sh_size; 81419370Spst ef->strbase = malloc(strcnt, M_LINKER, M_WAITOK); 81599002Sobrien 81619370Spst if (ef->symbase == NULL || ef->strbase == NULL) { 81799002Sobrien error = ENOMEM; 81899002Sobrien goto out; 81999002Sobrien } 82099002Sobrien error = vn_rdwr(UIO_READ, nd.ni_vp, 82199002Sobrien ef->symbase, symcnt, shdr[symtabindex].sh_offset, 82299002Sobrien UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, 82399002Sobrien &resid, td); 82446289Sdfr if (error) 82519370Spst goto out; 82699002Sobrien error = vn_rdwr(UIO_READ, nd.ni_vp, 82799002Sobrien ef->strbase, strcnt, shdr[symstrindex].sh_offset, 82899002Sobrien UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, 82999002Sobrien &resid, td); 83099002Sobrien if (error) 83199002Sobrien goto out; 83299002Sobrien 83399002Sobrien ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); 83446289Sdfr ef->ddbsymtab = (const Elf_Sym *)ef->symbase; 835130809Smarcel ef->ddbstrcnt = strcnt; 83646289Sdfr ef->ddbstrtab = ef->strbase; 83746289Sdfr 83846289Sdfr error = link_elf_link_common_finish(lf); 83999002Sobrien if (error) 84046289Sdfr goto out; 84146289Sdfr 84299002Sobriennosyms: 84346289Sdfr 84499002Sobrien *result = lf; 84599002Sobrien 84646289Sdfrout: 84799002Sobrien if (error && lf) 84846289Sdfr linker_file_unload(lf); 84919370Spst if (shdr) 85019370Spst free(shdr, M_LINKER); 85119370Spst if (firstpage) 85219370Spst free(firstpage, M_LINKER); 85319370Spst VOP_UNLOCK(nd.ni_vp, 0, td); 85499002Sobrien vn_close(nd.ni_vp, FREAD, td->td_ucred, td); 85519370Spst 85619370Spst return error; 85719370Spst} 85819370Spst 85999002Sobrienstatic void 86019370Spstlink_elf_unload_file(linker_file_t file) 86119370Spst{ 86219370Spst elf_file_t ef = (elf_file_t) file; 86399002Sobrien 86419370Spst#ifdef DDB 86599002Sobrien if (ef->gdb.l_ld) { 86619370Spst GDB_STATE(RT_DELETE); 86719370Spst free((void *)(uintptr_t)ef->gdb.l_name, M_LINKER); 86819370Spst link_elf_delete_gdb(&ef->gdb); 86919370Spst GDB_STATE(RT_CONSISTENT); 87099002Sobrien } 87119370Spst#endif 87219370Spst 87319370Spst /* Notify MD code that a module is being unloaded. */ 87419370Spst elf_cpu_unload_file(file); 87519370Spst 87619370Spst if (ef->preloaded) { 87719370Spst link_elf_unload_preload(file); 87819370Spst return; 87919370Spst } 88099002Sobrien 88119370Spst#ifdef SPARSE_MAPPING 88246289Sdfr if (ef->object) { 88346289Sdfr vm_map_remove(kernel_map, (vm_offset_t) ef->address, 88446289Sdfr (vm_offset_t) ef->address 88546289Sdfr + (ef->object->size << PAGE_SHIFT)); 88646289Sdfr vm_object_deallocate(ef->object); 88799002Sobrien } 88846289Sdfr#else 88946289Sdfr if (ef->address) 89099002Sobrien free(ef->address, M_LINKER); 89146289Sdfr#endif 89299002Sobrien if (ef->symbase) 89399002Sobrien free(ef->symbase, M_LINKER); 89419370Spst if (ef->strbase) 89599002Sobrien free(ef->strbase, M_LINKER); 89699002Sobrien} 89799002Sobrien 89899002Sobrienstatic void 89999002Sobrienlink_elf_unload_preload(linker_file_t file) 90099002Sobrien{ 90199002Sobrien if (file->filename) 902130809Smarcel preload_delete_name(file->filename); 903130809Smarcel} 904130809Smarcel 90599002Sobrienstatic const char * 90699002Sobriensymbol_name(elf_file_t ef, Elf_Word r_info) 90799002Sobrien{ 90899002Sobrien const Elf_Sym *ref; 90999002Sobrien 910130809Smarcel if (ELF_R_SYM(r_info)) { 91199002Sobrien ref = ef->symtab + ELF_R_SYM(r_info); 91299002Sobrien return ef->strtab + ref->st_name; 91399002Sobrien } else 91499002Sobrien return NULL; 91599002Sobrien} 91699002Sobrien 91799002Sobrienstatic int 91899002Sobrienrelocate_file(elf_file_t ef) 919130809Smarcel{ 92099002Sobrien const Elf_Rel *rellim; 92199002Sobrien const Elf_Rel *rel; 92299002Sobrien const Elf_Rela *relalim; 92319370Spst const Elf_Rela *rela; 92419370Spst const char *symname; 92519370Spst 92619370Spst /* Perform relocations without addend if there are any: */ 92799002Sobrien rel = ef->rel; 92899002Sobrien if (rel) { 92919370Spst rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize); 93046289Sdfr while (rel < rellim) { 93146289Sdfr if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL)) { 93299002Sobrien symname = symbol_name(ef, rel->r_info); 93399002Sobrien printf("link_elf: symbol %s undefined\n", symname); 93446289Sdfr return ENOENT; 93546289Sdfr } 93699002Sobrien rel++; 93799002Sobrien } 93899002Sobrien } 93946289Sdfr 94099002Sobrien /* Perform relocations with addend if there are any: */ 94199002Sobrien rela = ef->rela; 94299002Sobrien if (rela) { 94399002Sobrien relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize); 94499002Sobrien while (rela < relalim) { 94599002Sobrien if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA)) { 94699002Sobrien symname = symbol_name(ef, rela->r_info); 947130809Smarcel printf("link_elf: symbol %s undefined\n", symname); 94899002Sobrien return ENOENT; 94999002Sobrien } 95099002Sobrien rela++; 95199002Sobrien } 95299002Sobrien } 95399002Sobrien 95499002Sobrien /* Perform PLT relocations without addend if there are any: */ 95599002Sobrien rel = ef->pltrel; 95699002Sobrien if (rel) { 95799002Sobrien rellim = (const Elf_Rel *)((const char *)ef->pltrel + ef->pltrelsize); 95899002Sobrien while (rel < rellim) { 95999002Sobrien if (elf_reloc(&ef->lf, rel, ELF_RELOC_REL)) { 96099002Sobrien symname = symbol_name(ef, rel->r_info); 96199002Sobrien printf("link_elf: symbol %s undefined\n", symname); 96299002Sobrien return ENOENT; 96399002Sobrien } 96499002Sobrien rel++; 96599002Sobrien } 96699002Sobrien } 96799002Sobrien 96846289Sdfr /* Perform relocations with addend if there are any: */ 96946289Sdfr rela = ef->pltrela; 97099002Sobrien if (rela) { 97146289Sdfr relalim = (const Elf_Rela *)((const char *)ef->pltrela + ef->pltrelasize); 97246289Sdfr while (rela < relalim) { 97346289Sdfr if (elf_reloc(&ef->lf, rela, ELF_RELOC_RELA)) { 97446289Sdfr symname = symbol_name(ef, rela->r_info); 97546289Sdfr printf("link_elf: symbol %s undefined\n", symname); 97646289Sdfr return ENOENT; 97799002Sobrien } 97846289Sdfr rela++; 97946289Sdfr } 98046289Sdfr } 98146289Sdfr 98246289Sdfr return 0; 98399002Sobrien} 98446289Sdfr 98546289Sdfr/* 98699002Sobrien * Hash function for symbol table lookup. Don't even think about changing 98746289Sdfr * this. It is specified by the System V ABI. 98899002Sobrien */ 98999002Sobrienstatic unsigned long 99099002Sobrienelf_hash(const char *name) 99199002Sobrien{ 992130809Smarcel const unsigned char *p = (const unsigned char *) name; 99399002Sobrien unsigned long h = 0; 99419370Spst unsigned long g; 99599002Sobrien 99699002Sobrien while (*p != '\0') { 99799002Sobrien h = (h << 4) + *p++; 99899002Sobrien if ((g = h & 0xf0000000) != 0) 99999002Sobrien h ^= g >> 24; 100099002Sobrien h &= ~g; 100199002Sobrien } 100299002Sobrien return h; 100399002Sobrien} 100499002Sobrien 1005130809Smarcelstatic int 1006130809Smarcellink_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym) 1007130809Smarcel{ 1008130809Smarcel elf_file_t ef = (elf_file_t) lf; 1009130809Smarcel unsigned long symnum; 101099002Sobrien const Elf_Sym* symp; 101199002Sobrien const char *strp; 101219370Spst unsigned long hash; 101319370Spst int i; 101419370Spst 101519370Spst /* First, search hashed global symbols */ 101619370Spst hash = elf_hash(name); 101719370Spst symnum = ef->buckets[hash % ef->nbuckets]; 101819370Spst 101919370Spst while (symnum != STN_UNDEF) { 102019370Spst if (symnum >= ef->nchains) { 102119370Spst printf("link_elf_lookup_symbol: corrupt symbol table\n"); 102219370Spst return ENOENT; 102319370Spst } 1024130809Smarcel 1025130809Smarcel symp = ef->symtab + symnum; 102619370Spst if (symp->st_name == 0) { 102719370Spst printf("link_elf_lookup_symbol: corrupt symbol table\n"); 1028130809Smarcel return ENOENT; 1029130809Smarcel } 1030130809Smarcel 1031130809Smarcel strp = ef->strtab + symp->st_name; 1032130809Smarcel 1033130809Smarcel if (strcmp(name, strp) == 0) { 1034130809Smarcel if (symp->st_shndx != SHN_UNDEF || 103546289Sdfr (symp->st_value != 0 && 103646289Sdfr ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { 103799002Sobrien *sym = (c_linker_sym_t) symp; 103899002Sobrien return 0; 103946289Sdfr } else 104046289Sdfr return ENOENT; 104146289Sdfr } 104246289Sdfr 104346289Sdfr symnum = ef->chains[symnum]; 104446289Sdfr } 104546289Sdfr 104646289Sdfr /* If we have not found it, look at the full table (if loaded) */ 1047130809Smarcel if (ef->symtab == ef->ddbsymtab) 104819370Spst return ENOENT; 1049130809Smarcel 1050130809Smarcel /* Exhaustive search */ 105119370Spst for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { 105219370Spst strp = ef->ddbstrtab + symp->st_name; 105319370Spst if (strcmp(name, strp) == 0) { 105419370Spst if (symp->st_shndx != SHN_UNDEF || 105519370Spst (symp->st_value != 0 && 105619370Spst ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { 1057130809Smarcel *sym = (c_linker_sym_t) symp; 1058130809Smarcel return 0; 1059130809Smarcel } else 1060130809Smarcel return ENOENT; 106119370Spst } 106246289Sdfr } 106346289Sdfr 1064130809Smarcel return ENOENT; 106546289Sdfr} 106619370Spst 106746289Sdfrstatic int 106846289Sdfrlink_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, linker_symval_t* symval) 106946289Sdfr{ 107046289Sdfr elf_file_t ef = (elf_file_t) lf; 107146289Sdfr const Elf_Sym* es = (const Elf_Sym*) sym; 1072130809Smarcel 1073130809Smarcel if (es >= ef->symtab && es < (ef->symtab + ef->nchains)) { 1074130809Smarcel symval->name = ef->strtab + es->st_name; 107519370Spst symval->value = (caddr_t) ef->address + es->st_value; 1076130809Smarcel symval->size = es->st_size; 1077130809Smarcel return 0; 1078130809Smarcel } 107919370Spst if (ef->symtab == ef->ddbsymtab) 108019370Spst return ENOENT; 1081130809Smarcel if (es >= ef->ddbsymtab && es < (ef->ddbsymtab + ef->ddbsymcnt)) { 1082130809Smarcel symval->name = ef->ddbstrtab + es->st_name; 1083130809Smarcel symval->value = (caddr_t) ef->address + es->st_value; 1084130809Smarcel symval->size = es->st_size; 1085130809Smarcel return 0; 108619370Spst } 108719370Spst return ENOENT; 108819370Spst} 1089130809Smarcel 1090130809Smarcelstatic int 109119370Spstlink_elf_search_symbol(linker_file_t lf, caddr_t value, 109219370Spst c_linker_sym_t* sym, long* diffp) 109346289Sdfr{ 109446289Sdfr elf_file_t ef = (elf_file_t) lf; 109546289Sdfr u_long off = (uintptr_t) (void *) value; 109699002Sobrien u_long diff = off; 109799002Sobrien u_long st_value; 109846289Sdfr const Elf_Sym* es; 109946289Sdfr const Elf_Sym* best = 0; 110046289Sdfr int i; 110146289Sdfr 110246289Sdfr for (i = 0, es = ef->ddbsymtab; i < ef->ddbsymcnt; i++, es++) { 110346289Sdfr if (es->st_name == 0) 110446289Sdfr continue; 110546289Sdfr st_value = es->st_value + (uintptr_t) (void *) ef->address; 110646289Sdfr if (off >= st_value) { 110746289Sdfr if (off - st_value < diff) { 110899002Sobrien diff = off - st_value; 110946289Sdfr best = es; 111046289Sdfr if (diff == 0) 111146289Sdfr break; 111246289Sdfr } else if (off - st_value == diff) { 111346289Sdfr best = es; 111446289Sdfr } 111546289Sdfr } 111646289Sdfr } 111746289Sdfr if (best == 0) 111899002Sobrien *diffp = off; 111999002Sobrien else 112046289Sdfr *diffp = diff; 112146289Sdfr *sym = (c_linker_sym_t) best; 112246289Sdfr 112346289Sdfr return 0; 112419370Spst} 112519370Spst 112619370Spst/* 112719370Spst * Look up a linker set on an ELF system. 112819370Spst */ 112999002Sobrienstatic int 113099002Sobrienlink_elf_lookup_set(linker_file_t lf, const char *name, 113199002Sobrien void ***startp, void ***stopp, int *countp) 113219370Spst{ 113319370Spst c_linker_sym_t sym; 113499002Sobrien linker_symval_t symval; 113599002Sobrien char *setsym; 113619370Spst void **start, **stop; 113799002Sobrien int len, error = 0, count; 113819370Spst 113999002Sobrien len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */ 114019370Spst setsym = malloc(len, M_LINKER, M_WAITOK); 114199002Sobrien if (setsym == NULL) 114219370Spst return ENOMEM; 114399002Sobrien 114419370Spst /* get address of first entry */ 114599002Sobrien snprintf(setsym, len, "%s%s", "__start_set_", name); 114619370Spst error = link_elf_lookup_symbol(lf, setsym, &sym); 114799002Sobrien if (error) 114819370Spst goto out; 114919370Spst link_elf_symbol_values(lf, sym, &symval); 115019370Spst if (symval.value == 0) { 115119370Spst error = ESRCH; 115219370Spst goto out; 115399002Sobrien } 115499002Sobrien start = (void **)symval.value; 115599002Sobrien 115699002Sobrien /* get address of last entry */ 115719370Spst snprintf(setsym, len, "%s%s", "__stop_set_", name); 1158130809Smarcel error = link_elf_lookup_symbol(lf, setsym, &sym); 115919370Spst if (error) 116099002Sobrien goto out; 116199002Sobrien link_elf_symbol_values(lf, sym, &symval); 116219370Spst if (symval.value == 0) { 1163130809Smarcel error = ESRCH; 1164130809Smarcel goto out; 1165130809Smarcel } 116619370Spst stop = (void **)symval.value; 116719370Spst 116819370Spst /* and the number of entries */ 116919370Spst count = stop - start; 117099002Sobrien 117119370Spst /* and copy out */ 117299002Sobrien if (startp) 117319370Spst *startp = start; 117499002Sobrien if (stopp) 117599002Sobrien *stopp = stop; 117699002Sobrien if (countp) 117799002Sobrien *countp = count; 117846289Sdfr 117919370Spstout: 118019370Spst free(setsym, M_LINKER); 118199002Sobrien return error; 118246289Sdfr} 118399002Sobrien 118419370Spststatic int 118599002Sobrienlink_elf_each_function_name(linker_file_t file, 118619370Spst int (*callback)(const char *, void *), void *opaque) { 118799002Sobrien elf_file_t ef = (elf_file_t)file; 118819370Spst const Elf_Sym* symp; 118999002Sobrien int i, error; 119099002Sobrien 119199002Sobrien /* Exhaustive search */ 119299002Sobrien for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { 119399002Sobrien if (symp->st_value != 0 && 119499002Sobrien ELF_ST_TYPE(symp->st_info) == STT_FUNC) { 1195130809Smarcel error = callback(ef->ddbstrtab + symp->st_name, opaque); 1196130809Smarcel if (error) 119799002Sobrien return (error); 119899002Sobrien } 119999002Sobrien } 120019370Spst return (0); 120119370Spst} 120219370Spst 120319370Spst#ifdef __ia64__ 120419370Spst/* 120519370Spst * Each KLD has its own GP. The GP value for each load module is given by 120619370Spst * DT_PLTGOT on ia64. We need GP to construct function descriptors, but 120719370Spst * don't have direct access to the ELF file structure. The link_elf_get_gp() 120819370Spst * function returns the GP given a pointer to a generic linker file struct. 120946289Sdfr */ 121046289SdfrElf_Addr 121146289Sdfrlink_elf_get_gp(linker_file_t lf) 121219370Spst{ 121319370Spst elf_file_t ef = (elf_file_t)lf; 121419370Spst return (Elf_Addr)ef->got; 121519370Spst} 121699002Sobrien#endif 121719370Spst 121899002Sobrienconst Elf_Sym * 121999002Sobrienelf_get_sym(linker_file_t lf, Elf_Word symidx) 122099002Sobrien{ 122199002Sobrien elf_file_t ef = (elf_file_t)lf; 122299002Sobrien 122399002Sobrien if (symidx >= ef->nchains) 122499002Sobrien return (NULL); 122519370Spst return (ef->symtab + symidx); 122699002Sobrien} 122799002Sobrien 122899002Sobrienconst char * 122999002Sobrienelf_get_symname(linker_file_t lf, Elf_Word symidx) 123099002Sobrien{ 123199002Sobrien elf_file_t ef = (elf_file_t)lf; 123299002Sobrien const Elf_Sym *sym; 123399002Sobrien 123499002Sobrien if (symidx >= ef->nchains) 123599002Sobrien return (NULL); 123699002Sobrien sym = ef->symtab + symidx; 123799002Sobrien return (ef->strtab + sym->st_name); 123899002Sobrien} 123999002Sobrien 124019370Spst/* 124199002Sobrien * Symbol lookup function that can be used when the symbol index is known (ie 124219370Spst * in relocations). It uses the symbol index instead of doing a fully fledged 124346289Sdfr * hash table based lookup when such is valid. For example for local symbols. 124499002Sobrien * This is not only more efficient, it's also more correct. It's not always 124546289Sdfr * the case that the symbol can be found through the hash table. 124646289Sdfr */ 124746289SdfrElf_Addr 124846289Sdfrelf_lookup(linker_file_t lf, Elf_Word symidx, int deps) 124999002Sobrien{ 125046289Sdfr elf_file_t ef = (elf_file_t)lf; 125199002Sobrien const Elf_Sym *sym; 1252 const char *symbol; 1253 1254 /* Don't even try to lookup the symbol if the index is bogus. */ 1255 if (symidx >= ef->nchains) 1256 return (0); 1257 1258 sym = ef->symtab + symidx; 1259 1260 /* 1261 * Don't do a full lookup when the symbol is local. It may even 1262 * fail because it may not be found through the hash table. 1263 */ 1264 if (ELF_ST_BIND(sym->st_info) == STB_LOCAL) { 1265 /* Force lookup failure when we have an insanity. */ 1266 if (sym->st_shndx == SHN_UNDEF || sym->st_value == 0) 1267 return (0); 1268 return ((Elf_Addr)ef->address + sym->st_value); 1269 } 1270 1271 /* 1272 * XXX we can avoid doing a hash table based lookup for global 1273 * symbols as well. This however is not always valid, so we'll 1274 * just do it the hard way for now. Performance tweaks can 1275 * always be added. 1276 */ 1277 1278 symbol = ef->strtab + sym->st_name; 1279 1280 /* Force a lookup failure if the symbol name is bogus. */ 1281 if (*symbol == 0) 1282 return (0); 1283 1284 return ((Elf_Addr)linker_file_lookup_symbol(lf, symbol, deps)); 1285} 1286 1287static void 1288link_elf_reloc_local(linker_file_t lf) 1289{ 1290 const Elf_Rel *rellim; 1291 const Elf_Rel *rel; 1292 const Elf_Rela *relalim; 1293 const Elf_Rela *rela; 1294 elf_file_t ef = (elf_file_t)lf; 1295 1296 /* Perform relocations without addend if there are any: */ 1297 if ((rel = ef->rel) != NULL) { 1298 rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize); 1299 while (rel < rellim) { 1300 elf_reloc_local(lf, rel, ELF_RELOC_REL); 1301 rel++; 1302 } 1303 } 1304 1305 /* Perform relocations with addend if there are any: */ 1306 if ((rela = ef->rela) != NULL) { 1307 relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize); 1308 while (rela < relalim) { 1309 elf_reloc_local(lf, rela, ELF_RELOC_RELA); 1310 rela++; 1311 } 1312 } 1313} 1314