kern_linker.c revision 38275
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.6 1998/01/01 08:56:24 bde 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    linker_sym_t sym;
335    linker_symval_t symval;
336    caddr_t address;
337    size_t common_size = 0;
338    int i;
339
340    KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d",
341		  file, name, deps));
342
343    if (file->ops->lookup_symbol(file, name, &sym) == 0) {
344	file->ops->symbol_values(file, sym, &symval);
345	if (symval.value == 0)
346	    /*
347	     * For commons, first look them up in the dependancies and
348	     * only allocate space if not found there.
349	     */
350	    common_size = symval.size;
351	else
352	    return symval.value;
353    }
354
355    if (deps)
356	for (i = 0; i < file->ndeps; i++) {
357	    address = linker_file_lookup_symbol(file->deps[i], name, 0);
358	    if (address)
359		return address;
360	}
361
362    if (common_size > 0) {
363	/*
364	 * This is a common symbol which was not found in the
365	 * dependancies.  We maintain a simple common symbol table in
366	 * the file object.
367	 */
368	struct common_symbol* cp;
369
370	for (cp = STAILQ_FIRST(&file->common); cp;
371	     cp = STAILQ_NEXT(cp, link))
372	    if (!strcmp(cp->name, name))
373		return cp->address;
374
375	/*
376	 * Round the symbol size up to align.
377	 */
378	common_size = (common_size + sizeof(int) - 1) & -sizeof(int);
379	cp = malloc(sizeof(struct common_symbol)
380		    + common_size
381		    + strlen(name) + 1,
382		    M_LINKER, M_WAITOK);
383	if (!cp)
384	    return 0;
385
386	cp->address = (caddr_t) (cp + 1);
387	cp->name = cp->address + common_size;
388	strcpy(cp->name, name);
389	bzero(cp->address, common_size);
390	STAILQ_INSERT_TAIL(&file->common, cp, link);
391
392	return cp->address;
393    }
394
395    return 0;
396}
397
398/*
399 * Syscalls.
400 */
401
402int
403kldload(struct proc* p, struct kldload_args* uap)
404{
405    char* filename = NULL;
406    linker_file_t lf;
407    int error = 0;
408
409    p->p_retval[0] = -1;
410
411    if (securelevel > 0)
412	return EPERM;
413
414    if (error = suser(p->p_ucred, &p->p_acflag))
415	return error;
416
417    filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
418    if (error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL))
419	goto out;
420
421    if (error = linker_load_file(uap->file, &lf))
422	goto out;
423
424    lf->userrefs++;
425    p->p_retval[0] = lf->id;
426
427out:
428    if (filename)
429	free(filename, M_TEMP);
430    return error;
431}
432
433int
434kldunload(struct proc* p, struct kldunload_args* uap)
435{
436    linker_file_t lf;
437    int error = 0;
438
439    if (securelevel > 0)
440	return EPERM;
441
442    if (error = suser(p->p_ucred, &p->p_acflag))
443	return error;
444
445    lf = linker_find_file_by_id(SCARG(uap, fileid));
446    if (lf) {
447	KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs));
448	if (lf->userrefs == 0) {
449	    printf("linkerunload: attempt to unload file which was not loaded by user\n");
450	    error = EBUSY;
451	    goto out;
452	}
453	lf->userrefs--;
454	error = linker_file_unload(lf);
455    } else
456	error = ENOENT;
457
458out:
459    return error;
460}
461
462int
463kldfind(struct proc* p, struct kldfind_args* uap)
464{
465    char* filename = NULL;
466    linker_file_t lf;
467    int error = 0;
468
469    p->p_retval[0] = -1;
470
471    filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
472    if (error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL))
473	goto out;
474
475    lf = linker_find_file_by_name(filename);
476    if (lf)
477	p->p_retval[0] = lf->id;
478    else
479	error = ENOENT;
480
481out:
482    if (filename)
483	free(filename, M_TEMP);
484    return error;
485}
486
487int
488kldnext(struct proc* p, struct kldnext_args* uap)
489{
490    linker_file_t lf;
491    int error = 0;
492
493    if (SCARG(uap, fileid) == 0) {
494	if (TAILQ_FIRST(&files))
495	    p->p_retval[0] = TAILQ_FIRST(&files)->id;
496	else
497	    p->p_retval[0] = 0;
498	return 0;
499    }
500
501    lf = linker_find_file_by_id(SCARG(uap, fileid));
502    if (lf) {
503	if (TAILQ_NEXT(lf, link))
504	    p->p_retval[0] = TAILQ_NEXT(lf, link)->id;
505	else
506	    p->p_retval[0] = 0;
507    } else
508	error = ENOENT;
509
510    return error;
511}
512
513int
514kldstat(struct proc* p, struct kldstat_args* uap)
515{
516    linker_file_t lf;
517    int error = 0;
518    int version;
519    struct kld_file_stat* stat;
520    int namelen;
521
522    lf = linker_find_file_by_id(SCARG(uap, fileid));
523    if (!lf) {
524	error = ENOENT;
525	goto out;
526    }
527
528    stat = SCARG(uap, stat);
529
530    /*
531     * Check the version of the user's structure.
532     */
533    if (error = copyin(&stat->version, &version, sizeof(version)))
534	goto out;
535    if (version != sizeof(struct kld_file_stat)) {
536	error = EINVAL;
537	goto out;
538    }
539
540    namelen = strlen(lf->filename) + 1;
541    if (namelen > MAXPATHLEN)
542	namelen = MAXPATHLEN;
543    if (error = copyout(lf->filename, &stat->name[0], namelen))
544	goto out;
545    if (error = copyout(&lf->refs, &stat->refs, sizeof(int)))
546	goto out;
547    if (error = copyout(&lf->id, &stat->id, sizeof(int)))
548	goto out;
549    if (error = copyout(&lf->address, &stat->address, sizeof(caddr_t)))
550	goto out;
551    if (error = copyout(&lf->size, &stat->size, sizeof(size_t)))
552	goto out;
553
554    p->p_retval[0] = 0;
555
556out:
557    return error;
558}
559
560int
561kldfirstmod(struct proc* p, struct kldfirstmod_args* uap)
562{
563    linker_file_t lf;
564    int error = 0;
565
566    lf = linker_find_file_by_id(SCARG(uap, fileid));
567    if (lf) {
568	if (TAILQ_FIRST(&lf->modules))
569	    p->p_retval[0] = module_getid(TAILQ_FIRST(&lf->modules));
570	else
571	    p->p_retval[0] = 0;
572    } else
573	error = ENOENT;
574
575    return error;
576}
577