kern_linker.c revision 40395
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 *
2640395Speter *	$Id: kern_linker.c,v 1.9 1998/10/10 02:29:07 peter Exp $
2725537Sdfr */
2825537Sdfr
2940159Speter#include "opt_ddb.h"
3040159Speter
3125537Sdfr#include <sys/param.h>
3225537Sdfr#include <sys/kernel.h>
3325537Sdfr#include <sys/systm.h>
3425537Sdfr#include <sys/malloc.h>
3525537Sdfr#include <sys/sysproto.h>
3625537Sdfr#include <sys/sysent.h>
3725537Sdfr#include <sys/proc.h>
3825537Sdfr#include <sys/lock.h>
3925537Sdfr#include <machine/cpu.h>
4040159Speter#include <machine/bootinfo.h>
4125537Sdfr#include <sys/module.h>
4225537Sdfr#include <sys/linker.h>
4331675Sdyson#include <sys/unistd.h>
4440159Speter#include <sys/fcntl.h>
4540159Speter#include <sys/libkern.h>
4640159Speter#include <sys/namei.h>
4740159Speter#include <sys/vnode.h>
4840159Speter#include <sys/sysctl.h>
4925537Sdfr
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
52625537Sdfr    if (error = linker_load_file(uap->file, &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) {
70540159Speter	    printf("Preloaded module at 0x%p does not have a name!\n", modptr);
70640159Speter	    continue;
70740159Speter	}
70840162Speter	if (modtype == NULL) {
70940162Speter	    printf("Preloaded module at 0x%p does not have a type!\n", modptr);
71040162Speter	    continue;
71140162Speter	}
71240162Speter	printf("Preloaded %s \"%s\" at 0x%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