kern_linker.c revision 50272
125537Sdfr/*- 225537Sdfr * Copyright (c) 1997 Doug Rabson 325537Sdfr * All rights reserved. 425537Sdfr * 525537Sdfr * Redistribution and use in source and binary forms, with or without 625537Sdfr * modification, are permitted provided that the following conditions 725537Sdfr * are met: 825537Sdfr * 1. Redistributions of source code must retain the above copyright 925537Sdfr * notice, this list of conditions and the following disclaimer. 1025537Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1125537Sdfr * notice, this list of conditions and the following disclaimer in the 1225537Sdfr * documentation and/or other materials provided with the distribution. 1325537Sdfr * 1425537Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1525537Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1625537Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1725537Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1825537Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1925537Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2025537Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2125537Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2225537Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2325537Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2425537Sdfr * SUCH DAMAGE. 2525537Sdfr * 2650272Sbde * $Id: kern_linker.c,v 1.35 1999/08/20 00:18:07 grog Exp $ 2725537Sdfr */ 2825537Sdfr 2940159Speter#include "opt_ddb.h" 3040159Speter 3125537Sdfr#include <sys/param.h> 3225537Sdfr#include <sys/kernel.h> 3325537Sdfr#include <sys/systm.h> 3425537Sdfr#include <sys/malloc.h> 3525537Sdfr#include <sys/sysproto.h> 3625537Sdfr#include <sys/sysent.h> 3725537Sdfr#include <sys/proc.h> 3825537Sdfr#include <sys/lock.h> 3925537Sdfr#include <machine/cpu.h> 4040159Speter#include <machine/bootinfo.h> 4125537Sdfr#include <sys/module.h> 4225537Sdfr#include <sys/linker.h> 4331675Sdyson#include <sys/unistd.h> 4440159Speter#include <sys/fcntl.h> 4540159Speter#include <sys/libkern.h> 4640159Speter#include <sys/namei.h> 4740159Speter#include <sys/vnode.h> 4840159Speter#include <sys/sysctl.h> 4925537Sdfr 5040961Speter#ifdef KLD_DEBUG 5140961Speterint kld_debug = 0; 5240961Speter#endif 5340961Speter 5432153SbdeMALLOC_DEFINE(M_LINKER, "kld", "kernel linker"); 5531324Sbdelinker_file_t linker_current_file; 5640906Speterlinker_file_t linker_kernel_file; 5731324Sbde 5825537Sdfrstatic struct lock lock; /* lock for the file list */ 5925537Sdfrstatic linker_class_list_t classes; 6050068Sgrogstatic linker_file_list_t linker_files; 6125537Sdfrstatic int next_file_id = 1; 6225537Sdfr 6325537Sdfrstatic void 6425537Sdfrlinker_init(void* arg) 6525537Sdfr{ 6625537Sdfr lockinit(&lock, PVM, "klink", 0, 0); 6725537Sdfr TAILQ_INIT(&classes); 6850068Sgrog TAILQ_INIT(&linker_files); 6925537Sdfr} 7025537Sdfr 7140159SpeterSYSINIT(linker, SI_SUB_KLD, SI_ORDER_FIRST, linker_init, 0); 7225537Sdfr 7325537Sdfrint 7425537Sdfrlinker_add_class(const char* desc, void* priv, 7525537Sdfr struct linker_class_ops* ops) 7625537Sdfr{ 7725537Sdfr linker_class_t lc; 7825537Sdfr 7925537Sdfr lc = malloc(sizeof(struct linker_class), M_LINKER, M_NOWAIT); 8025537Sdfr if (!lc) 8125537Sdfr return ENOMEM; 8240395Speter bzero(lc, sizeof(*lc)); 8325537Sdfr 8425537Sdfr lc->desc = desc; 8525537Sdfr lc->priv = priv; 8625537Sdfr lc->ops = ops; 8725537Sdfr TAILQ_INSERT_HEAD(&classes, lc, link); 8825537Sdfr 8925537Sdfr return 0; 9025537Sdfr} 9125537Sdfr 9225537Sdfrstatic void 9325537Sdfrlinker_file_sysinit(linker_file_t lf) 9425537Sdfr{ 9525537Sdfr struct linker_set* sysinits; 9625537Sdfr struct sysinit** sipp; 9725537Sdfr struct sysinit** xipp; 9825537Sdfr struct sysinit* save; 9946693Speter const moduledata_t *moddata; 10046693Speter int error; 10125537Sdfr 10225537Sdfr KLD_DPF(FILE, ("linker_file_sysinit: calling SYSINITs for %s\n", 10325537Sdfr lf->filename)); 10425537Sdfr 10525537Sdfr sysinits = (struct linker_set*) 10625537Sdfr linker_file_lookup_symbol(lf, "sysinit_set", 0); 10740159Speter 10840159Speter KLD_DPF(FILE, ("linker_file_sysinit: SYSINITs %p\n", sysinits)); 10925537Sdfr if (!sysinits) 11025537Sdfr return; 11125537Sdfr 11240159Speter /* HACK ALERT! */ 11340159Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 11440159Speter if ((*sipp)->func == module_register_init) { 11540159Speter moddata = (*sipp)->udata; 11646693Speter error = module_register(moddata, lf); 11746693Speter if (error) 11846693Speter printf("linker_file_sysinit \"%s\" failed to register! %d\n", 11946693Speter lf->filename, error); 12040159Speter } 12140159Speter } 12240159Speter 12325537Sdfr /* 12425537Sdfr * Perform a bubble sort of the system initialization objects by 12525537Sdfr * their subsystem (primary key) and order (secondary key). 12625537Sdfr * 12725537Sdfr * Since some things care about execution order, this is the 12825537Sdfr * operation which ensures continued function. 12925537Sdfr */ 13041055Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 13141055Speter for (xipp = sipp + 1; *xipp; xipp++) { 13241055Speter if ((*sipp)->subsystem <= (*xipp)->subsystem || 13341055Speter ((*sipp)->subsystem == (*xipp)->subsystem && 13441055Speter (*sipp)->order <= (*xipp)->order)) 13525537Sdfr continue; /* skip*/ 13625537Sdfr save = *sipp; 13725537Sdfr *sipp = *xipp; 13825537Sdfr *xipp = save; 13925537Sdfr } 14025537Sdfr } 14125537Sdfr 14225537Sdfr 14325537Sdfr /* 14425537Sdfr * Traverse the (now) ordered list of system initialization tasks. 14525537Sdfr * Perform each task, and continue on to the next task. 14625537Sdfr */ 14741055Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 14841055Speter if ((*sipp)->subsystem == SI_SUB_DUMMY) 14925537Sdfr continue; /* skip dummy task(s)*/ 15025537Sdfr 15148391Speter /* Call function */ 15248391Speter (*((*sipp)->func))((*sipp)->udata); 15325537Sdfr } 15425537Sdfr} 15525537Sdfr 15641055Speterstatic void 15741055Speterlinker_file_sysuninit(linker_file_t lf) 15841055Speter{ 15941055Speter struct linker_set* sysuninits; 16041055Speter struct sysinit** sipp; 16141055Speter struct sysinit** xipp; 16241055Speter struct sysinit* save; 16341055Speter 16441055Speter KLD_DPF(FILE, ("linker_file_sysuninit: calling SYSUNINITs for %s\n", 16541055Speter lf->filename)); 16641055Speter 16741055Speter sysuninits = (struct linker_set*) 16841055Speter linker_file_lookup_symbol(lf, "sysuninit_set", 0); 16941055Speter 17041055Speter KLD_DPF(FILE, ("linker_file_sysuninit: SYSUNINITs %p\n", sysuninits)); 17141055Speter if (!sysuninits) 17241055Speter return; 17341055Speter 17441055Speter /* 17541055Speter * Perform a reverse bubble sort of the system initialization objects 17641055Speter * by their subsystem (primary key) and order (secondary key). 17741055Speter * 17841055Speter * Since some things care about execution order, this is the 17941055Speter * operation which ensures continued function. 18041055Speter */ 18141055Speter for (sipp = (struct sysinit **)sysuninits->ls_items; *sipp; sipp++) { 18241055Speter for (xipp = sipp + 1; *xipp; xipp++) { 18341055Speter if ((*sipp)->subsystem >= (*xipp)->subsystem || 18441055Speter ((*sipp)->subsystem == (*xipp)->subsystem && 18541055Speter (*sipp)->order >= (*xipp)->order)) 18641055Speter continue; /* skip*/ 18741055Speter save = *sipp; 18841055Speter *sipp = *xipp; 18941055Speter *xipp = save; 19041055Speter } 19141055Speter } 19241055Speter 19341055Speter 19441055Speter /* 19541055Speter * Traverse the (now) ordered list of system initialization tasks. 19641055Speter * Perform each task, and continue on to the next task. 19741055Speter */ 19841055Speter for (sipp = (struct sysinit **)sysuninits->ls_items; *sipp; sipp++) { 19941055Speter if ((*sipp)->subsystem == SI_SUB_DUMMY) 20041055Speter continue; /* skip dummy task(s)*/ 20141055Speter 20248391Speter /* Call function */ 20348391Speter (*((*sipp)->func))((*sipp)->udata); 20441055Speter } 20541055Speter} 20641055Speter 20744078Sdfrstatic void 20844078Sdfrlinker_file_register_sysctls(linker_file_t lf) 20944078Sdfr{ 21044078Sdfr struct linker_set* sysctls; 21144078Sdfr 21244078Sdfr KLD_DPF(FILE, ("linker_file_register_sysctls: registering SYSCTLs for %s\n", 21344078Sdfr lf->filename)); 21444078Sdfr 21544078Sdfr sysctls = (struct linker_set*) 21644078Sdfr linker_file_lookup_symbol(lf, "sysctl_set", 0); 21744078Sdfr 21844078Sdfr KLD_DPF(FILE, ("linker_file_register_sysctls: SYSCTLs %p\n", sysctls)); 21944078Sdfr if (!sysctls) 22044078Sdfr return; 22144078Sdfr 22244078Sdfr sysctl_register_set(sysctls); 22344078Sdfr} 22444078Sdfr 22544078Sdfrstatic void 22644078Sdfrlinker_file_unregister_sysctls(linker_file_t lf) 22744078Sdfr{ 22844078Sdfr struct linker_set* sysctls; 22944078Sdfr 23044078Sdfr KLD_DPF(FILE, ("linker_file_unregister_sysctls: registering SYSCTLs for %s\n", 23144078Sdfr lf->filename)); 23244078Sdfr 23344078Sdfr sysctls = (struct linker_set*) 23444078Sdfr linker_file_lookup_symbol(lf, "sysctl_set", 0); 23544078Sdfr 23644078Sdfr KLD_DPF(FILE, ("linker_file_unregister_sysctls: SYSCTLs %p\n", sysctls)); 23744078Sdfr if (!sysctls) 23844078Sdfr return; 23944078Sdfr 24044078Sdfr sysctl_unregister_set(sysctls); 24144078Sdfr} 24244078Sdfr 24325537Sdfrint 24425537Sdfrlinker_load_file(const char* filename, linker_file_t* result) 24525537Sdfr{ 24625537Sdfr linker_class_t lc; 24725537Sdfr linker_file_t lf; 24842755Speter int foundfile, error = 0; 24940861Speter char *koname = NULL; 25025537Sdfr 25125537Sdfr lf = linker_find_file_by_name(filename); 25225537Sdfr if (lf) { 25325537Sdfr KLD_DPF(FILE, ("linker_load_file: file %s is already loaded, incrementing refs\n", filename)); 25425537Sdfr *result = lf; 25525537Sdfr lf->refs++; 25625537Sdfr goto out; 25725537Sdfr } 25825537Sdfr 25940861Speter koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK); 26040861Speter if (koname == NULL) { 26140861Speter error = ENOMEM; 26240861Speter goto out; 26340861Speter } 26440861Speter sprintf(koname, "%s.ko", filename); 26525537Sdfr lf = NULL; 26642755Speter foundfile = 0; 26725537Sdfr for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) { 26825537Sdfr KLD_DPF(FILE, ("linker_load_file: trying to load %s as %s\n", 26925537Sdfr filename, lc->desc)); 27042755Speter 27142755Speter error = lc->ops->load_file(koname, &lf); /* First with .ko */ 27242755Speter if (lf == NULL && error == ENOENT) 27342755Speter error = lc->ops->load_file(filename, &lf); /* Then try without */ 27442755Speter /* 27542755Speter * If we got something other than ENOENT, then it exists but we cannot 27642755Speter * load it for some other reason. 27742755Speter */ 27842755Speter if (error != ENOENT) 27942755Speter foundfile = 1; 28025537Sdfr if (lf) { 28144549Sdfr linker_file_register_sysctls(lf); 28225537Sdfr linker_file_sysinit(lf); 28325537Sdfr 28425537Sdfr *result = lf; 28540861Speter error = 0; 28625537Sdfr goto out; 28725537Sdfr } 28825537Sdfr } 28942755Speter /* 29042755Speter * Less than ideal, but tells the user whether it failed to load or 29142755Speter * the module was not found. 29242755Speter */ 29342755Speter if (foundfile) 29442755Speter error = ENOEXEC; /* Format not recognised (or unloadable) */ 29542755Speter else 29642755Speter error = ENOENT; /* Nothing found */ 29725537Sdfr 29825537Sdfrout: 29940861Speter if (koname) 30040861Speter free(koname, M_LINKER); 30125537Sdfr return error; 30225537Sdfr} 30325537Sdfr 30425537Sdfrlinker_file_t 30525537Sdfrlinker_find_file_by_name(const char* filename) 30625537Sdfr{ 30725537Sdfr linker_file_t lf = 0; 30840861Speter char *koname; 30925537Sdfr 31040861Speter koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK); 31140861Speter if (koname == NULL) 31240861Speter goto out; 31340861Speter sprintf(koname, "%s.ko", filename); 31440861Speter 31525537Sdfr lockmgr(&lock, LK_SHARED, 0, curproc); 31650068Sgrog for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { 31740861Speter if (!strcmp(lf->filename, koname)) 31840861Speter break; 31925537Sdfr if (!strcmp(lf->filename, filename)) 32025537Sdfr break; 32140861Speter } 32225537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 32325537Sdfr 32440861Speterout: 32540861Speter if (koname) 32640861Speter free(koname, M_LINKER); 32725537Sdfr return lf; 32825537Sdfr} 32925537Sdfr 33025537Sdfrlinker_file_t 33125537Sdfrlinker_find_file_by_id(int fileid) 33225537Sdfr{ 33325537Sdfr linker_file_t lf = 0; 33425537Sdfr 33525537Sdfr lockmgr(&lock, LK_SHARED, 0, curproc); 33650068Sgrog for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) 33725537Sdfr if (lf->id == fileid) 33825537Sdfr break; 33925537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 34025537Sdfr 34125537Sdfr return lf; 34225537Sdfr} 34325537Sdfr 34425537Sdfrlinker_file_t 34540159Speterlinker_make_file(const char* pathname, void* priv, struct linker_file_ops* ops) 34625537Sdfr{ 34725537Sdfr linker_file_t lf = 0; 34825537Sdfr int namelen; 34940159Speter const char *filename; 35025537Sdfr 35140159Speter filename = rindex(pathname, '/'); 35240159Speter if (filename && filename[1]) 35340159Speter filename++; 35440159Speter else 35540159Speter filename = pathname; 35640159Speter 35725537Sdfr KLD_DPF(FILE, ("linker_make_file: new file, filename=%s\n", filename)); 35845356Speter lockmgr(&lock, LK_EXCLUSIVE, 0, curproc); 35925537Sdfr namelen = strlen(filename) + 1; 36025537Sdfr lf = malloc(sizeof(struct linker_file) + namelen, M_LINKER, M_WAITOK); 36125537Sdfr if (!lf) 36225537Sdfr goto out; 36340395Speter bzero(lf, sizeof(*lf)); 36425537Sdfr 36525537Sdfr lf->refs = 1; 36625537Sdfr lf->userrefs = 0; 36743185Sdfr lf->flags = 0; 36825537Sdfr lf->filename = (char*) (lf + 1); 36925537Sdfr strcpy(lf->filename, filename); 37025537Sdfr lf->id = next_file_id++; 37125537Sdfr lf->ndeps = 0; 37225537Sdfr lf->deps = NULL; 37325537Sdfr STAILQ_INIT(&lf->common); 37425537Sdfr TAILQ_INIT(&lf->modules); 37525537Sdfr 37625537Sdfr lf->priv = priv; 37725537Sdfr lf->ops = ops; 37850068Sgrog TAILQ_INSERT_TAIL(&linker_files, lf, link); 37925537Sdfr 38025537Sdfrout: 38125537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 38225537Sdfr return lf; 38325537Sdfr} 38425537Sdfr 38525537Sdfrint 38625537Sdfrlinker_file_unload(linker_file_t file) 38725537Sdfr{ 38825537Sdfr module_t mod, next; 38925537Sdfr struct common_symbol* cp; 39025537Sdfr int error = 0; 39125537Sdfr int i; 39225537Sdfr 39340159Speter KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", file->refs)); 39445356Speter lockmgr(&lock, LK_EXCLUSIVE, 0, curproc); 39525537Sdfr if (file->refs == 1) { 39625537Sdfr KLD_DPF(FILE, ("linker_file_unload: file is unloading, informing modules\n")); 39725537Sdfr /* 39825537Sdfr * Inform any modules associated with this file. 39925537Sdfr */ 40025537Sdfr for (mod = TAILQ_FIRST(&file->modules); mod; mod = next) { 40125537Sdfr next = module_getfnext(mod); 40225537Sdfr 40325537Sdfr /* 40425537Sdfr * Give the module a chance to veto the unload. 40525537Sdfr */ 40643301Sdillon if ((error = module_unload(mod)) != 0) { 40725537Sdfr KLD_DPF(FILE, ("linker_file_unload: module %x vetoes unload\n", 40825537Sdfr mod)); 40925537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 41025537Sdfr goto out; 41125537Sdfr } 41225537Sdfr 41325537Sdfr module_release(mod); 41425537Sdfr } 41525537Sdfr } 41625537Sdfr 41725537Sdfr file->refs--; 41825537Sdfr if (file->refs > 0) { 41925537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 42025537Sdfr goto out; 42125537Sdfr } 42225537Sdfr 42343185Sdfr /* Don't try to run SYSUNINITs if we are unloaded due to a link error */ 42444078Sdfr if (file->flags & LINKER_FILE_LINKED) { 42543185Sdfr linker_file_sysuninit(file); 42644078Sdfr linker_file_unregister_sysctls(file); 42744078Sdfr } 42841055Speter 42950068Sgrog TAILQ_REMOVE(&linker_files, file, link); 43025537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 43140159Speter 43225537Sdfr for (i = 0; i < file->ndeps; i++) 43325537Sdfr linker_file_unload(file->deps[i]); 43425537Sdfr free(file->deps, M_LINKER); 43525537Sdfr 43625537Sdfr for (cp = STAILQ_FIRST(&file->common); cp; 43725537Sdfr cp = STAILQ_FIRST(&file->common)) { 43825537Sdfr STAILQ_REMOVE(&file->common, cp, common_symbol, link); 43925537Sdfr free(cp, M_LINKER); 44025537Sdfr } 44125537Sdfr 44225537Sdfr file->ops->unload(file); 44325537Sdfr free(file, M_LINKER); 44425537Sdfr 44525537Sdfrout: 44625537Sdfr return error; 44725537Sdfr} 44825537Sdfr 44925537Sdfrint 45025537Sdfrlinker_file_add_dependancy(linker_file_t file, linker_file_t dep) 45125537Sdfr{ 45225537Sdfr linker_file_t* newdeps; 45325537Sdfr 45425537Sdfr newdeps = malloc((file->ndeps + 1) * sizeof(linker_file_t*), 45525537Sdfr M_LINKER, M_WAITOK); 45625537Sdfr if (newdeps == NULL) 45725537Sdfr return ENOMEM; 45840395Speter bzero(newdeps, (file->ndeps + 1) * sizeof(linker_file_t*)); 45925537Sdfr 46025537Sdfr if (file->deps) { 46125537Sdfr bcopy(file->deps, newdeps, file->ndeps * sizeof(linker_file_t*)); 46225537Sdfr free(file->deps, M_LINKER); 46325537Sdfr } 46425537Sdfr file->deps = newdeps; 46525537Sdfr file->deps[file->ndeps] = dep; 46625537Sdfr file->ndeps++; 46725537Sdfr 46825537Sdfr return 0; 46925537Sdfr} 47025537Sdfr 47125537Sdfrcaddr_t 47225537Sdfrlinker_file_lookup_symbol(linker_file_t file, const char* name, int deps) 47325537Sdfr{ 47443309Sdillon c_linker_sym_t sym; 47538275Sdfr linker_symval_t symval; 47642849Speter linker_file_t lf; 47725537Sdfr caddr_t address; 47825537Sdfr size_t common_size = 0; 47925537Sdfr int i; 48025537Sdfr 48140159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d\n", 48225537Sdfr file, name, deps)); 48325537Sdfr 48438275Sdfr if (file->ops->lookup_symbol(file, name, &sym) == 0) { 48538275Sdfr file->ops->symbol_values(file, sym, &symval); 48638275Sdfr if (symval.value == 0) 48725537Sdfr /* 48825537Sdfr * For commons, first look them up in the dependancies and 48925537Sdfr * only allocate space if not found there. 49025537Sdfr */ 49138275Sdfr common_size = symval.size; 49240159Speter else { 49340159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: symbol.value=%x\n", symval.value)); 49438275Sdfr return symval.value; 49540159Speter } 49638275Sdfr } 49725537Sdfr 49842849Speter if (deps) { 49925537Sdfr for (i = 0; i < file->ndeps; i++) { 50025537Sdfr address = linker_file_lookup_symbol(file->deps[i], name, 0); 50140159Speter if (address) { 50240159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: deps value=%x\n", address)); 50325537Sdfr return address; 50440159Speter } 50525537Sdfr } 50625537Sdfr 50742849Speter /* If we have not found it in the dependencies, search globally */ 50850068Sgrog for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { 50942849Speter /* But skip the current file if it's on the list */ 51042849Speter if (lf == file) 51142849Speter continue; 51242849Speter /* And skip the files we searched above */ 51342849Speter for (i = 0; i < file->ndeps; i++) 51442849Speter if (lf == file->deps[i]) 51542849Speter break; 51642849Speter if (i < file->ndeps) 51742849Speter continue; 51842849Speter address = linker_file_lookup_symbol(lf, name, 0); 51942849Speter if (address) { 52042849Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: global value=%x\n", address)); 52142849Speter return address; 52242849Speter } 52342849Speter } 52442849Speter } 52542849Speter 52625537Sdfr if (common_size > 0) { 52725537Sdfr /* 52825537Sdfr * This is a common symbol which was not found in the 52925537Sdfr * dependancies. We maintain a simple common symbol table in 53025537Sdfr * the file object. 53125537Sdfr */ 53225537Sdfr struct common_symbol* cp; 53325537Sdfr 53425537Sdfr for (cp = STAILQ_FIRST(&file->common); cp; 53525537Sdfr cp = STAILQ_NEXT(cp, link)) 53640159Speter if (!strcmp(cp->name, name)) { 53740159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: old common value=%x\n", cp->address)); 53825537Sdfr return cp->address; 53940159Speter } 54025537Sdfr 54125537Sdfr /* 54225537Sdfr * Round the symbol size up to align. 54325537Sdfr */ 54425537Sdfr common_size = (common_size + sizeof(int) - 1) & -sizeof(int); 54525537Sdfr cp = malloc(sizeof(struct common_symbol) 54625537Sdfr + common_size 54725537Sdfr + strlen(name) + 1, 54825537Sdfr M_LINKER, M_WAITOK); 54940159Speter if (!cp) { 55040159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: nomem\n")); 55125537Sdfr return 0; 55240159Speter } 55340395Speter bzero(cp, sizeof(struct common_symbol) + common_size + strlen(name)+ 1); 55425537Sdfr 55525537Sdfr cp->address = (caddr_t) (cp + 1); 55625537Sdfr cp->name = cp->address + common_size; 55725537Sdfr strcpy(cp->name, name); 55825537Sdfr bzero(cp->address, common_size); 55925537Sdfr STAILQ_INSERT_TAIL(&file->common, cp, link); 56025537Sdfr 56140159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: new common value=%x\n", cp->address)); 56225537Sdfr return cp->address; 56325537Sdfr } 56425537Sdfr 56540159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: fail\n")); 56625537Sdfr return 0; 56725537Sdfr} 56825537Sdfr 56940159Speter#ifdef DDB 57025537Sdfr/* 57140159Speter * DDB Helpers. DDB has to look across multiple files with their own 57240159Speter * symbol tables and string tables. 57340159Speter * 57440159Speter * Note that we do not obey list locking protocols here. We really don't 57540159Speter * need DDB to hang because somebody's got the lock held. We'll take the 57640159Speter * chance that the files list is inconsistant instead. 57740159Speter */ 57840159Speter 57940159Speterint 58043309Sdillonlinker_ddb_lookup(const char *symstr, c_linker_sym_t *sym) 58140159Speter{ 58240159Speter linker_file_t lf; 58340159Speter 58450068Sgrog for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { 58540159Speter if (lf->ops->lookup_symbol(lf, symstr, sym) == 0) 58640159Speter return 0; 58740159Speter } 58840159Speter return ENOENT; 58940159Speter} 59040159Speter 59140159Speterint 59243309Sdillonlinker_ddb_search_symbol(caddr_t value, c_linker_sym_t *sym, long *diffp) 59340159Speter{ 59440159Speter linker_file_t lf; 59550272Sbde u_long off = (uintptr_t)value; 59640159Speter u_long diff, bestdiff; 59743309Sdillon c_linker_sym_t best; 59843309Sdillon c_linker_sym_t es; 59940159Speter 60040159Speter best = 0; 60140159Speter bestdiff = off; 60250068Sgrog for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { 60340159Speter if (lf->ops->search_symbol(lf, value, &es, &diff) != 0) 60440159Speter continue; 60540159Speter if (es != 0 && diff < bestdiff) { 60640159Speter best = es; 60740159Speter bestdiff = diff; 60840159Speter } 60940159Speter if (bestdiff == 0) 61040159Speter break; 61140159Speter } 61240159Speter if (best) { 61340159Speter *sym = best; 61440159Speter *diffp = bestdiff; 61540159Speter return 0; 61640159Speter } else { 61740159Speter *sym = 0; 61840159Speter *diffp = off; 61940159Speter return ENOENT; 62040159Speter } 62140159Speter} 62240159Speter 62340159Speterint 62443309Sdillonlinker_ddb_symbol_values(c_linker_sym_t sym, linker_symval_t *symval) 62540159Speter{ 62640159Speter linker_file_t lf; 62740159Speter 62850068Sgrog for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { 62940159Speter if (lf->ops->symbol_values(lf, sym, symval) == 0) 63040159Speter return 0; 63140159Speter } 63240159Speter return ENOENT; 63340159Speter} 63440159Speter 63540159Speter#endif 63640159Speter 63740159Speter/* 63825537Sdfr * Syscalls. 63925537Sdfr */ 64025537Sdfr 64125537Sdfrint 64230994Sphkkldload(struct proc* p, struct kldload_args* uap) 64325537Sdfr{ 64442316Smsmith char* filename = NULL, *modulename; 64525537Sdfr linker_file_t lf; 64625537Sdfr int error = 0; 64725537Sdfr 64830994Sphk p->p_retval[0] = -1; 64925537Sdfr 65025537Sdfr if (securelevel > 0) 65125537Sdfr return EPERM; 65225537Sdfr 65346112Sphk if ((error = suser(p)) != 0) 65425537Sdfr return error; 65525537Sdfr 65625537Sdfr filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 65743301Sdillon if ((error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) != 0) 65825537Sdfr goto out; 65925537Sdfr 66042316Smsmith /* Can't load more than one module with the same name */ 66142316Smsmith modulename = rindex(filename, '/'); 66242316Smsmith if (modulename == NULL) 66342316Smsmith modulename = filename; 66444173Sdfr else 66544173Sdfr modulename++; 66642316Smsmith if (linker_find_file_by_name(modulename)) { 66742316Smsmith error = EEXIST; 66842316Smsmith goto out; 66942316Smsmith } 67042316Smsmith 67143301Sdillon if ((error = linker_load_file(filename, &lf)) != 0) 67225537Sdfr goto out; 67325537Sdfr 67425537Sdfr lf->userrefs++; 67530994Sphk p->p_retval[0] = lf->id; 67640159Speter 67725537Sdfrout: 67825537Sdfr if (filename) 67925537Sdfr free(filename, M_TEMP); 68025537Sdfr return error; 68125537Sdfr} 68225537Sdfr 68325537Sdfrint 68430994Sphkkldunload(struct proc* p, struct kldunload_args* uap) 68525537Sdfr{ 68625537Sdfr linker_file_t lf; 68725537Sdfr int error = 0; 68825537Sdfr 68925537Sdfr if (securelevel > 0) 69025537Sdfr return EPERM; 69125537Sdfr 69246112Sphk if ((error = suser(p)) != 0) 69325537Sdfr return error; 69425537Sdfr 69525537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 69625537Sdfr if (lf) { 69725537Sdfr KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs)); 69825537Sdfr if (lf->userrefs == 0) { 69943084Speter printf("linkerunload: attempt to unload file that was loaded by the kernel\n"); 70025537Sdfr error = EBUSY; 70125537Sdfr goto out; 70225537Sdfr } 70343084Speter lf->userrefs--; 70442837Speter error = linker_file_unload(lf); 70542837Speter if (error) 70643084Speter lf->userrefs++; 70725537Sdfr } else 70825537Sdfr error = ENOENT; 70925537Sdfr 71025537Sdfrout: 71125537Sdfr return error; 71225537Sdfr} 71325537Sdfr 71425537Sdfrint 71530994Sphkkldfind(struct proc* p, struct kldfind_args* uap) 71625537Sdfr{ 71742316Smsmith char* filename = NULL, *modulename; 71825537Sdfr linker_file_t lf; 71925537Sdfr int error = 0; 72025537Sdfr 72130994Sphk p->p_retval[0] = -1; 72225537Sdfr 72325537Sdfr filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 72443301Sdillon if ((error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) != 0) 72525537Sdfr goto out; 72625537Sdfr 72742316Smsmith modulename = rindex(filename, '/'); 72842316Smsmith if (modulename == NULL) 72942316Smsmith modulename = filename; 73042316Smsmith 73142316Smsmith lf = linker_find_file_by_name(modulename); 73225537Sdfr if (lf) 73330994Sphk p->p_retval[0] = lf->id; 73425537Sdfr else 73525537Sdfr error = ENOENT; 73640159Speter 73725537Sdfrout: 73825537Sdfr if (filename) 73925537Sdfr free(filename, M_TEMP); 74025537Sdfr return error; 74125537Sdfr} 74225537Sdfr 74325537Sdfrint 74430994Sphkkldnext(struct proc* p, struct kldnext_args* uap) 74525537Sdfr{ 74625537Sdfr linker_file_t lf; 74725537Sdfr int error = 0; 74825537Sdfr 74925537Sdfr if (SCARG(uap, fileid) == 0) { 75050068Sgrog if (TAILQ_FIRST(&linker_files)) 75150068Sgrog p->p_retval[0] = TAILQ_FIRST(&linker_files)->id; 75225537Sdfr else 75330994Sphk p->p_retval[0] = 0; 75425537Sdfr return 0; 75525537Sdfr } 75640159Speter 75725537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 75825537Sdfr if (lf) { 75925537Sdfr if (TAILQ_NEXT(lf, link)) 76030994Sphk p->p_retval[0] = TAILQ_NEXT(lf, link)->id; 76125537Sdfr else 76230994Sphk p->p_retval[0] = 0; 76325537Sdfr } else 76425537Sdfr error = ENOENT; 76525537Sdfr 76625537Sdfr return error; 76725537Sdfr} 76825537Sdfr 76925537Sdfrint 77030994Sphkkldstat(struct proc* p, struct kldstat_args* uap) 77125537Sdfr{ 77225537Sdfr linker_file_t lf; 77325537Sdfr int error = 0; 77425537Sdfr int version; 77525537Sdfr struct kld_file_stat* stat; 77625537Sdfr int namelen; 77725537Sdfr 77825537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 77925537Sdfr if (!lf) { 78025537Sdfr error = ENOENT; 78125537Sdfr goto out; 78225537Sdfr } 78325537Sdfr 78425537Sdfr stat = SCARG(uap, stat); 78525537Sdfr 78625537Sdfr /* 78725537Sdfr * Check the version of the user's structure. 78825537Sdfr */ 78943301Sdillon if ((error = copyin(&stat->version, &version, sizeof(version))) != 0) 79025537Sdfr goto out; 79125537Sdfr if (version != sizeof(struct kld_file_stat)) { 79225537Sdfr error = EINVAL; 79325537Sdfr goto out; 79425537Sdfr } 79525537Sdfr 79625537Sdfr namelen = strlen(lf->filename) + 1; 79725537Sdfr if (namelen > MAXPATHLEN) 79825537Sdfr namelen = MAXPATHLEN; 79943301Sdillon if ((error = copyout(lf->filename, &stat->name[0], namelen)) != 0) 80025537Sdfr goto out; 80143301Sdillon if ((error = copyout(&lf->refs, &stat->refs, sizeof(int))) != 0) 80225537Sdfr goto out; 80343301Sdillon if ((error = copyout(&lf->id, &stat->id, sizeof(int))) != 0) 80425537Sdfr goto out; 80543301Sdillon if ((error = copyout(&lf->address, &stat->address, sizeof(caddr_t))) != 0) 80625537Sdfr goto out; 80743301Sdillon if ((error = copyout(&lf->size, &stat->size, sizeof(size_t))) != 0) 80825537Sdfr goto out; 80925537Sdfr 81030994Sphk p->p_retval[0] = 0; 81125537Sdfr 81225537Sdfrout: 81325537Sdfr return error; 81425537Sdfr} 81525537Sdfr 81625537Sdfrint 81730994Sphkkldfirstmod(struct proc* p, struct kldfirstmod_args* uap) 81825537Sdfr{ 81925537Sdfr linker_file_t lf; 82025537Sdfr int error = 0; 82125537Sdfr 82225537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 82325537Sdfr if (lf) { 82425537Sdfr if (TAILQ_FIRST(&lf->modules)) 82530994Sphk p->p_retval[0] = module_getid(TAILQ_FIRST(&lf->modules)); 82625537Sdfr else 82730994Sphk p->p_retval[0] = 0; 82825537Sdfr } else 82925537Sdfr error = ENOENT; 83025537Sdfr 83125537Sdfr return error; 83225537Sdfr} 83340159Speter 83441090Speterint 83541090Speterkldsym(struct proc *p, struct kldsym_args *uap) 83641090Speter{ 83741090Speter char *symstr = NULL; 83843309Sdillon c_linker_sym_t sym; 83941090Speter linker_symval_t symval; 84041090Speter linker_file_t lf; 84141090Speter struct kld_sym_lookup lookup; 84241090Speter int error = 0; 84341090Speter 84443301Sdillon if ((error = copyin(SCARG(uap, data), &lookup, sizeof(lookup))) != 0) 84541090Speter goto out; 84641090Speter if (lookup.version != sizeof(lookup) || SCARG(uap, cmd) != KLDSYM_LOOKUP) { 84741090Speter error = EINVAL; 84841090Speter goto out; 84941090Speter } 85041090Speter 85141090Speter symstr = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 85243301Sdillon if ((error = copyinstr(lookup.symname, symstr, MAXPATHLEN, NULL)) != 0) 85341090Speter goto out; 85441090Speter 85541090Speter if (SCARG(uap, fileid) != 0) { 85641090Speter lf = linker_find_file_by_id(SCARG(uap, fileid)); 85741090Speter if (lf == NULL) { 85841090Speter error = ENOENT; 85941090Speter goto out; 86041090Speter } 86141090Speter if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 && 86241090Speter lf->ops->symbol_values(lf, sym, &symval) == 0) { 86350272Sbde lookup.symvalue = (uintptr_t)symval.value; 86441090Speter lookup.symsize = symval.size; 86541090Speter error = copyout(&lookup, SCARG(uap, data), sizeof(lookup)); 86641090Speter } else 86741090Speter error = ENOENT; 86841090Speter } else { 86950068Sgrog for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { 87041090Speter if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 && 87141090Speter lf->ops->symbol_values(lf, sym, &symval) == 0) { 87250272Sbde lookup.symvalue = (uintptr_t)symval.value; 87341090Speter lookup.symsize = symval.size; 87441090Speter error = copyout(&lookup, SCARG(uap, data), sizeof(lookup)); 87541090Speter break; 87641090Speter } 87741090Speter } 87841090Speter if (!lf) 87941090Speter error = ENOENT; 88041090Speter } 88141090Speterout: 88241090Speter if (symstr) 88341090Speter free(symstr, M_TEMP); 88441090Speter return error; 88541090Speter} 88641090Speter 88740159Speter/* 88840159Speter * Preloaded module support 88940159Speter */ 89040159Speter 89140159Speterstatic void 89240159Speterlinker_preload(void* arg) 89340159Speter{ 89440159Speter caddr_t modptr; 89540159Speter char *modname; 89640162Speter char *modtype; 89740159Speter linker_file_t lf; 89840159Speter linker_class_t lc; 89940159Speter int error; 90040159Speter struct linker_set *sysinits; 90140159Speter struct sysinit **sipp; 90246693Speter const moduledata_t *moddata; 90340159Speter 90440159Speter modptr = NULL; 90540159Speter while ((modptr = preload_search_next_name(modptr)) != NULL) { 90640159Speter modname = (char *)preload_search_info(modptr, MODINFO_NAME); 90740162Speter modtype = (char *)preload_search_info(modptr, MODINFO_TYPE); 90840159Speter if (modname == NULL) { 90940625Smsmith printf("Preloaded module at %p does not have a name!\n", modptr); 91040159Speter continue; 91140159Speter } 91240162Speter if (modtype == NULL) { 91340625Smsmith printf("Preloaded module at %p does not have a type!\n", modptr); 91440162Speter continue; 91540162Speter } 91640625Smsmith printf("Preloaded %s \"%s\" at %p.\n", modtype, modname, modptr); 91740159Speter lf = linker_find_file_by_name(modname); 91840159Speter if (lf) { 91940159Speter lf->userrefs++; 92040159Speter continue; 92140159Speter } 92240159Speter lf = NULL; 92340159Speter for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) { 92440159Speter error = lc->ops->load_file(modname, &lf); 92540159Speter if (error) { 92640159Speter lf = NULL; 92740159Speter break; 92840159Speter } 92940159Speter } 93040159Speter if (lf) { 93140159Speter lf->userrefs++; 93240159Speter 93340159Speter sysinits = (struct linker_set*) 93440159Speter linker_file_lookup_symbol(lf, "sysinit_set", 0); 93540159Speter if (sysinits) { 93640159Speter /* HACK ALERT! 93740159Speter * This is to set the sysinit moduledata so that the module 93840159Speter * can attach itself to the correct containing file. 93940159Speter * The sysinit could be run at *any* time. 94040159Speter */ 94140159Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 94240159Speter if ((*sipp)->func == module_register_init) { 94340159Speter moddata = (*sipp)->udata; 94446693Speter error = module_register(moddata, lf); 94546693Speter if (error) 94646693Speter printf("Preloaded %s \"%s\" failed to register: %d\n", 94746693Speter modtype, modname, error); 94840159Speter } 94940159Speter } 95040159Speter sysinit_add((struct sysinit **)sysinits->ls_items); 95140159Speter } 95244078Sdfr linker_file_register_sysctls(lf); 95340159Speter } 95440159Speter } 95540159Speter} 95640159Speter 95740159SpeterSYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, 0); 95840159Speter 95940159Speter/* 96040159Speter * Search for a not-loaded module by name. 96140159Speter * 96240159Speter * Modules may be found in the following locations: 96340159Speter * 96440159Speter * - preloaded (result is just the module name) 96540159Speter * - on disk (result is full path to module) 96640159Speter * 96740159Speter * If the module name is qualified in any way (contains path, etc.) 96840159Speter * the we simply return a copy of it. 96940159Speter * 97040159Speter * The search path can be manipulated via sysctl. Note that we use the ';' 97140159Speter * character as a separator to be consistent with the bootloader. 97240159Speter */ 97340159Speter 97450272Sbdestatic char linker_path[MAXPATHLEN] = "/;/boot/;/modules/"; 97540159Speter 97640159SpeterSYSCTL_STRING(_kern, OID_AUTO, module_path, CTLFLAG_RW, linker_path, 97740159Speter sizeof(linker_path), "module load search path"); 97840159Speter 97940159Speterstatic char * 98040159Speterlinker_strdup(const char *str) 98140159Speter{ 98240159Speter char *result; 98340159Speter 98440159Speter if ((result = malloc((strlen(str) + 1), M_LINKER, M_WAITOK)) != NULL) 98540159Speter strcpy(result, str); 98640159Speter return(result); 98740159Speter} 98840159Speter 98940159Speterchar * 99040159Speterlinker_search_path(const char *name) 99140159Speter{ 99240159Speter struct nameidata nd; 99340159Speter struct proc *p = curproc; /* XXX */ 99440159Speter char *cp, *ep, *result; 99540159Speter int error; 99640159Speter enum vtype type; 99740159Speter 99840159Speter /* qualified at all? */ 99940159Speter if (index(name, '/')) 100040159Speter return(linker_strdup(name)); 100140159Speter 100240159Speter /* traverse the linker path */ 100340159Speter cp = linker_path; 100440159Speter for (;;) { 100540159Speter 100640159Speter /* find the end of this component */ 100740159Speter for (ep = cp; (*ep != 0) && (*ep != ';'); ep++) 100840159Speter ; 100940159Speter result = malloc((strlen(name) + (ep - cp) + 1), M_LINKER, M_WAITOK); 101040159Speter if (result == NULL) /* actually ENOMEM */ 101140159Speter return(NULL); 101240159Speter 101340159Speter strncpy(result, cp, ep - cp); 101440159Speter strcpy(result + (ep - cp), name); 101540159Speter 101640159Speter /* 101740159Speter * Attempt to open the file, and return the path if we succeed and it's 101840159Speter * a regular file. 101940159Speter */ 102040159Speter NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, result, p); 102140159Speter error = vn_open(&nd, FREAD, 0); 102240159Speter if (error == 0) { 102340159Speter type = nd.ni_vp->v_type; 102440159Speter VOP_UNLOCK(nd.ni_vp, 0, p); 102540159Speter vn_close(nd.ni_vp, FREAD, p->p_ucred, p); 102640159Speter if (type == VREG) 102740159Speter return(result); 102840159Speter } 102940159Speter free(result, M_LINKER); 103040159Speter 103140159Speter if (*ep == 0) 103240159Speter break; 103340159Speter cp = ep + 1; 103440159Speter } 103540159Speter return(NULL); 103640159Speter} 1037