Deleted Added
sdiff udiff text old ( 30994 ) new ( 31324 )
full compact
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.2 1997/08/02 14:31:28 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
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
82 linker_current_file = lf;
83
84 KLD_DPF(FILE, ("linker_file_sysinit: calling SYSINITs for %s\n",
85 lf->filename));
86
87 sysinits = (struct linker_set*)
88 linker_file_lookup_symbol(lf, "sysinit_set", 0);
89 if (!sysinits)
90 return;
91
92 /*
93 * Perform a bubble sort of the system initialization objects by
94 * their subsystem (primary key) and order (secondary key).
95 *
96 * Since some things care about execution order, this is the
97 * operation which ensures continued function.
98 */
99 for( sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) {
100 for( xipp = sipp + 1; *xipp; xipp++) {
101 if( (*sipp)->subsystem < (*xipp)->subsystem ||
102 ( (*sipp)->subsystem == (*xipp)->subsystem &&
103 (*sipp)->order < (*xipp)->order))
104 continue; /* skip*/
105 save = *sipp;
106 *sipp = *xipp;
107 *xipp = save;
108 }
109 }
110
111
112 /*
113 * Traverse the (now) ordered list of system initialization tasks.
114 * Perform each task, and continue on to the next task.
115 *
116 * The last item on the list is expected to be the scheduler,
117 * which will not return.
118 */
119 for( sipp = (struct sysinit **)sysinits->ls_items; *sipp; sipp++) {
120 if( (*sipp)->subsystem == SI_SUB_DUMMY)
121 continue; /* skip dummy task(s)*/
122
123 switch( (*sipp)->type) {
124 case SI_TYPE_DEFAULT:
125 /* no special processing*/
126 (*((*sipp)->func))( (*sipp)->udata);
127 break;
128
129 case SI_TYPE_KTHREAD:
130 /* kernel thread*/
131 if (fork(&proc0, NULL))
132 panic("fork kernel process");
133 cpu_set_fork_handler(pfind(proc0.p_retval[0]),
134 (*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)
387{
388 char* filename = NULL;
389 linker_file_t lf;
390 int error = 0;
391
392 p->p_retval[0] = -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 p->p_retval[0] = 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)
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)
447{
448 char* filename = NULL;
449 linker_file_t lf;
450 int error = 0;
451
452 p->p_retval[0] = -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 p->p_retval[0] = 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)
472{
473 linker_file_t lf;
474 int error = 0;
475
476 if (SCARG(uap, fileid) == 0) {
477 if (TAILQ_FIRST(&files))
478 p->p_retval[0] = TAILQ_FIRST(&files)->id;
479 else
480 p->p_retval[0] = 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 p->p_retval[0] = TAILQ_NEXT(lf, link)->id;
488 else
489 p->p_retval[0] = 0;
490 } else
491 error = ENOENT;
492
493 return error;
494}
495
496int
497kldstat(struct proc* p, struct kldstat_args* uap)
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 p->p_retval[0] = 0;
538
539out:
540 return error;
541}
542
543int
544kldfirstmod(struct proc* p, struct kldfirstmod_args* uap)
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 p->p_retval[0] = module_getid(TAILQ_FIRST(&lf->modules));
553 else
554 p->p_retval[0] = 0;
555 } else
556 error = ENOENT;
557
558 return error;
559}