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