xref: /freebsd/sys/kern/kern_linker.c (revision 0de89efe5c443f213c7ea28773ef2dc6cf3af2ed)
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 
41 static struct lock lock;	/* lock for the file list */
42 static linker_class_list_t classes;
43 static linker_file_list_t files;
44 static int next_file_id = 1;
45 
46 static void
47 linker_init(void* arg)
48 {
49     lockinit(&lock, PVM, "klink", 0, 0);
50     TAILQ_INIT(&classes);
51     TAILQ_INIT(&files);
52 }
53 
54 SYSINIT(linker, SI_SUB_KMEM, SI_ORDER_SECOND, linker_init, 0);
55 
56 int
57 linker_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 
74 static void
75 linker_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 
143 int
144 linker_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 
174 out:
175     return error;
176 }
177 
178 linker_file_t
179 linker_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 
192 linker_file_t
193 linker_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 
206 linker_file_t
207 linker_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 
233 out:
234     lockmgr(&lock, LK_RELEASE, 0, curproc);
235     return lf;
236 }
237 
238 int
239 linker_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 
292 out:
293     return error;
294 }
295 
296 int
297 linker_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 
317 caddr_t
318 linker_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 
385 int
386 kldload(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 
410 out:
411     if (filename)
412 	free(filename, M_TEMP);
413     return error;
414 }
415 
416 int
417 kldunload(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 
441 out:
442     return error;
443 }
444 
445 int
446 kldfind(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 
464 out:
465     if (filename)
466 	free(filename, M_TEMP);
467     return error;
468 }
469 
470 int
471 kldnext(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 
496 int
497 kldstat(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 
539 out:
540     return error;
541 }
542 
543 int
544 kldfirstmod(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