kern_linker.c revision 50272
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 *
2650272Sbde *	$Id: kern_linker.c,v 1.35 1999/08/20 00:18:07 grog 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;
6050068Sgrogstatic linker_file_list_t linker_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);
6850068Sgrog    TAILQ_INIT(&linker_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
15148391Speter	/* Call function */
15248391Speter	(*((*sipp)->func))((*sipp)->udata);
15325537Sdfr    }
15425537Sdfr}
15525537Sdfr
15641055Speterstatic void
15741055Speterlinker_file_sysuninit(linker_file_t lf)
15841055Speter{
15941055Speter    struct linker_set* sysuninits;
16041055Speter    struct sysinit** sipp;
16141055Speter    struct sysinit** xipp;
16241055Speter    struct sysinit* save;
16341055Speter
16441055Speter    KLD_DPF(FILE, ("linker_file_sysuninit: calling SYSUNINITs for %s\n",
16541055Speter		   lf->filename));
16641055Speter
16741055Speter    sysuninits = (struct linker_set*)
16841055Speter	linker_file_lookup_symbol(lf, "sysuninit_set", 0);
16941055Speter
17041055Speter    KLD_DPF(FILE, ("linker_file_sysuninit: SYSUNINITs %p\n", sysuninits));
17141055Speter    if (!sysuninits)
17241055Speter	return;
17341055Speter
17441055Speter    /*
17541055Speter     * Perform a reverse bubble sort of the system initialization objects
17641055Speter     * by their subsystem (primary key) and order (secondary key).
17741055Speter     *
17841055Speter     * Since some things care about execution order, this is the
17941055Speter     * operation which ensures continued function.
18041055Speter     */
18141055Speter    for (sipp = (struct sysinit **)sysuninits->ls_items; *sipp; sipp++) {
18241055Speter	for (xipp = sipp + 1; *xipp; xipp++) {
18341055Speter	    if ((*sipp)->subsystem >= (*xipp)->subsystem ||
18441055Speter		 ((*sipp)->subsystem == (*xipp)->subsystem &&
18541055Speter		  (*sipp)->order >= (*xipp)->order))
18641055Speter		continue;	/* skip*/
18741055Speter	    save = *sipp;
18841055Speter	    *sipp = *xipp;
18941055Speter	    *xipp = save;
19041055Speter	}
19141055Speter    }
19241055Speter
19341055Speter
19441055Speter    /*
19541055Speter     * Traverse the (now) ordered list of system initialization tasks.
19641055Speter     * Perform each task, and continue on to the next task.
19741055Speter     */
19841055Speter    for (sipp = (struct sysinit **)sysuninits->ls_items; *sipp; sipp++) {
19941055Speter	if ((*sipp)->subsystem == SI_SUB_DUMMY)
20041055Speter	    continue;	/* skip dummy task(s)*/
20141055Speter
20248391Speter	/* Call function */
20348391Speter	(*((*sipp)->func))((*sipp)->udata);
20441055Speter    }
20541055Speter}
20641055Speter
20744078Sdfrstatic void
20844078Sdfrlinker_file_register_sysctls(linker_file_t lf)
20944078Sdfr{
21044078Sdfr    struct linker_set* sysctls;
21144078Sdfr
21244078Sdfr    KLD_DPF(FILE, ("linker_file_register_sysctls: registering SYSCTLs for %s\n",
21344078Sdfr		   lf->filename));
21444078Sdfr
21544078Sdfr    sysctls = (struct linker_set*)
21644078Sdfr	linker_file_lookup_symbol(lf, "sysctl_set", 0);
21744078Sdfr
21844078Sdfr    KLD_DPF(FILE, ("linker_file_register_sysctls: SYSCTLs %p\n", sysctls));
21944078Sdfr    if (!sysctls)
22044078Sdfr	return;
22144078Sdfr
22244078Sdfr    sysctl_register_set(sysctls);
22344078Sdfr}
22444078Sdfr
22544078Sdfrstatic void
22644078Sdfrlinker_file_unregister_sysctls(linker_file_t lf)
22744078Sdfr{
22844078Sdfr    struct linker_set* sysctls;
22944078Sdfr
23044078Sdfr    KLD_DPF(FILE, ("linker_file_unregister_sysctls: registering SYSCTLs for %s\n",
23144078Sdfr		   lf->filename));
23244078Sdfr
23344078Sdfr    sysctls = (struct linker_set*)
23444078Sdfr	linker_file_lookup_symbol(lf, "sysctl_set", 0);
23544078Sdfr
23644078Sdfr    KLD_DPF(FILE, ("linker_file_unregister_sysctls: SYSCTLs %p\n", sysctls));
23744078Sdfr    if (!sysctls)
23844078Sdfr	return;
23944078Sdfr
24044078Sdfr    sysctl_unregister_set(sysctls);
24144078Sdfr}
24244078Sdfr
24325537Sdfrint
24425537Sdfrlinker_load_file(const char* filename, linker_file_t* result)
24525537Sdfr{
24625537Sdfr    linker_class_t lc;
24725537Sdfr    linker_file_t lf;
24842755Speter    int foundfile, error = 0;
24940861Speter    char *koname = NULL;
25025537Sdfr
25125537Sdfr    lf = linker_find_file_by_name(filename);
25225537Sdfr    if (lf) {
25325537Sdfr	KLD_DPF(FILE, ("linker_load_file: file %s is already loaded, incrementing refs\n", filename));
25425537Sdfr	*result = lf;
25525537Sdfr	lf->refs++;
25625537Sdfr	goto out;
25725537Sdfr    }
25825537Sdfr
25940861Speter    koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK);
26040861Speter    if (koname == NULL) {
26140861Speter	error = ENOMEM;
26240861Speter	goto out;
26340861Speter    }
26440861Speter    sprintf(koname, "%s.ko", filename);
26525537Sdfr    lf = NULL;
26642755Speter    foundfile = 0;
26725537Sdfr    for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) {
26825537Sdfr	KLD_DPF(FILE, ("linker_load_file: trying to load %s as %s\n",
26925537Sdfr		       filename, lc->desc));
27042755Speter
27142755Speter	error = lc->ops->load_file(koname, &lf);	/* First with .ko */
27242755Speter	if (lf == NULL && error == ENOENT)
27342755Speter	    error = lc->ops->load_file(filename, &lf);	/* Then try without */
27442755Speter	/*
27542755Speter	 * If we got something other than ENOENT, then it exists but we cannot
27642755Speter	 * load it for some other reason.
27742755Speter	 */
27842755Speter	if (error != ENOENT)
27942755Speter	    foundfile = 1;
28025537Sdfr	if (lf) {
28144549Sdfr	    linker_file_register_sysctls(lf);
28225537Sdfr	    linker_file_sysinit(lf);
28325537Sdfr
28425537Sdfr	    *result = lf;
28540861Speter	    error = 0;
28625537Sdfr	    goto out;
28725537Sdfr	}
28825537Sdfr    }
28942755Speter    /*
29042755Speter     * Less than ideal, but tells the user whether it failed to load or
29142755Speter     * the module was not found.
29242755Speter     */
29342755Speter    if (foundfile)
29442755Speter	error = ENOEXEC;	/* Format not recognised (or unloadable) */
29542755Speter    else
29642755Speter	error = ENOENT;		/* Nothing found */
29725537Sdfr
29825537Sdfrout:
29940861Speter    if (koname)
30040861Speter	free(koname, M_LINKER);
30125537Sdfr    return error;
30225537Sdfr}
30325537Sdfr
30425537Sdfrlinker_file_t
30525537Sdfrlinker_find_file_by_name(const char* filename)
30625537Sdfr{
30725537Sdfr    linker_file_t lf = 0;
30840861Speter    char *koname;
30925537Sdfr
31040861Speter    koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK);
31140861Speter    if (koname == NULL)
31240861Speter	goto out;
31340861Speter    sprintf(koname, "%s.ko", filename);
31440861Speter
31525537Sdfr    lockmgr(&lock, LK_SHARED, 0, curproc);
31650068Sgrog    for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) {
31740861Speter	if (!strcmp(lf->filename, koname))
31840861Speter	    break;
31925537Sdfr	if (!strcmp(lf->filename, filename))
32025537Sdfr	    break;
32140861Speter    }
32225537Sdfr    lockmgr(&lock, LK_RELEASE, 0, curproc);
32325537Sdfr
32440861Speterout:
32540861Speter    if (koname)
32640861Speter	free(koname, M_LINKER);
32725537Sdfr    return lf;
32825537Sdfr}
32925537Sdfr
33025537Sdfrlinker_file_t
33125537Sdfrlinker_find_file_by_id(int fileid)
33225537Sdfr{
33325537Sdfr    linker_file_t lf = 0;
33425537Sdfr
33525537Sdfr    lockmgr(&lock, LK_SHARED, 0, curproc);
33650068Sgrog    for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link))
33725537Sdfr	if (lf->id == fileid)
33825537Sdfr	    break;
33925537Sdfr    lockmgr(&lock, LK_RELEASE, 0, curproc);
34025537Sdfr
34125537Sdfr    return lf;
34225537Sdfr}
34325537Sdfr
34425537Sdfrlinker_file_t
34540159Speterlinker_make_file(const char* pathname, void* priv, struct linker_file_ops* ops)
34625537Sdfr{
34725537Sdfr    linker_file_t lf = 0;
34825537Sdfr    int namelen;
34940159Speter    const char *filename;
35025537Sdfr
35140159Speter    filename = rindex(pathname, '/');
35240159Speter    if (filename && filename[1])
35340159Speter	filename++;
35440159Speter    else
35540159Speter	filename = pathname;
35640159Speter
35725537Sdfr    KLD_DPF(FILE, ("linker_make_file: new file, filename=%s\n", filename));
35845356Speter    lockmgr(&lock, LK_EXCLUSIVE, 0, curproc);
35925537Sdfr    namelen = strlen(filename) + 1;
36025537Sdfr    lf = malloc(sizeof(struct linker_file) + namelen, M_LINKER, M_WAITOK);
36125537Sdfr    if (!lf)
36225537Sdfr	goto out;
36340395Speter    bzero(lf, sizeof(*lf));
36425537Sdfr
36525537Sdfr    lf->refs = 1;
36625537Sdfr    lf->userrefs = 0;
36743185Sdfr    lf->flags = 0;
36825537Sdfr    lf->filename = (char*) (lf + 1);
36925537Sdfr    strcpy(lf->filename, filename);
37025537Sdfr    lf->id = next_file_id++;
37125537Sdfr    lf->ndeps = 0;
37225537Sdfr    lf->deps = NULL;
37325537Sdfr    STAILQ_INIT(&lf->common);
37425537Sdfr    TAILQ_INIT(&lf->modules);
37525537Sdfr
37625537Sdfr    lf->priv = priv;
37725537Sdfr    lf->ops = ops;
37850068Sgrog    TAILQ_INSERT_TAIL(&linker_files, lf, link);
37925537Sdfr
38025537Sdfrout:
38125537Sdfr    lockmgr(&lock, LK_RELEASE, 0, curproc);
38225537Sdfr    return lf;
38325537Sdfr}
38425537Sdfr
38525537Sdfrint
38625537Sdfrlinker_file_unload(linker_file_t file)
38725537Sdfr{
38825537Sdfr    module_t mod, next;
38925537Sdfr    struct common_symbol* cp;
39025537Sdfr    int error = 0;
39125537Sdfr    int i;
39225537Sdfr
39340159Speter    KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", file->refs));
39445356Speter    lockmgr(&lock, LK_EXCLUSIVE, 0, curproc);
39525537Sdfr    if (file->refs == 1) {
39625537Sdfr	KLD_DPF(FILE, ("linker_file_unload: file is unloading, informing modules\n"));
39725537Sdfr	/*
39825537Sdfr	 * Inform any modules associated with this file.
39925537Sdfr	 */
40025537Sdfr	for (mod = TAILQ_FIRST(&file->modules); mod; mod = next) {
40125537Sdfr	    next = module_getfnext(mod);
40225537Sdfr
40325537Sdfr	    /*
40425537Sdfr	     * Give the module a chance to veto the unload.
40525537Sdfr	     */
40643301Sdillon	    if ((error = module_unload(mod)) != 0) {
40725537Sdfr		KLD_DPF(FILE, ("linker_file_unload: module %x vetoes unload\n",
40825537Sdfr			       mod));
40925537Sdfr		lockmgr(&lock, LK_RELEASE, 0, curproc);
41025537Sdfr		goto out;
41125537Sdfr	    }
41225537Sdfr
41325537Sdfr	    module_release(mod);
41425537Sdfr	}
41525537Sdfr    }
41625537Sdfr
41725537Sdfr    file->refs--;
41825537Sdfr    if (file->refs > 0) {
41925537Sdfr	lockmgr(&lock, LK_RELEASE, 0, curproc);
42025537Sdfr	goto out;
42125537Sdfr    }
42225537Sdfr
42343185Sdfr    /* Don't try to run SYSUNINITs if we are unloaded due to a link error */
42444078Sdfr    if (file->flags & LINKER_FILE_LINKED) {
42543185Sdfr	linker_file_sysuninit(file);
42644078Sdfr	linker_file_unregister_sysctls(file);
42744078Sdfr    }
42841055Speter
42950068Sgrog    TAILQ_REMOVE(&linker_files, file, link);
43025537Sdfr    lockmgr(&lock, LK_RELEASE, 0, curproc);
43140159Speter
43225537Sdfr    for (i = 0; i < file->ndeps; i++)
43325537Sdfr	linker_file_unload(file->deps[i]);
43425537Sdfr    free(file->deps, M_LINKER);
43525537Sdfr
43625537Sdfr    for (cp = STAILQ_FIRST(&file->common); cp;
43725537Sdfr	 cp = STAILQ_FIRST(&file->common)) {
43825537Sdfr	STAILQ_REMOVE(&file->common, cp, common_symbol, link);
43925537Sdfr	free(cp, M_LINKER);
44025537Sdfr    }
44125537Sdfr
44225537Sdfr    file->ops->unload(file);
44325537Sdfr    free(file, M_LINKER);
44425537Sdfr
44525537Sdfrout:
44625537Sdfr    return error;
44725537Sdfr}
44825537Sdfr
44925537Sdfrint
45025537Sdfrlinker_file_add_dependancy(linker_file_t file, linker_file_t dep)
45125537Sdfr{
45225537Sdfr    linker_file_t* newdeps;
45325537Sdfr
45425537Sdfr    newdeps = malloc((file->ndeps + 1) * sizeof(linker_file_t*),
45525537Sdfr		     M_LINKER, M_WAITOK);
45625537Sdfr    if (newdeps == NULL)
45725537Sdfr	return ENOMEM;
45840395Speter    bzero(newdeps, (file->ndeps + 1) * sizeof(linker_file_t*));
45925537Sdfr
46025537Sdfr    if (file->deps) {
46125537Sdfr	bcopy(file->deps, newdeps, file->ndeps * sizeof(linker_file_t*));
46225537Sdfr	free(file->deps, M_LINKER);
46325537Sdfr    }
46425537Sdfr    file->deps = newdeps;
46525537Sdfr    file->deps[file->ndeps] = dep;
46625537Sdfr    file->ndeps++;
46725537Sdfr
46825537Sdfr    return 0;
46925537Sdfr}
47025537Sdfr
47125537Sdfrcaddr_t
47225537Sdfrlinker_file_lookup_symbol(linker_file_t file, const char* name, int deps)
47325537Sdfr{
47443309Sdillon    c_linker_sym_t sym;
47538275Sdfr    linker_symval_t symval;
47642849Speter    linker_file_t lf;
47725537Sdfr    caddr_t address;
47825537Sdfr    size_t common_size = 0;
47925537Sdfr    int i;
48025537Sdfr
48140159Speter    KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d\n",
48225537Sdfr		  file, name, deps));
48325537Sdfr
48438275Sdfr    if (file->ops->lookup_symbol(file, name, &sym) == 0) {
48538275Sdfr	file->ops->symbol_values(file, sym, &symval);
48638275Sdfr	if (symval.value == 0)
48725537Sdfr	    /*
48825537Sdfr	     * For commons, first look them up in the dependancies and
48925537Sdfr	     * only allocate space if not found there.
49025537Sdfr	     */
49138275Sdfr	    common_size = symval.size;
49240159Speter	else {
49340159Speter	    KLD_DPF(SYM, ("linker_file_lookup_symbol: symbol.value=%x\n", symval.value));
49438275Sdfr	    return symval.value;
49540159Speter	}
49638275Sdfr    }
49725537Sdfr
49842849Speter    if (deps) {
49925537Sdfr	for (i = 0; i < file->ndeps; i++) {
50025537Sdfr	    address = linker_file_lookup_symbol(file->deps[i], name, 0);
50140159Speter	    if (address) {
50240159Speter		KLD_DPF(SYM, ("linker_file_lookup_symbol: deps value=%x\n", address));
50325537Sdfr		return address;
50440159Speter	    }
50525537Sdfr	}
50625537Sdfr
50742849Speter	/* If we have not found it in the dependencies, search globally */
50850068Sgrog	for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) {
50942849Speter	    /* But skip the current file if it's on the list */
51042849Speter	    if (lf == file)
51142849Speter		continue;
51242849Speter	    /* And skip the files we searched above */
51342849Speter	    for (i = 0; i < file->ndeps; i++)
51442849Speter		if (lf == file->deps[i])
51542849Speter		    break;
51642849Speter	    if (i < file->ndeps)
51742849Speter		continue;
51842849Speter	    address = linker_file_lookup_symbol(lf, name, 0);
51942849Speter	    if (address) {
52042849Speter		KLD_DPF(SYM, ("linker_file_lookup_symbol: global value=%x\n", address));
52142849Speter		return address;
52242849Speter	    }
52342849Speter	}
52442849Speter    }
52542849Speter
52625537Sdfr    if (common_size > 0) {
52725537Sdfr	/*
52825537Sdfr	 * This is a common symbol which was not found in the
52925537Sdfr	 * dependancies.  We maintain a simple common symbol table in
53025537Sdfr	 * the file object.
53125537Sdfr	 */
53225537Sdfr	struct common_symbol* cp;
53325537Sdfr
53425537Sdfr	for (cp = STAILQ_FIRST(&file->common); cp;
53525537Sdfr	     cp = STAILQ_NEXT(cp, link))
53640159Speter	    if (!strcmp(cp->name, name)) {
53740159Speter		KLD_DPF(SYM, ("linker_file_lookup_symbol: old common value=%x\n", cp->address));
53825537Sdfr		return cp->address;
53940159Speter	    }
54025537Sdfr
54125537Sdfr	/*
54225537Sdfr	 * Round the symbol size up to align.
54325537Sdfr	 */
54425537Sdfr	common_size = (common_size + sizeof(int) - 1) & -sizeof(int);
54525537Sdfr	cp = malloc(sizeof(struct common_symbol)
54625537Sdfr		    + common_size
54725537Sdfr		    + strlen(name) + 1,
54825537Sdfr		    M_LINKER, M_WAITOK);
54940159Speter	if (!cp) {
55040159Speter	    KLD_DPF(SYM, ("linker_file_lookup_symbol: nomem\n"));
55125537Sdfr	    return 0;
55240159Speter	}
55340395Speter	bzero(cp, sizeof(struct common_symbol) + common_size + strlen(name)+ 1);
55425537Sdfr
55525537Sdfr	cp->address = (caddr_t) (cp + 1);
55625537Sdfr	cp->name = cp->address + common_size;
55725537Sdfr	strcpy(cp->name, name);
55825537Sdfr	bzero(cp->address, common_size);
55925537Sdfr	STAILQ_INSERT_TAIL(&file->common, cp, link);
56025537Sdfr
56140159Speter	KLD_DPF(SYM, ("linker_file_lookup_symbol: new common value=%x\n", cp->address));
56225537Sdfr	return cp->address;
56325537Sdfr    }
56425537Sdfr
56540159Speter    KLD_DPF(SYM, ("linker_file_lookup_symbol: fail\n"));
56625537Sdfr    return 0;
56725537Sdfr}
56825537Sdfr
56940159Speter#ifdef DDB
57025537Sdfr/*
57140159Speter * DDB Helpers.  DDB has to look across multiple files with their own
57240159Speter * symbol tables and string tables.
57340159Speter *
57440159Speter * Note that we do not obey list locking protocols here.  We really don't
57540159Speter * need DDB to hang because somebody's got the lock held.  We'll take the
57640159Speter * chance that the files list is inconsistant instead.
57740159Speter */
57840159Speter
57940159Speterint
58043309Sdillonlinker_ddb_lookup(const char *symstr, c_linker_sym_t *sym)
58140159Speter{
58240159Speter    linker_file_t lf;
58340159Speter
58450068Sgrog    for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) {
58540159Speter	if (lf->ops->lookup_symbol(lf, symstr, sym) == 0)
58640159Speter	    return 0;
58740159Speter    }
58840159Speter    return ENOENT;
58940159Speter}
59040159Speter
59140159Speterint
59243309Sdillonlinker_ddb_search_symbol(caddr_t value, c_linker_sym_t *sym, long *diffp)
59340159Speter{
59440159Speter    linker_file_t lf;
59550272Sbde    u_long off = (uintptr_t)value;
59640159Speter    u_long diff, bestdiff;
59743309Sdillon    c_linker_sym_t best;
59843309Sdillon    c_linker_sym_t es;
59940159Speter
60040159Speter    best = 0;
60140159Speter    bestdiff = off;
60250068Sgrog    for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) {
60340159Speter	if (lf->ops->search_symbol(lf, value, &es, &diff) != 0)
60440159Speter	    continue;
60540159Speter	if (es != 0 && diff < bestdiff) {
60640159Speter	    best = es;
60740159Speter	    bestdiff = diff;
60840159Speter	}
60940159Speter	if (bestdiff == 0)
61040159Speter	    break;
61140159Speter    }
61240159Speter    if (best) {
61340159Speter	*sym = best;
61440159Speter	*diffp = bestdiff;
61540159Speter	return 0;
61640159Speter    } else {
61740159Speter	*sym = 0;
61840159Speter	*diffp = off;
61940159Speter	return ENOENT;
62040159Speter    }
62140159Speter}
62240159Speter
62340159Speterint
62443309Sdillonlinker_ddb_symbol_values(c_linker_sym_t sym, linker_symval_t *symval)
62540159Speter{
62640159Speter    linker_file_t lf;
62740159Speter
62850068Sgrog    for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) {
62940159Speter	if (lf->ops->symbol_values(lf, sym, symval) == 0)
63040159Speter	    return 0;
63140159Speter    }
63240159Speter    return ENOENT;
63340159Speter}
63440159Speter
63540159Speter#endif
63640159Speter
63740159Speter/*
63825537Sdfr * Syscalls.
63925537Sdfr */
64025537Sdfr
64125537Sdfrint
64230994Sphkkldload(struct proc* p, struct kldload_args* uap)
64325537Sdfr{
64442316Smsmith    char* filename = NULL, *modulename;
64525537Sdfr    linker_file_t lf;
64625537Sdfr    int error = 0;
64725537Sdfr
64830994Sphk    p->p_retval[0] = -1;
64925537Sdfr
65025537Sdfr    if (securelevel > 0)
65125537Sdfr	return EPERM;
65225537Sdfr
65346112Sphk    if ((error = suser(p)) != 0)
65425537Sdfr	return error;
65525537Sdfr
65625537Sdfr    filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
65743301Sdillon    if ((error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) != 0)
65825537Sdfr	goto out;
65925537Sdfr
66042316Smsmith    /* Can't load more than one module with the same name */
66142316Smsmith    modulename = rindex(filename, '/');
66242316Smsmith    if (modulename == NULL)
66342316Smsmith	modulename = filename;
66444173Sdfr    else
66544173Sdfr	modulename++;
66642316Smsmith    if (linker_find_file_by_name(modulename)) {
66742316Smsmith	error = EEXIST;
66842316Smsmith	goto out;
66942316Smsmith    }
67042316Smsmith
67143301Sdillon    if ((error = linker_load_file(filename, &lf)) != 0)
67225537Sdfr	goto out;
67325537Sdfr
67425537Sdfr    lf->userrefs++;
67530994Sphk    p->p_retval[0] = lf->id;
67640159Speter
67725537Sdfrout:
67825537Sdfr    if (filename)
67925537Sdfr	free(filename, M_TEMP);
68025537Sdfr    return error;
68125537Sdfr}
68225537Sdfr
68325537Sdfrint
68430994Sphkkldunload(struct proc* p, struct kldunload_args* uap)
68525537Sdfr{
68625537Sdfr    linker_file_t lf;
68725537Sdfr    int error = 0;
68825537Sdfr
68925537Sdfr    if (securelevel > 0)
69025537Sdfr	return EPERM;
69125537Sdfr
69246112Sphk    if ((error = suser(p)) != 0)
69325537Sdfr	return error;
69425537Sdfr
69525537Sdfr    lf = linker_find_file_by_id(SCARG(uap, fileid));
69625537Sdfr    if (lf) {
69725537Sdfr	KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs));
69825537Sdfr	if (lf->userrefs == 0) {
69943084Speter	    printf("linkerunload: attempt to unload file that was loaded by the kernel\n");
70025537Sdfr	    error = EBUSY;
70125537Sdfr	    goto out;
70225537Sdfr	}
70343084Speter	lf->userrefs--;
70442837Speter	error = linker_file_unload(lf);
70542837Speter	if (error)
70643084Speter	    lf->userrefs++;
70725537Sdfr    } else
70825537Sdfr	error = ENOENT;
70925537Sdfr
71025537Sdfrout:
71125537Sdfr    return error;
71225537Sdfr}
71325537Sdfr
71425537Sdfrint
71530994Sphkkldfind(struct proc* p, struct kldfind_args* uap)
71625537Sdfr{
71742316Smsmith    char* filename = NULL, *modulename;
71825537Sdfr    linker_file_t lf;
71925537Sdfr    int error = 0;
72025537Sdfr
72130994Sphk    p->p_retval[0] = -1;
72225537Sdfr
72325537Sdfr    filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
72443301Sdillon    if ((error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) != 0)
72525537Sdfr	goto out;
72625537Sdfr
72742316Smsmith    modulename = rindex(filename, '/');
72842316Smsmith    if (modulename == NULL)
72942316Smsmith	modulename = filename;
73042316Smsmith
73142316Smsmith    lf = linker_find_file_by_name(modulename);
73225537Sdfr    if (lf)
73330994Sphk	p->p_retval[0] = lf->id;
73425537Sdfr    else
73525537Sdfr	error = ENOENT;
73640159Speter
73725537Sdfrout:
73825537Sdfr    if (filename)
73925537Sdfr	free(filename, M_TEMP);
74025537Sdfr    return error;
74125537Sdfr}
74225537Sdfr
74325537Sdfrint
74430994Sphkkldnext(struct proc* p, struct kldnext_args* uap)
74525537Sdfr{
74625537Sdfr    linker_file_t lf;
74725537Sdfr    int error = 0;
74825537Sdfr
74925537Sdfr    if (SCARG(uap, fileid) == 0) {
75050068Sgrog	if (TAILQ_FIRST(&linker_files))
75150068Sgrog	    p->p_retval[0] = TAILQ_FIRST(&linker_files)->id;
75225537Sdfr	else
75330994Sphk	    p->p_retval[0] = 0;
75425537Sdfr	return 0;
75525537Sdfr    }
75640159Speter
75725537Sdfr    lf = linker_find_file_by_id(SCARG(uap, fileid));
75825537Sdfr    if (lf) {
75925537Sdfr	if (TAILQ_NEXT(lf, link))
76030994Sphk	    p->p_retval[0] = TAILQ_NEXT(lf, link)->id;
76125537Sdfr	else
76230994Sphk	    p->p_retval[0] = 0;
76325537Sdfr    } else
76425537Sdfr	error = ENOENT;
76525537Sdfr
76625537Sdfr    return error;
76725537Sdfr}
76825537Sdfr
76925537Sdfrint
77030994Sphkkldstat(struct proc* p, struct kldstat_args* uap)
77125537Sdfr{
77225537Sdfr    linker_file_t lf;
77325537Sdfr    int error = 0;
77425537Sdfr    int version;
77525537Sdfr    struct kld_file_stat* stat;
77625537Sdfr    int namelen;
77725537Sdfr
77825537Sdfr    lf = linker_find_file_by_id(SCARG(uap, fileid));
77925537Sdfr    if (!lf) {
78025537Sdfr	error = ENOENT;
78125537Sdfr	goto out;
78225537Sdfr    }
78325537Sdfr
78425537Sdfr    stat = SCARG(uap, stat);
78525537Sdfr
78625537Sdfr    /*
78725537Sdfr     * Check the version of the user's structure.
78825537Sdfr     */
78943301Sdillon    if ((error = copyin(&stat->version, &version, sizeof(version))) != 0)
79025537Sdfr	goto out;
79125537Sdfr    if (version != sizeof(struct kld_file_stat)) {
79225537Sdfr	error = EINVAL;
79325537Sdfr	goto out;
79425537Sdfr    }
79525537Sdfr
79625537Sdfr    namelen = strlen(lf->filename) + 1;
79725537Sdfr    if (namelen > MAXPATHLEN)
79825537Sdfr	namelen = MAXPATHLEN;
79943301Sdillon    if ((error = copyout(lf->filename, &stat->name[0], namelen)) != 0)
80025537Sdfr	goto out;
80143301Sdillon    if ((error = copyout(&lf->refs, &stat->refs, sizeof(int))) != 0)
80225537Sdfr	goto out;
80343301Sdillon    if ((error = copyout(&lf->id, &stat->id, sizeof(int))) != 0)
80425537Sdfr	goto out;
80543301Sdillon    if ((error = copyout(&lf->address, &stat->address, sizeof(caddr_t))) != 0)
80625537Sdfr	goto out;
80743301Sdillon    if ((error = copyout(&lf->size, &stat->size, sizeof(size_t))) != 0)
80825537Sdfr	goto out;
80925537Sdfr
81030994Sphk    p->p_retval[0] = 0;
81125537Sdfr
81225537Sdfrout:
81325537Sdfr    return error;
81425537Sdfr}
81525537Sdfr
81625537Sdfrint
81730994Sphkkldfirstmod(struct proc* p, struct kldfirstmod_args* uap)
81825537Sdfr{
81925537Sdfr    linker_file_t lf;
82025537Sdfr    int error = 0;
82125537Sdfr
82225537Sdfr    lf = linker_find_file_by_id(SCARG(uap, fileid));
82325537Sdfr    if (lf) {
82425537Sdfr	if (TAILQ_FIRST(&lf->modules))
82530994Sphk	    p->p_retval[0] = module_getid(TAILQ_FIRST(&lf->modules));
82625537Sdfr	else
82730994Sphk	    p->p_retval[0] = 0;
82825537Sdfr    } else
82925537Sdfr	error = ENOENT;
83025537Sdfr
83125537Sdfr    return error;
83225537Sdfr}
83340159Speter
83441090Speterint
83541090Speterkldsym(struct proc *p, struct kldsym_args *uap)
83641090Speter{
83741090Speter    char *symstr = NULL;
83843309Sdillon    c_linker_sym_t sym;
83941090Speter    linker_symval_t symval;
84041090Speter    linker_file_t lf;
84141090Speter    struct kld_sym_lookup lookup;
84241090Speter    int error = 0;
84341090Speter
84443301Sdillon    if ((error = copyin(SCARG(uap, data), &lookup, sizeof(lookup))) != 0)
84541090Speter	goto out;
84641090Speter    if (lookup.version != sizeof(lookup) || SCARG(uap, cmd) != KLDSYM_LOOKUP) {
84741090Speter	error = EINVAL;
84841090Speter	goto out;
84941090Speter    }
85041090Speter
85141090Speter    symstr = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
85243301Sdillon    if ((error = copyinstr(lookup.symname, symstr, MAXPATHLEN, NULL)) != 0)
85341090Speter	goto out;
85441090Speter
85541090Speter    if (SCARG(uap, fileid) != 0) {
85641090Speter	lf = linker_find_file_by_id(SCARG(uap, fileid));
85741090Speter	if (lf == NULL) {
85841090Speter	    error = ENOENT;
85941090Speter	    goto out;
86041090Speter	}
86141090Speter	if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 &&
86241090Speter	    lf->ops->symbol_values(lf, sym, &symval) == 0) {
86350272Sbde	    lookup.symvalue = (uintptr_t)symval.value;
86441090Speter	    lookup.symsize = symval.size;
86541090Speter	    error = copyout(&lookup, SCARG(uap, data), sizeof(lookup));
86641090Speter	} else
86741090Speter	    error = ENOENT;
86841090Speter    } else {
86950068Sgrog	for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) {
87041090Speter	    if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 &&
87141090Speter		lf->ops->symbol_values(lf, sym, &symval) == 0) {
87250272Sbde		lookup.symvalue = (uintptr_t)symval.value;
87341090Speter		lookup.symsize = symval.size;
87441090Speter		error = copyout(&lookup, SCARG(uap, data), sizeof(lookup));
87541090Speter		break;
87641090Speter	    }
87741090Speter	}
87841090Speter	if (!lf)
87941090Speter	    error = ENOENT;
88041090Speter    }
88141090Speterout:
88241090Speter    if (symstr)
88341090Speter	free(symstr, M_TEMP);
88441090Speter    return error;
88541090Speter}
88641090Speter
88740159Speter/*
88840159Speter * Preloaded module support
88940159Speter */
89040159Speter
89140159Speterstatic void
89240159Speterlinker_preload(void* arg)
89340159Speter{
89440159Speter    caddr_t		modptr;
89540159Speter    char		*modname;
89640162Speter    char		*modtype;
89740159Speter    linker_file_t	lf;
89840159Speter    linker_class_t	lc;
89940159Speter    int			error;
90040159Speter    struct linker_set	*sysinits;
90140159Speter    struct sysinit	**sipp;
90246693Speter    const moduledata_t	*moddata;
90340159Speter
90440159Speter    modptr = NULL;
90540159Speter    while ((modptr = preload_search_next_name(modptr)) != NULL) {
90640159Speter	modname = (char *)preload_search_info(modptr, MODINFO_NAME);
90740162Speter	modtype = (char *)preload_search_info(modptr, MODINFO_TYPE);
90840159Speter	if (modname == NULL) {
90940625Smsmith	    printf("Preloaded module at %p does not have a name!\n", modptr);
91040159Speter	    continue;
91140159Speter	}
91240162Speter	if (modtype == NULL) {
91340625Smsmith	    printf("Preloaded module at %p does not have a type!\n", modptr);
91440162Speter	    continue;
91540162Speter	}
91640625Smsmith	printf("Preloaded %s \"%s\" at %p.\n", modtype, modname, modptr);
91740159Speter	lf = linker_find_file_by_name(modname);
91840159Speter	if (lf) {
91940159Speter	    lf->userrefs++;
92040159Speter	    continue;
92140159Speter	}
92240159Speter	lf = NULL;
92340159Speter	for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) {
92440159Speter	    error = lc->ops->load_file(modname, &lf);
92540159Speter	    if (error) {
92640159Speter		lf = NULL;
92740159Speter		break;
92840159Speter	    }
92940159Speter	}
93040159Speter	if (lf) {
93140159Speter	    lf->userrefs++;
93240159Speter
93340159Speter	    sysinits = (struct linker_set*)
93440159Speter		linker_file_lookup_symbol(lf, "sysinit_set", 0);
93540159Speter	    if (sysinits) {
93640159Speter		/* HACK ALERT!
93740159Speter		 * This is to set the sysinit moduledata so that the module
93840159Speter		 * can attach itself to the correct containing file.
93940159Speter		 * The sysinit could be run at *any* time.
94040159Speter		 */
94140159Speter		for (sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) {
94240159Speter		    if ((*sipp)->func == module_register_init) {
94340159Speter			moddata = (*sipp)->udata;
94446693Speter			error = module_register(moddata, lf);
94546693Speter			if (error)
94646693Speter			    printf("Preloaded %s \"%s\" failed to register: %d\n",
94746693Speter				modtype, modname, error);
94840159Speter		    }
94940159Speter		}
95040159Speter		sysinit_add((struct sysinit **)sysinits->ls_items);
95140159Speter	    }
95244078Sdfr	    linker_file_register_sysctls(lf);
95340159Speter	}
95440159Speter    }
95540159Speter}
95640159Speter
95740159SpeterSYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, 0);
95840159Speter
95940159Speter/*
96040159Speter * Search for a not-loaded module by name.
96140159Speter *
96240159Speter * Modules may be found in the following locations:
96340159Speter *
96440159Speter * - preloaded (result is just the module name)
96540159Speter * - on disk (result is full path to module)
96640159Speter *
96740159Speter * If the module name is qualified in any way (contains path, etc.)
96840159Speter * the we simply return a copy of it.
96940159Speter *
97040159Speter * The search path can be manipulated via sysctl.  Note that we use the ';'
97140159Speter * character as a separator to be consistent with the bootloader.
97240159Speter */
97340159Speter
97450272Sbdestatic char linker_path[MAXPATHLEN] = "/;/boot/;/modules/";
97540159Speter
97640159SpeterSYSCTL_STRING(_kern, OID_AUTO, module_path, CTLFLAG_RW, linker_path,
97740159Speter	      sizeof(linker_path), "module load search path");
97840159Speter
97940159Speterstatic char *
98040159Speterlinker_strdup(const char *str)
98140159Speter{
98240159Speter    char	*result;
98340159Speter
98440159Speter    if ((result = malloc((strlen(str) + 1), M_LINKER, M_WAITOK)) != NULL)
98540159Speter	strcpy(result, str);
98640159Speter    return(result);
98740159Speter}
98840159Speter
98940159Speterchar *
99040159Speterlinker_search_path(const char *name)
99140159Speter{
99240159Speter    struct nameidata	nd;
99340159Speter    struct proc		*p = curproc;	/* XXX */
99440159Speter    char		*cp, *ep, *result;
99540159Speter    int			error;
99640159Speter    enum vtype		type;
99740159Speter
99840159Speter    /* qualified at all? */
99940159Speter    if (index(name, '/'))
100040159Speter	return(linker_strdup(name));
100140159Speter
100240159Speter    /* traverse the linker path */
100340159Speter    cp = linker_path;
100440159Speter    for (;;) {
100540159Speter
100640159Speter	/* find the end of this component */
100740159Speter	for (ep = cp; (*ep != 0) && (*ep != ';'); ep++)
100840159Speter	    ;
100940159Speter	result = malloc((strlen(name) + (ep - cp) + 1), M_LINKER, M_WAITOK);
101040159Speter	if (result == NULL)	/* actually ENOMEM */
101140159Speter	    return(NULL);
101240159Speter
101340159Speter	strncpy(result, cp, ep - cp);
101440159Speter	strcpy(result + (ep - cp), name);
101540159Speter
101640159Speter	/*
101740159Speter	 * Attempt to open the file, and return the path if we succeed and it's
101840159Speter	 * a regular file.
101940159Speter	 */
102040159Speter	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, result, p);
102140159Speter	error = vn_open(&nd, FREAD, 0);
102240159Speter	if (error == 0) {
102340159Speter	    type = nd.ni_vp->v_type;
102440159Speter	    VOP_UNLOCK(nd.ni_vp, 0, p);
102540159Speter	    vn_close(nd.ni_vp, FREAD, p->p_ucred, p);
102640159Speter	    if (type == VREG)
102740159Speter		return(result);
102840159Speter	}
102940159Speter	free(result, M_LINKER);
103040159Speter
103140159Speter	if (*ep == 0)
103240159Speter	    break;
103340159Speter	cp = ep + 1;
103440159Speter    }
103540159Speter    return(NULL);
103640159Speter}
1037