| 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 | * $FreeBSD: src/sys/kern/kern_linker.c,v 1.41.2.3 2001/11/21 17:50:35 luigi Exp $ |
| 27 | * $DragonFly: src/sys/kern/kern_linker.c,v 1.20 2004/11/12 00:09:23 dillon Exp $ |
| 28 | */ |
| 29 | |
| 30 | #include "opt_ddb.h" |
| 31 | |
| 32 | #include <sys/param.h> |
| 33 | #include <sys/kernel.h> |
| 34 | #include <sys/systm.h> |
| 35 | #include <sys/malloc.h> |
| 36 | #include <sys/sysproto.h> |
| 37 | #include <sys/sysent.h> |
| 38 | #include <sys/proc.h> |
| 39 | #include <sys/lock.h> |
| 40 | #include <sys/module.h> |
| 41 | #include <sys/linker.h> |
| 42 | #include <sys/fcntl.h> |
| 43 | #include <sys/libkern.h> |
| 44 | #include <sys/nlookup.h> |
| 45 | #include <sys/vnode.h> |
| 46 | #include <sys/sysctl.h> |
| 47 | |
| 48 | #include <vm/vm_zone.h> |
| 49 | |
| 50 | #ifdef KLD_DEBUG |
| 51 | int kld_debug = 0; |
| 52 | #endif |
| 53 | |
| 54 | /* Metadata from the static kernel */ |
| 55 | SET_DECLARE(modmetadata_set, struct mod_metadata); |
| 56 | MALLOC_DEFINE(M_LINKER, "kld", "kernel linker"); |
| 57 | |
| 58 | linker_file_t linker_current_file; |
| 59 | linker_file_t linker_kernel_file; |
| 60 | |
| 61 | static struct lock lock; /* lock for the file list */ |
| 62 | static linker_class_list_t classes; |
| 63 | static linker_file_list_t linker_files; |
| 64 | static int next_file_id = 1; |
| 65 | |
| 66 | static void |
| 67 | linker_init(void* arg) |
| 68 | { |
| 69 | lockinit(&lock, 0, "klink", 0, 0); |
| 70 | TAILQ_INIT(&classes); |
| 71 | TAILQ_INIT(&linker_files); |
| 72 | } |
| 73 | |
| 74 | SYSINIT(linker, SI_SUB_KLD, SI_ORDER_FIRST, linker_init, 0); |
| 75 | |
| 76 | int |
| 77 | linker_add_class(const char* desc, void* priv, |
| 78 | struct linker_class_ops* ops) |
| 79 | { |
| 80 | linker_class_t lc; |
| 81 | |
| 82 | lc = malloc(sizeof(struct linker_class), M_LINKER, M_NOWAIT); |
| 83 | if (!lc) |
| 84 | return ENOMEM; |
| 85 | bzero(lc, sizeof(*lc)); |
| 86 | |
| 87 | lc->desc = desc; |
| 88 | lc->priv = priv; |
| 89 | lc->ops = ops; |
| 90 | TAILQ_INSERT_HEAD(&classes, lc, link); |
| 91 | |
| 92 | return 0; |
| 93 | } |
| 94 | |
| 95 | static int |
| 96 | linker_file_sysinit(linker_file_t lf) |
| 97 | { |
| 98 | struct sysinit** start, ** stop; |
| 99 | struct sysinit** sipp; |
| 100 | struct sysinit** xipp; |
| 101 | struct sysinit* save; |
| 102 | const moduledata_t *moddata; |
| 103 | int error; |
| 104 | |
| 105 | KLD_DPF(FILE, ("linker_file_sysinit: calling SYSINITs for %s\n", |
| 106 | lf->filename)); |
| 107 | |
| 108 | if (linker_file_lookup_set(lf, "sysinit_set", &start, &stop, NULL) != 0) |
| 109 | return 0; /* XXX is this correct ? No sysinit ? */ |
| 110 | |
| 111 | /* HACK ALERT! */ |
| 112 | for (sipp = start; sipp < stop; sipp++) { |
| 113 | if ((*sipp)->func == module_register_init) { |
| 114 | moddata = (*sipp)->udata; |
| 115 | error = module_register(moddata, lf); |
| 116 | if (error) { |
| 117 | printf("linker_file_sysinit \"%s\" failed to register! %d\n", |
| 118 | lf->filename, error); |
| 119 | return error; |
| 120 | } |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | /* |
| 125 | * Perform a bubble sort of the system initialization objects by |
| 126 | * their subsystem (primary key) and order (secondary key). |
| 127 | * |
| 128 | * Since some things care about execution order, this is the |
| 129 | * operation which ensures continued function. |
| 130 | */ |
| 131 | for (sipp = start; sipp < stop; sipp++) { |
| 132 | for (xipp = sipp + 1; xipp < stop; xipp++) { |
| 133 | if ((*sipp)->subsystem < (*xipp)->subsystem || |
| 134 | ((*sipp)->subsystem == (*xipp)->subsystem && |
| 135 | (*sipp)->order <= (*xipp)->order)) |
| 136 | continue; /* skip*/ |
| 137 | save = *sipp; |
| 138 | *sipp = *xipp; |
| 139 | *xipp = save; |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | |
| 144 | /* |
| 145 | * Traverse the (now) ordered list of system initialization tasks. |
| 146 | * Perform each task, and continue on to the next task. |
| 147 | */ |
| 148 | for (sipp = start; sipp < stop; sipp++) { |
| 149 | if ((*sipp)->subsystem == SI_SUB_DUMMY) |
| 150 | continue; /* skip dummy task(s)*/ |
| 151 | |
| 152 | /* Call function */ |
| 153 | (*((*sipp)->func))((*sipp)->udata); |
| 154 | } |
| 155 | return 0; /* no errors */ |
| 156 | } |
| 157 | |
| 158 | static void |
| 159 | linker_file_sysuninit(linker_file_t lf) |
| 160 | { |
| 161 | struct sysinit** start, ** stop; |
| 162 | struct sysinit** sipp; |
| 163 | struct sysinit** xipp; |
| 164 | struct sysinit* save; |
| 165 | |
| 166 | KLD_DPF(FILE, ("linker_file_sysuninit: calling SYSUNINITs for %s\n", |
| 167 | lf->filename)); |
| 168 | |
| 169 | if (linker_file_lookup_set(lf, "sysuninit_set", &start, &stop, NULL) != 0) |
| 170 | return; |
| 171 | |
| 172 | /* |
| 173 | * Perform a reverse bubble sort of the system initialization objects |
| 174 | * by their subsystem (primary key) and order (secondary key). |
| 175 | * |
| 176 | * Since some things care about execution order, this is the |
| 177 | * operation which ensures continued function. |
| 178 | */ |
| 179 | for (sipp = start; sipp < stop; sipp++) { |
| 180 | for (xipp = sipp + 1; xipp < stop; xipp++) { |
| 181 | if ((*sipp)->subsystem > (*xipp)->subsystem || |
| 182 | ((*sipp)->subsystem == (*xipp)->subsystem && |
| 183 | (*sipp)->order >= (*xipp)->order)) |
| 184 | continue; /* skip*/ |
| 185 | save = *sipp; |
| 186 | *sipp = *xipp; |
| 187 | *xipp = save; |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | |
| 192 | /* |
| 193 | * Traverse the (now) ordered list of system initialization tasks. |
| 194 | * Perform each task, and continue on to the next task. |
| 195 | */ |
| 196 | for (sipp = start; sipp < stop; sipp++) { |
| 197 | if ((*sipp)->subsystem == SI_SUB_DUMMY) |
| 198 | continue; /* skip dummy task(s)*/ |
| 199 | |
| 200 | /* Call function */ |
| 201 | (*((*sipp)->func))((*sipp)->udata); |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | static void |
| 206 | linker_file_register_sysctls(linker_file_t lf) |
| 207 | { |
| 208 | struct sysctl_oid **start, **stop, **oidp; |
| 209 | |
| 210 | KLD_DPF(FILE, ("linker_file_register_sysctls: registering SYSCTLs for %s\n", |
| 211 | lf->filename)); |
| 212 | |
| 213 | if (linker_file_lookup_set(lf, "sysctl_set", &start, &stop, NULL) != 0) |
| 214 | return; |
| 215 | for (oidp = start; oidp < stop; oidp++) |
| 216 | sysctl_register_oid(*oidp); |
| 217 | } |
| 218 | |
| 219 | static void |
| 220 | linker_file_unregister_sysctls(linker_file_t lf) |
| 221 | { |
| 222 | struct sysctl_oid **start, **stop, **oidp; |
| 223 | |
| 224 | KLD_DPF(FILE, ("linker_file_unregister_sysctls: registering SYSCTLs for %s\n", |
| 225 | lf->filename)); |
| 226 | |
| 227 | if (linker_file_lookup_set(lf, "sysctl_set", &start, &stop, NULL) != 0) |
| 228 | return; |
| 229 | for (oidp = start; oidp < stop; oidp++) |
| 230 | sysctl_unregister_oid(*oidp); |
| 231 | } |
| 232 | |
| 233 | int |
| 234 | linker_load_file(const char* filename, linker_file_t* result) |
| 235 | { |
| 236 | linker_class_t lc; |
| 237 | linker_file_t lf; |
| 238 | int foundfile, error = 0; |
| 239 | char *koname = NULL; |
| 240 | |
| 241 | /* Refuse to load modules if securelevel raised */ |
| 242 | if (securelevel > 0) |
| 243 | return EPERM; |
| 244 | |
| 245 | lf = linker_find_file_by_name(filename); |
| 246 | if (lf) { |
| 247 | KLD_DPF(FILE, ("linker_load_file: file %s is already loaded, incrementing refs\n", filename)); |
| 248 | *result = lf; |
| 249 | lf->refs++; |
| 250 | goto out; |
| 251 | } |
| 252 | if (find_mod_metadata(filename)) { |
| 253 | if (linker_kernel_file) |
| 254 | ++linker_kernel_file->refs; |
| 255 | *result = linker_kernel_file; |
| 256 | goto out; |
| 257 | } |
| 258 | |
| 259 | koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK); |
| 260 | if (koname == NULL) { |
| 261 | error = ENOMEM; |
| 262 | goto out; |
| 263 | } |
| 264 | sprintf(koname, "%s.ko", filename); |
| 265 | lf = NULL; |
| 266 | foundfile = 0; |
| 267 | for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) { |
| 268 | KLD_DPF(FILE, ("linker_load_file: trying to load %s as %s\n", |
| 269 | filename, lc->desc)); |
| 270 | |
| 271 | error = lc->ops->load_file(koname, &lf); /* First with .ko */ |
| 272 | if (lf == NULL && error == ENOENT) |
| 273 | error = lc->ops->load_file(filename, &lf); /* Then try without */ |
| 274 | /* |
| 275 | * If we got something other than ENOENT, then it exists but we cannot |
| 276 | * load it for some other reason. |
| 277 | */ |
| 278 | if (error != ENOENT) |
| 279 | foundfile = 1; |
| 280 | if (lf) { |
| 281 | linker_file_register_sysctls(lf); |
| 282 | error = linker_file_sysinit(lf); |
| 283 | |
| 284 | *result = lf; |
| 285 | goto out; |
| 286 | } |
| 287 | } |
| 288 | /* |
| 289 | * Less than ideal, but tells the user whether it failed to load or |
| 290 | * the module was not found. |
| 291 | */ |
| 292 | if (foundfile) { |
| 293 | /* |
| 294 | * Format not recognized or otherwise unloadable. |
| 295 | * When loading a module that is statically built into |
| 296 | * the kernel EEXIST percolates back up as the return |
| 297 | * value. Preserve this so that apps like sysinstall |
| 298 | * can recognize this special case and not post bogus |
| 299 | * dialog messages. |
| 300 | */ |
| 301 | if (error != EEXIST) |
| 302 | error = ENOEXEC; |
| 303 | } else { |
| 304 | error = ENOENT; /* Nothing found */ |
| 305 | } |
| 306 | |
| 307 | out: |
| 308 | if (koname) |
| 309 | free(koname, M_LINKER); |
| 310 | return error; |
| 311 | } |
| 312 | |
| 313 | linker_file_t |
| 314 | linker_find_file_by_name(const char* filename) |
| 315 | { |
| 316 | linker_file_t lf = 0; |
| 317 | char *koname; |
| 318 | int i; |
| 319 | |
| 320 | for (i = strlen(filename); i > 0 && filename[i-1] != '/'; --i) |
| 321 | ; |
| 322 | filename += i; |
| 323 | |
| 324 | koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK); |
| 325 | if (koname == NULL) |
| 326 | goto out; |
| 327 | sprintf(koname, "%s.ko", filename); |
| 328 | |
| 329 | lockmgr(&lock, LK_SHARED, NULL, curthread); |
| 330 | for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { |
| 331 | if (!strcmp(lf->filename, koname)) |
| 332 | break; |
| 333 | if (!strcmp(lf->filename, filename)) |
| 334 | break; |
| 335 | } |
| 336 | lockmgr(&lock, LK_RELEASE, NULL, curthread); |
| 337 | |
| 338 | out: |
| 339 | if (koname) |
| 340 | free(koname, M_LINKER); |
| 341 | return lf; |
| 342 | } |
| 343 | |
| 344 | linker_file_t |
| 345 | linker_find_file_by_id(int fileid) |
| 346 | { |
| 347 | linker_file_t lf = 0; |
| 348 | |
| 349 | lockmgr(&lock, LK_SHARED, NULL, curthread); |
| 350 | for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) |
| 351 | if (lf->id == fileid) |
| 352 | break; |
| 353 | lockmgr(&lock, LK_RELEASE, NULL, curthread); |
| 354 | |
| 355 | return lf; |
| 356 | } |
| 357 | |
| 358 | linker_file_t |
| 359 | linker_make_file(const char* pathname, void* priv, struct linker_file_ops* ops) |
| 360 | { |
| 361 | linker_file_t lf = 0; |
| 362 | int namelen; |
| 363 | const char *filename; |
| 364 | |
| 365 | filename = rindex(pathname, '/'); |
| 366 | if (filename && filename[1]) |
| 367 | filename++; |
| 368 | else |
| 369 | filename = pathname; |
| 370 | |
| 371 | KLD_DPF(FILE, ("linker_make_file: new file, filename=%s\n", filename)); |
| 372 | lockmgr(&lock, LK_EXCLUSIVE, NULL, curthread); |
| 373 | namelen = strlen(filename) + 1; |
| 374 | lf = malloc(sizeof(struct linker_file) + namelen, M_LINKER, M_WAITOK); |
| 375 | if (!lf) |
| 376 | goto out; |
| 377 | bzero(lf, sizeof(*lf)); |
| 378 | |
| 379 | lf->refs = 1; |
| 380 | lf->userrefs = 0; |
| 381 | lf->flags = 0; |
| 382 | lf->filename = (char*) (lf + 1); |
| 383 | strcpy(lf->filename, filename); |
| 384 | lf->id = next_file_id++; |
| 385 | lf->ndeps = 0; |
| 386 | lf->deps = NULL; |
| 387 | STAILQ_INIT(&lf->common); |
| 388 | TAILQ_INIT(&lf->modules); |
| 389 | |
| 390 | lf->priv = priv; |
| 391 | lf->ops = ops; |
| 392 | TAILQ_INSERT_TAIL(&linker_files, lf, link); |
| 393 | |
| 394 | out: |
| 395 | lockmgr(&lock, LK_RELEASE, NULL, curthread); |
| 396 | return lf; |
| 397 | } |
| 398 | |
| 399 | int |
| 400 | linker_file_unload(linker_file_t file) |
| 401 | { |
| 402 | module_t mod, next; |
| 403 | struct common_symbol* cp; |
| 404 | int error = 0; |
| 405 | int i; |
| 406 | |
| 407 | /* Refuse to unload modules if securelevel raised */ |
| 408 | if (securelevel > 0) |
| 409 | return EPERM; |
| 410 | |
| 411 | KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", file->refs)); |
| 412 | lockmgr(&lock, LK_EXCLUSIVE, NULL, curthread); |
| 413 | if (file->refs == 1) { |
| 414 | KLD_DPF(FILE, ("linker_file_unload: file is unloading, informing modules\n")); |
| 415 | /* |
| 416 | * Temporarily bump file->refs to prevent recursive unloading |
| 417 | */ |
| 418 | ++file->refs; |
| 419 | |
| 420 | /* |
| 421 | * Inform any modules associated with this file. |
| 422 | */ |
| 423 | mod = TAILQ_FIRST(&file->modules); |
| 424 | while (mod) { |
| 425 | /* |
| 426 | * Give the module a chance to veto the unload. Note that the |
| 427 | * act of unloading the module may cause other modules in the |
| 428 | * same file list to be unloaded recursively. |
| 429 | */ |
| 430 | if ((error = module_unload(mod)) != 0) { |
| 431 | KLD_DPF(FILE, ("linker_file_unload: module %x vetoes unload\n", |
| 432 | mod)); |
| 433 | lockmgr(&lock, LK_RELEASE, NULL, curthread); |
| 434 | goto out; |
| 435 | } |
| 436 | |
| 437 | /* |
| 438 | * Recursive relationships may prevent the release from |
| 439 | * immediately removing the module, or may remove other |
| 440 | * modules in the list. |
| 441 | */ |
| 442 | next = module_getfnext(mod); |
| 443 | module_release(mod); |
| 444 | mod = next; |
| 445 | } |
| 446 | |
| 447 | /* |
| 448 | * Since we intend to destroy the file structure, we expect all |
| 449 | * modules to have been removed by now. |
| 450 | */ |
| 451 | for (mod = TAILQ_FIRST(&file->modules); |
| 452 | mod; |
| 453 | mod = module_getfnext(mod) |
| 454 | ) { |
| 455 | printf("linker_file_unload: module %p still has refs!\n", mod); |
| 456 | } |
| 457 | --file->refs; |
| 458 | } |
| 459 | |
| 460 | file->refs--; |
| 461 | if (file->refs > 0) { |
| 462 | lockmgr(&lock, LK_RELEASE, NULL, curthread); |
| 463 | goto out; |
| 464 | } |
| 465 | |
| 466 | /* Don't try to run SYSUNINITs if we are unloaded due to a link error */ |
| 467 | if (file->flags & LINKER_FILE_LINKED) { |
| 468 | linker_file_sysuninit(file); |
| 469 | linker_file_unregister_sysctls(file); |
| 470 | } |
| 471 | |
| 472 | TAILQ_REMOVE(&linker_files, file, link); |
| 473 | lockmgr(&lock, LK_RELEASE, NULL, curthread); |
| 474 | |
| 475 | for (i = 0; i < file->ndeps; i++) |
| 476 | linker_file_unload(file->deps[i]); |
| 477 | free(file->deps, M_LINKER); |
| 478 | |
| 479 | for (cp = STAILQ_FIRST(&file->common); cp; |
| 480 | cp = STAILQ_FIRST(&file->common)) { |
| 481 | STAILQ_REMOVE(&file->common, cp, common_symbol, link); |
| 482 | free(cp, M_LINKER); |
| 483 | } |
| 484 | |
| 485 | file->ops->unload(file); |
| 486 | free(file, M_LINKER); |
| 487 | |
| 488 | out: |
| 489 | return error; |
| 490 | } |
| 491 | |
| 492 | int |
| 493 | linker_file_add_dependancy(linker_file_t file, linker_file_t dep) |
| 494 | { |
| 495 | linker_file_t* newdeps; |
| 496 | |
| 497 | newdeps = malloc((file->ndeps + 1) * sizeof(linker_file_t*), |
| 498 | M_LINKER, M_WAITOK); |
| 499 | if (newdeps == NULL) |
| 500 | return ENOMEM; |
| 501 | bzero(newdeps, (file->ndeps + 1) * sizeof(linker_file_t*)); |
| 502 | |
| 503 | if (file->deps) { |
| 504 | bcopy(file->deps, newdeps, file->ndeps * sizeof(linker_file_t*)); |
| 505 | free(file->deps, M_LINKER); |
| 506 | } |
| 507 | file->deps = newdeps; |
| 508 | file->deps[file->ndeps] = dep; |
| 509 | file->ndeps++; |
| 510 | |
| 511 | return 0; |
| 512 | } |
| 513 | |
| 514 | /* |
| 515 | * Locate a linker set and its contents. |
| 516 | * This is a helper function to avoid linker_if.h exposure elsewhere. |
| 517 | * Note: firstp and lastp are really void *** |
| 518 | */ |
| 519 | int |
| 520 | linker_file_lookup_set(linker_file_t file, const char *name, |
| 521 | void *firstp, void *lastp, int *countp) |
| 522 | { |
| 523 | return file->ops->lookup_set(file, name, firstp, lastp, countp); |
| 524 | } |
| 525 | |
| 526 | int |
| 527 | linker_file_lookup_symbol(linker_file_t file, const char* name, int deps, caddr_t *raddr) |
| 528 | { |
| 529 | c_linker_sym_t sym; |
| 530 | linker_symval_t symval; |
| 531 | linker_file_t lf; |
| 532 | size_t common_size = 0; |
| 533 | int i; |
| 534 | |
| 535 | KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d\n", |
| 536 | file, name, deps)); |
| 537 | |
| 538 | if (file->ops->lookup_symbol(file, name, &sym) == 0) { |
| 539 | file->ops->symbol_values(file, sym, &symval); |
| 540 | |
| 541 | /* |
| 542 | * XXX Assume a common symbol if its value is 0 and it has a non-zero |
| 543 | * size, otherwise it could be an absolute symbol with a value of 0. |
| 544 | */ |
| 545 | if (symval.value == 0 && symval.size != 0) { |
| 546 | /* |
| 547 | * For commons, first look them up in the dependancies and |
| 548 | * only allocate space if not found there. |
| 549 | */ |
| 550 | common_size = symval.size; |
| 551 | } else { |
| 552 | KLD_DPF(SYM, ("linker_file_lookup_symbol: symbol.value=%x\n", symval.value)); |
| 553 | *raddr = symval.value; |
| 554 | return 0; |
| 555 | } |
| 556 | } |
| 557 | if (deps) { |
| 558 | for (i = 0; i < file->ndeps; i++) { |
| 559 | if (linker_file_lookup_symbol(file->deps[i], name, 0, raddr) == 0) { |
| 560 | KLD_DPF(SYM, ("linker_file_lookup_symbol: deps value=%x\n", *raddr)); |
| 561 | return 0; |
| 562 | } |
| 563 | } |
| 564 | |
| 565 | /* If we have not found it in the dependencies, search globally */ |
| 566 | for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { |
| 567 | /* But skip the current file if it's on the list */ |
| 568 | if (lf == file) |
| 569 | continue; |
| 570 | /* And skip the files we searched above */ |
| 571 | for (i = 0; i < file->ndeps; i++) |
| 572 | if (lf == file->deps[i]) |
| 573 | break; |
| 574 | if (i < file->ndeps) |
| 575 | continue; |
| 576 | if (linker_file_lookup_symbol(lf, name, 0, raddr) == 0) { |
| 577 | KLD_DPF(SYM, ("linker_file_lookup_symbol: global value=%x\n", *raddr)); |
| 578 | return 0; |
| 579 | } |
| 580 | } |
| 581 | } |
| 582 | |
| 583 | if (common_size > 0) { |
| 584 | /* |
| 585 | * This is a common symbol which was not found in the |
| 586 | * dependancies. We maintain a simple common symbol table in |
| 587 | * the file object. |
| 588 | */ |
| 589 | struct common_symbol* cp; |
| 590 | |
| 591 | for (cp = STAILQ_FIRST(&file->common); cp; |
| 592 | cp = STAILQ_NEXT(cp, link)) |
| 593 | if (!strcmp(cp->name, name)) { |
| 594 | KLD_DPF(SYM, ("linker_file_lookup_symbol: old common value=%x\n", cp->address)); |
| 595 | *raddr = cp->address; |
| 596 | return 0; |
| 597 | } |
| 598 | |
| 599 | /* |
| 600 | * Round the symbol size up to align. |
| 601 | */ |
| 602 | common_size = (common_size + sizeof(int) - 1) & -sizeof(int); |
| 603 | cp = malloc(sizeof(struct common_symbol) |
| 604 | + common_size |
| 605 | + strlen(name) + 1, |
| 606 | M_LINKER, M_WAITOK); |
| 607 | if (!cp) { |
| 608 | KLD_DPF(SYM, ("linker_file_lookup_symbol: nomem\n")); |
| 609 | return ENOMEM; |
| 610 | } |
| 611 | bzero(cp, sizeof(struct common_symbol) + common_size + strlen(name)+ 1); |
| 612 | |
| 613 | cp->address = (caddr_t) (cp + 1); |
| 614 | cp->name = cp->address + common_size; |
| 615 | strcpy(cp->name, name); |
| 616 | bzero(cp->address, common_size); |
| 617 | STAILQ_INSERT_TAIL(&file->common, cp, link); |
| 618 | |
| 619 | KLD_DPF(SYM, ("linker_file_lookup_symbol: new common value=%x\n", cp->address)); |
| 620 | *raddr = cp->address; |
| 621 | return 0; |
| 622 | } |
| 623 | |
| 624 | KLD_DPF(SYM, ("linker_file_lookup_symbol: fail\n")); |
| 625 | return ENOENT; |
| 626 | } |
| 627 | |
| 628 | #ifdef DDB |
| 629 | /* |
| 630 | * DDB Helpers. DDB has to look across multiple files with their own |
| 631 | * symbol tables and string tables. |
| 632 | * |
| 633 | * Note that we do not obey list locking protocols here. We really don't |
| 634 | * need DDB to hang because somebody's got the lock held. We'll take the |
| 635 | * chance that the files list is inconsistant instead. |
| 636 | */ |
| 637 | |
| 638 | int |
| 639 | linker_ddb_lookup(const char *symstr, c_linker_sym_t *sym) |
| 640 | { |
| 641 | linker_file_t lf; |
| 642 | |
| 643 | for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { |
| 644 | if (lf->ops->lookup_symbol(lf, symstr, sym) == 0) |
| 645 | return 0; |
| 646 | } |
| 647 | return ENOENT; |
| 648 | } |
| 649 | |
| 650 | int |
| 651 | linker_ddb_search_symbol(caddr_t value, c_linker_sym_t *sym, long *diffp) |
| 652 | { |
| 653 | linker_file_t lf; |
| 654 | u_long off = (uintptr_t)value; |
| 655 | u_long diff, bestdiff; |
| 656 | c_linker_sym_t best; |
| 657 | c_linker_sym_t es; |
| 658 | |
| 659 | best = 0; |
| 660 | bestdiff = off; |
| 661 | for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { |
| 662 | if (lf->ops->search_symbol(lf, value, &es, &diff) != 0) |
| 663 | continue; |
| 664 | if (es != 0 && diff < bestdiff) { |
| 665 | best = es; |
| 666 | bestdiff = diff; |
| 667 | } |
| 668 | if (bestdiff == 0) |
| 669 | break; |
| 670 | } |
| 671 | if (best) { |
| 672 | *sym = best; |
| 673 | *diffp = bestdiff; |
| 674 | return 0; |
| 675 | } else { |
| 676 | *sym = 0; |
| 677 | *diffp = off; |
| 678 | return ENOENT; |
| 679 | } |
| 680 | } |
| 681 | |
| 682 | int |
| 683 | linker_ddb_symbol_values(c_linker_sym_t sym, linker_symval_t *symval) |
| 684 | { |
| 685 | linker_file_t lf; |
| 686 | |
| 687 | for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { |
| 688 | if (lf->ops->symbol_values(lf, sym, symval) == 0) |
| 689 | return 0; |
| 690 | } |
| 691 | return ENOENT; |
| 692 | } |
| 693 | |
| 694 | #endif |
| 695 | |
| 696 | /* |
| 697 | * Syscalls. |
| 698 | */ |
| 699 | |
| 700 | int |
| 701 | kldload(struct kldload_args *uap) |
| 702 | { |
| 703 | struct thread *td = curthread; |
| 704 | char* filename = NULL, *modulename; |
| 705 | linker_file_t lf; |
| 706 | int error = 0; |
| 707 | |
| 708 | uap->sysmsg_result = -1; |
| 709 | |
| 710 | if (securelevel > 0) /* redundant, but that's OK */ |
| 711 | return EPERM; |
| 712 | |
| 713 | if ((error = suser(td)) != 0) |
| 714 | return error; |
| 715 | |
| 716 | filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); |
| 717 | if ((error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) != 0) |
| 718 | goto out; |
| 719 | |
| 720 | /* Can't load more than one module with the same name */ |
| 721 | modulename = rindex(filename, '/'); |
| 722 | if (modulename == NULL) |
| 723 | modulename = filename; |
| 724 | else |
| 725 | modulename++; |
| 726 | if (linker_find_file_by_name(modulename)) { |
| 727 | error = EEXIST; |
| 728 | goto out; |
| 729 | } |
| 730 | |
| 731 | if ((error = linker_load_file(filename, &lf)) != 0) |
| 732 | goto out; |
| 733 | |
| 734 | lf->userrefs++; |
| 735 | uap->sysmsg_result = lf->id; |
| 736 | |
| 737 | out: |
| 738 | if (filename) |
| 739 | free(filename, M_TEMP); |
| 740 | return error; |
| 741 | } |
| 742 | |
| 743 | int |
| 744 | kldunload(struct kldunload_args *uap) |
| 745 | { |
| 746 | struct thread *td = curthread; |
| 747 | linker_file_t lf; |
| 748 | int error = 0; |
| 749 | |
| 750 | if (securelevel > 0) /* redundant, but that's OK */ |
| 751 | return EPERM; |
| 752 | |
| 753 | if ((error = suser(td)) != 0) |
| 754 | return error; |
| 755 | |
| 756 | lf = linker_find_file_by_id(SCARG(uap, fileid)); |
| 757 | if (lf) { |
| 758 | KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs)); |
| 759 | if (lf->userrefs == 0) { |
| 760 | printf("linkerunload: attempt to unload file that was loaded by the kernel\n"); |
| 761 | error = EBUSY; |
| 762 | goto out; |
| 763 | } |
| 764 | lf->userrefs--; |
| 765 | error = linker_file_unload(lf); |
| 766 | if (error) |
| 767 | lf->userrefs++; |
| 768 | } else |
| 769 | error = ENOENT; |
| 770 | |
| 771 | out: |
| 772 | return error; |
| 773 | } |
| 774 | |
| 775 | int |
| 776 | kldfind(struct kldfind_args *uap) |
| 777 | { |
| 778 | char *filename = NULL, *modulename; |
| 779 | linker_file_t lf; |
| 780 | int error = 0; |
| 781 | |
| 782 | uap->sysmsg_result = -1; |
| 783 | |
| 784 | filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); |
| 785 | if ((error = copyinstr(SCARG(uap, file), filename, MAXPATHLEN, NULL)) != 0) |
| 786 | goto out; |
| 787 | |
| 788 | modulename = rindex(filename, '/'); |
| 789 | if (modulename == NULL) |
| 790 | modulename = filename; |
| 791 | |
| 792 | lf = linker_find_file_by_name(modulename); |
| 793 | if (lf) |
| 794 | uap->sysmsg_result = lf->id; |
| 795 | else |
| 796 | error = ENOENT; |
| 797 | |
| 798 | out: |
| 799 | if (filename) |
| 800 | free(filename, M_TEMP); |
| 801 | return error; |
| 802 | } |
| 803 | |
| 804 | int |
| 805 | kldnext(struct kldnext_args *uap) |
| 806 | { |
| 807 | linker_file_t lf; |
| 808 | int error = 0; |
| 809 | |
| 810 | if (SCARG(uap, fileid) == 0) { |
| 811 | if (TAILQ_FIRST(&linker_files)) |
| 812 | uap->sysmsg_result = TAILQ_FIRST(&linker_files)->id; |
| 813 | else |
| 814 | uap->sysmsg_result = 0; |
| 815 | return 0; |
| 816 | } |
| 817 | |
| 818 | lf = linker_find_file_by_id(SCARG(uap, fileid)); |
| 819 | if (lf) { |
| 820 | if (TAILQ_NEXT(lf, link)) |
| 821 | uap->sysmsg_result = TAILQ_NEXT(lf, link)->id; |
| 822 | else |
| 823 | uap->sysmsg_result = 0; |
| 824 | } else |
| 825 | error = ENOENT; |
| 826 | |
| 827 | return error; |
| 828 | } |
| 829 | |
| 830 | int |
| 831 | kldstat(struct kldstat_args *uap) |
| 832 | { |
| 833 | linker_file_t lf; |
| 834 | int error = 0; |
| 835 | int version; |
| 836 | struct kld_file_stat* stat; |
| 837 | int namelen; |
| 838 | |
| 839 | lf = linker_find_file_by_id(SCARG(uap, fileid)); |
| 840 | if (!lf) { |
| 841 | error = ENOENT; |
| 842 | goto out; |
| 843 | } |
| 844 | |
| 845 | stat = SCARG(uap, stat); |
| 846 | |
| 847 | /* |
| 848 | * Check the version of the user's structure. |
| 849 | */ |
| 850 | if ((error = copyin(&stat->version, &version, sizeof(version))) != 0) |
| 851 | goto out; |
| 852 | if (version != sizeof(struct kld_file_stat)) { |
| 853 | error = EINVAL; |
| 854 | goto out; |
| 855 | } |
| 856 | |
| 857 | namelen = strlen(lf->filename) + 1; |
| 858 | if (namelen > MAXPATHLEN) |
| 859 | namelen = MAXPATHLEN; |
| 860 | if ((error = copyout(lf->filename, &stat->name[0], namelen)) != 0) |
| 861 | goto out; |
| 862 | if ((error = copyout(&lf->refs, &stat->refs, sizeof(int))) != 0) |
| 863 | goto out; |
| 864 | if ((error = copyout(&lf->id, &stat->id, sizeof(int))) != 0) |
| 865 | goto out; |
| 866 | if ((error = copyout(&lf->address, &stat->address, sizeof(caddr_t))) != 0) |
| 867 | goto out; |
| 868 | if ((error = copyout(&lf->size, &stat->size, sizeof(size_t))) != 0) |
| 869 | goto out; |
| 870 | |
| 871 | uap->sysmsg_result = 0; |
| 872 | |
| 873 | out: |
| 874 | return error; |
| 875 | } |
| 876 | |
| 877 | int |
| 878 | kldfirstmod(struct kldfirstmod_args *uap) |
| 879 | { |
| 880 | linker_file_t lf; |
| 881 | int error = 0; |
| 882 | |
| 883 | lf = linker_find_file_by_id(SCARG(uap, fileid)); |
| 884 | if (lf) { |
| 885 | if (TAILQ_FIRST(&lf->modules)) |
| 886 | uap->sysmsg_result = module_getid(TAILQ_FIRST(&lf->modules)); |
| 887 | else |
| 888 | uap->sysmsg_result = 0; |
| 889 | } else |
| 890 | error = ENOENT; |
| 891 | |
| 892 | return error; |
| 893 | } |
| 894 | |
| 895 | int |
| 896 | kldsym(struct kldsym_args *uap) |
| 897 | { |
| 898 | char *symstr = NULL; |
| 899 | c_linker_sym_t sym; |
| 900 | linker_symval_t symval; |
| 901 | linker_file_t lf; |
| 902 | struct kld_sym_lookup lookup; |
| 903 | int error = 0; |
| 904 | |
| 905 | if ((error = copyin(SCARG(uap, data), &lookup, sizeof(lookup))) != 0) |
| 906 | goto out; |
| 907 | if (lookup.version != sizeof(lookup) || SCARG(uap, cmd) != KLDSYM_LOOKUP) { |
| 908 | error = EINVAL; |
| 909 | goto out; |
| 910 | } |
| 911 | |
| 912 | symstr = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); |
| 913 | if ((error = copyinstr(lookup.symname, symstr, MAXPATHLEN, NULL)) != 0) |
| 914 | goto out; |
| 915 | |
| 916 | if (SCARG(uap, fileid) != 0) { |
| 917 | lf = linker_find_file_by_id(SCARG(uap, fileid)); |
| 918 | if (lf == NULL) { |
| 919 | error = ENOENT; |
| 920 | goto out; |
| 921 | } |
| 922 | if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 && |
| 923 | lf->ops->symbol_values(lf, sym, &symval) == 0) { |
| 924 | lookup.symvalue = (uintptr_t)symval.value; |
| 925 | lookup.symsize = symval.size; |
| 926 | error = copyout(&lookup, SCARG(uap, data), sizeof(lookup)); |
| 927 | } else |
| 928 | error = ENOENT; |
| 929 | } else { |
| 930 | for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) { |
| 931 | if (lf->ops->lookup_symbol(lf, symstr, &sym) == 0 && |
| 932 | lf->ops->symbol_values(lf, sym, &symval) == 0) { |
| 933 | lookup.symvalue = (uintptr_t)symval.value; |
| 934 | lookup.symsize = symval.size; |
| 935 | error = copyout(&lookup, SCARG(uap, data), sizeof(lookup)); |
| 936 | break; |
| 937 | } |
| 938 | } |
| 939 | if (!lf) |
| 940 | error = ENOENT; |
| 941 | } |
| 942 | out: |
| 943 | if (symstr) |
| 944 | free(symstr, M_TEMP); |
| 945 | return error; |
| 946 | } |
| 947 | |
| 948 | /* |
| 949 | * Look for module metadata in the static kernel |
| 950 | */ |
| 951 | struct mod_metadata * |
| 952 | find_mod_metadata(const char *modname) |
| 953 | { |
| 954 | int len; |
| 955 | struct mod_metadata **mdp; |
| 956 | struct mod_metadata *mdt; |
| 957 | |
| 958 | /* |
| 959 | * Strip path prefixes and any dot extension. MDT_MODULE names |
| 960 | * are just the module name without a path or ".ko". |
| 961 | */ |
| 962 | for (len = strlen(modname) - 1; len >= 0; --len) { |
| 963 | if (modname[len] == '/') |
| 964 | break; |
| 965 | } |
| 966 | modname += len + 1; |
| 967 | for (len = 0; modname[len] && modname[len] != '.'; ++len) |
| 968 | ; |
| 969 | |
| 970 | /* |
| 971 | * Look for the module declaration |
| 972 | */ |
| 973 | SET_FOREACH(mdp, modmetadata_set) { |
| 974 | mdt = *mdp; |
| 975 | if (mdt->md_type != MDT_MODULE) |
| 976 | continue; |
| 977 | if (strlen(mdt->md_cval) == len && |
| 978 | strncmp(mdt->md_cval, modname, len) == 0 |
| 979 | ) { |
| 980 | return(mdt); |
| 981 | } |
| 982 | } |
| 983 | return(NULL); |
| 984 | } |
| 985 | |
| 986 | /* |
| 987 | * Preloaded module support |
| 988 | */ |
| 989 | static void |
| 990 | linker_preload(void* arg) |
| 991 | { |
| 992 | caddr_t modptr; |
| 993 | char *modname; |
| 994 | char *modtype; |
| 995 | linker_file_t lf; |
| 996 | linker_class_t lc; |
| 997 | int error; |
| 998 | struct sysinit **sipp; |
| 999 | const moduledata_t *moddata; |
| 1000 | struct sysinit **si_start, **si_stop; |
| 1001 | |
| 1002 | modptr = NULL; |
| 1003 | while ((modptr = preload_search_next_name(modptr)) != NULL) { |
| 1004 | modname = (char *)preload_search_info(modptr, MODINFO_NAME); |
| 1005 | modtype = (char *)preload_search_info(modptr, MODINFO_TYPE); |
| 1006 | if (modname == NULL) { |
| 1007 | printf("Preloaded module at %p does not have a name!\n", modptr); |
| 1008 | continue; |
| 1009 | } |
| 1010 | if (modtype == NULL) { |
| 1011 | printf("Preloaded module at %p does not have a type!\n", modptr); |
| 1012 | continue; |
| 1013 | } |
| 1014 | |
| 1015 | /* |
| 1016 | * This is a hack at the moment, but what's in FreeBSD-5 is even |
| 1017 | * worse so I'd rather the hack. |
| 1018 | */ |
| 1019 | printf("Preloaded %s \"%s\" at %p", modtype, modname, modptr); |
| 1020 | if (find_mod_metadata(modname)) { |
| 1021 | printf(" (ignored, already in static kernel)\n"); |
| 1022 | continue; |
| 1023 | } |
| 1024 | printf(".\n"); |
| 1025 | |
| 1026 | lf = linker_find_file_by_name(modname); |
| 1027 | if (lf) { |
| 1028 | lf->userrefs++; |
| 1029 | continue; |
| 1030 | } |
| 1031 | lf = NULL; |
| 1032 | for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) { |
| 1033 | error = lc->ops->load_file(modname, &lf); |
| 1034 | if (error) { |
| 1035 | lf = NULL; |
| 1036 | break; |
| 1037 | } |
| 1038 | } |
| 1039 | if (lf) { |
| 1040 | lf->userrefs++; |
| 1041 | |
| 1042 | if (linker_file_lookup_set(lf, "sysinit_set", &si_start, &si_stop, NULL) == 0) { |
| 1043 | /* HACK ALERT! |
| 1044 | * This is to set the sysinit moduledata so that the module |
| 1045 | * can attach itself to the correct containing file. |
| 1046 | * The sysinit could be run at *any* time. |
| 1047 | */ |
| 1048 | for (sipp = si_start; sipp < si_stop; sipp++) { |
| 1049 | if ((*sipp)->func == module_register_init) { |
| 1050 | moddata = (*sipp)->udata; |
| 1051 | error = module_register(moddata, lf); |
| 1052 | if (error) |
| 1053 | printf("Preloaded %s \"%s\" failed to register: %d\n", |
| 1054 | modtype, modname, error); |
| 1055 | } |
| 1056 | } |
| 1057 | sysinit_add(si_start, si_stop); |
| 1058 | } |
| 1059 | linker_file_register_sysctls(lf); |
| 1060 | } |
| 1061 | } |
| 1062 | } |
| 1063 | |
| 1064 | SYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, 0); |
| 1065 | |
| 1066 | /* |
| 1067 | * Search for a not-loaded module by name. |
| 1068 | * |
| 1069 | * Modules may be found in the following locations: |
| 1070 | * |
| 1071 | * - preloaded (result is just the module name) |
| 1072 | * - on disk (result is full path to module) |
| 1073 | * |
| 1074 | * If the module name is qualified in any way (contains path, etc.) |
| 1075 | * the we simply return a copy of it. |
| 1076 | * |
| 1077 | * The search path can be manipulated via sysctl. Note that we use the ';' |
| 1078 | * character as a separator to be consistent with the bootloader. |
| 1079 | */ |
| 1080 | |
| 1081 | static char linker_path[MAXPATHLEN] = "/;/boot/;/modules/"; |
| 1082 | |
| 1083 | SYSCTL_STRING(_kern, OID_AUTO, module_path, CTLFLAG_RW, linker_path, |
| 1084 | sizeof(linker_path), "module load search path"); |
| 1085 | |
| 1086 | static char * |
| 1087 | linker_strdup(const char *str) |
| 1088 | { |
| 1089 | char *result; |
| 1090 | |
| 1091 | if ((result = malloc((strlen(str) + 1), M_LINKER, M_WAITOK)) != NULL) |
| 1092 | strcpy(result, str); |
| 1093 | return(result); |
| 1094 | } |
| 1095 | |
| 1096 | char * |
| 1097 | linker_search_path(const char *name) |
| 1098 | { |
| 1099 | struct nlookupdata nd; |
| 1100 | char *cp, *ep, *result; |
| 1101 | int error; |
| 1102 | enum vtype type; |
| 1103 | |
| 1104 | /* qualified at all? */ |
| 1105 | if (index(name, '/')) |
| 1106 | return(linker_strdup(name)); |
| 1107 | |
| 1108 | /* traverse the linker path */ |
| 1109 | cp = linker_path; |
| 1110 | for (;;) { |
| 1111 | |
| 1112 | /* find the end of this component */ |
| 1113 | for (ep = cp; (*ep != 0) && (*ep != ';'); ep++) |
| 1114 | ; |
| 1115 | result = malloc((strlen(name) + (ep - cp) + 1), M_LINKER, M_WAITOK); |
| 1116 | if (result == NULL) /* actually ENOMEM */ |
| 1117 | return(NULL); |
| 1118 | |
| 1119 | strncpy(result, cp, ep - cp); |
| 1120 | strcpy(result + (ep - cp), name); |
| 1121 | |
| 1122 | /* |
| 1123 | * Attempt to open the file, and return the path if we succeed and it's |
| 1124 | * a regular file. |
| 1125 | */ |
| 1126 | error = nlookup_init(&nd, result, UIO_SYSSPACE, NLC_FOLLOW|NLC_LOCKVP); |
| 1127 | if (error == 0) |
| 1128 | error = vn_open(&nd, NULL, FREAD, 0); |
| 1129 | if (error == 0) { |
| 1130 | type = nd.nl_open_vp->v_type; |
| 1131 | if (type == VREG) { |
| 1132 | nlookup_done(&nd); |
| 1133 | return (result); |
| 1134 | } |
| 1135 | } |
| 1136 | nlookup_done(&nd); |
| 1137 | free(result, M_LINKER); |
| 1138 | |
| 1139 | if (*ep == 0) |
| 1140 | break; |
| 1141 | cp = ep + 1; |
| 1142 | } |
| 1143 | return(NULL); |
| 1144 | } |