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