kern_linker.c revision 25537
1/*-
2 * Copyright (c) 1997 Doug Rabson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 *	$Id$
27 */
28
29#include <sys/param.h>
30#include <sys/kernel.h>
31#include <sys/systm.h>
32#include <sys/sysctl.h>
33#include <sys/queue.h>
34#include <sys/libkern.h>
35#include <sys/malloc.h>
36#include <sys/sysproto.h>
37#include <sys/sysent.h>
38#include <sys/proc.h>
39#include <sys/lock.h>
40#include <machine/cpu.h>
41#include <sys/module.h>
42#include <sys/linker.h>
43
44static struct lock lock;	/* lock for the file list */
45static linker_class_list_t classes;
46static linker_file_list_t files;
47static int next_file_id = 1;
48
49static void
50linker_init(void* arg)
51{
52    lockinit(&lock, PVM, "klink", 0, 0);
53    TAILQ_INIT(&classes);
54    TAILQ_INIT(&files);
55}
56
57SYSINIT(linker, SI_SUB_KMEM, SI_ORDER_SECOND, linker_init, 0);
58
59int
60linker_add_class(const char* desc, void* priv,
61		 struct linker_class_ops* ops)
62{
63    linker_class_t lc;
64
65    lc = malloc(sizeof(struct linker_class), M_LINKER, M_NOWAIT);
66    if (!lc)
67	return ENOMEM;
68
69    lc->desc = desc;
70    lc->priv = priv;
71    lc->ops = ops;
72    TAILQ_INSERT_HEAD(&classes, lc, link);
73
74    return 0;
75}
76
77static void
78linker_file_sysinit(linker_file_t lf)
79{
80    struct linker_set* sysinits;
81    struct sysinit** sipp;
82    struct sysinit** xipp;
83    struct sysinit* save;
84    int rval[2];		/* SI_TYPE_KTHREAD support*/
85
86    linker_current_file = lf;
87
88    KLD_DPF(FILE, ("linker_file_sysinit: calling SYSINITs for %s\n",
89		   lf->filename));
90
91    sysinits = (struct linker_set*)
92	linker_file_lookup_symbol(lf, "sysinit_set", 0);
93    if (!sysinits)
94	return;
95
96    /*
97     * Perform a bubble sort of the system initialization objects by
98     * their subsystem (primary key) and order (secondary key).
99     *
100     * Since some things care about execution order, this is the
101     * operation which ensures continued function.
102     */
103    for( sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) {
104	for( xipp = sipp + 1; *xipp; xipp++) {
105	    if( (*sipp)->subsystem < (*xipp)->subsystem ||
106		( (*sipp)->subsystem == (*xipp)->subsystem &&
107		  (*sipp)->order < (*xipp)->order))
108		continue;	/* skip*/
109	    save = *sipp;
110	    *sipp = *xipp;
111	    *xipp = save;
112	}
113    }
114
115
116    /*
117     * Traverse the (now) ordered list of system initialization tasks.
118     * Perform each task, and continue on to the next task.
119     *
120     * The last item on the list is expected to be the scheduler,
121     * which will not return.
122     */
123    for( sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) {
124	if( (*sipp)->subsystem == SI_SUB_DUMMY)
125	    continue;	/* skip dummy task(s)*/
126
127	switch( (*sipp)->type) {
128	case SI_TYPE_DEFAULT:
129	    /* no special processing*/
130	    (*((*sipp)->func))( (*sipp)->udata);
131	    break;
132
133	case SI_TYPE_KTHREAD:
134	    /* kernel thread*/
135	    if (fork(&proc0, NULL, rval))
136		panic("fork kernel process");
137	    cpu_set_fork_handler(pfind(rval[0]), (*sipp)->func, (*sipp)->udata);
138	    break;
139
140	default:
141	    panic( "linker_file_sysinit: unrecognized init type");
142	}
143    }
144}
145
146int
147linker_load_file(const char* filename, linker_file_t* result)
148{
149    linker_class_t lc;
150    linker_file_t lf;
151    int error = 0;
152
153    lf = linker_find_file_by_name(filename);
154    if (lf) {
155	KLD_DPF(FILE, ("linker_load_file: file %s is already loaded, incrementing refs\n", filename));
156	*result = lf;
157	lf->refs++;
158	goto out;
159    }
160
161    lf = NULL;
162    for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) {
163	KLD_DPF(FILE, ("linker_load_file: trying to load %s as %s\n",
164		       filename, lc->desc));
165	if (error = lc->ops->load_file(filename, &lf))
166	    goto out;
167	if (lf) {
168	    linker_file_sysinit(lf);
169
170	    *result = lf;
171	    goto out;
172	}
173    }
174
175    error = ENOEXEC;		/* format not recognised */
176
177out:
178    return error;
179}
180
181linker_file_t
182linker_find_file_by_name(const char* filename)
183{
184    linker_file_t lf = 0;
185
186    lockmgr(&lock, LK_SHARED, 0, curproc);
187    for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link))
188	if (!strcmp(lf->filename, filename))
189	    break;
190    lockmgr(&lock, LK_RELEASE, 0, curproc);
191
192    return lf;
193}
194
195linker_file_t
196linker_find_file_by_id(int fileid)
197{
198    linker_file_t lf = 0;
199
200    lockmgr(&lock, LK_SHARED, 0, curproc);
201    for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link))
202	if (lf->id == fileid)
203	    break;
204    lockmgr(&lock, LK_RELEASE, 0, curproc);
205
206    return lf;
207}
208
209linker_file_t
210linker_make_file(const char* filename, void* priv, struct linker_file_ops* ops)
211{
212    linker_file_t lf = 0;
213    int namelen;
214
215    KLD_DPF(FILE, ("linker_make_file: new file, filename=%s\n", filename));
216    lockmgr(&lock, LK_EXCLUSIVE|LK_RETRY, 0, curproc);
217    namelen = strlen(filename) + 1;
218    lf = malloc(sizeof(struct linker_file) + namelen, M_LINKER, M_WAITOK);
219    if (!lf)
220	goto out;
221
222    lf->refs = 1;
223    lf->userrefs = 0;
224    lf->filename = (char*) (lf + 1);
225    strcpy(lf->filename, filename);
226    lf->id = next_file_id++;
227    lf->ndeps = 0;
228    lf->deps = NULL;
229    STAILQ_INIT(&lf->common);
230    TAILQ_INIT(&lf->modules);
231
232    lf->priv = priv;
233    lf->ops = ops;
234    TAILQ_INSERT_TAIL(&files, lf, link);
235
236out:
237    lockmgr(&lock, LK_RELEASE, 0, curproc);
238    return lf;
239}
240
241int
242linker_file_unload(linker_file_t file)
243{
244    module_t mod, next;
245    struct common_symbol* cp;
246    int error = 0;
247    int i;
248
249    KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", lf->refs));
250    lockmgr(&lock, LK_EXCLUSIVE|LK_RETRY, 0, curproc);
251    if (file->refs == 1) {
252	KLD_DPF(FILE, ("linker_file_unload: file is unloading, informing modules\n"));
253	/*
254	 * Inform any modules associated with this file.
255	 */
256	for (mod = TAILQ_FIRST(&file->modules); mod; mod = next) {
257	    next = module_getfnext(mod);
258
259	    /*
260	     * Give the module a chance to veto the unload.
261	     */
262	    if (error = module_unload(mod)) {
263		KLD_DPF(FILE, ("linker_file_unload: module %x vetoes unload\n",
264			       mod));
265		lockmgr(&lock, LK_RELEASE, 0, curproc);
266		goto out;
267	    }
268
269	    module_release(mod);
270	}
271    }
272
273    file->refs--;
274    if (file->refs > 0) {
275	lockmgr(&lock, LK_RELEASE, 0, curproc);
276	goto out;
277    }
278
279    TAILQ_REMOVE(&files, file, link);
280    lockmgr(&lock, LK_RELEASE, 0, curproc);
281
282    for (i = 0; i < file->ndeps; i++)
283	linker_file_unload(file->deps[i]);
284    free(file->deps, M_LINKER);
285
286    for (cp = STAILQ_FIRST(&file->common); cp;
287	 cp = STAILQ_FIRST(&file->common)) {
288	STAILQ_REMOVE(&file->common, cp, common_symbol, link);
289	free(cp, M_LINKER);
290    }
291
292    file->ops->unload(file);
293    free(file, M_LINKER);
294
295out:
296    return error;
297}
298
299int
300linker_file_add_dependancy(linker_file_t file, linker_file_t dep)
301{
302    linker_file_t* newdeps;
303
304    newdeps = malloc((file->ndeps + 1) * sizeof(linker_file_t*),
305		     M_LINKER, M_WAITOK);
306    if (newdeps == NULL)
307	return ENOMEM;
308
309    if (file->deps) {
310	bcopy(file->deps, newdeps, file->ndeps * sizeof(linker_file_t*));
311	free(file->deps, M_LINKER);
312    }
313    file->deps = newdeps;
314    file->deps[file->ndeps] = dep;
315    file->ndeps++;
316
317    return 0;
318}
319
320caddr_t
321linker_file_lookup_symbol(linker_file_t file, const char* name, int deps)
322{
323    caddr_t address;
324    size_t size;
325    size_t common_size = 0;
326    int i;
327
328    KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d",
329		  file, name, deps));
330
331    if (file->ops->lookup_symbol(file, name, &address, &size) == 0)
332	if (address == 0)
333	    /*
334	     * For commons, first look them up in the dependancies and
335	     * only allocate space if not found there.
336	     */
337	    common_size = size;
338	else
339	    return address;
340
341    if (deps)
342	for (i = 0; i < file->ndeps; i++) {
343	    address = linker_file_lookup_symbol(file->deps[i], name, 0);
344	    if (address)
345		return address;
346	}
347
348    if (common_size > 0) {
349	/*
350	 * This is a common symbol which was not found in the
351	 * dependancies.  We maintain a simple common symbol table in
352	 * the file object.
353	 */
354	struct common_symbol* cp;
355
356	for (cp = STAILQ_FIRST(&file->common); cp;
357	     cp = STAILQ_NEXT(cp, link))
358	    if (!strcmp(cp->name, name))
359		return cp->address;
360
361	/*
362	 * Round the symbol size up to align.
363	 */
364	common_size = (common_size + sizeof(int) - 1) & -sizeof(int);
365	cp = malloc(sizeof(struct common_symbol)
366		    + common_size
367		    + strlen(name) + 1,
368		    M_LINKER, M_WAITOK);
369	if (!cp)
370	    return 0;
371
372	cp->address = (caddr_t) (cp + 1);
373	cp->name = cp->address + common_size;
374	strcpy(cp->name, name);
375	bzero(cp->address, common_size);
376	STAILQ_INSERT_TAIL(&file->common, cp, link);
377
378	return cp->address;
379    }
380
381    return 0;
382}
383
384/*
385 * Syscalls.
386 */
387
388int
389kldload(struct proc* p, struct kldload_args* uap, int* retval)
390{
391    char* filename = NULL;
392    linker_file_t lf;
393    int error = 0;
394
395    *retval = -1;
396
397    if (securelevel > 0)
398	return EPERM;
399
400    if (error = suser(p->p_ucred, &p->p_acflag))
401	return error;
402
403    filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
404    if (error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL))
405	goto out;
406
407    if (error = linker_load_file(uap->file, &lf))
408	goto out;
409
410    lf->userrefs++;
411    *retval = lf->id;
412
413out:
414    if (filename)
415	free(filename, M_TEMP);
416    return error;
417}
418
419int
420kldunload(struct proc* p, struct kldunload_args* uap, int* retval)
421{
422    linker_file_t lf;
423    int error = 0;
424
425    if (securelevel > 0)
426	return EPERM;
427
428    if (error = suser(p->p_ucred, &p->p_acflag))
429	return error;
430
431    lf = linker_find_file_by_id(SCARG(uap, fileid));
432    if (lf) {
433	KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs));
434	if (lf->userrefs == 0) {
435	    printf("linkerunload: attempt to unload file which was not loaded by user\n");
436	    error = EBUSY;
437	    goto out;
438	}
439	lf->userrefs--;
440	error = linker_file_unload(lf);
441    } else
442	error = ENOENT;
443
444out:
445    return error;
446}
447
448int
449kldfind(struct proc* p, struct kldfind_args* uap, int* retval)
450{
451    char* filename = NULL;
452    linker_file_t lf;
453    int error = 0;
454
455    *retval = -1;
456
457    filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
458    if (error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL))
459	goto out;
460
461    lf = linker_find_file_by_name(filename);
462    if (lf)
463	*retval = lf->id;
464    else
465	error = ENOENT;
466
467out:
468    if (filename)
469	free(filename, M_TEMP);
470    return error;
471}
472
473int
474kldnext(struct proc* p, struct kldnext_args* uap, int* retval)
475{
476    linker_file_t lf;
477    int error = 0;
478
479    if (SCARG(uap, fileid) == 0) {
480	if (TAILQ_FIRST(&files))
481	    *retval = TAILQ_FIRST(&files)->id;
482	else
483	    *retval = 0;
484	return 0;
485    }
486
487    lf = linker_find_file_by_id(SCARG(uap, fileid));
488    if (lf) {
489	if (TAILQ_NEXT(lf, link))
490	    *retval = TAILQ_NEXT(lf, link)->id;
491	else
492	    *retval = 0;
493    } else
494	error = ENOENT;
495
496    return error;
497}
498
499int
500kldstat(struct proc* p, struct kldstat_args* uap, int* retval)
501{
502    linker_file_t lf;
503    int error = 0;
504    int version;
505    struct kld_file_stat* stat;
506    int namelen;
507
508    lf = linker_find_file_by_id(SCARG(uap, fileid));
509    if (!lf) {
510	error = ENOENT;
511	goto out;
512    }
513
514    stat = SCARG(uap, stat);
515
516    /*
517     * Check the version of the user's structure.
518     */
519    if (error = copyin(&stat->version, &version, sizeof(version)))
520	goto out;
521    if (version != sizeof(struct kld_file_stat)) {
522	error = EINVAL;
523	goto out;
524    }
525
526    namelen = strlen(lf->filename) + 1;
527    if (namelen > MAXPATHLEN)
528	namelen = MAXPATHLEN;
529    if (error = copyout(lf->filename, &stat->name[0], namelen))
530	goto out;
531    if (error = copyout(&lf->refs, &stat->refs, sizeof(int)))
532	goto out;
533    if (error = copyout(&lf->id, &stat->id, sizeof(int)))
534	goto out;
535    if (error = copyout(&lf->address, &stat->address, sizeof(caddr_t)))
536	goto out;
537    if (error = copyout(&lf->size, &stat->size, sizeof(size_t)))
538	goto out;
539
540    *retval = 0;
541
542out:
543    return error;
544}
545
546int
547kldfirstmod(struct proc* p, struct kldfirstmod_args* uap, int* retval)
548{
549    linker_file_t lf;
550    int error = 0;
551
552    lf = linker_find_file_by_id(SCARG(uap, fileid));
553    if (lf) {
554	if (TAILQ_FIRST(&lf->modules))
555	    *retval = module_getid(TAILQ_FIRST(&lf->modules));
556	else
557	    *retval = 0;
558    } else
559	error = ENOENT;
560
561    return error;
562}
563