kern_linker.c revision 46112
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 * 2646112Sphk * $Id: kern_linker.c,v 1.29 1999/04/06 03:02:11 peter 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; 6025537Sdfrstatic linker_file_list_t 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); 6825537Sdfr TAILQ_INIT(&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; 9940159Speter moduledata_t *moddata; 10025537Sdfr 10125537Sdfr KLD_DPF(FILE, ("linker_file_sysinit: calling SYSINITs for %s\n", 10225537Sdfr lf->filename)); 10325537Sdfr 10425537Sdfr sysinits = (struct linker_set*) 10525537Sdfr linker_file_lookup_symbol(lf, "sysinit_set", 0); 10640159Speter 10740159Speter KLD_DPF(FILE, ("linker_file_sysinit: SYSINITs %p\n", sysinits)); 10825537Sdfr if (!sysinits) 10925537Sdfr return; 11025537Sdfr 11140159Speter /* HACK ALERT! */ 11240159Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 11340159Speter if ((*sipp)->func == module_register_init) { 11440159Speter moddata = (*sipp)->udata; 11540159Speter moddata->_file = lf; 11640159Speter } 11740159Speter } 11840159Speter 11925537Sdfr /* 12025537Sdfr * Perform a bubble sort of the system initialization objects by 12125537Sdfr * their subsystem (primary key) and order (secondary key). 12225537Sdfr * 12325537Sdfr * Since some things care about execution order, this is the 12425537Sdfr * operation which ensures continued function. 12525537Sdfr */ 12641055Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 12741055Speter for (xipp = sipp + 1; *xipp; xipp++) { 12841055Speter if ((*sipp)->subsystem <= (*xipp)->subsystem || 12941055Speter ((*sipp)->subsystem == (*xipp)->subsystem && 13041055Speter (*sipp)->order <= (*xipp)->order)) 13125537Sdfr continue; /* skip*/ 13225537Sdfr save = *sipp; 13325537Sdfr *sipp = *xipp; 13425537Sdfr *xipp = save; 13525537Sdfr } 13625537Sdfr } 13725537Sdfr 13825537Sdfr 13925537Sdfr /* 14025537Sdfr * Traverse the (now) ordered list of system initialization tasks. 14125537Sdfr * Perform each task, and continue on to the next task. 14225537Sdfr */ 14341055Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 14441055Speter if ((*sipp)->subsystem == SI_SUB_DUMMY) 14525537Sdfr continue; /* skip dummy task(s)*/ 14625537Sdfr 14741055Speter switch ((*sipp)->type) { 14825537Sdfr case SI_TYPE_DEFAULT: 14925537Sdfr /* no special processing*/ 15041055Speter (*((*sipp)->func))((*sipp)->udata); 15125537Sdfr break; 15225537Sdfr 15325537Sdfr case SI_TYPE_KTHREAD: 15431675Sdyson#if !defined(SMP) 15525537Sdfr /* kernel thread*/ 15631675Sdyson if (fork1(&proc0, RFFDG|RFPROC|RFMEM)) 15731675Sdyson panic("fork kernel thread"); 15831675Sdyson cpu_set_fork_handler(pfind(proc0.p_retval[0]), 15931675Sdyson (*sipp)->func, (*sipp)->udata); 16031675Sdyson break; 16131675Sdyson#endif 16231675Sdyson 16331675Sdyson case SI_TYPE_KPROCESS: 16431675Sdyson /* kernel thread*/ 16531675Sdyson if (fork1(&proc0, RFFDG|RFPROC)) 16625537Sdfr panic("fork kernel process"); 16730994Sphk cpu_set_fork_handler(pfind(proc0.p_retval[0]), 16830994Sphk (*sipp)->func, (*sipp)->udata); 16925537Sdfr break; 17025537Sdfr 17125537Sdfr default: 17241055Speter panic ("linker_file_sysinit: unrecognized init type"); 17325537Sdfr } 17425537Sdfr } 17525537Sdfr} 17625537Sdfr 17741055Speterstatic void 17841055Speterlinker_file_sysuninit(linker_file_t lf) 17941055Speter{ 18041055Speter struct linker_set* sysuninits; 18141055Speter struct sysinit** sipp; 18241055Speter struct sysinit** xipp; 18341055Speter struct sysinit* save; 18441055Speter 18541055Speter KLD_DPF(FILE, ("linker_file_sysuninit: calling SYSUNINITs for %s\n", 18641055Speter lf->filename)); 18741055Speter 18841055Speter sysuninits = (struct linker_set*) 18941055Speter linker_file_lookup_symbol(lf, "sysuninit_set", 0); 19041055Speter 19141055Speter KLD_DPF(FILE, ("linker_file_sysuninit: SYSUNINITs %p\n", sysuninits)); 19241055Speter if (!sysuninits) 19341055Speter return; 19441055Speter 19541055Speter /* 19641055Speter * Perform a reverse bubble sort of the system initialization objects 19741055Speter * by their subsystem (primary key) and order (secondary key). 19841055Speter * 19941055Speter * Since some things care about execution order, this is the 20041055Speter * operation which ensures continued function. 20141055Speter */ 20241055Speter for (sipp = (struct sysinit **)sysuninits->ls_items; *sipp; sipp++) { 20341055Speter for (xipp = sipp + 1; *xipp; xipp++) { 20441055Speter if ((*sipp)->subsystem >= (*xipp)->subsystem || 20541055Speter ((*sipp)->subsystem == (*xipp)->subsystem && 20641055Speter (*sipp)->order >= (*xipp)->order)) 20741055Speter continue; /* skip*/ 20841055Speter save = *sipp; 20941055Speter *sipp = *xipp; 21041055Speter *xipp = save; 21141055Speter } 21241055Speter } 21341055Speter 21441055Speter 21541055Speter /* 21641055Speter * Traverse the (now) ordered list of system initialization tasks. 21741055Speter * Perform each task, and continue on to the next task. 21841055Speter */ 21941055Speter for (sipp = (struct sysinit **)sysuninits->ls_items; *sipp; sipp++) { 22041055Speter if ((*sipp)->subsystem == SI_SUB_DUMMY) 22141055Speter continue; /* skip dummy task(s)*/ 22241055Speter 22341055Speter switch ((*sipp)->type) { 22441055Speter case SI_TYPE_DEFAULT: 22541055Speter /* no special processing*/ 22641055Speter (*((*sipp)->func))((*sipp)->udata); 22741055Speter break; 22841055Speter 22941055Speter default: 23041055Speter panic("linker_file_sysuninit: unrecognized uninit type"); 23141055Speter } 23241055Speter } 23341055Speter} 23441055Speter 23544078Sdfrstatic void 23644078Sdfrlinker_file_register_sysctls(linker_file_t lf) 23744078Sdfr{ 23844078Sdfr struct linker_set* sysctls; 23944078Sdfr 24044078Sdfr KLD_DPF(FILE, ("linker_file_register_sysctls: registering SYSCTLs for %s\n", 24144078Sdfr lf->filename)); 24244078Sdfr 24344078Sdfr sysctls = (struct linker_set*) 24444078Sdfr linker_file_lookup_symbol(lf, "sysctl_set", 0); 24544078Sdfr 24644078Sdfr KLD_DPF(FILE, ("linker_file_register_sysctls: SYSCTLs %p\n", sysctls)); 24744078Sdfr if (!sysctls) 24844078Sdfr return; 24944078Sdfr 25044078Sdfr sysctl_register_set(sysctls); 25144078Sdfr} 25244078Sdfr 25344078Sdfrstatic void 25444078Sdfrlinker_file_unregister_sysctls(linker_file_t lf) 25544078Sdfr{ 25644078Sdfr struct linker_set* sysctls; 25744078Sdfr 25844078Sdfr KLD_DPF(FILE, ("linker_file_unregister_sysctls: registering SYSCTLs for %s\n", 25944078Sdfr lf->filename)); 26044078Sdfr 26144078Sdfr sysctls = (struct linker_set*) 26244078Sdfr linker_file_lookup_symbol(lf, "sysctl_set", 0); 26344078Sdfr 26444078Sdfr KLD_DPF(FILE, ("linker_file_unregister_sysctls: SYSCTLs %p\n", sysctls)); 26544078Sdfr if (!sysctls) 26644078Sdfr return; 26744078Sdfr 26844078Sdfr sysctl_unregister_set(sysctls); 26944078Sdfr} 27044078Sdfr 27125537Sdfrint 27225537Sdfrlinker_load_file(const char* filename, linker_file_t* result) 27325537Sdfr{ 27425537Sdfr linker_class_t lc; 27525537Sdfr linker_file_t lf; 27642755Speter int foundfile, error = 0; 27740861Speter char *koname = NULL; 27825537Sdfr 27925537Sdfr lf = linker_find_file_by_name(filename); 28025537Sdfr if (lf) { 28125537Sdfr KLD_DPF(FILE, ("linker_load_file: file %s is already loaded, incrementing refs\n", filename)); 28225537Sdfr *result = lf; 28325537Sdfr lf->refs++; 28425537Sdfr goto out; 28525537Sdfr } 28625537Sdfr 28740861Speter koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK); 28840861Speter if (koname == NULL) { 28940861Speter error = ENOMEM; 29040861Speter goto out; 29140861Speter } 29240861Speter sprintf(koname, "%s.ko", filename); 29325537Sdfr lf = NULL; 29442755Speter foundfile = 0; 29525537Sdfr for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) { 29625537Sdfr KLD_DPF(FILE, ("linker_load_file: trying to load %s as %s\n", 29725537Sdfr filename, lc->desc)); 29842755Speter 29942755Speter error = lc->ops->load_file(koname, &lf); /* First with .ko */ 30042755Speter if (lf == NULL && error == ENOENT) 30142755Speter error = lc->ops->load_file(filename, &lf); /* Then try without */ 30242755Speter /* 30342755Speter * If we got something other than ENOENT, then it exists but we cannot 30442755Speter * load it for some other reason. 30542755Speter */ 30642755Speter if (error != ENOENT) 30742755Speter foundfile = 1; 30825537Sdfr if (lf) { 30944549Sdfr linker_file_register_sysctls(lf); 31025537Sdfr linker_file_sysinit(lf); 31125537Sdfr 31225537Sdfr *result = lf; 31340861Speter error = 0; 31425537Sdfr goto out; 31525537Sdfr } 31625537Sdfr } 31742755Speter /* 31842755Speter * Less than ideal, but tells the user whether it failed to load or 31942755Speter * the module was not found. 32042755Speter */ 32142755Speter if (foundfile) 32242755Speter error = ENOEXEC; /* Format not recognised (or unloadable) */ 32342755Speter else 32442755Speter error = ENOENT; /* Nothing found */ 32525537Sdfr 32625537Sdfrout: 32740861Speter if (koname) 32840861Speter free(koname, M_LINKER); 32925537Sdfr return error; 33025537Sdfr} 33125537Sdfr 33225537Sdfrlinker_file_t 33325537Sdfrlinker_find_file_by_name(const char* filename) 33425537Sdfr{ 33525537Sdfr linker_file_t lf = 0; 33640861Speter char *koname; 33725537Sdfr 33840861Speter koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK); 33940861Speter if (koname == NULL) 34040861Speter goto out; 34140861Speter sprintf(koname, "%s.ko", filename); 34240861Speter 34325537Sdfr lockmgr(&lock, LK_SHARED, 0, curproc); 34440861Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 34540861Speter if (!strcmp(lf->filename, koname)) 34640861Speter break; 34725537Sdfr if (!strcmp(lf->filename, filename)) 34825537Sdfr break; 34940861Speter } 35025537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 35125537Sdfr 35240861Speterout: 35340861Speter if (koname) 35440861Speter free(koname, M_LINKER); 35525537Sdfr return lf; 35625537Sdfr} 35725537Sdfr 35825537Sdfrlinker_file_t 35925537Sdfrlinker_find_file_by_id(int fileid) 36025537Sdfr{ 36125537Sdfr linker_file_t lf = 0; 36225537Sdfr 36325537Sdfr lockmgr(&lock, LK_SHARED, 0, curproc); 36425537Sdfr for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) 36525537Sdfr if (lf->id == fileid) 36625537Sdfr break; 36725537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 36825537Sdfr 36925537Sdfr return lf; 37025537Sdfr} 37125537Sdfr 37225537Sdfrlinker_file_t 37340159Speterlinker_make_file(const char* pathname, void* priv, struct linker_file_ops* ops) 37425537Sdfr{ 37525537Sdfr linker_file_t lf = 0; 37625537Sdfr int namelen; 37740159Speter const char *filename; 37825537Sdfr 37940159Speter filename = rindex(pathname, '/'); 38040159Speter if (filename && filename[1]) 38140159Speter filename++; 38240159Speter else 38340159Speter filename = pathname; 38440159Speter 38525537Sdfr KLD_DPF(FILE, ("linker_make_file: new file, filename=%s\n", filename)); 38645356Speter lockmgr(&lock, LK_EXCLUSIVE, 0, curproc); 38725537Sdfr namelen = strlen(filename) + 1; 38825537Sdfr lf = malloc(sizeof(struct linker_file) + namelen, M_LINKER, M_WAITOK); 38925537Sdfr if (!lf) 39025537Sdfr goto out; 39140395Speter bzero(lf, sizeof(*lf)); 39225537Sdfr 39325537Sdfr lf->refs = 1; 39425537Sdfr lf->userrefs = 0; 39543185Sdfr lf->flags = 0; 39625537Sdfr lf->filename = (char*) (lf + 1); 39725537Sdfr strcpy(lf->filename, filename); 39825537Sdfr lf->id = next_file_id++; 39925537Sdfr lf->ndeps = 0; 40025537Sdfr lf->deps = NULL; 40125537Sdfr STAILQ_INIT(&lf->common); 40225537Sdfr TAILQ_INIT(&lf->modules); 40325537Sdfr 40425537Sdfr lf->priv = priv; 40525537Sdfr lf->ops = ops; 40625537Sdfr TAILQ_INSERT_TAIL(&files, lf, link); 40725537Sdfr 40825537Sdfrout: 40925537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 41025537Sdfr return lf; 41125537Sdfr} 41225537Sdfr 41325537Sdfrint 41425537Sdfrlinker_file_unload(linker_file_t file) 41525537Sdfr{ 41625537Sdfr module_t mod, next; 41725537Sdfr struct common_symbol* cp; 41825537Sdfr int error = 0; 41925537Sdfr int i; 42025537Sdfr 42140159Speter KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", file->refs)); 42245356Speter lockmgr(&lock, LK_EXCLUSIVE, 0, curproc); 42325537Sdfr if (file->refs == 1) { 42425537Sdfr KLD_DPF(FILE, ("linker_file_unload: file is unloading, informing modules\n")); 42525537Sdfr /* 42625537Sdfr * Inform any modules associated with this file. 42725537Sdfr */ 42825537Sdfr for (mod = TAILQ_FIRST(&file->modules); mod; mod = next) { 42925537Sdfr next = module_getfnext(mod); 43025537Sdfr 43125537Sdfr /* 43225537Sdfr * Give the module a chance to veto the unload. 43325537Sdfr */ 43443301Sdillon if ((error = module_unload(mod)) != 0) { 43525537Sdfr KLD_DPF(FILE, ("linker_file_unload: module %x vetoes unload\n", 43625537Sdfr mod)); 43725537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 43825537Sdfr goto out; 43925537Sdfr } 44025537Sdfr 44125537Sdfr module_release(mod); 44225537Sdfr } 44325537Sdfr } 44425537Sdfr 44525537Sdfr file->refs--; 44625537Sdfr if (file->refs > 0) { 44725537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 44825537Sdfr goto out; 44925537Sdfr } 45025537Sdfr 45143185Sdfr /* Don't try to run SYSUNINITs if we are unloaded due to a link error */ 45244078Sdfr if (file->flags & LINKER_FILE_LINKED) { 45343185Sdfr linker_file_sysuninit(file); 45444078Sdfr linker_file_unregister_sysctls(file); 45544078Sdfr } 45641055Speter 45725537Sdfr TAILQ_REMOVE(&files, file, link); 45825537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 45940159Speter 46025537Sdfr for (i = 0; i < file->ndeps; i++) 46125537Sdfr linker_file_unload(file->deps[i]); 46225537Sdfr free(file->deps, M_LINKER); 46325537Sdfr 46425537Sdfr for (cp = STAILQ_FIRST(&file->common); cp; 46525537Sdfr cp = STAILQ_FIRST(&file->common)) { 46625537Sdfr STAILQ_REMOVE(&file->common, cp, common_symbol, link); 46725537Sdfr free(cp, M_LINKER); 46825537Sdfr } 46925537Sdfr 47025537Sdfr file->ops->unload(file); 47125537Sdfr free(file, M_LINKER); 47225537Sdfr 47325537Sdfrout: 47425537Sdfr return error; 47525537Sdfr} 47625537Sdfr 47725537Sdfrint 47825537Sdfrlinker_file_add_dependancy(linker_file_t file, linker_file_t dep) 47925537Sdfr{ 48025537Sdfr linker_file_t* newdeps; 48125537Sdfr 48225537Sdfr newdeps = malloc((file->ndeps + 1) * sizeof(linker_file_t*), 48325537Sdfr M_LINKER, M_WAITOK); 48425537Sdfr if (newdeps == NULL) 48525537Sdfr return ENOMEM; 48640395Speter bzero(newdeps, (file->ndeps + 1) * sizeof(linker_file_t*)); 48725537Sdfr 48825537Sdfr if (file->deps) { 48925537Sdfr bcopy(file->deps, newdeps, file->ndeps * sizeof(linker_file_t*)); 49025537Sdfr free(file->deps, M_LINKER); 49125537Sdfr } 49225537Sdfr file->deps = newdeps; 49325537Sdfr file->deps[file->ndeps] = dep; 49425537Sdfr file->ndeps++; 49525537Sdfr 49625537Sdfr return 0; 49725537Sdfr} 49825537Sdfr 49925537Sdfrcaddr_t 50025537Sdfrlinker_file_lookup_symbol(linker_file_t file, const char* name, int deps) 50125537Sdfr{ 50243309Sdillon c_linker_sym_t sym; 50338275Sdfr linker_symval_t symval; 50442849Speter linker_file_t lf; 50525537Sdfr caddr_t address; 50625537Sdfr size_t common_size = 0; 50725537Sdfr int i; 50825537Sdfr 50940159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d\n", 51025537Sdfr file, name, deps)); 51125537Sdfr 51238275Sdfr if (file->ops->lookup_symbol(file, name, &sym) == 0) { 51338275Sdfr file->ops->symbol_values(file, sym, &symval); 51438275Sdfr if (symval.value == 0) 51525537Sdfr /* 51625537Sdfr * For commons, first look them up in the dependancies and 51725537Sdfr * only allocate space if not found there. 51825537Sdfr */ 51938275Sdfr common_size = symval.size; 52040159Speter else { 52140159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: symbol.value=%x\n", symval.value)); 52238275Sdfr return symval.value; 52340159Speter } 52438275Sdfr } 52525537Sdfr 52642849Speter if (deps) { 52725537Sdfr for (i = 0; i < file->ndeps; i++) { 52825537Sdfr address = linker_file_lookup_symbol(file->deps[i], name, 0); 52940159Speter if (address) { 53040159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: deps value=%x\n", address)); 53125537Sdfr return address; 53240159Speter } 53325537Sdfr } 53425537Sdfr 53542849Speter /* If we have not found it in the dependencies, search globally */ 53642849Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 53742849Speter /* But skip the current file if it's on the list */ 53842849Speter if (lf == file) 53942849Speter continue; 54042849Speter /* And skip the files we searched above */ 54142849Speter for (i = 0; i < file->ndeps; i++) 54242849Speter if (lf == file->deps[i]) 54342849Speter break; 54442849Speter if (i < file->ndeps) 54542849Speter continue; 54642849Speter address = linker_file_lookup_symbol(lf, name, 0); 54742849Speter if (address) { 54842849Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: global value=%x\n", address)); 54942849Speter return address; 55042849Speter } 55142849Speter } 55242849Speter } 55342849Speter 55425537Sdfr if (common_size > 0) { 55525537Sdfr /* 55625537Sdfr * This is a common symbol which was not found in the 55725537Sdfr * dependancies. We maintain a simple common symbol table in 55825537Sdfr * the file object. 55925537Sdfr */ 56025537Sdfr struct common_symbol* cp; 56125537Sdfr 56225537Sdfr for (cp = STAILQ_FIRST(&file->common); cp; 56325537Sdfr cp = STAILQ_NEXT(cp, link)) 56440159Speter if (!strcmp(cp->name, name)) { 56540159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: old common value=%x\n", cp->address)); 56625537Sdfr return cp->address; 56740159Speter } 56825537Sdfr 56925537Sdfr /* 57025537Sdfr * Round the symbol size up to align. 57125537Sdfr */ 57225537Sdfr common_size = (common_size + sizeof(int) - 1) & -sizeof(int); 57325537Sdfr cp = malloc(sizeof(struct common_symbol) 57425537Sdfr + common_size 57525537Sdfr + strlen(name) + 1, 57625537Sdfr M_LINKER, M_WAITOK); 57740159Speter if (!cp) { 57840159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: nomem\n")); 57925537Sdfr return 0; 58040159Speter } 58140395Speter bzero(cp, sizeof(struct common_symbol) + common_size + strlen(name)+ 1); 58225537Sdfr 58325537Sdfr cp->address = (caddr_t) (cp + 1); 58425537Sdfr cp->name = cp->address + common_size; 58525537Sdfr strcpy(cp->name, name); 58625537Sdfr bzero(cp->address, common_size); 58725537Sdfr STAILQ_INSERT_TAIL(&file->common, cp, link); 58825537Sdfr 58940159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: new common value=%x\n", cp->address)); 59025537Sdfr return cp->address; 59125537Sdfr } 59225537Sdfr 59340159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: fail\n")); 59425537Sdfr return 0; 59525537Sdfr} 59625537Sdfr 59740159Speter#ifdef DDB 59825537Sdfr/* 59940159Speter * DDB Helpers. DDB has to look across multiple files with their own 60040159Speter * symbol tables and string tables. 60140159Speter * 60240159Speter * Note that we do not obey list locking protocols here. We really don't 60340159Speter * need DDB to hang because somebody's got the lock held. We'll take the 60440159Speter * chance that the files list is inconsistant instead. 60540159Speter */ 60640159Speter 60740159Speterint 60843309Sdillonlinker_ddb_lookup(const char *symstr, c_linker_sym_t *sym) 60940159Speter{ 61040159Speter linker_file_t lf; 61140159Speter 61240159Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 61340159Speter if (lf->ops->lookup_symbol(lf, symstr, sym) == 0) 61440159Speter return 0; 61540159Speter } 61640159Speter return ENOENT; 61740159Speter} 61840159Speter 61940159Speterint 62043309Sdillonlinker_ddb_search_symbol(caddr_t value, c_linker_sym_t *sym, long *diffp) 62140159Speter{ 62240159Speter linker_file_t lf; 62340159Speter u_long off = (u_long)value; 62440159Speter u_long diff, bestdiff; 62543309Sdillon c_linker_sym_t best; 62643309Sdillon c_linker_sym_t es; 62740159Speter 62840159Speter best = 0; 62940159Speter bestdiff = off; 63040159Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 63140159Speter if (lf->ops->search_symbol(lf, value, &es, &diff) != 0) 63240159Speter continue; 63340159Speter if (es != 0 && diff < bestdiff) { 63440159Speter best = es; 63540159Speter bestdiff = diff; 63640159Speter } 63740159Speter if (bestdiff == 0) 63840159Speter break; 63940159Speter } 64040159Speter if (best) { 64140159Speter *sym = best; 64240159Speter *diffp = bestdiff; 64340159Speter return 0; 64440159Speter } else { 64540159Speter *sym = 0; 64640159Speter *diffp = off; 64740159Speter return ENOENT; 64840159Speter } 64940159Speter} 65040159Speter 65140159Speterint 65243309Sdillonlinker_ddb_symbol_values(c_linker_sym_t sym, linker_symval_t *symval) 65340159Speter{ 65440159Speter linker_file_t lf; 65540159Speter 65640159Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 65740159Speter if (lf->ops->symbol_values(lf, sym, symval) == 0) 65840159Speter return 0; 65940159Speter } 66040159Speter return ENOENT; 66140159Speter} 66240159Speter 66340159Speter#endif 66440159Speter 66540159Speter/* 66625537Sdfr * Syscalls. 66725537Sdfr */ 66825537Sdfr 66925537Sdfrint 67030994Sphkkldload(struct proc* p, struct kldload_args* uap) 67125537Sdfr{ 67242316Smsmith char* filename = NULL, *modulename; 67325537Sdfr linker_file_t lf; 67425537Sdfr int error = 0; 67525537Sdfr 67630994Sphk p->p_retval[0] = -1; 67725537Sdfr 67825537Sdfr if (securelevel > 0) 67925537Sdfr return EPERM; 68025537Sdfr 68146112Sphk if ((error = suser(p)) != 0) 68225537Sdfr return error; 68325537Sdfr 68425537Sdfr filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 68543301Sdillon if ((error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) != 0) 68625537Sdfr goto out; 68725537Sdfr 68842316Smsmith /* Can't load more than one module with the same name */ 68942316Smsmith modulename = rindex(filename, '/'); 69042316Smsmith if (modulename == NULL) 69142316Smsmith modulename = filename; 69244173Sdfr else 69344173Sdfr modulename++; 69442316Smsmith if (linker_find_file_by_name(modulename)) { 69542316Smsmith error = EEXIST; 69642316Smsmith goto out; 69742316Smsmith } 69842316Smsmith 69943301Sdillon if ((error = linker_load_file(filename, &lf)) != 0) 70025537Sdfr goto out; 70125537Sdfr 70225537Sdfr lf->userrefs++; 70330994Sphk p->p_retval[0] = lf->id; 70440159Speter 70525537Sdfrout: 70625537Sdfr if (filename) 70725537Sdfr free(filename, M_TEMP); 70825537Sdfr return error; 70925537Sdfr} 71025537Sdfr 71125537Sdfrint 71230994Sphkkldunload(struct proc* p, struct kldunload_args* uap) 71325537Sdfr{ 71425537Sdfr linker_file_t lf; 71525537Sdfr int error = 0; 71625537Sdfr 71725537Sdfr if (securelevel > 0) 71825537Sdfr return EPERM; 71925537Sdfr 72046112Sphk if ((error = suser(p)) != 0) 72125537Sdfr return error; 72225537Sdfr 72325537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 72425537Sdfr if (lf) { 72525537Sdfr KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs)); 72625537Sdfr if (lf->userrefs == 0) { 72743084Speter printf("linkerunload: attempt to unload file that was loaded by the kernel\n"); 72825537Sdfr error = EBUSY; 72925537Sdfr goto out; 73025537Sdfr } 73143084Speter lf->userrefs--; 73242837Speter error = linker_file_unload(lf); 73342837Speter if (error) 73443084Speter lf->userrefs++; 73525537Sdfr } else 73625537Sdfr error = ENOENT; 73725537Sdfr 73825537Sdfrout: 73925537Sdfr return error; 74025537Sdfr} 74125537Sdfr 74225537Sdfrint 74330994Sphkkldfind(struct proc* p, struct kldfind_args* uap) 74425537Sdfr{ 74542316Smsmith char* filename = NULL, *modulename; 74625537Sdfr linker_file_t lf; 74725537Sdfr int error = 0; 74825537Sdfr 74930994Sphk p->p_retval[0] = -1; 75025537Sdfr 75125537Sdfr filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 75243301Sdillon if ((error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) != 0) 75325537Sdfr goto out; 75425537Sdfr 75542316Smsmith modulename = rindex(filename, '/'); 75642316Smsmith if (modulename == NULL) 75742316Smsmith modulename = filename; 75842316Smsmith 75942316Smsmith lf = linker_find_file_by_name(modulename); 76025537Sdfr if (lf) 76130994Sphk p->p_retval[0] = lf->id; 76225537Sdfr else 76325537Sdfr error = ENOENT; 76440159Speter 76525537Sdfrout: 76625537Sdfr if (filename) 76725537Sdfr free(filename, M_TEMP); 76825537Sdfr return error; 76925537Sdfr} 77025537Sdfr 77125537Sdfrint 77230994Sphkkldnext(struct proc* p, struct kldnext_args* uap) 77325537Sdfr{ 77425537Sdfr linker_file_t lf; 77525537Sdfr int error = 0; 77625537Sdfr 77725537Sdfr if (SCARG(uap, fileid) == 0) { 77825537Sdfr if (TAILQ_FIRST(&files)) 77930994Sphk p->p_retval[0] = TAILQ_FIRST(&files)->id; 78025537Sdfr else 78130994Sphk p->p_retval[0] = 0; 78225537Sdfr return 0; 78325537Sdfr } 78440159Speter 78525537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 78625537Sdfr if (lf) { 78725537Sdfr if (TAILQ_NEXT(lf, link)) 78830994Sphk p->p_retval[0] = TAILQ_NEXT(lf, link)->id; 78925537Sdfr else 79030994Sphk p->p_retval[0] = 0; 79125537Sdfr } else 79225537Sdfr error = ENOENT; 79325537Sdfr 79425537Sdfr return error; 79525537Sdfr} 79625537Sdfr 79725537Sdfrint 79830994Sphkkldstat(struct proc* p, struct kldstat_args* uap) 79925537Sdfr{ 80025537Sdfr linker_file_t lf; 80125537Sdfr int error = 0; 80225537Sdfr int version; 80325537Sdfr struct kld_file_stat* stat; 80425537Sdfr int namelen; 80525537Sdfr 80625537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 80725537Sdfr if (!lf) { 80825537Sdfr error = ENOENT; 80925537Sdfr goto out; 81025537Sdfr } 81125537Sdfr 81225537Sdfr stat = SCARG(uap, stat); 81325537Sdfr 81425537Sdfr /* 81525537Sdfr * Check the version of the user's structure. 81625537Sdfr */ 81743301Sdillon if ((error = copyin(&stat->version, &version, sizeof(version))) != 0) 81825537Sdfr goto out; 81925537Sdfr if (version != sizeof(struct kld_file_stat)) { 82025537Sdfr error = EINVAL; 82125537Sdfr goto out; 82225537Sdfr } 82325537Sdfr 82425537Sdfr namelen = strlen(lf->filename) + 1; 82525537Sdfr if (namelen > MAXPATHLEN) 82625537Sdfr namelen = MAXPATHLEN; 82743301Sdillon if ((error = copyout(lf->filename, &stat->name[0], namelen)) != 0) 82825537Sdfr goto out; 82943301Sdillon if ((error = copyout(&lf->refs, &stat->refs, sizeof(int))) != 0) 83025537Sdfr goto out; 83143301Sdillon if ((error = copyout(&lf->id, &stat->id, sizeof(int))) != 0) 83225537Sdfr goto out; 83343301Sdillon if ((error = copyout(&lf->address, &stat->address, sizeof(caddr_t))) != 0) 83425537Sdfr goto out; 83543301Sdillon if ((error = copyout(&lf->size, &stat->size, sizeof(size_t))) != 0) 83625537Sdfr goto out; 83725537Sdfr 83830994Sphk p->p_retval[0] = 0; 83925537Sdfr 84025537Sdfrout: 84125537Sdfr return error; 84225537Sdfr} 84325537Sdfr 84425537Sdfrint 84530994Sphkkldfirstmod(struct proc* p, struct kldfirstmod_args* uap) 84625537Sdfr{ 84725537Sdfr linker_file_t lf; 84825537Sdfr int error = 0; 84925537Sdfr 85025537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 85125537Sdfr if (lf) { 85225537Sdfr if (TAILQ_FIRST(&lf->modules)) 85330994Sphk p->p_retval[0] = module_getid(TAILQ_FIRST(&lf->modules)); 85425537Sdfr else 85530994Sphk p->p_retval[0] = 0; 85625537Sdfr } else 85725537Sdfr error = ENOENT; 85825537Sdfr 85925537Sdfr return error; 86025537Sdfr} 86140159Speter 86241090Speterint 86341090Speterkldsym(struct proc *p, struct kldsym_args *uap) 86441090Speter{ 86541090Speter char *symstr = NULL; 86643309Sdillon c_linker_sym_t sym; 86741090Speter linker_symval_t symval; 86841090Speter linker_file_t lf; 86941090Speter struct kld_sym_lookup lookup; 87041090Speter int error = 0; 87141090Speter 87243301Sdillon if ((error = copyin(SCARG(uap, data), &lookup, sizeof(lookup))) != 0) 87341090Speter goto out; 87441090Speter if (lookup.version != sizeof(lookup) || SCARG(uap, cmd) != KLDSYM_LOOKUP) { 87541090Speter error = EINVAL; 87641090Speter goto out; 87741090Speter } 87841090Speter 87941090Speter symstr = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 88043301Sdillon if ((error = copyinstr(lookup.symname, symstr, MAXPATHLEN, NULL)) != 0) 88141090Speter goto out; 88241090Speter 88341090Speter if (SCARG(uap, fileid) != 0) { 88441090Speter lf = linker_find_file_by_id(SCARG(uap, fileid)); 88541090Speter if (lf == NULL) { 88641090Speter error = ENOENT; 88741090Speter goto out; 88841090Speter } 88941090Speter if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 && 89041090Speter lf->ops->symbol_values(lf, sym, &symval) == 0) { 89141090Speter lookup.symvalue = (u_long)symval.value; 89241090Speter lookup.symsize = symval.size; 89341090Speter error = copyout(&lookup, SCARG(uap, data), sizeof(lookup)); 89441090Speter } else 89541090Speter error = ENOENT; 89641090Speter } else { 89741090Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 89841090Speter if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 && 89941090Speter lf->ops->symbol_values(lf, sym, &symval) == 0) { 90041090Speter lookup.symvalue = (u_long)symval.value; 90141090Speter lookup.symsize = symval.size; 90241090Speter error = copyout(&lookup, SCARG(uap, data), sizeof(lookup)); 90341090Speter break; 90441090Speter } 90541090Speter } 90641090Speter if (!lf) 90741090Speter error = ENOENT; 90841090Speter } 90941090Speterout: 91041090Speter if (symstr) 91141090Speter free(symstr, M_TEMP); 91241090Speter return error; 91341090Speter} 91441090Speter 91540159Speter/* 91640159Speter * Preloaded module support 91740159Speter */ 91840159Speter 91940159Speterstatic void 92040159Speterlinker_preload(void* arg) 92140159Speter{ 92240159Speter caddr_t modptr; 92340159Speter char *modname; 92440162Speter char *modtype; 92540159Speter linker_file_t lf; 92640159Speter linker_class_t lc; 92740159Speter int error; 92840159Speter struct linker_set *sysinits; 92940159Speter struct sysinit **sipp; 93040159Speter moduledata_t *moddata; 93140159Speter 93240159Speter modptr = NULL; 93340159Speter while ((modptr = preload_search_next_name(modptr)) != NULL) { 93440159Speter modname = (char *)preload_search_info(modptr, MODINFO_NAME); 93540162Speter modtype = (char *)preload_search_info(modptr, MODINFO_TYPE); 93640159Speter if (modname == NULL) { 93740625Smsmith printf("Preloaded module at %p does not have a name!\n", modptr); 93840159Speter continue; 93940159Speter } 94040162Speter if (modtype == NULL) { 94140625Smsmith printf("Preloaded module at %p does not have a type!\n", modptr); 94240162Speter continue; 94340162Speter } 94440625Smsmith printf("Preloaded %s \"%s\" at %p.\n", modtype, modname, modptr); 94540159Speter lf = linker_find_file_by_name(modname); 94640159Speter if (lf) { 94740159Speter lf->userrefs++; 94840159Speter continue; 94940159Speter } 95040159Speter lf = NULL; 95140159Speter for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) { 95240159Speter error = lc->ops->load_file(modname, &lf); 95340159Speter if (error) { 95440159Speter lf = NULL; 95540159Speter break; 95640159Speter } 95740159Speter } 95840159Speter if (lf) { 95940159Speter lf->userrefs++; 96040159Speter 96140159Speter sysinits = (struct linker_set*) 96240159Speter linker_file_lookup_symbol(lf, "sysinit_set", 0); 96340159Speter if (sysinits) { 96440159Speter /* HACK ALERT! 96540159Speter * This is to set the sysinit moduledata so that the module 96640159Speter * can attach itself to the correct containing file. 96740159Speter * The sysinit could be run at *any* time. 96840159Speter */ 96940159Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 97040159Speter if ((*sipp)->func == module_register_init) { 97140159Speter moddata = (*sipp)->udata; 97240159Speter moddata->_file = lf; 97340159Speter } 97440159Speter } 97540159Speter sysinit_add((struct sysinit **)sysinits->ls_items); 97640159Speter } 97744078Sdfr linker_file_register_sysctls(lf); 97840159Speter } 97940159Speter } 98040159Speter} 98140159Speter 98240159SpeterSYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, 0); 98340159Speter 98440159Speter/* 98540159Speter * Search for a not-loaded module by name. 98640159Speter * 98740159Speter * Modules may be found in the following locations: 98840159Speter * 98940159Speter * - preloaded (result is just the module name) 99040159Speter * - on disk (result is full path to module) 99140159Speter * 99240159Speter * If the module name is qualified in any way (contains path, etc.) 99340159Speter * the we simply return a copy of it. 99440159Speter * 99540159Speter * The search path can be manipulated via sysctl. Note that we use the ';' 99640159Speter * character as a separator to be consistent with the bootloader. 99740159Speter */ 99840159Speter 99940159Speterstatic char linker_path[MAXPATHLEN + 1] = "/;/boot/;/modules/"; 100040159Speter 100140159SpeterSYSCTL_STRING(_kern, OID_AUTO, module_path, CTLFLAG_RW, linker_path, 100240159Speter sizeof(linker_path), "module load search path"); 100340159Speter 100440159Speterstatic char * 100540159Speterlinker_strdup(const char *str) 100640159Speter{ 100740159Speter char *result; 100840159Speter 100940159Speter if ((result = malloc((strlen(str) + 1), M_LINKER, M_WAITOK)) != NULL) 101040159Speter strcpy(result, str); 101140159Speter return(result); 101240159Speter} 101340159Speter 101440159Speterchar * 101540159Speterlinker_search_path(const char *name) 101640159Speter{ 101740159Speter struct nameidata nd; 101840159Speter struct proc *p = curproc; /* XXX */ 101940159Speter char *cp, *ep, *result; 102040159Speter int error; 102140159Speter enum vtype type; 102240159Speter 102340159Speter /* qualified at all? */ 102440159Speter if (index(name, '/')) 102540159Speter return(linker_strdup(name)); 102640159Speter 102740159Speter /* traverse the linker path */ 102840159Speter cp = linker_path; 102940159Speter for (;;) { 103040159Speter 103140159Speter /* find the end of this component */ 103240159Speter for (ep = cp; (*ep != 0) && (*ep != ';'); ep++) 103340159Speter ; 103440159Speter result = malloc((strlen(name) + (ep - cp) + 1), M_LINKER, M_WAITOK); 103540159Speter if (result == NULL) /* actually ENOMEM */ 103640159Speter return(NULL); 103740159Speter 103840159Speter strncpy(result, cp, ep - cp); 103940159Speter strcpy(result + (ep - cp), name); 104040159Speter 104140159Speter /* 104240159Speter * Attempt to open the file, and return the path if we succeed and it's 104340159Speter * a regular file. 104440159Speter */ 104540159Speter NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, result, p); 104640159Speter error = vn_open(&nd, FREAD, 0); 104740159Speter if (error == 0) { 104840159Speter type = nd.ni_vp->v_type; 104940159Speter VOP_UNLOCK(nd.ni_vp, 0, p); 105040159Speter vn_close(nd.ni_vp, FREAD, p->p_ucred, p); 105140159Speter if (type == VREG) 105240159Speter return(result); 105340159Speter } 105440159Speter free(result, M_LINKER); 105540159Speter 105640159Speter if (*ep == 0) 105740159Speter break; 105840159Speter cp = ep + 1; 105940159Speter } 106040159Speter return(NULL); 106140159Speter} 1062