kern_linker.c revision 42755
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 * 2642755Speter * $Id: kern_linker.c,v 1.18 1999/01/05 20:24:28 msmith 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; 35825537Sdfr lf->filename = (char*) (lf + 1); 35925537Sdfr strcpy(lf->filename, filename); 36025537Sdfr lf->id = next_file_id++; 36125537Sdfr lf->ndeps = 0; 36225537Sdfr lf->deps = NULL; 36325537Sdfr STAILQ_INIT(&lf->common); 36425537Sdfr TAILQ_INIT(&lf->modules); 36525537Sdfr 36625537Sdfr lf->priv = priv; 36725537Sdfr lf->ops = ops; 36825537Sdfr TAILQ_INSERT_TAIL(&files, lf, link); 36925537Sdfr 37025537Sdfrout: 37125537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 37225537Sdfr return lf; 37325537Sdfr} 37425537Sdfr 37525537Sdfrint 37625537Sdfrlinker_file_unload(linker_file_t file) 37725537Sdfr{ 37825537Sdfr module_t mod, next; 37925537Sdfr struct common_symbol* cp; 38025537Sdfr int error = 0; 38125537Sdfr int i; 38225537Sdfr 38340159Speter KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", file->refs)); 38425537Sdfr lockmgr(&lock, LK_EXCLUSIVE|LK_RETRY, 0, curproc); 38525537Sdfr if (file->refs == 1) { 38625537Sdfr KLD_DPF(FILE, ("linker_file_unload: file is unloading, informing modules\n")); 38725537Sdfr /* 38825537Sdfr * Inform any modules associated with this file. 38925537Sdfr */ 39025537Sdfr for (mod = TAILQ_FIRST(&file->modules); mod; mod = next) { 39125537Sdfr next = module_getfnext(mod); 39225537Sdfr 39325537Sdfr /* 39425537Sdfr * Give the module a chance to veto the unload. 39525537Sdfr */ 39625537Sdfr if (error = module_unload(mod)) { 39725537Sdfr KLD_DPF(FILE, ("linker_file_unload: module %x vetoes unload\n", 39825537Sdfr mod)); 39925537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 40025537Sdfr goto out; 40125537Sdfr } 40225537Sdfr 40325537Sdfr module_release(mod); 40425537Sdfr } 40525537Sdfr } 40625537Sdfr 40725537Sdfr file->refs--; 40825537Sdfr if (file->refs > 0) { 40925537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 41025537Sdfr goto out; 41125537Sdfr } 41225537Sdfr 41341055Speter linker_file_sysuninit(file); 41441055Speter 41525537Sdfr TAILQ_REMOVE(&files, file, link); 41625537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 41740159Speter 41825537Sdfr for (i = 0; i < file->ndeps; i++) 41925537Sdfr linker_file_unload(file->deps[i]); 42025537Sdfr free(file->deps, M_LINKER); 42125537Sdfr 42225537Sdfr for (cp = STAILQ_FIRST(&file->common); cp; 42325537Sdfr cp = STAILQ_FIRST(&file->common)) { 42425537Sdfr STAILQ_REMOVE(&file->common, cp, common_symbol, link); 42525537Sdfr free(cp, M_LINKER); 42625537Sdfr } 42725537Sdfr 42825537Sdfr file->ops->unload(file); 42925537Sdfr free(file, M_LINKER); 43025537Sdfr 43125537Sdfrout: 43225537Sdfr return error; 43325537Sdfr} 43425537Sdfr 43525537Sdfrint 43625537Sdfrlinker_file_add_dependancy(linker_file_t file, linker_file_t dep) 43725537Sdfr{ 43825537Sdfr linker_file_t* newdeps; 43925537Sdfr 44025537Sdfr newdeps = malloc((file->ndeps + 1) * sizeof(linker_file_t*), 44125537Sdfr M_LINKER, M_WAITOK); 44225537Sdfr if (newdeps == NULL) 44325537Sdfr return ENOMEM; 44440395Speter bzero(newdeps, (file->ndeps + 1) * sizeof(linker_file_t*)); 44525537Sdfr 44625537Sdfr if (file->deps) { 44725537Sdfr bcopy(file->deps, newdeps, file->ndeps * sizeof(linker_file_t*)); 44825537Sdfr free(file->deps, M_LINKER); 44925537Sdfr } 45025537Sdfr file->deps = newdeps; 45125537Sdfr file->deps[file->ndeps] = dep; 45225537Sdfr file->ndeps++; 45325537Sdfr 45425537Sdfr return 0; 45525537Sdfr} 45625537Sdfr 45725537Sdfrcaddr_t 45825537Sdfrlinker_file_lookup_symbol(linker_file_t file, const char* name, int deps) 45925537Sdfr{ 46038275Sdfr linker_sym_t sym; 46138275Sdfr linker_symval_t symval; 46225537Sdfr caddr_t address; 46325537Sdfr size_t common_size = 0; 46425537Sdfr int i; 46525537Sdfr 46640159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d\n", 46725537Sdfr file, name, deps)); 46825537Sdfr 46938275Sdfr if (file->ops->lookup_symbol(file, name, &sym) == 0) { 47038275Sdfr file->ops->symbol_values(file, sym, &symval); 47138275Sdfr if (symval.value == 0) 47225537Sdfr /* 47325537Sdfr * For commons, first look them up in the dependancies and 47425537Sdfr * only allocate space if not found there. 47525537Sdfr */ 47638275Sdfr common_size = symval.size; 47740159Speter else { 47840159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: symbol.value=%x\n", symval.value)); 47938275Sdfr return symval.value; 48040159Speter } 48138275Sdfr } 48225537Sdfr 48325537Sdfr if (deps) 48425537Sdfr for (i = 0; i < file->ndeps; i++) { 48525537Sdfr address = linker_file_lookup_symbol(file->deps[i], name, 0); 48640159Speter if (address) { 48740159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: deps value=%x\n", address)); 48825537Sdfr return address; 48940159Speter } 49025537Sdfr } 49125537Sdfr 49225537Sdfr if (common_size > 0) { 49325537Sdfr /* 49425537Sdfr * This is a common symbol which was not found in the 49525537Sdfr * dependancies. We maintain a simple common symbol table in 49625537Sdfr * the file object. 49725537Sdfr */ 49825537Sdfr struct common_symbol* cp; 49925537Sdfr 50025537Sdfr for (cp = STAILQ_FIRST(&file->common); cp; 50125537Sdfr cp = STAILQ_NEXT(cp, link)) 50240159Speter if (!strcmp(cp->name, name)) { 50340159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: old common value=%x\n", cp->address)); 50425537Sdfr return cp->address; 50540159Speter } 50625537Sdfr 50725537Sdfr /* 50825537Sdfr * Round the symbol size up to align. 50925537Sdfr */ 51025537Sdfr common_size = (common_size + sizeof(int) - 1) & -sizeof(int); 51125537Sdfr cp = malloc(sizeof(struct common_symbol) 51225537Sdfr + common_size 51325537Sdfr + strlen(name) + 1, 51425537Sdfr M_LINKER, M_WAITOK); 51540159Speter if (!cp) { 51640159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: nomem\n")); 51725537Sdfr return 0; 51840159Speter } 51940395Speter bzero(cp, sizeof(struct common_symbol) + common_size + strlen(name)+ 1); 52025537Sdfr 52125537Sdfr cp->address = (caddr_t) (cp + 1); 52225537Sdfr cp->name = cp->address + common_size; 52325537Sdfr strcpy(cp->name, name); 52425537Sdfr bzero(cp->address, common_size); 52525537Sdfr STAILQ_INSERT_TAIL(&file->common, cp, link); 52625537Sdfr 52740159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: new common value=%x\n", cp->address)); 52825537Sdfr return cp->address; 52925537Sdfr } 53025537Sdfr 53140159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: fail\n")); 53225537Sdfr return 0; 53325537Sdfr} 53425537Sdfr 53540159Speter#ifdef DDB 53625537Sdfr/* 53740159Speter * DDB Helpers. DDB has to look across multiple files with their own 53840159Speter * symbol tables and string tables. 53940159Speter * 54040159Speter * Note that we do not obey list locking protocols here. We really don't 54140159Speter * need DDB to hang because somebody's got the lock held. We'll take the 54240159Speter * chance that the files list is inconsistant instead. 54340159Speter */ 54440159Speter 54540159Speterint 54640159Speterlinker_ddb_lookup(char *symstr, linker_sym_t *sym) 54740159Speter{ 54840159Speter linker_file_t lf; 54940159Speter 55040159Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 55140159Speter if (lf->ops->lookup_symbol(lf, symstr, sym) == 0) 55240159Speter return 0; 55340159Speter } 55440159Speter return ENOENT; 55540159Speter} 55640159Speter 55740159Speterint 55840159Speterlinker_ddb_search_symbol(caddr_t value, linker_sym_t *sym, long *diffp) 55940159Speter{ 56040159Speter linker_file_t lf; 56140159Speter u_long off = (u_long)value; 56240159Speter u_long diff, bestdiff; 56340159Speter linker_sym_t best; 56440159Speter linker_sym_t es; 56540159Speter 56640159Speter best = 0; 56740159Speter bestdiff = off; 56840159Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 56940159Speter if (lf->ops->search_symbol(lf, value, &es, &diff) != 0) 57040159Speter continue; 57140159Speter if (es != 0 && diff < bestdiff) { 57240159Speter best = es; 57340159Speter bestdiff = diff; 57440159Speter } 57540159Speter if (bestdiff == 0) 57640159Speter break; 57740159Speter } 57840159Speter if (best) { 57940159Speter *sym = best; 58040159Speter *diffp = bestdiff; 58140159Speter return 0; 58240159Speter } else { 58340159Speter *sym = 0; 58440159Speter *diffp = off; 58540159Speter return ENOENT; 58640159Speter } 58740159Speter} 58840159Speter 58940159Speterint 59040159Speterlinker_ddb_symbol_values(linker_sym_t sym, linker_symval_t *symval) 59140159Speter{ 59240159Speter linker_file_t lf; 59340159Speter 59440159Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 59540159Speter if (lf->ops->symbol_values(lf, sym, symval) == 0) 59640159Speter return 0; 59740159Speter } 59840159Speter return ENOENT; 59940159Speter} 60040159Speter 60140159Speter#endif 60240159Speter 60340159Speter/* 60425537Sdfr * Syscalls. 60525537Sdfr */ 60625537Sdfr 60725537Sdfrint 60830994Sphkkldload(struct proc* p, struct kldload_args* uap) 60925537Sdfr{ 61042316Smsmith char* filename = NULL, *modulename; 61125537Sdfr linker_file_t lf; 61225537Sdfr int error = 0; 61325537Sdfr 61430994Sphk p->p_retval[0] = -1; 61525537Sdfr 61625537Sdfr if (securelevel > 0) 61725537Sdfr return EPERM; 61825537Sdfr 61925537Sdfr if (error = suser(p->p_ucred, &p->p_acflag)) 62025537Sdfr return error; 62125537Sdfr 62225537Sdfr filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 62325537Sdfr if (error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) 62425537Sdfr goto out; 62525537Sdfr 62642316Smsmith /* Can't load more than one module with the same name */ 62742316Smsmith modulename = rindex(filename, '/'); 62842316Smsmith if (modulename == NULL) 62942316Smsmith modulename = filename; 63042316Smsmith if (linker_find_file_by_name(modulename)) { 63142316Smsmith error = EEXIST; 63242316Smsmith goto out; 63342316Smsmith } 63442316Smsmith 63540859Speter if (error = linker_load_file(filename, &lf)) 63625537Sdfr goto out; 63725537Sdfr 63825537Sdfr lf->userrefs++; 63930994Sphk p->p_retval[0] = lf->id; 64040159Speter 64125537Sdfrout: 64225537Sdfr if (filename) 64325537Sdfr free(filename, M_TEMP); 64425537Sdfr return error; 64525537Sdfr} 64625537Sdfr 64725537Sdfrint 64830994Sphkkldunload(struct proc* p, struct kldunload_args* uap) 64925537Sdfr{ 65025537Sdfr linker_file_t lf; 65125537Sdfr int error = 0; 65225537Sdfr 65325537Sdfr if (securelevel > 0) 65425537Sdfr return EPERM; 65525537Sdfr 65625537Sdfr if (error = suser(p->p_ucred, &p->p_acflag)) 65725537Sdfr return error; 65825537Sdfr 65925537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 66025537Sdfr if (lf) { 66125537Sdfr KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs)); 66225537Sdfr if (lf->userrefs == 0) { 66325537Sdfr printf("linkerunload: attempt to unload file which was not loaded by user\n"); 66425537Sdfr error = EBUSY; 66525537Sdfr goto out; 66625537Sdfr } 66725537Sdfr lf->userrefs--; 66825537Sdfr error = linker_file_unload(lf); 66925537Sdfr } else 67025537Sdfr error = ENOENT; 67125537Sdfr 67225537Sdfrout: 67325537Sdfr return error; 67425537Sdfr} 67525537Sdfr 67625537Sdfrint 67730994Sphkkldfind(struct proc* p, struct kldfind_args* uap) 67825537Sdfr{ 67942316Smsmith char* filename = NULL, *modulename; 68025537Sdfr linker_file_t lf; 68125537Sdfr int error = 0; 68225537Sdfr 68330994Sphk p->p_retval[0] = -1; 68425537Sdfr 68525537Sdfr filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 68625537Sdfr if (error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) 68725537Sdfr goto out; 68825537Sdfr 68942316Smsmith modulename = rindex(filename, '/'); 69042316Smsmith if (modulename == NULL) 69142316Smsmith modulename = filename; 69242316Smsmith 69342316Smsmith lf = linker_find_file_by_name(modulename); 69425537Sdfr if (lf) 69530994Sphk p->p_retval[0] = lf->id; 69625537Sdfr else 69725537Sdfr error = ENOENT; 69840159Speter 69925537Sdfrout: 70025537Sdfr if (filename) 70125537Sdfr free(filename, M_TEMP); 70225537Sdfr return error; 70325537Sdfr} 70425537Sdfr 70525537Sdfrint 70630994Sphkkldnext(struct proc* p, struct kldnext_args* uap) 70725537Sdfr{ 70825537Sdfr linker_file_t lf; 70925537Sdfr int error = 0; 71025537Sdfr 71125537Sdfr if (SCARG(uap, fileid) == 0) { 71225537Sdfr if (TAILQ_FIRST(&files)) 71330994Sphk p->p_retval[0] = TAILQ_FIRST(&files)->id; 71425537Sdfr else 71530994Sphk p->p_retval[0] = 0; 71625537Sdfr return 0; 71725537Sdfr } 71840159Speter 71925537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 72025537Sdfr if (lf) { 72125537Sdfr if (TAILQ_NEXT(lf, link)) 72230994Sphk p->p_retval[0] = TAILQ_NEXT(lf, link)->id; 72325537Sdfr else 72430994Sphk p->p_retval[0] = 0; 72525537Sdfr } else 72625537Sdfr error = ENOENT; 72725537Sdfr 72825537Sdfr return error; 72925537Sdfr} 73025537Sdfr 73125537Sdfrint 73230994Sphkkldstat(struct proc* p, struct kldstat_args* uap) 73325537Sdfr{ 73425537Sdfr linker_file_t lf; 73525537Sdfr int error = 0; 73625537Sdfr int version; 73725537Sdfr struct kld_file_stat* stat; 73825537Sdfr int namelen; 73925537Sdfr 74025537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 74125537Sdfr if (!lf) { 74225537Sdfr error = ENOENT; 74325537Sdfr goto out; 74425537Sdfr } 74525537Sdfr 74625537Sdfr stat = SCARG(uap, stat); 74725537Sdfr 74825537Sdfr /* 74925537Sdfr * Check the version of the user's structure. 75025537Sdfr */ 75125537Sdfr if (error = copyin(&stat->version, &version, sizeof(version))) 75225537Sdfr goto out; 75325537Sdfr if (version != sizeof(struct kld_file_stat)) { 75425537Sdfr error = EINVAL; 75525537Sdfr goto out; 75625537Sdfr } 75725537Sdfr 75825537Sdfr namelen = strlen(lf->filename) + 1; 75925537Sdfr if (namelen > MAXPATHLEN) 76025537Sdfr namelen = MAXPATHLEN; 76125537Sdfr if (error = copyout(lf->filename, &stat->name[0], namelen)) 76225537Sdfr goto out; 76325537Sdfr if (error = copyout(&lf->refs, &stat->refs, sizeof(int))) 76425537Sdfr goto out; 76525537Sdfr if (error = copyout(&lf->id, &stat->id, sizeof(int))) 76625537Sdfr goto out; 76725537Sdfr if (error = copyout(&lf->address, &stat->address, sizeof(caddr_t))) 76825537Sdfr goto out; 76925537Sdfr if (error = copyout(&lf->size, &stat->size, sizeof(size_t))) 77025537Sdfr goto out; 77125537Sdfr 77230994Sphk p->p_retval[0] = 0; 77325537Sdfr 77425537Sdfrout: 77525537Sdfr return error; 77625537Sdfr} 77725537Sdfr 77825537Sdfrint 77930994Sphkkldfirstmod(struct proc* p, struct kldfirstmod_args* uap) 78025537Sdfr{ 78125537Sdfr linker_file_t lf; 78225537Sdfr int error = 0; 78325537Sdfr 78425537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 78525537Sdfr if (lf) { 78625537Sdfr if (TAILQ_FIRST(&lf->modules)) 78730994Sphk p->p_retval[0] = module_getid(TAILQ_FIRST(&lf->modules)); 78825537Sdfr else 78930994Sphk p->p_retval[0] = 0; 79025537Sdfr } else 79125537Sdfr error = ENOENT; 79225537Sdfr 79325537Sdfr return error; 79425537Sdfr} 79540159Speter 79641090Speterint 79741090Speterkldsym(struct proc *p, struct kldsym_args *uap) 79841090Speter{ 79941090Speter char *symstr = NULL; 80041090Speter linker_sym_t sym; 80141090Speter linker_symval_t symval; 80241090Speter linker_file_t lf; 80341090Speter struct kld_sym_lookup lookup; 80441090Speter int error = 0; 80541090Speter 80641090Speter if (error = copyin(SCARG(uap, data), &lookup, sizeof(lookup))) 80741090Speter goto out; 80841090Speter if (lookup.version != sizeof(lookup) || SCARG(uap, cmd) != KLDSYM_LOOKUP) { 80941090Speter error = EINVAL; 81041090Speter goto out; 81141090Speter } 81241090Speter 81341090Speter symstr = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 81441090Speter if (error = copyinstr(lookup.symname, symstr, MAXPATHLEN, NULL)) 81541090Speter goto out; 81641090Speter 81741090Speter if (SCARG(uap, fileid) != 0) { 81841090Speter lf = linker_find_file_by_id(SCARG(uap, fileid)); 81941090Speter if (lf == NULL) { 82041090Speter error = ENOENT; 82141090Speter goto out; 82241090Speter } 82341090Speter if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 && 82441090Speter lf->ops->symbol_values(lf, sym, &symval) == 0) { 82541090Speter lookup.symvalue = (u_long)symval.value; 82641090Speter lookup.symsize = symval.size; 82741090Speter error = copyout(&lookup, SCARG(uap, data), sizeof(lookup)); 82841090Speter } else 82941090Speter error = ENOENT; 83041090Speter } else { 83141090Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 83241090Speter if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 && 83341090Speter lf->ops->symbol_values(lf, sym, &symval) == 0) { 83441090Speter lookup.symvalue = (u_long)symval.value; 83541090Speter lookup.symsize = symval.size; 83641090Speter error = copyout(&lookup, SCARG(uap, data), sizeof(lookup)); 83741090Speter break; 83841090Speter } 83941090Speter } 84041090Speter if (!lf) 84141090Speter error = ENOENT; 84241090Speter } 84341090Speterout: 84441090Speter if (symstr) 84541090Speter free(symstr, M_TEMP); 84641090Speter return error; 84741090Speter} 84841090Speter 84940159Speter/* 85040159Speter * Preloaded module support 85140159Speter */ 85240159Speter 85340159Speterstatic void 85440159Speterlinker_preload(void* arg) 85540159Speter{ 85640159Speter caddr_t modptr; 85740159Speter char *modname; 85840162Speter char *modtype; 85940159Speter linker_file_t lf; 86040159Speter linker_class_t lc; 86140159Speter int error; 86240159Speter struct linker_set *sysinits; 86340159Speter struct sysinit **sipp; 86440159Speter moduledata_t *moddata; 86540159Speter 86640159Speter modptr = NULL; 86740159Speter while ((modptr = preload_search_next_name(modptr)) != NULL) { 86840159Speter modname = (char *)preload_search_info(modptr, MODINFO_NAME); 86940162Speter modtype = (char *)preload_search_info(modptr, MODINFO_TYPE); 87040159Speter if (modname == NULL) { 87140625Smsmith printf("Preloaded module at %p does not have a name!\n", modptr); 87240159Speter continue; 87340159Speter } 87440162Speter if (modtype == NULL) { 87540625Smsmith printf("Preloaded module at %p does not have a type!\n", modptr); 87640162Speter continue; 87740162Speter } 87840625Smsmith printf("Preloaded %s \"%s\" at %p.\n", modtype, modname, modptr); 87940159Speter lf = linker_find_file_by_name(modname); 88040159Speter if (lf) { 88140159Speter lf->userrefs++; 88240159Speter continue; 88340159Speter } 88440159Speter lf = NULL; 88540159Speter for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) { 88640159Speter error = lc->ops->load_file(modname, &lf); 88740159Speter if (error) { 88840159Speter lf = NULL; 88940159Speter break; 89040159Speter } 89140159Speter } 89240159Speter if (lf) { 89340159Speter lf->userrefs++; 89440159Speter 89540159Speter sysinits = (struct linker_set*) 89640159Speter linker_file_lookup_symbol(lf, "sysinit_set", 0); 89740159Speter if (sysinits) { 89840159Speter /* HACK ALERT! 89940159Speter * This is to set the sysinit moduledata so that the module 90040159Speter * can attach itself to the correct containing file. 90140159Speter * The sysinit could be run at *any* time. 90240159Speter */ 90340159Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 90440159Speter if ((*sipp)->func == module_register_init) { 90540159Speter moddata = (*sipp)->udata; 90640159Speter moddata->_file = lf; 90740159Speter } 90840159Speter } 90940159Speter sysinit_add((struct sysinit **)sysinits->ls_items); 91040159Speter } 91140159Speter } 91240159Speter } 91340159Speter} 91440159Speter 91540159SpeterSYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, 0); 91640159Speter 91740159Speter/* 91840159Speter * Search for a not-loaded module by name. 91940159Speter * 92040159Speter * Modules may be found in the following locations: 92140159Speter * 92240159Speter * - preloaded (result is just the module name) 92340159Speter * - on disk (result is full path to module) 92440159Speter * 92540159Speter * If the module name is qualified in any way (contains path, etc.) 92640159Speter * the we simply return a copy of it. 92740159Speter * 92840159Speter * The search path can be manipulated via sysctl. Note that we use the ';' 92940159Speter * character as a separator to be consistent with the bootloader. 93040159Speter */ 93140159Speter 93240159Speterstatic char linker_path[MAXPATHLEN + 1] = "/;/boot/;/modules/"; 93340159Speter 93440159SpeterSYSCTL_STRING(_kern, OID_AUTO, module_path, CTLFLAG_RW, linker_path, 93540159Speter sizeof(linker_path), "module load search path"); 93640159Speter 93740159Speterstatic char * 93840159Speterlinker_strdup(const char *str) 93940159Speter{ 94040159Speter char *result; 94140159Speter 94240159Speter if ((result = malloc((strlen(str) + 1), M_LINKER, M_WAITOK)) != NULL) 94340159Speter strcpy(result, str); 94440159Speter return(result); 94540159Speter} 94640159Speter 94740159Speterchar * 94840159Speterlinker_search_path(const char *name) 94940159Speter{ 95040159Speter struct nameidata nd; 95140159Speter struct proc *p = curproc; /* XXX */ 95240159Speter char *cp, *ep, *result; 95340159Speter int error; 95440159Speter enum vtype type; 95540159Speter 95640159Speter /* qualified at all? */ 95740159Speter if (index(name, '/')) 95840159Speter return(linker_strdup(name)); 95940159Speter 96040159Speter /* traverse the linker path */ 96140159Speter cp = linker_path; 96240159Speter for (;;) { 96340159Speter 96440159Speter /* find the end of this component */ 96540159Speter for (ep = cp; (*ep != 0) && (*ep != ';'); ep++) 96640159Speter ; 96740159Speter result = malloc((strlen(name) + (ep - cp) + 1), M_LINKER, M_WAITOK); 96840159Speter if (result == NULL) /* actually ENOMEM */ 96940159Speter return(NULL); 97040159Speter 97140159Speter strncpy(result, cp, ep - cp); 97240159Speter strcpy(result + (ep - cp), name); 97340159Speter 97440159Speter /* 97540159Speter * Attempt to open the file, and return the path if we succeed and it's 97640159Speter * a regular file. 97740159Speter */ 97840159Speter NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, result, p); 97940159Speter error = vn_open(&nd, FREAD, 0); 98040159Speter if (error == 0) { 98140159Speter type = nd.ni_vp->v_type; 98240159Speter VOP_UNLOCK(nd.ni_vp, 0, p); 98340159Speter vn_close(nd.ni_vp, FREAD, p->p_ucred, p); 98440159Speter if (type == VREG) 98540159Speter return(result); 98640159Speter } 98740159Speter free(result, M_LINKER); 98840159Speter 98940159Speter if (*ep == 0) 99040159Speter break; 99140159Speter cp = ep + 1; 99240159Speter } 99340159Speter return(NULL); 99440159Speter} 995