1/*
2 * Copyright (c) 2006, 2014 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 <IOKit/kext/OSKext.h>
24#include <IOKit/kext/OSKextPrivate.h>
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <unistd.h>
29
30#include <sys/types.h>
31#include <sys/param.h>
32
33#include <mach/mach.h>
34#include <mach/mach_error.h>
35#include <mach/mach_host.h>
36
37#include "kextstat_main.h"
38
39// not a utility.[ch] customer yet
40static const char * progname = "(unknown)";
41
42void printPList_new(FILE * stream, CFTypeRef aPlist);
43/*******************************************************************************
44*
45*******************************************************************************/
46ExitStatus main(int argc, char * const * argv)
47{
48    ExitStatus          result        = EX_OK;
49    KextstatArgs        toolArgs;
50    CFDictionaryRef   * kextInfoList  = NULL;  // must free
51    CFIndex             count, i;
52
53    if (argv[0]) {
54        progname = argv[0];
55    }
56
57   /* Set the OSKext log callback right away.
58    */
59    OSKextSetLogOutputFunction(&tool_log);
60
61    result = readArgs(argc, argv, &toolArgs);
62    if (result != EX_OK) {
63        if (result == kKextstatExitHelp) {
64            result = EX_OK;
65        }
66        goto finish;
67    }
68
69    toolArgs.runningKernelArch = OSKextGetRunningKernelArchitecture();
70    if (!toolArgs.runningKernelArch) {
71        result = EX_OSERR;
72        goto finish;
73    }
74
75    toolArgs.loadedKextInfo = OSKextCopyLoadedKextInfo(toolArgs.bundleIDs,
76        NULL /* all info */);
77
78    if (!toolArgs.loadedKextInfo) {
79        OSKextLog(/* kext */ NULL,
80            kOSKextLogErrorLevel | kOSKextLogGeneralFlag | kOSKextLogIPCFlag,
81            "Couldn't get list of loaded kexts from kernel.");
82        result = EX_OSERR;
83        goto finish;
84    }
85
86    if (!toolArgs.flagListOnly) {
87        printf("Index Refs Address    ");
88        if (toolArgs.runningKernelArch->cputype & CPU_ARCH_ABI64) {
89            printf("        ");
90        }
91        printf("Size       Wired      ");
92        if (toolArgs.flagShowArchitecture) {
93            printf("Architecture       ");
94        }
95        printf("Name (Version) <Linked Against>\n");
96    }
97
98    count = CFDictionaryGetCount(toolArgs.loadedKextInfo);
99    if (!count) {
100        goto finish;
101    }
102
103    kextInfoList = (CFDictionaryRef *)malloc(count * sizeof(CFDictionaryRef));
104    if (!kextInfoList) {
105        OSKextLogMemError();
106        result = EX_OSERR;
107        goto finish;
108    }
109
110    CFDictionaryGetKeysAndValues(toolArgs.loadedKextInfo, /* keys */ NULL,
111        (const void **)kextInfoList);
112    qsort(kextInfoList, count, sizeof(CFDictionaryRef), &compareKextInfo);
113    for (i = 0; i < count; i++) {
114        printKextInfo(kextInfoList[i], &toolArgs);
115    }
116
117finish:
118    exit(result);
119
120    SAFE_FREE(kextInfoList);
121
122    return result;
123}
124
125/*******************************************************************************
126*******************************************************************************/
127ExitStatus readArgs(int argc, char * const * argv, KextstatArgs * toolArgs)
128{
129    ExitStatus   result        = EX_USAGE;
130    CFStringRef  scratchString = NULL;  // must release
131    int          optChar       = 0;
132
133    bzero(toolArgs, sizeof(*toolArgs));
134
135   /*****
136    * Allocate collection objects needed for command line argument processing.
137    */
138    if (!createCFMutableArray(&toolArgs->bundleIDs, &kCFTypeArrayCallBacks)) {
139        goto finish;
140    }
141
142   /*****
143    * Process command-line arguments.
144    */
145    result = EX_USAGE;
146
147    while ((optChar = getopt_long_only(argc, argv, kOptChars,
148        sOptInfo, NULL)) != -1) {
149
150        SAFE_RELEASE_NULL(scratchString);
151
152        switch (optChar) {
153
154            case kOptHelp:
155                usage(kUsageLevelFull);
156                result = kKextstatExitHelp;
157                goto finish;
158                break;
159
160            case kOptNoKernelComponents:
161                toolArgs->flagNoKernelComponents = true;
162                break;
163
164            case kOptListOnly:
165                toolArgs->flagListOnly = true;
166                break;
167
168            case kOptBundleIdentifier:
169                scratchString = CFStringCreateWithCString(kCFAllocatorDefault,
170                    optarg, kCFStringEncodingUTF8);
171                if (!scratchString) {
172                    OSKextLogMemError();
173                    result = EX_OSERR;
174                    goto finish;
175                }
176                CFArrayAppendValue(toolArgs->bundleIDs, scratchString);
177                break;
178
179            case kOptArchitecture:
180                toolArgs->flagShowArchitecture = true;
181                break;
182
183            }
184    }
185
186    argc -= optind;
187    argv += optind;
188
189    if (argc) {
190        OSKextLog(/* kext */ NULL,
191            kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
192            "Extra arguments starting at %s....", argv[0]);
193        usage(kUsageLevelBrief);
194        goto finish;
195    }
196
197    result = EX_OK;
198
199finish:
200    SAFE_RELEASE_NULL(scratchString);
201
202    if (result == EX_USAGE) {
203        usage(kUsageLevelBrief);
204    }
205    return result;
206}
207
208/*******************************************************************************
209*******************************************************************************/
210#define kStringInvalidShort    "??"
211#define kStringInvalidLong   "????"
212
213void printKextInfo(CFDictionaryRef kextInfo, KextstatArgs * toolArgs)
214{
215    CFBooleanRef      isKernelComponent      = NULL;  // do not release
216    CFNumberRef       loadTag                = NULL;  // do not release
217    CFNumberRef       retainCount            = NULL;  // do not release
218    CFNumberRef       loadAddress            = NULL;  // do not release
219    CFNumberRef       loadSize               = NULL;  // do not release
220    CFNumberRef       wiredSize              = NULL;  // do not release
221    CFStringRef       bundleID               = NULL;  // do not release
222    CFStringRef       bundleVersion          = NULL;  // do not release
223    CFArrayRef        dependencyLoadTags     = NULL;  // do not release
224    CFMutableArrayRef sortedLoadTags         = NULL;  // must release
225
226    uint32_t          loadTagValue           = kOSKextInvalidLoadTag;
227    uint32_t          retainCountValue       = (uint32_t)-1;
228    uint64_t          loadAddressValue       = (uint64_t)-1;
229    uint32_t          loadSizeValue          = (uint32_t)-1;
230    uint32_t          wiredSizeValue         = (uint32_t)-1;
231    uint32_t          cpuTypeValue           = (uint32_t)-1;
232    uint32_t          cpuSubTypeValue        = (uint32_t)-1;
233    char            * bundleIDCString        = NULL;  // must free
234    char            * bundleVersionCString   = NULL;  // must free
235
236    CFIndex           count, i;
237
238    loadTag = (CFNumberRef)CFDictionaryGetValue(kextInfo,
239        CFSTR(kOSBundleLoadTagKey));
240    retainCount = (CFNumberRef)CFDictionaryGetValue(kextInfo,
241        CFSTR(kOSBundleRetainCountKey));
242    loadAddress = (CFNumberRef)CFDictionaryGetValue(kextInfo,
243        CFSTR(kOSBundleLoadAddressKey));
244    loadSize = (CFNumberRef)CFDictionaryGetValue(kextInfo,
245        CFSTR(kOSBundleLoadSizeKey));
246    wiredSize = (CFNumberRef)CFDictionaryGetValue(kextInfo,
247        CFSTR(kOSBundleWiredSizeKey));
248    bundleID = (CFStringRef)CFDictionaryGetValue(kextInfo,
249        kCFBundleIdentifierKey);
250    bundleVersion = (CFStringRef)CFDictionaryGetValue(kextInfo,
251        kCFBundleVersionKey);
252    dependencyLoadTags = (CFArrayRef)CFDictionaryGetValue(kextInfo,
253        CFSTR(kOSBundleDependenciesKey));
254
255   /* If the -k flag was given, skip any kernel components unless
256    * they are explicitly requested.
257    */
258    if (toolArgs->flagNoKernelComponents) {
259        isKernelComponent = (CFBooleanRef)CFDictionaryGetValue(kextInfo,
260            CFSTR(kOSKernelResourceKey));
261        if (isKernelComponent && CFBooleanGetValue(isKernelComponent)) {
262            if (bundleID &&
263                kCFNotFound == CFArrayGetFirstIndexOfValue(toolArgs->bundleIDs,
264                    RANGE_ALL(toolArgs->bundleIDs), bundleID)) {
265
266                goto finish;
267            }
268        }
269    }
270
271    if (!getNumValue(loadTag, kCFNumberSInt32Type, &loadTagValue)) {
272        loadTagValue = kOSKextInvalidLoadTag;
273    }
274
275   /* Never print the info for the kernel (loadTag 0, id __kernel__).
276    */
277    if (loadTagValue == 0) {
278        goto finish;
279    }
280
281    if (!getNumValue(retainCount, kCFNumberSInt32Type, &retainCountValue)) {
282        retainCountValue = (uint32_t)-1;
283    }
284    if (!getNumValue(loadAddress, kCFNumberSInt64Type, &loadAddressValue)) {
285        loadAddressValue = (uint64_t)-1;
286    }
287    if (!getNumValue(loadSize, kCFNumberSInt32Type, &loadSizeValue)) {
288        loadSizeValue = (uint32_t)-1;
289    }
290    if (!getNumValue(wiredSize, kCFNumberSInt32Type, &wiredSizeValue)) {
291        wiredSizeValue = (uint32_t)-1;
292    }
293    if (!getNumValue(((CFNumberRef)CFDictionaryGetValue(kextInfo, CFSTR(kOSBundleCPUTypeKey))), kCFNumberSInt32Type, &cpuTypeValue)) {
294        cpuTypeValue = (uint32_t)-1;
295    }
296    if (!getNumValue(((CFNumberRef)CFDictionaryGetValue(kextInfo, CFSTR(kOSBundleCPUSubtypeKey))), kCFNumberSInt32Type, &cpuSubTypeValue)) {
297        cpuSubTypeValue = (uint32_t)-1;
298    }
299
300    bundleIDCString = createUTF8CStringForCFString(bundleID);
301    bundleVersionCString = createUTF8CStringForCFString(bundleVersion);
302
303   /* First column has no leading space.
304    *
305    * These field widths are from the old kextstat, may want to change them.
306    */
307    if (loadTagValue == kOSKextInvalidLoadTag) {
308        fprintf(stdout, "%5s", kStringInvalidShort);
309    } else {
310        fprintf(stdout, "%5d", loadTagValue);
311    }
312
313    if (retainCountValue == (uint32_t)-1) {
314        fprintf(stdout, " %4s", kStringInvalidShort);
315    } else {
316        fprintf(stdout, " %4d", retainCountValue);
317    }
318
319    if (toolArgs->runningKernelArch->cputype & CPU_ARCH_ABI64) {
320        if (loadAddressValue == (uint64_t)-1) {
321            fprintf(stdout, " %-18s", kStringInvalidLong);
322        } else {
323            fprintf(stdout, " %#-18llx", (uint64_t)loadAddressValue);
324        }
325    } else {
326        if (loadAddressValue == (uint64_t)-1) {
327            fprintf(stdout, " %-10s", kStringInvalidLong);
328        } else {
329            fprintf(stdout, " %#-10x", (uint32_t)loadAddressValue);
330        }
331    }
332
333    if (loadSizeValue == (uint32_t)-1) {
334        fprintf(stdout, " %-10s", kStringInvalidLong);
335    } else {
336        fprintf(stdout, " %#-10x", loadSizeValue);
337    }
338
339    if (wiredSizeValue == (uint32_t)-1) {
340        fprintf(stdout, " %-10s", kStringInvalidLong);
341    } else {
342        fprintf(stdout, " %#-10x", wiredSizeValue);
343    }
344
345    if (toolArgs->flagShowArchitecture) {
346        // include kext cputype/cpusubtype info
347        if (cpuTypeValue == (uint32_t) -1) {
348            fprintf(stdout, " %10s/%-7s", kStringInvalidLong, kStringInvalidLong);
349        }
350        else {
351            const NXArchInfo * archName = NXGetArchInfoFromCpuType(cpuTypeValue, cpuSubTypeValue);
352
353            if (archName != NULL) {
354                fprintf(stdout, " %-18s", archName->name);
355            }
356            else {
357                fprintf(stdout, " %#010x/%#-7x", cpuTypeValue, cpuSubTypeValue);
358            }
359        }
360    }
361
362    fprintf(stdout, " %s",
363        bundleIDCString ? bundleIDCString : kStringInvalidLong);
364
365    fprintf(stdout, " (%s)",
366        bundleVersionCString ? bundleVersionCString : kStringInvalidLong);
367
368    if (dependencyLoadTags && CFArrayGetCount(dependencyLoadTags)) {
369        sortedLoadTags = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0,
370            dependencyLoadTags);
371        if (!sortedLoadTags) {
372            OSKextLogMemError();
373            goto finish;
374        }
375
376        CFArraySortValues(sortedLoadTags, RANGE_ALL(sortedLoadTags),
377            &compareNumbers, /* context */ NULL);
378
379        fprintf(stdout, " <");
380        count = CFArrayGetCount(sortedLoadTags);
381        for (i = 0; i < count; i++) {
382            loadTag = (CFNumberRef)CFArrayGetValueAtIndex(sortedLoadTags, i);
383            if (!getNumValue(loadTag, kCFNumberSInt32Type, &loadTagValue)) {
384                loadTagValue = kOSKextInvalidLoadTag;
385            }
386
387            if (loadTagValue == kOSKextInvalidLoadTag) {
388                fprintf(stdout, "%s%s", i == 0 ? "" : " ", kStringInvalidShort);
389            } else {
390                fprintf(stdout, "%s%d", i == 0 ? "" : " ", loadTagValue);
391            }
392
393        }
394
395        fprintf(stdout, ">");
396
397    }
398
399    fprintf(stdout, "\n");
400
401finish:
402
403    SAFE_RELEASE(sortedLoadTags);
404    SAFE_FREE(bundleIDCString);
405    SAFE_FREE(bundleVersionCString);
406    return;
407}
408
409/*******************************************************************************
410*******************************************************************************/
411Boolean getNumValue(CFNumberRef aNumber, CFNumberType type, void * valueOut)
412{
413    if (aNumber && (CFNumberGetTypeID() == CFGetTypeID(aNumber))) {
414        return CFNumberGetValue(aNumber, type, valueOut);
415    }
416    return false;
417}
418
419/*******************************************************************************
420*******************************************************************************/
421int compareKextInfo(const void * vKextInfo1, const void * vKextInfo2)
422{
423    int             result = 0;
424    CFDictionaryRef kextInfo1 = *(CFDictionaryRef *)vKextInfo1;
425    CFDictionaryRef kextInfo2 = *(CFDictionaryRef *)vKextInfo2;
426    CFNumberRef     loadTag1 = CFDictionaryGetValue(kextInfo1, CFSTR(kOSBundleLoadTagKey));
427    CFNumberRef     loadTag2 = CFDictionaryGetValue(kextInfo2, CFSTR(kOSBundleLoadTagKey));
428    OSKextLoadTag   tag1Value = kOSKextInvalidLoadTag;
429    OSKextLoadTag   tag2Value = kOSKextInvalidLoadTag;
430
431    getNumValue(loadTag1, kCFNumberSInt32Type, &tag1Value);
432    getNumValue(loadTag2, kCFNumberSInt32Type, &tag2Value);
433
434    if (tag1Value == tag2Value) {
435       /* Whether invalid or valid, same is same. */
436        result = 0;
437    } else if (tag1Value == kOSKextInvalidLoadTag) {
438        result = -1;
439    } else if (tag2Value == kOSKextInvalidLoadTag) {
440        result = 1;
441    } else if (tag1Value < tag2Value) {
442        result = -1;
443    } else if (tag2Value < tag1Value) {
444        result = 1;
445    }
446
447    return result;
448}
449
450/*******************************************************************************
451*******************************************************************************/
452CFComparisonResult compareNumbers(
453    const void * val1,
454    const void * val2,
455          void * context)
456{
457    CFComparisonResult result = CFNumberCompare((CFNumberRef)val1,
458         (CFNumberRef)val2, context);
459     if (result == kCFCompareLessThan) {
460         result = kCFCompareGreaterThan;
461     } else if (result == kCFCompareGreaterThan) {
462         result = kCFCompareLessThan;
463     }
464     return result;
465}
466
467/*******************************************************************************
468*******************************************************************************/
469static void usage(UsageLevel usageLevel)
470{
471    fprintf(stderr, "usage: %s [-a] [-k] [-l] [-b bundle_id] ...\n", progname);
472
473    if (usageLevel == kUsageLevelBrief) {
474        fprintf(stderr, "\nUse %s -%s (-%c) for a list of options.\n",
475            progname, kOptNameHelp, kOptHelp);
476        return;
477    }
478
479    fprintf(stderr, "-%s (-%c): show only loadable kexts (omit kernel components).\n",
480        kOptNameNoKernelComponents, kOptNoKernelComponents);
481    fprintf(stderr, "-%s (-%c): print the list only, omitting the header.\n",
482        kOptNameListOnly, kOptListOnly);
483    fprintf(stderr, "-%s (-%c) <bundle_id>: print info for kexts named by identifier.\n",
484        kOptNameBundleIdentifier, kOptBundleIdentifier);
485    fprintf(stderr, "-%s (-%c): Include architecture info in output.\n",
486        kOptNameArchitecture, kOptArchitecture);
487
488    return;
489}
490
491