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