#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/kext/OSKext.h>
#include <IOKit/kext/OSKextPrivate.h>
#include "kextlibs_main.h"
#include "kext_tools_util.h"
const char * progname = "(unknown)";
int main(int argc, char * const * argv)
{
ExitStatus result = EX_OSERR;
ExitStatus printResult = EX_OSERR;
KextlibsArgs toolArgs;
CFArrayRef kexts = NULL;
const NXArchInfo ** arches = NULL; KextlibsInfo * libInfo = NULL; Boolean libsAreArchSpecific = FALSE;
CFIndex count, i;
CFIndex numArches;
progname = rindex(argv[0], '/');
if (progname) {
progname++; } else {
progname = (char *)argv[0];
}
OSKextSetLogOutputFunction(&tool_log);
result = readArgs(argc, argv, &toolArgs);
if (result != EX_OK) {
if (result == kKextlibsExitHelp) {
result = EX_OK;
}
goto finish;
}
result = EX_OSERR;
count = CFArrayGetCount(toolArgs.repositoryURLs);
if (!count || toolArgs.flagSysKexts) {
CFArrayRef osExtFolders = OSKextGetSystemExtensionsFolderURLs();
if (!osExtFolders) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Library error - can't get system extensions folders.");
goto finish;
}
count = CFArrayGetCount(osExtFolders);
for (i = 0; i < count; i++) {
CFTypeRef osExtFolder = CFArrayGetValueAtIndex(osExtFolders, i);
CFIndex folderIndex = CFArrayGetFirstIndexOfValue(
toolArgs.repositoryURLs, RANGE_ALL(toolArgs.repositoryURLs),
osExtFolder);
if (folderIndex == kCFNotFound) {
CFArrayInsertValueAtIndex(toolArgs.repositoryURLs, i,
osExtFolder);
}
}
}
kexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault,
toolArgs.repositoryURLs);
if (!kexts) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Can't read kexts from folders.");
goto finish;
}
toolArgs.kextURL = CFURLCreateFromFileSystemRepresentation(
kCFAllocatorDefault, (u_char *)toolArgs.kextName,
strlen(toolArgs.kextName), true);
if (!toolArgs.kextURL) {
OSKextLogStringError( NULL);
goto finish;
}
toolArgs.theKext = OSKextCreate(kCFAllocatorDefault,toolArgs. kextURL);
if (!toolArgs.theKext) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Can't open %s.", toolArgs.kextName);
goto finish;
}
arches = OSKextCopyArchitectures(toolArgs.theKext);
if (!arches || !arches[0]) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Can't determine architectures of %s.",
toolArgs.kextName);
goto finish;
}
result = EX_OK;
for (numArches = 0; arches[numArches]; numArches++) {
}
libInfo = (KextlibsInfo *)malloc(numArches * sizeof(KextlibsInfo));
if (!libInfo) {
OSKextLogMemError();
goto finish;
}
for (i = 0; i < numArches; i++) {
OSKextSetArchitecture(arches[i]);
libInfo[i].libKexts = OSKextFindLinkDependencies(toolArgs.theKext,
toolArgs.flagNonKPI, toolArgs.flagAllowUnsupported,
&libInfo[i].undefSymbols, &libInfo[i].onedefSymbols,
&libInfo[i].multdefSymbols, &libInfo[i].multdefLibs);
if (!libInfo[i].libKexts) {
OSKextLogMemError();
result = EX_OSERR;
goto finish;
}
}
if (numArches >= 2) {
for (i = 0; i < numArches - 1; i++) {
if (!CFEqual(libInfo[i].libKexts, libInfo[i+1].libKexts)) {
libsAreArchSpecific = TRUE;
break;
}
}
}
if (!libsAreArchSpecific) {
printResult = printLibs(&toolArgs, NULL, libInfo[0].libKexts,
TRUE);
if (printResult > result) {
result = printResult;
}
} else if (toolArgs.flagXML) {
for (i = 0; i < numArches; i++) {
if (libsAreArchSpecific) {
printResult = printLibs(&toolArgs, arches[i],
libInfo[i].libKexts,
i + 1 == numArches);
if (printResult > result) {
result = printResult;
}
}
}
}
for (i = 0; i < numArches; i++) {
if (libsAreArchSpecific && !toolArgs.flagXML) {
printResult = printLibs(&toolArgs, arches[i], libInfo[i].libKexts,
i + 1 == numArches);
if (printResult > result) {
result = printResult;
}
}
printResult = printProblems(&toolArgs, arches[i],
libInfo[i].undefSymbols, libInfo[i].onedefSymbols,
libInfo[i].multdefSymbols, libInfo[i].multdefLibs,
!libsAreArchSpecific || toolArgs.flagXML,
i + i < numArches);
if (printResult != EX_OK) {
if (printResult > result) {
result = printResult;
}
}
}
finish:
exit(result);
SAFE_FREE(arches);
SAFE_RELEASE(toolArgs.repositoryURLs);
SAFE_RELEASE(toolArgs.kextURL);
SAFE_RELEASE(toolArgs.theKext);
SAFE_RELEASE(kexts);
if (libInfo) {
for (i = 0; numArches; i++) {
SAFE_RELEASE_NULL(libInfo[i].libKexts);
SAFE_RELEASE_NULL(libInfo[i].undefSymbols);
SAFE_RELEASE_NULL(libInfo[i].onedefSymbols);
SAFE_RELEASE_NULL(libInfo[i].multdefSymbols);
SAFE_RELEASE_NULL(libInfo[i].multdefLibs);
}
free(libInfo);
}
return result;
}
ExitStatus readArgs(
int argc,
char * const * argv,
KextlibsArgs * toolArgs)
{
ExitStatus result = EX_USAGE;
int optChar = 0;
bzero(toolArgs, sizeof(*toolArgs));
toolArgs->repositoryURLs = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!toolArgs->repositoryURLs) {
OSKextLogMemError();
result = EX_OSERR;
goto finish;
}
result = EX_USAGE;
while ((optChar = getopt_long_only(argc, argv, kOptChars,
sOptInfo, NULL)) != -1) {
switch (optChar) {
case kOptHelp:
usage(kUsageLevelFull);
result = kKextlibsExitHelp;
goto finish;
break;
case kOptRepository:
result = addRepository(toolArgs, optarg);
if (result != EX_OK) {
goto finish;
};
break;
case kOptCompatible:
toolArgs->flagCompatible = true;
break;
case kOptSystemExtensions:
toolArgs->flagSysKexts = true;
break;
case kOptQuiet:
beQuiet();
break;
case kOptVerbose:
result = setLogFilterForOpt(argc, argv, 0);
break;
case 0:
switch (longopt) {
case kLongOptXML:
toolArgs->flagXML = true;
break;
case kLongOptAllSymbols:
toolArgs->flagPrintUndefSymbols = true;
toolArgs->flagPrintOnedefSymbols = true;
toolArgs->flagPrintMultdefSymbols = true;
break;
case kLongOptUndefSymbols:
toolArgs->flagPrintUndefSymbols = true;
break;
case kLongOptOnedefSymbols:
toolArgs->flagPrintOnedefSymbols = true;
break;
case kLongOptMultdefSymbols:
toolArgs->flagPrintMultdefSymbols = true;
break;
case kLongOptNonKPI:
toolArgs->flagNonKPI = true;
break;
case kLongOptUnsupported:
toolArgs->flagAllowUnsupported = true;
break;
}
break;
default:
usage(kUsageLevelBrief);
goto finish;
break;
}
}
argc -= optind;
argv += optind;
if (!argv[0]) {
fprintf(stderr, "No kext specified.");
usage(kUsageLevelBrief);
goto finish;
}
result = checkPath(argv[0], kOSKextBundleExtension,
TRUE, FALSE);
if (result != EX_OK) {
goto finish;
}
toolArgs->kextName = argv[0];
argc--;
argv++;
if (argc) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Too many arguments starting at '%s'.", argv[0]);
usage(kUsageLevelBrief);
goto finish;
}
result = EX_OK;
finish:
return result;
}
ExitStatus addRepository(
KextlibsArgs * toolArgs,
const char * path)
{
ExitStatus result = EX_OSERR;
CFURLRef url = NULL;
result = checkPath(path, NULL,
TRUE, FALSE);
if (result != EX_OK) {
goto finish;
}
url = CFURLCreateFromFileSystemRepresentation(
kCFAllocatorDefault, (const UInt8 *)path, strlen(path), true);
if (!url) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Can't create CFURL for '%s'.", path);
goto finish;
}
addToArrayIfAbsent(toolArgs->repositoryURLs, url);
result = EX_OK;
finish:
SAFE_RELEASE(url);
return result;
}
ExitStatus printLibs(
KextlibsArgs * toolArgs,
const NXArchInfo * arch,
CFArrayRef libKexts,
Boolean trailingNewlineFlag)
{
ExitStatus result = EX_OSERR;
const NXArchInfo * genericArch = NULL; char * libIdentifier = NULL; CFIndex count, i;
if (arch) {
genericArch = NXGetArchInfoFromCpuType(arch->cputype,
CPU_SUBTYPE_MULTIPLE);
if (!genericArch) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Can't find generic NXArchInfo for %s.",
arch->name);
goto finish;
}
}
count = CFArrayGetCount(libKexts);
if (toolArgs->flagXML) {
if (count) {
fprintf(stdout, "\t<key>OSBundleLibraries%s%s</key>\n",
genericArch ? "_" : "",
genericArch ? genericArch->name : "");
fprintf(stdout, "\t<dict>\n");
}
} else {
if (arch) {
fprintf(stderr, "For %s:\n", arch->name);
} else {
fprintf(stderr, "For all architectures:\n");
}
if (!count) {
fprintf(stdout, " No libraries found.\n");
}
}
for (i = 0; i < count; i++) {
OSKextRef libKext = (OSKextRef)CFArrayGetValueAtIndex(libKexts, i);
OSKextVersion version;
char versCString[kOSKextVersionMaxLength];
SAFE_FREE_NULL(libIdentifier);
libIdentifier = createUTF8CStringForCFString(OSKextGetIdentifier(libKext));
if (toolArgs->flagCompatible) {
version = OSKextGetCompatibleVersion(libKext);
} else {
version = OSKextGetVersion(libKext);
}
if (libIdentifier && version > kOSKextVersionUndefined) {
OSKextVersionGetString(version, versCString, sizeof(versCString));
if (toolArgs->flagXML) {
fprintf(stdout, "\t\t<key>%s</key>\n", libIdentifier);
fprintf(stdout, "\t\t<string>%s</string>\n", versCString);
} else {
fprintf(stdout, " %s = %s\n", libIdentifier, versCString);
}
} else {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Internal error generating library list.");
goto finish;
}
}
if (count && toolArgs->flagXML) {
fprintf(stdout, "\t</dict>\n");
}
if (trailingNewlineFlag) {
fprintf(stderr, "\n");
}
result = kKextlibsExitOK;
finish:
SAFE_FREE(libIdentifier);
return result;
}
ExitStatus printProblems(
KextlibsArgs * toolArgs,
const NXArchInfo * arch,
CFDictionaryRef undefSymbols,
CFDictionaryRef onedefSymbols,
CFDictionaryRef multdefSymbols,
CFArrayRef multdefLibs,
Boolean printArchFlag,
Boolean trailingNewlineFlag)
{
ExitStatus result = EX_OSERR;
CFIndex undefCount, onedefCount, multdefCount;
CFIndex count, i;
onedefCount = CFDictionaryGetCount(onedefSymbols);
undefCount = CFDictionaryGetCount(undefSymbols);
multdefCount = CFDictionaryGetCount(multdefSymbols);
if (!toolArgs->flagPrintOnedefSymbols && !undefCount && !multdefCount) {
result = kKextlibsExitOK;
goto finish;
}
if (printArchFlag) {
fprintf(stderr, "For %s:\n", arch->name);
}
if (toolArgs->flagPrintOnedefSymbols) {
fprintf(stderr, " %ld symbol%s found in one library kext each%s\n",
onedefCount,
onedefCount > 1 ? "s" : "",
onedefCount && toolArgs->flagPrintOnedefSymbols ? ":" : ".");
CFDictionaryApplyFunction(onedefSymbols, printOnedefSymbol,
&(toolArgs->flagCompatible));
}
if (undefCount) {
fprintf(stderr, " %ld symbol%s not found in any library kext%s\n",
undefCount,
undefCount > 1 ? "s" : "",
toolArgs->flagPrintUndefSymbols ? ":" : ".");
if (toolArgs->flagPrintUndefSymbols) {
CFDictionaryApplyFunction(undefSymbols, printUndefSymbol, NULL);
}
result = kKextlibsExitUndefineds;
}
if (multdefCount) {
if (toolArgs->flagPrintMultdefSymbols) {
fprintf(stderr, " %ld symbol%s found in more than one library kext:\n",
multdefCount,
multdefCount > 1 ? "s" : "");
CFDictionaryApplyFunction(multdefSymbols, printMultdefSymbol,
&(toolArgs->flagCompatible));
} else {
count = CFArrayGetCount(multdefLibs);
fprintf(stderr, " Multiple symbols found among %ld libraries:\n",
count);
for (i = 0; i < count; i++) {
OSKextRef lib = (OSKextRef)CFArrayGetValueAtIndex(multdefLibs, i);
char * name = NULL; name = createUTF8CStringForCFString(OSKextGetIdentifier(lib));
if (name) {
fprintf(stderr, "\t%s\n", name);
}
SAFE_FREE(name);
}
}
result = kKextlibsExitMultiples;
}
if (trailingNewlineFlag) {
fprintf(stderr, "\n");
}
if (undefCount || multdefCount) {
goto finish;
}
result = kKextlibsExitOK;
finish:
return result;
}
void printUndefSymbol(const void * key, const void * value, void * context)
{
char * cSymbol = NULL;
cSymbol = createUTF8CStringForCFString((CFStringRef)key);
if (cSymbol) {
fprintf(stderr, "\t%s\n", cSymbol);
}
SAFE_FREE(cSymbol);
return;
}
void printOnedefSymbol(const void * key, const void * value, void * context)
{
Boolean flagCompatible = *(Boolean *)context;
OSKextRef libKext = (OSKextRef)value;
char * cSymbol = NULL; char * libVers = NULL; CFURLRef libKextURL;
char libKextName[PATH_MAX];
CFStringRef libVersString = NULL;
cSymbol = createUTF8CStringForCFString((CFStringRef)key);
if (!cSymbol) {
OSKextLogStringError( NULL);
goto finish;
}
libKextURL = OSKextGetURL(libKext);
if (!CFURLGetFileSystemRepresentation(libKextURL,
false,
(u_char *)libKextName, PATH_MAX)) {
OSKextLogStringError( NULL);
goto finish;
}
if (flagCompatible) {
libVersString = OSKextGetValueForInfoDictionaryKey(libKext,
CFSTR("OSBundleCompatibleVersion"));
} else {
libVersString = OSKextGetValueForInfoDictionaryKey(libKext,
kCFBundleVersionKey);
}
libVers = createUTF8CStringForCFString(libVersString);
if (!libVers) {
OSKextLogStringError( NULL);
goto finish;
}
fprintf(stderr, " %s in %s (%s%s)\n", cSymbol, libKextName,
flagCompatible ? "compatible version " : "", libVers);
finish:
SAFE_FREE(cSymbol);
SAFE_FREE(libVers);
return;
}
void printMultdefSymbol(const void * key, const void * value, void * context)
{
char * cSymbol = NULL; char * libVers = NULL; CFArrayRef libs = (CFArrayRef)value;
Boolean flagCompatible = *(Boolean *)context;
CFIndex count, i;
cSymbol = createUTF8CStringForCFString((CFStringRef)key);
if (cSymbol) {
fprintf(stderr, " %s: in\n", cSymbol);
}
count = CFArrayGetCount(libs);
for (i = 0; i < count; i++) {
OSKextRef libKext = (OSKextRef)CFArrayGetValueAtIndex(libs, i);
CFURLRef libKextURL;
char libKextName[PATH_MAX];
CFStringRef libVersString = NULL;
SAFE_FREE_NULL(libVers);
libKextURL = OSKextGetURL(libKext);
if (!CFURLGetFileSystemRepresentation(libKextURL,
true,
(u_char *)libKextName, PATH_MAX)) {
fprintf(stderr, "string/url conversion error\n");
goto finish;
}
if (flagCompatible) {
libVersString = OSKextGetValueForInfoDictionaryKey(libKext,
CFSTR("OSBundleCompatibleVersion"));
} else {
libVersString = OSKextGetValueForInfoDictionaryKey(libKext,
kCFBundleVersionKey);
}
libVers = createUTF8CStringForCFString(libVersString);
if (!libVers) {
fprintf(stderr, "string/url conversion error\n");
goto finish;
}
fprintf(stderr, " %s (%s%s)\n", libKextName,
flagCompatible ? "compatible version " : "", libVers);
}
finish:
SAFE_FREE(cSymbol);
SAFE_FREE(libVers);
return;
}
static void usage(UsageLevel usageLevel)
{
fprintf(stderr, "usage: %s [options] kext\n", progname);
if (usageLevel == kUsageLevelBrief) {
fprintf(stderr, "\nuse %s -%s for a list of options\n",
progname, kOptNameHelp);
return;
}
fprintf(stderr, "\n");
fprintf(stderr, "<kext>: the kext to find libraries for\n");
fprintf(stderr, "-%s <arch>:\n", kOptNameArch);
fprintf(stderr, " resolve for architecture <arch> instead of running kernel's\n");
fprintf(stderr, "-%s: print XML fragment suitable for pasting\n", kOptNameXML);
fprintf(stderr, "\n");
fprintf(stderr, "-%s (-%c):\n",
kOptNameSystemExtensions, kOptSystemExtensions);
fprintf(stderr, " look in the system exensions folder (assumed if no other folders\n"
" specified with %s)\n",kOptNameRepository);
fprintf(stderr, "-%s <directory> (-%c):\n", kOptNameRepository, kOptRepository);
fprintf(stderr, " look in <directory> for library kexts\n");
fprintf(stderr, "%s", "\n");
fprintf(stderr, "-%s:\n", kOptNameAllSymbols);
fprintf(stderr, " list all symbols, found, not found, or found more than once\n");
fprintf(stderr, "-%s:\n", kOptNameOnedefSymbols);
fprintf(stderr, " list all symbols found with the library kext they were found in\n");
fprintf(stderr, "-%s:\n", kOptNameUndefSymbols);
fprintf(stderr, " list all symbols not found in any library\n");
fprintf(stderr, "-%s:\n", kOptNameMultdefSymbols);
fprintf(stderr, " list all symbols found more than once with their library kexts\n");
fprintf(stderr, "%s", "\n");
fprintf(stderr, "-%s (-%c):\n", kOptNameCompatible, kOptCompatible);
fprintf(stderr, " use library kext compatble versions rather than current versions\n");
fprintf(stderr, "-%s:\n", kOptNameUnsupported);
fprintf(stderr, " look in unsupported kexts for symbols\n");
return;
}