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