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