kern_linker.c revision 32153
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: kern_linker.c,v 1.5 1997/12/12 04:00:59 dyson Exp $
27 */
28
29#include <sys/param.h>
30#include <sys/kernel.h>
31#include <sys/systm.h>
32#include <sys/malloc.h>
33#include <sys/sysproto.h>
34#include <sys/sysent.h>
35#include <sys/proc.h>
36#include <sys/lock.h>
37#include <machine/cpu.h>
38#include <sys/module.h>
39#include <sys/linker.h>
40#include <sys/unistd.h>
41
42MALLOC_DEFINE(M_LINKER, "kld", "kernel linker");
43linker_file_t linker_current_file;
44
45static struct lock lock;	/* lock for the file list */
46static linker_class_list_t classes;
47static linker_file_list_t files;
48static int next_file_id = 1;
49
50static void
51linker_init(void* arg)
52{
53    lockinit(&lock, PVM, "klink", 0, 0);
54    TAILQ_INIT(&classes);
55    TAILQ_INIT(&files);
56}
57
58SYSINIT(linker, SI_SUB_KMEM, SI_ORDER_SECOND, linker_init, 0);
59
60int
61linker_add_class(const char* desc, void* priv,
62		 struct linker_class_ops* ops)
63{
64    linker_class_t lc;
65
66    lc = malloc(sizeof(struct linker_class), M_LINKER, M_NOWAIT);
67    if (!lc)
68	return ENOMEM;
69
70    lc->desc = desc;
71    lc->priv = priv;
72    lc->ops = ops;
73    TAILQ_INSERT_HEAD(&classes, lc, link);
74
75    return 0;
76}
77
78static void
79linker_file_sysinit(linker_file_t lf)
80{
81    struct linker_set* sysinits;
82    struct sysinit** sipp;
83    struct sysinit** xipp;
84    struct sysinit* save;
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#if !defined(SMP)
135	    /* kernel thread*/
136	    if (fork1(&proc0, RFFDG|RFPROC|RFMEM))
137		panic("fork kernel thread");
138	    cpu_set_fork_handler(pfind(proc0.p_retval[0]),
139		(*sipp)->func, (*sipp)->udata);
140	    break;
141#endif
142
143	case SI_TYPE_KPROCESS:
144	    /* kernel thread*/
145	    if (fork1(&proc0, RFFDG|RFPROC))
146		panic("fork kernel process");
147	    cpu_set_fork_handler(pfind(proc0.p_retval[0]),
148		(*sipp)->func, (*sipp)->udata);
149	    break;
150
151	default:
152	    panic( "linker_file_sysinit: unrecognized init type");
153	}
154    }
155}
156
157int
158linker_load_file(const char* filename, linker_file_t* result)
159{
160    linker_class_t lc;
161    linker_file_t lf;
162    int error = 0;
163
164    lf = linker_find_file_by_name(filename);
165    if (lf) {
166	KLD_DPF(FILE, ("linker_load_file: file %s is already loaded, incrementing refs\n", filename));
167	*result = lf;
168	lf->refs++;
169	goto out;
170    }
171
172    lf = NULL;
173    for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) {
174	KLD_DPF(FILE, ("linker_load_file: trying to load %s as %s\n",
175		       filename, lc->desc));
176	if (error = lc->ops->load_file(filename, &lf))
177	    goto out;
178	if (lf) {
179	    linker_file_sysinit(lf);
180
181	    *result = lf;
182	    goto out;
183	}
184    }
185
186    error = ENOEXEC;		/* format not recognised */
187
188out:
189    return error;
190}
191
192linker_file_t
193linker_find_file_by_name(const char* filename)
194{
195    linker_file_t lf = 0;
196
197    lockmgr(&lock, LK_SHARED, 0, curproc);
198    for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link))
199	if (!strcmp(lf->filename, filename))
200	    break;
201    lockmgr(&lock, LK_RELEASE, 0, curproc);
202
203    return lf;
204}
205
206linker_file_t
207linker_find_file_by_id(int fileid)
208{
209    linker_file_t lf = 0;
210
211    lockmgr(&lock, LK_SHARED, 0, curproc);
212    for (lf = TAILQ_FIRST(&files); lf; lf = TAILQ_NEXT(lf, link))
213	if (lf->id == fileid)
214	    break;
215    lockmgr(&lock, LK_RELEASE, 0, curproc);
216
217    return lf;
218}
219
220linker_file_t
221linker_make_file(const char* filename, void* priv, struct linker_file_ops* ops)
222{
223    linker_file_t lf = 0;
224    int namelen;
225
226    KLD_DPF(FILE, ("linker_make_file: new file, filename=%s\n", filename));
227    lockmgr(&lock, LK_EXCLUSIVE|LK_RETRY, 0, curproc);
228    namelen = strlen(filename) + 1;
229    lf = malloc(sizeof(struct linker_file) + namelen, M_LINKER, M_WAITOK);
230    if (!lf)
231	goto out;
232
233    lf->refs = 1;
234    lf->userrefs = 0;
235    lf->filename = (char*) (lf + 1);
236    strcpy(lf->filename, filename);
237    lf->id = next_file_id++;
238    lf->ndeps = 0;
239    lf->deps = NULL;
240    STAILQ_INIT(&lf->common);
241    TAILQ_INIT(&lf->modules);
242
243    lf->priv = priv;
244    lf->ops = ops;
245    TAILQ_INSERT_TAIL(&files, lf, link);
246
247out:
248    lockmgr(&lock, LK_RELEASE, 0, curproc);
249    return lf;
250}
251
252int
253linker_file_unload(linker_file_t file)
254{
255    module_t mod, next;
256    struct common_symbol* cp;
257    int error = 0;
258    int i;
259
260    KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", lf->refs));
261    lockmgr(&lock, LK_EXCLUSIVE|LK_RETRY, 0, curproc);
262    if (file->refs == 1) {
263	KLD_DPF(FILE, ("linker_file_unload: file is unloading, informing modules\n"));
264	/*
265	 * Inform any modules associated with this file.
266	 */
267	for (mod = TAILQ_FIRST(&file->modules); mod; mod = next) {
268	    next = module_getfnext(mod);
269
270	    /*
271	     * Give the module a chance to veto the unload.
272	     */
273	    if (error = module_unload(mod)) {
274		KLD_DPF(FILE, ("linker_file_unload: module %x vetoes unload\n",
275			       mod));
276		lockmgr(&lock, LK_RELEASE, 0, curproc);
277		goto out;
278	    }
279
280	    module_release(mod);
281	}
282    }
283
284    file->refs--;
285    if (file->refs > 0) {
286	lockmgr(&lock, LK_RELEASE, 0, curproc);
287	goto out;
288    }
289
290    TAILQ_REMOVE(&files, file, link);
291    lockmgr(&lock, LK_RELEASE, 0, curproc);
292
293    for (i = 0; i < file->ndeps; i++)
294	linker_file_unload(file->deps[i]);
295    free(file->deps, M_LINKER);
296
297    for (cp = STAILQ_FIRST(&file->common); cp;
298	 cp = STAILQ_FIRST(&file->common)) {
299	STAILQ_REMOVE(&file->common, cp, common_symbol, link);
300	free(cp, M_LINKER);
301    }
302
303    file->ops->unload(file);
304    free(file, M_LINKER);
305
306out:
307    return error;
308}
309
310int
311linker_file_add_dependancy(linker_file_t file, linker_file_t dep)
312{
313    linker_file_t* newdeps;
314
315    newdeps = malloc((file->ndeps + 1) * sizeof(linker_file_t*),
316		     M_LINKER, M_WAITOK);
317    if (newdeps == NULL)
318	return ENOMEM;
319
320    if (file->deps) {
321	bcopy(file->deps, newdeps, file->ndeps * sizeof(linker_file_t*));
322	free(file->deps, M_LINKER);
323    }
324    file->deps = newdeps;
325    file->deps[file->ndeps] = dep;
326    file->ndeps++;
327
328    return 0;
329}
330
331caddr_t
332linker_file_lookup_symbol(linker_file_t file, const char* name, int deps)
333{
334    caddr_t address;
335    size_t size;
336    size_t common_size = 0;
337    int i;
338
339    KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d",
340		  file, name, deps));
341
342    if (file->ops->lookup_symbol(file, name, &address, &size) == 0)
343	if (address == 0)
344	    /*
345	     * For commons, first look them up in the dependancies and
346	     * only allocate space if not found there.
347	     */
348	    common_size = size;
349	else
350	    return address;
351
352    if (deps)
353	for (i = 0; i < file->ndeps; i++) {
354	    address = linker_file_lookup_symbol(file->deps[i], name, 0);
355	    if (address)
356		return address;
357	}
358
359    if (common_size > 0) {
360	/*
361	 * This is a common symbol which was not found in the
362	 * dependancies.  We maintain a simple common symbol table in
363	 * the file object.
364	 */
365	struct common_symbol* cp;
366
367	for (cp = STAILQ_FIRST(&file->common); cp;
368	     cp = STAILQ_NEXT(cp, link))
369	    if (!strcmp(cp->name, name))
370		return cp->address;
371
372	/*
373	 * Round the symbol size up to align.
374	 */
375	common_size = (common_size + sizeof(int) - 1) & -sizeof(int);
376	cp = malloc(sizeof(struct common_symbol)
377		    + common_size
378		    + strlen(name) + 1,
379		    M_LINKER, M_WAITOK);
380	if (!cp)
381	    return 0;
382
383	cp->address = (caddr_t) (cp + 1);
384	cp->name = cp->address + common_size;
385	strcpy(cp->name, name);
386	bzero(cp->address, common_size);
387	STAILQ_INSERT_TAIL(&file->common, cp, link);
388
389	return cp->address;
390    }
391
392    return 0;
393}
394
395/*
396 * Syscalls.
397 */
398
399int
400kldload(struct proc* p, struct kldload_args* uap)
401{
402    char* filename = NULL;
403    linker_file_t lf;
404    int error = 0;
405
406    p->p_retval[0] = -1;
407
408    if (securelevel > 0)
409	return EPERM;
410
411    if (error = suser(p->p_ucred, &p->p_acflag))
412	return error;
413
414    filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
415    if (error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL))
416	goto out;
417
418    if (error = linker_load_file(uap->file, &lf))
419	goto out;
420
421    lf->userrefs++;
422    p->p_retval[0] = lf->id;
423
424out:
425    if (filename)
426	free(filename, M_TEMP);
427    return error;
428}
429
430int
431kldunload(struct proc* p, struct kldunload_args* uap)
432{
433    linker_file_t lf;
434    int error = 0;
435
436    if (securelevel > 0)
437	return EPERM;
438
439    if (error = suser(p->p_ucred, &p->p_acflag))
440	return error;
441
442    lf = linker_find_file_by_id(SCARG(uap, fileid));
443    if (lf) {
444	KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs));
445	if (lf->userrefs == 0) {
446	    printf("linkerunload: attempt to unload file which was not loaded by user\n");
447	    error = EBUSY;
448	    goto out;
449	}
450	lf->userrefs--;
451	error = linker_file_unload(lf);
452    } else
453	error = ENOENT;
454
455out:
456    return error;
457}
458
459int
460kldfind(struct proc* p, struct kldfind_args* uap)
461{
462    char* filename = NULL;
463    linker_file_t lf;
464    int error = 0;
465
466    p->p_retval[0] = -1;
467
468    filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
469    if (error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL))
470	goto out;
471
472    lf = linker_find_file_by_name(filename);
473    if (lf)
474	p->p_retval[0] = lf->id;
475    else
476	error = ENOENT;
477
478out:
479    if (filename)
480	free(filename, M_TEMP);
481    return error;
482}
483
484int
485kldnext(struct proc* p, struct kldnext_args* uap)
486{
487    linker_file_t lf;
488    int error = 0;
489
490    if (SCARG(uap, fileid) == 0) {
491	if (TAILQ_FIRST(&files))
492	    p->p_retval[0] = TAILQ_FIRST(&files)->id;
493	else
494	    p->p_retval[0] = 0;
495	return 0;
496    }
497
498    lf = linker_find_file_by_id(SCARG(uap, fileid));
499    if (lf) {
500	if (TAILQ_NEXT(lf, link))
501	    p->p_retval[0] = TAILQ_NEXT(lf, link)->id;
502	else
503	    p->p_retval[0] = 0;
504    } else
505	error = ENOENT;
506
507    return error;
508}
509
510int
511kldstat(struct proc* p, struct kldstat_args* uap)
512{
513    linker_file_t lf;
514    int error = 0;
515    int version;
516    struct kld_file_stat* stat;
517    int namelen;
518
519    lf = linker_find_file_by_id(SCARG(uap, fileid));
520    if (!lf) {
521	error = ENOENT;
522	goto out;
523    }
524
525    stat = SCARG(uap, stat);
526
527    /*
528     * Check the version of the user's structure.
529     */
530    if (error = copyin(&stat->version, &version, sizeof(version)))
531	goto out;
532    if (version != sizeof(struct kld_file_stat)) {
533	error = EINVAL;
534	goto out;
535    }
536
537    namelen = strlen(lf->filename) + 1;
538    if (namelen > MAXPATHLEN)
539	namelen = MAXPATHLEN;
540    if (error = copyout(lf->filename, &stat->name[0], namelen))
541	goto out;
542    if (error = copyout(&lf->refs, &stat->refs, sizeof(int)))
543	goto out;
544    if (error = copyout(&lf->id, &stat->id, sizeof(int)))
545	goto out;
546    if (error = copyout(&lf->address, &stat->address, sizeof(caddr_t)))
547	goto out;
548    if (error = copyout(&lf->size, &stat->size, sizeof(size_t)))
549	goto out;
550
551    p->p_retval[0] = 0;
552
553out:
554    return error;
555}
556
557int
558kldfirstmod(struct proc* p, struct kldfirstmod_args* uap)
559{
560    linker_file_t lf;
561    int error = 0;
562
563    lf = linker_find_file_by_id(SCARG(uap, fileid));
564    if (lf) {
565	if (TAILQ_FIRST(&lf->modules))
566	    p->p_retval[0] = module_getid(TAILQ_FIRST(&lf->modules));
567	else
568	    p->p_retval[0] = 0;
569    } else
570	error = ENOENT;
571
572    return error;
573}
574