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