1/* 2 * Copyright (c) 2006, 2012 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23#include <CoreFoundation/CoreFoundation.h> 24#include <CoreFoundation/CFBundlePriv.h> 25#include <errno.h> 26#include <libc.h> 27#include <libgen.h> // dirname() 28#include <sys/types.h> 29#include <sys/mman.h> 30#include <fts.h> 31#include <paths.h> 32#include <mach-o/arch.h> 33#include <mach-o/fat.h> 34#include <mach-o/loader.h> 35#include <mach-o/nlist.h> 36#include <mach-o/swap.h> 37#include <mach/mach.h> 38#include <mach/mach_error.h> 39#include <mach/mach_port.h> // mach_port_allocate() 40#include <mach/mach_types.h> 41#include <mach/machine/vm_param.h> 42#include <mach/kmod.h> 43#include <notify.h> 44#include <servers/bootstrap.h> // bootstrap mach ports 45#include <stdlib.h> 46#include <unistd.h> // sleep(3) 47#include <sys/types.h> 48#include <sys/stat.h> 49 50#include <DiskArbitration/DiskArbitrationPrivate.h> 51#include <IOKit/IOTypes.h> 52#include <IOKit/IOKitLib.h> 53#include <IOKit/IOKitServer.h> 54#include <IOKit/IOCFUnserialize.h> 55#include <IOKit/IOCFSerialize.h> 56#include <libkern/OSByteOrder.h> 57 58#include <IOKit/kext/OSKext.h> 59#include <IOKit/kext/OSKextPrivate.h> 60#include <IOKit/kext/kextmanager_types.h> 61#include <IOKit/kext/kextmanager_mig.h> 62 63#include <IOKit/pwr_mgt/IOPMLib.h> 64 65#include "kcgen_main.h" 66#include "compression.h" 67 68/******************************************************************************* 69* Program Globals 70*******************************************************************************/ 71const char * progname = "(unknown)"; 72 73/******************************************************************************* 74*******************************************************************************/ 75int main(int argc, char * const * argv) 76{ 77 ExitStatus result = EX_SOFTWARE; 78 KcgenArgs toolArgs; 79 Boolean fatal = false; 80 81 /***** 82 * Find out what the program was invoked as. 83 */ 84 progname = rindex(argv[0], '/'); 85 if (progname) { 86 progname++; // go past the '/' 87 } else { 88 progname = (char *)argv[0]; 89 } 90 91 /* Set the OSKext log callback right away. 92 */ 93 OSKextSetLogOutputFunction(&tool_log); 94 95 /* Make the library not sort opened kexts by version for bundle ID lookups. 96 */ 97 _OSKextSetStrictRecordingByLastOpened(TRUE); 98 99 /***** 100 * Check if we were spawned by kextd, set up straightaway 101 * for service log filtering, and hook up to ASL. 102 */ 103 if (getenv("KEXTD_SPAWNED")) { 104 OSKextSetLogFilter(kDefaultServiceLogFilter | kOSKextLogKextOrGlobalMask, 105 /* kernel? */ false); 106 OSKextSetLogFilter(kDefaultServiceLogFilter | kOSKextLogKextOrGlobalMask, 107 /* kernel? */ true); 108 tool_openlog("com.apple.kcgen"); 109 } 110 111 /***** 112 * Process args & check for permission to load. 113 */ 114 result = readArgs(&argc, &argv, &toolArgs); 115 if (result != EX_OK) { 116 if (result == kKcgenExitHelp) { 117 result = EX_OK; 118 } 119 goto finish; 120 } 121 122 result = checkArgs(&toolArgs); 123 if (result != EX_OK) { 124 goto finish; 125 } 126 127 /* From here on out the default exit status is ok. 128 */ 129 result = EX_OK; 130 131 /* The whole point of this program is to update caches, so let's not 132 * try to read any (we'll briefly turn this back on when checking them). 133 */ 134 OSKextSetUsesCaches(false); 135 136 /* If we're compressing the prelinked kernel, take care of that here 137 * and exit. 138 */ 139 if (toolArgs.prelinkedKernelPath && !CFArrayGetCount(toolArgs.argURLs) && 140 (toolArgs.compress || toolArgs.uncompress)) 141 { 142 result = compressPrelinkedKernel(toolArgs.prelinkedKernelPath, 143 toolArgs.compress, 144 toolArgs.compressionType); 145 goto finish; 146 } 147 148 /***** 149 * Read the kexts we'll be working with; first the set of all kexts, then 150 * the repository and named kexts for use with mkext-creation flags. 151 */ 152 if (toolArgs.printTestResults) { 153 OSKextSetRecordsDiagnostics(kOSKextDiagnosticsFlagAll); 154 } 155 toolArgs.allKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, toolArgs.argURLs); 156 if (!toolArgs.allKexts || !CFArrayGetCount(toolArgs.allKexts)) { 157 OSKextLog(/* kext */ NULL, 158 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 159 "Error - no kernel extensions found."); 160 result = EX_SOFTWARE; 161 goto finish; 162 } 163 164 toolArgs.repositoryKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, 165 toolArgs.repositoryURLs); 166 toolArgs.namedKexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, 167 toolArgs.namedKextURLs); 168 if (!toolArgs.repositoryKexts || !toolArgs.namedKexts) { 169 OSKextLog(/* kext */ NULL, 170 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 171 "Error - failed to read extensions."); 172 result = EX_SOFTWARE; 173 goto finish; 174 } 175 176 if (result != EX_OK) { 177 goto finish; 178 } 179 180 if (toolArgs.prelinkedKernelPath) { 181 result = createPrelinkedKernel(&toolArgs); 182 if (result != EX_OK) { 183 goto finish; 184 } 185 186 } 187 188finish: 189 190 /* We're actually not going to free anything else because we're exiting! 191 */ 192 exit(result); 193 194 SAFE_RELEASE(toolArgs.kextIDs); 195 SAFE_RELEASE(toolArgs.argURLs); 196 SAFE_RELEASE(toolArgs.repositoryURLs); 197 SAFE_RELEASE(toolArgs.namedKextURLs); 198 SAFE_RELEASE(toolArgs.allKexts); 199 SAFE_RELEASE(toolArgs.repositoryKexts); 200 SAFE_RELEASE(toolArgs.namedKexts); 201 SAFE_RELEASE(toolArgs.kernelFile); 202 SAFE_RELEASE(toolArgs.symbolDirURL); 203 SAFE_FREE(toolArgs.prelinkedKernelPath); 204 SAFE_FREE(toolArgs.kernelPath); 205 206 return result; 207} 208 209/******************************************************************************* 210*******************************************************************************/ 211ExitStatus readArgs( 212 int * argc, 213 char * const ** argv, 214 KcgenArgs * toolArgs) 215{ 216 ExitStatus result = EX_USAGE; 217 ExitStatus scratchResult = EX_USAGE; 218 CFStringRef scratchString = NULL; // must release 219 CFNumberRef scratchNumber = NULL; // must release 220 CFURLRef scratchURL = NULL; // must release 221 size_t len = 0; 222 int32_t i = 0; 223 int optchar = 0; 224 int longindex = -1; 225 226 bzero(toolArgs, sizeof(*toolArgs)); 227 228 /***** 229 * Allocate collection objects. 230 */ 231 if (!createCFMutableSet(&toolArgs->kextIDs, &kCFTypeSetCallBacks) || 232 !createCFMutableSet(&toolArgs->optionalKextIDs, &kCFTypeSetCallBacks) || 233 !createCFMutableArray(&toolArgs->argURLs, &kCFTypeArrayCallBacks) || 234 !createCFMutableArray(&toolArgs->repositoryURLs, &kCFTypeArrayCallBacks) || 235 !createCFMutableArray(&toolArgs->namedKextURLs, &kCFTypeArrayCallBacks) || 236 !createCFMutableArray(&toolArgs->targetArchs, NULL)) { 237 238 OSKextLogMemError(); 239 result = EX_OSERR; 240 exit(result); 241 } 242 243 /***** 244 * Process command line arguments. 245 */ 246 while ((optchar = getopt_long_only(*argc, *argv, 247 kOptChars, sOptInfo, &longindex)) != -1) { 248 249 SAFE_RELEASE_NULL(scratchString); 250 SAFE_RELEASE_NULL(scratchNumber); 251 SAFE_RELEASE_NULL(scratchURL); 252 253 /* When processing short (single-char) options, there is no way to 254 * express optional arguments. Instead, we suppress missing option 255 * argument errors by adding a leading ':' to the option string. 256 * When getopt detects a missing argument, it will return a ':' so that 257 * we can screen for options that are not required to have an argument. 258 */ 259 if (optchar == ':') { 260 switch (optopt) { 261 case kOptPrelinkedKernel: 262 optchar = optopt; 263 break; 264 default: 265 OSKextLog(/* kext */ NULL, 266 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 267 "Error - option requires an argument -- -%c.", 268 optopt); 269 break; 270 } 271 } 272 273 switch (optchar) { 274 275 case kOptArch: 276 if (!addArchForName(toolArgs, optarg)) { 277 OSKextLog(/* kext */ NULL, 278 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 279 "Error - unknown architecture %s.", optarg); 280 goto finish; 281 } 282 break; 283 284 case kOptBundleIdentifier: 285 scratchString = CFStringCreateWithCString(kCFAllocatorDefault, 286 optarg, kCFStringEncodingUTF8); 287 if (!scratchString) { 288 OSKextLogMemError(); 289 result = EX_OSERR; 290 goto finish; 291 } 292 CFSetAddValue(toolArgs->kextIDs, scratchString); 293 break; 294 295 case kOptPrelinkedKernel: 296 scratchResult = readPrelinkedKernelArgs(toolArgs, *argc, *argv, 297 /* isLongopt */ longindex != -1); 298 if (scratchResult != EX_OK) { 299 result = scratchResult; 300 goto finish; 301 } 302 break; 303 304 case kOptHelp: 305 usage(kUsageLevelFull); 306 result = kKcgenExitHelp; 307 goto finish; 308 309 case kOptKernel: 310 if (toolArgs->kernelPath) { 311 OSKextLog(/* kext */ NULL, 312 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 313 "Warning - kernel file already specified; using last."); 314 } else { 315 toolArgs->kernelPath = malloc(PATH_MAX); 316 if (!toolArgs->kernelPath) { 317 OSKextLogMemError(); 318 result = EX_OSERR; 319 goto finish; 320 } 321 } 322 323 len = strlcpy(toolArgs->kernelPath, optarg, PATH_MAX); 324 if (len >= PATH_MAX) { 325 OSKextLog(/* kext */ NULL, 326 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 327 "Error - kernel filename length exceeds PATH_MAX"); 328 goto finish; 329 } 330 break; 331 332 case kOptTests: 333 toolArgs->printTestResults = true; 334 break; 335 336 case kOptQuiet: 337 beQuiet(); 338 break; 339 340 case kOptVerbose: 341 scratchResult = setLogFilterForOpt(*argc, *argv, 342 /* forceOnFlags */ kOSKextLogKextOrGlobalMask); 343 if (scratchResult != EX_OK) { 344 result = scratchResult; 345 goto finish; 346 } 347 break; 348 349 case kOptNoAuthentication: 350 OSKextLog(/* kext */ NULL, 351 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 352 "Note: -%s is implicitly set for %s.", kOptNameNoAuthentication, progname); 353 break; 354 355 case 0: 356 switch (longopt) { 357 case kLongOptOptionalBundleIdentifier: 358 scratchString = CFStringCreateWithCString(kCFAllocatorDefault, 359 optarg, kCFStringEncodingUTF8); 360 if (!scratchString) { 361 OSKextLogMemError(); 362 result = EX_OSERR; 363 goto finish; 364 } 365 CFSetAddValue(toolArgs->optionalKextIDs, scratchString); 366 break; 367 368 case kLongOptCompressed: 369 toolArgs->compress = true; 370 if (optarg == NULL) { 371 toolArgs->compressionType = COMP_TYPE_LZSS; 372 } else if (0 == strcasecmp(optarg, "lzvn")) { 373 if (!supportsFastLibCompression()) { 374 OSKextLog(/* kext */ NULL, 375 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 376 "Error - lzvn compression specified but not supported"); 377 goto finish; 378 } 379 toolArgs->compressionType = COMP_TYPE_FASTLIB; 380 } else if (0 == strcasecmp(optarg, "lzss")) { 381 toolArgs->compressionType = COMP_TYPE_LZSS; 382 } else { 383 OSKextLog(/* kext */ NULL, 384 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 385 "Error - unrecognized compression type %s", optarg); 386 goto finish; 387 } 388 break; 389 390 case kLongOptUncompressed: 391 toolArgs->uncompress = true; 392 break; 393 394 case kLongOptSymbols: 395 if (toolArgs->symbolDirURL) { 396 OSKextLog(/* kext */ NULL, 397 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 398 "Warning - symbol directory already specified; using last."); 399 SAFE_RELEASE_NULL(toolArgs->symbolDirURL); 400 } 401 402 scratchURL = CFURLCreateFromFileSystemRepresentation( 403 kCFAllocatorDefault, 404 (const UInt8 *)optarg, strlen(optarg), true); 405 if (!scratchURL) { 406 OSKextLogStringError(/* kext */ NULL); 407 result = EX_OSERR; 408 goto finish; 409 } 410 411 toolArgs->symbolDirURL = CFRetain(scratchURL); 412 toolArgs->generatePrelinkedSymbols = true; 413 break; 414 415 case kLongOptVolumeRoot: 416 if (toolArgs->volumeRootURL) { 417 OSKextLog(/* kext */ NULL, 418 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 419 "Warning: volume root already specified; using last."); 420 SAFE_RELEASE_NULL(toolArgs->volumeRootURL); 421 } 422 423 scratchURL = CFURLCreateFromFileSystemRepresentation( 424 kCFAllocatorDefault, 425 (const UInt8 *)optarg, strlen(optarg), true); 426 if (!scratchURL) { 427 OSKextLogStringError(/* kext */ NULL); 428 result = EX_OSERR; 429 goto finish; 430 } 431 432 toolArgs->volumeRootURL = CFRetain(scratchURL); 433 break; 434 435 case kLongOptAllPersonalities: 436 OSKextLog(/* kext */ NULL, 437 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 438 "Note: -%s is implicitly set for %s.", kOptNameAllPersonalities, progname); 439 break; 440 441 case kLongOptNoLinkFailures: 442 OSKextLog(/* kext */ NULL, 443 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 444 "Note: -%s is implicitly set for %s.", kOptNameNoLinkFailures, progname); 445 break; 446 447 case kLongOptStripSymbols: 448 toolArgs->stripSymbols = true; 449 break; 450 451 case kLongOptMaxSliceSize: 452 toolArgs->maxSliceSize = atol(optarg); 453 break; 454 455 default: 456 /* Because we use ':', getopt_long doesn't print an error message. 457 */ 458 OSKextLog(/* kext */ NULL, 459 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 460 "Error - unrecognized option %s", (*argv)[optind-1]); 461 goto finish; 462 break; 463 } 464 break; 465 466 default: 467 /* Because we use ':', getopt_long doesn't print an error message. 468 */ 469 OSKextLog(/* kext */ NULL, 470 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 471 "Error - unrecognized option %s", (*argv)[optind-1]); 472 goto finish; 473 break; 474 475 } 476 477 /* Reset longindex, because getopt_long_only() is stupid and doesn't. 478 */ 479 longindex = -1; 480 } 481 482 /* Update the argc & argv seen by main(). 483 */ 484 *argc -= optind; 485 *argv += optind; 486 487 /* 488 * Record the kext & directory names from the command line. 489 */ 490 for (i = 0; i < *argc; i++) { 491 SAFE_RELEASE_NULL(scratchURL); 492 SAFE_RELEASE_NULL(scratchString); 493 494 scratchURL = CFURLCreateFromFileSystemRepresentation( 495 kCFAllocatorDefault, 496 (const UInt8 *)(*argv)[i], strlen((*argv)[i]), true); 497 if (!scratchURL) { 498 OSKextLogMemError(); 499 result = EX_OSERR; 500 goto finish; 501 } 502 CFArrayAppendValue(toolArgs->argURLs, scratchURL); 503 504 scratchString = CFURLCopyPathExtension(scratchURL); 505 if (scratchString && CFEqual(scratchString, CFSTR("kext"))) { 506 CFArrayAppendValue(toolArgs->namedKextURLs, scratchURL); 507 } else { 508 CFArrayAppendValue(toolArgs->repositoryURLs, scratchURL); 509 } 510 } 511 512 result = EX_OK; 513 514finish: 515 SAFE_RELEASE(scratchString); 516 SAFE_RELEASE(scratchNumber); 517 SAFE_RELEASE(scratchURL); 518 519 if (result == EX_USAGE) { 520 usage(kUsageLevelBrief); 521 } 522 return result; 523} 524 525/******************************************************************************* 526*******************************************************************************/ 527void logUsedKexts( 528 KcgenArgs * toolArgs, 529 CFArrayRef prelinkKexts) 530{ 531 int fd; 532 int ret; 533 char * kextTracePath; 534 char tmpBuffer[PATH_MAX + 1 + 1]; 535 size_t tmpBufLen; 536 ssize_t writeSize; 537 CFIndex count, i; 538 539 /* Log used kext bundle identifiers to out-of-band log file */ 540 kextTracePath = getenv("KEXT_TRACE_FILE"); 541 if (!kextTracePath) { 542 return; 543 } 544 545 fd = open(kextTracePath, O_WRONLY|O_APPEND|O_CREAT, DEFFILEMODE); 546 if (fd < 0) { 547 return; 548 } 549 550 snprintf(tmpBuffer, sizeof(tmpBuffer), "%s\n", toolArgs->prelinkedKernelPath); 551 tmpBufLen = strlen(tmpBuffer); 552 writeSize = write(fd, tmpBuffer, tmpBufLen); 553 if (writeSize != (ssize_t)tmpBufLen) { 554 close(fd); 555 return; 556 } 557 558 count = CFArrayGetCount(prelinkKexts); 559 for (i = 0; i < count; i++) { 560 CFStringRef kextID; 561 OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex(prelinkKexts, i); 562 563 kextID = OSKextGetIdentifier(theKext); 564 if (kextID) { 565 char kextIDCString[KMOD_MAX_NAME]; 566 567 CFStringGetCString(kextID, kextIDCString, sizeof(kextIDCString), 568 kCFStringEncodingUTF8); 569 570 snprintf(tmpBuffer, sizeof(tmpBuffer), "[Logging for XBS] Used kext bundle identifier: %s\n", kextIDCString); 571 tmpBufLen = strlen(tmpBuffer); 572 writeSize = write(fd, tmpBuffer, tmpBufLen); 573 if (writeSize != (ssize_t)tmpBufLen) { 574 close(fd); 575 return; 576 } 577 } 578 } 579 580 close(fd); 581} 582 583/******************************************************************************* 584*******************************************************************************/ 585ExitStatus readPrelinkedKernelArgs( 586 KcgenArgs * toolArgs, 587 int argc, 588 char * const * argv, 589 Boolean isLongopt) 590{ 591 char * filename = NULL; // do not free 592 593 if (optarg) { 594 filename = optarg; 595 } else if (isLongopt && optind < argc) { 596 filename = argv[optind]; 597 optind++; 598 } 599 600 if (filename && !filename[0]) { 601 filename = NULL; 602 } 603 604 return setPrelinkedKernelArgs(toolArgs, filename); 605} 606 607/******************************************************************************* 608*******************************************************************************/ 609ExitStatus setPrelinkedKernelArgs( 610 KcgenArgs * toolArgs, 611 char * filename) 612{ 613 ExitStatus result = EX_USAGE; 614 615 if (toolArgs->prelinkedKernelPath) { 616 OSKextLog(/* kext */ NULL, 617 kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 618 "Warning - prelinked kernel already specified; using last."); 619 } else { 620 toolArgs->prelinkedKernelPath = malloc(PATH_MAX); 621 if (!toolArgs->prelinkedKernelPath) { 622 OSKextLogMemError(); 623 result = EX_OSERR; 624 goto finish; 625 } 626 } 627 628 /* If we don't have a filename we construct a default one, automatically 629 * add the system extensions folders, and note that we're using default 630 * info. 631 */ 632 if (!filename) { 633 OSKextLog(/* kext */ NULL, 634 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 635 "Error - prelinked kernel filename required"); 636 goto finish; 637 } else { 638 size_t len = strlcpy(toolArgs->prelinkedKernelPath, filename, PATH_MAX); 639 if (len >= PATH_MAX) { 640 OSKextLog(/* kext */ NULL, 641 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 642 "Error - prelinked kernel filename length exceeds PATH_MAX"); 643 goto finish; 644 } 645 } 646 647 result = EX_OK; 648finish: 649 return result; 650} 651 652/******************************************************************************* 653********************************************************************************/ 654void addArch( 655 KcgenArgs * toolArgs, 656 const NXArchInfo * arch) 657{ 658 if (CFArrayContainsValue(toolArgs->targetArchs, 659 RANGE_ALL(toolArgs->targetArchs), arch)) 660 { 661 return; 662 } 663 664 CFArrayAppendValue(toolArgs->targetArchs, arch); 665} 666 667/******************************************************************************* 668*******************************************************************************/ 669const NXArchInfo * addArchForName( 670 KcgenArgs * toolArgs, 671 const char * archname) 672{ 673 const NXArchInfo * result = NULL; 674 675 result = NXGetArchInfoFromName(archname); 676 if (!result) { 677 goto finish; 678 } 679 680 addArch(toolArgs, result); 681 682finish: 683 return result; 684} 685 686/******************************************************************************* 687*******************************************************************************/ 688ExitStatus checkArgs(KcgenArgs * toolArgs) 689{ 690 ExitStatus result = EX_USAGE; 691 692 if (!toolArgs->prelinkedKernelPath) 693 { 694 OSKextLog(/* kext */ NULL, 695 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 696 "Error - no work to do; check options and try again."); 697 goto finish; 698 } 699 700 if (!CFArrayGetCount(toolArgs->argURLs) && 701 !toolArgs->compress && !toolArgs->uncompress) 702 { 703 OSKextLog(/* kext */ NULL, 704 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 705 "Error - no kexts or directories specified."); 706 goto finish; 707 } 708 709 if (!toolArgs->compress && !toolArgs->uncompress) { 710 toolArgs->compress = true; 711 toolArgs->compressionType = COMP_TYPE_LZSS; 712 } else if (toolArgs->compress && toolArgs->uncompress) { 713 OSKextLog(/* kext */ NULL, 714 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 715 "Both -%s and -%s specified; using -%s.", 716 kOptNameCompressed, kOptNameUncompressed, kOptNameCompressed); 717 toolArgs->compress = true; 718 toolArgs->uncompress = false; 719 } 720 721 result = EX_OK; 722 723finish: 724 if (result == EX_USAGE) { 725 usage(kUsageLevelBrief); 726 } 727 return result; 728} 729 730/******************************************************************************* 731*******************************************************************************/ 732typedef struct { 733 KcgenArgs * toolArgs; 734 CFMutableArrayRef kextArray; 735 const NXArchInfo * arch; 736 Boolean optional; 737 Boolean error; 738} FilterIDContext; 739 740void filterKextID(const void * vValue, void * vContext) 741{ 742 CFStringRef kextID = (CFStringRef)vValue; 743 FilterIDContext * context = (FilterIDContext *)vContext; 744 OSKextRef theKext = OSKextGetKextWithIdentifier(kextID); 745 746 if (!theKext) { 747 char kextIDCString[KMOD_MAX_NAME]; 748 749 CFStringGetCString(kextID, kextIDCString, sizeof(kextIDCString), 750 kCFStringEncodingUTF8); 751 752 if (context->optional) { 753 OSKextLog(/* kext */ NULL, 754 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 755 "Can't find kext with optional identifier %s; skipping.", kextIDCString); 756 } else { 757 OSKextLog(/* kext */ NULL, 758 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 759 "Error - can't find kext with identifier %s.", kextIDCString); 760 context->error = TRUE; 761 } 762 763 goto finish; 764 } 765 766 if (checkKextForProblems(context->toolArgs, theKext, context->arch)) { 767 if (!context->optional) { 768 OSKextLog(/* kext */ NULL, 769 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 770 "Error - a required kext was omitted"); 771 context->error = true; 772 } 773 goto finish; 774 } 775 776 if (!CFArrayContainsValue(context->kextArray, 777 RANGE_ALL(context->kextArray), theKext)) 778 { 779 CFArrayAppendValue(context->kextArray, theKext); 780 } 781 782finish: 783 return; 784} 785 786/******************************************************************************* 787 *******************************************************************************/ 788ExitStatus checkKextForProblems( 789 KcgenArgs * toolArgs, 790 OSKextRef theKext, 791 const NXArchInfo * arch) 792{ 793 ExitStatus result = EX_SOFTWARE; 794 char kextPath[PATH_MAX]; 795 796 if (!CFURLGetFileSystemRepresentation(OSKextGetURL(theKext), 797 /* resolveToBase */ false, (UInt8 *)kextPath, sizeof(kextPath))) 798 { 799 strlcpy(kextPath, "(unknown)", sizeof(kextPath)); 800 } 801 802 /* Skip kexts we have no interest in for the current arch. 803 */ 804 if (!OSKextSupportsArchitecture(theKext, arch)) { 805 OSKextLog(/* kext */ NULL, 806 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 807 "%s doesn't support architecture '%s'; ommiting.", kextPath, 808 arch->name); 809 goto finish; 810 } 811 812 if (!OSKextIsValid(theKext)) { 813 OSKextLog(/* kext */ NULL, 814 kOSKextLogErrorLevel | kOSKextLogArchiveFlag | 815 kOSKextLogValidationFlag | kOSKextLogGeneralFlag, 816 "%s is not valid; omitting.", kextPath); 817 if (toolArgs->printTestResults) { 818 OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll); 819 } 820 goto finish; 821 } 822 823 if (!OSKextResolveDependencies(theKext)) { 824 OSKextLog(/* kext */ NULL, 825 kOSKextLogWarningLevel | kOSKextLogArchiveFlag | 826 kOSKextLogDependenciesFlag | kOSKextLogGeneralFlag, 827 "%s is missing dependencies (including anyway; " 828 "dependencies may be available from elsewhere)", kextPath); 829 if (toolArgs->printTestResults) { 830 OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll); 831 } 832 } 833 834 result = EX_OK; 835 836finish: 837 return result; 838 839} 840 841/******************************************************************************* 842*******************************************************************************/ 843ExitStatus filterKextsForCache( 844 KcgenArgs * toolArgs, 845 CFMutableArrayRef kextArray, 846 const NXArchInfo * arch, 847 Boolean * fatalOut __unused) 848{ 849 ExitStatus result = EX_SOFTWARE; 850 CFIndex count, i; 851 852 CFArrayRemoveAllValues(kextArray); 853 854 /***** 855 * Apply filters to select the kexts. 856 * 857 * If kexts have been specified by identifier, those are the only kexts we are going to use. 858 * Otherwise run through the repository and named kexts and see which ones match the filter. 859 */ 860 if (CFSetGetCount(toolArgs->kextIDs) || CFSetGetCount(toolArgs->optionalKextIDs)) { 861 FilterIDContext context; 862 863 context.toolArgs = toolArgs; 864 context.kextArray = kextArray; 865 context.arch = arch; 866 context.optional = FALSE; 867 context.error = FALSE; 868 869 CFSetApplyFunction(toolArgs->kextIDs, filterKextID, &context); 870 871 context.optional = TRUE; 872 CFSetApplyFunction(toolArgs->optionalKextIDs, filterKextID, &context); 873 874 if (context.error) { 875 goto finish; 876 } 877 878 } else { 879 880 count = CFArrayGetCount(toolArgs->repositoryKexts); 881 for (i = 0; i < count; i++) { 882 char kextPath[PATH_MAX]; 883 OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex( 884 toolArgs->repositoryKexts, i); 885 886 if (!CFArrayContainsValue(kextArray, RANGE_ALL(kextArray), theKext) && 887 !checkKextForProblems(toolArgs, theKext, arch)) { 888 CFArrayAppendValue(kextArray, theKext); 889 } 890 } 891 892 count = CFArrayGetCount(toolArgs->namedKexts); 893 for (i = 0; i < count; i++) { 894 OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex( 895 toolArgs->namedKexts, i); 896 if (!CFArrayContainsValue(kextArray, RANGE_ALL(kextArray), theKext) && 897 !checkKextForProblems(toolArgs, theKext, arch)) { 898 CFArrayAppendValue(kextArray, theKext); 899 } 900 } 901 } 902 903 result = EX_OK; 904 905finish: 906 return result; 907} 908 909/******************************************************************************* 910 * Creates a list of architectures to generate prelinked kernel slices for by 911 * selecting the requested architectures for which the kernel has a slice. 912 * Warns when a requested architecture does not have a corresponding kernel 913 * slice. 914 *******************************************************************************/ 915ExitStatus 916createPrelinkedKernelArchs( 917 KcgenArgs * toolArgs, 918 CFMutableArrayRef * prelinkArchsOut) 919{ 920 ExitStatus result = EX_OSERR; 921 CFMutableArrayRef kernelArchs = NULL; // must release 922 CFMutableArrayRef prelinkArchs = NULL; // must release 923 const NXArchInfo * targetArch = NULL; // do not free 924 int i = 0; 925 926 result = readFatFileArchsWithPath(toolArgs->kernelPath, &kernelArchs); 927 if (result != EX_OK) { 928 goto finish; 929 } 930 931 prelinkArchs = CFArrayCreateMutableCopy(kCFAllocatorDefault, 932 /* capacity */ 0, toolArgs->targetArchs); 933 if (!prelinkArchs) { 934 OSKextLogMemError(); 935 result = EX_OSERR; 936 goto finish; 937 } 938 939 for (i = 0; i < CFArrayGetCount(prelinkArchs); ++i) { 940 targetArch = CFArrayGetValueAtIndex(prelinkArchs, i); 941 if (!CFArrayContainsValue(kernelArchs, 942 RANGE_ALL(kernelArchs), targetArch)) 943 { 944 OSKextLog(/* kext */ NULL, 945 kOSKextLogWarningLevel | kOSKextLogArchiveFlag, 946 "Warning - kernel file %s does not contain requested arch: %s", 947 toolArgs->kernelPath, targetArch->name); 948 CFArrayRemoveValueAtIndex(prelinkArchs, i); 949 i--; 950 continue; 951 } 952 } 953 954 *prelinkArchsOut = (CFMutableArrayRef) CFRetain(prelinkArchs); 955 result = EX_OK; 956 957finish: 958 SAFE_RELEASE(kernelArchs); 959 SAFE_RELEASE(prelinkArchs); 960 961 return result; 962} 963 964/******************************************************************************* 965*******************************************************************************/ 966ExitStatus 967createPrelinkedKernel( 968 KcgenArgs * toolArgs) 969{ 970 ExitStatus result = EX_OSERR; 971 struct timeval prelinkFileTimes[2]; 972 CFMutableArrayRef generatedArchs = NULL; // must release 973 CFMutableArrayRef generatedSymbols = NULL; // must release 974 CFMutableArrayRef existingArchs = NULL; // must release 975 CFMutableArrayRef existingSlices = NULL; // must release 976 CFMutableArrayRef prelinkArchs = NULL; // must release 977 CFMutableArrayRef prelinkSlices = NULL; // must release 978 CFDataRef prelinkSlice = NULL; // must release 979 CFDictionaryRef sliceSymbols = NULL; // must release 980 const NXArchInfo * targetArch = NULL; // do not free 981 Boolean updateModTime = false; 982 u_int numArchs = 0; 983 u_int i = 0; 984 int j = 0; 985 986 bzero(prelinkFileTimes, sizeof(prelinkFileTimes)); 987 988 result = createPrelinkedKernelArchs(toolArgs, &prelinkArchs); 989 if (result != EX_OK) { 990 goto finish; 991 } 992 numArchs = (u_int)CFArrayGetCount(prelinkArchs); 993 994 prelinkSlices = CFArrayCreateMutable(kCFAllocatorDefault, 995 numArchs, &kCFTypeArrayCallBacks); 996 generatedSymbols = CFArrayCreateMutable(kCFAllocatorDefault, 997 numArchs, &kCFTypeArrayCallBacks); 998 generatedArchs = CFArrayCreateMutable(kCFAllocatorDefault, 999 numArchs, NULL); 1000 if (!prelinkSlices || !generatedSymbols || !generatedArchs) { 1001 OSKextLogMemError(); 1002 result = EX_OSERR; 1003 goto finish; 1004 } 1005 1006 for (i = 0; i < numArchs; i++) { 1007 targetArch = CFArrayGetValueAtIndex(prelinkArchs, i); 1008 1009 SAFE_RELEASE_NULL(prelinkSlice); 1010 SAFE_RELEASE_NULL(sliceSymbols); 1011 1012 /* We always create a new prelinked kernel for the current 1013 * running architecture if asked, but we'll reuse existing slices 1014 * for other architectures if possible. 1015 */ 1016 if (existingArchs && 1017 targetArch != OSKextGetRunningKernelArchitecture()) 1018 { 1019 j = (int)CFArrayGetFirstIndexOfValue(existingArchs, 1020 RANGE_ALL(existingArchs), targetArch); 1021 if (j != -1) { 1022 prelinkSlice = CFArrayGetValueAtIndex(existingSlices, j); 1023 CFArrayAppendValue(prelinkSlices, prelinkSlice); 1024 prelinkSlice = NULL; 1025 OSKextLog(/* kext */ NULL, 1026 kOSKextLogDebugLevel | kOSKextLogArchiveFlag, 1027 "Using existing prelinked slice for arch %s", 1028 targetArch->name); 1029 continue; 1030 } 1031 } 1032 1033 OSKextLog(/* kext */ NULL, 1034 kOSKextLogDebugLevel | kOSKextLogArchiveFlag, 1035 "Generating a new prelinked slice for arch %s", 1036 targetArch->name); 1037 1038 result = createPrelinkedKernelForArch(toolArgs, &prelinkSlice, 1039 &sliceSymbols, targetArch); 1040 if (result != EX_OK) { 1041 goto finish; 1042 } 1043 1044 if (toolArgs->maxSliceSize && 1045 (CFDataGetLength(prelinkSlice) > toolArgs->maxSliceSize)) { 1046 1047 result = EX_SOFTWARE; 1048 OSKextLog(/* kext */ NULL, 1049 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1050 "Error - prelink slice is larger (%ld) than requested maximum %ld.", 1051 (long int)CFDataGetLength(prelinkSlice), 1052 (long int)toolArgs->maxSliceSize); 1053 goto finish; 1054 } 1055 1056 CFArrayAppendValue(prelinkSlices, prelinkSlice); 1057 CFArrayAppendValue(generatedSymbols, sliceSymbols); 1058 CFArrayAppendValue(generatedArchs, targetArch); 1059 } 1060 1061 result = writeFatFile(toolArgs->prelinkedKernelPath, prelinkSlices, 1062 prelinkArchs, (0644), // !!! - need macro for perms 1063 (updateModTime) ? prelinkFileTimes : NULL); 1064 if (result != EX_OK) { 1065 goto finish; 1066 } 1067 1068 if (toolArgs->symbolDirURL) { 1069 result = writePrelinkedSymbols(toolArgs->symbolDirURL, 1070 generatedSymbols, generatedArchs); 1071 if (result != EX_OK) { 1072 goto finish; 1073 } 1074 } 1075 1076 OSKextLog(/* kext */ NULL, 1077 kOSKextLogBasicLevel | kOSKextLogGeneralFlag | kOSKextLogArchiveFlag, 1078 "Created prelinked kernel %s.", 1079 toolArgs->prelinkedKernelPath); 1080 1081 result = EX_OK; 1082 1083finish: 1084 SAFE_RELEASE(generatedArchs); 1085 SAFE_RELEASE(generatedSymbols); 1086 SAFE_RELEASE(existingArchs); 1087 SAFE_RELEASE(existingSlices); 1088 SAFE_RELEASE(prelinkArchs); 1089 SAFE_RELEASE(prelinkSlices); 1090 SAFE_RELEASE(prelinkSlice); 1091 SAFE_RELEASE(sliceSymbols); 1092 1093 return result; 1094} 1095 1096/******************************************************************************* 1097*******************************************************************************/ 1098ExitStatus createPrelinkedKernelForArch( 1099 KcgenArgs * toolArgs, 1100 CFDataRef * prelinkedKernelOut, 1101 CFDictionaryRef * prelinkedSymbolsOut, 1102 const NXArchInfo * archInfo) 1103{ 1104 ExitStatus result = EX_OSERR; 1105 CFMutableArrayRef prelinkKexts = NULL; 1106 CFDataRef kernelImage = NULL; 1107 CFDataRef prelinkedKernel = NULL; 1108 uint32_t flags = 0; 1109 Boolean fatalOut = false; 1110 1111 /* Retrieve the kernel image for the requested architecture. 1112 */ 1113 kernelImage = readMachOSliceForArch(toolArgs->kernelPath, archInfo, /* checkArch */ TRUE); 1114 if (!kernelImage) { 1115 OSKextLog(/* kext */ NULL, 1116 kOSKextLogErrorLevel | kOSKextLogArchiveFlag | kOSKextLogFileAccessFlag, 1117 "Error - failed to read kernel file."); 1118 goto finish; 1119 } 1120 1121 /* Set the architecture in the OSKext library */ 1122 1123 if (!OSKextSetArchitecture(archInfo)) { 1124 OSKextLog(/* kext */ NULL, 1125 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1126 "Error - can't set architecture %s to create prelinked kernel.", 1127 archInfo->name); 1128 result = EX_OSERR; 1129 goto finish; 1130 } 1131 1132 /***** 1133 * Figure out which kexts we're actually archiving. 1134 * This uses toolArgs->allKexts, which must already be created. 1135 */ 1136 prelinkKexts = CFArrayCreateMutable(kCFAllocatorDefault, 0, 1137 &kCFTypeArrayCallBacks); 1138 if (!prelinkKexts) { 1139 OSKextLogMemError(); 1140 result = EX_OSERR; 1141 goto finish; 1142 } 1143 1144 result = filterKextsForCache(toolArgs, prelinkKexts, 1145 archInfo, &fatalOut); 1146 if (result != EX_OK || fatalOut) { 1147 goto finish; 1148 } 1149 1150 result = EX_OSERR; 1151 1152 if (!CFArrayGetCount(prelinkKexts)) { 1153 OSKextLog(/* kext */ NULL, 1154 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 1155 "Error - no kexts found for architecture %s.", 1156 archInfo->name); 1157 goto finish; 1158 } 1159 1160 /* Create the prelinked kernel from the given kernel and kexts */ 1161 1162 flags |= kOSKextKernelcacheKASLRFlag; 1163 flags |= kOSKextKernelcacheNeedAllFlag; 1164 flags |= kOSKextKernelcacheSkipAuthenticationFlag; 1165 flags |= kOSKextKernelcacheIncludeAllPersonalitiesFlag; 1166 1167 flags |= (toolArgs->stripSymbols) ? kOSKextKernelcacheStripSymbolsFlag : 0; 1168 flags |= (toolArgs->printTestResults) ? kOSKextKernelcachePrintDiagnosticsFlag : 0; 1169 1170 prelinkedKernel = OSKextCreatePrelinkedKernel(kernelImage, prelinkKexts, 1171 toolArgs->volumeRootURL, flags, prelinkedSymbolsOut); 1172 if (!prelinkedKernel) { 1173 OSKextLog(/* kext */ NULL, 1174 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 1175 "Error - failed to generate prelinked kernel."); 1176 result = EX_OSERR; 1177 goto finish; 1178 } 1179 1180 /* Log used bundle identifiers for B&I */ 1181 logUsedKexts(toolArgs, prelinkKexts); 1182 1183 /* Compress the prelinked kernel if needed */ 1184 1185 if (toolArgs->compress) { 1186 *prelinkedKernelOut = compressPrelinkedSlice(toolArgs->compressionType, 1187 prelinkedKernel, 1188 true); 1189 } else { 1190 *prelinkedKernelOut = CFRetain(prelinkedKernel); 1191 } 1192 1193 if (!*prelinkedKernelOut) { 1194 goto finish; 1195 } 1196 1197 result = EX_OK; 1198 1199finish: 1200 SAFE_RELEASE(kernelImage); 1201 SAFE_RELEASE(prelinkKexts); 1202 SAFE_RELEASE(prelinkedKernel); 1203 1204 return result; 1205} 1206 1207 1208/********************************************************************* 1209 *********************************************************************/ 1210ExitStatus compressPrelinkedKernel( 1211 const char * prelinkPath, 1212 Boolean compress, 1213 uint32_t compressionType) 1214{ 1215 ExitStatus result = EX_SOFTWARE; 1216 struct timeval prelinkedKernelTimes[2]; 1217 CFMutableArrayRef prelinkedSlices = NULL; // must release 1218 CFMutableArrayRef prelinkedArchs = NULL; // must release 1219 CFDataRef prelinkedSlice = NULL; // must release 1220 const NXArchInfo * archInfo = NULL; // do not free 1221 const u_char * sliceBytes = NULL; // do not free 1222 mode_t fileMode = 0; 1223 int i = 0; 1224 1225 result = readMachOSlices(prelinkPath, &prelinkedSlices, 1226 &prelinkedArchs, &fileMode, prelinkedKernelTimes); 1227 if (result != EX_OK) { 1228 goto finish; 1229 } 1230 1231 /* Compress/uncompress each slice of the prelinked kernel. 1232 */ 1233 1234 for (i = 0; i < CFArrayGetCount(prelinkedSlices); ++i) { 1235 1236 SAFE_RELEASE_NULL(prelinkedSlice); 1237 prelinkedSlice = CFArrayGetValueAtIndex(prelinkedSlices, i); 1238 1239 if (compress) { 1240 prelinkedSlice = compressPrelinkedSlice(compressionType, 1241 prelinkedSlice, 1242 true); 1243 if (!prelinkedSlice) { 1244 result = EX_DATAERR; 1245 goto finish; 1246 } 1247 } else { 1248 prelinkedSlice = uncompressPrelinkedSlice(prelinkedSlice); 1249 if (!prelinkedSlice) { 1250 result = EX_DATAERR; 1251 goto finish; 1252 } 1253 } 1254 1255 CFArraySetValueAtIndex(prelinkedSlices, i, prelinkedSlice); 1256 } 1257 SAFE_RELEASE_NULL(prelinkedSlice); 1258 1259 /* Snow Leopard prelinked kernels are not wrapped in a fat header, so we 1260 * have to decompress the prelinked kernel and look at the mach header 1261 * to get the architecture information. 1262 */ 1263 1264 if (!prelinkedArchs && CFArrayGetCount(prelinkedSlices) == 1) { 1265 if (!createCFMutableArray(&prelinkedArchs, NULL)) { 1266 OSKextLogMemError(); 1267 result = EX_OSERR; 1268 goto finish; 1269 } 1270 1271 sliceBytes = CFDataGetBytePtr(CFArrayGetValueAtIndex(prelinkedSlices, 0)); 1272 1273 archInfo = getThinHeaderPageArch(sliceBytes); 1274 if (archInfo) { 1275 CFArrayAppendValue(prelinkedArchs, archInfo); 1276 } else { 1277 SAFE_RELEASE_NULL(prelinkedArchs); 1278 } 1279 } 1280 1281 /* If we still don't have architecture information, then something 1282 * definitely went wrong. 1283 */ 1284 1285 if (!prelinkedArchs) { 1286 OSKextLog(/* kext */ NULL, 1287 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 1288 "Couldn't determine prelinked kernel's architecture"); 1289 result = EX_SOFTWARE; 1290 goto finish; 1291 } 1292 1293 result = writeFatFile(prelinkPath, prelinkedSlices, 1294 prelinkedArchs, fileMode, prelinkedKernelTimes); 1295 if (result != EX_OK) { 1296 goto finish; 1297 } 1298 1299 result = EX_OK; 1300 1301finish: 1302 SAFE_RELEASE(prelinkedSlices); 1303 SAFE_RELEASE(prelinkedArchs); 1304 SAFE_RELEASE(prelinkedSlice); 1305 1306 return result; 1307} 1308 1309 1310/******************************************************************************* 1311* usage() 1312*******************************************************************************/ 1313void usage(UsageLevel usageLevel) 1314{ 1315 fprintf(stderr, 1316 "usage: %1$s -prelinked-kernel <filename> [options] [--] [kext or directory]\n" 1317 "\n", 1318 progname); 1319 1320 if (usageLevel == kUsageLevelBrief) { 1321 fprintf(stderr, "use %s -%s for an explanation of each option\n", 1322 progname, kOptNameHelp); 1323 } 1324 1325 if (usageLevel == kUsageLevelBrief) { 1326 return; 1327 } 1328 1329 fprintf(stderr, "-%s <filename> (-%c):\n" 1330 " create/update prelinked kernel\n", 1331 kOptNamePrelinkedKernel, kOptPrelinkedKernel); 1332 1333 fprintf(stderr, "\n"); 1334 1335 fprintf(stderr, 1336 "kext or directory: Consider kext or all kexts in directory for inclusion\n"); 1337 fprintf(stderr, "-%s <bundle_id> (-%c):\n" 1338 " include the kext whose CFBundleIdentifier is <bundle_id>\n", 1339 kOptNameBundleIdentifier, kOptBundleIdentifier); 1340 fprintf(stderr, "-%s <bundle_id>:\n" 1341 " include the kext whose CFBundleIdentifier is <bundle_id> if it exists\n", 1342 kOptNameOptionalBundleIdentifier); 1343 fprintf(stderr, "-%s <kernel_filename> (-%c): Use kernel_filename for a prelinked kernel\n", 1344 kOptNameKernel, kOptKernel); 1345 1346 fprintf(stderr, "-%s <archname>:\n" 1347 " include architecture <archname> in created cache(s)\n", 1348 kOptNameArch); 1349 fprintf(stderr, "-%s <volume>:\n" 1350 " Save kext paths in the prelinked kernel relative to <volume>\n", 1351 kOptNameVolumeRoot); 1352 fprintf(stderr, "\n"); 1353 1354 fprintf(stderr, "-%s (-%c): quiet mode: print no informational or error messages\n", 1355 kOptNameQuiet, kOptQuiet); 1356 fprintf(stderr, "-%s [ 0-6 | 0x<flags> ] (-%c):\n" 1357 " verbose mode; print info about analysis & loading\n", 1358 kOptNameVerbose, kOptVerbose); 1359 fprintf(stderr, "\n"); 1360 1361 fprintf(stderr, "-%s (-%c):\n" 1362 " print diagnostics for kexts with problems\n", 1363 kOptNameTests, kOptTests); 1364 1365 fprintf(stderr, "-%s (-%c): print this message and exit\n", 1366 kOptNameHelp, kOptHelp); 1367 1368 return; 1369} 1370