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