kern_linker.c revision 54411
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 * 2650477Speter * $FreeBSD: head/sys/kern/kern_linker.c 54411 1999-12-10 17:38:41Z peter $ 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 <sys/module.h> 4025537Sdfr#include <sys/linker.h> 4140159Speter#include <sys/fcntl.h> 4240159Speter#include <sys/libkern.h> 4340159Speter#include <sys/namei.h> 4440159Speter#include <sys/vnode.h> 4540159Speter#include <sys/sysctl.h> 4625537Sdfr 4740961Speter#ifdef KLD_DEBUG 4840961Speterint kld_debug = 0; 4940961Speter#endif 5040961Speter 5132153SbdeMALLOC_DEFINE(M_LINKER, "kld", "kernel linker"); 5231324Sbdelinker_file_t linker_current_file; 5340906Speterlinker_file_t linker_kernel_file; 5431324Sbde 5525537Sdfrstatic struct lock lock; /* lock for the file list */ 5625537Sdfrstatic linker_class_list_t classes; 5750068Sgrogstatic linker_file_list_t linker_files; 5825537Sdfrstatic int next_file_id = 1; 5925537Sdfr 6025537Sdfrstatic void 6125537Sdfrlinker_init(void* arg) 6225537Sdfr{ 6325537Sdfr lockinit(&lock, PVM, "klink", 0, 0); 6425537Sdfr TAILQ_INIT(&classes); 6550068Sgrog TAILQ_INIT(&linker_files); 6625537Sdfr} 6725537Sdfr 6840159SpeterSYSINIT(linker, SI_SUB_KLD, SI_ORDER_FIRST, linker_init, 0); 6925537Sdfr 7025537Sdfrint 7125537Sdfrlinker_add_class(const char* desc, void* priv, 7225537Sdfr struct linker_class_ops* ops) 7325537Sdfr{ 7425537Sdfr linker_class_t lc; 7525537Sdfr 7625537Sdfr lc = malloc(sizeof(struct linker_class), M_LINKER, M_NOWAIT); 7725537Sdfr if (!lc) 7825537Sdfr return ENOMEM; 7940395Speter bzero(lc, sizeof(*lc)); 8025537Sdfr 8125537Sdfr lc->desc = desc; 8225537Sdfr lc->priv = priv; 8325537Sdfr lc->ops = ops; 8425537Sdfr TAILQ_INSERT_HEAD(&classes, lc, link); 8525537Sdfr 8625537Sdfr return 0; 8725537Sdfr} 8825537Sdfr 8925537Sdfrstatic void 9025537Sdfrlinker_file_sysinit(linker_file_t lf) 9125537Sdfr{ 9225537Sdfr struct linker_set* sysinits; 9325537Sdfr struct sysinit** sipp; 9425537Sdfr struct sysinit** xipp; 9525537Sdfr struct sysinit* save; 9646693Speter const moduledata_t *moddata; 9746693Speter int error; 9825537Sdfr 9925537Sdfr KLD_DPF(FILE, ("linker_file_sysinit: calling SYSINITs for %s\n", 10025537Sdfr lf->filename)); 10125537Sdfr 10225537Sdfr sysinits = (struct linker_set*) 10325537Sdfr linker_file_lookup_symbol(lf, "sysinit_set", 0); 10440159Speter 10540159Speter KLD_DPF(FILE, ("linker_file_sysinit: SYSINITs %p\n", sysinits)); 10625537Sdfr if (!sysinits) 10725537Sdfr return; 10825537Sdfr 10940159Speter /* HACK ALERT! */ 11040159Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 11140159Speter if ((*sipp)->func == module_register_init) { 11240159Speter moddata = (*sipp)->udata; 11346693Speter error = module_register(moddata, lf); 11446693Speter if (error) 11546693Speter printf("linker_file_sysinit \"%s\" failed to register! %d\n", 11646693Speter lf->filename, error); 11740159Speter } 11840159Speter } 11940159Speter 12025537Sdfr /* 12125537Sdfr * Perform a bubble sort of the system initialization objects by 12225537Sdfr * their subsystem (primary key) and order (secondary key). 12325537Sdfr * 12425537Sdfr * Since some things care about execution order, this is the 12525537Sdfr * operation which ensures continued function. 12625537Sdfr */ 12741055Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 12841055Speter for (xipp = sipp + 1; *xipp; xipp++) { 12941055Speter if ((*sipp)->subsystem <= (*xipp)->subsystem || 13041055Speter ((*sipp)->subsystem == (*xipp)->subsystem && 13141055Speter (*sipp)->order <= (*xipp)->order)) 13225537Sdfr continue; /* skip*/ 13325537Sdfr save = *sipp; 13425537Sdfr *sipp = *xipp; 13525537Sdfr *xipp = save; 13625537Sdfr } 13725537Sdfr } 13825537Sdfr 13925537Sdfr 14025537Sdfr /* 14125537Sdfr * Traverse the (now) ordered list of system initialization tasks. 14225537Sdfr * Perform each task, and continue on to the next task. 14325537Sdfr */ 14441055Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 14541055Speter if ((*sipp)->subsystem == SI_SUB_DUMMY) 14625537Sdfr continue; /* skip dummy task(s)*/ 14725537Sdfr 14848391Speter /* Call function */ 14948391Speter (*((*sipp)->func))((*sipp)->udata); 15025537Sdfr } 15125537Sdfr} 15225537Sdfr 15341055Speterstatic void 15441055Speterlinker_file_sysuninit(linker_file_t lf) 15541055Speter{ 15641055Speter struct linker_set* sysuninits; 15741055Speter struct sysinit** sipp; 15841055Speter struct sysinit** xipp; 15941055Speter struct sysinit* save; 16041055Speter 16141055Speter KLD_DPF(FILE, ("linker_file_sysuninit: calling SYSUNINITs for %s\n", 16241055Speter lf->filename)); 16341055Speter 16441055Speter sysuninits = (struct linker_set*) 16541055Speter linker_file_lookup_symbol(lf, "sysuninit_set", 0); 16641055Speter 16741055Speter KLD_DPF(FILE, ("linker_file_sysuninit: SYSUNINITs %p\n", sysuninits)); 16841055Speter if (!sysuninits) 16941055Speter return; 17041055Speter 17141055Speter /* 17241055Speter * Perform a reverse bubble sort of the system initialization objects 17341055Speter * by their subsystem (primary key) and order (secondary key). 17441055Speter * 17541055Speter * Since some things care about execution order, this is the 17641055Speter * operation which ensures continued function. 17741055Speter */ 17841055Speter for (sipp = (struct sysinit **)sysuninits->ls_items; *sipp; sipp++) { 17941055Speter for (xipp = sipp + 1; *xipp; xipp++) { 18041055Speter if ((*sipp)->subsystem >= (*xipp)->subsystem || 18141055Speter ((*sipp)->subsystem == (*xipp)->subsystem && 18241055Speter (*sipp)->order >= (*xipp)->order)) 18341055Speter continue; /* skip*/ 18441055Speter save = *sipp; 18541055Speter *sipp = *xipp; 18641055Speter *xipp = save; 18741055Speter } 18841055Speter } 18941055Speter 19041055Speter 19141055Speter /* 19241055Speter * Traverse the (now) ordered list of system initialization tasks. 19341055Speter * Perform each task, and continue on to the next task. 19441055Speter */ 19541055Speter for (sipp = (struct sysinit **)sysuninits->ls_items; *sipp; sipp++) { 19641055Speter if ((*sipp)->subsystem == SI_SUB_DUMMY) 19741055Speter continue; /* skip dummy task(s)*/ 19841055Speter 19948391Speter /* Call function */ 20048391Speter (*((*sipp)->func))((*sipp)->udata); 20141055Speter } 20241055Speter} 20341055Speter 20444078Sdfrstatic void 20544078Sdfrlinker_file_register_sysctls(linker_file_t lf) 20644078Sdfr{ 20744078Sdfr struct linker_set* sysctls; 20844078Sdfr 20944078Sdfr KLD_DPF(FILE, ("linker_file_register_sysctls: registering SYSCTLs for %s\n", 21044078Sdfr lf->filename)); 21144078Sdfr 21244078Sdfr sysctls = (struct linker_set*) 21344078Sdfr linker_file_lookup_symbol(lf, "sysctl_set", 0); 21444078Sdfr 21544078Sdfr KLD_DPF(FILE, ("linker_file_register_sysctls: SYSCTLs %p\n", sysctls)); 21644078Sdfr if (!sysctls) 21744078Sdfr return; 21844078Sdfr 21944078Sdfr sysctl_register_set(sysctls); 22044078Sdfr} 22144078Sdfr 22244078Sdfrstatic void 22344078Sdfrlinker_file_unregister_sysctls(linker_file_t lf) 22444078Sdfr{ 22544078Sdfr struct linker_set* sysctls; 22644078Sdfr 22744078Sdfr KLD_DPF(FILE, ("linker_file_unregister_sysctls: registering SYSCTLs for %s\n", 22844078Sdfr lf->filename)); 22944078Sdfr 23044078Sdfr sysctls = (struct linker_set*) 23144078Sdfr linker_file_lookup_symbol(lf, "sysctl_set", 0); 23244078Sdfr 23344078Sdfr KLD_DPF(FILE, ("linker_file_unregister_sysctls: SYSCTLs %p\n", sysctls)); 23444078Sdfr if (!sysctls) 23544078Sdfr return; 23644078Sdfr 23744078Sdfr sysctl_unregister_set(sysctls); 23844078Sdfr} 23944078Sdfr 24025537Sdfrint 24125537Sdfrlinker_load_file(const char* filename, linker_file_t* result) 24225537Sdfr{ 24325537Sdfr linker_class_t lc; 24425537Sdfr linker_file_t lf; 24542755Speter int foundfile, error = 0; 24640861Speter char *koname = NULL; 24725537Sdfr 24825537Sdfr lf = linker_find_file_by_name(filename); 24925537Sdfr if (lf) { 25025537Sdfr KLD_DPF(FILE, ("linker_load_file: file %s is already loaded, incrementing refs\n", filename)); 25125537Sdfr *result = lf; 25225537Sdfr lf->refs++; 25325537Sdfr goto out; 25425537Sdfr } 25525537Sdfr 25640861Speter koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK); 25740861Speter if (koname == NULL) { 25840861Speter error = ENOMEM; 25940861Speter goto out; 26040861Speter } 26140861Speter sprintf(koname, "%s.ko", filename); 26225537Sdfr lf = NULL; 26342755Speter foundfile = 0; 26425537Sdfr for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) { 26525537Sdfr KLD_DPF(FILE, ("linker_load_file: trying to load %s as %s\n", 26625537Sdfr filename, lc->desc)); 26742755Speter 26842755Speter error = lc->ops->load_file(koname, &lf); /* First with .ko */ 26942755Speter if (lf == NULL && error == ENOENT) 27042755Speter error = lc->ops->load_file(filename, &lf); /* Then try without */ 27142755Speter /* 27242755Speter * If we got something other than ENOENT, then it exists but we cannot 27342755Speter * load it for some other reason. 27442755Speter */ 27542755Speter if (error != ENOENT) 27642755Speter foundfile = 1; 27725537Sdfr if (lf) { 27844549Sdfr linker_file_register_sysctls(lf); 27925537Sdfr linker_file_sysinit(lf); 28025537Sdfr 28125537Sdfr *result = lf; 28240861Speter error = 0; 28325537Sdfr goto out; 28425537Sdfr } 28525537Sdfr } 28642755Speter /* 28742755Speter * Less than ideal, but tells the user whether it failed to load or 28842755Speter * the module was not found. 28942755Speter */ 29042755Speter if (foundfile) 29142755Speter error = ENOEXEC; /* Format not recognised (or unloadable) */ 29242755Speter else 29342755Speter error = ENOENT; /* Nothing found */ 29425537Sdfr 29525537Sdfrout: 29640861Speter if (koname) 29740861Speter free(koname, M_LINKER); 29825537Sdfr return error; 29925537Sdfr} 30025537Sdfr 30125537Sdfrlinker_file_t 30225537Sdfrlinker_find_file_by_name(const char* filename) 30325537Sdfr{ 30425537Sdfr linker_file_t lf = 0; 30540861Speter char *koname; 30625537Sdfr 30740861Speter koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK); 30840861Speter if (koname == NULL) 30940861Speter goto out; 31040861Speter sprintf(koname, "%s.ko", filename); 31140861Speter 31225537Sdfr lockmgr(&lock, LK_SHARED, 0, curproc); 31350068Sgrog for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { 31440861Speter if (!strcmp(lf->filename, koname)) 31540861Speter break; 31625537Sdfr if (!strcmp(lf->filename, filename)) 31725537Sdfr break; 31840861Speter } 31925537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 32025537Sdfr 32140861Speterout: 32240861Speter if (koname) 32340861Speter free(koname, M_LINKER); 32425537Sdfr return lf; 32525537Sdfr} 32625537Sdfr 32725537Sdfrlinker_file_t 32825537Sdfrlinker_find_file_by_id(int fileid) 32925537Sdfr{ 33025537Sdfr linker_file_t lf = 0; 33125537Sdfr 33225537Sdfr lockmgr(&lock, LK_SHARED, 0, curproc); 33350068Sgrog for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) 33425537Sdfr if (lf->id == fileid) 33525537Sdfr break; 33625537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 33725537Sdfr 33825537Sdfr return lf; 33925537Sdfr} 34025537Sdfr 34125537Sdfrlinker_file_t 34240159Speterlinker_make_file(const char* pathname, void* priv, struct linker_file_ops* ops) 34325537Sdfr{ 34425537Sdfr linker_file_t lf = 0; 34525537Sdfr int namelen; 34640159Speter const char *filename; 34725537Sdfr 34854411Speter filename = rindex(pathname, '/'); 34940159Speter if (filename && filename[1]) 35040159Speter filename++; 35140159Speter else 35240159Speter filename = pathname; 35340159Speter 35425537Sdfr KLD_DPF(FILE, ("linker_make_file: new file, filename=%s\n", filename)); 35545356Speter lockmgr(&lock, LK_EXCLUSIVE, 0, curproc); 35625537Sdfr namelen = strlen(filename) + 1; 35725537Sdfr lf = malloc(sizeof(struct linker_file) + namelen, M_LINKER, M_WAITOK); 35825537Sdfr if (!lf) 35925537Sdfr goto out; 36040395Speter bzero(lf, sizeof(*lf)); 36125537Sdfr 36225537Sdfr lf->refs = 1; 36325537Sdfr lf->userrefs = 0; 36443185Sdfr lf->flags = 0; 36525537Sdfr lf->filename = (char*) (lf + 1); 36625537Sdfr strcpy(lf->filename, filename); 36725537Sdfr lf->id = next_file_id++; 36825537Sdfr lf->ndeps = 0; 36925537Sdfr lf->deps = NULL; 37025537Sdfr STAILQ_INIT(&lf->common); 37125537Sdfr TAILQ_INIT(&lf->modules); 37225537Sdfr 37325537Sdfr lf->priv = priv; 37425537Sdfr lf->ops = ops; 37550068Sgrog TAILQ_INSERT_TAIL(&linker_files, lf, link); 37625537Sdfr 37725537Sdfrout: 37825537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 37925537Sdfr return lf; 38025537Sdfr} 38125537Sdfr 38225537Sdfrint 38325537Sdfrlinker_file_unload(linker_file_t file) 38425537Sdfr{ 38525537Sdfr module_t mod, next; 38625537Sdfr struct common_symbol* cp; 38725537Sdfr int error = 0; 38825537Sdfr int i; 38925537Sdfr 39040159Speter KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", file->refs)); 39145356Speter lockmgr(&lock, LK_EXCLUSIVE, 0, curproc); 39225537Sdfr if (file->refs == 1) { 39325537Sdfr KLD_DPF(FILE, ("linker_file_unload: file is unloading, informing modules\n")); 39425537Sdfr /* 39525537Sdfr * Inform any modules associated with this file. 39625537Sdfr */ 39725537Sdfr for (mod = TAILQ_FIRST(&file->modules); mod; mod = next) { 39825537Sdfr next = module_getfnext(mod); 39925537Sdfr 40025537Sdfr /* 40125537Sdfr * Give the module a chance to veto the unload. 40225537Sdfr */ 40343301Sdillon if ((error = module_unload(mod)) != 0) { 40425537Sdfr KLD_DPF(FILE, ("linker_file_unload: module %x vetoes unload\n", 40525537Sdfr mod)); 40625537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 40725537Sdfr goto out; 40825537Sdfr } 40925537Sdfr 41025537Sdfr module_release(mod); 41125537Sdfr } 41225537Sdfr } 41325537Sdfr 41425537Sdfr file->refs--; 41525537Sdfr if (file->refs > 0) { 41625537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 41725537Sdfr goto out; 41825537Sdfr } 41925537Sdfr 42043185Sdfr /* Don't try to run SYSUNINITs if we are unloaded due to a link error */ 42144078Sdfr if (file->flags & LINKER_FILE_LINKED) { 42243185Sdfr linker_file_sysuninit(file); 42344078Sdfr linker_file_unregister_sysctls(file); 42444078Sdfr } 42541055Speter 42650068Sgrog TAILQ_REMOVE(&linker_files, file, link); 42725537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 42840159Speter 42925537Sdfr for (i = 0; i < file->ndeps; i++) 43025537Sdfr linker_file_unload(file->deps[i]); 43125537Sdfr free(file->deps, M_LINKER); 43225537Sdfr 43325537Sdfr for (cp = STAILQ_FIRST(&file->common); cp; 43425537Sdfr cp = STAILQ_FIRST(&file->common)) { 43525537Sdfr STAILQ_REMOVE(&file->common, cp, common_symbol, link); 43625537Sdfr free(cp, M_LINKER); 43725537Sdfr } 43825537Sdfr 43925537Sdfr file->ops->unload(file); 44025537Sdfr free(file, M_LINKER); 44125537Sdfr 44225537Sdfrout: 44325537Sdfr return error; 44425537Sdfr} 44525537Sdfr 44625537Sdfrint 44725537Sdfrlinker_file_add_dependancy(linker_file_t file, linker_file_t dep) 44825537Sdfr{ 44925537Sdfr linker_file_t* newdeps; 45025537Sdfr 45125537Sdfr newdeps = malloc((file->ndeps + 1) * sizeof(linker_file_t*), 45225537Sdfr M_LINKER, M_WAITOK); 45325537Sdfr if (newdeps == NULL) 45425537Sdfr return ENOMEM; 45540395Speter bzero(newdeps, (file->ndeps + 1) * sizeof(linker_file_t*)); 45625537Sdfr 45725537Sdfr if (file->deps) { 45825537Sdfr bcopy(file->deps, newdeps, file->ndeps * sizeof(linker_file_t*)); 45925537Sdfr free(file->deps, M_LINKER); 46025537Sdfr } 46125537Sdfr file->deps = newdeps; 46225537Sdfr file->deps[file->ndeps] = dep; 46325537Sdfr file->ndeps++; 46425537Sdfr 46525537Sdfr return 0; 46625537Sdfr} 46725537Sdfr 46825537Sdfrcaddr_t 46925537Sdfrlinker_file_lookup_symbol(linker_file_t file, const char* name, int deps) 47025537Sdfr{ 47143309Sdillon c_linker_sym_t sym; 47238275Sdfr linker_symval_t symval; 47342849Speter linker_file_t lf; 47425537Sdfr caddr_t address; 47525537Sdfr size_t common_size = 0; 47625537Sdfr int i; 47725537Sdfr 47840159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d\n", 47925537Sdfr file, name, deps)); 48025537Sdfr 48138275Sdfr if (file->ops->lookup_symbol(file, name, &sym) == 0) { 48238275Sdfr file->ops->symbol_values(file, sym, &symval); 48338275Sdfr if (symval.value == 0) 48425537Sdfr /* 48525537Sdfr * For commons, first look them up in the dependancies and 48625537Sdfr * only allocate space if not found there. 48725537Sdfr */ 48838275Sdfr common_size = symval.size; 48940159Speter else { 49040159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: symbol.value=%x\n", symval.value)); 49138275Sdfr return symval.value; 49240159Speter } 49338275Sdfr } 49425537Sdfr 49542849Speter if (deps) { 49625537Sdfr for (i = 0; i < file->ndeps; i++) { 49725537Sdfr address = linker_file_lookup_symbol(file->deps[i], name, 0); 49840159Speter if (address) { 49940159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: deps value=%x\n", address)); 50025537Sdfr return address; 50140159Speter } 50225537Sdfr } 50325537Sdfr 50442849Speter /* If we have not found it in the dependencies, search globally */ 50550068Sgrog for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { 50642849Speter /* But skip the current file if it's on the list */ 50742849Speter if (lf == file) 50842849Speter continue; 50942849Speter /* And skip the files we searched above */ 51042849Speter for (i = 0; i < file->ndeps; i++) 51142849Speter if (lf == file->deps[i]) 51242849Speter break; 51342849Speter if (i < file->ndeps) 51442849Speter continue; 51542849Speter address = linker_file_lookup_symbol(lf, name, 0); 51642849Speter if (address) { 51742849Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: global value=%x\n", address)); 51842849Speter return address; 51942849Speter } 52042849Speter } 52142849Speter } 52242849Speter 52325537Sdfr if (common_size > 0) { 52425537Sdfr /* 52525537Sdfr * This is a common symbol which was not found in the 52625537Sdfr * dependancies. We maintain a simple common symbol table in 52725537Sdfr * the file object. 52825537Sdfr */ 52925537Sdfr struct common_symbol* cp; 53025537Sdfr 53125537Sdfr for (cp = STAILQ_FIRST(&file->common); cp; 53225537Sdfr cp = STAILQ_NEXT(cp, link)) 53340159Speter if (!strcmp(cp->name, name)) { 53440159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: old common value=%x\n", cp->address)); 53525537Sdfr return cp->address; 53640159Speter } 53725537Sdfr 53825537Sdfr /* 53925537Sdfr * Round the symbol size up to align. 54025537Sdfr */ 54125537Sdfr common_size = (common_size + sizeof(int) - 1) & -sizeof(int); 54225537Sdfr cp = malloc(sizeof(struct common_symbol) 54325537Sdfr + common_size 54425537Sdfr + strlen(name) + 1, 54525537Sdfr M_LINKER, M_WAITOK); 54640159Speter if (!cp) { 54740159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: nomem\n")); 54825537Sdfr return 0; 54940159Speter } 55040395Speter bzero(cp, sizeof(struct common_symbol) + common_size + strlen(name)+ 1); 55125537Sdfr 55225537Sdfr cp->address = (caddr_t) (cp + 1); 55325537Sdfr cp->name = cp->address + common_size; 55425537Sdfr strcpy(cp->name, name); 55525537Sdfr bzero(cp->address, common_size); 55625537Sdfr STAILQ_INSERT_TAIL(&file->common, cp, link); 55725537Sdfr 55840159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: new common value=%x\n", cp->address)); 55925537Sdfr return cp->address; 56025537Sdfr } 56125537Sdfr 56240159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: fail\n")); 56325537Sdfr return 0; 56425537Sdfr} 56525537Sdfr 56640159Speter#ifdef DDB 56725537Sdfr/* 56840159Speter * DDB Helpers. DDB has to look across multiple files with their own 56940159Speter * symbol tables and string tables. 57040159Speter * 57140159Speter * Note that we do not obey list locking protocols here. We really don't 57240159Speter * need DDB to hang because somebody's got the lock held. We'll take the 57340159Speter * chance that the files list is inconsistant instead. 57440159Speter */ 57540159Speter 57640159Speterint 57743309Sdillonlinker_ddb_lookup(const char *symstr, c_linker_sym_t *sym) 57840159Speter{ 57940159Speter linker_file_t lf; 58040159Speter 58150068Sgrog for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { 58240159Speter if (lf->ops->lookup_symbol(lf, symstr, sym) == 0) 58340159Speter return 0; 58440159Speter } 58540159Speter return ENOENT; 58640159Speter} 58740159Speter 58840159Speterint 58943309Sdillonlinker_ddb_search_symbol(caddr_t value, c_linker_sym_t *sym, long *diffp) 59040159Speter{ 59140159Speter linker_file_t lf; 59250272Sbde u_long off = (uintptr_t)value; 59340159Speter u_long diff, bestdiff; 59443309Sdillon c_linker_sym_t best; 59543309Sdillon c_linker_sym_t es; 59640159Speter 59740159Speter best = 0; 59840159Speter bestdiff = off; 59950068Sgrog for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { 60040159Speter if (lf->ops->search_symbol(lf, value, &es, &diff) != 0) 60140159Speter continue; 60240159Speter if (es != 0 && diff < bestdiff) { 60340159Speter best = es; 60440159Speter bestdiff = diff; 60540159Speter } 60640159Speter if (bestdiff == 0) 60740159Speter break; 60840159Speter } 60940159Speter if (best) { 61040159Speter *sym = best; 61140159Speter *diffp = bestdiff; 61240159Speter return 0; 61340159Speter } else { 61440159Speter *sym = 0; 61540159Speter *diffp = off; 61640159Speter return ENOENT; 61740159Speter } 61840159Speter} 61940159Speter 62040159Speterint 62143309Sdillonlinker_ddb_symbol_values(c_linker_sym_t sym, linker_symval_t *symval) 62240159Speter{ 62340159Speter linker_file_t lf; 62440159Speter 62550068Sgrog for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { 62640159Speter if (lf->ops->symbol_values(lf, sym, symval) == 0) 62740159Speter return 0; 62840159Speter } 62940159Speter return ENOENT; 63040159Speter} 63140159Speter 63240159Speter#endif 63340159Speter 63440159Speter/* 63525537Sdfr * Syscalls. 63625537Sdfr */ 63725537Sdfr 63825537Sdfrint 63930994Sphkkldload(struct proc* p, struct kldload_args* uap) 64025537Sdfr{ 64142316Smsmith char* filename = NULL, *modulename; 64225537Sdfr linker_file_t lf; 64325537Sdfr int error = 0; 64425537Sdfr 64530994Sphk p->p_retval[0] = -1; 64625537Sdfr 64725537Sdfr if (securelevel > 0) 64825537Sdfr return EPERM; 64925537Sdfr 65046112Sphk if ((error = suser(p)) != 0) 65125537Sdfr return error; 65225537Sdfr 65325537Sdfr filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 65443301Sdillon if ((error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) != 0) 65525537Sdfr goto out; 65625537Sdfr 65742316Smsmith /* Can't load more than one module with the same name */ 65842316Smsmith modulename = rindex(filename, '/'); 65942316Smsmith if (modulename == NULL) 66042316Smsmith modulename = filename; 66144173Sdfr else 66244173Sdfr modulename++; 66342316Smsmith if (linker_find_file_by_name(modulename)) { 66442316Smsmith error = EEXIST; 66542316Smsmith goto out; 66642316Smsmith } 66742316Smsmith 66843301Sdillon if ((error = linker_load_file(filename, &lf)) != 0) 66925537Sdfr goto out; 67025537Sdfr 67125537Sdfr lf->userrefs++; 67230994Sphk p->p_retval[0] = lf->id; 67340159Speter 67425537Sdfrout: 67525537Sdfr if (filename) 67625537Sdfr free(filename, M_TEMP); 67725537Sdfr return error; 67825537Sdfr} 67925537Sdfr 68025537Sdfrint 68130994Sphkkldunload(struct proc* p, struct kldunload_args* uap) 68225537Sdfr{ 68325537Sdfr linker_file_t lf; 68425537Sdfr int error = 0; 68525537Sdfr 68625537Sdfr if (securelevel > 0) 68725537Sdfr return EPERM; 68825537Sdfr 68946112Sphk if ((error = suser(p)) != 0) 69025537Sdfr return error; 69125537Sdfr 69225537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 69325537Sdfr if (lf) { 69425537Sdfr KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs)); 69525537Sdfr if (lf->userrefs == 0) { 69643084Speter printf("linkerunload: attempt to unload file that was loaded by the kernel\n"); 69725537Sdfr error = EBUSY; 69825537Sdfr goto out; 69925537Sdfr } 70043084Speter lf->userrefs--; 70142837Speter error = linker_file_unload(lf); 70242837Speter if (error) 70343084Speter lf->userrefs++; 70425537Sdfr } else 70525537Sdfr error = ENOENT; 70625537Sdfr 70725537Sdfrout: 70825537Sdfr return error; 70925537Sdfr} 71025537Sdfr 71125537Sdfrint 71230994Sphkkldfind(struct proc* p, struct kldfind_args* uap) 71325537Sdfr{ 71442316Smsmith char* filename = NULL, *modulename; 71525537Sdfr linker_file_t lf; 71625537Sdfr int error = 0; 71725537Sdfr 71830994Sphk p->p_retval[0] = -1; 71925537Sdfr 72025537Sdfr filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 72143301Sdillon if ((error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) != 0) 72225537Sdfr goto out; 72325537Sdfr 72442316Smsmith modulename = rindex(filename, '/'); 72542316Smsmith if (modulename == NULL) 72642316Smsmith modulename = filename; 72742316Smsmith 72842316Smsmith lf = linker_find_file_by_name(modulename); 72925537Sdfr if (lf) 73030994Sphk p->p_retval[0] = lf->id; 73125537Sdfr else 73225537Sdfr error = ENOENT; 73340159Speter 73425537Sdfrout: 73525537Sdfr if (filename) 73625537Sdfr free(filename, M_TEMP); 73725537Sdfr return error; 73825537Sdfr} 73925537Sdfr 74025537Sdfrint 74130994Sphkkldnext(struct proc* p, struct kldnext_args* uap) 74225537Sdfr{ 74325537Sdfr linker_file_t lf; 74425537Sdfr int error = 0; 74525537Sdfr 74625537Sdfr if (SCARG(uap, fileid) == 0) { 74750068Sgrog if (TAILQ_FIRST(&linker_files)) 74850068Sgrog p->p_retval[0] = TAILQ_FIRST(&linker_files)->id; 74925537Sdfr else 75030994Sphk p->p_retval[0] = 0; 75125537Sdfr return 0; 75225537Sdfr } 75340159Speter 75425537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 75525537Sdfr if (lf) { 75625537Sdfr if (TAILQ_NEXT(lf, link)) 75730994Sphk p->p_retval[0] = TAILQ_NEXT(lf, link)->id; 75825537Sdfr else 75930994Sphk p->p_retval[0] = 0; 76025537Sdfr } else 76125537Sdfr error = ENOENT; 76225537Sdfr 76325537Sdfr return error; 76425537Sdfr} 76525537Sdfr 76625537Sdfrint 76730994Sphkkldstat(struct proc* p, struct kldstat_args* uap) 76825537Sdfr{ 76925537Sdfr linker_file_t lf; 77025537Sdfr int error = 0; 77125537Sdfr int version; 77225537Sdfr struct kld_file_stat* stat; 77325537Sdfr int namelen; 77425537Sdfr 77525537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 77625537Sdfr if (!lf) { 77725537Sdfr error = ENOENT; 77825537Sdfr goto out; 77925537Sdfr } 78025537Sdfr 78125537Sdfr stat = SCARG(uap, stat); 78225537Sdfr 78325537Sdfr /* 78425537Sdfr * Check the version of the user's structure. 78525537Sdfr */ 78643301Sdillon if ((error = copyin(&stat->version, &version, sizeof(version))) != 0) 78725537Sdfr goto out; 78825537Sdfr if (version != sizeof(struct kld_file_stat)) { 78925537Sdfr error = EINVAL; 79025537Sdfr goto out; 79125537Sdfr } 79225537Sdfr 79325537Sdfr namelen = strlen(lf->filename) + 1; 79425537Sdfr if (namelen > MAXPATHLEN) 79525537Sdfr namelen = MAXPATHLEN; 79643301Sdillon if ((error = copyout(lf->filename, &stat->name[0], namelen)) != 0) 79725537Sdfr goto out; 79843301Sdillon if ((error = copyout(&lf->refs, &stat->refs, sizeof(int))) != 0) 79925537Sdfr goto out; 80043301Sdillon if ((error = copyout(&lf->id, &stat->id, sizeof(int))) != 0) 80125537Sdfr goto out; 80243301Sdillon if ((error = copyout(&lf->address, &stat->address, sizeof(caddr_t))) != 0) 80325537Sdfr goto out; 80443301Sdillon if ((error = copyout(&lf->size, &stat->size, sizeof(size_t))) != 0) 80525537Sdfr goto out; 80625537Sdfr 80730994Sphk p->p_retval[0] = 0; 80825537Sdfr 80925537Sdfrout: 81025537Sdfr return error; 81125537Sdfr} 81225537Sdfr 81325537Sdfrint 81430994Sphkkldfirstmod(struct proc* p, struct kldfirstmod_args* uap) 81525537Sdfr{ 81625537Sdfr linker_file_t lf; 81725537Sdfr int error = 0; 81825537Sdfr 81925537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 82025537Sdfr if (lf) { 82125537Sdfr if (TAILQ_FIRST(&lf->modules)) 82230994Sphk p->p_retval[0] = module_getid(TAILQ_FIRST(&lf->modules)); 82325537Sdfr else 82430994Sphk p->p_retval[0] = 0; 82525537Sdfr } else 82625537Sdfr error = ENOENT; 82725537Sdfr 82825537Sdfr return error; 82925537Sdfr} 83040159Speter 83141090Speterint 83241090Speterkldsym(struct proc *p, struct kldsym_args *uap) 83341090Speter{ 83441090Speter char *symstr = NULL; 83543309Sdillon c_linker_sym_t sym; 83641090Speter linker_symval_t symval; 83741090Speter linker_file_t lf; 83841090Speter struct kld_sym_lookup lookup; 83941090Speter int error = 0; 84041090Speter 84143301Sdillon if ((error = copyin(SCARG(uap, data), &lookup, sizeof(lookup))) != 0) 84241090Speter goto out; 84341090Speter if (lookup.version != sizeof(lookup) || SCARG(uap, cmd) != KLDSYM_LOOKUP) { 84441090Speter error = EINVAL; 84541090Speter goto out; 84641090Speter } 84741090Speter 84841090Speter symstr = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 84943301Sdillon if ((error = copyinstr(lookup.symname, symstr, MAXPATHLEN, NULL)) != 0) 85041090Speter goto out; 85141090Speter 85241090Speter if (SCARG(uap, fileid) != 0) { 85341090Speter lf = linker_find_file_by_id(SCARG(uap, fileid)); 85441090Speter if (lf == NULL) { 85541090Speter error = ENOENT; 85641090Speter goto out; 85741090Speter } 85841090Speter if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 && 85941090Speter lf->ops->symbol_values(lf, sym, &symval) == 0) { 86050272Sbde lookup.symvalue = (uintptr_t)symval.value; 86141090Speter lookup.symsize = symval.size; 86241090Speter error = copyout(&lookup, SCARG(uap, data), sizeof(lookup)); 86341090Speter } else 86441090Speter error = ENOENT; 86541090Speter } else { 86650068Sgrog for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { 86741090Speter if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 && 86841090Speter lf->ops->symbol_values(lf, sym, &symval) == 0) { 86950272Sbde lookup.symvalue = (uintptr_t)symval.value; 87041090Speter lookup.symsize = symval.size; 87141090Speter error = copyout(&lookup, SCARG(uap, data), sizeof(lookup)); 87241090Speter break; 87341090Speter } 87441090Speter } 87541090Speter if (!lf) 87641090Speter error = ENOENT; 87741090Speter } 87841090Speterout: 87941090Speter if (symstr) 88041090Speter free(symstr, M_TEMP); 88141090Speter return error; 88241090Speter} 88341090Speter 88440159Speter/* 88540159Speter * Preloaded module support 88640159Speter */ 88740159Speter 88840159Speterstatic void 88940159Speterlinker_preload(void* arg) 89040159Speter{ 89140159Speter caddr_t modptr; 89240159Speter char *modname; 89340162Speter char *modtype; 89440159Speter linker_file_t lf; 89540159Speter linker_class_t lc; 89640159Speter int error; 89740159Speter struct linker_set *sysinits; 89840159Speter struct sysinit **sipp; 89946693Speter const moduledata_t *moddata; 90040159Speter 90140159Speter modptr = NULL; 90240159Speter while ((modptr = preload_search_next_name(modptr)) != NULL) { 90340159Speter modname = (char *)preload_search_info(modptr, MODINFO_NAME); 90440162Speter modtype = (char *)preload_search_info(modptr, MODINFO_TYPE); 90540159Speter if (modname == NULL) { 90640625Smsmith printf("Preloaded module at %p does not have a name!\n", modptr); 90740159Speter continue; 90840159Speter } 90940162Speter if (modtype == NULL) { 91040625Smsmith printf("Preloaded module at %p does not have a type!\n", modptr); 91140162Speter continue; 91240162Speter } 91340625Smsmith printf("Preloaded %s \"%s\" at %p.\n", modtype, modname, modptr); 91440159Speter lf = linker_find_file_by_name(modname); 91540159Speter if (lf) { 91640159Speter lf->userrefs++; 91740159Speter continue; 91840159Speter } 91940159Speter lf = NULL; 92040159Speter for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) { 92140159Speter error = lc->ops->load_file(modname, &lf); 92240159Speter if (error) { 92340159Speter lf = NULL; 92440159Speter break; 92540159Speter } 92640159Speter } 92740159Speter if (lf) { 92840159Speter lf->userrefs++; 92940159Speter 93040159Speter sysinits = (struct linker_set*) 93140159Speter linker_file_lookup_symbol(lf, "sysinit_set", 0); 93240159Speter if (sysinits) { 93340159Speter /* HACK ALERT! 93440159Speter * This is to set the sysinit moduledata so that the module 93540159Speter * can attach itself to the correct containing file. 93640159Speter * The sysinit could be run at *any* time. 93740159Speter */ 93840159Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 93940159Speter if ((*sipp)->func == module_register_init) { 94040159Speter moddata = (*sipp)->udata; 94146693Speter error = module_register(moddata, lf); 94246693Speter if (error) 94346693Speter printf("Preloaded %s \"%s\" failed to register: %d\n", 94446693Speter modtype, modname, error); 94540159Speter } 94640159Speter } 94740159Speter sysinit_add((struct sysinit **)sysinits->ls_items); 94840159Speter } 94944078Sdfr linker_file_register_sysctls(lf); 95040159Speter } 95140159Speter } 95240159Speter} 95340159Speter 95440159SpeterSYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, 0); 95540159Speter 95640159Speter/* 95740159Speter * Search for a not-loaded module by name. 95840159Speter * 95940159Speter * Modules may be found in the following locations: 96040159Speter * 96140159Speter * - preloaded (result is just the module name) 96240159Speter * - on disk (result is full path to module) 96340159Speter * 96440159Speter * If the module name is qualified in any way (contains path, etc.) 96540159Speter * the we simply return a copy of it. 96640159Speter * 96740159Speter * The search path can be manipulated via sysctl. Note that we use the ';' 96840159Speter * character as a separator to be consistent with the bootloader. 96940159Speter */ 97040159Speter 97150272Sbdestatic char linker_path[MAXPATHLEN] = "/;/boot/;/modules/"; 97240159Speter 97340159SpeterSYSCTL_STRING(_kern, OID_AUTO, module_path, CTLFLAG_RW, linker_path, 97440159Speter sizeof(linker_path), "module load search path"); 97540159Speter 97640159Speterstatic char * 97740159Speterlinker_strdup(const char *str) 97840159Speter{ 97940159Speter char *result; 98040159Speter 98140159Speter if ((result = malloc((strlen(str) + 1), M_LINKER, M_WAITOK)) != NULL) 98240159Speter strcpy(result, str); 98340159Speter return(result); 98440159Speter} 98540159Speter 98640159Speterchar * 98740159Speterlinker_search_path(const char *name) 98840159Speter{ 98940159Speter struct nameidata nd; 99040159Speter struct proc *p = curproc; /* XXX */ 99140159Speter char *cp, *ep, *result; 99240159Speter int error; 99340159Speter enum vtype type; 99440159Speter 99540159Speter /* qualified at all? */ 99654411Speter if (index(name, '/')) 99740159Speter return(linker_strdup(name)); 99840159Speter 99940159Speter /* traverse the linker path */ 100040159Speter cp = linker_path; 100140159Speter for (;;) { 100240159Speter 100340159Speter /* find the end of this component */ 100440159Speter for (ep = cp; (*ep != 0) && (*ep != ';'); ep++) 100540159Speter ; 100640159Speter result = malloc((strlen(name) + (ep - cp) + 1), M_LINKER, M_WAITOK); 100740159Speter if (result == NULL) /* actually ENOMEM */ 100840159Speter return(NULL); 100940159Speter 101040159Speter strncpy(result, cp, ep - cp); 101140159Speter strcpy(result + (ep - cp), name); 101240159Speter 101340159Speter /* 101440159Speter * Attempt to open the file, and return the path if we succeed and it's 101540159Speter * a regular file. 101640159Speter */ 101740159Speter NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, result, p); 101840159Speter error = vn_open(&nd, FREAD, 0); 101940159Speter if (error == 0) { 102040159Speter type = nd.ni_vp->v_type; 102140159Speter VOP_UNLOCK(nd.ni_vp, 0, p); 102240159Speter vn_close(nd.ni_vp, FREAD, p->p_ucred, p); 102340159Speter if (type == VREG) 102440159Speter return(result); 102540159Speter } 102640159Speter free(result, M_LINKER); 102740159Speter 102840159Speter if (*ep == 0) 102940159Speter break; 103040159Speter cp = ep + 1; 103140159Speter } 103240159Speter return(NULL); 103340159Speter} 1034