kern_linker.c revision 42755
125537Sdfr/*-
225537Sdfr * Copyright (c) 1997 Doug Rabson
325537Sdfr * All rights reserved.
425537Sdfr *
525537Sdfr * Redistribution and use in source and binary forms, with or without
625537Sdfr * modification, are permitted provided that the following conditions
725537Sdfr * are met:
825537Sdfr * 1. Redistributions of source code must retain the above copyright
925537Sdfr *    notice, this list of conditions and the following disclaimer.
1025537Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1125537Sdfr *    notice, this list of conditions and the following disclaimer in the
1225537Sdfr *    documentation and/or other materials provided with the distribution.
1325537Sdfr *
1425537Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1525537Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1625537Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1725537Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1825537Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1925537Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2025537Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2125537Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2225537Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2325537Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2425537Sdfr * SUCH DAMAGE.
2525537Sdfr *
2642755Speter *	$Id: kern_linker.c,v 1.18 1999/01/05 20:24:28 msmith Exp $
2725537Sdfr */
2825537Sdfr
2940159Speter#include "opt_ddb.h"
3040159Speter
3125537Sdfr#include <sys/param.h>
3225537Sdfr#include <sys/kernel.h>
3325537Sdfr#include <sys/systm.h>
3425537Sdfr#include <sys/malloc.h>
3525537Sdfr#include <sys/sysproto.h>
3625537Sdfr#include <sys/sysent.h>
3725537Sdfr#include <sys/proc.h>
3825537Sdfr#include <sys/lock.h>
3925537Sdfr#include <machine/cpu.h>
4040159Speter#include <machine/bootinfo.h>
4125537Sdfr#include <sys/module.h>
4225537Sdfr#include <sys/linker.h>
4331675Sdyson#include <sys/unistd.h>
4440159Speter#include <sys/fcntl.h>
4540159Speter#include <sys/libkern.h>
4640159Speter#include <sys/namei.h>
4740159Speter#include <sys/vnode.h>
4840159Speter#include <sys/sysctl.h>
4925537Sdfr
5040961Speter#ifdef KLD_DEBUG
5140961Speterint kld_debug = 0;
5240961Speter#endif
5340961Speter
5432153SbdeMALLOC_DEFINE(M_LINKER, "kld", "kernel linker");
5531324Sbdelinker_file_t linker_current_file;
5640906Speterlinker_file_t linker_kernel_file;
5731324Sbde
5825537Sdfrstatic struct lock lock;	/* lock for the file list */
5925537Sdfrstatic linker_class_list_t classes;
6025537Sdfrstatic linker_file_list_t files;
6125537Sdfrstatic int next_file_id = 1;
6225537Sdfr
6325537Sdfrstatic void
6425537Sdfrlinker_init(void* arg)
6525537Sdfr{
6625537Sdfr    lockinit(&lock, PVM, "klink", 0, 0);
6725537Sdfr    TAILQ_INIT(&classes);
6825537Sdfr    TAILQ_INIT(&files);
6925537Sdfr}
7025537Sdfr
7140159SpeterSYSINIT(linker, SI_SUB_KLD, SI_ORDER_FIRST, linker_init, 0);
7225537Sdfr
7325537Sdfrint
7425537Sdfrlinker_add_class(const char* desc, void* priv,
7525537Sdfr		 struct linker_class_ops* ops)
7625537Sdfr{
7725537Sdfr    linker_class_t lc;
7825537Sdfr
7925537Sdfr    lc = malloc(sizeof(struct linker_class), M_LINKER, M_NOWAIT);
8025537Sdfr    if (!lc)
8125537Sdfr	return ENOMEM;
8240395Speter    bzero(lc, sizeof(*lc));
8325537Sdfr
8425537Sdfr    lc->desc = desc;
8525537Sdfr    lc->priv = priv;
8625537Sdfr    lc->ops = ops;
8725537Sdfr    TAILQ_INSERT_HEAD(&classes, lc, link);
8825537Sdfr
8925537Sdfr    return 0;
9025537Sdfr}
9125537Sdfr
9225537Sdfrstatic void
9325537Sdfrlinker_file_sysinit(linker_file_t lf)
9425537Sdfr{
9525537Sdfr    struct linker_set* sysinits;
9625537Sdfr    struct sysinit** sipp;
9725537Sdfr    struct sysinit** xipp;
9825537Sdfr    struct sysinit* save;
9940159Speter    moduledata_t *moddata;
10025537Sdfr
10125537Sdfr    KLD_DPF(FILE, ("linker_file_sysinit: calling SYSINITs for %s\n",
10225537Sdfr		   lf->filename));
10325537Sdfr
10425537Sdfr    sysinits = (struct linker_set*)
10525537Sdfr	linker_file_lookup_symbol(lf, "sysinit_set", 0);
10640159Speter
10740159Speter    KLD_DPF(FILE, ("linker_file_sysinit: SYSINITs %p\n", sysinits));
10825537Sdfr    if (!sysinits)
10925537Sdfr	return;
11025537Sdfr
11140159Speter    /* HACK ALERT! */
11240159Speter    for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) {
11340159Speter	if ((*sipp)->func == module_register_init) {
11440159Speter	    moddata = (*sipp)->udata;
11540159Speter	    moddata->_file = lf;
11640159Speter	}
11740159Speter    }
11840159Speter
11925537Sdfr    /*
12025537Sdfr     * Perform a bubble sort of the system initialization objects by
12125537Sdfr     * their subsystem (primary key) and order (secondary key).
12225537Sdfr     *
12325537Sdfr     * Since some things care about execution order, this is the
12425537Sdfr     * operation which ensures continued function.
12525537Sdfr     */
12641055Speter    for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) {
12741055Speter	for (xipp = sipp + 1; *xipp; xipp++) {
12841055Speter	    if ((*sipp)->subsystem <= (*xipp)->subsystem ||
12941055Speter		 ((*sipp)->subsystem == (*xipp)->subsystem &&
13041055Speter		  (*sipp)->order <= (*xipp)->order))
13125537Sdfr		continue;	/* skip*/
13225537Sdfr	    save = *sipp;
13325537Sdfr	    *sipp = *xipp;
13425537Sdfr	    *xipp = save;
13525537Sdfr	}
13625537Sdfr    }
13725537Sdfr
13825537Sdfr
13925537Sdfr    /*
14025537Sdfr     * Traverse the (now) ordered list of system initialization tasks.
14125537Sdfr     * Perform each task, and continue on to the next task.
14225537Sdfr     */
14341055Speter    for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) {
14441055Speter	if ((*sipp)->subsystem == SI_SUB_DUMMY)
14525537Sdfr	    continue;	/* skip dummy task(s)*/
14625537Sdfr
14741055Speter	switch ((*sipp)->type) {
14825537Sdfr	case SI_TYPE_DEFAULT:
14925537Sdfr	    /* no special processing*/
15041055Speter	    (*((*sipp)->func))((*sipp)->udata);
15125537Sdfr	    break;
15225537Sdfr
15325537Sdfr	case SI_TYPE_KTHREAD:
15431675Sdyson#if !defined(SMP)
15525537Sdfr	    /* kernel thread*/
15631675Sdyson	    if (fork1(&proc0, RFFDG|RFPROC|RFMEM))
15731675Sdyson		panic("fork kernel thread");
15831675Sdyson	    cpu_set_fork_handler(pfind(proc0.p_retval[0]),
15931675Sdyson		(*sipp)->func, (*sipp)->udata);
16031675Sdyson	    break;
16131675Sdyson#endif
16231675Sdyson
16331675Sdyson	case SI_TYPE_KPROCESS:
16431675Sdyson	    /* kernel thread*/
16531675Sdyson	    if (fork1(&proc0, RFFDG|RFPROC))
16625537Sdfr		panic("fork kernel process");
16730994Sphk	    cpu_set_fork_handler(pfind(proc0.p_retval[0]),
16830994Sphk		(*sipp)->func, (*sipp)->udata);
16925537Sdfr	    break;
17025537Sdfr
17125537Sdfr	default:
17241055Speter	    panic ("linker_file_sysinit: unrecognized init type");
17325537Sdfr	}
17425537Sdfr    }
17525537Sdfr}
17625537Sdfr
17741055Speterstatic void
17841055Speterlinker_file_sysuninit(linker_file_t lf)
17941055Speter{
18041055Speter    struct linker_set* sysuninits;
18141055Speter    struct sysinit** sipp;
18241055Speter    struct sysinit** xipp;
18341055Speter    struct sysinit* save;
18441055Speter
18541055Speter    KLD_DPF(FILE, ("linker_file_sysuninit: calling SYSUNINITs for %s\n",
18641055Speter		   lf->filename));
18741055Speter
18841055Speter    sysuninits = (struct linker_set*)
18941055Speter	linker_file_lookup_symbol(lf, "sysuninit_set", 0);
19041055Speter
19141055Speter    KLD_DPF(FILE, ("linker_file_sysuninit: SYSUNINITs %p\n", sysuninits));
19241055Speter    if (!sysuninits)
19341055Speter	return;
19441055Speter
19541055Speter    /*
19641055Speter     * Perform a reverse bubble sort of the system initialization objects
19741055Speter     * by their subsystem (primary key) and order (secondary key).
19841055Speter     *
19941055Speter     * Since some things care about execution order, this is the
20041055Speter     * operation which ensures continued function.
20141055Speter     */
20241055Speter    for (sipp = (struct sysinit **)sysuninits->ls_items; *sipp; sipp++) {
20341055Speter	for (xipp = sipp + 1; *xipp; xipp++) {
20441055Speter	    if ((*sipp)->subsystem >= (*xipp)->subsystem ||
20541055Speter		 ((*sipp)->subsystem == (*xipp)->subsystem &&
20641055Speter		  (*sipp)->order >= (*xipp)->order))
20741055Speter		continue;	/* skip*/
20841055Speter	    save = *sipp;
20941055Speter	    *sipp = *xipp;
21041055Speter	    *xipp = save;
21141055Speter	}
21241055Speter    }
21341055Speter
21441055Speter
21541055Speter    /*
21641055Speter     * Traverse the (now) ordered list of system initialization tasks.
21741055Speter     * Perform each task, and continue on to the next task.
21841055Speter     */
21941055Speter    for (sipp = (struct sysinit **)sysuninits->ls_items; *sipp; sipp++) {
22041055Speter	if ((*sipp)->subsystem == SI_SUB_DUMMY)
22141055Speter	    continue;	/* skip dummy task(s)*/
22241055Speter
22341055Speter	switch ((*sipp)->type) {
22441055Speter	case SI_TYPE_DEFAULT:
22541055Speter	    /* no special processing*/
22641055Speter	    (*((*sipp)->func))((*sipp)->udata);
22741055Speter	    break;
22841055Speter
22941055Speter	default:
23041055Speter	    panic("linker_file_sysuninit: unrecognized uninit type");
23141055Speter	}
23241055Speter    }
23341055Speter}
23441055Speter
23525537Sdfrint
23625537Sdfrlinker_load_file(const char* filename, linker_file_t* result)
23725537Sdfr{
23825537Sdfr    linker_class_t lc;
23925537Sdfr    linker_file_t lf;
24042755Speter    int foundfile, error = 0;
24140861Speter    char *koname = NULL;
24225537Sdfr
24325537Sdfr    lf = linker_find_file_by_name(filename);
24425537Sdfr    if (lf) {
24525537Sdfr	KLD_DPF(FILE, ("linker_load_file: file %s is already loaded, incrementing refs\n", filename));
24625537Sdfr	*result = lf;
24725537Sdfr	lf->refs++;
24825537Sdfr	goto out;
24925537Sdfr    }
25025537Sdfr
25140861Speter    koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK);
25240861Speter    if (koname == NULL) {
25340861Speter	error = ENOMEM;
25440861Speter	goto out;
25540861Speter    }
25640861Speter    sprintf(koname, "%s.ko", filename);
25725537Sdfr    lf = NULL;
25842755Speter    foundfile = 0;
25925537Sdfr    for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) {
26025537Sdfr	KLD_DPF(FILE, ("linker_load_file: trying to load %s as %s\n",
26125537Sdfr		       filename, lc->desc));
26242755Speter
26342755Speter	error = lc->ops->load_file(koname, &lf);	/* First with .ko */
26442755Speter	if (lf == NULL && error == ENOENT)
26542755Speter	    error = lc->ops->load_file(filename, &lf);	/* Then try without */
26642755Speter	/*
26742755Speter	 * If we got something other than ENOENT, then it exists but we cannot
26842755Speter	 * load it for some other reason.
26942755Speter	 */
27042755Speter	if (error != ENOENT)
27142755Speter	    foundfile = 1;
27225537Sdfr	if (lf) {
27325537Sdfr	    linker_file_sysinit(lf);
27425537Sdfr
27525537Sdfr	    *result = lf;
27640861Speter	    error = 0;
27725537Sdfr	    goto out;
27825537Sdfr	}
27925537Sdfr    }
28042755Speter    /*
28142755Speter     * Less than ideal, but tells the user whether it failed to load or
28242755Speter     * the module was not found.
28342755Speter     */
28442755Speter    if (foundfile)
28542755Speter	error = ENOEXEC;	/* Format not recognised (or unloadable) */
28642755Speter    else
28742755Speter	error = ENOENT;		/* Nothing found */
28825537Sdfr
28925537Sdfrout:
29040861Speter    if (koname)
29140861Speter	free(koname, M_LINKER);
29225537Sdfr    return error;
29325537Sdfr}
29425537Sdfr
29525537Sdfrlinker_file_t
29625537Sdfrlinker_find_file_by_name(const char* filename)
29725537Sdfr{
29825537Sdfr    linker_file_t lf = 0;
29940861Speter    char *koname;
30025537Sdfr
30140861Speter    koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK);
30240861Speter    if (koname == NULL)
30340861Speter	goto out;
30440861Speter    sprintf(koname, "%s.ko", filename);
30540861Speter
30625537Sdfr    lockmgr(&lock, LK_SHARED, 0, curproc);
30740861Speter    for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) {
30840861Speter	if (!strcmp(lf->filename, koname))
30940861Speter	    break;
31025537Sdfr	if (!strcmp(lf->filename, filename))
31125537Sdfr	    break;
31240861Speter    }
31325537Sdfr    lockmgr(&lock, LK_RELEASE, 0, curproc);
31425537Sdfr
31540861Speterout:
31640861Speter    if (koname)
31740861Speter	free(koname, M_LINKER);
31825537Sdfr    return lf;
31925537Sdfr}
32025537Sdfr
32125537Sdfrlinker_file_t
32225537Sdfrlinker_find_file_by_id(int fileid)
32325537Sdfr{
32425537Sdfr    linker_file_t lf = 0;
32525537Sdfr
32625537Sdfr    lockmgr(&lock, LK_SHARED, 0, curproc);
32725537Sdfr    for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link))
32825537Sdfr	if (lf->id == fileid)
32925537Sdfr	    break;
33025537Sdfr    lockmgr(&lock, LK_RELEASE, 0, curproc);
33125537Sdfr
33225537Sdfr    return lf;
33325537Sdfr}
33425537Sdfr
33525537Sdfrlinker_file_t
33640159Speterlinker_make_file(const char* pathname, void* priv, struct linker_file_ops* ops)
33725537Sdfr{
33825537Sdfr    linker_file_t lf = 0;
33925537Sdfr    int namelen;
34040159Speter    const char *filename;
34125537Sdfr
34240159Speter    filename = rindex(pathname, '/');
34340159Speter    if (filename && filename[1])
34440159Speter	filename++;
34540159Speter    else
34640159Speter	filename = pathname;
34740159Speter
34825537Sdfr    KLD_DPF(FILE, ("linker_make_file: new file, filename=%s\n", filename));
34925537Sdfr    lockmgr(&lock, LK_EXCLUSIVE|LK_RETRY, 0, curproc);
35025537Sdfr    namelen = strlen(filename) + 1;
35125537Sdfr    lf = malloc(sizeof(struct linker_file) + namelen, M_LINKER, M_WAITOK);
35225537Sdfr    if (!lf)
35325537Sdfr	goto out;
35440395Speter    bzero(lf, sizeof(*lf));
35525537Sdfr
35625537Sdfr    lf->refs = 1;
35725537Sdfr    lf->userrefs = 0;
35825537Sdfr    lf->filename = (char*) (lf + 1);
35925537Sdfr    strcpy(lf->filename, filename);
36025537Sdfr    lf->id = next_file_id++;
36125537Sdfr    lf->ndeps = 0;
36225537Sdfr    lf->deps = NULL;
36325537Sdfr    STAILQ_INIT(&lf->common);
36425537Sdfr    TAILQ_INIT(&lf->modules);
36525537Sdfr
36625537Sdfr    lf->priv = priv;
36725537Sdfr    lf->ops = ops;
36825537Sdfr    TAILQ_INSERT_TAIL(&files, lf, link);
36925537Sdfr
37025537Sdfrout:
37125537Sdfr    lockmgr(&lock, LK_RELEASE, 0, curproc);
37225537Sdfr    return lf;
37325537Sdfr}
37425537Sdfr
37525537Sdfrint
37625537Sdfrlinker_file_unload(linker_file_t file)
37725537Sdfr{
37825537Sdfr    module_t mod, next;
37925537Sdfr    struct common_symbol* cp;
38025537Sdfr    int error = 0;
38125537Sdfr    int i;
38225537Sdfr
38340159Speter    KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", file->refs));
38425537Sdfr    lockmgr(&lock, LK_EXCLUSIVE|LK_RETRY, 0, curproc);
38525537Sdfr    if (file->refs == 1) {
38625537Sdfr	KLD_DPF(FILE, ("linker_file_unload: file is unloading, informing modules\n"));
38725537Sdfr	/*
38825537Sdfr	 * Inform any modules associated with this file.
38925537Sdfr	 */
39025537Sdfr	for (mod = TAILQ_FIRST(&file->modules); mod; mod = next) {
39125537Sdfr	    next = module_getfnext(mod);
39225537Sdfr
39325537Sdfr	    /*
39425537Sdfr	     * Give the module a chance to veto the unload.
39525537Sdfr	     */
39625537Sdfr	    if (error = module_unload(mod)) {
39725537Sdfr		KLD_DPF(FILE, ("linker_file_unload: module %x vetoes unload\n",
39825537Sdfr			       mod));
39925537Sdfr		lockmgr(&lock, LK_RELEASE, 0, curproc);
40025537Sdfr		goto out;
40125537Sdfr	    }
40225537Sdfr
40325537Sdfr	    module_release(mod);
40425537Sdfr	}
40525537Sdfr    }
40625537Sdfr
40725537Sdfr    file->refs--;
40825537Sdfr    if (file->refs > 0) {
40925537Sdfr	lockmgr(&lock, LK_RELEASE, 0, curproc);
41025537Sdfr	goto out;
41125537Sdfr    }
41225537Sdfr
41341055Speter    linker_file_sysuninit(file);
41441055Speter
41525537Sdfr    TAILQ_REMOVE(&files, file, link);
41625537Sdfr    lockmgr(&lock, LK_RELEASE, 0, curproc);
41740159Speter
41825537Sdfr    for (i = 0; i < file->ndeps; i++)
41925537Sdfr	linker_file_unload(file->deps[i]);
42025537Sdfr    free(file->deps, M_LINKER);
42125537Sdfr
42225537Sdfr    for (cp = STAILQ_FIRST(&file->common); cp;
42325537Sdfr	 cp = STAILQ_FIRST(&file->common)) {
42425537Sdfr	STAILQ_REMOVE(&file->common, cp, common_symbol, link);
42525537Sdfr	free(cp, M_LINKER);
42625537Sdfr    }
42725537Sdfr
42825537Sdfr    file->ops->unload(file);
42925537Sdfr    free(file, M_LINKER);
43025537Sdfr
43125537Sdfrout:
43225537Sdfr    return error;
43325537Sdfr}
43425537Sdfr
43525537Sdfrint
43625537Sdfrlinker_file_add_dependancy(linker_file_t file, linker_file_t dep)
43725537Sdfr{
43825537Sdfr    linker_file_t* newdeps;
43925537Sdfr
44025537Sdfr    newdeps = malloc((file->ndeps + 1) * sizeof(linker_file_t*),
44125537Sdfr		     M_LINKER, M_WAITOK);
44225537Sdfr    if (newdeps == NULL)
44325537Sdfr	return ENOMEM;
44440395Speter    bzero(newdeps, (file->ndeps + 1) * sizeof(linker_file_t*));
44525537Sdfr
44625537Sdfr    if (file->deps) {
44725537Sdfr	bcopy(file->deps, newdeps, file->ndeps * sizeof(linker_file_t*));
44825537Sdfr	free(file->deps, M_LINKER);
44925537Sdfr    }
45025537Sdfr    file->deps = newdeps;
45125537Sdfr    file->deps[file->ndeps] = dep;
45225537Sdfr    file->ndeps++;
45325537Sdfr
45425537Sdfr    return 0;
45525537Sdfr}
45625537Sdfr
45725537Sdfrcaddr_t
45825537Sdfrlinker_file_lookup_symbol(linker_file_t file, const char* name, int deps)
45925537Sdfr{
46038275Sdfr    linker_sym_t sym;
46138275Sdfr    linker_symval_t symval;
46225537Sdfr    caddr_t address;
46325537Sdfr    size_t common_size = 0;
46425537Sdfr    int i;
46525537Sdfr
46640159Speter    KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d\n",
46725537Sdfr		  file, name, deps));
46825537Sdfr
46938275Sdfr    if (file->ops->lookup_symbol(file, name, &sym) == 0) {
47038275Sdfr	file->ops->symbol_values(file, sym, &symval);
47138275Sdfr	if (symval.value == 0)
47225537Sdfr	    /*
47325537Sdfr	     * For commons, first look them up in the dependancies and
47425537Sdfr	     * only allocate space if not found there.
47525537Sdfr	     */
47638275Sdfr	    common_size = symval.size;
47740159Speter	else {
47840159Speter	    KLD_DPF(SYM, ("linker_file_lookup_symbol: symbol.value=%x\n", symval.value));
47938275Sdfr	    return symval.value;
48040159Speter	}
48138275Sdfr    }
48225537Sdfr
48325537Sdfr    if (deps)
48425537Sdfr	for (i = 0; i < file->ndeps; i++) {
48525537Sdfr	    address = linker_file_lookup_symbol(file->deps[i], name, 0);
48640159Speter	    if (address) {
48740159Speter		KLD_DPF(SYM, ("linker_file_lookup_symbol: deps value=%x\n", address));
48825537Sdfr		return address;
48940159Speter	    }
49025537Sdfr	}
49125537Sdfr
49225537Sdfr    if (common_size > 0) {
49325537Sdfr	/*
49425537Sdfr	 * This is a common symbol which was not found in the
49525537Sdfr	 * dependancies.  We maintain a simple common symbol table in
49625537Sdfr	 * the file object.
49725537Sdfr	 */
49825537Sdfr	struct common_symbol* cp;
49925537Sdfr
50025537Sdfr	for (cp = STAILQ_FIRST(&file->common); cp;
50125537Sdfr	     cp = STAILQ_NEXT(cp, link))
50240159Speter	    if (!strcmp(cp->name, name)) {
50340159Speter		KLD_DPF(SYM, ("linker_file_lookup_symbol: old common value=%x\n", cp->address));
50425537Sdfr		return cp->address;
50540159Speter	    }
50625537Sdfr
50725537Sdfr	/*
50825537Sdfr	 * Round the symbol size up to align.
50925537Sdfr	 */
51025537Sdfr	common_size = (common_size + sizeof(int) - 1) & -sizeof(int);
51125537Sdfr	cp = malloc(sizeof(struct common_symbol)
51225537Sdfr		    + common_size
51325537Sdfr		    + strlen(name) + 1,
51425537Sdfr		    M_LINKER, M_WAITOK);
51540159Speter	if (!cp) {
51640159Speter	    KLD_DPF(SYM, ("linker_file_lookup_symbol: nomem\n"));
51725537Sdfr	    return 0;
51840159Speter	}
51940395Speter	bzero(cp, sizeof(struct common_symbol) + common_size + strlen(name)+ 1);
52025537Sdfr
52125537Sdfr	cp->address = (caddr_t) (cp + 1);
52225537Sdfr	cp->name = cp->address + common_size;
52325537Sdfr	strcpy(cp->name, name);
52425537Sdfr	bzero(cp->address, common_size);
52525537Sdfr	STAILQ_INSERT_TAIL(&file->common, cp, link);
52625537Sdfr
52740159Speter	KLD_DPF(SYM, ("linker_file_lookup_symbol: new common value=%x\n", cp->address));
52825537Sdfr	return cp->address;
52925537Sdfr    }
53025537Sdfr
53140159Speter    KLD_DPF(SYM, ("linker_file_lookup_symbol: fail\n"));
53225537Sdfr    return 0;
53325537Sdfr}
53425537Sdfr
53540159Speter#ifdef DDB
53625537Sdfr/*
53740159Speter * DDB Helpers.  DDB has to look across multiple files with their own
53840159Speter * symbol tables and string tables.
53940159Speter *
54040159Speter * Note that we do not obey list locking protocols here.  We really don't
54140159Speter * need DDB to hang because somebody's got the lock held.  We'll take the
54240159Speter * chance that the files list is inconsistant instead.
54340159Speter */
54440159Speter
54540159Speterint
54640159Speterlinker_ddb_lookup(char *symstr, linker_sym_t *sym)
54740159Speter{
54840159Speter    linker_file_t lf;
54940159Speter
55040159Speter    for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) {
55140159Speter	if (lf->ops->lookup_symbol(lf, symstr, sym) == 0)
55240159Speter	    return 0;
55340159Speter    }
55440159Speter    return ENOENT;
55540159Speter}
55640159Speter
55740159Speterint
55840159Speterlinker_ddb_search_symbol(caddr_t value, linker_sym_t *sym, long *diffp)
55940159Speter{
56040159Speter    linker_file_t lf;
56140159Speter    u_long off = (u_long)value;
56240159Speter    u_long diff, bestdiff;
56340159Speter    linker_sym_t best;
56440159Speter    linker_sym_t es;
56540159Speter
56640159Speter    best = 0;
56740159Speter    bestdiff = off;
56840159Speter    for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) {
56940159Speter	if (lf->ops->search_symbol(lf, value, &es, &diff) != 0)
57040159Speter	    continue;
57140159Speter	if (es != 0 && diff < bestdiff) {
57240159Speter	    best = es;
57340159Speter	    bestdiff = diff;
57440159Speter	}
57540159Speter	if (bestdiff == 0)
57640159Speter	    break;
57740159Speter    }
57840159Speter    if (best) {
57940159Speter	*sym = best;
58040159Speter	*diffp = bestdiff;
58140159Speter	return 0;
58240159Speter    } else {
58340159Speter	*sym = 0;
58440159Speter	*diffp = off;
58540159Speter	return ENOENT;
58640159Speter    }
58740159Speter}
58840159Speter
58940159Speterint
59040159Speterlinker_ddb_symbol_values(linker_sym_t sym, linker_symval_t *symval)
59140159Speter{
59240159Speter    linker_file_t lf;
59340159Speter
59440159Speter    for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) {
59540159Speter	if (lf->ops->symbol_values(lf, sym, symval) == 0)
59640159Speter	    return 0;
59740159Speter    }
59840159Speter    return ENOENT;
59940159Speter}
60040159Speter
60140159Speter#endif
60240159Speter
60340159Speter/*
60425537Sdfr * Syscalls.
60525537Sdfr */
60625537Sdfr
60725537Sdfrint
60830994Sphkkldload(struct proc* p, struct kldload_args* uap)
60925537Sdfr{
61042316Smsmith    char* filename = NULL, *modulename;
61125537Sdfr    linker_file_t lf;
61225537Sdfr    int error = 0;
61325537Sdfr
61430994Sphk    p->p_retval[0] = -1;
61525537Sdfr
61625537Sdfr    if (securelevel > 0)
61725537Sdfr	return EPERM;
61825537Sdfr
61925537Sdfr    if (error = suser(p->p_ucred, &p->p_acflag))
62025537Sdfr	return error;
62125537Sdfr
62225537Sdfr    filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
62325537Sdfr    if (error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL))
62425537Sdfr	goto out;
62525537Sdfr
62642316Smsmith    /* Can't load more than one module with the same name */
62742316Smsmith    modulename = rindex(filename, '/');
62842316Smsmith    if (modulename == NULL)
62942316Smsmith	modulename = filename;
63042316Smsmith    if (linker_find_file_by_name(modulename)) {
63142316Smsmith	error = EEXIST;
63242316Smsmith	goto out;
63342316Smsmith    }
63442316Smsmith
63540859Speter    if (error = linker_load_file(filename, &lf))
63625537Sdfr	goto out;
63725537Sdfr
63825537Sdfr    lf->userrefs++;
63930994Sphk    p->p_retval[0] = lf->id;
64040159Speter
64125537Sdfrout:
64225537Sdfr    if (filename)
64325537Sdfr	free(filename, M_TEMP);
64425537Sdfr    return error;
64525537Sdfr}
64625537Sdfr
64725537Sdfrint
64830994Sphkkldunload(struct proc* p, struct kldunload_args* uap)
64925537Sdfr{
65025537Sdfr    linker_file_t lf;
65125537Sdfr    int error = 0;
65225537Sdfr
65325537Sdfr    if (securelevel > 0)
65425537Sdfr	return EPERM;
65525537Sdfr
65625537Sdfr    if (error = suser(p->p_ucred, &p->p_acflag))
65725537Sdfr	return error;
65825537Sdfr
65925537Sdfr    lf = linker_find_file_by_id(SCARG(uap, fileid));
66025537Sdfr    if (lf) {
66125537Sdfr	KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs));
66225537Sdfr	if (lf->userrefs == 0) {
66325537Sdfr	    printf("linkerunload: attempt to unload file which was not loaded by user\n");
66425537Sdfr	    error = EBUSY;
66525537Sdfr	    goto out;
66625537Sdfr	}
66725537Sdfr	lf->userrefs--;
66825537Sdfr	error = linker_file_unload(lf);
66925537Sdfr    } else
67025537Sdfr	error = ENOENT;
67125537Sdfr
67225537Sdfrout:
67325537Sdfr    return error;
67425537Sdfr}
67525537Sdfr
67625537Sdfrint
67730994Sphkkldfind(struct proc* p, struct kldfind_args* uap)
67825537Sdfr{
67942316Smsmith    char* filename = NULL, *modulename;
68025537Sdfr    linker_file_t lf;
68125537Sdfr    int error = 0;
68225537Sdfr
68330994Sphk    p->p_retval[0] = -1;
68425537Sdfr
68525537Sdfr    filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
68625537Sdfr    if (error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL))
68725537Sdfr	goto out;
68825537Sdfr
68942316Smsmith    modulename = rindex(filename, '/');
69042316Smsmith    if (modulename == NULL)
69142316Smsmith	modulename = filename;
69242316Smsmith
69342316Smsmith    lf = linker_find_file_by_name(modulename);
69425537Sdfr    if (lf)
69530994Sphk	p->p_retval[0] = lf->id;
69625537Sdfr    else
69725537Sdfr	error = ENOENT;
69840159Speter
69925537Sdfrout:
70025537Sdfr    if (filename)
70125537Sdfr	free(filename, M_TEMP);
70225537Sdfr    return error;
70325537Sdfr}
70425537Sdfr
70525537Sdfrint
70630994Sphkkldnext(struct proc* p, struct kldnext_args* uap)
70725537Sdfr{
70825537Sdfr    linker_file_t lf;
70925537Sdfr    int error = 0;
71025537Sdfr
71125537Sdfr    if (SCARG(uap, fileid) == 0) {
71225537Sdfr	if (TAILQ_FIRST(&files))
71330994Sphk	    p->p_retval[0] = TAILQ_FIRST(&files)->id;
71425537Sdfr	else
71530994Sphk	    p->p_retval[0] = 0;
71625537Sdfr	return 0;
71725537Sdfr    }
71840159Speter
71925537Sdfr    lf = linker_find_file_by_id(SCARG(uap, fileid));
72025537Sdfr    if (lf) {
72125537Sdfr	if (TAILQ_NEXT(lf, link))
72230994Sphk	    p->p_retval[0] = TAILQ_NEXT(lf, link)->id;
72325537Sdfr	else
72430994Sphk	    p->p_retval[0] = 0;
72525537Sdfr    } else
72625537Sdfr	error = ENOENT;
72725537Sdfr
72825537Sdfr    return error;
72925537Sdfr}
73025537Sdfr
73125537Sdfrint
73230994Sphkkldstat(struct proc* p, struct kldstat_args* uap)
73325537Sdfr{
73425537Sdfr    linker_file_t lf;
73525537Sdfr    int error = 0;
73625537Sdfr    int version;
73725537Sdfr    struct kld_file_stat* stat;
73825537Sdfr    int namelen;
73925537Sdfr
74025537Sdfr    lf = linker_find_file_by_id(SCARG(uap, fileid));
74125537Sdfr    if (!lf) {
74225537Sdfr	error = ENOENT;
74325537Sdfr	goto out;
74425537Sdfr    }
74525537Sdfr
74625537Sdfr    stat = SCARG(uap, stat);
74725537Sdfr
74825537Sdfr    /*
74925537Sdfr     * Check the version of the user's structure.
75025537Sdfr     */
75125537Sdfr    if (error = copyin(&stat->version, &version, sizeof(version)))
75225537Sdfr	goto out;
75325537Sdfr    if (version != sizeof(struct kld_file_stat)) {
75425537Sdfr	error = EINVAL;
75525537Sdfr	goto out;
75625537Sdfr    }
75725537Sdfr
75825537Sdfr    namelen = strlen(lf->filename) + 1;
75925537Sdfr    if (namelen > MAXPATHLEN)
76025537Sdfr	namelen = MAXPATHLEN;
76125537Sdfr    if (error = copyout(lf->filename, &stat->name[0], namelen))
76225537Sdfr	goto out;
76325537Sdfr    if (error = copyout(&lf->refs, &stat->refs, sizeof(int)))
76425537Sdfr	goto out;
76525537Sdfr    if (error = copyout(&lf->id, &stat->id, sizeof(int)))
76625537Sdfr	goto out;
76725537Sdfr    if (error = copyout(&lf->address, &stat->address, sizeof(caddr_t)))
76825537Sdfr	goto out;
76925537Sdfr    if (error = copyout(&lf->size, &stat->size, sizeof(size_t)))
77025537Sdfr	goto out;
77125537Sdfr
77230994Sphk    p->p_retval[0] = 0;
77325537Sdfr
77425537Sdfrout:
77525537Sdfr    return error;
77625537Sdfr}
77725537Sdfr
77825537Sdfrint
77930994Sphkkldfirstmod(struct proc* p, struct kldfirstmod_args* uap)
78025537Sdfr{
78125537Sdfr    linker_file_t lf;
78225537Sdfr    int error = 0;
78325537Sdfr
78425537Sdfr    lf = linker_find_file_by_id(SCARG(uap, fileid));
78525537Sdfr    if (lf) {
78625537Sdfr	if (TAILQ_FIRST(&lf->modules))
78730994Sphk	    p->p_retval[0] = module_getid(TAILQ_FIRST(&lf->modules));
78825537Sdfr	else
78930994Sphk	    p->p_retval[0] = 0;
79025537Sdfr    } else
79125537Sdfr	error = ENOENT;
79225537Sdfr
79325537Sdfr    return error;
79425537Sdfr}
79540159Speter
79641090Speterint
79741090Speterkldsym(struct proc *p, struct kldsym_args *uap)
79841090Speter{
79941090Speter    char *symstr = NULL;
80041090Speter    linker_sym_t sym;
80141090Speter    linker_symval_t symval;
80241090Speter    linker_file_t lf;
80341090Speter    struct kld_sym_lookup lookup;
80441090Speter    int error = 0;
80541090Speter
80641090Speter    if (error = copyin(SCARG(uap, data), &lookup, sizeof(lookup)))
80741090Speter	goto out;
80841090Speter    if (lookup.version != sizeof(lookup) || SCARG(uap, cmd) != KLDSYM_LOOKUP) {
80941090Speter	error = EINVAL;
81041090Speter	goto out;
81141090Speter    }
81241090Speter
81341090Speter    symstr = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
81441090Speter    if (error = copyinstr(lookup.symname, symstr, MAXPATHLEN, NULL))
81541090Speter	goto out;
81641090Speter
81741090Speter    if (SCARG(uap, fileid) != 0) {
81841090Speter	lf = linker_find_file_by_id(SCARG(uap, fileid));
81941090Speter	if (lf == NULL) {
82041090Speter	    error = ENOENT;
82141090Speter	    goto out;
82241090Speter	}
82341090Speter	if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 &&
82441090Speter	    lf->ops->symbol_values(lf, sym, &symval) == 0) {
82541090Speter	    lookup.symvalue = (u_long)symval.value;
82641090Speter	    lookup.symsize = symval.size;
82741090Speter	    error = copyout(&lookup, SCARG(uap, data), sizeof(lookup));
82841090Speter	} else
82941090Speter	    error = ENOENT;
83041090Speter    } else {
83141090Speter	for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link)) {
83241090Speter	    if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 &&
83341090Speter		lf->ops->symbol_values(lf, sym, &symval) == 0) {
83441090Speter		lookup.symvalue = (u_long)symval.value;
83541090Speter		lookup.symsize = symval.size;
83641090Speter		error = copyout(&lookup, SCARG(uap, data), sizeof(lookup));
83741090Speter		break;
83841090Speter	    }
83941090Speter	}
84041090Speter	if (!lf)
84141090Speter	    error = ENOENT;
84241090Speter    }
84341090Speterout:
84441090Speter    if (symstr)
84541090Speter	free(symstr, M_TEMP);
84641090Speter    return error;
84741090Speter}
84841090Speter
84940159Speter/*
85040159Speter * Preloaded module support
85140159Speter */
85240159Speter
85340159Speterstatic void
85440159Speterlinker_preload(void* arg)
85540159Speter{
85640159Speter    caddr_t		modptr;
85740159Speter    char		*modname;
85840162Speter    char		*modtype;
85940159Speter    linker_file_t	lf;
86040159Speter    linker_class_t	lc;
86140159Speter    int			error;
86240159Speter    struct linker_set	*sysinits;
86340159Speter    struct sysinit	**sipp;
86440159Speter    moduledata_t	*moddata;
86540159Speter
86640159Speter    modptr = NULL;
86740159Speter    while ((modptr = preload_search_next_name(modptr)) != NULL) {
86840159Speter	modname = (char *)preload_search_info(modptr, MODINFO_NAME);
86940162Speter	modtype = (char *)preload_search_info(modptr, MODINFO_TYPE);
87040159Speter	if (modname == NULL) {
87140625Smsmith	    printf("Preloaded module at %p does not have a name!\n", modptr);
87240159Speter	    continue;
87340159Speter	}
87440162Speter	if (modtype == NULL) {
87540625Smsmith	    printf("Preloaded module at %p does not have a type!\n", modptr);
87640162Speter	    continue;
87740162Speter	}
87840625Smsmith	printf("Preloaded %s \"%s\" at %p.\n", modtype, modname, modptr);
87940159Speter	lf = linker_find_file_by_name(modname);
88040159Speter	if (lf) {
88140159Speter	    lf->userrefs++;
88240159Speter	    continue;
88340159Speter	}
88440159Speter	lf = NULL;
88540159Speter	for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) {
88640159Speter	    error = lc->ops->load_file(modname, &lf);
88740159Speter	    if (error) {
88840159Speter		lf = NULL;
88940159Speter		break;
89040159Speter	    }
89140159Speter	}
89240159Speter	if (lf) {
89340159Speter	    lf->userrefs++;
89440159Speter
89540159Speter	    sysinits = (struct linker_set*)
89640159Speter		linker_file_lookup_symbol(lf, "sysinit_set", 0);
89740159Speter	    if (sysinits) {
89840159Speter		/* HACK ALERT!
89940159Speter		 * This is to set the sysinit moduledata so that the module
90040159Speter		 * can attach itself to the correct containing file.
90140159Speter		 * The sysinit could be run at *any* time.
90240159Speter		 */
90340159Speter		for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) {
90440159Speter		    if ((*sipp)->func == module_register_init) {
90540159Speter			moddata = (*sipp)->udata;
90640159Speter			moddata->_file = lf;
90740159Speter		    }
90840159Speter		}
90940159Speter		sysinit_add((struct sysinit **)sysinits->ls_items);
91040159Speter	    }
91140159Speter	}
91240159Speter    }
91340159Speter}
91440159Speter
91540159SpeterSYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, 0);
91640159Speter
91740159Speter/*
91840159Speter * Search for a not-loaded module by name.
91940159Speter *
92040159Speter * Modules may be found in the following locations:
92140159Speter *
92240159Speter * - preloaded (result is just the module name)
92340159Speter * - on disk (result is full path to module)
92440159Speter *
92540159Speter * If the module name is qualified in any way (contains path, etc.)
92640159Speter * the we simply return a copy of it.
92740159Speter *
92840159Speter * The search path can be manipulated via sysctl.  Note that we use the ';'
92940159Speter * character as a separator to be consistent with the bootloader.
93040159Speter */
93140159Speter
93240159Speterstatic char linker_path[MAXPATHLEN + 1] = "/;/boot/;/modules/";
93340159Speter
93440159SpeterSYSCTL_STRING(_kern, OID_AUTO, module_path, CTLFLAG_RW, linker_path,
93540159Speter	      sizeof(linker_path), "module load search path");
93640159Speter
93740159Speterstatic char *
93840159Speterlinker_strdup(const char *str)
93940159Speter{
94040159Speter    char	*result;
94140159Speter
94240159Speter    if ((result = malloc((strlen(str) + 1), M_LINKER, M_WAITOK)) != NULL)
94340159Speter	strcpy(result, str);
94440159Speter    return(result);
94540159Speter}
94640159Speter
94740159Speterchar *
94840159Speterlinker_search_path(const char *name)
94940159Speter{
95040159Speter    struct nameidata	nd;
95140159Speter    struct proc		*p = curproc;	/* XXX */
95240159Speter    char		*cp, *ep, *result;
95340159Speter    int			error;
95440159Speter    enum vtype		type;
95540159Speter
95640159Speter    /* qualified at all? */
95740159Speter    if (index(name, '/'))
95840159Speter	return(linker_strdup(name));
95940159Speter
96040159Speter    /* traverse the linker path */
96140159Speter    cp = linker_path;
96240159Speter    for (;;) {
96340159Speter
96440159Speter	/* find the end of this component */
96540159Speter	for (ep = cp; (*ep != 0) && (*ep != ';'); ep++)
96640159Speter	    ;
96740159Speter	result = malloc((strlen(name) + (ep - cp) + 1), M_LINKER, M_WAITOK);
96840159Speter	if (result == NULL)	/* actually ENOMEM */
96940159Speter	    return(NULL);
97040159Speter
97140159Speter	strncpy(result, cp, ep - cp);
97240159Speter	strcpy(result + (ep - cp), name);
97340159Speter
97440159Speter	/*
97540159Speter	 * Attempt to open the file, and return the path if we succeed and it's
97640159Speter	 * a regular file.
97740159Speter	 */
97840159Speter	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, result, p);
97940159Speter	error = vn_open(&nd, FREAD, 0);
98040159Speter	if (error == 0) {
98140159Speter	    type = nd.ni_vp->v_type;
98240159Speter	    VOP_UNLOCK(nd.ni_vp, 0, p);
98340159Speter	    vn_close(nd.ni_vp, FREAD, p->p_ucred, p);
98440159Speter	    if (type == VREG)
98540159Speter		return(result);
98640159Speter	}
98740159Speter	free(result, M_LINKER);
98840159Speter
98940159Speter	if (*ep == 0)
99040159Speter	    break;
99140159Speter	cp = ep + 1;
99240159Speter    }
99340159Speter    return(NULL);
99440159Speter}
995