kern_linker.c revision 46693
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 * 2646693Speter * $Id: kern_linker.c,v 1.31 1999/04/28 01:04:28 luoqi 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; 9946693Speter const moduledata_t *moddata; 10046693Speter int error; 10125537Sdfr 10225537Sdfr KLD_DPF(FILE, ("linker_file_sysinit: calling SYSINITs for %s\n", 10325537Sdfr lf->filename)); 10425537Sdfr 10525537Sdfr sysinits = (struct linker_set*) 10625537Sdfr linker_file_lookup_symbol(lf, "sysinit_set", 0); 10740159Speter 10840159Speter KLD_DPF(FILE, ("linker_file_sysinit: SYSINITs %p\n", sysinits)); 10925537Sdfr if (!sysinits) 11025537Sdfr return; 11125537Sdfr 11240159Speter /* HACK ALERT! */ 11340159Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 11440159Speter if ((*sipp)->func == module_register_init) { 11540159Speter moddata = (*sipp)->udata; 11646693Speter error = module_register(moddata, lf); 11746693Speter if (error) 11846693Speter printf("linker_file_sysinit \"%s\" failed to register! %d\n", 11946693Speter lf->filename, error); 12040159Speter } 12140159Speter } 12240159Speter 12325537Sdfr /* 12425537Sdfr * Perform a bubble sort of the system initialization objects by 12525537Sdfr * their subsystem (primary key) and order (secondary key). 12625537Sdfr * 12725537Sdfr * Since some things care about execution order, this is the 12825537Sdfr * operation which ensures continued function. 12925537Sdfr */ 13041055Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 13141055Speter for (xipp = sipp + 1; *xipp; xipp++) { 13241055Speter if ((*sipp)->subsystem <= (*xipp)->subsystem || 13341055Speter ((*sipp)->subsystem == (*xipp)->subsystem && 13441055Speter (*sipp)->order <= (*xipp)->order)) 13525537Sdfr continue; /* skip*/ 13625537Sdfr save = *sipp; 13725537Sdfr *sipp = *xipp; 13825537Sdfr *xipp = save; 13925537Sdfr } 14025537Sdfr } 14125537Sdfr 14225537Sdfr 14325537Sdfr /* 14425537Sdfr * Traverse the (now) ordered list of system initialization tasks. 14525537Sdfr * Perform each task, and continue on to the next task. 14625537Sdfr */ 14741055Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 14841055Speter if ((*sipp)->subsystem == SI_SUB_DUMMY) 14925537Sdfr continue; /* skip dummy task(s)*/ 15025537Sdfr 15141055Speter switch ((*sipp)->type) { 15225537Sdfr case SI_TYPE_DEFAULT: 15325537Sdfr /* no special processing*/ 15441055Speter (*((*sipp)->func))((*sipp)->udata); 15525537Sdfr break; 15625537Sdfr 15725537Sdfr case SI_TYPE_KTHREAD: 15825537Sdfr /* kernel thread*/ 15931675Sdyson if (fork1(&proc0, RFFDG|RFPROC|RFMEM)) 16031675Sdyson panic("fork kernel thread"); 16131675Sdyson cpu_set_fork_handler(pfind(proc0.p_retval[0]), 16231675Sdyson (*sipp)->func, (*sipp)->udata); 16331675Sdyson break; 16431675Sdyson 16531675Sdyson case SI_TYPE_KPROCESS: 16631675Sdyson /* kernel thread*/ 16731675Sdyson if (fork1(&proc0, RFFDG|RFPROC)) 16825537Sdfr panic("fork kernel process"); 16930994Sphk cpu_set_fork_handler(pfind(proc0.p_retval[0]), 17030994Sphk (*sipp)->func, (*sipp)->udata); 17125537Sdfr break; 17225537Sdfr 17325537Sdfr default: 17441055Speter panic ("linker_file_sysinit: unrecognized init type"); 17525537Sdfr } 17625537Sdfr } 17725537Sdfr} 17825537Sdfr 17941055Speterstatic void 18041055Speterlinker_file_sysuninit(linker_file_t lf) 18141055Speter{ 18241055Speter struct linker_set* sysuninits; 18341055Speter struct sysinit** sipp; 18441055Speter struct sysinit** xipp; 18541055Speter struct sysinit* save; 18641055Speter 18741055Speter KLD_DPF(FILE, ("linker_file_sysuninit: calling SYSUNINITs for %s\n", 18841055Speter lf->filename)); 18941055Speter 19041055Speter sysuninits = (struct linker_set*) 19141055Speter linker_file_lookup_symbol(lf, "sysuninit_set", 0); 19241055Speter 19341055Speter KLD_DPF(FILE, ("linker_file_sysuninit: SYSUNINITs %p\n", sysuninits)); 19441055Speter if (!sysuninits) 19541055Speter return; 19641055Speter 19741055Speter /* 19841055Speter * Perform a reverse bubble sort of the system initialization objects 19941055Speter * by their subsystem (primary key) and order (secondary key). 20041055Speter * 20141055Speter * Since some things care about execution order, this is the 20241055Speter * operation which ensures continued function. 20341055Speter */ 20441055Speter for (sipp = (struct sysinit **)sysuninits->ls_items; *sipp; sipp++) { 20541055Speter for (xipp = sipp + 1; *xipp; xipp++) { 20641055Speter if ((*sipp)->subsystem >= (*xipp)->subsystem || 20741055Speter ((*sipp)->subsystem == (*xipp)->subsystem && 20841055Speter (*sipp)->order >= (*xipp)->order)) 20941055Speter continue; /* skip*/ 21041055Speter save = *sipp; 21141055Speter *sipp = *xipp; 21241055Speter *xipp = save; 21341055Speter } 21441055Speter } 21541055Speter 21641055Speter 21741055Speter /* 21841055Speter * Traverse the (now) ordered list of system initialization tasks. 21941055Speter * Perform each task, and continue on to the next task. 22041055Speter */ 22141055Speter for (sipp = (struct sysinit **)sysuninits->ls_items; *sipp; sipp++) { 22241055Speter if ((*sipp)->subsystem == SI_SUB_DUMMY) 22341055Speter continue; /* skip dummy task(s)*/ 22441055Speter 22541055Speter switch ((*sipp)->type) { 22641055Speter case SI_TYPE_DEFAULT: 22741055Speter /* no special processing*/ 22841055Speter (*((*sipp)->func))((*sipp)->udata); 22941055Speter break; 23041055Speter 23141055Speter default: 23241055Speter panic("linker_file_sysuninit: unrecognized uninit type"); 23341055Speter } 23441055Speter } 23541055Speter} 23641055Speter 23744078Sdfrstatic void 23844078Sdfrlinker_file_register_sysctls(linker_file_t lf) 23944078Sdfr{ 24044078Sdfr struct linker_set* sysctls; 24144078Sdfr 24244078Sdfr KLD_DPF(FILE, ("linker_file_register_sysctls: registering SYSCTLs for %s\n", 24344078Sdfr lf->filename)); 24444078Sdfr 24544078Sdfr sysctls = (struct linker_set*) 24644078Sdfr linker_file_lookup_symbol(lf, "sysctl_set", 0); 24744078Sdfr 24844078Sdfr KLD_DPF(FILE, ("linker_file_register_sysctls: SYSCTLs %p\n", sysctls)); 24944078Sdfr if (!sysctls) 25044078Sdfr return; 25144078Sdfr 25244078Sdfr sysctl_register_set(sysctls); 25344078Sdfr} 25444078Sdfr 25544078Sdfrstatic void 25644078Sdfrlinker_file_unregister_sysctls(linker_file_t lf) 25744078Sdfr{ 25844078Sdfr struct linker_set* sysctls; 25944078Sdfr 26044078Sdfr KLD_DPF(FILE, ("linker_file_unregister_sysctls: registering SYSCTLs for %s\n", 26144078Sdfr lf->filename)); 26244078Sdfr 26344078Sdfr sysctls = (struct linker_set*) 26444078Sdfr linker_file_lookup_symbol(lf, "sysctl_set", 0); 26544078Sdfr 26644078Sdfr KLD_DPF(FILE, ("linker_file_unregister_sysctls: SYSCTLs %p\n", sysctls)); 26744078Sdfr if (!sysctls) 26844078Sdfr return; 26944078Sdfr 27044078Sdfr sysctl_unregister_set(sysctls); 27144078Sdfr} 27244078Sdfr 27325537Sdfrint 27425537Sdfrlinker_load_file(const char* filename, linker_file_t* result) 27525537Sdfr{ 27625537Sdfr linker_class_t lc; 27725537Sdfr linker_file_t lf; 27842755Speter int foundfile, error = 0; 27940861Speter char *koname = NULL; 28025537Sdfr 28125537Sdfr lf = linker_find_file_by_name(filename); 28225537Sdfr if (lf) { 28325537Sdfr KLD_DPF(FILE, ("linker_load_file: file %s is already loaded, incrementing refs\n", filename)); 28425537Sdfr *result = lf; 28525537Sdfr lf->refs++; 28625537Sdfr goto out; 28725537Sdfr } 28825537Sdfr 28940861Speter koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK); 29040861Speter if (koname == NULL) { 29140861Speter error = ENOMEM; 29240861Speter goto out; 29340861Speter } 29440861Speter sprintf(koname, "%s.ko", filename); 29525537Sdfr lf = NULL; 29642755Speter foundfile = 0; 29725537Sdfr for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) { 29825537Sdfr KLD_DPF(FILE, ("linker_load_file: trying to load %s as %s\n", 29925537Sdfr filename, lc->desc)); 30042755Speter 30142755Speter error = lc->ops->load_file(koname, &lf); /* First with .ko */ 30242755Speter if (lf == NULL && error == ENOENT) 30342755Speter error = lc->ops->load_file(filename, &lf); /* Then try without */ 30442755Speter /* 30542755Speter * If we got something other than ENOENT, then it exists but we cannot 30642755Speter * load it for some other reason. 30742755Speter */ 30842755Speter if (error != ENOENT) 30942755Speter foundfile = 1; 31025537Sdfr if (lf) { 31144549Sdfr linker_file_register_sysctls(lf); 31225537Sdfr linker_file_sysinit(lf); 31325537Sdfr 31425537Sdfr *result = lf; 31540861Speter error = 0; 31625537Sdfr goto out; 31725537Sdfr } 31825537Sdfr } 31942755Speter /* 32042755Speter * Less than ideal, but tells the user whether it failed to load or 32142755Speter * the module was not found. 32242755Speter */ 32342755Speter if (foundfile) 32442755Speter error = ENOEXEC; /* Format not recognised (or unloadable) */ 32542755Speter else 32642755Speter error = ENOENT; /* Nothing found */ 32725537Sdfr 32825537Sdfrout: 32940861Speter if (koname) 33040861Speter free(koname, M_LINKER); 33125537Sdfr return error; 33225537Sdfr} 33325537Sdfr 33425537Sdfrlinker_file_t 33525537Sdfrlinker_find_file_by_name(const char* filename) 33625537Sdfr{ 33725537Sdfr linker_file_t lf = 0; 33840861Speter char *koname; 33925537Sdfr 34040861Speter koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK); 34140861Speter if (koname == NULL) 34240861Speter goto out; 34340861Speter sprintf(koname, "%s.ko", filename); 34440861Speter 34525537Sdfr lockmgr(&lock, LK_SHARED, 0, curproc); 34640861Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 34740861Speter if (!strcmp(lf->filename, koname)) 34840861Speter break; 34925537Sdfr if (!strcmp(lf->filename, filename)) 35025537Sdfr break; 35140861Speter } 35225537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 35325537Sdfr 35440861Speterout: 35540861Speter if (koname) 35640861Speter free(koname, M_LINKER); 35725537Sdfr return lf; 35825537Sdfr} 35925537Sdfr 36025537Sdfrlinker_file_t 36125537Sdfrlinker_find_file_by_id(int fileid) 36225537Sdfr{ 36325537Sdfr linker_file_t lf = 0; 36425537Sdfr 36525537Sdfr lockmgr(&lock, LK_SHARED, 0, curproc); 36625537Sdfr for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) 36725537Sdfr if (lf->id == fileid) 36825537Sdfr break; 36925537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 37025537Sdfr 37125537Sdfr return lf; 37225537Sdfr} 37325537Sdfr 37425537Sdfrlinker_file_t 37540159Speterlinker_make_file(const char* pathname, void* priv, struct linker_file_ops* ops) 37625537Sdfr{ 37725537Sdfr linker_file_t lf = 0; 37825537Sdfr int namelen; 37940159Speter const char *filename; 38025537Sdfr 38140159Speter filename = rindex(pathname, '/'); 38240159Speter if (filename && filename[1]) 38340159Speter filename++; 38440159Speter else 38540159Speter filename = pathname; 38640159Speter 38725537Sdfr KLD_DPF(FILE, ("linker_make_file: new file, filename=%s\n", filename)); 38845356Speter lockmgr(&lock, LK_EXCLUSIVE, 0, curproc); 38925537Sdfr namelen = strlen(filename) + 1; 39025537Sdfr lf = malloc(sizeof(struct linker_file) + namelen, M_LINKER, M_WAITOK); 39125537Sdfr if (!lf) 39225537Sdfr goto out; 39340395Speter bzero(lf, sizeof(*lf)); 39425537Sdfr 39525537Sdfr lf->refs = 1; 39625537Sdfr lf->userrefs = 0; 39743185Sdfr lf->flags = 0; 39825537Sdfr lf->filename = (char*) (lf + 1); 39925537Sdfr strcpy(lf->filename, filename); 40025537Sdfr lf->id = next_file_id++; 40125537Sdfr lf->ndeps = 0; 40225537Sdfr lf->deps = NULL; 40325537Sdfr STAILQ_INIT(&lf->common); 40425537Sdfr TAILQ_INIT(&lf->modules); 40525537Sdfr 40625537Sdfr lf->priv = priv; 40725537Sdfr lf->ops = ops; 40825537Sdfr TAILQ_INSERT_TAIL(&files, lf, link); 40925537Sdfr 41025537Sdfrout: 41125537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 41225537Sdfr return lf; 41325537Sdfr} 41425537Sdfr 41525537Sdfrint 41625537Sdfrlinker_file_unload(linker_file_t file) 41725537Sdfr{ 41825537Sdfr module_t mod, next; 41925537Sdfr struct common_symbol* cp; 42025537Sdfr int error = 0; 42125537Sdfr int i; 42225537Sdfr 42340159Speter KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", file->refs)); 42445356Speter lockmgr(&lock, LK_EXCLUSIVE, 0, curproc); 42525537Sdfr if (file->refs == 1) { 42625537Sdfr KLD_DPF(FILE, ("linker_file_unload: file is unloading, informing modules\n")); 42725537Sdfr /* 42825537Sdfr * Inform any modules associated with this file. 42925537Sdfr */ 43025537Sdfr for (mod = TAILQ_FIRST(&file->modules); mod; mod = next) { 43125537Sdfr next = module_getfnext(mod); 43225537Sdfr 43325537Sdfr /* 43425537Sdfr * Give the module a chance to veto the unload. 43525537Sdfr */ 43643301Sdillon if ((error = module_unload(mod)) != 0) { 43725537Sdfr KLD_DPF(FILE, ("linker_file_unload: module %x vetoes unload\n", 43825537Sdfr mod)); 43925537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 44025537Sdfr goto out; 44125537Sdfr } 44225537Sdfr 44325537Sdfr module_release(mod); 44425537Sdfr } 44525537Sdfr } 44625537Sdfr 44725537Sdfr file->refs--; 44825537Sdfr if (file->refs > 0) { 44925537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 45025537Sdfr goto out; 45125537Sdfr } 45225537Sdfr 45343185Sdfr /* Don't try to run SYSUNINITs if we are unloaded due to a link error */ 45444078Sdfr if (file->flags & LINKER_FILE_LINKED) { 45543185Sdfr linker_file_sysuninit(file); 45644078Sdfr linker_file_unregister_sysctls(file); 45744078Sdfr } 45841055Speter 45925537Sdfr TAILQ_REMOVE(&files, file, link); 46025537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 46140159Speter 46225537Sdfr for (i = 0; i < file->ndeps; i++) 46325537Sdfr linker_file_unload(file->deps[i]); 46425537Sdfr free(file->deps, M_LINKER); 46525537Sdfr 46625537Sdfr for (cp = STAILQ_FIRST(&file->common); cp; 46725537Sdfr cp = STAILQ_FIRST(&file->common)) { 46825537Sdfr STAILQ_REMOVE(&file->common, cp, common_symbol, link); 46925537Sdfr free(cp, M_LINKER); 47025537Sdfr } 47125537Sdfr 47225537Sdfr file->ops->unload(file); 47325537Sdfr free(file, M_LINKER); 47425537Sdfr 47525537Sdfrout: 47625537Sdfr return error; 47725537Sdfr} 47825537Sdfr 47925537Sdfrint 48025537Sdfrlinker_file_add_dependancy(linker_file_t file, linker_file_t dep) 48125537Sdfr{ 48225537Sdfr linker_file_t* newdeps; 48325537Sdfr 48425537Sdfr newdeps = malloc((file->ndeps + 1) * sizeof(linker_file_t*), 48525537Sdfr M_LINKER, M_WAITOK); 48625537Sdfr if (newdeps == NULL) 48725537Sdfr return ENOMEM; 48840395Speter bzero(newdeps, (file->ndeps + 1) * sizeof(linker_file_t*)); 48925537Sdfr 49025537Sdfr if (file->deps) { 49125537Sdfr bcopy(file->deps, newdeps, file->ndeps * sizeof(linker_file_t*)); 49225537Sdfr free(file->deps, M_LINKER); 49325537Sdfr } 49425537Sdfr file->deps = newdeps; 49525537Sdfr file->deps[file->ndeps] = dep; 49625537Sdfr file->ndeps++; 49725537Sdfr 49825537Sdfr return 0; 49925537Sdfr} 50025537Sdfr 50125537Sdfrcaddr_t 50225537Sdfrlinker_file_lookup_symbol(linker_file_t file, const char* name, int deps) 50325537Sdfr{ 50443309Sdillon c_linker_sym_t sym; 50538275Sdfr linker_symval_t symval; 50642849Speter linker_file_t lf; 50725537Sdfr caddr_t address; 50825537Sdfr size_t common_size = 0; 50925537Sdfr int i; 51025537Sdfr 51140159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d\n", 51225537Sdfr file, name, deps)); 51325537Sdfr 51438275Sdfr if (file->ops->lookup_symbol(file, name, &sym) == 0) { 51538275Sdfr file->ops->symbol_values(file, sym, &symval); 51638275Sdfr if (symval.value == 0) 51725537Sdfr /* 51825537Sdfr * For commons, first look them up in the dependancies and 51925537Sdfr * only allocate space if not found there. 52025537Sdfr */ 52138275Sdfr common_size = symval.size; 52240159Speter else { 52340159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: symbol.value=%x\n", symval.value)); 52438275Sdfr return symval.value; 52540159Speter } 52638275Sdfr } 52725537Sdfr 52842849Speter if (deps) { 52925537Sdfr for (i = 0; i < file->ndeps; i++) { 53025537Sdfr address = linker_file_lookup_symbol(file->deps[i], name, 0); 53140159Speter if (address) { 53240159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: deps value=%x\n", address)); 53325537Sdfr return address; 53440159Speter } 53525537Sdfr } 53625537Sdfr 53742849Speter /* If we have not found it in the dependencies, search globally */ 53842849Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 53942849Speter /* But skip the current file if it's on the list */ 54042849Speter if (lf == file) 54142849Speter continue; 54242849Speter /* And skip the files we searched above */ 54342849Speter for (i = 0; i < file->ndeps; i++) 54442849Speter if (lf == file->deps[i]) 54542849Speter break; 54642849Speter if (i < file->ndeps) 54742849Speter continue; 54842849Speter address = linker_file_lookup_symbol(lf, name, 0); 54942849Speter if (address) { 55042849Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: global value=%x\n", address)); 55142849Speter return address; 55242849Speter } 55342849Speter } 55442849Speter } 55542849Speter 55625537Sdfr if (common_size > 0) { 55725537Sdfr /* 55825537Sdfr * This is a common symbol which was not found in the 55925537Sdfr * dependancies. We maintain a simple common symbol table in 56025537Sdfr * the file object. 56125537Sdfr */ 56225537Sdfr struct common_symbol* cp; 56325537Sdfr 56425537Sdfr for (cp = STAILQ_FIRST(&file->common); cp; 56525537Sdfr cp = STAILQ_NEXT(cp, link)) 56640159Speter if (!strcmp(cp->name, name)) { 56740159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: old common value=%x\n", cp->address)); 56825537Sdfr return cp->address; 56940159Speter } 57025537Sdfr 57125537Sdfr /* 57225537Sdfr * Round the symbol size up to align. 57325537Sdfr */ 57425537Sdfr common_size = (common_size + sizeof(int) - 1) & -sizeof(int); 57525537Sdfr cp = malloc(sizeof(struct common_symbol) 57625537Sdfr + common_size 57725537Sdfr + strlen(name) + 1, 57825537Sdfr M_LINKER, M_WAITOK); 57940159Speter if (!cp) { 58040159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: nomem\n")); 58125537Sdfr return 0; 58240159Speter } 58340395Speter bzero(cp, sizeof(struct common_symbol) + common_size + strlen(name)+ 1); 58425537Sdfr 58525537Sdfr cp->address = (caddr_t) (cp + 1); 58625537Sdfr cp->name = cp->address + common_size; 58725537Sdfr strcpy(cp->name, name); 58825537Sdfr bzero(cp->address, common_size); 58925537Sdfr STAILQ_INSERT_TAIL(&file->common, cp, link); 59025537Sdfr 59140159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: new common value=%x\n", cp->address)); 59225537Sdfr return cp->address; 59325537Sdfr } 59425537Sdfr 59540159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: fail\n")); 59625537Sdfr return 0; 59725537Sdfr} 59825537Sdfr 59940159Speter#ifdef DDB 60025537Sdfr/* 60140159Speter * DDB Helpers. DDB has to look across multiple files with their own 60240159Speter * symbol tables and string tables. 60340159Speter * 60440159Speter * Note that we do not obey list locking protocols here. We really don't 60540159Speter * need DDB to hang because somebody's got the lock held. We'll take the 60640159Speter * chance that the files list is inconsistant instead. 60740159Speter */ 60840159Speter 60940159Speterint 61043309Sdillonlinker_ddb_lookup(const char *symstr, c_linker_sym_t *sym) 61140159Speter{ 61240159Speter linker_file_t lf; 61340159Speter 61440159Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 61540159Speter if (lf->ops->lookup_symbol(lf, symstr, sym) == 0) 61640159Speter return 0; 61740159Speter } 61840159Speter return ENOENT; 61940159Speter} 62040159Speter 62140159Speterint 62243309Sdillonlinker_ddb_search_symbol(caddr_t value, c_linker_sym_t *sym, long *diffp) 62340159Speter{ 62440159Speter linker_file_t lf; 62540159Speter u_long off = (u_long)value; 62640159Speter u_long diff, bestdiff; 62743309Sdillon c_linker_sym_t best; 62843309Sdillon c_linker_sym_t es; 62940159Speter 63040159Speter best = 0; 63140159Speter bestdiff = off; 63240159Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 63340159Speter if (lf->ops->search_symbol(lf, value, &es, &diff) != 0) 63440159Speter continue; 63540159Speter if (es != 0 && diff < bestdiff) { 63640159Speter best = es; 63740159Speter bestdiff = diff; 63840159Speter } 63940159Speter if (bestdiff == 0) 64040159Speter break; 64140159Speter } 64240159Speter if (best) { 64340159Speter *sym = best; 64440159Speter *diffp = bestdiff; 64540159Speter return 0; 64640159Speter } else { 64740159Speter *sym = 0; 64840159Speter *diffp = off; 64940159Speter return ENOENT; 65040159Speter } 65140159Speter} 65240159Speter 65340159Speterint 65443309Sdillonlinker_ddb_symbol_values(c_linker_sym_t sym, linker_symval_t *symval) 65540159Speter{ 65640159Speter linker_file_t lf; 65740159Speter 65840159Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 65940159Speter if (lf->ops->symbol_values(lf, sym, symval) == 0) 66040159Speter return 0; 66140159Speter } 66240159Speter return ENOENT; 66340159Speter} 66440159Speter 66540159Speter#endif 66640159Speter 66740159Speter/* 66825537Sdfr * Syscalls. 66925537Sdfr */ 67025537Sdfr 67125537Sdfrint 67230994Sphkkldload(struct proc* p, struct kldload_args* uap) 67325537Sdfr{ 67442316Smsmith char* filename = NULL, *modulename; 67525537Sdfr linker_file_t lf; 67625537Sdfr int error = 0; 67725537Sdfr 67830994Sphk p->p_retval[0] = -1; 67925537Sdfr 68025537Sdfr if (securelevel > 0) 68125537Sdfr return EPERM; 68225537Sdfr 68346112Sphk if ((error = suser(p)) != 0) 68425537Sdfr return error; 68525537Sdfr 68625537Sdfr filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 68743301Sdillon if ((error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) != 0) 68825537Sdfr goto out; 68925537Sdfr 69042316Smsmith /* Can't load more than one module with the same name */ 69142316Smsmith modulename = rindex(filename, '/'); 69242316Smsmith if (modulename == NULL) 69342316Smsmith modulename = filename; 69444173Sdfr else 69544173Sdfr modulename++; 69642316Smsmith if (linker_find_file_by_name(modulename)) { 69742316Smsmith error = EEXIST; 69842316Smsmith goto out; 69942316Smsmith } 70042316Smsmith 70143301Sdillon if ((error = linker_load_file(filename, &lf)) != 0) 70225537Sdfr goto out; 70325537Sdfr 70425537Sdfr lf->userrefs++; 70530994Sphk p->p_retval[0] = lf->id; 70640159Speter 70725537Sdfrout: 70825537Sdfr if (filename) 70925537Sdfr free(filename, M_TEMP); 71025537Sdfr return error; 71125537Sdfr} 71225537Sdfr 71325537Sdfrint 71430994Sphkkldunload(struct proc* p, struct kldunload_args* uap) 71525537Sdfr{ 71625537Sdfr linker_file_t lf; 71725537Sdfr int error = 0; 71825537Sdfr 71925537Sdfr if (securelevel > 0) 72025537Sdfr return EPERM; 72125537Sdfr 72246112Sphk if ((error = suser(p)) != 0) 72325537Sdfr return error; 72425537Sdfr 72525537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 72625537Sdfr if (lf) { 72725537Sdfr KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs)); 72825537Sdfr if (lf->userrefs == 0) { 72943084Speter printf("linkerunload: attempt to unload file that was loaded by the kernel\n"); 73025537Sdfr error = EBUSY; 73125537Sdfr goto out; 73225537Sdfr } 73343084Speter lf->userrefs--; 73442837Speter error = linker_file_unload(lf); 73542837Speter if (error) 73643084Speter lf->userrefs++; 73725537Sdfr } else 73825537Sdfr error = ENOENT; 73925537Sdfr 74025537Sdfrout: 74125537Sdfr return error; 74225537Sdfr} 74325537Sdfr 74425537Sdfrint 74530994Sphkkldfind(struct proc* p, struct kldfind_args* uap) 74625537Sdfr{ 74742316Smsmith char* filename = NULL, *modulename; 74825537Sdfr linker_file_t lf; 74925537Sdfr int error = 0; 75025537Sdfr 75130994Sphk p->p_retval[0] = -1; 75225537Sdfr 75325537Sdfr filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 75443301Sdillon if ((error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) != 0) 75525537Sdfr goto out; 75625537Sdfr 75742316Smsmith modulename = rindex(filename, '/'); 75842316Smsmith if (modulename == NULL) 75942316Smsmith modulename = filename; 76042316Smsmith 76142316Smsmith lf = linker_find_file_by_name(modulename); 76225537Sdfr if (lf) 76330994Sphk p->p_retval[0] = lf->id; 76425537Sdfr else 76525537Sdfr error = ENOENT; 76640159Speter 76725537Sdfrout: 76825537Sdfr if (filename) 76925537Sdfr free(filename, M_TEMP); 77025537Sdfr return error; 77125537Sdfr} 77225537Sdfr 77325537Sdfrint 77430994Sphkkldnext(struct proc* p, struct kldnext_args* uap) 77525537Sdfr{ 77625537Sdfr linker_file_t lf; 77725537Sdfr int error = 0; 77825537Sdfr 77925537Sdfr if (SCARG(uap, fileid) == 0) { 78025537Sdfr if (TAILQ_FIRST(&files)) 78130994Sphk p->p_retval[0] = TAILQ_FIRST(&files)->id; 78225537Sdfr else 78330994Sphk p->p_retval[0] = 0; 78425537Sdfr return 0; 78525537Sdfr } 78640159Speter 78725537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 78825537Sdfr if (lf) { 78925537Sdfr if (TAILQ_NEXT(lf, link)) 79030994Sphk p->p_retval[0] = TAILQ_NEXT(lf, link)->id; 79125537Sdfr else 79230994Sphk p->p_retval[0] = 0; 79325537Sdfr } else 79425537Sdfr error = ENOENT; 79525537Sdfr 79625537Sdfr return error; 79725537Sdfr} 79825537Sdfr 79925537Sdfrint 80030994Sphkkldstat(struct proc* p, struct kldstat_args* uap) 80125537Sdfr{ 80225537Sdfr linker_file_t lf; 80325537Sdfr int error = 0; 80425537Sdfr int version; 80525537Sdfr struct kld_file_stat* stat; 80625537Sdfr int namelen; 80725537Sdfr 80825537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 80925537Sdfr if (!lf) { 81025537Sdfr error = ENOENT; 81125537Sdfr goto out; 81225537Sdfr } 81325537Sdfr 81425537Sdfr stat = SCARG(uap, stat); 81525537Sdfr 81625537Sdfr /* 81725537Sdfr * Check the version of the user's structure. 81825537Sdfr */ 81943301Sdillon if ((error = copyin(&stat->version, &version, sizeof(version))) != 0) 82025537Sdfr goto out; 82125537Sdfr if (version != sizeof(struct kld_file_stat)) { 82225537Sdfr error = EINVAL; 82325537Sdfr goto out; 82425537Sdfr } 82525537Sdfr 82625537Sdfr namelen = strlen(lf->filename) + 1; 82725537Sdfr if (namelen > MAXPATHLEN) 82825537Sdfr namelen = MAXPATHLEN; 82943301Sdillon if ((error = copyout(lf->filename, &stat->name[0], namelen)) != 0) 83025537Sdfr goto out; 83143301Sdillon if ((error = copyout(&lf->refs, &stat->refs, sizeof(int))) != 0) 83225537Sdfr goto out; 83343301Sdillon if ((error = copyout(&lf->id, &stat->id, sizeof(int))) != 0) 83425537Sdfr goto out; 83543301Sdillon if ((error = copyout(&lf->address, &stat->address, sizeof(caddr_t))) != 0) 83625537Sdfr goto out; 83743301Sdillon if ((error = copyout(&lf->size, &stat->size, sizeof(size_t))) != 0) 83825537Sdfr goto out; 83925537Sdfr 84030994Sphk p->p_retval[0] = 0; 84125537Sdfr 84225537Sdfrout: 84325537Sdfr return error; 84425537Sdfr} 84525537Sdfr 84625537Sdfrint 84730994Sphkkldfirstmod(struct proc* p, struct kldfirstmod_args* uap) 84825537Sdfr{ 84925537Sdfr linker_file_t lf; 85025537Sdfr int error = 0; 85125537Sdfr 85225537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 85325537Sdfr if (lf) { 85425537Sdfr if (TAILQ_FIRST(&lf->modules)) 85530994Sphk p->p_retval[0] = module_getid(TAILQ_FIRST(&lf->modules)); 85625537Sdfr else 85730994Sphk p->p_retval[0] = 0; 85825537Sdfr } else 85925537Sdfr error = ENOENT; 86025537Sdfr 86125537Sdfr return error; 86225537Sdfr} 86340159Speter 86441090Speterint 86541090Speterkldsym(struct proc *p, struct kldsym_args *uap) 86641090Speter{ 86741090Speter char *symstr = NULL; 86843309Sdillon c_linker_sym_t sym; 86941090Speter linker_symval_t symval; 87041090Speter linker_file_t lf; 87141090Speter struct kld_sym_lookup lookup; 87241090Speter int error = 0; 87341090Speter 87443301Sdillon if ((error = copyin(SCARG(uap, data), &lookup, sizeof(lookup))) != 0) 87541090Speter goto out; 87641090Speter if (lookup.version != sizeof(lookup) || SCARG(uap, cmd) != KLDSYM_LOOKUP) { 87741090Speter error = EINVAL; 87841090Speter goto out; 87941090Speter } 88041090Speter 88141090Speter symstr = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 88243301Sdillon if ((error = copyinstr(lookup.symname, symstr, MAXPATHLEN, NULL)) != 0) 88341090Speter goto out; 88441090Speter 88541090Speter if (SCARG(uap, fileid) != 0) { 88641090Speter lf = linker_find_file_by_id(SCARG(uap, fileid)); 88741090Speter if (lf == NULL) { 88841090Speter error = ENOENT; 88941090Speter goto out; 89041090Speter } 89141090Speter if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 && 89241090Speter lf->ops->symbol_values(lf, sym, &symval) == 0) { 89341090Speter lookup.symvalue = (u_long)symval.value; 89441090Speter lookup.symsize = symval.size; 89541090Speter error = copyout(&lookup, SCARG(uap, data), sizeof(lookup)); 89641090Speter } else 89741090Speter error = ENOENT; 89841090Speter } else { 89941090Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 90041090Speter if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 && 90141090Speter lf->ops->symbol_values(lf, sym, &symval) == 0) { 90241090Speter lookup.symvalue = (u_long)symval.value; 90341090Speter lookup.symsize = symval.size; 90441090Speter error = copyout(&lookup, SCARG(uap, data), sizeof(lookup)); 90541090Speter break; 90641090Speter } 90741090Speter } 90841090Speter if (!lf) 90941090Speter error = ENOENT; 91041090Speter } 91141090Speterout: 91241090Speter if (symstr) 91341090Speter free(symstr, M_TEMP); 91441090Speter return error; 91541090Speter} 91641090Speter 91740159Speter/* 91840159Speter * Preloaded module support 91940159Speter */ 92040159Speter 92140159Speterstatic void 92240159Speterlinker_preload(void* arg) 92340159Speter{ 92440159Speter caddr_t modptr; 92540159Speter char *modname; 92640162Speter char *modtype; 92740159Speter linker_file_t lf; 92840159Speter linker_class_t lc; 92940159Speter int error; 93040159Speter struct linker_set *sysinits; 93140159Speter struct sysinit **sipp; 93246693Speter const moduledata_t *moddata; 93340159Speter 93440159Speter modptr = NULL; 93540159Speter while ((modptr = preload_search_next_name(modptr)) != NULL) { 93640159Speter modname = (char *)preload_search_info(modptr, MODINFO_NAME); 93740162Speter modtype = (char *)preload_search_info(modptr, MODINFO_TYPE); 93840159Speter if (modname == NULL) { 93940625Smsmith printf("Preloaded module at %p does not have a name!\n", modptr); 94040159Speter continue; 94140159Speter } 94240162Speter if (modtype == NULL) { 94340625Smsmith printf("Preloaded module at %p does not have a type!\n", modptr); 94440162Speter continue; 94540162Speter } 94640625Smsmith printf("Preloaded %s \"%s\" at %p.\n", modtype, modname, modptr); 94740159Speter lf = linker_find_file_by_name(modname); 94840159Speter if (lf) { 94940159Speter lf->userrefs++; 95040159Speter continue; 95140159Speter } 95240159Speter lf = NULL; 95340159Speter for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) { 95440159Speter error = lc->ops->load_file(modname, &lf); 95540159Speter if (error) { 95640159Speter lf = NULL; 95740159Speter break; 95840159Speter } 95940159Speter } 96040159Speter if (lf) { 96140159Speter lf->userrefs++; 96240159Speter 96340159Speter sysinits = (struct linker_set*) 96440159Speter linker_file_lookup_symbol(lf, "sysinit_set", 0); 96540159Speter if (sysinits) { 96640159Speter /* HACK ALERT! 96740159Speter * This is to set the sysinit moduledata so that the module 96840159Speter * can attach itself to the correct containing file. 96940159Speter * The sysinit could be run at *any* time. 97040159Speter */ 97140159Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 97240159Speter if ((*sipp)->func == module_register_init) { 97340159Speter moddata = (*sipp)->udata; 97446693Speter error = module_register(moddata, lf); 97546693Speter if (error) 97646693Speter printf("Preloaded %s \"%s\" failed to register: %d\n", 97746693Speter modtype, modname, error); 97840159Speter } 97940159Speter } 98040159Speter sysinit_add((struct sysinit **)sysinits->ls_items); 98140159Speter } 98244078Sdfr linker_file_register_sysctls(lf); 98340159Speter } 98440159Speter } 98540159Speter} 98640159Speter 98740159SpeterSYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, 0); 98840159Speter 98940159Speter/* 99040159Speter * Search for a not-loaded module by name. 99140159Speter * 99240159Speter * Modules may be found in the following locations: 99340159Speter * 99440159Speter * - preloaded (result is just the module name) 99540159Speter * - on disk (result is full path to module) 99640159Speter * 99740159Speter * If the module name is qualified in any way (contains path, etc.) 99840159Speter * the we simply return a copy of it. 99940159Speter * 100040159Speter * The search path can be manipulated via sysctl. Note that we use the ';' 100140159Speter * character as a separator to be consistent with the bootloader. 100240159Speter */ 100340159Speter 100440159Speterstatic char linker_path[MAXPATHLEN + 1] = "/;/boot/;/modules/"; 100540159Speter 100640159SpeterSYSCTL_STRING(_kern, OID_AUTO, module_path, CTLFLAG_RW, linker_path, 100740159Speter sizeof(linker_path), "module load search path"); 100840159Speter 100940159Speterstatic char * 101040159Speterlinker_strdup(const char *str) 101140159Speter{ 101240159Speter char *result; 101340159Speter 101440159Speter if ((result = malloc((strlen(str) + 1), M_LINKER, M_WAITOK)) != NULL) 101540159Speter strcpy(result, str); 101640159Speter return(result); 101740159Speter} 101840159Speter 101940159Speterchar * 102040159Speterlinker_search_path(const char *name) 102140159Speter{ 102240159Speter struct nameidata nd; 102340159Speter struct proc *p = curproc; /* XXX */ 102440159Speter char *cp, *ep, *result; 102540159Speter int error; 102640159Speter enum vtype type; 102740159Speter 102840159Speter /* qualified at all? */ 102940159Speter if (index(name, '/')) 103040159Speter return(linker_strdup(name)); 103140159Speter 103240159Speter /* traverse the linker path */ 103340159Speter cp = linker_path; 103440159Speter for (;;) { 103540159Speter 103640159Speter /* find the end of this component */ 103740159Speter for (ep = cp; (*ep != 0) && (*ep != ';'); ep++) 103840159Speter ; 103940159Speter result = malloc((strlen(name) + (ep - cp) + 1), M_LINKER, M_WAITOK); 104040159Speter if (result == NULL) /* actually ENOMEM */ 104140159Speter return(NULL); 104240159Speter 104340159Speter strncpy(result, cp, ep - cp); 104440159Speter strcpy(result + (ep - cp), name); 104540159Speter 104640159Speter /* 104740159Speter * Attempt to open the file, and return the path if we succeed and it's 104840159Speter * a regular file. 104940159Speter */ 105040159Speter NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, result, p); 105140159Speter error = vn_open(&nd, FREAD, 0); 105240159Speter if (error == 0) { 105340159Speter type = nd.ni_vp->v_type; 105440159Speter VOP_UNLOCK(nd.ni_vp, 0, p); 105540159Speter vn_close(nd.ni_vp, FREAD, p->p_ucred, p); 105640159Speter if (type == VREG) 105740159Speter return(result); 105840159Speter } 105940159Speter free(result, M_LINKER); 106040159Speter 106140159Speter if (*ep == 0) 106240159Speter break; 106340159Speter cp = ep + 1; 106440159Speter } 106540159Speter return(NULL); 106640159Speter} 1067