kern_linker.c revision 43309
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 * 2643309Sdillon * $Id: kern_linker.c,v 1.24 1999/01/27 21:49:56 dillon 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 23525537Sdfrint 23625537Sdfrlinker_load_file(const char* filename, linker_file_t* result) 23725537Sdfr{ 23825537Sdfr linker_class_t lc; 23925537Sdfr linker_file_t lf; 24042755Speter int foundfile, error = 0; 24140861Speter char *koname = NULL; 24225537Sdfr 24325537Sdfr lf = linker_find_file_by_name(filename); 24425537Sdfr if (lf) { 24525537Sdfr KLD_DPF(FILE, ("linker_load_file: file %s is already loaded, incrementing refs\n", filename)); 24625537Sdfr *result = lf; 24725537Sdfr lf->refs++; 24825537Sdfr goto out; 24925537Sdfr } 25025537Sdfr 25140861Speter koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK); 25240861Speter if (koname == NULL) { 25340861Speter error = ENOMEM; 25440861Speter goto out; 25540861Speter } 25640861Speter sprintf(koname, "%s.ko", filename); 25725537Sdfr lf = NULL; 25842755Speter foundfile = 0; 25925537Sdfr for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) { 26025537Sdfr KLD_DPF(FILE, ("linker_load_file: trying to load %s as %s\n", 26125537Sdfr filename, lc->desc)); 26242755Speter 26342755Speter error = lc->ops->load_file(koname, &lf); /* First with .ko */ 26442755Speter if (lf == NULL && error == ENOENT) 26542755Speter error = lc->ops->load_file(filename, &lf); /* Then try without */ 26642755Speter /* 26742755Speter * If we got something other than ENOENT, then it exists but we cannot 26842755Speter * load it for some other reason. 26942755Speter */ 27042755Speter if (error != ENOENT) 27142755Speter foundfile = 1; 27225537Sdfr if (lf) { 27325537Sdfr linker_file_sysinit(lf); 27425537Sdfr 27525537Sdfr *result = lf; 27640861Speter error = 0; 27725537Sdfr goto out; 27825537Sdfr } 27925537Sdfr } 28042755Speter /* 28142755Speter * Less than ideal, but tells the user whether it failed to load or 28242755Speter * the module was not found. 28342755Speter */ 28442755Speter if (foundfile) 28542755Speter error = ENOEXEC; /* Format not recognised (or unloadable) */ 28642755Speter else 28742755Speter error = ENOENT; /* Nothing found */ 28825537Sdfr 28925537Sdfrout: 29040861Speter if (koname) 29140861Speter free(koname, M_LINKER); 29225537Sdfr return error; 29325537Sdfr} 29425537Sdfr 29525537Sdfrlinker_file_t 29625537Sdfrlinker_find_file_by_name(const char* filename) 29725537Sdfr{ 29825537Sdfr linker_file_t lf = 0; 29940861Speter char *koname; 30025537Sdfr 30140861Speter koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK); 30240861Speter if (koname == NULL) 30340861Speter goto out; 30440861Speter sprintf(koname, "%s.ko", filename); 30540861Speter 30625537Sdfr lockmgr(&lock, LK_SHARED, 0, curproc); 30740861Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 30840861Speter if (!strcmp(lf->filename, koname)) 30940861Speter break; 31025537Sdfr if (!strcmp(lf->filename, filename)) 31125537Sdfr break; 31240861Speter } 31325537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 31425537Sdfr 31540861Speterout: 31640861Speter if (koname) 31740861Speter free(koname, M_LINKER); 31825537Sdfr return lf; 31925537Sdfr} 32025537Sdfr 32125537Sdfrlinker_file_t 32225537Sdfrlinker_find_file_by_id(int fileid) 32325537Sdfr{ 32425537Sdfr linker_file_t lf = 0; 32525537Sdfr 32625537Sdfr lockmgr(&lock, LK_SHARED, 0, curproc); 32725537Sdfr for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) 32825537Sdfr if (lf->id == fileid) 32925537Sdfr break; 33025537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 33125537Sdfr 33225537Sdfr return lf; 33325537Sdfr} 33425537Sdfr 33525537Sdfrlinker_file_t 33640159Speterlinker_make_file(const char* pathname, void* priv, struct linker_file_ops* ops) 33725537Sdfr{ 33825537Sdfr linker_file_t lf = 0; 33925537Sdfr int namelen; 34040159Speter const char *filename; 34125537Sdfr 34240159Speter filename = rindex(pathname, '/'); 34340159Speter if (filename && filename[1]) 34440159Speter filename++; 34540159Speter else 34640159Speter filename = pathname; 34740159Speter 34825537Sdfr KLD_DPF(FILE, ("linker_make_file: new file, filename=%s\n", filename)); 34925537Sdfr lockmgr(&lock, LK_EXCLUSIVE|LK_RETRY, 0, curproc); 35025537Sdfr namelen = strlen(filename) + 1; 35125537Sdfr lf = malloc(sizeof(struct linker_file) + namelen, M_LINKER, M_WAITOK); 35225537Sdfr if (!lf) 35325537Sdfr goto out; 35440395Speter bzero(lf, sizeof(*lf)); 35525537Sdfr 35625537Sdfr lf->refs = 1; 35725537Sdfr lf->userrefs = 0; 35843185Sdfr lf->flags = 0; 35925537Sdfr lf->filename = (char*) (lf + 1); 36025537Sdfr strcpy(lf->filename, filename); 36125537Sdfr lf->id = next_file_id++; 36225537Sdfr lf->ndeps = 0; 36325537Sdfr lf->deps = NULL; 36425537Sdfr STAILQ_INIT(&lf->common); 36525537Sdfr TAILQ_INIT(&lf->modules); 36625537Sdfr 36725537Sdfr lf->priv = priv; 36825537Sdfr lf->ops = ops; 36925537Sdfr TAILQ_INSERT_TAIL(&files, lf, link); 37025537Sdfr 37125537Sdfrout: 37225537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 37325537Sdfr return lf; 37425537Sdfr} 37525537Sdfr 37625537Sdfrint 37725537Sdfrlinker_file_unload(linker_file_t file) 37825537Sdfr{ 37925537Sdfr module_t mod, next; 38025537Sdfr struct common_symbol* cp; 38125537Sdfr int error = 0; 38225537Sdfr int i; 38325537Sdfr 38440159Speter KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", file->refs)); 38525537Sdfr lockmgr(&lock, LK_EXCLUSIVE|LK_RETRY, 0, curproc); 38625537Sdfr if (file->refs == 1) { 38725537Sdfr KLD_DPF(FILE, ("linker_file_unload: file is unloading, informing modules\n")); 38825537Sdfr /* 38925537Sdfr * Inform any modules associated with this file. 39025537Sdfr */ 39125537Sdfr for (mod = TAILQ_FIRST(&file->modules); mod; mod = next) { 39225537Sdfr next = module_getfnext(mod); 39325537Sdfr 39425537Sdfr /* 39525537Sdfr * Give the module a chance to veto the unload. 39625537Sdfr */ 39743301Sdillon if ((error = module_unload(mod)) != 0) { 39825537Sdfr KLD_DPF(FILE, ("linker_file_unload: module %x vetoes unload\n", 39925537Sdfr mod)); 40025537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 40125537Sdfr goto out; 40225537Sdfr } 40325537Sdfr 40425537Sdfr module_release(mod); 40525537Sdfr } 40625537Sdfr } 40725537Sdfr 40825537Sdfr file->refs--; 40925537Sdfr if (file->refs > 0) { 41025537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 41125537Sdfr goto out; 41225537Sdfr } 41325537Sdfr 41443185Sdfr /* Don't try to run SYSUNINITs if we are unloaded due to a link error */ 41543185Sdfr if (file->flags & LINKER_FILE_LINKED) 41643185Sdfr linker_file_sysuninit(file); 41741055Speter 41825537Sdfr TAILQ_REMOVE(&files, file, link); 41925537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 42040159Speter 42125537Sdfr for (i = 0; i < file->ndeps; i++) 42225537Sdfr linker_file_unload(file->deps[i]); 42325537Sdfr free(file->deps, M_LINKER); 42425537Sdfr 42525537Sdfr for (cp = STAILQ_FIRST(&file->common); cp; 42625537Sdfr cp = STAILQ_FIRST(&file->common)) { 42725537Sdfr STAILQ_REMOVE(&file->common, cp, common_symbol, link); 42825537Sdfr free(cp, M_LINKER); 42925537Sdfr } 43025537Sdfr 43125537Sdfr file->ops->unload(file); 43225537Sdfr free(file, M_LINKER); 43325537Sdfr 43425537Sdfrout: 43525537Sdfr return error; 43625537Sdfr} 43725537Sdfr 43825537Sdfrint 43925537Sdfrlinker_file_add_dependancy(linker_file_t file, linker_file_t dep) 44025537Sdfr{ 44125537Sdfr linker_file_t* newdeps; 44225537Sdfr 44325537Sdfr newdeps = malloc((file->ndeps + 1) * sizeof(linker_file_t*), 44425537Sdfr M_LINKER, M_WAITOK); 44525537Sdfr if (newdeps == NULL) 44625537Sdfr return ENOMEM; 44740395Speter bzero(newdeps, (file->ndeps + 1) * sizeof(linker_file_t*)); 44825537Sdfr 44925537Sdfr if (file->deps) { 45025537Sdfr bcopy(file->deps, newdeps, file->ndeps * sizeof(linker_file_t*)); 45125537Sdfr free(file->deps, M_LINKER); 45225537Sdfr } 45325537Sdfr file->deps = newdeps; 45425537Sdfr file->deps[file->ndeps] = dep; 45525537Sdfr file->ndeps++; 45625537Sdfr 45725537Sdfr return 0; 45825537Sdfr} 45925537Sdfr 46025537Sdfrcaddr_t 46125537Sdfrlinker_file_lookup_symbol(linker_file_t file, const char* name, int deps) 46225537Sdfr{ 46343309Sdillon c_linker_sym_t sym; 46438275Sdfr linker_symval_t symval; 46542849Speter linker_file_t lf; 46625537Sdfr caddr_t address; 46725537Sdfr size_t common_size = 0; 46825537Sdfr int i; 46925537Sdfr 47040159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d\n", 47125537Sdfr file, name, deps)); 47225537Sdfr 47338275Sdfr if (file->ops->lookup_symbol(file, name, &sym) == 0) { 47438275Sdfr file->ops->symbol_values(file, sym, &symval); 47538275Sdfr if (symval.value == 0) 47625537Sdfr /* 47725537Sdfr * For commons, first look them up in the dependancies and 47825537Sdfr * only allocate space if not found there. 47925537Sdfr */ 48038275Sdfr common_size = symval.size; 48140159Speter else { 48240159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: symbol.value=%x\n", symval.value)); 48338275Sdfr return symval.value; 48440159Speter } 48538275Sdfr } 48625537Sdfr 48742849Speter if (deps) { 48825537Sdfr for (i = 0; i < file->ndeps; i++) { 48925537Sdfr address = linker_file_lookup_symbol(file->deps[i], name, 0); 49040159Speter if (address) { 49140159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: deps value=%x\n", address)); 49225537Sdfr return address; 49340159Speter } 49425537Sdfr } 49525537Sdfr 49642849Speter /* If we have not found it in the dependencies, search globally */ 49742849Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 49842849Speter /* But skip the current file if it's on the list */ 49942849Speter if (lf == file) 50042849Speter continue; 50142849Speter /* And skip the files we searched above */ 50242849Speter for (i = 0; i < file->ndeps; i++) 50342849Speter if (lf == file->deps[i]) 50442849Speter break; 50542849Speter if (i < file->ndeps) 50642849Speter continue; 50742849Speter address = linker_file_lookup_symbol(lf, name, 0); 50842849Speter if (address) { 50942849Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: global value=%x\n", address)); 51042849Speter return address; 51142849Speter } 51242849Speter } 51342849Speter } 51442849Speter 51525537Sdfr if (common_size > 0) { 51625537Sdfr /* 51725537Sdfr * This is a common symbol which was not found in the 51825537Sdfr * dependancies. We maintain a simple common symbol table in 51925537Sdfr * the file object. 52025537Sdfr */ 52125537Sdfr struct common_symbol* cp; 52225537Sdfr 52325537Sdfr for (cp = STAILQ_FIRST(&file->common); cp; 52425537Sdfr cp = STAILQ_NEXT(cp, link)) 52540159Speter if (!strcmp(cp->name, name)) { 52640159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: old common value=%x\n", cp->address)); 52725537Sdfr return cp->address; 52840159Speter } 52925537Sdfr 53025537Sdfr /* 53125537Sdfr * Round the symbol size up to align. 53225537Sdfr */ 53325537Sdfr common_size = (common_size + sizeof(int) - 1) & -sizeof(int); 53425537Sdfr cp = malloc(sizeof(struct common_symbol) 53525537Sdfr + common_size 53625537Sdfr + strlen(name) + 1, 53725537Sdfr M_LINKER, M_WAITOK); 53840159Speter if (!cp) { 53940159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: nomem\n")); 54025537Sdfr return 0; 54140159Speter } 54240395Speter bzero(cp, sizeof(struct common_symbol) + common_size + strlen(name)+ 1); 54325537Sdfr 54425537Sdfr cp->address = (caddr_t) (cp + 1); 54525537Sdfr cp->name = cp->address + common_size; 54625537Sdfr strcpy(cp->name, name); 54725537Sdfr bzero(cp->address, common_size); 54825537Sdfr STAILQ_INSERT_TAIL(&file->common, cp, link); 54925537Sdfr 55040159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: new common value=%x\n", cp->address)); 55125537Sdfr return cp->address; 55225537Sdfr } 55325537Sdfr 55440159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: fail\n")); 55525537Sdfr return 0; 55625537Sdfr} 55725537Sdfr 55840159Speter#ifdef DDB 55925537Sdfr/* 56040159Speter * DDB Helpers. DDB has to look across multiple files with their own 56140159Speter * symbol tables and string tables. 56240159Speter * 56340159Speter * Note that we do not obey list locking protocols here. We really don't 56440159Speter * need DDB to hang because somebody's got the lock held. We'll take the 56540159Speter * chance that the files list is inconsistant instead. 56640159Speter */ 56740159Speter 56840159Speterint 56943309Sdillonlinker_ddb_lookup(const char *symstr, c_linker_sym_t *sym) 57040159Speter{ 57140159Speter linker_file_t lf; 57240159Speter 57340159Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 57440159Speter if (lf->ops->lookup_symbol(lf, symstr, sym) == 0) 57540159Speter return 0; 57640159Speter } 57740159Speter return ENOENT; 57840159Speter} 57940159Speter 58040159Speterint 58143309Sdillonlinker_ddb_search_symbol(caddr_t value, c_linker_sym_t *sym, long *diffp) 58240159Speter{ 58340159Speter linker_file_t lf; 58440159Speter u_long off = (u_long)value; 58540159Speter u_long diff, bestdiff; 58643309Sdillon c_linker_sym_t best; 58743309Sdillon c_linker_sym_t es; 58840159Speter 58940159Speter best = 0; 59040159Speter bestdiff = off; 59140159Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 59240159Speter if (lf->ops->search_symbol(lf, value, &es, &diff) != 0) 59340159Speter continue; 59440159Speter if (es != 0 && diff < bestdiff) { 59540159Speter best = es; 59640159Speter bestdiff = diff; 59740159Speter } 59840159Speter if (bestdiff == 0) 59940159Speter break; 60040159Speter } 60140159Speter if (best) { 60240159Speter *sym = best; 60340159Speter *diffp = bestdiff; 60440159Speter return 0; 60540159Speter } else { 60640159Speter *sym = 0; 60740159Speter *diffp = off; 60840159Speter return ENOENT; 60940159Speter } 61040159Speter} 61140159Speter 61240159Speterint 61343309Sdillonlinker_ddb_symbol_values(c_linker_sym_t sym, linker_symval_t *symval) 61440159Speter{ 61540159Speter linker_file_t lf; 61640159Speter 61740159Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 61840159Speter if (lf->ops->symbol_values(lf, sym, symval) == 0) 61940159Speter return 0; 62040159Speter } 62140159Speter return ENOENT; 62240159Speter} 62340159Speter 62440159Speter#endif 62540159Speter 62640159Speter/* 62725537Sdfr * Syscalls. 62825537Sdfr */ 62925537Sdfr 63025537Sdfrint 63130994Sphkkldload(struct proc* p, struct kldload_args* uap) 63225537Sdfr{ 63342316Smsmith char* filename = NULL, *modulename; 63425537Sdfr linker_file_t lf; 63525537Sdfr int error = 0; 63625537Sdfr 63730994Sphk p->p_retval[0] = -1; 63825537Sdfr 63925537Sdfr if (securelevel > 0) 64025537Sdfr return EPERM; 64125537Sdfr 64243301Sdillon if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) 64325537Sdfr return error; 64425537Sdfr 64525537Sdfr filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 64643301Sdillon if ((error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) != 0) 64725537Sdfr goto out; 64825537Sdfr 64942316Smsmith /* Can't load more than one module with the same name */ 65042316Smsmith modulename = rindex(filename, '/'); 65142316Smsmith if (modulename == NULL) 65242316Smsmith modulename = filename; 65342316Smsmith if (linker_find_file_by_name(modulename)) { 65442316Smsmith error = EEXIST; 65542316Smsmith goto out; 65642316Smsmith } 65742316Smsmith 65843301Sdillon if ((error = linker_load_file(filename, &lf)) != 0) 65925537Sdfr goto out; 66025537Sdfr 66125537Sdfr lf->userrefs++; 66230994Sphk p->p_retval[0] = lf->id; 66340159Speter 66425537Sdfrout: 66525537Sdfr if (filename) 66625537Sdfr free(filename, M_TEMP); 66725537Sdfr return error; 66825537Sdfr} 66925537Sdfr 67025537Sdfrint 67130994Sphkkldunload(struct proc* p, struct kldunload_args* uap) 67225537Sdfr{ 67325537Sdfr linker_file_t lf; 67425537Sdfr int error = 0; 67525537Sdfr 67625537Sdfr if (securelevel > 0) 67725537Sdfr return EPERM; 67825537Sdfr 67943301Sdillon if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) 68025537Sdfr return error; 68125537Sdfr 68225537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 68325537Sdfr if (lf) { 68425537Sdfr KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs)); 68525537Sdfr if (lf->userrefs == 0) { 68643084Speter printf("linkerunload: attempt to unload file that was loaded by the kernel\n"); 68725537Sdfr error = EBUSY; 68825537Sdfr goto out; 68925537Sdfr } 69043084Speter lf->userrefs--; 69142837Speter error = linker_file_unload(lf); 69242837Speter if (error) 69343084Speter lf->userrefs++; 69425537Sdfr } else 69525537Sdfr error = ENOENT; 69625537Sdfr 69725537Sdfrout: 69825537Sdfr return error; 69925537Sdfr} 70025537Sdfr 70125537Sdfrint 70230994Sphkkldfind(struct proc* p, struct kldfind_args* uap) 70325537Sdfr{ 70442316Smsmith char* filename = NULL, *modulename; 70525537Sdfr linker_file_t lf; 70625537Sdfr int error = 0; 70725537Sdfr 70830994Sphk p->p_retval[0] = -1; 70925537Sdfr 71025537Sdfr filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 71143301Sdillon if ((error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) != 0) 71225537Sdfr goto out; 71325537Sdfr 71442316Smsmith modulename = rindex(filename, '/'); 71542316Smsmith if (modulename == NULL) 71642316Smsmith modulename = filename; 71742316Smsmith 71842316Smsmith lf = linker_find_file_by_name(modulename); 71925537Sdfr if (lf) 72030994Sphk p->p_retval[0] = lf->id; 72125537Sdfr else 72225537Sdfr error = ENOENT; 72340159Speter 72425537Sdfrout: 72525537Sdfr if (filename) 72625537Sdfr free(filename, M_TEMP); 72725537Sdfr return error; 72825537Sdfr} 72925537Sdfr 73025537Sdfrint 73130994Sphkkldnext(struct proc* p, struct kldnext_args* uap) 73225537Sdfr{ 73325537Sdfr linker_file_t lf; 73425537Sdfr int error = 0; 73525537Sdfr 73625537Sdfr if (SCARG(uap, fileid) == 0) { 73725537Sdfr if (TAILQ_FIRST(&files)) 73830994Sphk p->p_retval[0] = TAILQ_FIRST(&files)->id; 73925537Sdfr else 74030994Sphk p->p_retval[0] = 0; 74125537Sdfr return 0; 74225537Sdfr } 74340159Speter 74425537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 74525537Sdfr if (lf) { 74625537Sdfr if (TAILQ_NEXT(lf, link)) 74730994Sphk p->p_retval[0] = TAILQ_NEXT(lf, link)->id; 74825537Sdfr else 74930994Sphk p->p_retval[0] = 0; 75025537Sdfr } else 75125537Sdfr error = ENOENT; 75225537Sdfr 75325537Sdfr return error; 75425537Sdfr} 75525537Sdfr 75625537Sdfrint 75730994Sphkkldstat(struct proc* p, struct kldstat_args* uap) 75825537Sdfr{ 75925537Sdfr linker_file_t lf; 76025537Sdfr int error = 0; 76125537Sdfr int version; 76225537Sdfr struct kld_file_stat* stat; 76325537Sdfr int namelen; 76425537Sdfr 76525537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 76625537Sdfr if (!lf) { 76725537Sdfr error = ENOENT; 76825537Sdfr goto out; 76925537Sdfr } 77025537Sdfr 77125537Sdfr stat = SCARG(uap, stat); 77225537Sdfr 77325537Sdfr /* 77425537Sdfr * Check the version of the user's structure. 77525537Sdfr */ 77643301Sdillon if ((error = copyin(&stat->version, &version, sizeof(version))) != 0) 77725537Sdfr goto out; 77825537Sdfr if (version != sizeof(struct kld_file_stat)) { 77925537Sdfr error = EINVAL; 78025537Sdfr goto out; 78125537Sdfr } 78225537Sdfr 78325537Sdfr namelen = strlen(lf->filename) + 1; 78425537Sdfr if (namelen > MAXPATHLEN) 78525537Sdfr namelen = MAXPATHLEN; 78643301Sdillon if ((error = copyout(lf->filename, &stat->name[0], namelen)) != 0) 78725537Sdfr goto out; 78843301Sdillon if ((error = copyout(&lf->refs, &stat->refs, sizeof(int))) != 0) 78925537Sdfr goto out; 79043301Sdillon if ((error = copyout(&lf->id, &stat->id, sizeof(int))) != 0) 79125537Sdfr goto out; 79243301Sdillon if ((error = copyout(&lf->address, &stat->address, sizeof(caddr_t))) != 0) 79325537Sdfr goto out; 79443301Sdillon if ((error = copyout(&lf->size, &stat->size, sizeof(size_t))) != 0) 79525537Sdfr goto out; 79625537Sdfr 79730994Sphk p->p_retval[0] = 0; 79825537Sdfr 79925537Sdfrout: 80025537Sdfr return error; 80125537Sdfr} 80225537Sdfr 80325537Sdfrint 80430994Sphkkldfirstmod(struct proc* p, struct kldfirstmod_args* uap) 80525537Sdfr{ 80625537Sdfr linker_file_t lf; 80725537Sdfr int error = 0; 80825537Sdfr 80925537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 81025537Sdfr if (lf) { 81125537Sdfr if (TAILQ_FIRST(&lf->modules)) 81230994Sphk p->p_retval[0] = module_getid(TAILQ_FIRST(&lf->modules)); 81325537Sdfr else 81430994Sphk p->p_retval[0] = 0; 81525537Sdfr } else 81625537Sdfr error = ENOENT; 81725537Sdfr 81825537Sdfr return error; 81925537Sdfr} 82040159Speter 82141090Speterint 82241090Speterkldsym(struct proc *p, struct kldsym_args *uap) 82341090Speter{ 82441090Speter char *symstr = NULL; 82543309Sdillon c_linker_sym_t sym; 82641090Speter linker_symval_t symval; 82741090Speter linker_file_t lf; 82841090Speter struct kld_sym_lookup lookup; 82941090Speter int error = 0; 83041090Speter 83143301Sdillon if ((error = copyin(SCARG(uap, data), &lookup, sizeof(lookup))) != 0) 83241090Speter goto out; 83341090Speter if (lookup.version != sizeof(lookup) || SCARG(uap, cmd) != KLDSYM_LOOKUP) { 83441090Speter error = EINVAL; 83541090Speter goto out; 83641090Speter } 83741090Speter 83841090Speter symstr = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 83943301Sdillon if ((error = copyinstr(lookup.symname, symstr, MAXPATHLEN, NULL)) != 0) 84041090Speter goto out; 84141090Speter 84241090Speter if (SCARG(uap, fileid) != 0) { 84341090Speter lf = linker_find_file_by_id(SCARG(uap, fileid)); 84441090Speter if (lf == NULL) { 84541090Speter error = ENOENT; 84641090Speter goto out; 84741090Speter } 84841090Speter if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 && 84941090Speter lf->ops->symbol_values(lf, sym, &symval) == 0) { 85041090Speter lookup.symvalue = (u_long)symval.value; 85141090Speter lookup.symsize = symval.size; 85241090Speter error = copyout(&lookup, SCARG(uap, data), sizeof(lookup)); 85341090Speter } else 85441090Speter error = ENOENT; 85541090Speter } else { 85641090Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 85741090Speter if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 && 85841090Speter lf->ops->symbol_values(lf, sym, &symval) == 0) { 85941090Speter lookup.symvalue = (u_long)symval.value; 86041090Speter lookup.symsize = symval.size; 86141090Speter error = copyout(&lookup, SCARG(uap, data), sizeof(lookup)); 86241090Speter break; 86341090Speter } 86441090Speter } 86541090Speter if (!lf) 86641090Speter error = ENOENT; 86741090Speter } 86841090Speterout: 86941090Speter if (symstr) 87041090Speter free(symstr, M_TEMP); 87141090Speter return error; 87241090Speter} 87341090Speter 87440159Speter/* 87540159Speter * Preloaded module support 87640159Speter */ 87740159Speter 87840159Speterstatic void 87940159Speterlinker_preload(void* arg) 88040159Speter{ 88140159Speter caddr_t modptr; 88240159Speter char *modname; 88340162Speter char *modtype; 88440159Speter linker_file_t lf; 88540159Speter linker_class_t lc; 88640159Speter int error; 88740159Speter struct linker_set *sysinits; 88840159Speter struct sysinit **sipp; 88940159Speter moduledata_t *moddata; 89040159Speter 89140159Speter modptr = NULL; 89240159Speter while ((modptr = preload_search_next_name(modptr)) != NULL) { 89340159Speter modname = (char *)preload_search_info(modptr, MODINFO_NAME); 89440162Speter modtype = (char *)preload_search_info(modptr, MODINFO_TYPE); 89540159Speter if (modname == NULL) { 89640625Smsmith printf("Preloaded module at %p does not have a name!\n", modptr); 89740159Speter continue; 89840159Speter } 89940162Speter if (modtype == NULL) { 90040625Smsmith printf("Preloaded module at %p does not have a type!\n", modptr); 90140162Speter continue; 90240162Speter } 90340625Smsmith printf("Preloaded %s \"%s\" at %p.\n", modtype, modname, modptr); 90440159Speter lf = linker_find_file_by_name(modname); 90540159Speter if (lf) { 90640159Speter lf->userrefs++; 90740159Speter continue; 90840159Speter } 90940159Speter lf = NULL; 91040159Speter for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) { 91140159Speter error = lc->ops->load_file(modname, &lf); 91240159Speter if (error) { 91340159Speter lf = NULL; 91440159Speter break; 91540159Speter } 91640159Speter } 91740159Speter if (lf) { 91840159Speter lf->userrefs++; 91940159Speter 92040159Speter sysinits = (struct linker_set*) 92140159Speter linker_file_lookup_symbol(lf, "sysinit_set", 0); 92240159Speter if (sysinits) { 92340159Speter /* HACK ALERT! 92440159Speter * This is to set the sysinit moduledata so that the module 92540159Speter * can attach itself to the correct containing file. 92640159Speter * The sysinit could be run at *any* time. 92740159Speter */ 92840159Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 92940159Speter if ((*sipp)->func == module_register_init) { 93040159Speter moddata = (*sipp)->udata; 93140159Speter moddata->_file = lf; 93240159Speter } 93340159Speter } 93440159Speter sysinit_add((struct sysinit **)sysinits->ls_items); 93540159Speter } 93640159Speter } 93740159Speter } 93840159Speter} 93940159Speter 94040159SpeterSYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, 0); 94140159Speter 94240159Speter/* 94340159Speter * Search for a not-loaded module by name. 94440159Speter * 94540159Speter * Modules may be found in the following locations: 94640159Speter * 94740159Speter * - preloaded (result is just the module name) 94840159Speter * - on disk (result is full path to module) 94940159Speter * 95040159Speter * If the module name is qualified in any way (contains path, etc.) 95140159Speter * the we simply return a copy of it. 95240159Speter * 95340159Speter * The search path can be manipulated via sysctl. Note that we use the ';' 95440159Speter * character as a separator to be consistent with the bootloader. 95540159Speter */ 95640159Speter 95740159Speterstatic char linker_path[MAXPATHLEN + 1] = "/;/boot/;/modules/"; 95840159Speter 95940159SpeterSYSCTL_STRING(_kern, OID_AUTO, module_path, CTLFLAG_RW, linker_path, 96040159Speter sizeof(linker_path), "module load search path"); 96140159Speter 96240159Speterstatic char * 96340159Speterlinker_strdup(const char *str) 96440159Speter{ 96540159Speter char *result; 96640159Speter 96740159Speter if ((result = malloc((strlen(str) + 1), M_LINKER, M_WAITOK)) != NULL) 96840159Speter strcpy(result, str); 96940159Speter return(result); 97040159Speter} 97140159Speter 97240159Speterchar * 97340159Speterlinker_search_path(const char *name) 97440159Speter{ 97540159Speter struct nameidata nd; 97640159Speter struct proc *p = curproc; /* XXX */ 97740159Speter char *cp, *ep, *result; 97840159Speter int error; 97940159Speter enum vtype type; 98040159Speter 98140159Speter /* qualified at all? */ 98240159Speter if (index(name, '/')) 98340159Speter return(linker_strdup(name)); 98440159Speter 98540159Speter /* traverse the linker path */ 98640159Speter cp = linker_path; 98740159Speter for (;;) { 98840159Speter 98940159Speter /* find the end of this component */ 99040159Speter for (ep = cp; (*ep != 0) && (*ep != ';'); ep++) 99140159Speter ; 99240159Speter result = malloc((strlen(name) + (ep - cp) + 1), M_LINKER, M_WAITOK); 99340159Speter if (result == NULL) /* actually ENOMEM */ 99440159Speter return(NULL); 99540159Speter 99640159Speter strncpy(result, cp, ep - cp); 99740159Speter strcpy(result + (ep - cp), name); 99840159Speter 99940159Speter /* 100040159Speter * Attempt to open the file, and return the path if we succeed and it's 100140159Speter * a regular file. 100240159Speter */ 100340159Speter NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, result, p); 100440159Speter error = vn_open(&nd, FREAD, 0); 100540159Speter if (error == 0) { 100640159Speter type = nd.ni_vp->v_type; 100740159Speter VOP_UNLOCK(nd.ni_vp, 0, p); 100840159Speter vn_close(nd.ni_vp, FREAD, p->p_ucred, p); 100940159Speter if (type == VREG) 101040159Speter return(result); 101140159Speter } 101240159Speter free(result, M_LINKER); 101340159Speter 101440159Speter if (*ep == 0) 101540159Speter break; 101640159Speter cp = ep + 1; 101740159Speter } 101840159Speter return(NULL); 101940159Speter} 1020