#include <IOKit/kext/OSKext.h>
#include <IOKit/kext/OSKextPrivate.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/mach_host.h>
#include "kextstat_main.h"
static const char * progname = "(unknown)";
ExitStatus main(int argc, char * const * argv)
{
ExitStatus result = EX_OK;
KextstatArgs toolArgs;
CFIndex count, i;
if (argv[0]) {
progname = argv[0];
}
OSKextSetLogOutputFunction(&tool_log);
result = readArgs(argc, argv, &toolArgs);
if (result != EX_OK) {
if (result == kKextstatExitHelp) {
result = EX_OK;
}
goto finish;
}
toolArgs.runningKernelArch = OSKextGetRunningKernelArchitecture();
if (!toolArgs.runningKernelArch) {
result = EX_OSERR;
goto finish;
}
toolArgs.loadedKextInfo = OSKextCreateLoadedKextInfo(toolArgs.bundleIDs);
if (!toolArgs.loadedKextInfo) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag | kOSKextLogIPCFlag,
"Couldn't get list of loaded kexts from kernel.");
result = EX_OSERR;
goto finish;
}
if (!toolArgs.flagListOnly) {
printf("Index Refs Address ");
if (toolArgs.runningKernelArch->cputype & CPU_ARCH_ABI64) {
printf(" ");
}
printf("Size Wired "
"Name (Version) <Linked Against>\n");
}
count = CFArrayGetCount(toolArgs.loadedKextInfo);
for (i = 0; i < count; i++) {
CFDictionaryRef kextInfo = (CFDictionaryRef)CFArrayGetValueAtIndex(
toolArgs.loadedKextInfo, i);
printKextInfo(kextInfo, &toolArgs);
}
finish:
exit(result);
return result;
}
ExitStatus readArgs(int argc, char * const * argv, KextstatArgs * toolArgs)
{
ExitStatus result = EX_USAGE;
CFStringRef scratchString = NULL; int optChar = 0;
bzero(toolArgs, sizeof(*toolArgs));
if (!createCFMutableArray(&toolArgs->bundleIDs, &kCFTypeArrayCallBacks)) {
goto finish;
}
result = EX_USAGE;
while ((optChar = getopt_long_only(argc, argv, kOptChars,
sOptInfo, NULL)) != -1) {
SAFE_RELEASE_NULL(scratchString);
switch (optChar) {
case kOptHelp:
usage(kUsageLevelFull);
result = kKextstatExitHelp;
goto finish;
break;
case kOptNoKernelComponents:
toolArgs->flagNoKernelComponents = true;
break;
case kOptListOnly:
toolArgs->flagListOnly = true;
break;
case kOptBundleIdentifier:
scratchString = CFStringCreateWithCString(kCFAllocatorDefault,
optarg, kCFStringEncodingUTF8);
if (!scratchString) {
OSKextLogMemError();
result = EX_OSERR;
goto finish;
}
CFArrayAppendValue(toolArgs->bundleIDs, scratchString);
break;
}
}
argc -= optind;
argv += optind;
if (argc) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Extra arguments starting at %s....", argv[0]);
usage(kUsageLevelBrief);
goto finish;
}
result = EX_OK;
finish:
SAFE_RELEASE_NULL(scratchString);
if (result == EX_USAGE) {
usage(kUsageLevelBrief);
}
return result;
}
#define kStringInvalidShort "??"
#define kStringInvalidLong "????"
void printKextInfo(CFDictionaryRef kextInfo, KextstatArgs * toolArgs)
{
CFBooleanRef isKernelComponent = NULL; CFNumberRef loadTag = NULL; CFNumberRef retainCount = NULL; CFNumberRef loadAddress = NULL; CFNumberRef loadSize = NULL; CFNumberRef wiredSize = NULL; CFStringRef bundleID = NULL; CFStringRef bundleVersion = NULL; CFArrayRef dependencyLoadTags = NULL; CFMutableArrayRef sortedLoadTags = NULL;
uint32_t loadTagValue = kOSKextInvalidLoadTag;
uint32_t retainCountValue = (uint32_t)-1;
uint64_t loadAddressValue = (uint64_t)-1;
uint32_t loadSizeValue = (uint32_t)-1;
uint32_t wiredSizeValue = (uint32_t)-1;
char * bundleIDCString = NULL; char * bundleVersionCString = NULL;
CFIndex count, i;
loadTag = (CFNumberRef)CFDictionaryGetValue(kextInfo,
CFSTR(kOSBundleLoadTagKey));
retainCount = (CFNumberRef)CFDictionaryGetValue(kextInfo,
CFSTR(kOSBundleRetainCountKey));
loadAddress = (CFNumberRef)CFDictionaryGetValue(kextInfo,
CFSTR(kOSBundleLoadAddressKey));
loadSize = (CFNumberRef)CFDictionaryGetValue(kextInfo,
CFSTR(kOSBundleLoadSizeKey));
wiredSize = (CFNumberRef)CFDictionaryGetValue(kextInfo,
CFSTR(kOSBundleWiredSizeKey));
bundleID = (CFStringRef)CFDictionaryGetValue(kextInfo,
kCFBundleIdentifierKey);
bundleVersion = (CFStringRef)CFDictionaryGetValue(kextInfo,
kCFBundleVersionKey);
dependencyLoadTags = (CFArrayRef)CFDictionaryGetValue(kextInfo,
CFSTR(kOSBundleDependenciesKey));
if (toolArgs->flagNoKernelComponents) {
isKernelComponent = (CFBooleanRef)CFDictionaryGetValue(kextInfo,
CFSTR(kOSKernelResourceKey));
if (isKernelComponent && CFBooleanGetValue(isKernelComponent)) {
if (bundleID &&
kCFNotFound == CFArrayGetFirstIndexOfValue(toolArgs->bundleIDs,
RANGE_ALL(toolArgs->bundleIDs), bundleID)) {
goto finish;
}
}
}
if (!getNumValue(loadTag, kCFNumberSInt32Type, &loadTagValue)) {
loadTagValue = kOSKextInvalidLoadTag;
}
if (!getNumValue(retainCount, kCFNumberSInt32Type, &retainCountValue)) {
retainCountValue = (uint32_t)-1;
}
if (!getNumValue(loadAddress, kCFNumberSInt64Type, &loadAddressValue)) {
loadAddressValue = (uint64_t)-1;
}
if (!getNumValue(loadSize, kCFNumberSInt32Type, &loadSizeValue)) {
loadSizeValue = (uint32_t)-1;
}
if (!getNumValue(wiredSize, kCFNumberSInt32Type, &wiredSizeValue)) {
wiredSizeValue = (uint32_t)-1;
}
bundleIDCString = createUTF8CStringForCFString(bundleID);
bundleVersionCString = createUTF8CStringForCFString(bundleVersion);
if (loadTagValue == kOSKextInvalidLoadTag) {
fprintf(stdout, "%5s", kStringInvalidShort);
} else {
fprintf(stdout, "%5d", loadTagValue);
}
if (retainCountValue == (uint32_t)-1) {
fprintf(stdout, " %4s", kStringInvalidShort);
} else {
fprintf(stdout, " %4d", retainCountValue);
}
if (toolArgs->runningKernelArch->cputype & CPU_ARCH_ABI64) {
if (loadAddressValue == (uint64_t)-1) {
fprintf(stdout, " %-18s", kStringInvalidLong);
} else {
fprintf(stdout, " %#-18llx", (uint64_t)loadAddressValue);
}
} else {
if (loadAddressValue == (uint64_t)-1) {
fprintf(stdout, " %-10s", kStringInvalidLong);
} else {
fprintf(stdout, " %#-10x", (uint32_t)loadAddressValue);
}
}
if (loadSizeValue == (uint32_t)-1) {
fprintf(stdout, " %-10s", kStringInvalidLong);
} else {
fprintf(stdout, " %#-10x", loadSizeValue);
}
if (wiredSizeValue == (uint32_t)-1) {
fprintf(stdout, " %-10s", kStringInvalidLong);
} else {
fprintf(stdout, " %#-10x", wiredSizeValue);
}
fprintf(stdout, " %s",
bundleIDCString ? bundleIDCString : kStringInvalidLong);
fprintf(stdout, " (%s)",
bundleVersionCString ? bundleVersionCString : kStringInvalidLong);
if (dependencyLoadTags && CFArrayGetCount(dependencyLoadTags)) {
sortedLoadTags = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0,
dependencyLoadTags);
if (!sortedLoadTags) {
OSKextLogMemError();
goto finish;
}
CFArraySortValues(sortedLoadTags, RANGE_ALL(sortedLoadTags),
&compareNumbers, NULL);
fprintf(stdout, " <");
count = CFArrayGetCount(sortedLoadTags);
for (i = 0; i < count; i++) {
loadTag = (CFNumberRef)CFArrayGetValueAtIndex(sortedLoadTags, i);
if (!getNumValue(loadTag, kCFNumberSInt32Type, &loadTagValue)) {
loadTagValue = kOSKextInvalidLoadTag;
}
if (loadTagValue == kOSKextInvalidLoadTag) {
fprintf(stdout, "%s%s", i == 0 ? "" : " ", kStringInvalidShort);
} else {
fprintf(stdout, "%s%d", i == 0 ? "" : " ", loadTagValue);
}
}
fprintf(stdout, ">");
}
fprintf(stdout, "\n");
finish:
SAFE_RELEASE(sortedLoadTags);
SAFE_FREE(bundleIDCString);
SAFE_FREE(bundleVersionCString);
return;
}
Boolean getNumValue(CFNumberRef aNumber, CFNumberType type, void * valueOut)
{
if (aNumber) {
return CFNumberGetValue(aNumber, type, valueOut);
}
return false;
}
CFComparisonResult compareNumbers(
const void * val1,
const void * val2,
void * context)
{
CFComparisonResult result = CFNumberCompare((CFNumberRef)val1,
(CFNumberRef)val2, context);
if (result == kCFCompareLessThan) {
result = kCFCompareGreaterThan;
} else if (result == kCFCompareGreaterThan) {
result = kCFCompareLessThan;
}
return result;
}
static void usage(UsageLevel usageLevel)
{
fprintf(stderr, "usage: %s [-k] [-l] [-b bundle_id] ...\n", progname);
if (usageLevel == kUsageLevelBrief) {
fprintf(stderr, "\nUse %s -%s (-%c) for a list of options.\n",
progname, kOptNameHelp, kOptHelp);
return;
}
fprintf(stderr, "-%s (-%c): show only loadable kexts (omit kernel components).\n",
kOptNameNoKernelComponents, kOptNoKernelComponents);
fprintf(stderr, "-%s (-%c): print the list only, omitting the header.\n",
kOptNameListOnly, kOptListOnly);
fprintf(stderr, "-%s (-%c) <bundle_id>: print info for kexts named by identifier.\n",
kOptNameBundleIdentifier, kOptBundleIdentifier);
return;
}