kern_linker.c revision 40859
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 * 2640859Speter * $Id: kern_linker.c,v 1.11 1998/10/24 18:35:09 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 5032153SbdeMALLOC_DEFINE(M_LINKER, "kld", "kernel linker"); 5131324Sbdelinker_file_t linker_current_file; 5231324Sbde 5325537Sdfrstatic struct lock lock; /* lock for the file list */ 5425537Sdfrstatic linker_class_list_t classes; 5525537Sdfrstatic linker_file_list_t files; 5625537Sdfrstatic int next_file_id = 1; 5725537Sdfr 5825537Sdfrstatic void 5925537Sdfrlinker_init(void* arg) 6025537Sdfr{ 6125537Sdfr lockinit(&lock, PVM, "klink", 0, 0); 6225537Sdfr TAILQ_INIT(&classes); 6325537Sdfr TAILQ_INIT(&files); 6425537Sdfr} 6525537Sdfr 6640159SpeterSYSINIT(linker, SI_SUB_KLD, SI_ORDER_FIRST, linker_init, 0); 6725537Sdfr 6825537Sdfrint 6925537Sdfrlinker_add_class(const char* desc, void* priv, 7025537Sdfr struct linker_class_ops* ops) 7125537Sdfr{ 7225537Sdfr linker_class_t lc; 7325537Sdfr 7425537Sdfr lc = malloc(sizeof(struct linker_class), M_LINKER, M_NOWAIT); 7525537Sdfr if (!lc) 7625537Sdfr return ENOMEM; 7740395Speter bzero(lc, sizeof(*lc)); 7825537Sdfr 7925537Sdfr lc->desc = desc; 8025537Sdfr lc->priv = priv; 8125537Sdfr lc->ops = ops; 8225537Sdfr TAILQ_INSERT_HEAD(&classes, lc, link); 8325537Sdfr 8425537Sdfr return 0; 8525537Sdfr} 8625537Sdfr 8725537Sdfrstatic void 8825537Sdfrlinker_file_sysinit(linker_file_t lf) 8925537Sdfr{ 9025537Sdfr struct linker_set* sysinits; 9125537Sdfr struct sysinit** sipp; 9225537Sdfr struct sysinit** xipp; 9325537Sdfr struct sysinit* save; 9440159Speter moduledata_t *moddata; 9525537Sdfr 9625537Sdfr KLD_DPF(FILE, ("linker_file_sysinit: calling SYSINITs for %s\n", 9725537Sdfr lf->filename)); 9825537Sdfr 9925537Sdfr sysinits = (struct linker_set*) 10025537Sdfr linker_file_lookup_symbol(lf, "sysinit_set", 0); 10140159Speter 10240159Speter KLD_DPF(FILE, ("linker_file_sysinit: SYSINITs %p\n", sysinits)); 10325537Sdfr if (!sysinits) 10425537Sdfr return; 10525537Sdfr 10640159Speter /* HACK ALERT! */ 10740159Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 10840159Speter if ((*sipp)->func == module_register_init) { 10940159Speter moddata = (*sipp)->udata; 11040159Speter moddata->_file = lf; 11140159Speter } 11240159Speter } 11340159Speter 11425537Sdfr /* 11525537Sdfr * Perform a bubble sort of the system initialization objects by 11625537Sdfr * their subsystem (primary key) and order (secondary key). 11725537Sdfr * 11825537Sdfr * Since some things care about execution order, this is the 11925537Sdfr * operation which ensures continued function. 12025537Sdfr */ 12125537Sdfr for( sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 12225537Sdfr for( xipp = sipp + 1; *xipp; xipp++) { 12325537Sdfr if( (*sipp)->subsystem < (*xipp)->subsystem || 12425537Sdfr ( (*sipp)->subsystem == (*xipp)->subsystem && 12525537Sdfr (*sipp)->order < (*xipp)->order)) 12625537Sdfr continue; /* skip*/ 12725537Sdfr save = *sipp; 12825537Sdfr *sipp = *xipp; 12925537Sdfr *xipp = save; 13025537Sdfr } 13125537Sdfr } 13225537Sdfr 13325537Sdfr 13425537Sdfr /* 13525537Sdfr * Traverse the (now) ordered list of system initialization tasks. 13625537Sdfr * Perform each task, and continue on to the next task. 13725537Sdfr * 13825537Sdfr * The last item on the list is expected to be the scheduler, 13925537Sdfr * which will not return. 14025537Sdfr */ 14125537Sdfr for( sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 14225537Sdfr if( (*sipp)->subsystem == SI_SUB_DUMMY) 14325537Sdfr continue; /* skip dummy task(s)*/ 14425537Sdfr 14525537Sdfr switch( (*sipp)->type) { 14625537Sdfr case SI_TYPE_DEFAULT: 14725537Sdfr /* no special processing*/ 14825537Sdfr (*((*sipp)->func))( (*sipp)->udata); 14925537Sdfr break; 15025537Sdfr 15125537Sdfr case SI_TYPE_KTHREAD: 15231675Sdyson#if !defined(SMP) 15325537Sdfr /* kernel thread*/ 15431675Sdyson if (fork1(&proc0, RFFDG|RFPROC|RFMEM)) 15531675Sdyson panic("fork kernel thread"); 15631675Sdyson cpu_set_fork_handler(pfind(proc0.p_retval[0]), 15731675Sdyson (*sipp)->func, (*sipp)->udata); 15831675Sdyson break; 15931675Sdyson#endif 16031675Sdyson 16131675Sdyson case SI_TYPE_KPROCESS: 16231675Sdyson /* kernel thread*/ 16331675Sdyson if (fork1(&proc0, RFFDG|RFPROC)) 16425537Sdfr panic("fork kernel process"); 16530994Sphk cpu_set_fork_handler(pfind(proc0.p_retval[0]), 16630994Sphk (*sipp)->func, (*sipp)->udata); 16725537Sdfr break; 16825537Sdfr 16925537Sdfr default: 17025537Sdfr panic( "linker_file_sysinit: unrecognized init type"); 17125537Sdfr } 17225537Sdfr } 17325537Sdfr} 17425537Sdfr 17525537Sdfrint 17625537Sdfrlinker_load_file(const char* filename, linker_file_t* result) 17725537Sdfr{ 17825537Sdfr linker_class_t lc; 17925537Sdfr linker_file_t lf; 18025537Sdfr int error = 0; 18125537Sdfr 18225537Sdfr lf = linker_find_file_by_name(filename); 18325537Sdfr if (lf) { 18425537Sdfr KLD_DPF(FILE, ("linker_load_file: file %s is already loaded, incrementing refs\n", filename)); 18525537Sdfr *result = lf; 18625537Sdfr lf->refs++; 18725537Sdfr goto out; 18825537Sdfr } 18925537Sdfr 19025537Sdfr lf = NULL; 19125537Sdfr for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) { 19225537Sdfr KLD_DPF(FILE, ("linker_load_file: trying to load %s as %s\n", 19325537Sdfr filename, lc->desc)); 19425537Sdfr if (error = lc->ops->load_file(filename, &lf)) 19525537Sdfr goto out; 19625537Sdfr if (lf) { 19725537Sdfr linker_file_sysinit(lf); 19825537Sdfr 19925537Sdfr *result = lf; 20025537Sdfr goto out; 20125537Sdfr } 20225537Sdfr } 20325537Sdfr error = ENOEXEC; /* format not recognised */ 20425537Sdfr 20525537Sdfrout: 20625537Sdfr return error; 20725537Sdfr} 20825537Sdfr 20925537Sdfrlinker_file_t 21025537Sdfrlinker_find_file_by_name(const char* filename) 21125537Sdfr{ 21225537Sdfr linker_file_t lf = 0; 21325537Sdfr 21425537Sdfr lockmgr(&lock, LK_SHARED, 0, curproc); 21525537Sdfr for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) 21625537Sdfr if (!strcmp(lf->filename, filename)) 21725537Sdfr break; 21825537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 21925537Sdfr 22025537Sdfr return lf; 22125537Sdfr} 22225537Sdfr 22325537Sdfrlinker_file_t 22425537Sdfrlinker_find_file_by_id(int fileid) 22525537Sdfr{ 22625537Sdfr linker_file_t lf = 0; 22725537Sdfr 22825537Sdfr lockmgr(&lock, LK_SHARED, 0, curproc); 22925537Sdfr for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) 23025537Sdfr if (lf->id == fileid) 23125537Sdfr break; 23225537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 23325537Sdfr 23425537Sdfr return lf; 23525537Sdfr} 23625537Sdfr 23725537Sdfrlinker_file_t 23840159Speterlinker_make_file(const char* pathname, void* priv, struct linker_file_ops* ops) 23925537Sdfr{ 24025537Sdfr linker_file_t lf = 0; 24125537Sdfr int namelen; 24240159Speter const char *filename; 24325537Sdfr 24440159Speter filename = rindex(pathname, '/'); 24540159Speter if (filename && filename[1]) 24640159Speter filename++; 24740159Speter else 24840159Speter filename = pathname; 24940159Speter 25025537Sdfr KLD_DPF(FILE, ("linker_make_file: new file, filename=%s\n", filename)); 25125537Sdfr lockmgr(&lock, LK_EXCLUSIVE|LK_RETRY, 0, curproc); 25225537Sdfr namelen = strlen(filename) + 1; 25325537Sdfr lf = malloc(sizeof(struct linker_file) + namelen, M_LINKER, M_WAITOK); 25425537Sdfr if (!lf) 25525537Sdfr goto out; 25640395Speter bzero(lf, sizeof(*lf)); 25725537Sdfr 25825537Sdfr lf->refs = 1; 25925537Sdfr lf->userrefs = 0; 26025537Sdfr lf->filename = (char*) (lf + 1); 26125537Sdfr strcpy(lf->filename, filename); 26225537Sdfr lf->id = next_file_id++; 26325537Sdfr lf->ndeps = 0; 26425537Sdfr lf->deps = NULL; 26525537Sdfr STAILQ_INIT(&lf->common); 26625537Sdfr TAILQ_INIT(&lf->modules); 26725537Sdfr 26825537Sdfr lf->priv = priv; 26925537Sdfr lf->ops = ops; 27025537Sdfr TAILQ_INSERT_TAIL(&files, lf, link); 27125537Sdfr 27225537Sdfrout: 27325537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 27425537Sdfr return lf; 27525537Sdfr} 27625537Sdfr 27725537Sdfrint 27825537Sdfrlinker_file_unload(linker_file_t file) 27925537Sdfr{ 28025537Sdfr module_t mod, next; 28125537Sdfr struct common_symbol* cp; 28225537Sdfr int error = 0; 28325537Sdfr int i; 28425537Sdfr 28540159Speter KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", file->refs)); 28625537Sdfr lockmgr(&lock, LK_EXCLUSIVE|LK_RETRY, 0, curproc); 28725537Sdfr if (file->refs == 1) { 28825537Sdfr KLD_DPF(FILE, ("linker_file_unload: file is unloading, informing modules\n")); 28925537Sdfr /* 29025537Sdfr * Inform any modules associated with this file. 29125537Sdfr */ 29225537Sdfr for (mod = TAILQ_FIRST(&file->modules); mod; mod = next) { 29325537Sdfr next = module_getfnext(mod); 29425537Sdfr 29525537Sdfr /* 29625537Sdfr * Give the module a chance to veto the unload. 29725537Sdfr */ 29825537Sdfr if (error = module_unload(mod)) { 29925537Sdfr KLD_DPF(FILE, ("linker_file_unload: module %x vetoes unload\n", 30025537Sdfr mod)); 30125537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 30225537Sdfr goto out; 30325537Sdfr } 30425537Sdfr 30525537Sdfr module_release(mod); 30625537Sdfr } 30725537Sdfr } 30825537Sdfr 30925537Sdfr file->refs--; 31025537Sdfr if (file->refs > 0) { 31125537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 31225537Sdfr goto out; 31325537Sdfr } 31425537Sdfr 31525537Sdfr TAILQ_REMOVE(&files, file, link); 31625537Sdfr lockmgr(&lock, LK_RELEASE, 0, curproc); 31740159Speter 31825537Sdfr for (i = 0; i < file->ndeps; i++) 31925537Sdfr linker_file_unload(file->deps[i]); 32025537Sdfr free(file->deps, M_LINKER); 32125537Sdfr 32225537Sdfr for (cp = STAILQ_FIRST(&file->common); cp; 32325537Sdfr cp = STAILQ_FIRST(&file->common)) { 32425537Sdfr STAILQ_REMOVE(&file->common, cp, common_symbol, link); 32525537Sdfr free(cp, M_LINKER); 32625537Sdfr } 32725537Sdfr 32825537Sdfr file->ops->unload(file); 32925537Sdfr free(file, M_LINKER); 33025537Sdfr 33125537Sdfrout: 33225537Sdfr return error; 33325537Sdfr} 33425537Sdfr 33525537Sdfrint 33625537Sdfrlinker_file_add_dependancy(linker_file_t file, linker_file_t dep) 33725537Sdfr{ 33825537Sdfr linker_file_t* newdeps; 33925537Sdfr 34025537Sdfr newdeps = malloc((file->ndeps + 1) * sizeof(linker_file_t*), 34125537Sdfr M_LINKER, M_WAITOK); 34225537Sdfr if (newdeps == NULL) 34325537Sdfr return ENOMEM; 34440395Speter bzero(newdeps, (file->ndeps + 1) * sizeof(linker_file_t*)); 34525537Sdfr 34625537Sdfr if (file->deps) { 34725537Sdfr bcopy(file->deps, newdeps, file->ndeps * sizeof(linker_file_t*)); 34825537Sdfr free(file->deps, M_LINKER); 34925537Sdfr } 35025537Sdfr file->deps = newdeps; 35125537Sdfr file->deps[file->ndeps] = dep; 35225537Sdfr file->ndeps++; 35325537Sdfr 35425537Sdfr return 0; 35525537Sdfr} 35625537Sdfr 35725537Sdfrcaddr_t 35825537Sdfrlinker_file_lookup_symbol(linker_file_t file, const char* name, int deps) 35925537Sdfr{ 36038275Sdfr linker_sym_t sym; 36138275Sdfr linker_symval_t symval; 36225537Sdfr caddr_t address; 36325537Sdfr size_t common_size = 0; 36425537Sdfr int i; 36525537Sdfr 36640159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d\n", 36725537Sdfr file, name, deps)); 36825537Sdfr 36938275Sdfr if (file->ops->lookup_symbol(file, name, &sym) == 0) { 37038275Sdfr file->ops->symbol_values(file, sym, &symval); 37138275Sdfr if (symval.value == 0) 37225537Sdfr /* 37325537Sdfr * For commons, first look them up in the dependancies and 37425537Sdfr * only allocate space if not found there. 37525537Sdfr */ 37638275Sdfr common_size = symval.size; 37740159Speter else { 37840159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: symbol.value=%x\n", symval.value)); 37938275Sdfr return symval.value; 38040159Speter } 38138275Sdfr } 38225537Sdfr 38325537Sdfr if (deps) 38425537Sdfr for (i = 0; i < file->ndeps; i++) { 38525537Sdfr address = linker_file_lookup_symbol(file->deps[i], name, 0); 38640159Speter if (address) { 38740159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: deps value=%x\n", address)); 38825537Sdfr return address; 38940159Speter } 39025537Sdfr } 39125537Sdfr 39225537Sdfr if (common_size > 0) { 39325537Sdfr /* 39425537Sdfr * This is a common symbol which was not found in the 39525537Sdfr * dependancies. We maintain a simple common symbol table in 39625537Sdfr * the file object. 39725537Sdfr */ 39825537Sdfr struct common_symbol* cp; 39925537Sdfr 40025537Sdfr for (cp = STAILQ_FIRST(&file->common); cp; 40125537Sdfr cp = STAILQ_NEXT(cp, link)) 40240159Speter if (!strcmp(cp->name, name)) { 40340159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: old common value=%x\n", cp->address)); 40425537Sdfr return cp->address; 40540159Speter } 40625537Sdfr 40725537Sdfr /* 40825537Sdfr * Round the symbol size up to align. 40925537Sdfr */ 41025537Sdfr common_size = (common_size + sizeof(int) - 1) & -sizeof(int); 41125537Sdfr cp = malloc(sizeof(struct common_symbol) 41225537Sdfr + common_size 41325537Sdfr + strlen(name) + 1, 41425537Sdfr M_LINKER, M_WAITOK); 41540159Speter if (!cp) { 41640159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: nomem\n")); 41725537Sdfr return 0; 41840159Speter } 41940395Speter bzero(cp, sizeof(struct common_symbol) + common_size + strlen(name)+ 1); 42025537Sdfr 42125537Sdfr cp->address = (caddr_t) (cp + 1); 42225537Sdfr cp->name = cp->address + common_size; 42325537Sdfr strcpy(cp->name, name); 42425537Sdfr bzero(cp->address, common_size); 42525537Sdfr STAILQ_INSERT_TAIL(&file->common, cp, link); 42625537Sdfr 42740159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: new common value=%x\n", cp->address)); 42825537Sdfr return cp->address; 42925537Sdfr } 43025537Sdfr 43140159Speter KLD_DPF(SYM, ("linker_file_lookup_symbol: fail\n")); 43225537Sdfr return 0; 43325537Sdfr} 43425537Sdfr 43540159Speter#ifdef DDB 43625537Sdfr/* 43740159Speter * DDB Helpers. DDB has to look across multiple files with their own 43840159Speter * symbol tables and string tables. 43940159Speter * 44040159Speter * Note that we do not obey list locking protocols here. We really don't 44140159Speter * need DDB to hang because somebody's got the lock held. We'll take the 44240159Speter * chance that the files list is inconsistant instead. 44340159Speter */ 44440159Speter 44540159Speterint 44640159Speterlinker_ddb_lookup(char *symstr, linker_sym_t *sym) 44740159Speter{ 44840159Speter linker_file_t lf; 44940159Speter 45040159Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 45140159Speter if (lf->ops->lookup_symbol(lf, symstr, sym) == 0) 45240159Speter return 0; 45340159Speter } 45440159Speter return ENOENT; 45540159Speter} 45640159Speter 45740159Speterint 45840159Speterlinker_ddb_search_symbol(caddr_t value, linker_sym_t *sym, long *diffp) 45940159Speter{ 46040159Speter linker_file_t lf; 46140159Speter u_long off = (u_long)value; 46240159Speter u_long diff, bestdiff; 46340159Speter linker_sym_t best; 46440159Speter linker_sym_t es; 46540159Speter 46640159Speter best = 0; 46740159Speter bestdiff = off; 46840159Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 46940159Speter if (lf->ops->search_symbol(lf, value, &es, &diff) != 0) 47040159Speter continue; 47140159Speter if (es != 0 && diff < bestdiff) { 47240159Speter best = es; 47340159Speter bestdiff = diff; 47440159Speter } 47540159Speter if (bestdiff == 0) 47640159Speter break; 47740159Speter } 47840159Speter if (best) { 47940159Speter *sym = best; 48040159Speter *diffp = bestdiff; 48140159Speter return 0; 48240159Speter } else { 48340159Speter *sym = 0; 48440159Speter *diffp = off; 48540159Speter return ENOENT; 48640159Speter } 48740159Speter} 48840159Speter 48940159Speterint 49040159Speterlinker_ddb_symbol_values(linker_sym_t sym, linker_symval_t *symval) 49140159Speter{ 49240159Speter linker_file_t lf; 49340159Speter 49440159Speter for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) { 49540159Speter if (lf->ops->symbol_values(lf, sym, symval) == 0) 49640159Speter return 0; 49740159Speter } 49840159Speter return ENOENT; 49940159Speter} 50040159Speter 50140159Speter#endif 50240159Speter 50340159Speter/* 50425537Sdfr * Syscalls. 50525537Sdfr */ 50625537Sdfr 50725537Sdfrint 50830994Sphkkldload(struct proc* p, struct kldload_args* uap) 50925537Sdfr{ 51025537Sdfr char* filename = NULL; 51125537Sdfr linker_file_t lf; 51225537Sdfr int error = 0; 51325537Sdfr 51430994Sphk p->p_retval[0] = -1; 51525537Sdfr 51625537Sdfr if (securelevel > 0) 51725537Sdfr return EPERM; 51825537Sdfr 51925537Sdfr if (error = suser(p->p_ucred, &p->p_acflag)) 52025537Sdfr return error; 52125537Sdfr 52225537Sdfr filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 52325537Sdfr if (error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) 52425537Sdfr goto out; 52525537Sdfr 52640859Speter if (error = linker_load_file(filename, &lf)) 52725537Sdfr goto out; 52825537Sdfr 52925537Sdfr lf->userrefs++; 53030994Sphk p->p_retval[0] = lf->id; 53140159Speter 53225537Sdfrout: 53325537Sdfr if (filename) 53425537Sdfr free(filename, M_TEMP); 53525537Sdfr return error; 53625537Sdfr} 53725537Sdfr 53825537Sdfrint 53930994Sphkkldunload(struct proc* p, struct kldunload_args* uap) 54025537Sdfr{ 54125537Sdfr linker_file_t lf; 54225537Sdfr int error = 0; 54325537Sdfr 54425537Sdfr if (securelevel > 0) 54525537Sdfr return EPERM; 54625537Sdfr 54725537Sdfr if (error = suser(p->p_ucred, &p->p_acflag)) 54825537Sdfr return error; 54925537Sdfr 55025537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 55125537Sdfr if (lf) { 55225537Sdfr KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs)); 55325537Sdfr if (lf->userrefs == 0) { 55425537Sdfr printf("linkerunload: attempt to unload file which was not loaded by user\n"); 55525537Sdfr error = EBUSY; 55625537Sdfr goto out; 55725537Sdfr } 55825537Sdfr lf->userrefs--; 55925537Sdfr error = linker_file_unload(lf); 56025537Sdfr } else 56125537Sdfr error = ENOENT; 56225537Sdfr 56325537Sdfrout: 56425537Sdfr return error; 56525537Sdfr} 56625537Sdfr 56725537Sdfrint 56830994Sphkkldfind(struct proc* p, struct kldfind_args* uap) 56925537Sdfr{ 57025537Sdfr char* filename = NULL; 57125537Sdfr linker_file_t lf; 57225537Sdfr int error = 0; 57325537Sdfr 57430994Sphk p->p_retval[0] = -1; 57525537Sdfr 57625537Sdfr filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 57725537Sdfr if (error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) 57825537Sdfr goto out; 57925537Sdfr 58025537Sdfr lf = linker_find_file_by_name(filename); 58125537Sdfr if (lf) 58230994Sphk p->p_retval[0] = lf->id; 58325537Sdfr else 58425537Sdfr error = ENOENT; 58540159Speter 58625537Sdfrout: 58725537Sdfr if (filename) 58825537Sdfr free(filename, M_TEMP); 58925537Sdfr return error; 59025537Sdfr} 59125537Sdfr 59225537Sdfrint 59330994Sphkkldnext(struct proc* p, struct kldnext_args* uap) 59425537Sdfr{ 59525537Sdfr linker_file_t lf; 59625537Sdfr int error = 0; 59725537Sdfr 59825537Sdfr if (SCARG(uap, fileid) == 0) { 59925537Sdfr if (TAILQ_FIRST(&files)) 60030994Sphk p->p_retval[0] = TAILQ_FIRST(&files)->id; 60125537Sdfr else 60230994Sphk p->p_retval[0] = 0; 60325537Sdfr return 0; 60425537Sdfr } 60540159Speter 60625537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 60725537Sdfr if (lf) { 60825537Sdfr if (TAILQ_NEXT(lf, link)) 60930994Sphk p->p_retval[0] = TAILQ_NEXT(lf, link)->id; 61025537Sdfr else 61130994Sphk p->p_retval[0] = 0; 61225537Sdfr } else 61325537Sdfr error = ENOENT; 61425537Sdfr 61525537Sdfr return error; 61625537Sdfr} 61725537Sdfr 61825537Sdfrint 61930994Sphkkldstat(struct proc* p, struct kldstat_args* uap) 62025537Sdfr{ 62125537Sdfr linker_file_t lf; 62225537Sdfr int error = 0; 62325537Sdfr int version; 62425537Sdfr struct kld_file_stat* stat; 62525537Sdfr int namelen; 62625537Sdfr 62725537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 62825537Sdfr if (!lf) { 62925537Sdfr error = ENOENT; 63025537Sdfr goto out; 63125537Sdfr } 63225537Sdfr 63325537Sdfr stat = SCARG(uap, stat); 63425537Sdfr 63525537Sdfr /* 63625537Sdfr * Check the version of the user's structure. 63725537Sdfr */ 63825537Sdfr if (error = copyin(&stat->version, &version, sizeof(version))) 63925537Sdfr goto out; 64025537Sdfr if (version != sizeof(struct kld_file_stat)) { 64125537Sdfr error = EINVAL; 64225537Sdfr goto out; 64325537Sdfr } 64425537Sdfr 64525537Sdfr namelen = strlen(lf->filename) + 1; 64625537Sdfr if (namelen > MAXPATHLEN) 64725537Sdfr namelen = MAXPATHLEN; 64825537Sdfr if (error = copyout(lf->filename, &stat->name[0], namelen)) 64925537Sdfr goto out; 65025537Sdfr if (error = copyout(&lf->refs, &stat->refs, sizeof(int))) 65125537Sdfr goto out; 65225537Sdfr if (error = copyout(&lf->id, &stat->id, sizeof(int))) 65325537Sdfr goto out; 65425537Sdfr if (error = copyout(&lf->address, &stat->address, sizeof(caddr_t))) 65525537Sdfr goto out; 65625537Sdfr if (error = copyout(&lf->size, &stat->size, sizeof(size_t))) 65725537Sdfr goto out; 65825537Sdfr 65930994Sphk p->p_retval[0] = 0; 66025537Sdfr 66125537Sdfrout: 66225537Sdfr return error; 66325537Sdfr} 66425537Sdfr 66525537Sdfrint 66630994Sphkkldfirstmod(struct proc* p, struct kldfirstmod_args* uap) 66725537Sdfr{ 66825537Sdfr linker_file_t lf; 66925537Sdfr int error = 0; 67025537Sdfr 67125537Sdfr lf = linker_find_file_by_id(SCARG(uap, fileid)); 67225537Sdfr if (lf) { 67325537Sdfr if (TAILQ_FIRST(&lf->modules)) 67430994Sphk p->p_retval[0] = module_getid(TAILQ_FIRST(&lf->modules)); 67525537Sdfr else 67630994Sphk p->p_retval[0] = 0; 67725537Sdfr } else 67825537Sdfr error = ENOENT; 67925537Sdfr 68025537Sdfr return error; 68125537Sdfr} 68240159Speter 68340159Speter/* 68440159Speter * Preloaded module support 68540159Speter */ 68640159Speter 68740159Speterstatic void 68840159Speterlinker_preload(void* arg) 68940159Speter{ 69040159Speter caddr_t modptr; 69140159Speter char *modname; 69240162Speter char *modtype; 69340159Speter linker_file_t lf; 69440159Speter linker_class_t lc; 69540159Speter int error; 69640159Speter struct linker_set *sysinits; 69740159Speter struct sysinit **sipp; 69840159Speter moduledata_t *moddata; 69940159Speter 70040159Speter modptr = NULL; 70140159Speter while ((modptr = preload_search_next_name(modptr)) != NULL) { 70240159Speter modname = (char *)preload_search_info(modptr, MODINFO_NAME); 70340162Speter modtype = (char *)preload_search_info(modptr, MODINFO_TYPE); 70440159Speter if (modname == NULL) { 70540625Smsmith printf("Preloaded module at %p does not have a name!\n", modptr); 70640159Speter continue; 70740159Speter } 70840162Speter if (modtype == NULL) { 70940625Smsmith printf("Preloaded module at %p does not have a type!\n", modptr); 71040162Speter continue; 71140162Speter } 71240625Smsmith printf("Preloaded %s \"%s\" at %p.\n", modtype, modname, modptr); 71340159Speter lf = linker_find_file_by_name(modname); 71440159Speter if (lf) { 71540159Speter lf->userrefs++; 71640159Speter continue; 71740159Speter } 71840159Speter lf = NULL; 71940159Speter for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) { 72040159Speter error = lc->ops->load_file(modname, &lf); 72140159Speter if (error) { 72240159Speter lf = NULL; 72340159Speter break; 72440159Speter } 72540159Speter } 72640159Speter if (lf) { 72740159Speter lf->userrefs++; 72840159Speter 72940159Speter sysinits = (struct linker_set*) 73040159Speter linker_file_lookup_symbol(lf, "sysinit_set", 0); 73140159Speter if (sysinits) { 73240159Speter /* HACK ALERT! 73340159Speter * This is to set the sysinit moduledata so that the module 73440159Speter * can attach itself to the correct containing file. 73540159Speter * The sysinit could be run at *any* time. 73640159Speter */ 73740159Speter for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) { 73840159Speter if ((*sipp)->func == module_register_init) { 73940159Speter moddata = (*sipp)->udata; 74040159Speter moddata->_file = lf; 74140159Speter } 74240159Speter } 74340159Speter sysinit_add((struct sysinit **)sysinits->ls_items); 74440159Speter } 74540159Speter } 74640159Speter } 74740159Speter} 74840159Speter 74940159SpeterSYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, 0); 75040159Speter 75140159Speter/* 75240159Speter * Search for a not-loaded module by name. 75340159Speter * 75440159Speter * Modules may be found in the following locations: 75540159Speter * 75640159Speter * - preloaded (result is just the module name) 75740159Speter * - on disk (result is full path to module) 75840159Speter * 75940159Speter * If the module name is qualified in any way (contains path, etc.) 76040159Speter * the we simply return a copy of it. 76140159Speter * 76240159Speter * The search path can be manipulated via sysctl. Note that we use the ';' 76340159Speter * character as a separator to be consistent with the bootloader. 76440159Speter */ 76540159Speter 76640159Speterstatic char linker_path[MAXPATHLEN + 1] = "/;/boot/;/modules/"; 76740159Speter 76840159SpeterSYSCTL_STRING(_kern, OID_AUTO, module_path, CTLFLAG_RW, linker_path, 76940159Speter sizeof(linker_path), "module load search path"); 77040159Speter 77140159Speterstatic char * 77240159Speterlinker_strdup(const char *str) 77340159Speter{ 77440159Speter char *result; 77540159Speter 77640159Speter if ((result = malloc((strlen(str) + 1), M_LINKER, M_WAITOK)) != NULL) 77740159Speter strcpy(result, str); 77840159Speter return(result); 77940159Speter} 78040159Speter 78140159Speterchar * 78240159Speterlinker_search_path(const char *name) 78340159Speter{ 78440159Speter struct nameidata nd; 78540159Speter struct proc *p = curproc; /* XXX */ 78640159Speter char *cp, *ep, *result; 78740159Speter int error; 78840159Speter enum vtype type; 78940159Speter 79040159Speter /* qualified at all? */ 79140159Speter if (index(name, '/')) 79240159Speter return(linker_strdup(name)); 79340159Speter 79440159Speter /* traverse the linker path */ 79540159Speter cp = linker_path; 79640159Speter for (;;) { 79740159Speter 79840159Speter /* find the end of this component */ 79940159Speter for (ep = cp; (*ep != 0) && (*ep != ';'); ep++) 80040159Speter ; 80140159Speter result = malloc((strlen(name) + (ep - cp) + 1), M_LINKER, M_WAITOK); 80240159Speter if (result == NULL) /* actually ENOMEM */ 80340159Speter return(NULL); 80440159Speter 80540159Speter strncpy(result, cp, ep - cp); 80640159Speter strcpy(result + (ep - cp), name); 80740159Speter 80840159Speter /* 80940159Speter * Attempt to open the file, and return the path if we succeed and it's 81040159Speter * a regular file. 81140159Speter */ 81240159Speter NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, result, p); 81340159Speter error = vn_open(&nd, FREAD, 0); 81440159Speter if (error == 0) { 81540159Speter type = nd.ni_vp->v_type; 81640159Speter VOP_UNLOCK(nd.ni_vp, 0, p); 81740159Speter vn_close(nd.ni_vp, FREAD, p->p_ucred, p); 81840159Speter if (type == VREG) 81940159Speter return(result); 82040159Speter } 82140159Speter free(result, M_LINKER); 82240159Speter 82340159Speter if (*ep == 0) 82440159Speter break; 82540159Speter cp = ep + 1; 82640159Speter } 82740159Speter return(NULL); 82840159Speter} 829