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