1/*
2 * Copyright (c) 2006 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
25#include <IOKit/kext/OSKext.h>
26#include <IOKit/kext/OSKextPrivate.h>
27
28#include "kextlibs_main.h"
29#include "kext_tools_util.h"
30
31
32/*******************************************************************************
33* Local function prototypes and misc grotty  bits.
34*******************************************************************************/
35const char * progname = "(unknown)";
36
37/*******************************************************************************
38*******************************************************************************/
39int main(int argc, char * const * argv)
40{
41    ExitStatus          result              = EX_OSERR;
42    ExitStatus          printResult         = EX_OSERR;
43
44    KextlibsArgs        toolArgs;
45    CFArrayRef          kexts               = NULL;  // must release
46
47    const NXArchInfo ** arches              = NULL;  // must free
48    KextlibsInfo      * libInfo             = NULL;  // must release contents & free
49    Boolean             libsAreArchSpecific = FALSE;
50
51    CFIndex             count, i;
52    CFIndex             numArches;
53
54   /*****
55    * Find out what the program was invoked as.
56    */
57    progname = rindex(argv[0], '/');
58    if (progname) {
59        progname++;   // go past the '/'
60    } else {
61        progname = (char *)argv[0];
62    }
63
64   /* Set the OSKext log callback right away.
65    */
66    OSKextSetLogOutputFunction(&tool_log);
67
68    result = readArgs(argc, argv, &toolArgs);
69    if (result != EX_OK) {
70        if (result == kKextlibsExitHelp) {
71            result = EX_OK;
72        }
73        goto finish;
74    }
75
76    result = EX_OSERR;
77
78   /*****
79    * If necessary or requested, add the extensions folders to the
80    * BEGINNING of the list of folders.
81    */
82    count = CFArrayGetCount(toolArgs.repositoryURLs);
83    if (!count || toolArgs.flagSysKexts) {
84        CFArrayRef osExtFolders = OSKextGetSystemExtensionsFolderURLs(); // do not release
85
86        if (!osExtFolders) {
87            OSKextLog(/* kext */ NULL,
88                kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
89                "Library error - can't get system extensions folders.");
90            goto finish;
91        }
92
93        count = CFArrayGetCount(osExtFolders);
94        for (i = 0; i < count; i++) {
95            CFTypeRef osExtFolder = CFArrayGetValueAtIndex(osExtFolders, i);
96            CFIndex   folderIndex = CFArrayGetFirstIndexOfValue(
97                toolArgs.repositoryURLs, RANGE_ALL(toolArgs.repositoryURLs),
98                osExtFolder);
99            if (folderIndex == kCFNotFound) {
100                CFArrayInsertValueAtIndex(toolArgs.repositoryURLs, i,
101                    osExtFolder);
102            }
103        }
104    }
105
106    kexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault,
107        toolArgs.repositoryURLs);
108    if (!kexts) {
109        OSKextLog(/* kext */ NULL,
110            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
111            "Can't read kexts from folders.");
112        goto finish;
113    }
114
115    toolArgs.kextURL = CFURLCreateFromFileSystemRepresentation(
116        kCFAllocatorDefault, (u_char *)toolArgs.kextName,
117        strlen(toolArgs.kextName), /* isDirectory */ true);
118    if (!toolArgs.kextURL) {
119        OSKextLogStringError(/* kext */ NULL);
120        goto finish;
121    }
122    toolArgs.theKext = OSKextCreate(kCFAllocatorDefault,toolArgs. kextURL);
123    if (!toolArgs.theKext) {
124        OSKextLog(/* kext */ NULL,
125            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
126            "Can't open %s.", toolArgs.kextName);
127        goto finish;
128    }
129
130   /* A codeless kext is either a library redirect,
131    * so we can't advise, or it doesn't need any libraries!
132    */
133    if (!OSKextDeclaresExecutable(toolArgs.theKext)) {
134        if (OSKextIsLibrary(toolArgs.theKext)) {
135            OSKextLog(/* kext */ NULL,
136                kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
137                "%s is a library without an executable; "
138                "defining its OSBundleLibraries (if any) is up to you.",
139                toolArgs.kextName);
140        } else {
141            CFDictionaryRef libs = OSKextGetValueForInfoDictionaryKey(toolArgs.theKext,
142                CFSTR(kOSBundleLibrariesKey));
143
144            if (libs &&
145                CFDictionaryGetTypeID() == CFGetTypeID(libs) &&
146                CFDictionaryGetCount(libs)) {
147
148                OSKextLog(/* kext */ NULL,
149                    kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
150                    "%s has no executable and should not declare OSBundleLibraries.",
151                    toolArgs.kextName);
152            } else {
153
154               /* In this one case, the exit status will be EX_OK.
155                */
156                OSKextLog(/* kext */ NULL,
157                    kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
158                    "%s has no executable and does not need to declare OSBundleLibraries.",
159                    toolArgs.kextName);
160                result = EX_OK;
161            }
162        }
163
164        goto finish;
165    }
166
167    arches = OSKextCopyArchitectures(toolArgs.theKext);
168    if (!arches || !arches[0]) {
169        OSKextLog(/* kext */ NULL,
170            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
171            "Can't determine architectures of %s.",
172            toolArgs.kextName);
173        goto finish;
174    }
175
176    result = EX_OK;
177
178    for (numArches = 0; arches[numArches]; numArches++) {
179        /* just counting */
180    }
181
182    libInfo = (KextlibsInfo *)malloc(numArches * sizeof(KextlibsInfo));
183    if (!libInfo) {
184        OSKextLogMemError();
185        goto finish;
186    }
187
188   /* Find libraries for the kext for each architecture in the kext.
189    */
190    for (i = 0; i < numArches; i++) {
191        OSKextSetArchitecture(arches[i]);
192
193        libInfo[i].libKexts = OSKextFindLinkDependencies(toolArgs.theKext,
194            toolArgs.flagNonKPI, toolArgs.flagAllowUnsupported,
195            &libInfo[i].undefSymbols, &libInfo[i].onedefSymbols,
196            &libInfo[i].multdefSymbols, &libInfo[i].multdefLibs);
197
198        if (!libInfo[i].libKexts) {
199            OSKextLogMemError();
200            result = EX_OSERR;
201            goto finish;
202        }
203    }
204
205   /* If there's more than 1 arch, see if we have to print arch-specific
206    * results.
207    */
208    if (numArches >= 2) {
209        for (i = 0; i < numArches - 1; i++) {
210            if (!CFEqual(libInfo[i].libKexts, libInfo[i+1].libKexts)) {
211                libsAreArchSpecific = TRUE;
212                break;
213            }
214        }
215    }
216
217   /* If all the libs are the same for all arches, then just print them
218    * once at the top of the output. Otherwise, only when doing XML,
219    * print the arch-specific XML declarations before the diagnostics.
220    */
221    if (!libsAreArchSpecific) {
222        printResult = printLibs(&toolArgs, NULL, libInfo[0].libKexts,
223            /* extraNewline? */ TRUE);
224
225       /* Higher exit statuses always win.
226        */
227        if (printResult > result) {
228            result = printResult;
229        }
230    } else if (toolArgs.flagXML) {
231
232        for (i = 0; i < numArches; i++) {
233            printResult = printLibs(&toolArgs, arches[i],
234                libInfo[i].libKexts,
235                /* extraNewline? */ i + 1 == numArches);
236            if (printResult > result) {
237                result = printResult;
238            }
239        }
240    }
241
242   /* Down here, for each arch, print arch-specific non-XML library declarations,
243    * followed by any problems found for that arch.
244    */
245    for (i = 0; i < numArches; i++) {
246
247        if (libsAreArchSpecific && !toolArgs.flagXML) {
248            printResult = printLibs(&toolArgs, arches[i], libInfo[i].libKexts,
249                /* extraNewline? */ i + 1 == numArches);
250            if (printResult > result) {
251                result = printResult;
252            }
253        }
254
255        printResult = printProblems(&toolArgs, arches[i],
256            libInfo[i].undefSymbols, libInfo[i].onedefSymbols,
257            libInfo[i].multdefSymbols, libInfo[i].multdefLibs,
258            /* printArchFlag */ !libsAreArchSpecific || toolArgs.flagXML,
259            /* extraNewline? */ i + i < numArches);
260
261        if (printResult != EX_OK) {
262           /* Higher exit statuses always win.
263            */
264            if (printResult > result) {
265                result = printResult;
266            }
267        }
268    }
269
270
271finish:
272
273   /* Theoretically, we could exit w/o cleaning up.  However, 12569152
274      points out that while clang should know exit() cleans up, its
275      analyzer *should* treat CF objects differently: releasing them might
276      have (important, expected) side effects.  So we just clean up
277      everything and return from main, letting the runtime exit(result).
278    */
279
280
281    SAFE_FREE(arches);
282
283    SAFE_RELEASE(toolArgs.repositoryURLs);
284    SAFE_RELEASE(toolArgs.kextURL);
285    SAFE_RELEASE(toolArgs.theKext);
286    SAFE_RELEASE(kexts);        // this is the one clang unexpectedly noticed
287
288    if (libInfo) {
289        for (i = 0; i < numArches; i++) {
290            SAFE_RELEASE_NULL(libInfo[i].libKexts);
291            SAFE_RELEASE_NULL(libInfo[i].undefSymbols);
292            SAFE_RELEASE_NULL(libInfo[i].onedefSymbols);
293            SAFE_RELEASE_NULL(libInfo[i].multdefSymbols);
294            SAFE_RELEASE_NULL(libInfo[i].multdefLibs);
295        }
296        free(libInfo);
297    }
298    return result;
299}
300
301/*******************************************************************************
302*******************************************************************************/
303ExitStatus readArgs(
304    int            argc,
305    char * const * argv,
306    KextlibsArgs * toolArgs)
307{
308    ExitStatus  result         = EX_USAGE;
309    ExitStatus  scratchResult  = EX_USAGE;
310    int         optChar        = 0;
311
312    bzero(toolArgs, sizeof(*toolArgs));
313
314   /*****
315    * Allocate collection objects needed for command line argument processing.
316    */
317    toolArgs->repositoryURLs = CFArrayCreateMutable(kCFAllocatorDefault, 0,
318        &kCFTypeArrayCallBacks);
319    if (!toolArgs->repositoryURLs) {
320        OSKextLogMemError();
321        result = EX_OSERR;
322        goto finish;
323    }
324
325   /*****
326    * Process command-line arguments.
327    */
328    result = EX_USAGE;
329
330    while ((optChar = getopt_long_only(argc, argv, kOptChars,
331        sOptInfo, NULL)) != -1) {
332
333        switch (optChar) {
334
335            case kOptHelp:
336                usage(kUsageLevelFull);
337                result = kKextlibsExitHelp;
338                goto finish;
339                break;
340
341            case kOptRepository:
342                scratchResult = addRepository(toolArgs, optarg);
343                if (scratchResult != EX_OK) {
344                    result = scratchResult;
345                    goto finish;
346                }
347                break;
348
349            case kOptCompatible:
350                toolArgs->flagCompatible = true;
351                break;
352
353            case kOptSystemExtensions:
354                toolArgs->flagSysKexts = true;
355                break;
356
357            case kOptQuiet:
358                beQuiet();
359                break;
360
361            case kOptVerbose:
362                scratchResult = setLogFilterForOpt(argc, argv, /* forceOnFlags */ 0);
363                if (scratchResult != EX_OK) {
364                    result = scratchResult;
365                    goto finish;
366                }
367                break;
368
369            case 0:
370                switch (longopt) {
371                    case kLongOptXML:
372                        toolArgs->flagXML = true;
373                        break;
374
375                    case kLongOptAllSymbols:
376                        toolArgs->flagPrintUndefSymbols = true;
377                        toolArgs->flagPrintOnedefSymbols = true;
378                        toolArgs->flagPrintMultdefSymbols = true;
379                        break;
380
381                    case kLongOptUndefSymbols:
382                        toolArgs->flagPrintUndefSymbols = true;
383                        break;
384
385                    case kLongOptOnedefSymbols:
386                        toolArgs->flagPrintOnedefSymbols = true;
387                        break;
388
389                    case kLongOptMultdefSymbols:
390                        toolArgs->flagPrintMultdefSymbols = true;
391                        break;
392
393                    case kLongOptNonKPI:
394                        toolArgs->flagNonKPI = true;
395                        break;
396
397                    case kLongOptUnsupported:
398                        toolArgs->flagAllowUnsupported = true;
399                        break;
400
401                }
402                break;
403
404            default:
405                usage(kUsageLevelBrief);
406                goto finish;
407                break;
408            }
409    }
410
411    argc -= optind;
412    argv += optind;
413
414    if (!argv[0]) {
415        fprintf(stderr, "No kext specified.");
416        usage(kUsageLevelBrief);
417        goto finish;
418    }
419
420    scratchResult = checkPath(argv[0], kOSKextBundleExtension,
421        /* directoryRequired */ TRUE, /* writableRequired */ FALSE);
422    if (scratchResult != EX_OK) {
423        result = scratchResult;
424        goto finish;
425    }
426    toolArgs->kextName = argv[0];
427
428    argc--;
429    argv++;
430
431    if (argc) {
432        OSKextLog(/* kext */ NULL,
433            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
434            "Too many arguments starting at '%s'.", argv[0]);
435        usage(kUsageLevelBrief);
436        goto finish;
437    }
438
439    result = EX_OK;
440finish:
441    return result;
442}
443
444/*******************************************************************************
445*******************************************************************************/
446ExitStatus addRepository(
447    KextlibsArgs * toolArgs,
448    const char   * path)
449{
450    ExitStatus  result = EX_OSERR;
451    CFURLRef    url    = NULL;   // must release
452
453    result = checkPath(path, /* suffix */ NULL,
454        /* dirRequired? */ TRUE, /* writableRequired? */ FALSE);
455    if (result != EX_OK) {
456        goto finish;
457    }
458
459    url = CFURLCreateFromFileSystemRepresentation(
460        kCFAllocatorDefault, (const UInt8 *)path, strlen(path), true);
461    if (!url) {
462        OSKextLog(/* kext */ NULL,
463            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
464            "Can't create CFURL for '%s'.", path);
465        goto finish;
466    }
467    addToArrayIfAbsent(toolArgs->repositoryURLs, url);
468
469    result = EX_OK;
470finish:
471    SAFE_RELEASE(url);
472    return result;
473}
474
475/*******************************************************************************
476* This function prints to stdout, as it represents the only real output of
477* the program and we want to be able to pipe it into pbcopy.
478*******************************************************************************/
479ExitStatus printLibs(
480    KextlibsArgs     * toolArgs,
481    const NXArchInfo * arch,
482    CFArrayRef         libKexts,
483    Boolean            trailingNewlineFlag)
484{
485    ExitStatus         result        = EX_OSERR;
486    const NXArchInfo * genericArch   = NULL;  // do not free
487    char             * libIdentifier = NULL;  // must free
488    CFIndex            count, i;
489
490   /* Get the generic architecture name for the arch, except for PPC,
491    * which we no longer support.
492    */
493    if (arch && arch->cputype != CPU_TYPE_POWERPC) {
494        genericArch = NXGetArchInfoFromCpuType(arch->cputype,
495            CPU_SUBTYPE_MULTIPLE);
496        if (!genericArch) {
497            OSKextLog(/* kext */ NULL,
498                kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
499                "Can't find generic NXArchInfo for %s.",
500                arch->name);
501            goto finish;
502        }
503    }
504
505    count = CFArrayGetCount(libKexts);
506
507   /* For XML output, don't print anything if we found no libraries;
508    * an empty OSBundleLibraries might look like good output to somebody.
509    */
510    if (toolArgs->flagXML) {
511        if (count) {
512            fprintf(stdout, "\t<key>OSBundleLibraries%s%s</key>\n",
513                genericArch ? "_" : "",
514                genericArch ? genericArch->name : "");
515            fprintf(stdout, "\t<dict>\n");
516        }
517    } else {
518        if (arch) {
519             fprintf(stderr, "For %s:\n", arch->name);
520        } else {
521             fprintf(stderr, "For all architectures:\n");
522        }
523        if (!count) {
524            fprintf(stdout, "    No libraries found.\n");
525        }
526    }
527    for (i = 0; i < count; i++) {
528        OSKextRef      libKext = (OSKextRef)CFArrayGetValueAtIndex(libKexts, i);
529        OSKextVersion  version;
530        char           versCString[kOSKextVersionMaxLength];
531
532        SAFE_FREE_NULL(libIdentifier);
533
534        libIdentifier = createUTF8CStringForCFString(OSKextGetIdentifier(libKext));
535
536        if (toolArgs->flagCompatible) {
537            version = OSKextGetCompatibleVersion(libKext);
538        } else {
539            version = OSKextGetVersion(libKext);
540        }
541
542        if (libIdentifier && version > kOSKextVersionUndefined) {
543            OSKextVersionGetString(version, versCString, sizeof(versCString));
544
545            if (toolArgs->flagXML) {
546                fprintf(stdout, "\t\t<key>%s</key>\n", libIdentifier);
547                fprintf(stdout, "\t\t<string>%s</string>\n", versCString);
548            } else {
549                fprintf(stdout, "    %s = %s\n", libIdentifier, versCString);
550            }
551        } else {
552            OSKextLog(/* kext */ NULL,
553                kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
554                "Internal error generating library list.");
555            goto finish;
556        }
557    }
558    if (count && toolArgs->flagXML) {
559        fprintf(stdout, "\t</dict>\n");
560    }
561
562    if (trailingNewlineFlag) {
563        fprintf(stderr, "\n");
564    }
565
566    result = kKextlibsExitOK;
567finish:
568    SAFE_FREE(libIdentifier);
569    return result;
570}
571
572/*******************************************************************************
573*******************************************************************************/
574ExitStatus printProblems(
575    KextlibsArgs     * toolArgs,
576    const NXArchInfo * arch,
577    CFDictionaryRef    undefSymbols,
578    CFDictionaryRef    onedefSymbols,
579    CFDictionaryRef    multdefSymbols,
580    CFArrayRef         multdefLibs,
581    Boolean            printArchFlag,
582    Boolean            trailingNewlineFlag)
583{
584    ExitStatus         result              = EX_OSERR;
585    CFIndex            undefCount, onedefCount, multdefCount;
586    CFIndex            count, i;
587
588    onedefCount  = CFDictionaryGetCount(onedefSymbols);
589    undefCount   = CFDictionaryGetCount(undefSymbols);
590    multdefCount = CFDictionaryGetCount(multdefSymbols);
591
592    if (!toolArgs->flagPrintOnedefSymbols && !undefCount && !multdefCount) {
593        result = kKextlibsExitOK;
594        goto finish;
595    }
596
597    if (printArchFlag) {
598        fprintf(stderr, "For %s:\n", arch->name);
599    }
600
601    if (toolArgs->flagPrintOnedefSymbols) {
602        fprintf(stderr, "    %ld symbol%s found in one library kext each%s\n",
603            onedefCount,
604            onedefCount > 1 ? "s" : "",
605            onedefCount && toolArgs->flagPrintOnedefSymbols ? ":" : ".");
606        CFDictionaryApplyFunction(onedefSymbols, printOnedefSymbol,
607            &(toolArgs->flagCompatible));
608    }
609
610    if (undefCount) {
611        fprintf(stderr, "    %ld symbol%s not found in any library kext%s\n",
612            undefCount,
613            undefCount > 1 ? "s" : "",
614            toolArgs->flagPrintUndefSymbols ? ":" : ".");
615        if (toolArgs->flagPrintUndefSymbols) {
616            CFDictionaryApplyFunction(undefSymbols, printUndefSymbol, NULL);
617        }
618        result = kKextlibsExitUndefineds;
619    }
620    if (multdefCount) {
621        if (toolArgs->flagPrintMultdefSymbols) {
622            fprintf(stderr, "    %ld symbol%s found in more than one library kext:\n",
623                multdefCount,
624                multdefCount > 1 ? "s" : "");
625            CFDictionaryApplyFunction(multdefSymbols, printMultdefSymbol,
626                &(toolArgs->flagCompatible));
627        } else {
628            count = CFArrayGetCount(multdefLibs);
629            fprintf(stderr, "    Multiple symbols found among %ld libraries:\n",
630                count);
631            for (i = 0; i < count; i++) {
632                OSKextRef lib = (OSKextRef)CFArrayGetValueAtIndex(multdefLibs, i);
633                char * name = NULL; // must free
634                name = createUTF8CStringForCFString(OSKextGetIdentifier(lib));
635                if (name) {
636                    fprintf(stderr, "\t%s\n", name);
637                }
638                SAFE_FREE(name);
639            }
640        }
641        result = kKextlibsExitMultiples;
642    }
643
644    if (trailingNewlineFlag) {
645        fprintf(stderr, "\n");
646    }
647
648    if (undefCount || multdefCount) {
649        goto finish;
650    }
651
652    result = kKextlibsExitOK;
653
654finish:
655    return result;
656}
657
658/*******************************************************************************
659*
660*******************************************************************************/
661void printUndefSymbol(const void * key,
662                      const void * value __unused, void * context __unused)
663{
664    char * cSymbol = NULL; // must free
665
666    cSymbol = createUTF8CStringForCFString((CFStringRef)key);
667    if (cSymbol) {
668        fprintf(stderr, "\t%s\n", cSymbol);
669    }
670    SAFE_FREE(cSymbol);
671    return;
672}
673
674/*******************************************************************************
675*
676*******************************************************************************/
677void printOnedefSymbol(const void * key, const void * value, void * context)
678{
679    Boolean       flagCompatible  = *(Boolean *)context;
680    OSKextRef     libKext         = (OSKextRef)value;
681    char        * cSymbol         = NULL; // must free
682    char        * libVers         = NULL; // must free
683    CFURLRef      libKextURL;
684    char          libKextName[PATH_MAX];
685    CFStringRef   libVersString = NULL; // do not release
686
687    cSymbol = createUTF8CStringForCFString((CFStringRef)key);
688    if (!cSymbol) {
689        OSKextLogStringError(/* kext */ NULL);
690        goto finish;
691    }
692
693    libKextURL = OSKextGetURL(libKext);
694    if (!CFURLGetFileSystemRepresentation(libKextURL,
695        /* resolveToBase */ false,
696        (u_char *)libKextName, PATH_MAX)) {
697
698        OSKextLogStringError(/* kext */ NULL);
699        goto finish;
700    }
701    if (flagCompatible) {
702        libVersString = OSKextGetValueForInfoDictionaryKey(libKext,
703            CFSTR("OSBundleCompatibleVersion"));
704    } else {
705        libVersString = OSKextGetValueForInfoDictionaryKey(libKext,
706            kCFBundleVersionKey);
707    }
708
709    libVers = createUTF8CStringForCFString(libVersString);
710    if (!libVers) {
711        OSKextLogStringError(/* kext */ NULL);
712        goto finish;
713    }
714
715    fprintf(stderr, "    %s in %s (%s%s)\n", cSymbol, libKextName,
716        flagCompatible ? "compatible version " : "", libVers);
717
718finish:
719    SAFE_FREE(cSymbol);
720    SAFE_FREE(libVers);
721
722    return;
723}
724
725/*******************************************************************************
726*
727*******************************************************************************/
728void printMultdefSymbol(const void * key, const void * value, void * context)
729{
730    char        * cSymbol         = NULL; // must free
731    char        * libVers         = NULL; // must free
732    CFArrayRef   libs = (CFArrayRef)value;
733    Boolean      flagCompatible   = *(Boolean *)context;
734    CFIndex      count, i;
735
736    cSymbol = createUTF8CStringForCFString((CFStringRef)key);
737    if (cSymbol) {
738        fprintf(stderr, "    %s: in\n", cSymbol);
739    }
740
741    count = CFArrayGetCount(libs);
742    for (i = 0; i < count; i++) {
743        OSKextRef     libKext = (OSKextRef)CFArrayGetValueAtIndex(libs, i);
744        CFURLRef      libKextURL;
745        char          libKextName[PATH_MAX];
746        CFStringRef   libVersString = NULL; // do not release
747
748        SAFE_FREE_NULL(libVers);
749
750        libKextURL = OSKextGetURL(libKext);
751        if (!CFURLGetFileSystemRepresentation(libKextURL,
752            /* resolveToBase */ true,
753            (u_char *)libKextName, PATH_MAX)) {
754
755            fprintf(stderr, "string/url conversion error\n");
756            goto finish;
757        }
758        if (flagCompatible) {
759            libVersString = OSKextGetValueForInfoDictionaryKey(libKext,
760                CFSTR("OSBundleCompatibleVersion"));
761        } else {
762            libVersString = OSKextGetValueForInfoDictionaryKey(libKext,
763                kCFBundleVersionKey);
764        }
765
766        libVers = createUTF8CStringForCFString(libVersString);
767        if (!libVers) {
768            fprintf(stderr, "string/url conversion error\n");
769            goto finish;
770        }
771        fprintf(stderr, "        %s (%s%s)\n", libKextName,
772            flagCompatible ? "compatible version " : "", libVers);
773    }
774
775finish:
776    SAFE_FREE(cSymbol);
777    SAFE_FREE(libVers);
778
779    return;
780}
781
782/*******************************************************************************
783* usage()
784*******************************************************************************/
785static void usage(UsageLevel usageLevel)
786{
787    fprintf(stderr, "usage: %s [options] kext\n", progname);
788
789    if (usageLevel == kUsageLevelBrief) {
790        fprintf(stderr, "\nuse %s -%s for a list of options\n",
791            progname, kOptNameHelp);
792        return;
793    }
794
795    fprintf(stderr, "\n");
796
797    // extra newline for spacing
798    fprintf(stderr, "<kext>: the kext to find libraries for\n");
799
800    fprintf(stderr, "-%s <arch>:\n", kOptNameArch);
801    fprintf(stderr, "        resolve for architecture <arch> instead of running kernel's\n");
802    fprintf(stderr, "-%s:   print XML fragment suitable for pasting\n", kOptNameXML);
803
804     // fake out compiler for blank line
805     fprintf(stderr, "\n");
806
807    fprintf(stderr, "-%s (-%c):\n",
808        kOptNameSystemExtensions, kOptSystemExtensions);
809    fprintf(stderr,  "        look in the system exensions folder (assumed if no other folders\n"
810        "        specified with %s)\n",kOptNameRepository);
811    fprintf(stderr, "-%s <directory> (-%c):\n", kOptNameRepository, kOptRepository);
812    fprintf(stderr, "        look in <directory> for library kexts\n");
813
814    fprintf(stderr, "%s", "\n");
815
816    fprintf(stderr, "-%s:\n", kOptNameAllSymbols);
817    fprintf(stderr, "        list all symbols, found, not found, or found more than once\n");
818    fprintf(stderr, "-%s:\n", kOptNameOnedefSymbols);
819    fprintf(stderr, "        list all symbols found with the library kext they were found in\n");
820    fprintf(stderr, "-%s:\n", kOptNameUndefSymbols);
821    fprintf(stderr, "        list all symbols not found in any library\n");
822    fprintf(stderr, "-%s:\n", kOptNameMultdefSymbols);
823    fprintf(stderr, "        list all symbols found more than once with their library kexts\n");
824
825    fprintf(stderr, "%s", "\n");
826
827    fprintf(stderr, "-%s (-%c):\n", kOptNameCompatible, kOptCompatible);
828    fprintf(stderr, "        use library kext compatble versions rather than current versions\n");
829    fprintf(stderr, "-%s:\n", kOptNameUnsupported);
830    fprintf(stderr, "        look in unsupported kexts for symbols\n");
831    return;
832}
833