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